Files
intaleq/backend/bot/cron_generate_pricing_cache.php
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

120 lines
4.2 KiB
PHP

<?php
/**
* cron_generate_pricing_cache.php
* يجمع أسعار المنافسين لكل مربع جغرافي (2.5km) ويحفظ النتيجة في Redis
* يتم تشغيله كـ Cron Job (CLI) لتخفيف الضغط تماماً عن الاستعلام المباشر للركاب.
*/
require_once __DIR__ . '/../core/bootstrap.php';
require_once __DIR__ . '/../functions.php';
try {
$con = Database::get('main');
$redis = getRedisConnection();
} catch (Exception $e) {
die("Connection failed: " . $e->getMessage() . "\n");
}
echo "Starting Pricing Cache Generation (Redis)...\n";
try {
// 1. جلب متوسط الأسعار من المنافسين في آخر ساعة للشبكة
$sql = "SELECT country_code,
ROUND(latitude / 0.025) * 0.025 AS grid_lat,
ROUND(longitude / 0.025) * 0.025 AS grid_lng,
competitor_name,
AVG(price) as avg_price,
COUNT(*) as requests_count
FROM competitor_prices
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 1 HOUR)
GROUP BY country_code, grid_lat, grid_lng, competitor_name";
$stmt = $con->query($sql);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
// بناء مصفوفة الشبكات
$grids = [];
foreach ($results as $row) {
$cc = strtoupper($row['country_code']);
$gLat = number_format((float)$row['grid_lat'], 3);
$gLng = number_format((float)$row['grid_lng'], 3);
$key = "{$cc}_{$gLat}_{$gLng}";
if (!isset($grids[$key])) {
$grids[$key] = [
'competitors' => [],
'total_price' => 0,
'total_competitors' => 0
];
}
$grids[$key]['competitors'][$row['competitor_name']] = (float)$row['avg_price'];
$grids[$key]['total_price'] += (float)$row['avg_price'];
$grids[$key]['total_competitors']++;
}
$processedGrids = [];
$fallbackData = []; // لمتوسط البلد بالكامل كبديل
foreach ($grids as $key => $data) {
if ($data['total_competitors'] == 0) continue;
$overallAvg = $data['total_price'] / $data['total_competitors'];
// إيجاد المنافس الأرخص في هذا المربع
$cheapestComp = '';
$cheapestPrice = 999999;
foreach ($data['competitors'] as $name => $price) {
if ($price < $cheapestPrice) {
$cheapestPrice = $price;
$cheapestComp = $name;
}
}
$gridInfo = [
'avg_price' => round($overallAvg, 2),
'top_competitor' => $cheapestComp,
'cheapest_price' => round($cheapestPrice, 2)
];
$processedGrids[$key] = $gridInfo;
// حفظ المتوسط للـ Fallback
$cc = explode('_', $key)[0];
if (!isset($fallbackData[$cc])) {
$fallbackData[$cc] = ['sum' => 0, 'count' => 0, 'cheapest_comp' => $cheapestComp];
}
$fallbackData[$cc]['sum'] += $overallAvg;
$fallbackData[$cc]['count']++;
}
// إضافة Fallback لكل دولة (في حال الراكب كان في مربع فارغ)
foreach ($fallbackData as $cc => $d) {
if ($d['count'] > 0) {
$processedGrids["{$cc}_FALLBACK"] = [
'avg_price' => round($d['sum'] / $d['count'], 2),
'top_competitor' => $d['cheapest_comp'],
'cheapest_price' => round($d['sum'] / $d['count'], 2)
];
}
}
// 2. الحفظ في Redis
// نحفظ المصفوفة بالكامل كـ JSON String داخل مفتاح رئيسي واحد للسرعة العالية في القراءة
// مفتاح: siro:cache:pricing:grids
$redisData = [
'last_updated' => date('Y-m-d H:i:s'),
'grids' => $processedGrids
];
$redis->set('siro:cache:pricing:grids', json_encode($redisData, JSON_UNESCAPED_UNICODE));
echo "Pricing Cache Generated Successfully in Redis. Grids: " . count($processedGrids) . "\n";
} catch (Exception $e) {
error_log("Error generating pricing cache (Redis): " . $e->getMessage());
echo "Error: " . $e->getMessage();
}
?>