更新 AdminJackpotPoolUpdateController 校验规则,禁止传入 current_amount。 优化 AdminRiskPoolManualStatusController:更新奖池状态后同步 Redis 状态。 在 TransferOrderReconcileController 中新增 completeCredit 方法,用于处理卡住的转账订单对账。 调整 TransferOrderListController:优化转账订单处理条件。 在 TicketItemsIndexController 中实现支持时区的日期筛选,提升日期处理准确性。 扩展 JackpotPool 模型,新增 adjustments 关联关系。 改进票据与钱包相关服务中的错误处理和事务管理。
92 lines
3.5 KiB
PHP
92 lines
3.5 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Draw;
|
|
|
|
use App\Models\Draw;
|
|
use App\Models\AdminUser;
|
|
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): DrawResultBatch
|
|
{
|
|
return DB::transaction(function () use ($draw, $admin, $items): 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');
|
|
}
|
|
|
|
if (DrawResultBatch::query()
|
|
->where('draw_id', $locked->id)
|
|
->where('status', DrawResultBatchStatus::PendingReview->value)
|
|
->exists()) {
|
|
throw new \RuntimeException('draw_pending_result_batch_exists');
|
|
}
|
|
|
|
$nextVersion = max(1, (int) $locked->current_result_version + 1);
|
|
$batch = DrawResultBatch::query()->create([
|
|
'draw_id' => $locked->id,
|
|
'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;
|
|
}
|
|
}
|