feat: 增强玩家管理功能,集成接入站点权限控制

在多个玩家相关控制器中引入 AdminSiteScope,确保管理员在执行操作前具备相应的接入站点权限。更新 Player 相关请求以支持 site_code 参数,增强权限验证逻辑,确保系统安全性与灵活性。同时,新增 AdminUser 模型方法以获取可访问的站点 ID 列表,优化权限管理。
This commit is contained in:
2026-05-27 13:36:23 +08:00
parent b649c862ef
commit a10135d6ee
47 changed files with 2265 additions and 38 deletions

View File

@@ -0,0 +1,237 @@
<?php
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use App\Support\AdminAuthorizationRegistry;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
return new class extends Migration
{
public function up(): void
{
Schema::table('admin_sites', function (Blueprint $table): void {
$table->string('wallet_api_url', 512)->nullable()->after('extra_json');
$table->string('wallet_debit_path', 128)->default('/wallet/debit-for-lottery')->after('wallet_api_url');
$table->string('wallet_credit_path', 128)->default('/wallet/credit-from-lottery')->after('wallet_debit_path');
$table->string('wallet_balance_path', 128)->default('/wallet/balance')->after('wallet_credit_path');
$table->text('wallet_api_key_encrypted')->nullable()->after('wallet_balance_path');
$table->text('sso_jwt_secret_encrypted')->nullable()->after('wallet_api_key_encrypted');
$table->unsignedSmallInteger('wallet_timeout_seconds')->default(10)->after('sso_jwt_secret_encrypted');
$table->json('iframe_allowed_origins')->nullable()->after('wallet_timeout_seconds');
$table->string('lottery_h5_base_url', 512)->nullable()->after('iframe_allowed_origins');
$table->text('notes')->nullable()->after('lottery_h5_base_url');
});
$this->seedIntegrationMenuActions();
$this->backfillDefaultSiteFromEnv();
$this->syncIntegrationApiResources();
}
public function down(): void
{
$resourceIds = DB::table('admin_api_resources')
->where('code', 'like', 'admin.integration-sites.%')
->pluck('id')
->all();
if ($resourceIds !== []) {
DB::table('admin_role_api_resources')->whereIn('api_resource_id', $resourceIds)->delete();
DB::table('admin_api_resource_bindings')->whereIn('api_resource_id', $resourceIds)->delete();
DB::table('admin_api_resources')->whereIn('id', $resourceIds)->delete();
}
Schema::table('admin_sites', function (Blueprint $table): void {
$table->dropColumn([
'wallet_api_url',
'wallet_debit_path',
'wallet_credit_path',
'wallet_balance_path',
'wallet_api_key_encrypted',
'sso_jwt_secret_encrypted',
'wallet_timeout_seconds',
'iframe_allowed_origins',
'lottery_h5_base_url',
'notes',
]);
});
}
private function seedIntegrationMenuActions(): 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;
}
$configMenuId = DB::table('admin_menus')->where('code', 'config')->value('id');
if ($configMenuId === null) {
return;
}
$integrationMenuId = DB::table('admin_menus')->where('code', 'config.integration')->value('id');
if ($integrationMenuId === null) {
$integrationMenuId = DB::table('admin_menus')->insertGetId([
'parent_id' => (int) $configMenuId,
'menu_type' => 'page',
'code' => 'config.integration',
'name' => '主站接入站点',
'path' => '/admin/config/integration-sites',
'route_name' => 'admin.config.integration',
'component' => 'config/integration',
'icon' => null,
'active_menu_code' => null,
'sort_order' => 45,
'is_visible' => true,
'is_cache' => false,
'is_external' => false,
'status' => 1,
'meta_json' => null,
'created_at' => $now,
'updated_at' => $now,
]);
}
foreach ([
['permission_code' => 'integration.site.view', 'action_id' => (int) $viewActionId, 'name' => '接入站点查看'],
['permission_code' => 'integration.site.manage', 'action_id' => (int) $manageActionId, 'name' => '接入站点管理'],
] as $row) {
$exists = DB::table('admin_menu_actions')
->where('permission_code', $row['permission_code'])
->exists();
if ($exists) {
continue;
}
DB::table('admin_menu_actions')->insert([
'menu_id' => (int) $integrationMenuId,
'action_id' => $row['action_id'],
'permission_code' => $row['permission_code'],
'name' => $row['name'],
'status' => 1,
'created_at' => $now,
'updated_at' => $now,
]);
}
}
private function backfillDefaultSiteFromEnv(): void
{
$siteId = DB::table('admin_sites')->where('is_default', true)->value('id')
?? DB::table('admin_sites')->orderBy('id')->value('id');
if ($siteId === null) {
return;
}
$walletUrl = env('MAIN_SITE_WALLET_API_URL');
$ssoSecret = env('MAIN_SITE_SSO_JWT_SECRET');
$walletKey = env('MAIN_SITE_WALLET_API_KEY');
$payload = [
'updated_at' => Carbon::now(),
];
if (is_string($walletUrl) && trim($walletUrl) !== '') {
$payload['wallet_api_url'] = rtrim(trim($walletUrl), '/');
}
$debitPath = env('MAIN_SITE_WALLET_DEBIT_PATH');
if (is_string($debitPath) && $debitPath !== '') {
$payload['wallet_debit_path'] = $debitPath;
}
$creditPath = env('MAIN_SITE_WALLET_CREDIT_PATH');
if (is_string($creditPath) && $creditPath !== '') {
$payload['wallet_credit_path'] = $creditPath;
}
$balancePath = env('MAIN_SITE_WALLET_BALANCE_PATH');
if (is_string($balancePath) && $balancePath !== '') {
$payload['wallet_balance_path'] = $balancePath;
}
$timeout = env('MAIN_SITE_WALLET_TIMEOUT');
if (is_numeric($timeout)) {
$payload['wallet_timeout_seconds'] = max(1, (int) $timeout);
}
if (is_string($ssoSecret) && $ssoSecret !== '') {
$payload['sso_jwt_secret_encrypted'] = encrypt($ssoSecret);
}
if (is_string($walletKey) && $walletKey !== '') {
$payload['wallet_api_key_encrypted'] = encrypt($walletKey);
}
if (count($payload) > 1) {
DB::table('admin_sites')->where('id', (int) $siteId)->update($payload);
}
}
private function syncIntegrationApiResources(): void
{
if (! Schema::hasTable('admin_api_resources')) {
return;
}
$now = Carbon::now();
$menuActionIds = DB::table('admin_menu_actions')->pluck('id', 'permission_code');
$resources = array_values(array_filter(
AdminAuthorizationRegistry::resources(),
static fn (array $resource): bool => str_starts_with((string) $resource['code'], 'admin.integration-sites.'),
));
foreach ($resources as $resource) {
$resourceId = DB::table('admin_api_resources')
->where('code', $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' => $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,
]);
}
}
}
};

