إنتقل إلى المحتوى الرئيسي

RFC-002: نظام الإشعارات

الحقلالقيمة
الحالة📋 مسودة
المؤلففريق التطوير
تاريخ الإنشاء2025-01-01
آخر تحديث2025-01-01

الملخص

إنشاء نظام إشعارات موحد وقابل للتوسع يدعم قنوات متعددة (Push, SMS, WhatsApp, Email) مع إمكانية التخصيص والجدولة.

الدافع

الوضع الحالي

الإشعارات موزعة في الكود:

  • WhatsApp عبر Twilio (محدود)
  • SMS للـ OTP فقط
  • لا يوجد Push Notifications
  • لا يوجد Email

المشاكل

  1. التجزئة:

    • منطق الإشعارات موزع
    • صعوبة الصيانة
    • تكرار الكود
  2. القصور:

    • لا تذكير بالرحلات
    • لا إشعارات للتحديثات
    • لا إشعارات للعروض
  3. عدم التتبع:

    • لا نعرف نسبة التسليم
    • لا تحليلات للإشعارات

التصميم المقترح

البنية العامة

┌─────────────────────────────────────────────────────┐
│ Notification Service │
├─────────────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Template │ │ Scheduler│ │ Analytics│ │
│ │ Engine │ │ │ │ │ │
│ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────────────┤
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌─────┐ │
│ │ Push │ │ SMS │ │ WhatsApp │ │Email│ │
│ │ Provider │ │ Provider │ │ Provider │ │Prov.│ │
│ └──────────┘ └──────────┘ └──────────┘ └─────┘ │
└─────────────────────────────────────────────────────┘

نموذج البيانات

-- قوالب الإشعارات
CREATE TABLE notification_templates (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
code VARCHAR(100) UNIQUE NOT NULL,
name_ar VARCHAR(255) NOT NULL,
name_en VARCHAR(255),
channels notification_channel[] NOT NULL,
subject_ar TEXT,
subject_en TEXT,
body_ar TEXT NOT NULL,
body_en TEXT,
variables JSONB DEFAULT '[]',
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);

-- القنوات المدعومة
CREATE TYPE notification_channel AS ENUM (
'push',
'sms',
'whatsapp',
'email'
);

-- سجل الإشعارات
CREATE TABLE notifications (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
template_id UUID REFERENCES notification_templates(id),
recipient_type VARCHAR(50) NOT NULL, -- 'passenger', 'company_user', 'admin'
recipient_id UUID NOT NULL,
channel notification_channel NOT NULL,
status notification_status NOT NULL DEFAULT 'pending',
content JSONB NOT NULL,
scheduled_at TIMESTAMPTZ,
sent_at TIMESTAMPTZ,
delivered_at TIMESTAMPTZ,
read_at TIMESTAMPTZ,
failed_at TIMESTAMPTZ,
error_message TEXT,
metadata JSONB,
created_at TIMESTAMPTZ DEFAULT NOW()
);

-- حالات الإشعار
CREATE TYPE notification_status AS ENUM (
'pending', -- بانتظار الإرسال
'scheduled', -- مجدول
'sending', -- قيد الإرسال
'sent', -- تم الإرسال
'delivered', -- تم التسليم
'read', -- تم القراءة
'failed' -- فشل
);

-- تفضيلات المستخدم
CREATE TABLE notification_preferences (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_type VARCHAR(50) NOT NULL,
user_id UUID NOT NULL,
channel notification_channel NOT NULL,
enabled BOOLEAN DEFAULT true,
quiet_hours_start TIME,
quiet_hours_end TIME,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(user_type, user_id, channel)
);

-- اشتراكات Push
CREATE TABLE push_subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_type VARCHAR(50) NOT NULL,
user_id UUID NOT NULL,
platform VARCHAR(20) NOT NULL, -- 'web', 'ios', 'android'
token TEXT NOT NULL,
device_info JSONB,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);

أنواع الإشعارات

الكودالوصفالقنوات
booking_confirmedتأكيد الحجزPush, SMS, WhatsApp
booking_cancelledإلغاء الحجزPush, SMS, WhatsApp
trip_reminder_24hتذكير قبل 24 ساعةPush
trip_reminder_2hتذكير قبل ساعتينPush, SMS
trip_delayedتأخير الرحلةPush, SMS, WhatsApp
trip_cancelledإلغاء الرحلةPush, SMS, WhatsApp
payment_receivedاستلام الدفعةPush
promo_offerعرض ترويجيPush, Email

