diff --git a/docs/PROJECT.md b/docs/PROJECT.md new file mode 100644 index 00000000..68d92624 --- /dev/null +++ b/docs/PROJECT.md @@ -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 +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 | ۲۸ |