- 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.4 KiB
دیتابیس — تسک ۰۴
تغییر جدول موجود: service_items
ALTER TABLE service_items
ADD COLUMN service_category_id INT NULL AFTER section_id,
ADD COLUMN session_count SMALLINT NOT NULL DEFAULT 1, -- ۱ = تکجلسه، >۱ = دورهای
ADD COLUMN preparation_note TEXT NULL, -- «آمادهسازی بیمار» — مستند بند ۵
ADD CONSTRAINT fk_service_items_category
FOREIGN KEY (service_category_id) REFERENCES service_categories(id) ON DELETE SET NULL,
ADD KEY idx_service_items_category (service_category_id);
duration_minutes موجود حذف نمیشود و همچنان «مدت پایهٔ سرویس» است. آیتمها روی آن
اضافه میکنند. در حالت booking_mode=service فعلی هیچ تغییری در رفتار نیست چون هیچ
سرویسی گروه ندارد و totalMinutes برابر همان duration_minutes میماند.
service_categories — درختی
| ستون | نوع | توضیح |
|---|---|---|
id |
INT PK AI | |
uuid |
VARCHAR(36) UNIQUE | |
entity_type / entity_id |
VARCHAR(10) / INT NOT NULL | |
parent_id |
INT NULL | FK → خودش، ON DELETE RESTRICT |
name |
VARCHAR(150) NOT NULL | |
path |
VARCHAR(255) NOT NULL | materialized path: /1/7/23/ |
depth |
TINYINT NOT NULL DEFAULT 0 | |
sort_order |
SMALLINT NOT NULL DEFAULT 0 | |
active |
TINYINT(1) NOT NULL DEFAULT 1 | |
created_at/updated_at |
INT NOT NULL |
KEY idx_svc_cat_tenant (entity_type, entity_id, active)
KEY idx_svc_cat_parent (parent_id, sort_order)
KEY idx_svc_cat_path (path)
materialized path بهجای adjacency خالص: تسک ۰۹ شرط «سرویس در دستهٔ جراحی یا
زیردستههایش» را میخواهد و با path LIKE '/1/7/%' یک کوئری است، نه یک پیمایش بازگشتی.
سقف عمق: ۴. در ServiceCategoryService اجبار شود.
item_groups
| ستون | نوع | توضیح |
|---|---|---|
id |
INT PK AI | |
uuid |
VARCHAR(36) UNIQUE | |
entity_type / entity_id |
VARCHAR(10) / INT NOT NULL | از سرویس مشتق میشود |
service_item_id |
INT NOT NULL | FK → service_items.id ON DELETE CASCADE |
name |
VARCHAR(150) NOT NULL | «نواحی موردنظر» |
min_select |
SMALLINT NOT NULL DEFAULT 0 | ۰ = اختیاری |
max_select |
SMALLINT NULL | NULL = نامحدود |
sort_order |
SMALLINT NOT NULL DEFAULT 0 | |
active |
TINYINT(1) NOT NULL DEFAULT 1 | |
created_at/updated_at |
INT NOT NULL |
KEY idx_item_groups_tenant (entity_type, entity_id, active)
KEY idx_item_groups_service (service_item_id, sort_order)
service_options — «آیتم» مستند
| ستون | نوع | توضیح |
|---|---|---|
id |
INT PK AI | |
uuid |
VARCHAR(36) UNIQUE | از request میآید |
entity_type / entity_id |
VARCHAR(10) / INT NOT NULL | |
item_group_id |
INT NOT NULL | FK ON DELETE CASCADE |
name |
VARCHAR(150) NOT NULL | |
solo_minutes |
SMALLINT NOT NULL | «زمان تنها» |
additional_minutes |
SMALLINT NULL | «زمان اضافه»؛ NULL → solo_minutes |
price_rials |
INT NOT NULL DEFAULT 0 | |
sort_order |
SMALLINT NOT NULL DEFAULT 0 | |
active |
TINYINT(1) NOT NULL DEFAULT 1 | |
created_at/updated_at |
INT NOT NULL |
KEY idx_service_options_tenant (entity_type, entity_id, active)
KEY idx_service_options_group (item_group_id, sort_order)
قید اپلیکیشنی: additional_minutes <= solo_minutes (زمان اضافه هرگز بیشتر از زمان تنها
نیست — آمادهسازی که دو بار نمیشود). نقض → 422 با پیام روشن.
service_option_relations
CREATE TABLE service_option_relations (
id INT PRIMARY KEY AUTO_INCREMENT,
source_option_id INT NOT NULL,
target_option_id INT NOT NULL,
type VARCHAR(20) NOT NULL, -- incompatible | requires
UNIQUE KEY uniq_sor (source_option_id, target_option_id, type),
KEY idx_sor_target (target_option_id, type),
CONSTRAINT fk_sor_source FOREIGN KEY (source_option_id) REFERENCES service_options(id) ON DELETE CASCADE,
CONSTRAINT fk_sor_target FOREIGN KEY (target_option_id) REFERENCES service_options(id) ON DELETE CASCADE
);
فرزند aggregate با ریشهٔ ServiceOption. قید اپلیکیشنی: source != target.
service_branch_overrides
CREATE TABLE service_branch_overrides (
id INT PRIMARY KEY AUTO_INCREMENT,
uuid VARCHAR(36) NOT NULL UNIQUE,
entity_type VARCHAR(10) NOT NULL,
entity_id INT NOT NULL,
service_item_id INT NOT NULL,
branch_id INT NOT NULL,
price_rials INT NULL, -- NULL = ارث از سرویس
duration_minutes SMALLINT NULL, -- NULL = ارث از سرویس
bookable TINYINT(1) NULL, -- NULL = ارث؛ 0 = این شعبه ارائه نمیدهد
created_at INT NOT NULL,
updated_at INT NOT NULL,
UNIQUE KEY uniq_sbo (service_item_id, branch_id),
KEY idx_sbo_tenant (entity_type, entity_id),
KEY idx_sbo_branch (branch_id),
CONSTRAINT fk_sbo_service FOREIGN KEY (service_item_id) REFERENCES service_items(id) ON DELETE CASCADE,
CONSTRAINT fk_sbo_branch FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE
);
سه ستون تهیپذیرند تا override جزئی ممکن باشد: فقط قیمت، بدون دست زدن به مدت.
Migration و backfill
ddev exec php bin/console doctrine:migrations:diff --no-interaction
ddev exec php bin/console doctrine:migrations:migrate --no-interaction
backfill لازم نیست: سرویسهای موجود گروه ندارند، DurationCalculator مقدار
service_items.duration_minutes را برمیگرداند و رفتار حالت service بدون تغییر میماند.
طبقهبندی tenant
| جدول | وضعیت |
|---|---|
service_categories, item_groups, service_options, service_branch_overrides |
جفت tenant |
service_option_relations |
AGGREGATE_CHILDREN → ریشه ServiceOption |
TenantLookupInventoryTest شمارنده دارد؛ findByUuid های جدید (ServiceOptionRepository,
ItemGroupRepository) باید با TenantOwnershipChecker جفت شوند و بعد عدد بهروز شود.