feat: 增强管理员权限与角色管理功能

- 在 SyncAdminAuthorizationCommand 中新增对代理和抽奖菜单操作的同步功能,确保缺失的菜单操作行能够被创建。
- 更新多个控制器中的权限检查逻辑,使用 hasPermissionCode 替代原有的权限验证方式,提升权限管理的灵活性。
- 引入 ApiMessage 统一错误响应格式,确保在权限不足时返回一致的错误信息。
- 更新 AdminRole 和 AdminUser 模型,增强角色与用户的权限管理功能,支持更细粒度的权限控制。
This commit is contained in:
2026-06-03 10:56:36 +08:00
parent 1dcd4716c5
commit 0527c7c392
96 changed files with 2215 additions and 139 deletions

View File

@@ -0,0 +1,177 @@
<?php
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use App\Support\AdminAuthorizationRegistry;
use Illuminate\Database\Migrations\Migration;
/**
* 代理「节点 / 角色 / 账号」查看与管理拆分为独立 permission_code避免只勾一项却获得全部管理能力。
*/
return new class extends Migration
{
public function up(): void
{
$now = Carbon::now();
$viewActionId = DB::table('admin_action_catalog')->where('code', 'view')->value('id');
$manageActionId = DB::table('admin_action_catalog')->where('code', 'manage')->value('id');
if ($viewActionId === null || $manageActionId === null) {
return;
}
$agentMenuId = (int) DB::table('admin_menus')->where('code', 'system.agents')->value('id');
if ($agentMenuId === 0) {
return;
}
$rolesMenuId = $this->ensureChildMenu($agentMenuId, 'system.agents.roles', '代理角色', $now);
$usersMenuId = $this->ensureChildMenu($agentMenuId, 'system.agents.users', '代理账号', $now);
$this->ensureMenuAction((int) $rolesMenuId, (int) $viewActionId, 'agent.role.view', '代理角色查看', $now);
$this->ensureMenuAction((int) $rolesMenuId, (int) $manageActionId, 'agent.role.manage', '代理角色管理', $now);
$this->ensureMenuAction((int) $usersMenuId, (int) $viewActionId, 'agent.user.view', '代理账号查看', $now);
$this->ensureMenuAction((int) $usersMenuId, (int) $manageActionId, 'agent.user.manage', '代理账号管理', $now);
$menuActionIds = DB::table('admin_menu_actions')->pluck('id', 'permission_code');
$nodeViewId = $menuActionIds['agent.node.view'] ?? null;
$nodeManageId = $menuActionIds['agent.node.manage'] ?? null;
$roleViewId = $menuActionIds['agent.role.view'] ?? null;
$roleManageId = $menuActionIds['agent.role.manage'] ?? null;
$userViewId = $menuActionIds['agent.user.view'] ?? null;
$userManageId = $menuActionIds['agent.user.manage'] ?? null;
if ($nodeViewId !== null && $roleViewId !== null && $userViewId !== null) {
$roleIdsWithNodeView = DB::table('admin_role_menu_actions')
->where('menu_action_id', (int) $nodeViewId)
->pluck('role_id')
->unique()
->all();
foreach ($roleIdsWithNodeView as $roleId) {
foreach ([$roleViewId, $userViewId] as $actionId) {
$this->attachRoleMenuAction((int) $roleId, (int) $actionId, $now);
}
}
}
if ($nodeManageId !== null && $roleManageId !== null && $userManageId !== null) {
$roleIdsWithNodeManage = DB::table('admin_role_menu_actions')
->where('menu_action_id', (int) $nodeManageId)
->pluck('role_id')
->unique()
->all();
foreach ($roleIdsWithNodeManage as $roleId) {
foreach ([$roleManageId, $userManageId] as $actionId) {
$this->attachRoleMenuAction((int) $roleId, (int) $actionId, $now);
}
}
}
$resources = array_values(array_filter(
AdminAuthorizationRegistry::resources(),
static fn (array $resource): bool => str_starts_with((string) $resource['code'], 'admin.agent-')
));
foreach ($resources as $resource) {
$resourceId = DB::table('admin_api_resources')->where('code', $resource['code'])->value('id');
if ($resourceId === null) {
continue;
}
DB::table('admin_api_resource_bindings')
->where('api_resource_id', (int) $resourceId)
->delete();
foreach ($resource['permission_codes'] ?? [] as $permissionCode) {
$menuActionId = $menuActionIds[$permissionCode] ?? null;
if ($menuActionId === null) {
continue;
}
DB::table('admin_api_resource_bindings')->insert([
'api_resource_id' => (int) $resourceId,
'menu_action_id' => (int) $menuActionId,
'created_at' => $now,
'updated_at' => $now,
]);
}
}
}
private function ensureChildMenu(int $parentId, string $code, string $name, Carbon $now): int
{
$existing = DB::table('admin_menus')->where('code', $code)->value('id');
if ($existing !== null) {
return (int) $existing;
}
return (int) DB::table('admin_menus')->insertGetId([
'parent_id' => $parentId,
'menu_type' => 'button',
'code' => $code,
'name' => $name,
'path' => null,
'route_name' => null,
'component' => null,
'icon' => null,
'active_menu_code' => 'system.agents',
'sort_order' => 0,
'is_visible' => false,
'is_cache' => false,
'is_external' => false,
'status' => 1,
'meta_json' => null,
'created_at' => $now,
'updated_at' => $now,
]);
}
private function ensureMenuAction(int $menuId, int $actionId, string $permissionCode, string $name, Carbon $now): void
{
if (DB::table('admin_menu_actions')->where('permission_code', $permissionCode)->exists()) {
return;
}
DB::table('admin_menu_actions')->insert([
'menu_id' => $menuId,
'action_id' => $actionId,
'permission_code' => $permissionCode,
'name' => $name,
'status' => 1,
'created_at' => $now,
'updated_at' => $now,
]);
}
private function attachRoleMenuAction(int $roleId, int $menuActionId, Carbon $now): void
{
$exists = DB::table('admin_role_menu_actions')
->where('role_id', $roleId)
->where('menu_action_id', $menuActionId)
->exists();
if ($exists) {
return;
}
DB::table('admin_role_menu_actions')->insert([
'role_id' => $roleId,
'menu_action_id' => $menuActionId,
]);
}
public function down(): void
{
$codes = ['agent.role.view', 'agent.role.manage', 'agent.user.view', 'agent.user.manage'];
$actionIds = DB::table('admin_menu_actions')->whereIn('permission_code', $codes)->pluck('id')->all();
if ($actionIds !== []) {
DB::table('admin_role_menu_actions')->whereIn('menu_action_id', $actionIds)->delete();
DB::table('admin_api_resource_bindings')->whereIn('menu_action_id', $actionIds)->delete();
DB::table('admin_menu_actions')->whereIn('permission_code', $codes)->delete();
}
DB::table('admin_menus')->whereIn('code', ['system.agents.roles', 'system.agents.users'])->delete();
}
};

