Update: 2026-06-22 00:31:28

This commit is contained in:
Hamza-Ayed
2026-06-22 00:31:29 +03:00
parent e73be65a72
commit efe26c95be
19 changed files with 2635 additions and 32 deletions

View File

@@ -0,0 +1,122 @@
<?php
/**
* auto_adapt.php
* Auto-Adaptive Pricing — تعديل أسعار kazan الأساسية تلقائياً
*
* المعادلة: سعر_الكيلو_الجديد = أقل_متوسط_سعر_منافس × 0.92
* الحماية: ما ينزل عن 85% من السعر الحالي ولا يزيد عن 115%
*
* التشغيل: cron job كل 30-60 دقيقة
* CLI: php backend/ride/pricing/auto_adapt.php
* HTTP: curl -X POST https://.../backend/ride/pricing/auto_adapt.php -H "X-Internal-Key: ..."
*/
require_once __DIR__ . '/../../core/bootstrap.php';
try {
$con = Database::get('main');
} catch (Exception $e) {
error_log("[AutoAdapt] DB connection failed: " . $e->getMessage());
exit(1);
}
$countries = [
'Syria' => 'SY',
'Jordan' => 'JO',
'Egypt' => 'EG',
'Iraq' => 'IQ'
];
$undercutFactor = 0.92;
$minFloorRatio = 0.85;
$maxCeilRatio = 1.15;
$kmColumns = [
'speedPrice', 'comfortPrice', 'ladyPrice', 'electricPrice',
'vanPrice', 'deliveryPrice', 'mishwarVipPrice', 'fixedPrice', 'awfarPrice'
];
$logEntries = [];
foreach ($countries as $country => $cc) {
// 1. متوسط سعر الكيلو لكل منافس (آخر 24 ساعة)
$sql = "SELECT competitor_name, AVG(price_per_km) AS avg_ppm
FROM competitor_prices
WHERE country_code = :cc
AND created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
AND price_per_km > 0
GROUP BY competitor_name
ORDER BY avg_ppm ASC";
$stmt = $con->prepare($sql);
$stmt->execute([':cc' => $cc]);
$competitorAvgs = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (empty($competitorAvgs)) {
error_log("[AutoAdapt] $country — لا توجد بيانات منافسين");
continue;
}
$lowestAvg = (float)$competitorAvgs[0]['avg_ppm'];
// 2. الأسعار الحالية من kazan
$kazanSql = "SELECT * FROM kazan WHERE country = :country LIMIT 1";
$stmtK = $con->prepare($kazanSql);
$stmtK->execute([':country' => $country]);
$kazanRow = $stmtK->fetch(PDO::FETCH_ASSOC);
if (!$kazanRow) {
error_log("[AutoAdapt] $country — لا يوجد صف في kazan");
continue;
}
$currentSpeed = (float)$kazanRow['speedPrice'];
$targetSpeed = $lowestAvg * $undercutFactor;
$minAllowed = $currentSpeed * $minFloorRatio;
$maxAllowed = $currentSpeed * $maxCeilRatio;
if ($targetSpeed < $minAllowed) {
$targetSpeed = $minAllowed;
} elseif ($targetSpeed > $maxAllowed) {
$targetSpeed = $maxAllowed;
}
$ratio = $targetSpeed / $currentSpeed;
$updates = [];
$params = [':country' => $country];
foreach ($kmColumns as $col) {
$oldVal = (float)($kazanRow[$col] ?? 0);
if ($oldVal > 0) {
$newVal = round($oldVal * $ratio, 2);
$updates[] = "`$col` = :$col";
$params[":$col"] = (string)$newVal;
}
}
if (empty($updates)) {
continue;
}
$updateSql = "UPDATE kazan SET " . implode(', ', $updates) . " WHERE country = :country";
$stmtU = $con->prepare($updateSql);
$stmtU->execute($params);
$logEntries[] = [
'country' => $country,
'code' => $cc,
'old_speed' => $currentSpeed,
'new_speed' => $targetSpeed,
'lowest_competitor' => $lowestAvg,
'ratio' => round($ratio, 4),
];
error_log("[AutoAdapt] ✅ $country ($cc): speedPrice $currentSpeed$targetSpeed (ratio: $ratio)");
}
if (php_sapi_name() === 'cli') {
echo json_encode(['status' => 'success', 'updates' => $logEntries], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
} else {
echo json_encode(['status' => 'success', 'updates' => $logEntries]);
}

View File

@@ -124,6 +124,16 @@ function calculateDynamicPrice($country, $minFare, $distance, $duration, $kazanR
$demandCount = (int)$redis->get("demand:grid:" . $grid_id);
$availableDrivers = 0;
// Check competitor surge opportunities
$competitorSurgeMultiplier = 1.0;
$surgeOpsJson = $redis->get("surge:opportunities");
if ($surgeOpsJson) {
$surgeOps = json_decode($surgeOpsJson, true);
if (is_array($surgeOps) && isset($surgeOps[$grid_id])) {
$competitorSurgeMultiplier = (float)$surgeOps[$grid_id];
}
}
// Driver locations are handled by Location Redis (no prefix)
try {
if (isset($redisLocation) && $redisLocation !== null) {
@@ -139,6 +149,11 @@ function calculateDynamicPrice($country, $minFare, $distance, $duration, $kazanR
$surgeMultiplier = min(3.0, $surgeMultiplier); // Cap at 3.0
}
}
// Auto-Adaptive Pricing: Apply competitor surge if it's higher
if ($competitorSurgeMultiplier > $surgeMultiplier) {
$surgeMultiplier = $competitorSurgeMultiplier;
}
} catch (Exception $e) {}
}