From f28264351e6dd6723b7119f20639322c82440be7 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Tue, 28 Apr 2026 15:34:43 +0300 Subject: [PATCH] 11 --- ride/rides/acceptRide.php | 320 +++++++++++++++--------------- ride/rides/add_ride.php | 403 +++++++++++++++----------------------- 2 files changed, 318 insertions(+), 405 deletions(-) diff --git a/ride/rides/acceptRide.php b/ride/rides/acceptRide.php index aee4706..459efbf 100755 --- a/ride/rides/acceptRide.php +++ b/ride/rides/acceptRide.php @@ -1,185 +1,181 @@ prepare(" - UPDATE `ride` - SET `status` = ?, `driver_id` = ?, `rideTimeStart` = NOW() - WHERE `id` = ? AND `status` IN ('waiting', 'wait') + // ═══════════════════════════════════════════════════════════ + // STEP A — القفل على ride DB (المرجع الأساسي) + // Optimistic lock: نغير فقط إذا status لا يزال 'waiting' أو 'wait' + // السائق الأول الذي يصل يربح — الباقي يجدون rowCount=0 + // ═══════════════════════════════════════════════════════════ + $stmtLock = $con_ride->prepare(" + UPDATE `ride` + SET `status` = ?, + `driver_id` = ?, + `rideTimeStart` = NOW() + WHERE `id` = ? + AND `status` IN ('waiting', 'wait') "); - $stmtRemote->execute([$status, $driverId, $rideId]); - - // Check if the update actually changed a row. - // If rowCount > 0, IT MEANS SUCCESS! This driver won the ride. - if ($stmtRemote->rowCount() > 0) { - - // 4. Synchronization: Update Local Database - // Now that we secured the ride, we update the main server's DB ($con) to match. - if (isset($con)) { - $stmtLocal = $con->prepare("UPDATE `ride` SET `driver_id` = ?, `status` = ?, `rideTimeStart` = NOW() WHERE id = ?"); - $stmtLocal->execute([$driverId, $status, $rideId]); - } + $stmtLock->execute([$status, $driverId, $rideId]); - // 5. Update/Insert Driver Orders Table - // This tracks the driver's history or active orders. - $checkSql = "SELECT `order_id` FROM `driver_orders` WHERE `order_id` = ?"; - $checkStmt = $con->prepare($checkSql); - $checkStmt->execute([$rideId]); + if ($stmtLock->rowCount() === 0) { + // الرحلة غير متاحة — سائق آخر سبق أو الرحلة ألغيت + error_log("[accept_ride] RideID=$rideId not available for DriverID=$driverId (rowCount=0)"); + printFailure("Ride not available"); + exit; + } - if ($checkStmt->rowCount() > 0) { - // If entry exists, update it - $updateSql = "UPDATE `driver_orders` SET `driver_id` = ?, `status` = ?, `created_at` = NOW() WHERE `order_id` = ?"; - $con->prepare($updateSql)->execute([$driverId, $status, $rideId]); - } else { - // If not, insert new record - $insertSql = "INSERT INTO `driver_orders` (`driver_id`, `order_id`, `created_at`, `status`) VALUES (?, ?, NOW(), ?)"; - $con->prepare($insertSql)->execute([$driverId, $rideId, $status]); - } + error_log("[accept_ride] ride DB locked. RideID=$rideId → DriverID=$driverId"); - // ================================================================= - // 6. 👤 GET DRIVER INFO (For the Passenger) - // We need to fetch driver details (Car, Name, Rating) to show to the passenger. - // ================================================================= - - $driverInfo = []; - - $sqlDetails = "SELECT - d.id as driver_id, - d.first_name, - d.last_name, - d.gender, - d.phone, - c.make, - c.model, - c.car_plate, - c.year, - c.color, + // ═══════════════════════════════════════════════════════════ + // STEP B — تزامن primary DB (بعد نجاح القفل) + // ═══════════════════════════════════════════════════════════ + try { + $con->prepare(" + UPDATE `ride` + SET `driver_id` = ?, + `status` = ?, + `rideTimeStart` = NOW() + WHERE `id` = ? + ")->execute([$driverId, $status, $rideId]); + error_log("[accept_ride] primary DB synced. RideID=$rideId"); + } catch (PDOException $eSync) { + // لا نوقف — ride DB هو المرجع + error_log("[accept_ride] primary DB sync WARNING: " . $eSync->getMessage()); + } + + // ═══════════════════════════════════════════════════════════ + // STEP C — driver_orders (INSERT أو UPDATE بسطر واحد آمن) + // ON DUPLICATE KEY يمنع race condition ثانية على هذا الجدول + // ═══════════════════════════════════════════════════════════ + try { + $con->prepare(" + INSERT INTO `driver_orders` (`driver_id`, `order_id`, `status`, `created_at`) + VALUES (?, ?, ?, NOW()) + ON DUPLICATE KEY UPDATE + `driver_id` = VALUES(`driver_id`), + `status` = VALUES(`status`), + `created_at` = NOW() + ")->execute([$driverId, $rideId, $status]); + } catch (PDOException $eOrders) { + error_log("[accept_ride] driver_orders WARNING: " . $eOrders->getMessage()); + } + + // ═══════════════════════════════════════════════════════════ + // STEP D — جلب بيانات السائق للراكب + // ═══════════════════════════════════════════════════════════ + $driverInfo = []; + + $stmtDriver = $con->prepare(" + SELECT + d.id AS driver_id, + d.first_name, + d.last_name, + d.gender, + d.phone, + c.make, + c.model, + c.car_plate, + c.year, + c.color, c.color_hex, (SELECT ROUND(AVG(rating), 2) FROM ratingDriver WHERE driver_id = d.id) AS ratingDriver, dt.token FROM driver d - LEFT JOIN CarRegistration c ON c.driverID = d.id - LEFT JOIN driverToken dt ON dt.captain_id = d.id - WHERE d.id = ?"; + LEFT JOIN CarRegistration c ON c.driverID = d.id + LEFT JOIN driverToken dt ON dt.captain_id = d.id + WHERE d.id = ? + LIMIT 1 + "); + $stmtDriver->execute([$driverId]); + $driverRaw = $stmtDriver->fetch(PDO::FETCH_ASSOC); - $stmtDetails = $con->prepare($sqlDetails); - $stmtDetails->execute([$driverId]); - $driverRawData = $stmtDetails->fetch(PDO::FETCH_ASSOC); - - if ($driverRawData) { - // List of encrypted fields that need decryption - $fieldsToDecrypt = ['first_name', 'last_name', 'gender', 'phone', 'car_plate', 'token']; - - foreach ($driverRawData as $key => $value) { - if (in_array($key, $fieldsToDecrypt) && !empty($value)) { - // Decrypt sensitive data - $driverInfo[$key] = $encryptionHelper->decryptData($value); - } else { - $driverInfo[$key] = $value; - } - } - - // Format Full Name - $driverInfo['driverName'] = trim(($driverInfo['first_name'] ?? '') . ' ' . ($driverInfo['last_name'] ?? '')); - - // Default rating if null - if (empty($driverInfo['ratingDriver'])) { - $driverInfo['ratingDriver'] = "5.0"; - } + if ($driverRaw) { + $encryptedFields = ['first_name', 'last_name', 'gender', 'phone', 'car_plate', 'token']; + foreach ($driverRaw as $key => $value) { + $driverInfo[$key] = (in_array($key, $encryptedFields) && !empty($value)) + ? $encryptionHelper->decryptData($value) + : $value; } - - // ================================================================= - // 7. 🔔 NOTIFY PASSENGER (Socket + FCM) - // Inform the passenger that a driver has been found. - // ================================================================= - - // Fetch Passenger ID based on Ride ID - $stmtPas = $con->prepare("SELECT passenger_id FROM ride WHERE id = ?"); - $stmtPas->execute([$rideId]); - $passenger_id = $stmtPas->fetchColumn(); - - if ($passenger_id) { - // A. Send Socket Notification (Real-time update on map) - if (function_exists('notifyPassengerOnRideServer')) { - notifyPassengerOnRideServer($passenger_id, [ - 'status' => 'accepted', - 'ride_id' => $rideId, - 'driver_id' => $driverId, - 'driver_info' => $driverInfo - ]); - } - - // B. Send FCM Notification (Push Notification) - if (!empty($passengerToken)) { - // Using the standardized FCM function - sendFCM_Internal( - $passengerToken, - "Ride Accepted 🚖", // Title - "Captain " . ($driverInfo['driverName'] ?? 'Driver') . " is coming to you.", // Body - ['ride_id' => (string)$rideId, 'driver_info' => $driverInfo], // Data Payload - "Accepted Ride", // Category - false // Not a topic - ); - } - } - - // ================================================================= - // 8. 🧹 MARKETPLACE CLEANUP (Notify Location Server) - // Crucial Step: We tell the Location Server that this ride is taken. - // The Location Server will: - // 1. Remove the ride from Redis (geo:rides:waiting). - // 2. Broadcast 'ride_taken' to other drivers to remove it from their screens. - // ================================================================= - sendToLocationServer('ride_taken_event', [ - 'ride_id' => $rideId, - 'taken_by_driver_id' => $driverId - ]); - - // 9. Final Response to the Driver App - echo json_encode([ - "status" => "success", - "message" => "Ride Accepted", - "data" => $driverInfo - ]); - - } else { - // Failure: This means rowCount was 0. - // Reason: The ride status was NOT 'waiting' (another driver took it milliseconds ago). - error_log("⛔ [ACCEPT_RIDE_FAIL] Row count 0 for RideID: '$rideId'. Status wasn't 'waiting'/'wait' or ID is wrong."); - jsonError("Ride not available (Already taken)"); + $driverInfo['driverName'] = trim(($driverInfo['first_name'] ?? '') . ' ' . ($driverInfo['last_name'] ?? '')); + $driverInfo['ratingDriver'] = $driverInfo['ratingDriver'] ?: "5.0"; } -} catch (Exception $e) { - // Handle unexpected errors - error_log("⛔ [ACCEPT_RIDE_EXCEPTION] " . $e->getMessage()); - jsonError("Error: " . $e->getMessage()); -} -?> \ No newline at end of file + // ═══════════════════════════════════════════════════════════ + // STEP E — جلب passenger_id وإرسال الإشعارات + // ═══════════════════════════════════════════════════════════ + $passengerId = $con->prepare("SELECT passenger_id FROM ride WHERE id = ? LIMIT 1"); + $passengerId->execute([$rideId]); + $passengerIdValue = $passengerId->fetchColumn(); + + if ($passengerIdValue) { + // Socket — real-time update على خريطة الراكب + if (function_exists('notifyPassengerOnRideServer')) { + notifyPassengerOnRideServer($passengerIdValue, [ + 'status' => 'accepted', + 'ride_id' => $rideId, + 'driver_id' => $driverId, + 'driver_info' => $driverInfo, + ]); + } + + // FCM — push notification + if (!empty($passengerToken)) { + sendFCM_Internal( + $passengerToken, + "تم قبول رحلتك", + "الكابتن " . ($driverInfo['driverName'] ?? '') . " في طريقه إليك", + ['ride_id' => (string) $rideId, 'driver_info' => $driverInfo], + "ride_accepted", + false + ); + } + } + + // ═══════════════════════════════════════════════════════════ + // STEP F — تنظيف السوق (أبلغ location server إن الرحلة محجوزة) + // ═══════════════════════════════════════════════════════════ + sendToLocationServer('ride_taken_event', [ + 'ride_id' => $rideId, + 'taken_by_driver_id' => $driverId, + ]); + + error_log("[accept_ride] SUCCESS. RideID=$rideId accepted by DriverID=$driverId"); + + // ═══════════════════════════════════════════════════════════ + // STEP G — رد النجاح للسائق (نفس بنية الرد القديمة) + // ═══════════════════════════════════════════════════════════ + echo json_encode([ + "status" => "success", + "message" => "Ride Accepted", + "data" => $driverInfo, + ]); + +} catch (PDOException $e) { + error_log("[accept_ride] CRITICAL: " . $e->getMessage()); + printFailure("Server error"); +} \ No newline at end of file diff --git a/ride/rides/add_ride.php b/ride/rides/add_ride.php index d78ff8b..a752215 100755 --- a/ride/rides/add_ride.php +++ b/ride/rides/add_ride.php @@ -1,270 +1,187 @@ (string)$rideId, - 'start_lat' => $lat, - 'start_lng' => $lng, - 'price' => $payloadData[2], // السعر - 'carType' => $payloadData[31], // نوع السيارة - 'startName' => $payloadData[29], // اسم موقع البدء - 'endName' => $payloadData[30], // اسم موقع الوصول - 'distance' => $payloadData[11], // المسافة - 'duration' => $payloadData[15], // الوقت - 'passengerRate' => $payloadData[33], // تقييم الراكب - // يمكنك إضافة المزيد هنا حسب الحاجة - ]; - - $postData = [ - 'action' => 'market_new_ride', // اسم الحدث في السوكيت - 'payload' => $marketPayload - ]; - - // إرسال الطلب (cURL) - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_POST, 1); - curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postData)); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - // وقت انتظار قصير جداً (200ms) لأننا لا نريد تأخير استجابة الراكب - curl_setopt($ch, CURLOPT_TIMEOUT_MS, 200); - curl_setopt($ch, CURLOPT_HTTPHEADER, ["x-internal-key: $INTERNAL_KEY"]); - curl_exec($ch); - curl_close($ch); -} - -// ================================================================================= -// 2. استقبال وتصفية البيانات من التطبيق -// ================================================================================= -$start_location = filterRequest("start_location"); -$end_location = filterRequest("end_location"); -$date_raw = filterRequest("date"); -$time_raw = filterRequest("time"); -$endtime_raw = filterRequest("endtime"); -$price = filterRequest("price"); -$passenger_id = filterRequest("passenger_id"); -$driver_id = filterRequest("driver_id") ?: 0; -$status = filterRequest("status"); -$price_for_driver = filterRequest("price_for_driver"); +// ── 1. Input ─────────────────────────────────────────────────── +$start_location = filterRequest("start_location"); +$end_location = filterRequest("end_location"); +$price = filterRequest("price"); +$passenger_id = filterRequest("passenger_id"); +$driver_id = filterRequest("driver_id") ?: 0; +$status = filterRequest("status"); +$price_for_driver = filterRequest("price_for_driver"); $price_for_passenger = filterRequest("price_for_passenger"); -$distance = filterRequest("distance"); -$carType = filterRequest("carType"); - -// بيانات الراكب الإضافية -$passenger_name = filterRequest("passenger_name"); -$passenger_phone = filterRequest("passenger_phone"); -$passenger_token = filterRequest("passenger_token"); -$passenger_email = filterRequest("passenger_email"); -$passenger_wallet = filterRequest("passenger_wallet"); -$passenger_rating = filterRequest("passenger_rating"); - -// تفاصيل الرحلة والنصوص -$start_name_loc = filterRequest("start_name"); -$end_name_loc = filterRequest("end_name"); -$duration_text = filterRequest("duration_text"); -$distance_text = filterRequest("distance_text"); -$is_wallet = filterRequest("is_wallet"); -$has_steps = filterRequest("has_steps"); - -$step0 = filterRequest("step0"); $step1 = filterRequest("step1"); -$step2 = filterRequest("step2"); $step3 = filterRequest("step3"); +$distance = filterRequest("distance"); +$carType = filterRequest("carType"); +$passenger_name = filterRequest("passenger_name"); +$passenger_phone = filterRequest("passenger_phone"); +$passenger_token = filterRequest("passenger_token"); +$passenger_email = filterRequest("passenger_email"); +$passenger_wallet = filterRequest("passenger_wallet"); +$passenger_rating = filterRequest("passenger_rating"); +$start_name_loc = filterRequest("start_name"); +$end_name_loc = filterRequest("end_name"); +$duration_text = filterRequest("duration_text"); +$distance_text = filterRequest("distance_text"); +$is_wallet = filterRequest("is_wallet"); +$has_steps = filterRequest("has_steps"); +$step0 = filterRequest("step0"); +$step1 = filterRequest("step1"); +$step2 = filterRequest("step2"); +$step3 = filterRequest("step3"); $step4 = filterRequest("step4"); -// معالجة الإحداثيات (فصل النص إلى Lat/Lng) -$startLat = ""; $startLng = ""; +// Validation +if (empty($passenger_id) || empty($start_location) || empty($end_location) || empty($price)) { + error_log("[add_ride] Validation failed — missing required fields."); + printFailure("Missing required fields"); + exit; +} + +// ── 2. تنسيق التواريخ ───────────────────────────────────────── +$date_formatted = date("Y-m-d"); +$time_formatted = date("H:i:s"); +$endtime_formatted = filterRequest("endtime") + ? date("H:i:s", strtotime(filterRequest("endtime"))) + : "00:00:00"; + +// ── 3. إحداثيات البداية والنهاية ────────────────────────────── +$startLat = $startLng = $endLat = $endLng = ""; if (!empty($start_location)) { - $parts = explode(',', $start_location); - $startLat = trim($parts[0] ?? ""); $startLng = trim($parts[1] ?? ""); + [$startLat, $startLng] = array_map('trim', explode(',', $start_location, 2)); } - -$endLat = ""; $endLng = ""; if (!empty($end_location)) { - $parts = explode(',', $end_location); - $endLat = trim($parts[0] ?? ""); $endLng = trim($parts[1] ?? ""); + [$endLat, $endLng] = array_map('trim', explode(',', $end_location, 2)); } -// تنسيق التواريخ -$date_formatted = date("Y-m-d", strtotime($date_raw)); -$time_formatted = date("H:i:s", strtotime($time_raw)); -$endtime_formatted = $endtime_raw ? date("H:i:s", strtotime($endtime_raw)) : "00:00:00"; - -// مصفوفة البيانات للإدخال -$data = [ - ":start_location" => $start_location, - ":end_location" => $end_location, - ":date" => $date_formatted, - ":time" => $time_formatted, - ":endtime" => $endtime_formatted, - ":price" => $price, - ":passenger_id" => $passenger_id, - ":driver_id" => $driver_id, - ":status" => $status, - ":carType" => $carType, - ":price_for_driver" => $price_for_driver, - ":price_for_passenger" => $price_for_passenger, - ":distance" => $distance, +// ── 4. مصفوفة بيانات الإدخال ────────────────────────────────── +$insertData = [ + ':start_location' => $start_location, + ':end_location' => $end_location, + ':date' => $date_formatted, + ':time' => $time_formatted, + ':endtime' => $endtime_formatted, + ':price' => $price, + ':passenger_id' => $passenger_id, + ':driver_id' => $driver_id, + ':status' => $status, + ':carType' => $carType, + ':price_for_driver' => $price_for_driver, + ':price_for_passenger' => $price_for_passenger, + ':distance' => $distance, ]; -// جملة SQL للإدخال -$sql = "INSERT INTO `ride` ( - `start_location`, `end_location`, `date`, `time`, `endtime`, - `price`, `passenger_id`, `driver_id`, `status`, `carType`, - `price_for_driver`, `price_for_passenger`, `distance` -) VALUES ( - :start_location, :end_location, :date, :time, :endtime, - :price, :passenger_id, :driver_id, :status, :carType, - :price_for_driver, :price_for_passenger, :distance -)"; +$sqlInsert = "INSERT INTO `ride` + (`start_location`,`end_location`,`date`,`time`,`endtime`, + `price`,`passenger_id`,`driver_id`,`status`,`carType`, + `price_for_driver`,`price_for_passenger`,`distance`) + VALUES + (:start_location,:end_location,:date,:time,:endtime, + :price,:passenger_id,:driver_id,:status,:carType, + :price_for_driver,:price_for_passenger,:distance)"; try { - // 3. الإدخال في قاعدة بيانات الرحلات أولاً (Ride DB - المرجع الأساسي) - $stmtRide = $con_ride->prepare($sql); - $stmtRide->execute($data); - $insertedId = $con_ride->lastInsertId(); // ID الرحلة الجديد + // ═══════════════════════════════════════════════════════════ + // STEP A — ride DB أولاً (هو المرجع الأساسي) + // ═══════════════════════════════════════════════════════════ + $stmtRide = $con_ride->prepare($sqlInsert); + $stmtRide->execute($insertData); + $insertedId = $con_ride->lastInsertId(); if (!$insertedId) { - jsonError("Failed to create ride"); + error_log("[add_ride] ride DB insert returned no ID."); + printFailure("Failed to create ride"); + exit; } - // 4. الإدخال في قاعدة البيانات الرئيسية ثانياً (Main DB) بنفس الـ ID + error_log("[add_ride] ride DB insert success. RideID=$insertedId"); + + // ═══════════════════════════════════════════════════════════ + // STEP B — primary DB ثانياً (نسخة أرشيفية بنفس الـ ID) + // ═══════════════════════════════════════════════════════════ + $sqlInsertWithId = "INSERT INTO `ride` + (`id`,`start_location`,`end_location`,`date`,`time`,`endtime`, + `price`,`passenger_id`,`driver_id`,`status`,`carType`, + `price_for_driver`,`price_for_passenger`,`distance`) + VALUES + (:id,:start_location,:end_location,:date,:time,:endtime, + :price,:passenger_id,:driver_id,:status,:carType, + :price_for_driver,:price_for_passenger,:distance)"; + try { - $sqlMain = "INSERT INTO `ride` ( - `id`, `start_location`, `end_location`, `date`, `time`, `endtime`, - `price`, `passenger_id`, `driver_id`, `status`, `carType`, - `price_for_driver`, `price_for_passenger`, `distance` - ) VALUES ( - :id, :start_location, :end_location, :date, :time, :endtime, - :price, :passenger_id, :driver_id, :status, :carType, - :price_for_driver, :price_for_passenger, :distance - )"; - $stmtMain = $con->prepare($sqlMain); - $data[':id'] = $insertedId; - $stmtMain->execute($data); - } catch (Exception $eMain) { - error_log("⚠️ Main DB Sync Warning: " . $eMain->getMessage()); + $primaryData = $insertData; + $primaryData[':id'] = $insertedId; + $stmtPrimary = $con->prepare($sqlInsertWithId); + $stmtPrimary->execute($primaryData); + error_log("[add_ride] primary DB sync success. RideID=$insertedId"); + } catch (PDOException $ePrimary) { + // لا نوقف العملية — ride DB هو المرجع + error_log("[add_ride] primary DB sync WARNING: " . $ePrimary->getMessage()); } - if ($insertedId) { - error_log("📝 Ride #$insertedId added successfully."); + // ═══════════════════════════════════════════════════════════ + // STEP C — بناء الـ payload وإرسال الرحلة للسائقين + // ═══════════════════════════════════════════════════════════ + $kazan = (float) $price - (float) $price_for_driver; + $payload = [ + (string) $startLat, + (string) $startLng, + number_format((float) $price, 2, '.', ''), + (string) $endLat, + (string) $endLng, + (string) $distance_text, + "", + (string) $passenger_id, + (string) $passenger_name, + (string) $passenger_token, + (string) $passenger_phone, + (string) $distance, + "1", + (string) $is_wallet, + (string) $distance, + (string) $duration_text, + (string) $insertedId, + "", + "", + (string) $duration_text, + $has_steps ?: 'false', + (string) $step0, + (string) $step1, + (string) $step2, + (string) $step3, + (string) $step4, + number_format((float) $price_for_driver, 2, '.', ''), + (string) $passenger_wallet, + (string) $passenger_email, + (string) $start_name_loc, + (string) $end_name_loc, + (string) $carType, + number_format($kazan, 2, '.', ''), + (string) $passenger_rating, + ]; - // 5. تجهيز الـ Payload (قائمة البيانات للتطبيق) - $kazan = (double)$price - (double)$price_for_driver; - $payloadTemplate = []; - // تعبئة البيانات بالترتيب الذي يتوقعه التطبيق (Indices 0-33) - $payloadTemplate[0] = (string)$startLat; - $payloadTemplate[1] = (string)$startLng; - $payloadTemplate[2] = (string)number_format($price, 2, '.', ''); - $payloadTemplate[3] = (string)$endLat; - $payloadTemplate[4] = (string)$endLng; - $payloadTemplate[5] = (string)$distance_text; - $payloadTemplate[6] = ""; - $payloadTemplate[7] = (string)$passenger_id; - $payloadTemplate[8] = (string)$passenger_name; - $payloadTemplate[9] = (string)$passenger_token; - $payloadTemplate[10] = (string)$passenger_phone; - $payloadTemplate[11] = (string)$distance; - $payloadTemplate[12] = "1"; - $payloadTemplate[13] = (string)$is_wallet; - $payloadTemplate[14] = (string)$distance; - $payloadTemplate[15] = (string)$duration_text; - $payloadTemplate[16] = (string)$insertedId; - $payloadTemplate[17] = ""; - $payloadTemplate[18] = ""; - $payloadTemplate[19] = (string)$duration_text; - $payloadTemplate[20] = $has_steps ?: 'false'; - $payloadTemplate[21] = (string)$step0; - $payloadTemplate[22] = (string)$step1; - $payloadTemplate[23] = (string)$step2; - $payloadTemplate[24] = (string)$step3; - $payloadTemplate[25] = (string)$step4; - $payloadTemplate[26] = (string)number_format($price_for_driver, 2, '.', ''); - $payloadTemplate[27] = (string)$passenger_wallet; - $payloadTemplate[28] = (string)$passenger_email; - $payloadTemplate[29] = (string)$start_name_loc; - $payloadTemplate[30] = (string)$end_name_loc; - $payloadTemplate[31] = (string)$carType; - $payloadTemplate[32] = (string)number_format($kazan, 2, '.', ''); - $payloadTemplate[33] = (string)$passenger_rating; - - ksort($payloadTemplate); - $payloadTemplate = array_values($payloadTemplate); - - // 6. البحث عن السائقين للتوزيع المباشر (Direct Dispatch) - $driversData = findBestDrivers($con, $startLat, $startLng, $carType); - - // متغير لنعرف هل وجدنا سائقين للتوجيه المباشر أم لا - $foundDirectDrivers = false; - - if (!empty($driversData)) { - // أ. إرسال إشعار مباشر للسائقين المختارين - dispatchRideToDrivers($driversData, $insertedId, $payloadTemplate, $start_name_loc, $encryptionHelper); - error_log("📨 Dispatched Ride #$insertedId to " . count($driversData) . " drivers."); - $foundDirectDrivers = true; - } else { - error_log("⚠️ No specific drivers found for Direct Dispatch for Ride #$insertedId. Moved to Market only."); - } - - // ب. 🔥 نشر الرحلة في السوق المفتوح (Marketplace) دائماً 🔥 - // هذا هو طوق النجاة: حتى لو لم نجد سائقين أعلاه، نضعها في السوق - broadcastRideToMarket($insertedId, $startLat, $startLng, $payloadTemplate); - - // ج. ✅ إرجاع نجاح للتطبيق دائماً (ليبقى الراكب في شاشة البحث) - // يمكنك إرسال معلومة إضافية للتطبيق أن البحث "عام" وليس "مباشر" إذا أردت - jsonSuccess($insertedId); - - // ملاحظة: قمنا بإزالة كود الإلغاء (UPDATE ride SET status = 'cancelled...') - // لأننا نريد منح الفرصة للسائقين البعيدين قليلاً أو الذين فتحوا التطبيق للتو - - - /* - else { - // 🛑 حالة عدم العثور على سائقين - error_log("⚠️ No drivers found for Ride #$insertedId."); - - // أ. إلغاء الرحلة فوراً في قواعد البيانات - // ملاحظة: غيرنا الحالة إلى رسالة واضحة - $con->prepare("UPDATE ride SET status = 'cancelled_no_driver_found' WHERE id = ?")->execute([$insertedId]); - $con_ride->prepare("UPDATE ride SET status = 'cancelled_no_driver_found' WHERE id = ?")->execute([$insertedId]); - - // ب. إشعار الراكب عبر السوكيت (لإظهار Popup) - if (function_exists('notifyPassengerSocket')) { - notifyPassengerSocket($passenger_id, 'no_drivers_found', [ - 'ride_id' => $insertedId, - 'message' => 'No drivers available nearby' - ]); - } - - // ج. إرجاع فشل للتطبيق - jsonError("no_drivers_found"); - */ + // Direct dispatch للسائقين القريبين + $driversData = findBestDrivers($con, $startLat, $startLng, $carType); + if (!empty($driversData)) { + dispatchRideToDrivers($driversData, $insertedId, $payload, $start_name_loc, $encryptionHelper); + error_log("[add_ride] Dispatched RideID=$insertedId to " . count($driversData) . " drivers."); } else { - jsonError("Failed to add ride"); + error_log("[add_ride] No direct drivers found for RideID=$insertedId — market only."); } -} catch (Exception $e) { - error_log("AddRide Critical Error: " . $e->getMessage()); - jsonError("Database Error"); -} -?> \ No newline at end of file + // Broadcast للـ marketplace دائماً + broadcastRideToMarket($insertedId, $startLat, $startLng, $payload); + + // رد النجاح للتطبيق + printSuccess($insertedId); + +} catch (PDOException $e) { + error_log("[add_ride] CRITICAL ride DB error: " . $e->getMessage()); + printFailure("Database error"); +} \ No newline at end of file