- 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.
6.5 KiB
6.5 KiB
معماری — تسک ۰۵: ماژول دکتر
ساختار فایلها
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)
#[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)
#[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)
{
"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