- 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.
6.0 KiB
نکات پیادهسازی — تسک ۰۲
۱. ظرفیت: یک ردیف با ظرفیت ۳، نه سه ردیف
مستند بند ۶ صریح است و دلیلش در تسک ۰۶ روشن میشود: با سه ردیف، موتور جستجو باید سه تقویم را ادغام کند و «کدام تخت» بشود یک تصمیم که هیچکس نمیخواهد بگیرد. با ظرفیت ۳، شرط اشغال یک شمارش ساده است:
SELECT COUNT(*) FROM resource_occupancy
WHERE resource_id = ? AND [بازهها تداخل دارند] AND status IN (…)
→ اگر < capacity، جا هست
این را همین حالا در docs/api/resource.md بنویس تا کسی وسوسه نشود «اتاق ۱ تخت ۲» بسازد.
۲. ClinicResource نه Resource
نام کلاس Resource در PHP مشکل ندارد ولی در این کدبیس با App\Shared\… و مفهوم
«منبع API» قاطی میشود و در جستجوی کد نویز شدیدی میسازد. نام جدول clinic_resources
هم به همان دلیل. متغیرها و متدها میتوانند $resource باشند.
۳. پلها و active
ClinicStaff.active = false باید ClinicResource.active را هم false کند، وگرنه پرسنل
غیرفعال همچنان در جستجوی وقت ظاهر میشود. این را در StaffService (تسک موجود) با یک
فراخوانی به ResourceLinker::syncActive() انجام بده — نه با Doctrine lifecycle callback،
چون callback در getArrayResult() اجرا نمیشود و رفتار نامتقارن میسازد.
عکسش برقرار نیست: غیرفعال کردن منبع، پرسنل را غیرفعال نمیکند (پرسنل ممکن است فقط اداری باشد).
۴. setup_minutes / cleanup_minutes چه هستند و چه نیستند
- جزو نوبت بیمار نیستند — بیمار ساعت ۱۰:۰۰ میآید و ۱۰:۳۰ میرود
- منبع را اشغال میکنند — یونیت از ۹:۵۵ تا ۱۰:۴۰ در دسترس نیست
پس در تسک ۰۷ بازهٔ ثبتشده در resource_occupancy گستردهتر از بازهٔ نوبت است. الان فقط
ستون را بساز و در docs/api/resource.md این تفاوت را بنویس؛ محاسبهاش کار تسک ۰۶ است.
اشتباه رایج: این را با WeeklySchedule.meta.buffer_minutes موجود یکی گرفتن. آن یکی
فاصلهٔ سراسری بین دو نوبتِ پزشک است؛ این یکی per منبع است. تا وقتی حالت resource
نیامده، هر دو کنار هم زندگی میکنند و buffer_minutes دستنخورده میماند.
۵. مهارت را با jobTitle قاطی نکن
ClinicStaff.jobTitle متن آزاد و برای نمایش است. مهارت یک موجودیت با هویت است که در
شرط نیازمندی (تسک ۰۵) و شرط قانون (تسک ۰۹) استفاده میشود. هیچجا jobTitle را برای
تصمیمگیری parse نکن.
۶. edge case ها
| حالت | رفتار درست |
|---|---|
| منبع بدون هیچ پل (دستگاه) | معتبر — حالت عادی تجهیزات |
دو پل همزمان (doctor_id و staff_id) |
422 در سازنده |
حذف resource_type که منبع دارد |
422 |
حذف resource_type با is_system=1 |
422 همیشه |
| غیرفعال کردن منبعی که نوبت آیندهٔ فعال دارد | مجاز، ولی پاسخ شامل warnings[] با تعداد نوبتها |
| استخر با صفر عضو | معتبر (در حال ساخت)، ولی تسک ۰۶ آن را «هیچ منبعی» میبیند |
capacity روی منبع type=doctor بزرگتر از ۱ |
422 — پزشک همزمان دو بیمار ندارد |
level خارج از ۱..۵ |
422 |
| مهارت محیط A روی منبع محیط B | 404 (پیش از هر بررسی: TenantOwnershipChecker) |
۷. کارایی
پرسوجوی داغ تسک ۰۶: «منابع فعالِ شعبهٔ X از نوع Y که مهارت Z را دارند».
// ClinicResourceRepository::findEligible()
// یک کوئری با JOIN به resource_skills، بدون N+1
$qb->select('r')
->from(ClinicResource::class, 'r')
->join('r.skills', 'rs')
->where('r.branch = :branch')
->andWhere('r.type = :type')
->andWhere('r.active = true')
->andWhere('rs.skill IN (:skills)')
->groupBy('r.id')
->having('COUNT(DISTINCT rs.skill) = :skillCount'); // همهٔ مهارتها، نه یکی
HAVING COUNT(DISTINCT …) عمدی است: نیازمندی «مهارت الف و ب» یعنی هر دو، نه یکی.
۸. تست
tests/Resource/ResourceCrudTest.php
- ساخت منبع + خواندن → tenant و branch درست
- منبع با شعبهٔ محیط دیگر → 404
- capacity=0 → 422 · capacity=2 روی type=doctor → 422
- دو پل همزمان → 422
tests/Resource/SkillAssignmentTest.php
- PUT skills جایگزینی کامل (حذف ندادهها)
- level خارج بازه → 422
- حذف مهارتِ در استفاده → 422
tests/Resource/ResourcePoolTest.php
- عضو از شعبهٔ دیگر → 422
- عضو از نوع دیگر → 422
tests/Resource/ResourceEligibilityTest.php
- findEligible با دو مهارت: منبعی که فقط یکی را دارد برنمیگردد
tests/Resource/BackfillResourceTest.php
- idempotent: دو بار اجرا = یک بار
tests/Shared/TenantSchemaCoverageTest.php
tests/Shared/TenantLookupInventoryTest.php ← شمارنده بهروز شود
۹. مستندات
docs/api/resource.md بساز. در docs/api/staff.md یک بخش «رابطه با منبع» اضافه کن.
docs/architecture/tenancy.md جدول طبقهبندی را بهروز کن.