- Updated the form schema in ClinicsPage to change the field name from 'phone' to 'telephone'. - Adjusted the input registration to reflect the new field name. - Added a new interface ClinicDetail to define detailed clinic information including phone and other attributes. - Modified the ClinicController to use null-safe access for province ID retrieval.
ClinicPro — مستندات کامل پروژه
تاریخ آخرین بهروزرسانی: ۱۴۰۵/۰۳/۲۰ | نسخه Symfony: 7.4 | PHP: ≥ 8.2
فهرست مطالب
- معرفی پروژه
- تکنولوژی Stack
- ساختار دایرکتوری
- راهاندازی محیط توسعه
- متغیرهای محیطی
- پایگاه داده — موجودیتها
- API Endpoints
- امنیت و احراز هویت
- پنل ادمین React
- Frontend Build
- دستورات Console
- Migrations
- سرویسهای خارجی
۱. معرفی پروژه
ClinicPro یک سیستم جامع مدیریت کلینیک و نوبتدهی پزشکی است که شامل:
- API Backend: Symfony 7.4 (REST API + JWT Auth)
- Admin Panel: React 19 + TypeScript (SPA داخل Symfony)
- ویژگیهای اصلی:
- مدیریت پزشکان، کلینیکها و کاربران
- سیستم نوبتدهی با مدیریت اسلات زمانی
- درگاه پرداخت (ملت و سپ)
- سیستم کیف پول و تسویهحساب
- نمایندگان (Representation) با کمیسیون
- احراز هویت OTP + رمز عبور
- مدیریت منشی با دسترسیهای دانهای
- ارسال پیامک (KaveNegar / Rangineh)
- امتیاز و نظرات پزشکان
- بلاگ
- مستندات API تعاملی (Swagger UI) — محافظتشده با HTTP Basic Auth
۲. تکنولوژی Stack
Backend
| لایه | تکنولوژی |
|---|---|
| Framework | Symfony 7.4 |
| PHP | >= 8.2 |
| ORM | Doctrine ORM 3.6 |
| Auth | JWT (lexik/jwt-authentication-bundle) |
| Queue | Symfony Messenger |
| Cache/Session | Redis |
| Database | MariaDB 11.8 |
| API Docs | NelmioApiDocBundle v5 + swagger-php v6 |
| Rate Limiting | Symfony Rate Limiter |
Frontend (Admin)
| لایه | تکنولوژی |
|---|---|
| Framework | React 19 + TypeScript |
| Routing | React Router DOM v7 |
| Styling | Tailwind CSS v4 |
| State (server) | TanStack Query v5 |
| State (client) | Zustand v5 |
| Forms | React Hook Form + Zod |
| Icons | Heroicons v2 |
| Toast | Sonner |
| Tables | TanStack Table v8 |
| Build | Webpack Encore (Symfony UX React) |
۳. ساختار دایرکتوری
clinic-pro-symfony/
├── assets/
│ ├── admin/ ← React Admin SPA
│ │ ├── index.tsx ← Entry point
│ │ ├── App.tsx ← Router + PrivateRoute
│ │ ├── styles.css ← Tailwind CSS v4
│ │ ├── components/
│ │ │ └── layout/
│ │ │ ├── AdminLayout.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ └── Topbar.tsx
│ │ ├── pages/ ← ۲۲ صفحه پیادهسازیشده
│ │ ├── stores/
│ │ │ ├── authStore.ts ← Zustand JWT store
│ │ │ └── uiStore.ts ← Sidebar state
│ │ ├── hooks/
│ │ └── lib/
│ ├── app.js ← Symfony main entry
│ ├── styles/app.css
│ └── react/controllers/ ← UX React controllers
│
├── config/
│ ├── packages/
│ │ ├── security.yaml ← Firewalls + ACL
│ │ ├── doctrine.yaml
│ │ ├── lexik_jwt_authentication.yaml
│ │ ├── messenger.yaml
│ │ ├── nelmio_api_doc.yaml ← Swagger config
│ │ ├── rate_limiter.yaml
│ │ └── webpack_encore.yaml
│ └── routes/
│ ├── security.yaml
│ └── nelmio_api_doc.yaml
│
├── migrations/ ← 17 Doctrine migrations
│
├── public/
│ ├── index.php
│ ├── build/ ← Webpack output
│ └── bundles/nelmioapidoc/ ← Swagger UI assets (local)
│
├── src/
│ ├── Admin/Controller/ ← AdminApiController + SPA catch-all
│ ├── Appointment/ ← نوبتدهی
│ ├── Auth/ ← احراز هویت
│ ├── Blog/ ← بلاگ
│ ├── Category/ ← دستهبندیها
│ ├── Clinic/ ← کلینیکها
│ ├── Doctor/ ← پزشکان
│ ├── Insurance/ ← بیمه
│ ├── Payment/ ← پرداخت
│ ├── Rating/ ← امتیاز و نظرات
│ ├── Representation/ ← نمایندگان
│ ├── Secretary/ ← منشیها
│ ├── Settlement/ ← تسویهحساب
│ ├── Shared/ ← زیرساخت مشترک
│ ├── Sms/ ← پیامک
│ ├── UserProfile/ ← پروفایل کاربر
│ └── Kernel.php
│
├── tests/
├── webpack.config.js
├── tsconfig.json
├── postcss.config.js
├── composer.json
├── package.json
└── .env
۴. راهاندازی محیط توسعه
پیشنیازها
- ddev نصب شده
- Docker Desktop
مراحل اجرا
# ۱. کلون پروژه
git clone <repo-url>
cd clinic-pro-symfony
# ۲. راهاندازی ddev
ddev start
# ۳. نصب PHP packages
ddev composer install
# ۴. ساخت JWT keys
ddev exec php bin/console lexik:jwt:generate-keypair
# ۵. اجرای migrations
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
# ۶. نصب npm packages
npm install --legacy-peer-deps
# ۷. Build frontend (dev)
npm run dev
# ۸. یا watch mode
npm run watch
دسترسی
| سرویس | آدرس | توضیح |
|---|---|---|
| Admin Panel | https://clinic-pro.ddev.site/admin | JWT auth |
| API | https://clinic-pro.ddev.site/api/v1 | REST API |
| Swagger UI | https://clinic-pro.ddev.site/api/doc | HTTP Basic Auth |
| Health Check | https://clinic-pro.ddev.site/health | عمومی |
اطلاعات ادمین پیشفرض
| فیلد | مقدار |
|---|---|
| موبایل | 09120671713 |
| رمز عبور | admin1234 |
| نقش | ROLE_ADMIN |
اطلاعات ورود به Swagger UI
| فیلد | مقدار پیشفرض |
|---|---|
| Username | admin |
| Password | clinic-pro-docs |
برای تغییر رمز:
ddev exec php -r "echo password_hash('رمز-جدید', PASSWORD_BCRYPT) . PHP_EOL;"— hash را در.envدرAPI_DOC_PASSWORDقرار دهید.
۵. متغیرهای محیطی
فایل .env — کلیدها (مقادیر حساس در .env.local تنظیم میشوند):
APP_ENV=dev|prod
APP_SECRET= # کلید امنیتی Symfony
DEFAULT_URI= # آدرس پایه سایت (مثلاً https://clinic-pro.ddev.site)
DATABASE_URL= # DSN پایگاه داده MariaDB
JWT_SECRET_KEY= # مسیر کلید خصوصی JWT
JWT_PUBLIC_KEY= # مسیر کلید عمومی JWT
JWT_PASSPHRASE= # رمز کلید JWT
CORS_ALLOW_ORIGIN= # آدرسهای مجاز CORS (regex)
MESSENGER_TRANSPORT_DSN= # DSN صف پیام (Redis)
REDIS_URL= # آدرس Redis
REFRESH_TOKEN_TTL=2592000 # عمر refresh token (ثانیه) = ۳۰ روز
OTP_TTL=1200 # عمر کد OTP (ثانیه) = ۲۰ دقیقه
# SMS
KAVENEGAR_API_KEY=
KAVENEGAR_SENDER=
RANGINEH_API_KEY=
RANGINEH_SENDER=
SMS_PROVIDER=kavenegar|rangineh # ارائهدهنده فعال
# File Upload
MAX_FILE_SIZE_BYTES=5242880 # حداکثر حجم فایل (۵ مگابایت)
UPLOAD_DIR= # مسیر ذخیره فایلها
# Payment
ALLOWED_FRONTEND_HOSTS= # هاستهای مجاز برای redirect پرداخت
MELLAT_TERMINAL_ID=
MELLAT_USERNAME=
MELLAT_PASSWORD=
SEP_TERMINAL_ID=
APP_BASE_URL= # آدرس پایه برای callback پرداخت
# API Documentation (Swagger UI)
API_DOC_USERNAME=admin # نام کاربری ورود به /api/doc
API_DOC_PASSWORD= # bcrypt hash رمز عبور
۶. پایگاه داده — موجودیتها
جداول پایگاه داده
| جدول | Entity | توضیح |
|---|---|---|
users |
Auth\Entity\User |
کاربران سیستم |
doctors |
Doctor\Entity\Doctor |
پروفایل پزشکان |
doctor_addresses |
Doctor\Entity\DoctorAddress |
آدرس مطب |
clinics |
Clinic\Entity\Clinic |
کلینیکها |
categories |
Category\Entity\Category |
دستهبندیها |
appointments |
Appointment\Entity\Appointment |
نوبتها |
weekly_schedules |
Appointment\Entity\WeeklySchedule |
برنامه هفتگی |
date_overrides |
Appointment\Entity\DateOverride |
تغییر برنامه خاص |
holidays |
Appointment\Entity\Holiday |
تعطیلات |
payments |
Payment\Entity\Payment |
پرداختها |
settlements |
Settlement\Entity\Settlement |
درخواست تسویه |
wallet_transactions |
Settlement\Entity\WalletTransaction |
تراکنش کیف پول |
representations |
Representation\Entity\Representation |
نمایندگان |
doctor_secretaries |
Secretary\Entity\DoctorSecretary |
منشیهای پزشک |
comments |
Rating\Entity\Comment |
نظرات |
rates |
Rating\Entity\Rate |
امتیازها |
likes |
Rating\Entity\Like |
لایک نظرات |
blogs |
Blog\Entity\Blog |
مقالات بلاگ |
sms_templates |
Sms\Entity\SmsTemplate |
قالبهای پیامک |
sms_logs |
Sms\Entity\SmsLog |
لاگ ارسال پیامک |
profiles |
UserProfile\Entity\UserProfile |
پروفایل پزشکی کاربر |
doctor_insurances |
Insurance\Entity\DoctorInsurance |
بیمههای پزشک |
جداول Join (ManyToMany)
| جدول | رابطه |
|---|---|
doctor_specialties |
Doctor ↔ Category (specialty) |
doctor_expertise |
Doctor ↔ Category (expertise) |
doctor_states |
Doctor ↔ Category (state) |
doctor_cities |
Doctor ↔ Category (city) |
clinic_doctors |
Clinic ↔ Doctor |
clinic_specialties |
Clinic ↔ Category |
clinic_services |
Clinic ↔ Category |
clinic_insurances |
Clinic ↔ Category |
User (users)
id INT PK AUTO_INCREMENT
uuid VARCHAR(36) UNIQUE
mobile_number VARCHAR(20) UNIQUE ← شناسه ورود
password_hash VARCHAR(255) nullable
email VARCHAR(100) nullable
real_name VARCHAR(100) nullable
roles JSON ← ['ROLE_USER', 'ROLE_ADMIN', ...]
status SMALLINT default:1 ← 1=active, 0=inactive
created_at INT (unix timestamp)
updated_at INT (unix timestamp)
نقشهای موجود: ROLE_USER | ROLE_ADMIN | ROLE_DOCTOR | ROLE_CLINIC | ROLE_REPRESENTATION | ROLE_SECRETARY
Doctor (doctors)
id INT PK
uuid VARCHAR(36) UNIQUE
user_id INT FK → users
name VARCHAR(255)
gender VARCHAR(10) nullable
medical_system_code VARCHAR(25) nullable
mobile_number VARCHAR(15) nullable
activity_time INT nullable ← مدت ویزیت (دقیقه)
degree VARCHAR(30) nullable ← general/specialist/subspecialist
info TEXT nullable
images JSON nullable
doctor_rate FLOAT default:3.5
doctor_rate_percentage FLOAT default:60.0 ← سهم پزشک از پرداخت (%)
active_doctor_appointment BOOL default:true
representation_id INT nullable
created_at / updated_at INT
Clinic (clinics)
id INT PK
uuid VARCHAR(36) UNIQUE
user_id INT FK → users
name VARCHAR(255) nullable
info TEXT nullable
address TEXT nullable
telephone VARCHAR(50) nullable
is_24_7 BOOL default:false
working_days VARCHAR(255) nullable
latitude/longitude FLOAT nullable
city_id INT nullable → categories (bundle='city')
state_id INT nullable → categories (bundle='state')
representation_id INT nullable
images_clinic JSON nullable
clinic_logo JSON nullable
Representation (representations)
id INT PK
uuid VARCHAR(36) UNIQUE
full_name VARCHAR(255)
mobile_number VARCHAR(20)
city_id INT nullable → categories (bundle='city') ← ID شهر (نه نام)
wallet_balance INT default:0
commission_rate FLOAT default:10.0
is_active BOOL default:true
created_at INT (unix timestamp)
updated_at INT (unix timestamp)
توجه:
city_idیک FK عددی به جدولcategories(bundle='city') است. نام شهر از طریق JOIN در API برگردانده میشود.
Appointment (appointments)
id INT PK
uuid VARCHAR(36) UNIQUE
doctor_id INT FK → doctors
user_id INT FK → users
slot_start INT (unix timestamp)
slot_end INT (unix timestamp)
status VARCHAR(30)
note VARCHAR(255) nullable
version INT (optimistic locking)
وضعیتهای نوبت:
waiting_for_payment → در انتظار پرداخت
reserved → رزرو شده
checked_in → ورود به مطب
waiting → در صف انتظار
in_progress → در حال ویزیت
visited → ویزیت شده
completed → تکمیل شده
cancelled_by_doctor → لغو توسط پزشک
cancelled_by_user → لغو توسط کاربر
auto_cancel_unpaid → لغو خودکار (پرداختنشده)
no_show → غیبت
Payment (payments)
id INT PK
uuid VARCHAR(36) UNIQUE
order_id VARCHAR(64) UNIQUE
user_id INT FK
appointment_id INT FK nullable
amount_rials INT
status VARCHAR(30) ← pending|success|failed|refunded
gateway VARCHAR(20) ← mellat|sep
type VARCHAR(30) ← appointment|subscription
gateway_token VARCHAR(255) nullable
reference_id VARCHAR(255) nullable
frontend_address VARCHAR(500) nullable
callback_ip VARCHAR(45) nullable
Category (categories)
id INT PK
uuid VARCHAR(36) UNIQUE
bundle VARCHAR(32) ← نوع دستهبندی
label VARCHAR(255) nullable
status SMALLINT default:1
parent_id INT nullable ← برای city → state
weight INT default:0
representation_id INT nullable
نوعهای bundle:
state ← استانها
city ← شهرها (parent_id = state.id)
specially_doctor ← تخصص پزشک
doctor_services ← خدمات پزشک
insurance_type ← نوع بیمه پایه
supplementary_insurance ← بیمه تکمیلی
tag ← تگ بلاگ
Settlement + Wallet
-- settlements
uuid, user_id, amount_rials, status(pending|approved|rejected|paid),
bank_account(JSON), admin_note, reviewed_by, reviewed_at
-- wallet_transactions
user_id, amount, type(credit|debit), balance_after, description
DoctorSecretary
uuid
doctor_id FK → doctors
secretary_id FK → users
permission JSON:
{
"appointments": { "view": true, "create": true, "cancel": false, "update_status": false },
"addresses": { "view": true, "create": false, "update": false, "delete": false },
"clinic_info": { "view": true, "update": false },
"insurances": { "view": true, "create": false, "update": false, "delete": false }
}
active BOOL
۷. API Endpoints
Base URL: https://clinic-pro.ddev.site
مستندات کامل تعاملی: https://clinic-pro.ddev.site/api/doc (نیاز به HTTP Basic Auth)
🔐 Auth (/api/v1/user/ & /oauth/)
| Method | Path | Auth | توضیح |
|---|---|---|---|
| POST | /api/v1/user/login |
Public | ورود با موبایل و رمز → JWT + refresh |
| POST | /api/v1/user/send-code |
Public | ارسال کد OTP به موبایل |
| POST | /api/v1/user/verify-code |
Public | تأیید کد OTP |
| POST | /api/v1/user/register |
Public | ثبتنام کاربر جدید |
| POST | /oauth/token |
Public | دریافت JWT (mobile grant) |
| POST | /oauth/token/refresh |
Public | تمدید JWT با refresh token |
| GET | /oauth/userinfo |
✅ | اطلاعات کاربر جاری |
| POST | /oauth/logout |
✅ | خروج و ابطال refresh token |
| GET | /session/token |
Public | CSRF session token |
درخواست Login:
POST /api/v1/user/login
{ "mobile_number": "09120671713", "password": "admin1234" }
پاسخ موفق:
{
"access_token": "eyJ...",
"refresh_token": "8e75...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token_expires_in": 2592000
}
🩺 Doctors
| Method | Path | Auth | توضیح |
|---|---|---|---|
| GET | /api/v1/doctors |
Public | لیست پزشکان (فیلتر + pagination) |
| GET | /api/v1/doctor/{uuid} |
Public | جزئیات پزشک |
| POST | /api/v1/doctor |
✅ | ایجاد پروفایل پزشک |
| PATCH | /api/v1/doctor/{uuid} |
✅ | بهروزرسانی پروفایل |
| DELETE | /api/v1/doctor/{uuid} |
ADMIN | حذف پزشک |
| POST | /file/upload/clinic_pro/doctor/field_image |
✅ | آپلود تصویر |
| GET | /api/v1/clinic-pro/doctor-addresses/{doctorId} |
Public | لیست آدرسها |
| POST | /api/v1/clinic-pro/doctor-address |
✅ | افزودن آدرس |
| PATCH | /api/v1/clinic-pro/doctor-address/{id} |
✅ | ویرایش آدرس |
| DELETE | /api/v1/clinic-pro/doctor-address/{id} |
✅ | حذف آدرس |
🏥 Clinics
| Method | Path | Auth | توضیح |
|---|---|---|---|
| GET | /api/v1/clinics |
Public | لیست کلینیکها |
| GET | /api/v1/clinic/{uuid} |
Public | جزئیات کلینیک |
| POST | /api/v1/clinic |
✅ | ایجاد کلینیک |
| PATCH | /api/v1/clinic/{uuid} |
✅ | ویرایش کلینیک |
| GET | /api/v1/clinic/doctor-list/{clinicUuid} |
Public | پزشکان کلینیک |
| POST | /file/upload/clinic_pro/clinic/field_image_clinic |
✅ | آپلود تصویر |
| POST | /file/upload/clinic_pro/clinic/field_clinic_logo |
✅ | آپلود لوگو |
📅 Appointments
| Method | Path | Auth | توضیح |
|---|---|---|---|
| GET | /api/v1/appointment-slots |
Public | اسلاتهای خالی پزشک |
| 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 |
✅ | نوبتهای کاربر |
تنظیمات نوبتدهی:
| Method | Path | توضیح |
|---|---|---|
| POST | /api/v1/appointment-settings/weekly-schedule |
برنامه هفتگی |
| POST | /api/v1/appointment-settings/date-override |
تغییر برنامه خاص |
| POST | /api/v1/appointment-settings/holidays |
ثبت تعطیلات |
💳 Payments
| Method | Path | Auth | توضیح |
|---|---|---|---|
| POST | /api/v1/payment/appointment |
✅ | شروع پرداخت نوبت |
| GET/POST | /api/v1/payment/callback/{gateway} |
Public | callback درگاه |
| GET | /api/v1/payment/{uuid} |
✅ | وضعیت پرداخت |
| POST | /api/v1/subscription-payment |
✅ | پرداخت اشتراک |
پارامتر {gateway}: mellat یا sep
🏦 Settlement & Wallet
| Method | Path | Auth | توضیح |
|---|---|---|---|
| GET | /api/v1/wallet/balance |
✅ | موجودی کیف پول |
| GET | /api/v1/wallet/transactions |
✅ | تاریخچه تراکنشها |
| POST | /api/v1/settlement |
✅ | درخواست تسویه |
| GET | /api/v1/settlement |
✅ | لیست درخواستها |
| GET | /api/v1/settlement/{uuid} |
✅ | جزئیات درخواست |
| POST | /api/v1/settlement/{uuid}/approve |
ADMIN | تأیید تسویه |
| POST | /api/v1/settlement/{uuid}/reject |
ADMIN | رد تسویه |
⭐ Ratings & Comments
| Method | Path | Auth | توضیح |
|---|---|---|---|
| POST | /api/v1/rate |
✅ | امتیاز به پزشک (۱-۵) |
| GET | /api/v1/rate/{doctorUuid} |
Public | میانگین امتیاز پزشک |
| POST | /api/v1/comment |
✅ | ثبت نظر |
| GET | /api/v1/comments/{doctorUuid} |
Public | نظرات تأییدشده |
| DELETE | /api/v1/comment/{uuid} |
✅ | حذف نظر |
| POST | /api/v1/like/{commentUuid} |
✅ | لایک/آنلایک نظر |
🤝 Representations (نمایندگان)
| Method | Path | Auth | توضیح |
|---|---|---|---|
| POST | /api/v1/representation |
ADMIN | ایجاد نماینده |
| GET | /api/v1/representation/{uuid} |
✅ | جزئیات نماینده |
| PATCH | /api/v1/representation/{uuid} |
✅ | ویرایش |
| DELETE | /api/v1/representation/{uuid} |
ADMIN | حذف |
| GET | /api/v1/representation/{uuid}/dashboard/monthly |
✅ | آمار ماهانه (شمسی) |
| GET | /api/v1/representation/{uuid}/dashboard/yearly |
✅ | آمار سالانه |
🔑 Secretaries (منشیها)
| Method | Path | Auth | توضیح |
|---|---|---|---|
| POST | /api/v1/secretary |
✅ | ایجاد منشی |
| GET | /api/v1/secretary/{uuid} |
✅ | جزئیات |
| PATCH | /api/v1/secretary/{uuid} |
✅ | ویرایش دسترسیها |
| DELETE | /api/v1/secretary/{uuid} |
✅ | حذف |
| GET | /api/v1/secretaries/{doctorUuid} |
✅ | لیست منشیهای پزشک |
📱 SMS
| Method | Path | Auth | توضیح |
|---|---|---|---|
| GET | /api/v1/admin/sms/templates |
ADMIN | لیست قالبها |
| POST | /api/v1/sms/template |
ADMIN | ایجاد قالب |
| PATCH | /api/v1/sms/template/{uuid} |
ADMIN | ویرایش قالب |
| POST | /api/v1/sms/template/{uuid}/submit |
ADMIN | ارسال برای تأیید |
| POST | /api/v1/admin/sms/template/{uuid}/approve |
ADMIN | تأیید قالب |
| POST | /api/v1/admin/sms/template/{uuid}/reject |
ADMIN | رد قالب |
| POST | /api/v1/sms/send |
ADMIN | ارسال پیامک مستقیم |
| POST | /api/v1/sms/send-template |
ADMIN | ارسال با قالب |
🗂 Categories
| Method | Path | Auth | توضیح |
|---|---|---|---|
| GET | /api/v1/categorys/{bundle} |
Public | لیست دستهبندی (?state_id=X) |
| POST | /api/v1/category |
ADMIN | ایجاد دستهبندی |
| PATCH | /api/v1/category/{id} |
ADMIN | ویرایش |
| DELETE | /api/v1/category/{id} |
ADMIN | حذف |
مقادیر {bundle}: state | city | specially_doctor | doctor_services | insurance_type | supplementary_insurance | tag
📝 Blog
| Method | Path | Auth | توضیح |
|---|---|---|---|
| GET | /api/v1/blogs |
Public | لیست مقالات منتشرشده |
| GET | /api/v1/blog/{slug} |
Public | مقاله با slug یا uuid |
| POST | /api/v1/blog |
ADMIN | ایجاد مقاله |
| PATCH | /api/v1/blog/{uuid} |
ADMIN | ویرایش |
| DELETE | /api/v1/blog/{uuid} |
ADMIN | حذف |
| POST | /file/upload/clinic_pro/blog/field_image |
ADMIN | آپلود تصویر مقاله |
🖥 Admin API (/api/v1/admin/)
همه endpointهای ادمین نیاز به ROLE_ADMIN دارند:
| Method | Path | توضیح |
|---|---|---|
| GET | /api/v1/admin/users |
لیست کاربران (?search=) |
| GET | /api/v1/admin/appointments |
لیست نوبتها |
| GET | /api/v1/admin/payments |
لیست پرداختها |
| GET | /api/v1/admin/representations |
لیست نمایندگان (?city_id=) |
| 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/settlements |
لیست تسویهحسابها |
| GET | /api/v1/admin/sms/sample-templates |
قالبهای نمونه پیامک |
| GET | /api/v1/admin/dashboard/stats |
آمار کلی داشبورد |
| GET | /api/v1/admin/dashboard/recent |
فعالیتهای اخیر |
❤️ Health
| Method | Path | توضیح |
|---|---|---|
| GET | /health |
بررسی وضعیت DB و Redis |
۸. امنیت و احراز هویت
جریان احراز هویت
[Client] → POST /api/v1/user/login
{ mobile_number, password }
↓ PasswordAuthenticator
[Symfony] → Rate Limit check (IP)
→ findByMobile()
→ verify password_hash
→ check isStaff()
↓ موفق
[Response] → {
access_token: "eyJ...", // JWT — عمر: ۱ ساعت
refresh_token: "8e75...", // در DB کش — عمر: ۳۰ روز
expires_in: 3600
}
JWT Token Payload
{
"iat": 1781031215,
"exp": 1781034815,
"roles": ["ROLE_ADMIN", "ROLE_USER"],
"username": "09120671713"
}
استفاده از Token
Authorization: Bearer eyJ...
Firewalls
dev → ^/(_profiler|_wdt|assets|build)/ — no security
health → ^/health$ — no security
api_doc → ^/api/doc — HTTP Basic Auth (InMemoryUser)
public_endpoints → مسیرهای عمومی API — no security
payment_callback → callback درگاه پرداخت — no security
api → ^/(api|oauth)/ — JWT authenticator
محافظت از Swagger UI
مسیر /api/doc از طریق یک firewall جداگانه با HTTP Basic Authentication محافظت میشود:
# config/packages/security.yaml
api_doc:
pattern: ^/api/doc
http_basic:
realm: "ClinicPro API Documentation"
provider: api_doc_provider
اطلاعات ورود از متغیرهای محیطی API_DOC_USERNAME و API_DOC_PASSWORD (bcrypt hash) خوانده میشود.
نقشها و دسترسیها
| نقش | دسترسی |
|---|---|
ROLE_USER |
کاربر عادی — رزرو نوبت، پرداخت |
ROLE_DOCTOR |
پزشک — مدیریت پروفایل و نوبتها |
ROLE_CLINIC |
مدیر کلینیک |
ROLE_REPRESENTATION |
نماینده — آمار و کیف پول |
ROLE_SECRETARY |
منشی — بر اساس permissions JSON |
ROLE_ADMIN |
ادمین کامل — تمام endpointها |
Rate Limiting
# config/packages/rate_limiter.yaml
login: 5 تلاش در دقیقه (per IP)
send_code: 3 درخواست در 5 دقیقه (per IP)
۹. پنل ادمین React
آدرس: https://clinic-pro.ddev.site/admin
نحوه کار
[Browser] GET /admin/**
↓
[Symfony] AdminController::index()
↓
[Twig] templates/admin/index.html.twig
↓ (HTML shell + Webpack assets)
[React] mounts در #admin-root
↓
[React Router] مسیریابی client-side
مسیرهای React (پیادهسازیشده)
| مسیر | صفحه | توضیح |
|---|---|---|
/admin/login |
LoginPage | ورود با JWT |
/admin/dashboard |
DashboardPage | آمار واقعی از API |
/admin/users |
UsersPage | لیست کاربران |
/admin/users/:uuid |
UserDetailPage | جزئیات کاربر |
/admin/doctors |
DoctorsPage | لیست پزشکان |
/admin/doctors/:uuid |
DoctorDetailPage | جزئیات پزشک |
/admin/clinics |
ClinicsPage | لیست کلینیکها |
/admin/clinics/:uuid |
ClinicDetailPage | جزئیات کلینیک |
/admin/appointments |
AppointmentsPage | لیست نوبتها |
/admin/appointments/:uuid |
AppointmentDetailPage | جزئیات نوبت |
/admin/payments |
PaymentsPage | لیست پرداختها |
/admin/payments/:uuid |
PaymentDetailPage | جزئیات پرداخت |
/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/blogs/:uuid/edit |
BlogFormPage | ویرایش مقاله |
/admin/secretaries |
SecretariesPage | مدیریت منشیها |
Auth Guard
// اگر کاربر لاگین نباشد → redirect به /admin/login
// اگر کاربر لاگین باشد و /admin/login باز کند → redirect به /admin/dashboard
JWT ذخیرهسازی
// Zustand + localStorage (با persist middleware)
// کلید: 'clinicpro-auth'
{
token: string | null, // access_token
refreshToken: string | null,
isAuthenticated: boolean
}
طراحی (Design System)
/* رنگ اصلی */
--color-primary-500: #8b5cf6; /* بنفش */
--color-bg-sidebar: #0f172a; /* dark navy */
--color-bg-body: #f1f5f9; /* خاکستری روشن */
/* فونت */
font-family: "Vazirmatn", "Inter", sans-serif;
direction: rtl;
۱۰. Frontend Build
دستورات npm
ddev exec yarn dev # build یکبار (dev)
ddev exec yarn watch # build + watch
ddev exec yarn build # build production (minified + hashed)
توجه: خطای
lightningcss.linux-arm64-gnu.nodeدر محیط ddev از پیش وجود دارد و JS/TS compilation را مسدود نمیکند.
فایلهای خروجی
public/build/
├── runtime.js ← webpack runtime
├── admin.js ← React Admin bundle
├── admin.css ← Tailwind CSS
├── app.js ← Symfony main bundle
├── vendors-*.js ← کتابخانههای مشترک
└── manifest.json ← نقشه فایلها
تنظیمات TypeScript (tsconfig.json)
{
"compilerOptions": {
"target": "ES2020",
"jsx": "react-jsx",
"strict": true,
"baseUrl": ".",
"paths": { "@/*": ["assets/admin/*"] }
}
}
۱۱. دستورات Console
دستورات کاربردی
# لغو خودکار نوبتهای منقضیشده
ddev exec php bin/console app:cancel-expired-appointments
# مشاهده همه routeها
ddev exec php bin/console debug:router | grep api
# پاک کردن کش
ddev exec php bin/console cache:clear
# اجرای migrations
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
# ساخت migration بعد از تغییر entity
ddev exec php bin/console doctrine:migrations:diff --no-interaction
# هش کردن پسورد
ddev exec php bin/console security:hash-password "رمز-جدید"
# تولید JWT key
ddev exec php bin/console lexik:jwt:generate-keypair
# بررسی صف پیام
ddev exec php bin/console messenger:consume async
# خروجی OpenAPI/Swagger
ddev exec php bin/console nelmio:apidoc:dump
۱۲. Migrations
تعداد: ۱۷ migration
# مشاهده وضعیت
ddev exec php bin/console doctrine:migrations:status
# اجرای migrations جدید
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
# ساخت migration جدید بعد از تغییر entity
ddev exec php bin/console doctrine:migrations:diff
۱۳. سرویسهای خارجی
درگاههای پرداخت
| درگاه | Provider | متغیرها |
|---|---|---|
| بانک ملت | MellatGateway |
MELLAT_TERMINAL_ID, MELLAT_USERNAME, MELLAT_PASSWORD |
| سامان (SEP) | SepGateway |
SEP_TERMINAL_ID |
SMS Providers
| ارائهدهنده | Provider Class | انتخاب |
|---|---|---|
| کاوهنگار | KavehNegarProvider |
SMS_PROVIDER=kavenegar |
| رنگینه | RanginehProvider |
SMS_PROVIDER=rangineh |
Redis
- صف پیام (Symfony Messenger)
- کش کدهای OTP
- کش Refresh Tokens
- Rate limiter storage
JWT Keys
config/jwt/private.pem ← کلید خصوصی (در .gitignore)
config/jwt/public.pem ← کلید عمومی (در .gitignore)
خلاصه آماری
| معیار | تعداد |
|---|---|
| Domain modules | ۱۵ |
| PHP Controllers | ۱۷ |
| API Endpoints | ۹۷ (مستندسازیشده در Swagger) |
| Doctrine Entities | ۲۲ |
| Database Tables | ۳۰ |
| Migrations | ۱۷ |
| React Pages | ۲۳ مسیر (کاملاً پیادهسازیشده) |
| OpenAPI Tags | ۱۱ گروه |
| npm packages | ۳۴ |
| composer packages | ۲۸ |