Files
clinicpro/docs/PROJECT.md
T

986 lines
31 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 | ۲۸ |