Update: 2026-08-08 14:57:58
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* db_primary.php — اتصال ثانٍ بالقاعدة الأساسية من خادم الدفع
|
||||||
|
* ─────────────────────────────────────────────────────────────
|
||||||
|
* خادم الدفع يتصل بقاعدته عبر `DB_PAYMENT_*` وحدها (connect.php)، لكن
|
||||||
|
* نقطتين فيه تحتاجان جدولَي `ride` و`driver_orders` وهما في القاعدة
|
||||||
|
* الأساسية. كانتا تشيران إليهما بلا تأهيل فسقطتا بـ
|
||||||
|
* `SQLSTATE[42S02]: Table 'payment.ride' doesn't exist` — وهو ما كان
|
||||||
|
* يُعطّل شاشة رصيد الكابتن بالكامل.
|
||||||
|
*
|
||||||
|
* ولماذا اتصال منفصل لا إشارة مؤهَّلة (`jorSiroDB.ride`)؟
|
||||||
|
*
|
||||||
|
* لأن التأهيل يشترط أن تكون القاعدتان على **نفس خادم MySQL**. وخريطة
|
||||||
|
* `backend/core/Database/Database.php` تُفرد لكل قاعدة host وuser وpass
|
||||||
|
* مستقلة — أي أن الفصل على خوادم مختلفة مفترضٌ في التصميم لا مستبعَد،
|
||||||
|
* وقاعدة الدفع أولى ما يُفصل. إشارة مؤهَّلة تعمل اليوم بالصدفة (حاوية
|
||||||
|
* mysql واحدة) وتسقط صامتةً يوم يُنقل الدفع. الاتصال المنفصل يقرأ
|
||||||
|
* `DB_PRIMARY_HOST_V2` فيتبع القاعدة أينما ذهبت.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!function_exists('primaryDbConnection')) {
|
||||||
|
/**
|
||||||
|
* يعيد اتصالاً بالقاعدة الأساسية، أو null إن تعذّر — ولا يرمي أبداً.
|
||||||
|
* المستدعي يقرّر ماذا يفعل عند الغياب؛ في نقاط المحفظة القرار هو
|
||||||
|
* عرض الرصيد بأصفار في الإحصاء لا إسقاط الشاشة.
|
||||||
|
*/
|
||||||
|
function primaryDbConnection(): ?PDO
|
||||||
|
{
|
||||||
|
static $conn = null;
|
||||||
|
static $tried = false;
|
||||||
|
|
||||||
|
if ($tried) {
|
||||||
|
return $conn;
|
||||||
|
}
|
||||||
|
$tried = true;
|
||||||
|
|
||||||
|
$name = getenv('DB_PRIMARY_NAME_V2') ?: getenv('DB_PRIMARY_NAME');
|
||||||
|
$host = getenv('DB_PRIMARY_HOST_V2') ?: getenv('DB_PRIMARY_HOST') ?: 'mysql';
|
||||||
|
$user = getenv('DB_PRIMARY_USER_V2') ?: getenv('DB_PRIMARY_USER');
|
||||||
|
$pass = getenv('DB_PRIMARY_PASS_V2') ?: getenv('DB_PRIMARY_PASS');
|
||||||
|
|
||||||
|
if (empty($name) || empty($user)) {
|
||||||
|
error_log('[db_primary] إعداد القاعدة الأساسية ناقص — تعذّر الاتصال.');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$conn = new PDO(
|
||||||
|
"mysql:host=$host;dbname=$name;charset=utf8mb4",
|
||||||
|
$user,
|
||||||
|
$pass,
|
||||||
|
[
|
||||||
|
PDO::ATTR_EMULATE_PREPARES => false,
|
||||||
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
||||||
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||||
|
PDO::ATTR_TIMEOUT => 5,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log('[db_primary] تعذّر الاتصال بالقاعدة الأساسية: ' . $e->getMessage());
|
||||||
|
$conn = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $conn;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,23 @@
|
|||||||
<?php
|
<?php
|
||||||
include "../../connect.php";
|
include "../../connect.php";
|
||||||
|
include_once "../../db_primary.php";
|
||||||
|
|
||||||
$driverID = filterRequest("driverID");
|
$driverID = filterRequest("driverID");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `driver_orders` و`ride` كلاهما في القاعدة الأساسية لا في قاعدة الدفع —
|
||||||
|
* نفس علّة getAllPayment.php التي كانت تُسقط الرصيد بـ
|
||||||
|
* `Table 'payment.ride' doesn't exist`.
|
||||||
|
*
|
||||||
|
* كل جداول هذا الاستعلام في القاعدة الأساسية، فيُنفَّذ عليها كاملاً عبر
|
||||||
|
* اتصال مستقل — لا إشارة مؤهَّلة، لأن القاعدتين قد تكونان على خادمين.
|
||||||
|
*/
|
||||||
|
$primary = primaryDbConnection();
|
||||||
|
if ($primary === null) {
|
||||||
|
printFailure("Statistics unavailable");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
$sql = "SELECT
|
$sql = "SELECT
|
||||||
YEAR(`driver_orders`.`created_at`) AS `year`,
|
YEAR(`driver_orders`.`created_at`) AS `year`,
|
||||||
MONTH(`driver_orders`.`created_at`) AS `month`,
|
MONTH(`driver_orders`.`created_at`) AS `month`,
|
||||||
@@ -30,19 +46,25 @@ ORDER BY
|
|||||||
|
|
||||||
";
|
";
|
||||||
// كان المعرّف يُدمج في نص الاستعلام مباشرةً — حقن SQL.
|
// كان المعرّف يُدمج في نص الاستعلام مباشرةً — حقن SQL.
|
||||||
$stmt = $con->prepare($sql);
|
try {
|
||||||
$stmt->bindValue(':driverID', $driverID);
|
$stmt = $primary->prepare($sql);
|
||||||
$stmt->execute();
|
$stmt->bindValue(':driverID', $driverID);
|
||||||
|
$stmt->execute();
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
// بلا هذا الالتقاط كان أي خلل في القاعدة يصير 500 يُسقط الشاشة كلها.
|
||||||
|
error_log('[driverStatistic] ' . $e->getMessage());
|
||||||
|
printFailure("Statistics unavailable");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
if ($stmt->rowCount() > 0) {
|
if ($stmt->rowCount() > 0) {
|
||||||
// Fetch the record
|
// Fetch the record
|
||||||
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
printSuccess( $row);
|
printSuccess( $row);
|
||||||
|
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
// Print a failure message
|
// Print a failure message
|
||||||
printFailure($message = "No wallet record found");
|
printFailure($message = "No wallet record found");
|
||||||
}
|
}
|
||||||
?>
|
|
||||||
@@ -1,70 +1,71 @@
|
|||||||
<?php
|
<?php
|
||||||
include "../../connect.php";
|
include "../../connect.php";
|
||||||
|
include_once "../../db_primary.php";
|
||||||
|
|
||||||
$driverID = filterRequest("driverID");
|
$driverID = filterRequest("driverID");
|
||||||
|
|
||||||
$sql = "SELECT
|
|
||||||
(
|
|
||||||
SELECT
|
|
||||||
COUNT(*)
|
|
||||||
FROM
|
|
||||||
`ride`
|
|
||||||
WHERE
|
|
||||||
LOWER(`ride`.`status`) IN ('finished','completed')
|
|
||||||
AND `ride`.`created_at` BETWEEN CURRENT_DATE() + INTERVAL 7 HOUR AND CURRENT_DATE() + INTERVAL 10 HOUR
|
|
||||||
AND `ride`.`driver_id` = :driverID
|
|
||||||
) AS morning_count,
|
|
||||||
(
|
|
||||||
SELECT
|
|
||||||
COUNT(*)
|
|
||||||
FROM
|
|
||||||
`ride`
|
|
||||||
WHERE
|
|
||||||
LOWER(`ride`.`status`) IN ('finished','completed')
|
|
||||||
AND `ride`.`created_at` BETWEEN CURRENT_DATE() + INTERVAL 15 HOUR AND CURRENT_DATE() + INTERVAL 18 HOUR
|
|
||||||
AND `ride`.`driver_id` = :driverID
|
|
||||||
) AS afternoon_count,
|
|
||||||
(
|
|
||||||
SELECT
|
|
||||||
COALESCE(SUM(amount), 0) AS total_amount
|
|
||||||
FROM
|
|
||||||
payments
|
|
||||||
WHERE
|
|
||||||
isGiven = 'waiting' AND `driverID` = :driverID
|
|
||||||
) AS total_amount,
|
|
||||||
(
|
|
||||||
SELECT
|
|
||||||
COALESCE(SUM(price), 0) AS total_amount
|
|
||||||
FROM
|
|
||||||
ride
|
|
||||||
WHERE
|
|
||||||
`driver_id` = :driverID
|
|
||||||
AND LOWER(`ride`.`status`) IN ('finished','completed')
|
|
||||||
AND `ride`.`created_at` > CURRENT_DATE() - INTERVAL 1 WEEK
|
|
||||||
) AS total_amount_last_week
|
|
||||||
FROM
|
|
||||||
dual
|
|
||||||
LIMIT 1;
|
|
||||||
|
|
||||||
|
|
||||||
";
|
|
||||||
/**
|
/**
|
||||||
* كان معرّف السائق يُدمج في نص الاستعلام مباشرةً (حقن SQL)، وكانت الحالة
|
* كان استعلاماً واحداً يقرأ `payments` و`ride` معاً — و`ride` ليس في قاعدة
|
||||||
* تُطابق 'Finished' فقط بينما خط الرحلات الحالي يكتب 'completed' — فتظهر
|
* الدفع. النتيجة `SQLSTATE[42S02]: Table 'payment.ride' doesn't exist`، وبلا
|
||||||
* أرباح السائق ورحلاته أصفاراً.
|
* try/catch صار خطأً قاتلاً يُسقط النقطة بـ 500 فتعلق شاشة رصيد الكابتن.
|
||||||
|
* (وقبل إطفاء display_errors كان الخطأ يُطبع داخل الردّ فيصل التطبيق كـ
|
||||||
|
* FormatException على `<br />` — نفس العلّة بوجه آخر.)
|
||||||
|
*
|
||||||
|
* الفصل المعتمد: الرصيد أولاً ومستقلاً، والإحصاءات ثانياً وقابلة للفشل.
|
||||||
|
* الرصيد رقم مالي من `payments` في قاعدة الدفع ولا عذر لسقوطه، أما عدّ
|
||||||
|
* الرحلات فمعلومة مساعدة من قاعدة أخرى — تعذّرها يُعيد أصفاراً ولا يحجب
|
||||||
|
* عن الكابتن رصيده.
|
||||||
|
*
|
||||||
|
* و`ride` تُقرأ عبر اتصال مستقل لا عبر إشارة مؤهَّلة: القاعدتان قد تكونان
|
||||||
|
* على خادمين مختلفين — انظر db_primary.php.
|
||||||
*/
|
*/
|
||||||
$stmt = $con->prepare($sql);
|
|
||||||
$stmt->bindValue(':driverID', $driverID);
|
|
||||||
$stmt->execute();
|
|
||||||
|
|
||||||
if ($stmt->rowCount() > 0) {
|
// ── ١. الرصيد — من قاعدة الدفع، لا يعتمد على شيء خارجها ──
|
||||||
// Fetch the record
|
$balanceStmt = $con->prepare(
|
||||||
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
"SELECT COALESCE(SUM(amount), 0) AS total_amount
|
||||||
|
FROM payments
|
||||||
|
WHERE isGiven = 'waiting' AND `driverID` = :driverID"
|
||||||
|
);
|
||||||
|
$balanceStmt->bindValue(':driverID', $driverID);
|
||||||
|
$balanceStmt->execute();
|
||||||
|
$totalAmount = $balanceStmt->fetchColumn();
|
||||||
|
|
||||||
printSuccess( $row);
|
$result = [
|
||||||
|
'total_amount' => $totalAmount !== false ? $totalAmount : 0,
|
||||||
|
'morning_count' => 0,
|
||||||
|
'afternoon_count' => 0,
|
||||||
|
'total_amount_last_week' => 0,
|
||||||
|
];
|
||||||
|
|
||||||
|
// ── ٢. إحصاءات الرحلات — اتصال مستقل بالقاعدة الأساسية ──
|
||||||
|
$primary = primaryDbConnection();
|
||||||
|
|
||||||
|
if ($primary !== null) {
|
||||||
|
try {
|
||||||
|
$statsStmt = $primary->prepare(
|
||||||
|
"SELECT
|
||||||
|
COALESCE(SUM(`created_at` BETWEEN CURRENT_DATE() + INTERVAL 7 HOUR
|
||||||
|
AND CURRENT_DATE() + INTERVAL 10 HOUR), 0) AS morning_count,
|
||||||
|
COALESCE(SUM(`created_at` BETWEEN CURRENT_DATE() + INTERVAL 15 HOUR
|
||||||
|
AND CURRENT_DATE() + INTERVAL 18 HOUR), 0) AS afternoon_count,
|
||||||
|
COALESCE(SUM(CASE WHEN `created_at` > CURRENT_DATE() - INTERVAL 1 WEEK
|
||||||
|
THEN `price` ELSE 0 END), 0) AS total_amount_last_week
|
||||||
|
FROM `ride`
|
||||||
|
WHERE `driver_id` = :driverID
|
||||||
|
AND LOWER(`status`) IN ('finished','completed')"
|
||||||
|
);
|
||||||
|
$statsStmt->bindValue(':driverID', $driverID);
|
||||||
|
$statsStmt->execute();
|
||||||
|
|
||||||
|
if ($stats = $statsStmt->fetch(PDO::FETCH_ASSOC)) {
|
||||||
|
$result['morning_count'] = (int) $stats['morning_count'];
|
||||||
|
$result['afternoon_count'] = (int) $stats['afternoon_count'];
|
||||||
|
$result['total_amount_last_week'] = $stats['total_amount_last_week'];
|
||||||
|
}
|
||||||
|
} catch (PDOException $e) {
|
||||||
|
error_log('[getAllPayment] ride stats unavailable: ' . $e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
// Print a failure message
|
// الشكل كما كان: مصفوفة من صفّ واحد، والتطبيق يقرأ message[0]['total_amount'].
|
||||||
printFailure($message = "No wallet record found");
|
printSuccess([$result]);
|
||||||
}
|
|
||||||
?>
|
|
||||||
|
|||||||
Reference in New Issue
Block a user