getMessage() . "\n"); } echo "Starting Siro AI Engine v2 (Powered by Pricing Engine)...\n"; // ========================================== // 0. جدول افتراضات عمولة المنافسين (لميزة "أرباحك أعلى" للسائق) // ========================================== $con->exec(" CREATE TABLE IF NOT EXISTS `competitor_commission_assumptions` ( `country_code` VARCHAR(5) NOT NULL, `competitor_name` VARCHAR(64) NOT NULL, `commission_pct` DECIMAL(5,2) NOT NULL, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`country_code`, `competitor_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; "); // زرع أولي بأرقام حقيقية زوّدنا فيها — INSERT IGNORE عشان ما نبهدل أي تعديل يدوي لاحق من الأدمن $commissionSeed = [ // الأردن — كل التطبيقات المذكورة أعطتنا نفس النسبة 23% ['JO', 'com.careem.ae', 23.0], ['JO', 'com.ubercab', 23.0], ['JO', 'com.jeeny.app', 23.0], ['JO', 'com.taxif.passenger', 23.0], ['JO', 'gogo', 23.0], ['JO', 'petra_ride', 23.0], // سوريا ['SY', 'yallago', 20.0], ['SY', 'zaken', 17.0], ['SY', 'tufaddal', 15.0], // مصر — استخدمنا الطرف الأدنى من كل مجال أعطانا إياه المستخدم (تحفظاً) ['EG', 'uber', 25.0], ['EG', 'careem', 20.0], ['EG', 'didi', 15.0], ['EG', 'indrive', 14.0], ]; $seedStmt = $con->prepare(" INSERT IGNORE INTO competitor_commission_assumptions (country_code, competitor_name, commission_pct) VALUES (:cc, :name, :pct) "); foreach ($commissionSeed as [$cc, $name, $pct]) { $seedStmt->execute([':cc' => $cc, ':name' => $name, ':pct' => $pct]); } // ========================================== // 1. التسعير الديناميكي بناءً على المعادلات الإحصائية // ========================================== echo "1. Reading competitor formulas from Pricing Engine...\n"; try { // قراءة آخر المعادلات من التحليل الإحصائي $sql = "SELECT * FROM competitor_secret_formulas WHERE last_updated >= DATE_SUB(NOW(), INTERVAL 24 HOUR) ORDER BY last_updated DESC"; $stmt = $con->query($sql); $formulas = $stmt->fetchAll(PDO::FETCH_ASSOC); if (empty($formulas)) { echo " ⚠️ No recent formulas found. Run pricing-engine first.\n"; } else { // تجميع المعادلات حسب الدولة $byCountry = []; foreach ($formulas as $f) { $country = $f['country_code']; if (!isset($byCountry[$country])) $byCountry[$country] = []; $byCountry[$country][] = $f; } foreach ($byCountry as $country => $countryFormulas) { $countryNameMap = ['JO' => 'Jordan', 'SY' => 'Syria', 'EG' => 'Egypt', 'IQ' => 'Iraq']; $countryName = $countryNameMap[$country] ?? $country; // اختيار أفضل معادلة للتسعير حسب الأولوية: // 1. Economy tier (الأكثر تنافسية) // 2. Standard tier (إذا ما في Economy) // 3. أي tier ثاني بأعلى R² $tierPriority = ['economy', 'standard', 'premium']; $bestFormula = null; $bestTierIdx = 999; foreach ($countryFormulas as $f) { $tier = $f['tier'] ?? 'standard'; $tierIdx = array_search($tier, $tierPriority); if ($tierIdx === false) $tierIdx = 999; $currentRSq = (float)($f['r_squared'] ?? 0); $currentKm = (float)($f['price_per_km'] ?? 0); // أفضلية: Tier أعلى أولوية، ثم R² أعلى if ($currentKm > 0 && ( $bestFormula === null || $tierIdx < $bestTierIdx || ($tierIdx === $bestTierIdx && $currentRSq > (float)($bestFormula['r_squared'] ?? 0)) )) { $bestTierIdx = $tierIdx; $bestFormula = $f; } } $bestTier = null; if ($bestFormula) { $bestKmRate = (float)$bestFormula['price_per_km']; $bestMinRate = (float)$bestFormula['price_per_min']; $bestBaseFare = (float)$bestFormula['base_fare']; $bestMinFare = (float)$bestFormula['min_fare']; $bestRSq = (float)($bestFormula['r_squared'] ?? 0); $bestTier = $bestFormula['tier'] ?? 'standard'; } if ($bestKmRate === null) { echo " ⚠️ No valid formula for $countryName. Skipping.\n"; continue; } // تسعير Siro: أرخص بنسبة 6.5% من المنافس مع الحفاظ على هيكل التسعير $discountFactor = 1 - 0.065; $ourKmRate = round($bestKmRate * $discountFactor, 3); $ourMinRate = round($bestMinRate * $discountFactor, 3); $ourBase = round($bestBaseFare * $discountFactor, 3); echo " 📊 $countryName: Using formula [tier=$bestTier] (R²=$bestRSq)\n"; echo " Competitor: KM=$bestKmRate, MIN=$bestMinRate, Base=$bestBaseFare, MinFare=$bestMinFare\n"; echo " Siro (6.5% less): KM=$ourKmRate, MIN=$ourMinRate, Base=$ourBase\n"; // تحديث جدول kazan $speedPrice = $ourKmRate; $comfortPrice = round($ourKmRate * 1.30, 3); $ladyPrice = round($ourKmRate * 1.10, 3); $electricPrice = round($ourKmRate * 1.20, 3); $vanPrice = round($ourKmRate * 1.50, 3); $deliveryPrice = round($ourKmRate * 0.90, 3); $mishwarVipPrice = round($ourKmRate * 1.40, 3); $fixedPrice = $speedPrice; $awfarPrice = round($ourKmRate * 0.85, 3); // أسعار الدقائق if ($country === 'JO') { $normalMin = $ourMinRate > 0 ? $ourMinRate : 0.05; $peakMin = round($normalMin * 1.15, 3); $lateMin = $normalMin; } else { $normalMin = $ourMinRate > 0 ? $ourMinRate : round($speedPrice / 4, 3); $peakMin = round($normalMin * 1.15, 3); $lateMin = round($normalMin * 1.25, 3); } $updateSql = "UPDATE kazan SET speedPrice = :speedPrice, comfortPrice = :comfortPrice, ladyPrice = :ladyPrice, electricPrice = :electricPrice, vanPrice = :vanPrice, deliveryPrice = :deliveryPrice, mishwarVipPrice = :mishwarVipPrice, fixedPrice = :fixedPrice, awfarPrice = :awfarPrice, normalMinPrice = :normalMin, peakMinPrice = :peakMin, lateMinPrice = :lateMin, startPrice = :startPrice WHERE country = :countryName"; $upStmt = $con->prepare($updateSql); $upStmt->execute([ ':speedPrice' => $speedPrice, ':comfortPrice' => $comfortPrice, ':ladyPrice' => $ladyPrice, ':electricPrice' => $electricPrice, ':vanPrice' => $vanPrice, ':deliveryPrice' => $deliveryPrice, ':mishwarVipPrice' => $mishwarVipPrice, ':fixedPrice' => $fixedPrice, ':awfarPrice' => $awfarPrice, ':normalMin' => $normalMin, ':peakMin' => $peakMin, ':lateMin' => $lateMin, ':startPrice' => $ourBase, ':countryName' => $countryName ]); echo " ✅ Updated $countryName pricing in kazan table.\n"; // ── تخزين معاملات المنافس الحالي بـ Redis لميزة "أرباحك أعلى" للسائق ── // get.php / add_ride.php يقرأوا هذا المفتاح فقط (بدون أي استعلام DB // وقت إنشاء الرحلة) عبر estimateDriverEarningsAdvantage() try { $commStmt = $con->prepare(" SELECT commission_pct FROM competitor_commission_assumptions WHERE country_code = :cc AND competitor_name = :name LIMIT 1 "); $commStmt->execute([':cc' => $country, ':name' => $bestFormula['competitor_name']]); $commissionPct = (float)($commStmt->fetchColumn() ?: 20.0); $driverComparisonData = [ 'competitor_name' => $bestFormula['competitor_name'], 'base_fare' => $bestBaseFare, 'km_rate' => $bestKmRate, 'min_rate' => $bestMinRate, 'commission_pct' => $commissionPct, 'currency' => getCurrencyByCountry($countryName), 'updated_at' => date('Y-m-d H:i:s'), ]; // TTL أطول من فترة الجدولة (3 ساعات) بهامش أمان $redis->setex("pricing:driver_comparison:{$country}", 14400, json_encode($driverComparisonData)); echo " 💰 Cached driver-comparison data for $countryName (commission assumed: {$commissionPct}%)\n"; } catch (Exception $e) { echo " ⚠️ Failed to cache driver-comparison data: " . $e->getMessage() . "\n"; } } } } catch (Exception $e) { echo " ❌ Error in Pricing Module: " . $e->getMessage() . "\n"; } // ========================================== // 2. قراءة Surge Insights من الـ Pricing Engine // ========================================== echo "2. Reading surge insights from Pricing Engine...\n"; try { $sql = "SELECT * FROM competitor_surge_insights WHERE detected_at >= DATE_SUB(NOW(), INTERVAL 12 HOUR) ORDER BY surge_multiplier DESC LIMIT 20"; $stmt = $con->query($sql); $surgeRecords = $stmt->fetchAll(PDO::FETCH_ASSOC); if (!empty($surgeRecords)) { $surgeByCountry = []; foreach ($surgeRecords as $sr) { $country = $sr['country_code']; if (!isset($surgeByCountry[$country])) { $surgeByCountry[$country] = [ 'avg_multiplier' => 0, 'count' => 0, 'peak_hours' => [] ]; } $surgeByCountry[$country]['avg_multiplier'] += $sr['surge_multiplier']; $surgeByCountry[$country]['count']++; $surgeByCountry[$country]['peak_hours'][] = "{$sr['peak_start_hour']}:00-{$sr['peak_end_hour']}:00"; } foreach ($surgeByCountry as $country => $data) { $avgMult = round($data['avg_multiplier'] / $data['count'], 3); $peakHoursStr = implode(', ', array_unique($data['peak_hours'])); // تخزين ملخص إحصائي (object) على مفتاح خاص ومستقل — لا نكتب على // surge:opportunities / surge:opportunities:{country} لأنها خرائط // grid_id → multiplier يملأها ويقرأها نظام آخر بالكامل // (cron_surge_opportunity.php / get.php / get_surge_heatmap.php / // winback_hotspot_targets.php / cron_kazan_adjuster.php). كتابة // object مسطّح على نفس المفتاح كانت تبهدل تلك الخريطة كل 3 ساعات. $surgeData = [ 'avg_competitor_surge' => $avgMult, 'suggested_multiplier' => round(1.0 + ($avgMult - 1.0) * 0.6, 3), 'peak_hours' => $data['peak_hours'], 'updated_at' => date('Y-m-d H:i:s') ]; $redis->setex("pricing_engine:competitor_surge_summary:{$country}", 7200, json_encode($surgeData)); echo " ⚡ $country: Avg competitor surge = {$avgMult}x, peak hours: {$peakHoursStr}\n"; } } else { echo " ℹ️ No recent surge insights found.\n"; } } catch (Exception $e) { echo " ❌ Error in Surge Module: " . $e->getMessage() . "\n"; } // ========================================== // 3. وحدة استهداف الركاب الخاملين (Smart Retention) - كما هي // ========================================== echo "3. Running Smart Retention Module...\n"; try { $sql = "SELECT source, COUNT(*) as opens FROM passenger_opening_locations WHERE created_at >= DATE_SUB(NOW(), INTERVAL 3 HOUR) GROUP BY source HAVING opens >= 3"; $stmt = $con->query($sql); $idleRiders = $stmt->fetchAll(PDO::FETCH_ASSOC); $notifiedCount = count($idleRiders); echo " 📱 Identified $notifiedCount idle riders requiring push notifications.\n"; } catch (Exception $e) { echo " ❌ Error in Smart Retention: " . $e->getMessage() . "\n"; } // ========================================== // 4. Export AI Hotzones to Redis for Captains // ========================================== echo "4. Exporting AI Hotzones to Redis...\n"; try { $sql = "SELECT latitude, longitude, competitor_name AS top_competitor, avg_ppk AS avg_price FROM competitor_surge_zones WHERE detected_at >= DATE_SUB(NOW(), INTERVAL 6 HOUR) AND surge_multiplier > 1.1"; $stmt = $con->query($sql); $zones = $stmt->fetchAll(PDO::FETCH_ASSOC); if (!empty($zones)) { $redisData = [ 'status' => 'success', 'data' => $zones ]; $redis->setex('siro:cache:ai:hotzones', 3600, json_encode($redisData)); echo " 🗺️ Exported " . count($zones) . " hot zones to Redis (siro:cache:ai:hotzones).\n"; } else { // Clear if no surge to avoid stale data $redis->del('siro:cache:ai:hotzones'); echo " ℹ️ No active hot zones to export.\n"; } } catch (Exception $e) { echo " ❌ Error in Hotzones Export: " . $e->getMessage() . "\n"; } echo "AI Engine v2 finished successfully.\n";