# مراجعة شاملة لنظام مواصلاتي — 2026-07-12 دراسة كاملة للنظام عبر: الباك اند (`backend/transit/` + `backend/Admin/transit/`)، سيرفرات السوكيت (`loction_server/driver_socket.php` + `passenger_server/passenger_socket.php`)، والتطبيقات الثلاثة (siro_rider / siro_driver / siro_admin). كل مهمة في القسم الأخير مكتوبة كأمر مستقل جاهز للتنفيذ على نموذج آخر — تحتوي المسارات والسياق الكافي. --- ## 1. جرد ما هو مبني وشغّال ### الباك اند — `backend/transit/` (بوابتان) | البوابة | المصادقة | الملفات | |---|---|---| | `connect_app.php` | JWT الرئيسي (راكب/سائق) + RateLimiter | trip/*, enrollment/activate + my_enrollments, route/for_org, org/browse, driver/me, driver/activate, notification/subscribe | | `connect_admin.php` | Session token (هاتف + OTP) + CORS | admin/*, vehicle/*, driver/invite + list, route/add + get + list, schedule/add, enrollment/list + approve + import_roster, broadcast/send | ### الباك اند — `backend/Admin/transit/` (فريق سيرو، JWT admin/super_admin) `org/create` · `org/list` · `org/details` · `org/admins_list` · `org/admin_add` · `org/admin_toggle` ### سيرفرات السوكيت (إضافات فقط، لم يُمس منطق الرحلات) - سائق الباص: `update_bus_location` مع **تحقق ملكية الرحلة** من `transit:trip:{id}:owner` (يُكتب عند trip/start، يُحذف عند trip/end) — و`route_id` يُؤخذ حصرياً من الكاش الموثوق لا من العميل - الراكب: `subscribe_transit_route` / `unsubscribe_transit_route` + بثّ `bus_location_update` لغرفة `transit_route_{id}` - HTTP داخلي: `get_bus_position` (driver_socket) + `broadcast_bus_location` (passenger_socket) ### التطبيقات - **الراكب**: 4 شاشات (عضوياتي → تصفح مؤسسات وتفعيل بالرقم الجامعي → خطوط → خريطة حية بالسوكيت) + مدخل بالقائمة الجانبية + ترجمات ar_jo/ar_sy/ar_eg - **السائق**: شاشة رحلات اليوم (بدء/إنهاء/تأخير) + "وضع الباص" داخل LocationController (يحوّل البث لـ update_bus_location ويخرج من حوض الرحلات) + مدخل بالـ Drawer - **سيرو آدمن**: قائمة مؤسسات + إنشاء مؤسسة + تفاصيل وتحليلات + إدارة مشرفين (إضافة/تعليق مع إبطال جلسات فوري) --- ## 2. أخطاء حرجة أُصلحت في هذه الجلسة (2026-07-12) | # | الخطأ | الملفات | الأثر قبل الإصلاح | |---|---|---|---| | 1 | تضارب تعريف `normalizePhone()` — fatal error | `Admin/rides/admin_get_rides_by_phone.php`, `Admin/rides/monitorRide.php`, `nabeh/resolve_user.php` | **ثلاثة endpoints إنتاجية معطلة تماماً** (منها تكامل نابه مع سيرفر الدفع) — حُذفت التعريفات المحلية لصالح الموحّدة في helpers.php | | 2 | `schedule/add.php` يُدرج عمود `created_by` غير موجود في `transit_schedules` | `transit/schedule/add.php` | إضافة أي جدول زمني = SQL error دائم | | 3 | `transit_enrollments.passenger_id NOT NULL` بينما استيراد الكشف يُدرج NULL | `schema_transit.sql` | استيراد كشف الطلاب معطّل بالكامل (+ ALTER للقاعدة القائمة بآخر الملف) | | 4 | `transit_trips.vehicle_id NOT NULL` بينما الجداول تسمح بجدول بلا باص | `schema_transit.sql` | إنشاء رحلات اليوم يفشل لأي جدول بدون مركبة (+ ALTER) | | 5 | `driver/list.php` يفك تشفير عمود `phone` غير مُحدد في SELECT | `transit/driver/list.php` | الهاتف لا يظهر أبداً في قائمة السائقين | | 6 | `org/register.php` يقرأ `SOCKET_INTERNAL_KEY` (اسم خاطئ) بدل `INTERNAL_SOCKET_KEY` | `transit/org/register.php` | التسجيل الداخلي يرفض دائماً 403 — الآن عبر `getInternalSocketKey()` | نتائج فحص إضافية مطمئنة: `encryptData` **حتمي** (CBC بـ IV ثابت) فمطابقة الهاتف/الرقم الجامعي المشفّر تعمل. مفاتيح Redis متسقة (بادئة `siro:` للجلسات على المثيلين). مسارات AppLink بالتطبيقات الثلاثة تطابق مسارات الباك اند. --- ## 3. الثغرات الأمنية المتبقية (بالترتيب) ### أ. حرجة — IDOR في endpoints رحلات السائق `trip/today.php` و`trip/start.php` و`trip/end.php` و`trip/delay.php` تستقبل `driver_transit_id` من العميل **دون التحقق أن JWT المتصل هو صاحب هذا المعرّف**. أي مستخدم مسجّل (حتى راكب) يستطيع بدء/إنهاء/تأخير رحلات أي سائق باص بتخمين أرقام تسلسلية. السوكيت محمي بملكية الرحلة، لكن REST مكشوف. **الإصلاح**: في الملفات الأربعة — اشترط `$transit_user_role === 'driver'` ثم اجلب سجل السائق بـ `WHERE main_driver_id = $transit_user_id` وتجاهل `driver_transit_id` القادم من العميل نهائياً (أو تحقق من تطابقه). ### ب. حرجة وظيفياً — `main_driver_id` لا يُضبط أبداً `driver/invite.php` يقبله كحقل اختياري يدخله مشرف المؤسسة (لا يعرفه عملياً)، و`driver/activate.php` لا يضبطه. النتيجة: `driver/me.php` يرجع `is_bus_driver=false` للجميع، و`trip/start.php` يرفض بـ 422 — **مسار السائق كامل غير قابل للاستخدام end-to-end**. **الإصلاح المقترح**: نقطة تفعيل جديدة تعمل بـ JWT السائق (`connect_app.php` + role=driver): يرسل `invite_token` فقط، والباك اند يطابق التوكن ويضبط `main_driver_id = $transit_user_id` تلقائياً. هذا يحل (أ) و(ب) معاً ويلغي الحاجة لـ OTP في التفعيل. ### ج. عالية — التفعيل الحالي `driver/activate.php` بلا مصدر OTP يتحقق بـ `transitVerifyAdminOtp` لكن لا يوجد endpoint يرسل OTP لهاتف السائق (login_request يشترط وجوده في `transit_org_admins`). المسار ميت — يُستبدل بحل النقطة (ب). ### د. عالية — اشتراك سوكيت الراكب بلا تحقق عضوية `subscribe_transit_route` في passenger_socket يضم أي راكب مصادَق لأي غرفة خط دون التحقق من عضويته النشطة — تسريب مواقع الباصات لغير المشتركين (REST محمي، السوكيت لا). **الإصلاح**: عند التفعيل/الموافقة يكتب الباك اند عضوية الراكب في Redis (مثال `transit:route_members:{org_id}` Set على Redis الموقع)، والسوكيت يتحقق منها قبل `join`. ### هـ. متوسطة — لا Rate limiting على مسارات OTP والجلسات `admin/login_request.php` و`login_verify.php` و`connect_admin.php` تُحمّل bootstrap مباشرة بدون `RateLimiter->enforce()` — إغراق OTP (سبام واتساب) وتعداد هواتف المشرفين (رسالة 401 مميزة) ممكنان. مع OTP من 3 خانات (900 احتمال، قفل بعد 3 محاولات) يصبح الحد الإلزامي أهم. **الإصلاح**: أضف RateLimiter في الملفين + `connect_admin.php`، ووحّد رد login_request لغير الموجود (نفس رسالة النجاح دون إرسال). ### و. متوسطة — `trip/update_position.php` بلا أي مصادقة موثّق كـ fallback مهجور لكنه منشور ويكتب مواقع في Redis لأي طارق. **احذفه أو ضعه خلف JWT**. ### ز. منخفضة - جلسات `transit_sessions` المنتهية لا تُنظّف من MySQL (أضف لتنظيف الكرون الموجود) - لا endpoint لتسجيل خروج مشرف المؤسسة (حذف الجلسة) - رسائل أخطاء 4xx من `jsonError` لا تصل للتطبيقات — دوال CRUD في التطبيقات الثلاثة ترجع `'failure'` لأي 4xx (عدا 401) دون قراءة الرسالة → المستخدم يرى خطأ عام بدل "Enrollment already exists". قرار معماري: إما إرجاع 200 مع `status=failure` بمسارات transit، أو تعديل `_makeRequest` ليقرأ جسم 4xx --- ## 4. الفجوات الوظيفية لكل طرف ### مشرف المؤسسة (الأكبر — لا واجهة إطلاقاً) الـ API موجود لكن **لا توجد لوحة ويب**. راجع القسم 5 للمواصفات الكاملة. فجوات API نفسها: - **لا يوجد endpoint لاعتماد/تفعيل خط**: `route/add` ينشئ `status='draft'` والمخطط يقول الاعتماد لفريق سيرو (`approved_by`) — لكن لا endpoint في `Admin/transit/` ولا في `transit/` يحوّل draft→active. **بدونها الراكب لا يرى أي خط أبداً** - لا تعديل/حذف: route/update، stop إدارة مستقلة (مجلد `transit/stop/` فارغ)، schedule/list+delete، vehicle/update+delete، driver/suspend، org/profile update - لا إلغاء رحلة (enum فيه cancelled/no_show بلا endpoint) - لا قائمة كشوف مستوردة (rosters list) ولا سجل إعلانات (broadcasts list — موجود جزئياً في dashboard آخر 5) ### فريق سيرو (siro_admin) - اعتماد الخطوط (النقطة أعلاه) — endpoint + شاشة - إدارة حالة العقد (trial/active/suspended/terminated) — لا endpoint ولا UI - تعديل بيانات مؤسسة قائمة ### الراكب (siro_rider) - **اشتراك FCM Topic فعلي غائب**: التطبيق لا يستدعي `subscribeToTopic('transit_route_{id}')` ولا `transit_org_{id}` — إشعارات الانطلاق/التأخير/الإعلانات لا تصل لأحد - إشعار الموافقة على العضوية يُرسل لموضوع `passenger_{id}` والتطبيق يشترك فقط بموضوع `"passengers"` العام — لا يصل. الحل: اشتراك التطبيق بموضوعه الشخصي عند تسجيل الدخول، أو تغيير آلية الإرسال - الخريطة الحية لا ترسم polyline الخط (البيانات موجودة في `trip/live.php`؛ متوفر `PolylineUtils.decode` في intaleq_maps) ولا أيقونة باص مخصصة (ماركر hue فقط) - زر "فاتك الباص؟ اطلب سيارة" — الميزة التسويقية الأساسية من الرؤية، غير مبنية ### السائق (siro_driver) - **كشف الوصول للمحطات (geofence) غير مبني**: لا منطق يكتشف دخول نطاق محطة ويرسل `current_stop_seq` — تتبع تقدّم الباص على المحطات لا يعمل - خدمة الخلفية (`background_service.dart`) لها سوكيت مستقل غير واعٍ لوضع الباص — عند طيّ التطبيق أثناء رحلة باص قد يعود البث العادي (تلوث حوض الرحلات) ويتوقف بث الباص. تحتاج مراجعة وتمرير حالة وضع الباص - لا معالج deep link لرابط الدعوة `siromove.com/driver/transit-activate?token=` (يُحل ضمن مسار التفعيل الجديد بالنقطة 3-ب) - `trip/start` قد يرسل lat/lng = 0,0 إن لم يُلتقط الموقع بعد — أضف انتظار/تحقق ### النظام - كرون مقترح: تنظيف `transit_sessions` المنتهية + إنشاء رحلات الغد مسبقاً (اختياري — حالياً تُنشأ عند أول فتح للسائق) + تقارير أسبوعية للمؤسسات (مؤجل باتفاق) - `TRANSIT_STUDENT_ID_KEY` في `.env.example` لم يعد مستخدماً (نستخدم `$encryptionHelper` العام) — احذفه منعاً للالتباس --- ## 5. لوحة مشرف المؤسسة — المواصفات الكاملة الأسئلة المطروحة: كيف يدخل المشرف؟ كيف يرسم الخطوط بتفاصيلها؟ كيف يديرها؟ هذه الإجابة الكاملة، مبنية على الـ API القائم: **التقنية المقترحة**: ويب SPA (تُنشر على `transit.siromove.com` — CORS جاهز في `connect_admin.php`). كل الطلبات POST مع هيدر `Authorization: Bearer {session_token}`. الردود: `{status:'success', message:{...}}`. ### تدفق الدخول 1. شاشة هاتف → `transit/admin/login_request.php` {phone} → OTP واتساب (3 خانات) 2. شاشة رمز → `transit/admin/login_verify.php` {phone, otp} → `{token, expires_in, admin{}, org{}}` — يُخزن التوكن (صالح 24 ساعة) ### الشاشات (بترتيب البناء) 1. **اليوم (Dashboard)** — `admin/dashboard.php`: رحلات اليوم بحالاتها + موقع حي لكل باص started + عدادات + آخر 5 إعلانات. تحديث كل 15 ثانية 2. **الأسطول** — vehicles: قائمة `vehicle/list` + نموذج `vehicle/add` (plate, make, model, year, color, capacity, vehicle_type) 3. **السائقون** — `driver/list` + دعوة `driver/invite` {name, phone, license_number} → واتساب تلقائي برابط تفعيل. حالات invited/active/suspended 4. **الخطوط (الأهم)** — `route/list` + إنشاء بخريطة تفاعلية: - خريطة (Leaflet/MapLibre) ينقر عليها المشرف لإضافة المحطات بالترتيب؛ لكل محطة: name_ar، name_en، نصف قطر الجيوفينس (افتراضي 150م)، eta_offset_min، is_major - يرسم المسار (polyline مشفّر) بين المحطات — إما يدوياً أو عبر routes-osm القائم - حفظ → `route/add` {name_ar, name_en, direction, polyline, distance_km, duration_min, stops:JSON} → يُنشأ **draft** بانتظار اعتماد فريق سيرو - تفاصيل خط: `route/get` (محطات + جداول) 5. **الجداول** — من شاشة الخط: `schedule/add` {route_id, departure_time, days_mask, driver_id?, vehicle_id?, valid_from, valid_until} — واجهة اختيار أيام أسبوع تبني الـ bitmask (bit0=أحد … bit6=سبت، 62=أحد–خميس) 6. **الطلاب** — `enrollment/list` (فلترة بالحالة + ترقيم) + رفع كشف CSV `enrollment/import_roster` (أعمدة student_id, name) + موافقة/رفض `enrollment/approve` 7. **الإعلانات** — `broadcast/send` {body_ar, title_ar, target_type: all|route, target_id} ### ما يجب إضافته للـ API قبل/أثناء بناء اللوحة route/update + route/submit (طلب اعتماد) · schedule/delete · vehicle/toggle · driver/suspend · trip/cancel · logout · rosters/list --- ## 6. تعليمات التنفيذ للنموذج الآخر — مهام مرتبة بالأولوية كل بند أدناه Prompt مستقل. نفّذها بالترتيب؛ المهام 1–4 شرط لأي إطلاق تجريبي. ### المهمة 1 — إغلاق IDOR وربط حساب السائق (حرجة، باك اند + فلاتر) > في مشروع Siro: أصلح ثغرة IDOR في `backend/transit/trip/today.php` و`start.php` و`end.php` و`delay.php` — جميعها تثق بـ `driver_transit_id` من العميل. المطلوب: (1) في كل ملف اشترط `$transit_user_role === 'driver'` ثم استخرج سجل السائق بـ `SELECT id, org_id FROM transit_drivers WHERE main_driver_id = ? AND status='active'` باستخدام `$transit_user_id` من `connect_app.php`، وتجاهل `driver_transit_id` القادم من العميل. (2) أنشئ `backend/transit/driver/activate_by_app.php` يعمل عبر `connect_app.php` بدور driver: يستقبل `invite_token` فقط، يطابقه في `transit_drivers`، يضبط `main_driver_id = $transit_user_id` و`status='active'` و`activated_at=NOW()` ويصفّر التوكن — هذا يحل مشكلة أن `main_driver_id` لا يُضبط أبداً حالياً. (3) في `siro_driver/lib/controller/transit/transit_driver_service.dart` أضف دالة `activateByInviteToken` وأنشئ شاشة إدخال رمز الدعوة تُعرض داخل `transit_driver_home_page.dart` عندما `is_bus_driver=false`، مع معالجة deep link للمسار `siromove.com/driver/transit-activate?token=`. (4) احذف `backend/transit/driver/activate.php` القديم (مساره ميت — لا مصدر OTP له) وحدّث نص رسالة الواتساب في `driver/invite.php` ليوجّه لفتح تطبيق السائق. اتبع أنماط النظام: `filterRequest`, `jsonSuccess/jsonError`, `appLog`. لينت PHP وDart بعد كل تعديل. ### المهمة 2 — اعتماد الخطوط draft→active (حرجة، باك اند + سيرو آدمن) > في مشروع Siro: لا يوجد أي مسار يحوّل خط مواصلاتي من draft إلى active، فالراكب لا يرى الخطوط أبداً. المطلوب: (1) أنشئ `backend/Admin/transit/route/approve.php` بنمط ملفات `backend/Admin/transit/org/` (فحص `$role` admin/super_admin، `Database::get('transit')`): يستقبل route_id و action (approve|suspend|reject)، يحدّث `transit_routes.status` و`approved_by` (من `$user_id` بالـ JWT) و`approved_at`. (2) أنشئ `backend/Admin/transit/route/pending.php` يرجع كل خطوط draft عبر المؤسسات مع اسم المؤسسة وعدد المحطات. (3) في siro_admin أضف شاشة "اعتماد الخطوط" (`lib/views/transit/route_approval_page.dart` بنفس ثيم `org_list_page.dart` الداكن) تعرض المسودات مع محطاتها على خريطة مصغرة إن أمكن وزرّي اعتماد/رفض، واربطها من `admin_home_page.dart` فئة مواصلاتي، ووسّع `transit_admin_service.dart` و`transit_admin_controller.dart`. لينت كل شيء. ### المهمة 3 — تحقق العضوية في اشتراك السوكيت (حرجة، باك اند + سوكيت) > في مشروع Siro: `subscribe_transit_route` في `passenger_server/passenger_socket.php` يضم أي راكب لأي غرفة خط دون تحقق. المطلوب: (1) في `backend/transit/functions.php` أضف `transitCacheEnrollment(int $orgId, string $passengerId)` تكتب `SADD transit:org_members:{orgId}` على `$redisLocation` (بدون بادئة) مع TTL تجديدي 7 أيام، و`transitDropEnrollment` للحذف. استدعِ الإضافة عند تفعيل العضوية في `enrollment/activate.php` (الحالتان) وعند الموافقة في `enrollment/approve.php`، والحذف عند الرفض/التعليق. (2) أنشئ سكربت `backend/transit/cron_sync_members.php` يعيد بناء المجموعات من MySQL (للتشغيل اليدوي والكرون اليومي). (3) في `passenger_socket.php` عند `subscribe_transit_route` استعلم Redis المحلي: خذ org_id للخط من هاش جديد `transit:route_org:{routeId}` (اكتبه من `route/add.php` وapprove في المهمة 2) ثم `SISMEMBER transit:org_members:{orgId} passengerId` — ارفض الانضمام إن لم يكن عضواً وسجّل بالـ log. انتبه: سوكيت الراكب يتصل بـ Redis؟ إن لم يكن فيه اتصال Redis أضف واحداً بنمط `getRedis()` من `loction_server/driver_socket.php`. لينت PHP. ### المهمة 4 — Rate limiting ومصادقة المسارات المكشوفة (عالية، باك اند) > في مشروع Siro: (1) أضف `RateLimiter($redis)->enforce(RateLimiter::identifier(), 'api')` (كما في `backend/transit/connect_app.php`) إلى: `backend/transit/admin/login_request.php`، `login_verify.php`، و`backend/transit/connect_admin.php`. (2) في `login_request.php` وحّد الرد: عند هاتف غير موجود أعد نفس رسالة النجاح دون إرسال OTP (منع تعداد المشرفين). (3) احذف `backend/transit/trip/update_position.php` نهائياً (fallback مهجور بلا مصادقة — المسار الفعلي عبر السوكيت). (4) أنشئ `backend/transit/admin/logout.php` يحذف الجلسة من Redis (`siro:transit:session:{hash}`) وMySQL. (5) أضف تنظيف `DELETE FROM transit_sessions WHERE expires_at < NOW()` إلى سكربت كرون التنظيف القائم في الباك اند (ابحث عن كرونات التنظيف الموجودة واتبع نمطها). لينت. ### المهمة 5 — إشعارات FCM فعلية (عالية، فلاتر رايدر + باك اند) > في مشروع Siro: الإشعارات لا تصل لأن الاشتراك بالمواضيع غير مبني. المطلوب: (1) في `siro_rider/lib/controller/firebase/firbase_messge.dart` يوجد `subscribeToTopic("passengers")` — أضف بعده اشتراكاً بالموضوع الشخصي `passenger_{id}` من `box.read(BoxName.passengerID)`. (2) في `siro_rider/lib/controller/transit/transit_controller.dart`: عند نجاح `activateEnrollment` اشترك بـ `transit_org_{orgId}`، وفي `openLiveRoute`/زر جديد "تنبيهات الخط" اشترك بـ `transit_route_{routeId}` عبر `FirebaseMessaging.instance.subscribeToTopic` مع استدعاء `backend/transit/notification/subscribe.php` القائم لتسجيل المحطة المفضلة، ومع إلغاء الاشتراك المقابل. (3) تحقق أن معالج الرسائل الحالي يعرض إشعارات data type=transit_* عندما يكون التطبيق بالمقدمة. لينت Dart. ### المهمة 6 — لوحة مشرف المؤسسة الويب (كبيرة — قسّمها على جلسات) > في مشروع Siro: ابنِ لوحة ويب لمشرف المؤسسة حسب المواصفات في `docs/mawasalati_full_system_review.md` قسم 5. أنشئها في مجلد جديد `transit_dashboard/` بجذر المشروع (Vue 3 أو React + Vite، عربي RTL أساسي مع تبديل فاتح/داكن). الدخول: `POST {SERVER}/transit/admin/login_request.php` ثم `login_verify.php`، التوكن في `Authorization: Bearer`. ابدأ بجلسة أولى: هيكل المشروع + الدخول + Dashboard اليوم + قائمة المركبات والسائقين. الجلسة الثانية: منشئ الخطوط بالخريطة (MapLibre + نقر لإضافة محطات + حفظ route/add) والجداول (days_mask bitmask: bit0=أحد…bit6=سبت). الجلسة الثالثة: الطلاب (قائمة/كشف CSV/موافقات) والإعلانات. أضف أثناء ذلك endpoints الناقصة الموثقة نهاية قسم 5 بنفس أنماط `backend/transit/` (connect_admin, filterRequest, jsonSuccess). ### المهمة 7 — جيوفينس المحطات في تطبيق السائق (متوسطة) > في مشروع Siro: سائق الباص لا يكتشف وصوله للمحطات. في `siro_driver/lib/controller/transit/transit_driver_controller.dart` أضف منطقاً أثناء الرحلة النشطة: قارن موقع `LocationController.myLocation` (استمع عبر نفس تدفق التحديث) مع محطات `activeTrip.stops` — عند دخول نطاق `geofence_radius` لمحطة تسلسلها أعلى من الحالي، مرّر `currentStopSeq` الجديد إلى `emitBusLocationToSocket` (المعامل موجود جاهز في `location_controller.dart`) وحدّث الواجهة بشريط تقدم المحطات في `transit_driver_home_page.dart`. استخدم `geo.Geolocator.distanceBetween` الموجود. لا تستدعِ REST — السوكيت يخزّن التسلسل في Redis ويبثه. ### المهمة 8 — مراجعة خدمة الخلفية لوضع الباص (متوسطة، سائق) > في مشروع Siro: افحص `siro_driver/lib/controller/functions/background_service.dart` بالكامل: هل ينشئ سوكيتاً خاصاً ويبث `update_location` عندما يكون التطبيق بالخلفية؟ إن كان كذلك فعند وضع الباص (`LocationController.isBusMode`) سيلوث حوض الرحلات ويوقف بث الباص. مرّر حالة وضع الباص وبيانات الرحلة (trip_id/route_id — خزّنها في GetStorage box عند `setBusMode`) إلى الخدمة الخلفية وبدّل الحدث إلى `update_bus_location` بنفس الحمولة المستخدمة في `emitBusLocationToSocket`. اختبر السيناريو: بدء رحلة باص → طي التطبيق → التأكد من استمرار البث الصحيح. ### المهمة 9 — تحسينات خريطة الراكب (منخفضة) > في مشروع Siro، ملف `siro_rider/lib/views/transit/transit_live_map_page.dart`: (1) ارسم polyline الخط — الحقل `polyline` يأتي ضمن `trip/live.php`؟ تحقق؛ إن لم يكن أضفه للاستعلام في `backend/transit/trip/live.php` (من `transit_routes.polyline`)، ثم فكّه بـ `PolylineUtils.decode` من حزمة intaleq_maps وأضف `Polyline` للخريطة بلون `AppColor.accentColor`. (2) استخدم أيقونة باص مخصصة للماركر عبر `InlqBitmap.fromAsset` (أضف أصل `assets/images/bus.png` — انظر pubspec). (3) أضف بطاقة سفلية بمعلومات الرحلة (السائق، الانطلاق، المحطة الحالية باسمها بدل الرقم). (4) زر "فاتك الباص؟ اطلب سيارة الآن" يوجّه لطلب رحلة عادية من موقع المستخدم — نقطة التحويل التجارية الأساسية. ### المهمة 10 — إدارة العقود وتعديل المؤسسات (منخفضة، سيرو آدمن) > في مشروع Siro: أنشئ `backend/Admin/transit/org/update.php` (تعديل بيانات مؤسسة + `contract_status` مع تحقق enum) بنمط `org/create.php`، وعند suspended/terminated أبطل كل جلسات مشرفي المؤسسة (انظر منطق `org/admin_toggle.php`). في siro_admin أضف للشاشة `org_details_page.dart` قائمة إجراءات (تعليق/تفعيل/إنهاء عقد + تعديل الحقول) عبر `transit_admin_service.dart`. لينت. --- ## 7. تشيك ليست النشر (قبل أول تجربة حقيقية) 1. تنفيذ ALTERs الترحيل (نهاية `schema_transit.sql`) على `siroTransitDb` القائمة 2. `.env` الإنتاج: قيم `DB_TRANSIT_*` + تأكيد `REDIS_LOCATION_HOST` مضبوط (وإلا لا يظهر موقع الباص إطلاقاً — نفس شرط كثافة السائقين) 3. إعادة تشغيل `driver_socket.php` و`passenger_socket.php` بالنسخ المحدثة ومراقبة اللوج: رسائل `🚫 update_bus_location rejected` تعني مشكلة ملكية؛ `[TRANSIT]` بسوكيت الراكب تؤكد الاشتراكات 4. جدولة كرون: تنظيف الجلسات + مزامنة أعضاء Redis (المهمة 3) 5. اختبار دخاني كامل: إنشاء مؤسسة من سيرو آدمن → دخول مشرف OTP → مركبة + سائق (دعوة) → تفعيل السائق من تطبيقه (المهمة 1) → خط + محطات + جدول → اعتماد الخط من سيرو آدمن (المهمة 2) → كشف طلاب → تفعيل طالب بالرقم الجامعي → رؤية الخط → بدء الرحلة من السائق → الموقع الحي يصل للراكب → إنهاء الرحلة