feat: 重构管理员权限管理,移除 AdminPermission 模型,整合权限与角色管理逻辑,优化 API 接口以支持角色与权限的同步,增强数据库填充器以对齐权限配置
This commit is contained in:
10
app/Models/AdminMenu.php
Normal file
10
app/Models/AdminMenu.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class AdminMenu extends Model
|
||||
{
|
||||
protected $table = 'admin_menus';
|
||||
}
|
||||
25
app/Models/AdminMenuAction.php
Normal file
25
app/Models/AdminMenuAction.php
Normal file
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class AdminMenuAction extends Model
|
||||
{
|
||||
protected $table = 'admin_menu_actions';
|
||||
|
||||
protected $fillable = [
|
||||
'menu_id',
|
||||
'action_id',
|
||||
'permission_code',
|
||||
'name',
|
||||
'status',
|
||||
];
|
||||
|
||||
/** @return BelongsTo<AdminMenu, AdminMenuAction> */
|
||||
public function menu(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(AdminMenu::class, 'menu_id');
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
|
||||
class AdminPermission extends Model
|
||||
{
|
||||
protected $table = 'admin_permissions';
|
||||
|
||||
protected $fillable = [
|
||||
'slug',
|
||||
'name',
|
||||
];
|
||||
|
||||
/** @return BelongsToMany<AdminRole, AdminPermission> */
|
||||
public function roles(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
AdminRole::class,
|
||||
'admin_role_permissions',
|
||||
'permission_id',
|
||||
'role_id',
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -2,26 +2,44 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Support\AdminPermissionBridge;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class AdminRole extends Model
|
||||
{
|
||||
protected $table = 'admin_roles';
|
||||
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (AdminRole $role): void {
|
||||
if (($role->code ?? '') === '' && is_string($role->slug) && $role->slug !== '') {
|
||||
$role->code = $role->slug;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'slug',
|
||||
'name',
|
||||
'code',
|
||||
'description',
|
||||
'status',
|
||||
'is_system',
|
||||
'sort_order',
|
||||
];
|
||||
|
||||
/** @return BelongsToMany<AdminPermission, AdminRole> */
|
||||
public function permissions(): BelongsToMany
|
||||
/**
|
||||
* @return BelongsToMany<AdminMenuAction, AdminRole>
|
||||
*/
|
||||
public function menuActions(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
AdminPermission::class,
|
||||
'admin_role_permissions',
|
||||
AdminMenuAction::class,
|
||||
'admin_role_menu_actions',
|
||||
'role_id',
|
||||
'permission_id',
|
||||
'menu_action_id',
|
||||
);
|
||||
}
|
||||
|
||||
@@ -30,9 +48,24 @@ class AdminRole extends Model
|
||||
{
|
||||
return $this->belongsToMany(
|
||||
AdminUser::class,
|
||||
'admin_user_roles',
|
||||
'admin_user_site_roles',
|
||||
'role_id',
|
||||
'admin_user_id',
|
||||
);
|
||||
)->withPivot(['site_id', 'granted_at']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return list<string>
|
||||
*/
|
||||
public function legacyPermissionSlugs(): array
|
||||
{
|
||||
$codes = DB::table('admin_role_menu_actions as rma')
|
||||
->join('admin_menu_actions as ma', 'ma.id', '=', 'rma.menu_action_id')
|
||||
->where('rma.role_id', $this->id)
|
||||
->where('ma.status', 1)
|
||||
->pluck('ma.permission_code')
|
||||
->all();
|
||||
|
||||
return AdminPermissionBridge::legacySlugsGrantedByMenuActionCodes($codes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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