مبادئ التصميم

معرفات نصية قصيرة

جميع المفاتيح الأساسية تستخدم معرّفات نصية قصيرة مخصصة (أقصى 15 حرفًا) عبر BaseEntity. لا أعداد صحيحة متزايدة تلقائيًا مكشوفة للعملاء.

DATETIMEOFFSET

جميع الطوابع الزمنية تستخدم DATETIMEOFFSET للتوعية بالمنطقة الزمنية. التطبيق يعمل بتوقيت Asia/Riyadh (UTC+3)، بدون توقيت صيفي.

فرض على مستوى قاعدة البيانات

الثوابت الحرجة تُفرض عبر قيود SQL Server والمشغّلات (triggers) والفهارس الفريدة — وليس فقط في كود التطبيق.

سجل تدقيق كامل

جدول audit_logs غير قابل للتغيير وملحق فقط. كل تعديل يُسجّل.

القيود الحرجة

منع الحجز المزدوج يجب أن يُفرض على مستوى قاعدة البيانات. في SQL Server، استخدم مزيجًا من UNIQUE INDEX على (field_id, date, start_time) مع INSTEAD OF INSERT trigger (أو serializable transaction) للتحقق من النطاقات الزمنية المتداخلة. حتى لو كان هناك خطأ في كود التطبيق، يجب على قاعدة البيانات رفض الإدراجات المتعارضة.
القيد الجدول الغرض
UNIQUE INDEX + Trigger bookings يمنع الحجوزات المتداخلة على نفس الملعب + النطاق الزمني. غير قابل للتفاوض.
NOT NULL + CHECK(price > 0) bookings كل حجز يجب أن يكون له سعر. الإصدار السابق كان يفتقد 58.3%.
معرفات نصية قصيرة (BaseEntity) جميع الجداول معرّفات قصيرة مخصصة (أقصى 15 حرفًا). لا أعداد صحيحة متزايدة تلقائيًا مكشوفة.
DATETIMEOFFSET جميع أعمدة الطوابع الزمنية طوابع زمنية واعية بالمنطقة الزمنية (UTC+3).
التعدادات كنصوص أعمدة الحالة EF Core HasConversion<string>() + قيود CHECK للقيم الصالحة.
UNIQUE(field_id, date, start_time) booking_holds حجز مؤقت واحد فقط لكل فترة في كل مرة.
INSERT ONLY (بدون UPDATE/DELETE) audit_logs سجل تدقيق غير قابل للتغيير. يُفرض عبر trigger أو سياسة التطبيق.

أنواع التعدادات تُخزّن كنصوص

جميع التعدادات تُخزّن كأعمدة NVARCHAR باستخدام HasConversion<string>() في EF Core. يُعرّف التطبيق 18 نوع تعداد.

التعداد القيم
booking_status booked confirmed completed cancelled no_show
payment_status unpaid partial paid refunded
booking_source admin fm customer contract guest
user_role admin executive field_manager customer
notification_status pending sent delivered failed
sport_type football volleyball padel
schedule_rule_type add_slots remove_slots modify_price modify_time

الجداول حسب النطاق

التسلسل الجغرافي

citieslocationsfields — مدينة واحدة تحتوي على عدة مواقع، وموقع واحد يحتوي على عدة ملاعب.

الجدول الأعمدة الرئيسية الغرض
cities id, name_ar, name_en, is_active التجميع الجغرافي الأعلى (مثلًا: بريدة، عنيزة)
locations id, city_id, name_ar, name_en, lat, lng, address, operating_hours موقع فعلي مع إحداثيات GPS. موقع واحد يمكن أن يحتوي على عدة ملاعب.
fields id, location_id, name_ar, name_en, sport_type, is_active, base_price, schedule_profile_id ملعب فردي. له نوع رياضة، سعر أساسي، وملف جدولة مُسند.

الجدولة

نظام قائم على القوالب يحل محل التسلسل الرباعي غير المستدام في الإصدار السابق. الملف يحتوي على قواعد مرتبة تحدد الفترات المتاحة.

الجدول الأعمدة الرئيسية الغرض
schedule_profiles id, name, description, is_default قالب مسمى يُسند للملاعب. يحتوي على مجموعة من القواعد.
schedule_rules id, profile_id, rule_type, priority, days_of_week, date_from, date_to, start_time, end_time, slot_duration, price_override قاعدة فردية بأولوية رقمية. الأولوية الأعلى تفوز في التعارضات. الأنواع: add_slots, remove_slots, modify_price, modify_time.
prayer_times id, city_id, date, fajr, zuhr, asr, maghrib, isha أوقات الصلاة اليومية لكل مدينة. قواعد الجدولة يمكنها الإشارة إليها مع إزاحات.

دورة حياة الحجز

