- 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.
7.1 KiB
نکات پیادهسازی — تسک ۰۴
۱. ServiceItem را تغییر نام نده
در این جدولها و کدها به آن ارجاع هست:
appointment_service_items · service_item_staff · service_item_consumables
service_item_audit_logs · tariffs.service_item_id · session_services
appointments.service_item_id
و در nobat724_front/services/response.js و clinic-pro-tauri/src/service/response.js
کلید service_item_uuid در بدنهٔ رزرو میرود. تغییر نام یعنی شکستن سه ریپو بدون یک
خطای build. کلاس جدید ServiceOption بساز و در docs/api/clinic-services.md جدول
واژگان (فایل architecture) را عیناً بنویس.
۲. /service-selection/validate هم عمومی است هم پنلی
سایت عمومی بدون توکن آن را صدا میزند (بیمار هنوز وارد نشده). پس:
- در
security.yamlمسیرش را whitelist کن - بدون کاربر احراز شده،
TenantFilterخاموش است → گارد دستی اجباری است: محیط ازdoctor_uuid+clinic_uuidدرخواست حل میشود و همهٔ uuid ها باTenantOwnershipChecker::belongsToPair()سنجیده میشوند - نرخمحدودسازی: این endpoint یک enumerate کنندهٔ کاتالوگ است.
symfony/rate-limiterروی IP، مثل بقیهٔ endpoint های عمومی
این دقیقاً همان اشتباهی است که یک بار در GET /api/v1/appointment-service-slots رخ داد و
در فاز ۸ tenancy رفع شد. تکرارش نکن.
۳. قطعیت محاسبهٔ مدت
دو انتخاب یکسان با ترتیب متفاوت باید همیشه یک عدد بدهند. تست:
$a = $calc->totalMinutes($service, [$face, $bikini]);
$b = $calc->totalMinutes($service, [$bikini, $face]);
self::assertSame($a, $b);
اگر این تست نباشد، اولین بهینهسازی که ترتیب آرایه را عوض کند، قیمتها را تغییر میدهد و هیچکس نمیفهمد چرا.
۴. تصمیم: مرتبسازی نزولی بر اساس «زمان تنها»
مستند نگفته کدام آیتم «اولی» است. سه گزینه بررسی شد:
| گزینه | مشکل |
|---|---|
| ترتیب انتخاب کاربر | غیرقطعی — همان انتخاب دو مدت میدهد |
sort_order تعریفشده |
کلینیک باید برای هر ترکیب فکر کند؛ عملاً پر نمیشود |
| بیشترین «زمان تنها» ✅ | قطعی، بدون ورودی اضافه، و از نظر کسبوکار درست: کار بزرگتر آمادهسازی را میبلعد |
انتخاب سوم. دلیلش را در کد بهصورت کامنت بنویس، وگرنه اولین بازبینیکننده آن را «مرتبسازی بیدلیل» میبیند و حذفش میکند.
۵. additional_minutes = null یعنی محافظهکار
$rest->getAdditionalMinutes() ?? $rest->getSoloMinutes()
نه صفر. اگر null را صفر بگیری، سرویسهای موجود که این ستون را ندارند یکشبه مدتشان نصف میشود و ظرفیت الکی باز میشود — یعنی نوبت روی نوبت.
۶. ناسازگاری متقارن، پیشنیاز جهتدار
// ناسازگاری — یک ردیف کافی است، هر دو جهت پرسوجو میشوند
$conflicts = $relationRepo->createQueryBuilder('r')
->where('r.type = :incompatible')
->andWhere('r.source IN (:sel) AND r.target IN (:sel)')
->setParameter('sel', $selectedIds)
->getQuery()->getResult();
پیشنیاز جهتدار است و حلقه ممنوع. assertNoCycle() با DFS هنگام ثبت اجرا شود، نه
هنگام اعتبارسنجی انتخاب — بررسی حلقه در مسیر داغ رزرو، هزینهٔ بیدلیل است.
۷. عمق درخت و حذف دسته
- سقف عمق ۴ (
depth <= 3با ریشهٔ صفر) - حذف دستهای که فرزند یا سرویس دارد →
422 - جابهجایی دسته →
pathهمهٔ نوادگان با یکUPDATE … SET path = REPLACE(path, :old, :new)بهروز شود، در یک تراکنش
۸. edge case ها
| حالت | رفتار درست |
|---|---|
| سرویس بدون هیچ گروه | معتبر — رفتار امروزی، مدت = duration_minutes |
گروه بدون هیچ آیتم فعال و min_select=1 |
انتخاب همیشه نامعتبر میشود → هشدار در پنل هنگام ذخیره |
min_select > max_select |
422 |
max_select بزرگتر از تعداد آیتمهای فعال |
مجاز؛ عملاً یعنی نامحدود |
additional_minutes > solo_minutes |
422 |
| آیتم غیرفعال در انتخاب | 422 با کد inactive_option |
override شعبه با bookable=0 |
سرویس در آن شعبه در لیست رزرو نیاید |
| دو آیتم ناسازگار در دو گروه مختلف | همچنان ناسازگار — رابطه بین آیتمهاست، نه گروهها |
| انتخاب آیتم از سرویس دیگر | 422 option_not_in_service (بعد از بررسی tenant) |
۹. تست
tests/ClinicService/DurationCalculatorTest.php
- یک آیتم → solo
- دو آیتم یک گروه → solo(بزرگتر) + additional(کوچکتر)
- دو گروه → هر گروه solo خودش
- additional=null → از solo استفاده شود
- قطعیت: جابهجایی ترتیب ورودی، همان عدد
tests/ClinicService/ServiceSelectionValidatorTest.php
- min_select نقض → کد min_select
- max_select نقض → کد max_select
- ناسازگار → کد incompatible با نام هر دو
- پیشنیاز غایب → کد missing_prerequisite
- چند خطا همزمان → همه با هم برگردند
- uuid محیط دیگر → 404 و هیچ اطلاعاتی در بدنه
tests/ClinicService/ServicePriceResolverTest.php
- override شعبه بر تعرفه اولویت دارد
- override جزئی (فقط قیمت) مدت را دست نمیزند
tests/ClinicService/ServiceCategoryTreeTest.php
- عمق ۵ → 422 · حذف دستهٔ دارای فرزند → 422 · جابهجایی path نوادگان
tests/ClinicService/BackwardCompatibilityTest.php
- سرویس بدون گروه: appointment-service-slots دقیقاً همان خروجی قبلی
آخرین تست مهمترین است: این تسک نباید رفتار نوبتدهی سرویسی فعلی را تغییر دهد.
۱۰. مستندات
docs/api/clinic-services.md بهروزرسانی با جدول واژگان + endpoint های جدید.
یادآوری: قرارداد POST /service-selection/validate را nobat724_front مصرف میکند و
شکستنش در build خطا نمیدهد.