优化游戏实时对局页面样式
This commit is contained in:
@@ -86,7 +86,6 @@ class Record extends Backend
|
||||
|
||||
$rows = Db::name('game_record')
|
||||
->where('status', 5)
|
||||
->whereLike('void_reason', 'system_recover:%')
|
||||
->field(['id', 'period_no', 'void_reason', 'update_time'])
|
||||
->order('id', 'desc')
|
||||
->limit($limit)
|
||||
@@ -96,6 +95,8 @@ class Record extends Backend
|
||||
$list = [];
|
||||
foreach ($rows as $row) {
|
||||
$meta = $this->parseRecoverVoidReason(is_string($row['void_reason'] ?? null) ? $row['void_reason'] : '');
|
||||
$reason = is_string($row['void_reason'] ?? null) ? $row['void_reason'] : '';
|
||||
$isAutoRecover = $this->isSystemRecoverReason($reason);
|
||||
$list[] = [
|
||||
'id' => (int) ($row['id'] ?? 0),
|
||||
'period_no' => (string) ($row['period_no'] ?? ''),
|
||||
@@ -104,7 +105,8 @@ class Record extends Backend
|
||||
'refunded_order_count' => $meta['orders'],
|
||||
'refunded_total_amount' => $meta['amount'],
|
||||
'recovered_at' => (int) ($row['update_time'] ?? 0),
|
||||
'void_reason' => (string) ($row['void_reason'] ?? ''),
|
||||
'void_reason' => $reason,
|
||||
'is_auto_recover' => $isAutoRecover ? 1 : 0,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -125,10 +127,13 @@ class Record extends Backend
|
||||
'orders' => 0,
|
||||
'amount' => '0.00',
|
||||
];
|
||||
if ($reason === '' || str_starts_with($reason, 'system_recover:') === false) {
|
||||
if (!$this->isSystemRecoverReason($reason)) {
|
||||
return $meta;
|
||||
}
|
||||
$payload = substr($reason, strlen('system_recover:'));
|
||||
if (!str_contains($payload, '=')) {
|
||||
return $meta;
|
||||
}
|
||||
$parts = explode('|', $payload);
|
||||
foreach ($parts as $part) {
|
||||
$item = trim($part);
|
||||
@@ -156,4 +161,9 @@ class Record extends Backend
|
||||
}
|
||||
return $meta;
|
||||
}
|
||||
|
||||
private function isSystemRecoverReason(string $reason): bool
|
||||
{
|
||||
return $reason !== '' && str_starts_with($reason, 'system_recover:');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,23 +52,102 @@ final class GameLiveService
|
||||
if ($recordId <= 0) {
|
||||
return;
|
||||
}
|
||||
$status = (int) ($row['status'] ?? 0);
|
||||
$resultNumber = isset($row['result_number']) ? (int) $row['result_number'] : 0;
|
||||
if ($resultNumber > 0 && in_array($status, [0, 1, 2, 3], true)) {
|
||||
self::recoverPayoutForRecordOnStartup($recordId);
|
||||
return;
|
||||
}
|
||||
|
||||
$periodStartAt = (int) ($row['period_start_at'] ?? 0);
|
||||
if ($periodStartAt <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$periodSeconds = self::getConfigInt(self::KEY_PERIOD_SECONDS, 30);
|
||||
$timeoutAt = $periodStartAt + $periodSeconds + self::PAYOUT_GRACE_SECONDS + self::STARTUP_RECOVER_GRACE_SECONDS;
|
||||
if (time() <= $timeoutAt) {
|
||||
return;
|
||||
}
|
||||
self::markAbnormalAndRefundOnStartup($recordId, $status);
|
||||
}
|
||||
|
||||
$status = (int) ($row['status'] ?? 0);
|
||||
private static function recoverPayoutForRecordOnStartup(int $recordId): void
|
||||
{
|
||||
$lock = GameHotDataLock::tryAcquireWithWait(GameHotDataLock::TYPE_GAME_RECORD, (string) $recordId, 3000);
|
||||
if (!$lock['acquired']) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$row = Db::name('game_record')->where('id', $recordId)->find();
|
||||
if (!$row) {
|
||||
return;
|
||||
}
|
||||
$status = (int) ($row['status'] ?? 0);
|
||||
if (!in_array($status, [0, 1, 2, 3], true)) {
|
||||
return;
|
||||
}
|
||||
$resultNumber = isset($row['result_number']) ? (int) $row['result_number'] : 0;
|
||||
if ($resultNumber <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$now = time();
|
||||
$payoutUntil = isset($row['payout_until']) ? (int) $row['payout_until'] : 0;
|
||||
Db::startTrans();
|
||||
try {
|
||||
GameBetSettleService::settleBetsForDraw($recordId, $resultNumber);
|
||||
if ($status === 2) {
|
||||
if ($payoutUntil <= 0) {
|
||||
$payoutUntil = $now + self::PAYOUT_GRACE_SECONDS;
|
||||
}
|
||||
Db::name('game_record')->where('id', $recordId)->update([
|
||||
'status' => 3,
|
||||
'payout_until' => $payoutUntil,
|
||||
'update_time' => $now,
|
||||
]);
|
||||
} elseif ($status === 3) {
|
||||
if ($payoutUntil <= 0) {
|
||||
$payoutUntil = $now;
|
||||
Db::name('game_record')->where('id', $recordId)->update([
|
||||
'payout_until' => $payoutUntil,
|
||||
'update_time' => $now,
|
||||
]);
|
||||
}
|
||||
} else {
|
||||
$payoutUntil = $now;
|
||||
Db::name('game_record')->where('id', $recordId)->update([
|
||||
'status' => 3,
|
||||
'payout_until' => $payoutUntil,
|
||||
'update_time' => $now,
|
||||
]);
|
||||
}
|
||||
Db::commit();
|
||||
} catch (Throwable $e) {
|
||||
Db::rollback();
|
||||
Log::warning('game live startup payout recover failed', [
|
||||
'record_id' => $recordId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
GameHotDataCoordinator::afterGameRecordCommitted($recordId);
|
||||
self::publishSnapshot(null);
|
||||
|
||||
if ($payoutUntil <= $now) {
|
||||
self::finalizePayoutForRecordLocked($recordId);
|
||||
}
|
||||
} finally {
|
||||
GameHotDataLock::release(GameHotDataLock::TYPE_GAME_RECORD, (string) $recordId, $lock['token'], $lock['redis_lock']);
|
||||
}
|
||||
}
|
||||
|
||||
private static function markAbnormalAndRefundOnStartup(int $recordId, int $status): void
|
||||
{
|
||||
$lock = GameHotDataLock::tryAcquireWithWait(GameHotDataLock::TYPE_GAME_RECORD, (string) $recordId, 3000);
|
||||
if (!$lock['acquired']) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
$fresh = Db::name('game_record')->where('id', $recordId)->find();
|
||||
if (!$fresh) {
|
||||
@@ -78,12 +157,8 @@ final class GameLiveService
|
||||
if (!in_array($freshStatus, [0, 1, 2, 3], true)) {
|
||||
return;
|
||||
}
|
||||
$freshPeriodStartAt = (int) ($fresh['period_start_at'] ?? 0);
|
||||
if ($freshPeriodStartAt <= 0) {
|
||||
return;
|
||||
}
|
||||
$freshTimeoutAt = $freshPeriodStartAt + $periodSeconds + self::PAYOUT_GRACE_SECONDS + self::STARTUP_RECOVER_GRACE_SECONDS;
|
||||
if (time() <= $freshTimeoutAt) {
|
||||
$freshResultNumber = isset($fresh['result_number']) ? (int) $fresh['result_number'] : 0;
|
||||
if ($freshResultNumber > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -92,13 +167,12 @@ final class GameLiveService
|
||||
Db::startTrans();
|
||||
try {
|
||||
$refund = self::refundPendingBetsSummaryForPeriodLocked($recordId, $now);
|
||||
$oldStatus = $freshStatus;
|
||||
$refundedUserCount = count($refund['user_ids']);
|
||||
$refundedOrderCount = (int) ($refund['order_count'] ?? 0);
|
||||
$refundedTotalAmount = is_string($refund['total_amount'] ?? null) ? $refund['total_amount'] : '0.00';
|
||||
$reason = sprintf(
|
||||
'system_recover:from=%d|users=%d|orders=%d|amount=%s',
|
||||
$oldStatus,
|
||||
$freshStatus,
|
||||
$refundedUserCount,
|
||||
$refundedOrderCount,
|
||||
$refundedTotalAmount
|
||||
@@ -114,8 +188,9 @@ final class GameLiveService
|
||||
Db::commit();
|
||||
} catch (Throwable $e) {
|
||||
Db::rollback();
|
||||
Log::warning('game live startup recover failed', [
|
||||
Log::warning('game live startup abnormal recover failed', [
|
||||
'record_id' => $recordId,
|
||||
'status' => $status,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
return;
|
||||
@@ -129,7 +204,7 @@ final class GameLiveService
|
||||
}
|
||||
GameRecordService::bootstrapPeriodWhenRuntimeEnabled();
|
||||
self::publishSnapshot(null);
|
||||
Log::info('game live startup recovered abnormal period', [
|
||||
Log::info('game live startup marked abnormal and refunded', [
|
||||
'record_id' => $recordId,
|
||||
'old_status' => $freshStatus,
|
||||
'refunded_user_count' => count($refund['user_ids']),
|
||||
@@ -141,6 +216,34 @@ final class GameLiveService
|
||||
}
|
||||
}
|
||||
|
||||
private static function finalizePayoutForRecordLocked(int $recordId): void
|
||||
{
|
||||
$now = time();
|
||||
Db::startTrans();
|
||||
try {
|
||||
Db::name('game_record')->where('id', $recordId)->where('status', 3)->update([
|
||||
'status' => 4,
|
||||
'payout_until' => null,
|
||||
'update_time' => $now,
|
||||
]);
|
||||
GameRecordService::createNextRecordAfterDraw();
|
||||
Db::commit();
|
||||
} catch (Throwable $e) {
|
||||
Db::rollback();
|
||||
Log::warning('game live startup finalize payout failed', [
|
||||
'record_id' => $recordId,
|
||||
'error' => $e->getMessage(),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
GameHotDataCoordinator::afterGameRecordCommitted($recordId);
|
||||
try {
|
||||
GameRecordStatService::refreshForRecordId($recordId);
|
||||
} catch (Throwable) {
|
||||
}
|
||||
self::publishSnapshot(null);
|
||||
}
|
||||
|
||||
public static function buildSnapshot(?int $recordId = null): array
|
||||
{
|
||||
$record = self::resolveRecord($recordId);
|
||||
@@ -170,9 +273,12 @@ final class GameLiveService
|
||||
}
|
||||
|
||||
$bets = Db::name('bet_order')
|
||||
->where('period_id', $rid)
|
||||
->order('id', 'desc')
|
||||
->alias('bo')
|
||||
->leftJoin('user gu', 'gu.id = bo.user_id')
|
||||
->where('bo.period_id', $rid)
|
||||
->order('bo.id', 'desc')
|
||||
->limit(200)
|
||||
->field('bo.id,bo.user_id,bo.period_no,bo.pick_numbers,bo.total_amount,bo.streak_at_bet,bo.create_time,gu.username as user_username')
|
||||
->select()
|
||||
->toArray();
|
||||
|
||||
@@ -218,6 +324,7 @@ final class GameLiveService
|
||||
return [
|
||||
'id' => (int) $row['id'],
|
||||
'user_id' => (int) $row['user_id'],
|
||||
'username' => isset($row['user_username']) && is_string($row['user_username']) ? $row['user_username'] : '',
|
||||
'period_no' => (string) $row['period_no'],
|
||||
'pick_numbers' => $row['pick_numbers'],
|
||||
'total_amount' => (string) $row['total_amount'],
|
||||
|
||||
Reference in New Issue
Block a user