Files
clinicpro/docs/new_feture/taskes/task-06-availability-engine/database.md
T
hamed 021d0eb6b2 feat: implement cancellation policy, no-show tracking, and waitlist management
- 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.
2026-07-30 11:43:58 +03:30

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 همهٔ منابع آن محیط در آن روز
ثبت/لغو نوبت فقط monthwin هرگز (اشغال کش نمی‌شود)

آخرین سطر مهم‌ترین است: اگر کسی وسوسه شد اشغال را هم کش کند، نتیجه‌اش نمایش وقتِ گرفته‌شده و شکست رزرو در مرحلهٔ آخر است.

تست کارایی — داده مصنوعی

ddev exec php bin/console app:dev:seed-availability-benchmark --force

می‌سازد:

  • ۱ شعبه · ۳ اتاق (ظرفیت ۱) · ۲ اپراتور · ۳ دستگاه در یک استخر
  • ۱ سرویس با ۴ بخش (سناریوی مستند)
  • ۵۰۰ نوبت پراکنده در ۳۰ روز آینده

AvailabilityPerformanceTest روی همین داده اجرا می‌شود و دو چیز را می‌سنجد:

self::assertLessThan(500, $elapsedMs, 'جستجوی ۳۰ روزه باید زیر نیم ثانیه باشد');
self::assertLessThanOrEqual(5, $queryCount, 'تعداد کوئری نباید با تعداد روز رشد کند');

شرط دوم مهم‌تر از اولی است: زمان روی ماشین‌های مختلف فرق می‌کند، تعداد کوئری نه.