forceFill([ 'status' => DrawStatus::Drawing->value, ])->save(); $manualReview = (bool) config('lottery.draw.require_manual_review', false); $seedMaterial = bin2hex(random_bytes(32)); $rngSeedHash = hash('sha256', $seedMaterial); $nextVersion = max(1, (int) $draw->current_result_version + 1); $batch = DrawResultBatch::query()->create([ 'draw_id' => $draw->id, 'result_version' => $nextVersion, 'source_type' => DrawResultSourceType::Rng->value, 'rng_seed_hash' => $rngSeedHash, 'raw_seed_encrypted' => null, 'status' => $manualReview ? DrawResultBatchStatus::PendingReview->value : DrawResultBatchStatus::Published->value, 'created_by' => null, 'confirmed_by' => null, 'confirmed_at' => $manualReview ? null : now(), ]); foreach (DrawPrizeLayout::slots() as $slot) { $num = str_pad((string) random_int(0, 9999), 4, '0', STR_PAD_LEFT); $suffix3 = substr($num, -3); $suffix2 = substr($num, -2); DrawResultItem::query()->create([ 'draw_id' => $draw->id, 'result_batch_id' => $batch->id, 'prize_type' => $slot['prize_type'], 'prize_index' => $slot['prize_index'], 'number_4d' => $num, 'suffix_3d' => $suffix3, 'suffix_2d' => $suffix2, 'head_digit' => $num !== '' ? (int) substr($num, 0, 1) : null, 'tail_digit' => $num !== '' ? (int) substr($num, 3, 1) : null, ]); } if ($manualReview) { $draw->forceFill([ 'status' => DrawStatus::Review->value, 'result_source' => DrawResultSourceType::Rng->value, ])->save(); } else { $this->publisher->markPublishedInTransaction($draw->fresh(), $batch->fresh()); } return $batch->fresh(); } /** * @return array{rung: int, errors: array} */ public function runDue(?Carbon $now = null): array { $nowUtc = ($now ?? Carbon::now())->utc(); $rung = 0; $errors = []; $ids = Draw::query() ->where('status', DrawStatus::Closed->value) ->whereNotNull('draw_time') ->where('draw_time', '<=', $nowUtc) ->whereDoesntHave('resultBatches') ->orderBy('draw_time') ->pluck('id'); foreach ($ids as $drawId) { try { DB::transaction(function () use ($drawId, &$rung): void { /** @var Draw|null $locked */ $locked = Draw::query()->whereKey($drawId)->lockForUpdate()->first(); if ($locked === null || $locked->status !== DrawStatus::Closed->value) { return; } if ($locked->resultBatches()->exists()) { return; } $this->executeLocked($locked); $rung++; }); } catch (\Throwable $e) { $errors[] = (string) $drawId.': '.$e->getMessage(); } } return ['rung' => $rung, 'errors' => $errors]; } }