API

// خدمة الإشعارات
interface NotificationService {
// إرسال إشعار
send(notification: SendNotificationRequest): Promise<Notification>;

// إرسال مجمع
sendBulk(notifications: SendNotificationRequest[]): Promise<Notification[]>;

// جدولة إشعار
schedule(notification: ScheduleNotificationRequest): Promise<Notification>;

// إلغاء إشعار مجدول
cancel(notificationId: string): Promise<void>;
}

interface SendNotificationRequest {
templateCode: string;
recipientType: "passenger" | "company_user" | "admin";
recipientId: string;
channels?: NotificationChannel[];
variables?: Record<string, any>;
metadata?: Record<string, any>;
}

محرك القوالب

// مثال قالب
const template = {
code: "booking_confirmed",
body_ar: `
تم تأكيد حجزك!

رقم الحجز: {{booking_code}}
من: {{origin_city}}
إلى: {{destination_city}}
التاريخ: {{departure_date}}
الوقت: {{departure_time}}

نتمنى لك رحلة سعيدة! 🚌
`,
variables: [
"booking_code",
"origin_city",
"destination_city",
"departure_date",
"departure_time",
],
};

// الاستخدام
await notificationService.send({
templateCode: "booking_confirmed",
recipientType: "passenger",
recipientId: passengerId,
variables: {
booking_code: "SB-123456",
origin_city: "دمشق",
destination_city: "حلب",
departure_date: "15/01/2025",
departure_time: "08:00",
},
});

الجدولة

// جدولة تذكير
await notificationService.schedule({
templateCode: 'trip_reminder_24h',
recipientType: 'passenger',
recipientId: passengerId,
scheduledAt: subHours(tripDeparture, 24),
variables: { ... }
});

البدائل المدروسة

1. خدمات جاهزة (OneSignal, Firebase)

الإيجابيات:

  • جاهز للاستخدام
  • Dashboard مدمج
  • تحليلات متقدمة

السلبيات:

  • تكلفة مرتفعة
  • اعتماد على طرف ثالث
  • قد لا تعمل بشكل جيد في سوريا

2. النظام الحالي المحسّن

الإيجابيات:

  • لا تغييرات كبيرة
  • أسرع للتنفيذ

السلبيات:

  • لا قابلية للتوسع
  • لا تتبع
  • صعوبة الصيانة

3. نظام مخصص (القرار المتخذ)

الإيجابيات:

  • تحكم كامل
  • قابل للتوسع
  • مخصص لاحتياجاتنا

السلبيات:

  • وقت تطوير أطول
  • صيانة مستمرة

التأثير

قاعدة البيانات

  • جداول جديدة: notification_templates, notifications, notification_preferences, push_subscriptions

API

  • خدمة إشعارات مستقلة
  • Endpoints للتفضيلات

التطبيقات

  • Web: Service Worker للـ Push
  • Mobile: Firebase Cloud Messaging
  • تفضيلات: شاشة إعدادات الإشعارات

الأمان والخصوصية

احترام التفضيلات

  • لا إرسال بدون موافقة
  • ساعات الهدوء
  • إلغاء الاشتراك السهل

Rate Limiting

const rateLimits = {
sms: { max: 5, window: "1h" },
whatsapp: { max: 10, window: "1h" },
push: { max: 50, window: "1h" },
email: { max: 20, window: "1h" },
};

خطة التنفيذ

المرحلة 1: البنية الأساسية (2 أسابيع)

  • تصميم قاعدة البيانات
  • خدمة الإشعارات الأساسية
  • محرك القوالب

المرحلة 2: قنوات الإرسال (2 أسابيع)

  • Push (Firebase)
  • SMS (Twilio)
  • WhatsApp (Twilio)

المرحلة 3: الجدولة والتفضيلات (1 أسبوع)

  • نظام الجدولة
  • شاشة التفضيلات
  • ساعات الهدوء

المرحلة 4: التحليلات (1 أسبوع)

  • تتبع التسليم
  • Dashboard
  • تقارير

المقاييس

المقياسالهدف
نسبة التسليم> 95%
وقت الإرسال< 5 ثوانٍ
نسبة القراءة (Push)> 30%
نسبة إلغاء الاشتراك< 5%

الأسئلة المفتوحة

  1. هل نحتاج إشعارات داخل التطبيق (in-app)?
  2. ما الحد الأقصى للإشعارات اليومية للمستخدم؟
  3. كيف نتعامل مع الفشل المتكرر؟

المراجع