feat(27-shipment-tracking-backend): infrastruktura sledzenia przesylek — statusy, tracking services, cron handler

Dwupoziomowy system statusow dostawy (normalized + raw z API), implementacje
trackingu dla InPost ShipX, Apaczka i Allegro WZA, cron handler odpytujacy
aktywne przesylki co 15 minut.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-23 20:33:44 +01:00
parent c59d431083
commit 228c0e96cf
17 changed files with 1365 additions and 27 deletions

View File

@@ -24,6 +24,14 @@ use App\Modules\Settings\ShopproProductImageResolver;
use App\Modules\Settings\ShopproPaymentStatusSyncService;
use App\Modules\Settings\ShopproStatusMappingRepository;
use App\Modules\Settings\ShopproStatusSyncService;
use App\Modules\Shipments\AllegroTrackingService;
use App\Modules\Shipments\ApaczkaTrackingService;
use App\Modules\Shipments\InpostTrackingService;
use App\Modules\Shipments\ShipmentPackageRepository;
use App\Modules\Shipments\ShipmentTrackingRegistry;
use App\Modules\Settings\ApaczkaApiClient;
use App\Modules\Settings\ApaczkaIntegrationRepository;
use App\Modules\Settings\InpostIntegrationRepository;
use PDO;
final class CronHandlerFactory
@@ -102,6 +110,22 @@ final class CronHandlerFactory
'shoppro_payment_status_sync' => new ShopproPaymentStatusSyncHandler(
$shopproPaymentSyncService
),
'shipment_tracking_sync' => new ShipmentTrackingHandler(
new ShipmentTrackingRegistry([
new InpostTrackingService(
new InpostIntegrationRepository($this->db, $this->integrationSecret)
),
new ApaczkaTrackingService(
new ApaczkaApiClient(),
new ApaczkaIntegrationRepository($this->db, $this->integrationSecret)
),
new AllegroTrackingService(
$apiClient,
$tokenManager
),
]),
new ShipmentPackageRepository($this->db)
),
]
);
}

View File

@@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace App\Modules\Cron;
use App\Modules\Shipments\ShipmentPackageRepository;
use App\Modules\Shipments\ShipmentTrackingRegistry;
use Throwable;
final class ShipmentTrackingHandler
{
public function __construct(
private readonly ShipmentTrackingRegistry $registry,
private readonly ShipmentPackageRepository $repository
) {
}
/**
* @param array<string, mixed> $payload
* @return array<string, mixed>
*/
public function handle(array $_payload): array
{
$packages = $this->repository->findActiveForTracking();
$total = count($packages);
$updated = 0;
$errors = 0;
foreach ($packages as $package) {
$provider = trim((string) ($package['provider'] ?? ''));
$packageId = (int) ($package['id'] ?? 0);
$service = $this->registry->getForProvider($provider);
if ($service === null) {
continue;
}
try {
$result = $service->getDeliveryStatus($package);
if ($result !== null) {
$this->repository->updateDeliveryStatus(
$packageId,
$result['status'],
$result['status_raw']
);
$updated++;
}
} catch (Throwable) {
$errors++;
}
}
return [
'total' => $total,
'updated' => $updated,
'errors' => $errors,
];
}
}