diff --git a/app/common/service/GameBetSettleService.php b/app/common/service/GameBetSettleService.php index e5440b2..5a1f5a2 100644 --- a/app/common/service/GameBetSettleService.php +++ b/app/common/service/GameBetSettleService.php @@ -339,7 +339,15 @@ final class GameBetSettleService 'payout_pending_review' => $payoutPendingReview, 'server_time' => $now, ]), $userId); - GameWebSocketEventBus::publish(self::TOPIC_BET_WIN, $data); + $ok = GameWebSocketEventBus::publish(self::TOPIC_BET_WIN, $data); + if (!$ok) { + Log::warning('bet.win publish failed (will retry next round)', [ + 'period_id' => $periodId, + 'user_id' => $userId, + 'total_win' => $payload['total_win'] ?? '', + ]); + continue; + } if ($periodId > 0) { self::markBetWinNotifyOnce($periodId, $userId); } diff --git a/app/common/service/GameWebSocketEventBus.php b/app/common/service/GameWebSocketEventBus.php index 36c610d..1a801b5 100644 --- a/app/common/service/GameWebSocketEventBus.php +++ b/app/common/service/GameWebSocketEventBus.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace app\common\service; +use support\Log; use support\Redis; use Throwable; @@ -17,12 +18,13 @@ final class GameWebSocketEventBus /** * @param array $data + * @return bool 是否成功入队(false 表示 Redis 不可用或参数非法,调用方应避免标记“已推送”) */ - public static function publish(string $topic, array $data): void + public static function publish(string $topic, array $data): bool { $topic = trim($topic); if ($topic === '') { - return; + return false; } $payload = [ 'topic' => $topic, @@ -32,11 +34,17 @@ final class GameWebSocketEventBus ]; $json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); if (!is_string($json) || $json === '') { - return; + return false; } try { - Redis::lPush(self::KEY_QUEUE, $json); - } catch (Throwable) { + $len = Redis::lPush(self::KEY_QUEUE, $json); + return is_numeric($len) && (int) $len > 0; + } catch (Throwable $e) { + Log::warning('ws event bus publish failed', [ + 'topic' => $topic, + 'error' => $e->getMessage(), + ]); + return false; } } diff --git a/scripts/debug_period_bet_win.php b/scripts/debug_period_bet_win.php index 172fc77..a9d7172 100644 --- a/scripts/debug_period_bet_win.php +++ b/scripts/debug_period_bet_win.php @@ -5,17 +5,136 @@ declare(strict_types=1); require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../support/bootstrap.php'; +use support\Redis; 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); +/** + * bet.win 推送链路诊断脚本(线上排查专用)。 + * + * 用法: + * php scripts/debug_period_bet_win.php # 默认查最近 5 期 + * php scripts/debug_period_bet_win.php # 指定单期 + * php scripts/debug_period_bet_win.php --recent=10 # 查最近 N 期 + * + * 输出维度: + * 1) 期次基本信息(开奖号、状态) + * 2) 中奖注单(user_id / win_amount / status) + * 3) Redis bet.win 去重键:dfw:v1:ws:betwin:{period_id}:{user_id} + * —— 若库内有中奖、Redis 无键 ⇒ publish 未执行(请重启 webman + 看 runtime/logs 是否有 'bet.win published') + * 4) Redis 事件队列 dfw:v1:ws:event:queue 当前 bet.win 待消费数(>0 说明 WS 消费滞后或进程未启动) + */ + +$argvList = $argv; +array_shift($argvList); + +$periodNo = ''; +$recent = 0; +foreach ($argvList as $a) { + if (str_starts_with($a, '--recent=')) { + $recent = max(1, (int) substr($a, 9)); + continue; + } + if ($a !== '' && $a[0] !== '-') { + $periodNo = $a; + } } -$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"; +if ($periodNo === '' && $recent === 0) { + $recent = 5; } + +$queueKey = 'dfw:v1:ws:event:queue'; +try { + $queueLen = (int) Redis::lLen($queueKey); +} catch (\Throwable $e) { + $queueLen = -1; +} +echo "[redis] {$queueKey} length = {$queueLen}\n"; +if ($queueLen > 0) { + try { + $sample = Redis::lRange($queueKey, 0, 49); + $betWinPending = 0; + if (is_array($sample)) { + foreach ($sample as $raw) { + if (is_string($raw) && str_contains($raw, '"bet.win"')) { + $betWinPending++; + } + } + } + echo "[redis] bet.win pending in first 50 = {$betWinPending}\n"; + } catch (\Throwable) { + } +} +echo "\n"; + +$records = []; +if ($periodNo !== '') { + $row = Db::name('game_record')->where('period_no', $periodNo)->find(); + if (!is_array($row)) { + fwrite(STDERR, "period not found: {$periodNo}\n"); + exit(1); + } + $records[] = $row; +} else { + $records = Db::name('game_record') + ->whereIn('status', [2, 3, 4]) + ->whereNotNull('result_number') + ->order('id', 'desc') + ->limit($recent) + ->select() + ->toArray(); +} + +foreach ($records as $gr) { + $pid = (int) ($gr['id'] ?? 0); + $rn = (int) ($gr['result_number'] ?? 0); + $status = (int) ($gr['status'] ?? 0); + $pno = (string) ($gr['period_no'] ?? ''); + echo "==== period_id={$pid} no={$pno} status={$status} result={$rn} ====\n"; + + $bets = Db::name('bet_order') + ->where('period_id', $pid) + ->order('id', 'asc') + ->select() + ->toArray(); + + $winUserIds = []; + foreach ($bets as $b) { + $win = (string) ($b['win_amount'] ?? '0'); + $uid = (int) ($b['user_id'] ?? 0); + $bs = (int) ($b['status'] ?? 0); + $tag = bccomp($win, '0', 2) > 0 ? 'WIN' : '----'; + echo sprintf(" bet=%d user=%d status=%d win=%s [%s]\n", (int) ($b['id'] ?? 0), $uid, $bs, $win, $tag); + if ($tag === 'WIN' && $uid > 0) { + $winUserIds[$uid] = true; + } + } + + if ($winUserIds === []) { + echo " (no winners)\n\n"; + continue; + } + + echo " -- bet.win dedup keys --\n"; + foreach (array_keys($winUserIds) as $uid) { + $key = 'dfw:v1:ws:betwin:' . $pid . ':' . $uid; + try { + $val = Redis::get($key); + $ttl = Redis::ttl($key); + } catch (\Throwable $e) { + $val = false; + $ttl = -2; + } + $exists = ($val !== false && $val !== null && $val !== ''); + echo sprintf( + " %s user=%d exists=%s ttl=%s %s\n", + $key, + $uid, + $exists ? 'YES' : 'NO', + (string) $ttl, + $exists ? '(已推送过)' : '(未推送 → 重启 webman 后用 republish_bet_win.php 补发)' + ); + } + echo "\n"; +} + +echo "如需补发:php scripts/republish_bet_win.php --period-no= --force\n";