Files
clinicpro/docs/tasks/task-12-rating-comment/implementation_notes.md
T
hamed de1a78a235 feat: Implement SMS sending functionality with KavehNegar and Rangineh providers
- Add SendSmsMessage class for encapsulating SMS message data.
- Create KavehNegarProvider and RanginehProvider classes implementing SmsProviderInterface for sending SMS.
- Implement SmsLogRepository and SmsTemplateRepository for managing SMS logs and templates.
- Develop SendSmsHandler for handling SMS sending messages.
- Create SmsService to manage SMS dispatching and logging.
- Add UserProfileController for managing user profiles with CRUD operations.
- Implement UserProfile entity and repository for user profile data management.
- Update symfony.lock and bootstrap.php for project dependencies and environment setup.
2026-06-09 22:00:34 +03:30

5.6 KiB

نکات پیاده‌سازی — تسک ۱۲: ماژول امتیاز و نظرات

⚠ تناقض نام فیلدها: API vs DB (بسیار مهم!)

فیلدهایی که کلاینت ارسال می‌کند با نام فیلدهای پایگاه داده متفاوت هستند:

نام در Request (API) نام در DB (Drupal field) توضیح
correct_diagnosis accuracy_of_diagnosis دقت تشخیص
doctor_skill doctor_expertise مهارت پزشک
behavior_doctor doctor_behavior برخورد پزشک
office_cleaning clinic_cleanliness نظافت مطب
time_in_office waiting_time_at_clinic زمان انتظار
doctor doctor_id شناسه دکتر (integer)
rate starts امتیاز ستاره‌ای (DECIMAL 10,2)

در Symfony باید:

  • ورودی را با نام‌های API دریافت کن (correct_diagnosis, ...)
  • در Entity و DB با نام‌های Drupal ذخیره کن (accuracy_of_diagnosis, ...)

نمونه واقعی Request — POST /api/v1/clinicpro/rate

{
  "correct_diagnosis": 100,
  "doctor_skill": 100,
  "behavior_doctor": 100,
  "office_cleaning": 100,
  "time_in_office": 100,
  "doctor": 1,
  "rate": 2
}

نمونه واقعی Request — PATCH /api/v1/clinicpro/rate/{uuid}

{
  "correct_diagnosis": 50,
  "doctor_skill": 60,
  "behavior_doctor": 70,
  "office_cleaning": 80,
  "time_in_office": 90,
  "doctor": 1,
  "rate": 2
}

سیستم امتیازدهی وزنی

Rating در Drupal 5 معیار جداگانه دارد که هر کدام مقدار 0-100 می‌گیرند و با وزن‌های متفاوت محاسبه می‌شوند:

$weights = [
    "doctor_behavior"          => 1.5,   // behavior_doctor در API
    "accuracy_of_diagnosis"    => 3.0,   // correct_diagnosis در API
    "waiting_time_at_clinic"   => 1.0,   // time_in_office در API
    "doctor_expertise"         => 2.0,   // doctor_skill در API
    "clinic_cleanliness"       => 1.0,   // office_cleaning در API
];

// فرمول محاسبه:
$weightedAverage = SUM(value * weight) / SUM(weights);  // از 100
$stars = ($weightedAverage / 100) * 5;                  // از 5

پیاده‌سازی calculateDoctorRating در Symfony

public function calculateRating(array $apiScores): array
{
    // نگاشت نام‌های API به نام‌های DB
    $mapped = [
        'accuracy_of_diagnosis'    => $apiScores['correct_diagnosis'] ?? 0,
        'doctor_expertise'         => $apiScores['doctor_skill'] ?? 0,
        'doctor_behavior'          => $apiScores['behavior_doctor'] ?? 0,
        'clinic_cleanliness'       => $apiScores['office_cleaning'] ?? 0,
        'waiting_time_at_clinic'   => $apiScores['time_in_office'] ?? 0,
    ];

    $weights = [
        'doctor_behavior'          => 1.5,
        'accuracy_of_diagnosis'    => 3.0,
        'waiting_time_at_clinic'   => 1.0,
        'doctor_expertise'         => 2.0,
        'clinic_cleanliness'       => 1.0,
    ];

    $totalScore = 0.0;
    $totalWeight = 0.0;

    foreach ($weights as $key => $weight) {
        $totalScore += $mapped[$key] * $weight;
        $totalWeight += $weight;
    }

    $weightedAverage = $totalWeight > 0 ? $totalScore / $totalWeight : 0;
    $stars = ($weightedAverage / 100) * 5;

    return [
        'percent' => round($weightedAverage, 1),
        'starts'  => round(min(5.0, max(0.0, $stars)), 2),
        // ⚠️ نام فیلد DB: "starts" است نه "stars"!
    ];
}

آمار دکتر — GET /api/v1/clinicpro-comment/doctor-rate/{doctorUuid}

URL: /api/v1/clinicpro-comment/doctor-rate/{uuid_دکتر}
Auth: عمومی (بدون احراز هویت)
{
  "average_stars": 4.3,
  "total_rates": 87,
  "averages": {
    "average_doctor_behavior": 82.1,
    "average_accuracy_of_diagnosis": 88.5,
    "average_waiting_time_at_clinic": 65.3,
    "average_doctor_expertise": 90.2,
    "average_clinic_cleanliness": 78.4
  }
}

تأیید نظرات

نظرات با approved=0 ذخیره می‌شوند. ادمین آن‌ها را از GET /api/v1/clinicpro/unverified-comments/{doctorId} می‌بیند. سپس با PATCH /api/v1/clinicpro/unverified-comments/{commentId} تأیید می‌کند.


اعتبارسنجی مقادیر Rating

هر معیار باید بین 0 تا 100 باشد:

#[Assert\Range(min: 0, max: 100)]

مجوزها

POST   /api/v1/clinicpro/rate           → احراز هویت‌شده
PATCH  /api/v1/clinicpro/rate/{uuid}    → owner (هر کاربر فقط یک امتیاز برای هر دکتر)
DELETE /api/v1/rate/doctor/{uuid}       → ROLE_ADMIN
GET    /api/v1/clinicpro/rate/{uuid}    → owner (امتیاز کاربر برای دکتر مشخص)
GET    /api/v1/clinicpro-comment/doctor-rate/{uuid} → عمومی (آمار کلی دکتر)
POST   /api/v1/clinicpro/comment        → احراز هویت‌شده
PATCH  /api/v1/clinicpro/comment/{uuid} → owner یا ROLE_ADMIN
DELETE /api/v1/clinicpro/comment/{uuid} → owner یا ROLE_ADMIN
GET    /api/v1/clinicpro/comment/{uuid} → احراز هویت‌شده
GET    /api/v1/clinicpro/comments/{doctorId} → احراز هویت‌شده (فقط approved)
GET    /api/v1/clinicpro/unverified-comments/{doctorId} → ROLE_ADMIN
PATCH  /api/v1/clinicpro/unverified-comments/{id} → ROLE_ADMIN (تأیید/رد نظر)
POST   /api/v1/clinicpro/like           → احراز هویت‌شده
PATCH  /api/v1/clinicpro/like/{uuid}    → owner