|
|
<?php
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
use App\Models\TicketGrabEvent;
|
|
|
use App\Models\TicketGrabEventVenue;
|
|
|
use App\Models\TicketGrabVenueReleaseDay;
|
|
|
use Carbon\Carbon;
|
|
|
use Carbon\CarbonPeriod;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
use Illuminate\Validation\ValidationException;
|
|
|
|
|
|
class TicketGrabReleaseDayService
|
|
|
{
|
|
|
/**
|
|
|
* 将 total 均分至 n 天,不能整除时余量依次分给前 n 个日期各 +1,等价于前 remainder 天多 1 张(此处按「多给前面日期」与需求「多的分配到第一日」一致为:余数 r 个日期各 +1,从第 0 天开始;即 [ceil, floor, floor, ...])。
|
|
|
*
|
|
|
* @return int[]
|
|
|
*/
|
|
|
public static function equalSplit(int $total, int $n): array
|
|
|
{
|
|
|
if ($n <= 0) {
|
|
|
return [];
|
|
|
}
|
|
|
if ($total < 0) {
|
|
|
$total = 0;
|
|
|
}
|
|
|
$base = intdiv($total, $n);
|
|
|
$r = $total % $n;
|
|
|
$out = array_fill(0, $n, $base);
|
|
|
for ($i = 0; $i < $r; $i++) {
|
|
|
$out[$i]++;
|
|
|
}
|
|
|
|
|
|
return $out;
|
|
|
}
|
|
|
|
|
|
public static function syncCarryInChain(int $eventId, ?int $venueId = null): void
|
|
|
{
|
|
|
$q = TicketGrabEventVenue::query()->where('ticket_grab_event_id', $eventId);
|
|
|
if ($venueId !== null) {
|
|
|
$q->where('venue_id', $venueId);
|
|
|
}
|
|
|
$pivots = $q->get();
|
|
|
foreach ($pivots as $p) {
|
|
|
$days = TicketGrabVenueReleaseDay::query()
|
|
|
->where('ticket_grab_event_id', $eventId)
|
|
|
->where('venue_id', $p->venue_id)
|
|
|
->orderBy('release_date')
|
|
|
->get();
|
|
|
|
|
|
$tz = (string) config('app.timezone');
|
|
|
$today = Carbon::now($tz)->toDateString();
|
|
|
$run = 0;
|
|
|
foreach ($days as $d) {
|
|
|
$d->carry_in = $run;
|
|
|
$raw = (int) $d->carry_in + (int) $d->day_quota - (int) $d->booked_count;
|
|
|
$rem = max(0, $raw);
|
|
|
$rd = $d->release_date instanceof Carbon
|
|
|
? $d->release_date->format('Y-m-d')
|
|
|
: Carbon::parse($d->release_date)->format('Y-m-d');
|
|
|
// 仅当该放票日已早于「今天」才把当日余量滚入下一天;当日与未来的日期不提前把余量算进次日
|
|
|
if ($rd < $today) {
|
|
|
$run = $rem;
|
|
|
} else {
|
|
|
$run = 0;
|
|
|
}
|
|
|
$d->save();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 根据活动与预约起止、场馆及配额,重建每日放票行(会删除该活动下未删除场馆对应的旧行并插入新行,booked 清零 —— 见调用方在更新时的保护).
|
|
|
*/
|
|
|
public static function rebuildAllReleaseDaysForEvent(int $eventId, bool $preserveBooked = false): void
|
|
|
{
|
|
|
$event = TicketGrabEvent::query()->with('eventVenuePivots')->find($eventId);
|
|
|
if (! $event || ! $event->booking_start_at || ! $event->booking_end_at) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
$start = $event->booking_start_at->copy();
|
|
|
$end = $event->booking_end_at->copy();
|
|
|
if ($end->lt($start)) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
$dates = [];
|
|
|
foreach (CarbonPeriod::create($start, $end) as $d) {
|
|
|
$dates[] = $d->toDateString();
|
|
|
}
|
|
|
$n = count($dates);
|
|
|
if ($n === 0) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
DB::transaction(function () use ($event, $eventId, $dates, $n, $preserveBooked) {
|
|
|
if (! $preserveBooked) {
|
|
|
TicketGrabVenueReleaseDay::query()
|
|
|
->where('ticket_grab_event_id', $eventId)
|
|
|
->delete();
|
|
|
}
|
|
|
$pivots = TicketGrabEventVenue::query()->where('ticket_grab_event_id', $eventId)->get();
|
|
|
foreach ($pivots as $p) {
|
|
|
$alloc = self::equalSplit((int) $p->venue_total_quota, $n);
|
|
|
if (! $preserveBooked) {
|
|
|
foreach ($dates as $i => $dateStr) {
|
|
|
TicketGrabVenueReleaseDay::query()->create([
|
|
|
'ticket_grab_event_id' => $eventId,
|
|
|
'venue_id' => $p->venue_id,
|
|
|
'release_date' => $dateStr,
|
|
|
'day_quota' => (int) ($alloc[$i] ?? 0),
|
|
|
'booked_count' => 0,
|
|
|
'carry_in' => 0,
|
|
|
]);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
self::syncCarryInChain($eventId);
|
|
|
});
|
|
|
|
|
|
if (! $preserveBooked) {
|
|
|
$sum = (int) TicketGrabEventVenue::query()
|
|
|
->where('ticket_grab_event_id', $eventId)
|
|
|
->sum('venue_total_quota');
|
|
|
TicketGrabEvent::query()->where('id', $eventId)->update(['total_quota' => $sum]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public static function updateDayQuotasFromAdmin(int $eventId, int $venueId, array $dateToQuota): void
|
|
|
{
|
|
|
$pivot = TicketGrabEventVenue::query()
|
|
|
->where('ticket_grab_event_id', $eventId)
|
|
|
->where('venue_id', $venueId)
|
|
|
->firstOrFail();
|
|
|
|
|
|
$sum = 0;
|
|
|
foreach ($dateToQuota as $date => $q) {
|
|
|
$sum += (int) $q;
|
|
|
TicketGrabVenueReleaseDay::query()
|
|
|
->where('ticket_grab_event_id', $eventId)
|
|
|
->where('venue_id', $venueId)
|
|
|
->whereDate('release_date', $date)
|
|
|
->update(['day_quota' => (int) $q]);
|
|
|
}
|
|
|
if ($sum !== (int) $pivot->venue_total_quota) {
|
|
|
throw ValidationException::withMessages([
|
|
|
'day_quota' => ["各日放票数之和必须等于该馆放票总数({$pivot->venue_total_quota})。"],
|
|
|
]);
|
|
|
}
|
|
|
self::syncCarryInChain($eventId, $venueId);
|
|
|
}
|
|
|
}
|