Files
lotteryLaravel/app/Services/Integration/PartnerSiteConfigResolver.php
kang 1e7b2b31ee feat: enhance reconciliation job processing and reporting features
- Updated ReconcileItemIndexController to include admin user validation and improved transfer order handling.
- Refactored ReconcileJobStoreController to ensure date range handling is more precise with start and end of day adjustments.
- Enhanced various report controllers to include currency code in responses for better financial clarity.
- Introduced new methods in AdminReconcileJobService for wallet transfer job creation and improved item scanning logic.
- Added wallet lookup idempotent path configuration for integration services to enhance API interactions.
2026-06-16 13:50:55 +08:00

168 lines
6.3 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?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'),
walletLookupIdempotentPath: (string) config('lottery.main_site.wallet_lookup_idempotent_path', '/wallet/lookup-idempotent'),
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'),
walletLookupIdempotentPath: (string) config('lottery.main_site.wallet_lookup_idempotent_path', '/wallet/lookup-idempotent'),
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',
walletLookupIdempotentPath: '/wallet/lookup-idempotent',
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;
}
}