150 lines
7.0 KiB
PHP
150 lines
7.0 KiB
PHP
<?php
|
||
// arrive_ride.php
|
||
require_once __DIR__ . '/../../connect.php';
|
||
|
||
try {
|
||
$con_ride = Database::get('ride');
|
||
} catch (Exception $e) {
|
||
error_log("[arrive_ride] Failed to connect to Ride Database: " . $e->getMessage());
|
||
}
|
||
|
||
$rideId = filterRequest("ride_id");
|
||
$driverId = filterRequest("driver_id");
|
||
$passengerToken = filterRequest("passengerToken");
|
||
|
||
if (!$rideId || !$driverId) {
|
||
jsonError("Missing required parameters.");
|
||
exit;
|
||
}
|
||
|
||
// نصف قطر قبول ختم الوصول. أوسع من فحص الـ100م في التطبيق بهامش يستوعب
|
||
// ضعف الـGPS بين المباني، وأضيق من أن يسمح بالختم من حي آخر.
|
||
const ARRIVAL_STAMP_RADIUS_KM = 0.30;
|
||
|
||
try {
|
||
// 1. تحديث الحالة في السيرفر البعيد (Remote DB - con_ride)
|
||
$stmtRemote = $con_ride->prepare("UPDATE ride SET status = 'arrived', updated_at = NOW() WHERE id = ? AND driver_id = ? AND status = 'Apply'");
|
||
$stmtRemote->execute([$rideId, $driverId]);
|
||
|
||
// 2. تحديث الحالة في السيرفر المحلي (Local DB - con)
|
||
if (isset($con)) {
|
||
$stmtLocal = $con->prepare("UPDATE ride SET status = 'arrived', updated_at = NOW() WHERE id = ? AND driver_id = ? AND status = 'Apply'");
|
||
$stmtLocal->execute([$rideId, $driverId]);
|
||
}
|
||
|
||
// ─────────────────────────────────────────────────────────────
|
||
// 2.1 ختم لحظة الوصول — أساس تعويض عدم حضور الراكب
|
||
//
|
||
// فحص المسافة في التطبيق (100م) على العميل، فيمكن تجاوزه بموقع مزيَّف
|
||
// ثم انتظار خمس دقائق وقبض تعويض بلا وجود حقيقي. لذلك يتحقق الخادم
|
||
// هنا بنفسه من موقع السائق في Redis قبل أن يختم الوقت.
|
||
//
|
||
// إن تعذّر التحقق (Redis غير متاح أو الموقع مفقود) لا نمنع الوصول —
|
||
// الحالة تُحدَّث والراكب يُشعَر كالمعتاد — لكن لا نختم وقتاً يترتب عليه
|
||
// مال لا نستطيع إثباته. النتيجة: لا تعويض لهذه الرحلة، وسجلّ واضح.
|
||
// ─────────────────────────────────────────────────────────────
|
||
try {
|
||
$stmtRide = $con->prepare("SELECT * FROM ride WHERE id = ? AND driver_id = ?");
|
||
$stmtRide->execute([$rideId, $driverId]);
|
||
$rideRow = $stmtRide->fetch(PDO::FETCH_ASSOC);
|
||
|
||
if (is_array($rideRow)
|
||
&& array_key_exists('driverArrivedAt', $rideRow)
|
||
&& empty($rideRow['driverArrivedAt'])) {
|
||
|
||
[$pLat, $pLng] = array_pad(
|
||
array_map('trim', explode(',', (string) $rideRow['start_location'], 2)), 2, null
|
||
);
|
||
|
||
$canStamp = false;
|
||
if (isset($redisLocation) && $redisLocation
|
||
&& is_numeric($pLat) && is_numeric($pLng)) {
|
||
|
||
$loc = $redisLocation->hGetAll("driver:public:$driverId");
|
||
if (!empty($loc['lat']) && !empty($loc['lng'])) {
|
||
$gap = getDistanceBetweenPoints(
|
||
(float) $loc['lat'], (float) $loc['lng'],
|
||
(float) $pLat, (float) $pLng
|
||
);
|
||
$canStamp = ($gap <= ARRIVAL_STAMP_RADIUS_KM);
|
||
if (!$canStamp) {
|
||
error_log("[arrive_ride] ختم الوصول مرفوض — السائق $driverId"
|
||
. " يبعد " . round($gap, 3) . "كم عن نقطة الالتقاط (ride=$rideId)");
|
||
}
|
||
}
|
||
}
|
||
|
||
if ($canStamp) {
|
||
// WHERE driverArrivedAt IS NULL يجعله ذرّياً: نداء ثانٍ لا
|
||
// يعيد ضبط عدّاد الانتظار.
|
||
$sqlStamp = "UPDATE ride SET driverArrivedAt = NOW()
|
||
WHERE id = ? AND driver_id = ? AND driverArrivedAt IS NULL";
|
||
$con->prepare($sqlStamp)->execute([$rideId, $driverId]);
|
||
try {
|
||
$con_ride->prepare($sqlStamp)->execute([$rideId, $driverId]);
|
||
} catch (PDOException $e) { /* primary هو المرجع لهذا الحقل */ }
|
||
} else {
|
||
error_log("[arrive_ride] تعذّر إثبات الوصول — لا تعويض لهذه"
|
||
. " الرحلة (ride=$rideId driver=$driverId)");
|
||
}
|
||
}
|
||
} catch (PDOException $eStamp) {
|
||
// العمود غير مُرحَّل بعد — الوصول يعمل، التعويض معطّل.
|
||
error_log("[arrive_ride] تعذّر ختم driverArrivedAt (شغّل"
|
||
. " migrations/2026_08_07_driver_cancel_compensation.sql): " . $eStamp->getMessage());
|
||
}
|
||
|
||
// 3. جلب بيانات الراكب للإرسال
|
||
// نستخدم con_ride لضمان الدقة
|
||
$stmtPas = $con_ride->prepare("SELECT passenger_id FROM ride WHERE id = ?");
|
||
$stmtPas->execute([$rideId]);
|
||
$passenger_id = $stmtPas->fetchColumn();
|
||
|
||
if ($passenger_id) {
|
||
|
||
// أ) إرسال Socket (الأسرع)
|
||
$payload = [
|
||
'status' => 'arrived',
|
||
'ride_id' => $rideId,
|
||
'msg' => 'السائق وصل إلى موقعك 🚖'
|
||
];
|
||
|
||
if (function_exists('notifyPassengerOnRideServer')) {
|
||
notifyPassengerOnRideServer($passenger_id, $payload);
|
||
}
|
||
|
||
// ب) إرسال FCM (باستخدام الدالة الجديدة)
|
||
if (!empty($passengerToken)) {
|
||
$fcmData = [
|
||
'category' => 'Arrive Ride', // نفس الاسم القديم لضمان عمل التطبيق
|
||
'ride_id' => (string)$rideId,
|
||
'status' => 'arrived'
|
||
];
|
||
|
||
// 🔥 استخدام sendFCM_Internal كرسالة صامتة
|
||
sendFCM_Internal(
|
||
$passengerToken, // الهدف
|
||
"", // تفريغ العنوان
|
||
"", // تفريغ النص
|
||
$fcmData, // البيانات
|
||
"Arrive Ride", // التصنيف
|
||
false // ليس Topic
|
||
);
|
||
}
|
||
}
|
||
|
||
// 🆕 Cache ride state in Redis
|
||
sendToLocationServer('update_ride_state', [
|
||
'ride_id' => $rideId,
|
||
'status' => 'arrived',
|
||
'driver_id' => $driverId,
|
||
'passenger_id' => $passenger_id ?? '',
|
||
]);
|
||
|
||
jsonSuccess(null, "Arrival notified successfully");
|
||
|
||
} catch (Exception $e) {
|
||
error_log("[arrive_ride] " . $e->getMessage());
|
||
jsonError("Error notifying arrival");
|
||
}
|
||
?>
|