Update: 2026-06-14 22:10:07
This commit is contained in:
610
siro_admin_ride_monitoring_analysis.md
Normal file
610
siro_admin_ride_monitoring_analysis.md
Normal file
@@ -0,0 +1,610 @@
|
||||
<div dir="rtl" lang="ar">
|
||||
|
||||
# تحليل نظام متابعة الرحلات في لوحة تحكم المشرف (Siro Admin)
|
||||
|
||||
## دراسة شاملة للبحث، التتبع، التعديل، والتعامل مع الرحلات المعلقة
|
||||
|
||||
---
|
||||
|
||||
## 📋 فهرس المحتويات
|
||||
|
||||
1. [النظام الحالي لمتابعة الرحلات](#النظام-الحالي)
|
||||
2. [تحليل شاشات وأدوات متابعة الرحلات](#تحليل-الشاشات)
|
||||
3. [حالات الرحلة الفعلية من قاعدة البيانات](#حالات-الرحلة)
|
||||
4. [مشكلة رقم الهاتف والدولة](#مشكلة-رقم-الهاتف-والدولة)
|
||||
5. [الـ Endpoints المستخدمة واستجاباتها](#الـ-endpoints)
|
||||
6. [ما هو موجود وما هو مفقود](#ما-هو-موجود-وما-هو-مفقود)
|
||||
7. [التوصيات والتحسينات المطلوبة مع الإصلاحات](#التوصيات-والإصلاحات)
|
||||
|
||||
---
|
||||
|
||||
## النظام الحالي
|
||||
|
||||
### الملفات المشاركة في متابعة الرحلات
|
||||
|
||||
| الملف | الدور |
|
||||
|-------|--------|
|
||||
| `siro_admin/lib/views/admin/rides/ride_lookup_page.dart` | لوحة إدارة الرحلات (Dashboard) - الأحدث |
|
||||
| `siro_admin/lib/views/admin/rides/rides.dart` | إحصائيات الرحلات الشهرية (قديم) |
|
||||
| `siro_admin/lib/views/admin/drivers/monitor_ride.dart` | شاشة مراقبة رحلة محددة بالخريطة |
|
||||
| `siro_admin/lib/controller/rides/ride_lookup_controller.dart` | تحكم البحث عن رحلة وتحديث حالتها |
|
||||
| `siro_admin/lib/controller/admin/ride_admin_controller.dart` | تحكم إحصائيات الرحلات |
|
||||
|
||||
### ملفات الباك إند
|
||||
|
||||
| الملف | الوظيفة |
|
||||
|-------|---------|
|
||||
| `backend/Admin/rides/get_rides_by_status.php` | جلب الرحلات حسب الحالة (Begin, New, Finished, Canceled) |
|
||||
| `backend/Admin/rides/admin_get_rides_by_phone.php` | البحث عن رحلة برقم هاتف الراكب (مشفر) |
|
||||
| `backend/Admin/rides/admin_update_ride_status.php` | تحديث حالة الرحلة (Pending→Completed...) |
|
||||
| `backend/Admin/rides/monitorRide.php` | مراقبة رحلة نشطة برقم الهاتف (يبحث في السائق والراكب) |
|
||||
| `backend/Admin/rides/get_driver_live_pos.php` | جلب الموقع اللحظي للسائق من `car_locations` |
|
||||
|
||||
---
|
||||
|
||||
## حالات الرحلة الفعلية في قاعدة البيانات
|
||||
|
||||
### 📌 هيكل الجدول
|
||||
|
||||
```sql
|
||||
CREATE TABLE `ride` (
|
||||
`id` int NOT NULL AUTO_INCREMENT,
|
||||
`status` varchar(200) NOT NULL DEFAULT 'nothing', -- ❌ NOT ENUM! نص عادي
|
||||
...
|
||||
) ENGINE=InnoDB;
|
||||
|
||||
CREATE TABLE `waitingRides` (
|
||||
`id` varchar(100) NOT NULL,
|
||||
`status` varchar(200) NOT NULL DEFAULT 'nothing', -- ❌ NOT ENUM! نص عادي
|
||||
...
|
||||
) ENGINE=InnoDB;
|
||||
```
|
||||
|
||||
### 🗺️ جميع حالات الرحلة المستخدمة فعلياً في الكود
|
||||
|
||||
| الحالة في DB | المعنى | أين تستخدم |
|
||||
|-------------|--------|------------|
|
||||
| `nothing` | القيمة الافتراضية | عند إنشاء الرحلة في `add.php` و `addWaitingRide.php` |
|
||||
| `New` | رحلة جديدة (لم يتم تعيين سائق بعد) | في `Admin/rides/get_rides_by_status.php` للـ Admin Dashboard |
|
||||
| `waiting` | الرحلة قيد البحث عن سائق | في `retry_search_drivers.php` عند إعادة البحث |
|
||||
| `wait` | في انتظار (مستخدم في بعض السيرفرات) | في `getRideStatusFromStartApp.php` و `cancelRideByPassenger.php` |
|
||||
| `Apply` | سائق قبل الرحلة (قيد التوجه) | في `acceptRide.php`, `overLay/get.php`, `driver_statistic.php` |
|
||||
| `Applied` | تم قبول التطبيق | في `get_rides_by_status.php` (مع Begin) |
|
||||
| `arrived` | السائق وصل إلى موقع الراكب (lowercase) | في `arrive_ride.php` |
|
||||
| `Arrived` | السائق وصل (uppercase) | في `get_rides_by_status.php` (مستخدم أيضاً) |
|
||||
| `Begin` | **الرحلة قيد التشغيل** 🚗 | في `start_ride.php`, `monitorRide.php`, `finish_ride_updates.php` |
|
||||
| `started` | بدأت الرحلة (مرادف لـ Begin) | في `start_ride.php` (رد API) |
|
||||
| `Finished` | **الرحلة اكتملت** ✅ | في معظم التقارير والإحصائيات |
|
||||
| `Cancel` | ملغاة (عام) | في `cancelRideFromDriver.php`, `cancel_ride_by_passenger.php` |
|
||||
| `CancelFromDriver` | ألغى السائق | في `get_rides_by_status.php` |
|
||||
| `CancelFromDriverAfterApply` | ألغى السائق بعد القبول | في `get_rides_by_status.php` |
|
||||
| `CancelFromPassenger` | ألغى الراكب | في `get_rides_by_status.php` |
|
||||
| `TimeOut` | انتهت المهلة الزمنية | في `get_rides_by_status.php` |
|
||||
|
||||
### 🔄 تسلسل حالة الرحلة الطبيعي:
|
||||
|
||||
```
|
||||
nothing / New
|
||||
│
|
||||
├──→ waiting ──→ Apply ──→ Applied ──→ Arrived ──→ Begin ──→ Finished
|
||||
│ │ │ │ │ │
|
||||
│ │ │ │ │ └── ✅ مكتملة
|
||||
│ │ │ │ │
|
||||
│ │ │ │ └── CancelFromDriverAfterApply
|
||||
│ │ │ │
|
||||
│ │ │ └── CancelFromPassenger
|
||||
│ │ │
|
||||
│ │ └── TimeOut (انتهاء المهلة)
|
||||
│ │
|
||||
│ └── CancelFromDriver
|
||||
│
|
||||
└── Cancel (إلغاء مباشر)
|
||||
```
|
||||
|
||||
### ❌ المشكلة في Admin Dashboard الحالي:
|
||||
|
||||
في `get_rides_by_status.php`، التصنيف خاطئ:
|
||||
|
||||
```php
|
||||
case 'Begin':
|
||||
$whereClause = "WHERE r.status IN ('Begin','Apply','Applied')"; // ✅ Begin + Apply + Applied
|
||||
break;
|
||||
case 'New':
|
||||
$whereClause = "WHERE r.status = 'New'"; // ✅ جديد
|
||||
break;
|
||||
case 'Completed':
|
||||
$whereClause = "WHERE r.status = 'Finished'"; // ✅ مكتملة
|
||||
break;
|
||||
case 'Canceled':
|
||||
$whereClause = "WHERE r.status IN ('Cancel', 'CancelFromDriverAfterApply', 'TimeOut')"; // ❌ ناقص!
|
||||
break;
|
||||
```
|
||||
|
||||
**🔴 ملاحظات على التصنيف:**
|
||||
1. `CancelFromDriver` و `CancelFromPassenger` و `Cancel` و `arrived` (lowercase) **غير مشمولة** في أي تصنيف
|
||||
2. `Apply` و `Applied` مصنفين مع `Begin` (جارية) — هذا صحيح جزئياً لأن السائق في طريقه
|
||||
3. `TimeOut` مصنف مع `Canceled` — صحيح
|
||||
4. الرحلة التي حالتها `wait` أو `waiting` **غير معروضة إطلاقاً** في أي تبويب
|
||||
|
||||
---
|
||||
|
||||
## تحليل الشاشات
|
||||
|
||||
### 1️⃣ شاشة إدارة الرحلات (RidesDashboardScreen)
|
||||
|
||||
**الملف**: `ride_lookup_page.dart`
|
||||
|
||||
#### الوظائف الحالية:
|
||||
- ✅ عرض الرحلات مصنفة حسب الحالة (جارية، جديدة، مكتملة، ملغاة)
|
||||
- ✅ إحصائيات فورية (عدد الرحلات لكل حالة + الإيرادات + المسافة)
|
||||
- ✅ شريط بحث ديناميكي يصفّي النتائج مباشرة
|
||||
- ✅ الضغط على رحلة → يفتح خريطة التتبع (RideMapMonitorScreen)
|
||||
- ✅ إظهار معلومات السائق والراكب مع أيقونة اتصال (للمشرف العام)
|
||||
|
||||
#### منطق البحث الحالي:
|
||||
```dart
|
||||
void filterRides(String query) {
|
||||
displayedRides.value = allRidesList.where((ride) {
|
||||
return ride.driverPhone.contains(query) ||
|
||||
ride.passengerPhone.contains(query) ||
|
||||
ride.driverName.toLowerCase().contains(query.toLowerCase()) ||
|
||||
ride.passengerName.toLowerCase().contains(query.toLowerCase()) ||
|
||||
ride.rideId.contains(query);
|
||||
}).toList();
|
||||
}
|
||||
```
|
||||
|
||||
🔴 **مشكلة**: البحث يتم **محلياً** (في الذاكرة) بعد جلب كل الرحلات من API. إذا كانت الرحلات كثيرة (100+ رحلة)، البحث لا يشمل الرحلات الغير محملة.
|
||||
|
||||
🔴 **مشكلة**: الـ API يجلب فقط آخر 100 رحلة (LIMIT 100) ولا يوجد Pagination.
|
||||
|
||||
#### الـ API المستخدم:
|
||||
```
|
||||
POST $server/Admin/rides/get_rides_by_status.php
|
||||
payload: { "status": "Begin" }
|
||||
← { "status": "success", "message": [ {...}, {...} ] }
|
||||
```
|
||||
|
||||
#### خريطة التتبع (RideMapMonitorScreen):
|
||||
- ✅ يعرض مسار الرحلة (نقطة بداية → نقطة نهاية)
|
||||
- ✅ يتابع موقع السائق اللحظي (Polling كل 10 ثوانٍ)
|
||||
- ✅ زر اتصال بالسائق أو الراكب
|
||||
- 🔴 **لا يوجد تحديث لحالة الرحلة** من هذه الشاشة
|
||||
|
||||
---
|
||||
|
||||
### 2️⃣ شاشة مراقبة رحلة (RideMonitorScreen)
|
||||
|
||||
**الملف**: `monitor_ride.dart`
|
||||
|
||||
#### الوظائف الحالية:
|
||||
- ✅ إدخال رقم هاتف للبحث عن رحلة نشطة
|
||||
- ✅ عرض خريطة كاملة مع مسار الرحلة وموقع السائق
|
||||
- ✅ تحديث تلقائي (Polling كل 10 ثوانٍ)
|
||||
- ✅ إظهار اسم السائق، حالة الرحلة، السرعة، آخر تحديث
|
||||
|
||||
#### الـ API المستخدم:
|
||||
```
|
||||
POST $server/Admin/rides/monitorRide.php
|
||||
payload: { "phone": "963$phone" }
|
||||
← { "status": "success", "message": {
|
||||
"ride_details": { ... },
|
||||
"driver_details": { ... },
|
||||
"driver_location": { "latitude": ..., "longitude": ..., "speed": ..., "heading": ... }
|
||||
}}
|
||||
```
|
||||
|
||||
#### منطق البحث الحالي:
|
||||
```dart
|
||||
final response = await CRUD().post(
|
||||
link: apiUrl,
|
||||
payload: {"phone": "963$phone"}, // ⚠️ Hardcoded Syria prefix!
|
||||
);
|
||||
```
|
||||
|
||||
🔴 **مشكلة حرجة**: `963$phone` ثابت (سوريا فقط). الأردن يحتاج `962` ومصر تحتاج `20`.
|
||||
|
||||
#### منطق الباك إند (`monitorRide.php`):
|
||||
```php
|
||||
$encPhone = $encryptionHelper->encryptData($phone);
|
||||
// يبحث أولاً في جدول driver
|
||||
$driverQuery = $con->prepare("SELECT id FROM driver WHERE phone = :phone LIMIT 1");
|
||||
// ثم في جدول passengers
|
||||
$customerQuery = $con->prepare("SELECT id FROM passengers WHERE phone = :phone LIMIT 1");
|
||||
// ثم يجلب آخر رحلة status='Begin'
|
||||
```
|
||||
|
||||
🔴 **ملاحظة**: `monitorRide.php` يبحث فقط عن الرحلات التي حالتها `Begin`. إذا كانت الرحلة بحالة `Apply` أو `Arrived` لن يجدها.
|
||||
|
||||
---
|
||||
|
||||
### 3️⃣ شاشة البحث عن رحلة وتحديث حالتها (RideLookupController)
|
||||
|
||||
**الملف**: `ride_lookup_controller.dart`
|
||||
|
||||
#### الوظائف الحالية:
|
||||
- ✅ البحث برقم هاتف الراكب (مشفر) عبر `admin_get_rides_by_phone.php`
|
||||
- ✅ عرض بيانات الراكب + آخر رحلة له
|
||||
- ✅ تحديث حالة الرحلة عبر `admin_update_ride_status.php`
|
||||
- ✅ قائمة منسدلة للحالات المسموحة: `Pending, Accepted, EnRoute, Arrived, Started, Completed, Canceled`
|
||||
|
||||
**🔴 هذه القائمة لا تطابق حالات الرحلة الفعلية في النظام!**
|
||||
|
||||
| القيمة في التطبيق | هل تستخدم في DB؟ | الحالة الصحيحة |
|
||||
|------------------|------------------|----------------|
|
||||
| `Pending` | ❌ لا | `waiting` أو `New` أو `nothing` |
|
||||
| `Accepted` | ❌ لا | `Apply` أو `Applied` |
|
||||
| `EnRoute` | ❌ لا | `Apply` أو `Applied` (السائق في الطريق) |
|
||||
| `Arrived` | ✅ نعم | `arrived` أو `Arrived` |
|
||||
| `Started` | ❌ لا | `Begin` |
|
||||
| `Completed` | ✅ نعم | `Finished` |
|
||||
| `Canceled` | ✅ نعم | `Cancel` أو `CancelFrom*` |
|
||||
|
||||
#### الـ API المستخدم للبحث:
|
||||
```
|
||||
POST $server/Admin/rides/admin_get_rides_by_phone.php
|
||||
payload: { "phone": "...", "status": "..." }
|
||||
← { "status": "success", "message": {
|
||||
"passenger": { "id", "first_name", "last_name", "phone" },
|
||||
"ride": { "id", "status", "start_location", ... }
|
||||
}}
|
||||
```
|
||||
|
||||
🔴 **مشكلة**: هذا الـ API يبحث فقط في جدول `passengers`. إذا كان الرقم تابعاً لسائق، لن يعثر على شيء.
|
||||
|
||||
---
|
||||
|
||||
## مشكلة رقم الهاتف والدولة
|
||||
|
||||
### كيف يتم تخزين أرقام الهواتف؟
|
||||
|
||||
في قاعدة البيانات، جميع الأرقام مشفرة باستخدام `encryptData()`:
|
||||
|
||||
```php
|
||||
$encryptedPhone = $encryptionHelper->encryptData($raw);
|
||||
```
|
||||
|
||||
**الأرقام تخزن مع كود الدولة:**
|
||||
- الأردن: `9627XXXXXXXX`
|
||||
- مصر: `2010XXXXXXXX`
|
||||
- سوريا: `9639XXXXXXXX`
|
||||
|
||||
### كيف يتم البحث في تطبيق المشرف حالياً؟
|
||||
|
||||
#### 1️⃣ في `monitor_ride.dart`:
|
||||
```dart
|
||||
payload: {"phone": "963$phone"}
|
||||
// المستخدم يدخل: 0992952235
|
||||
// التطبيق يرسل: 9630992952235 ← غلط! الصحيح: 963992952235
|
||||
```
|
||||
|
||||
#### 2️⃣ في `admin_get_rides_by_phone.php`:
|
||||
```php
|
||||
$phone = filterRequest('phone');
|
||||
$enc_raw = $encryptionHelper->encryptData($raw);
|
||||
// يبحث فقط في passengers
|
||||
```
|
||||
|
||||
### جدول مقارنة تنسيق الأرقام:
|
||||
|
||||
| الدولة | كود الدولة | تنسيق الإدخال | التخزين في DB | ما يرسله `monitor_ride.dart` | هل يتطابق؟ |
|
||||
|--------|-----------|--------------|---------------|------------------------------|-----------|
|
||||
| 🇯🇴 الأردن | `962` | `079XXXXXXX` | `96279XXXXXXX` | `963079XXXXXXX` ❌ | لا |
|
||||
| 🇪🇬 مصر | `20` | `010XXXXXXXX` | `2010XXXXXXXX` | `963010XXXXXXXX` ❌ | لا |
|
||||
| 🇸🇾 سوريا | `963` | `0992952235` | `963992952235` | `9630992952235` ❌ | لا (0 زائد) |
|
||||
|
||||
### تحليل المشكلة:
|
||||
|
||||
1. **المشرف يدخل**: `0992952235` (سوريا) أو `079XXXXXXX` (أردن)
|
||||
2. **التطبيق يضيف**: `963` ثابت (سوريا فقط)
|
||||
3. **الباك إند يبحث**: عن الرقم `9630992952235` أو `963079XXXXXXX`
|
||||
4. **الرقم المخزن**: `963992952235` (بدون 0 بعد كود الدولة) أو `96279XXXXXXX`
|
||||
5. **النتيجة**: فشل في البحث عن أي رقم غير سوري، وحتى السوري يفشل إذا كان المدخل بـ `0`
|
||||
|
||||
---
|
||||
|
||||
## الـ Endpoints
|
||||
|
||||
### الحالية في `backend/Admin/rides/`:
|
||||
|
||||
| الإند بوينت | الطريقة | المدخلات | الاستجابة | المشاكل |
|
||||
|------------|---------|----------|-----------|---------|
|
||||
| `get_rides_by_status.php` | POST | `status` (Begin/New/Completed/Canceled) | قائمة رحلات (100) مع تفاصيل السائق والراكب | لا يوجد Pagination + بعض الحالات غير مشمولة (`wait`, `CancelFromDriver`, `arrived`) |
|
||||
| `admin_get_rides_by_phone.php` | POST | `phone` | بيانات الراكب + آخر رحلة | يبحث فقط في passengers |
|
||||
| `admin_update_ride_status.php` | POST | `id, status, reason (optional)` | الرحلة المحدّثة | Whitelist لا يطابق حالات DB الفعلية |
|
||||
| `monitorRide.php` | POST | `phone` | تفاصيل الرحلة + السائق + الموقع | يبحث فقط عن `Begin` + كود الدولة ثابت 963 |
|
||||
| `get_driver_live_pos.php` | POST | `driver_id` | آخر موقع للسائق | يعمل بشكل صحيح |
|
||||
|
||||
---
|
||||
|
||||
## التوصيات والإصلاحات
|
||||
|
||||
### 🔴 الإصلاح 1: تصحيح حالات الرحلة في Admin Dashboard
|
||||
|
||||
**المشكلة**: `get_rides_by_status.php` يستخدم حالات غير دقيقة.
|
||||
|
||||
**الحل**: تعديل mapping الحالات ليطابق الواقع:
|
||||
|
||||
```php
|
||||
// backend/Admin/rides/get_rides_by_status.php - تصحيح
|
||||
switch ($statusFilter) {
|
||||
case 'Begin':
|
||||
// الرحلات الجارية: من Apply إلى Begin
|
||||
$whereClause = "WHERE r.status IN ('Apply','Applied','Arrived','arrived','Begin')";
|
||||
break;
|
||||
|
||||
case 'New':
|
||||
// الرحلات الجديدة: بانتظار سائق
|
||||
$whereClause = "WHERE r.status IN ('New','nothing','waiting','wait')";
|
||||
break;
|
||||
|
||||
case 'Completed':
|
||||
$whereClause = "WHERE r.status = 'Finished'";
|
||||
break;
|
||||
|
||||
case 'Canceled':
|
||||
// جميع أنواع الإلغاء
|
||||
$whereClause = "WHERE r.status IN ('Cancel','CancelFromDriver','CancelFromDriverAfterApply','CancelFromPassenger','TimeOut')";
|
||||
break;
|
||||
|
||||
default:
|
||||
$whereClause = "WHERE r.status = ?";
|
||||
$params[] = $statusFilter;
|
||||
break;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🔴 الإصلاح 2: تصحيح قائمة الحالات المسموحة في RideLookupController
|
||||
|
||||
**المشكلة**: `statusOptions` في `ride_lookup_controller.dart` لا تطابق حالات DB.
|
||||
|
||||
**الحل**: تعديل القائمة:
|
||||
|
||||
```dart
|
||||
// في ride_lookup_controller.dart
|
||||
final List<String> statusOptions = const [
|
||||
'New', // جديد (بدلاً من Pending)
|
||||
'waiting', // في انتظار سائق
|
||||
'Apply', // سائق قبل (بدلاً من Accepted)
|
||||
'Arrived', // وصل السائق
|
||||
'Begin', // الرحلة بدأت (بدلاً من Started)
|
||||
'Finished', // مكتملة (بدلاً من Completed)
|
||||
'Cancel', // إلغاء (بدلاً من Canceled)
|
||||
];
|
||||
```
|
||||
|
||||
**وتحديث `admin_update_ride_status.php`**:
|
||||
```php
|
||||
// backend/Admin/rides/admin_update_ride_status.php
|
||||
$allowed = [
|
||||
'New', 'waiting', 'wait', 'Apply', 'Applied',
|
||||
'Arrived', 'arrived', 'Begin', 'Finished',
|
||||
'Cancel', 'CancelFromDriver', 'CancelFromPassenger', 'TimeOut'
|
||||
];
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🔴 الإصلاح 3: معالجة أرقام الهواتف حسب الدولة
|
||||
|
||||
**المشكلة**: `monitor_ride.dart` يستخدم `963` ثابت.
|
||||
|
||||
**الحل**: إضافة دالة لتوحيد تنسيق الرقم:
|
||||
|
||||
```dart
|
||||
// في monitor_ride.dart
|
||||
String normalizePhone(String input) {
|
||||
final clean = input.replaceAll(RegExp(r'\D+'), '');
|
||||
|
||||
// Syria: 099XXXXXXX or 9639XXXXXXX
|
||||
if (clean.length == 10 && clean.startsWith('09'))
|
||||
return '963${clean.substring(1)}';
|
||||
if (clean.length == 12 && clean.startsWith('963'))
|
||||
return clean;
|
||||
if (clean.length == 9 && clean.startsWith('9'))
|
||||
return '963$clean';
|
||||
|
||||
// Jordan: 079XXXXXXX or 9627XXXXXXX
|
||||
if (clean.length == 10 && clean.startsWith('07'))
|
||||
return '962${clean.substring(1)}';
|
||||
if (clean.length == 12 && clean.startsWith('962'))
|
||||
return clean;
|
||||
if (clean.length == 9 && clean.startsWith('7'))
|
||||
return '962$clean';
|
||||
|
||||
// Egypt: 010XXXXXXXX or 2010XXXXXXXX
|
||||
if (clean.length == 11 && clean.startsWith('01'))
|
||||
return '20${clean.substring(1)}';
|
||||
if (clean.length == 13 && clean.startsWith('20'))
|
||||
return clean;
|
||||
|
||||
return clean;
|
||||
}
|
||||
|
||||
// عند البحث:
|
||||
final normalizedPhone = normalizePhone(phoneInputController.text.trim());
|
||||
final response = await CRUD().post(
|
||||
link: apiUrl,
|
||||
payload: {"phone": normalizedPhone},
|
||||
);
|
||||
```
|
||||
|
||||
**وفي الباك إند (`monitorRide.php` و `admin_get_rides_by_phone.php`)**:
|
||||
```php
|
||||
function normalizePhone($phone) {
|
||||
$clean = preg_replace('/\D+/', '', $phone);
|
||||
// Syria: remove leading 0 after country code
|
||||
if (strlen($clean) === 10 && strpos($clean, '09') === 0) return '963' . substr($clean, 1);
|
||||
if (strlen($clean) === 12 && strpos($clean, '963') === 0) return $clean;
|
||||
if (strlen($clean) === 9 && strpos($clean, '9') === 0) return '963' . $clean;
|
||||
// Jordan
|
||||
if (strlen($clean) === 10 && strpos($clean, '07') === 0) return '962' . substr($clean, 1);
|
||||
if (strlen($clean) === 12 && strpos($clean, '962') === 0) return $clean;
|
||||
if (strlen($clean) === 9 && strpos($clean, '7') === 0) return '962' . $clean;
|
||||
// Egypt
|
||||
if (strlen($clean) === 11 && strpos($clean, '01') === 0) return '20' . substr($clean, 1);
|
||||
if (strlen($clean) === 13 && strpos($clean, '20') === 0) return $clean;
|
||||
return $clean;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🔴 الإصلاح 4: البحث في السائق والراكب معاً (مع جميع حالات الرحلة)
|
||||
|
||||
**المشكلة**: `admin_get_rides_by_phone.php` يبحث فقط في `passengers` وحالة `Begin` فقط.
|
||||
|
||||
**الحل**: تعديل `admin_get_rides_by_phone.php` و `monitorRide.php` ليشمل driver + passenger + جميع الحالات النشطة:
|
||||
|
||||
```php
|
||||
// backend/Admin/rides/admin_get_rides_by_phone.php - معدل
|
||||
$phone = filterRequest('phone');
|
||||
if (!$phone) { jsonError("Phone is required"); exit; }
|
||||
|
||||
$normalizedPhone = normalizePhone($phone);
|
||||
$encPhone = $encryptionHelper->encryptData($normalizedPhone);
|
||||
|
||||
// 1) ابحث في driver
|
||||
$driverStmt = $con->prepare("SELECT id FROM driver WHERE phone = :phone LIMIT 1");
|
||||
$driverStmt->execute([':phone' => $encPhone]);
|
||||
$driver = $driverStmt->fetch();
|
||||
|
||||
// 2) ابحث في passengers
|
||||
$passengerStmt = $con->prepare("SELECT id FROM passengers WHERE phone = :phone LIMIT 1");
|
||||
$passengerStmt->execute([':phone' => $encPhone]);
|
||||
$passenger = $passengerStmt->fetch();
|
||||
|
||||
if (!$driver && !$passenger) {
|
||||
jsonError('Phone number not found in system');
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3) اجلب الرحلة حسب النوع
|
||||
$userId = $driver ? $driver['id'] : $passenger['id'];
|
||||
$userField = $driver ? 'r.driver_id' : 'r.passenger_id';
|
||||
$filterStatus = filterRequest('status') ?: 'all';
|
||||
|
||||
$whereExtra = '';
|
||||
if ($filterStatus !== 'all') {
|
||||
$whereExtra = "AND r.status = :filter_status";
|
||||
}
|
||||
|
||||
$rideStmt = $con->prepare("
|
||||
SELECT r.*, d.first_name as d_fname, d.last_name as d_lname, d.phone as d_phone,
|
||||
p.first_name as p_fname, p.last_name as p_lname, p.phone as p_phone
|
||||
FROM ride r
|
||||
LEFT JOIN driver d ON r.driver_id = d.id
|
||||
LEFT JOIN passengers p ON r.passenger_id = p.id
|
||||
WHERE $userField = :uid $whereExtra
|
||||
ORDER BY r.id DESC
|
||||
LIMIT 20
|
||||
");
|
||||
|
||||
$params = [':uid' => $userId];
|
||||
if ($filterStatus !== 'all') $params[':filter_status'] = $filterStatus;
|
||||
$rideStmt->execute($params);
|
||||
$rides = $rideStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🟡 الإصلاح 5: إضافة شاشة للرحلات المعلقة (Pending/Waiting)
|
||||
|
||||
**المشكلة**: لا توجد واجهة لعرض الرحلات العالقة (`waiting`, `New`, `nothing`).
|
||||
|
||||
**الحل المقترح**:
|
||||
- إضافة تبويب خامس باسم **"معلقة"** في RidesDashboardScreen
|
||||
- يعرض الرحلات ذات الحالة: `New`, `waiting`, `wait`, `nothing`
|
||||
- إضافة زر "إلغاء الرحلة" مع إدخال سبب
|
||||
- تحديث `admin_update_ride_status.php` ليدعم الإلغاء مع إشعار
|
||||
|
||||
```dart
|
||||
// إضافة في ride_lookup_page.dart (RidesListController)
|
||||
void changeTab(String status) {
|
||||
currentStatus = status;
|
||||
searchController.clear();
|
||||
fetchRides();
|
||||
}
|
||||
|
||||
// إضافة في RidesDashboardScreen
|
||||
Tab(text: 'معلقة', icon: Icon(Icons.hourglass_empty_rounded)),
|
||||
// عند اختيار هذا التبويب، يرسل status='Pending'
|
||||
// الـ API يتعامل معها كـ: IN ('New','waiting','wait','nothing')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 🟢 الإصلاح 6: إضافة البحث برقم الرحلة (Ride ID) + Pagination
|
||||
|
||||
**لماذا Ride ID هو الأفضل؟**
|
||||
- المعرف الوحيد الفريد (Primary Key)
|
||||
- لا توجد مشاكل تشفير (الأرقام المعرفية غير مشفرة)
|
||||
- لا توجد مشاكل تنسيق دولة
|
||||
- دقيق 100%
|
||||
|
||||
**مثال الإند بوينت الجديد**:
|
||||
```php
|
||||
// backend/Admin/rides/admin_get_ride_by_id.php
|
||||
$rideId = filterRequest('id');
|
||||
$stmt = $con->prepare("
|
||||
SELECT r.*,
|
||||
d.first_name as d_fname, d.last_name as d_lname, d.phone as d_phone,
|
||||
p.first_name as p_fname, p.last_name as p_lname, p.phone as p_phone
|
||||
FROM ride r
|
||||
LEFT JOIN driver d ON r.driver_id = d.id
|
||||
LEFT JOIN passengers p ON r.passenger_id = p.id
|
||||
WHERE r.id = :id
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([':id' => $rideId]);
|
||||
jsonSuccess($stmt->fetch());
|
||||
```
|
||||
|
||||
**Pagination في `get_rides_by_status.php`**:
|
||||
```php
|
||||
$page = (int)(filterRequest('page') ?? 1);
|
||||
$limit = 50;
|
||||
$offset = ($page - 1) * $limit;
|
||||
$sql .= " ORDER BY r.id DESC LIMIT $limit OFFSET $offset";
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## الملخص النهائي
|
||||
|
||||
### جدول حالات الرحلة - ما هو صحيح وما هو خاطئ:
|
||||
|
||||
| ما في النظام (DB) | ما في التطبيق (Admin) | التصحيح |
|
||||
|-------------------|----------------------|---------|
|
||||
| `nothing` / `New` | `New` ✅ | ✅ صحيح (جديد) |
|
||||
| `waiting` / `wait` | غير موجود ❌ | 🟡 يجب إضافته (معلق) |
|
||||
| `Apply` / `Applied` | `Begin` (مصنف مع الجارية) ✅ | ✅ صحيح (جاري التوصيل) |
|
||||
| `arrived` / `Arrived` | غير موجود ❌ | 🟡 يجب إضافته مع الجارية |
|
||||
| `Begin` | `Begin` ✅ | ✅ صحيح (قيد التشغيل) |
|
||||
| `Finished` | `Completed` ✅ | ✅ صحيح (مكتملة) |
|
||||
| `Cancel` / `CancelFrom*` / `TimeOut` | `Canceled` (ناقص) ❌ | 🔴 يجب إضافة `CancelFromDriver` و `CancelFromPassenger` |
|
||||
|
||||
### قائمة الإصلاحات:
|
||||
|
||||
| # | الإصلاح | الملف | الأولوية |
|
||||
|---|---------|-------|----------|
|
||||
| 1 | تصحيح حالات الرحلة في `get_rides_by_status.php` | `backend/Admin/rides/get_rides_by_status.php` | 🔴 عاجل |
|
||||
| 2 | تصحيح `statusOptions` في `ride_lookup_controller.dart` | `siro_admin/lib/controller/rides/ride_lookup_controller.dart` | 🔴 عاجل |
|
||||
| 3 | تصحيح whitelist في `admin_update_ride_status.php` | `backend/Admin/rides/admin_update_ride_status.php` | 🔴 عاجل |
|
||||
| 4 | إضافة `normalizePhone()` في `monitor_ride.dart` | `siro_admin/lib/views/admin/drivers/monitor_ride.dart` | 🔴 عاجل |
|
||||
| 5 | إضافة `normalizePhone()` في `admin_get_rides_by_phone.php` | `backend/Admin/rides/admin_get_rides_by_phone.php` | 🔴 عاجل |
|
||||
| 6 | إضافة `normalizePhone()` في `monitorRide.php` | `backend/Admin/rides/monitorRide.php` | 🔴 عاجل |
|
||||
| 7 | إضافة البحث في driver + passenger في `admin_get_rides_by_phone.php` | `backend/Admin/rides/admin_get_rides_by_phone.php` | 🟡 ضروري |
|
||||
| 8 | إضافة شاشة للرحلات المعلقة (Pending/Waiting) | `siro_admin/lib/views/admin/rides/ride_lookup_page.dart` | 🟡 ضروري |
|
||||
| 9 | إضافة البحث برقم الرحلة (Ride ID) | Endpoint + Flutter جديد | 🟡 ضروري |
|
||||
| 10 | إضافة Pagination | `backend/Admin/rides/get_rides_by_status.php` | 🟢 مفيد |
|
||||
|
||||
### أفضل طرق البحث (مرتبة حسب الأولوية):
|
||||
|
||||
1. **رقم الرحلة (Ride ID)** — الأدق، بدون مشاكل تشفير أو دولة
|
||||
2. **رقم هاتف الراكب/السائق** — مع معالجة الدولة (normalizePhone)
|
||||
3. **بحث بنطاق تاريخي** — لاستعراض الرحلات حسب الفترة الزمنية
|
||||
|
||||
</div>
|
||||
Reference in New Issue
Block a user