الجدول الأعمدة الرئيسية الغرض
bookings id, field_id, customer_id, creator_id, date, start_time, end_time, status, source, price, payment_status, cancellation_reason, notes الجدول المركزي. آلة حالة (5 حالات). UNIQUE INDEX + Trigger يمنع التداخلات. السعر إلزامي.
booking_holds id, field_id, date, start_time, end_time, held_by, expires_at حجز مؤقت للفترة لمدة 5 دقائق أثناء ملء النموذج. تنتهي صلاحيته عبر مهمة خلفية.
tickets id, booking_id, slot_date, slot_start, slot_end فترات زمنية فردية ضمن الحجز (للحجوزات متعددة الفترات).

العقود (الحجوزات المتكررة)

الجدول الأعمدة الرئيسية الغرض
contracts id, customer_id, creator_id, start_date, end_date, is_active, total_price, notes اتفاقية حجز متكرر. تُنشئ الحجوزات تلقائيًا.
contract_days id, contract_id, field_id, day_of_week, start_time, end_time, price, is_enabled إعداد كل يوم. تفعيل/تعطيل الأيام بدون إعادة إنشاء العقد.

المستخدمون والمصادقة

الجدول الأعمدة الرئيسية الغرض
users id, phone, email, name, role, pin_hash, is_active, assigned_field_ids جميع الأدوار في جدول واحد. رقم الهاتف هو المعرّف الأساسي للعملاء. نطاق مدير الملعب عبر assigned_field_ids.
otp_codes id, phone, code, expires_at, is_used, attempts رموز OTP من 6 أرقام. صلاحية 5 دقائق. بحد أقصى 3 كل 15 دقيقة لكل رقم هاتف.
sessions id, user_id, token, ip_address, user_agent, expires_at الجلسات النشطة للويب (cookie) والموبايل (JWT).
login_attempts id, phone_or_email, attempt_type, ip_address, success, created_at تتبع محاولات تسجيل الدخول لتحديد المعدل والأمان.

المالية

الجدول الأعمدة الرئيسية الغرض
payments id, booking_id, amount, method, recorded_by, vat_amount, notes مدفوعات نقدية عند الإطلاق. جاهز لبوابة الدفع مستقبلًا. أول دفعة تؤكد الحجز تلقائيًا.
refunds id, payment_id, amount, reason, processed_by مرتبط بالدفعة الأصلية. يتتبع من قام بالمعالجة ولماذا.

الاتصالات

الجدول الأعمدة الرئيسية الغرض
notifications id, user_id, booking_id, template_id, channel, status, sent_at, delivered_at, provider, external_id الإشعارات الصادرة (WhatsApp). تتبع حالة التسليم والمزوّد المستخدم.
notification_templates id, key, name_ar, body_ar, variables 4 قوالب أساسية: booking_created, booking_confirmed, 24h_reminder, booking_cancelled.
incoming_messages id, from_phone, body, received_at, is_processed رسائل WhatsApp الواردة عبر WAHA webhook. للمراسلة ثنائية الاتجاه مستقبلًا.

التدقيق والعملاء

الجدول الأعمدة الرئيسية الغرض
audit_logs id, user_id, entity_type, entity_id, action, field_name, old_value, new_value, reason, created_at غير قابل للتغيير وملحق فقط. لا يُسمح بـ UPDATE أو DELETE. يسجل كل تعديل على البيانات.
customer_contacts id, phone, name, notes, created_by بحث العملاء بالهاتف للإكمال التلقائي أثناء إنشاء الحجز.

الجداول المؤجلة المخطط فقط

هذه الجداول موجودة في المخطط لكنها معطّلة عند الإطلاق بعلامات الميزات. أنشئ الترحيلات لكن لا تبنِ منطق التطبيق.

الجدول الغرض
captains كباتن الفرق الذين ينظمون مجموعات اللاعبين.
teams الفرق المرتبطة بالكباتن.
team_members الأعضاء المنتمون للفرق.
booking_attendance تتبع الحضور (check-in) للحجوزات الفردية.

العلاقات الرئيسية

// التسلسل الجغرافي
City  1 ──> * Location  1 ──> * Field

// جدولة الملاعب
Field * ──> 1 ScheduleProfile  1 ──> * ScheduleRule

// جوهر الحجز
Field    1 ──> * Booking
Customer 1 ──> * Booking
User     1 ──> * Booking  (كمنشئ)
Booking  1 ──> * Ticket
Booking  1 ──> * Payment
Payment  1 ──> * Refund

// العقود
Customer 1 ──> * Contract
Contract 1 ──> * ContractDay
Contract 1 ──> * Booking  (حجوزات مُولّدة)

// الإشعارات
Booking  1 ──> * Notification
User     1 ──> * Notification
Template 1 ──> * Notification

// التدقيق
User 1 ──> * AuditLog  (من أجرى التغيير)
المواصفات الكاملة للمخطط: راجع design/SCHEMA.md في المستودع لتعريفات الأعمدة والفهارس الكاملة. ترحيل EF Core في OneTwoApi/Migrations/ يُظهر المخطط المُنفّذ الحالي.