Get a quote

نظام تتبع الطلبات في الوقت الحقيقي للمطاعم والتوصيل في لبنان والمنطقة

"أين طلبي؟" هذا السؤال وحده يكشف غياب البنية التحتية الحقيقية لأنظمة التوصيل. بناء نظام تتبع آني يتطلب اختيارات معمارية دقيقة تأخذ بعين الاعتبار انقطاع الكهرباء وضعف الشبكة في بيئات كلبنان.

المشكلة التشغيلية: الهاتف كمشكلة هندسية

في كل مطعم يعمل على التوصيل دون نظام تتبع آني، ثمة ظاهرة متكررة: ارتفاع حجم المكالمات الواردة بين الدقيقة الثلاثين والخامسة والأربعين من كل طلب. المستخدم لا يعرف ما إذا كان الطلب قيد التحضير أو في الطريق أو ضائعاً. يتصل للطمأنينة.

كل مكالمة من هذا النوع تكلف المطعم:

  • وقت موظف الاستقبال أو المدير الذي يتتبع الطلب يدوياً
  • وقت السائق الذي قد يتلقى اتصالاً أثناء القيادة
  • تجربة مستخدم سلبية تُقلل احتمال إعادة الطلب

الحل ليس توظيف شخص للرد على هذه المكالمات، بل بناء نظام يجعلها غير ضرورية.

اختيار البنية: WebSockets أم Polling أم SSE

هذا القرار المعماري الأول له تأثير على كل شيء من بعده: استهلاك الموارد، تجربة المستخدم، وسلوك النظام عند انقطاع الاتصال.

Polling التقليدي: العميل يرسل طلباً إلى الخادم كل N ثانية. بسيط للتطبيق، لكنه مكلف عند التوسع: 100 طلب نشط يعني 600 طلب HTTP في الدقيقة فقط لفحوصات الحالة.

Server-Sent Events (SSE): اتصال أحادي الاتجاه من الخادم إلى العميل. الخادم يُرسِل التحديثات حين تحدث. يعمل فوق HTTP/1.1، يدعمه كل المتصفحات، ويُعيد الاتصال تلقائياً عند الانقطاع. مناسب جداً لتتبع الطلبات حيث التحديثات تسير في اتجاه واحد فقط.

WebSockets: اتصال ثنائي الاتجاه. ضروري إذا احتجت إرسال بيانات من العميل إلى الخادم عبر الاتصال ذاته.

توصيتنا لنظام تتبع الطلبات: SSE للعميل، WebSocket للسائق.

app.get('/orders/:orderId/track', (req, res) => {
  const { orderId } = req.params;
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  const sendUpdate = (data: OrderUpdate) => {
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  };

  const unsubscribe = orderEventBus.subscribe(orderId, sendUpdate);
  req.on('close', () => { unsubscribe(); });
});

آلة حالات الطلب

الطلب ليس كياناً ببيانات جامدة، بل كيان يمر بمراحل محددة:

received (استُقبِل)
  accepted (قُبِل من المطعم)
  preparing (قيد التحضير)
  ready (جاهز للاستلام)
  picked_up (استُلِم من السائق)
  delivered (سُلِّم للعميل)

في أي مرحلة قبل picked_up يمكن الانتقال إلى:
  failed (فشل/إلغاء)

كل انتقال يُطلق حدثاً يصل إلى واجهة العميل عبر SSE ونظام الإشعارات ولوحة تحكم المطعم ولوحة تحكم السائق.

لا تسمح أبداً بانتقالات مباشرة تتجاوز مراحل. هذه القاعدة تكشف الأخطاء التشغيلية وتمنع حالات البيانات المستحيلة.

تحديثات الموقع الجغرافي للسائق

الموقع الحالي للسائق يُخزَّن في Redis ويُستبدَل بكل تحديث:

SET driver:{driverId}:location {lat, lng, timestamp} EX 30

وقت انتهاء الصلاحية 30 ثانية يعني: إذا توقفت التحديثات، تُحذف البيانات تلقائياً ونعرف أن السائق فقد الاتصال.

mعدل إرسال التحديثات: كل 5-10 ثوانٍ مناسب. أقل من ذلك يستنزف البطارية، وأكثر من ذلك يخلق تأخراً ملحوظاً.

