1.ws优化bet.win订阅,修复中大奖没有推送
This commit is contained in:
@@ -339,7 +339,15 @@ final class GameBetSettleService
|
|||||||
'payout_pending_review' => $payoutPendingReview,
|
'payout_pending_review' => $payoutPendingReview,
|
||||||
'server_time' => $now,
|
'server_time' => $now,
|
||||||
]), $userId);
|
]), $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) {
|
if ($periodId > 0) {
|
||||||
self::markBetWinNotifyOnce($periodId, $userId);
|
self::markBetWinNotifyOnce($periodId, $userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace app\common\service;
|
namespace app\common\service;
|
||||||
|
|
||||||
|
use support\Log;
|
||||||
use support\Redis;
|
use support\Redis;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
@@ -17,12 +18,13 @@ final class GameWebSocketEventBus
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param array<string, mixed> $data
|
* @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);
|
$topic = trim($topic);
|
||||||
if ($topic === '') {
|
if ($topic === '') {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
$payload = [
|
$payload = [
|
||||||
'topic' => $topic,
|
'topic' => $topic,
|
||||||
@@ -32,11 +34,17 @@ final class GameWebSocketEventBus
|
|||||||
];
|
];
|
||||||
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||||
if (!is_string($json) || $json === '') {
|
if (!is_string($json) || $json === '') {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Redis::lPush(self::KEY_QUEUE, $json);
|
$len = Redis::lPush(self::KEY_QUEUE, $json);
|
||||||
} catch (Throwable) {
|
return is_numeric($len) && (int) $len > 0;
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
Log::warning('ws event bus publish failed', [
|
||||||
|
'topic' => $topic,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,17 +5,136 @@ declare(strict_types=1);
|
|||||||
require_once __DIR__ . '/../vendor/autoload.php';
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
require_once __DIR__ . '/../support/bootstrap.php';
|
require_once __DIR__ . '/../support/bootstrap.php';
|
||||||
|
|
||||||
|
use support\Redis;
|
||||||
use support\think\Db;
|
use support\think\Db;
|
||||||
|
|
||||||
$periodNo = $argv[1] ?? '20260526-190442-c340448b';
|
/**
|
||||||
$gr = Db::name('game_record')->where('period_no', $periodNo)->find();
|
* bet.win 推送链路诊断脚本(线上排查专用)。
|
||||||
if (!is_array($gr)) {
|
*
|
||||||
fwrite(STDERR, "period not found: {$periodNo}\n");
|
* 用法:
|
||||||
exit(1);
|
* 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'];
|
if ($periodNo === '' && $recent === 0) {
|
||||||
$bets = Db::name('bet_order')->where('period_id', $pid)->select()->toArray();
|
$recent = 5;
|
||||||
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";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$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";
|
||||||
|
|||||||
Reference in New Issue
Block a user