|
| 1 | +<?php |
| 2 | + |
| 3 | +declare(strict_types=1); |
| 4 | + |
| 5 | +namespace App\Jobs; |
| 6 | + |
| 7 | +use App\Models\RouteSegment; |
| 8 | +use App\Models\Stopover; |
| 9 | +use App\Repositories\TripRepository; |
| 10 | +use Illuminate\Bus\Queueable; |
| 11 | +use Illuminate\Contracts\Queue\ShouldQueue; |
| 12 | +use Illuminate\Foundation\Bus\Dispatchable; |
| 13 | +use Illuminate\Queue\InteractsWithQueue; |
| 14 | +use Illuminate\Queue\SerializesModels; |
| 15 | +use Illuminate\Support\Collection; |
| 16 | +use Illuminate\Support\Facades\Log; |
| 17 | +use romanzipp\QueueMonitor\Traits\IsMonitored; |
| 18 | + |
| 19 | +class DeleteRouteSegment implements ShouldQueue |
| 20 | +{ |
| 21 | + use Dispatchable, InteractsWithQueue, IsMonitored, Queueable, SerializesModels; |
| 22 | + |
| 23 | + public int $tries = 3; |
| 24 | + |
| 25 | + public int $backoff = 30; |
| 26 | + |
| 27 | + public int $timeout = 600; |
| 28 | + |
| 29 | + public function __construct(private readonly RouteSegment $segment) {} |
| 30 | + |
| 31 | + public function handle(TripRepository $tripRepository): void |
| 32 | + { |
| 33 | + $segment = $this->segment; |
| 34 | + $segmentId = $segment->id; |
| 35 | + $affectedTrips = []; |
| 36 | + $reassigned = 0; |
| 37 | + $nulled = 0; |
| 38 | + |
| 39 | + Stopover::where('route_segment_id', $segmentId) |
| 40 | + ->with('trip') |
| 41 | + ->chunkById(200, function (Collection $stopovers) use ( |
| 42 | + $segment, $segmentId, $tripRepository, &$affectedTrips, &$reassigned, &$nulled, |
| 43 | + ): void { |
| 44 | + $tripIds = $stopovers->pluck('trip_id')->unique(); |
| 45 | + $allTripStopovers = Stopover::whereIn('trip_id', $tripIds) |
| 46 | + ->orderBy('arrival_planned') |
| 47 | + ->get(['id', 'trip_id', 'train_station_id', 'arrival_planned', 'departure_planned']) |
| 48 | + ->groupBy('trip_id'); |
| 49 | + |
| 50 | + foreach ($stopovers as $fromStop) { |
| 51 | + $startTime = $fromStop->departure_planned ?? $fromStop->arrival_planned; |
| 52 | + $allStopoversForTrip = $allTripStopovers->get($fromStop->trip_id, collect()); |
| 53 | + |
| 54 | + // Find the immediately next stop in the trip after the fromStop. |
| 55 | + $nextStop = $allStopoversForTrip |
| 56 | + ->filter(function (Stopover $ts) use ($startTime): bool { |
| 57 | + if (!$startTime) { |
| 58 | + return true; |
| 59 | + } |
| 60 | + |
| 61 | + return $ts->arrival_planned?->gt($startTime) |
| 62 | + || $ts->departure_planned?->gt($startTime); |
| 63 | + }) |
| 64 | + ->sortBy('arrival_planned') |
| 65 | + ->first(); |
| 66 | + |
| 67 | + if (!$nextStop || $nextStop->train_station_id !== $segment->to_station_id) { |
| 68 | + // Can't determine next stop: null out assignment. |
| 69 | + $fromStop->route_segment_id = null; |
| 70 | + $fromStop->save(); |
| 71 | + $nulled++; |
| 72 | + $affectedTrips[$fromStop->trip_id] = true; |
| 73 | + |
| 74 | + continue; |
| 75 | + } |
| 76 | + |
| 77 | + $endTime = $nextStop->arrival_planned ?? $nextStop->departure_planned; |
| 78 | + |
| 79 | + if (!$startTime || !$endTime) { |
| 80 | + $fromStop->route_segment_id = null; |
| 81 | + $fromStop->save(); |
| 82 | + $nulled++; |
| 83 | + $affectedTrips[$fromStop->trip_id] = true; |
| 84 | + |
| 85 | + continue; |
| 86 | + } |
| 87 | + |
| 88 | + $duration = (int) round($startTime->diffInSeconds($endTime)); |
| 89 | + $pathType = $fromStop->trip?->category?->getORRProfile(); |
| 90 | + |
| 91 | + $replacement = $tripRepository->getRouteSegmentBetweenStops( |
| 92 | + start: $fromStop, |
| 93 | + end: $nextStop, |
| 94 | + duration: $duration, |
| 95 | + pathType: $pathType, |
| 96 | + excludeId: $segmentId, |
| 97 | + ); |
| 98 | + |
| 99 | + if ($replacement !== null) { |
| 100 | + $tripRepository->setRouteSegmentForStop($fromStop, $replacement); |
| 101 | + $reassigned++; |
| 102 | + } else { |
| 103 | + $fromStop->route_segment_id = null; |
| 104 | + $fromStop->save(); |
| 105 | + $nulled++; |
| 106 | + } |
| 107 | + |
| 108 | + $affectedTrips[$fromStop->trip_id] = true; |
| 109 | + } |
| 110 | + }); |
| 111 | + |
| 112 | + $segment->delete(); |
| 113 | + |
| 114 | + foreach (array_keys($affectedTrips) as $tripId) { |
| 115 | + RecalculateStatusesDistanceForTrip::dispatch($tripId); |
| 116 | + } |
| 117 | + |
| 118 | + Log::info('DeleteRouteSegment: Completed', [ |
| 119 | + 'segment_id' => $segmentId, |
| 120 | + 'reassigned' => $reassigned, |
| 121 | + 'nulled' => $nulled, |
| 122 | + 'trips_queued' => count($affectedTrips), |
| 123 | + ]); |
| 124 | + } |
| 125 | +} |
0 commit comments