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:
@@ -0,0 +1,150 @@
|
||||
# جریان کاربری — تسک ۰۷
|
||||
|
||||
## الف) مسیر موفق — بیمار از سایت عمومی
|
||||
|
||||
```
|
||||
[تسک ۰۶] بیمار ساعت ۰۹:۰۰ را انتخاب میکند
|
||||
│
|
||||
▼
|
||||
POST /api/v1/appointment-hold
|
||||
{ "doctor_uuid":"…", "branch_uuid":"…", "service_item_uuid":"…",
|
||||
"option_uuids":["…"], "start": 1754…, "assignment": { "operator":"…", "room":"…", "device":"…" } }
|
||||
│
|
||||
├─ سرور: برنامه را دوباره میسازد (به assignment اعتماد نمیکند)
|
||||
├─ نوبت pending با expires_at = now + 900
|
||||
├─ appointment_segments × ۵
|
||||
└─ resource_occupancy × (بخش × منبع) — بخش انتظار فقط اتاق
|
||||
▼
|
||||
201 { "hold_uuid":"…", "expires_at": 1754…, "total_price_rials": … }
|
||||
│
|
||||
│ ⏱ تایمر ۱۵ دقیقهای در UI: «۱۴:۵۹ برای تکمیل رزرو»
|
||||
▼
|
||||
پرداخت بیعانه (اگر deposit_required) → درگاه → بازگشت
|
||||
▼
|
||||
POST /api/v1/appointment-confirm { "hold_uuid":"…" }
|
||||
│
|
||||
├─ ۱ hold معتبر است؟
|
||||
├─ ۲ قوانین صلاحیت و فاصله (تسک ۰۹)
|
||||
├─ ۳ وضعیت → confirmed
|
||||
├─ ۴ اشغالها hold → booked
|
||||
├─ ۵ snapshot قیمت (تسک ۰۸)
|
||||
└─ ۶ رویداد AppointmentBooked (بعد از commit)
|
||||
▼
|
||||
200 { "appointment_uuid":"…", "status":"confirmed" }
|
||||
▼
|
||||
پیامک تأییدیه (از راه رویداد، async)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ب) رقابت روی ساعت پرتقاضا
|
||||
|
||||
```
|
||||
بیمار الف بیمار ب
|
||||
│ ۰۹:۰۰ را میبیند │ ۰۹:۰۰ را میبیند
|
||||
│ │
|
||||
▼ POST /appointment-hold ▼ POST /appointment-hold
|
||||
│ │
|
||||
│ INSERT slot (res=7,b=…,u=0) ✓ │ INSERT slot (res=7,b=…,u=0) ✗ duplicate
|
||||
▼ ▼
|
||||
201 hold_uuid 409 ERR_SLOT_TAKEN
|
||||
│
|
||||
▼
|
||||
UI: «این ساعت همین لحظه رزرو شد.»
|
||||
+ لیست بهروزشدهٔ وقتهای نزدیک
|
||||
(خودکار، بدون کلیک دوباره)
|
||||
```
|
||||
|
||||
پیام «این ساعت همین لحظه رزرو شد» + پیشنهاد جایگزین، همان کاری است که مستند بند ۱۷
|
||||
برای ریسک «رقابت روی ساعتهای پرتقاضا» میخواهد. `409` خالی بدون جایگزین یعنی بیمار میرود.
|
||||
|
||||
---
|
||||
|
||||
## ج) hold منقضی میشود
|
||||
|
||||
```
|
||||
hold ساخته شد ─── ۱۵ دقیقه ───▶ منقضی
|
||||
│
|
||||
┌─────────────────────────┴──────────────────────────┐
|
||||
│ │
|
||||
cron هر دقیقه یا: بیمار confirm میزند
|
||||
ExpireAppointmentsHandler │
|
||||
│ ▼
|
||||
├─ status → expired 409 ERR_HOLD_EXPIRED
|
||||
├─ occupancy → released │
|
||||
└─ occupancy_slot → DELETE ▼
|
||||
▼ UI: «زمان رزرو شما به پایان رسید»
|
||||
زمان دوباره در جستجو ظاهر میشود + بازگشت به لیست وقتها
|
||||
```
|
||||
|
||||
نکته: حتی پیش از اجرای cron، `hasRoom` تسک ۰۶ شرط `expires_at > now` را دارد، پس
|
||||
hold مردهٔ چند ثانیهای هم مانع کسی نمیشود. cron فقط تمیزکاری است.
|
||||
|
||||
---
|
||||
|
||||
## د) منشی نوبت را جابهجا میکند
|
||||
|
||||
```
|
||||
پنل › نوبتها › جزئیات نوبت › «جابهجایی»
|
||||
│
|
||||
▼
|
||||
انتخاب تاریخ/ساعت جدید (همان UI تسک ۰۶، با forManagement=true)
|
||||
▼
|
||||
POST /api/v1/appointment/{uuid}/reschedule { "start": … , "assignment": {…} }
|
||||
│
|
||||
├─ ۱ hold جدید ساخته میشود ← اگر شکست: rollback کامل، نوبت قدیم سالم
|
||||
├─ ۲ اشغالهای قدیم released
|
||||
├─ ۳ نوبت قدیم → rescheduled
|
||||
└─ ۴ لینک قدیم ↔ جدید در appointment_events
|
||||
▼
|
||||
200 { "new_appointment_uuid": "…" }
|
||||
▼
|
||||
پیامک اطلاعرسانی جابهجایی
|
||||
```
|
||||
|
||||
اگر hold جدید `409` بدهد:
|
||||
|
||||
```
|
||||
422 { "errors": [{ "code":"ERR_SLOT_TAKEN",
|
||||
"message":"زمان جدید در دسترس نیست. نوبت فعلی تغییری نکرد." }] }
|
||||
```
|
||||
|
||||
جملهٔ دوم پیام اجباری است — منشی باید بداند وضعیت فعلی امن است و لازم نیست چیزی را
|
||||
درست کند.
|
||||
|
||||
---
|
||||
|
||||
## ه) لغو نوبت
|
||||
|
||||
```
|
||||
لغو (بیمار یا پزشک یا منشی)
|
||||
│
|
||||
├─ status → cancelled_by_user / cancelled_by_doctor
|
||||
├─ active_slot_key → NULL (مکانیزم موجود، دستنخورده)
|
||||
├─ resource_occupancy → released
|
||||
└─ resource_occupancy_slot → DELETE
|
||||
▼
|
||||
ظرفیت فوری آزاد میشود
|
||||
▼
|
||||
[تسک ۱۳] لیست انتظار همان بازه اطلاع میگیرد
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## و) مسدودسازی دستی یک منبع
|
||||
|
||||
حالت خاصی که `appointment_id = NULL` را توضیح میدهد:
|
||||
|
||||
```
|
||||
پنل › منابع › دستگاه کندلا ۲ › «مسدودسازی بازه»
|
||||
تاریخ/ساعت + دلیل: «سرویس دورهای»
|
||||
▼
|
||||
یک ردیف resource_occupancy با appointment_id=NULL و status='booked'
|
||||
(یا معادلاً یک resource_exception از تسک ۰۳ — هر دو کار میکنند)
|
||||
```
|
||||
|
||||
**تصمیم:** مسدودسازی **بلندمدت و تکرارشونده** → `resource_exception` (تسک ۰۳).
|
||||
مسدودسازی **موردی و کوتاه** → `resource_occupancy` با `appointment_id=NULL`.
|
||||
دلیل: دومی در همان ایندکس داغ مینشیند و در `OccupancyIndex` بدون کد اضافه دیده میشود.
|
||||
این تفکیک را در `docs/api/appointment-booking.md` بنویس، وگرنه دو راه انجام یک کار
|
||||
گیجکننده میشود.
|
||||
Reference in New Issue
Block a user