feat: 添加结算功能,更新 TicketItem 模型以支持最新结算详情,增强 DrawTickService 以自动处理结算,更新 TicketWalletService 以支持派彩入账,扩展 API 路由以管理结算批次和奖池

This commit is contained in:
2026-05-11 15:34:34 +08:00
parent 6a55fa9592
commit 19003f5041
50 changed files with 3604 additions and 3 deletions

View File

@@ -0,0 +1,128 @@
<?php
namespace App\Services\Settlement;
use App\Models\DrawResultItem;
use Illuminate\Support\Collection;
/**
* 已发布开奖批次的号码视图,供结算匹配(产品文档 §5 奖项分区 {@see DrawPrizeLayout})。
*/
final class PublishedDrawResultBoard
{
/** @var array<string, int> prize_type => 越小越优 */
private const TIER_RANK = [
'first' => 0,
'second' => 1,
'third' => 2,
'starter' => 3,
'consolation' => 4,
];
/** @var Collection<int, DrawResultItem> */
private readonly Collection $items;
private string $firstPrizeNumber = '';
/** @var array<string, array{tier: string, rank: int}> */
private array $numberToBestTier = [];
/**
* @param Collection<int, DrawResultItem> $items
*/
public function __construct(Collection $items)
{
$this->items = $items;
$first = $items->firstWhere(fn (DrawResultItem $r) => $r->prize_type === 'first' && (int) $r->prize_index === 0);
$this->firstPrizeNumber = $first !== null ? (string) $first->number_4d : '';
foreach ($items as $row) {
$num = (string) $row->number_4d;
if ($num === '') {
continue;
}
$tier = (string) $row->prize_type;
$rank = self::TIER_RANK[$tier] ?? 99;
if (! isset($this->numberToBestTier[$num]) || $rank < $this->numberToBestTier[$num]['rank']) {
$this->numberToBestTier[$num] = ['tier' => $tier, 'rank' => $rank];
}
}
}
/** @return Collection<int, DrawResultItem> */
public function allRows(): Collection
{
return $this->items;
}
public function row(string $prizeType, int $prizeIndex = 0): ?DrawResultItem
{
return $this->items->firstWhere(
fn (DrawResultItem $r) => (string) $r->prize_type === $prizeType && (int) $r->prize_index === $prizeIndex,
);
}
/**
* @return list<string>
*/
public function numbersForPrizeType(string $prizeType): array
{
$out = [];
foreach ($this->items as $row) {
if ((string) $row->prize_type !== $prizeType) {
continue;
}
$n = (string) $row->number_4d;
if ($n !== '') {
$out[] = $n;
}
}
return $out;
}
public function firstPrizeNumber4d(): string
{
return $this->firstPrizeNumber;
}
public function suffix3ForTier(string $prizeType, int $prizeIndex = 0): string
{
$r = $this->row($prizeType, $prizeIndex);
return $r !== null ? (string) $r->suffix_3d : '';
}
public function suffix2ForTier(string $prizeType, int $prizeIndex = 0): string
{
$r = $this->row($prizeType, $prizeIndex);
return $r !== null ? (string) $r->suffix_2d : '';
}
/**
* Big任意 23 档中最佳命中档。
*
* @return array{tier: string, rank: int}|null
*/
public function bestTierForNumber(string $number4d): ?array
{
return $this->numberToBestTier[$number4d] ?? null;
}
/**
* Small仅头 / / 三奖rank 02
*
* @return array{tier: string, rank: int}|null
*/
public function bestSmallTierForNumber(string $number4d): ?array
{
$hit = $this->bestTierForNumber($number4d);
if ($hit === null) {
return null;
}
return $hit['rank'] <= 2 ? $hit : null;
}
}