Files
intaleq/docs/03_pricing/PRICING_ENGINE_ARCHITECTURE.md
Hamza-AyedandClaude Opus 5 92dc6b3641 chore: استيراد أولي من سيرو (ecfe7568) — بلا أي تعديل
نسخة كاملة من مستودع سيرو عند ecfe7568 لتكون أساس تطبيق «انطلق».
نُسخ المتعقَّب في git فقط (12,509 ملفاً / 302 م.ب) بـ git archive، لا
`cp -r` — فاستُثنيت تلقائياً مخلفات البناء (build · node_modules ·
.dart_tool · .gradle · Pods ≈ 10.7 غ.ب) وكل ما يستثنيه .gitignore.

هذا الكوميت **بلا أي تعديل عمداً** حتى يكون كل ما يليه فرقاً مقروءاً
مقابل سيرو الأصلي. سيرو نفسه لم يُمسّ.

⚠️ لا يبني بعد: `.env` و`lib/env/env.g.dart` غير متعقَّبين في سيرو (وهذا
صحيح — أسرار لكل مستأجر). كل تطبيق فلاتر هنا يحتاج .env خاصاً بانطلق ثم
توليد env.g.dart عبر build_runner. لا تُنسخ أسرار سيرو.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 05:10:29 +03:00

19 KiB
Raw Permalink Blame History

Siro Pricing Engine — Architecture & Deployment Guide

1. نظرة عامة على المنظومة

┌─────────────────────────────────────────────────────────────────────┐
│                    Siro PRICING ECOSYSTEM                           │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌──────────────────────┐      ┌──────────────────────────────┐    │
│  │  Android Bot Scraper │      │  Node.js Pricing Engine      │    │
│  │  (Java/Kotlin)       │─────▶│  (TypeScript)                │    │
│  │  يرسخن أسعار         │      │  تحليل إحصائي متقدم          │    │
│  │  TaxiF, Careem, Uber │      │  ┌────────────────────────┐  │    │
│  │  Jeeny...            │      │  │ MAD Outlier Detection  │  │    │
│  └──────────┬───────────┘      │  │ K-Means Tier Clustering│  │    │
│             │                  │  │ Ridge Regression (MLR) │  │    │
│             ▼                  │  │ Min Fare Detection     │  │    │
│  ┌──────────────────────┐      │  │ Surge Analysis         │  │    │
│  │  MySQL:              │      │  │ Zone Pricing           │  │    │
│  │  scraped_competitor_ │◀─────│  └────────────────────────┘  │    │
│  │  prices              │      └──────────────┬───────────────┘    │
│  └──────────────────────┘                     │                    │
│             │                                 │                    │
│             ▼                                 ▼                    │
│  ┌──────────────────────┐      ┌──────────────────────────────┐    │
│  │  MySQL:              │      │  MySQL:                      │    │
│  │  competitor_secret_  │      │  competitor_surge_insights   │    │
│  │  formulas            │      │  (ساعات الذروة + المضاعف)    │    │
│  │  (معادلات المنافسين)  │      └──────────────┬───────────────┘    │
│  └──────────┬───────────┘                     │                    │
│             │                                 │                    │
│             ▼                                 ▼                    │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │              PHP Cron Jobs (Backend)                       │    │
│  │                                                            │    │
│  │  cron_ai_engine.php:   يقرأ المعادلات ويحدث kazan          │    │
│  │  cron_kazan_adjuster:  يقرأ surge ويضبط العمولة            │    │
│  │  cron_gemini_advisor:  يرسل المعادلات لـ Gemini لتقارير    │    │
│  └────────────────────────┬───────────────────────────────────┘    │
│                           │                                        │
│                           ▼                                        │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │  Redis Cache Layer                                        │    │
│  │                                                            │    │
│  │  surge:opportunities         → مضاعف Surge المقترح         │    │
│  │  surge:opportunities:{JO}    → لكل دولة                     │    │
│  │  siro:cache:pricing:grids    → أسعار حسب Grid 2.5km        │    │
│  └────────────────────────┬───────────────────────────────────┘    │
│                           │                                        │
│                           ▼                                        │
│  ┌────────────────────────────────────────────────────────────┐    │
│  │  Real-time APIs (Rider & Driver Apps)                     │    │
│  │                                                            │    │
│  │  ride/pricing/get.php:   حساب السعر الفوري للراكب           │    │
│  │  ride/heatmap/:          خريطة حرارية للسائق                │    │
│  │  api/ride/competitor:    مقارنة أسعار المنافسين             │    │
│  └────────────────────────────────────────────────────────────┘    │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2. Deployment: Node.js إلى جانب PHP

