feat: 重构管理员权限管理,移除 AdminPermission 模型,整合权限与角色管理逻辑,优化 API 接口以支持角色与权限的同步,增强数据库填充器以对齐权限配置

This commit is contained in:
2026-05-13 10:40:07 +08:00
parent 3c92bef774
commit edd863764b
18 changed files with 1486 additions and 224 deletions

View File

@@ -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();
}