first commit
This commit is contained in:
286
backend/ride/feedBack/add_solve_all.php
Executable file
286
backend/ride/feedBack/add_solve_all.php
Executable file
@@ -0,0 +1,286 @@
|
||||
<?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\": \"بخصوص شكوتك في رحلة Intaleq\", \"body\": \"رسالة واضحة للراكب بنتيجة الشكوى\" },
|
||||
\"driverReport\": { \"title\": \"بخصوص بلاغ رحلتك الأخيرة في Intaleq\", \"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']
|
||||
]);
|
||||
|
||||
?>
|
||||
Reference in New Issue
Block a user