1.修复自动创建下一期bug
This commit is contained in:
@@ -54,6 +54,20 @@ final class GameBetSettleService
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$periodStatus = filter_var(
|
||||||
|
Db::name('game_record')->where('id', $recordId)->value('status'),
|
||||||
|
FILTER_VALIDATE_INT
|
||||||
|
);
|
||||||
|
if ($periodStatus === 5) {
|
||||||
|
return [
|
||||||
|
'jackpot_hits' => [],
|
||||||
|
'bet_wins' => [],
|
||||||
|
'user_streak_events' => [],
|
||||||
|
'wallet_events' => [],
|
||||||
|
'settled_order_count' => 0,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
$now = time();
|
$now = time();
|
||||||
$jackpotMaxAmount = self::jackpotMaxAmount();
|
$jackpotMaxAmount = self::jackpotMaxAmount();
|
||||||
$bets = Db::name('bet_order')
|
$bets = Db::name('bet_order')
|
||||||
@@ -84,6 +98,14 @@ final class GameBetSettleService
|
|||||||
$settledOrderCount = 0;
|
$settledOrderCount = 0;
|
||||||
|
|
||||||
foreach ($bets as $bet) {
|
foreach ($bets as $bet) {
|
||||||
|
$periodStatusNow = filter_var(
|
||||||
|
Db::name('game_record')->where('id', $recordId)->value('status'),
|
||||||
|
FILTER_VALIDATE_INT
|
||||||
|
);
|
||||||
|
if ($periodStatusNow === 5) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
$betId = (int) ($bet['id'] ?? 0);
|
$betId = (int) ($bet['id'] ?? 0);
|
||||||
if ($betId <= 0) {
|
if ($betId <= 0) {
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ use Throwable;
|
|||||||
|
|
||||||
final class GameLiveService
|
final class GameLiveService
|
||||||
{
|
{
|
||||||
|
/** @var array<int, true> 防止同进程内 recover→draw 重入导致 Redis 自锁 */
|
||||||
|
private static array $drawingRecordIds = [];
|
||||||
|
|
||||||
private const CHANNEL = 'game-live';
|
private const CHANNEL = 'game-live';
|
||||||
private const EVENT = 'bet-updated';
|
private const EVENT = 'bet-updated';
|
||||||
|
|
||||||
@@ -612,15 +615,20 @@ final class GameLiveService
|
|||||||
}
|
}
|
||||||
|
|
||||||
$rid = (int) $record['id'];
|
$rid = (int) $record['id'];
|
||||||
$lock = GameHotDataLock::tryAcquireWithWait(GameHotDataLock::TYPE_GAME_RECORD, (string) $rid, 2000);
|
if (isset(self::$drawingRecordIds[$rid])) {
|
||||||
|
return ['ok' => false, 'msg' => __('Another operation is in progress for this period; please try again later')];
|
||||||
|
}
|
||||||
|
|
||||||
|
$lock = GameHotDataLock::tryAcquireWithWait(GameHotDataLock::TYPE_GAME_RECORD, (string) $rid, 500);
|
||||||
if (!$lock['acquired']) {
|
if (!$lock['acquired']) {
|
||||||
return ['ok' => false, 'msg' => __('Another operation is in progress for this period; please try again later')];
|
return ['ok' => false, 'msg' => __('Another operation is in progress for this period; please try again later')];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var null|callable(): void */
|
/** @var null|callable(): void */
|
||||||
$notifyAfterLock = null;
|
$postLockWork = null;
|
||||||
$result = ['ok' => false, 'msg' => __('Game live: settlement error')];
|
$result = ['ok' => false, 'msg' => __('Game live: settlement error')];
|
||||||
|
|
||||||
|
self::$drawingRecordIds[$rid] = true;
|
||||||
try {
|
try {
|
||||||
self::ensureAiLocked($rid);
|
self::ensureAiLocked($rid);
|
||||||
|
|
||||||
@@ -645,7 +653,7 @@ final class GameLiveService
|
|||||||
if ($existingResult !== false && $existingResult >= 1 && $existingResult <= self::DRAW_NUMBER_MAX && $st >= 2) {
|
if ($existingResult !== false && $existingResult >= 1 && $existingResult <= self::DRAW_NUMBER_MAX && $st >= 2) {
|
||||||
Db::commit();
|
Db::commit();
|
||||||
$periodNo = is_string($record['period_no'] ?? null) ? (string) $record['period_no'] : '';
|
$periodNo = is_string($record['period_no'] ?? null) ? (string) $record['period_no'] : '';
|
||||||
$notifyAfterLock = static function () use ($rid): void {
|
$postLockWork = static function () use ($rid): void {
|
||||||
GameHotDataCoordinator::afterGameRecordCommitted($rid);
|
GameHotDataCoordinator::afterGameRecordCommitted($rid);
|
||||||
self::publishSnapshot($rid, false);
|
self::publishSnapshot($rid, false);
|
||||||
};
|
};
|
||||||
@@ -679,7 +687,6 @@ final class GameLiveService
|
|||||||
'payout_until' => $payoutUntil,
|
'payout_until' => $payoutUntil,
|
||||||
'update_time' => $now,
|
'update_time' => $now,
|
||||||
]);
|
]);
|
||||||
$settleOut = GameBetSettleService::settleBetsForDraw($rid, $finalNumber);
|
|
||||||
Db::commit();
|
Db::commit();
|
||||||
$drawCommitted = true;
|
$drawCommitted = true;
|
||||||
}
|
}
|
||||||
@@ -691,15 +698,24 @@ final class GameLiveService
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($drawCommitted) {
|
if ($drawCommitted) {
|
||||||
$notifyAfterLock = static function () use (
|
$postLockWork = static function () use (
|
||||||
$rid,
|
$rid,
|
||||||
$periodNo,
|
$periodNo,
|
||||||
$finalNumber,
|
$finalNumber,
|
||||||
$drawMode,
|
$drawMode,
|
||||||
$payoutUntil,
|
$payoutUntil,
|
||||||
$now,
|
$now
|
||||||
$settleOut
|
|
||||||
): void {
|
): void {
|
||||||
|
try {
|
||||||
|
$settleOut = GameBetSettleService::settleBetsForDraw($rid, $finalNumber);
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
Log::warning('drawResult settle after lock failed', [
|
||||||
|
'record_id' => $rid,
|
||||||
|
'error' => $e->getMessage(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
GameBetSettleService::publishSettlementWinsAfterCommit(
|
GameBetSettleService::publishSettlementWinsAfterCommit(
|
||||||
$settleOut,
|
$settleOut,
|
||||||
$rid,
|
$rid,
|
||||||
@@ -736,11 +752,12 @@ final class GameLiveService
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
unset(self::$drawingRecordIds[$rid]);
|
||||||
GameHotDataLock::release(GameHotDataLock::TYPE_GAME_RECORD, (string) $rid, $lock['token'], $lock['redis_lock']);
|
GameHotDataLock::release(GameHotDataLock::TYPE_GAME_RECORD, (string) $rid, $lock['token'], $lock['redis_lock']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($notifyAfterLock !== null) {
|
if ($postLockWork !== null) {
|
||||||
$notifyAfterLock();
|
$postLockWork();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
@@ -865,12 +882,33 @@ final class GameLiveService
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$rid = (int) $record['id'];
|
$rid = (int) $record['id'];
|
||||||
|
if (GameHotDataRedis::isStaleOpenPeriodRecord($record, $periodSeconds)) {
|
||||||
|
GameHotDataLock::forceRelease(GameHotDataLock::TYPE_GAME_RECORD, (string) $rid);
|
||||||
|
}
|
||||||
$out = self::drawResult($rid, null);
|
$out = self::drawResult($rid, null);
|
||||||
if (!($out['ok'] ?? false)) {
|
if ($out['ok'] ?? false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$msg = is_string($out['msg'] ?? null) ? (string) $out['msg'] : '';
|
||||||
|
if (!str_contains($msg, 'Another operation is in progress')) {
|
||||||
Log::warning('tickAutoDraw: drawResult failed', [
|
Log::warning('tickAutoDraw: drawResult failed', [
|
||||||
'record_id' => $rid,
|
'record_id' => $rid,
|
||||||
'period_no' => $record['period_no'] ?? '',
|
'period_no' => $record['period_no'] ?? '',
|
||||||
'msg' => $out['msg'] ?? '',
|
'msg' => $msg,
|
||||||
|
]);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!GameHotDataRedis::isStaleOpenPeriodRecord($record, $periodSeconds)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GameHotDataLock::forceRelease(GameHotDataLock::TYPE_GAME_RECORD, (string) $rid);
|
||||||
|
$retry = self::drawResult($rid, null);
|
||||||
|
if (!($retry['ok'] ?? false)) {
|
||||||
|
Log::warning('tickAutoDraw: drawResult failed after lock force-release', [
|
||||||
|
'record_id' => $rid,
|
||||||
|
'period_no' => $record['period_no'] ?? '',
|
||||||
|
'msg' => is_string($retry['msg'] ?? null) ? $retry['msg'] : '',
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -965,10 +1003,10 @@ final class GameLiveService
|
|||||||
return ['ok' => false, 'msg' => __('No active game in progress')];
|
return ['ok' => false, 'msg' => __('No active game in progress')];
|
||||||
}
|
}
|
||||||
$st = (int) ($record['status'] ?? -1);
|
$st = (int) ($record['status'] ?? -1);
|
||||||
if (!in_array($st, [0, 1], true)) {
|
if (!in_array($st, [0, 1, 3], true)) {
|
||||||
return ['ok' => false, 'msg' => __('Current period cannot be voided')];
|
return ['ok' => false, 'msg' => __('Current period cannot be voided')];
|
||||||
}
|
}
|
||||||
$lock = self::acquireRecordLockForAdminMutation((string) $recordId, 1500);
|
$lock = self::acquireRecordLockForAdminMutation((string) $recordId, 8000);
|
||||||
if (!$lock['acquired']) {
|
if (!$lock['acquired']) {
|
||||||
return ['ok' => false, 'msg' => __('Another operation is in progress for this period; please try again later')];
|
return ['ok' => false, 'msg' => __('Another operation is in progress for this period; please try again later')];
|
||||||
}
|
}
|
||||||
@@ -976,23 +1014,16 @@ final class GameLiveService
|
|||||||
$refund = ['user_ids' => [], 'order_count' => 0, 'total_amount' => '0.00', 'order_ids' => []];
|
$refund = ['user_ids' => [], 'order_count' => 0, 'total_amount' => '0.00', 'order_ids' => []];
|
||||||
try {
|
try {
|
||||||
$now = time();
|
$now = time();
|
||||||
Db::startTrans();
|
$marked = self::markPeriodVoidedInDb($recordId, $reason, $now);
|
||||||
try {
|
if (!($marked['ok'] ?? false)) {
|
||||||
$refund = self::refundPendingBetsSummaryForPeriodLocked($recordId, $now);
|
$errMsg = $marked['msg'] ?? null;
|
||||||
$refundedUserIds = $refund['user_ids'];
|
|
||||||
Db::name('game_record')->where('id', $recordId)->update([
|
return ['ok' => false, 'msg' => is_string($errMsg) ? $errMsg : __('Void failed')];
|
||||||
'status' => 5,
|
|
||||||
'void_reason' => $reason,
|
|
||||||
'pending_draw_number' => null,
|
|
||||||
'payout_until' => null,
|
|
||||||
'ai_locked_number' => null,
|
|
||||||
'update_time' => $now,
|
|
||||||
]);
|
|
||||||
Db::commit();
|
|
||||||
} catch (Throwable $e) {
|
|
||||||
Db::rollback();
|
|
||||||
return ['ok' => false, 'msg' => __('Void failed') . ': ' . $e->getMessage()];
|
|
||||||
}
|
}
|
||||||
|
$refund = self::refundPendingBetsSummaryForPeriod($recordId, $now);
|
||||||
|
$refundedUserIds = $refund['user_ids'];
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
return ['ok' => false, 'msg' => __('Void failed') . ': ' . $e->getMessage()];
|
||||||
} finally {
|
} finally {
|
||||||
GameHotDataLock::release(GameHotDataLock::TYPE_GAME_RECORD, (string) $recordId, $lock['token'], $lock['redis_lock']);
|
GameHotDataLock::release(GameHotDataLock::TYPE_GAME_RECORD, (string) $recordId, $lock['token'], $lock['redis_lock']);
|
||||||
}
|
}
|
||||||
@@ -1037,7 +1068,7 @@ final class GameLiveService
|
|||||||
return ['ok' => false, 'msg' => __('No active game in progress')];
|
return ['ok' => false, 'msg' => __('No active game in progress')];
|
||||||
}
|
}
|
||||||
$st = (int) $record['status'];
|
$st = (int) $record['status'];
|
||||||
if (!in_array($st, [0, 1], true)) {
|
if (!in_array($st, [0, 1, 3], true)) {
|
||||||
return ['ok' => false, 'msg' => __('Current period cannot be voided')];
|
return ['ok' => false, 'msg' => __('Current period cannot be voided')];
|
||||||
}
|
}
|
||||||
$rid = (int) $record['id'];
|
$rid = (int) $record['id'];
|
||||||
@@ -1081,10 +1112,71 @@ final class GameLiveService
|
|||||||
return $summary['user_ids'];
|
return $summary['user_ids'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 先将本期标记为作废(短事务、尽快释放 game_record 行锁),再逐笔退款。
|
||||||
|
*
|
||||||
|
* @return array{ok: bool, msg?: string}
|
||||||
|
*/
|
||||||
|
private static function markPeriodVoidedInDb(int $recordId, string $reason, int $now): array
|
||||||
|
{
|
||||||
|
$attempts = 0;
|
||||||
|
while ($attempts < 4) {
|
||||||
|
$attempts++;
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
|
$row = self::loadRecordRowFromDb($recordId, true);
|
||||||
|
if (!$row) {
|
||||||
|
Db::rollback();
|
||||||
|
|
||||||
|
return ['ok' => false, 'msg' => __('No active game in progress')];
|
||||||
|
}
|
||||||
|
$st = (int) ($row['status'] ?? -1);
|
||||||
|
if (!in_array($st, [0, 1, 3], true)) {
|
||||||
|
Db::rollback();
|
||||||
|
|
||||||
|
return ['ok' => false, 'msg' => __('Current period cannot be voided')];
|
||||||
|
}
|
||||||
|
Db::name('game_record')->where('id', $recordId)->update([
|
||||||
|
'status' => 5,
|
||||||
|
'void_reason' => $reason,
|
||||||
|
'pending_draw_number' => null,
|
||||||
|
'payout_until' => null,
|
||||||
|
'ai_locked_number' => $st === 3 ? ($row['ai_locked_number'] ?? null) : null,
|
||||||
|
'update_time' => $now,
|
||||||
|
]);
|
||||||
|
Db::commit();
|
||||||
|
|
||||||
|
return ['ok' => true];
|
||||||
|
} catch (Throwable $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$msg = $e->getMessage();
|
||||||
|
if ($attempts < 4 && str_contains($msg, '1205')) {
|
||||||
|
usleep(200_000);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['ok' => false, 'msg' => __('Void failed') . ': ' . $msg];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ['ok' => false, 'msg' => __('Void failed') . ': lock wait timeout'];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array{user_ids:list<int>,order_count:int,total_amount:string,order_ids:list<int>}
|
* @return array{user_ids:list<int>,order_count:int,total_amount:string,order_ids:list<int>}
|
||||||
*/
|
*/
|
||||||
private static function refundPendingBetsSummaryForPeriodLocked(int $periodId, int $now): array
|
private static function refundPendingBetsSummaryForPeriodLocked(int $periodId, int $now): array
|
||||||
|
{
|
||||||
|
return self::refundPendingBetsSummaryForPeriod($periodId, $now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 逐笔退款(每笔独立短事务),避免与开奖结算共用一个长事务抢 InnoDB 行锁。
|
||||||
|
*
|
||||||
|
* @return array{user_ids:list<int>,order_count:int,total_amount:string,order_ids:list<int>}
|
||||||
|
*/
|
||||||
|
private static function refundPendingBetsSummaryForPeriod(int $periodId, int $now): array
|
||||||
{
|
{
|
||||||
$userIdSet = [];
|
$userIdSet = [];
|
||||||
$orderCount = 0;
|
$orderCount = 0;
|
||||||
@@ -1097,22 +1189,65 @@ final class GameLiveService
|
|||||||
->select()
|
->select()
|
||||||
->toArray();
|
->toArray();
|
||||||
foreach ($bets as $bet) {
|
foreach ($bets as $bet) {
|
||||||
|
$single = self::refundSinglePendingBet($bet, $now);
|
||||||
|
if ($single === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($single['user_id'] > 0) {
|
||||||
|
$userIdSet[$single['user_id']] = true;
|
||||||
|
}
|
||||||
|
$orderCount++;
|
||||||
|
$totalAmount = bcadd($totalAmount, $single['amount'], 2);
|
||||||
|
$orderIds[] = $single['bet_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$out = [];
|
||||||
|
foreach (array_keys($userIdSet) as $uid) {
|
||||||
|
$out[] = (int) $uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'user_ids' => $out,
|
||||||
|
'order_count' => $orderCount,
|
||||||
|
'total_amount' => $totalAmount,
|
||||||
|
'order_ids' => $orderIds,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<string, mixed> $bet
|
||||||
|
* @return array{user_id: int, bet_id: int, amount: string}|null
|
||||||
|
*/
|
||||||
|
private static function refundSinglePendingBet(array $bet, int $now): ?array
|
||||||
|
{
|
||||||
$betId = (int) ($bet['id'] ?? 0);
|
$betId = (int) ($bet['id'] ?? 0);
|
||||||
|
if ($betId <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
$userId = (int) ($bet['user_id'] ?? 0);
|
$userId = (int) ($bet['user_id'] ?? 0);
|
||||||
$totalRaw = $bet['total_amount'] ?? '0';
|
$totalRaw = $bet['total_amount'] ?? '0';
|
||||||
$total = is_string($totalRaw) ? $totalRaw : (string) $totalRaw;
|
$total = is_string($totalRaw) ? $totalRaw : (string) $totalRaw;
|
||||||
if ($betId <= 0) {
|
|
||||||
continue;
|
$attempts = 0;
|
||||||
}
|
while ($attempts < 4) {
|
||||||
|
$attempts++;
|
||||||
|
Db::startTrans();
|
||||||
|
try {
|
||||||
if ($userId <= 0 || bccomp($total, '0', 2) <= 0) {
|
if ($userId <= 0 || bccomp($total, '0', 2) <= 0) {
|
||||||
Db::name('bet_order')->where('id', $betId)->where('status', 1)->update([
|
$bo = Db::name('bet_order')->where('id', $betId)->where('status', 1)->update([
|
||||||
'status' => 3,
|
'status' => 3,
|
||||||
'update_time' => $now,
|
'update_time' => $now,
|
||||||
]);
|
]);
|
||||||
$orderCount++;
|
if ($bo !== 1) {
|
||||||
$orderIds[] = $betId;
|
Db::rollback();
|
||||||
continue;
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
Db::commit();
|
||||||
|
|
||||||
|
return ['user_id' => 0, 'bet_id' => $betId, 'amount' => '0.00'];
|
||||||
|
}
|
||||||
|
|
||||||
$before = (string) (Db::name('user')->where('id', $userId)->value('coin') ?? '0');
|
$before = (string) (Db::name('user')->where('id', $userId)->value('coin') ?? '0');
|
||||||
$after = bcadd($before, $total, 2);
|
$after = bcadd($before, $total, 2);
|
||||||
$u = Db::name('user')->where('id', $userId)->where('coin', $before)->update([
|
$u = Db::name('user')->where('id', $userId)->where('coin', $before)->update([
|
||||||
@@ -1127,7 +1262,9 @@ final class GameLiveService
|
|||||||
'update_time' => $now,
|
'update_time' => $now,
|
||||||
]);
|
]);
|
||||||
if ($bo !== 1) {
|
if ($bo !== 1) {
|
||||||
throw new \RuntimeException((string) __('Bet order state changed; please retry'));
|
Db::rollback();
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
$channelIdRaw = $bet['channel_id'] ?? null;
|
$channelIdRaw = $bet['channel_id'] ?? null;
|
||||||
$channelId = filter_var($channelIdRaw, FILTER_VALIDATE_INT);
|
$channelId = filter_var($channelIdRaw, FILTER_VALIDATE_INT);
|
||||||
@@ -1146,28 +1283,27 @@ final class GameLiveService
|
|||||||
'remark' => (string) __('Period void refund'),
|
'remark' => (string) __('Period void refund'),
|
||||||
'create_time' => $now,
|
'create_time' => $now,
|
||||||
]);
|
]);
|
||||||
$userIdSet[$userId] = true;
|
Db::commit();
|
||||||
$orderCount++;
|
|
||||||
$totalAmount = bcadd($totalAmount, $total, 2);
|
return ['user_id' => $userId, 'bet_id' => $betId, 'amount' => $total];
|
||||||
$orderIds[] = $betId;
|
} catch (Throwable $e) {
|
||||||
|
Db::rollback();
|
||||||
|
$msg = $e->getMessage();
|
||||||
|
if ($attempts < 4 && str_contains($msg, '1205')) {
|
||||||
|
usleep(200_000);
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$out = [];
|
throw new \RuntimeException((string) __('Void failed') . ': lock wait timeout');
|
||||||
foreach (array_keys($userIdSet) as $uid) {
|
|
||||||
$out[] = (int) $uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'user_ids' => $out,
|
|
||||||
'order_count' => $orderCount,
|
|
||||||
'total_amount' => $totalAmount,
|
|
||||||
'order_ids' => $orderIds,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function publishSnapshot(?int $recordId = null, bool $runRecovery = true): void
|
public static function publishSnapshot(?int $recordId = null, bool $runRecovery = true): void
|
||||||
{
|
{
|
||||||
if ($runRecovery) {
|
if ($runRecovery && empty(self::$drawingRecordIds)) {
|
||||||
self::recoverLiveRoundState();
|
self::recoverLiveRoundState();
|
||||||
}
|
}
|
||||||
$snapshot = self::buildSnapshot($recordId);
|
$snapshot = self::buildSnapshot($recordId);
|
||||||
@@ -1669,7 +1805,6 @@ final class GameLiveService
|
|||||||
if ($recordId === '') {
|
if ($recordId === '') {
|
||||||
return ['acquired' => false, 'token' => null, 'redis_lock' => false];
|
return ['acquired' => false, 'token' => null, 'redis_lock' => false];
|
||||||
}
|
}
|
||||||
GameHotDataLock::forceRelease(GameHotDataLock::TYPE_GAME_RECORD, $recordId);
|
|
||||||
$lock = GameHotDataLock::tryAcquireWithWait(GameHotDataLock::TYPE_GAME_RECORD, $recordId, $waitMs);
|
$lock = GameHotDataLock::tryAcquireWithWait(GameHotDataLock::TYPE_GAME_RECORD, $recordId, $waitMs);
|
||||||
if ($lock['acquired']) {
|
if ($lock['acquired']) {
|
||||||
return $lock;
|
return $lock;
|
||||||
@@ -1677,7 +1812,7 @@ final class GameLiveService
|
|||||||
GameHotDataLock::forceRelease(GameHotDataLock::TYPE_GAME_RECORD, $recordId);
|
GameHotDataLock::forceRelease(GameHotDataLock::TYPE_GAME_RECORD, $recordId);
|
||||||
Log::warning('admin record lock force-released before void retry', ['record_id' => $recordId]);
|
Log::warning('admin record lock force-released before void retry', ['record_id' => $recordId]);
|
||||||
|
|
||||||
return GameHotDataLock::tryAcquire(GameHotDataLock::TYPE_GAME_RECORD, $recordId);
|
return GameHotDataLock::tryAcquireWithWait(GameHotDataLock::TYPE_GAME_RECORD, $recordId, 2000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -85,7 +85,6 @@ class GameWebSocketServer
|
|||||||
if (!$hasAdminSubscriber) {
|
if (!$hasAdminSubscriber) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
GameLiveService::recoverLiveRoundState();
|
|
||||||
$snapshot = GameLiveService::buildSnapshot(null);
|
$snapshot = GameLiveService::buildSnapshot(null);
|
||||||
$payload = json_encode([
|
$payload = json_encode([
|
||||||
'event' => 'admin.live.snapshot',
|
'event' => 'admin.live.snapshot',
|
||||||
|
|||||||
@@ -668,7 +668,7 @@ const canVoidPeriod = computed(() => {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const s = Number(r.status)
|
const s = Number(r.status)
|
||||||
return s === 0 || s === 1
|
return s === 0 || s === 1 || s === 3
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 派彩结束后的完整维护态:操作区除顶部开关外全部锁定 */
|
/** 派彩结束后的完整维护态:操作区除顶部开关外全部锁定 */
|
||||||
|
|||||||
Reference in New Issue
Block a user