180 lines
5.5 KiB
PHP
180 lines
5.5 KiB
PHP
<?php
|
|
|
|
namespace Acme\Dhl\Services;
|
|
|
|
use Acme\Dhl\Support\DhlClient;
|
|
use Acme\Dhl\Models\DhlShipment;
|
|
use Acme\Dhl\Models\DhlTrackingEvent;
|
|
use InvalidArgumentException;
|
|
use Exception;
|
|
use Acme\Dhl\Jobs\SyncTrackingJob;
|
|
use Illuminate\Support\Facades\Log;
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
/**
|
|
* DHL Tracking Service for fetching and managing tracking information
|
|
*/
|
|
class TrackingService
|
|
{
|
|
public function __construct(protected DhlClient $client) {}
|
|
|
|
/**
|
|
* Fetch tracking status for a shipment
|
|
*
|
|
* @param string $trackingNumber DHL tracking number
|
|
* @return array Tracking data from DHL API
|
|
* @throws InvalidArgumentException When tracking number is empty
|
|
* @throws Exception When API request fails
|
|
*/
|
|
public function fetchStatus(string $trackingNumber): array
|
|
{
|
|
if (empty($trackingNumber)) {
|
|
throw new InvalidArgumentException('Tracking number is required');
|
|
}
|
|
if (config('dhl.use_queue')) {
|
|
SyncTrackingJob::dispatch($trackingNumber);
|
|
return ['queued' => true];
|
|
}
|
|
|
|
$response = $this->client->request('get', '/post-tracking/api/shipments', [], [
|
|
'trackingNumber' => $trackingNumber
|
|
]);
|
|
Log::info('Fetched tracking status', ['trackingNumber' => $trackingNumber]);
|
|
|
|
$events = data_get($response, 'shipments.0.events', []);
|
|
$shipment = $this->findOrCreateShipment($trackingNumber);
|
|
|
|
$this->updateTrackingEvents($shipment, $events);
|
|
|
|
return $response;
|
|
}
|
|
|
|
/**
|
|
* Update tracking status for multiple shipments
|
|
*
|
|
* @param array $trackingNumbers Array of tracking numbers to update
|
|
* @return array Results for each tracking number
|
|
*/
|
|
public function updateMultipleStatus(array $trackingNumbers): array
|
|
{
|
|
$results = [];
|
|
|
|
foreach ($trackingNumbers as $trackingNumber) {
|
|
try {
|
|
$results[$trackingNumber] = $this->fetchStatus($trackingNumber);
|
|
} catch (Exception $e) {
|
|
Log::error('Tracking update failed', ['trackingNumber' => $trackingNumber, 'error' => $e->getMessage()]);
|
|
$results[$trackingNumber] = ['error' => $e->getMessage()];
|
|
}
|
|
}
|
|
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Get latest tracking status for a shipment
|
|
*
|
|
* @param string $trackingNumber DHL tracking number
|
|
* @return array|null Latest tracking event or null if not found
|
|
*/
|
|
public function getLatestStatus(string $trackingNumber): ?array
|
|
{
|
|
$shipment = DhlShipment::where('dhl_shipment_no', $trackingNumber)->first();
|
|
|
|
if (!$shipment) {
|
|
return null;
|
|
}
|
|
|
|
$latestEvent = $shipment->trackingEvents()
|
|
->orderBy('event_time', 'desc')
|
|
->first();
|
|
|
|
return $latestEvent ? $latestEvent->toArray() : null;
|
|
}
|
|
|
|
/**
|
|
* Find existing shipment or create new one
|
|
*/
|
|
private function findOrCreateShipment(string $trackingNumber): DhlShipment
|
|
{
|
|
return DB::transaction(function () use ($trackingNumber) {
|
|
return DhlShipment::firstOrCreate(
|
|
['dhl_shipment_no' => $trackingNumber],
|
|
[
|
|
'type' => 'outbound',
|
|
'status' => 'unknown'
|
|
]
|
|
);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Update tracking events for a shipment
|
|
*/
|
|
private function updateTrackingEvents(DhlShipment $shipment, array $events): void
|
|
{
|
|
foreach ($events as $eventData) {
|
|
$this->createOrUpdateTrackingEvent($shipment, $eventData);
|
|
}
|
|
|
|
// Update shipment status based on latest event
|
|
$this->updateShipmentStatus($shipment, $events);
|
|
}
|
|
|
|
/**
|
|
* Create or update a tracking event
|
|
*/
|
|
private function createOrUpdateTrackingEvent(DhlShipment $shipment, array $eventData): void
|
|
{
|
|
DhlTrackingEvent::updateOrCreate(
|
|
[
|
|
'shipment_id' => $shipment->id,
|
|
'status_code' => $eventData['statusCode'] ?? null,
|
|
'event_time' => $eventData['timestamp'] ?? null
|
|
],
|
|
[
|
|
'status_text' => $eventData['status'] ?? null,
|
|
'location' => $this->extractLocation($eventData),
|
|
'raw' => $eventData
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Extract location from event data
|
|
*/
|
|
private function extractLocation(array $eventData): ?string
|
|
{
|
|
return data_get($eventData, 'location.address.addressLocality')
|
|
?? data_get($eventData, 'location')
|
|
?? null;
|
|
}
|
|
|
|
/**
|
|
* Update shipment status based on latest tracking event
|
|
*/
|
|
private function updateShipmentStatus(DhlShipment $shipment, array $events): void
|
|
{
|
|
if (empty($events)) {
|
|
return;
|
|
}
|
|
|
|
// Sort events by timestamp to get the latest
|
|
usort($events, function ($a, $b) {
|
|
return strtotime($b['timestamp'] ?? '0') <=> strtotime($a['timestamp'] ?? '0');
|
|
});
|
|
|
|
$latestEvent = $events[0];
|
|
$statusCode = $latestEvent['statusCode'] ?? null;
|
|
|
|
// Map DHL status codes to our internal status
|
|
$status = DhlShipment::STATUS_MAP[$statusCode] ?? 'unknown';
|
|
|
|
$shipment->update([
|
|
'status' => $status,
|
|
'tracking_status' => $latestEvent['status'] ?? null,
|
|
'last_tracked_at' => now()
|
|
]);
|
|
Log::info('Updated shipment status', ['shipmentId' => $shipment->id, 'newStatus' => $status]);
|
|
}
|
|
}
|