- 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.1 KiB
دیتابیس — تسک ۰۶
این تسک جدول جدیدی نمیسازد جز کش. مصرفکنندهٔ جدولهای تسک ۰۲/۰۳ و
resource_occupancy تسک ۰۷ است.
⚠️ وابستگی معکوس: موتور جستجو به resource_occupancy نیاز دارد ولی آن جدول در تسک ۰۷
ساخته میشود. راهحل: مهاجرت جدول resource_occupancy در همین تسک انجام شود و تسک ۰۷
فقط منطق نوشتن در آن را اضافه کند. تعریف کامل جدول در
task-07/database.md است؛ اینجا فقط ایندکسهای
لازم برای خواندن ذکر میشوند.
ایندکسهای حیاتی خواندن
-- کوئری داغ: اشغالهای این منابع در این بازه
KEY idx_occupancy_resource_range (resource_id, start_at, end_at, status)
پرسوجو:
SELECT resource_id, start_at, end_at, units
FROM resource_occupancy
WHERE resource_id IN (?, ?, …)
AND start_at < :to AND end_at > :from
AND status IN ('hold', 'booked')
resource_id ستون اول است چون IN روی آن، محدودکنندهترین شرط است. اضافه کردن
entity_type به ابتدای این ایندکس اشتباه است: اینجا فیلتر tenant از راه منابع
(که خودشان محیط دارند) اعمال شده و ستون tenant-پیشرو فقط ایندکس را بیاثر میکند.
یک ایندکس دوم tenant-پیشرو برای لیستهای پنل جدا تعریف میشود (تسک ۰۷).
تنظیمات جدید روی weekly_schedules.setting.meta
بدون تغییر schema (ستون JSON موجود):
{
"meta": {
"booking_mode": "resource",
"slot_granularity": 15,
"picker_strategy": "least_gap",
"buffer_minutes": 0
}
}
setMeta() باید کلیدهای جدید را با اعتبارسنجی بپذیرد:
slot_granularity∈ {5, 10, 15, 20, 30, 60}picker_strategy∈ کلیدهای ثبتشدهٔResourcePickerInterface
مقدار نامعتبر → مقدار فعلی حفظ میشود (همان الگوی موجود setMeta).
کش
Redis، بدون جدول. کلیدها:
cp:avail:res:{resourceId}:win:{Y-m-d} → JSON بازههای آزاد TTL تا پایان روز
cp:avail:month:{branchId}:{serviceId}:{Y-m} → JSON بولین per روز TTL 300s
ابطال:
| رویداد | کلیدهای باطل |
|---|---|
تغییر resource_calendars |
همهٔ win آن منبع |
ثبت/حذف resource_exceptions |
win آن منبع در بازهٔ استثنا |
تغییر branch_working_hours |
win همهٔ منابع آن شعبه |
تغییر tenant_holiday_overrides |
win همهٔ منابع آن محیط در آن روز |
| ثبت/لغو نوبت | فقط month — win هرگز (اشغال کش نمیشود) |
آخرین سطر مهمترین است: اگر کسی وسوسه شد اشغال را هم کش کند، نتیجهاش نمایش وقتِ گرفتهشده و شکست رزرو در مرحلهٔ آخر است.
تست کارایی — داده مصنوعی
ddev exec php bin/console app:dev:seed-availability-benchmark --force
میسازد:
- ۱ شعبه · ۳ اتاق (ظرفیت ۱) · ۲ اپراتور · ۳ دستگاه در یک استخر
- ۱ سرویس با ۴ بخش (سناریوی مستند)
- ۵۰۰ نوبت پراکنده در ۳۰ روز آینده
AvailabilityPerformanceTest روی همین داده اجرا میشود و دو چیز را میسنجد:
self::assertLessThan(500, $elapsedMs, 'جستجوی ۳۰ روزه باید زیر نیم ثانیه باشد');
self::assertLessThanOrEqual(5, $queryCount, 'تعداد کوئری نباید با تعداد روز رشد کند');
شرط دوم مهمتر از اولی است: زمان روی ماشینهای مختلف فرق میکند، تعداد کوئری نه.