# چک‌لیست — تسک ۱۴ (رویدادهای دامنه و گزارش بهره‌وری) **وضعیت کلی:** ⏳ شروع نشده · **آخرین بازبینی:** — قواعد: [_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` سبز | ⏳ | | | ۰.۲ | `AppointmentEvent` موجود دست‌نخورده | ⏳ | تاریخچهٔ وضعیت ≠ رویداد دامنه | | ۰.۳ | پیامک‌های موجود (`Sms` domain) نشکستند | ⏳ | | | ۰.۴ | گزارش با داده حدسی ساخته **نشد** | ⏳ | ⭐ بند ۱.۹ | ## ۱. بک‌اند — رویدادها | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۱.۱ | `DomainEvent` پایه + `DomainEventPublisher` + `DomainEventLog` | ⏳ | | | ۱.۲ | payload **فقط uuid و اسکالر** — هیچ entity | ⏳ | ⭐ | | ۱.۳ | هر رویداد `entityType`/`entityId` دارد | ⏳ | وگرنه پیامک محیط اشتباه | | ۱.۴ | الگوی **outbox**: `record()` داخل تراکنش کاری، فقط persist | ⏳ | ⭐ | | ۱.۵ | `PublishDomainEventHandler` + `scheduler` هر ۱۰ ثانیه | ⏳ | | | ۱.۶ | `attempts < 5`؛ ردیف شکست‌خورده **حذف نمی‌شود** | ⏳ | | | ۱.۷ | همهٔ `dispatch` های تسک‌های ۰۷ تا ۱۳ به `record()` تغییر کردند | ⏳ | ⭐ | | ۱.۸ | چهارده رویداد بند ۱۶ مستند ثبت شدند | ⏳ | | | ۱.۹ | idempotency در **مصرف‌کننده**، با `domain_events.uuid` | ⏳ | at-least-once | | ۱.۱۰ | worker با loop-wrap برای Coolify | ⏳ | کانتینر خارج نشود | | ۱.۱۱ | `app:events:prune --older-than=180d` | ⏳ | | | ۱.۱۲ | `GET /domain-events` فقط `ROLE_ADMIN` | ⏳ | | ## ۲. بک‌اند — گزارش‌ها | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۲.۱ | `ResourceUtilizationReporter` با چهار عدد | ⏳ | | | ۲.۲ | `available_minutes` **× `capacity`** منبع | ⏳ | ⭐ اتاق سه‌تخته سه برابر | | ۲.۳ | `passive` در `occupied` هست، در `active` نه | ⏳ | | | ۲.۴ | `setup/cleanup` در `occupied` هست | ⏳ | | | ۲.۵ | `released` شمرده نمی‌شود (`status='booked'` فقط) | ⏳ | | | ۲.۶ | `available = 0` → `utilization = null`، **نه صفر** | ⏳ | ⭐ معنای متفاوت | | ۲.۷ | مرز بازه: `start_at >= from AND start_at < to` | ⏳ | نه `end_at <= to` | | ۲.۸ | کوئری تجمعی با `GROUP BY`، بدون پیمایش | ⏳ | | | ۲.۹ | **پیش از پیاده‌سازی** `plan-accuracy`: وجود `patient_sessions.started_at/ended_at` تأیید شد | ⏳ | ⭐ اگر نبود → تسک جدا، نه داده حدسی | | ۲.۱۰ | `PlanAccuracyReporter` با آستانه‌های شدت | ⏳ | | | ۲.۱۱ | انحراف **منفی** بزرگ هم `high` است | ⏳ | نصف ظرفیت هدر می‌رود | | ۲.۱۲ | حداقل نمونه ۱۰، وگرنه `insufficient_data` | ⏳ | | | ۲.۱۳ | بازه > ۹۰ روز → ۴۲۲ | ⏳ | | | ۲.۱۴ | سه endpoint | ⏳ | | ## ۳. دیتابیس | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۳.۱ | `domain_events` (BIGINT id) با سه ایندکس | ⏳ | | | ۳.۲ | `idx_de_pending (published_at, occurred_at)` | ⏳ | کوئری worker | | ۳.۳ | هیچ جدول دیگری تغییر نکرد | ⏳ | | | ۳.۴ | `TenantSchemaCoverageTest` سبز | ⏳ | | ## ۴. UI | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۴.۱ | `ResourceUtilizationPage` — جدول + نمودار `Recharts` | ⏳ | کتابخانهٔ موجود | | ۴.۲ | `PlanAccuracyPage` — جدول انحراف با شدت | ⏳ | | | ۴.۳ | ردیف‌های `active_ratio < 0.3` نشان هشدار دارند | ⏳ | | | ۴.۴ | **tooltip توضیح `active_ratio` در خودِ UI** | ⏳ | ⭐ نه فقط در مستندات | | ۴.۵ | `utilization = null` → `—` با tooltip «تقویم تعریف نشده» + لینک تنظیم | ⏳ | | | ۴.۶ | لینک «ویرایش بخش‌های این سرویس» از `PlanAccuracyPage` | ⏳ | ⭐ گزارشی که راه اصلاح ندهد خوانده نمی‌شود | | ۴.۷ | بازهٔ زمانی با `PersianDatePicker` | ⏳ | | | ۴.۸ | وضعیت (بازه، فیلتر) در URL با `useUrlState` | ⏳ | | | ۴.۹ | `DataTable` با skeleton و empty state | ⏳ | | | ۴.۱۰ | رنگ نمودار از توکن‌های `--stat-*`، نه پالت پیش‌فرض Recharts | ⏳ | ⭐ | | ۴.۱۱ | هیچ رنگ/شعاع hard-code | ⏳ | | | ۴.۱۲ | دارک‌مود — نمودار هم در دارک خوانا است | ⏳ | ⭐ محور و legend | | ۴.۱۳ | حالت فشرده | ⏳ | | | ۴.۱۴ | RTL و موبایل — جدول و نمودار اسکرول افقی داخلی | ⏳ | | | ۴.۱۵ | همهٔ رشته‌ها فارسی · اعداد با `formatNumber` | ⏳ | | | ۴.۱۶ | `backTo` روی صفحات گزارش | ⏳ | | ## ۵. تست | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۵.۱ | `OutboxTest` — record داخل تراکنش، rollback، انتشار، شکست، سقف تلاش | ⏳ | ⭐ | | ۵.۲ | `EventPayloadTest` — reflection روی همهٔ زیرکلاس‌ها: فقط اسکالر | ⏳ | | | ۵.۳ | `ResourceUtilizationTest` — شش سنجهٔ سند | ⏳ | ⭐ شامل `capacity` و `null` | | ۵.۴ | `PlanAccuracyTest` — انحراف دوطرفه، نمونهٔ کم | ⏳ | | | ۵.۵ | `ReportAuthTest` — منشی ۴۰۳، بازه ۴۲۲ | ⏳ | | | ۵.۶ | `ReportQueryCountTest` — تعداد کوئری مستقل از تعداد منبع | ⏳ | | ## ۶. مستندات | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۶.۱ | `docs/api/reports.md` — معنی هر عدد + جدول `active_ratio` | ⏳ | | | ۶.۲ | `docs/architecture/domain-events.md` — قرارداد، فهرست، outbox، idempotency | ⏳ | | | ۶.۳ | جدول تفاوت `AppointmentEvent` با `DomainEventLog` | ⏳ | ⭐ وگرنه یکی حذف می‌شود | ## ۷. بازبینی پایانی | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۷.۱ | هیچ 🔄 و ⏳ بی‌دلیل نمانده | ⏳ | | | ۷.۲ | `bin/phpunit` کامل سبز | ⏳ | | | ۷.۳ | `--group=slot-mode-frozen` سبز | ⏳ | | | ۷.۴ | `phpstan` بدون خطای جدید | ⏳ | | | ۷.۵ | `npx tsc --noEmit` و `yarn test` سبز | ⏳ | | | ۷.۶ | تست‌های tenant سبز | ⏳ | | | ۷.۷ | `docs/api/*` به‌روز | ⏳ | | | ۷.۸ | چک‌لیست UI کامل | ⏳ | | | ۷.۹ | پیامک‌های موجود سرتاسر تست شدند (outbox نشکستشان) | ⏳ | ⭐ | | ۷.۱۰ | دو کلاینت دیگر بررسی شدند | ⏳ | | | ۷.۱۱ | commit، سپس `graphify update .` | ⏳ | | | ۷.۱۲ | موارد به‌تعویق با دلیل و تسک مقصد | ⏳ | `plan-accuracy` اگر داده نبود |