feat: 重构管理员权限管理,移除 AdminPermission 模型,整合权限与角色管理逻辑,优化 API 接口以支持角色与权限的同步,增强数据库填充器以对齐权限配置
This commit is contained in:
@@ -2,9 +2,11 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Support\AdminPermissionBridge;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
|
||||
class AdminUser extends Authenticatable
|
||||
@@ -38,76 +40,159 @@ class AdminUser extends Authenticatable
|
||||
];
|
||||
}
|
||||
|
||||
/** @return BelongsToMany<AdminRole, AdminUser> */
|
||||
public static function defaultAdminSiteId(): int
|
||||
{
|
||||
static $cached = null;
|
||||
if ($cached !== null) {
|
||||
return $cached;
|
||||
}
|
||||
$id = DB::table('admin_sites')->where('is_default', true)->value('id');
|
||||
if ($id === null) {
|
||||
$id = DB::table('admin_sites')->orderBy('id')->value('id');
|
||||
}
|
||||
if ($id === null) {
|
||||
throw new \RuntimeException('No admin_sites row found.');
|
||||
}
|
||||
$cached = (int) $id;
|
||||
|
||||
return $cached;
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户在各站点上的角色(多站点 RBAC)。
|
||||
*
|
||||
* @return BelongsToMany<AdminRole, AdminUser>
|
||||
*/
|
||||
public function roles(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
AdminRole::class,
|
||||
'admin_user_roles',
|
||||
'admin_user_site_roles',
|
||||
'admin_user_id',
|
||||
'role_id',
|
||||
);
|
||||
)->withPivot(['site_id', 'granted_at']);
|
||||
}
|
||||
|
||||
/** @return BelongsToMany<AdminPermission, AdminUser> */
|
||||
public function permissions(): BelongsToMany
|
||||
public function isSuperAdmin(): bool
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
AdminPermission::class,
|
||||
'admin_user_permissions',
|
||||
'admin_user_id',
|
||||
'permission_id',
|
||||
);
|
||||
}
|
||||
|
||||
/** 是否具备指定权限(含 `super_admin` 角色全放行)。 */
|
||||
public function hasAdminPermission(string $slug): bool
|
||||
{
|
||||
$this->loadMissing(['roles.permissions', 'permissions']);
|
||||
|
||||
foreach ($this->roles as $role) {
|
||||
if ($role->slug === self::ROLE_SUPER_ADMIN) {
|
||||
return true;
|
||||
}
|
||||
foreach ($role->permissions as $permission) {
|
||||
if ($permission->slug === $slug) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ($this->relationLoaded('roles')) {
|
||||
return $this->roles->contains('slug', self::ROLE_SUPER_ADMIN);
|
||||
}
|
||||
|
||||
foreach ($this->permissions as $permission) {
|
||||
if ($permission->slug === $slug) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return $this->roles()->where('admin_roles.slug', self::ROLE_SUPER_ADMIN)->exists();
|
||||
}
|
||||
|
||||
/**
|
||||
* 仅来自「直接授权」的 menu_action.permission_code(默认站点,含 site_id 为 null 的历史行)。
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public function adminPermissionSlugs(): array
|
||||
public function directMenuActionPermissionCodes(): array
|
||||
{
|
||||
$this->loadMissing(['roles.permissions', 'permissions']);
|
||||
if ($this->roles->contains('slug', self::ROLE_SUPER_ADMIN)) {
|
||||
return AdminPermission::query()->orderBy('slug')->pluck('slug')->all();
|
||||
}
|
||||
$siteId = self::defaultAdminSiteId();
|
||||
$rows = DB::table('admin_user_menu_actions as uma')
|
||||
->join('admin_menu_actions as ma', 'ma.id', '=', 'uma.menu_action_id')
|
||||
->where('uma.admin_user_id', $this->id)
|
||||
->where(function ($q) use ($siteId): void {
|
||||
$q->where('uma.site_id', $siteId)->orWhereNull('uma.site_id');
|
||||
})
|
||||
->where('ma.status', 1)
|
||||
->pluck('ma.permission_code')
|
||||
->all();
|
||||
|
||||
$out = [];
|
||||
foreach ($this->roles as $role) {
|
||||
foreach ($role->permissions as $permission) {
|
||||
$out[$permission->slug] = true;
|
||||
foreach ($rows as $code) {
|
||||
if (is_string($code) && $code !== '') {
|
||||
$out[$code] = true;
|
||||
}
|
||||
}
|
||||
foreach ($this->permissions as $permission) {
|
||||
$out[$permission->slug] = true;
|
||||
}
|
||||
|
||||
return array_keys($out);
|
||||
}
|
||||
|
||||
/**
|
||||
* 直接授权对应的 `prd.*` 展示列表(与 {@see self::directMenuActionPermissionCodes()} 桥接)。
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public function directLegacyPermissionSlugs(): array
|
||||
{
|
||||
return AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes($this->directMenuActionPermissionCodes());
|
||||
}
|
||||
|
||||
/**
|
||||
* 角色 + 直接授权合并后的 menu_action.permission_code。
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public function effectiveMenuActionPermissionCodes(): array
|
||||
{
|
||||
if ($this->isSuperAdmin()) {
|
||||
$codes = DB::table('admin_menu_actions')->where('status', 1)->pluck('permission_code')->all();
|
||||
$out = [];
|
||||
foreach ($codes as $c) {
|
||||
if (is_string($c) && $c !== '') {
|
||||
$out[$c] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return array_keys($out);
|
||||
}
|
||||
|
||||
$fromRoles = DB::table('admin_user_site_roles as usr')
|
||||
->join('admin_role_menu_actions as rma', 'rma.role_id', '=', 'usr.role_id')
|
||||
->join('admin_menu_actions as ma', 'ma.id', '=', 'rma.menu_action_id')
|
||||
->where('usr.admin_user_id', $this->id)
|
||||
->where('ma.status', 1)
|
||||
->pluck('ma.permission_code')
|
||||
->all();
|
||||
|
||||
$merged = [];
|
||||
foreach (array_merge($fromRoles, $this->directMenuActionPermissionCodes()) as $c) {
|
||||
if (is_string($c) && $c !== '') {
|
||||
$merged[$c] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return array_keys($merged);
|
||||
}
|
||||
|
||||
/** 是否具备指定权限:`prd.*` 走 legacy_map;否则按 permission_code 精确匹配。含 `super_admin` 全放行。 */
|
||||
public function hasAdminPermission(string $slug): bool
|
||||
{
|
||||
if ($this->isSuperAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$effective = $this->effectiveMenuActionPermissionCodes();
|
||||
if ($slug !== '' && in_array($slug, $effective, true)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (! str_starts_with($slug, 'prd.')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$needed = AdminPermissionBridge::menuActionCodesForLegacy($slug);
|
||||
if ($needed === []) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return count(array_intersect($needed, $effective)) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string> 与 Next 侧栏、`admin.permission` 中间件一致的 `prd.*` slug 列表
|
||||
*/
|
||||
public function adminPermissionSlugs(): array
|
||||
{
|
||||
if ($this->isSuperAdmin()) {
|
||||
return AdminPermissionBridge::allLegacySlugs();
|
||||
}
|
||||
|
||||
return AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes($this->effectiveMenuActionPermissionCodes());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
@@ -118,6 +203,7 @@ class AdminUser extends Authenticatable
|
||||
return $this->roles
|
||||
->pluck('slug')
|
||||
->filter(static fn ($slug): bool => is_string($slug) && $slug !== '')
|
||||
->unique()
|
||||
->values()
|
||||
->all();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user