fix(core): harden settlement and wallet integration

This commit is contained in:
wchino
2026-07-22 01:40:37 +08:00
parent 150c3e7ebd
commit 35f6e46958
54 changed files with 2564 additions and 359 deletions

View File

@@ -2,11 +2,10 @@
namespace App\Services\AgentSettlement;
use App\Models\Player;
use App\Models\TicketItem;
use App\Services\Player\PlayerCreditService;
use App\Support\PlayerFundingMode;
use Illuminate\Support\Facades\DB;
use App\Services\Player\PlayerCreditService;
/**
* 彩票注单游戏结算侧入账:快照、占成流水、回水计提、玩家已用额度。
@@ -30,8 +29,12 @@ final class AgentGameSettlementRecorder
&& (int) ($player->agent_node_id ?? 0) > 0;
}
public function recordForTicketItem(TicketItem $item, int $netWin, string $terminalStatus): void
{
public function recordForTicketItem(
TicketItem $item,
int $netWin,
string $terminalStatus,
int $settlementVersion = 0,
): void {
if (! $this->shouldRecord($item)) {
return;
}
@@ -77,7 +80,7 @@ final class AgentGameSettlementRecorder
$settledAt = now();
DB::transaction(function () use ($item, $player, $snapshot, $shareSnapshot, $basicRebateRate, $gameWinLoss, $basicRebate, $result, $settledAt, $validBet, $extraRebate, $gameType): void {
DB::transaction(function () use ($item, $player, $snapshot, $shareSnapshot, $basicRebateRate, $gameWinLoss, $basicRebate, $result, $settledAt, $validBet, $extraRebate, $gameType, $settlementVersion): void {
$item->forceFill([
'agent_node_id' => $snapshot['agent_node_id'],
'share_snapshot' => $shareSnapshot,
@@ -134,13 +137,13 @@ final class AgentGameSettlementRecorder
$holdAmount = (int) $item->actual_deduct_amount;
if ($holdAmount > 0) {
$this->playerCreditService->releaseBetHold($player, $holdAmount, $item->id);
$this->playerCreditService->releaseBetHold($player, $holdAmount, $item->id, $settlementVersion);
}
if ($gameWinLoss > 0) {
$this->playerCreditService->applySettledLoss($player, (int) round($gameWinLoss), $item->id);
$this->playerCreditService->applySettledLoss($player, (int) round($gameWinLoss), $item->id, $settlementVersion);
} elseif ($gameWinLoss < 0) {
$this->playerCreditService->applySettledWin($player, (int) round(abs($gameWinLoss)), $item->id);
$this->playerCreditService->applySettledWin($player, (int) round(abs($gameWinLoss)), $item->id, $settlementVersion);
}
});
}

View File

@@ -4,10 +4,10 @@ namespace App\Services\AgentSettlement;
use App\Models\Player;
use App\Models\TicketItem;
use App\Services\Player\PlayerCreditService;
use App\Support\PlayerFundingMode;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\QueryException;
use App\Services\Player\PlayerCreditService;
final class GameSettlementReversalService
{
@@ -15,16 +15,26 @@ final class GameSettlementReversalService
private readonly PlayerCreditService $playerCreditService,
) {}
public function reverseTicketItem(TicketItem $item): void
public function reverseTicketItem(TicketItem $item, int $settlementVersion = 0): void
{
$ledger = DB::table('share_ledger')->where('ticket_item_id', $item->id)->whereNull('reversal_of_id')->first();
$ledger = DB::table('share_ledger as sl')
->where('sl.ticket_item_id', $item->id)
->whereNull('sl.reversal_of_id')
->whereNotExists(function ($query): void {
$query->selectRaw('1')
->from('share_ledger as reversal')
->whereColumn('reversal.reversal_of_id', 'sl.id');
})
->orderByDesc('sl.id')
->select('sl.*')
->first();
if ($ledger === null) {
return;
}
$settledAt = now();
DB::transaction(function () use ($item, $ledger, $settledAt): void {
DB::transaction(function () use ($item, $ledger, $settledAt, $settlementVersion): void {
// 幂等闸门share_ledger.reversal_of_id 上有 partial unique 索引,
// 同一原账不允许插入多条反转记录。重复调用时唯一约束冲突即视为已反转、跳过。
// 一次只让 reversal_of_id 出现一次写入确保所有后续副作用rebate、credit 冲正)
@@ -79,8 +89,14 @@ final class GameSettlementReversalService
if ($player !== null && PlayerFundingMode::usesCredit($player)) {
$gameWinLoss = (int) $ledger->game_win_loss;
if ($gameWinLoss !== 0) {
$this->playerCreditService->reverseGameSettlement($player, $gameWinLoss, $item->id);
$this->playerCreditService->reverseGameSettlement($player, $gameWinLoss, $item->id, $settlementVersion);
}
$this->playerCreditService->restoreBetHoldAfterSettlementReversal(
$player,
(int) $item->actual_deduct_amount,
(int) $item->id,
$settlementVersion,
);
}
});
}