170 lines
5.2 KiB
PHP
170 lines
5.2 KiB
PHP
<?php
|
||
|
||
namespace App\Services\Draw;
|
||
|
||
use App\Lottery\DrawResultBatchStatus;
|
||
use App\Lottery\DrawStatus;
|
||
use App\Models\Draw;
|
||
use App\Models\DrawResultBatch;
|
||
use App\Models\DrawResultItem;
|
||
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
|
||
use Illuminate\Support\Collection;
|
||
|
||
/**
|
||
* 将已发布的 {@see DrawResultItem} 聚合成前端/文档约定结构。
|
||
*/
|
||
final class DrawResultViewService
|
||
{
|
||
/**
|
||
* 与 `docs/01-产品文档` GET /api/v1/results 示例键名对齐(1st/2nd/3rd/starter/consolation)。
|
||
*
|
||
* @return array{
|
||
* 1st: string,
|
||
* 2nd: string,
|
||
* 3rd: string,
|
||
* starter: array<int, string>,
|
||
* consolation: array<int, string>
|
||
* }
|
||
*/
|
||
public function numbersFromItems(Collection $items): array
|
||
{
|
||
$byType = [
|
||
'first' => [],
|
||
'second' => [],
|
||
'third' => [],
|
||
'starter' => [],
|
||
'consolation' => [],
|
||
];
|
||
|
||
foreach ($items->sortBy(['prize_type', 'prize_index']) as $row) {
|
||
/** @var DrawResultItem $row */
|
||
$t = (string) $row->prize_type;
|
||
if (! isset($byType[$t])) {
|
||
continue;
|
||
}
|
||
$byType[$t][] = (string) $row->number_4d;
|
||
}
|
||
|
||
return [
|
||
'1st' => $byType['first'][0] ?? '',
|
||
'2nd' => $byType['second'][0] ?? '',
|
||
'3rd' => $byType['third'][0] ?? '',
|
||
'starter' => array_values($byType['starter']),
|
||
'consolation' => array_values($byType['consolation']),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 返回 null 若该期尚未有可展示的开奖采纳版本。
|
||
*
|
||
* @return array<string, mixed>|null
|
||
*/
|
||
public function summarizeDraw(Draw $draw): ?array
|
||
{
|
||
$version = (int) $draw->current_result_version;
|
||
if ($version < 1) {
|
||
return null;
|
||
}
|
||
|
||
$batch = DrawResultBatch::query()
|
||
->where('draw_id', $draw->id)
|
||
->where('result_version', $version)
|
||
->where('status', DrawResultBatchStatus::Published->value)
|
||
->first();
|
||
|
||
if ($batch === null) {
|
||
return null;
|
||
}
|
||
|
||
$items = DrawResultItem::query()
|
||
->where('result_batch_id', $batch->id)
|
||
->orderBy('prize_type')
|
||
->orderBy('prize_index')
|
||
->get([
|
||
'prize_type', 'prize_index', 'number_4d',
|
||
'suffix_3d', 'suffix_2d', 'head_digit', 'tail_digit',
|
||
]);
|
||
|
||
if ($items->isEmpty()) {
|
||
return null;
|
||
}
|
||
|
||
$numbers = $this->numbersFromItems($items);
|
||
|
||
return [
|
||
'draw_id' => $draw->draw_no,
|
||
'draw_no' => $draw->draw_no,
|
||
'business_date' => $draw->business_date?->format('Y-m-d') ?? (string) $draw->business_date,
|
||
'draw_time' => $draw->draw_time?->format('Y-m-d H:i:s'),
|
||
'draw_time_iso' => $draw->draw_time?->toIso8601String(),
|
||
'result_version' => $version,
|
||
'result_source' => $draw->result_source,
|
||
'results' => $numbers,
|
||
'result_items' => $items->map(fn (DrawResultItem $r) => [
|
||
'prize_type' => $r->prize_type,
|
||
'prize_index' => (int) $r->prize_index,
|
||
'number_4d' => $r->number_4d,
|
||
'suffix_3d' => $r->suffix_3d,
|
||
'suffix_2d' => $r->suffix_2d,
|
||
'head_digit' => $r->head_digit !== null ? (int) $r->head_digit : null,
|
||
'tail_digit' => $r->tail_digit !== null ? (int) $r->tail_digit : null,
|
||
])->values()->all(),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* @param LengthAwarePaginator<int, Draw> $paginator
|
||
*/
|
||
public function decoratePaginator(LengthAwarePaginator $paginator): LengthAwarePaginator
|
||
{
|
||
$collection = $paginator->getCollection()->map(function (Draw $draw): ?array {
|
||
return $this->summarizeDraw($draw);
|
||
})->filter();
|
||
|
||
$paginator->setCollection($collection->values());
|
||
|
||
return $paginator;
|
||
}
|
||
|
||
/** 已发布开奖结果的可查询状态(对外展示往期)。 */
|
||
public static function publishedDrawStatuses(): array
|
||
{
|
||
return [
|
||
DrawStatus::Cooldown->value,
|
||
DrawStatus::Settling->value,
|
||
DrawStatus::Settled->value,
|
||
];
|
||
}
|
||
|
||
public function neighborsIsoTime(Draw $draw): array
|
||
{
|
||
$statuses = self::publishedDrawStatuses();
|
||
$t = $draw->draw_time;
|
||
$prevNo = null;
|
||
$nextNo = null;
|
||
|
||
if ($t !== null) {
|
||
$prevNo = Draw::query()
|
||
->whereIn('status', $statuses)
|
||
->where('current_result_version', '>', 0)
|
||
->whereNotNull('draw_time')
|
||
->where('draw_time', '<', $t)
|
||
->orderByDesc('draw_time')
|
||
->value('draw_no');
|
||
|
||
$nextNo = Draw::query()
|
||
->whereIn('status', $statuses)
|
||
->where('current_result_version', '>', 0)
|
||
->whereNotNull('draw_time')
|
||
->where('draw_time', '>', $t)
|
||
->orderBy('draw_time')
|
||
->value('draw_no');
|
||
}
|
||
|
||
return [
|
||
'previous_draw_no' => $prevNo,
|
||
'next_draw_no' => $nextNo,
|
||
];
|
||
}
|
||
}
|