geoRadius('geo:drivers:available', $centerLon, $centerLat, $radiusKm, 'km'); if ($redisResults) { foreach ($redisResults as $res) { // قد يرجع Redis مصفوفة داخلية إذا تم تمرير خيارات، ولكن بالوضع الافتراضي يرجع سلاسل نصية $driver_ids[] = is_array($res) ? $res[0] : $res; } } } if (empty($driver_ids)) { jsonError("No car locations found in the specified area."); exit; } // ================================================================= // الخطوة 2: جلب تفاصيل الموقع الدقيقة والسرعة لكل سائق من Redis Pipeline // ================================================================= $pipe = $redisLocation->pipeline(); foreach ($driver_ids as $id) { $pipe->hGetAll("driver:profile:$id"); } $profiles = $pipe->exec(); $locations = []; foreach ($driver_ids as $index => $id) { $profile = $profiles[$index]; if (!$profile || empty($profile['lat'])) continue; // تجاهل المواقع القديمة (أكثر من 3 دقائق) $updatedAt = $profile['updated_at'] ?? 0; if (time() - $updatedAt > 180) continue; $locations[] = [ 'driver_id' => $id, 'latitude' => $profile['lat'], 'longitude' => $profile['lng'], 'heading' => $profile['heading'] ?? 0, 'speed' => $profile['speed'] ?? 0, 'status' => 'off', // متواجدون في geo:drivers:available 'updated_at' => date('Y-m-d H:i:s', $updatedAt) ]; } if (empty($locations)) { jsonError("No fresh car locations found in the specified area."); exit; } // ================================================================= // الخطوة 3: جلب البيانات الثابتة (السيارة، الموديل، التقييم) من MySQL // ================================================================= $drivers_info = []; $valid_driver_ids = array_column($locations, 'driver_id'); $placeholders = implode(',', array_fill(0, count($valid_driver_ids), '?')); $sql_drivers_info = " SELECT d.id AS driver_id, d.phone, d.email, d.birthdate, d.first_name, d.last_name, d.gender, d.maritalStatus, cr.make, cr.model, cr.color, cr.color_hex, cr.year, dt.token, COALESCE(rdAvg.ratingDriver, 0) AS ratingDriver, COALESCE(rdAvg.ratingCount, 0) AS ratingCount FROM driver d LEFT JOIN CarRegistration cr ON cr.driverID = d.id LEFT JOIN driverToken dt ON dt.captain_id = d.id LEFT JOIN ( SELECT driver_id, AVG(rating) AS ratingDriver, COUNT(id) AS ratingCount FROM ratingDriver GROUP BY driver_id ) rdAvg ON rdAvg.driver_id = d.id WHERE d.id IN ($placeholders) AND (cr.model NOT LIKE '%Van%' AND cr.make NOT LIKE '%Van%') "; $stmt_drivers_info = $con->prepare($sql_drivers_info); $stmt_drivers_info->execute($valid_driver_ids); $drivers_info_raw = $stmt_drivers_info->fetchAll(PDO::FETCH_ASSOC); foreach ($drivers_info_raw as $driver) { $drivers_info[$driver['driver_id']] = $driver; } // ================================================================= // الخطوة 4: دمج النتائج والترتيب // ================================================================= $final_results = []; foreach ($locations as $location) { $driver_id = $location['driver_id']; if (isset($drivers_info[$driver_id])) { $final_results[] = array_merge($location, $drivers_info[$driver_id]); } } usort($final_results, function ($a, $b) { if ($a['ratingDriver'] != $b['ratingDriver']) { return $b['ratingDriver'] <=> $a['ratingDriver']; } if ($a['ratingCount'] != $b['ratingCount']) { return $b['ratingCount'] <=> $a['ratingCount']; } return strtotime($b['updated_at']) <=> strtotime($a['updated_at']); }); $limited_results = array_slice($final_results, 0, 10); if (empty($limited_results)) { jsonError("No cars matching the specific criteria found."); exit; } // ================================================================= // الخطوة 5: فك التشفير وحساب العمر // ================================================================= $fieldsToDecrypt = ['phone','email','gender','birthdate', 'first_name','last_name', 'token','car_plate','vin']; foreach ($limited_results as &$row) { foreach ($fieldsToDecrypt as $field) { if (isset($row[$field]) && !empty($row[$field])) { try { $row[$field] = $encryptionHelper->decryptData($row[$field]); } catch (Exception $e) { $row[$field] = null; } } } if (!empty($row['birthdate'])) { try { $birthDate = new DateTime($row['birthdate']); $today = new DateTime(); $row['age'] = $today->diff($birthDate)->y; } catch (Exception $e) { $row['age'] = null; } } else { $row['age'] = null; } } unset($row); jsonSuccess($limited_results); } catch (PDOException $e) { error_log("[getSpeed.php PDO] " . $e->getMessage()); jsonError("An internal error occurred. Please try again later."); } catch (Throwable $e) { error_log("[getSpeed.php] " . $e->getMessage()); jsonError("An internal error occurred. Please try again later."); }