1.ws优化bet.win订阅

This commit is contained in:
2026-05-26 18:54:30 +08:00
parent e163090bc2
commit 98b2696f89
2 changed files with 40 additions and 7 deletions

View File

@@ -171,8 +171,7 @@ final class GameBetSettleService
} }
if (bccomp($win, '0', 2) > 0) { if (bccomp($win, '0', 2) > 0) {
$streakAtBet = (int) ($bet['streak_at_bet'] ?? 0); $jackpotFlags = self::betWinJackpotFlagsForOrder($bet, $win, $needReview);
$isJackpotTier = StreakWinReward::isJackpotForStreakAtBet($streakAtBet);
if (!isset($winByUser[$userId])) { if (!isset($winByUser[$userId])) {
$winByUser[$userId] = [ $winByUser[$userId] = [
'user_id' => $userId, 'user_id' => $userId,
@@ -182,12 +181,16 @@ final class GameBetSettleService
'total_win' => '0.00', 'total_win' => '0.00',
'balance_after' => $balanceAfter, 'balance_after' => $balanceAfter,
'is_jackpot' => false, 'is_jackpot' => false,
'payout_pending_review' => false,
'bets' => [], 'bets' => [],
]; ];
} }
if ($isJackpotTier) { if ($jackpotFlags['is_jackpot']) {
$winByUser[$userId]['is_jackpot'] = true; $winByUser[$userId]['is_jackpot'] = true;
} }
if ($jackpotFlags['payout_pending_review']) {
$winByUser[$userId]['payout_pending_review'] = true;
}
$winByUser[$userId]['total_win'] = bcadd($winByUser[$userId]['total_win'], $win, 2); $winByUser[$userId]['total_win'] = bcadd($winByUser[$userId]['total_win'], $win, 2);
$winByUser[$userId]['balance_after'] = $balanceAfter; $winByUser[$userId]['balance_after'] = $balanceAfter;
$winByUser[$userId]['bets'][] = [ $winByUser[$userId]['bets'][] = [
@@ -263,6 +266,7 @@ final class GameBetSettleService
'total_win' => (string) ($agg['total_win'] ?? '0.00'), 'total_win' => (string) ($agg['total_win'] ?? '0.00'),
'balance_after' => (string) ($agg['balance_after'] ?? '0'), 'balance_after' => (string) ($agg['balance_after'] ?? '0'),
'is_jackpot' => $isJackpotTier, 'is_jackpot' => $isJackpotTier,
'payout_pending_review' => false,
'bets' => [], 'bets' => [],
]; ];
Log::warning('bet.win fallback emitted for settled winner', [ Log::warning('bet.win fallback emitted for settled winner', [
@@ -328,9 +332,11 @@ final class GameBetSettleService
continue; continue;
} }
$isJackpot = !empty($payload['is_jackpot']); $isJackpot = !empty($payload['is_jackpot']);
$payoutPendingReview = !empty($payload['payout_pending_review']);
$data = GameWebSocketPayloadHelper::mergeUserStreakInto(array_merge($payload, [ $data = GameWebSocketPayloadHelper::mergeUserStreakInto(array_merge($payload, [
'is_jackpot' => $isJackpot, 'is_jackpot' => $isJackpot,
'is_win' => true, 'is_win' => true,
'payout_pending_review' => $payoutPendingReview,
'server_time' => $now, 'server_time' => $now,
]), $userId); ]), $userId);
GameWebSocketEventBus::publish(self::TOPIC_BET_WIN, $data); GameWebSocketEventBus::publish(self::TOPIC_BET_WIN, $data);
@@ -368,6 +374,9 @@ final class GameBetSettleService
if ($userId === false || $userId <= 0) { if ($userId === false || $userId <= 0) {
continue; continue;
} }
$orderStatus = filter_var($bet['status'] ?? 0, FILTER_VALIDATE_INT);
$needReview = $orderStatus === self::PLAY_STATUS_PENDING_REVIEW;
$jackpotFlags = self::betWinJackpotFlagsForOrder($bet, $win, $needReview);
if (!isset($winByUser[$userId])) { if (!isset($winByUser[$userId])) {
$coin = Db::name('user')->where('id', $userId)->value('coin'); $coin = Db::name('user')->where('id', $userId)->value('coin');
$winByUser[$userId] = [ $winByUser[$userId] = [
@@ -378,12 +387,16 @@ final class GameBetSettleService
'total_win' => '0.00', 'total_win' => '0.00',
'balance_after' => (string) ($coin ?? '0'), 'balance_after' => (string) ($coin ?? '0'),
'is_jackpot' => false, 'is_jackpot' => false,
'payout_pending_review' => false,
'bets' => [], 'bets' => [],
]; ];
} }
if (StreakWinReward::isJackpotForStreakAtBet((int) ($bet['streak_at_bet'] ?? 0))) { if ($jackpotFlags['is_jackpot']) {
$winByUser[$userId]['is_jackpot'] = true; $winByUser[$userId]['is_jackpot'] = true;
} }
if ($jackpotFlags['payout_pending_review']) {
$winByUser[$userId]['payout_pending_review'] = true;
}
$winByUser[$userId]['total_win'] = bcadd((string) $winByUser[$userId]['total_win'], $win, 2); $winByUser[$userId]['total_win'] = bcadd((string) $winByUser[$userId]['total_win'], $win, 2);
$betId = filter_var($bet['id'] ?? 0, FILTER_VALIDATE_INT); $betId = filter_var($bet['id'] ?? 0, FILTER_VALIDATE_INT);
$winByUser[$userId]['bets'][] = [ $winByUser[$userId]['bets'][] = [
@@ -396,7 +409,7 @@ final class GameBetSettleService
} }
/** /**
* 大奖档命中时额外推送公共频道 jackpot.hit与 bet.win 同一结算时刻,先后发出)。 * 大奖档命中时额外推送公共频道 jackpot.hit全站公告;个人中奖通知仍以 bet.win 为准,不可替代)。
* *
* @param list<array<string, mixed>> $jackpotHits * @param list<array<string, mixed>> $jackpotHits
*/ */
@@ -622,6 +635,7 @@ final class GameBetSettleService
FILTER_VALIDATE_INT FILTER_VALIDATE_INT
); );
if ($periodId !== false && $periodId > 0 && $resultNumber !== false && $resultNumber > 0 && bccomp($winAmount, '0', 2) > 0) { if ($periodId !== false && $periodId > 0 && $resultNumber !== false && $resultNumber > 0 && bccomp($winAmount, '0', 2) > 0) {
$jackpotFlags = self::betWinJackpotFlagsForOrder($row, $winAmount, false);
self::publishBetWinsAfterCommit([[ self::publishBetWinsAfterCommit([[
'user_id' => $userId, 'user_id' => $userId,
'period_id' => $periodId, 'period_id' => $periodId,
@@ -629,7 +643,8 @@ final class GameBetSettleService
'result_number' => $resultNumber, 'result_number' => $resultNumber,
'total_win' => $winAmount, 'total_win' => $winAmount,
'balance_after' => is_string($balanceAfter ?? null) ? $balanceAfter : (string) (Db::name('user')->where('id', $userId)->value('coin') ?? '0'), 'balance_after' => is_string($balanceAfter ?? null) ? $balanceAfter : (string) (Db::name('user')->where('id', $userId)->value('coin') ?? '0'),
'is_jackpot' => StreakWinReward::isJackpotForStreakAtBet((int) ($row['streak_at_bet'] ?? 0)), 'is_jackpot' => $jackpotFlags['is_jackpot'],
'payout_pending_review' => false,
'bets' => [['bet_id' => $playRecordId, 'win_amount' => $winAmount]], 'bets' => [['bet_id' => $playRecordId, 'win_amount' => $winAmount]],
]], $periodId); ]], $periodId);
} }
@@ -902,4 +917,21 @@ final class GameBetSettleService
} }
return bccomp($winAmount, $threshold, 2) >= 0; return bccomp($winAmount, $threshold, 2) >= 0;
} }
/**
* bet.win 展示用大奖标记:连胜大奖档 或 触发后台大奖审核阈值,均视为「中大奖」并走 bet.win 通知。
*
* @param array<string, mixed> $bet
* @return array{is_jackpot: bool, payout_pending_review: bool}
*/
private static function betWinJackpotFlagsForOrder(array $bet, string $winAmount, bool $needReview): array
{
$streakJackpot = StreakWinReward::isJackpotForStreakAtBet((int) ($bet['streak_at_bet'] ?? 0));
$amountJackpot = self::shouldRequireJackpotReview($winAmount, self::jackpotMaxAmount());
return [
'is_jackpot' => $streakJackpot || $amountJackpot || $needReview,
'payout_pending_review' => $needReview,
];
}
} }

