- 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.
144 lines
6.1 KiB
Markdown
144 lines
6.1 KiB
Markdown
# دیتابیس — تسک ۰۲
|
|
|
|
## `resource_types`
|
|
|
|
| ستون | نوع | توضیح |
|
|
|---|---|---|
|
|
| `id` | INT PK AI | |
|
|
| `uuid` | VARCHAR(36) UNIQUE | |
|
|
| `entity_type` / `entity_id` | VARCHAR(10) / INT NOT NULL | |
|
|
| `code` | VARCHAR(40) NOT NULL | `doctor`, `staff`, `room`, `device`, … |
|
|
| `name` | VARCHAR(100) NOT NULL | نام نمایشی فارسی |
|
|
| `is_system` | TINYINT(1) NOT NULL DEFAULT 0 | نوعهای `doctor/staff/room` توسط backfill ساخته میشوند و حذف نمیشوند |
|
|
| `active` | TINYINT(1) NOT NULL DEFAULT 1 | |
|
|
| `created_at`/`updated_at` | INT NOT NULL | |
|
|
|
|
```sql
|
|
KEY idx_resource_types_tenant (entity_type, entity_id, active)
|
|
UNIQUE KEY uniq_rt_tenant_code (entity_type, entity_id, code)
|
|
```
|
|
|
|
## `clinic_resources`
|
|
|
|
| ستون | نوع | توضیح |
|
|
|---|---|---|
|
|
| `id` | INT PK AI | |
|
|
| `uuid` | VARCHAR(36) UNIQUE | |
|
|
| `entity_type` / `entity_id` | VARCHAR(10) / INT NOT NULL | از `branch` مشتق میشود |
|
|
| `branch_id` | INT NOT NULL | FK → `branches.id` ON DELETE RESTRICT |
|
|
| `resource_type_id` | INT NOT NULL | FK → `resource_types.id` ON DELETE RESTRICT |
|
|
| `name` | VARCHAR(150) NOT NULL | |
|
|
| `capacity` | SMALLINT NOT NULL DEFAULT 1 | ظرفیت همزمان |
|
|
| `setup_minutes` | SMALLINT NOT NULL DEFAULT 0 | آمادهسازی پیش از بیمار |
|
|
| `cleanup_minutes` | SMALLINT NOT NULL DEFAULT 0 | تمیزکاری پس از بیمار |
|
|
| `attributes` | JSON NULL | اسکالر فقط، حداکثر ۲۰ کلید |
|
|
| `doctor_id` | INT NULL UNIQUE | FK → `doctors.id` ON DELETE CASCADE |
|
|
| `staff_id` | INT NULL UNIQUE | FK → `clinic_staff.id` ON DELETE CASCADE |
|
|
| `room_id` | INT NULL UNIQUE | FK → `rooms.id` ON DELETE CASCADE |
|
|
| `active` | TINYINT(1) NOT NULL DEFAULT 1 | |
|
|
| `created_at`/`updated_at` | INT NOT NULL | |
|
|
|
|
```sql
|
|
KEY idx_resources_tenant (entity_type, entity_id, active)
|
|
KEY idx_resources_branch_type (branch_id, resource_type_id, active)
|
|
UNIQUE KEY uniq_resource_doctor (doctor_id)
|
|
UNIQUE KEY uniq_resource_staff (staff_id)
|
|
UNIQUE KEY uniq_resource_room (room_id)
|
|
```
|
|
|
|
سه کلید یکتای تهیپذیر تضمین میکنند یک پزشک/پرسنل/اتاق بیش از یک منبع نگیرد.
|
|
MariaDB چند `NULL` را در UNIQUE میپذیرد، پس دستگاههای بدون پل مشکلی ندارند.
|
|
|
|
> **قید سطح اپلیکیشن (نه DB):** حداکثر یکی از `doctor_id`/`staff_id`/`room_id` غیر-NULL.
|
|
> در سازندهٔ entity اجبار شود؛ MariaDB `CHECK` چندستونی را قابل اتکا اجرا نمیکند.
|
|
|
|
## `skills` و `resource_skills`
|
|
|
|
```sql
|
|
CREATE TABLE skills (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
uuid VARCHAR(36) NOT NULL UNIQUE,
|
|
entity_type VARCHAR(10) NOT NULL,
|
|
entity_id INT NOT NULL,
|
|
name VARCHAR(120) NOT NULL,
|
|
active TINYINT(1) NOT NULL DEFAULT 1,
|
|
created_at INT NOT NULL,
|
|
updated_at INT NOT NULL,
|
|
KEY idx_skills_tenant (entity_type, entity_id, active)
|
|
);
|
|
|
|
CREATE TABLE resource_skills (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
resource_id INT NOT NULL,
|
|
skill_id INT NOT NULL,
|
|
level TINYINT NOT NULL DEFAULT 1, -- 1..5
|
|
created_at INT NOT NULL,
|
|
UNIQUE KEY uniq_resource_skill (resource_id, skill_id),
|
|
KEY idx_resource_skills_skill (skill_id, level),
|
|
CONSTRAINT fk_rs_resource FOREIGN KEY (resource_id) REFERENCES clinic_resources(id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_rs_skill FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE RESTRICT
|
|
);
|
|
```
|
|
|
|
`idx_resource_skills_skill (skill_id, level)` عمدی است: پرسوجوی داغِ تسک ۰۶
|
|
«کدام منابع مهارت X را دارند» از این سمت میآید.
|
|
|
|
`resource_skills` بدون ستون tenant — فرزند aggregate با ریشهٔ `ClinicResource`،
|
|
و uuid از request نمیگیرد (همیشه از `/resource/{uuid}/skills`).
|
|
|
|
## `resource_pools` و `resource_pool_members`
|
|
|
|
```sql
|
|
CREATE TABLE resource_pools (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
uuid VARCHAR(36) NOT NULL UNIQUE,
|
|
entity_type VARCHAR(10) NOT NULL,
|
|
entity_id INT NOT NULL,
|
|
branch_id INT NOT NULL,
|
|
resource_type_id INT NOT NULL,
|
|
name VARCHAR(150) NOT NULL,
|
|
active TINYINT(1) NOT NULL DEFAULT 1,
|
|
created_at INT NOT NULL,
|
|
updated_at INT NOT NULL,
|
|
KEY idx_pools_tenant (entity_type, entity_id, active),
|
|
KEY idx_pools_branch (branch_id, resource_type_id),
|
|
CONSTRAINT fk_pool_branch FOREIGN KEY (branch_id) REFERENCES branches(id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_pool_type FOREIGN KEY (resource_type_id) REFERENCES resource_types(id) ON DELETE RESTRICT
|
|
);
|
|
|
|
CREATE TABLE resource_pool_members (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
pool_id INT NOT NULL,
|
|
resource_id INT NOT NULL,
|
|
priority SMALLINT NOT NULL DEFAULT 0, -- ترتیب ترجیح در استراتژی انتخاب
|
|
UNIQUE KEY uniq_pool_resource (pool_id, resource_id),
|
|
KEY idx_pool_members_resource (resource_id),
|
|
CONSTRAINT fk_pm_pool FOREIGN KEY (pool_id) REFERENCES resource_pools(id) ON DELETE CASCADE,
|
|
CONSTRAINT fk_pm_resource FOREIGN KEY (resource_id) REFERENCES clinic_resources(id) ON DELETE CASCADE
|
|
);
|
|
```
|
|
|
|
## Migration و backfill
|
|
|
|
```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:resource:backfill # dry-run
|
|
ddev exec php bin/console app:resource:backfill --force
|
|
```
|
|
|
|
`app:resource:backfill` برای هر محیط:
|
|
1. سه `resource_type` سیستمی میسازد (`doctor`, `staff`, `room`) اگر نباشند
|
|
2. هر `Room` فعال → یک منبع با `capacity` همان اتاق
|
|
3. هر `ClinicStaff` فعال → یک منبع `type=staff` در شعبهٔ اول محیط
|
|
4. هر `Doctor` دارای `WeeklySchedule` در آن محیط → یک منبع `type=doctor`
|
|
|
|
idempotent باشد: اجرای دوباره چیزی دوباره نمیسازد.
|
|
|
|
## طبقهبندی tenant
|
|
|
|
| جدول | وضعیت |
|
|
|---|---|
|
|
| `resource_types`, `clinic_resources`, `skills`, `resource_pools` | جفت tenant |
|
|
| `resource_skills`, `resource_pool_members` | `AGGREGATE_CHILDREN` |
|