feat: 增强奖池与钱包服务的多币种支持能力

更新 JackpotManualBurstService:在解析头奖中奖者时支持币种代码处理。
重构 SettlementBatchWorkflowService:按币种聚合玩家派奖金额,确保各币种结算准确入账。
修改 SettlementOrchestrator:按币种分别处理奖池爆奖流程,提升派奖准确性。
优化 TicketWalletService:在派奖幂等键中加入币种信息,避免多币种场景下的重复处理问题。
新增测试用例,验证多币种派奖场景及待处理交易的正确处理逻辑。
This commit is contained in:
2026-05-28 16:50:24 +08:00
parent 8ccf39dff5
commit 0323d92381
8 changed files with 857 additions and 73 deletions

View File

@@ -125,28 +125,41 @@ final class SettlementOrchestrator
];
}
$currency = strtoupper((string) ($ticketItems->first()?->order?->currency_code ?? 'NPR'));
$pool = JackpotPool::query()
->where('currency_code', $currency)
->where('status', 1)
->lockForUpdate()
->first();
$allocations = [];
$totalJackpotPayout = 0;
$jackpotTrigger = null;
$jackpotPoolAfter = null;
if ($pool !== null) {
$burstInput = collect($prepared)->map(fn (array $p): array => [
$jackpotBursts = [];
$preparedByCurrency = collect($prepared)->groupBy(
fn (array $p): string => strtoupper((string) ($p['item']->order?->currency_code ?? 'NPR')),
);
foreach ($preparedByCurrency as $currency => $currencyPrepared) {
$pool = JackpotPool::query()
->where('currency_code', $currency)
->where('status', 1)
->lockForUpdate()
->first();
if ($pool === null) {
continue;
}
$burstInput = collect($currencyPrepared)->map(fn (array $p): array => [
'item' => $p['item'],
'matched_tier' => $p['matched_tier'],
'gross_win' => $p['gross_win'],
]);
$burstOut = $this->jackpotBurst->allocate($locked, $pool, $burstInput);
$allocations = $burstOut['allocations'];
$totalJackpotPayout = (int) $burstOut['pool_payout'];
$jackpotTrigger = $burstOut['trigger'];
$jackpotPoolAfter = (int) $pool->fresh()->current_amount;
$allocations = array_replace($allocations, $burstOut['allocations']);
$currencyPayout = (int) $burstOut['pool_payout'];
$totalJackpotPayout += $currencyPayout;
if ($currencyPayout > 0 && is_string($burstOut['trigger'])) {
$jackpotBursts[] = [
'currency' => $currency,
'payout' => $currencyPayout,
'trigger' => $burstOut['trigger'],
'pool_after' => (int) $pool->fresh()->current_amount,
'winner_count' => count($burstOut['allocations']),
];
}
}
$ticketCount = 0;
@@ -206,16 +219,16 @@ final class SettlementOrchestrator
'settle_version' => $nextSettleVersion,
])->save();
if ($pool !== null && $totalJackpotPayout > 0 && is_string($jackpotTrigger)) {
foreach ($jackpotBursts as $burst) {
$this->hallRealtime->notifyJackpotBurst(
(int) $locked->id,
(string) $locked->draw_no,
$board->firstPrizeNumber4d(),
$currency,
$totalJackpotPayout,
count($allocations),
$jackpotTrigger,
(int) $jackpotPoolAfter,
(string) $burst['currency'],
(int) $burst['payout'],
(int) $burst['winner_count'],
(string) $burst['trigger'],
(int) $burst['pool_after'],
);
$this->hallRealtime->notifyStatusChange($this->hallSnapshot->build());
}