diff --git a/app/Services/Draw/DrawPublishService.php b/app/Services/Draw/DrawPublishService.php index bece2c9..dcc9d28 100644 --- a/app/Services/Draw/DrawPublishService.php +++ b/app/Services/Draw/DrawPublishService.php @@ -7,6 +7,7 @@ use App\Models\AdminUser; use App\Lottery\DrawStatus; use App\Models\DrawResultBatch; use Illuminate\Support\Facades\DB; +use App\Services\LotterySettings; use App\Lottery\DrawResultBatchStatus; /** @@ -67,7 +68,10 @@ final class DrawPublishService private function applyPublishedToDraw(Draw $draw, DrawResultBatch $batch): Draw { - $cooldownMinutes = (int) config('lottery.draw.cooldown_minutes', 15); + $cooldownMinutes = max(0, (int) LotterySettings::get( + 'draw.cooldown_minutes', + (int) config('lottery.draw.cooldown_minutes', 15), + )); if ($cooldownMinutes > 0) { $draw->forceFill([ 'status' => DrawStatus::Cooldown->value, diff --git a/app/Services/Draw/DrawRngRunner.php b/app/Services/Draw/DrawRngRunner.php index f879882..0906aea 100644 --- a/app/Services/Draw/DrawRngRunner.php +++ b/app/Services/Draw/DrawRngRunner.php @@ -8,6 +8,7 @@ use App\Lottery\DrawStatus; use App\Models\DrawResultItem; use App\Models\DrawResultBatch; use Illuminate\Support\Facades\DB; +use App\Services\LotterySettings; use App\Lottery\DrawResultSourceType; use App\Lottery\DrawResultBatchStatus; @@ -27,7 +28,10 @@ final class DrawRngRunner 'status' => DrawStatus::Drawing->value, ])->save(); - $manualReview = (bool) config('lottery.draw.require_manual_review', false); + $manualReview = (bool) LotterySettings::get( + 'draw.require_manual_review', + (bool) config('lottery.draw.require_manual_review', false), + ); $seedMaterial = bin2hex(random_bytes(32)); $rngSeedHash = hash('sha256', $seedMaterial); diff --git a/database/seeders/LotterySettingsSeeder.php b/database/seeders/LotterySettingsSeeder.php index 32df2fb..eb3cd28 100644 --- a/database/seeders/LotterySettingsSeeder.php +++ b/database/seeders/LotterySettingsSeeder.php @@ -61,6 +61,20 @@ final class LotterySettingsSeeder extends Seeder '客户端展示用短名称(示例)', ); + LotterySettings::put( + 'draw.require_manual_review', + false, + 'draw', + 'RNG 开奖后是否必须进入人工审核;false 时系统直接发布结果', + ); + + LotterySettings::put( + 'draw.cooldown_minutes', + 15, + 'draw', + '开奖结果发布后的冷静期分钟数;0 表示直接进入结算态', + ); + LotterySettings::put( 'settlement.auto_run_on_tick', true, diff --git a/tests/Feature/DrawPipelineTest.php b/tests/Feature/DrawPipelineTest.php index 1861ae8..a79662a 100644 --- a/tests/Feature/DrawPipelineTest.php +++ b/tests/Feature/DrawPipelineTest.php @@ -10,6 +10,7 @@ use App\Models\DrawResultBatch; use App\Models\SettlementBatch; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Event; +use App\Services\LotterySettings; use App\Events\DrawCountdownBroadcast; use App\Lottery\DrawResultBatchStatus; use App\Services\Draw\DrawTickService; @@ -225,6 +226,7 @@ test('draw tick moves open draw to closing when close_time passed before draw_ti test('draw tick rng publishes result when manual review disabled', function (): void { config(['lottery.draw.require_manual_review' => false]); + LotterySettings::put('draw.require_manual_review', false, 'draw', 'RNG 开奖后是否必须进入人工审核'); Carbon::setTestNow(Carbon::parse('2026-05-09 14:05:00', 'UTC')); $drawTime = now()->copy()->subMinute(); @@ -261,6 +263,7 @@ test('draw tick rng publishes result when manual review disabled', function (): test('draw tick rng awaits manual publish when review enabled', function (): void { config(['lottery.draw.require_manual_review' => true]); + LotterySettings::put('draw.require_manual_review', true, 'draw', 'RNG 开奖后是否必须进入人工审核'); Carbon::setTestNow(Carbon::parse('2026-05-09 14:06:00', 'UTC')); $drawTime = now()->copy()->subMinute(); @@ -513,6 +516,8 @@ test('cooldown expiry tick moves draw to settling', function (): void { 'lottery.draw.require_manual_review' => false, 'lottery.draw.cooldown_minutes' => 15, ]); + LotterySettings::put('draw.require_manual_review', false, 'draw', 'RNG 开奖后是否必须进入人工审核'); + LotterySettings::put('draw.cooldown_minutes', 15, 'draw', '开奖结果发布后的冷静期分钟数'); Carbon::setTestNow(Carbon::parse('2026-05-09 14:07:00', 'UTC')); $drawTime = now()->copy()->subMinute(); @@ -549,6 +554,45 @@ test('cooldown expiry tick moves draw to settling', function (): void { Carbon::setTestNow(); }); +test('draw tick uses lottery settings to bypass manual review and cooldown', function (): void { + config([ + 'lottery.draw.require_manual_review' => true, + 'lottery.draw.cooldown_minutes' => 15, + ]); + LotterySettings::put('draw.require_manual_review', false, 'draw', 'RNG 开奖后是否必须进入人工审核'); + LotterySettings::put('draw.cooldown_minutes', 0, 'draw', '开奖结果发布后的冷静期分钟数'); + Carbon::setTestNow(Carbon::parse('2026-05-09 14:08:00', 'UTC')); + + $drawTime = now()->copy()->subMinute(); + $closeTime = $drawTime->copy()->subSeconds(30); + + Draw::query()->create([ + 'draw_no' => '20260509-778', + 'business_date' => '2026-05-09', + 'sequence_no' => 778, + 'status' => DrawStatus::Open->value, + 'start_time' => $closeTime->copy()->subMinutes(10), + 'close_time' => $closeTime, + 'draw_time' => $drawTime, + 'cooling_end_time' => null, + 'result_source' => null, + 'current_result_version' => 0, + 'settle_version' => 0, + 'is_reopened' => false, + ]); + + app(DrawTickService::class)->tick(now()->utc()); + + $draw = Draw::query()->where('draw_no', '20260509-778')->firstOrFail(); + expect($draw->status)->toBe(DrawStatus::Settling->value); + expect($draw->cooling_end_time)->toBeNull(); + + $batch = DrawResultBatch::query()->where('draw_id', $draw->id)->firstOrFail(); + expect($batch->status)->toBe(DrawResultBatchStatus::Published->value); + + Carbon::setTestNow(); +}); + test('GET draw current returns open draw with seconds to close', function (): void { Carbon::setTestNow(Carbon::parse('2026-05-09 15:00:00', 'UTC'));