# تسک ۱۴ — رویدادهای دامنه و گزارش بهره‌وری منابع **فاز:** ۴ (بهینه‌سازی) · **وابستگی:** ۰۷ · **زمان:** ۸-۱۰ ساعت --- ## هدف دو چیز از مستند: 1. **بند ۱۶** — فهرست رویدادهایی که سیستم منتشر می‌کند تا سیستم‌های دیگر (پیامک، حسابداری، گزارش) به آن‌ها گوش بدهند. 2. **بند ۱۷، ریسک سوم** — «کلینیک بخش‌های نوبت را اشتباه تعریف کند → ظرفیت غلط حساب می‌شود». راه‌حل مستند: **گزارش بهره‌وری منابع برای پیدا کردن اشکال.** گزارش بهره‌وری تنها ابزاری است که به کلینیک می‌گوید تعریف بخش‌هایش درست است یا نه. بدون آن، تسک ۰۵ یک ابزار قدرتمند بدون بازخورد است. ## وضعیت فعلی - `AppointmentEvent` وجود دارد و تاریخچهٔ تغییر وضعیت نوبت را ثبت می‌کند - `symfony/messenger` + `symfony/redis-messenger` + `symfony/scheduler` در استک هستند - پیامک از راه `Sms` domain و `messenger:consume async` کار می‌کند - تسک‌های ۰۷ تا ۱۳ هر کدام یک `dispatch` گذاشته‌اند بدون یک قرارداد واحد ## دامنه **هست:** - قرارداد واحد رویداد دامنه: نام، payload (فقط uuid)، زمان انتشار (بعد از commit) - ثبت همهٔ رویدادهای بند ۱۶ مستند - `domain_events` — جدول outbox برای تضمین انتشار - گزارش بهره‌وری منابع: ساعت آزاد / اشغال / انتظار / کار فعال per منبع per بازه - گزارش «مدت پیش‌بینی‌شده در برابر مدت واقعی» برای تشخیص تعریف غلط بخش‌ها **نیست:** پیش‌بینی عدم حضور، پیشنهاد هوشمند وقت (فاز ۴ مستند، خارج از دامنه). ## Endpoint ها | متد | مسیر | توضیح | |---|---|---| | GET | `/api/v1/reports/resource-utilization` | بهره‌وری منابع در بازه | | GET | `/api/v1/reports/plan-accuracy` | مقایسهٔ مدت پیش‌بینی و واقعی per سرویس | | GET | `/api/v1/domain-events` | (ادمین) رویدادهای منتشرشده — عیب‌یابی | ## فهرست رویدادها (مستند بند ۱۶) ``` HoldCreated AppointmentBooked AppointmentCancelled AppointmentRescheduled PatientNoShow AppointmentCompleted ResourceBlocked ResourceReleased CourseStarted CourseSessionCompleted CourseCompleted PackagePurchased CreditConsumed CreditRefunded ``` ## معیار پذیرش - ✅ موفق: ثبت نوبت → یک ردیف در `domain_events` با نام `AppointmentBooked` و payload شامل `appointment_uuid`؛ و `GET /domain-events` آن را نشان می‌دهد. - ✅ موفق: رویداد **بعد از** commit منتشر می‌شود. تست: تراکنشی که rollback می‌شود هیچ رویدادی منتشر نمی‌کند. - ✅ موفق: گزارش بهره‌وری برای اپراتور مریم در یک هفته → `{ available_minutes: 2400, occupied_minutes: 1800, active_minutes: 1200, utilization: 0.75, active_ratio: 0.50 }`. - ✅ موفق (**تشخیص تعریف غلط بخش‌ها**): سرویسی که `total_minutes` پیش‌بینی‌اش ۶۰ است ولی میانگین مدت واقعی مراجعاتش ۹۰ دقیقه → `GET /reports/plan-accuracy` آن را با `deviation_percent: +50` و `severity: 'high'` برمی‌گرداند. - ✅ موفق: منبعی با `active_ratio` زیر ۰.۳ در گزارش با نشان «ظرفیت هدررفته» می‌آید — یعنی بخش‌های `passive` یا انتظار زیادی به آن نسبت داده شده. - ❌ خطا: گزارش با بازهٔ بزرگ‌تر از ۹۰ روز → `422`. - ❌ خطا: منشی روی `GET /domain-events` → `403` (فقط `ROLE_ADMIN`). - ⚠️ مرزی: منبع بدون هیچ تقویم → `available_minutes: 0` و `utilization: null` (نه صفر — تقسیم بر صفر معنایی متفاوت دارد). - ⚠️ مرزی: بخش‌های `passive` در `occupied_minutes` می‌آیند ولی در `active_minutes` نه. - ⚠️ مرزی: `setup/cleanup` در `occupied_minutes` می‌آید (منبع واقعاً اشغال بوده). - ⚠️ مرزی: رویداد تکراری (پیام دوباره از messenger) → مصرف‌کننده idempotent، نه رویداد. ## خروجی - `src/Shared/Event/` — قرارداد رویداد + outbox - `src/Report/` — دو گزارش - `assets/admin/pages/ResourceUtilizationPage.tsx` + `PlanAccuracyPage.tsx` - `docs/api/reports.md` + `docs/architecture/domain-events.md`