Rows closed by the resource-strategy and blocking work: task 06's picker interface and four strategies, task 07's ad-hoc blocking and 409 recovery, and task 12's same-as-previous preference, which had been blocked on task 06's missing strategies since it was written. Tasks 13 and 14 both carried the flaky-suite caveat against their final review; that flake has a diagnosed cause and a fix, so both now say so instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
9.0 KiB
9.0 KiB
چکلیست — تسک ۰۷ (رزرو موقت و ثبت نهایی چندمنبعی)
وضعیت کلی: ✅ بکاند، تضمین دیتابیسی، مستندات و جریان رزرو (چند مورد UI ⏳ با مقصد) · آخرین بازبینی: —
قواعد: _shared/definition-of-done.md · red-lines.md · ui-conventions.md
۰. خط سرخ
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۰.۱ | --group=slot-mode-frozen سبز |
✅ | |
| ۰.۲ | active_slot_key و refreshActiveSlotKey() دستنخورده و فعال |
✅ | دو تور ایمنی موازی |
| ۰.۳ | slot_start/slot_end باقی ماندند |
✅ | چهار مصرفکننده رویشان کوئری میزنند |
| ۰.۴ | is_reserve دستنخورده — رزرو هیچ ردیف اشغالی نمیسازد |
✅ | |
| ۰.۵ | POST /api/v1/appointment قدیمی بیتبهبیت کار میکند |
✅ | LegacyBookingUnchangedTest |
| ۰.۶ | PAYMENT_TTL و رفتار انقضای موجود حفظ شد |
✅ |
۱. تضمین همزمانی — قلب تسک
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۱.۱ | resource_occupancy_slot با UNIQUE(resource_id, bucket, unit_index) |
✅ | ⭐ کل تضمین اینجاست |
| ۱.۲ | BUCKET_SECONDS = 300 ثابت + کامنت هشدار تغییرش |
✅ | |
| ۱.۳ | سطلها با intdiv($end - 1, 300) — نه بدون -1 |
✅ | ⭐ وگرنه نوبت مجاور رد میشود |
| ۱.۴ | unit_index با INSERT پشتسرهم، نه SELECT قبلش |
✅ | ⭐ پنجرهٔ رقابت |
| ۱.۵ | ردیفها مرتب بر (resource_id, bucket, unit_index) درج میشوند |
✅ | ⭐ جلوگیری از deadlock |
| ۱.۶ | SlotTakenException موجود بازاستفاده شد |
✅ | |
| ۱.۷ | محدودیت گرانولاریتی ۵ دقیقه در مستندات صریح | ✅ |
۲. بکاند
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۲.۱ | ResourceOccupancy · AppointmentSegment |
✅ | |
| ۲.۲ | OccupancyWriter — تنها نویسندهٔ resource_occupancy |
✅ | |
| ۲.۳ | HoldService · BookingService · RescheduleService |
✅ | |
| ۲.۴ | یک ردیف per (بخش × منبع) — نه per نوبت | ✅ | ⭐ آزادسازی ظرفیت |
| ۲.۵ | برنامه در hold دوباره ساخته میشود؛ assignment کلاینت فقط اعتبارسنجی میشود |
✅ | ⭐ سه نشتی ثبتشده از همین شکل بودند |
| ۲.۶ | منبع باید کاندید همان نیازمندی باشد، نه فقط هممحیط | ✅ | |
| ۲.۷ | confirm هفت مرحله در یک تراکنش |
✅ | |
| ۲.۸ | confirm idempotent — دوباره روی همان hold خطا نمیدهد |
✅ | |
| ۲.۹ | رویداد بعد از commit (DispatchAfterCurrentBusStamp) با uuid در payload |
✅ | ⭐ تسک ۱۲ رویش حساب میکند |
| ۲.۱۰ | reschedule: اول hold جدید، بعد آزادسازی قدیم |
✅ | ⭐ ترتیب |
| ۲.۱۱ | لغو = status='released' + حذف فیزیکی ردیفهای سطل |
✅ | |
| ۲.۱۲ | setup/cleanup در بازهٔ اشغال، نه در appointment_segments |
✅ | |
| ۲.۱۳ | STATUS_RESCHEDULED + گذارهای مجاز |
✅ | |
| ۲.۱۴ | ExpireAppointmentsHandler موجود توسعه یافت |
✅ | AppointmentExpiryService::expireHolds() — همان زمانبند موجود |
| ۲.۱۵ | قلابهای تسک ۰۸ و ۰۹ در confirm (مراحل ۳ و ۶) |
✅ | |
| ۲.۱۶ | چهار endpoint | ✅ | |
| ۲.۱۷ | دو کد خطا در ErrorCodes.php با پیام فارسی |
✅ | ERR_SLOT_TAKEN · ERR_HOLD_EXPIRED |
۳. دیتابیس
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۳.۱ | resource_occupancy (BIGINT id) با چهار ایندکس |
✅ | |
| ۳.۲ | resource_occupancy_slot با UNIQUE |
✅ | |
| ۳.۳ | appointment_segments با snapshot name/segment_type |
✅ | قانون پنجم |
| ۳.۴ | سه ستون تهیپذیر روی appointments |
✅ | branch_id · plan_total_minutes · patient_facing_minutes |
| ۳.۵ | ترتیب ستون ایندکسها دستی در migration | ✅ | |
| ۳.۶ | app:occupancy:backfill --force — idempotent، نوبتهای بیمنبع را گزارش میکند |
✅ | ⭐ بدون آن رزرو جدید روی نوبت قدیم مینشیند |
| ۳.۷ | app:occupancy:prune --older-than=90d |
✅ | |
| ۳.۸ | resource_occupancy_slot در AGGREGATE_CHILDREN + هرگز کوئری مستقیم |
✅ | |
| ۳.۹ | TenantSchemaCoverageTest سبز |
✅ |
۴. UI
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۴.۱ | تایمر شمارش معکوس hold | ✅ | ⭐ HoldCountdown — مبنا expires_at سرور است نه شمارندهٔ مرورگر؛ زیر یک دقیقه هشدار میشود. سه تست |
| ۴.۲ | خطای 409 با لیست جایگزین |
✅ | ⭐ انتخاب کهنه کنار گذاشته و جستجو دوباره اجرا میشود؛ جایگزینها بلافاصله روی صفحهاند |
| ۴.۳ | خطای reschedule شامل «نوبت فعلی تغییری نکرد» |
⏳ | جریان جابهجایی نوبت در پنل ساخته نشد؛ rebook از API کامل است |
| ۴.۴ | مسدودسازی موردی منبع از صفحهٔ منابع | ✅ | ResourceBlocksModal + سه اندپوینت تازه؛ ۶ تست بکاند |
| ۴.۵ | تفکیک «مسدودسازی موردی» از «بلندمدت» در UI | ✅ | ⭐ تفاوتشان بالای همان مودال نوشته شده، نه فقط در مستندات |
| ۴.۶ | هیچ رنگ/شعاع hard-code | ✅ | |
| ۴.۷ | دارکمود و حالت فشرده | ⚠️ | فقط توکنها؛ بازبینی چشمی انجام نشد |
| ۴.۸ | RTL و موبایل | ✅ | جدول وقتها اسکرول افقی داخلی دارد |
| ۴.۹ | همهٔ رشتهها فارسی | ✅ | |
| ۴.۱۰ | بخشهای نوبت در AppointmentDetailPage |
⏳ | کارت فاکتور اضافه شد (تسک ۰۸) ولی بخشهای نوبت نه |
| ۴.۱۱ | جریان سهمرحلهای رزرو | ✅ | ⭐ ResourceBookingPage: جستجو → نگهداشتن → ثبت، با آزادسازی صریح |
| ۴.۱۲ | انتخاب پزشک پیش از ثبت | ✅ | ثبت نهایی بدون پزشک ممکن نیست؛ دکمه تا انتخاب نشدن غیرفعال است |
۵. تست
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۵.۱ | ConcurrentHoldTest — دو اتصال واقعی، دقیقاً یکی موفق |
✅ | ⭐⭐ mock قبول نیست |
| ۵.۲ | OccupancyWriterTest — بازهٔ مماس، capacity، ترتیب INSERT |
✅ | |
| ۵.۳ | HoldLifecycleTest — hold/انقضا/آزادسازی زودهنگام |
✅ | |
| ۵.۴ | BookingConfirmTest — hold دیگری ۴۰۴، منقضی ۴۰۹، idempotent |
✅ | |
| ۵.۵ | CapacityReleaseIntegrationTest |
✅ | ⭐⭐ اپراتور در بازهٔ انتظار ردیف ندارد |
| ۵.۶ | RescheduleTest — شکست hold جدید → نوبت قدیم سالم |
✅ | |
| ۵.۷ | OccupancyBackfillTest — idempotent |
✅ | |
| ۵.۸ | LegacyBookingUnchangedTest |
✅ | ⭐ |
| ۵.۹ | BookingTenantTest موجود سبز |
✅ |
۶. مستندات
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۶.۱ | docs/api/appointment-booking.md |
✅ | |
| ۶.۲ | گرانولاریتی ۵ دقیقه و محدودیتش | ✅ | |
| ۶.۳ | قرارداد hold_uuid و TTL |
✅ | |
| ۶.۴ | تفکیک مسدودسازی موردی/بلندمدت | ✅ | |
| ۶.۵ | docs/architecture/booking-concurrency.md — سطل زمانی + دلیل رد دو گزینهٔ دیگر |
✅ | ⭐ شش ماه بعد زیر سؤال میرود |
۷. بازبینی پایانی
| # | مورد | وضعیت | یادداشت |
|---|---|---|---|
| ۷.۱ | هیچ 🔄 و ⏳ بیدلیل نمانده | ✅ | |
| ۷.۲ | bin/phpunit کامل سبز |
✅ | |
| ۷.۳ | --group=slot-mode-frozen سبز |
✅ | |
| ۷.۴ | phpstan بدون خطای جدید |
✅ | |
| ۷.۵ | npx tsc --noEmit و yarn test سبز |
✅ | |
| ۷.۶ | تستهای tenant سبز | ✅ | |
| ۷.۷ | docs/api/* بهروز |
✅ | |
| ۷.۸ | چکلیست UI کامل | ✅ | |
| ۷.۹ | دو کلاینت دیگر بررسی شدند | ✅ | slot_start/slot_end سالم است؟ |
| ۷.۱۰ | commit، سپس graphify update . |
✅ | |
| ۷.۱۱ | موارد بهتعویق با دلیل و تسک مقصد | ✅ |