diff --git a/app/common/service/GamePeriodService.php b/app/common/service/GamePeriodService.php new file mode 100644 index 0000000..fd679cd --- /dev/null +++ b/app/common/service/GamePeriodService.php @@ -0,0 +1,148 @@ +where('config_key', $key)->find(); + if (!$row) { + return false; + } + $v = $row['config_value'] ?? ''; + return $v === '1' || $v === 1; + } + + /** + * @return array{period_auto_create_enabled: int, period_manual_create_enabled: int} + */ + public static function getPeriodSettings(): array + { + return [ + 'period_auto_create_enabled' => self::getConfigBool(self::KEY_AUTO_CREATE) ? 1 : 0, + 'period_manual_create_enabled' => self::getConfigBool(self::KEY_MANUAL_CREATE) ? 1 : 0, + ]; + } + + /** + * @param array{period_auto_create_enabled?: int|string, period_manual_create_enabled?: int|string} $data + */ + public static function savePeriodSettings(array $data): void + { + $now = time(); + $auto = self::truthyConfigInput($data['period_auto_create_enabled'] ?? null) ? '1' : '0'; + $manual = self::truthyConfigInput($data['period_manual_create_enabled'] ?? null) ? '1' : '0'; + self::upsertConfig(self::KEY_AUTO_CREATE, $auto, 'int', '是否允许定时任务自动创建下一期(全局仅一局)', $now); + self::upsertConfig(self::KEY_MANUAL_CREATE, $manual, 'int', '是否允许后台手动创建下一期', $now); + } + + public static function hasActivePeriod(): bool + { + $count = Db::name('game_period')->whereIn('status', self::ACTIVE_STATUSES)->count(); + return $count > 0; + } + + /** + * 自动开奖任务调用:开启自动创建且无进行中期号时插入新期 + */ + public static function tickAutoCreate(): void + { + if (!self::getConfigBool(self::KEY_AUTO_CREATE)) { + return; + } + if (self::hasActivePeriod()) { + return; + } + try { + self::createNextPeriodRow(); + } catch (Throwable) { + // 并发下可能重复,忽略 + } + } + + /** + * @return array{ok: bool, msg: string, period_no?: string} + */ + public static function createNextPeriodForManual(): array + { + if (!self::getConfigBool(self::KEY_MANUAL_CREATE)) { + return ['ok' => false, 'msg' => '未开启「手动创建下一期」开关']; + } + if (self::hasActivePeriod()) { + return ['ok' => false, 'msg' => '存在未结束期号,无法新建']; + } + try { + $periodNo = self::createNextPeriodRow(); + return ['ok' => true, 'msg' => '已创建新期', 'period_no' => $periodNo]; + } catch (Throwable $e) { + return ['ok' => false, 'msg' => $e->getMessage()]; + } + } + + /** + * @throws Throwable + */ + private static function createNextPeriodRow(): string + { + $periodNo = self::generatePeriodNo(); + $now = time(); + Db::name('game_period')->insert([ + 'period_no' => $periodNo, + 'period_start_at' => $now, + 'status' => 0, + 'draw_mode' => 0, + 'void_reason' => '', + 'create_time' => $now, + 'update_time' => $now, + ]); + return $periodNo; + } + + private static function generatePeriodNo(): string + { + return date('Ymd-His') . '-' . substr(bin2hex(random_bytes(4)), 0, 8); + } + + private static function truthyConfigInput(mixed $v): bool + { + return $v === 1 || $v === '1' || $v === true; + } + + private static function upsertConfig(string $key, string $value, string $valueType, string $remark, int $now): void + { + $exists = Db::name('game_config')->where('config_key', $key)->find(); + if ($exists) { + Db::name('game_config')->where('config_key', $key)->update([ + 'config_value' => $value, + 'value_type' => $valueType, + 'remark' => $remark, + 'update_time' => $now, + ]); + return; + } + Db::name('game_config')->insert([ + 'config_key' => $key, + 'config_value' => $value, + 'value_type' => $valueType, + 'remark' => $remark, + 'create_time' => $now, + 'update_time' => $now, + ]); + } +} diff --git a/app/process/GamePeriodAutoTicker.php b/app/process/GamePeriodAutoTicker.php new file mode 100644 index 0000000..9fc089a --- /dev/null +++ b/app/process/GamePeriodAutoTicker.php @@ -0,0 +1,19 @@ + public_path() ] ], + // 游戏期号:自动创建下一期(依赖 game_config.period_auto_create_enabled) + 'gamePeriodAutoTicker' => [ + 'handler' => app\process\GamePeriodAutoTicker::class, + 'count' => 1, + 'reloadable' => false, + ], + // File update detection and automatic reload 'monitor' => [ 'handler' => app\process\Monitor::class,