View File

@@ -840,8 +840,9 @@
- `data.total_win`:本期该用户派彩合计(已入账部分;若触发**后台大奖审核**`win_amount >= game_config.jackpot_max_amount`)且注单为待审核,可能尚未入账,但仍会推送本事件) - `data.total_win`:本期该用户派彩合计(已入账部分;若触发**后台大奖审核**`win_amount >= game_config.jackpot_max_amount`)且注单为待审核,可能尚未入账,但仍会推送本事件)
- `data.balance_after`:推送时用户余额(已派彩则为派彩后余额) - `data.balance_after`:推送时用户余额(已派彩则为派彩后余额)
- `data.bets[]``{ bet_id, win_amount }` 明细 - `data.bets[]``{ bet_id, win_amount }` 明细
- **`data.is_jackpot`**`bool``true` 表示该用户本期中奖注单含**大奖档**`streak_win_reward``is_jackpot=true` 的档位,与下注时 `streak_at_bet` 对应),`false` 为普通档 - **`data.is_jackpot`**`bool``true` 表示**中大奖**(满足任一:连胜**大奖档**、或派彩金额达 `jackpot_max_amount` 需后台审核)。**客户端用此字段做大奖样式,勿仅依赖 `jackpot.hit`**
- **`data.is_win`**`bool`,固定为 `true`(便于与 `user.streak``extra.is_win` 对齐) - **`data.is_win`**`bool`,固定为 `true`(便于与 `user.streak``extra.is_win` 对齐)
- **`data.payout_pending_review`**`bool``true` 表示已中奖但派彩待后台大奖审核,尚未入账(仍应展示中奖 UI
- **合并赔率字段**(与 §7.1.2A 一致):`current_streak``streak_level``odds_factor``is_jackpot` - **合并赔率字段**(与 §7.1.2A 一致):`current_streak``streak_level``odds_factor``is_jackpot`
- `data.server_time`Unix 秒 - `data.server_time`Unix 秒
- **服务端去重**Redis Key `dfw:v1:ws:betwin:{period_id}:{user_id}`TTL 86400s**每期每用户至多推送一次**;与 `user.streak` / `wallet.changed` 的整期去重键 `dfw:v1:settle:notify:{period_id}` **分离**,避免后者先占位导致 `bet.win` 被吞。 - **服务端去重**Redis Key `dfw:v1:ws:betwin:{period_id}:{user_id}`TTL 86400s**每期每用户至多推送一次**;与 `user.streak` / `wallet.changed` 的整期去重键 `dfw:v1:settle:notify:{period_id}` **分离**,避免后者先占位导致 `bet.win` 被吞。