المشكلة

النظام الحالي PHP على Apache/Nginx. نحتاج Node.js للتشغيل جنباً إلى جنب.

الحل: PM2 Process Manager

# 1. تثبيت Node.js على السيرفر (مرة واحدة)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs

# 2. رفع مجلد pricing-engine إلى السيرفر
# (scp أو git pull)

# 3. تثبيت PM2 (مدير عمليات Node.js)
npm install -g pm2

# 4. تثبيت dependencies
cd /var/www/siro/backend/pricing-engine
npm install
cp .env.example .env
# عدّل .env ببيانات MySQL + Redis

# 5. تشغيل الخدمات مع PM2
pm2 start ecosystem.config.js
pm2 save
pm2 startup  # عشان يشتغل تلقائياً بعد reboot

ملف PM2 Ecosystem

// backend/pricing-engine/ecosystem.config.js
module.exports = {
  apps: [
    {
      name: 'siro-pricing-hourly',
      script: 'dist/index.js',
      args: '--mode=surge --hours=3',
      cron_restart: '0 * * * *',        // كل ساعة
      autorestart: false,
      time: true,
    },
    {
      name: 'siro-pricing-daily',
      script: 'dist/index.js',
      args: '--mode=full --hours=72',
      cron_restart: '0 6 * * *',        // كل يوم 6 صباحاً
      autorestart: false,
      time: true,
    },
    {
      name: 'siro-pricing-weekly',
      script: 'dist/index.js',
      args: '--mode=report --hours=168',
      cron_restart: '0 8 * * 1',        // كل أسبوع الإثنين 8 صباحاً
      autorestart: false,
      time: true,
    },
  ],
};

PM2 يتولى تشغيل cron jobs بدون الحاجة إلى crontab نظامي. لكنه autorestart: false لأنها عمليات لمرة واحدة، مو servers.

بديل: Crontab عادي (أبسط)

# crontab -e
0 * * * * cd /var/www/siro/backend/pricing-engine && node dist/index.js --mode=surge --hours=3 >> /var/log/siro-pricing.log 2>&1
0 6 * * * cd /var/www/siro/backend/pricing-engine && node dist/index.js --mode=full --hours=72 >> /var/log/siro-pricing.log 2>&1
0 8 * * 1 cd /var/www/siro/backend/pricing-engine && node dist/index.js --mode=report --hours=168 >> /var/log/siro-pricing.log 2>&1

3. دفق التسعير الكامل (Full Pricing Flow)

3.1 تحليل المنافسين ← حفظ المعادلات

Pricing Engine (Node.js)
  │
  ├─ 1. يسحب بيانات من scraped_competitor_prices
  ├─ 2. ينظف الشواذ (MAD)
  ├─ 3. يصنّف الفئات (K-Means) → Economy / Standard / Premium
  ├─ 4. يحسب الانحدار لكل فئة:
  │      price = baseFare + kmRate×dist + minRate×duration
  ├─ 5. يكتشف Minimum Fare
  ├─ 6. يحلل Surge حسب الساعة
  └─ 7. يحفظ في:
       ├─ competitor_secret_formulas (معادلات لكل tier)
       └─ competitor_surge_insights (ساعات الذروة)

3.2 PHP يقرأ ويطبّق التسعير

cron_ai_engine.php (PHP, كل 30-60 دقيقة)
  │
  ├─ 1. يقرأ competitor_secret_formulas
  ├─ 2. يختار Economy tier (الأرخص)
  ├─ 3. يطبّق خصم 6.5%:
  │      Siro_kmRate = competitor_kmRate × 0.935
  ├─ 4. يحدّث جدول kazan
  └─ 5. يحفظ surge في Redis

3.3 حساب السعر للتطبيقات

ride/pricing/get.php (API, يتم استدعاؤه عند طلب رحلة)
  │
  ├─ 1. يقرأ kazan table (آخر تحديث من cron_ai_engine)
  ├─ 2. يحسب:
  │      basePrice = kazan.baseFare
  │      + kazan.speedPrice × distance
  │      + kazan.normalMinPrice × duration
  ├─ 3. يقرأ Redis surge:opportunities
  ├─ 4. يطبّق surge multiplier إذا كانت ساعة ذروة
  └─ 5. يرجع السعر النهائي للتطبيق

4. تقسيم المناطق (Zone-Based Pricing)

4.1 تصنيف المناطق

