- 更新多个控制器和服务,使用 LotterySettings 服务获取彩票相关配置,如默认币种、开奖间隔、下注窗口等,提升代码一致性与可维护性。 - 移除 .env.example 中不再使用的配置项,建议通过后台管理进行设置。
107 lines
3.6 KiB
PHP
107 lines
3.6 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Draw;
|
|
|
|
use Carbon\Carbon;
|
|
use App\Models\Draw;
|
|
use App\Services\LotterySettings;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/**
|
|
* 管理员手动创建期号(可指定开奖/封盘/开始时间)。
|
|
*/
|
|
final class DrawManualCreateService
|
|
{
|
|
public function __construct(
|
|
private readonly DrawTimelineBuilder $timeline,
|
|
) {}
|
|
|
|
/**
|
|
* @param array{
|
|
* draw_time: string,
|
|
* start_time?: string|null,
|
|
* close_time?: string|null,
|
|
* draw_no?: string|null,
|
|
* business_date?: string|null,
|
|
* sequence_no?: int|null,
|
|
* } $input
|
|
*/
|
|
public function create(array $input, ?Carbon $now = null): Draw
|
|
{
|
|
$tz = LotterySettings::drawTimezone();
|
|
$nowUtc = ($now ?? Carbon::now())->utc();
|
|
|
|
$drawLocal = $this->parseInTimezone((string) $input['draw_time'], $tz);
|
|
$startLocal = isset($input['start_time']) && $input['start_time'] !== null && $input['start_time'] !== ''
|
|
? $this->parseInTimezone((string) $input['start_time'], $tz)
|
|
: null;
|
|
$closeLocal = isset($input['close_time']) && $input['close_time'] !== null && $input['close_time'] !== ''
|
|
? $this->parseInTimezone((string) $input['close_time'], $tz)
|
|
: null;
|
|
|
|
if ($startLocal === null || $closeLocal === null) {
|
|
$defaults = $this->timeline->windowsFromDrawLocal($drawLocal);
|
|
$startLocal ??= $defaults['start_local'];
|
|
$closeLocal ??= $defaults['close_local'];
|
|
}
|
|
|
|
if (! $startLocal->lt($closeLocal) || ! $closeLocal->lt($drawLocal)) {
|
|
throw new \RuntimeException('draw_timeline_invalid');
|
|
}
|
|
|
|
$businessDate = isset($input['business_date']) && $input['business_date'] !== ''
|
|
? (string) $input['business_date']
|
|
: $drawLocal->format('Y-m-d');
|
|
|
|
$sequenceNo = isset($input['sequence_no']) && $input['sequence_no'] !== null
|
|
? max(1, (int) $input['sequence_no'])
|
|
: $this->nextSequenceForDate($businessDate);
|
|
|
|
$drawNo = isset($input['draw_no']) && trim((string) $input['draw_no']) !== ''
|
|
? trim((string) $input['draw_no'])
|
|
: $this->timeline->drawNo($businessDate, $sequenceNo);
|
|
|
|
if (Draw::query()->where('draw_no', $drawNo)->exists()) {
|
|
throw new \RuntimeException('draw_no_exists');
|
|
}
|
|
|
|
$built = $this->timeline->buildFromLocals($startLocal, $closeLocal, $drawLocal, $nowUtc);
|
|
|
|
return DB::transaction(function () use (
|
|
$drawNo,
|
|
$businessDate,
|
|
$sequenceNo,
|
|
$built,
|
|
): Draw {
|
|
return Draw::query()->create([
|
|
'draw_no' => $drawNo,
|
|
'business_date' => $businessDate,
|
|
'sequence_no' => $sequenceNo,
|
|
'status' => $built['status'],
|
|
'start_time' => $built['start_utc'],
|
|
'close_time' => $built['close_utc'],
|
|
'draw_time' => $built['draw_utc'],
|
|
'cooling_end_time' => null,
|
|
'result_source' => null,
|
|
'current_result_version' => 0,
|
|
'settle_version' => 0,
|
|
'is_reopened' => false,
|
|
]);
|
|
});
|
|
}
|
|
|
|
private function parseInTimezone(string $value, string $tz): Carbon
|
|
{
|
|
return Carbon::parse($value, $tz);
|
|
}
|
|
|
|
private function nextSequenceForDate(string $businessDate): int
|
|
{
|
|
$max = (int) Draw::query()
|
|
->where('business_date', $businessDate)
|
|
->max('sequence_no');
|
|
|
|
return $max + 1;
|
|
}
|
|
}
|