feat: 增强玩家管理功能,集成接入站点权限控制
在多个玩家相关控制器中引入 AdminSiteScope,确保管理员在执行操作前具备相应的接入站点权限。更新 Player 相关请求以支持 site_code 参数,增强权限验证逻辑,确保系统安全性与灵活性。同时,新增 AdminUser 模型方法以获取可访问的站点 ID 列表,优化权限管理。
This commit is contained in:
164
app/Services/Integration/PartnerSiteConfigResolver.php
Normal file
164
app/Services/Integration/PartnerSiteConfigResolver.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Integration;
|
||||
|
||||
use App\Models\AdminSite;
|
||||
use App\Models\Player;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* 按 {@see site_code} 解析主站对接配置;未命中库表时回退全局 MAIN_SITE_* env(并写 warning 日志)。
|
||||
*/
|
||||
final class PartnerSiteConfigResolver
|
||||
{
|
||||
private const CACHE_PREFIX = 'partner_site_config:';
|
||||
|
||||
private const CACHE_TTL_SECONDS = 60;
|
||||
|
||||
public function resolveForPlayer(Player $player): PartnerSiteConfig
|
||||
{
|
||||
return $this->resolveBySiteCode((string) $player->site_code);
|
||||
}
|
||||
|
||||
public function resolveBySiteCode(string $siteCode): PartnerSiteConfig
|
||||
{
|
||||
$siteCode = trim($siteCode);
|
||||
if ($siteCode === '') {
|
||||
return $this->legacyFallbackConfig($siteCode);
|
||||
}
|
||||
|
||||
$cacheKey = self::CACHE_PREFIX.$siteCode;
|
||||
|
||||
/** @var array<string, mixed> $cached */
|
||||
$cached = Cache::remember($cacheKey, self::CACHE_TTL_SECONDS, function () use ($siteCode): array {
|
||||
$site = AdminSite::query()->where('code', $siteCode)->first();
|
||||
$config = $site !== null
|
||||
? $this->fromAdminSite($site)
|
||||
: $this->legacyFallbackConfig($siteCode);
|
||||
|
||||
return $config->toCacheArray();
|
||||
});
|
||||
|
||||
return PartnerSiteConfig::fromCacheArray($cached);
|
||||
}
|
||||
|
||||
public function forgetCache(string $siteCode): void
|
||||
{
|
||||
Cache::forget(self::CACHE_PREFIX.trim($siteCode));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从未验签的 JWT 中读取 site_code(仅用于选取验签密钥)。
|
||||
*/
|
||||
public function peekSiteCodeFromJwt(string $jwt): ?string
|
||||
{
|
||||
$jwt = trim($jwt);
|
||||
$parts = explode('.', $jwt);
|
||||
if (count($parts) !== 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$payloadJson = $this->base64UrlDecode($parts[1]);
|
||||
if ($payloadJson === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$payload = json_decode($payloadJson, true);
|
||||
if (! is_array($payload)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$siteKey = (string) config('lottery.player_auth.jwt.claim_site_code', 'site_code');
|
||||
$siteCode = $payload[$siteKey] ?? null;
|
||||
|
||||
return is_string($siteCode) && $siteCode !== '' ? $siteCode : null;
|
||||
}
|
||||
|
||||
private function fromAdminSite(AdminSite $site): PartnerSiteConfig
|
||||
{
|
||||
return new PartnerSiteConfig(
|
||||
siteCode: (string) $site->code,
|
||||
enabled: $site->isEnabled(),
|
||||
walletApiUrl: is_string($site->wallet_api_url) && $site->wallet_api_url !== ''
|
||||
? rtrim($site->wallet_api_url, '/')
|
||||
: null,
|
||||
walletDebitPath: (string) ($site->wallet_debit_path ?: '/wallet/debit-for-lottery'),
|
||||
walletCreditPath: (string) ($site->wallet_credit_path ?: '/wallet/credit-from-lottery'),
|
||||
walletBalancePath: (string) ($site->wallet_balance_path ?: '/wallet/balance'),
|
||||
ssoJwtSecret: $site->decryptedSsoJwtSecret(),
|
||||
walletApiKey: $site->decryptedWalletApiKey(),
|
||||
walletTimeoutSeconds: max(1, (int) ($site->wallet_timeout_seconds ?? 10)),
|
||||
source: PartnerSiteConfig::SOURCE_DATABASE,
|
||||
);
|
||||
}
|
||||
|
||||
private function legacyFallbackConfig(string $siteCode): PartnerSiteConfig
|
||||
{
|
||||
$defaultCode = (string) config('lottery.integration.default_site_code', 'default_site');
|
||||
$legacyCodes = array_filter([
|
||||
$defaultCode,
|
||||
(string) config('lottery.integration.legacy_env_site_code', ''),
|
||||
]);
|
||||
|
||||
$sso = config('lottery.main_site.sso_jwt_secret');
|
||||
$walletUrl = config('lottery.main_site.wallet_api_url');
|
||||
$walletKey = config('lottery.main_site.wallet_api_key');
|
||||
|
||||
$hasLegacy = (is_string($sso) && $sso !== '')
|
||||
|| (is_string($walletUrl) && trim((string) $walletUrl) !== '');
|
||||
|
||||
if ($hasLegacy && (
|
||||
$siteCode === ''
|
||||
|| in_array($siteCode, $legacyCodes, true)
|
||||
|| app()->environment(['local', 'testing'])
|
||||
)) {
|
||||
if ($siteCode !== '') {
|
||||
Log::warning('partner_site_config.legacy_env_fallback', [
|
||||
'site_code' => $siteCode,
|
||||
'hint' => 'Configure admin_sites row for this site_code',
|
||||
]);
|
||||
}
|
||||
|
||||
return new PartnerSiteConfig(
|
||||
siteCode: $siteCode !== '' ? $siteCode : $defaultCode,
|
||||
enabled: true,
|
||||
walletApiUrl: is_string($walletUrl) && trim($walletUrl) !== ''
|
||||
? rtrim(trim($walletUrl), '/')
|
||||
: null,
|
||||
walletDebitPath: (string) config('lottery.main_site.wallet_debit_path', '/wallet/debit-for-lottery'),
|
||||
walletCreditPath: (string) config('lottery.main_site.wallet_credit_path', '/wallet/credit-from-lottery'),
|
||||
walletBalancePath: (string) config('lottery.main_site.wallet_balance_path', '/wallet/balance'),
|
||||
ssoJwtSecret: is_string($sso) && $sso !== '' ? $sso : null,
|
||||
walletApiKey: is_string($walletKey) && $walletKey !== '' ? $walletKey : null,
|
||||
walletTimeoutSeconds: max(1, (int) config('lottery.main_site.wallet_timeout', 10)),
|
||||
source: PartnerSiteConfig::SOURCE_LEGACY_ENV,
|
||||
);
|
||||
}
|
||||
|
||||
return new PartnerSiteConfig(
|
||||
siteCode: $siteCode,
|
||||
enabled: false,
|
||||
walletApiUrl: null,
|
||||
walletDebitPath: '/wallet/debit-for-lottery',
|
||||
walletCreditPath: '/wallet/credit-from-lottery',
|
||||
walletBalancePath: '/wallet/balance',
|
||||
ssoJwtSecret: null,
|
||||
walletApiKey: null,
|
||||
walletTimeoutSeconds: 10,
|
||||
source: PartnerSiteConfig::SOURCE_DATABASE,
|
||||
);
|
||||
}
|
||||
|
||||
private function base64UrlDecode(string $segment): ?string
|
||||
{
|
||||
$remainder = strlen($segment) % 4;
|
||||
if ($remainder > 0) {
|
||||
$segment .= str_repeat('=', 4 - $remainder);
|
||||
}
|
||||
|
||||
$decoded = base64_decode(strtr($segment, '-_', '+/'), true);
|
||||
|
||||
return $decoded === false ? null : $decoded;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user