From 8b1d08d3b191fe43c5df01926aaf3eed3bc3ee87 Mon Sep 17 00:00:00 2001 From: kang Date: Tue, 9 Jun 2026 15:06:43 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=BC=BA=E4=B8=8B=E6=B3=A8?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E6=A0=A1=E9=AA=8C=E5=B9=B6=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E5=90=8E=E5=8F=B0=E9=92=B1=E5=8C=85=E4=B8=8E=E6=8E=A5=E5=85=A5?= =?UTF-8?q?=E7=AB=99=E7=82=B9=E6=9D=83=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Requests/Ticket/TicketPlaceRequest.php | 1 + app/Support/AdminAuthorizationRegistry.php | 10 +-- ...ayer_credit_accounts_and_credit_ledger.php | 77 +++++++++++++++++++ 3 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 database/migrations/2026_06_09_120000_create_player_credit_accounts_and_credit_ledger.php diff --git a/app/Http/Requests/Ticket/TicketPlaceRequest.php b/app/Http/Requests/Ticket/TicketPlaceRequest.php index 2d69697..81c96ae 100644 --- a/app/Http/Requests/Ticket/TicketPlaceRequest.php +++ b/app/Http/Requests/Ticket/TicketPlaceRequest.php @@ -10,6 +10,7 @@ final class TicketPlaceRequest extends TicketBetRequest public function rules(): array { return array_merge(parent::rules(), [ + 'client_trace_id' => ['required', 'string', 'max:64'], 'expected_config_versions' => ['nullable', 'array'], 'expected_config_versions.play_config_version_no' => ['required_with:expected_config_versions', 'integer', 'min:1'], 'expected_config_versions.odds_version_no' => ['required_with:expected_config_versions', 'integer', 'min:1'], diff --git a/app/Support/AdminAuthorizationRegistry.php b/app/Support/AdminAuthorizationRegistry.php index 876feae..b1ee823 100644 --- a/app/Support/AdminAuthorizationRegistry.php +++ b/app/Support/AdminAuthorizationRegistry.php @@ -48,7 +48,7 @@ final class AdminAuthorizationRegistry ['slug' => AdminPermissionLanguage::prdSlug('integration-sites', 'view'), 'name' => AdminPermissionLanguage::prdName('integration-sites', 'view'), 'nav_segment' => 'integration', 'permission_codes' => AdminPermissionLanguage::permissionCodes('integration-sites', 'view')], ['slug' => AdminPermissionLanguage::prdSlug('integration-sites', 'manage'), 'name' => AdminPermissionLanguage::prdName('integration-sites', 'manage'), 'nav_segment' => 'integration', 'permission_codes' => AdminPermissionLanguage::permissionCodes('integration-sites', 'manage')], - ['slug' => 'prd.wallet_reconcile.manage', 'name' => '钱包对账·可管理', 'nav_segment' => 'wallet', 'permission_codes' => ['service.wallet.manage', 'service.reconcile.manage', 'service.wallet.adjust']], + ['slug' => 'prd.wallet_reconcile.manage', 'name' => '钱包对账·可管理', 'nav_segment' => 'wallet', 'permission_codes' => ['service.wallet.manage', 'service.reconcile.manage']], ['slug' => 'prd.wallet_reconcile.view', 'name' => '钱包对账·查看', 'nav_segment' => 'wallet', 'permission_codes' => ['service.wallet.view', 'service.reconcile.view']], ['slug' => 'prd.wallet_reconcile.view_cs', 'name' => '钱包对账·客服单用户', 'nav_segment' => 'wallet', 'permission_codes' => ['service.wallet.view', 'service.reconcile.view']], ['slug' => 'prd.wallet_adjust.manage', 'name' => '补单/冲正·可管理', 'nav_segment' => 'wallet', 'permission_codes' => ['service.wallet.adjust']], @@ -489,7 +489,7 @@ final class AdminAuthorizationRegistry ['code' => 'admin.integration-sites.show', 'module_code' => 'integration', 'name' => '接入站点详情', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/integration-sites/{admin_site}', 'route_name' => 'api.v1.admin.integration-sites.show', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['integration.site.view', 'integration.site.manage']], ['code' => 'admin.integration-sites.update', 'module_code' => 'integration', 'name' => '更新接入站点', 'http_method' => 'PUT', 'uri_pattern' => '/api/v1/admin/integration-sites/{admin_site}', 'route_name' => 'api.v1.admin.integration-sites.update', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'permission_codes' => ['integration.site.manage']], ['code' => 'admin.integration-sites.rotate-secrets', 'module_code' => 'integration', 'name' => '重置接入密钥', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/integration-sites/{admin_site}/rotate-secrets', 'route_name' => 'api.v1.admin.integration-sites.rotate-secrets', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'permission_codes' => ['integration.site.manage']], - ['code' => 'admin.integration-sites.connectivity-test', 'module_code' => 'integration', 'name' => '接入站点联通检测', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/integration-sites/{admin_site}/connectivity-test', 'route_name' => 'api.v1.admin.integration-sites.connectivity-test', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['integration.site.manage']], + ['code' => 'admin.integration-sites.connectivity-test', 'module_code' => 'integration', 'name' => '接入站点联通检测', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/integration-sites/{admin_site}/connectivity-test', 'route_name' => 'api.v1.admin.integration-sites.connectivity-test', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['integration.site.view', 'integration.site.manage']], ['code' => 'admin.integration-sites.export', 'module_code' => 'integration', 'name' => '导出接入参数表', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/integration-sites/{admin_site}/export', 'route_name' => 'api.v1.admin.integration-sites.export', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['integration.site.view', 'integration.site.manage']], ['code' => 'admin.integration-sites.secrets', 'module_code' => 'integration', 'name' => '查看接入密钥明文', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/integration-sites/{admin_site}/secrets', 'route_name' => 'api.v1.admin.integration-sites.secrets', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'permission_codes' => ['integration.site.manage']], @@ -546,9 +546,9 @@ final class AdminAuthorizationRegistry ['code' => 'admin.wallet.transfer-orders', 'module_code' => 'wallet', 'name' => '转账单查询', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/wallet/transfer-orders', 'route_name' => 'api.v1.admin.wallet.transfer-orders', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'legacy_permission_slugs' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs', 'prd.wallet_adjust.manage']], ['code' => 'admin.wallet.transactions', 'module_code' => 'wallet', 'name' => '钱包流水查询', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/wallet/transactions', 'route_name' => 'api.v1.admin.wallet.transactions', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'legacy_permission_slugs' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs']], - ['code' => 'admin.wallet.transfer-orders.reverse', 'module_code' => 'wallet', 'name' => '冲正转账单', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/wallet/transfer-orders/{transfer_no}/reverse', 'route_name' => 'api.v1.admin.wallet.transfer-orders.reverse', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'legacy_permission_slugs' => ['prd.wallet_adjust.manage', 'prd.wallet_reconcile.manage']], - ['code' => 'admin.wallet.transfer-orders.manually-process', 'module_code' => 'wallet', 'name' => '手工处理转账单', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/wallet/transfer-orders/{transfer_no}/manually-process', 'route_name' => 'api.v1.admin.wallet.transfer-orders.manually-process', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'legacy_permission_slugs' => ['prd.wallet_adjust.manage', 'prd.wallet_reconcile.manage']], - ['code' => 'admin.wallet.transfer-orders.complete-credit', 'module_code' => 'wallet', 'name' => '补完成转入入账', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/wallet/transfer-orders/{transfer_no}/complete-credit', 'route_name' => 'api.v1.admin.wallet.transfer-orders.complete-credit', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'legacy_permission_slugs' => ['prd.wallet_adjust.manage', 'prd.wallet_reconcile.manage']], + ['code' => 'admin.wallet.transfer-orders.reverse', 'module_code' => 'wallet', 'name' => '冲正转账单', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/wallet/transfer-orders/{transfer_no}/reverse', 'route_name' => 'api.v1.admin.wallet.transfer-orders.reverse', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'permission_codes' => ['service.wallet.adjust']], + ['code' => 'admin.wallet.transfer-orders.manually-process', 'module_code' => 'wallet', 'name' => '手工处理转账单', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/wallet/transfer-orders/{transfer_no}/manually-process', 'route_name' => 'api.v1.admin.wallet.transfer-orders.manually-process', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'permission_codes' => ['service.wallet.adjust']], + ['code' => 'admin.wallet.transfer-orders.complete-credit', 'module_code' => 'wallet', 'name' => '补完成转入入账', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/wallet/transfer-orders/{transfer_no}/complete-credit', 'route_name' => 'api.v1.admin.wallet.transfer-orders.complete-credit', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'permission_codes' => ['service.wallet.adjust']], ['code' => 'admin.reconcile-jobs.index', 'module_code' => 'reconcile', 'name' => '对账任务列表', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/reconcile-jobs', 'route_name' => 'api.v1.admin.reconcile-jobs.index', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'legacy_permission_slugs' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs']], ['code' => 'admin.reconcile-jobs.show', 'module_code' => 'reconcile', 'name' => '对账任务详情', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/reconcile-jobs/{reconcile_job}', 'route_name' => 'api.v1.admin.reconcile-jobs.show', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'legacy_permission_slugs' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs']], ['code' => 'admin.reconcile-jobs.items.index', 'module_code' => 'reconcile', 'name' => '对账任务明细', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/reconcile-jobs/{reconcile_job}/items', 'route_name' => 'api.v1.admin.reconcile-jobs.items.index', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'legacy_permission_slugs' => ['prd.wallet_reconcile.manage', 'prd.wallet_reconcile.view', 'prd.wallet_reconcile.view_cs']], diff --git a/database/migrations/2026_06_09_120000_create_player_credit_accounts_and_credit_ledger.php b/database/migrations/2026_06_09_120000_create_player_credit_accounts_and_credit_ledger.php new file mode 100644 index 0000000..ff998e6 --- /dev/null +++ b/database/migrations/2026_06_09_120000_create_player_credit_accounts_and_credit_ledger.php @@ -0,0 +1,77 @@ +unsignedBigInteger('player_id')->primary(); + $table->bigInteger('credit_limit')->default(0); + $table->bigInteger('used_credit')->default(0); + $table->bigInteger('frozen_credit')->default(0); + $table->timestamps(); + + $table->foreign('player_id') + ->references('id') + ->on('players') + ->cascadeOnDelete(); + }); + } + + if (! Schema::hasTable('credit_ledger')) { + Schema::create('credit_ledger', function (Blueprint $table): void { + $table->id(); + $table->string('owner_type', 16); + $table->unsignedBigInteger('owner_id'); + $table->bigInteger('amount'); + $table->string('reason', 64); + $table->string('ref_type', 32)->nullable(); + $table->unsignedBigInteger('ref_id')->nullable(); + $table->timestamps(); + + $table->index(['owner_type', 'owner_id', 'created_at']); + }); + } + + if (Schema::hasTable('players')) { + DB::table('players') + ->where('funding_mode', 'credit') + ->whereNotExists(function ($query): void { + $query->selectRaw('1') + ->from('player_credit_accounts') + ->whereColumn('player_credit_accounts.player_id', 'players.id'); + }) + ->orderBy('id') + ->select('id') + ->chunkById(500, function ($players): void { + $now = now(); + $rows = []; + foreach ($players as $player) { + $rows[] = [ + 'player_id' => (int) $player->id, + 'credit_limit' => 0, + 'used_credit' => 0, + 'frozen_credit' => 0, + 'created_at' => $now, + 'updated_at' => $now, + ]; + } + if ($rows !== []) { + DB::table('player_credit_accounts')->insertOrIgnore($rows); + } + }, 'id'); + } + } + + public function down(): void + { + Schema::dropIfExists('credit_ledger'); + Schema::dropIfExists('player_credit_accounts'); + } +};