Files
lotteryLaravel/app/Services/Draw/DrawManualResultService.php
kang d4779660c8
Some checks failed
lotterLaravel CI / test (push) Has been cancelled
lotterLaravel E2E / e2e-api (push) Has been cancelled
feat(draw): 支持多玩法provider及本地化结算时区功能
- Draw相关控制器添加provider_code和provider_name字段支持
- 允许手动录入开奖批次时指定provider_code
- RNG开奖批次生成支持多provider,分别生成对应批次数据
- DrawPublishService和DrawManualResultService中支持基于provider_code管理开奖版本和发布流程
- DrawResultViewService调整,支持按provider汇总及筛选开奖结果
- Settlement处理逻辑调整,按provider区分待结算票据及批次,分开结算
- 结算期间管理相关服务支持使用站点本地结算时区计算开账建议和日期处理
- 增加AdminSite的settlement_timezone字段及相关请求验证和数据存取支持
- 优化AdminSettlementPeriod相关代码,防止存在未结清票据时关账
- Ticket明细返回接口添加结算时区信息,提升用户时间体验
- 多处查询排序和版本号处理改进,保证多provider数据正确顺序与一致性
2026-07-09 15:48:21 +08:00

111 lines
4.5 KiB
PHP

<?php
namespace App\Services\Draw;
use App\Models\Draw;
use App\Models\AdminUser;
use App\Models\BetProvider;
use App\Lottery\DrawStatus;
use App\Models\DrawResultItem;
use App\Models\DrawResultBatch;
use Illuminate\Support\Facades\DB;
use App\Lottery\DrawResultSourceType;
use App\Lottery\DrawResultBatchStatus;
final class DrawManualResultService
{
/**
* @param list<array{prize_type: string, prize_index: int, number_4d: string}> $items
*/
public function createPendingBatch(Draw $draw, AdminUser $admin, array $items, ?string $providerCode = null): DrawResultBatch
{
return DB::transaction(function () use ($draw, $admin, $items, $providerCode): DrawResultBatch {
/** @var Draw $locked */
$locked = Draw::query()->whereKey($draw->id)->lockForUpdate()->firstOrFail();
if (! in_array($locked->status, [DrawStatus::Closed->value, DrawStatus::Review->value], true)) {
throw new \RuntimeException('draw_not_editable');
}
if ($locked->settle_version > 0 || $locked->status === DrawStatus::Settled->value) {
throw new \RuntimeException('draw_already_settled');
}
$providerCode = strtoupper(trim((string) ($providerCode ?: BetProvider::DEFAULT_CODE)));
/** @var BetProvider|null $provider */
$provider = BetProvider::query()
->where('code', $providerCode)
->where('is_enabled', true)
->first();
if ($provider === null && $providerCode !== BetProvider::DEFAULT_CODE) {
throw new \RuntimeException('bet_provider_not_found');
}
$providerName = $provider?->name ?? 'Singapore';
if (DrawResultBatch::query()
->where('draw_id', $locked->id)
->where('provider_code', $providerCode)
->where('status', DrawResultBatchStatus::PendingReview->value)
->exists()) {
throw new \RuntimeException('draw_pending_result_batch_exists');
}
$latestVersion = (int) DrawResultBatch::query()
->where('draw_id', $locked->id)
->where('provider_code', $providerCode)
->max('result_version');
$nextVersion = max(1, $latestVersion + 1);
$batch = DrawResultBatch::query()->create([
'draw_id' => $locked->id,
'provider_code' => $providerCode,
'provider_name' => $providerName,
'result_version' => $nextVersion,
'source_type' => DrawResultSourceType::Manual->value,
'rng_seed_hash' => null,
'raw_seed_encrypted' => null,
'status' => DrawResultBatchStatus::PendingReview->value,
'created_by' => $admin->id,
'confirmed_by' => null,
'confirmed_at' => null,
]);
foreach ($this->sortByLayout($items) as $item) {
$number = (string) $item['number_4d'];
DrawResultItem::query()->create([
'draw_id' => $locked->id,
'result_batch_id' => $batch->id,
'prize_type' => $item['prize_type'],
'prize_index' => (int) $item['prize_index'],
'number_4d' => $number,
'suffix_3d' => substr($number, -3),
'suffix_2d' => substr($number, -2),
'head_digit' => (int) substr($number, 0, 1),
'tail_digit' => (int) substr($number, 3, 1),
]);
}
$locked->forceFill([
'status' => DrawStatus::Review->value,
'result_source' => DrawResultSourceType::Manual->value,
])->save();
return $batch->fresh(['items']);
});
}
/**
* @param list<array{prize_type: string, prize_index: int, number_4d: string}> $items
* @return list<array{prize_type: string, prize_index: int, number_4d: string}>
*/
private function sortByLayout(array $items): array
{
$order = [];
foreach (DrawPrizeLayout::slots() as $i => $slot) {
$order[$slot['prize_type'].':'.$slot['prize_index']] = $i;
}
usort($items, fn (array $a, array $b): int => ($order[$a['prize_type'].':'.$a['prize_index']] ?? 99)
<=> ($order[$b['prize_type'].':'.$b['prize_index']] ?? 99));
return $items;
}
}