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.
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
# معماری — تسک ۰۵: ماژول دکتر
|
||||
|
||||
## ساختار فایلها
|
||||
```
|
||||
src/Module/Doctor/
|
||||
├── Controller/
|
||||
│ ├── DoctorController.php ← CRUD دکتر + لیست + جستجو
|
||||
│ ├── DoctorImageController.php ← آپلود تصویر
|
||||
│ └── DoctorAddressController.php ← CRUD آدرس مطب
|
||||
├── Service/
|
||||
│ ├── DoctorService.php
|
||||
│ └── DoctorAddressService.php
|
||||
├── Repository/
|
||||
│ ├── DoctorRepository.php
|
||||
│ └── DoctorAddressRepository.php
|
||||
├── Entity/
|
||||
│ ├── Doctor.php
|
||||
│ └── DoctorAddress.php
|
||||
├── DTO/
|
||||
│ ├── Request/
|
||||
│ │ ├── CreateDoctorRequest.php
|
||||
│ │ ├── UpdateDoctorRequest.php
|
||||
│ │ ├── DoctorFilterRequest.php
|
||||
│ │ ├── CreateDoctorAddressRequest.php
|
||||
│ │ └── UpdateDoctorAddressRequest.php
|
||||
│ └── Response/
|
||||
│ ├── DoctorResponse.php ← view کامل با آمار
|
||||
│ ├── DoctorListItemResponse.php ← لیست/جستجو
|
||||
│ └── DoctorAddressResponse.php
|
||||
└── Voter/
|
||||
└── DoctorVoter.php
|
||||
```
|
||||
|
||||
## Entity: Doctor (بر اساس فیلدهای واقعی Drupal)
|
||||
```php
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'doctors')]
|
||||
class Doctor
|
||||
{
|
||||
#[ORM\Id, ORM\GeneratedValue, ORM\Column]
|
||||
private int $id;
|
||||
|
||||
#[ORM\Column(type: UuidType::NAME, unique: true)]
|
||||
private Uuid $uuid;
|
||||
|
||||
#[ORM\OneToOne(targetEntity: User::class)]
|
||||
#[ORM\JoinColumn(nullable: false)]
|
||||
private User $user; // uid در Drupal
|
||||
|
||||
#[ORM\Column(length: 100)]
|
||||
private string $name; // field_name
|
||||
|
||||
#[ORM\Column(length: 10, nullable: true)]
|
||||
private ?string $gender; // field_gender
|
||||
|
||||
#[ORM\Column(length: 50)]
|
||||
private string $medicalSystemCode; // field_doctor_id (شماره نظام پزشکی)
|
||||
|
||||
#[ORM\Column(type: 'integer', nullable: true)]
|
||||
private ?int $activityTime; // field_activity_time (timestamp سال شروع)
|
||||
|
||||
#[ORM\Column(length: 100, nullable: true)]
|
||||
private ?string $degree; // field_degree
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
private ?string $info; // field_info
|
||||
|
||||
#[ORM\Column(length: 255, nullable: true)]
|
||||
private ?string $imagePath; // field_image (media)
|
||||
|
||||
#[ORM\Column(type: 'decimal', precision: 3, scale: 1, options: ['default' => 3.5])]
|
||||
private float $doctorRate = 3.5; // field_doctor_rate
|
||||
|
||||
#[ORM\Column(type: 'decimal', precision: 5, scale: 1, options: ['default' => 60])]
|
||||
private float $doctorRatePercentage = 60; // field_doctor_rate_percentage
|
||||
|
||||
#[ORM\Column(type: 'boolean', options: ['default' => true])]
|
||||
private bool $activeDoctorAppointment = true; // field_active_doctor_appointmen
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Representation::class)]
|
||||
#[ORM\JoinColumn(nullable: true)]
|
||||
private ?Representation $representation; // field_representation (multi-tenant)
|
||||
|
||||
// ManyToMany relations (field_specialty, field_doctor_services, field_state, field_city)
|
||||
#[ORM\ManyToMany(targetEntity: Category::class)]
|
||||
#[ORM\JoinTable(name: 'doctor_specialties')]
|
||||
private Collection $specialties; // field_specialty (چند مقداری)
|
||||
|
||||
#[ORM\ManyToMany(targetEntity: Category::class)]
|
||||
#[ORM\JoinTable(name: 'doctor_services')]
|
||||
private Collection $services; // field_doctor_services (چند مقداری)
|
||||
|
||||
#[ORM\ManyToMany(targetEntity: Category::class)]
|
||||
#[ORM\JoinTable(name: 'doctor_states')]
|
||||
private Collection $states; // field_state
|
||||
|
||||
#[ORM\ManyToMany(targetEntity: Category::class)]
|
||||
#[ORM\JoinTable(name: 'doctor_cities')]
|
||||
private Collection $cities; // field_city
|
||||
|
||||
#[ORM\OneToMany(targetEntity: DoctorAddress::class, mappedBy: 'doctor')]
|
||||
private Collection $addresses;
|
||||
|
||||
// TimestampableTrait
|
||||
}
|
||||
```
|
||||
|
||||
## Entity: DoctorAddress (بر اساس فیلدهای واقعی Drupal)
|
||||
```php
|
||||
#[ORM\Entity]
|
||||
#[ORM\Table(name: 'doctor_addresses')]
|
||||
class DoctorAddress
|
||||
{
|
||||
#[ORM\Id, ORM\GeneratedValue, ORM\Column]
|
||||
private int $id;
|
||||
|
||||
#[ORM\Column(type: UuidType::NAME, unique: true)]
|
||||
private Uuid $uuid;
|
||||
|
||||
#[ORM\ManyToOne(targetEntity: Doctor::class, inversedBy: 'addresses')]
|
||||
#[ORM\JoinColumn(nullable: false, onDelete: 'CASCADE')]
|
||||
private Doctor $doctor; // field_doctor
|
||||
|
||||
#[ORM\Column(length: 100, nullable: true)]
|
||||
private ?string $name; // field_name (نام مطب/شعبه)
|
||||
|
||||
#[ORM\Column(type: 'text', nullable: true)]
|
||||
private ?string $address; // field_address
|
||||
|
||||
#[ORM\Column(length: 20, nullable: true)]
|
||||
private ?string $telephone; // field_telephone (نه phone!)
|
||||
|
||||
#[ORM\Column(type: 'decimal', precision: 10, scale: 8, nullable: true)]
|
||||
private ?float $latitude; // field_latitude
|
||||
|
||||
#[ORM\Column(type: 'decimal', precision: 11, scale: 8, nullable: true)]
|
||||
private ?float $longitude; // field_longitude
|
||||
|
||||
// TimestampableTrait
|
||||
}
|
||||
```
|
||||
|
||||
## Response کامل دکتر (finalizeData)
|
||||
```json
|
||||
{
|
||||
"id": 5, "uuid": "...",
|
||||
"name": "دکتر محمدی",
|
||||
"gender": "male",
|
||||
"experience": 12,
|
||||
"activity_time": 1388534400,
|
||||
"medical_system_code": "12345",
|
||||
"detail": "متخصص قلب و عروق...",
|
||||
"degree": "دکترای تخصصی",
|
||||
"specialties": [{"id": 3, "uuid": "...", "name": "قلب و عروق"}],
|
||||
"img": ["https://..."],
|
||||
"expertise": [{"id": 10, "uuid": "...", "name": "اکوکاردیوگرافی"}],
|
||||
"satisfaction": 87.5,
|
||||
"point": 4.4,
|
||||
"free_turn": "اولین نوبت آزاد: دوشنبه ۱۲ اردیبهشت ساعت ۱۰:۳۰",
|
||||
"hours_of_work": ["شنبه", "یکشنبه", "دوشنبه"],
|
||||
"address": [...],
|
||||
"average_rate": {"average_stars": 4.3, "total_rates": 87},
|
||||
"state": [{"id": 2, "uuid": "...", "name": "فارس"}],
|
||||
"city": [{"id": 5, "uuid": "...", "name": "شیراز"}]
|
||||
}
|
||||
```
|
||||
|
||||
## منطق create دکتر (از DoctorService.php)
|
||||
۱. اگر `doctor_mobile_number` داده شد:
|
||||
- کاربر با این موبایل را پیدا کن
|
||||
- اگر نقش `doctor` ندارد → 403
|
||||
- اگر قبلاً profile دکتر دارد → 403
|
||||
- اگر کاربر وجود ندارد → کاربر جدید با نقش doctor بساز
|
||||
۲. اگر `doctor_mobile_number` نداده شد → کاربر جاری استفاده شود
|
||||
۳. اعتبارسنجی همه فیلدهای اجباری
|
||||
۴. ایجاد entity
|
||||
@@ -0,0 +1,160 @@
|
||||
# پایگاه داده — تسک ۰۵: ماژول دکتر
|
||||
|
||||
## جدول: doctors
|
||||
_(entity_type=clinic_pro, bundle=doctor — از DB backup و config تأیید شده)_
|
||||
|
||||
| ستون | نوع | نام Drupal | توضیح |
|
||||
|------|-----|-----------|-------|
|
||||
| id | INT UNSIGNED AUTO_INCREMENT PK | id | |
|
||||
| uuid | CHAR(36) UNIQUE NOT NULL | uuid | |
|
||||
| user_id | INT FK → users.id UNIQUE NOT NULL | uid | کاربر صاحب پروفایل |
|
||||
| name | VARCHAR(255) NOT NULL | field_name | نام دکتر |
|
||||
| gender | VARCHAR(10) NULL | field_gender | `woman` یا `man` |
|
||||
| medical_system_code | VARCHAR(25) NULL | field_doctor_id | شماره نظام پزشکی (max 25) |
|
||||
| mobile_number | VARCHAR(15) NULL | field_doctor_mobile_number | شماره موبایل دکتر (max 15) |
|
||||
| activity_time | INT NULL | field_activity_time | timestamp سال شروع فعالیت |
|
||||
| degree | VARCHAR(30) NULL | field_degree | مدرک (مقادیر زیر) |
|
||||
| info | LONGTEXT NULL | field_info | بیوگرافی |
|
||||
| image_path | VARCHAR(255) NULL | field_image | تصویر (media) |
|
||||
| doctor_rate | FLOAT NULL | field_doctor_rate | امتیاز ستارهای (default: 3.5) |
|
||||
| doctor_rate_percentage | FLOAT NULL | field_doctor_rate_percentage | درصد رضایت (default: 60) |
|
||||
| active_doctor_appointment | TINYINT(1) DEFAULT 1 | field_active_doctor_appointmen | نوبتدهی فعال (بدون حرف 't') |
|
||||
| representation_id | INT FK → representations.id NULL | field_representation | entity ref → clinic_pro |
|
||||
| created_at | INT NOT NULL | created | Unix timestamp |
|
||||
| updated_at | INT NOT NULL | changed | Unix timestamp |
|
||||
|
||||
## مقادیر field_degree (از config)
|
||||
```
|
||||
expert → کارشناس
|
||||
general → پزشک عمومی
|
||||
specialist → پزشک متخصص
|
||||
subspecialistplus → پزشک فوق تخصص
|
||||
```
|
||||
|
||||
## مقادیر field_gender (از config)
|
||||
```
|
||||
woman → زن
|
||||
man → مرد
|
||||
```
|
||||
|
||||
## جدول: doctor_specialties (ManyToMany pivot)
|
||||
| ستون | نوع | توضیح |
|
||||
|------|-----|-------|
|
||||
| doctor_id | INT FK → doctors.id | |
|
||||
| category_id | INT FK → categories.id | |
|
||||
| PRIMARY KEY (doctor_id, category_id) | | field_specialty → cardinality=2 (حداکثر ۲ تخصص) |
|
||||
|
||||
## جدول: doctor_services (ManyToMany pivot)
|
||||
| ستون | نوع | توضیح |
|
||||
|------|-----|-------|
|
||||
| doctor_id | INT FK → doctors.id | |
|
||||
| category_id | INT FK → categories.id | |
|
||||
| PRIMARY KEY (doctor_id, category_id) | | field_doctor_services → cardinality=8 (حداکثر ۸ سرویس) |
|
||||
|
||||
## جدول: doctor_states (ManyToMany pivot)
|
||||
| ستون | نوع | توضیح |
|
||||
|------|-----|-------|
|
||||
| doctor_id | INT FK → doctors.id | |
|
||||
| category_id | INT FK → categories.id | |
|
||||
| PRIMARY KEY (doctor_id, category_id) | | field_state → cardinality=1 |
|
||||
|
||||
## جدول: doctor_cities (ManyToMany pivot)
|
||||
| ستون | نوع | توضیح |
|
||||
|------|-----|-------|
|
||||
| doctor_id | INT FK → doctors.id | |
|
||||
| category_id | INT FK → categories.id | |
|
||||
| PRIMARY KEY (doctor_id, category_id) | | field_city → cardinality=1 |
|
||||
|
||||
## جدول: doctor_addresses
|
||||
_(entity_type=clinic_pro, bundle=doctors_addresses — از DB backup)_
|
||||
|
||||
| ستون | نوع | نام Drupal | توضیح |
|
||||
|------|-----|-----------|-------|
|
||||
| id | INT UNSIGNED AUTO_INCREMENT PK | id | |
|
||||
| uuid | CHAR(36) UNIQUE NOT NULL | uuid | |
|
||||
| doctor_id | INT FK → doctors.id CASCADE | field_doctor | دکتر |
|
||||
| name | VARCHAR(255) NULL | field_name | نام مطب/آدرس |
|
||||
| address | LONGTEXT NULL | field_address | آدرس کامل |
|
||||
| telephone | VARCHAR(50) NULL | field_telephone | تلفن مطب |
|
||||
| latitude | FLOAT NULL | field_latitude | عرض جغرافیایی |
|
||||
| longitude | FLOAT NULL | field_longitude | طول جغرافیایی |
|
||||
| created_at | INT NOT NULL | created | Unix timestamp |
|
||||
| updated_at | INT NOT NULL | changed | Unix timestamp |
|
||||
|
||||
## جدول: clinics
|
||||
_(entity_type=clinic_pro, bundle=clinic — از config تأیید شده)_
|
||||
|
||||
| ستون | نوع | نام Drupal | توضیح |
|
||||
|------|-----|-----------|-------|
|
||||
| id | INT UNSIGNED AUTO_INCREMENT PK | id | |
|
||||
| uuid | CHAR(36) UNIQUE NOT NULL | uuid | |
|
||||
| user_id | INT FK → users.id NOT NULL | uid | مالک |
|
||||
| name | VARCHAR(255) NULL | field_name | نام کلینیک |
|
||||
| info | LONGTEXT NULL | field_info | توضیحات |
|
||||
| address | LONGTEXT NULL | field_address | آدرس |
|
||||
| telephone | VARCHAR(50) NULL | field_telephone | تلفن |
|
||||
| is_24_7 | TINYINT(1) DEFAULT 0 | field_24_7 | باز ۲۴/۷ |
|
||||
| working_days | VARCHAR(255) NULL | field_working_days | روزهای کاری (متن آزاد، مثال: 'شنبه تا سه شنبه ساعت ۱۲:۲۰') |
|
||||
| latitude | FLOAT NULL | field_latitude | |
|
||||
| longitude | FLOAT NULL | field_longitude | |
|
||||
| city_id | INT FK → categories.id NULL | field_city | |
|
||||
| state_id | INT FK → categories.id NULL | field_state | |
|
||||
| representation_id | INT FK → representations.id NULL | field_agent | نماینده کلینیک |
|
||||
| created_at | INT NOT NULL | created | |
|
||||
| updated_at | INT NOT NULL | changed | |
|
||||
|
||||
جداول pivot مرتبط با clinic:
|
||||
- `clinic_doctors` (clinic_id, doctor_id) → field_doctors (cardinality نامحدود)
|
||||
- `clinic_specialties` (clinic_id, category_id) → field_clinic_specialty
|
||||
- `clinic_services` (clinic_id, category_id) → field_doctor_services
|
||||
- `clinic_insurances` (clinic_id, category_id) → field_insurance
|
||||
|
||||
## جدول: doctor_secretaries
|
||||
_(entity_type=clinic_pro, bundle=doctor_secretary — از config تأیید شده)_
|
||||
|
||||
| ستون | نوع | نام Drupal | توضیح |
|
||||
|------|-----|-----------|-------|
|
||||
| id | INT UNSIGNED AUTO_INCREMENT PK | id | |
|
||||
| uuid | CHAR(36) UNIQUE NOT NULL | uuid | |
|
||||
| user_id | INT FK → users.id NOT NULL | uid | |
|
||||
| doctor_id | INT FK → doctors.id NOT NULL | field_doctor | entity ref → clinic_pro/doctor |
|
||||
| secretary_id | INT FK → users.id NOT NULL | field_secretary | entity ref → user (منشی) |
|
||||
| telephone | VARCHAR(50) NULL | field_telephone | تلفن |
|
||||
| permission | LONGTEXT NULL | field_permission | مجوزها (JSON) |
|
||||
| active | TINYINT(1) DEFAULT 1 | field_active | فعال/غیرفعال |
|
||||
| created_at | INT NOT NULL | created | |
|
||||
| updated_at | INT NOT NULL | changed | |
|
||||
|
||||
## ایندکسها
|
||||
```sql
|
||||
CREATE UNIQUE INDEX idx_doctors_user ON doctors(user_id);
|
||||
CREATE INDEX idx_doctors_representation ON doctors(representation_id);
|
||||
CREATE INDEX idx_doctors_active ON doctors(active_doctor_appointment);
|
||||
CREATE INDEX idx_doctors_rate ON doctors(doctor_rate DESC);
|
||||
CREATE INDEX idx_doctor_addresses_doctor ON doctor_addresses(doctor_id);
|
||||
CREATE INDEX idx_clinics_user ON clinics(user_id);
|
||||
CREATE UNIQUE INDEX idx_doctor_secretaries_doctor_secretary ON doctor_secretaries(doctor_id, secretary_id);
|
||||
```
|
||||
|
||||
## نمونه داده واقعی از DB (clinic_pro table)
|
||||
```
|
||||
id=18, bundle='doctor'
|
||||
id=22, bundle='doctor'
|
||||
id=23, bundle='clinic'
|
||||
id=24, bundle='clinic'
|
||||
id=27, bundle='doctor_secretary'
|
||||
id=29, bundle='doctor'
|
||||
id=38, bundle='doctors_addresses'
|
||||
id=41, bundle='representation'
|
||||
```
|
||||
|
||||
## روابط کامل clinic_pro entity
|
||||
- `doctors` → `users` (ManyToOne, UNIQUE → OneToOne)
|
||||
- `doctors` → `representations` (ManyToOne)
|
||||
- `doctors` ↔ `categories` (ManyToMany: specialties, services, states, cities)
|
||||
- `doctor_addresses` → `doctors` (ManyToOne, CASCADE)
|
||||
- `clinics` → `users` (ManyToOne)
|
||||
- `clinics` ↔ `doctors` (ManyToMany)
|
||||
- `clinics` ↔ `categories` (ManyToMany)
|
||||
- `doctor_secretaries` → `doctors` (ManyToOne)
|
||||
- `doctor_secretaries` → `users` as secretary (ManyToOne)
|
||||
@@ -0,0 +1,64 @@
|
||||
# نکات پیادهسازی — تسک ۰۵: ماژول دکتر
|
||||
|
||||
## فیلتر لیست دکترها
|
||||
لیست دکترها باید فیلترهای زیر را پشتیبانی کند:
|
||||
```php
|
||||
// DoctorRepository.php
|
||||
public function findFiltered(DoctorFilterRequest $filter): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('d')
|
||||
->join('d.user', 'u');
|
||||
|
||||
if ($filter->specialty) {
|
||||
$qb->andWhere('d.specialty = :specialty')
|
||||
->setParameter('specialty', $filter->specialty);
|
||||
}
|
||||
if ($filter->city) {
|
||||
$qb->join('d.addresses', 'a')
|
||||
->andWhere('a.city = :city')
|
||||
->setParameter('city', $filter->city);
|
||||
}
|
||||
if ($filter->name) {
|
||||
$qb->andWhere('u.firstName LIKE :name OR u.lastName LIKE :name')
|
||||
->setParameter('name', '%'.$filter->name.'%');
|
||||
}
|
||||
if ($filter->insurance) {
|
||||
$qb->andWhere('JSON_CONTAINS(d.insurances, :ins) = 1')
|
||||
->setParameter('ins', json_encode([$filter->insurance]));
|
||||
}
|
||||
|
||||
return $qb->getQuery()->getResult();
|
||||
}
|
||||
```
|
||||
|
||||
## آپدیت میانگین امتیاز
|
||||
وقتی یک rating جدید ثبت میشود (تسک ۱۲)، average_rating را آپدیت کن:
|
||||
```php
|
||||
// در RatingService (تسک ۱۲)
|
||||
$this->em->createQuery(
|
||||
'UPDATE Doctor d SET d.averageRating = (
|
||||
SELECT AVG(r.score) FROM Rating r WHERE r.doctor = d
|
||||
), d.reviewCount = (
|
||||
SELECT COUNT(r.id) FROM Rating r WHERE r.doctor = d
|
||||
) WHERE d.id = :id'
|
||||
)->setParameter('id', $doctor->getId())->execute();
|
||||
```
|
||||
|
||||
## آدرس مطب
|
||||
- یک دکتر میتواند چندین آدرس مطب داشته باشد
|
||||
- `{doctorId}` در endpoint لیست آدرسها، UUID دکتر است (نه ID)
|
||||
- دکتر میتواند آدرسهای خودش را ویرایش/حذف کند
|
||||
|
||||
## مجوزها
|
||||
```
|
||||
POST /api/v1/doctor → ROLE_ADMIN
|
||||
PATCH /api/v1/doctor/{uuid} → owner (دکتر خودش) یا ROLE_ADMIN
|
||||
GET /api/v1/doctor/{uuid} → عمومی
|
||||
GET /api/v1/doctors → عمومی
|
||||
|
||||
POST doctor-address → دکتر احراز هویتشده (برای خودش)
|
||||
PATCH doctor-address/{id} → owner یا ROLE_ADMIN
|
||||
DELETE doctor-address/{id} → owner یا ROLE_ADMIN
|
||||
GET doctor-address/{id} → عمومی
|
||||
GET doctor-addresses/{doctorId} → عمومی
|
||||
```
|
||||
@@ -0,0 +1,178 @@
|
||||
# تسک ۰۵: ماژول دکتر
|
||||
|
||||
## توضیح
|
||||
پیادهسازی مدیریت پروفایل دکترها، لیست دکترها با فیلتر،
|
||||
آپلود تصویر پروفایل و مدیریت آدرسهای مطب.
|
||||
|
||||
## Endpoint ها (واقعی از Drupal)
|
||||
|
||||
| متد | مسیر | توضیح | نیاز به Auth |
|
||||
|-----|------|-------|-------------|
|
||||
| POST | `/api/v1/doctor` | ایجاد پروفایل دکتر | بله |
|
||||
| PATCH | `/api/v1/doctor/{uuid}` | ویرایش پروفایل دکتر | بله (Owner/Admin) |
|
||||
| DELETE | `/api/v1/doctor/{uuid}` | حذف دکتر | بله (Admin) |
|
||||
| GET | `/api/v1/doctor/{uuid}` | دریافت پروفایل کامل دکتر | خیر |
|
||||
| GET | `/api/v1/doctors` | لیست دکترها با فیلتر | خیر |
|
||||
| POST | `/file/upload/clinic_pro/doctor/field_image` | آپلود تصویر پروفایل دکتر | بله |
|
||||
| GET | `/api/v1/clinic/doctor-list/{clinic_uuid}` | لیست دکترهای یک کلینیک | خیر |
|
||||
| POST | `/api/v1/clinic-pro/doctor-address` | ایجاد آدرس مطب | بله |
|
||||
| PATCH | `/api/v1/clinic-pro/doctor-address/{id}` | ویرایش آدرس | بله |
|
||||
| DELETE | `/api/v1/clinic-pro/doctor-address/{id}` | حذف آدرس | بله |
|
||||
| GET | `/api/v1/clinic-pro/doctor-address/{id}` | دریافت یک آدرس | بله |
|
||||
| GET | `/api/v1/clinic-pro/doctor-addresses/{doctorId}` | لیست آدرسهای دکتر | خیر |
|
||||
|
||||
## پیشنیازها
|
||||
- تسک ۰۱، ۰۲، ۰۸ (Categories برای تخصص و سرویسها)
|
||||
|
||||
## زمان تخمینی
|
||||
۸ تا ۱۰ ساعت
|
||||
|
||||
---
|
||||
|
||||
## نمونه واقعی Response — GET /api/v1/doctor/{uuid}
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "29",
|
||||
"uuid": "61be915b-595a-42e5-bca5-f80d22f4f14a",
|
||||
"name": "single doctor",
|
||||
"gender": "woman",
|
||||
"experience": 21,
|
||||
"activity_time": "1107808200",
|
||||
"medical_system_code": "121212121212",
|
||||
"detail": "test",
|
||||
"degree": "specialist",
|
||||
"specialties": [
|
||||
{
|
||||
"uuid": "d60a269d-f7d7-4589-8eab-451e6f740d40",
|
||||
"id": "603",
|
||||
"name": "داخلی عمومی",
|
||||
"parent": "602"
|
||||
}
|
||||
],
|
||||
"img": [
|
||||
{
|
||||
"url": "https://domain.com/sites/default/files/doctors/2025-11/image.png",
|
||||
"fid": "98",
|
||||
"filename": "image.png",
|
||||
"filemime": "image/png",
|
||||
"filesize": 173665
|
||||
}
|
||||
],
|
||||
"expertise": [
|
||||
{ "uuid": "...", "id": "1601", "name": "معاینه و تشخیص پزشک متخصص" },
|
||||
{ "uuid": "...", "id": "1602", "name": "ویزیت تخصصی" }
|
||||
],
|
||||
"satisfaction": "60",
|
||||
"point": "3.5",
|
||||
"free_turn": "اولین نوبت آزاد: سهشنبه 19 خرداد ساعت 15:00",
|
||||
"hours_of_work": "از شنبه تا چهارشنبه از ساعت 08:00 تا 18:00",
|
||||
"address": [
|
||||
{
|
||||
"id": "39",
|
||||
"uuid": "7b759d2a-af8a-4730-8eb0-e77dcd3a724e",
|
||||
"name": "مطب اصلی",
|
||||
"map": { "latitude": "53.121212", "longitude": "57.121212" },
|
||||
"address": "آدرس کامل مطب",
|
||||
"telephone": "09120671756"
|
||||
}
|
||||
],
|
||||
"average_rate": { "total_rates": null },
|
||||
"state": [{ "uuid": "...", "id": "23", "name": "کهگیلویه و بویراحمد" }],
|
||||
"city": [{ "uuid": "...", "id": "123", "name": "یاسوج", "parent": "23" }]
|
||||
}
|
||||
```
|
||||
|
||||
> **توجه فیلدها:**
|
||||
> - `img` (نه `image` یا `images`!) — آرایه با url/fid/filename/filemime/filesize
|
||||
> - `specialties` → تخصص اصلی + والد
|
||||
> - `expertise` → همان doctor_services
|
||||
> - `satisfaction` و `point` به صورت **string** برگردانده میشوند
|
||||
> - `free_turn` → متن محاسبهشده از برنامه هفتگی (مثلاً "نوبت آزادی موجود نیست")
|
||||
> - `hours_of_work` → متن محاسبهشده از برنامه هفتگی
|
||||
> - `average_rate.total_rates` → میتواند null باشد
|
||||
> - `experience` → محاسبهشده از `activity_time` (Unix timestamp شروع فعالیت)
|
||||
|
||||
---
|
||||
|
||||
## فیلترهای GET /api/v1/doctors
|
||||
|
||||
| پارامتر | نوع | الزامی | مثال |
|
||||
|---------|-----|--------|------|
|
||||
| `state` | string | بله | `31` |
|
||||
| `city` | string | بله | `62` |
|
||||
| `specialty` | string | خیر | `503` |
|
||||
| `gender` | string | خیر | `man` یا `woman` |
|
||||
| `degree` | string | خیر | `general`, `specialist`, `expert`, `subspecialistplus` |
|
||||
| `name` | string | خیر | `علی` |
|
||||
| `active` | array | خیر | `0` یا `1` |
|
||||
| `page` | string | خیر | `1` |
|
||||
| `limit` | string | خیر | `10` |
|
||||
| `sort` | string | خیر | `ASC` یا `DESC` |
|
||||
|
||||
## نمونه Response لیست دکترها
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "29",
|
||||
"uuid": "...",
|
||||
"name": "single doctor",
|
||||
"gender": "woman",
|
||||
"degree": "specialist",
|
||||
"img": [{ "url": "...", "fid": "98", "filename": "image.png", "filemime": "image/png", "filesize": 173665 }],
|
||||
"specialties": [{ "uuid": "...", "id": "603", "name": "داخلی عمومی", "parent": "602" }],
|
||||
"satisfaction": "60",
|
||||
"point": "3.5",
|
||||
"free_turn": "اولین نوبت آزاد: سهشنبه 19 خرداد ساعت 15:00",
|
||||
"hours_of_work": "از شنبه تا چهارشنبه از ساعت 08:00 تا 18:00",
|
||||
"active": true
|
||||
}
|
||||
],
|
||||
"page": {
|
||||
"totalRecords": 7,
|
||||
"totalPages": 1,
|
||||
"currentPage": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## PATCH /api/v1/doctor/{uuid} — فیلدهای قابل ویرایش
|
||||
```json
|
||||
{
|
||||
"title": "نام دکتر",
|
||||
"doctor_services": ["اکوکاردیوگرافی", "ویزیت تخصصی"]
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## آپلود تصویر دکتر — POST /file/upload/clinic_pro/doctor/field_image
|
||||
```
|
||||
Headers:
|
||||
Content-Type: application/octet-stream
|
||||
Content-Disposition: file; filename="doctor.png"
|
||||
X-CSRF-Token: {token}
|
||||
Authorization: Bearer {token}
|
||||
Body: binary file content
|
||||
Response → { fid, uuid, ... } که در PATCH doctor استفاده میشود
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## نمونه واقعی Response — GET /api/v1/clinic-pro/doctor-address/{id}
|
||||
```json
|
||||
{
|
||||
"id": "39",
|
||||
"uuid": "7b759d2a-af8a-4730-8eb0-e77dcd3a724e",
|
||||
"name": "مطب اصلی",
|
||||
"map": {
|
||||
"latitude": "53.121212",
|
||||
"longitude": "57.121212"
|
||||
},
|
||||
"address": "آذربايجان غربي، مياندوآب، خيابان ۱۵ خرداد، برج ماندگار",
|
||||
"telephone": "09120671756"
|
||||
}
|
||||
```
|
||||
Reference in New Issue
Block a user