- 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.
4.9 KiB
دیتابیس — تسک ۱۴
domain_events — outbox
| ستون | نوع | توضیح |
|---|---|---|
id |
BIGINT PK AI | |
uuid |
VARCHAR(36) UNIQUE | شناسهٔ idempotency برای مصرفکننده |
entity_type / entity_id |
VARCHAR(10) / INT NOT NULL | محیط رویداد |
name |
VARCHAR(60) NOT NULL | AppointmentBooked |
payload |
JSON NOT NULL | فقط uuid و اسکالر |
occurred_at |
INT NOT NULL | زمان وقوع (نه انتشار) |
published_at |
INT NULL | NULL = منتشر نشده |
attempts |
SMALLINT NOT NULL DEFAULT 0 | |
last_error |
VARCHAR(255) NULL |
KEY idx_de_pending (published_at, occurred_at) -- worker: WHERE published_at IS NULL
KEY idx_de_tenant (entity_type, entity_id, occurred_at)
KEY idx_de_name (name, occurred_at)
idx_de_pending کوئری worker است:
SELECT * FROM domain_events
WHERE published_at IS NULL AND attempts < 5
ORDER BY occurred_at ASC
LIMIT 100
attempts < 5 سقف تلاش. ردیف مرده با last_error باقی میماند تا ادمین ببیند —
حذف خاموش یعنی رویداد گمشدهٔ بیرد.
تغییر جدول موجود
هیچ. resource_occupancy (تسک ۰۷) ستونهای لازم برای گزارش بهرهوری را دارد:
start_at, end_at, occupancy_kind, status, resource_id.
appointments.plan_total_minutes (تسک ۰۷) پیشبینی را دارد.
AppointmentEvent موجود دستنخورده میماند — تاریخچهٔ وضعیت نوبت است، رویداد دامنه نیست.
تفاوتشان را در docs/architecture/domain-events.md بنویس:
AppointmentEvent |
DomainEventLog |
|
|---|---|---|
| دامنه | فقط نوبت | همهٔ دامنهها |
| مصرفکننده | UI تاریخچه | سیستمهای دیگر (پیامک، حسابداری) |
| انتشار | ندارد | messenger |
کوئری گزارش بهرهوری
SELECT ro.resource_id,
SUM(ro.end_at - ro.start_at) AS occupied_seconds,
SUM(CASE WHEN ro.occupancy_kind <> 'passive'
THEN ro.end_at - ro.start_at ELSE 0 END) AS active_seconds,
COUNT(DISTINCT ro.appointment_id) AS appointment_count
FROM resource_occupancy ro
WHERE ro.entity_type = :type AND ro.entity_id = :id
AND ro.status = 'booked'
AND ro.start_at >= :from AND ro.start_at < :to
GROUP BY ro.resource_id
ro.start_at < :to (نه end_at <= :to) — نوبتی که در بازه شروع شده ولی بیرون تمام شده،
باید شمرده شود. جزئی است ولی روی گزارش هفتگی چند درصد اختلاف میسازد.
ایندکس idx_occ_tenant_range (entity_type, entity_id, start_at) تسک ۰۷ این را پوشش میدهد.
کوئری دقت برنامه
SELECT a.service_item_id,
AVG(a.plan_total_minutes) AS planned_avg,
AVG((ps.ended_at - ps.started_at) / 60) AS actual_avg,
COUNT(*) AS sample
FROM appointments a
JOIN patient_sessions ps ON ps.appointment_id = a.id
WHERE a.entity_type = :type AND a.entity_id = :id
AND a.status = 'completed'
AND a.slot_start >= :from AND a.slot_start < :to
AND a.plan_total_minutes IS NOT NULL
AND ps.ended_at IS NOT NULL
GROUP BY a.service_item_id
HAVING sample >= 10
⚠️ بررسی لازم پیش از پیادهسازی: patient_sessions باید appointment_id و
started_at/ended_at داشته باشد. اگر ندارد، دو گزینه:
- از
appointment_eventsاستفاده کن: فاصلهٔ بین انتقال بهsalonو انتقال بهcompleted - اگر آن هم نیست، این گزارش به یک تسک جدا موکول شود و فقط گزارش بهرهوری در این تسک بماند
تصمیم را بگیر و بنویس — نه یک گزارش با داده حدسی.
نگهداشت
ddev exec php bin/console app:events:prune --older-than=180d --force
ردیفهای published_at IS NOT NULL قدیمیتر از ۶ ماه. ردیفهای شکستخورده
(published_at IS NULL AND attempts >= 5) هرگز حذف نمیشوند.
Migration
ddev exec php bin/console doctrine:migrations:diff --no-interaction
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
worker انتشار در config/packages/messenger.yaml و scheduler:
# هر ۱۰ ثانیه
App\Shared\Event\Message\FlushOutboxMessage: { frequency: 10 }
⚠️ طبق حافظهٔ عملیاتی پروژه، worker های Coolify باید loop-wrap شوند تا کانتینر خارج نشود. دستور جدید را با همان الگو اضافه کن.
طبقهبندی tenant
| جدول | وضعیت |
|---|---|
domain_events |
جفت tenant |