Fix payment server: remove global conRide to prevent crashing non-ride DB queries
This commit is contained in:
@@ -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,11 +355,11 @@ class LocationController extends GetxController with WidgetsBindingObserver {
|
||||
if (sid != null || eid != null) {
|
||||
isSocketConnected = true;
|
||||
|
||||
if (myLocation.latitude != 0) {
|
||||
print("🚀 Sending initial location to socket after connect...");
|
||||
emitLocationToSocket(myLocation, heading, speed);
|
||||
}
|
||||
|
||||
// هذا الـ emit هو ما يُسجّل الكابتن في geo:drivers:available بعد كل
|
||||
// اتصال جديد، فلا نتخطاه لمجرد أن أول تثبيت GPS لم يصل بعد.
|
||||
print("🚀 Sending initial location to socket after connect...");
|
||||
_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,68 +373,115 @@ 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 (isActive) {
|
||||
try {
|
||||
_checkFatigueBeforeOnline(); // Throws exception if tired
|
||||
|
||||
if (double.parse(totalPoints) > minPointsThreshold) {
|
||||
locationController.startLocationUpdates();
|
||||
HapticFeedback.heavyImpact();
|
||||
activeStartTime = DateTime.now();
|
||||
|
||||
activeTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
activeDuration = DateTime.now().difference(activeStartTime!);
|
||||
stringActiveDuration = formatDuration(activeDuration);
|
||||
|
||||
// Increment Fatigue Counter (write to box every 30s)
|
||||
_fatigueSeconds++;
|
||||
if (_fatigueSeconds % 30 == 0) {
|
||||
int totalSeconds =
|
||||
(box.read('fatigue_total_seconds') ?? 0) + _fatigueSeconds;
|
||||
box.write('fatigue_total_seconds', totalSeconds);
|
||||
_fatigueSeconds = 0;
|
||||
if (totalSeconds >= 12 * 3600) {
|
||||
// 12 hours
|
||||
_forceOfflineDueToFatigue();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
isActive = false;
|
||||
update();
|
||||
}
|
||||
} else {
|
||||
locationController.stopLocationUpdates();
|
||||
activeStartTime = null;
|
||||
activeTimer?.cancel();
|
||||
savePeriod(activeDuration);
|
||||
activeDuration = Duration.zero;
|
||||
|
||||
// Save offline time for Fatigue Monitoring reset
|
||||
box.write('fatigue_last_offline', DateTime.now().toIso8601String());
|
||||
update();
|
||||
if (!active) {
|
||||
_goOffline();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
_checkFatigueBeforeOnline(); // يرمي استثناءً إذا تجاوز 12 ساعة
|
||||
} catch (_) {
|
||||
isActive = false;
|
||||
update();
|
||||
return;
|
||||
}
|
||||
|
||||
// الرصيد غير كافٍ: نُطفئ فعلياً بدل إبقاء 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);
|
||||
|
||||
// Increment Fatigue Counter (write to box every 30s)
|
||||
_fatigueSeconds++;
|
||||
if (_fatigueSeconds % 30 == 0) {
|
||||
int totalSeconds =
|
||||
(box.read('fatigue_total_seconds') ?? 0) + _fatigueSeconds;
|
||||
box.write('fatigue_total_seconds', totalSeconds);
|
||||
_fatigueSeconds = 0;
|
||||
if (totalSeconds >= 12 * 3600) {
|
||||
// 12 hours
|
||||
_forceOfflineDueToFatigue();
|
||||
}
|
||||
}
|
||||
|
||||
update();
|
||||
});
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void _goOffline() {
|
||||
isActive = false;
|
||||
locationController.stopLocationUpdates();
|
||||
activeStartTime = null;
|
||||
activeTimer?.cancel();
|
||||
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.
|
||||
|
||||
@@ -761,13 +761,27 @@ $io->on('connection', function ($socket) use ($INTERNAL_KEY) {
|
||||
$driverState[$driverId] = [
|
||||
'lat' => 0.0,
|
||||
'lng' => 0.0,
|
||||
'speed' => -999.0,
|
||||
'speed' => -999.0,
|
||||
'heading' => -999.0,
|
||||
'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)");
|
||||
|
||||
// 🆕 Deliver any pending orders missed during disconnection
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user