Files
tripz-llc/backend/bot/cron_generate_pricing_cache.php
T
Hamza-AyedandClaude Opus 5 4d8414c96b feat: استيراد كود سيرو إلى تريبز (سيرو @ecfe7568) — بلا تعديل
قرار المالك 2026-07-27: باك إند سيرو PHP هو المعتمد، وتطبيقاته المجرّبة
ميدانياً تحل محل إعادة البناء المؤرشفة. سيرو نفسه لم يُمسّ.

الخريطة:
  backend · payment_server · loction_server · ride_server ·
  passenger_server · docker · dashboard · stress_test  → الجذر
  siro_rider  → apps/rider          siro_driver  → apps/driver
  siro_admin  → dashboards/admin    siro_service → dashboards/service
  android_bot → apps/android_bot    socialBot    → apps/socialBot

نُسخ المتعقَّب في git سيرو فقط عبر `git archive` (3,198 ملفاً / ~169 م.ب)
لا `cp -r` — فاستُثنيت مخلفات البناء تلقائياً. بلا أي تعديل محتوى عمداً:
كل ما يلي يصير فرقاً مقروءاً مقابل المصدر.

لم يُستورد وسببه: siromove.com (الموقع التسويقي يبقى marketing/ في تريبز،
سيرو فيه 8 ملفات) · docs و planning (تريبز له docs/ الخاص) · deploy.sh
(ليس نشراً على سيرفر بل `git add . && git push origin --all` — فخّ في
مستودع آخر) · transit_dashboard (بانتظار قرار مصير backend-transit و
dashboards/transit-web).

⚠️ لا يبني بعد — ثلاثة نواقص متوقعة ومقصودة:
1. `.env` و `lib/env/env.g.dart` غير متعقَّبين في سيرو (أسرار لكل مستأجر):
   كل تطبيق فلاتر يحتاج .env خاصاً ثم توليد env.g.dart بـ build_runner.
2. إعدادات Firebase (9 ملفات google-services.json و GoogleService-Info.plist)
   يستبعدها .gitignore تريبز — ولكل مستأجر مشروع Firebase خاص أصلاً.
3. apps/driver في سيرو يشير إلى `../../Intaleq/packages/get` خارج المستودع →
   يجب ضمّ الحزم داخله أسوة بـ apps/rider.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 05:14:13 +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();
}
?>