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-...",
|
"uuid": "appt-uuid-...",
|
||||||
"doctor": {
|
"doctor": {
|
||||||
"uuid": "...",
|
"uuid": "...",
|
||||||
"title": "دکتر علی احمدی",
|
"name": "دکتر علی احمدی",
|
||||||
"image": "https://..."
|
"specialties": [
|
||||||
|
{ "uuid": "...", "name": "اورولوژی عمومی" }
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"user": {
|
"address": {
|
||||||
"uuid": "...",
|
"uuid": "...",
|
||||||
"real_name": "کاربر",
|
"name": "مطب دکتر علی احمدی",
|
||||||
"mobile_number": "09..."
|
"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_start": 1718438400,
|
||||||
"slot_end": 1718439600,
|
"slot_end": 1718439600,
|
||||||
"status": "confirmed",
|
"status": "confirmed",
|
||||||
"note": "...",
|
"note": "...",
|
||||||
"price": 500000,
|
"patient_name": "...",
|
||||||
"payment_uuid": "pay-uuid-...",
|
"patient_mobile": "...",
|
||||||
"created_at": 1717000000
|
"created_at": 1717000000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
> `doctor.specialties` آرایه (ممکن است خالی)؛ `address` اولین آدرس پزشک است (ممکن است `null` اگر پزشک آدرسی ندارد). `address.map.latitude/longitude` رشته یا `null`. تاریخها Unix.
|
||||||
|
|
||||||
### Errors
|
### Errors
|
||||||
| Code | HTTP | Description |
|
| Code | HTTP | Description |
|
||||||
|
|||||||
@@ -153,12 +153,19 @@ class Appointment
|
|||||||
|
|
||||||
public function toArray(): array
|
public function toArray(): array
|
||||||
{
|
{
|
||||||
|
$firstAddress = $this->doctor->getAddresses()->first() ?: null;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'uuid' => $this->uuid,
|
'uuid' => $this->uuid,
|
||||||
'doctor' => [
|
'doctor' => [
|
||||||
'uuid' => $this->doctor->getUuid(),
|
'uuid' => $this->doctor->getUuid(),
|
||||||
'name' => $this->doctor->getName(),
|
'name' => $this->doctor->getName(),
|
||||||
|
'specialties' => array_map(
|
||||||
|
fn($s) => ['uuid' => $s->getUuid(), 'name' => $s->getName()],
|
||||||
|
$this->doctor->getSpecialties()->toArray()
|
||||||
|
),
|
||||||
],
|
],
|
||||||
|
'address' => $firstAddress?->toArray(),
|
||||||
'user' => [
|
'user' => [
|
||||||
'uuid' => $this->user->getUuid(),
|
'uuid' => $this->user->getUuid(),
|
||||||
'mobile' => $this->user->getMobileNumber(),
|
'mobile' => $this->user->getMobileNumber(),
|
||||||
|
|||||||
Reference in New Issue
Block a user