1.ws优化bet.win订阅,修复中大奖没有推送

This commit is contained in:
2026-05-27 09:47:42 +08:00
parent 4c69a8c77f
commit a7c2a29764
3 changed files with 151 additions and 16 deletions

View File

@@ -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);
}

View File

@@ -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<string, mixed> $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;
}
}

View File

@@ -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 <period_no> # 指定单期
* 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=<no> --force\n";