نسخة كاملة من مستودع سيرو عند 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>
177 lines
7.1 KiB
PHP
177 lines
7.1 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../../connect.php';
|
|
|
|
$driver_id = filterRequest("driver_id");
|
|
|
|
if (!$driver_id) {
|
|
jsonError("Missing driver_id");
|
|
}
|
|
|
|
try {
|
|
global $redis;
|
|
$redisKey = "gamification_dashboard:{$driver_id}";
|
|
|
|
// 1. Try to read from Redis first
|
|
if (isset($redis)) {
|
|
try {
|
|
$cached = $redis->get($redisKey);
|
|
if ($cached) {
|
|
$data = json_decode($cached, true);
|
|
if ($data) {
|
|
jsonSuccess($data);
|
|
exit;
|
|
}
|
|
}
|
|
} catch (Exception $e) {
|
|
error_log("Redis read error in getGamificationDashboard: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
// 2. Fallback to Database & External API
|
|
// Get Country and Currency Info from Environment Variables (to avoid querying kazan table)
|
|
$country = getenv('APP_COUNTRY') ?: getenv('COUNTRY') ?: "Jordan";
|
|
$currency = getenv('APP_CURRENCY') ?: getenv('CURRENCY') ?: "JOD";
|
|
$kazan = ["country" => $country, "currency" => $currency];
|
|
|
|
// 2. Get Total Completed Trips
|
|
$stmtTrips = $con->prepare("SELECT COUNT(*) as count FROM `ride` WHERE driver_id = :driver_id AND status = 'Finished'");
|
|
$stmtTrips->execute([':driver_id' => $driver_id]);
|
|
$totalTrips = (int)($stmtTrips->fetchColumn() ?: 0);
|
|
|
|
// 3. Get Average Rating
|
|
$stmtRate = $con->prepare("SELECT COALESCE(ROUND(AVG(rating), 2), 5.0) as rating FROM ratingDriver WHERE driver_id = :driver_id");
|
|
$stmtRate->execute([':driver_id' => $driver_id]);
|
|
$avgRating = (float)($stmtRate->fetchColumn() ?: 5.0);
|
|
|
|
// 4. Get Referral Counts (Installed/Verified)
|
|
$stmtDInv = $con->prepare("SELECT COUNT(*) FROM invites WHERE driverId = :driver_id AND isInstall = 1");
|
|
$stmtDInv->execute([':driver_id' => $driver_id]);
|
|
$driverInvites = (int)($stmtDInv->fetchColumn() ?: 0);
|
|
|
|
$stmtPInv = $con->prepare("SELECT COUNT(*) FROM invitesToPassengers WHERE driverId = :driver_id AND isInstall = 1");
|
|
$stmtPInv->execute([':driver_id' => $driver_id]);
|
|
$passengerInvites = (int)($stmtPInv->fetchColumn() ?: 0);
|
|
|
|
$totalReferrals = $driverInvites + $passengerInvites;
|
|
|
|
// 5. Get Driver Behavior (Last 30 Days)
|
|
$stmtBehavior = $con->prepare("
|
|
SELECT
|
|
COALESCE(ROUND(AVG(behavior_score), 1), 100) as avg_score,
|
|
COALESCE(SUM(hard_brakes), 0) as total_hard_brakes,
|
|
COALESCE(MAX(max_speed), 0) as max_speed
|
|
FROM `driver_behavior`
|
|
WHERE driver_id = :driver_id
|
|
AND created_at >= DATE(NOW()) - INTERVAL 30 DAY
|
|
");
|
|
$stmtBehavior->execute([':driver_id' => $driver_id]);
|
|
$behavior = $stmtBehavior->fetch(PDO::FETCH_ASSOC) ?: ["avg_score" => 100.0, "total_hard_brakes" => 0, "max_speed" => 0.0];
|
|
|
|
// 6. Get Today's Completed Trips & Earnings (Local Ride Database)
|
|
$stmtTodayTrips = $con->prepare("SELECT COUNT(*) FROM `ride` WHERE driver_id = :driver_id AND status = 'Finished' AND DATE(created_at) = CURDATE()");
|
|
$stmtTodayTrips->execute([':driver_id' => $driver_id]);
|
|
$todayTrips = (int)($stmtTodayTrips->fetchColumn() ?: 0);
|
|
|
|
$stmtTodayEarnings = $con->prepare("SELECT COALESCE(SUM(price_for_driver), 0) FROM `ride` WHERE driver_id = :driver_id AND status = 'Finished' AND DATE(created_at) = CURDATE()");
|
|
$stmtTodayEarnings->execute([':driver_id' => $driver_id]);
|
|
$todayEarnings = (float)($stmtTodayEarnings->fetchColumn() ?: 0.0);
|
|
|
|
// 7. Get Claimed Challenge Points from Payment Server via S2S
|
|
$walletServer = "https://walletintaleq.intaleq.xyz";
|
|
if (strtolower($kazan["country"]) == 'jordan') {
|
|
$walletServer = getenv('WALLET_SERVER_JORDAN') ?: "https://walletintaleq.intaleq.xyz";
|
|
} elseif (strtolower($kazan["country"]) == 'egypt') {
|
|
$walletServer = getenv('WALLET_SERVER_EGYPT') ?: "https://walletintaleq.intaleq.xyz";
|
|
} else {
|
|
$walletServer = getenv('WALLET_SERVER_SYRIA') ?: "https://walletintaleq.intaleq.xyz";
|
|
}
|
|
|
|
$walletUrl = "$walletServer/v2/main/ride/driverWallet/get_s2s_wallet_dashboard.php";
|
|
|
|
$ch = curl_init($walletUrl);
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => http_build_query(["driverID" => $driver_id]),
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 10,
|
|
CURLOPT_HTTPHEADER => [
|
|
'Content-Type: application/x-www-form-urlencoded',
|
|
'X-S2S-Api-Key: ' . getenv('S2S_SHARED_KEY')
|
|
]
|
|
]);
|
|
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$curlErr = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
$challengePoints = 0;
|
|
if (!$curlErr && $httpCode === 200) {
|
|
$resDecoded = json_decode($response, true);
|
|
if ($resDecoded && isset($resDecoded['status']) && $resDecoded['status'] === 'success') {
|
|
$challengePoints = (int)($resDecoded['message']['challengePoints'] ?? 0);
|
|
}
|
|
}
|
|
|
|
// 8. Calculate Normalized Points
|
|
$normalizedPoints = ($totalTrips * 10) + ($totalReferrals * 100) + ((int)$behavior['avg_score'] * 2) + $challengePoints;
|
|
|
|
// 9. Fetch Available Challenges
|
|
$stmtCh = $con->prepare("SELECT * FROM gamification_challenges WHERE is_active = 1 AND country = :country ORDER BY target_count ASC");
|
|
$stmtCh->execute([':country' => $kazan['country']]);
|
|
$challengesRaw = $stmtCh->fetchAll(PDO::FETCH_ASSOC);
|
|
|
|
$challenges = [];
|
|
$tier = 'Bronze'; // Placeholder logic
|
|
foreach ($challengesRaw as $chRaw) {
|
|
$isCompleted = false;
|
|
$isClaimed = false;
|
|
|
|
$challenges[] = [
|
|
'id' => (int)$chRaw['id'],
|
|
'title' => $chRaw['title'],
|
|
'description' => $chRaw['description'],
|
|
'type' => $chRaw['challenge_type'],
|
|
'target' => (int)$chRaw['target_count'],
|
|
'reward_amount' => (float)$chRaw['reward_amount'],
|
|
'reward_type' => $chRaw['reward_type'],
|
|
'is_completed' => $isCompleted,
|
|
'is_claimed' => $isClaimed
|
|
];
|
|
}
|
|
|
|
$responseData = [
|
|
"country" => $kazan["country"],
|
|
"currency" => $kazan["currency"],
|
|
"totalTrips" => $totalTrips,
|
|
"averageRating" => $avgRating,
|
|
"totalReferrals" => $totalReferrals,
|
|
"driverInvites" => $driverInvites,
|
|
"passengerInvites" => $passengerInvites,
|
|
"behaviorScore" => (float)$behavior["avg_score"],
|
|
"hardBrakes" => (int)$behavior["total_hard_brakes"],
|
|
"maxSpeed" => (float)$behavior["max_speed"],
|
|
"todayTrips" => $todayTrips,
|
|
"todayEarnings" => $todayEarnings,
|
|
"totalPoints" => $normalizedPoints,
|
|
"tier" => $tier,
|
|
"challenges" => $challenges
|
|
];
|
|
|
|
// 10. Save the compiled data into Redis
|
|
if (isset($redis)) {
|
|
try {
|
|
$redis->setex($redisKey, 900, json_encode($responseData));
|
|
} catch (Exception $e) {
|
|
error_log("Redis write error in getGamificationDashboard: " . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
jsonSuccess($responseData);
|
|
|
|
} catch (Exception $e) {
|
|
error_log("getGamificationDashboard Error: " . $e->getMessage());
|
|
jsonError("An internal error occurred. Please try again later.");
|
|
}
|
|
?>
|