Update: 2026-07-09 04:13:14
This commit is contained in:
@@ -17,6 +17,7 @@ class RateLimiter
|
|||||||
'api' => ['requests' => 120, 'window' => 60], // 120 طلب / دقيقة
|
'api' => ['requests' => 120, 'window' => 60], // 120 طلب / دقيقة
|
||||||
'ride' => ['requests' => 30, 'window' => 60], // 30 طلب / دقيقة
|
'ride' => ['requests' => 30, 'window' => 60], // 30 طلب / دقيقة
|
||||||
'upload' => ['requests' => 10, 'window' => 300], // 10 رفع / 5 دقائق
|
'upload' => ['requests' => 10, 'window' => 300], // 10 رفع / 5 دقائق
|
||||||
|
'complaint' => ['requests' => 5, 'window' => 600], // 5 شكاوى / 10 دقائق (كل شكوى تستدعي Gemini + واتساب)
|
||||||
];
|
];
|
||||||
|
|
||||||
public function __construct(?Redis $redis)
|
public function __construct(?Redis $redis)
|
||||||
|
|||||||
@@ -5,6 +5,13 @@
|
|||||||
// ! تأكد من أن هذا المسار صحيح بالنسبة لهيكل مشروعك
|
// ! تأكد من أن هذا المسار صحيح بالنسبة لهيكل مشروعك
|
||||||
require_once __DIR__ . '/../../connect.php';
|
require_once __DIR__ . '/../../connect.php';
|
||||||
|
|
||||||
|
// 🔥 [Fix Rate Limit] كل شكوى تستدعي Gemini API (مدفوع) وترسل رسالة واتساب
|
||||||
|
// لخدمة العملاء — بدون حد كانت قابلة للإغراق (spam) من أي مستخدم مسجّل.
|
||||||
|
global $limiter, $user_id;
|
||||||
|
if (isset($limiter)) {
|
||||||
|
$limiter->enforce(RateLimiter::identifier($user_id), 'complaint');
|
||||||
|
}
|
||||||
|
|
||||||
// --- إعدادات النظام ---
|
// --- إعدادات النظام ---
|
||||||
$geminiApiKey = getenv("GEMINI_API_KEY");
|
$geminiApiKey = getenv("GEMINI_API_KEY");
|
||||||
$customerServiceWhatsapp = getenv("SERVICE_PHONE1"); // يُفترض أن هذا مُعرّف في connect.php أو متغيرات البيئة
|
$customerServiceWhatsapp = getenv("SERVICE_PHONE1"); // يُفترض أن هذا مُعرّف في connect.php أو متغيرات البيئة
|
||||||
|
|||||||
+64
-17
@@ -1,35 +1,82 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once __DIR__ . '/../../connect.php';
|
require_once __DIR__ . '/../../connect.php';
|
||||||
|
|
||||||
// Force passenger_id from JWT — never trust user-supplied passenger_id
|
// هذا المسار: السائق يقيّم الراكب — المُرسِل هو السائق، وليس الراكب.
|
||||||
if ($role !== 'passenger') {
|
// نفرض هوية السائق من التوكن (driverID) بدل الثقة بأي قيمة من العميل،
|
||||||
jsonError("Only passengers can submit ratings");
|
// و passenger_id هو "الهدف" (الراكب المُقيَّم) القادم من الطلب لكن يجب
|
||||||
|
// التحقق أنه فعلاً راكب هذه الرحلة قبل قبوله (لمنع IDOR).
|
||||||
|
if ($role !== 'driver') {
|
||||||
|
jsonError("Only drivers can rate passengers");
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
$passenger_id = $user_id;
|
$driverID = $user_id;
|
||||||
$driverID = filterRequest("driverID");
|
$passenger_id = filterRequest("passenger_id");
|
||||||
$rideId = filterRequest("rideId");
|
$rideId = filterRequest("rideId");
|
||||||
$rating = filterRequest("rating");
|
$rating = filterRequest("rating");
|
||||||
$comment = filterRequest("comment");
|
$comment = filterRequest("comment");
|
||||||
|
|
||||||
$sql = "INSERT INTO `ratingPassenger` (
|
try {
|
||||||
|
if (empty($passenger_id) || empty($rideId) || empty($rating)) {
|
||||||
|
throw new Exception("Required fields are missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔥 التحقق من ملكية الرحلة: يجب أن تخص هذا السائق وهذا الراكب فعلاً،
|
||||||
|
// وأن تكون منتهية، قبل قبول التقييم — يمنع تقييم رحلات عشوائية (IDOR).
|
||||||
|
$stmtRide = $con->prepare("SELECT driver_id, passenger_id, status FROM ride WHERE id = ?");
|
||||||
|
$stmtRide->execute([$rideId]);
|
||||||
|
$ride = $stmtRide->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$ride) {
|
||||||
|
jsonError("Ride not found");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ((string)$ride['driver_id'] !== (string)$driverID) {
|
||||||
|
jsonError("This ride does not belong to you");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ((string)$ride['passenger_id'] !== (string)$passenger_id) {
|
||||||
|
jsonError("Passenger does not match this ride");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ($ride['status'] !== 'Finished') {
|
||||||
|
jsonError("Ride must be finished before rating");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔥 منع تكرار التقييم لنفس الرحلة من نفس السائق
|
||||||
|
$stmtDup = $con->prepare("SELECT COUNT(*) FROM `ratingPassenger` WHERE rideId = ? AND driverID = ?");
|
||||||
|
$stmtDup->execute([$rideId, $driverID]);
|
||||||
|
if ($stmtDup->fetchColumn() > 0) {
|
||||||
|
jsonError("This ride has already been rated");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sql = "INSERT INTO `ratingPassenger` (
|
||||||
`passenger_id`, `driverID`, `rideId`, `rating`, `comment`
|
`passenger_id`, `driverID`, `rideId`, `rating`, `comment`
|
||||||
) VALUES (
|
) VALUES (
|
||||||
:passenger_id, :driverID, :rideId, :rating, :comment
|
:passenger_id, :driverID, :rideId, :rating, :comment
|
||||||
)";
|
)";
|
||||||
|
|
||||||
$stmt = $con->prepare($sql);
|
$stmt = $con->prepare($sql);
|
||||||
$stmt->bindParam(':passenger_id', $passenger_id);
|
$stmt->bindParam(':passenger_id', $passenger_id);
|
||||||
$stmt->bindParam(':driverID', $driverID);
|
$stmt->bindParam(':driverID', $driverID);
|
||||||
$stmt->bindParam(':rideId', $rideId);
|
$stmt->bindParam(':rideId', $rideId);
|
||||||
$stmt->bindParam(':rating', $rating);
|
$stmt->bindParam(':rating', $rating);
|
||||||
$stmt->bindParam(':comment', $comment);
|
$stmt->bindParam(':comment', $comment);
|
||||||
|
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
|
|
||||||
if ($stmt->rowCount() > 0) {
|
if ($stmt->rowCount() > 0) {
|
||||||
jsonSuccess(null, "Rate inserted successfully");
|
jsonSuccess(null, "Rate inserted successfully");
|
||||||
} else {
|
} else {
|
||||||
jsonError("Failed to save rating information");
|
jsonError("Failed to save rating information");
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log("[rate/add] DB Error: " . $e->getMessage() . " | RideID: $rideId");
|
||||||
|
jsonError("Database Error: Could not save rating");
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log("[rate/add] General Error: " . $e->getMessage());
|
||||||
|
jsonError("Error: Could not save rating");
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
@@ -19,6 +19,37 @@ try {
|
|||||||
throw new Exception("Required fields are missing");
|
throw new Exception("Required fields are missing");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🔥 التحقق من ملكية الرحلة: يجب أن تخص هذا الراكب وهذا السائق فعلاً،
|
||||||
|
// وأن تكون منتهية، قبل قبول التقييم — يمنع تقييم رحلات عشوائية (IDOR).
|
||||||
|
$stmtRide = $con->prepare("SELECT driver_id, passenger_id, status FROM ride WHERE id = ?");
|
||||||
|
$stmtRide->execute([$ride_id]);
|
||||||
|
$ride = $stmtRide->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$ride) {
|
||||||
|
jsonError("Ride not found");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ((string)$ride['passenger_id'] !== (string)$passenger_id) {
|
||||||
|
jsonError("This ride does not belong to you");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ((string)$ride['driver_id'] !== (string)$driver_id) {
|
||||||
|
jsonError("Driver does not match this ride");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
if ($ride['status'] !== 'Finished') {
|
||||||
|
jsonError("Ride must be finished before rating");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 🔥 منع تكرار التقييم لنفس الرحلة من نفس الراكب
|
||||||
|
$stmtDup = $con->prepare("SELECT COUNT(*) FROM `ratingDriver` WHERE ride_id = ? AND passenger_id = ?");
|
||||||
|
$stmtDup->execute([$ride_id, $passenger_id]);
|
||||||
|
if ($stmtDup->fetchColumn() > 0) {
|
||||||
|
jsonError("This ride has already been rated");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
$sql = "INSERT INTO `ratingDriver`(
|
$sql = "INSERT INTO `ratingDriver`(
|
||||||
`passenger_id`, `driver_id`, `ride_id`, `rating`, `comment`
|
`passenger_id`, `driver_id`, `ride_id`, `rating`, `comment`
|
||||||
) VALUES (
|
) VALUES (
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ try {
|
|||||||
// Fetch ride data from remote/local DB for server-side calculation
|
// Fetch ride data from remote/local DB for server-side calculation
|
||||||
$stmtRideData = $con->prepare("
|
$stmtRideData = $con->prepare("
|
||||||
SELECT id, price AS quoted_price, car_type,
|
SELECT id, price AS quoted_price, car_type,
|
||||||
distance AS planned_distance, passenger_id, driver_id
|
distance AS planned_distance, passenger_id, driver_id, price_for_driver
|
||||||
FROM ride WHERE id = ? AND driver_id = ?
|
FROM ride WHERE id = ? AND driver_id = ?
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
");
|
");
|
||||||
@@ -117,6 +117,20 @@ try {
|
|||||||
$kazanPercent = floatval($countryPricing['kazanPercent'] ?? $countryPricing['kazan'] ?? 10); // 🆕 من جدول kazan (kazanPercent هو الاسم الجديد)
|
$kazanPercent = floatval($countryPricing['kazanPercent'] ?? $countryPricing['kazan'] ?? 10); // 🆕 من جدول kazan (kazanPercent هو الاسم الجديد)
|
||||||
$carType = $rideData['car_type'] ?? 'Fixed Price';
|
$carType = $rideData['car_type'] ?? 'Fixed Price';
|
||||||
|
|
||||||
|
// 🔥 [Fix Driver Commission] عند عرض السعر (pricing/get.php) قد تُطبَّق نسبة
|
||||||
|
// خصم عمولة خاصة بالمنطقة (kazanDiscountFactor من surge:kazan_discounts)
|
||||||
|
// فتُحفَظ نتيجتها في عمود ride.price_for_driver — وهو "الوعد" الذي رآه
|
||||||
|
// السائق كـ"أرباحك أعلى" وقت قبول الطلب. إعادة جلب kazanPercent الخام هنا
|
||||||
|
// كانت تتجاهل ذلك الخصم بالكامل. نشتق نسبة العمولة الفعلية من الفرق
|
||||||
|
// المحفوظ فعلياً بين السعر المُقتبَس وحصة السائق منه، ونستخدمها بدل
|
||||||
|
// النسبة الخام، حتى تبقى النسبة المطبقة عند التسوية مطابقة لما وُعد به.
|
||||||
|
$priceForDriver = floatval($rideData['price_for_driver'] ?? 0);
|
||||||
|
if ($quotedPrice > 0 && $priceForDriver > 0 && $priceForDriver <= $quotedPrice) {
|
||||||
|
$effectiveKazanPercent = (($quotedPrice - $priceForDriver) / $quotedPrice) * 100;
|
||||||
|
error_log("[finish_ride_updates] Using locked commission rate for ride $rideId: {$effectiveKazanPercent}% (was raw {$kazanPercent}%)");
|
||||||
|
$kazanPercent = $effectiveKazanPercent;
|
||||||
|
}
|
||||||
|
|
||||||
// Fixed-price types, Speed & Awfar: use quoted price as-is
|
// Fixed-price types, Speed & Awfar: use quoted price as-is
|
||||||
$fixedPriceTypes = ['Speed', 'Fixed Price', 'Awfar Car'];
|
$fixedPriceTypes = ['Speed', 'Fixed Price', 'Awfar Car'];
|
||||||
if (in_array($carType, $fixedPriceTypes)) {
|
if (in_array($carType, $fixedPriceTypes)) {
|
||||||
@@ -144,6 +158,19 @@ try {
|
|||||||
$cleanDist = preg_replace('/[^0-9.]/', '', $actualDistance);
|
$cleanDist = preg_replace('/[^0-9.]/', '', $actualDistance);
|
||||||
$distanceKm = floatval($cleanDist);
|
$distanceKm = floatval($cleanDist);
|
||||||
|
|
||||||
|
// 🔥 [Fix Price Cap] سقف أعلى على الانحراف عن المسافة المخططة —
|
||||||
|
// actualDistance يأتي من العميل، بدون هذا السقف يمكن لانجراف GPS
|
||||||
|
// أو قيمة مُتلاعَب بها أن تُضخّم السعر النهائي بلا حدود. نسمح بهامش
|
||||||
|
// معقول للانحرافات الحقيقية (تحويلة، إغلاق طريق...) ونقصّ الباقي.
|
||||||
|
$plannedDistanceKm = floatval($rideData['planned_distance'] ?? 0);
|
||||||
|
if ($plannedDistanceKm > 0) {
|
||||||
|
$maxAllowedDistanceKm = max($plannedDistanceKm * 1.5, $plannedDistanceKm + 5);
|
||||||
|
if ($distanceKm > $maxAllowedDistanceKm) {
|
||||||
|
error_log("[finish_ride_updates] ⚠️ actualDistance ($distanceKm km) exceeds cap ($maxAllowedDistanceKm km) for ride $rideId — planned was $plannedDistanceKm km. Clamping.");
|
||||||
|
$distanceKm = $maxAllowedDistanceKm;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ($distanceKm <= 0) {
|
if ($distanceKm <= 0) {
|
||||||
$finalPrice = $quotedPrice; // fallback
|
$finalPrice = $quotedPrice; // fallback
|
||||||
} else {
|
} else {
|
||||||
@@ -151,6 +178,13 @@ try {
|
|||||||
$perKmRate = getPerKmRate($carType, $countryPricing);
|
$perKmRate = getPerKmRate($carType, $countryPricing);
|
||||||
$perMinRate = getPerMinRate($countryPricing);
|
$perMinRate = getPerMinRate($countryPricing);
|
||||||
$durationMin = intval(preg_replace('/[^0-9]/', '', $actualDuration));
|
$durationMin = intval(preg_replace('/[^0-9]/', '', $actualDuration));
|
||||||
|
// نفس فكرة السقف على المدة: لا نسمح بمدة أكبر من ضعف زمن الرحلة
|
||||||
|
// المعقول (نفترض حد أقصى واسع 3 ساعات إذا لم تتوفر مدة مخططة)
|
||||||
|
$maxAllowedDurationMin = 180;
|
||||||
|
if ($durationMin > $maxAllowedDurationMin) {
|
||||||
|
error_log("[finish_ride_updates] ⚠️ actualDuration ($durationMin min) exceeds cap ($maxAllowedDurationMin min) for ride $rideId. Clamping.");
|
||||||
|
$durationMin = $maxAllowedDurationMin;
|
||||||
|
}
|
||||||
|
|
||||||
$calculated = ($distanceKm * $perKmRate) + ($durationMin * $perMinRate);
|
$calculated = ($distanceKm * $perKmRate) + ($durationMin * $perMinRate);
|
||||||
|
|
||||||
@@ -165,7 +199,10 @@ try {
|
|||||||
// لكن إذا كانت العمولة صفر للسائق، يتم إعطاؤها للسائق بدلاً من الشركة عبر السيرفر المالي.
|
// لكن إذا كانت العمولة صفر للسائق، يتم إعطاؤها للسائق بدلاً من الشركة عبر السيرفر المالي.
|
||||||
$calculated *= (1 + ($kazanPercent / 100));
|
$calculated *= (1 + ($kazanPercent / 100));
|
||||||
|
|
||||||
$finalPrice = max($quotedPrice, round($calculated, 2));
|
// 🔥 [Fix Price Cap] سقف أعلى مطلق أيضاً على السعر النهائي نفسه
|
||||||
|
// (دفاع ثانٍ) — لا يتجاوز 1.6 * السعر المُقتبَس أصلاً بأي حال.
|
||||||
|
$maxAllowedPrice = $quotedPrice > 0 ? $quotedPrice * 1.6 : round($calculated, 2);
|
||||||
|
$finalPrice = max($quotedPrice, min(round($calculated, 2), $maxAllowedPrice));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,13 +219,12 @@ try {
|
|||||||
// 4. Atomic Transaction: Update DBs + Process Payment
|
// 4. Atomic Transaction: Update DBs + Process Payment
|
||||||
// ============================================================
|
// ============================================================
|
||||||
try {
|
try {
|
||||||
// --- Update Remote DB (con_ride) FIRST ---
|
// 🔥 [Fix Split-Brain] كان تحديث قاعدة البيانات البعيدة (con_ride) يحدث
|
||||||
if (isset($con_ride)) {
|
// هنا قبل محاولة الدفع وبدون أي Rollback عليه — فإذا فشل الدفع لاحقاً،
|
||||||
$stmtRemote = $con_ride->prepare(
|
// كانت con_ride تبقى 'Finished' بينما المحلية تُرجَع لـ 'Begin'،
|
||||||
"UPDATE ride SET status = ?, rideTimeFinish = NOW(), price = ? WHERE id = ? AND status = 'Begin'"
|
// فإعادة محاولة لاحقة تفشل بصمت على con_ride (شرط WHERE status='Begin'
|
||||||
);
|
// لم يعد يتحقق) وتُنتج سعرين مختلفين بين القاعدتين. الآن نؤجل تحديث
|
||||||
$stmtRemote->execute([$newStatus, $finalPrice, $rideId]);
|
// con_ride إلى ما بعد نجاح الدفع فعلياً (انظر الأسفل بعد commit()).
|
||||||
}
|
|
||||||
|
|
||||||
// --- BEGIN Local DB Transaction ---
|
// --- BEGIN Local DB Transaction ---
|
||||||
$con->beginTransaction();
|
$con->beginTransaction();
|
||||||
@@ -277,6 +313,20 @@ try {
|
|||||||
// ✅ Payment succeeded — COMMIT
|
// ✅ Payment succeeded — COMMIT
|
||||||
$con->commit();
|
$con->commit();
|
||||||
|
|
||||||
|
// 🔥 [Fix Split-Brain] تحديث القاعدة البعيدة الآن فقط، بعد أن أصبح الدفع
|
||||||
|
// والتحديث المحلي مؤكدَين نجاحهما — يبقي الحالتين متطابقتين دائماً.
|
||||||
|
// فشل هذا التحديث best-effort فقط (لا يُرجع الرحلة المحلية المُنجَزة فعلاً).
|
||||||
|
if (isset($con_ride)) {
|
||||||
|
try {
|
||||||
|
$stmtRemote = $con_ride->prepare(
|
||||||
|
"UPDATE ride SET status = ?, rideTimeFinish = NOW(), price = ? WHERE id = ? AND status = 'Begin'"
|
||||||
|
);
|
||||||
|
$stmtRemote->execute([$newStatus, $finalPrice, $rideId]);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log("[finish_ride_updates] Remote DB (con_ride) update failed for ride $rideId: " . $e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 🆕 Update Driver Streak (Increment because ride is finished successfully)
|
// 🆕 Update Driver Streak (Increment because ride is finished successfully)
|
||||||
handleDriverStreak($con, $driver_id, 'increment');
|
handleDriverStreak($con, $driver_id, 'increment');
|
||||||
|
|
||||||
|
|||||||
@@ -1396,14 +1396,25 @@ class MapDriverController extends GetxController
|
|||||||
box.remove(BoxName.passengerID);
|
box.remove(BoxName.passengerID);
|
||||||
box.remove(BoxName.rideId);
|
box.remove(BoxName.rideId);
|
||||||
|
|
||||||
|
// 🔥 [Fix Actual Distance] نُرسل المسافة/المدة الفعليتين المُتراكمتين
|
||||||
|
// من GPS الحي (currentRideDistanceKm/_rideStartTime) بدل الحقلين
|
||||||
|
// الثابتين (distance/duration) اللذين يمثلان المسار المخطط أصلاً
|
||||||
|
// ولا يُحدَّثان أبداً أثناء الرحلة — كانا يُبطلان فعلياً حماية
|
||||||
|
// "إعادة حساب السعر من المسافة الفعلية" في الباك إند.
|
||||||
|
final double actualDistanceKm =
|
||||||
|
currentRideDistanceKm > 0 ? currentRideDistanceKm : safeParseDouble(distance);
|
||||||
|
final int actualDurationMinutes = _rideStartTime != null
|
||||||
|
? (DateTime.now().difference(_rideStartTime!).inSeconds / 60).ceil()
|
||||||
|
: safeParseInt(duration);
|
||||||
|
|
||||||
// تجهيز البيانات الخام الموحدة للسيرفر ليقوم بمعالجة الدفع والإنهاء معاً بنظام المعاملة الواحدة
|
// تجهيز البيانات الخام الموحدة للسيرفر ليقوم بمعالجة الدفع والإنهاء معاً بنظام المعاملة الواحدة
|
||||||
final finishPayload = {
|
final finishPayload = {
|
||||||
'rideId': rideId.toString(),
|
'rideId': rideId.toString(),
|
||||||
'driver_id': box.read(BoxName.driverID).toString(),
|
'driver_id': box.read(BoxName.driverID).toString(),
|
||||||
'passengerId': passengerId.toString(),
|
'passengerId': passengerId.toString(),
|
||||||
'status': 'Finished',
|
'status': 'Finished',
|
||||||
'actualDistance': distance.toString(),
|
'actualDistance': actualDistanceKm.toString(),
|
||||||
'actualDuration': duration.toString(),
|
'actualDuration': actualDurationMinutes.toString(),
|
||||||
'walletChecked': walletChecked.toString(),
|
'walletChecked': walletChecked.toString(),
|
||||||
'passengerWalletBurc': passengerWalletBurc.toString(),
|
'passengerWalletBurc': passengerWalletBurc.toString(),
|
||||||
'passengerToken': tokenPassenger.toString(),
|
'passengerToken': tokenPassenger.toString(),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:siro_driver/controller/firebase/firbase_messge.dart';
|
import 'package:siro_driver/controller/firebase/firbase_messge.dart';
|
||||||
import 'package:siro_driver/controller/home/captin/map_driver_controller.dart';
|
import 'package:siro_driver/controller/home/captin/map_driver_controller.dart';
|
||||||
|
import 'package:siro_driver/print.dart';
|
||||||
import 'package:siro_driver/views/widgets/error_snakbar.dart';
|
import 'package:siro_driver/views/widgets/error_snakbar.dart';
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -112,6 +113,7 @@ class RateController extends GetxController {
|
|||||||
middleText: '',
|
middleText: '',
|
||||||
confirm: MyElevatedButton(title: 'Ok', onPressed: () => Get.back()));
|
confirm: MyElevatedButton(title: 'Ok', onPressed: () => Get.back()));
|
||||||
} else {
|
} else {
|
||||||
|
final rateResponse =
|
||||||
await CRUD().post(link: "${AppLink.server}/ride/rate/add.php", payload: {
|
await CRUD().post(link: "${AppLink.server}/ride/rate/add.php", payload: {
|
||||||
'passenger_id': passengerId,
|
'passenger_id': passengerId,
|
||||||
'driverID': box.read(BoxName.driverID).toString(),
|
'driverID': box.read(BoxName.driverID).toString(),
|
||||||
@@ -119,6 +121,10 @@ class RateController extends GetxController {
|
|||||||
'rating': selectedRateItemId.toString(),
|
'rating': selectedRateItemId.toString(),
|
||||||
'comment': comment.text ?? 'none',
|
'comment': comment.text ?? 'none',
|
||||||
});
|
});
|
||||||
|
// 🔥 لا نمنع إكمال الرحلة إذا فشل التقييم، لكن يجب تسجيله بدل تجاهله بصمت
|
||||||
|
if (rateResponse is Map && rateResponse['status'] != 'success') {
|
||||||
|
Log.print('⚠️ Failed to submit passenger rating: $rateResponse');
|
||||||
|
}
|
||||||
|
|
||||||
CRUD().sendEmail(AppLink.sendEmailToPassengerForTripDetails, {
|
CRUD().sendEmail(AppLink.sendEmailToPassengerForTripDetails, {
|
||||||
'startLocation':
|
'startLocation':
|
||||||
|
|||||||
@@ -1056,10 +1056,9 @@ packages:
|
|||||||
intaleq_maps:
|
intaleq_maps:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: intaleq_maps
|
path: "../../map-saas/packages/flutter-sdk"
|
||||||
sha256: b74c4e6f1d890f81bf253c4d3996db53149b64fcbf9869b279d417067f73128b
|
relative: true
|
||||||
url: "https://pub.dev"
|
source: path
|
||||||
source: hosted
|
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
internet_connection_checker:
|
internet_connection_checker:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
|
|||||||
@@ -78,7 +78,11 @@ dependencies:
|
|||||||
internet_connection_checker: ^3.0.1
|
internet_connection_checker: ^3.0.1
|
||||||
connectivity_plus: ^6.1.5
|
connectivity_plus: ^6.1.5
|
||||||
app_links: ^7.0.0
|
app_links: ^7.0.0
|
||||||
intaleq_maps: ^2.2.0
|
# 🔧 مؤقتاً: نستخدم نفس نسخة الحزمة المحلية المُصلَحة التي يستخدمها تطبيق
|
||||||
|
# السائق (بدل النسخة 2.2.0 المنشورة على pub.dev) لاختبار إصلاح مقارنة
|
||||||
|
# Polyline/Marker على التطبيقين معاً قبل رفع نسخة رسمية جديدة على pub.dev.
|
||||||
|
intaleq_maps:
|
||||||
|
path: ../../map-saas/packages/flutter-sdk/
|
||||||
socket_io_client: 1.0.2
|
socket_io_client: 1.0.2
|
||||||
# home_widget: ^0.7.0+1
|
# home_widget: ^0.7.0+1
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user