View File

@@ -0,0 +1,87 @@
<?php
use App\Support\AdminAuthorizationRegistry;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
/** 补齐代理账号删除 API 资源admin.agent-admin-users.destroy。 */
return new class extends Migration
{
private const RESOURCE_CODE = 'admin.agent-admin-users.destroy';
public function up(): void
{
$resource = collect(AdminAuthorizationRegistry::resources())
->firstWhere('code', self::RESOURCE_CODE);
if ($resource === null) {
return;
}
$now = Carbon::now();
$menuActionIds = DB::table('admin_menu_actions')->pluck('id', 'permission_code');
$resourceId = DB::table('admin_api_resources')
->where('code', self::RESOURCE_CODE)
->value('id');
$payload = [
'module_code' => $resource['module_code'],
'name' => $resource['name'],
'http_method' => $resource['http_method'],
'uri_pattern' => $resource['uri_pattern'],
'route_name' => $resource['route_name'],
'auth_mode' => $resource['auth_mode'],
'is_audit_required' => $resource['is_audit_required'],
'status' => 1,
'meta_json' => null,
'updated_at' => $now,
];
if ($resourceId === null) {
$resourceId = DB::table('admin_api_resources')->insertGetId($payload + [
'code' => self::RESOURCE_CODE,
'created_at' => $now,
]);
} else {
DB::table('admin_api_resources')
->where('id', (int) $resourceId)
->update($payload);
}
DB::table('admin_api_resource_bindings')
->where('api_resource_id', (int) $resourceId)
->delete();
foreach ($resource['permission_codes'] as $permissionCode) {
$menuActionId = $menuActionIds[$permissionCode] ?? null;
if ($menuActionId === null) {
continue;
}
DB::table('admin_api_resource_bindings')->insert([
'api_resource_id' => (int) $resourceId,
'menu_action_id' => (int) $menuActionId,
'created_at' => $now,
'updated_at' => $now,
]);
}
}
public function down(): void
{
$resourceId = DB::table('admin_api_resources')
->where('code', self::RESOURCE_CODE)
->value('id');
if ($resourceId === null) {
return;
}
DB::table('admin_api_resource_bindings')
->where('api_resource_id', (int) $resourceId)
->delete();
DB::table('admin_api_resources')->where('id', (int) $resourceId)->delete();
}
};

View File

@@ -5,6 +5,8 @@ namespace Database\Seeders;
use App\Models\AdminRole;
use App\Models\AdminUser;
use Illuminate\Database\Seeder;
use App\Support\AdminAgentPermissionMenuActionSync;
use App\Support\AdminDrawPermissionMenuActionSync;
use App\Support\AdminPermissionBridge;
/**
@@ -28,6 +30,9 @@ final class AdminRbacAndUserSeeder extends Seeder
public function run(): void
{
AdminAgentPermissionMenuActionSync::syncMissing();
AdminDrawPermissionMenuActionSync::syncMissing();
$super = AdminRole::query()->updateOrCreate(
['slug' => AdminUser::ROLE_SUPER_ADMIN],
['code' => AdminUser::ROLE_SUPER_ADMIN, 'name' => '超级管理员'],