# دیتابیس — تسک ۰۵ ## `segment_templates` | ستون | نوع | توضیح | |---|---|---| | `id` | INT PK AI | | | `uuid` | VARCHAR(36) UNIQUE | از request می‌آید | | `entity_type` / `entity_id` | VARCHAR(10) / INT NOT NULL | | | `owner_type` | VARCHAR(10) NOT NULL | `service` \| `option` | | `service_item_id` | INT NULL | FK → `service_items.id` ON DELETE CASCADE | | `service_option_id` | INT NULL | FK → `service_options.id` ON DELETE CASCADE | | `name` | VARCHAR(150) NOT NULL | | | `segment_type` | VARCHAR(40) NOT NULL | کلید ادغام | | `sequence` | SMALLINT NOT NULL | | | `fixed_minutes` | SMALLINT NULL | | | `duration_share` | SMALLINT NULL | درصد ۰..۱۰۰ | | `patient_present` | TINYINT(1) NOT NULL DEFAULT 1 | | | `mergeable` | TINYINT(1) NOT NULL DEFAULT 0 | | | `active` | TINYINT(1) NOT NULL DEFAULT 1 | | | `created_at`/`updated_at` | INT NOT NULL | | ```sql KEY idx_seg_tpl_tenant (entity_type, entity_id, active) KEY idx_seg_tpl_service (service_item_id, sequence) KEY idx_seg_tpl_option (service_option_id, sequence) ``` قیدهای اپلیکیشنی (در سازنده/سرویس، نه `CHECK`): - دقیقاً یکی از `service_item_id` / `service_option_id` غیر-NULL و با `owner_type` سازگار - دقیقاً یکی از `fixed_minutes` / `duration_share` غیر-NULL - جمع `duration_share` بخش‌های یک سرویس = ۱۰۰ (اگر حداقل یکی سهمی باشد) ## `segment_requirements` | ستون | نوع | توضیح | |---|---|---| | `id` | INT PK AI | | | `uuid` | VARCHAR(36) UNIQUE | | | `segment_template_id` | INT NOT NULL | FK ON DELETE CASCADE | | `resource_type_id` | INT NOT NULL | FK → `resource_types.id` ON DELETE RESTRICT | | `count` | SMALLINT NOT NULL DEFAULT 1 | | | `resource_pool_id` | INT NULL | FK → `resource_pools.id` ON DELETE SET NULL | | `specific_resource_id` | INT NULL | FK → `clinic_resources.id` ON DELETE SET NULL | | `required_skills` | JSON NULL | آرایهٔ `skill_id` | | `constraints` | JSON NULL | فهرست بسته — جدول architecture | | `occupancy` | VARCHAR(10) NOT NULL DEFAULT 'exclusive' | `exclusive`\|`shared`\|`passive` | | `sort_order` | SMALLINT NOT NULL DEFAULT 0 | | ```sql KEY idx_seg_req_segment (segment_template_id, sort_order) KEY idx_seg_req_pool (resource_pool_id) ``` فرزند aggregate با ریشهٔ `SegmentTemplate` — uuid از request فقط در `PUT /segment-template/{uuid}/requirements` می‌آید که خودش از ریشه لنگر می‌خورد، پس ستون tenant لازم ندارد. > `required_skills` عمداً JSON است نه جدول واسط: همیشه کامل خوانده و کامل جایگزین می‌شود، > و هیچ کوئری‌ای از سمت مهارت به نیازمندی نمی‌رود. جدول واسط اینجا فقط سه JOIN اضافه > به مسیر داغ تسک ۰۶ می‌آورد. ## هیچ جدول جدیدی برای «برنامهٔ ساخته‌شده» نیست `AppointmentPlan` یک DTO درون‌حافظه‌ای است، نه entity. ذخیره‌اش وقتی معنی پیدا می‌کند که نوبت ثبت شود — که کار تسک ۰۷ است (`appointment_segments`). دلیل: برنامه یک تابع خالص از (سرویس، آیتم‌ها، بیمار، شعبه، قوانین فعال) است. ذخیره‌کردنش پیش از ثبت یعنی نگه‌داشتن حالت موقتی که باید منقضی شود — همان مسئله‌ای که رزرو موقت تسک ۰۷ حل می‌کند و دو مکانیزم موازی لازم نیست. ## Migration ```bash ddev exec php bin/console doctrine:migrations:diff --no-interaction ddev exec php bin/console doctrine:migrations:migrate --no-interaction ddev exec php bin/console app:segment:seed-templates --preset=beauty --tenant=clinic:12 --force ``` `app:segment:seed-templates` سه پریست دارد (مستند بند ۱۷: «الگوی آماده برای هر نوع کلینیک»): | پریست | بخش‌ها | |---|---| | `beauty` | آماده‌سازی ۵ · انتظار ۳۰ (فقط اتاق) · درمان (سهمی ۱۰۰) · مراقبت ۵ | | `dental` | آماده‌سازی ۵ · درمان (سهمی ۱۰۰) · تمیزکاری یونیت ۱۰ (بدون حضور بیمار) | | `physio` | درمان (سهمی ۱۰۰) | dry-run پیش‌فرض. پریست‌ها روی سرویس‌های موجود اعمال نمی‌شوند مگر با `--service=`. ## طبقه‌بندی tenant | جدول | وضعیت | |---|---| | `segment_templates` | جفت tenant | | `segment_requirements` | `AGGREGATE_CHILDREN` → ریشه `SegmentTemplate` |