28 KiB
مراجعة شاملة لنظام مواصلاتي — 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:{...}}.
تدفق الدخول
- شاشة هاتف →
transit/admin/login_request.php{phone} → OTP واتساب (3 خانات) - شاشة رمز →
transit/admin/login_verify.php{phone, otp} →{token, expires_in, admin{}, org{}}— يُخزن التوكن (صالح 24 ساعة)
الشاشات (بترتيب البناء)
- اليوم (Dashboard) —
admin/dashboard.php: رحلات اليوم بحالاتها + موقع حي لكل باص started + عدادات + آخر 5 إعلانات. تحديث كل 15 ثانية - الأسطول — vehicles: قائمة
vehicle/list+ نموذجvehicle/add(plate, make, model, year, color, capacity, vehicle_type) - السائقون —
driver/list+ دعوةdriver/invite{name, phone, license_number} → واتساب تلقائي برابط تفعيل. حالات invited/active/suspended - الخطوط (الأهم) —
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(محطات + جداول)
- الجداول — من شاشة الخط:
schedule/add{route_id, departure_time, days_mask, driver_id?, vehicle_id?, valid_from, valid_until} — واجهة اختيار أيام أسبوع تبني الـ bitmask (bit0=أحد … bit6=سبت، 62=أحد–خميس) - الطلاب —
enrollment/list(فلترة بالحالة + ترقيم) + رفع كشف CSVenrollment/import_roster(أعمدة student_id, name) + موافقة/رفضenrollment/approve - الإعلانات —
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/(فحص$roleadmin/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. تشيك ليست النشر (قبل أول تجربة حقيقية)
- تنفيذ ALTERs الترحيل (نهاية
schema_transit.sql) علىsiroTransitDbالقائمة .envالإنتاج: قيمDB_TRANSIT_*+ تأكيدREDIS_LOCATION_HOSTمضبوط (وإلا لا يظهر موقع الباص إطلاقاً — نفس شرط كثافة السائقين)- إعادة تشغيل
driver_socket.phpوpassenger_socket.phpبالنسخ المحدثة ومراقبة اللوج: رسائل🚫 update_bus_location rejectedتعني مشكلة ملكية؛[TRANSIT]بسوكيت الراكب تؤكد الاشتراكات - جدولة كرون: تنظيف الجلسات + مزامنة أعضاء Redis (المهمة 3)
- اختبار دخاني كامل: إنشاء مؤسسة من سيرو آدمن → دخول مشرف OTP → مركبة + سائق (دعوة) → تفعيل السائق من تطبيقه (المهمة 1) → خط + محطات + جدول → اعتماد الخط من سيرو آدمن (المهمة 2) → كشف طلاب → تفعيل طالب بالرقم الجامعي → رؤية الخط → بدء الرحلة من السائق → الموقع الحي يصل للراكب → إنهاء الرحلة