176 lines
7.3 KiB
PHP
176 lines
7.3 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../../connect.php'; // Provides $con, $redisLocation, $encryptionHelper, jsonSuccess/jsonError
|
|
|
|
// getSpeed.php (Redis-Optimized Version)
|
|
try {
|
|
// 1) قراءة والتحقق من الإحداثيات
|
|
$southwestLat = filterRequest("southwestLat");
|
|
$southwestLon = filterRequest("southwestLon");
|
|
$northeastLat = filterRequest("northeastLat");
|
|
$northeastLon = filterRequest("northeastLon");
|
|
|
|
if ($southwestLat === false || $southwestLon === false || $northeastLat === false || $northeastLon === false) {
|
|
jsonError("Invalid coordinates provided");
|
|
exit;
|
|
}
|
|
|
|
// =================================================================
|
|
// الخطوة 1: البحث في Redis باستخدام تقنية GeoRadius (أسرع 100 مرة من MySQL)
|
|
// =================================================================
|
|
$centerLat = ($southwestLat + $northeastLat) / 2.0;
|
|
$centerLon = ($southwestLon + $northeastLon) / 2.0;
|
|
|
|
// حساب تقريبي لنصف القطر بالكيلومترات بناءً على الصندوق (Bounding Box)
|
|
$earth_radius = 6371;
|
|
$dLat = deg2rad($southwestLat - $centerLat);
|
|
$dLon = deg2rad($southwestLon - $centerLon);
|
|
$a = sin($dLat/2) * sin($dLat/2) + cos(deg2rad($centerLat)) * cos(deg2rad($southwestLat)) * sin($dLon/2) * sin($dLon/2);
|
|
$c = 2 * asin(sqrt($a));
|
|
$radiusKm = max(1, ($earth_radius * $c) + 1);
|
|
|
|
// سحب معرفات السائقين المتاحين حول الراكب من سيرفر المواقع (Dual Redis)
|
|
$driver_ids = [];
|
|
if (isset($redisLocation)) {
|
|
$redisResults = $redisLocation->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.");
|
|
}
|