# 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 ```bash # 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 ```javascript // 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 عادي (أبسط) ```bash # 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؟ بدلاً من معادلة تسعير واحدة لكل البلد، يصبح: ```sql -- جدول 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`: ```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 عالي: ```php // 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: ```php // في ride/pricing/get.php if ($surgeMultiplier > 1.0) { $response['surge_warning'] = "⚠️ هذه المنطقة تشهد طلباً مرتفعاً، الأسعار أعلى بنسبة " . round(($surgeMultiplier - 1) * 100) . "%"; } ``` --- ## 5. دعم تطبيقات منافسة متعددة ### 5.1 لكل منافس معادلاته الخاصة ```sql -- competitor_secret_formulas يدعم: -- competitor_name = 'com.taxif.passenger' | 'com.careem.ae' | 'com.ubercab' | 'com.jeeny.app' -- لكل منافس 3 tiers (Economy/Standard/Premium) -- لكل tier معادلة مستقلة ``` ### 5.2 مقارنة الأسعار في التطبيق ```php // 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`: ```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) ```bash □ 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 |