1.ws优化bet.win订阅,修复中大奖没有推送
This commit is contained in:
@@ -328,7 +328,7 @@ final class GameBetSettleService
|
||||
if ($userId === false || $userId <= 0) {
|
||||
continue;
|
||||
}
|
||||
if ($periodId > 0 && !self::markBetWinNotifyOnce($periodId, $userId)) {
|
||||
if ($periodId > 0 && self::hasBetWinNotifyMarked($periodId, $userId)) {
|
||||
continue;
|
||||
}
|
||||
$isJackpot = !empty($payload['is_jackpot']);
|
||||
@@ -340,6 +340,15 @@ final class GameBetSettleService
|
||||
'server_time' => $now,
|
||||
]), $userId);
|
||||
GameWebSocketEventBus::publish(self::TOPIC_BET_WIN, $data);
|
||||
if ($periodId > 0) {
|
||||
self::markBetWinNotifyOnce($periodId, $userId);
|
||||
}
|
||||
Log::info('bet.win published', [
|
||||
'period_id' => $periodId,
|
||||
'user_id' => $userId,
|
||||
'total_win' => $payload['total_win'] ?? '',
|
||||
'is_jackpot' => $isJackpot,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -489,9 +498,13 @@ final class GameBetSettleService
|
||||
}
|
||||
}
|
||||
|
||||
$effectiveBetWins = $periodId > 0 && $resultNumber > 0
|
||||
? self::buildBetWinPayloadsFromSettledOrders($periodId, $resultNumber)
|
||||
: [];
|
||||
if ($effectiveBetWins === []) {
|
||||
$effectiveBetWins = $betWins;
|
||||
if ($effectiveBetWins === [] && $periodId > 0 && $resultNumber > 0) {
|
||||
$effectiveBetWins = self::buildBetWinPayloadsFromSettledOrders($periodId, $resultNumber);
|
||||
} else {
|
||||
$effectiveBetWins = self::mergeBetWinPayloads($betWins, $effectiveBetWins);
|
||||
}
|
||||
self::publishBetWinsAfterCommit($effectiveBetWins, $periodId);
|
||||
if ($periodId > 0 && $resultNumber > 0) {
|
||||
@@ -520,14 +533,9 @@ final class GameBetSettleService
|
||||
if ($userId === false || $userId <= 0) {
|
||||
continue;
|
||||
}
|
||||
$key = self::BET_WIN_NOTIFY_DEDUP_PREFIX . $periodId . ':' . $userId;
|
||||
try {
|
||||
$existing = Redis::get($key);
|
||||
if ($existing !== false && $existing !== null && $existing !== '') {
|
||||
if (self::hasBetWinNotifyMarked($periodId, $userId)) {
|
||||
continue;
|
||||
}
|
||||
} catch (Throwable) {
|
||||
}
|
||||
$missing[] = $payload;
|
||||
}
|
||||
if ($missing !== []) {
|
||||
@@ -540,21 +548,65 @@ final class GameBetSettleService
|
||||
}
|
||||
}
|
||||
|
||||
private static function markBetWinNotifyOnce(int $periodId, int $userId): bool
|
||||
private static function hasBetWinNotifyMarked(int $periodId, int $userId): bool
|
||||
{
|
||||
if ($periodId <= 0 || $userId <= 0) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
$key = self::BET_WIN_NOTIFY_DEDUP_PREFIX . $periodId . ':' . $userId;
|
||||
try {
|
||||
$ok = Redis::set($key, '1', ['nx', 'ex' => 86400]);
|
||||
$existing = Redis::get($key);
|
||||
|
||||
return $ok === true || $ok === 'OK';
|
||||
return $existing !== false && $existing !== null && $existing !== '';
|
||||
} catch (Throwable) {
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static function markBetWinNotifyOnce(int $periodId, int $userId): void
|
||||
{
|
||||
if ($periodId <= 0 || $userId <= 0) {
|
||||
return;
|
||||
}
|
||||
$key = self::BET_WIN_NOTIFY_DEDUP_PREFIX . $periodId . ':' . $userId;
|
||||
try {
|
||||
Redis::setEx($key, 86400, '1');
|
||||
} catch (Throwable) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param list<array<string, mixed>> $primary
|
||||
* @param list<array<string, mixed>> $secondary
|
||||
* @return list<array<string, mixed>>
|
||||
*/
|
||||
private static function mergeBetWinPayloads(array $primary, array $secondary): array
|
||||
{
|
||||
/** @var array<int, array<string, mixed>> $byUser */
|
||||
$byUser = [];
|
||||
foreach (array_merge($primary, $secondary) as $payload) {
|
||||
if (!is_array($payload)) {
|
||||
continue;
|
||||
}
|
||||
$userId = filter_var($payload['user_id'] ?? 0, FILTER_VALIDATE_INT);
|
||||
if ($userId === false || $userId <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (!isset($byUser[$userId])) {
|
||||
$byUser[$userId] = $payload;
|
||||
continue;
|
||||
}
|
||||
if (!empty($payload['is_jackpot'])) {
|
||||
$byUser[$userId]['is_jackpot'] = true;
|
||||
}
|
||||
if (!empty($payload['payout_pending_review'])) {
|
||||
$byUser[$userId]['payout_pending_review'] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($byUser);
|
||||
}
|
||||
|
||||
private static function markSettlementNotifyOnce(int $periodId): bool
|
||||
{
|
||||
if ($periodId <= 0) {
|
||||
|
||||
@@ -845,7 +845,7 @@
|
||||
- **`data.payout_pending_review`**:`bool`,`true` 表示已中奖但派彩待后台大奖审核,尚未入账(仍应展示中奖 UI)
|
||||
- **合并赔率字段**(与 §7.1.2A 一致):`current_streak`、`streak_level`、`odds_factor`、`is_jackpot`
|
||||
- `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),**入队成功后再写入**,每期每用户至多推送一次;与 `dfw:v1:settle:notify:{period_id}` **分离**。结算推送以库内已结算中奖注单为准重建载荷,避免内存聚合丢失。
|
||||
- **补偿**:若内存聚合 `bet_wins` 为空但库内已有本期已结算中奖注单,结算服务会从库重建载荷并补发(`buildBetWinPayloadsFromSettledOrders`)。
|
||||
- **大奖审核通过后**:后台 `approveJackpot` 会再次向该用户推送 `bet.win`(入账后)。
|
||||
- **`jackpot.hit`(公共大奖广播,补充)**:在 **`bet.win` 之后**(同一结算批次内),若本期存在**大奖档命中**用户,**额外**向公共频道推送一帧,供全站公告/跑马灯;无大奖命中则不推送。**个人弹窗仍以 `bet.win` 为主**;`jackpot.hit` 用于全站展示昵称与金额。
|
||||
|
||||
21
scripts/debug_period_bet_win.php
Normal file
21
scripts/debug_period_bet_win.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../support/bootstrap.php';
|
||||
|
||||
use support\think\Db;
|
||||
|
||||
$periodNo = $argv[1] ?? '20260526-190442-c340448b';
|
||||
$gr = Db::name('game_record')->where('period_no', $periodNo)->find();
|
||||
if (!is_array($gr)) {
|
||||
fwrite(STDERR, "period not found: {$periodNo}\n");
|
||||
exit(1);
|
||||
}
|
||||
$pid = (int) $gr['id'];
|
||||
$bets = Db::name('bet_order')->where('period_id', $pid)->select()->toArray();
|
||||
echo "period_id={$pid} status={$gr['status']} result={$gr['result_number']}\n";
|
||||
foreach ($bets as $b) {
|
||||
echo "bet {$b['id']} user={$b['user_id']} status={$b['status']} win={$b['win_amount']}\n";
|
||||
}
|
||||
Reference in New Issue
Block a user