Fix payment server: remove global conRide to prevent crashing non-ride DB queries

This commit is contained in:
Hamza-Ayed
2026-08-01 18:28:30 +03:00
parent 998cad6756
commit f7df080b27
5 changed files with 175 additions and 72 deletions
-6
View File
@@ -53,12 +53,6 @@ class Database
$cfg = self::$map[$name];
$dbname = getenv($cfg['name']);
// 🔥 OVERRIDE: Prevent crash if .env on server still has the old wrong name
if ($name === 'ride' && $dbname === 'intaleq-ridesDB') {
$dbname = 'rideDB';
}
$host = getenv($cfg['host']) ?: 'localhost';
$user = getenv($cfg['user']);
$pass = getenv($cfg['pass']);
@@ -187,7 +187,13 @@ class LocationController extends GetxController with WidgetsBindingObserver {
});
socket!.disconnect();
}
return;
}
// ‏أي تغيّر آخر (متاح ⇄ مشغول) يجب أن يصل السيرفر فوراً: هو الحدث الذي
// ‏ينقل الكابتن بين geo:drivers:available و geo:drivers:busy. وبلا هذا
// ‏ننتظر أول حدث GPS — وفلتر المسافة قد يؤجّله دقائق إن كان واقفاً.
_emitCurrentLocationNow();
});
bool deps = await _awaitDependencies();
@@ -349,10 +355,10 @@ class LocationController extends GetxController with WidgetsBindingObserver {
if (sid != null || eid != null) {
isSocketConnected = true;
if (myLocation.latitude != 0) {
// ‏هذا الـ emit هو ما يُسجّل الكابتن في geo:drivers:available بعد كل
// ‏اتصال جديد، فلا نتخطاه لمجرد أن أول تثبيت GPS لم يصل بعد.
print("🚀 Sending initial location to socket after connect...");
emitLocationToSocket(myLocation, heading, speed);
}
_emitCurrentLocationNow();
_startHeartbeat();
@@ -546,6 +552,30 @@ class LocationController extends GetxController with WidgetsBindingObserver {
_socketHeartbeat?.cancel();
}
/// يبثّ الموقع الحالي فوراً، ويجلب تثبيتاً واحداً إن لم يكن لدينا موقع بعد.
///
/// ضروري بعد كل اتصال جديد وبعد كل تغيّر حالة: ملفّ التتبّع في وضع الانتظار
/// يستخدم فلتر مسافة (50–100 متر)، فالكابتن الواقف قد لا يُصدر أي حدث
/// onLocationChanged إطلاقاً — ولو اعتمدنا عليه وحده لبقي غير مرئي على
/// خريطة الراكب حتى يتحرّك فعلياً.
Future<void> _emitCurrentLocationNow() async {
if (socket == null || !socket!.connected) return;
if (myLocation.latitude == 0 && myLocation.longitude == 0) {
await getLocation();
}
if (myLocation.latitude == 0 && myLocation.longitude == 0) {
Log.print("⚠️ No GPS fix yet — initial location emit skipped.");
return;
}
if (isBusMode) {
emitBusLocationToSocket(myLocation, heading, speed);
} else {
emitLocationToSocket(myLocation, heading, speed);
}
}
// In LocationController.dart
void emitLocationToSocket(LatLng pos, double head, double spd) {
@@ -373,24 +373,51 @@ class HomeCaptainController extends GetxController {
}
}
void onButtonSelected() {
/// زرّ «متاح / غير متاح» في الواجهة — يقلب الحالة الحالية.
void onButtonSelected() => setActive(!isActive);
/// تفعيل/إيقاف الكابتن بشكل صريح.
/// مُعامِلة صريحة بدل القلب (toggle) لأن الاستدعاء التلقائي في onInit كان
/// يعتمد على أن isActive تساوي false — فأي إعادة بناء للكونترولر أو
/// استدعاء ثانٍ كانت تُطفئ الكابتن بدل تشغيله. الاستدعاء بنفس القيمة
/// الحالية آمن: يعيد التأكد من أن التتبّع شغّال فقط.
void setActive(bool active) {
if (!Get.isRegistered<CaptainWalletController>()) {
Get.put(CaptainWalletController());
}
totalPoints = Get.find<CaptainWalletController>().totalPoints;
// Toggle Active State
isActive = !isActive;
if (!active) {
_goOffline();
return;
}
if (isActive) {
try {
_checkFatigueBeforeOnline(); // Throws exception if tired
_checkFatigueBeforeOnline(); // يرمي استثناءً إذا تجاوز 12 ساعة
} catch (_) {
isActive = false;
update();
return;
}
if (double.parse(totalPoints) > minPointsThreshold) {
// الرصيد غير كافٍ: نُطفئ فعلياً بدل إبقاء isActive = true بلا تتبّع،
// فقد كانت الواجهة تعرض «متاح» بينما لا يُبَث أي موقع.
final double? points = double.tryParse(totalPoints);
if (points == null || points <= minPointsThreshold) {
_goOffline();
return;
}
final bool wasActive = isActive;
isActive = true;
// آمنة للاستدعاء المتكرر: تتجاهل الاشتراك إن كان قائماً بالفعل.
locationController.startLocationUpdates();
if (!wasActive) {
HapticFeedback.heavyImpact();
activeStartTime = DateTime.now();
activeTimer?.cancel();
activeTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
activeDuration = DateTime.now().difference(activeStartTime!);
stringActiveDuration = formatDuration(activeDuration);
@@ -410,31 +437,51 @@ class HomeCaptainController extends GetxController {
update();
});
} else {
locationController.stopLocationUpdates();
activeStartTime = null;
activeTimer?.cancel();
savePeriod(activeDuration);
activeDuration = Duration.zero;
box.write('fatigue_last_offline', DateTime.now().toIso8601String());
}
update();
}
} catch (e) {
// Driver is fatigued, revert state
void _goOffline() {
isActive = false;
update();
}
} else {
locationController.stopLocationUpdates();
activeStartTime = null;
activeTimer?.cancel();
savePeriod(activeDuration);
if (activeDuration.inSeconds > 0) savePeriod(activeDuration);
activeDuration = Duration.zero;
// Save offline time for Fatigue Monitoring reset
box.write('fatigue_last_offline', DateTime.now().toIso8601String());
update();
}
/// يضبط حالة الكابتن المخزَّنة عند إقلاع التطبيق.
/// تذكير بمصطلحات التطبيق: 'off' = متاح، 'on' = مشغول برحلة،
/// 'blocked' = محظور، وهي القيمة التي يقرأها emitLocationToSocket ويبني
/// عليها السيرفر عضوية geo:drivers:available.
///
/// كانت 'off' تُكتب دون شرط في آخر onInit، فتُلغي حظراً ساري المفعول
/// كتبه checkAndShowBlockDialog قبلها بسطر، وتُعيد كابتناً في رحلة نشطة
/// إلى حوض المتاحين بعد إعادة فتح التطبيق.
void _applyInitialDriverStatus() {
final String? blockStr = box.read(BoxName.blockUntilDate);
if (blockStr != null && blockStr.isNotEmpty) {
final DateTime? blockExpiry = DateTime.tryParse(blockStr);
if (blockExpiry != null && DateTime.now().isBefore(blockExpiry)) {
box.write(BoxName.statusDriverLocation, 'blocked');
return;
}
}
// رحلة نجت من إغلاق التطبيق: نُبقيه مشغولاً حتى لا يُوزَّع عليه طلب ثانٍ.
final String? rideStatus = box.read(BoxName.rideStatus);
if (rideStatus == 'Apply' || rideStatus == 'Arrived' ||
rideStatus == 'Begin') {
box.write(BoxName.statusDriverLocation, 'on');
return;
}
box.write(BoxName.statusDriverLocation, 'off'); // متاح
}
// متغيرات العداد للحظر
@@ -815,8 +862,16 @@ class HomeCaptainController extends GetxController {
Get.put(FirebaseMessagesController());
addToken();
// ⚠️ الترتيب مقصود: الحالة تُكتب قبل تشغيل التتبّع.
// ‏أول update_location يُرسَل بعد اتصال السوكيت مباشرة، وهو الذي يُدخل
// ‏الكابتن إلى geo:drivers:available على السيرفر. وكتابة الحالة بعد
// ‏تشغيل التتبّع (كما كانت في آخر السطور) تعني أن ذلك الـ emit قد يحمل
// ‏حالة قديمة ('on' من رحلة سابقة لم تُغلق مثلاً).
_applyInitialDriverStatus();
await getlocation();
onButtonSelected();
setActive(true);
getDriverRate();
addCustomCarIcon();
getKazanPercent();
@@ -829,8 +884,8 @@ class HomeCaptainController extends GetxController {
// totalPoints = Get.find<CaptainWalletController>().totalPoints.toString();
// getRefusedOrderByCaptain();
// 🔥 الفحص عند تشغيل التطبيق
// ‏(يكتب 'blocked' عند وجود حظر ساري — ولم تعد أي كتابة بعده تدهسه)
checkAndShowBlockDialog();
box.write(BoxName.statusDriverLocation, 'off');
// 2. عدل الليسنر ليصبح مشروطاً
// Camera follow timer — only moves when the driver has
// actually moved > 15 meters, saving GPU/battery on idle.
+20 -1
View File
@@ -766,6 +766,20 @@ $io->on('connection', function ($socket) use ($INTERNAL_KEY) {
'status' => '',
'expire_ts' => 0,
];
} else {
// ‏اتصال جديد لا يضمن أن السائق ما زال عضواً في geo:drivers:* —
// ‏api_get_nearby.php يحذف العضوية كلّما وجد driver:profile منتهياً
// ‏(TTL = 900 ثانية)، بينما تبقى هذه الحالة في ذاكرة الـ worker لأن
// ‏سوكيت خدمة الخلفية (background_service) يظل مسجَّلاً فيمنع تنظيفها
// ‏عند إغلاق سوكيت الواجهة.
// ‏وبلا تصفيرها يجد أول update_location بعد فتح التطبيق أن status و
// ‏lat/lng بلا تغيير ⇒ لا GEOADD ⇒ يبقى الكابتن غير مرئي على خريطة
// ‏الراكب حتى يتحرك 10 أمتار أو يضغط زر التوفّر (الذي يقطع السوكيت).
// ‏نصفّر الجزء المتطاير فقط ليُجبَر أول تحديث على إعادة تسجيله.
$driverState[$driverId]['lat'] = 0.0;
$driverState[$driverId]['lng'] = 0.0;
$driverState[$driverId]['status'] = '';
$driverState[$driverId]['expire_ts'] = 0;
}
logMsg("✅ Driver Connected: #$driverId ($platform)");
@@ -941,7 +955,12 @@ $io->on('connection', function ($socket) use ($INTERNAL_KEY) {
}
}
if ($needGeoadd || $statusChanged) {
// ‏نُعيد تأكيد العضوية في geo:drivers:* مع كل تجديد صلاحية (كل
// ‏EXPIRE_REFRESH_SECONDS) لا عند الحركة فقط: الكابتن المتاح والواقف
// ‏لا يُصدر didMove ولا statusChanged أبداً، فلو حُذف من المجموعة
// ‏(انقطاع، إعادة تشغيل السيرفر، تنظيف api_get_nearby) لما عاد إليها
// ‏قط. التكلفة: عملية GEOADD واحدة لكل كابتن كل دقيقتين.
if ($needGeoadd || $statusChanged || $needExpireRefresh) {
$eventBuffer[$driverId]['geoadd'] = [
'status' => $status,
'lng' => $lng,
+5
View File
@@ -0,0 +1,5 @@
<?php
require_once __DIR__ . '/backend/core/bootstrap.php';
$jwtService = new JwtService($redis);
$token = $jwtService->generateAccessToken('34feffd3fa72d6bee56b', 'driver', 'wallet-app:ios');
echo $token;