feat: 增强抽奖管理功能,支持手动创建、更新和删除期号
- 新增 API 路由和控制器,允许管理员手动创建、更新和删除抽奖期号。 - 更新抽奖调度逻辑,确保在抽奖时间和封盘时间的管理上更加灵活。 - 添加多语言支持的错误信息,提升用户体验。 - 更新测试用例,确保新功能的正确性和稳定性。
This commit is contained in:
37
app/Services/Draw/DrawDestroyService.php
Normal file
37
app/Services/Draw/DrawDestroyService.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Draw;
|
||||
|
||||
use App\Models\Draw;
|
||||
use App\Models\TicketOrder;
|
||||
use App\Lottery\DrawStatus;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* 物理删除误建的未开始期号(无注单、无开奖结果)。
|
||||
*/
|
||||
final class DrawDestroyService
|
||||
{
|
||||
public function destroy(Draw $draw): void
|
||||
{
|
||||
DB::transaction(function () use ($draw): void {
|
||||
/** @var Draw $locked */
|
||||
$locked = Draw::query()->whereKey($draw->id)->lockForUpdate()->firstOrFail();
|
||||
|
||||
if ($locked->status !== DrawStatus::Pending->value) {
|
||||
throw new \RuntimeException('draw_not_deletable');
|
||||
}
|
||||
|
||||
if ($locked->resultBatches()->exists()) {
|
||||
throw new \RuntimeException('draw_result_exists');
|
||||
}
|
||||
|
||||
$betTotal = (int) TicketOrder::query()->where('draw_id', $locked->id)->sum('total_actual_deduct');
|
||||
if ($betTotal > 0) {
|
||||
throw new \RuntimeException('draw_has_bets');
|
||||
}
|
||||
|
||||
$locked->delete();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -94,12 +94,38 @@ final class DrawHallSnapshotBuilder
|
||||
return $bettingOpen;
|
||||
}
|
||||
|
||||
$upcoming = Draw::query()
|
||||
->whereNotIn('status', [
|
||||
DrawStatus::Settled->value,
|
||||
DrawStatus::Cancelled->value,
|
||||
])
|
||||
->where(function ($q) use ($nowUtc): void {
|
||||
$q->where(function ($q2) use ($nowUtc): void {
|
||||
$q2->whereNotNull('close_time')
|
||||
->where('close_time', '>', $nowUtc);
|
||||
})->orWhere(function ($q2) use ($nowUtc): void {
|
||||
$q2->whereNull('close_time')
|
||||
->whereNotNull('draw_time')
|
||||
->where('draw_time', '>', $nowUtc);
|
||||
});
|
||||
})
|
||||
->orderBy('draw_time')
|
||||
->get();
|
||||
|
||||
foreach ($upcoming as $candidate) {
|
||||
if ($this->isStalePendingRow($candidate, $nowUtc)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $candidate;
|
||||
}
|
||||
|
||||
$chronological = Draw::query()
|
||||
->whereNotIn('status', [
|
||||
DrawStatus::Settled->value,
|
||||
DrawStatus::Cancelled->value,
|
||||
])
|
||||
->orderBy('draw_time')
|
||||
->orderByDesc('draw_time')
|
||||
->first();
|
||||
|
||||
if ($chronological !== null && $this->isCooldownExpired($chronological, $nowUtc)) {
|
||||
@@ -120,6 +146,23 @@ final class DrawHallSnapshotBuilder
|
||||
return $chronological;
|
||||
}
|
||||
|
||||
/** 调度未跑时:库内仍是 pending,但封盘/开奖时刻已过,不应再作为大厅「当期」。 */
|
||||
private function isStalePendingRow(Draw $draw, Carbon $nowUtc): bool
|
||||
{
|
||||
if ((string) $draw->status !== DrawStatus::Pending->value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$closeUtc = $draw->close_time;
|
||||
if ($closeUtc instanceof Carbon && $closeUtc <= $nowUtc) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$drawUtc = $draw->draw_time;
|
||||
|
||||
return $drawUtc instanceof Carbon && $drawUtc <= $nowUtc;
|
||||
}
|
||||
|
||||
private function isCooldownExpired(Draw $draw, Carbon $nowUtc): bool
|
||||
{
|
||||
return (string) $draw->status === DrawStatus::Cooldown->value
|
||||
@@ -167,6 +210,11 @@ final class DrawHallSnapshotBuilder
|
||||
? max(0, (int) $target->draw_time->getTimestamp() - (int) $nowUtc->getTimestamp())
|
||||
: 0;
|
||||
|
||||
$startUtc = $target->start_time;
|
||||
$secsToStart = ($startUtc !== null && $startUtc > $nowUtc)
|
||||
? max(0, (int) $startUtc->getTimestamp() - (int) $nowUtc->getTimestamp())
|
||||
: 0;
|
||||
|
||||
$coolingRemain = null;
|
||||
if (
|
||||
$target->cooling_end_time instanceof Carbon
|
||||
@@ -180,7 +228,11 @@ final class DrawHallSnapshotBuilder
|
||||
|
||||
$effectiveStatus = $this->effectiveHallDisplayStatus($target, $nowUtc);
|
||||
|
||||
$scheduleTz = (string) config('lottery.draw.timezone', 'UTC');
|
||||
|
||||
$payload = [
|
||||
'schedule_timezone' => $scheduleTz,
|
||||
'schedule_now' => $nowUtc->copy()->timezone($scheduleTz)->format('Y-m-d H:i:s'),
|
||||
'draw_no' => $target->draw_no,
|
||||
'business_date' => $target->business_date instanceof Carbon
|
||||
? $target->business_date->format('Y-m-d')
|
||||
@@ -191,6 +243,7 @@ final class DrawHallSnapshotBuilder
|
||||
'close_time' => $target->close_time?->toIso8601String(),
|
||||
'draw_time' => $target->draw_time?->toIso8601String(),
|
||||
'seconds_to_close' => $secsToClose,
|
||||
'seconds_to_start' => $secsToStart,
|
||||
'seconds_to_draw' => $secsToDraw,
|
||||
'cooling_end_time' => $target->cooling_end_time?->toIso8601String(),
|
||||
'seconds_remaining_in_cooldown' => $coolingRemain,
|
||||
|
||||
105
app/Services/Draw/DrawManualCreateService.php
Normal file
105
app/Services/Draw/DrawManualCreateService.php
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Draw;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Draw;
|
||||
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 = (string) config('lottery.draw.timezone', 'UTC');
|
||||
$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;
|
||||
}
|
||||
}
|
||||
112
app/Services/Draw/DrawManualUpdateService.php
Normal file
112
app/Services/Draw/DrawManualUpdateService.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Draw;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Models\Draw;
|
||||
use App\Models\TicketOrder;
|
||||
use App\Lottery\DrawStatus;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
/**
|
||||
* 管理员修改未开奖期号的时间轴(仅 pending / 无注单的 open)。
|
||||
*/
|
||||
final class DrawManualUpdateService
|
||||
{
|
||||
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 update(Draw $draw, array $input, ?Carbon $now = null): Draw
|
||||
{
|
||||
$tz = (string) config('lottery.draw.timezone', 'UTC');
|
||||
$nowUtc = ($now ?? Carbon::now())->utc();
|
||||
|
||||
return DB::transaction(function () use ($draw, $input, $tz, $nowUtc): Draw {
|
||||
/** @var Draw $locked */
|
||||
$locked = Draw::query()->whereKey($draw->id)->lockForUpdate()->firstOrFail();
|
||||
$this->assertEditable($locked);
|
||||
|
||||
$drawLocal = Carbon::parse((string) $input['draw_time'], $tz);
|
||||
$startLocal = isset($input['start_time']) && $input['start_time'] !== null && $input['start_time'] !== ''
|
||||
? Carbon::parse((string) $input['start_time'], $tz)
|
||||
: null;
|
||||
$closeLocal = isset($input['close_time']) && $input['close_time'] !== null && $input['close_time'] !== ''
|
||||
? Carbon::parse((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'])
|
||||
: (int) $locked->sequence_no;
|
||||
|
||||
$drawNo = isset($input['draw_no']) && trim((string) $input['draw_no']) !== ''
|
||||
? trim((string) $input['draw_no'])
|
||||
: (string) $locked->draw_no;
|
||||
|
||||
if (
|
||||
Draw::query()
|
||||
->where('draw_no', $drawNo)
|
||||
->where('id', '!=', $locked->id)
|
||||
->exists()
|
||||
) {
|
||||
throw new \RuntimeException('draw_no_exists');
|
||||
}
|
||||
|
||||
$built = $this->timeline->buildFromLocals($startLocal, $closeLocal, $drawLocal, $nowUtc);
|
||||
|
||||
$locked->forceFill([
|
||||
'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'],
|
||||
])->save();
|
||||
|
||||
return $locked->refresh();
|
||||
});
|
||||
}
|
||||
|
||||
private function assertEditable(Draw $draw): void
|
||||
{
|
||||
if ($draw->resultBatches()->exists()) {
|
||||
throw new \RuntimeException('draw_result_exists');
|
||||
}
|
||||
|
||||
if (! in_array($draw->status, [DrawStatus::Pending->value, DrawStatus::Open->value], true)) {
|
||||
throw new \RuntimeException('draw_not_editable');
|
||||
}
|
||||
|
||||
if ($draw->status === DrawStatus::Open->value) {
|
||||
$betTotal = (int) TicketOrder::query()->where('draw_id', $draw->id)->sum('total_actual_deduct');
|
||||
if ($betTotal > 0) {
|
||||
throw new \RuntimeException('draw_has_bets');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,10 @@ use Illuminate\Database\QueryException;
|
||||
*/
|
||||
final class DrawPlannerService
|
||||
{
|
||||
public function __construct(
|
||||
private readonly DrawTimelineBuilder $timeline,
|
||||
) {}
|
||||
|
||||
/** @return array{created: int, buffer_target: int, upcoming: int} */
|
||||
public function ensureBuffer(?Carbon $now = null): array
|
||||
{
|
||||
@@ -39,7 +43,7 @@ final class DrawPlannerService
|
||||
|
||||
$row = $last === null
|
||||
? $this->firstSchedule($tz, $interval, $maxSeq, $nowLocal)
|
||||
: $this->scheduleAfter($last, $tz, $interval, $maxSeq, $nowLocal);
|
||||
: $this->scheduleAfter($last, $tz, $interval, $nowLocal);
|
||||
|
||||
try {
|
||||
DB::transaction(function () use ($row, $nowUtc, &$created): void {
|
||||
@@ -95,27 +99,27 @@ final class DrawPlannerService
|
||||
/**
|
||||
* @return array{business_date: string, sequence_no: int, draw_local: Carbon}
|
||||
*/
|
||||
private function scheduleAfter(Draw $last, string $tz, int $intervalMinutes, int $maxSeqPerDay, Carbon $nowLocal): array
|
||||
private function scheduleAfter(Draw $last, string $tz, int $intervalMinutes, Carbon $nowLocal): array
|
||||
{
|
||||
$day = Carbon::parse((string) $last->business_date, $tz)->startOfDay();
|
||||
$seq = (int) $last->sequence_no + 1;
|
||||
if ($seq > $maxSeqPerDay) {
|
||||
$day = $day->addDay();
|
||||
$seq = 1;
|
||||
$lastDrawLocal = $last->draw_time !== null
|
||||
? Carbon::parse($last->draw_time)->timezone($tz)
|
||||
: Carbon::parse((string) $last->business_date, $tz)
|
||||
->startOfDay()
|
||||
->addMinutes((int) $last->sequence_no * $intervalMinutes);
|
||||
|
||||
$drawLocal = $lastDrawLocal->copy()->addMinutes($intervalMinutes);
|
||||
while ($drawLocal <= $nowLocal) {
|
||||
$drawLocal->addMinutes($intervalMinutes);
|
||||
}
|
||||
|
||||
$drawLocal = $day->copy()->addMinutes($seq * $intervalMinutes);
|
||||
while ($drawLocal <= $nowLocal) {
|
||||
$seq++;
|
||||
if ($seq > $maxSeqPerDay) {
|
||||
$day = $day->addDay();
|
||||
$seq = 1;
|
||||
}
|
||||
$drawLocal = $day->copy()->addMinutes($seq * $intervalMinutes);
|
||||
}
|
||||
$businessDate = $drawLocal->format('Y-m-d');
|
||||
$lastDay = Carbon::parse((string) $last->business_date, $tz)->format('Y-m-d');
|
||||
$seq = $businessDate === $lastDay
|
||||
? (int) $last->sequence_no + 1
|
||||
: 1;
|
||||
|
||||
return [
|
||||
'business_date' => $day->format('Y-m-d'),
|
||||
'business_date' => $businessDate,
|
||||
'sequence_no' => $seq,
|
||||
'draw_local' => $drawLocal->copy()->timezone($tz),
|
||||
];
|
||||
@@ -127,37 +131,22 @@ final class DrawPlannerService
|
||||
*/
|
||||
private function timelinePayload(array $row, Carbon $nowUtc): array
|
||||
{
|
||||
$closeBefore = (int) config('lottery.draw.close_before_draw_seconds', 30);
|
||||
$bettingWindow = (int) config('lottery.draw.betting_window_seconds', 270);
|
||||
|
||||
$drawLocal = $row['draw_local']->copy();
|
||||
|
||||
$closeLocal = $drawLocal->copy()->subSeconds($closeBefore);
|
||||
$startLocal = $closeLocal->copy()->subSeconds($bettingWindow);
|
||||
|
||||
$startUtc = $startLocal->copy()->timezone('UTC');
|
||||
$closeUtc = $closeLocal->copy()->timezone('UTC');
|
||||
$drawUtc = $drawLocal->copy()->timezone('UTC');
|
||||
|
||||
if ($nowUtc < $startUtc) {
|
||||
$status = DrawStatus::Pending->value;
|
||||
} elseif ($nowUtc < $closeUtc) {
|
||||
$status = DrawStatus::Open->value;
|
||||
} elseif ($nowUtc < $drawUtc) {
|
||||
$status = DrawStatus::Closing->value;
|
||||
} else {
|
||||
$status = DrawStatus::Closed->value;
|
||||
}
|
||||
$windows = $this->timeline->windowsFromDrawLocal($row['draw_local']->copy());
|
||||
$built = $this->timeline->buildFromLocals(
|
||||
$windows['start_local'],
|
||||
$windows['close_local'],
|
||||
$windows['draw_local'],
|
||||
$nowUtc,
|
||||
);
|
||||
|
||||
return [
|
||||
'draw_no' => str_replace('-', '', $row['business_date']).'-'.
|
||||
str_pad((string) $row['sequence_no'], 3, '0', STR_PAD_LEFT),
|
||||
'draw_no' => $this->timeline->drawNo($row['business_date'], $row['sequence_no']),
|
||||
'business_date' => $row['business_date'],
|
||||
'sequence_no' => $row['sequence_no'],
|
||||
'status' => $status,
|
||||
'start_time' => $startLocal->copy()->timezone('UTC'),
|
||||
'close_time' => $closeLocal->copy()->timezone('UTC'),
|
||||
'draw_time' => $drawLocal->copy()->timezone('UTC'),
|
||||
'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,
|
||||
|
||||
75
app/Services/Draw/DrawTimelineBuilder.php
Normal file
75
app/Services/Draw/DrawTimelineBuilder.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Draw;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use App\Lottery\DrawStatus;
|
||||
|
||||
/**
|
||||
* 由开奖时刻推导下注窗口、封盘时刻与期号初始状态。
|
||||
*/
|
||||
final class DrawTimelineBuilder
|
||||
{
|
||||
public function closeBeforeDrawSeconds(): int
|
||||
{
|
||||
return (int) config('lottery.draw.close_before_draw_seconds', 30);
|
||||
}
|
||||
|
||||
public function bettingWindowSeconds(): int
|
||||
{
|
||||
return (int) config('lottery.draw.betting_window_seconds', 270);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{start_local: Carbon, close_local: Carbon, draw_local: Carbon}
|
||||
*/
|
||||
public function windowsFromDrawLocal(Carbon $drawLocal): array
|
||||
{
|
||||
$closeLocal = $drawLocal->copy()->subSeconds($this->closeBeforeDrawSeconds());
|
||||
$startLocal = $closeLocal->copy()->subSeconds($this->bettingWindowSeconds());
|
||||
|
||||
return [
|
||||
'start_local' => $startLocal,
|
||||
'close_local' => $closeLocal,
|
||||
'draw_local' => $drawLocal->copy(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{start_utc: Carbon, close_utc: Carbon, draw_utc: Carbon, status: string}
|
||||
*/
|
||||
public function buildFromLocals(Carbon $startLocal, Carbon $closeLocal, Carbon $drawLocal, Carbon $nowUtc): array
|
||||
{
|
||||
$startUtc = $startLocal->copy()->timezone('UTC');
|
||||
$closeUtc = $closeLocal->copy()->timezone('UTC');
|
||||
$drawUtc = $drawLocal->copy()->timezone('UTC');
|
||||
|
||||
return [
|
||||
'start_utc' => $startUtc,
|
||||
'close_utc' => $closeUtc,
|
||||
'draw_utc' => $drawUtc,
|
||||
'status' => $this->statusForTimeline($nowUtc, $startUtc, $closeUtc, $drawUtc),
|
||||
];
|
||||
}
|
||||
|
||||
public function statusForTimeline(Carbon $nowUtc, Carbon $startUtc, Carbon $closeUtc, Carbon $drawUtc): string
|
||||
{
|
||||
if ($nowUtc < $startUtc) {
|
||||
return DrawStatus::Pending->value;
|
||||
}
|
||||
if ($nowUtc < $closeUtc) {
|
||||
return DrawStatus::Open->value;
|
||||
}
|
||||
if ($nowUtc < $drawUtc) {
|
||||
return DrawStatus::Closing->value;
|
||||
}
|
||||
|
||||
return DrawStatus::Closed->value;
|
||||
}
|
||||
|
||||
public function drawNo(string $businessDate, int $sequenceNo): string
|
||||
{
|
||||
return str_replace('-', '', $businessDate).'-'.
|
||||
str_pad((string) $sequenceNo, 3, '0', STR_PAD_LEFT);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user