ifnex/04_Laravel/app/Http/Controllers/Api/TrackController.php
Kazem Alghasi 75fd65be2b feat(api): expand shipment data fields and enhance tracking security
Implement additional address and identification fields for both sender
and receiver across the WordPress plugin and Laravel API. This includes
state, postal code, and national ID/passport information.

Key changes:
- Added database migration to include postal and national ID fields in
  the shipments table.
- Updated WordPress order form shortcodes and JavaScript to capture
  new address fields.
- Refactored Laravel `CustomerOrderController` validation and storage
  to support expanded shipment data.
- Implemented tiered data visibility in `TrackController` to protect
  PII, providing masked data to anonymous users and full data to
  owners/admins.
- Fixed weight-based rate lookup logic in `PriceCalculatorService` to
  correctly select the appropriate shipping tier.
2026-10-29 00:51:50 +03:30

131 lines
5.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Shipment;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
/**
* API ترکینگ مرسولات — فاز ۰
*
* GET /api/v1/track/{awb_no}
*
* خروجی: تایم‌لاین کامل رویدادهای ترکینگ + اطلاعات پایه مرسوله + نگاشت‌های شرکت حمل
*
* امنیت: نمایش لایه‌بندی‌شده (Tiered Display)
* - کاربر ناشناس: فقط وضعیت فعلی و موقعیت
* - کاربر لاگین‌شده (مالک): اطلاعات کامل فرستنده/گیرنده (مخفف‌شده)
* - ادمین: اطلاعات کامل
*/
class TrackController extends Controller
{
/**
* دریافت تایم‌لاین ترکینگ یک مرسوله
*/
public function show(string $awb_no, Request $request): JsonResponse
{
$shipment = Shipment::where('awb_no', $awb_no)
->with([
'fromCountry:id,name,iso_code',
'toCountry:id,name,iso_code',
'carrierMappings:id,shipment_id,carrier_code,carrier_tracking_number,booking_number,direction,delivery_status,notes',
'trackingEvents' => fn ($query) => $query->orderBy('event_date', 'desc')->orderBy('event_time', 'desc'),
])
->first();
if (!$shipment) {
return response()->json([
'success' => false,
'message' => 'مرسوله‌ای با این کد یافت نشد',
'awb_no' => $awb_no,
], 404);
}
// بررسی سطح دسترسی کاربر
$user = $request->user();
$isOwner = $user && $shipment->user_id === $user->id;
$isAdmin = $user && $user->hasRole('super_admin');
// لایه ۱: اطلاعات عمومی (همیشه نمایش داده می‌شود)
$publicData = [
'shipment' => [
'awb_no' => $shipment->awb_no,
'direction' => $shipment->direction?->value,
'type' => $shipment->type?->value,
'status' => $shipment->status?->value,
'status_label' => $shipment->status?->label(),
'from_country' => $shipment->fromCountry?->name,
'to_country' => $shipment->toCountry?->name,
'weight' => $shipment->weight,
'created_at' => $shipment->created_at?->toIso8601String(),
],
'carrier_mappings' => $shipment->carrierMappings->map(fn ($mapping) => [
'carrier_code' => $mapping->carrier_code,
'carrier_tracking_number' => $mapping->carrier_tracking_number,
'booking_number' => $mapping->booking_number,
'delivery_status' => $mapping->delivery_status,
'notes' => $mapping->notes,
]),
'tracking_events' => $shipment->trackingEvents->map(fn ($event) => [
'date' => $event->event_date?->toDateString(),
'time' => $event->event_time ? ($event->event_time instanceof \Carbon\Carbon ? $event->event_time->format('H:i') : $event->event_time) : null,
'location' => $event->location,
'description' => $event->event_description,
'status' => $event->delivery_status,
'source' => $event->source,
]),
];
// لایه ۲: اطلاعات مالک (فقط اگه کاربر لاگین کرده و مالک مرسوله باشه)
if ($isOwner || $isAdmin) {
$publicData['shipment']['sender_name'] = $shipment->sender_name;
$publicData['shipment']['receiver_name'] = $shipment->receiver_name;
$publicData['shipment']['sender_phone'] = $shipment->sender_phone;
$publicData['shipment']['receiver_phone'] = $shipment->receiver_phone;
$publicData['shipment']['sender_address'] = $shipment->sender_address;
$publicData['shipment']['receiver_address'] = $shipment->receiver_address;
} else {
// برای کاربر ناشناس، فقط نام مخفف‌شده رو نشون بده
$publicData['shipment']['sender_name'] = $this->maskName($shipment->sender_name);
$publicData['shipment']['receiver_name'] = $this->maskName($shipment->receiver_name);
$publicData['shipment']['sender_phone'] = null;
$publicData['shipment']['receiver_phone'] = null;
$publicData['shipment']['sender_address'] = null;
$publicData['shipment']['receiver_address'] = null;
$publicData['shipment']['is_anonymous'] = true;
}
return response()->json([
'success' => true,
'data' => $publicData,
]);
}
/**
* مخفی‌سازی بخشی از نام برای کاربران ناشناس
* مثال: "Akbar Salmanizadeh" → "A. Sal***zadeh"
*/
private function maskName(?string $name): ?string
{
if (!$name) {
return null;
}
$parts = explode(' ', $name);
$first = substr($parts[0], 0, 1) . '.';
if (count($parts) > 1) {
$last = $parts[1];
if (strlen($last) > 4) {
$last = substr($last, 0, 3) . '***' . substr($last, -3);
} else {
$last = substr($last, 0, 1) . '***';
}
return $first . ' ' . $last;
}
return $first;
}
}