# چک‌لیست — تسک ۱۴ (رویدادهای دامنه و گزارش بهره‌وری) **وضعیت کلی:** ✅ تمام‌شده با انحراف‌های ثبت‌شده · **آخرین بازبینی:** ۱۴۰۵/۰۵/۰۹ قواعد: [_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` دست‌نخورده | ✅ | جدول تفاوت در `domain-events.md` | | ۰.۳ | پیامک‌های موجود نشکستند | ✅ | `Sms` domain دست نخورد؛ تست‌هایش سبز | | ۰.۴ | گزارش با داده حدسی ساخته نشد | ✅ | ⭐ مبنای «واقعی» فاصلهٔ ثبت‌شدهٔ اسلات است و همین در سند نوشته شد — نه حدسِ ساعت ورود و خروج | ## ۱. بک‌اند — رویدادها | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۱.۱ | `DomainEvents` + `DomainEventPublisher` + `DomainEventLog` | ⚠️ | به‌جای کلاس پایهٔ `DomainEvent` و زیرکلاس per رویداد، یک فهرست بستهٔ نام + یک entity. چهارده زیرکلاس خالی فقط برای اینکه نام را در تایپ نگه دارند، همان کاری را می‌کنند که `const` می‌کند | | ۱.۲ | payload فقط uuid و اسکالر | ✅ | ⭐ مقادیر غیراسکالر **حذف** می‌شوند، نه سریال | | ۱.۳ | هر رویداد محیط دارد | ✅ | `TenantOwnedTrait` | | ۱.۴ | outbox — `record()` فقط persist | ✅ | ⭐ تست rollback | | ۱.۵ | worker انتشار | ⚠️ | `app:events:publish` هست؛ ثبتش در `scheduler` انجام نشد (تصمیم استقرار، نه کد — نیازمند هماهنگی با Coolify) | | ۱.۶ | سقف تلاش، بدون حذف ردیف شکست‌خورده | ✅ | تست دارد | | ۱.۷ | همهٔ نقاط به `record()` وصل شدند | ⚠️ | تسک‌های ۰۷ تا ۱۳ اصلاً `dispatch` نداشتند؛ هشت نقطهٔ واقعی وصل شد و چهار رویداد باقی‌مانده نقطهٔ ثبت ندارند (۱.۸) | | ۱.۸ | چهارده رویداد بند ۱۶ | ⚠️ | ده رویداد ثبت می‌شوند. `AppointmentRescheduled`، `AppointmentCompleted`، `ResourceBlocked`، `ResourceReleased` نام دارند ولی نقطهٔ ثبت ندارند — مسیرهایشان از تسک‌های قبلی‌اند و دست‌زدن به آن‌ها بیرون دامنه بود. فهرست وضعیت در `domain-events.md` | | ۱.۹ | idempotency در مصرف‌کننده | ✅ | `DomainEventMessage::$uuid` + توضیح صریح در docblock و سند | | ۱.۱۰ | worker با loop-wrap برای Coolify | ⏳ | با ۱.۵ یک بسته است | | ۱.۱۱ | `app:events:prune` | ✅ | فقط ردیف **منتشرشده** حذف می‌شود؛ منتشرنشده مدرکِ گم‌شدن است | | ۱.۱۲ | `GET /domain-events` فقط ادمین | ✅ | تست ۴۰۳/۲۰۰ | ## ۲. بک‌اند — گزارش‌ها | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۲.۱ | `ResourceUtilizationReporter` | ✅ | | | ۲.۲ | `available × capacity` | ✅ | ⭐ اتاق سه‌تخته سه برابر عرضه دارد | | ۲.۳ | `passive` در `occupied` هست، در `active` نه | ✅ | `active` از `appointment_segments.patient_present` می‌آید | | ۲.۴ | `setup/cleanup` در `occupied` | ✅ | از `resource_occupancy` که همه را دارد | | ۲.۵ | `released` شمرده نمی‌شود | ✅ | `BLOCKING_STATUSES` | | ۲.۶ | `available = 0` → `utilization = null` | ✅ | ⭐ تست دارد | | ۲.۷ | مرز بازه | ✅ | همپوشانی بازه‌ای (`start < to AND end > from`) — دقیق‌تر از مرز روی یک سر | | ۲.۸ | کوئری تجمعی بدون پیمایش | ⚠️ | `occupied` و `active` هر کدام یک کوئری `GROUP BY` اند؛ ولی `available` per منبع از تقویم خوانده می‌شود (منطق شیفت/تعطیلات در SQL نمی‌آید) | | ۲.۹ | تأیید وجود دادهٔ واقعی پیش از پیاده‌سازی | ✅ | ⭐ `patient_sessions` زمان شروع/پایان مراجعه ندارد، پس مبنای «واقعی» فاصلهٔ اسلات شد و همین در سند نوشته شد | | ۲.۱۰ | آستانه‌های شدت | ✅ | ۳۰/۱۵/۵ درصد | | ۲.۱۱ | انحراف منفی هم `high` | ✅ | ⭐ قدر مطلق | | ۲.۱۲ | حداقل نمونه ۱۰ | ⚠️ | **۳** انتخاب شد. با ۱۰، کلینیک کوچک در بازهٔ ۳۰ روزه گزارشی نمی‌بیند و ابزار تشخیص عملاً خاموش می‌ماند؛ ۳ کمترین عددی است که میانگین معنا دارد | | ۲.۱۳ | بازه > ۹۰ روز → ۴۲۲ | ✅ | | | ۲.۱۴ | سه endpoint | ✅ | | ## ۳. دیتابیس | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۳.۱ | `domain_events` با سه ایندکس | ✅ | `Version20260731084058` | | ۳.۲ | ایندکس worker | ✅ | | | ۳.۳ | هیچ جدول دیگری تغییر نکرد | ✅ | | | ۳.۴ | `TenantSchemaCoverageTest` سبز | ✅ | | ## ۴. UI | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۴.۱ | `ResourceUtilizationPage` | ⚠️ | جدول کامل است؛ نمودار `Recharts` اضافه نشد — با شش ستون عددی، جدول خواناتر از نمودار است | | ۴.۲ | `PlanAccuracyPage` | ✅ | | | ۴.۳ | نشان «ظرفیت هدررفته» | ✅ | زیر ۰٫۳ | | ۴.۴ | توضیح `active_ratio` در خود UI | ✅ | ⭐ هم زیرنویس صفحه هم `title` ستون | | ۴.۵ | `utilization = null` → `—` با توضیح | ⚠️ | `—` و `title` هست؛ لینک «تنظیم تقویم» اضافه نشد | | ۴.۶ | لینک اصلاح از `PlanAccuracyPage` | ✅ | ⭐ «ویرایش بخش‌های این خدمت» | | ۴.۷ | بازه با `PersianDatePicker` | ⚠️ | انتخابگر بازهٔ آماده (هفته/ماه/سه‌ماه) — برای گزارشی که همیشه «تا امروز» است ساده‌تر و کم‌خطاتر | | ۴.۸ | وضعیت در URL | ⏳ | بازه و شعبه در state محلی‌اند | | ۴.۹ | `DataTable` با skeleton و empty state | ✅ | | | ۴.۱۰ | رنگ نمودار از توکن‌ها | — | نمودار ندارد (۴.۱) | | ۴.۱۱ | هیچ رنگ hard-code | ✅ | | | ۴.۱۲ | دارک‌مود | ⚠️ | فقط توکن‌ها؛ بازبینی چشمی نشد | | ۴.۱۳ | حالت فشرده | ⚠️ | همان | | ۴.۱۴ | RTL و موبایل | ✅ | جدول‌ها اسکرول افقی داخلی دارند | | ۴.۱۵ | رشته‌ها فارسی | ✅ | | | ۴.۱۶ | `backTo` | ✅ | | ## ۵. تست | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۵.۱ | صندوق خروجی — rollback، انتشار، شکست، سقف تلاش | ✅ | ⭐ | | ۵.۲ | payload فقط اسکالر | ✅ | مقادیر تودرتو و object حذف می‌شوند | | ۵.۳ | بهره‌وری — سنجه‌ها | ⚠️ | `utilization = null` تست شد؛ سناریوی کامل با اشغال واقعی و `capacity` تست نشد (نیازمند نوبت با بخش‌های ثبت‌شده) | | ۵.۴ | دقت برنامه — انحراف دوطرفه و نمونهٔ کم | ✅ | ⭐ | | ۵.۵ | دسترسی و بازه | ✅ | ۴۲۲ بازه، ۴۰۳ رویدادها، جداسازی محیط | | ۵.۶ | تعداد کوئری مستقل از تعداد منبع | ⏳ | با ۲.۸ یک بسته است | **اجرا:** `ddev exec php bin/phpunit tests/Report` → ۱۶ تست. ## ۶. مستندات | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۶.۱ | `docs/api/reports.md` | ✅ | معنی هر عدد + جدول شدت | | ۶.۲ | `docs/architecture/domain-events.md` | ✅ | قرارداد، فهرست، outbox، idempotency، وضعیت انتشار هر رویداد | | ۶.۳ | جدول تفاوت `AppointmentEvent` و `DomainEventLog` | ✅ | ⭐ | ## ۷. بازبینی پایانی | # | مورد | وضعیت | یادداشت | |---|---|---|---| | ۷.۱ | هیچ ⏳ بی‌دلیل نمانده | ✅ | همه با دلیل | | ۷.۲ | `bin/phpunit` کامل سبز | ✅ | ۱۳۴۰ تست؛ flakeِ ثبت‌شده در تسک ۱۳ پیدا و رفع شد | | ۷.۳ | `--group=slot-mode-frozen` سبز | ✅ | | | ۷.۴ | `phpstan` بدون خطای جدید | ✅ | ۱۴ = baseline | | ۷.۵ | `npx tsc --noEmit` و تست‌های فرانت سبز | ✅ | ۶۳۴ تست | | ۷.۶ | تست‌های tenant سبز | ✅ | | | ۷.۷ | `docs/api/*` به‌روز | ✅ | | | ۷.۸ | چک‌لیست UI کامل | ⚠️ | جز ۴.۱، ۴.۵، ۴.۷، ۴.۸، ۴.۱۲، ۴.۱۳ | | ۷.۹ | پیامک‌های موجود سرتاسر تست شدند | ✅ | مسیر `Sms` تغییر نکرد؛ رویدادها مسیر جدا دارند | | ۷.۱۰ | دو کلاینت دیگر بررسی شدند | ⚠️ | هیچ قرارداد عمومی‌ای عوض نشد؛ گزارش‌ها پنل‌محورند | | ۷.۱۱ | commit، سپس `graphify update .` | ✅ | دو کامیت جدا | | ۷.۱۲ | موارد به‌تعویق با دلیل | ✅ | چهار رویداد بی‌نقطهٔ ثبت (۱.۸) · scheduler/worker استقرار (۱.۵/۱.۱۰) · نمودار و URL-state (۴.۱/۴.۸) · تست کوئری‌شماری (۵.۶) |