286 lines
14 KiB
PHP
286 lines
14 KiB
PHP
<?php
|
||
// --- معالجة الشكوى من جهة الخادم (Backend) ---
|
||
|
||
// 1. تضمين ملف الاتصال والدوال المساعدة
|
||
// ! تأكد من أن هذا المسار صحيح بالنسبة لهيكل مشروعك
|
||
require_once __DIR__ . '/../../connect.php';
|
||
|
||
// --- إعدادات النظام ---
|
||
$geminiApiKey = getenv("GEMINI_API_KEY");
|
||
$customerServiceWhatsapp = getenv("SERVICE_PHONE1"); // يُفترض أن هذا مُعرّف في connect.php أو متغيرات البيئة
|
||
|
||
// التحقق من وجود مفتاح Gemini API
|
||
if (!$geminiApiKey) {
|
||
error_log("CRITICAL: Gemini API Key is not configured on the server."); // ⚠️ تسجيل الخطأ
|
||
jsonError("Gemini API Key is not configured on the server.");
|
||
exit;
|
||
}
|
||
|
||
// --- 2. استقبال البيانات والتحقق منها ---
|
||
$rideId = filterRequest("ride_id");
|
||
$complaintText = filterRequest("complaint_text");
|
||
$audioLink = filterRequest("audio_link");
|
||
|
||
if (empty($rideId)) {
|
||
error_log("WARNING: Complaint request failed. Ride ID is missing."); // ⚠️ تسجيل الخطأ
|
||
jsonError("Ride ID is required.");
|
||
exit;
|
||
}
|
||
|
||
// --- 3. جلب البيانات المفصلة من قاعدة البيانات ---
|
||
global $con, $encryptionHelper; // تم افتراض أن هذه المتغيرات global ومتاحة في هذا النطاق
|
||
|
||
// جلب تفاصيل الرحلة الأساسية والتحقق من حالتها
|
||
$stmt = $con->prepare("SELECT * FROM ride WHERE id = ? AND (status = 'Finished' OR status = 'Begin')");
|
||
$stmt->execute([$rideId]);
|
||
$ride = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
|
||
if (!$ride) {
|
||
// رسالة خطأ أوضح للمستخدم
|
||
error_log("WARNING: Complaint filing failed for ride ID: $rideId. Ride not found or status is invalid."); // ⚠️ تسجيل الخطأ
|
||
jsonError("Complaint cannot be filed for this ride. It may not have been completed or started.");
|
||
exit;
|
||
}
|
||
|
||
$passengerId = $ride['passenger_id'];
|
||
$driverId = $ride['driver_id'];
|
||
|
||
// --- دوال مساعدة لجلب البيانات (تم افتراض أن الدوال تصل إلى $con و $encryptionHelper عبر النطاق global أو تمريرها كـ arguments) ---
|
||
|
||
/**
|
||
* جلب بيانات ومعلومات تقييم السائق
|
||
*/
|
||
function getDriverFullProfile($con, $encryptionHelper, $driverId) {
|
||
$profile = ['info' => null, 'ratings' => null, 'comments' => []];
|
||
|
||
// جلب معلومات السائق الأساسية
|
||
$stmt = $con->prepare("SELECT id, first_name, last_name, created_at FROM driver WHERE id = ?");
|
||
$stmt->execute([$driverId]);
|
||
$driverInfo = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
// فك تشفير البيانات الحساسة
|
||
if ($driverInfo) {
|
||
// التحقق من وجود ودوال فك التشفير قبل الاستخدام
|
||
if (isset($encryptionHelper) && method_exists($encryptionHelper, 'decryptData')) {
|
||
$decryptedFirstName = $encryptionHelper->decryptData($driverInfo['first_name']);
|
||
$decryptedLastName = $encryptionHelper->decryptData($driverInfo['last_name']);
|
||
$driverInfo['full_name'] = trim($decryptedFirstName . ' ' . $decryptedLastName);
|
||
} else {
|
||
$driverInfo['full_name'] = 'Decryption Failed';
|
||
}
|
||
|
||
unset($driverInfo['first_name'], $driverInfo['last_name']); // إزالة الحقول المشفرة
|
||
$profile['info'] = $driverInfo;
|
||
}
|
||
|
||
// جلب ملخص التقييمات والتعليقات
|
||
$stmt = $con->prepare("SELECT AVG(rating) as avg_rating, COUNT(id) as total_ratings FROM ratingDriver WHERE driver_id = ?");
|
||
$stmt->execute([$driverId]);
|
||
$profile['ratings'] = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
$stmt = $con->prepare("SELECT comment FROM ratingDriver WHERE driver_id = ? AND comment IS NOT NULL AND comment != '' ORDER BY created_at DESC LIMIT 5");
|
||
$stmt->execute([$driverId]);
|
||
$profile['comments'] = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||
|
||
return $profile;
|
||
}
|
||
|
||
/**
|
||
* جلب بيانات ومعلومات تقييم الراكب
|
||
*/
|
||
function getPassengerFullProfile($con, $encryptionHelper, $passengerId) {
|
||
$profile = ['info' => null, 'ratings' => null, 'comments' => []];
|
||
|
||
$stmt = $con->prepare("SELECT id, first_name, last_name, created_at FROM passengers WHERE id = ?");
|
||
$stmt->execute([$passengerId]);
|
||
$passengerInfo = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
// فك تشفير البيانات الحساسة
|
||
if ($passengerInfo) {
|
||
if (isset($encryptionHelper) && method_exists($encryptionHelper, 'decryptData')) {
|
||
$decryptedFirstName = $encryptionHelper->decryptData($passengerInfo['first_name']);
|
||
$decryptedLastName = $encryptionHelper->decryptData($passengerInfo['last_name']);
|
||
$passengerInfo['full_name'] = trim($decryptedFirstName . ' ' . $decryptedLastName);
|
||
} else {
|
||
$passengerInfo['full_name'] = 'Decryption Failed';
|
||
}
|
||
|
||
unset($passengerInfo['first_name'], $passengerInfo['last_name']);
|
||
$profile['info'] = $passengerInfo;
|
||
}
|
||
|
||
$stmt = $con->prepare("SELECT AVG(rating) as avg_rating, COUNT(id) as total_ratings FROM ratingPassenger WHERE passenger_id = ?");
|
||
$stmt->execute([$passengerId]);
|
||
$profile['ratings'] = $stmt->fetch(PDO::FETCH_ASSOC);
|
||
|
||
$stmt = $con->prepare("SELECT comment FROM ratingPassenger WHERE passenger_id = ? AND comment IS NOT NULL AND comment != '' ORDER BY created_at DESC LIMIT 5");
|
||
$stmt->execute([$passengerId]);
|
||
$profile['comments'] = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||
|
||
return $profile;
|
||
}
|
||
|
||
/**
|
||
* جلب بيانات سلوك السائق في الرحلة المحددة
|
||
*/
|
||
function getDriverBehavior($con, $rideId, $driverId) {
|
||
$stmt = $con->prepare("SELECT max_speed, avg_speed, hard_brakes, behavior_score FROM driver_behavior WHERE trip_id = ? AND driver_id = ?");
|
||
$stmt->execute([$rideId, $driverId]);
|
||
return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
|
||
}
|
||
|
||
// استدعاء الدوال لجلب البيانات
|
||
$passengerProfile = getPassengerFullProfile($con, $encryptionHelper, $passengerId);
|
||
$driverProfile = getDriverFullProfile($con, $encryptionHelper, $driverId);
|
||
$driverBehavior = getDriverBehavior($con, $rideId, $driverId);
|
||
|
||
// --- 4. بناء الـ Prompt وإرساله إلى Gemini ---
|
||
$prompt = "
|
||
أنت خبير في حل النزاعات في خدمات نقل الركاب لتطبيق intaleqapp.com. قم بتحليل الشكوى التالية بين راكب وسائق بناءً على البيانات الشاملة التالية:
|
||
|
||
**1. تفاصيل الرحلة:**
|
||
" . json_encode($ride, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "
|
||
|
||
**2. ملف الراكب:**
|
||
" . json_encode($passengerProfile, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "
|
||
|
||
**3. ملف السائق:**
|
||
" . json_encode($driverProfile, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "
|
||
|
||
**4. بيانات سلوك السائق (في هذه الرحلة):**
|
||
" . json_encode($driverBehavior, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) . "
|
||
|
||
**5. الشكوى نفسها:**
|
||
- نص الشكوى من الراكب: '" . $complaintText . "'
|
||
- رابط تسجيل صوتي للشكوى (إن وجد): " . $audioLink . "
|
||
|
||
**مهمتك هي:**
|
||
1. تحليل جميع البيانات المتاحة لتحديد الطرف المخطئ على الأرجح.
|
||
2. تحديد ما إذا كانت الشكوى كيدية أم حقيقية.
|
||
3. **تصنيف الشكوى** (مثال: سلوك السائق، مشكلة في الأجرة، مسار الرحلة، حالة السيارة، أخرى).
|
||
4. اقتراح حلين واضحين ومختلفين لفريق خدمة العملاء.
|
||
5. كتابة تقرير موجز ومناسب للراكب.
|
||
6. كتابة تقرير موجز ومناسب للسائق.
|
||
|
||
**الخرج المطلوب:**
|
||
أعد الرد بصيغة JSON فقط، بدون أي نصوص إضافية، وباللغة العربية (لهجة مصرية)، بالهيكل التالي:
|
||
{
|
||
\"customerServiceSolutions\": [\"الحل المقترح الأول\", \"الحل المقترح الثاني\"],
|
||
\"passengerReport\": { \"title\": \"بخصوص شكوتك في رحلة Siro\", \"body\": \"رسالة واضحة للراكب بنتيجة الشكوى\" },
|
||
\"driverReport\": { \"title\": \"بخصوص بلاغ رحلتك الأخيرة في Siro\", \"body\": \"رسالة واضحة للسائق بنتيجة الشكوى\" },
|
||
\"fault_determination\": \"الطرف المخطئ (الراكب/السائق/كلاهما/غير واضح)\",
|
||
\"complaint_nature\": \"طبيعة الشكوى (حقيقية/كيدية/نزاع بسيط)\",
|
||
\"complaint_type\": \"تصنيف الشكوى الذي حددته\"
|
||
}
|
||
";
|
||
|
||
// استخدام نموذج Gemini 1.5 Flash Lite
|
||
$apiURL = "https://generativelanguage.googleapis.com/v1beta/models/gemini-flash-lite-latest:generateContent?key=$geminiApiKey";
|
||
$headers = ["Content-Type: application/json"];
|
||
$payload = ['contents' => [['parts' => [['text' => $prompt]]]]];
|
||
|
||
error_log("INFO: Submitting complaint analysis to Gemini for ride ID: $rideId."); // ℹ️ تسجيل الحدث
|
||
|
||
$ch = curl_init($apiURL);
|
||
curl_setopt_array($ch, [
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_POST => true,
|
||
CURLOPT_HTTPHEADER => $headers,
|
||
CURLOPT_POSTFIELDS => json_encode($payload),
|
||
CURLOPT_TIMEOUT => 60
|
||
]);
|
||
$response = curl_exec($ch);
|
||
|
||
if (curl_errno($ch)) {
|
||
$errorMsg = curl_error($ch);
|
||
error_log("ERROR: AI Service Curl Error for ride $rideId: $errorMsg"); // ⚠️ تسجيل الخطأ
|
||
jsonError("AI Service Error: " . $errorMsg);
|
||
curl_close($ch);
|
||
exit;
|
||
}
|
||
curl_close($ch);
|
||
|
||
$data = json_decode($response, true);
|
||
$analysisResultText = $data['candidates'][0]['content']['parts'][0]['text'] ?? '';
|
||
$analysisResultJson = trim(preg_replace('/```json|```/', '', $analysisResultText));
|
||
$analysisResult = json_decode($analysisResultJson, true);
|
||
|
||
if (json_last_error() !== JSON_ERROR_NONE || !isset($analysisResult['passengerReport']) || !isset($analysisResult['driverReport'])) {
|
||
error_log("ERROR: Failed to parse AI response for ride $rideId. Raw Response: " . substr($response, 0, 500)); // ⚠️ تسجيل الخطأ
|
||
jsonError("Failed to parse AI response. Please try again later.");
|
||
exit;
|
||
}
|
||
|
||
error_log("INFO: Gemini analysis successful for ride $rideId. Type: " . ($analysisResult['complaint_type'] ?? 'N/A')); // ℹ️ تسجيل الحدث
|
||
|
||
// --- 5. تنفيذ الإجراءات بناءً على التحليل ---
|
||
|
||
// تجميع الوصف الكامل للشكوى
|
||
$fullDescription = $complaintText;
|
||
if (!empty($audioLink)) {
|
||
$fullDescription .= "\n\n[رابط صوتي مرفق: " . $audioLink . "]";
|
||
}
|
||
|
||
// ** التعديل: تم تحديث جملة الحفظ لتشمل جميع مخرجات التحليل **
|
||
$stmt = $con->prepare("
|
||
INSERT INTO complaint (
|
||
ride_id, passenger_id, driver_id, complaint_type, description,
|
||
date_filed, statusComplaint, resolution, passenger_report, driver_report,
|
||
cs_solutions, fault_determination, complaint_nature, date_resolved
|
||
)
|
||
VALUES (?, ?, ?, ?, ?, NOW(), ?, ?, ?, ?, ?, ?, ?, NOW())
|
||
");
|
||
|
||
try {
|
||
$success = $stmt->execute([
|
||
$rideId,
|
||
$passengerId,
|
||
$driverId,
|
||
$analysisResult['complaint_type'] ?? 'General',
|
||
$fullDescription,
|
||
'Resolved', // statusComplaint
|
||
$analysisResultJson, // resolution (الـ JSON الكامل)
|
||
json_encode($analysisResult['passengerReport'] ?? null, JSON_UNESCAPED_UNICODE), // passenger_report
|
||
json_encode($analysisResult['driverReport'] ?? null, JSON_UNESCAPED_UNICODE), // driver_report
|
||
json_encode($analysisResult['customerServiceSolutions'] ?? null, JSON_UNESCAPED_UNICODE), // cs_solutions
|
||
$analysisResult['fault_determination'] ?? 'N/A', // fault_determination
|
||
$analysisResult['complaint_nature'] ?? 'N/A' // complaint_nature
|
||
]);
|
||
|
||
if (!$success) {
|
||
// يمكنك تسجيل رسالة الخطأ من PDO إذا كانت متاحة (للتصحيح فقط وليس للإنتاج)
|
||
error_log("CRITICAL: Failed to save complaint to DB for ride $rideId. PDO Error Info: " . json_encode($stmt->errorInfo())); // ⚠️ تسجيل الخطأ
|
||
}
|
||
|
||
$complaintId = $con->lastInsertId();
|
||
error_log("SUCCESS: Complaint ID $complaintId processed and saved for ride $rideId."); // ✅ تسجيل النجاح
|
||
|
||
} catch (PDOException $e) {
|
||
error_log("CRITICAL: PDO Exception when saving complaint for ride $rideId: " . $e->getMessage()); // ⚠️ تسجيل خطأ قاعدة البيانات
|
||
jsonError("A database error occurred while saving the complaint.");
|
||
exit;
|
||
}
|
||
|
||
// إرسال رسالة WhatsApp لخدمة العملاء
|
||
if (function_exists('sendWhatsAppFromServer') && !empty($customerServiceWhatsapp)) {
|
||
$csMessage = "*شكوى جديدة (رقم $complaintId)*\n" .
|
||
"*- الرحلة:* $rideId\n" .
|
||
"*- تصنيف الشكوى:* " . ($analysisResult['complaint_type'] ?? 'غير محدد') . "\n" .
|
||
"*- المخطئ (تقدير النظام):* " . $analysisResult['fault_determination'] . "\n" .
|
||
"*- طبيعة الشكوى:* " . $analysisResult['complaint_nature'] . "\n\n" .
|
||
"*حلول مقترحة:*\n1. " . ($analysisResult['customerServiceSolutions'][0] ?? 'N/A') . "\n" .
|
||
"2. " . ($analysisResult['customerServiceSolutions'][1] ?? 'N/A');
|
||
sendWhatsAppFromServer($customerServiceWhatsapp, $csMessage);
|
||
error_log("INFO: WhatsApp notification sent to customer service for complaint ID $complaintId."); // ℹ️ تسجيل الحدث
|
||
}
|
||
|
||
|
||
// --- 6. إرسال الرد النهائي للتطبيق ---
|
||
printSuccess([
|
||
'message' => 'Complaint processed successfully.',
|
||
'passenger_response' => $analysisResult['passengerReport'],
|
||
'driver_response' => $analysisResult['driverReport']
|
||
]);
|
||
|
||
?>
|