feat: implement cancellation policy, no-show tracking, and waitlist management

- Add implementation notes for cancellation and waitlist features.
- Create task documentation outlining goals, current status, and acceptance criteria for cancellation policy and resource utilization reporting.
- Establish architecture for domain events and outbox pattern to ensure reliable event publishing.
- Define database schema for domain events and necessary queries for resource utilization and plan accuracy reports.
- Implement detailed implementation notes covering edge cases, testing strategies, and documentation requirements.
This commit is contained in:
hamed
2026-07-30 11:43:58 +03:30
parent 1d338503c8
commit 021d0eb6b2
62 changed files with 8098 additions and 0 deletions
@@ -0,0 +1,78 @@
# تسک ۰۶ — موتور جستجوی وقت چندمنبعی
**فاز:** ۱ (هسته) · **وابستگی:** ۰۳، ۰۵ · **زمان:** ۲۰-۲۴ ساعت
---
## هدف
مستند بند ۱۰: برنامهٔ نوبت (تسک ۰۵) را روی تقویم منابع (تسک ۰۳) بلغزان و بگو چه
ساعت‌هایی واقعاً ممکن‌اند — با پیشنهاد اینکه کدام منبع استفاده شود.
**هدف کارایی: جستجوی یک ماهه زیر نیم ثانیه.**
## وضعیت فعلی
```php
// SlotCalculatorService::getServiceStartTimes() — تک‌منبعی، یک بلوک پیوسته
$busy = $this->appointmentRepo->findBusyIntervals($doctor, $dayStart, $dayStart + 86400);
while ($t + $durSec <= $winEnd) {
$conflict = $this->firstOverlap($t, $t + $needSec, $busy);
}
```
فقط تداخل **پزشک** بررسی می‌شود. اتاق، دستگاه و اپراتور اصلاً وجود ندارند.
## دامنه
**هست:**
- `AvailabilityEngine` — ورودی: برنامهٔ نوبت + بازهٔ تاریخ + شعبه؛ خروجی: وقت‌های معتبر
همراه با تخصیص منبع پیشنهادی
- تولید نقطه‌های شروع کاندید (پیش‌فرض هر ۱۵ دقیقه، قابل تنظیم per محیط)
- هرس زودهنگام کاندیدهای قطعاً ناممکن
- تخصیص منبع: تطبیق نیازمندی‌های هر بخش به منابع آزاد
- استراتژی انتخاب منبع: `least_gap` (پیش‌فرض) · `balanced` · `preserve_specialists` · `same_as_previous`
- کش روزانهٔ پنجرهٔ آزاد هر منبع
- endpoint عمومی و پنلی
- حالت `booking_mode = resource` روی `WeeklySchedule` و مسیر ارتقای داوطلبانه
**نیست:** ثبت اشغال و رزرو موقت (تسک ۰۷)، قوانین فاصلهٔ زمانی (تسک ۰۹ — قلاب اینجا گذاشته می‌شود).
## Endpoint ها
| متد | مسیر | توضیح |
|---|---|---|
| POST | `/api/v1/appointment-availability` | جستجوی وقت با برنامه (بدنه: سرویس، آیتم‌ها، شعبه، بازهٔ تاریخ) |
| GET | `/api/v1/appointment-availability/month` | روزهای دارای ظرفیت در یک ماه (سبک — فقط بولین per روز) |
## معیار پذیرش
- ✅ موفق: سناریوی مستند — سرویس لیزر با چهار بخش، شعبه با ۳ اتاق / ۲ اپراتور / ۳ دستگاه.
جستجوی یک روز → لیست زمان‌های شروع، و برای هر زمان `assignment` شامل اتاق، اپراتور و
دستگاه انتخابی.
- ✅ موفق (**آزادسازی ظرفیت — قلب کل پروژه**): بیمار الف نوبت ۱۰:۰۰-۱۱:۰۰ دارد
(اپراتور فقط ۱۰:۰۰-۱۰:۰۵ و ۱۰:۳۵-۱۱:۰۰ درگیر است). جستجو برای بیمار ب باید زمانی
در بازهٔ ۱۰:۰۵-۱۰:۳۵ پیدا کند اگر اتاق دومی آزاد باشد.
**تست بدون این سناریو، تسک را تأیید نمی‌کند.**
- ✅ موفق: کارایی — جستجوی ۳۰ روزه با ۲۰ منبع و ۵۰۰ نوبت ثبت‌شده، **زیر ۵۰۰ms**.
تست کارایی بخشی از تسک است، نه اختیاری.
- ✅ موفق: پزشکی که در حالت `slot` یا `service` است → این endpoint `422` با
`ERR_WRONG_BOOKING_MODE` می‌دهد و مسیر قدیمی دست‌نخورده کار می‌کند.
- ❌ خطا: بازهٔ بزرگ‌تر از ۹۰ روز → `422`.
- ❌ خطا: شعبهٔ محیط دیگر → `404`.
- ❌ خطا: نیازمندی بدون منبع واجد شرایط → `422` با پیام انسانی (از تسک ۰۵).
- ⚠️ مرزی: منبع با `capacity=3` و دو نوبت هم‌زمان → سومی هنوز جا دارد، چهارمی نه.
- ⚠️ مرزی: `setup/cleanup` منبع → بازهٔ اشغال گسترده‌تر از بازهٔ بخش است و باید در
بررسی تداخل لحاظ شود.
- ⚠️ مرزی: بخش با `occupancy=passive` → منبع را می‌گیرد ولی در گزارش بهره‌وری «کار» نیست.
- ⚠️ مرزی: نقطهٔ شروع در گذشته → حذف.
- ⚠️ مرزی: هیچ روزی ظرفیت ندارد → آرایهٔ خالی + `reason` قابل فهم، نه ۴۰۴.
- ⚠️ مرزی: منبع مشترک بین دو بخش غیرمجاور یک نوبت → **همان** منبع باید انتخاب شود
(اپراتور بخش ۱ و بخش ۳ یکی است، نه دو نفر).
## خروجی
- `src/Appointment/Availability/`
- `docs/api/appointment-availability.md`
- تست کارایی با داده مصنوعی: `tests/Appointment/AvailabilityPerformanceTest.php`
- توسعهٔ `AppointmentSettingsPage.tsx` برای انتخاب حالت `resource` و استراتژی