feat: 添加 Laravel Reverb 支持,更新 .env.example 文件以配置 WebSocket,增强彩票调度功能,更新 API 路由以支持期号管理与结果发布

This commit is contained in:
2026-05-09 17:40:49 +08:00
parent 781cf10928
commit aeaf124096
42 changed files with 3886 additions and 5 deletions

View File

@@ -0,0 +1,134 @@
<?php
namespace App\Services\Draw;
use App\Lottery\DrawStatus;
use App\Models\Draw;
use Carbon\Carbon;
/**
* 每分钟调度:期号状态推进 RNG若到期号 冷静期结束时进入结算态 补齐未来缓冲。
*
* @see 《04-领域字典》draw_status
*/
final class DrawTickService
{
public function __construct(
private readonly DrawPlannerService $planner,
private readonly DrawRngRunner $rng,
private readonly DrawHallSnapshotBuilder $hallSnapshot,
private readonly LotteryHallRealtimeBroadcaster $hallRealtime,
) {}
/**
* @return array{
* status_updates: array<string, int>,
* rng_rung: int,
* rng_errors: array<int, string>,
* planned: array<string, int>
* }
*/
public function tick(?Carbon $now = null): array
{
$nowUtc = ($now ?? Carbon::now())->utc();
$hallFpBefore = $this->hallSnapshot->hallTargetFingerprint($nowUtc);
$statusUpdates = [
'pending_to_open_or_later' => $this->promoteStalePendingRows($nowUtc),
'open_to_closing_or_closed' => $this->openToClosingOrClosed($nowUtc),
'closing_to_closed' => $this->closingToClosed($nowUtc),
'cooldown_to_settling' => $this->cooldownToSettling($nowUtc),
];
$rngOutcome = $this->rng->runDue($nowUtc);
$planned = $this->planner->ensureBuffer($nowUtc);
$report = [
'status_updates' => $statusUpdates,
'rng_rung' => $rngOutcome['rung'],
'rng_errors' => $rngOutcome['errors'],
'planned' => $planned,
];
$snapshotAfter = $this->hallSnapshot->build($nowUtc);
$hallFpAfter = $this->hallSnapshot->hallTargetFingerprint($nowUtc);
$this->hallRealtime->notifyStatusChangeIfHallDbChanged($hallFpBefore, $hallFpAfter, $snapshotAfter);
return $report;
}
/** 补偿迟到的调度pending 可依当前时刻落到 open / closing / closed。 */
private function promoteStalePendingRows(Carbon $nowUtc): int
{
$toClosed = Draw::query()
->where('status', DrawStatus::Pending->value)
->whereNotNull('draw_time')
->where('draw_time', '<=', $nowUtc)
->update(['status' => DrawStatus::Closed->value]);
$toClosing = Draw::query()
->where('status', DrawStatus::Pending->value)
->whereNotNull('close_time')
->whereNotNull('draw_time')
->where('close_time', '<=', $nowUtc)
->where('draw_time', '>', $nowUtc)
->update(['status' => DrawStatus::Closing->value]);
$toOpen = Draw::query()
->where('status', DrawStatus::Pending->value)
->whereNotNull('start_time')
->where('start_time', '<=', $nowUtc)
->where(function ($q) use ($nowUtc): void {
$q->whereNull('close_time')
->orWhere('close_time', '>', $nowUtc);
})
->update(['status' => DrawStatus::Open->value]);
return (int) $toClosed + (int) $toClosing + (int) $toOpen;
}
/** 先处理「已封盘且已越过开奖时刻」直达 closed再走正常封盘中。 */
private function openToClosingOrClosed(Carbon $nowUtc): int
{
$toClosed = Draw::query()
->where('status', DrawStatus::Open->value)
->whereNotNull('close_time')
->where('close_time', '<=', $nowUtc)
->whereNotNull('draw_time')
->where('draw_time', '<=', $nowUtc)
->update(['status' => DrawStatus::Closed->value]);
$toClosing = Draw::query()
->where('status', DrawStatus::Open->value)
->whereNotNull('close_time')
->where('close_time', '<=', $nowUtc)
->where(function ($q) use ($nowUtc): void {
$q->whereNull('draw_time')
->orWhere('draw_time', '>', $nowUtc);
})
->update(['status' => DrawStatus::Closing->value]);
return (int) $toClosed + (int) $toClosing;
}
private function closingToClosed(Carbon $nowUtc): int
{
return Draw::query()
->where('status', DrawStatus::Closing->value)
->whereNotNull('draw_time')
->where('draw_time', '<=', $nowUtc)
->update(['status' => DrawStatus::Closed->value]);
}
/** 冷静期结束 → settling结算/派彩由后续阶段补齐)。 */
private function cooldownToSettling(Carbon $nowUtc): int
{
return Draw::query()
->where('status', DrawStatus::Cooldown->value)
->whereNotNull('cooling_end_time')
->where('cooling_end_time', '<=', $nowUtc)
->update(['status' => DrawStatus::Settling->value]);
}
}