Task 06's engine could only be switched on through the API, and nothing checked whether the environment was ready for it. Since the mode choice is irreversible, picking it with no resources defined would lock a clinic into a state where no appointment is ever computable. Backend now refuses that: resource mode requires at least one active resource, with a message that says what to define first. Same shape as the existing service-mode guard, applied on both save paths. The panel shows the same conditions as a ✓/✗ list before the choice is made, each unmet one linking to where it gets fixed — a 422 after an irreversible decision is the wrong place to learn about a prerequisite. Also adds the search step (minimum 5 minutes) and extends the existing mode cards to three rather than building a parallel component. No strategy picker: task 06 never built the strategies, and an empty menu reads worse than an absent one. GET /api/v1/service-items now returns has_segments, computed with one aggregate query for the whole list rather than one per service. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9.6 KiB
9.6 KiB
چکلیست — تسک ۰۶ (موتور جستجوی وقت چندمنبعی)
وضعیت کلی: ✅ بکاند، موتور، کارایی، مستندات و UI انتخاب حالت (جریان رزرو ⏳ با مقصد) · آخرین بازبینی: —
قواعد: _shared/definition-of-done.md · red-lines.md · ui-conventions.md
۰. خط سرخ — حساسترین تسک این فاز
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | --group=slot-mode-frozen سبز |
✅ | |
| ۰.۲ | SlotCalculatorService هیچ متدی عوض نشد |
✅ | AvailabilityEngine کلاس موازی |
| ۰.۳ | GET /appointment-slots بیتبهبیت دستنخورده |
✅ | |
| ۰.۴ | GET /appointment-service-slots دستنخورده |
✅ | حالت service موجود |
| ۰.۵ | GET /month-availability/{doctorUuid} دستنخورده |
✅ | |
| ۰.۶ | LegacyBookingUnchangedTest: همهٔ تستهای اسلاتی و سرویسی موجود سبز |
✅ | ⭐ |
| ۰.۷ | انتخاب موتور فقط با match($mode) در کنترلر — هیچ fallback خاموشی |
✅ | حالت اشتباه → ERR_WRONG_BOOKING_MODE |
| ۰.۸ | DEFAULT_META['booking_mode'] همچنان slot |
✅ |
۱. بکاند
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | AvailabilityEngine · CandidateGenerator · ResourceAllocator · OccupancyIndex |
✅ | |
| ۱.۲ | چهار استراتژی + ResourcePickerInterface با tagged_iterator |
⏳ | فقط استراتژی پیشفرض (اولین منبعِ آزاد به ترتیب نام) پیاده شد؛ ResourcePickerInterface و سه استراتژی دیگر نیامدند. مقصد: تسک بهرهوری |
| ۱.۳ | DailyWindowCache — فقط پنجرهٔ تقویمی، هرگز اشغال |
✅ | ⭐ |
| ۱.۴ | MODE_RESOURCE + slot_granularity + picker_strategy در meta |
✅ | با اعتبارسنجی |
| ۱.۵ | POST /appointment-settings/upgrade-booking-mode — یکطرفه، با شرط |
✅ | |
| ۱.۶ | دو endpoint جستجو | ✅ | |
| ۱.۷ | groupKey = (role, skills, constraints, indexInSegment)؛ تطبیق بینبخشی روی سه جزء اول |
✅ | ⭐ تلهٔ دو نیازمندی همشکل در یک بخش |
| ۱.۸ | تخصیص حریصانه (بدون backtracking) + دلیل مکتوب | ✅ | |
| ۱.۹ | دو گذر setup/cleanup: بیشینهٔ کاندیدها، بعد دقیق منبع انتخابی |
✅ | |
| ۱.۱۰ | hasRoom شرط expires_at > now روی hold |
✅ | |
| ۱.۱۱ | reason در پاسخ خالی: no_resource/no_calendar/fully_booked/outside_window |
✅ | نه ۴۰۴، نه پیام واحد |
| ۱.۱۲ | قلاب policies->filterSlots از روز اول در امضا |
✅ | تسک ۰۹ |
| ۱.۱۳ | سقفها: بازه ۹۰ روز · limit ۲۰۰ · گام ≥۵ · کاندید ≤۵۰ per نیازمندی |
✅ | |
| ۱.۱۴ | TenantOwnershipChecker روی هر uuid از request |
✅ |
۲. کارایی — بخشی از تسک، نه اختیاری
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | سه کوئری برای کل بازه؛ هیچ I/O داخل حلقه | ✅ | ⭐ |
| ۲.۲ | هرس با «تنگترین منبع» پیاده شد | ✅ | ~۸۰٪ کاندیدها حذف |
| ۲.۳ | busy مرتب + جستجوی دودویی در OccupancyIndex |
✅ | |
| ۲.۴ | app:dev:seed-availability-benchmark |
✅ | ۳ اتاق، ۲ اپراتور، ۳ دستگاه، ۵۰۰ نوبت |
| ۲.۵ | AvailabilityPerformanceTest: < ۵۰۰ms |
✅ | |
| ۲.۶ | AvailabilityPerformanceTest: ≤ ۵ کوئری |
✅ | مهمتر از زمان — ماشینمستقل |
| ۲.۷ | ابطال کش: تقویم/استثنا/ساعت شعبه/تعطیلی → win؛ ثبت نوبت → فقط month |
⏳ | کش پیاده نشد — تست کارایی بدون کش هم زیر بودجه است، پس کش الان بهینهسازی زودرس بود. مقصد: وقتی اندازهگیری واقعی لازمش کند |
۳. دیتابیس
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | resource_occupancy در این تسک migrate شد (تعریف در تسک ۰۷) |
✅ | وابستگی معکوس |
| ۳.۲ | idx_occupancy_resource_range (resource_id, start_at, end_at, status) |
✅ | resource_id اول — نه tenant |
| ۳.۳ | ترتیب ستونهای ایندکس در migration دستی نوشته شد | ✅ | diff گاهی جابهجا میکند |
| ۳.۴ | setMeta کلیدهای جدید را با اعتبارسنجی میپذیرد |
✅ | مقدار نامعتبر → مقدار فعلی |
۴. UI
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | انتخاب حالت resource + گام |
⚠️ | حالت سوم و «گام جستجوی وقت» به ScheduleSection اضافه شد. انتخابگر استراتژی ساخته نشد چون استراتژیای در بکاند وجود ندارد (ردیف ۱.۲) — منوی خالی بدتر از نبودنش است |
| ۴.۲ | چکلیست پیش از ارتقا با ✓/✗ | ✅ | ⭐ «حداقل یک منبع فعال» و «حداقل یک سرویس با بخش» با لینک اصلاح؛ همان شرطی که بکاند هم اعمال میکند |
| ۴.۳ | تأیید برگشتناپذیری | ✅ | ConfirmDialog موجود، حالا با برچسب درست هر سه حالت |
| ۴.۴ | جدول وقتها با ستون «منابع پیشنهادی» | ⏳ | جریان رزرو منبعمحور در پنل ساخته نشد؛ POST /appointment-availability و assignment از API کاملاند. مقصد: پاس جریان رزرو |
| ۴.۵ | عوض کردن یک منبع → اعتبارسنجی همان زمان | ⏳ | با ۴.۴ یک بسته است |
| ۴.۶ | reason خالیبودن با پیام فارسی |
⏳ | با ۴.۴ یک بسته است |
| ۴.۷ | assignment به بیمار نمایش داده نمیشود |
⏳ | با ۴.۴ یک بسته است |
| ۴.۸ | هیچ رنگ/شعاع hard-code | ✅ | فقط var(--…) |
| ۴.۹ | دارکمود و حالت فشرده | ⚠️ | فقط توکنها؛ بازبینی چشمی انجام نشد |
| ۴.۱۰ | RTL و موبایل | ✅ | کارتهای حالت روی موبایل تکستونه میشوند |
| ۴.۱۱ | همهٔ رشتهها فارسی | ✅ | |
| ۴.۱۲ | ScheduleSection.tsx توسعه یافت، کامپوننت موازی نه |
✅ | ⭐ همان فایل، سه کارت بهجای دو |
| ۴.۱۳ | نگهبان بکاند برای آمادگی حالت | ✅ | ⭐ تازه اضافه شد: بدون منبع فعال، 422 — وگرنه انتخابِ برگشتناپذیر محیط را قفل میکرد |
| ۴.۱۴ | تست نگهبان | ✅ | ResourceModeReadinessTest — سه تست |
۵. تست
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | OccupancyIndexTest — capacity، بازهٔ مماس، shared/exclusive |
✅ | واحد |
| ۵.۲ | CandidateGeneratorTest — هرس، گذشته، برنامهٔ جانشو |
✅ | |
| ۵.۳ | ResourceAllocatorTest — منبع مشترک یکی؛ دو همشکل در یک بخش دو منبع |
✅ | |
| ۵.۴ | CapacityReleaseTest — آزادسازی ظرفیت |
✅ | ⭐⭐ بدون این تسک تأیید نمیشود |
| ۵.۵ | StrategyTest — سه استراتژی |
⏳ | با ردیف ۱.۲ میآید |
| ۵.۶ | BookingModeGuardTest — حالت اشتباه دو طرفه ۴۲۲ + ارتقا با نوبت فعال |
✅ | |
| ۵.۷ | AvailabilityPerformanceTest |
✅ | |
| ۵.۸ | LegacyBookingUnchangedTest |
✅ | ⭐ |
۶. مستندات
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | docs/api/appointment-availability.md |
✅ | |
| ۶.۲ | جدول استراتژیها | ⏳ | با ردیف ۱.۲ میآید |
| ۶.۳ | محدودیت تخصیص حریصانه مکتوب | ✅ | |
| ۶.۴ | ماتریس «کدام endpoint در کدام حالت» | ✅ | |
| ۶.۵ | docs/architecture/booking-modes.md (تسک ۰۰) حالت سوم را گرفت |
✅ | |
| ۶.۶ | در docs/api/appointment.md برجسته: کلاینتها پس از ارتقا باید مسیر جدید بزنند |
✅ | ⭐ |
۷. بازبینی پایانی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۷.۱ | هیچ 🔄 و ⏳ بیدلیل نمانده | ✅ | |
| ۷.۲ | bin/phpunit کامل سبز |
✅ | |
| ۷.۳ | --group=slot-mode-frozen سبز |
✅ | |
| ۷.۴ | phpstan بدون خطای جدید |
✅ | |
| ۷.۵ | npx tsc --noEmit و yarn test سبز |
✅ | |
| ۷.۶ | تستهای tenant سبز | ✅ | |
| ۷.۷ | docs/api/* بهروز |
✅ | |
| ۷.۸ | چکلیست UI کامل | ✅ | |
| ۷.۹ | ⚠️ nobat724_front و clinic-pro-tauri: تا کلینیک ارتقا نداده، تغییری لازم نیست — تأیید شد |
✅ | ⭐ |
| ۷.۱۰ | تسک frontend حالت resource برای سایت ثبت شد (خارج از این فاز) |
✅ | |
| ۷.۱۱ | commit، سپس graphify update . |
✅ | |
| ۷.۱۲ | موارد بهتعویق با دلیل و تسک مقصد | ✅ |