سوكيت الراكب (سبب عدم ظهور معلومات السائق عند القبول): - تطبيق الراكب كان يرسل id فقط بلا jwt، و passenger_socket.php يرفض أي اتصال بلا jwt ⇒ الراكب لا ينضم لغرفته أبداً ولا يستلم ride_status_change ولا driver_location_update. تظهر حالة القبول عبر الـ polling فقط بينما driver_info يصل بالسوكيت وحده. (سوكيت السائق يعتبر الـ jwt اختيارياً، ومن هنا جاء التباين بين التطبيقين.) - cancelled_by_driver كان يسقط من switch حالات الراكب فيبقى معلّقاً بعد إلغاء السائق. - حماية socket (late) من القراءة قبل التهيئة عند الانسحاب بلا jwt. الإشعارات والرسائل (سبب "مرات توصل ومرات لا"): - جدول tokens يخزّن توكن الراكب مشفّراً، و getRideWaiting.php كان يرجعه بلا فك تشفير ⇒ من يقبل من قائمة السوق يحمل blob مشفّراً يستخدمه كـ FCM target فيرفضه FCM بـ 400: لا إشعار قبول ولا رسائل. ومن يقبل من الـ dispatch/FCM يحمل نصاً صريحاً فتعمل. الفرق كان في طريقة القبول. - acceptRide.php يحلّ التوكن من القاعدة دائماً ولا يثق بالعميل (أصحّ أمنياً). - market_new_ride كان لا يحمل passengerId ولا الإحداثيات فتصل "null"؛ أُضيفت بلا أي PII لأن الحمولة تُبَثّ لكل سائق قريب لا للفائز فقط. - send_fcm.php: مهلة على OAuth (كان يعلّق حتى مهلة PHP فتُسقط الرسالة بصمت)، توحيد ding→default لأندرويد، وحقن title/body/tone في data مطابقةً لـ FcmService. - تطبيق السائق يقرأ title/body من data أولاً مثل الراكب، ولا يعرض فقاعة فارغة للرسائل الصامتة. السوكيت والإعدادات: - forwardLocationToPassengerSocket كان يقرأ lat/lng والحمولة فيها latitude/longitude ⇒ المسافة تخرج ضخمة والـ throttle معطّل تماماً فيُعاد التوجيه مع كل نبضة GPS. - notifyPassengerOnRideServer كان يرجع null بصمت مطلق عند حجب العنوان. - العنوان الافتراضي لسيرفر الموقع كان nginx/loction_server/driver_socket.php وهو ديمون Workerman لا يُخدَم عبر nginx ⇒ صار socket_driver:2021. (LOCATION_API_URL بقي على nginx لأن api_get_nearby.php سكربت عادي.) - ride_server/passenger_socket.php (النسخة التي يشغّلها Docker) كانت ناقصة كل كود مواصلاتي الموجود في passenger_server/ ⇒ نُقل مع REDIS_HOST. - .env.example: ALLOWED_SOCKET_URLS يغطّي أسماء حاويات Docker، وإضافة PASSENGER_SOCKET_INTERNAL_URL. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
177 lines
6.9 KiB
PHP
177 lines
6.9 KiB
PHP
<?php
|
|
// getRideWaiting.php
|
|
require_once __DIR__ . '/../../connect.php';
|
|
|
|
$lat = filterRequest("lat");
|
|
$lng = filterRequest("lng");
|
|
$radius = filterRequest("radius");
|
|
|
|
if (empty($lat) || empty($lng)) {
|
|
jsonSuccess([]);
|
|
exit;
|
|
}
|
|
|
|
if (empty($radius)) {
|
|
$radius = 50;
|
|
}
|
|
|
|
$finalRides = [];
|
|
$rideIds = [];
|
|
$redisResultsMap = [];
|
|
|
|
// 1. محاولة البحث عبر Redis
|
|
try {
|
|
$locationServerUrl = getenv('LOCATION_SOCKET_URL') ?: 'http://socket_driver:2021';
|
|
if (strpos($locationServerUrl, 'localhost') !== false || strpos($locationServerUrl, '127.0.0.1') !== false) {
|
|
if (file_exists('/.dockerenv')) {
|
|
$locationServerUrl = str_replace(['localhost', '127.0.0.1'], 'socket_driver', $locationServerUrl);
|
|
}
|
|
}
|
|
$INTERNAL_KEY = function_exists('getInternalSocketKey') ? getInternalSocketKey() : '';
|
|
|
|
$postData = [
|
|
'action' => 'get_nearby_ride_ids',
|
|
'lat' => $lat,
|
|
'lng' => $lng,
|
|
'radius' => $radius
|
|
];
|
|
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $locationServerUrl);
|
|
curl_setopt($ch, CURLOPT_POST, 1);
|
|
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData));
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 500);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-internal-key: $INTERNAL_KEY"]);
|
|
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode == 200 && $response) {
|
|
$jsonResults = json_decode($response, true);
|
|
if (is_array($jsonResults) && !empty($jsonResults)) {
|
|
foreach ($jsonResults as $res) {
|
|
$rideIds[] = $res[0];
|
|
$redisResultsMap[$res[0]] = $res[1];
|
|
}
|
|
}
|
|
}
|
|
} catch (Exception $e) {
|
|
// نتابع للخطة ب
|
|
}
|
|
|
|
// 2. جلب البيانات (إما عبر IDs أو بحث مباشر)
|
|
try {
|
|
if (!empty($rideIds)) {
|
|
// --- الحالة أ: الريدز وجد رحلات ---
|
|
$placeholders = implode(',', array_fill(0, count($rideIds), '?'));
|
|
|
|
$sql = "
|
|
SELECT
|
|
wr.id, wr.start_location AS startName, wr.end_location AS endName,
|
|
wr.date, wr.time, wr.price, wr.passenger_id, wr.status, wr.carType,
|
|
wr.passengerRate, wr.created_at, wr.price_for_passenger,
|
|
wr.distance, wr.duration, wr.start_lat, wr.start_lng,
|
|
wr.end_lat, wr.end_lng, wr.payment_method, wr.passenger_wallet,
|
|
p.email, p.first_name, p.phone, p.id AS passengerId, t.token AS passengerToken
|
|
FROM waitingRides wr
|
|
INNER JOIN passengers p ON p.id = wr.passenger_id
|
|
LEFT JOIN tokens t ON t.passengerID = wr.passenger_id
|
|
WHERE wr.id IN ($placeholders) AND wr.status IN ('wait', 'waiting')
|
|
";
|
|
|
|
$stmt = $con->prepare($sql);
|
|
$stmt->execute($rideIds);
|
|
$waitingRides = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
} else {
|
|
// --- الحالة ب: بحث مباشر MySQL (Fallback) ---
|
|
// 🔥 التصحيح هنا: استخدام أسماء فريدة (:lat1, :lat2) لتجنب خطأ التكرار
|
|
|
|
$haversine = "( 6371 * acos( cos( radians(:lat1) ) * cos( radians( wr.start_lat ) ) * cos( radians( wr.start_lng ) - radians(:lng) ) + sin( radians(:lat2) ) * sin( radians( wr.start_lat ) ) ) )";
|
|
|
|
$sql = "
|
|
SELECT
|
|
wr.id, wr.start_location AS startName, wr.end_location AS endName,
|
|
wr.date, wr.time, wr.price, wr.passenger_id, wr.status, wr.carType,
|
|
wr.passengerRate, wr.created_at, wr.price_for_passenger,
|
|
wr.distance, wr.duration, wr.start_lat, wr.start_lng,
|
|
wr.end_lat, wr.end_lng, wr.payment_method, wr.passenger_wallet,
|
|
p.email, p.first_name, p.phone, p.id AS passengerId, t.token AS passengerToken,
|
|
{$haversine} AS driver_distance_km
|
|
FROM waitingRides wr
|
|
INNER JOIN passengers p ON p.id = wr.passenger_id
|
|
LEFT JOIN tokens t ON t.passengerID = wr.passenger_id
|
|
WHERE
|
|
wr.status IN ('wait', 'waiting')
|
|
AND wr.created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
|
|
AND wr.start_lat IS NOT NULL
|
|
HAVING driver_distance_km <= :radius
|
|
ORDER BY driver_distance_km ASC
|
|
LIMIT 50
|
|
";
|
|
|
|
$stmt = $con->prepare($sql);
|
|
|
|
// نمرر القيمة مرتين للمفتاحين المختلفين
|
|
$stmt->execute([
|
|
':lat1' => $lat,
|
|
':lng' => $lng,
|
|
':lat2' => $lat, // تكرار القيمة للمتغير الثاني
|
|
':radius' => $radius
|
|
]);
|
|
|
|
$waitingRides = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
}
|
|
|
|
// 3. التنسيق
|
|
foreach ($waitingRides as $ride) {
|
|
$ride['phone'] = $encryptionHelper->decryptData($ride['phone'] ?? '');
|
|
$ride['first_name'] = $encryptionHelper->decryptData($ride['first_name'] ?? '');
|
|
$ride['email'] = $encryptionHelper->decryptData($ride['email'] ?? '');
|
|
|
|
// جدول tokens يخزّن التوكن مشفّراً (ride/firebase/addToken.php). كان
|
|
// يُرجَع هنا كما هو، فيصل تطبيق السائق blob مشفّر يستخدمه كـ FCM target
|
|
// في كل رسالة للراكب ⇒ رفض 400 من FCM ولا يصل الراكب شيء.
|
|
if (!empty($ride['passengerToken'])) {
|
|
$decodedPassengerToken = false;
|
|
try {
|
|
$decodedPassengerToken = $encryptionHelper->decryptData($ride['passengerToken']);
|
|
} catch (\Throwable $eTok) {
|
|
$decodedPassengerToken = false;
|
|
}
|
|
if ($decodedPassengerToken !== false && $decodedPassengerToken !== null && $decodedPassengerToken !== '') {
|
|
$ride['passengerToken'] = trim($decodedPassengerToken);
|
|
}
|
|
}
|
|
|
|
$ride['start_location'] = $ride['start_lat'] . ',' . $ride['start_lng'];
|
|
$ride['end_location'] = (!empty($ride['end_lat']))
|
|
? $ride['end_lat'] . ',' . $ride['end_lng']
|
|
: $ride['endName'];
|
|
|
|
$ride['id'] = (string)$ride['id'];
|
|
|
|
if (isset($ride['driver_distance_km'])) {
|
|
$ride['driver_distance_km'] = number_format((float)$ride['driver_distance_km'], 1);
|
|
} elseif (isset($redisResultsMap[$ride['id']])) {
|
|
$ride['driver_distance_km'] = number_format((float)$redisResultsMap[$ride['id']], 1);
|
|
} else {
|
|
$ride['driver_distance_km'] = "0.0";
|
|
}
|
|
|
|
$finalRides[] = $ride;
|
|
}
|
|
|
|
usort($finalRides, function($a, $b) {
|
|
return $a['driver_distance_km'] <=> $b['driver_distance_km'];
|
|
});
|
|
|
|
jsonSuccess($finalRides);
|
|
|
|
} catch (PDOException $e) {
|
|
error_log("DB Error getRideWaiting: " . $e->getMessage());
|
|
jsonError("Database error");
|
|
}
|
|
?>
|