feat: 彩票业务迁移并补全后台权限与代理结算体系
This commit is contained in:
@@ -5,6 +5,7 @@ namespace App\Services\Draw;
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Draw;
|
||||
use App\Lottery\DrawStatus;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use App\Services\LotterySettings;
|
||||
use App\Services\Settlement\SettlementOrchestrator;
|
||||
use App\Services\Settlement\SettlementTickFinalizer;
|
||||
@@ -38,21 +39,23 @@ final class DrawTickService
|
||||
public function tick(?Carbon $now = null): array
|
||||
{
|
||||
$nowUtc = ($now ?? Carbon::now())->utc();
|
||||
$startedAt = hrtime(true);
|
||||
$stageTimings = [];
|
||||
|
||||
$hallFpBefore = $this->hallSnapshot->hallTargetFingerprint($nowUtc);
|
||||
$hallFpBefore = $this->measureStage('hall_fp_before', $stageTimings, fn () => $this->hallSnapshot->hallTargetFingerprint($nowUtc));
|
||||
|
||||
$statusUpdates = [
|
||||
$statusUpdates = $this->measureStage('status_updates', $stageTimings, fn (): array => [
|
||||
'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),
|
||||
];
|
||||
]);
|
||||
|
||||
$settlingSettled = $this->settleSettlingDraws();
|
||||
$settlementFinalized = $this->settlementFinalizer->finalizePendingBatches();
|
||||
$settlingSettled = $this->measureStage('settle_settling_draws', $stageTimings, fn (): int => $this->settleSettlingDraws());
|
||||
$settlementFinalized = $this->measureStage('finalize_pending_batches', $stageTimings, fn (): array => $this->settlementFinalizer->finalizePendingBatches());
|
||||
|
||||
$rngOutcome = $this->rng->runDue($nowUtc);
|
||||
$planned = $this->planner->ensureBuffer($nowUtc);
|
||||
$rngOutcome = $this->measureStage('rng_run_due', $stageTimings, fn (): array => $this->rng->runDue($nowUtc));
|
||||
$planned = $this->measureStage('ensure_buffer', $stageTimings, fn (): array => $this->planner->ensureBuffer($nowUtc));
|
||||
|
||||
$report = [
|
||||
'status_updates' => $statusUpdates,
|
||||
@@ -63,10 +66,14 @@ final class DrawTickService
|
||||
'planned' => $planned,
|
||||
];
|
||||
|
||||
$snapshotAfter = $this->hallSnapshot->build($nowUtc);
|
||||
$hallFpAfter = $this->hallSnapshot->hallTargetFingerprint($nowUtc);
|
||||
$snapshotAfter = $this->measureStage('hall_snapshot_after', $stageTimings, fn () => $this->hallSnapshot->build($nowUtc));
|
||||
$hallFpAfter = $this->measureStage('hall_fp_after', $stageTimings, fn () => $this->hallSnapshot->hallTargetFingerprint($nowUtc));
|
||||
|
||||
$this->hallRealtime->notifyStatusChangeIfHallDbChanged($hallFpBefore, $hallFpAfter, $snapshotAfter);
|
||||
$this->measureStage('notify_status_change', $stageTimings, function () use ($hallFpBefore, $hallFpAfter, $snapshotAfter): void {
|
||||
$this->hallRealtime->notifyStatusChangeIfHallDbChanged($hallFpBefore, $hallFpAfter, $snapshotAfter);
|
||||
});
|
||||
|
||||
$this->logIfSlow($startedAt, $stageTimings, $report);
|
||||
|
||||
return $report;
|
||||
}
|
||||
@@ -156,7 +163,11 @@ final class DrawTickService
|
||||
}
|
||||
|
||||
$n = 0;
|
||||
$ids = Draw::query()->where('status', DrawStatus::Settling->value)->pluck('id');
|
||||
$ids = Draw::query()
|
||||
->where('status', DrawStatus::Settling->value)
|
||||
->orderBy('id')
|
||||
->limit((int) config('lottery.draw_tick_settle_limit', 3))
|
||||
->pluck('id');
|
||||
foreach ($ids as $drawId) {
|
||||
$draw = Draw::query()->find($drawId);
|
||||
if ($draw === null) {
|
||||
@@ -173,4 +184,49 @@ final class DrawTickService
|
||||
|
||||
return $n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param array<string, int> $stageTimings
|
||||
* @param \Closure(): T $callback
|
||||
* @return T
|
||||
*/
|
||||
private function measureStage(string $stage, array &$stageTimings, \Closure $callback): mixed
|
||||
{
|
||||
$startedAt = hrtime(true);
|
||||
$result = $callback();
|
||||
$stageTimings[$stage] = (int) round((hrtime(true) - $startedAt) / 1_000_000);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, int> $stageTimings
|
||||
* @param array<string, mixed> $report
|
||||
*/
|
||||
private function logIfSlow(int $startedAt, array $stageTimings, array $report): void
|
||||
{
|
||||
$totalMs = (int) round((hrtime(true) - $startedAt) / 1_000_000);
|
||||
$thresholdMs = (int) config('lottery.draw_tick_warn_threshold_ms', 1500);
|
||||
$stageThresholdMs = (int) config('lottery.draw_tick_stage_warn_threshold_ms', 500);
|
||||
$slowStages = array_filter($stageTimings, fn (int $elapsedMs): bool => $elapsedMs >= $stageThresholdMs);
|
||||
|
||||
if ($totalMs < $thresholdMs && $slowStages === []) {
|
||||
return;
|
||||
}
|
||||
|
||||
Log::warning('lottery:draw-tick exceeded warn threshold', [
|
||||
'elapsed_ms' => $totalMs,
|
||||
'threshold_ms' => $thresholdMs,
|
||||
'stage_threshold_ms' => $stageThresholdMs,
|
||||
'slow_stages_ms' => $slowStages,
|
||||
'all_stages_ms' => $stageTimings,
|
||||
'status_update_rows' => array_sum($report['status_updates'] ?? []),
|
||||
'settling_settled' => $report['settling_settled'] ?? 0,
|
||||
'approved_batches' => $report['settlement_finalized']['approved'] ?? 0,
|
||||
'paid_batches' => $report['settlement_finalized']['paid'] ?? 0,
|
||||
'rng_rung' => $report['rng_rung'] ?? 0,
|
||||
'planned_created' => $report['planned']['created'] ?? 0,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user