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

96 lines
4.1 KiB
Markdown

# دیتابیس — تسک ۰۶
این تسک **جدول جدیدی نمی‌سازد** جز کش. مصرف‌کنندهٔ جدول‌های تسک ۰۲/۰۳ و
`resource_occupancy` تسک ۰۷ است.
⚠️ **وابستگی معکوس:** موتور جستجو به `resource_occupancy` نیاز دارد ولی آن جدول در تسک ۰۷
ساخته می‌شود. راه‌حل: **مهاجرت جدول `resource_occupancy` در همین تسک انجام شود** و تسک ۰۷
فقط منطق نوشتن در آن را اضافه کند. تعریف کامل جدول در
[task-07/database.md](../task-07-hold-and-book/database.md) است؛ اینجا فقط ایندکس‌های
لازم برای خواندن ذکر می‌شوند.
## ایندکس‌های حیاتی خواندن
```sql
-- کوئری داغ: اشغال‌های این منابع در این بازه
KEY idx_occupancy_resource_range (resource_id, start_at, end_at, status)
```
پرس‌وجو:
```sql
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 موجود):
```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` هرگز** (اشغال کش نمی‌شود) |
آخرین سطر مهم‌ترین است: اگر کسی وسوسه شد اشغال را هم کش کند، نتیجه‌اش نمایش وقتِ
گرفته‌شده و شکست رزرو در مرحلهٔ آخر است.
## تست کارایی — داده مصنوعی
```bash
ddev exec php bin/console app:dev:seed-availability-benchmark --force
```
می‌سازد:
- ۱ شعبه · ۳ اتاق (ظرفیت ۱) · ۲ اپراتور · ۳ دستگاه در یک استخر
- ۱ سرویس با ۴ بخش (سناریوی مستند)
- ۵۰۰ نوبت پراکنده در ۳۰ روز آینده
`AvailabilityPerformanceTest` روی همین داده اجرا می‌شود و **دو** چیز را می‌سنجد:
```php
self::assertLessThan(500, $elapsedMs, 'جستجوی ۳۰ روزه باید زیر نیم ثانیه باشد');
self::assertLessThanOrEqual(5, $queryCount, 'تعداد کوئری نباید با تعداد روز رشد کند');
```
شرط دوم مهم‌تر از اولی است: زمان روی ماشین‌های مختلف فرق می‌کند، تعداد کوئری نه.