$data * @return array{site: AdminSite, secrets: array{sso_jwt_secret: string, wallet_api_key: string}, admin_user: AdminUser} */ public function create(array $data): array { $secrets = $this->generateSecrets(); ['site' => $site, 'admin_user' => $adminUser] = DB::transaction(function () use ($data, $secrets): array { /** @var array{username: string, nickname: string, password: string, email?: string|null} $adminAccount */ $adminAccount = $data['admin_account']; $site = AdminSite::query()->create([ 'code' => (string) $data['code'], 'name' => (string) $data['name'], 'currency_code' => (string) ($data['currency_code'] ?? 'NPR'), 'status' => (int) ($data['status'] ?? 1), 'is_default' => false, 'wallet_api_url' => $this->nullableTrim($data['wallet_api_url'] ?? null), 'wallet_debit_path' => (string) ($data['wallet_debit_path'] ?? '/wallet/debit-for-lottery'), 'wallet_credit_path' => (string) ($data['wallet_credit_path'] ?? '/wallet/credit-from-lottery'), 'wallet_balance_path' => (string) ($data['wallet_balance_path'] ?? '/wallet/balance'), 'wallet_timeout_seconds' => max(1, (int) ($data['wallet_timeout_seconds'] ?? 10)), 'iframe_allowed_origins' => $data['iframe_allowed_origins'] ?? null, 'lottery_h5_base_url' => $this->nullableTrim($data['lottery_h5_base_url'] ?? null), 'notes' => $this->nullableTrim($data['notes'] ?? null), 'sso_jwt_secret_encrypted' => encrypt($secrets['sso_jwt_secret']), 'wallet_api_key_encrypted' => encrypt($secrets['wallet_api_key']), ]); $role = SitePlatformRole::resolve(); $adminUser = $this->createSiteAdminUser($site, $role, $adminAccount); return [ 'site' => $site, 'admin_user' => $adminUser, ]; }); $this->configResolver->forgetCache((string) $site->code); return [ 'site' => $site->fresh(), 'secrets' => $secrets, 'admin_user' => $adminUser->fresh(), ]; } /** * @param array $data */ public function update(AdminSite $site, array $data): AdminSite { $site->fill([ 'name' => (string) $data['name'], 'currency_code' => (string) ($data['currency_code'] ?? $site->currency_code), 'status' => (int) ($data['status'] ?? $site->status), 'wallet_api_url' => array_key_exists('wallet_api_url', $data) ? $this->nullableTrim($data['wallet_api_url']) : $site->wallet_api_url, 'wallet_debit_path' => (string) ($data['wallet_debit_path'] ?? $site->wallet_debit_path), 'wallet_credit_path' => (string) ($data['wallet_credit_path'] ?? $site->wallet_credit_path), 'wallet_balance_path' => (string) ($data['wallet_balance_path'] ?? $site->wallet_balance_path), 'wallet_timeout_seconds' => max(1, (int) ($data['wallet_timeout_seconds'] ?? $site->wallet_timeout_seconds)), 'iframe_allowed_origins' => $data['iframe_allowed_origins'] ?? $site->iframe_allowed_origins, 'lottery_h5_base_url' => array_key_exists('lottery_h5_base_url', $data) ? $this->nullableTrim($data['lottery_h5_base_url']) : $site->lottery_h5_base_url, 'notes' => array_key_exists('notes', $data) ? $this->nullableTrim($data['notes']) : $site->notes, ]); $site->save(); $this->configResolver->forgetCache((string) $site->code); return $site->fresh(); } /** * @return array{site: AdminSite, secrets: array{sso_jwt_secret: string, wallet_api_key: string}} */ public function destroy(AdminSite $site): void { $siteCode = (string) $site->code; $siteId = (int) $site->id; $siteAdminRoleId = SitePlatformRole::resolve()->id; if (AdminSite::query()->count() <= 1) { $fallbackSiteId = null; } else { $fallbackSiteId = (int) AdminSite::query() ->where('id', '!=', $siteId) ->orderBy('id') ->value('id'); } DB::transaction(function () use ($site, $siteCode, $siteId, $siteAdminRoleId, $fallbackSiteId): void { $superRoleId = AdminRole::query() ->where('slug', AdminUser::ROLE_SUPER_ADMIN) ->value('id'); $platformBindings = DB::table('admin_user_site_roles') ->where('site_id', $siteId) ->when($siteAdminRoleId !== null, static fn ($query) => $query->where('role_id', '!=', $siteAdminRoleId)) ->when($superRoleId !== null, static fn ($query) => $query->where('role_id', '!=', $superRoleId)) ->get(['admin_user_id', 'role_id', 'granted_at']); foreach ($platformBindings as $binding) { if ($fallbackSiteId === null) { continue; } DB::table('admin_user_site_roles')->updateOrInsert( [ 'admin_user_id' => (int) $binding->admin_user_id, 'site_id' => $fallbackSiteId, 'role_id' => (int) $binding->role_id, ], ['granted_at' => $binding->granted_at ?? now()], ); } Player::query()->where('site_code', $siteCode)->delete(); if ($siteAdminRoleId !== null) { $siteAdminUserIds = DB::table('admin_user_site_roles') ->where('site_id', $siteId) ->where('role_id', $siteAdminRoleId) ->pluck('admin_user_id'); foreach ($siteAdminUserIds as $userId) { $bindings = DB::table('admin_user_site_roles') ->where('admin_user_id', $userId) ->get(['site_id', 'role_id']); $onlyAutoSiteAdmin = $bindings->count() === 1 && (int) $bindings[0]->site_id === $siteId && (int) $bindings[0]->role_id === (int) $siteAdminRoleId; if ($onlyAutoSiteAdmin) { AdminUser::query()->where('id', $userId)->delete(); } } } $site->delete(); }); $this->configResolver->forgetCache($siteCode); } /** * @return array{site: AdminSite, secrets: array{sso_jwt_secret: string, wallet_api_key: string}} */ public function rotateSecrets(AdminSite $site): array { $secrets = $this->generateSecrets(); $site->forceFill([ 'sso_jwt_secret_encrypted' => encrypt($secrets['sso_jwt_secret']), 'wallet_api_key_encrypted' => encrypt($secrets['wallet_api_key']), ])->save(); $this->configResolver->forgetCache((string) $site->code); return ['site' => $site->fresh(), 'secrets' => $secrets]; } /** * @return array{sso_jwt_secret: string, wallet_api_key: string} */ private function generateSecrets(): array { return [ 'sso_jwt_secret' => Str::random(48), 'wallet_api_key' => Str::random(40), ]; } private function nullableTrim(mixed $value): ?string { if (! is_string($value)) { return null; } $trimmed = trim($value); return $trimmed === '' ? null : $trimmed; } /** * @param array{username: string, nickname: string, password: string, email?: string|null} $adminAccount */ private function createSiteAdminUser(AdminSite $site, AdminRole $role, array $adminAccount): AdminUser { $username = trim((string) ($adminAccount['username'] ?? '')); $nickname = trim((string) ($adminAccount['nickname'] ?? '')); $password = (string) ($adminAccount['password'] ?? ''); $email = $this->nullableTrim($adminAccount['email'] ?? null); if ($username === '' || $nickname === '' || $password === '') { throw ValidationException::withMessages([ 'admin_account' => ['站点后台管理账号信息不完整。'], ]); } $user = AdminUser::query()->create([ 'username' => $username, 'name' => $nickname, 'email' => $email, 'password' => $password, 'status' => 0, ]); $user->syncSystemRoleSlugsForSite((int) $site->id, [SitePlatformRole::SLUG]); return $user; } }