feat: 增强管理员权限管理,添加 RBAC 支持,更新 AdminUser 模型以处理角色和权限,更新登录接口返回权限信息,扩展数据库填充器以同步角色权限

This commit is contained in:
2026-05-11 16:21:13 +08:00
parent 19003f5041
commit fc023242ce
39 changed files with 1587 additions and 123 deletions

View File

@@ -0,0 +1,25 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('reconcile_jobs', function (Blueprint $table): void {
$table->foreignId('admin_user_id')
->nullable()
->constrained('admin_users')
->nullOnDelete();
});
}
public function down(): void
{
Schema::table('reconcile_jobs', function (Blueprint $table): void {
$table->dropConstrainedForeignId('admin_user_id');
});
}
};

View File

@@ -2,53 +2,141 @@
namespace Database\Seeders;
use App\Models\AdminPermission;
use App\Models\AdminRole;
use App\Models\AdminUser;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
/**
* 后台角色super_admin、若干权限占位本地演示账号 **admin** / **123456**(仅限非 production
* 后台 RBAC {@see AdminUser::ROLE_SUPER_ADMIN} PRD 对齐
*
* - 角色 slug`01-产品文档.md` §3 + `04-领域字典与编码规范.md` §11
* - 权限点 slug`01-产品文档.md` §8「功能」行 `prd.{功能键}.{动作}`,路由中间件引用同表
*
* 演示账号 **admin** / **123456**(仅限非 production
*/
class AdminRbacAndUserSeeder extends Seeder
{
/** @return list<array{slug: string, name: string}> */
private function permissionDefinitions(): array
{
return [
['slug' => 'prd.users.manage', 'name' => '§8 用户管理·可管理'],
['slug' => 'prd.users.view_finance', 'name' => '§8 用户管理·财务查看'],
['slug' => 'prd.users.view_cs', 'name' => '§8 用户管理·客服单用户'],
['slug' => 'prd.play_switch.manage', 'name' => '§8 玩法开关·可管理'],
['slug' => 'prd.odds.manage', 'name' => '§8 赔率配置·可管理'],
['slug' => 'prd.risk_cap.manage', 'name' => '§8 封顶配置·可管理'],
['slug' => 'prd.risk_cap.view', 'name' => '§8 封顶配置·查看'],
['slug' => 'prd.rebate.manage', 'name' => '§8 佣金/回水·可管理'],
['slug' => 'prd.rebate.view', 'name' => '§8 佣金/回水·查看'],
['slug' => 'prd.jackpot.manage', 'name' => '§8 Jackpot 配置·可管理'],
['slug' => 'prd.jackpot.view', 'name' => '§8 Jackpot 配置·查看'],
['slug' => 'prd.draw_result.manage', 'name' => '§8 开奖结果录入·可管理'],
['slug' => 'prd.draw_result.view', 'name' => '§8 开奖结果·查看'],
['slug' => 'prd.draw_reopen.manage', 'name' => '§8 开奖结果重开·可管理'],
['slug' => 'prd.payout.manage', 'name' => '§8 派彩确认·可管理'],
['slug' => 'prd.payout.review', 'name' => '§8 派彩确认·可审核'],
['slug' => 'prd.payout.view', 'name' => '§8 派彩确认·查看'],
['slug' => 'prd.wallet_reconcile.manage', 'name' => '§8 钱包对账·可管理'],
['slug' => 'prd.wallet_reconcile.view', 'name' => '§8 钱包对账·查看'],
['slug' => 'prd.wallet_reconcile.view_cs', 'name' => '§8 钱包对账·客服单用户'],
['slug' => 'prd.wallet_adjust.manage', 'name' => '§8 补单/冲正·可管理'],
['slug' => 'prd.report.all', 'name' => '§8 报表·全部'],
['slug' => 'prd.report.risk', 'name' => '§8 报表·风控'],
['slug' => 'prd.report.finance', 'name' => '§8 报表·财务'],
['slug' => 'prd.report.player', 'name' => '§8 报表·单用户'],
['slug' => 'prd.audit.all', 'name' => '§8 审计日志·全部'],
['slug' => 'prd.audit.self', 'name' => '§8 审计日志·自身相关'],
['slug' => 'prd.audit.finance', 'name' => '§8 审计日志·资金相关'],
['slug' => 'prd.player_freeze.manage', 'name' => '§8 冻结/解冻玩家·可管理'],
];
}
/** @param list<string> $slugs */
private function syncRolePermissions(AdminRole $role, array $slugs): void
{
$ids = AdminPermission::query()->whereIn('slug', $slugs)->pluck('id')->all();
$role->permissions()->sync($ids);
}
public function run(): void
{
$now = now();
foreach ($this->permissionDefinitions() as $row) {
AdminPermission::query()->updateOrCreate(
['slug' => $row['slug']],
['name' => $row['name']],
);
}
DB::table('admin_roles')->updateOrInsert(
['slug' => 'super_admin'],
[
'name' => 'Super Admin',
'created_at' => $now,
'updated_at' => $now,
],
);
/** @var int $rid */
$rid = (int) DB::table('admin_roles')->where('slug', 'super_admin')->value('id');
$perms = [
['slug' => 'admin.dashboard', 'name' => 'Dashboard'],
['slug' => 'admin.players.read', 'name' => 'View players'],
['slug' => 'admin.wallet.read', 'name' => 'View wallets'],
$legacySlugs = [
'admin.dashboard', 'admin.players.read', 'admin.wallet.read', 'admin.draws.read',
'admin.draws.publish', 'admin.settlement.run', 'admin.settlement.read', 'admin.jackpot.read',
'admin.jackpot.write', 'admin.config.read', 'admin.config.write', 'admin.audit.read',
'admin.reports.manage', 'admin.reconcile.manage',
];
foreach ($perms as $p) {
DB::table('admin_permissions')->updateOrInsert(
['slug' => $p['slug']],
[
'name' => $p['name'],
'created_at' => $now,
'updated_at' => $now,
],
);
}
AdminPermission::query()->whereIn('slug', $legacySlugs)->delete();
$pidRows = DB::table('admin_permissions')->whereIn('slug', array_column($perms, 'slug'))->pluck('id');
foreach ($pidRows as $pid) {
DB::table('admin_role_permissions')->updateOrInsert(
['role_id' => $rid, 'permission_id' => $pid],
[],
);
}
$super = AdminRole::query()->updateOrCreate(
['slug' => AdminUser::ROLE_SUPER_ADMIN],
['name' => '超级管理员'],
);
$this->syncRolePermissions($super, array_column($this->permissionDefinitions(), 'slug'));
$risk = AdminRole::query()->updateOrCreate(
['slug' => 'risk_operator'],
['name' => '风控运营员'],
);
$this->syncRolePermissions($risk, [
'prd.play_switch.manage',
'prd.odds.manage',
'prd.risk_cap.manage',
'prd.rebate.manage',
'prd.jackpot.manage',
'prd.draw_result.manage',
'prd.payout.review',
'prd.wallet_reconcile.view',
'prd.report.risk',
'prd.audit.self',
'prd.player_freeze.manage',
]);
$finance = AdminRole::query()->updateOrCreate(
['slug' => 'finance'],
['name' => '财务/对账员'],
);
$this->syncRolePermissions($finance, [
'prd.users.view_finance',
'prd.risk_cap.view',
'prd.rebate.view',
'prd.jackpot.view',
'prd.draw_result.view',
'prd.payout.view',
'prd.wallet_reconcile.manage',
'prd.wallet_adjust.manage',
'prd.report.finance',
'prd.audit.finance',
]);
$cs = AdminRole::query()->updateOrCreate(
['slug' => 'customer_service'],
['name' => '客服人员'],
);
$this->syncRolePermissions($cs, [
'prd.users.view_cs',
'prd.draw_result.view',
'prd.wallet_reconcile.view_cs',
'prd.report.player',
]);
$username = 'admin';
AdminUser::query()->updateOrCreate(
@@ -56,17 +144,17 @@ class AdminRbacAndUserSeeder extends Seeder
[
'name' => '超级管理员',
'email' => null,
/** 明文;模型 casts `password => hashed`,勿在生产库使用种子弱口令 */
'password' => '123456',
'status' => 0,
],
);
/** @var int $uid */
$uid = (int) AdminUser::query()->where('username', $username)->value('id');
DB::table('admin_user_roles')->updateOrInsert(
['admin_user_id' => $uid, 'role_id' => $rid],
[],
);
/** @var AdminUser $admin */
$admin = AdminUser::query()->where('username', $username)->firstOrFail();
$admin->roles()->sync([(int) $super->getKey()]);
DB::table('admin_user_roles')->where('admin_user_id', $admin->id)
->whereNotIn('role_id', [(int) $super->getKey()])
->delete();
}
}