ClinicPro — مستندات کامل پروژه
آخرین بهروزرسانی: ۱۴۰۵/۰۳/۲۱ | Symfony: 7.x | PHP: ≥ 8.2 | React: 19
فهرست مطالب
- معرفی پروژه
- تکنولوژی Stack
- راهاندازی محیط توسعه
- آدرسهای مهم
- متغیرهای محیطی
- دستورات Console
- امنیت و احراز هویت
- پایگاه داده — جداول و موجودیتها
- API Endpoints
- پنل ادمین React
- Migrations
- سرویسهای خارجی
- ساختار دایرکتوری
۱. معرفی پروژه
ClinicPro سیستم جامع مدیریت کلینیک و نوبتدهی پزشکی است — مهاجرتیافته از Drupal به Symfony 7. شامل یک REST API کامل و یک پنل ادمین React SPA داخل Symfony.
ویژگیهای اصلی
- احراز هویت OTP + رمز عبور + JWT
- مدیریت پزشکان با پروفایل کامل، تخصص، آدرسهای مطب
- مدیریت کلینیکها با گالری، نقشه، دعوت پزشک
- سیستم نوبتدهی با اسلات هفتگی، روزهای استثناء، تعطیلات
- درگاه پرداخت (ملت + سپ) با کیف پول و تسویهحساب
- مدیریت نمایندگان (agents) با کمیسیون
- منشی با دسترسیهای دانهای (fine-grained permissions)
- سیستم دعوتنامه پزشک به کلینیک با توکن ۷۲ ساعته
- ارسال پیامک async از طریق KaveNegar / Rangineh
- امتیاز و نظرات پزشکان
- بلاگ با مدیریت کامل
- داشبورد آمار real-time
- Swagger UI محافظتشده با HTTP Basic Auth
۲. تکنولوژی Stack
Backend
| لایه |
تکنولوژی |
| Framework |
Symfony 7.x |
| PHP |
≥ 8.2 |
| ORM |
Doctrine ORM |
| Auth |
LexikJWT + OTP |
| Queue |
Symfony Messenger |
| Cache |
Redis |
| Database |
MariaDB 11.8 |
| API Docs |
NelmioApiDocBundle + swagger-php |
| Rate Limiting |
Symfony Rate Limiter |
Frontend (Admin SPA)
| لایه |
تکنولوژی |
| Framework |
React 19 + TypeScript |
| Routing |
React Router DOM v7 |
| Server State |
TanStack Query v5 |
| Client State |
Zustand v5 (persist) |
| Forms |
React Hook Form + Zod |
| Charts |
Recharts |
| Maps |
React Leaflet 5 |
| Icons |
Heroicons v2 |
| Notifications |
Sonner |
| Date |
jalaali-js |
| Build |
Webpack Encore 6 |
۳. راهاندازی محیط توسعه
پیشنیازها
مراحل
دستورات روزانه
۴. آدرسهای مهم
ورود به Swagger UI
آدرس: https://clinic-pro.ddev.site/api/doc
| فیلد |
مقدار |
| نام کاربری |
admin |
| رمز عبور |
clinic123 |
پس از ورود، برای احراز هویت API درون Swagger ابتدا از /api/v1/user/login توکن بگیرید، سپس دکمه Authorize را بزنید و Bearer <token> وارد کنید.
۵. متغیرهای محیطی
فایل .env (مقادیر پیشفرض توسعه):
۶. دستورات Console
۷. امنیت و احراز هویت
جریان احراز هویت
جریان Login با رمز عبور
نقشهای کاربری
| نقش |
توضیح |
دسترسی |
ROLE_USER |
پایه — همه کاربران |
نوبتگیری، پروفایل |
ROLE_ADMIN |
مدیر کل |
پنل ادمین کامل |
ROLE_CLINIC |
صاحب کلینیک |
مدیریت کلینیک خود |
ROLE_DOCTOR |
پزشک |
مدیریت نوبتها، پروفایل |
ROLE_SECRETARY |
منشی |
بر اساس permissions دکتر |
دسترسیهای منشی (JSON)
JWT
- مدت اعتبار access token: ۱ ساعت
- Refresh token: ۳۰ روز (ذخیره در Redis)
- پیلود:
{ username: mobile_number, roles: [...], iat, exp }
- تجدید:
POST /oauth/token/refresh
Firewall های security.yaml
| Firewall |
Pattern |
توضیح |
api_doc |
^/api/doc |
HTTP Basic Auth (admin / clinic123) |
public_endpoints |
OTP، OAuth، endpoint های عمومی |
بدون احراز هویت |
payment_callback |
/api/v1/payment/callback/ |
بدون احراز هویت |
api |
^/(api|oauth|file/) |
JWT stateless |
۸. پایگاه داده — جداول و موجودیتها
جداول اصلی
| جدول |
موجودیت |
توضیح |
users |
User |
uuid، mobile_number (unique)، roles (JSON)، status |
doctors |
Doctor |
user_id (OneToOne)، degree، doctor_rate، active_doctor_appointment |
doctor_addresses |
DoctorAddress |
doctor_id، name، latitude، longitude |
clinics |
Clinic |
user_id (owner)، name، telephone، is_24_7، latitude، longitude، images_clinic (JSON) |
clinic_doctor_invitations |
ClinicDoctorInvitation |
token (unique 96char hex)، status، expires_at (+72h)، mobile، uuid |
appointments |
Appointment |
doctor_id، user_id، slot_start، slot_end، status، price |
weekly_schedules |
WeeklySchedule |
doctor_id (OneToOne)، setting (JSON) |
date_overrides |
DateOverride |
doctor_id، date، active، setting (JSON) |
holidays |
Holiday |
doctor_id، start_date، end_date |
payments |
Payment |
order_id (unique)، amount_rials، status، gateway (mellat|sep) |
settlements |
Settlement |
user_id، amount_rials، status، bank_account (JSON) |
wallet_transactions |
WalletTransaction |
user_id، amount_rials، type (credit|debit)، balance_after |
representations |
Representation |
full_name، commission_percent، city_id |
doctor_secretaries |
DoctorSecretary |
doctor_id، secretary_id، permissions (JSON)، active |
rates |
Rate |
doctor_id، user_id، overall، diagnosis_accuracy، skill، behavior |
comments |
Comment |
doctor_id، user_id، body، status (approved|pending|rejected) |
likes |
Like |
comment_id، user_id |
blogs |
Blog |
author_id، title، slug (unique)، body، status |
sms_templates |
SmsTemplate |
name، body، provider_code، status |
sms_logs |
SmsLog |
mobile، message، provider، success |
profiles |
UserProfile |
blood_type، gender، insurance_id |
provinces |
Province |
name |
cities |
City |
name، province_id (FK) |
specialties |
Specialty |
name، parent_id (nullable برای زیر-تخصص) |
doctor_services |
DoctorService |
name |
insurances |
Insurance |
name، type (basic|supplementary) |
tags |
Tag |
name |
جداول رابطهای (ManyToMany)
| جدول |
رابطه |
clinic_doctors |
Clinic ↔ Doctor |
clinic_specialties |
Clinic ↔ Specialty |
clinic_services |
Clinic ↔ DoctorService |
clinic_insurances |
Clinic ↔ Insurance |
doctor_specialties |
Doctor ↔ Specialty |
doctor_expertise |
Doctor ↔ Category |
doctor_provinces |
Doctor ↔ Province |
وابستگیهای کلیدی
نکته timestamp
همه فیلدهای تاریخ (createdAt، updatedAt، slotStart، ...) به صورت integer Unix timestamp ذخیره میشوند — نه DateTime object.
۹. API Endpoints
احراز هویت
| متد |
آدرس |
توضیح |
دسترسی |
| POST |
/api/v1/user/send-code |
ارسال OTP |
عمومی |
| POST |
/api/v1/user/verify-code |
تأیید OTP |
عمومی |
| POST |
/api/v1/user/register |
ثبتنام |
عمومی |
| POST |
/api/v1/user/login |
لاگین با رمز عبور |
عمومی |
| POST |
/oauth/token |
دریافت JWT |
عمومی |
| POST |
/oauth/token/refresh |
تجدید توکن |
عمومی |
| GET |
/oauth/userinfo |
اطلاعات کاربر جاری |
احراز هویت |
| POST |
/oauth/logout |
خروج |
احراز هویت |
پزشکان
| متد |
آدرس |
توضیح |
دسترسی |
| GET |
/api/v1/doctors |
لیست پزشکان |
عمومی |
| GET |
/api/v1/doctor/{uuid} |
جزئیات پزشک |
عمومی |
| POST |
/api/v1/doctor |
ایجاد پروفایل |
احراز هویت |
| PATCH |
/api/v1/doctor/{uuid} |
ویرایش |
احراز هویت |
| DELETE |
/api/v1/doctor/{uuid} |
حذف |
احراز هویت |
| POST |
/file/upload/clinic_pro/doctor/field_image |
آپلود تصویر |
احراز هویت |
| GET |
/api/v1/clinic-pro/doctor-addresses/{doctorId} |
آدرسهای مطب |
عمومی |
| POST |
/api/v1/clinic-pro/doctor-address |
افزودن آدرس |
احراز هویت |
| PATCH |
/api/v1/clinic-pro/doctor-address/{id} |
ویرایش آدرس |
احراز هویت |
| DELETE |
/api/v1/clinic-pro/doctor-address/{id} |
حذف آدرس |
احراز هویت |
| POST |
/api/v1/clinic-pro/doctor-address/from-clinic/{clinicUuid} |
ایجاد از کلینیک |
احراز هویت |
کلینیکها
| متد |
آدرس |
توضیح |
دسترسی |
| GET |
/api/v1/clinics |
لیست کلینیکها |
عمومی |
| GET |
/api/v1/clinic/{uuid} |
جزئیات کلینیک |
عمومی |
| POST |
/api/v1/clinic |
ایجاد کلینیک |
احراز هویت |
| PATCH |
/api/v1/clinic/{uuid} |
ویرایش |
احراز هویت |
| GET |
/api/v1/clinic/doctor-list/{clinicUuid} |
پزشکان کلینیک |
عمومی |
| POST |
/file/upload/clinic_pro/clinic/field_clinic_logo |
آپلود لوگو |
احراز هویت |
| POST |
/file/upload/clinic_pro/clinic/field_image_clinic |
آپلود گالری |
احراز هویت |
دعوتنامه پزشک به کلینیک
| متد |
آدرس |
توضیح |
دسترسی |
| POST |
/api/v1/admin/clinic/{uuid}/invite-doctor |
ارسال دعوتنامه |
ROLE_ADMIN |
| GET |
/api/v1/admin/clinic/{uuid}/invitations |
لیست دعوتنامهها |
ROLE_ADMIN |
| POST |
/api/v1/admin/clinic/invitation/{invUuid}/resend |
ارسال مجدد پیامک |
ROLE_ADMIN |
| PATCH |
/api/v1/admin/clinic/invitation/{invUuid}/status |
تغییر وضعیت |
ROLE_ADMIN |
| DELETE |
/api/v1/admin/clinic/invitation/{invUuid} |
حذف |
ROLE_ADMIN |
| GET |
/api/v1/clinic-invitation/{token} |
مشاهده دعوتنامه |
عمومی |
| POST |
/api/v1/clinic-invitation/{token}/accept |
پذیرش توسط دکتر |
عمومی |
| POST |
/api/v1/clinic-invitation/{token}/reject |
رد کردن توسط دکتر |
عمومی |
توکن ۹۶ کاراکتر hex (bin2hex(random_bytes(48))) — مدت اعتبار ۷۲ ساعت. پیامک async از طریق Symfony Messenger ارسال میشود.
نوبتدهی
| متد |
آدرس |
توضیح |
دسترسی |
| GET |
/api/v1/appointment-slots |
اسلاتهای خالی |
عمومی |
| POST |
/api/v1/appointment |
رزرو نوبت |
احراز هویت |
| GET |
/api/v1/appointment/{uuid} |
جزئیات نوبت |
احراز هویت |
| PATCH |
/api/v1/appointment/{uuid}/status |
تغییر وضعیت |
احراز هویت |
| GET |
/api/v1/appointments/doctor/{doctorUuid} |
نوبتهای دکتر |
احراز هویت |
| GET |
/api/v1/appointments/user |
نوبتهای کاربر |
احراز هویت |
تنظیمات نوبتدهی
| متد |
آدرس |
توضیح |
| POST/PATCH/GET |
/api/v1/appointment-settings/weekly-schedule |
برنامه هفتگی |
| POST/PATCH/DELETE/GET |
/api/v1/appointment-settings/date-override |
روز استثناء |
| POST/PATCH/DELETE/GET |
/api/v1/appointment-settings/holidays |
تعطیلات |
پرداخت
| متد |
آدرس |
توضیح |
دسترسی |
| POST |
/api/v1/payment/appointment |
شروع پرداخت نوبت |
احراز هویت |
| POST/GET |
/api/v1/payment/callback/{gateway} |
callback (mellat|sep) |
عمومی |
| GET |
/api/v1/payment/{uuid} |
وضعیت پرداخت |
احراز هویت |
| POST |
/api/v1/subscription-payment |
پرداخت اشتراک |
احراز هویت |
کیف پول و تسویهحساب
| متد |
آدرس |
توضیح |
دسترسی |
| GET |
/api/v1/wallet/balance |
موجودی کیف پول |
احراز هویت |
| GET |
/api/v1/wallet/transactions |
تاریخچه تراکنشها |
احراز هویت |
| POST |
/api/v1/settlement |
درخواست تسویه |
احراز هویت |
| GET |
/api/v1/settlement |
لیست تسویههای خودم |
احراز هویت |
| POST |
/api/v1/settlement/{uuid}/approve |
تأیید |
ROLE_ADMIN |
| POST |
/api/v1/settlement/{uuid}/reject |
رد |
ROLE_ADMIN |
امتیاز و نظرات
| متد |
آدرس |
توضیح |
دسترسی |
| POST |
/api/v1/rate |
ثبت امتیاز |
احراز هویت |
| GET |
/api/v1/rate/{doctorUuid} |
میانگین امتیاز |
عمومی |
| POST |
/api/v1/comment |
ثبت نظر |
احراز هویت |
| GET |
/api/v1/comments/{doctorUuid} |
نظرات تأییدشده |
عمومی |
| DELETE |
/api/v1/comment/{uuid} |
حذف نظر |
احراز هویت |
| POST |
/api/v1/like/{commentUuid} |
لایک |
احراز هویت |
منشی
| متد |
آدرس |
توضیح |
دسترسی |
| POST |
/api/v1/secretary |
ایجاد منشی |
ROLE_DOCTOR |
| GET |
/api/v1/secretary/{uuid} |
جزئیات |
احراز هویت |
| PATCH |
/api/v1/secretary/{uuid} |
ویرایش permissions |
ROLE_DOCTOR |
| DELETE |
/api/v1/secretary/{uuid} |
حذف |
ROLE_DOCTOR |
| GET |
/api/v1/secretaries/{doctorUuid} |
لیست منشیهای دکتر |
احراز هویت |
نمایندگان
| متد |
آدرس |
توضیح |
دسترسی |
| POST/GET/PATCH/DELETE |
/api/v1/representation/{uuid?} |
مدیریت نماینده |
احراز هویت |
| GET |
/api/v1/representation/{uuid}/dashboard/monthly |
آمار ماهانه |
احراز هویت |
| GET |
/api/v1/representation/{uuid}/dashboard/yearly |
آمار سالانه |
احراز هویت |
پیامک
| متد |
آدرس |
توضیح |
دسترسی |
| POST |
/api/v1/sms/send |
ارسال مستقیم |
احراز هویت |
| POST |
/api/v1/sms/send-template |
ارسال با قالب |
احراز هویت |
| POST/PATCH/GET/DELETE |
/api/v1/sms/template/{uuid?} |
مدیریت قالب |
احراز هویت |
| POST |
/api/v1/sms/template/{uuid}/submit |
ارسال برای تأیید |
احراز هویت |
| POST |
/api/v1/admin/sms/template/{uuid}/approve |
تأیید قالب |
ROLE_ADMIN |
تخصص، خدمات، بیمه، تگ، موقعیت
بلاگ
| متد |
آدرس |
توضیح |
دسترسی |
| GET |
/api/v1/blogs |
لیست بلاگهای منتشرشده |
عمومی |
| GET |
/api/v1/blog/{slug} |
جزئیات بلاگ |
عمومی |
| POST |
/api/v1/blog |
نوشتن بلاگ |
احراز هویت |
| PATCH |
/api/v1/blog/{uuid} |
ویرایش |
احراز هویت |
| DELETE |
/api/v1/blog/{uuid} |
حذف |
احراز هویت |
| POST |
/file/upload/clinic_pro/blog/field_image |
آپلود تصویر |
احراز هویت |
پنل ادمین API
همه endpoint های زیر نیاز به ROLE_ADMIN دارند.
| آدرس |
توضیح |
GET /api/v1/admin/dashboard/stats |
۱۲ KPI (کاربران، پزشکان، نوبت، درآمد، ...) |
GET /api/v1/admin/dashboard/charts |
نمودار ۳۰ روزه نوبت و درآمد |
GET /api/v1/admin/dashboard/recent |
آخرین نوبتها، پرداختها، کاربران |
GET /api/v1/admin/users |
لیست کاربران (paginated) |
GET /api/v1/admin/users/{uuid} |
جزئیات کاربر |
PUT /api/v1/admin/users/{uuid}/role |
تغییر نقش |
POST /api/v1/admin/users/{uuid}/status |
فعال/غیرفعال |
DELETE /api/v1/admin/users/{uuid} |
حذف کاربر |
GET /api/v1/admin/doctors |
لیست پزشکان |
GET /api/v1/admin/clinics |
لیست کلینیکها |
PATCH /api/v1/admin/clinic/{uuid}/status |
تغییر وضعیت کلینیک |
DELETE /api/v1/admin/clinic/{uuid} |
حذف کلینیک |
GET /api/v1/admin/appointments |
لیست نوبتها |
GET /api/v1/admin/payments |
لیست پرداختها |
GET /api/v1/admin/settlements |
لیست تسویهها |
GET /api/v1/admin/representations |
لیست نمایندگان |
GET /api/v1/admin/secretaries |
لیست منشیها |
GET /api/v1/admin/rates |
لیست امتیازها |
GET /api/v1/admin/comments |
لیست نظرات |
GET /api/v1/admin/sms/logs |
لاگ پیامکها |
GET /api/v1/admin/sms/templates |
قالبهای پیامک |
فرمت پاسخهای API
نکته double-nested: برخی endpoint ها پاسخ را داخل data اضافه میکنند:
$this->success(['data' => $entity->toArray()]) → فرانت باید با data?.data?.data استخراج کند.
۱۰. پنل ادمین React
Route های پنل
| مسیر |
صفحه |
توضیح |
/admin/login |
LoginPage |
ورود با موبایل + رمز |
/admin/dashboard |
DashboardPage |
آمار KPI، نمودار، فعالیتهای اخیر |
/admin/users |
UsersPage |
لیست + جستجو + فیلتر نقش |
/admin/users/:uuid |
UserDetailPage |
پروفایل + ویرایش نقش |
/admin/doctors |
DoctorsPage |
لیست پزشکان |
/admin/doctors/new |
DoctorFormPage |
ایجاد پزشک جدید |
/admin/doctors/:uuid |
DoctorDetailPage |
پروفایل کامل + تخصص + نوبتها |
/admin/clinics |
ClinicsPage |
لیست کلینیکها |
/admin/clinics/:uuid |
ClinicDetailPage |
پروفایل + نقشه + گالری + دعوتنامهها |
/admin/appointments |
AppointmentsPage |
لیست نوبتها + فیلتر |
/admin/appointments/:uuid |
AppointmentDetailPage |
جزئیات نوبت |
/admin/payments |
PaymentsPage |
لیست پرداختها |
/admin/settlements |
SettlementsPage |
تسویهحساب |
/admin/representations |
RepresentationsPage |
نمایندگان |
/admin/representations/:uuid |
RepresentationDetailPage |
جزئیات نماینده |
/admin/comments |
CommentsPage |
مدیریت نظرات |
/admin/ratings |
RatingsPage |
امتیازها |
/admin/sms |
SmsPage |
مدیریت پیامک |
/admin/categories |
CategoriesPage |
تخصص/خدمات/بیمه/تگ |
/admin/blogs |
BlogsPage |
لیست بلاگها |
/admin/blogs/new |
BlogFormPage |
نوشتن بلاگ جدید |
/admin/secretaries |
SecretariesPage |
مدیریت منشیها |
الگوی دریافت داده
ویژگیهای فنی فرانتاند
- Auth Store (Zustand persist): توکن در
localStorage['clinicpro-auth'] → state.token
- Modal ها: از
createPortal(modal, document.body) استفاده میکنند تا از z-index Leaflet بگریزند
- CSS: کلاسهای template اختصاصی —
.card، .btn.primary/.ghost/.soft/.sm، .badge.green/.gray/.blue، .overlay، .seg، .skeleton، .empty
- Avatar: OKLCH gradient با
HUES_LIST = [256, 205, 162, 295, 272]
۱۱. Migrations
لیست migration های موجود
| فایل |
توضیح |
| Version20260609130407 |
جداول پایه: users, doctors, appointments |
| Version20260609131304 |
پرداخت، کیف پول |
| Version20260609131553 |
امتیاز، نظرات |
| Version20260609132009 |
نمایندگان |
| Version20260609132708 |
منشی |
| Version20260609133121 |
کلینیک |
| Version20260609133334 |
پیامک |
| Version20260609133546 |
بلاگ |
| Version20260609134112 |
تخصص، بیمه، تگ |
| Version20260609134741 |
استان، شهر |
| Version20260609135126 |
پروفایل، آدرس مطب |
| Version20260609135514 |
برنامه هفتگی |
| Version20260609135704 |
روز استثناء، تعطیلات |
| Version20260609135923 |
ManyToMany: clinic_doctors, clinic_specialties |
| Version20260609140223 |
ایندکسهای اضافی |
| Version20260609140423 |
doctor_insurances |
| Version20260610062539 |
فیلدهای اضافی clinic |
| Version20260610175105 |
فیلد is_active برای clinic |
| Version20260610183655 |
جدول clinic_doctor_invitations |
۱۲. سرویسهای خارجی
پیامک
KaveNegar (پیشفرض)
- API:
https://api.kavenegar.com/v1/{KEY}/sms/send.json
- پشتیبانی از: send (متن آزاد) + sendTemplate (قالب)
Rangineh
- API:
https://rest.payamresan.com/api/v1/send
- پشتیبانی از: send + sendTemplate
ارسال async:
درگاه پرداخت
بانک ملت (Mellat)
- SOAP WebService:
https://bpm.shaparak.ir/pgwchannel/services/pgw?wsdl
- تأیید: ResCode=0 + SettlePayment
سپ (SEP)
Redis
- صف پیام:
redis://redis:6379/messages
- Cache:
redis://redis:6379
- Refresh Token: کلید
refresh_<sha256>
۱۳. ساختار دایرکتوری
یادآور توسعه: تمام دستورات را با پیشوند ddev exec اجرا کنید.
CSS فرانتاند از کلاسهای template اختصاصی استفاده میکند (نه Tailwind).
بعد از هر تغییر backend: cache:clear
بعد از هر تغییر entity: migrations:diff سپس migrations:migrate