|
|
<?php
|
|
|
|
|
|
namespace App\Services;
|
|
|
|
|
|
use App\Models\Reservation;
|
|
|
use Carbon\Carbon;
|
|
|
use Illuminate\Support\Collection;
|
|
|
|
|
|
class ReservationExpiryService
|
|
|
{
|
|
|
/**
|
|
|
* 将「活动日/入馆日历日已过当日 23:59:59、仍为待核销、未核销」的预约标记为 expired。
|
|
|
* 均以应用时区的**日历日**为准:活动日当天(含整日至 23:59:59)仍可待核销,自**次日 0:00** 起视为已过活动日。
|
|
|
* - 普通活动:以 activity_days.activity_date 对应日历日为准(不再按场次 session_end_at 提前过期)。
|
|
|
* - 抢票:以 entry_date 对应日历日为准。
|
|
|
*/
|
|
|
public function expireStalePendingReservations(): int
|
|
|
{
|
|
|
$tz = (string) config('app.timezone');
|
|
|
$today = Carbon::now($tz)->toDateString();
|
|
|
|
|
|
$ids = $this->stalePendingReservationIds($today);
|
|
|
if ($ids->isEmpty()) {
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
return Reservation::query()->whereIn('id', $ids)->update([
|
|
|
'status' => 'expired',
|
|
|
'updated_at' => now(),
|
|
|
]);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @return Collection<int, int>
|
|
|
*/
|
|
|
private function stalePendingReservationIds(string $today): Collection
|
|
|
{
|
|
|
$idsActivity = Reservation::query()
|
|
|
->join('activity_days', 'activity_days.id', '=', 'reservations.activity_day_id')
|
|
|
->where('reservations.status', 'pending')
|
|
|
->whereNull('reservations.verified_at')
|
|
|
->whereNotNull('reservations.activity_day_id')
|
|
|
->whereDate('activity_days.activity_date', '<', $today)
|
|
|
->where(function ($q) {
|
|
|
$q->whereNull('reservations.reservation_kind')
|
|
|
->orWhere('reservations.reservation_kind', Reservation::KIND_ACTIVITY);
|
|
|
})
|
|
|
->pluck('reservations.id');
|
|
|
|
|
|
$idsTicketGrab = Reservation::query()
|
|
|
->where('reservation_kind', Reservation::KIND_TICKET_GRAB)
|
|
|
->where('status', 'pending')
|
|
|
->whereNull('verified_at')
|
|
|
->whereNotNull('entry_date')
|
|
|
->whereDate('entry_date', '<', $today)
|
|
|
->pluck('id');
|
|
|
|
|
|
return $idsActivity->merge($idsTicketGrab)->unique()->values();
|
|
|
}
|
|
|
}
|