Files
clinicpro/docs/new_feture/taskes/task-02-resource-model/implementation_notes.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

6.0 KiB
Raw Blame History

نکات پیاده‌سازی — تسک ۰۲

۱. ظرفیت: یک ردیف با ظرفیت ۳، نه سه ردیف

مستند بند ۶ صریح است و دلیلش در تسک ۰۶ روشن می‌شود: با سه ردیف، موتور جستجو باید سه تقویم را ادغام کند و «کدام تخت» بشود یک تصمیم که هیچ‌کس نمی‌خواهد بگیرد. با ظرفیت ۳، شرط اشغال یک شمارش ساده است:

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 جدول طبقه‌بندی را به‌روز کن.