feat(appointment): include doctor specialties and clinic address in toArray
The appointment detail view needs the doctor's specialty and the clinic address/phone/map, which toArray didn't return. Add doctor.specialties[] and a top-level `address` (the doctor's first address via DoctorAddress::toArray — address, telephone, map). Additive only; existing keys unchanged. Docs updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
# غنیسازی پاسخ نوبت با تخصص و آدرس مطب پزشک
|
||||
|
||||
## پروژه
|
||||
|
||||
`clinicpro` (Backend — منبع حقیقت).
|
||||
|
||||
> **Cross-repo:** صفحهی «جزئیات نوبت» در `nobat724_front` (داشبورد → نوبتهای من → مشاهده جزئیات) این فیلدها را مصرف میکند (پرامپت همتا: `nobat724_front/.claude/prompt/appointment-detail-fields-fix.md`). این پرامپت **اول** اجرا شود.
|
||||
|
||||
## زمینه
|
||||
|
||||
`Appointment::toArray()` فقط `doctor:{uuid,name}`, `slot_start/slot_end`, `status`, `note`, `patient_*` را برمیگرداند. اما صفحهی جزئیات نوبت در فرانت میخواهد **تخصص پزشک، آدرس مطب، تلفن و مختصات نقشه** را هم نشان دهد. چون این دادهها در پاسخ نیستند، آن بخشها خالی میمانند.
|
||||
|
||||
`Doctor` این روابط را دارد: `getSpecialties()` (ManyToMany به `Specialty`)، `getAddresses()` (OneToMany به `DoctorAddress`). `DoctorAddress::toArray()` شکل `{address, telephone, map:{latitude,longitude}, name, ...}` میدهد. نوبت آدرس مشخصی ذخیره نمیکند، پس **اولین آدرس پزشک** بهعنوان آدرس مطب استفاده میشود.
|
||||
|
||||
## مشکل / هدف
|
||||
|
||||
`Appointment::toArray()` گسترش یابد تا `doctor` شامل `specialties` و `address` (اولین آدرس پزشک با `address/telephone/map`) باشد — بدون شکستن مصرفکنندههای فعلی.
|
||||
|
||||
## فایلهای مرتبط
|
||||
|
||||
| فایل | نقش |
|
||||
|------|-----|
|
||||
| `src/Appointment/Entity/Appointment.php` | `toArray()` — افزودن specialties + address زیر `doctor` |
|
||||
| `src/Doctor/Entity/Doctor.php` | `getSpecialties()`, `getAddresses()` (موجود) |
|
||||
| `src/Doctor/Entity/DoctorAddress.php` | `toArray()` → `{address, telephone, map, ...}` (موجود) |
|
||||
| `src/Specialty/Entity/Specialty.php` | `getUuid()/getName()` (موجود) |
|
||||
|
||||
## وضعیت فعلی (کد واقعی)
|
||||
|
||||
```php
|
||||
// Appointment::toArray()
|
||||
'doctor' => [
|
||||
'uuid' => $this->doctor->getUuid(),
|
||||
'name' => $this->doctor->getName(),
|
||||
],
|
||||
'slot_start' => $this->slotStart,
|
||||
'slot_end' => $this->slotEnd,
|
||||
// ... بدون specialties / address
|
||||
```
|
||||
|
||||
`DoctorAddress::toArray()` (مرجع شکل):
|
||||
```php
|
||||
'map' => ['latitude' => (string)…, 'longitude' => (string)…],
|
||||
'address' => $this->address,
|
||||
'telephone' => $this->telephone,
|
||||
'name' => $this->name,
|
||||
```
|
||||
|
||||
## وظایف
|
||||
|
||||
### ۱. افزودن specialties و address به `doctor` در `Appointment::toArray()`
|
||||
```php
|
||||
$doctor = $this->doctor;
|
||||
$firstAddress = $doctor->getAddresses()->first() ?: null;
|
||||
|
||||
// ...
|
||||
'doctor' => [
|
||||
'uuid' => $doctor->getUuid(),
|
||||
'name' => $doctor->getName(),
|
||||
'specialties' => array_map(
|
||||
fn($s) => ['uuid' => $s->getUuid(), 'name' => $s->getName()],
|
||||
$doctor->getSpecialties()->toArray()
|
||||
),
|
||||
],
|
||||
'address' => $firstAddress ? $firstAddress->toArray() : null,
|
||||
```
|
||||
- `address` را در سطح بالای آرایهی نوبت بگذار (نه داخل doctor) تا فرانت `appointmentData.address.{address,telephone,map}` را مستقیم بخواند.
|
||||
- `getAddresses()->first()` ممکن است `false` برگرداند (Doctrine Collection) → با `?: null` ایمن کن.
|
||||
- بقیهی کلیدهای موجود (`slot_start`, `status`, ...) بدون تغییر بمانند.
|
||||
|
||||
### ۲. مستندسازی `docs/api/appointment.md`
|
||||
شکل جدید `doctor.specialties` و `address` را در response نمونهی `GET /api/v1/appointments/user` (و `GET /api/v1/appointment/{uuid}`) اضافه کن.
|
||||
|
||||
## نکات مهم
|
||||
|
||||
- **بدون شکستن مصرفکنندهها:** فقط کلید اضافه میشود؛ کلیدهای فعلی دستنخورده. (پنل ادمین/جاهای دیگری که `toArray` نوبت را میخوانند نباید بشکنند.)
|
||||
- تخصص ممکن است خالی باشد (آرایهی خالی) و آدرس ممکن است `null` باشد (پزشک بدون آدرس) — فرانت باید اینها را تحمل کند (در پرامپت فرانت لحاظ شده).
|
||||
- نوبت آدرس اختصاصی ذخیره نمیکند؛ «اولین آدرس پزشک» یک تقریب منطقی است. اگر بعداً نوبت به آدرس مشخص گره خورد، اینجا باید آن را ترجیح داد.
|
||||
- `slot_start`/`slot_end` Unixاند (فرانت تاریخ و ساعت را از `slot_start` میسازد).
|
||||
- پاسخها از `BaseController`؛ migration لازم نیست.
|
||||
- تست: `GET /api/v1/appointments/user` برای کاربری با نوبت → آیتم باید `doctor.specialties[]` و `address.{address,telephone,map}` داشته باشد.
|
||||
+15
-7
@@ -216,24 +216,32 @@ Get appointment detail.
|
||||
"uuid": "appt-uuid-...",
|
||||
"doctor": {
|
||||
"uuid": "...",
|
||||
"title": "دکتر علی احمدی",
|
||||
"image": "https://..."
|
||||
"name": "دکتر علی احمدی",
|
||||
"specialties": [
|
||||
{ "uuid": "...", "name": "اورولوژی عمومی" }
|
||||
]
|
||||
},
|
||||
"user": {
|
||||
"address": {
|
||||
"uuid": "...",
|
||||
"real_name": "کاربر",
|
||||
"mobile_number": "09..."
|
||||
"name": "مطب دکتر علی احمدی",
|
||||
"address": "یزد، خیابان ...",
|
||||
"telephone": "035...",
|
||||
"map": { "latitude": "31.8", "longitude": "54.3" },
|
||||
"city": { "id": "132", "name": "یزد" },
|
||||
"province": { "id": "100", "name": "یزد" }
|
||||
},
|
||||
"user": { "uuid": "...", "mobile": "09..." },
|
||||
"slot_start": 1718438400,
|
||||
"slot_end": 1718439600,
|
||||
"status": "confirmed",
|
||||
"note": "...",
|
||||
"price": 500000,
|
||||
"payment_uuid": "pay-uuid-...",
|
||||
"patient_name": "...",
|
||||
"patient_mobile": "...",
|
||||
"created_at": 1717000000
|
||||
}
|
||||
}
|
||||
```
|
||||
> `doctor.specialties` آرایه (ممکن است خالی)؛ `address` اولین آدرس پزشک است (ممکن است `null` اگر پزشک آدرسی ندارد). `address.map.latitude/longitude` رشته یا `null`. تاریخها Unix.
|
||||
|
||||
### Errors
|
||||
| Code | HTTP | Description |
|
||||
|
||||
@@ -153,12 +153,19 @@ class Appointment
|
||||
|
||||
public function toArray(): array
|
||||
{
|
||||
$firstAddress = $this->doctor->getAddresses()->first() ?: null;
|
||||
|
||||
return [
|
||||
'uuid' => $this->uuid,
|
||||
'doctor' => [
|
||||
'uuid' => $this->doctor->getUuid(),
|
||||
'name' => $this->doctor->getName(),
|
||||
'uuid' => $this->doctor->getUuid(),
|
||||
'name' => $this->doctor->getName(),
|
||||
'specialties' => array_map(
|
||||
fn($s) => ['uuid' => $s->getUuid(), 'name' => $s->getName()],
|
||||
$this->doctor->getSpecialties()->toArray()
|
||||
),
|
||||
],
|
||||
'address' => $firstAddress?->toArray(),
|
||||
'user' => [
|
||||
'uuid' => $this->user->getUuid(),
|
||||
'mobile' => $this->user->getMobileNumber(),
|
||||
|
||||
Reference in New Issue
Block a user