The first version of the seeder only filled the old skeleton: doctors, clinics, services, appointments written straight to the table. None of the sixteen tasks under docs/new_feture had any data, so nothing they built could be tried. BookingEngineSeeder now seeds, per environment: - catalog v2: a category tree, an item group with a 1..2 selection range, an incompatible pair, and a per-branch price override - resources: a skill with levels, a resource pool with priorities, and a maintenance window next week - a three-segment plan on the flagship service — numbing (room exclusive), wait (room passive, nobody else held), laser (room + device) — which is the whole point of the plan model and cannot be seen with single-segment services - an active price list, and a price snapshot per booked appointment - six policies, one per category, each with a condition and an effect - a package with a consumed session in the ledger, and a treatment protocol with per-session parameters plus an active course carrying its six sessions - a general and a per-service cancellation policy, no-show records, waitlist entries Two appointments per environment are booked through the real path — AppointmentPlanBuilder, AvailabilityEngine, HoldService, BookingService — so segments, resource occupancy and the domain-event outbox are populated by the code that will run in production rather than by INSERTs. Two defects in the seeding surfaced and are fixed here: - SegmentRequirement is the owning side, so persisting one leaves the template's in-memory collection empty. The plan built later in the same process saw segments that needed nothing, and the bookings occupied the device but never the room — while the same service was correct over HTTP, where the entity is read fresh. The collection is now kept in step. - passing the service as its own selected item produced a different plan than the booking flow builds. A course with no sessions also reported "everything is scheduled"; sessions are now created from the protocol steps. Verified over HTTP: segments come back as 5/30/20, utilization reports 110 minutes on the room and 90 on the laser, the course suggests session 1 with three slots, and the six policies list one per category. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
10 KiB
کاربران تستی
پنل ادمین: https://clinic-pro.ddev.site/admin — سایت عمومی: yasuj-nobat.localhost:3000
رمز عبور همهٔ حسابهای کارکنان:
QaTest@1234· کد OTP در dev همیشه12345
کل این محیط با یک دستور ساخته میشود:
ddev exec php bin/console app:seed-scenarios --reset -n
--reset دیتابیس را کامل خالی میکند، migrationها را از صفر میزند، دادهٔ پایه
(نمایندهها → دستهبندیها → کاتالوگ بیمه → خاموشکردن کپچا) را میسازد و بعد سه سناریو
را میچیند. بدون --reset فقط سناریوها ساخته میشوند و روی دیتابیسی که قبلاً seed شده
با خطا برمیگردد.
UUIDها با هر اجرا عوض میشوند. جدولهای زیر شکل داده را نشان میدهند نه شناسههای ثابت؛ برای گرفتن uuidهای فعلی از خود API یا دیتابیس بپرسید.
سناریوی ۱ — پزشک مستقل، نوبتدهی سرویسی
| نقش | موبایل | توضیح |
|---|---|---|
| پزشک | 0912000101 |
سارا مرادی — مطب شخصی، بدون کلینیک |
| منشی | 0912000109 |
منشیِ همان پزشک |
| بیماران | 09120001100 … 09120001104 |
۵ بیمار با پرونده |
- حالت نوبتدهی:
service· بافر ۱۰ دقیقه · شنبه تا چهارشنبه ۰۹:۰۰–۱۷:۰۰ - سرویسها: مشاوره پوست (۲۰د) · لیزر صورت (۳۰د، additional ۲۰) · تزریق ژل (۴۵د) · میکرونیدلینگ (۶۰د، additional ۴۰)
- بیمه: تامین اجتماعی، بیمه ایران
- نوبتها: ۹ نوبت در ۵ وضعیت (انجامشده، لغو کاربر، عدم حضور، تأییدشده، در انتظار پرداخت) که دو تای آنها امروز است
سناریوی ۲ — پزشکی که مالک کلینیک است
| نقش | موبایل | توضیح |
|---|---|---|
| پزشک + مالک کلینیک | 0912000201 |
امیر کاظمی — مالک «کلینیک تخصصی مهر»، خودش هم در کلینیک نوبت میدهد |
| پزشک عضو (سرویسی) | 0912000202 |
نگار سلطانی |
| پزشک عضو (اسلاتی) | 0912000203 |
بهرام فتحی |
| پزشک عضو (اسلاتی) | 0912000204 |
الهام قاسمی |
| منشی | 0912000209 |
روی هر ۴ پزشک |
| بیماران | 09120001200 … 09120001207 |
۸ بیمار |
- ترکیب حالتها: ۲ سرویسی + ۲ اسلاتی، همه شنبه تا چهارشنبه ۱۶:۰۰–۲۱:۰۰
- دستگاهها: ۲ لیزر (الکساندرایت، دایود) با setup/cleanup و تقویم کاری + ۲ اتاق درمان
- سرویسها: لیزر کامل بدن (۹۰د) · لیزر زیربغل (۲۰د) · ویزیت پوست (۱۵د) · هیدرودرم (۴۵د)
- وابستگی به دستگاه: دو سرویس لیزری هرکدام یک بخش (
SegmentTemplate) با نیازمندی انحصاری روی نوع منبع «دستگاه لیزر» دارند - بیمه: تامین اجتماعی، سلامت ایرانیان، بیمه آسیا
این حساب دو محیط کاری دارد (مطب شخصی + کلینیک). سرویسها و دستگاهها زیر محیط کلینیک اند، پس تا وقتی محیط عوض نشود لیستشان خالی است — این درست است، نه باگ:
POST /api/v1/auth/switch-context {"db_uuid": "<clinic_uuid>"}
سناریوی ۳ — کلینیک مستقل با مالکِ غیرپزشک
| نقش | موبایل | توضیح |
|---|---|---|
| مالک کلینیک | 0912000301 |
رضا شریفی — پزشک نیست، فقط ROLE_CLINIC |
| پزشک (اسلاتی) | 0912000302 |
پیمان اکبری |
| پزشک (سرویسی) | 0912000303 |
مینا یوسفی |
| پزشک (منبعمحور) | 0912000304 |
آرش نوری |
| منشی | 0912000309 |
روی هر ۳ پزشک |
| بیماران | 09120001300 … 09120001307 |
۸ بیمار |
- هر سه حالت نوبتدهی اینجا زندهاند:
slot،serviceوresource - ساعت کاری: شنبه تا پنجشنبه ۰۸:۰۰–۱۴:۰۰
- دستگاهها: ۳ لیزر (CO2 فرکشنال، NdYAG، دایود) + دستگاه RF + دستگاه کرایو
- سرویسها: ویزیت عمومی (۱۵د) · لیزر CO2 (۴۰د) · کرایوتراپی (۲۵د) · RF فرکشنال (۵۰د)
- بیمه: تامین اجتماعی، سلامت ایرانیان، بیمه دی
مالکِ غیرپزشک عمدی است: هر مسیری که فرض کند مالکِ کلینیک یک Doctor دارد، باید
همینجا بشکند نه در محیط واقعی.
موتور نوبتدهی جدید (تسکهای docs/new_feture)
هر سه محیط، علاوه بر دادههای بالا، دادهٔ زندهٔ موتور جدید هم دارند:
| تسک | چه چیزی seed میشود | کجا دیده میشود |
|---|---|---|
| ۰۱ شعبه و اتاق | DoctorAddress بهعنوان شعبه + منبع اتاق با تقویم |
GET /api/v1/resources |
| ۰۲ مدل منبع | نوع منبع، دستگاه، مهارت (کار با لیزر) با سطح، استخر منبع با اولویت |
GET /api/v1/resources · /skills |
| ۰۳ تقویم منبع | تقویم هفتگی هر دستگاه + یک استثنای تعمیرات هفتهٔ بعد | جستجوی آزاد آن بازه را رد میکند |
| ۰۴ کاتالوگ نسل دوم | دستهٔ درختی، گروه انتخاب با بازهٔ ۱ تا ۲، ناسازگاری دو سرویس، override شعبه، مدت solo/additional | GET /api/v1/service-items |
| ۰۵ برنامهٔ چندبخشی | سرویس اصلی هر محیط سه بخش دارد: بیحسی ۵د (اتاق انحصاری) → انتظار ۳۰د (اتاق passive) → لیزر ۲۰د (اتاق + دستگاه) | GET /api/v1/service-item/{uuid}/segments |
| ۰۶ موتور دسترسپذیری | همان برنامه با منابع واقعی جستجو میشود | POST /api/v1/appointment-availability |
| ۰۷ نگهداشتن و ثبت | ۲ نوبت در هر محیط از مسیر واقعی hold → confirm، با بخش و اشغال منبع | GET /api/v1/appointment/{uuid}/segments |
| ۰۸ عکس قیمت | لیست قیمت فعال + PriceSnapshot روی نوبتهای واقعی با بیعانه |
GET /api/v1/price-lists |
| ۰۹ موتور سیاست | ۶ سیاست، یکی از هر دسته (انتخاب، صلاحیت، منبع، زمان، فاصله، قیمت) | GET /api/v1/policies |
| ۱۱ پکیج و دفتر اعتبار | پکیج ۶ جلسهای + ۲ پکیج خریداریشده + ردیف مصرف در دفتر | GET /api/v1/packages |
| ۱۲ دورهٔ درمان | پروتکل ۶ جلسهای با پارامتر انرژی هر جلسه + دورهٔ فعال با ۶ جلسه | GET /api/v1/treatment-course/{uuid} |
| ۱۳ لغو و لیست انتظار | سیاست عمومی + سیاست سختگیرانه روی یک سرویس، رکورد عدم حضور، ۲ نفر در لیست انتظار | GET /api/v1/waitlist · /cancellation-policy |
| ۱۴ رویداد و بهرهوری | HoldCreated و AppointmentBooked در outbox + دقیقهٔ اشغال واقعی هر منبع |
GET /api/v1/reports/resource-utilization?branch_uuid=… |
نوبتهای موتور جدید از new Appointment(...) ساخته نمیشوند؛ از
AppointmentPlanBuilder → AvailabilityEngine → HoldService → BookingService رد
میشوند. برای همین اشغال منبع و رویداد دامنه واقعیاند: گزارش بهرهوری عدد نشان میدهد و
تقویم دستگاه واقعاً پر است.
چه چیزی با این داده قابل تست است
| قابلیت | کجا |
|---|---|
| نوبتدهی اسلاتی | 0912000203 · 0912000204 · 0912000302 |
| نوبتدهی سرویسی | 0912000101 · 0912000201 · 0912000202 · 0912000303 |
| نوبتدهی منبعمحور | 0912000304 (با تخصیص واقعی دستگاه) |
| مطب شخصی در برابر کلینیک | سناریوی ۲ (یک کاربر، دو محیط) |
| مالک پزشک در برابر مالک اداری | سناریوی ۲ در برابر سناریوی ۳ |
| دستگاه و وابستگی سرویس به دستگاه | سناریوی ۲ و ۳ |
| بیمه (پایه و تکمیلی، فرانشیز، سقف) | هر سه محیط |
| پرداخت | ۳۲ پرداخت موفق روی نوبتهای پرداختشده |
| پرونده بیمار | ۲۱ پرونده با کد ملی روی پروفایل |
نکتهها
- کپچا: روی دیتابیس تازه پیشفرضش روشن است و هیچکس نمیتواند وارد شود.
سیدر ردیف
site_config.altcha_enabled = '0'را میگذارد تا محیط قابل ورود بماند. - لاگین با رمز فقط برای کارکنان است. فیلد درخواست
mobile_numberاست نهmobile:POST /api/v1/user/login {"mobile_number": "...", "password": "..."}. بیماران (ROLE_USER) عمداً فقط با OTP وارد میشوند (send-code→verify-codeبا12345→otp-login). send-codeسقف ۵ درخواست در ساعت بهازای هر IP دارد.- نام پزشک بدون عنوان ذخیره میشود — «دکتر» را لایهٔ نمایش اضافه میکند
(
PersianText::stripDoctorTitle). - دادهٔ انبوه جداست:
app:seed-demo-dataحدود ۵۰۰ پزشک و ۲۰هزار نوبت با INSERT خام میسازد و برای تست کارایی و ماژول نمایندگان است، نه برای تست سناریویی.