بناء نظام حجز مواعيد طبية: منع التعارض، التذكيرات، وحماية بيانات المرضى

نظرة هندسية لبناء نظام حجز مواعيد طبية: نمذجة التوفّر، منع الحجز المزدوج، خط أنابيب التذكيرات، وحماية السجلات الطبية.

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

نمذجة التوفّر لا المواعيد فقط

الخطأ الشائع هو تخزين "المواعيد المتاحة" كسجلات جاهزة مسبقًا. الأصح نمذجة قواعد التوفّر بشكل منفصل: AvailabilityRule يحدد أيام وأوقات عمل كل طبيب بشكل متكرر (كل ثلاثاء 9-1 مثلًا)، مع استثناءات (AvailabilityException) للإجازات أو التغييرات المؤقتة. الفتحات الفعلية القابلة للحجز تُحسب ديناميكيًا من هذه القواعد عند الطلب، لا تُخزَّن مسبقًا لكل يوم في المستقبل — هذا يجعل تعديل جدول طبيب لأسبوع قادم عملية تحديث قاعدة واحدة، لا تعديل مئات السجلات.

منع الحجز المزدوج تحت التزامن

اللحظة الحرجة هندسيًا هي حين يحاول مريضان حجز نفس الفتحة في نفس اللحظة تقريبًا. الاعتماد على تحقق بسيط في طبقة التطبيق (application-level check) قبل الإدراج غير كافٍ — بينهما نافذة زمنية (race condition) كافية لحجز مزدوج. الحل الموثوق: قيد فريد (unique constraint) على مستوى قاعدة البيانات يجمع doctor_id وslot_start، بحيث يفشل الإدراج الثاني تلقائيًا مهما حدث على مستوى التطبيق، مع معاملة (transaction) تحيط عملية الحجز بأكملها. هذا القيد وحده أهم من أي منطق تحقق مكتوب يدويًا.

خط أنابيب التذكيرات

إرسال التذكير لحظة إنشاء الموعد خطأ شائع — التذكير يجب أن يُجدوَل ليُرسَل قبل الموعد بفترة محددة (يوم، ثم ساعات). أفضل تنفيذ هو مهمة مجدولة (scheduled job) تُنفَّذ دوريًا، تبحث عن مواعيد ضمن نافذة التذكير القادمة وترسل عبر طابور (Queue) منفصل — لا ضمن الطلب الذي أنشأ الحجز أصلًا. الطابور هنا ليس اختياريًا: مزوّدات SMS وواتساب قد تتأخر أو تفشل مؤقتًا، والقدرة على إعادة المحاولة (retry) دون تعطيل واجهة المستخدم تعتمد على فصل الإرسال عن طلب الحجز.

تعدد الفروع والتخصصات

مركز طبي بعدة فروع يحتاج عزلًا واضحًا للتوفّر والجدول لكل فرع، مع إمكانية أن يعمل نفس الطبيب في أكثر من فرع بجداول مختلفة — ما يعني أن AvailabilityRule يجب أن يرتبط بـ(doctor_id, branch_id) معًا لا بالطبيب وحده. التخصصات المختلفة قد تحتاج مدة فتحة مختلفة أيضًا (استشارة أولية 30 دقيقة، متابعة 15 دقيقة) — تخزين مدة الفتحة كخاصية للخدمة (Service) المحجوزة، لا قيمة ثابتة على مستوى النظام.

أين يفيد الذكاء الاصطناعي فعليًا

الاستخدام الأوضح فائدة هو التنبؤ بمخاطر الغياب (No-Show Prediction) بناءً على نمط المريض السابق (عدد الحجوزات الفائتة، الفترة الزمنية بين الحجز والموعد)، مما يسمح بجدولة تذكير إضافي أو حتى Overbooking محسوب للفتحات عالية الخطورة — بالضبط كما تفعل شركات الطيران. اقتراح أقرب فتحة متاحة تلقائيًا بناءً على تفضيلات المريض السابقة استخدام آخر مفيد وواقعي. كلا الاستخدامين يُحسّنان قرارًا تشغيليًا قائمًا، لا يستبدلان الحاجة لمراجعة بشرية لأي حالة حساسة طبيًا.

حماية بيانات المريض

السجلات الطبية تستحق معاملة أصرم من بيانات المستخدم العادية: تشفير الحقول الحساسة عند التخزين (Encryption at Rest)، سجل تدقيق (Audit Log) يُسجّل من اطّلع على أي سجل ومتى — لا فقط من عدّله، وصلاحيات دقيقة تمنع طبيبًا من الاطلاع على سجلات مرضى ليسوا تحت رعايته حتى ضمن نفس المركز. هذه ليست ميزات إضافية تُبنى لاحقًا، بل قرارات يجب أن تكون جزءًا من التصميم الأولي لقاعدة البيانات وطبقة الصلاحيات.

النظام الجيد هندسيًا هنا لا يُقاس بسرعة إطلاقه، بل بصموده أمام التزامن الحقيقي وحمايته لبيانات لا مجال للتهاون فيها — وهما نقطتان يصعب تصحيحهما لاحقًا دون إعادة بناء فعلية لطبقة البيانات.