feat: add comprehensive project documentation in PROJECT.md

This commit is contained in:
hamed
2026-06-09 22:34:38 +03:30
parent c98d7b7968
commit e522c741b8
+985
View File
@@ -0,0 +1,985 @@
# ClinicPro — مستندات کامل پروژه
> **تاریخ آخرین به‌روزرسانی:** ۱۴۰۵/۰۳/۱۹ | **نسخه Symfony:** 7.4 | **PHP:** ≥ 8.2
---
## فهرست مطالب
1. [معرفی پروژه](#۱-معرفی-پروژه)
2. [تکنولوژی Stack](#۲-تکنولوژی-stack)
3. [ساختار دایرکتوری](#۳-ساختار-دایرکتوری)
4. [راه‌اندازی محیط توسعه](#۴-راه‌اندازی-محیط-توسعه)
5. [متغیرهای محیطی](#۵-متغیرهای-محیطی)
6. [پایگاه داده — موجودیت‌ها](#۶-پایگاه-داده--موجودیت‌ها)
7. [API Endpoints](#۷-api-endpoints)
8. [امنیت و احراز هویت](#۸-امنیت-و-احراز-هویت)
9. [پنل ادمین React](#۹-پنل-ادمین-react)
10. [Frontend Build](#۱۰-frontend-build)
11. [دستورات Console](#۱۱-دستورات-console)
12. [Migrations](#۱۲-migrations)
13. [سرویس‌های خارجی](#۱۳-سرویس‌های-خارجی)
---
## ۱. معرفی پروژه
**ClinicPro** یک سیستم جامع مدیریت کلینیک و نوبت‌دهی پزشکی است که شامل:
- **API Backend:** Symfony 7.4 (REST API + JWT Auth)
- **Admin Panel:** React 19 + TypeScript (SPA داخل Symfony)
- **ویژگی‌های اصلی:**
- مدیریت پزشکان، کلینیک‌ها و کاربران
- سیستم نوبت‌دهی با مدیریت اسلات زمانی
- درگاه پرداخت (ملت و سپ)
- سیستم کیف پول و تسویه‌حساب
- نمایندگان (Representation) با کمیسیون
- احراز هویت OTP + رمز عبور
- مدیریت منشی با دسترسی‌های دانه‌ای
- ارسال پیامک (KaveNegar / Rangineh)
- امتیاز و نظرات پزشکان
- بلاگ
---
## ۲. تکنولوژی 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 | MySQL |
| API Docs | NelmioApiDocBundle (Swagger) |
| 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/
│ │ │ ├── LoginPage.tsx
│ │ │ └── DashboardPage.tsx
│ │ ├── 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
│ │ ├── rate_limiter.yaml
│ │ └── webpack_encore.yaml
│ └── routes/
│ ├── security.yaml
│ └── nelmio_api_doc.yaml
├── migrations/ ← 16 Doctrine migrations
├── public/
│ ├── index.php
│ └── build/ ← Webpack output
├── src/
│ ├── Admin/Controller/ ← 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](https://ddev.readthedocs.io/) نصب شده
- Docker Desktop
### مراحل اجرا
```bash
# ۱. کلون پروژه
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 |
| API | https://clinic-pro.ddev.site/api/v1 |
| Swagger UI | https://clinic-pro.ddev.site/api/doc |
| Health Check | https://clinic-pro.ddev.site/health |
### اطلاعات ادمین پیش‌فرض
| فیلد | مقدار |
|------|-------|
| موبایل | `09120671713` |
| رمز عبور | `admin1234` |
| نقش | `ROLE_ADMIN` |
---
## ۵. متغیرهای محیطی
فایل `.env` — کلیدها (مقادیر در `.env.local` تنظیم می‌شوند):
```env
APP_ENV=dev|prod
APP_SECRET= # کلید امنیتی Symfony
APP_SHARE_DIR= # مسیر shared assets
DEFAULT_URI= # آدرس پایه سایت (مثلاً https://clinic-pro.ddev.site)
DATABASE_URL= # DSN پایگاه داده MySQL
JWT_SECRET_KEY= # مسیر کلید خصوصی JWT
JWT_PUBLIC_KEY= # مسیر کلید عمومی JWT
JWT_PASSPHRASE= # رمز کلید JWT
CORS_ALLOW_ORIGIN= # آدرس‌های مجاز CORS
MESSENGER_TRANSPORT_DSN= # DSN صف پیام (Redis)
REDIS_URL= # آدرس Redis
REFRESH_TOKEN_TTL=2592000 # عمر refresh token (ثانیه) = ۳۰ روز
OTP_TTL=1200 # عمر کد OTP (ثانیه) = ۲۰ دقیقه
# SMS - KaveNegar
KAVENEGAR_API_KEY=
KAVENEGAR_SENDER=
# SMS - Rangineh
RANGINEH_API_KEY=
RANGINEH_SENDER=
SMS_PROVIDER=kavenegar|rangineh # ارائه‌دهنده فعال
# File Upload
MAX_FILE_SIZE_BYTES=5242880 # حداکثر حجم فایل (۵ مگابایت)
UPLOAD_DIR= # مسیر ذخیره فایل‌ها
ALLOWED_FRONTEND_HOSTS= # هاست‌های مجاز برای redirect پرداخت
# Payment - Mellat Bank
MELLAT_TERMINAL_ID=
MELLAT_USERNAME=
MELLAT_PASSWORD=
# Payment - SEP (Saman)
SEP_TERMINAL_ID=
APP_BASE_URL= # آدرس پایه برای callback پرداخت
```
---
## ۶. پایگاه داده — موجودیت‌ها
### جداول پایگاه داده
| جدول | 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
state_id INT nullable → categories
representation_id INT nullable
images_clinic JSON nullable
clinic_logo JSON nullable
```
---
### 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)
specially_doctor ← تخصص پزشک
doctor_services ← خدمات پزشک
insurance_type ← نوع بیمه پایه
supplementary_insurance ← بیمه تکمیلی
tag ← تگ بلاگ
```
---
### Settlement + Wallet
```sql
-- 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`
---
### 🔐 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:**
```json
POST /api/v1/user/login
{ "mobile_number": "09120671713", "password": "admin1234" }
```
**پاسخ موفق:**
```json
{
"access_token": "eyJ...",
"refresh_token": "8e75...",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token_expires_in": 2592000
}
```
---
### 👥 Users
| Method | Path | Auth | توضیح |
|--------|------|------|-------|
| GET | `/oauth/userinfo` | ✅ | پروفایل کاربر جاری |
| DELETE | `/api/v1/user/{id}` | ADMIN | حذف کاربر |
---
### 🩺 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 (IP) | 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}` | ✅ | لایک/آنلایک نظر |
| GET | `/api/v1/admin/comments/pending` | ADMIN | نظرات در انتظار |
| POST | `/api/v1/admin/comment/{uuid}/approve` | ADMIN | تأیید نظر |
| POST | `/api/v1/admin/comment/{uuid}/reject` | ADMIN | رد نظر |
---
### 🤝 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/state` | Public | لیست استان‌ها |
| GET | `/api/v1/categorys/city` | Public | لیست شهرها (`?state_id=X`) |
| GET | `/api/v1/categorys/specially_doctor` | Public | تخصص‌های پزشکی |
| GET | `/api/v1/categorys/doctor_services` | Public | خدمات پزشکی |
| GET | `/api/v1/categorys/insurance_type` | Public | انواع بیمه پایه |
| GET | `/api/v1/categorys/supplementary_insurance` | Public | بیمه تکمیلی |
| GET | `/api/v1/categorys/tag` | Public | تگ‌های بلاگ |
| POST | `/api/v1/category` | ADMIN | ایجاد دسته‌بندی |
| PATCH | `/api/v1/category/{id}` | ADMIN | ویرایش |
| DELETE | `/api/v1/category/{id}` | ADMIN | حذف |
---
### 📝 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 | آپلود تصویر مقاله |
---
### ❤️ 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() (ROLE_ADMIN or ROLE_DOCTOR or ROLE_CLINIC)
↓ موفق
[Response] → {
access_token: "eyJ...", // ← JWT، عمر: 1 ساعت
refresh_token: "8e75...", // ← در DB کش، عمر: 30 روز
expires_in: 3600
}
```
### JWT Token Payload
```json
{
"iat": 1781031215,
"exp": 1781034815,
"roles": ["ROLE_ADMIN", "ROLE_USER"],
"username": "09120671713"
}
```
### استفاده از Token
```
Authorization: Bearer eyJ...
```
### Refresh Token
```
POST /oauth/token/refresh
{ "refresh_token": "8e75..." }
```
### Firewalls
```
dev → مسیرهای profiler/assets (no security)
health → /health (no security)
public_endpoints → مسیرهای عمومی API (no security)
payment_callback → callback درگاه (no security)
api → ^/(api|oauth)/ — JWT authenticator
```
### نقش‌ها و دسترسی‌ها
| نقش | دسترسی |
|-----|--------|
| `ROLE_USER` | کاربر عادی — رزرو نوبت، پرداخت |
| `ROLE_DOCTOR` | پزشک — مدیریت پروفایل و نوبت‌ها |
| `ROLE_CLINIC` | مدیر کلینیک |
| `ROLE_REPRESENTATION` | نماینده — آمار و کیف پول |
| `ROLE_SECRETARY` | منشی — بر اساس permissions JSON |
| `ROLE_ADMIN` | ادمین کامل — تمام endpoint‌ها |
### Rate Limiting
```yaml
# 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
| مسیر | صفحه | Auth |
|------|------|------|
| `/admin/login` | LoginPage | عمومی |
| `/admin/dashboard` | DashboardPage | ✅ |
| `/admin/users` | *(در حال توسعه)* | ✅ |
| `/admin/doctors` | *(در حال توسعه)* | ✅ |
| `/admin/clinics` | *(در حال توسعه)* | ✅ |
| `/admin/appointments` | *(در حال توسعه)* | ✅ |
| `/admin/payments` | *(در حال توسعه)* | ✅ |
| `/admin/settlements` | *(در حال توسعه)* | ✅ |
| `/admin/comments` | *(در حال توسعه)* | ✅ |
| `/admin/ratings` | *(در حال توسعه)* | ✅ |
| `/admin/sms` | *(در حال توسعه)* | ✅ |
| `/admin/categories` | *(در حال توسعه)* | ✅ |
| `/admin/blogs` | *(در حال توسعه)* | ✅ |
| `/admin/representations` | *(در حال توسعه)* | ✅ |
| `/admin/secretaries` | *(در حال توسعه)* | ✅ |
### Auth Guard
```typescript
// اگر کاربر لاگین نباشد → redirect به /admin/login
// اگر کاربر لاگین باشد و /admin/login باز کند → redirect به /admin/dashboard
```
### JWT ذخیره‌سازی
```typescript
// Zustand + localStorage (با persist middleware)
// کلید: 'clinicpro-auth'
{
token: string | null, // access_token
refreshToken: string | null,
isAuthenticated: boolean
}
```
### طراحی (Design System)
```css
/* رنگ اصلی */
--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
```bash
npm run dev # build یک‌بار (dev)
npm run watch # build + watch
npm run build # build production (minified + hashed)
npm run dev-server # webpack dev server (HMR)
```
### فایل‌های خروجی
```
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`)
```json
{
"compilerOptions": {
"target": "ES2020",
"jsx": "react-jsx",
"strict": true,
"baseUrl": ".",
"paths": { "@/*": ["assets/admin/*"] }
}
}
```
### تنظیمات PostCSS (`postcss.config.js`)
```js
module.exports = {
plugins: { '@tailwindcss/postcss': {} }
};
```
---
## ۱۱. دستورات Console
### دستور موجود
```bash
# لغو خودکار نوبت‌های منقضی‌شده
ddev exec php bin/console app:cancel-expired-appointments
# توضیح: نوبت‌هایی که slot_start آنها گذشته و هنوز pending هستند
# را به وضعیت auto_cancel_unpaid تغییر می‌دهد
# اجرا: از طریق cron job (مثلاً هر ۱۵ دقیقه)
```
### دستورات Symfony مفید
```bash
# مشاهده همه route‌ها
ddev exec php bin/console debug:router
# پاک کردن کش
ddev exec php bin/console cache:clear
# اجرای migrations
ddev exec php bin/console doctrine:migrations:migrate
# هش کردن پسورد
ddev exec php bin/console security:hash-password "your_password"
# تولید JWT key
ddev exec php bin/console lexik:jwt:generate-keypair
```
---
## ۱۲. Migrations
**تعداد:** ۱۶ migration
**آخرین نسخه:** `Version20260609140423`
```bash
# مشاهده وضعیت
ddev exec php bin/console doctrine:migrations:status
# اجرای migrations جدید
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
# ساخت migration جدید
ddev exec php bin/console doctrine:migrations:diff
```
---
## ۱۳. سرویس‌های خارجی
### درگاه‌های پرداخت
| درگاه | Provider | متغیرها |
|-------|----------|---------|
| بانک ملت | `MellatGateway` | `MELLAT_TERMINAL_ID`, `MELLAT_USERNAME`, `MELLAT_PASSWORD` |
| سامان (SEP) | `SepGateway` | `SEP_TERMINAL_ID` |
**IP‌های مجاز callback:** در `ALLOWED_FRONTEND_HOSTS` تنظیم می‌شود.
---
### SMS Providers
| ارائه‌دهنده | Provider Class | انتخاب |
|------------|---------------|--------|
| کاوه‌نگار | `KavehNegarProvider` | `SMS_PROVIDER=kavenegar` |
| رنگینه | `RanginehProvider` | `SMS_PROVIDER=rangineh` |
---
### Redis
- صف پیام (Symfony Messenger)
- کش OTP codes
- کش Refresh Tokens
- Rate limiter storage
---
### JWT Keys
```
config/jwt/private.pem ← کلید خصوصی (در .gitignore)
config/jwt/public.pem ← کلید عمومی (در .gitignore)
```
---
## خلاصه آماری
| معیار | تعداد |
|-------|-------|
| Domain modules | ۱۵ |
| PHP Controllers | ۱۷ |
| API Endpoints | ۱۳۰+ |
| Doctrine Entities | ۲۲ |
| Database Tables | ۳۰ |
| Migrations | ۱۶ |
| React Pages | ۲ (پیاده‌سازی‌شده) + ۱۳ (در حال توسعه) |
| npm packages | ۳۴ |
| composer packages | ۲۸ |