لتحديث الخريطة في واجهة العميل، لا تُرسِل كل نقطة مباشرة. استخدم حداً أدنى للمسافة:

const MIN_DISTANCE_METERS = 20;

function shouldSendLocationUpdate(
  previous: LatLng,
  current: LatLng
): boolean {
  return getDistance(previous, current) > MIN_DISTANCE_METERS;
}

التعامل مع انقطاع الشبكة

في لبنان تحديداً، انقطاع الكهرباء والإنترنت جزء من البيئة التشغيلية. على جانب السائق، يجب أن يُخزِّن التطبيق التحديثات محلياً ويُرسِلها حين يعود الاتصال:

class LocationQueue {
  private queue: LocationUpdate[] = [];

  addUpdate(location: LatLng): void {
    this.queue.push({ location, timestamp: Date.now() });
    this.attemptFlush();
  }

  private async attemptFlush(): Promise<void> {
    if (!navigator.onLine || this.queue.length === 0) return;
    const batch = [...this.queue];
    this.queue = [];
    try {
      await api.sendLocationBatch(batch);
    } catch {
      this.queue = [...batch, ...this.queue];
    }
  }
}

على جانب العميل، يجب التمييز بين:

  • السائق يتحرك: تحديثات تصل منتظمة
  • السائق في منطقة ضعيفة الشبكة: آخر تحديث قبل 45 ثانية
  • الاتصال مقطوع: لا تحديثات لأكثر من دقيقتين

لا تُظهِر موقع السائق الأخير كأنه حالي. أضف توقيت آخر تحديث وتنبيهاً واضحاً عند انقطاع التغذية.

إشعارات Push للعميل

الإشعارات مكمِّلة لنظام التتبع. الإشعارات ذات القيمة الفعلية:

  • "قَبِل المطعم طلبك" (تأكيد فوري بعد الطلب)
  • "طلبك خرج للتوصيل" (لحظة الاستلام من السائق)
  • "السائق على بُعد 5 دقائق" (يُحسَب من الموقع الجغرافي)
  • "تم توصيل طلبك" (إغلاق حلقة التجربة)

أربعة إشعارات لطلب واحد هو الحد المعقول في معظم الحالات.

الأداء عند المئة طلب متزامن

mئة طلب نشط في وقت واحد يعني مئة اتصال SSE مفتوح ومئة اتصال WebSocket وتحديثات موقع جغرافي كل 5-10 ثوانٍ من كل سائق.

استخدم Redis Pub/Sub لتوزيع الأحداث إذا كان لديك أكثر من خادم:

redis.publish(`order:${orderId}:updates`, JSON.stringify(update));

subscriber.subscribe(`order:${orderId}:updates`, (message) => {
  const update = JSON.parse(message);
  broadcastToOrderListeners(orderId, update);
});

دروس من عمليات المطاعم في لبنان

جدول الانقطاع اليومي: في مناطق كثيرة، الكهرباء تنقطع في أوقات معروفة. صمِّم نظامك ليتعامل مع إعادة التشغيل بأمان، وتأكد من أن الطلبات النشطة لحظة الانقطاع لا تُفقَد بياناتها.

الشبكة المحمولة كخط أساسي: كثير من السائقين يعتمدون على الشبكة المحمولة حصراً. قلِّل حجم البيانات المُرسَلة قدر الإمكان. الإحداثيات الجغرافية المضغوطة وتحديثات الحالة المختصرة تصنع فرقاً في اتصال 3G ضعيف.

راقب عدد الاتصالات المفتوحة. ارتفاع مفاجئ في الاتصالات التي لا تُغلق بعد انتهاء الطلب يشير إلى خلل في التنظيف. اجعل هذا مرئياً في لوحة التحكم التشغيلية.

ابنِ نظام تتبع يعمل في بيئتك الفعلية

nظام التتبع الآني ليس ميزة تجميلية، بل بنية تحتية تشغيلية تُخفِّض المكالمات الواردة وتُحسِّن رضا العملاء. في فوكساير، نبني أنظمة التوصيل والتتبع لمطاعم وشركات لوجستية في لبنان والمنطقة.

https://voxire.com/get-a-quote/

العودة إلى المدونة
Chat on WhatsApp