View File

@@ -0,0 +1,105 @@
<?php
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use App\Support\AdminAuthorizationRegistry;
use Illuminate\Database\Migrations\Migration;
/**
* 为已执行 140000 的环境补种 integration 权限动作并同步 API 绑定。
*/
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;
}
$configMenuId = DB::table('admin_menus')->where('code', 'config')->value('id');
if ($configMenuId === null) {
return;
}
$integrationMenuId = DB::table('admin_menus')->where('code', 'config.integration')->value('id');
if ($integrationMenuId === null) {
$integrationMenuId = DB::table('admin_menus')->insertGetId([
'parent_id' => (int) $configMenuId,
'menu_type' => 'page',
'code' => 'config.integration',
'name' => '主站接入站点',
'path' => '/admin/config/integration-sites',
'route_name' => 'admin.config.integration',
'component' => 'config/integration',
'icon' => null,
'active_menu_code' => null,
'sort_order' => 45,
'is_visible' => true,
'is_cache' => false,
'is_external' => false,
'status' => 1,
'meta_json' => null,
'created_at' => $now,
'updated_at' => $now,
]);
}
foreach ([
['permission_code' => 'integration.site.view', 'action_id' => (int) $viewActionId, 'name' => '接入站点查看'],
['permission_code' => 'integration.site.manage', 'action_id' => (int) $manageActionId, 'name' => '接入站点管理'],
] as $row) {
if (DB::table('admin_menu_actions')->where('permission_code', $row['permission_code'])->exists()) {
continue;
}
DB::table('admin_menu_actions')->insert([
'menu_id' => (int) $integrationMenuId,
'action_id' => $row['action_id'],
'permission_code' => $row['permission_code'],
'name' => $row['name'],
'status' => 1,
'created_at' => $now,
'updated_at' => $now,
]);
}
$menuActionIds = DB::table('admin_menu_actions')->pluck('id', 'permission_code');
$resources = array_values(array_filter(
AdminAuthorizationRegistry::resources(),
static fn (array $resource): bool => str_starts_with((string) $resource['code'], 'admin.integration-sites.'),
));
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,
]);
}
}
}
public function down(): void
{
// 保留 menu_actions / bindings避免回滚后超管无法管理已创建的接入站点。
}
};