986 lines
31 KiB
Markdown
986 lines
31 KiB
Markdown
# 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 | ۲۸ |
|