From 8ccf39dff58e556a5e7db7e00637ff924296cb46 Mon Sep 17 00:00:00 2001 From: kang Date: Thu, 28 May 2026 14:50:25 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E8=BF=81=E7=A7=BB=E5=BD=A9?= =?UTF-8?q?=E7=A5=A8=E8=AE=BE=E7=BD=AE=E8=87=B3=20LotterySettings=20?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新多个控制器和服务,使用 LotterySettings 服务获取彩票相关配置,如默认币种、开奖间隔、下注窗口等,提升代码一致性与可维护性。 - 移除 .env.example 中不再使用的配置项,建议通过后台管理进行设置。 --- .env.example | 12 +-- .../LotteryPerfDrawScheduleAuditCommand.php | 3 +- .../AdminCurrencyDestroyController.php | 3 +- .../Admin/Draw/AdminDrawIndexController.php | 9 ++- .../Controllers/Api/V1/HealthController.php | 3 +- .../V1/Jackpot/JackpotSummaryController.php | 3 +- .../Admin/AdminReportQueryService.php | 12 ++- app/Services/Draw/DrawHallSnapshotBuilder.php | 5 +- app/Services/Draw/DrawManualCreateService.php | 3 +- app/Services/Draw/DrawManualUpdateService.php | 3 +- app/Services/Draw/DrawPlannerService.php | 7 +- app/Services/Draw/DrawPublishService.php | 5 +- app/Services/Draw/DrawResultViewService.php | 3 +- app/Services/Draw/DrawRngRunner.php | 5 +- app/Services/Draw/DrawTimelineBuilder.php | 5 +- app/Services/LotterySettings.php | 78 +++++++++++++++++++ app/Services/PlayerTokenResolver.php | 2 +- app/Support/CurrencyFormatter.php | 8 +- app/Support/CurrencyResolver.php | 3 +- app/Support/TicketItemListFilters.php | 3 +- 20 files changed, 131 insertions(+), 44 deletions(-) diff --git a/.env.example b/.env.example index 3d953aa..361be08 100644 --- a/.env.example +++ b/.env.example @@ -206,21 +206,13 @@ VITE_APP_NAME="${APP_NAME}" # 彩票业务(config/lottery.php、database/seeders;密钥仅写本机 .env) # ============================================================================= -# 默认结算币种(产品约定,如 NPR) -LOTTERY_DEFAULT_CURRENCY=NPR +# 运营项(默认币种、开奖间隔/窗口/封盘提前、预生成期数、金额格式)已迁移后台配置 lottery_settings, +# 建议在 /admin/settings 管理;此处不再提供对应 env 键。 # lottery_settings 表读缓存 TTL(秒);调小更易立即看到后台改值,调大减库压 LOTTERY_SETTINGS_CACHE_TTL=60 # 开发绕过:Authorization: Bearer dev:{players.id};仅当 APP_ENV 为 local 或 testing 且为 true 时生效(PHPUnit 依赖 testing),生产务必 false LOTTERY_PLAYER_AUTH_DEV_BYPASS=false -# 未来期缓冲条数(draw_time>now 的期数,分钟 tick 会补足);测试可 6–12,生产可 48+ -LOTTERY_DRAW_BUFFER_AHEAD=8 -# 期号时刻统一为 UTC(GMT),见 config/lottery.php lottery.draw.timezone 与 docs/01-界面文档.md;勿配置本地时区 -# 开奖间隔(分钟)、下注窗(秒)、封盘提前(秒)见 config/lottery.php,可按需覆盖: -# LOTTERY_DRAW_INTERVAL_MINUTES=5 -# LOTTERY_DRAW_BETTING_WINDOW_SECONDS=270 -# LOTTERY_DRAW_CLOSE_BEFORE_SECONDS=30 - # 校验主站 JWT 的算法(与签发方一致) LOTTERY_JWT_ALGORITHM=HS256 # JWT 内表示站点编码的 claim 名 diff --git a/app/Console/Commands/LotteryPerfDrawScheduleAuditCommand.php b/app/Console/Commands/LotteryPerfDrawScheduleAuditCommand.php index 801fe0b..26f1c7a 100644 --- a/app/Console/Commands/LotteryPerfDrawScheduleAuditCommand.php +++ b/app/Console/Commands/LotteryPerfDrawScheduleAuditCommand.php @@ -5,6 +5,7 @@ namespace App\Console\Commands; use App\Models\Draw; use Carbon\Carbon; use Illuminate\Console\Command; +use App\Services\LotterySettings; use App\Services\Draw\DrawPlannerService; /** @@ -22,7 +23,7 @@ final class LotteryPerfDrawScheduleAuditCommand extends Command { $samples = max(2, (int) $this->option('samples')); $tolerance = max(0, (int) $this->option('tolerance-seconds')); - $intervalMinutes = (int) config('lottery.draw.interval_minutes', 5); + $intervalMinutes = LotterySettings::drawIntervalMinutes(); $expectedSeconds = $intervalMinutes * 60; $planner->ensureBuffer(Carbon::now('UTC')); diff --git a/app/Http/Controllers/Api/V1/Admin/Currency/AdminCurrencyDestroyController.php b/app/Http/Controllers/Api/V1/Admin/Currency/AdminCurrencyDestroyController.php index a382236..5c86132 100644 --- a/app/Http/Controllers/Api/V1/Admin/Currency/AdminCurrencyDestroyController.php +++ b/app/Http/Controllers/Api/V1/Admin/Currency/AdminCurrencyDestroyController.php @@ -7,6 +7,7 @@ use App\Lottery\ErrorCode; use App\Support\ApiResponse; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\DB; +use App\Services\LotterySettings; use App\Http\Controllers\Controller; final class AdminCurrencyDestroyController extends Controller @@ -15,7 +16,7 @@ final class AdminCurrencyDestroyController extends Controller { $code = strtoupper((string) $currency->code); - if ($code === strtoupper((string) config('lottery.default_currency', 'NPR'))) { + if ($code === LotterySettings::defaultCurrency()) { return ApiResponse::error( '默认币种不可删除', ErrorCode::ValidationFailed->value, diff --git a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawIndexController.php b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawIndexController.php index cda0371..999811b 100644 --- a/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawIndexController.php +++ b/app/Http/Controllers/Api/V1/Admin/Draw/AdminDrawIndexController.php @@ -8,6 +8,7 @@ use App\Models\TicketItem; use App\Models\TicketOrder; use Illuminate\Http\Request; use App\Support\AdminApiList; +use App\Services\LotterySettings; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; use Illuminate\Contracts\Pagination\LengthAwarePaginator; @@ -38,10 +39,10 @@ final class AdminDrawIndexController extends Controller return AdminApiList::jsonWith($paginator, fn (Draw $row) => $this->row($row), [ 'schedule' => [ - 'timezone' => (string) config('lottery.draw.timezone', 'UTC'), - 'interval_minutes' => (int) config('lottery.draw.interval_minutes', 5), - 'betting_window_seconds' => (int) config('lottery.draw.betting_window_seconds', 270), - 'close_before_draw_seconds' => (int) config('lottery.draw.close_before_draw_seconds', 30), + 'timezone' => LotterySettings::drawTimezone(), + 'interval_minutes' => LotterySettings::drawIntervalMinutes(), + 'betting_window_seconds' => LotterySettings::drawBettingWindowSeconds(), + 'close_before_draw_seconds' => LotterySettings::drawCloseBeforeDrawSeconds(), ], ]); } diff --git a/app/Http/Controllers/Api/V1/HealthController.php b/app/Http/Controllers/Api/V1/HealthController.php index d7d62e1..d43e1e7 100644 --- a/app/Http/Controllers/Api/V1/HealthController.php +++ b/app/Http/Controllers/Api/V1/HealthController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api\V1; use App\Support\ApiResponse; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; +use App\Services\LotterySettings; final class HealthController extends Controller { @@ -17,7 +18,7 @@ final class HealthController extends Controller { $payload = [ 'app' => config('app.name'), - 'default_currency' => config('lottery.default_currency'), + 'default_currency' => LotterySettings::defaultCurrency(), ]; if (config('app.debug')) { diff --git a/app/Http/Controllers/Api/V1/Jackpot/JackpotSummaryController.php b/app/Http/Controllers/Api/V1/Jackpot/JackpotSummaryController.php index f4cafdd..ebee81a 100644 --- a/app/Http/Controllers/Api/V1/Jackpot/JackpotSummaryController.php +++ b/app/Http/Controllers/Api/V1/Jackpot/JackpotSummaryController.php @@ -6,6 +6,7 @@ use App\Support\ApiResponse; use Illuminate\Http\Request; use Illuminate\Http\JsonResponse; use App\Http\Controllers\Controller; +use App\Services\LotterySettings; use App\Services\Jackpot\JackpotSummaryService; /** @@ -21,7 +22,7 @@ final class JackpotSummaryController extends Controller { $currencyCode = strtoupper(trim((string) $request->query( 'currency_code', - (string) config('lottery.default_currency', 'NPR'), + LotterySettings::defaultCurrency(), ))); return ApiResponse::success($this->summary->summary($currencyCode)); diff --git a/app/Services/Admin/AdminReportQueryService.php b/app/Services/Admin/AdminReportQueryService.php index 4f2e3bb..9b71ede 100644 --- a/app/Services/Admin/AdminReportQueryService.php +++ b/app/Services/Admin/AdminReportQueryService.php @@ -26,8 +26,16 @@ final class AdminReportQueryService */ public function resolveDateRange(?array $filters): array { - $dateFrom = (string) ($filters['date_from'] ?? now()->toDateString()); - $dateTo = (string) ($filters['date_to'] ?? $dateFrom); + $fromRaw = trim((string) ($filters['date_from'] ?? '')); + $toRaw = trim((string) ($filters['date_to'] ?? '')); + + // 未传日期时按历史全量范围导出/查询,避免默认“仅今天”导致空数据。 + if ($fromRaw === '' && $toRaw === '') { + return $this->lifetimeBusinessDateBounds(); + } + + $dateFrom = $fromRaw !== '' ? $fromRaw : $toRaw; + $dateTo = $toRaw !== '' ? $toRaw : $dateFrom; if ($dateFrom > $dateTo) { [$dateFrom, $dateTo] = [$dateTo, $dateFrom]; diff --git a/app/Services/Draw/DrawHallSnapshotBuilder.php b/app/Services/Draw/DrawHallSnapshotBuilder.php index 97f8727..1399de7 100644 --- a/app/Services/Draw/DrawHallSnapshotBuilder.php +++ b/app/Services/Draw/DrawHallSnapshotBuilder.php @@ -10,6 +10,7 @@ use App\Models\DrawResultItem; use App\Models\DrawResultBatch; use App\Lottery\DrawResultBatchStatus; use App\Services\Jackpot\JackpotSummaryService; +use App\Services\LotterySettings; /** * `GET draw/current` 与大厅 WS 快照共用数据结构。 @@ -237,7 +238,7 @@ final class DrawHallSnapshotBuilder $effectiveStatus = $this->effectiveHallDisplayStatus($target, $nowUtc); - $scheduleTz = (string) config('lottery.draw.timezone', 'UTC'); + $scheduleTz = LotterySettings::drawTimezone(); $payload = [ 'schedule_timezone' => $scheduleTz, @@ -324,6 +325,6 @@ final class DrawHallSnapshotBuilder return $code; } - return strtoupper(substr(trim((string) config('lottery.default_currency', 'NPR')), 0, 16)); + return LotterySettings::defaultCurrency(); } } diff --git a/app/Services/Draw/DrawManualCreateService.php b/app/Services/Draw/DrawManualCreateService.php index cd41905..d60ea29 100644 --- a/app/Services/Draw/DrawManualCreateService.php +++ b/app/Services/Draw/DrawManualCreateService.php @@ -4,6 +4,7 @@ namespace App\Services\Draw; use Carbon\Carbon; use App\Models\Draw; +use App\Services\LotterySettings; use Illuminate\Support\Facades\DB; /** @@ -27,7 +28,7 @@ final class DrawManualCreateService */ public function create(array $input, ?Carbon $now = null): Draw { - $tz = (string) config('lottery.draw.timezone', 'UTC'); + $tz = LotterySettings::drawTimezone(); $nowUtc = ($now ?? Carbon::now())->utc(); $drawLocal = $this->parseInTimezone((string) $input['draw_time'], $tz); diff --git a/app/Services/Draw/DrawManualUpdateService.php b/app/Services/Draw/DrawManualUpdateService.php index a98814d..babf8d3 100644 --- a/app/Services/Draw/DrawManualUpdateService.php +++ b/app/Services/Draw/DrawManualUpdateService.php @@ -6,6 +6,7 @@ use Carbon\Carbon; use App\Models\Draw; use App\Models\TicketOrder; use App\Lottery\DrawStatus; +use App\Services\LotterySettings; use Illuminate\Support\Facades\DB; /** @@ -29,7 +30,7 @@ final class DrawManualUpdateService */ public function update(Draw $draw, array $input, ?Carbon $now = null): Draw { - $tz = (string) config('lottery.draw.timezone', 'UTC'); + $tz = LotterySettings::drawTimezone(); $nowUtc = ($now ?? Carbon::now())->utc(); return DB::transaction(function () use ($draw, $input, $tz, $nowUtc): Draw { diff --git a/app/Services/Draw/DrawPlannerService.php b/app/Services/Draw/DrawPlannerService.php index 572cff4..b8d2559 100644 --- a/app/Services/Draw/DrawPlannerService.php +++ b/app/Services/Draw/DrawPlannerService.php @@ -5,6 +5,7 @@ namespace App\Services\Draw; use Carbon\Carbon; use App\Models\Draw; use App\Lottery\DrawStatus; +use App\Services\LotterySettings; use Illuminate\Support\Facades\DB; use Illuminate\Database\QueryException; @@ -21,9 +22,9 @@ final class DrawPlannerService public function ensureBuffer(?Carbon $now = null): array { $nowUtc = ($now ?? Carbon::now())->utc(); - $tz = (string) config('lottery.draw.timezone', 'UTC'); - $interval = (int) config('lottery.draw.interval_minutes', 5); - $buffer = (int) config('lottery.draw.buffer_draws_ahead', 8); + $tz = LotterySettings::drawTimezone(); + $interval = LotterySettings::drawIntervalMinutes(); + $buffer = LotterySettings::drawBufferDrawsAhead(); $maxSeq = intdiv(24 * 60, $interval); $upcoming = Draw::query() diff --git a/app/Services/Draw/DrawPublishService.php b/app/Services/Draw/DrawPublishService.php index 2d86508..40d443a 100644 --- a/app/Services/Draw/DrawPublishService.php +++ b/app/Services/Draw/DrawPublishService.php @@ -79,10 +79,7 @@ final class DrawPublishService private function applyPublishedToDraw(Draw $draw, DrawResultBatch $batch): Draw { - $cooldownMinutes = max(0, (int) LotterySettings::get( - 'draw.cooldown_minutes', - (int) config('lottery.draw.cooldown_minutes', 15), - )); + $cooldownMinutes = LotterySettings::drawCooldownMinutes(); if ($cooldownMinutes > 0) { $draw->forceFill([ 'status' => DrawStatus::Cooldown->value, diff --git a/app/Services/Draw/DrawResultViewService.php b/app/Services/Draw/DrawResultViewService.php index ab3d4e0..dfe86e0 100644 --- a/app/Services/Draw/DrawResultViewService.php +++ b/app/Services/Draw/DrawResultViewService.php @@ -9,6 +9,7 @@ use App\Models\DrawResultBatch; use Illuminate\Support\Collection; use App\Lottery\DrawResultBatchStatus; use App\Services\Jackpot\JackpotSummaryService; +use App\Services\LotterySettings; use Illuminate\Contracts\Pagination\LengthAwarePaginator; /** @@ -194,6 +195,6 @@ final class DrawResultViewService return $code; } - return strtoupper(substr(trim((string) config('lottery.default_currency', 'NPR')), 0, 16)); + return LotterySettings::defaultCurrency(); } } diff --git a/app/Services/Draw/DrawRngRunner.php b/app/Services/Draw/DrawRngRunner.php index 363868c..594f2a5 100644 --- a/app/Services/Draw/DrawRngRunner.php +++ b/app/Services/Draw/DrawRngRunner.php @@ -28,10 +28,7 @@ final class DrawRngRunner 'status' => DrawStatus::Drawing->value, ])->save(); - $manualReview = (bool) LotterySettings::get( - 'draw.require_manual_review', - (bool) config('lottery.draw.require_manual_review', false), - ); + $manualReview = LotterySettings::drawRequireManualReview(); $seedHex = DrawRngSeedDerivation::generateSeedHex(); $rngSeedHash = DrawRngSeedDerivation::hashSeedHex($seedHex); $rawSeedEncrypted = DrawRngSeedDerivation::encryptSeedHex($seedHex); diff --git a/app/Services/Draw/DrawTimelineBuilder.php b/app/Services/Draw/DrawTimelineBuilder.php index 9581d3c..39a2590 100644 --- a/app/Services/Draw/DrawTimelineBuilder.php +++ b/app/Services/Draw/DrawTimelineBuilder.php @@ -4,6 +4,7 @@ namespace App\Services\Draw; use Carbon\Carbon; use App\Lottery\DrawStatus; +use App\Services\LotterySettings; /** * 由开奖时刻推导下注窗口、封盘时刻与期号初始状态。 @@ -12,12 +13,12 @@ final class DrawTimelineBuilder { public function closeBeforeDrawSeconds(): int { - return (int) config('lottery.draw.close_before_draw_seconds', 30); + return LotterySettings::drawCloseBeforeDrawSeconds(); } public function bettingWindowSeconds(): int { - return (int) config('lottery.draw.betting_window_seconds', 270); + return LotterySettings::drawBettingWindowSeconds(); } /** diff --git a/app/Services/LotterySettings.php b/app/Services/LotterySettings.php index 4ad43d8..1807c08 100644 --- a/app/Services/LotterySettings.php +++ b/app/Services/LotterySettings.php @@ -13,6 +13,84 @@ use Illuminate\Support\Facades\Cache; */ final class LotterySettings { + public static function defaultCurrency(): string + { + $fallback = (string) config('lottery.default_currency', 'NPR'); + $value = self::get('currency.default_code', $fallback); + + return strtoupper(substr(trim((string) $value), 0, 16)); + } + + public static function drawTimezone(): string + { + return (string) self::get('draw.timezone', (string) config('lottery.draw.timezone', 'UTC')); + } + + public static function drawIntervalMinutes(): int + { + $fallback = (int) config('lottery.draw.interval_minutes', 5); + + return max(1, min(1440, (int) self::get('draw.interval_minutes', $fallback))); + } + + public static function drawBufferDrawsAhead(): int + { + $fallback = (int) config('lottery.draw.buffer_draws_ahead', 8); + + return max(1, (int) self::get('draw.buffer_draws_ahead', $fallback)); + } + + public static function drawBettingWindowSeconds(): int + { + $fallback = (int) config('lottery.draw.betting_window_seconds', 270); + + return max(10, (int) self::get('draw.betting_window_seconds', $fallback)); + } + + public static function drawCloseBeforeDrawSeconds(): int + { + $fallback = (int) config('lottery.draw.close_before_draw_seconds', 30); + + return max(5, (int) self::get('draw.close_before_draw_seconds', $fallback)); + } + + public static function drawRequireManualReview(): bool + { + $fallback = (bool) config('lottery.draw.require_manual_review', true); + + return (bool) self::get('draw.require_manual_review', $fallback); + } + + public static function drawCooldownMinutes(): int + { + $fallback = (int) config('lottery.draw.cooldown_minutes', 15); + + return max(0, (int) self::get('draw.cooldown_minutes', $fallback)); + } + + public static function currencyDisplayDecimals(): int + { + $fallback = (int) config('lottery.ui.format.currency.decimals', 2); + + return max(0, min(12, (int) self::get('currency.display_decimals', $fallback))); + } + + public static function currencyDecimalSeparator(): string + { + return (string) self::get( + 'currency.decimal_separator', + (string) config('lottery.ui.format.currency.decimal_separator', '.') + ); + } + + public static function currencyThousandsSeparator(): string + { + return (string) self::get( + 'currency.thousands_separator', + (string) config('lottery.ui.format.currency.thousands_separator', ',') + ); + } + public static function cacheTtlSeconds(): int { return max(5, (int) config('lottery.settings.cache_ttl_seconds', 60)); diff --git a/app/Services/PlayerTokenResolver.php b/app/Services/PlayerTokenResolver.php index cf880fd..f8d84c0 100644 --- a/app/Services/PlayerTokenResolver.php +++ b/app/Services/PlayerTokenResolver.php @@ -178,7 +178,7 @@ final class PlayerTokenResolver $defaults = [ 'username' => null, 'nickname' => null, - 'default_currency' => (string) config('lottery.default_currency', 'NPR'), + 'default_currency' => LotterySettings::defaultCurrency(), 'status' => self::PLAYER_STATUS_ACTIVE, 'last_login_at' => $now, ]; diff --git a/app/Support/CurrencyFormatter.php b/app/Support/CurrencyFormatter.php index 7f1313d..f41af21 100644 --- a/app/Support/CurrencyFormatter.php +++ b/app/Support/CurrencyFormatter.php @@ -4,6 +4,8 @@ declare(strict_types=1); namespace App\Support; +use App\Services\LotterySettings; + /** * 将「最小货币单位」整数格式化为展示用字符串(不改变业务数字,仅格式化)。 * @@ -22,9 +24,9 @@ final class CurrencyFormatter private static function formatMinorInt(int $minorUnits): string { - $decimals = max(0, min(12, (int) config('lottery.ui.format.currency.decimals', 2))); - $decSep = (string) config('lottery.ui.format.currency.decimal_separator', '.'); - $thousandsSep = (string) config('lottery.ui.format.currency.thousands_separator', ','); + $decimals = LotterySettings::currencyDisplayDecimals(); + $decSep = LotterySettings::currencyDecimalSeparator(); + $thousandsSep = LotterySettings::currencyThousandsSeparator(); $divisor = (int) max(1, 10 ** $decimals); $negative = $minorUnits < 0; diff --git a/app/Support/CurrencyResolver.php b/app/Support/CurrencyResolver.php index 1c018f7..d32eb3b 100644 --- a/app/Support/CurrencyResolver.php +++ b/app/Support/CurrencyResolver.php @@ -7,6 +7,7 @@ namespace App\Support; use App\Models\Player; use App\Models\Currency; use Illuminate\Http\Request; +use App\Services\LotterySettings; /** * 币种码解析工具。 @@ -36,7 +37,7 @@ final class CurrencyResolver } else { $fallback = $default ?? $player?->default_currency - ?? config('lottery.default_currency', 'NPR'); + ?? LotterySettings::defaultCurrency(); $code = strtoupper(substr(trim((string) $fallback), 0, 16)); } diff --git a/app/Support/TicketItemListFilters.php b/app/Support/TicketItemListFilters.php index 375a7a8..8a7461f 100644 --- a/app/Support/TicketItemListFilters.php +++ b/app/Support/TicketItemListFilters.php @@ -3,6 +3,7 @@ namespace App\Support; use Carbon\Carbon; +use App\Services\LotterySettings; use Illuminate\Database\Eloquent\Builder; /** @@ -40,7 +41,7 @@ trait TicketItemListFilters private function scheduleTimezone(): string { - return (string) config('lottery.draw.timezone', 'UTC'); + return LotterySettings::drawTimezone(); } private function scheduleDateStartUtc(string $ymd): Carbon