1.修复关闭自动创建下一局后还自动创建下一期
This commit is contained in:
@@ -261,6 +261,7 @@ final class GameLiveService
|
||||
}
|
||||
if (!GameRecordService::isLiveRuntimeEnabled()) {
|
||||
self::voidRemainingOpenRoundsOnMaintenanceAfterFinalize();
|
||||
GameHotDataRedis::gameRecordRefreshAggregateCaches();
|
||||
} elseif (is_string($newPeriodNo ?? null) && $newPeriodNo !== '') {
|
||||
self::publishImmediateBettingTickAfterFinalize();
|
||||
}
|
||||
@@ -716,6 +717,7 @@ final class GameLiveService
|
||||
self::publishSnapshot(null);
|
||||
if (!GameRecordService::isLiveRuntimeEnabled()) {
|
||||
self::voidRemainingOpenRoundsOnMaintenanceAfterFinalize();
|
||||
GameHotDataRedis::gameRecordRefreshAggregateCaches();
|
||||
} elseif (is_string($newPeriodNo ?? null) && $newPeriodNo !== '') {
|
||||
self::publishImmediateBettingTickAfterFinalize();
|
||||
}
|
||||
@@ -726,6 +728,12 @@ final class GameLiveService
|
||||
|
||||
public static function tickAutoDraw(): void
|
||||
{
|
||||
if (!GameRecordService::isAutoCreateEnabled()) {
|
||||
$openCount = (int) Db::name('game_record')->whereIn('status', [0, 1])->count();
|
||||
if ($openCount <= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
$record = self::resolveRecord(null);
|
||||
if (!$record || !in_array((int) $record['status'], [0, 1], true)) {
|
||||
return;
|
||||
@@ -801,6 +809,7 @@ final class GameLiveService
|
||||
self::voidOpenPeriodInternal($rid, $reason);
|
||||
}
|
||||
GameHotDataRedis::gameRecordRefreshAggregateCaches();
|
||||
self::publishSnapshot(null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1145,6 +1154,9 @@ final class GameLiveService
|
||||
if (!self::shouldPublishPeriodTick($status, $periodNo)) {
|
||||
return;
|
||||
}
|
||||
if (empty($snapshot['runtime_enabled']) && in_array($status, ['betting', 'locked'], true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GameWebSocketEventBus::publish(self::EVT_PERIOD_TICK, $payload);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,31 @@ final class GameRecordService
|
||||
return false;
|
||||
}
|
||||
$v = $row['config_value'] ?? '';
|
||||
return self::truthyConfigValue($v);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否允许自动创建下一局(读库,避免 Redis 缓存仍为 1 时误开新期)。
|
||||
*/
|
||||
public static function isAutoCreateEnabled(): bool
|
||||
{
|
||||
return self::getConfigBoolFromDb(self::KEY_AUTO_CREATE);
|
||||
}
|
||||
|
||||
private static function getConfigBoolFromDb(string $key): bool
|
||||
{
|
||||
if ($key === '') {
|
||||
return false;
|
||||
}
|
||||
$row = Db::name('game_config')->where('config_key', $key)->find();
|
||||
if (!$row) {
|
||||
return false;
|
||||
}
|
||||
return self::truthyConfigValue($row['config_value'] ?? '');
|
||||
}
|
||||
|
||||
private static function truthyConfigValue(mixed $v): bool
|
||||
{
|
||||
return $v === '1' || $v === 1;
|
||||
}
|
||||
|
||||
@@ -51,7 +76,7 @@ final class GameRecordService
|
||||
|
||||
public static function tickAutoCreate(): void
|
||||
{
|
||||
if (!self::getConfigBool(self::KEY_AUTO_CREATE)) {
|
||||
if (!self::isAutoCreateEnabled()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -78,10 +103,6 @@ final class GameRecordService
|
||||
|
||||
public static function createNextRecordAfterDraw(): ?string
|
||||
{
|
||||
// 派彩结束后是否自动开新局:由 period_auto_create_enabled 控制
|
||||
if (!self::getConfigBool(self::KEY_AUTO_CREATE)) {
|
||||
return null;
|
||||
}
|
||||
return self::createNextRecordRowIfNoActive();
|
||||
}
|
||||
|
||||
@@ -90,7 +111,7 @@ final class GameRecordService
|
||||
*/
|
||||
public static function isLiveRuntimeEnabled(): bool
|
||||
{
|
||||
return self::getConfigBool(self::KEY_AUTO_CREATE);
|
||||
return self::isAutoCreateEnabled();
|
||||
}
|
||||
|
||||
public static function setLiveRuntimeEnabled(bool $enabled): void
|
||||
@@ -103,7 +124,7 @@ final class GameRecordService
|
||||
*/
|
||||
public static function bootstrapPeriodWhenRuntimeEnabled(): void
|
||||
{
|
||||
if (!self::getConfigBool(self::KEY_AUTO_CREATE)) {
|
||||
if (!self::isAutoCreateEnabled()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -116,6 +137,7 @@ final class GameRecordService
|
||||
{
|
||||
$now = time();
|
||||
$v = $enabled ? '1' : '0';
|
||||
GameHotDataRedis::gameConfigForget(self::KEY_AUTO_CREATE);
|
||||
self::upsertConfig(self::KEY_AUTO_CREATE, $v, 'int', '是否允许自动创建下一局(全局仅一局)', $now);
|
||||
}
|
||||
|
||||
@@ -124,7 +146,7 @@ final class GameRecordService
|
||||
*/
|
||||
public static function createNextRecordRow(): string
|
||||
{
|
||||
$created = self::createNextRecordRowIfNoActive();
|
||||
$created = self::createNextRecordRowIfNoActive(false);
|
||||
if ($created === null) {
|
||||
throw new \RuntimeException((string) __('There is an unfinished round; cannot create a new one'));
|
||||
}
|
||||
@@ -134,9 +156,14 @@ final class GameRecordService
|
||||
|
||||
/**
|
||||
* 幂等插入下一期:有进行中局则不插入,返回 null。
|
||||
*
|
||||
* @param bool $requireAutoCreate true=须开启自动开局(定时/派彩后);false=手动创建下一期
|
||||
*/
|
||||
public static function createNextRecordRowIfNoActive(): ?string
|
||||
public static function createNextRecordRowIfNoActive(bool $requireAutoCreate = true): ?string
|
||||
{
|
||||
if ($requireAutoCreate && !self::isAutoCreateEnabled()) {
|
||||
return null;
|
||||
}
|
||||
$lock = GameHotDataLock::tryAcquireWithWait(GameHotDataLock::TYPE_GAME_RECORD, self::AUTO_CREATE_LOCK_KEY, 1500);
|
||||
if (!$lock['acquired']) {
|
||||
return null;
|
||||
|
||||
@@ -426,6 +426,11 @@ function handlePeriodTickEvent(periodData: anyObj): void {
|
||||
const status = typeof periodData.status === 'string' ? periodData.status : ''
|
||||
const periodNo = typeof periodData.period_no === 'string' ? periodData.period_no : ''
|
||||
const currentNo = typeof snapshot.record?.period_no === 'string' ? snapshot.record.period_no : ''
|
||||
const runtimeOff =
|
||||
toBool(snapshot.runtime_enabled) === false || toBool(periodData.runtime_enabled) === false
|
||||
if (runtimeOff && (status === 'betting' || status === 'locked')) {
|
||||
return
|
||||
}
|
||||
if (status === 'betting' && periodNo !== '' && periodNo !== currentNo) {
|
||||
void loadSnapshot({ force: true })
|
||||
return
|
||||
@@ -436,7 +441,7 @@ function handlePeriodTickEvent(periodData: anyObj): void {
|
||||
}
|
||||
return
|
||||
}
|
||||
if (currentNo === '' && periodNo !== '') {
|
||||
if (currentNo === '' && periodNo !== '' && !runtimeOff) {
|
||||
void loadSnapshot({ force: true })
|
||||
}
|
||||
}
|
||||
@@ -698,7 +703,13 @@ function mergeLiveSnapshot(data: anyObj): void {
|
||||
if (data.record !== undefined) {
|
||||
const nextId = data.record?.id != null ? Number(data.record.id) : null
|
||||
periodChanged = prevPeriodId !== null && nextId !== null && prevPeriodId !== nextId
|
||||
snapshot.record = data.record
|
||||
const runtimeOff = toBool(data.runtime_enabled) === false || toBool(snapshot.runtime_enabled) === false
|
||||
const serverMaintenance = data.maintenance_ui === true
|
||||
if (runtimeOff && serverMaintenance) {
|
||||
snapshot.record = null
|
||||
} else {
|
||||
snapshot.record = data.record
|
||||
}
|
||||
}
|
||||
|
||||
const incomingBets = Array.isArray(data.bets) ? data.bets : null
|
||||
@@ -835,9 +846,7 @@ async function loadSnapshot(options?: { force?: boolean }): Promise<void> {
|
||||
|
||||
async function onRuntimeSwitch(val: boolean | string | number): void {
|
||||
const on = toBool(val) === true
|
||||
// 防止某些场景下 model-value 变化触发重复 change 事件,造成 runtime 接口循环调用
|
||||
const current = toBool(snapshot.runtime_enabled) === true
|
||||
if (on === current) {
|
||||
if (runtimeSwitchLoading.value) {
|
||||
return
|
||||
}
|
||||
// el-switch 为受控组件(model-value 来自 snapshot),接口返回前先乐观更新,避免点击后立刻回弹
|
||||
|
||||
Reference in New Issue
Block a user