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