# چک‌لیست — تسک ۰۶ (موتور جستجوی وقت چندمنبعی) **وضعیت کلی:** ✅ تمام‌شده — موتور، کارایی، مستندات، انتخاب حالت و جریان رزرو · **آخرین بازبینی:** — قواعد: [_shared/definition-of-done.md](../_shared/definition-of-done.md) · [red-lines.md](../_shared/red-lines.md) · [ui-conventions.md](../_shared/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` | ✅ | | | ۱.۲ | چهار استراتژی + `ResourcePicker` با tagged_iterator | ✅ | ⭐ `first_available` · `least_gap` · `least_loaded` · `same_as_previous`؛ استراتژی **مرتب می‌کند، انتخاب نمی‌کند** | | ۱.۳ | `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`؛ فهرست استراتژی‌ها از `GET /appointment-settings/resource-strategies` می‌آید، نه از فهرستی که در فرانت تکرار شود | | ۴.۲ | چک‌لیست پیش از ارتقا با ✓/✗ | ✅ | ⭐ «حداقل یک منبع فعال» و «حداقل یک سرویس با بخش» با لینک اصلاح؛ همان شرطی که بک‌اند هم اعمال می‌کند | | ۴.۳ | تأیید برگشت‌ناپذیری | ✅ | `ConfirmDialog` موجود، حالا با برچسب درست هر سه حالت | | ۴.۴ | جدول وقت‌ها با ستون «منابع پیشنهادی» | ✅ | `ResourceBookingPage` — تاریخ، ساعت، منابع، انتخاب | | ۴.۵ | عوض کردن یک منبع → گزینه‌های **همان زمان** | ✅ | ⭐ فهرست هر نقش فقط منابعی است که موتور برای همان زمان داده؛ فهرست کامل شعبه یعنی انتخابی که ۴۰۹ می‌گیرد | | ۴.۶ | `reason` خالی‌بودن با پیام فارسی | ✅ | `REASON_LABELS` — «بازه را بزرگ‌تر کنید یا شعبهٔ دیگری را امتحان کنید» | | ۴.۷ | `assignment` به بیمار نمایش داده نمی‌شود | ✅ | صفحه پنل‌محور است و همان‌جا هم نوشته شده | | ۴.۸ | هیچ رنگ/شعاع hard-code | ✅ | فقط `var(--…)` | | ۴.۹ | دارک‌مود و حالت فشرده | ⚠️ | فقط توکن‌ها؛ بازبینی چشمی انجام نشد | | ۴.۱۰ | RTL و موبایل | ✅ | کارت‌های حالت روی موبایل تک‌ستونه می‌شوند | | ۴.۱۱ | همهٔ رشته‌ها فارسی | ✅ | | | ۴.۱۲ | `ScheduleSection.tsx` توسعه یافت، کامپوننت موازی نه | ✅ | ⭐ همان فایل، سه کارت به‌جای دو | | ۴.۱۳ | نگهبان بک‌اند برای آمادگی حالت | ✅ | ⭐ تازه اضافه شد: بدون منبع فعال، `422` — وگرنه انتخابِ برگشت‌ناپذیر محیط را قفل می‌کرد | | ۴.۱۴ | تست نگهبان | ✅ | `ResourceModeReadinessTest` — سه تست | ## ۵. تست | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۵.۱ | `OccupancyIndexTest` — capacity، بازهٔ مماس، shared/exclusive | ✅ | واحد | | ۵.۲ | `CandidateGeneratorTest` — هرس، گذشته، برنامهٔ جا‌نشو | ✅ | | | ۵.۳ | `ResourceAllocatorTest` — منبع مشترک یکی؛ دو هم‌شکل در یک بخش دو منبع | ✅ | | | ۵.۴ | `CapacityReleaseTest` — **آزادسازی ظرفیت** | ✅ | ⭐⭐ بدون این تسک تأیید نمی‌شود | | ۵.۵ | تست استراتژی‌ها | ✅ | `ResourcePickerTest` — ۹ تست: ترتیب هر چهار استراتژی، رجیستری، بازگشت به پیش‌فرض روی کلید ناشناخته، رد شدن هنگام ذخیره | | ۵.۶ | `BookingModeGuardTest` — حالت اشتباه دو طرفه ۴۲۲ + ارتقا با نوبت فعال | ✅ | | | ۵.۷ | `AvailabilityPerformanceTest` | ✅ | | | ۵.۸ | `LegacyBookingUnchangedTest` | ✅ | ⭐ | ## ۶. مستندات | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۶.۱ | `docs/api/appointment-availability.md` | ✅ | | | ۶.۲ | جدول استراتژی‌ها | ✅ | در `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 .` | ✅ | | | ۷.۱۲ | موارد به‌تعویق با دلیل و تسک مقصد | ✅ | |