Files
clinicpro/docs/new_feture/taskes/task-12-treatment-course/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

5.3 KiB

دیتابیس — تسک ۱۲

course_protocols

ستون نوع توضیح
id INT PK AI
uuid VARCHAR(36) UNIQUE
entity_type / entity_id VARCHAR(10) / INT NOT NULL
service_item_id INT NOT NULL FK ON DELETE CASCADE
session_count SMALLINT NOT NULL
min_days SMALLINT NOT NULL
ideal_days SMALLINT NOT NULL
max_days SMALLINT NOT NULL
prefer_same_resource TINYINT(1) NOT NULL DEFAULT 1
active TINYINT(1) NOT NULL DEFAULT 1
created_at/updated_at INT NOT NULL
UNIQUE KEY uniq_protocol_service (service_item_id)     -- یک پروتکل فعال per سرویس
KEY idx_protocols_tenant (entity_type, entity_id, active)

قید اپلیکیشنی: min_days <= ideal_days <= max_days و session_count >= 2 (دورهٔ یک‌جلسه‌ای همان نوبت تکی است).

course_protocol_steps

CREATE TABLE course_protocol_steps (
  id INT PRIMARY KEY AUTO_INCREMENT,
  protocol_id INT NOT NULL,
  session_number SMALLINT NOT NULL,
  params JSON NULL,                          -- {"energy": 12} — اسکالر
  override_duration_minutes SMALLINT NULL,
  UNIQUE KEY uniq_step (protocol_id, session_number),
  CONSTRAINT fk_step_protocol FOREIGN KEY (protocol_id) REFERENCES course_protocols(id) ON DELETE CASCADE
);

فرزند aggregate با ریشهٔ CourseProtocol.

treatment_courses

ستون نوع توضیح
id INT PK AI
uuid VARCHAR(36) UNIQUE
entity_type / entity_id VARCHAR(10) / INT NOT NULL
patient_record_id INT NOT NULL FK ON DELETE RESTRICT
service_item_id INT NOT NULL FK ON DELETE RESTRICT
protocol_id INT NOT NULL FK ON DELETE RESTRICT
session_count SMALLINT NOT NULL snapshot
min_days SMALLINT NOT NULL snapshot
ideal_days SMALLINT NOT NULL snapshot
max_days SMALLINT NOT NULL snapshot
patient_package_id INT NULL FK → patient_packages.id ON DELETE SET NULL
preferred_resource_id INT NULL FK → clinic_resources.id ON DELETE SET NULL
status VARCHAR(12) NOT NULL DEFAULT 'active' active|completed|abandoned
abandon_reason VARCHAR(255) NULL
started_at INT NOT NULL
completed_at INT NULL
created_at/updated_at INT NOT NULL
KEY idx_courses_tenant  (entity_type, entity_id, status, started_at)
KEY idx_courses_patient (patient_record_id, status)
UNIQUE KEY uniq_active_course (patient_record_id, service_item_id, status)

⚠️ uniq_active_course با MariaDB روی مقدار status کار نمی‌کند به شکلی که فقط active را یکتا کند (چند ردیف completed مجازند). راه درست: قید اپلیکیشنی در CourseStarter + کلید یکتای جزئی که MariaDB ندارد.

جایگزین: یک ستون active_course_key VARCHAR(64) NULL UNIQUE با همان الگوی Appointment.active_slot_key:

$this->activeCourseKey = $this->status === self::STATUS_ACTIVE
    ? sprintf('%d:%d', $this->patient->getId(), $this->service->getId())
    : null;

الگوی اثبات‌شدهٔ همین کدبیس — استفاده‌اش کن، دوباره اختراع نکن.

course_sessions

ستون نوع توضیح
id INT PK AI
uuid VARCHAR(36) UNIQUE
entity_type / entity_id VARCHAR(10) / INT NOT NULL
course_id INT NOT NULL FK ON DELETE CASCADE
session_number SMALLINT NOT NULL
params JSON NULL snapshot از course_protocol_steps
appointment_id INT NULL UNIQUE FK ON DELETE SET NULL
status VARCHAR(12) NOT NULL DEFAULT 'planned' planned|booked|completed|skipped
completed_at INT NULL
created_at/updated_at INT NOT NULL
UNIQUE KEY uniq_course_session (course_id, session_number)
UNIQUE KEY uniq_session_appointment (appointment_id)
KEY idx_sessions_tenant (entity_type, entity_id, status)
KEY idx_sessions_course (course_id, session_number)

uniq_session_appointment: یک نوبت به بیش از یک جلسهٔ دوره وصل نمی‌شود.

تغییر appointments

ALTER TABLE appointments
  ADD COLUMN course_session_id INT NULL,
  ADD CONSTRAINT fk_appointments_course_session
      FOREIGN KEY (course_session_id) REFERENCES course_sessions(id) ON DELETE SET NULL,
  ADD KEY idx_appointments_course_session (course_session_id);

دو طرفه است (course_sessions.appointment_id هم وجود دارد) — عمدی: لیست نوبت‌های پنل باید بدون JOIN بفهمد نوبت جزو دوره است، و صفحهٔ دوره باید بدون JOIN نوبت را پیدا کند. هر دو در CourseSessionLinker هم‌زمان ست می‌شوند؛ هیچ جای دیگری ننویسد.

Migration

ddev exec php bin/console doctrine:migrations:diff --no-interaction
ddev exec php bin/console doctrine:migrations:migrate --no-interaction

بدون backfill.

طبقه‌بندی tenant

جدول وضعیت
course_protocols, treatment_courses, course_sessions جفت tenant
course_protocol_steps AGGREGATE_CHILDREN → ریشه CourseProtocol