1.ws优化bet.win订阅,修复中大奖没有推送
This commit is contained in:
@@ -494,6 +494,50 @@ final class GameBetSettleService
|
|||||||
$effectiveBetWins = self::buildBetWinPayloadsFromSettledOrders($periodId, $resultNumber);
|
$effectiveBetWins = self::buildBetWinPayloadsFromSettledOrders($periodId, $resultNumber);
|
||||||
}
|
}
|
||||||
self::publishBetWinsAfterCommit($effectiveBetWins, $periodId);
|
self::publishBetWinsAfterCommit($effectiveBetWins, $periodId);
|
||||||
|
if ($periodId > 0 && $resultNumber > 0) {
|
||||||
|
self::ensurePeriodBetWinNotifications($periodId, $resultNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开奖后兜底:库内已有中奖但 Redis 无 bet.win 去重键时补推(避免 streak/wallet 整期 dedup 或旧版逻辑漏推)。
|
||||||
|
*/
|
||||||
|
public static function ensurePeriodBetWinNotifications(int $periodId, int $resultNumber): void
|
||||||
|
{
|
||||||
|
if ($periodId <= 0 || $resultNumber < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$payloads = self::buildBetWinPayloadsFromSettledOrders($periodId, $resultNumber);
|
||||||
|
if ($payloads === []) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$missing = [];
|
||||||
|
foreach ($payloads as $payload) {
|
||||||
|
if (!is_array($payload)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$userId = filter_var($payload['user_id'] ?? 0, FILTER_VALIDATE_INT);
|
||||||
|
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 !== '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} catch (Throwable) {
|
||||||
|
}
|
||||||
|
$missing[] = $payload;
|
||||||
|
}
|
||||||
|
if ($missing !== []) {
|
||||||
|
Log::warning('bet.win ensurePeriodBetWinNotifications republish', [
|
||||||
|
'period_id' => $periodId,
|
||||||
|
'result_number' => $resultNumber,
|
||||||
|
'user_ids' => array_map(static fn (array $p): int => (int) ($p['user_id'] ?? 0), $missing),
|
||||||
|
]);
|
||||||
|
self::publishBetWinsAfterCommit($missing, $periodId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static function markBetWinNotifyOnce(int $periodId, int $userId): bool
|
private static function markBetWinNotifyOnce(int $periodId, int $userId): bool
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ final class GameLiveService
|
|||||||
is_string($row['period_no'] ?? null) ? (string) $row['period_no'] : '',
|
is_string($row['period_no'] ?? null) ? (string) $row['period_no'] : '',
|
||||||
(int) $resultNumber
|
(int) $resultNumber
|
||||||
);
|
);
|
||||||
|
GameBetSettleService::ensurePeriodBetWinNotifications($recordId, (int) $resultNumber);
|
||||||
|
|
||||||
GameHotDataCoordinator::afterGameRecordCommitted($recordId);
|
GameHotDataCoordinator::afterGameRecordCommitted($recordId);
|
||||||
self::publishSnapshot(null);
|
self::publishSnapshot(null);
|
||||||
|
|||||||
79
scripts/debug_bet_win_push.php
Normal file
79
scripts/debug_bet_win_push.php
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/../support/bootstrap.php';
|
||||||
|
|
||||||
|
use app\common\service\GameBetSettleService;
|
||||||
|
use support\Redis;
|
||||||
|
use support\think\Db;
|
||||||
|
|
||||||
|
$playId = (int) ($argv[1] ?? 1372);
|
||||||
|
|
||||||
|
$row = Db::name('game_play_record')->where('id', $playId)->find();
|
||||||
|
if (!is_array($row)) {
|
||||||
|
$row = Db::name('bet_order')->where('id', $playId)->find();
|
||||||
|
}
|
||||||
|
if (!is_array($row)) {
|
||||||
|
fwrite(STDERR, "play record not found: {$playId}\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$periodId = (int) ($row['period_id'] ?? 0);
|
||||||
|
$periodNo = (string) ($row['period_no'] ?? '');
|
||||||
|
$userId = (int) ($row['user_id'] ?? 0);
|
||||||
|
$status = (int) ($row['status'] ?? 0);
|
||||||
|
$winAmount = (string) ($row['win_amount'] ?? '0');
|
||||||
|
|
||||||
|
echo "=== play_record id={$playId} ===\n";
|
||||||
|
echo "period_id={$periodId} period_no={$periodNo}\n";
|
||||||
|
echo "user_id={$userId} status={$status} win_amount={$winAmount}\n";
|
||||||
|
echo "streak_at_bet=" . ($row['streak_at_bet'] ?? '') . "\n";
|
||||||
|
|
||||||
|
$user = Db::name('user')->where('id', $userId)->field('id,username,phone')->find();
|
||||||
|
if (is_array($user)) {
|
||||||
|
echo "user: id={$user['id']} username={$user['username']} phone={$user['phone']}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$gr = Db::name('game_record')->where('id', $periodId)->find();
|
||||||
|
if (is_array($gr)) {
|
||||||
|
echo "game_record: status={$gr['status']} result_number={$gr['result_number']} period_start_at={$gr['period_start_at']}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$settleKey = 'dfw:v1:settle:notify:' . $periodId;
|
||||||
|
$betWinKey = 'dfw:v1:ws:betwin:' . $periodId . ':' . $userId;
|
||||||
|
echo "\n=== Redis dedup keys ===\n";
|
||||||
|
try {
|
||||||
|
echo "settle_notify={$settleKey} => " . var_export(Redis::get($settleKey), true) . "\n";
|
||||||
|
echo "bet_win={$betWinKey} => " . var_export(Redis::get($betWinKey), true) . "\n";
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
echo 'redis err: ' . $e->getMessage() . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$queueLen = 0;
|
||||||
|
try {
|
||||||
|
$queueLen = (int) Redis::lLen('dfw:v1:ws:event:queue');
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
}
|
||||||
|
echo "ws queue length={$queueLen}\n";
|
||||||
|
|
||||||
|
$resultNumber = is_array($gr) ? (int) ($gr['result_number'] ?? 0) : 0;
|
||||||
|
$payloads = GameBetSettleService::buildBetWinPayloadsFromSettledOrders($periodId, $resultNumber);
|
||||||
|
echo "\n=== buildBetWinPayloadsFromSettledOrders ===\n";
|
||||||
|
echo 'winner_count=' . count($payloads) . "\n";
|
||||||
|
foreach ($payloads as $p) {
|
||||||
|
echo json_encode($p, JSON_UNESCAPED_UNICODE) . "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
$allWinners = Db::name('bet_order')
|
||||||
|
->where('period_id', $periodId)
|
||||||
|
->whereIn('status', [2, 5])
|
||||||
|
->whereRaw('CAST(win_amount AS DECIMAL(20,2)) > 0')
|
||||||
|
->field('id,user_id,win_amount,status,streak_at_bet')
|
||||||
|
->select()
|
||||||
|
->toArray();
|
||||||
|
echo "\n=== all winning orders in period ===\n";
|
||||||
|
foreach ($allWinners as $w) {
|
||||||
|
echo "bet_id={$w['id']} user_id={$w['user_id']} win={$w['win_amount']} status={$w['status']} streak_at_bet={$w['streak_at_bet']}\n";
|
||||||
|
}
|
||||||
84
scripts/republish_bet_win.php
Normal file
84
scripts/republish_bet_win.php
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 补发 bet.win WebSocket 推送(已结算中奖但客户端未收到时使用)。
|
||||||
|
*
|
||||||
|
* 用法:
|
||||||
|
* php scripts/republish_bet_win.php --period-id=123
|
||||||
|
* php scripts/republish_bet_win.php --play-record-id=1370
|
||||||
|
* php scripts/republish_bet_win.php --period-no=20260526-183418-c9c90ef1
|
||||||
|
* php scripts/republish_bet_win.php --period-id=123 --force
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../vendor/autoload.php';
|
||||||
|
require_once __DIR__ . '/../support/bootstrap.php';
|
||||||
|
|
||||||
|
use app\common\service\GameBetSettleService;
|
||||||
|
use support\Redis;
|
||||||
|
use support\think\Db;
|
||||||
|
|
||||||
|
$opts = getopt('', ['period-id:', 'play-record-id:', 'period-no:', 'force']);
|
||||||
|
$force = array_key_exists('force', $opts);
|
||||||
|
|
||||||
|
$periodId = isset($opts['period-id']) ? (int) $opts['period-id'] : 0;
|
||||||
|
if (isset($opts['play-record-id'])) {
|
||||||
|
$playId = (int) $opts['play-record-id'];
|
||||||
|
if ($playId > 0) {
|
||||||
|
$pid = Db::name('game_play_record')->where('id', $playId)->value('period_id');
|
||||||
|
$periodId = is_numeric((string) $pid) ? (int) $pid : 0;
|
||||||
|
echo "play_record_id={$playId} => period_id={$periodId}" . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($periodId <= 0 && isset($opts['period-no'])) {
|
||||||
|
$periodNo = trim((string) $opts['period-no']);
|
||||||
|
if ($periodNo !== '') {
|
||||||
|
$pid = Db::name('game_record')->where('period_no', $periodNo)->value('id');
|
||||||
|
$periodId = is_numeric((string) $pid) ? (int) $pid : 0;
|
||||||
|
echo "period_no={$periodNo} => period_id={$periodId}" . PHP_EOL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($periodId <= 0) {
|
||||||
|
fwrite(STDERR, "请指定 --period-id、--play-record-id 或 --period-no\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$row = Db::name('game_record')->where('id', $periodId)->find();
|
||||||
|
if (!is_array($row)) {
|
||||||
|
fwrite(STDERR, "对局不存在: period_id={$periodId}\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$resultNumber = filter_var($row['result_number'] ?? 0, FILTER_VALIDATE_INT);
|
||||||
|
if ($resultNumber === false || $resultNumber < 1) {
|
||||||
|
fwrite(STDERR, "对局尚未开奖,无法补发 bet.win\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$payloads = GameBetSettleService::buildBetWinPayloadsFromSettledOrders($periodId, $resultNumber);
|
||||||
|
if ($payloads === []) {
|
||||||
|
echo "本期无已结算中奖注单,无需补发。\n";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($force) {
|
||||||
|
foreach ($payloads as $payload) {
|
||||||
|
$uid = (int) ($payload['user_id'] ?? 0);
|
||||||
|
if ($uid <= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Redis::del('dfw:v1:ws:betwin:' . $periodId . ':' . $uid);
|
||||||
|
} catch (Throwable) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo "已清除 dedup 键(--force)\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
GameBetSettleService::publishBetWinsAfterCommit($payloads, $periodId);
|
||||||
|
echo '已补发 bet.win 用户数: ' . count($payloads) . PHP_EOL;
|
||||||
|
foreach ($payloads as $p) {
|
||||||
|
echo ' user_id=' . ($p['user_id'] ?? '') . ' total_win=' . ($p['total_win'] ?? '') . ' period_no=' . ($p['period_no'] ?? '') . PHP_EOL;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user