feat(rating): multi-dimensional ratings, rich comments, eligibility guard
Rebuild the doctor rating/review system to power the public site's rich
review UI, and restrict who may submit.
Ratings:
- Rate entity holds five 0–100 dimensions (waiting time, diagnosis
accuracy, behaviour, cleanliness, expertise) instead of a single score.
- GET /rate/{uuid} returns aggregate {point, satisfaction, averages[]}.
- POST /rate upserts all five dimensions and returns the new aggregate.
Comments:
- Comment gains parent/replies (threaded) and a rich toArray with author,
like_status (like/dislike counts + current user's vote) and nested
approved replies. POST /comment accepts {comment, parent}.
- Likes are directional (value 1=like, -1=dislike) with toggle/replace;
POST /like/{uuid} returns like_count/dislike_count/current_user_like.
Eligibility:
- Only a user with a confirmed appointment in the last 30 days may rate or
comment (AppointmentRepository::hasRecentConfirmed); otherwise
403 ERR_RATING_NOT_ELIGIBLE. New GET /rate/{uuid}/eligibility for the UI.
- security.yaml: narrow the public rate pattern so /eligibility stays auth'd.
Also updates admin rates listing to the new dimensions and the rating/admin
API docs. Includes migration for the new columns.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1091,7 +1091,8 @@ class AdminApiController extends BaseController
|
||||
|
||||
$qb = $this->em->createQueryBuilder()
|
||||
->select(
|
||||
'r.uuid, r.score, r.createdAt',
|
||||
'r.uuid, r.createdAt',
|
||||
'r.waitingTimeAtClinic, r.accuracyOfDiagnosis, r.doctorBehavior, r.clinicCleanliness, r.doctorExpertise',
|
||||
'u.realName as patient_name, u.mobileNumber as patient_mobile',
|
||||
'd.name as doctor_name',
|
||||
)
|
||||
@@ -1110,14 +1111,20 @@ class AdminApiController extends BaseController
|
||||
$rows = $qb->setFirstResult(($page - 1) * $limit)->setMaxResults($limit)
|
||||
->getQuery()->getArrayResult();
|
||||
|
||||
$items = array_map(fn(array $r) => [
|
||||
'uuid' => $r['uuid'],
|
||||
'patient_name' => $r['patient_name'] ?? $r['patient_mobile'],
|
||||
'doctor_name' => $r['doctor_name'],
|
||||
'overall' => (int) $r['score'],
|
||||
'score' => (int) $r['score'],
|
||||
'created_at' => date('c', (int) $r['createdAt']),
|
||||
], $rows);
|
||||
$items = array_map(function (array $r) {
|
||||
$overall = (int) round((
|
||||
$r['waitingTimeAtClinic'] + $r['accuracyOfDiagnosis'] + $r['doctorBehavior']
|
||||
+ $r['clinicCleanliness'] + $r['doctorExpertise']
|
||||
) / 5);
|
||||
return [
|
||||
'uuid' => $r['uuid'],
|
||||
'patient_name' => $r['patient_name'] ?? $r['patient_mobile'],
|
||||
'doctor_name' => $r['doctor_name'],
|
||||
'overall' => $overall,
|
||||
'score' => (int) round($overall / 20),
|
||||
'created_at' => date('c', (int) $r['createdAt']),
|
||||
];
|
||||
}, $rows);
|
||||
|
||||
return $this->paginated($items, (int) $total, $page, $limit);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user