قرار المالك 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>
123 lines
3.7 KiB
PHP
123 lines
3.7 KiB
PHP
<?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 scraped_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]);
|
|
}
|