Files
intaleq/docs/05_transit_mawasalati/mawasalati_full_system_review.md
T
Hamza-AyedandClaude Opus 5 0aab85c732 refactor: إعادة تسمية التطبيقات الأربعة siro_* → intaleq_*
المجلدات وأسماء حزم dart واستيراداتها:
  siro_rider → intaleq_rider    siro_driver  → intaleq_driver
  siro_admin → intaleq_admin    siro_service → intaleq_service

شمل ذلك `name:` في كل pubspec وكل `package:siro_*` في الاستيرادات
(115 ملفاً في rider وحده)، وإشارات أسماء المجلدات في docs/ وتعليق في
backend/Admin/notifications/broadcast.php. لم تبقَ إشارة واحدة (تحقّقت).

+ ضُمّت حزم get و get_storage داخل intaleq_driver/packages/ وحُوّلت
مساراتها الثلاثة من `../../Intaleq/packages/*` إلى `./packages/*`. كانت
تُحل عرضاً إلى مجلد App/Intaleq القديم المجاور — تبعية خارج المستودع
تنكسر بأول نقل. لم يبقَ في أي تطبيق تبعية مسار خارج الشجرة.

⚠️ لم تُمسّ بعد: هويات المتاجر (applicationId · PRODUCT_BUNDLE_IDENTIFIER ·
shorebird app_id) ولا الألوان ولا النصوص المرئية ولا النطاقات — كل منها
كوميت منفصل، والهويات تحتاج قرار المالك.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 16:10:58 +03:00

28 KiB

مراجعة شاملة لنظام مواصلاتي — 2026-07-12

دراسة كاملة للنظام عبر: الباك اند (backend/transit/ + backend/Admin/transit/)، سيرفرات السوكيت (loction_server/driver_socket.php + passenger_server/passenger_socket.php)، والتطبيقات الثلاثة (intaleq_rider / intaleq_driver / intaleq_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)

فريق سيرو (intaleq_admin)

  • اعتماد الخطوط (النقطة أعلاه) — endpoint + شاشة
  • إدارة حالة العقد (trial/active/suspended/terminated) — لا endpoint ولا UI
  • تعديل بيانات مؤسسة قائمة

الراكب (intaleq_rider)

  • اشتراك FCM Topic فعلي غائب: التطبيق لا يستدعي subscribeToTopic('transit_route_{id}') ولا transit_org_{id} — إشعارات الانطلاق/التأخير/الإعلانات لا تصل لأحد
  • إشعار الموافقة على العضوية يُرسل لموضوع passenger_{id} والتطبيق يشترك فقط بموضوع "passengers" العام — لا يصل. الحل: اشتراك التطبيق بموضوعه الشخصي عند تسجيل الدخول، أو تغيير آلية الإرسال
  • الخريطة الحية لا ترسم polyline الخط (البيانات موجودة في trip/live.php؛ متوفر PolylineUtils.decode في intaleq_maps) ولا أيقونة باص مخصصة (ماركر hue فقط)
  • زر "فاتك الباص؟ اطلب سيارة" — الميزة التسويقية الأساسية من الرؤية، غير مبنية

السائق (intaleq_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) في intaleq_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) في intaleq_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) في intaleq_rider/lib/controller/firebase/firbase_messge.dart يوجد subscribeToTopic("passengers") — أضف بعده اشتراكاً بالموضوع الشخصي passenger_{id} من box.read(BoxName.passengerID). (2) في intaleq_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: سائق الباص لا يكتشف وصوله للمحطات. في intaleq_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: افحص intaleq_driver/lib/controller/functions/background_service.dart بالكامل: هل ينشئ سوكيتاً خاصاً ويبث update_location عندما يكون التطبيق بالخلفية؟ إن كان كذلك فعند وضع الباص (LocationController.isBusMode) سيلوث حوض الرحلات ويوقف بث الباص. مرّر حالة وضع الباص وبيانات الرحلة (trip_id/route_id — خزّنها في GetStorage box عند setBusMode) إلى الخدمة الخلفية وبدّل الحدث إلى update_bus_location بنفس الحمولة المستخدمة في emitBusLocationToSocket. اختبر السيناريو: بدء رحلة باص → طي التطبيق → التأكد من استمرار البث الصحيح.

المهمة 9 — تحسينات خريطة الراكب (منخفضة)

في مشروع Siro، ملف intaleq_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). في intaleq_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) → كشف طلاب → تفعيل طالب بالرقم الجامعي → رؤية الخط → بدء الرحلة من السائق → الموقع الحي يصل للراكب → إنهاء الرحلة