Amman مقسمة حسب البعد عن المركز (31.95, 35.90):

  Centre   (مركز البلد)     → نصف قطر < 2.5km  → PPK 0.25-0.33
  Mid      (وسط)            → نصف قطر < 5km    → PPK 0.35-0.45
  Suburb   (ضواحي)          → نصف قطر < 10km   → PPK 0.40-0.50
  Outskirts (أطراف)         → > 10km           → PPK 0.40-0.60

4.2 كيف نطبّق Zone-Based Pricing؟

بدلاً من معادلة تسعير واحدة لكل البلد، يصبح:

-- جدول zone_pricing (جديد)
CREATE TABLE IF NOT EXISTS `zone_pricing` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `country_code` VARCHAR(5) NOT NULL,
  `zone_type` VARCHAR(20) NOT NULL,  -- centre, mid, suburb, outskirts
  `km_rate` DECIMAL(8,3) NOT NULL,
  `min_rate` DECIMAL(8,3) NOT NULL DEFAULT 0,
  `base_fare` DECIMAL(8,3) NOT NULL DEFAULT 0,
  `min_fare` DECIMAL(8,3) NOT NULL DEFAULT 0,
  `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  UNIQUE KEY `idx_country_zone` (`country_code`, `zone_type`)
);

-- يتم ملؤها من تحليل Pricing Engine

ثم في ride/pricing/get.php:

// 1. تحديد منطقة البداية
$zoneType = classifyZone($startLat, $startLng);  // centre | mid | suburb | outskirts

// 2. استخدام تسعير المنطقة
$zoneRate = getZonePricing($country, $zoneType);
$price = $zoneRate['base_fare'] 
       + $zoneRate['km_rate'] * $distance 
       + $zoneRate['min_rate'] * $duration;

// 3. تطبيق Surge حسب المنطقة
$surge = getSurgeForZone($country, $zoneType);
$finalPrice = $price * $surge;

4.3 إشعارات المناطق للسائقين

عند دخول سائق إلى منطقة ذات Surge عالي:

// cron_notify_drivers_zones.php (جديد - كل 5 دقائق)
$hotZones = getHotZones(); // من Redis surge:opportunities أو تحليل الـ Pricing Engine

foreach ($hotZones as $zone) {
    // إرسال FCM notification للسائقين القريبين
    sendPushToNearbyDrivers($zone['lat'], $zone['lng'], [
        'title' => '⚠️ منطقة طلب مرتفع',
        'body' => "منطقة {$zone['name']}: الطلب مرتفع، الأسعار مرتفعة {$zone['surge']}x"
    ]);
}

وللراكب عند فتح التطبيق في منطقة Surge:

// في ride/pricing/get.php
if ($surgeMultiplier > 1.0) {
    $response['surge_warning'] = "⚠️ هذه المنطقة تشهد طلباً مرتفعاً، الأسعار أعلى بنسبة "
        . round(($surgeMultiplier - 1) * 100) . "%";
}

5. دعم تطبيقات منافسة متعددة

5.1 لكل منافس معادلاته الخاصة

-- competitor_secret_formulas يدعم:
-- competitor_name = 'com.taxif.passenger' | 'com.careem.ae' | 'com.ubercab' | 'com.jeeny.app'
-- لكل منافس 3 tiers (Economy/Standard/Premium)
-- لكل tier معادلة مستقلة

5.2 مقارنة الأسعار في التطبيق

// api/ride/get_competitor_context.php
$competitors = ['com.taxif.passenger', 'com.careem.ae', 'com.ubercab', 'com.jeeny.app'];
$prices = [];

foreach ($competitors as $comp) {
    $formula = getLatestFormula($comp, $country, 'economy');
    $estimatedPrice = $formula['base_fare'] 
                    + $formula['price_per_km'] * $requestedDistance
                    + $formula['price_per_min'] * $requestedDuration;
    $prices[$comp] = [
        'name' => getCompetitorDisplayName($comp),
        'price' => $estimatedPrice,
        'currency' => 'JOD',
    ];
}

// Siro price (already 6.5% less)
$siroPrice = calculateSiroPrice($request);
$prices['siro'] = [
    'name' => 'Siro',
    'price' => $siroPrice,
    'currency' => 'JOD',
    'is_cheapest' => $siroPrice < min(array_column($prices, 'price')),
];

5.3 تسعير Siro بناءً على المنافس الأقوى

في cron_ai_engine.php:

// 1. اجلب معادلات جميع المنافسين للدولة
$competitors = getCompetitorFormulas($country, 'economy');

// 2. احسب السعر المتوقع لكل منافس لرحلة نموذجية (10km, 15min)
$sampleDist = 10;
$sampleDur = 15;
$competitorPrices = [];
foreach ($competitors as $comp) {
    $competitorPrices[$comp['competitor_name']] = 
        $comp['base_fare'] + $comp['price_per_km'] * $sampleDist + $comp['price_per_min'] * $sampleDur;
}

// 3. المنافس الأرخص هو المستهدف
$cheapestCompetitor = array_keys($competitorPrices, min($competitorPrices))[0];
$cheapestPrice = min($competitorPrices);

// 4. سعر Siro = أرخص منافس - 6.5%
$targetSiroPrice = $cheapestPrice * 0.935;

// 5. هندسة عكسية لمعاملات Siro
$ourKmRate = $competitors[$cheapestCompetitor]['price_per_km'] * 0.935;
$ourMinRate = $competitors[$cheapestCompetitor]['price_per_min'] * 0.935;
$ourBaseFare = $competitors[$cheapestCompetitor]['base_fare'] * 0.935;

6. تدفق البيانات من التحليل حتى يشوفها المستخدم

الوقت T0:  Pricing Engine يشتغل
           ↓
الوقت T0+5s:  يكتب competitor_secret_formulas + competitor_surge_insights
              ↓
الوقت T0+30m: cron_ai_engine.php (PHP) يقرأ المعادلات ويحدّث kazan
              ↓
الوقت T0+31m: kazan محدّث بأسعار جديدة (أقل 6.5% من المنافس)
              ↓
الوقت T0+31m+: rider يطلب رحلة
              → ride/pricing/get.php يقرأ kazan + Redis surge
              → يحسب السعر ← يرجع للراكب
              → السائق يشوف سعر الرحلة

المدة الكاملة من التحليل للمستخدم: ~31 دقيقة (يمكن تقليلها بتشغيل cron_ai_engine بعد Pricing Engine مباشرة).


7. متطلبات السيرفر

المكون المتطلب
Node.js v18+ (نوصي v20 LTS)
PM2 لإدارة العمليات (اختياري)
MySQL موجود مسبقاً
Redis موجود مسبقاً
RAM إضافي 256MB كافية (التطبيق خفيف)
مساحة 50MB للملفات + node_modules

أمان: Node.js ما اله Port

Pricing Engine هو CLI cron job، مش Web Server. ما اله Port مفتوح. يتصل فقط بـ MySQL و Redis داخلياً. لا يحتاج تعديل Nginx/Apache.


8. خطة الرفع (Deployment Checklist)

□ 1. git pull أحدث كود على السيرفر
□ 2. cd backend/pricing-engine && npm install
□ 3. cp .env.example .env  # عدّل بيانات MySQL + Redis
□ 4. mysql -u root siro < migrations/001_add_columns.sql
□ 5. npm run build  # compile TypeScript
□ 6. npm run analyze:taxif  # اختبار يدوي
□ 7. pm2 start ecosystem.config.js  # أو crontab
□ 8. pm2 save && pm2 startup
□ 9. تحقق من cron_ai_engine.php يقرأ المعادلات الجديدة
□ 10. اختبر ride/pricing/get.php مع الراكب

9. إضافة تطبيق منافس جديد

□ 1. أضف اسم الحزمة إلى generate_price_tasks.php
     (مثلاً: com.newcompetitor.app)
□ 2. انتظر تجميع بيانات كافية (أسبوع scraping)
□ 3. شغّل: npm run analyze -- --competitor=com.newcompetitor.app
□ 4. Pricing Engine سيكتشف الـ Tiers تلقائياً
□ 5. cron_ai_engine.php سيقرأ المعادلات ويطبّق التسعير
□ 6. تلقائياً: مقارنة الأسعار في تطبيق الراكب

10. الخلاصة

الميزة الحالة
تحليل إحصائي (MAD + Ridge Regression + K-Means) ✅ تم
اكتشاف 3 Tiers تسعيرية ✅ تم
Minimum Fare ✅ تم
اكتشاف Surge حسب ساعة اليوم ✅ تم
تحليل Zone ✅ تم
خصم 6.5% من المنافس ✅ في cron_ai_engine.php
Redis surge للـ get.php ✅ surge:opportunities
PK/FK متوافقة ✅ تم تحديث schema
دعم دول متعددة (JO/SY/EG/IQ) ✅ Currency-aware
دعم منافسين متعددين ✅ Arrays + foreach
Zone-Based Pricing ⬜ يحتاج إنشاء جدول zone_pricing
إشعارات للسائقين بالمناطق الساخنة ⬜ يحتاج cron_notify_drivers
مقارنة أسعار المنافسين في التطبيق ⬜ يحتاج ربط ride/pricing مع competitor_formulas