feat(lottery): 扩展传统马来赔率、玩法结算与开注商短码支持
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
lotterLaravel E2E / e2e-api (push) Has been cancelled

This commit is contained in:
2026-07-10 17:04:28 +08:00
parent 2ea3e810a0
commit cbb18b976f
24 changed files with 320 additions and 45 deletions

View File

@@ -16,6 +16,8 @@ final class Pos2TierSettlementMatcher implements SettlementPlayMatcher
'pos_2a' => 'first',
'pos_2b' => 'second',
'pos_2c' => 'third',
'pos_2d' => 'starter',
'pos_2e' => 'consolation',
];
public function __construct(

View File

@@ -16,6 +16,8 @@ final class Pos3TierSettlementMatcher implements SettlementPlayMatcher
'pos_3a' => 'first',
'pos_3b' => 'second',
'pos_3c' => 'third',
'pos_3d' => 'starter',
'pos_3e' => 'consolation',
];
public function __construct(

View File

@@ -0,0 +1,45 @@
<?php
namespace App\Services\Settlement\Matchers;
use App\Models\TicketItem;
use App\Models\TicketCombination;
use Illuminate\Support\Collection;
use App\Services\Settlement\OddsSnapshotReader;
use App\Services\Settlement\PublishedDrawResultBoard;
use App\Services\Settlement\Contracts\SettlementPlayMatcher;
/** Settles aggregate 4D / suffix plays against an explicit group of prize scopes. */
final class ScopeFilteredSettlementMatcher implements SettlementPlayMatcher
{
/** @var array<string, list<string>> */
private const SCOPES = [
'four_lower' => ['starter', 'consolation'],
'pos_3lower' => ['starter', 'consolation'],
'pos_2any' => ['first', 'second', 'third', 'starter', 'consolation'],
];
public function __construct(private readonly OddsSnapshotReader $odds) {}
public function match(TicketItem $item, PublishedDrawResultBoard $board, Collection $combinations): array
{
$allowed = self::SCOPES[(string) $item->play_code] ?? [];
$snapshot = is_array($item->odds_snapshot_json) ? $item->odds_snapshot_json : null;
$lines = [];
$total = 0;
$bestTier = null;
foreach ($combinations as $combo) {
/** @var TicketCombination $combo */
$hit = $board->bestTierForNumber((string) $combo->number_4d);
if ($hit === null || ! in_array($hit['tier'], $allowed, true)) continue;
$oddsValue = $this->odds->oddsValueForScope($snapshot, $hit['tier']);
$payout = (int) floor((int) $combo->bet_amount * ($oddsValue / 10_000));
$total += $payout;
$bestTier ??= $hit['tier'];
$lines[] = ['number_4d' => $combo->number_4d, 'tier' => $hit['tier'], 'payout' => $payout];
}
return ['win_amount' => $total, 'matched_prize_tier' => $bestTier, 'match_detail' => ['lines' => $lines]];
}
}