690514:2019 204-rfa-approval-refactor #01
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
# Glossary - คำศัพท์และคำย่อทางเทคนิค
|
||||
|
||||
**Project:** LCBP3-DMS
|
||||
**Version:** 1.7.0
|
||||
**Last Updated:** 2025-12-18
|
||||
**Version:** 1.9.0
|
||||
**Last Updated:** 2026-05-13
|
||||
|
||||
---
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
### A
|
||||
|
||||
**ADR (Architecture Decision Record)**
|
||||
เอกสารบันทึกการตัดสินใจทางสถาปัตยกรรมที่สำคัญ พร้อมบริบท ทางเลือก และเหตุผล
|
||||
เอกสารบันทึกการตัดสินใจทางสถาปัตยกรรมที่สำคัญ พร้อมบริบท ทางเลือก และเหตุผล (ดู ADR-001 ถึง ADR-021)
|
||||
|
||||
**API (Application Programming Interface)**
|
||||
ชุดคำสั่งและโปรโตคอลที่ใช้สำหรับการสื่อสารระหว่างระบบ
|
||||
@@ -448,6 +448,15 @@ Logging library สำหรับ Node.js
|
||||
**Closed**
|
||||
ปิดเรื่อง
|
||||
|
||||
**Lead Engineer**
|
||||
บทบาทวิศวกรอาวุโสที่มีอำนาจอนุมัติทางเทคนิคในระบบ RFA
|
||||
|
||||
**Parallel Review**
|
||||
กระบวนการตรวจสอบเอกสารพร้อมกันหลายบุคคลในขั้นตอนเดียวกัน (Workflow Engine)
|
||||
|
||||
**Polymorphic Distribution**
|
||||
การกระจายเอกสารที่ผู้รับสามารถเป็นได้ทั้ง User หรือ Role (ADR-019)
|
||||
|
||||
---
|
||||
|
||||
### Disciplines (สาขาวิชาชีพ)
|
||||
@@ -491,6 +500,6 @@ Logging library สำหรับ Node.js
|
||||
|
||||
---
|
||||
|
||||
**Version:** 1.7.0
|
||||
**Last Updated:** 2025-12-18
|
||||
**Next Review:** 2026-03-01
|
||||
**Version:** 1.9.0
|
||||
**Last Updated:** 2026-05-13
|
||||
**Next Review:** 2026-08-13
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
# 4. Access Control & RBAC Matrix (V1.8.0)
|
||||
# 4. Access Control & RBAC Matrix (V1.9.0)
|
||||
|
||||
---
|
||||
|
||||
title: 'Access Control & RBAC Matrix'
|
||||
version: 1.8.0
|
||||
version: 1.9.0
|
||||
status: APPROVED
|
||||
owner: Nattanin Peancharoen / Development Team
|
||||
last_updated: 2026-02-23
|
||||
last_updated: 2026-05-13
|
||||
related:
|
||||
|
||||
- specs/02-architecture/02-01-system-architecture.md
|
||||
- specs/03-implementation/03-02-backend-guidelines.md
|
||||
- specs/07-database/07-01-data-dictionary-v1.8.0.md
|
||||
- specs/05-Engineering-Guidelines/05-02-backend-guidelines.md
|
||||
- specs/03-Data-and-Storage/03-01-data-dictionary.md
|
||||
- specs/05-decisions/ADR-005-redis-usage-strategy.md
|
||||
- specs/05-decisions/ADR-001-unified-workflow-engine.md
|
||||
references:
|
||||
@@ -78,6 +78,7 @@ Global (ทั้งระบบ)
|
||||
| **Viewer** | Organization | Document viewer | View documents that have access permissions |
|
||||
| **Project Manager** | Project | Project manager | Manage members in the project (add/delete/assign roles), create/manage contracts in the project, view project reports |
|
||||
| **Contract Admin** | Contract | Contract administrator | Manage users in the contract, manage roles/permissions within the contract, view contract reports |
|
||||
| **Lead Engineer** | Contract | Lead Engineer | RFA Reviewer/Approver authority, technical validation, shop drawing approval |
|
||||
|
||||
### Master Data Management Authority
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: 'Data & Storage: Data Dictionary and Data Model Architecture'
|
||||
version: 1.8.7
|
||||
version: 1.9.0
|
||||
status: released
|
||||
owner: Nattanin Peancharoen
|
||||
last_updated: 2026-04-14
|
||||
@@ -84,7 +84,7 @@ erDiagram
|
||||
|
||||
---
|
||||
|
||||
# 3. Data Dictionary V1.8.0
|
||||
# 3. Data Dictionary V1.9.0
|
||||
|
||||
> หมายเหตุ: PK = Primary Key, FK = Foreign Key, AI = AUTO_INCREMENT. รูปแบบ Soft Delete จะปรากฏ Column `deleted_at DATETIME NULL` เป็นมาตรฐาน
|
||||
|
||||
@@ -165,14 +165,14 @@ erDiagram
|
||||
### 2.2 roles
|
||||
- - Purpose **: MASTER TABLE defining system roles WITH scope levels | COLUMN Name | Data TYPE | Constraints | Description | | ----------- | ------------ | --------------------------- | ---------------------------------------------------- |
|
||||
| role_id | INT | PRIMARY KEY,
|
||||
AUTO_INCREMENT | UNIQUE identifier FOR role | | role_name | VARCHAR(100) | NOT NULL | Role name (e.g., 'Superadmin', 'Document Control') | | scope | ENUM | NOT NULL | Scope LEVEL: GLOBAL,
|
||||
AUTO_INCREMENT | UNIQUE identifier FOR role | | **uuid** | **UUID** | **NOT NULL, DEFAULT (UUID()), UNIQUE** | **[delta-11] UUID Public Identifier (ADR-019) — ใช้ใน API Response / distribution_recipients** | | role_name | VARCHAR(100) | NOT NULL | Role name (e.g., 'Superadmin', 'Document Control') | | scope | ENUM | NOT NULL | Scope LEVEL: GLOBAL,
|
||||
Organization,
|
||||
Project,
|
||||
Contract | | description | TEXT | NULL | Role description | | is_system | BOOLEAN | DEFAULT FALSE | System role flag (cannot be deleted) |
|
||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
|
||||
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
|
||||
| deleted_at | DATETIME | NULL | Soft delete timestamp |
|
||||
** INDEXES **: - PRIMARY KEY (role_id) - INDEX (scope) ** Relationships \*\*: - Referenced by: role_permissions,
|
||||
** INDEXES **: - PRIMARY KEY (role_id) - **UNIQUE INDEX idx_roles_uuid (uuid) [delta-11]** - INDEX (scope) ** Relationships \*\*: - Referenced by: role_permissions,
|
||||
user_assignments ---
|
||||
|
||||
### 2.3 permissions
|
||||
@@ -2337,4 +2337,258 @@ PENDING_REVIEW ──→ VERIFIED ──→ IMPORTED (terminal)
|
||||
|
||||
---
|
||||
|
||||
**End of Data Dictionary V1.8.0**
|
||||
**End of Data Dictionary V1.9.0**
|
||||
|
||||
---
|
||||
|
||||
## **20. 🔍 RFA Approval System Tables (NEW v1.9.0)**
|
||||
|
||||
> Feature Branch: `1-rfa-approval-refactor` | Schema File: `lcbp3-v1.9.0-rfa-approval-schema.sql`
|
||||
|
||||
### 20.1 review_teams
|
||||
|
||||
**Purpose**: ทีมตรวจสอบแยกตาม Discipline สำหรับ RFA Approval
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| --------------------- | ------------ | ------------------------------- | --------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
|
||||
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID()| UUID Public Identifier (ADR-019) |
|
||||
| project_id | INT | NOT NULL, FK | Reference to projects |
|
||||
| name | VARCHAR(100) | NOT NULL | Team name |
|
||||
| description | VARCHAR(255) | NULL | Team description |
|
||||
| default_for_rfa_types | TEXT | NULL | Comma-separated RFA type codes (e.g. SDW,DDW) |
|
||||
| is_active | TINYINT(1) | NOT NULL, DEFAULT 1 | Active status |
|
||||
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
| updated_at | DATETIME(6) | NOT NULL, ON UPDATE | Last update timestamp |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
- PRIMARY KEY (id)
|
||||
- UNIQUE KEY uq_review_teams_uuid (uuid)
|
||||
- KEY idx_review_teams_project (project_id, is_active)
|
||||
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
|
||||
**Relationships**:
|
||||
|
||||
- Parent: projects
|
||||
- Children: review_team_members, review_tasks
|
||||
- Referenced by: distribution_recipients (recipient_type='TEAM')
|
||||
|
||||
---
|
||||
|
||||
### 20.2 review_team_members
|
||||
|
||||
**Purpose**: สมาชิกในทีมแยกตาม Discipline (M:N ระหว่าง review_teams และ users)
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| -------------- | ----------- | ------------------------------- | ------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
|
||||
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID()| UUID Public Identifier (ADR-019) |
|
||||
| team_id | INT | NOT NULL, FK | Reference to review_teams |
|
||||
| user_id | INT | NOT NULL, FK | Reference to users (user_id) |
|
||||
| discipline_id | INT | NOT NULL, FK | Reference to disciplines |
|
||||
| role | ENUM | NOT NULL, DEFAULT 'REVIEWER' | REVIEWER, LEAD, MANAGER |
|
||||
| priority_order | INT | NOT NULL, DEFAULT 0 | Assignment priority within discipline |
|
||||
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
- PRIMARY KEY (id)
|
||||
- UNIQUE KEY uq_review_team_members_uuid (uuid)
|
||||
- UNIQUE KEY uq_team_user_discipline (team_id, user_id, discipline_id)
|
||||
- FOREIGN KEY (team_id) REFERENCES review_teams(id) ON DELETE CASCADE
|
||||
- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
|
||||
- FOREIGN KEY (discipline_id) REFERENCES disciplines(id)
|
||||
|
||||
---
|
||||
|
||||
### 20.3 response_codes
|
||||
|
||||
**Purpose**: รหัสตอบกลับมาตรฐาน (Approval Matrix) สำหรับ RFA Review
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| --------------- | --------- | ----------------------------------- | -------------------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
|
||||
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID() | UUID Public Identifier (ADR-019) |
|
||||
| code | VARCHAR(10)| NOT NULL | Response code (1A, 1B, 1C, 1D, 1E, 1F, 1G, 2, 3, 4) |
|
||||
| sub_status | VARCHAR(10)| NULL | Optional sub-status |
|
||||
| category | ENUM | NOT NULL | ENGINEERING, MATERIAL, CONTRACT, TESTING, ESG |
|
||||
| description_th | TEXT | NOT NULL | Thai description |
|
||||
| description_en | TEXT | NOT NULL | English description |
|
||||
| implications | JSON | NULL | {affectsSchedule, affectsCost, requiresContractReview} |
|
||||
| notify_roles | TEXT | NULL | Comma-separated roles to notify (e.g. CONTRACT_MANAGER,QS_MANAGER) |
|
||||
| is_active | TINYINT(1)| NOT NULL, DEFAULT 1 | Active status |
|
||||
| is_system | TINYINT(1)| NOT NULL, DEFAULT 0 | System default — cannot delete |
|
||||
| created_at | DATETIME(6)| NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
- PRIMARY KEY (id)
|
||||
- UNIQUE KEY uq_response_codes_uuid (uuid)
|
||||
- UNIQUE KEY uq_response_code_category (code, category)
|
||||
- KEY idx_rc_category_active (category, is_active)
|
||||
|
||||
---
|
||||
|
||||
### 20.4 response_code_rules
|
||||
|
||||
**Purpose**: กฎการใช้ Response Code ต่อโครงการ/ประเภทเอกสาร (Project-level Override)
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ---------------------- | ----------- | ---------------------------- | ---------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
|
||||
| uuid | UUID | NOT NULL, UNIQUE | UUID Public Identifier (ADR-019) |
|
||||
| project_id | INT | NULL, FK | NULL = global default |
|
||||
| document_type_id | INT | NOT NULL, FK | Reference to correspondence_types |
|
||||
| response_code_id | INT | NOT NULL, FK | Reference to response_codes |
|
||||
| is_enabled | TINYINT(1) | NOT NULL, DEFAULT 1 | Enable/disable this code for the context |
|
||||
| requires_comments | TINYINT(1) | NOT NULL, DEFAULT 0 | Force comment when selecting this code |
|
||||
| triggers_notification | TINYINT(1) | NOT NULL, DEFAULT 0 | Send notification when used |
|
||||
| parent_rule_id | INT | NULL, FK | For inheritance tracking |
|
||||
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
| updated_at | DATETIME(6) | NOT NULL, ON UPDATE | Last update timestamp |
|
||||
|
||||
**Business Rules**:
|
||||
|
||||
- `project_id = NULL` = Global default rule (applies to all projects)
|
||||
- Project-level rules override global defaults
|
||||
- UNIQUE constraint: (project_id, document_type_id, response_code_id)
|
||||
|
||||
---
|
||||
|
||||
### 20.5 review_tasks
|
||||
|
||||
**Purpose**: งานตรวจสอบสำหรับแต่ละ Discipline ใน Parallel Review Flow
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ------------------------ | ----------- | ----------------------------------- | --------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
|
||||
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID() | UUID Public Identifier (ADR-019) |
|
||||
| rfa_revision_id | INT | NOT NULL, FK | Reference to rfa_revisions |
|
||||
| team_id | INT | NOT NULL, FK | Reference to review_teams |
|
||||
| discipline_id | INT | NOT NULL, FK | Reference to disciplines |
|
||||
| assigned_to_user_id | INT | NULL, FK | Assigned reviewer (NULL = auto-assign) |
|
||||
| status | ENUM | NOT NULL, DEFAULT 'PENDING' | PENDING, IN_PROGRESS, COMPLETED, DELEGATED, EXPIRED, CANCELLED |
|
||||
| due_date | DATE | NULL | Review due date |
|
||||
| response_code_id | INT | NULL, FK | Selected response code |
|
||||
| comments | TEXT | NULL | Reviewer comments |
|
||||
| attachments | JSON | NULL | Array of attachment publicIds (ADR-021) |
|
||||
| delegated_from_user_id | INT | NULL | Original assignee when task was delegated |
|
||||
| completed_at | TIMESTAMP | NULL | Completion timestamp |
|
||||
| version | INT | NOT NULL, DEFAULT 1 | Optimistic locking (ADR-002) |
|
||||
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
| updated_at | DATETIME(6) | NOT NULL, ON UPDATE | Last update timestamp |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
- UNIQUE KEY uq_review_task_per_revision_discipline (rfa_revision_id, team_id, discipline_id)
|
||||
- KEY idx_review_tasks_assigned (assigned_to_user_id, status)
|
||||
- FOREIGN KEY (assigned_to_user_id) REFERENCES users(user_id) ON DELETE SET NULL
|
||||
|
||||
**Business Rules**:
|
||||
|
||||
- One task per revision + team + discipline combination (UNIQUE enforced)
|
||||
- `version` column used for optimistic locking (ADR-002)
|
||||
- `attachments` JSON stores publicIds — resolved via AttachmentsService (no FK)
|
||||
|
||||
---
|
||||
|
||||
### 20.6 delegations
|
||||
|
||||
**Purpose**: การมอบหมายงาน (Task Delegation) ระหว่างผู้ใช้
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ------------------ | ----------- | ------------------------------- | ----------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
|
||||
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID()| UUID Public Identifier (ADR-019) |
|
||||
| delegator_user_id | INT | NOT NULL, FK | ผู้มอบหมาย → users(user_id) |
|
||||
| delegate_user_id | INT | NOT NULL, FK | ผู้รับมอบหมาย → users(user_id) |
|
||||
| start_date | DATE | NOT NULL | วันที่เริ่มการมอบหมาย |
|
||||
| end_date | DATE | NULL | วันที่สิ้นสุด — BullMQ job flip is_active=0 เมื่อผ่าน (ADR-008) |
|
||||
| scope | ENUM | NOT NULL, DEFAULT 'ALL' | ALL, RFA_ONLY, CORRESPONDENCE_ONLY, SPECIFIC_TYPES |
|
||||
| document_types | TEXT | NULL | Comma-separated doc type codes when scope=SPECIFIC_TYPES |
|
||||
| is_active | TINYINT(1) | NOT NULL, DEFAULT 1 | Managed by BullMQ scheduler — ห้าม flip manual |
|
||||
| reason | TEXT | NULL | เหตุผลการมอบหมาย |
|
||||
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
| updated_at | DATETIME(6) | NOT NULL, ON UPDATE | Last update timestamp |
|
||||
|
||||
**Business Rules**:
|
||||
|
||||
- **Auto-expiry**: BullMQ Scheduled Job ตรวจ `end_date < NOW()` และ flip `is_active = 0` (ADR-008)
|
||||
- **Active check query**: `WHERE delegator_user_id = ? AND is_active = 1 AND start_date <= NOW() AND (end_date IS NULL OR end_date >= NOW())`
|
||||
- ห้ามเปลี่ยน `is_active` โดยตรงในระดับ Service — ใช้ BullMQ เท่านั้น
|
||||
|
||||
---
|
||||
|
||||
### 20.7 reminder_rules
|
||||
|
||||
**Purpose**: กฎการแจ้งเตือน (Reminder/Escalation) สำหรับ Review Tasks
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| -------------------------- | ----------- | ------------------------ | -------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
|
||||
| uuid | UUID | NOT NULL, UNIQUE | UUID Public Identifier (ADR-019) |
|
||||
| name | VARCHAR(100)| NOT NULL | Rule name |
|
||||
| project_id | INT | NULL, FK | NULL = global rule |
|
||||
| document_type_id | INT | NULL, FK | NULL = applies to all types |
|
||||
| trigger_days_before_due | INT | NOT NULL, DEFAULT 2 | วันก่อน due date ที่จะแจ้งเตือน |
|
||||
| escalation_days_after_due | INT | NOT NULL, DEFAULT 1 | วันหลัง due date ที่จะ escalate |
|
||||
| reminder_type | ENUM | NOT NULL | DUE_SOON, ON_DUE, OVERDUE, ESCALATION_L1, ESCALATION_L2 |
|
||||
| recipients | TEXT | NOT NULL | Comma-separated: ASSIGNEE, MANAGER, PROJECT_MANAGER |
|
||||
| message_template_th | TEXT | NOT NULL | Thai message template |
|
||||
| message_template_en | TEXT | NOT NULL | English message template |
|
||||
| is_active | TINYINT(1) | NOT NULL, DEFAULT 1 | Active status |
|
||||
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
| updated_at | DATETIME(6) | NOT NULL, ON UPDATE | Last update timestamp |
|
||||
|
||||
---
|
||||
|
||||
### 20.8 distribution_matrices
|
||||
|
||||
**Purpose**: ตารางกำหนดกฎการกระจายเอกสาร (Distribution Matrix) ตาม Response Code
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ---------------- | ----------- | ------------------------ | ---------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
|
||||
| uuid | UUID | NOT NULL, UNIQUE | UUID Public Identifier (ADR-019) |
|
||||
| name | VARCHAR(100)| NOT NULL | Matrix name |
|
||||
| project_id | INT | NULL, FK | NULL = global matrix |
|
||||
| document_type_id | INT | NOT NULL, FK | Reference to correspondence_types |
|
||||
| response_code_id | INT | NULL, FK | NULL = applies to all codes |
|
||||
| conditions | JSON | NULL | {codes:["1A","1B"], excludeCodes:["3","4"]} |
|
||||
| is_active | TINYINT(1) | NOT NULL, DEFAULT 1 | Active status |
|
||||
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
|
||||
---
|
||||
|
||||
### 20.9 distribution_recipients
|
||||
|
||||
**Purpose**: ผู้รับเอกสารใน Distribution Matrix (Polymorphic Reference)
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| -------------------- | ----------- | ------------------------ | ----------------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal ID |
|
||||
| uuid | UUID | NOT NULL, UNIQUE | UUID Public Identifier (ADR-019) |
|
||||
| matrix_id | INT | NOT NULL, FK | Reference to distribution_matrices |
|
||||
| recipient_type | ENUM | NOT NULL | USER, ORGANIZATION, TEAM, ROLE |
|
||||
| recipient_public_id | UUID | NOT NULL | publicId ของ target entity (ดูตาราง Polymorphic Resolution ด้านล่าง) |
|
||||
| delivery_method | ENUM | NOT NULL, DEFAULT 'BOTH' | EMAIL, IN_APP, BOTH |
|
||||
| sequence | INT | NULL | For ordered delivery |
|
||||
| created_at | DATETIME(6) | NOT NULL, DEFAULT NOW | Record creation timestamp |
|
||||
|
||||
**Polymorphic Resolution Table**:
|
||||
|
||||
| recipient_type | recipient_public_id อ้างอิง | FK | หมายเหตุ |
|
||||
|---|---|---|---|
|
||||
| USER | users.uuid | Soft (no FK) | Resolved via UserService |
|
||||
| ORGANIZATION | organizations.uuid | Soft (no FK) | Resolved via OrgService |
|
||||
| TEAM | review_teams.uuid | Soft (no FK) | Q4A — ทีมตรวจสอบ RFA |
|
||||
| ROLE | roles.uuid | Soft (no FK) | Q4B — ต้องใช้หลัง Apply delta-11 |
|
||||
|
||||
**Business Rules**:
|
||||
|
||||
- **ไม่มี FK constraint** บน recipient_public_id โดยออกแบบ (Polymorphic Pattern)
|
||||
- Service Layer ต้องตรวจสอบ existence ก่อน save
|
||||
- ใช้ index `idx_dr_type_recipient (recipient_type, recipient_public_id)` สำหรับ lookup performance
|
||||
|
||||
---
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Database Indexing & Performance Strategy
|
||||
|
||||
**Version:** 1.0.0
|
||||
**Version:** 1.1.0 (v1.9.0 Core Schema)
|
||||
**Context:** Production-scale (100k+ documents, High Concurrency)
|
||||
**Database:** MySQL 8.x (On-Premise via Docker)
|
||||
**Database:** MariaDB 11.x (Native UUID Support)
|
||||
|
||||
## 1. Core Principles (หลักการสำคัญ)
|
||||
|
||||
@@ -64,13 +64,45 @@ ADD INDEX `idx_doc_status_is_current` (`is_current`, `status`, `project_id`);
|
||||
ALTER TABLE `documents`
|
||||
ADD INDEX `idx_project_updated` (`project_id`, `updated_at` DESC);
|
||||
|
||||
-- สำหรับ Inbox / Pending Tasks ของ User
|
||||
-- สำหรับ Inbox / Pending Tasks ของ User (General Workflow)
|
||||
ALTER TABLE `workflow_instances`
|
||||
ADD INDEX `idx_assignee_status` (`assignee_id`, `status`, `created_at` DESC);
|
||||
|
||||
-- สำหรับ Optimistic Locking (ADR-001 v1.1)
|
||||
ALTER TABLE `workflow_instances`
|
||||
ADD INDEX `idx_wf_inst_version` (`id`, `version_no`);
|
||||
|
||||
```
|
||||
|
||||
### 3.2 Full-Text Search (ทางเลือกเบื้องต้นก่อนใช้ Elasticsearch)
|
||||
### 3.2 RFA Parallel Review & Delegation (v1.9.0)
|
||||
|
||||
ในระบบ RFA ใหม่ มีการใช้ Parallel Review และการมอบหมายงาน (Delegation) ซึ่งต้องการ Index เฉพาะทาง:
|
||||
|
||||
```sql
|
||||
-- สำหรับดึงงานที่รอตรวจแยกตาม Discipline และสถานะ
|
||||
ALTER TABLE `review_tasks`
|
||||
ADD INDEX `idx_review_tasks_lookup` (`assigned_to_user_id`, `status`, `discipline_id`);
|
||||
|
||||
-- สำหรับการตรวจสอบสิทธิ์การตรวจรายคน (Priority Order)
|
||||
ALTER TABLE `review_team_members`
|
||||
ADD INDEX `idx_rtm_lookup` (`team_id`, `user_id`, `is_active`);
|
||||
|
||||
-- สำหรับเช็คการมอบหมายงานที่ยังมีผลอยู่ (Managed by BullMQ)
|
||||
ALTER TABLE `delegations`
|
||||
ADD INDEX `idx_delegations_active_lookup` (`delegator_user_id`, `is_active`, `start_date`, `end_date`);
|
||||
```
|
||||
|
||||
### 3.3 Polymorphic Distribution Matrix
|
||||
|
||||
เนื่องจาก `distribution_recipients` เก็บ `recipient_public_id` แบบ UUID (ADR-019) และไม่มี Hard FK:
|
||||
|
||||
```sql
|
||||
-- สำหรับ Lookup ผู้รับเอกสารตามประเภท
|
||||
ALTER TABLE `distribution_recipients`
|
||||
ADD INDEX `idx_dr_type_recipient` (`recipient_type`, `recipient_public_id`);
|
||||
```
|
||||
|
||||
### 3.4 Full-Text Search (ทางเลือกเบื้องต้นก่อนใช้ Elasticsearch)
|
||||
|
||||
หากผู้ใช้ต้องการค้นหาจากชื่อเอกสาร (`title`) หรือเนื้อหาบางส่วน
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
---
|
||||
|
||||
title: 'Data & Storage: File Storage and Handling (Two-Phase)'
|
||||
version: 1.8.0
|
||||
version: 1.9.0
|
||||
status: drafted
|
||||
owner: Nattanin Peancharoen
|
||||
last_updated: 2026-02-22
|
||||
@@ -80,23 +80,30 @@ LCBP3-DMS ต้องจัดการ File Uploads สำหรับ Attachm
|
||||
```sql
|
||||
CREATE TABLE attachments (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
uuid UUID NOT NULL DEFAULT UUID() COMMENT 'UUID Public Identifier (ADR-019)',
|
||||
original_filename VARCHAR(255) NOT NULL,
|
||||
stored_filename VARCHAR(255) NOT NULL, -- UUID-based
|
||||
file_path VARCHAR(500) NOT NULL, -- QNAP Mount path
|
||||
mime_type VARCHAR(100) NOT NULL,
|
||||
file_size INT NOT NULL,
|
||||
checksum VARCHAR(64) NULL, -- SHA-256
|
||||
reference_date DATE NULL COMMENT 'ADR-019/ADR-003: folder partition date',
|
||||
|
||||
-- Two-Phase Fields
|
||||
is_temporary BOOLEAN DEFAULT TRUE,
|
||||
temp_id VARCHAR(100) NULL, -- UUID for temp reference
|
||||
expires_at DATETIME NULL, -- Temp file expiration
|
||||
|
||||
-- ADR-021: Integrated Workflow Context
|
||||
workflow_history_id CHAR(36) NULL COMMENT 'FK to workflow_histories.id for step-specific attachments',
|
||||
|
||||
uploaded_by_user_id INT NOT NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id),
|
||||
INDEX idx_temp_files (is_temporary, expires_at)
|
||||
FOREIGN KEY (workflow_history_id) REFERENCES workflow_histories(id) ON DELETE SET NULL,
|
||||
INDEX idx_temp_files (is_temporary, expires_at),
|
||||
INDEX idx_attachments_reference_date (reference_date)
|
||||
);
|
||||
```
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
| description | version |
|
||||
| ------------------------------------------------------------------ | ------- |
|
||||
| legacy PDF document migration to system v1.8.0 uses n8n and Ollama | 1.8.0 |
|
||||
| legacy PDF document migration to system v1.9.0 uses n8n and Ollama | 1.9.0 |
|
||||
|
||||
> **Note:** Category Enum system-driven, Idempotency Contract, Duplicate Handling Clarification, Storage Enforcement, Audit Log Enhancement, Review Queue Integration, Revision Drift Protection, Execution Time, Encoding Normalization, Security Hardening, Orchestrator on QNAP, AI Physical Isolation (Desktop Desk-5439), Folder Standard (/share/np-dms/n8n), **AI Tag Extraction & Auto-Tagging**
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
-- =============================================================================
|
||||
-- Delta 11: เพิ่ม uuid column ให้ roles table
|
||||
-- =============================================================================
|
||||
-- เหตุผล: roles ไม่มี uuid column ทำให้ distribution_recipients ไม่สามารถ
|
||||
-- อ้างอิง ROLE type ด้วย publicId ได้ตาม ADR-019
|
||||
-- ใช้ใน: distribution_recipients.recipient_public_id (recipient_type = 'ROLE')
|
||||
-- ADR: ADR-019 (Hybrid Identifier Strategy)
|
||||
-- Feature: v1.9.0 RFA Approval Refactor (1-rfa-approval-refactor)
|
||||
-- Created: 2026-05-13
|
||||
-- =============================================================================
|
||||
|
||||
ALTER TABLE `roles`
|
||||
ADD COLUMN `uuid` UUID NOT NULL DEFAULT (UUID())
|
||||
COMMENT 'UUID Public Identifier (ADR-019) — ใช้ใน API Response แทน role_id'
|
||||
AFTER `role_id`;
|
||||
|
||||
-- สร้าง Unique Index สำหรับ uuid
|
||||
CREATE UNIQUE INDEX `idx_roles_uuid` ON `roles` (`uuid`);
|
||||
|
||||
-- =============================================================================
|
||||
-- หมายเหตุ: หลัง Apply delta นี้แล้ว
|
||||
-- - roles ที่มีอยู่แล้วจะได้ uuid อัตโนมัติจาก DEFAULT (UUID())
|
||||
-- - distribution_recipients.recipient_public_id (type=ROLE) ให้ใช้ roles.uuid
|
||||
-- - TypeORM Entity: เพิ่ม @Column({ unique: true }) uuid: string; ใน RoleEntity
|
||||
-- =============================================================================
|
||||
@@ -0,0 +1,121 @@
|
||||
-- ==========================================================
|
||||
-- DMS v1.9.0 Migration Support Tables
|
||||
-- ไฟล์นี้แยกจาก schema หลัก (lcbp3-v1.9.0-schema-01/02/03)
|
||||
-- ใช้สำหรับ n8n Migration Workflow เท่านั้น
|
||||
-- ลบได้ทั้งหมดหลัง Migration เสร็จสิ้น
|
||||
-- ==========================================================
|
||||
-- รันบน MariaDB **ก่อน** เริ่ม n8n Workflow
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
-- =====================================================
|
||||
-- 1. Checkpoint — ติดตามความคืบหน้าของ Batch
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS migration_progress (
|
||||
batch_id VARCHAR(50) PRIMARY KEY,
|
||||
last_processed_index INT DEFAULT 0,
|
||||
STATUS ENUM('RUNNING', 'COMPLETED', 'FAILED') DEFAULT 'RUNNING',
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: ติดตามความคืบหน้า Batch (ลบได้หลัง Migration เสร็จ)';
|
||||
|
||||
-- =====================================================
|
||||
-- 2. Review Queue — รายการที่ต้องตรวจสอบโดยคน
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS migration_review_queue (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
document_number VARCHAR(100) NOT NULL,
|
||||
subject TEXT COMMENT 'หัวข้อเรื่อง (ตรงกับ correspondence_revisions.subject)',
|
||||
original_subject TEXT COMMENT 'หัวข้อเดิมจาก Excel (ก่อน AI แก้ไข)',
|
||||
body TEXT NULL COMMENT 'เนื้อความสรุปจาก AI (เตรียมนำเข้า correspondence_revisions.body)',
|
||||
project_id INT NULL COMMENT 'Project ID จาก Lookups',
|
||||
sender_organization_id INT NULL COMMENT 'Sender ID จาก Lookups',
|
||||
receiver_organization_id INT NULL COMMENT 'Receiver ID จาก Lookups',
|
||||
received_date DATE NULL COMMENT 'วันที่รับเอกสาร',
|
||||
issued_date DATE NULL COMMENT 'วันที่ออกเอกสาร',
|
||||
remarks TEXT COMMENT 'หมายเหตุจากหน้างาน (response)',
|
||||
ai_suggested_category VARCHAR(50),
|
||||
ai_confidence DECIMAL(4, 3),
|
||||
ai_issues JSON,
|
||||
ai_summary TEXT COMMENT 'สรุปเนื้อหาจาก AI (4-5 บรรทัด)',
|
||||
extracted_tags JSON COMMENT 'Tag ที่ AI นำเสนอหรือจับคู่ได้',
|
||||
temp_attachment_id INT NULL COMMENT 'ID ของไฟล์ชั่วคราวจาก Two-Phase Storage',
|
||||
review_reason VARCHAR(255),
|
||||
STATUS ENUM('PENDING', 'APPROVED', 'REJECTED') DEFAULT 'PENDING',
|
||||
reviewed_by VARCHAR(100),
|
||||
reviewed_at TIMESTAMP NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uq_doc_number (document_number)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: Review Queue ชั่วคราว (ลบได้หลัง Migration เสร็จ)';
|
||||
|
||||
-- =====================================================
|
||||
-- 3. Error Log — บันทึก Error ระหว่าง Migration
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS migration_errors (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
batch_id VARCHAR(50),
|
||||
document_number VARCHAR(100),
|
||||
error_type ENUM(
|
||||
'FILE_NOT_FOUND',
|
||||
'MISSING_FILENAME',
|
||||
'FILE_ERROR',
|
||||
'AI_PARSE_ERROR',
|
||||
'API_ERROR',
|
||||
'DB_ERROR',
|
||||
'SECURITY',
|
||||
'UNKNOWN'
|
||||
),
|
||||
error_message TEXT,
|
||||
raw_ai_response TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_batch_id (batch_id),
|
||||
INDEX idx_error_type (error_type)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: Error Log (ลบได้หลัง Migration เสร็จ)';
|
||||
|
||||
-- =====================================================
|
||||
-- 4. Fallback State — สถานะ AI Model Fallback
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS migration_fallback_state (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
batch_id VARCHAR(50) UNIQUE,
|
||||
recent_error_count INT DEFAULT 0,
|
||||
is_fallback_active BOOLEAN DEFAULT FALSE,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: Fallback Model State (ลบได้หลัง Migration เสร็จ)';
|
||||
|
||||
-- =====================================================
|
||||
-- 5. Idempotency — ป้องกัน Import ซ้ำ
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS import_transactions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
idempotency_key VARCHAR(255) UNIQUE NOT NULL,
|
||||
document_number VARCHAR(100),
|
||||
batch_id VARCHAR(100),
|
||||
status_code INT DEFAULT 201,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
INDEX idx_idem_key (idempotency_key)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: Idempotency Tracking (ลบได้หลัง Migration เสร็จ)';
|
||||
|
||||
-- =====================================================
|
||||
-- 6. Daily Summary — สรุปผลรายวัน
|
||||
-- =====================================================
|
||||
CREATE TABLE IF NOT EXISTS migration_daily_summary (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
batch_id VARCHAR(50),
|
||||
summary_date DATE,
|
||||
total_processed INT DEFAULT 0,
|
||||
auto_ingested INT DEFAULT 0,
|
||||
sent_to_review INT DEFAULT 0,
|
||||
rejected INT DEFAULT 0,
|
||||
errors INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE KEY uq_batch_date (batch_id, summary_date)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'Migration: Daily Summary (ลบได้หลัง Migration เสร็จ)';
|
||||
|
||||
-- =====================================================
|
||||
-- Cleanup Script (รันหลัง Migration เสร็จสิ้นทั้งหมด)
|
||||
-- =====================================================
|
||||
-- DROP TABLE IF EXISTS migration_daily_summary;
|
||||
-- DROP TABLE IF EXISTS import_transactions;
|
||||
-- DROP TABLE IF EXISTS migration_fallback_state;
|
||||
-- DROP TABLE IF EXISTS migration_errors;
|
||||
-- DROP TABLE IF EXISTS migration_review_queue;
|
||||
-- DROP TABLE IF EXISTS migration_progress;
|
||||
@@ -1,8 +1,8 @@
|
||||
-- =============================================================================
|
||||
-- LCBP3-DMS v1.9.0 — RFA Approval System Refactor Schema
|
||||
-- Feature Branch: 1-rfa-approval-refactor
|
||||
-- Feature Branch: 204-rfa-approval-refactor
|
||||
-- ADR-009: No TypeORM migrations — edit SQL schema directly
|
||||
-- Created: 2026-05-12
|
||||
-- Created: 2026-05-13
|
||||
-- =============================================================================
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
@@ -42,7 +42,7 @@ CREATE TABLE IF NOT EXISTS `review_team_members` (
|
||||
KEY `idx_rtm_team` (`team_id`),
|
||||
KEY `idx_rtm_user` (`user_id`),
|
||||
CONSTRAINT `fk_rtm_team` FOREIGN KEY (`team_id`) REFERENCES `review_teams` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_rtm_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_rtm_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_rtm_discipline` FOREIGN KEY (`discipline_id`) REFERENCES `disciplines` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
@@ -120,7 +120,7 @@ CREATE TABLE IF NOT EXISTS `review_tasks` (
|
||||
CONSTRAINT `fk_rt_rfa_revision` FOREIGN KEY (`rfa_revision_id`) REFERENCES `rfa_revisions` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_rt_team` FOREIGN KEY (`team_id`) REFERENCES `review_teams` (`id`),
|
||||
CONSTRAINT `fk_rt_discipline` FOREIGN KEY (`discipline_id`) REFERENCES `disciplines` (`id`),
|
||||
CONSTRAINT `fk_rt_user` FOREIGN KEY (`assigned_to_user_id`) REFERENCES `users` (`id`) ON DELETE SET NULL,
|
||||
CONSTRAINT `fk_rt_user` FOREIGN KEY (`assigned_to_user_id`) REFERENCES `users` (`user_id`) ON DELETE SET NULL,
|
||||
CONSTRAINT `fk_rt_response_code` FOREIGN KEY (`response_code_id`) REFERENCES `response_codes` (`id`) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
@@ -128,44 +128,44 @@ CREATE TABLE IF NOT EXISTS `review_tasks` (
|
||||
-- 6. delegations — การมอบหมายงาน
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `delegations` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`delegator_id` INT NOT NULL COMMENT 'ผู้มอบหมาย',
|
||||
`delegatee_id` INT NOT NULL COMMENT 'ผู้รับมอบหมาย',
|
||||
`start_date` DATE NOT NULL,
|
||||
`end_date` DATE NULL,
|
||||
`scope` ENUM('ALL','RFA_ONLY','CORRESPONDENCE_ONLY','SPECIFIC_TYPES') NOT NULL DEFAULT 'ALL',
|
||||
`document_types` TEXT NULL COMMENT 'Comma-separated doc type codes when scope=SPECIFIC_TYPES',
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`reason` TEXT NULL,
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`delegator_user_id` INT NOT NULL COMMENT 'ผู้มอบหมาย (FK → users.user_id)',
|
||||
`delegate_user_id` INT NOT NULL COMMENT 'ผู้รับมอบหมาย (FK → users.user_id)',
|
||||
`start_date` DATE NOT NULL,
|
||||
`end_date` DATE NULL COMMENT 'BullMQ job flips is_active=0 when end_date < NOW() (ADR-008)',
|
||||
`scope` ENUM('ALL','RFA_ONLY','CORRESPONDENCE_ONLY','SPECIFIC_TYPES') NOT NULL DEFAULT 'ALL',
|
||||
`document_types` TEXT NULL COMMENT 'Comma-separated doc type codes when scope=SPECIFIC_TYPES',
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Managed by BullMQ scheduler — do not flip manually',
|
||||
`reason` TEXT NULL,
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_delegations_uuid` (`uuid`),
|
||||
KEY `idx_delegations_active` (`delegator_id`, `is_active`, `start_date`, `end_date`),
|
||||
KEY `idx_delegations_delegatee` (`delegatee_id`, `is_active`),
|
||||
CONSTRAINT `fk_del_delegator` FOREIGN KEY (`delegator_id`) REFERENCES `users` (`id`),
|
||||
CONSTRAINT `fk_del_delegatee` FOREIGN KEY (`delegatee_id`) REFERENCES `users` (`id`)
|
||||
KEY `idx_delegations_active` (`delegator_user_id`, `is_active`, `start_date`, `end_date`),
|
||||
KEY `idx_delegations_delegate` (`delegate_user_id`, `is_active`),
|
||||
CONSTRAINT `fk_del_delegator` FOREIGN KEY (`delegator_user_id`) REFERENCES `users` (`user_id`),
|
||||
CONSTRAINT `fk_del_delegate` FOREIGN KEY (`delegate_user_id`) REFERENCES `users` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 7. reminder_rules — กฎการแจ้งเตือน
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `reminder_rules` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`name` VARCHAR(100) NOT NULL,
|
||||
`project_id` INT NULL COMMENT 'NULL = global',
|
||||
`document_type_id` INT NULL COMMENT 'NULL = all types',
|
||||
`trigger_days_before_due` INT NOT NULL DEFAULT 2,
|
||||
`escalation_days_after_due` INT NOT NULL DEFAULT 1,
|
||||
`project_id` INT NULL COMMENT 'NULL = global',
|
||||
`document_type_id` INT NULL COMMENT 'NULL = all types',
|
||||
`trigger_days_before_due` INT NOT NULL DEFAULT 2,
|
||||
`escalation_days_after_due` INT NOT NULL DEFAULT 1,
|
||||
`reminder_type` ENUM('DUE_SOON','ON_DUE','OVERDUE','ESCALATION_L1','ESCALATION_L2') NOT NULL,
|
||||
`recipients` TEXT NOT NULL COMMENT 'Comma-separated: ASSIGNEE,MANAGER,PROJECT_MANAGER',
|
||||
`message_template_th` TEXT NOT NULL,
|
||||
`message_template_en` TEXT NOT NULL,
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
`recipients` TEXT NOT NULL COMMENT 'Comma-separated: ASSIGNEE,MANAGER,PROJECT_MANAGER',
|
||||
`message_template_th` TEXT NOT NULL,
|
||||
`message_template_en` TEXT NOT NULL,
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_reminder_rules_uuid` (`uuid`),
|
||||
KEY `idx_reminder_rules_active` (`is_active`, `project_id`)
|
||||
@@ -175,15 +175,15 @@ CREATE TABLE IF NOT EXISTS `reminder_rules` (
|
||||
-- 8. distribution_matrices — ตารางกระจายเอกสาร
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `distribution_matrices` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`name` VARCHAR(100) NOT NULL,
|
||||
`project_id` INT NULL COMMENT 'NULL = global',
|
||||
`document_type_id` INT NOT NULL,
|
||||
`response_code_id` INT NULL COMMENT 'NULL = applies to all codes',
|
||||
`conditions` JSON NULL COMMENT '{"codes":["1A","1B"],"excludeCodes":["3","4"]}',
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`project_id` INT NULL COMMENT 'NULL = global',
|
||||
`document_type_id` INT NOT NULL,
|
||||
`response_code_id` INT NULL COMMENT 'NULL = applies to all codes',
|
||||
`conditions` JSON NULL COMMENT '{"codes":["1A","1B"],"excludeCodes":["3","4"]}',
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_distribution_matrices_uuid` (`uuid`),
|
||||
KEY `idx_distribution_lookup` (`document_type_id`, `response_code_id`, `is_active`),
|
||||
@@ -195,22 +195,17 @@ CREATE TABLE IF NOT EXISTS `distribution_matrices` (
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `distribution_recipients` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()) COMMENT 'UUID Public Identifier (ADR-019)',
|
||||
`matrix_id` INT NOT NULL,
|
||||
`recipient_type` ENUM('USER','ORGANIZATION','TEAM','ROLE') NOT NULL,
|
||||
`recipient_public_id` UUID NOT NULL COMMENT 'publicId of user/org/team',
|
||||
`recipient_public_id` UUID NOT NULL COMMENT 'publicId of target: USER=users.uuid | ORGANIZATION=organizations.uuid | TEAM=review_teams.uuid | ROLE=roles.uuid',
|
||||
`delivery_method` ENUM('EMAIL','IN_APP','BOTH') NOT NULL DEFAULT 'BOTH',
|
||||
`sequence` INT NULL COMMENT 'For ordered delivery',
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_distribution_recipients_uuid` (`uuid`),
|
||||
KEY `idx_dr_matrix` (`matrix_id`),
|
||||
KEY `idx_dr_type_recipient` (`recipient_type`, `recipient_public_id`),
|
||||
CONSTRAINT `fk_dr_matrix` FOREIGN KEY (`matrix_id`) REFERENCES `distribution_matrices` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- =============================================================================
|
||||
-- Additional Indexes (Performance)
|
||||
-- =============================================================================
|
||||
-- (all created inline above with KEY statements)
|
||||
|
||||
-- =============================================================================
|
||||
-- END OF SCHEMA v1.9.0
|
||||
-- =============================================================================
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='Polymorphic recipients — no FK on recipient_public_id by design.';
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
-- ==========================================================
|
||||
-- DMS v1.9.0 Schema Part 1/3: DROP Statements
|
||||
-- รันไฟล์นี้ก่อน เพื่อล้างตารางเดิมทั้งหมด
|
||||
-- ==========================================================
|
||||
-- ==========================================================
|
||||
-- DMS v1.9.0 Document Management System Database
|
||||
-- Deploy Script Schema
|
||||
-- Server: Container Station on QNAP TS-473A
|
||||
-- Database service: MariaDB 11.8
|
||||
-- database web ui: phpmyadmin 5-apache
|
||||
-- database development ui: DBeaver
|
||||
-- backend service: NestJS
|
||||
-- frontend service: next.js
|
||||
-- reverse proxy: jc21/nginx-proxy-manager:latest
|
||||
-- cron service: n8n
|
||||
-- ==========================================================
|
||||
-- [v1.9.0 UPDATE] Prepare migration
|
||||
-- Update: Upgraded from v1.7.0
|
||||
-- Last Updated: 2026-02-27
|
||||
-- Major Changes:
|
||||
-- 1. ปรับปรุง:
|
||||
-- 1.1 TABLE correspondences
|
||||
-- - INDEX idx_doc_number (correspondence_number),
|
||||
-- - INDEX idx_deleted_at (deleted_at),
|
||||
-- - INDEX idx_created_by (created_by),
|
||||
-- ==========================================================
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET time_zone = '+07:00';
|
||||
|
||||
-- ปิดการตรวจสอบ Foreign Key ชั่วคราวเพื่อให้สามารถลบตารางได้ทั้งหมด
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
DROP VIEW IF EXISTS v_document_statistics;
|
||||
|
||||
DROP VIEW IF EXISTS v_documents_with_attachments;
|
||||
|
||||
DROP VIEW IF EXISTS v_user_all_permissions;
|
||||
|
||||
DROP VIEW IF EXISTS v_audit_log_details;
|
||||
|
||||
DROP VIEW IF EXISTS v_user_tasks;
|
||||
|
||||
DROP VIEW IF EXISTS v_contract_parties_all;
|
||||
|
||||
DROP VIEW IF EXISTS v_current_rfas;
|
||||
|
||||
DROP VIEW IF EXISTS v_current_correspondences;
|
||||
|
||||
-- DROP PROCEDURE IF EXISTS sp_get_next_document_number;
|
||||
-- 🗑️ DROP TABLE SCRIPT: LCBP3-DMS v1.4.2
|
||||
-- คำเตือน: ข้อมูลทั้งหมดจะหายไป กรุณา Backup ก่อนรันบน Production
|
||||
SET FOREIGN_KEY_CHECKS = 0;
|
||||
|
||||
-- ============================================================
|
||||
-- ส่วนที่ 1: ตาราง System, Logs & Preferences (ตารางปลายทาง/ส่วนเสริม)
|
||||
-- ============================================================
|
||||
DROP TABLE IF EXISTS backup_logs;
|
||||
|
||||
DROP TABLE IF EXISTS search_indices;
|
||||
|
||||
DROP TABLE IF EXISTS notifications;
|
||||
|
||||
DROP TABLE IF EXISTS audit_logs;
|
||||
|
||||
-- [NEW v1.4.2] ตารางการตั้งค่าส่วนตัวของผู้ใช้ (FK -> users)
|
||||
DROP TABLE IF EXISTS user_preferences;
|
||||
|
||||
-- [NEW v1.4.2] ตารางเก็บ Schema สำหรับ Validate JSON (Stand-alone)
|
||||
DROP TABLE IF EXISTS json_schemas;
|
||||
|
||||
-- [v1.5.1 NEW] ตาราง Audit และ Error Log สำหรับ Document Numbering
|
||||
DROP TABLE IF EXISTS document_number_errors;
|
||||
|
||||
DROP TABLE IF EXISTS document_number_audit;
|
||||
|
||||
DROP TABLE IF EXISTS document_number_reservations;
|
||||
|
||||
-- ============================================================
|
||||
-- ส่วนที่ 2: ตาราง Junction (เชื่อมโยงข้อมูล M:N)
|
||||
-- ============================================================
|
||||
DROP TABLE IF EXISTS correspondence_tags;
|
||||
|
||||
DROP TABLE IF EXISTS asbuilt_revision_shop_revisions_refs;
|
||||
|
||||
DROP TABLE IF EXISTS shop_drawing_revision_contract_refs;
|
||||
|
||||
DROP TABLE IF EXISTS contract_drawing_subcat_cat_maps;
|
||||
|
||||
-- ============================================================
|
||||
-- ส่วนที่ 3: ตารางไฟล์แนบและการเชื่อมโยง (Attachments)
|
||||
-- ============================================================
|
||||
DROP TABLE IF EXISTS contract_drawing_attachments;
|
||||
|
||||
DROP TABLE IF EXISTS circulation_attachments;
|
||||
|
||||
DROP TABLE IF EXISTS shop_drawing_revision_attachments;
|
||||
|
||||
DROP TABLE IF EXISTS asbuilt_drawing_revision_attachments;
|
||||
|
||||
DROP TABLE IF EXISTS correspondence_revision_attachments;
|
||||
|
||||
DROP TABLE IF EXISTS attachments;
|
||||
|
||||
-- ตารางหลักเก็บ path ไฟล์
|
||||
-- ============================================================
|
||||
-- ส่วนที่ 4: ตาราง Workflow & Routing (Process Logic)
|
||||
-- ============================================================
|
||||
-- Correspondence Workflow
|
||||
-- ============================================================
|
||||
-- ส่วนที่ 5: ตาราง Mapping สิทธิ์และโครงสร้าง (Access Control)
|
||||
-- ============================================================
|
||||
DROP TABLE IF EXISTS role_permissions;
|
||||
|
||||
DROP TABLE IF EXISTS user_assignments;
|
||||
|
||||
DROP TABLE IF EXISTS contract_organizations;
|
||||
|
||||
DROP TABLE IF EXISTS project_organizations;
|
||||
|
||||
-- ============================================================
|
||||
-- ส่วนที่ 6: ตารางรายละเอียดของเอกสาร (Revisions & Items)
|
||||
-- ============================================================
|
||||
DROP TABLE IF EXISTS transmittal_items;
|
||||
|
||||
DROP TABLE IF EXISTS shop_drawing_revisions;
|
||||
|
||||
DROP TABLE IF EXISTS asbuilt_drawing_revisions;
|
||||
|
||||
DROP TABLE IF EXISTS rfa_items;
|
||||
|
||||
DROP TABLE IF EXISTS rfa_revisions;
|
||||
|
||||
DROP TABLE IF EXISTS correspondence_references;
|
||||
|
||||
DROP TABLE IF EXISTS correspondence_recipients;
|
||||
|
||||
DROP TABLE IF EXISTS correspondence_revisions;
|
||||
|
||||
-- [Modified v1.4.2] มี Virtual Columns
|
||||
-- ============================================================
|
||||
-- ส่วนที่ 7: ตารางเอกสารหลัก (Core Documents)
|
||||
-- ============================================================
|
||||
DROP TABLE IF EXISTS circulations;
|
||||
|
||||
DROP TABLE IF EXISTS transmittals;
|
||||
|
||||
DROP TABLE IF EXISTS contract_drawings;
|
||||
|
||||
DROP TABLE IF EXISTS shop_drawings;
|
||||
|
||||
DROP TABLE IF EXISTS asbuilt_drawings;
|
||||
|
||||
DROP TABLE IF EXISTS rfas;
|
||||
|
||||
DROP TABLE IF EXISTS correspondences;
|
||||
|
||||
-- ============================================================
|
||||
-- ส่วนที่ 8: ตารางหมวดหมู่และข้อมูลหลัก (Master Data)
|
||||
-- ============================================================
|
||||
-- [NEW 6B] ลบตารางใหม่ที่เพิ่มเข้ามาเพื่อป้องกัน Error เวลา Re-deploy
|
||||
DROP TABLE IF EXISTS correspondence_sub_types;
|
||||
|
||||
DROP TABLE IF EXISTS disciplines;
|
||||
|
||||
DROP TABLE IF EXISTS shop_drawing_sub_categories;
|
||||
|
||||
DROP TABLE IF EXISTS shop_drawing_main_categories;
|
||||
|
||||
DROP TABLE IF EXISTS contract_drawing_sub_cats;
|
||||
|
||||
DROP TABLE IF EXISTS contract_drawing_cats;
|
||||
|
||||
DROP TABLE IF EXISTS contract_drawing_volumes;
|
||||
|
||||
DROP TABLE IF EXISTS circulation_status_codes;
|
||||
|
||||
DROP TABLE IF EXISTS rfa_approve_codes;
|
||||
|
||||
DROP TABLE IF EXISTS rfa_status_codes;
|
||||
|
||||
DROP TABLE IF EXISTS rfa_types;
|
||||
|
||||
DROP TABLE IF EXISTS correspondence_status;
|
||||
|
||||
DROP TABLE IF EXISTS correspondence_types;
|
||||
|
||||
DROP TABLE IF EXISTS document_number_counters;
|
||||
|
||||
-- [Modified v1.4.2] มี version column
|
||||
DROP TABLE IF EXISTS document_number_formats;
|
||||
|
||||
DROP TABLE IF EXISTS tags;
|
||||
|
||||
-- ============================================================
|
||||
-- ส่วนที่ 9: ตารางผู้ใช้ บทบาท และโครงสร้างรากฐาน (Root Tables)
|
||||
-- ============================================================
|
||||
DROP TABLE IF EXISTS organization_roles;
|
||||
|
||||
DROP TABLE IF EXISTS roles;
|
||||
|
||||
DROP TABLE IF EXISTS permissions;
|
||||
|
||||
DROP TABLE IF EXISTS contracts;
|
||||
|
||||
DROP TABLE IF EXISTS projects;
|
||||
|
||||
DROP TABLE IF EXISTS refresh_tokens;
|
||||
|
||||
DROP TABLE IF EXISTS users;
|
||||
|
||||
-- Referenced by user_preferences, audit_logs, etc.
|
||||
DROP TABLE IF EXISTS organizations;
|
||||
|
||||
-- Referenced by users, projects, etc.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,542 @@
|
||||
-- ==========================================================
|
||||
-- DMS v1.9.0 Schema Part 3/3: Views, Indexes, Partitioning
|
||||
-- รัน: หลังจาก 02-schema-tables.sql เสร็จ
|
||||
-- ==========================================================
|
||||
SET NAMES utf8mb4;
|
||||
|
||||
SET time_zone = '+07:00';
|
||||
|
||||
-- ============================================================
|
||||
-- 5. PARTITIONING PREPARATION (Advance - Optional)
|
||||
-- ============================================================
|
||||
-- หมายเหตุ: การทำ Partitioning บนตารางที่มีอยู่แล้ว (audit_logs, notifications)
|
||||
-- มักจะต้อง Drop Primary Key เดิม แล้วสร้างใหม่โดยรวม Partition Key (created_at) เข้าไป
|
||||
-- ขั้นตอนนี้ควรทำแยกต่างหากเมื่อระบบเริ่มมีข้อมูลเยอะ หรือทำใน Maintenance Window
|
||||
--
|
||||
-- ตัวอย่าง SQL สำหรับ Audit Logs (Reference Only):
|
||||
-- ALTER TABLE audit_logs DROP PRIMARY KEY, ADD PRIMARY KEY (audit_id, created_at);
|
||||
-- ALTER TABLE audit_logs PARTITION BY RANGE (YEAR(created_at)) (
|
||||
-- PARTITION p2024 VALUES LESS THAN (2025),
|
||||
-- PARTITION p2025 VALUES LESS THAN (2026),
|
||||
-- PARTITION p_future VALUES LESS THAN MAXVALUE
|
||||
-- );
|
||||
-- =====================================================
|
||||
-- CREATE INDEXES
|
||||
-- =====================================================
|
||||
-- Indexes for correspondences
|
||||
CREATE INDEX idx_corr_type ON correspondences(correspondence_type_id);
|
||||
|
||||
CREATE INDEX idx_corr_project ON correspondences(project_id);
|
||||
|
||||
CREATE INDEX idx_rfa_rev_v_drawing_count ON rfa_revisions (v_ref_drawing_count);
|
||||
|
||||
-- Indexes for document_number_formats
|
||||
CREATE INDEX idx_document_number_formats_project ON document_number_formats (project_id);
|
||||
|
||||
CREATE INDEX idx_document_number_formats_type ON document_number_formats (correspondence_type_id);
|
||||
|
||||
CREATE INDEX idx_document_number_formats_project_type ON document_number_formats (project_id, correspondence_type_id);
|
||||
|
||||
-- Indexes for document_number_counters
|
||||
CREATE INDEX idx_document_number_counters_project ON document_number_counters (project_id);
|
||||
|
||||
CREATE INDEX idx_document_number_counters_org ON document_number_counters (originator_organization_id);
|
||||
|
||||
CREATE INDEX idx_document_number_counters_type ON document_number_counters (correspondence_type_id);
|
||||
|
||||
-- Indexes for tags
|
||||
CREATE INDEX idx_tags_name ON tags (tag_name);
|
||||
|
||||
CREATE INDEX idx_tags_created_at ON tags (created_at);
|
||||
|
||||
-- Index for circulations deadline_date (EC-CIRC-003 Overdue Badge)
|
||||
CREATE INDEX idx_circulations_deadline ON circulations (deadline_date);
|
||||
|
||||
-- Indexes for correspondence_tags
|
||||
CREATE INDEX idx_correspondence_tags_correspondence ON correspondence_tags (correspondence_id);
|
||||
|
||||
CREATE INDEX idx_correspondence_tags_tag ON correspondence_tags (tag_id);
|
||||
|
||||
-- Indexes for audit_logs
|
||||
CREATE INDEX idx_audit_logs_user ON audit_logs (user_id);
|
||||
|
||||
CREATE INDEX idx_audit_logs_action ON audit_logs (ACTION);
|
||||
|
||||
CREATE INDEX idx_audit_logs_entity ON audit_logs (entity_type, entity_id);
|
||||
|
||||
CREATE INDEX idx_audit_logs_created_at ON audit_logs (created_at);
|
||||
|
||||
CREATE INDEX idx_audit_logs_ip ON audit_logs (ip_address);
|
||||
|
||||
-- Indexes for notifications
|
||||
CREATE INDEX idx_notifications_user ON notifications (user_id);
|
||||
|
||||
CREATE INDEX idx_notifications_type ON notifications (notification_type);
|
||||
|
||||
CREATE INDEX idx_notifications_read ON notifications (is_read);
|
||||
|
||||
CREATE INDEX idx_notifications_entity ON notifications (entity_type, entity_id);
|
||||
|
||||
CREATE INDEX idx_notifications_created_at ON notifications (created_at);
|
||||
|
||||
-- Indexes for search_indices
|
||||
CREATE INDEX idx_search_indices_entity ON search_indices (entity_type, entity_id);
|
||||
|
||||
CREATE INDEX idx_search_indices_indexed_at ON search_indices (indexed_at);
|
||||
|
||||
-- Indexes for backup_logs
|
||||
CREATE INDEX idx_backup_logs_type ON backup_logs (backup_type);
|
||||
|
||||
CREATE INDEX idx_backup_logs_status ON backup_logs (STATUS);
|
||||
|
||||
CREATE INDEX idx_backup_logs_started_at ON backup_logs (started_at);
|
||||
|
||||
CREATE INDEX idx_backup_logs_completed_at ON backup_logs (completed_at);
|
||||
|
||||
-- =====================================================
|
||||
-- Additional Composite Indexes for Performance
|
||||
-- =====================================================
|
||||
-- Composite index for document_number_counters for faster lookups
|
||||
-- Composite index for notifications for user-specific queries
|
||||
CREATE INDEX idx_notifications_user_unread ON notifications (user_id, is_read, created_at);
|
||||
|
||||
-- Composite index for audit_logs for reporting
|
||||
CREATE INDEX idx_audit_logs_reporting ON audit_logs (created_at, entity_type, ACTION);
|
||||
|
||||
-- Composite index for search_indices for entity-based queries
|
||||
CREATE INDEX idx_search_entities ON search_indices (entity_type, entity_id, indexed_at);
|
||||
|
||||
-- สร้าง Index สำหรับ Cleanup Job
|
||||
CREATE INDEX idx_attachments_temp_cleanup ON attachments (is_temporary, expires_at);
|
||||
|
||||
CREATE INDEX idx_attachments_temp_id ON attachments (temp_id);
|
||||
|
||||
CREATE INDEX idx_audit_request_id ON audit_logs (request_id);
|
||||
|
||||
-- =====================================================
|
||||
-- SQL Script for LCBP3-DMS (V1.4.0) - MariaDB
|
||||
-- Generated from Data Dictionary
|
||||
-- =====================================================
|
||||
-- =====================================================
|
||||
-- 11. 📊 Views & Procedures (วิว และ โปรซีเดอร์)
|
||||
-- =====================================================
|
||||
-- View แสดง Revision "ปัจจุบัน" ของ correspondences ทั้งหมด (ที่ไม่ใช่ RFA)
|
||||
CREATE VIEW v_current_correspondences AS
|
||||
SELECT c.id AS correspondence_id,
|
||||
c.uuid AS correspondence_uuid,
|
||||
c.correspondence_number,
|
||||
c.correspondence_type_id,
|
||||
ct.type_code AS correspondence_type_code,
|
||||
ct.type_name AS correspondence_type_name,
|
||||
c.project_id,
|
||||
p.uuid AS project_uuid,
|
||||
p.project_code,
|
||||
p.project_name,
|
||||
c.originator_id,
|
||||
org.uuid AS originator_uuid,
|
||||
org.organization_code AS originator_code,
|
||||
org.organization_name AS originator_name,
|
||||
cr.id AS revision_id,
|
||||
cr.uuid AS revision_uuid,
|
||||
cr.revision_number,
|
||||
cr.revision_label,
|
||||
cr.subject,
|
||||
cr.document_date,
|
||||
cr.issued_date,
|
||||
cr.received_date,
|
||||
cr.due_date,
|
||||
cr.correspondence_status_id,
|
||||
cs.status_code,
|
||||
cs.status_name,
|
||||
cr.created_by,
|
||||
u.username AS created_by_username,
|
||||
cr.created_at AS revision_created_at
|
||||
FROM correspondences c
|
||||
INNER JOIN correspondence_types ct ON c.correspondence_type_id = ct.id
|
||||
INNER JOIN projects p ON c.project_id = p.id
|
||||
LEFT JOIN organizations org ON c.originator_id = org.id
|
||||
INNER JOIN correspondence_revisions cr ON c.id = cr.correspondence_id
|
||||
INNER JOIN correspondence_status cs ON cr.correspondence_status_id = cs.id
|
||||
LEFT JOIN users u ON cr.created_by = u.user_id
|
||||
WHERE cr.is_current = TRUE
|
||||
AND c.correspondence_type_id NOT IN(
|
||||
SELECT id
|
||||
FROM correspondence_types
|
||||
WHERE type_code = 'RFA'
|
||||
)
|
||||
AND c.deleted_at IS NULL;
|
||||
|
||||
-- View แสดง Revision "ปัจจุบัน" ของ rfa_revisions ทั้งหมด
|
||||
CREATE VIEW v_current_rfas AS
|
||||
SELECT r.id AS rfa_id,
|
||||
c.uuid AS correspondence_uuid,
|
||||
r.rfa_type_id,
|
||||
rt.type_code AS rfa_type_code,
|
||||
rt.type_name_th AS rfa_type_name_th,
|
||||
rt.type_name_en AS rfa_type_name_en,
|
||||
c.correspondence_number,
|
||||
c.discipline_id,
|
||||
-- ✅ ดึงจาก Correspondences
|
||||
d.discipline_code,
|
||||
-- ✅ Join เพิ่มเพื่อแสดง code
|
||||
c.project_id,
|
||||
p.uuid AS project_uuid,
|
||||
p.project_code,
|
||||
p.project_name,
|
||||
c.originator_id,
|
||||
org.uuid AS originator_uuid,
|
||||
org.organization_name AS originator_name,
|
||||
rr.id AS revision_id,
|
||||
cr.uuid AS revision_uuid,
|
||||
cr.revision_number,
|
||||
cr.revision_label,
|
||||
cr.subject,
|
||||
cr.document_date,
|
||||
cr.issued_date,
|
||||
cr.received_date,
|
||||
rr.approved_date,
|
||||
rr.rfa_status_code_id,
|
||||
rsc.status_code AS rfa_status_code,
|
||||
rsc.status_name AS rfa_status_name,
|
||||
rr.rfa_approve_code_id,
|
||||
rac.approve_code AS rfa_approve_code,
|
||||
rac.approve_name AS rfa_approve_name,
|
||||
cr.created_by,
|
||||
u.username AS created_by_username,
|
||||
cr.created_at AS revision_created_at
|
||||
FROM rfas r
|
||||
INNER JOIN rfa_types rt ON r.rfa_type_id = rt.id
|
||||
INNER JOIN correspondences c ON r.id = c.id
|
||||
INNER JOIN correspondence_revisions cr ON c.id = cr.correspondence_id
|
||||
INNER JOIN rfa_revisions rr ON cr.id = rr.id -- RFA uses shared primary key with correspondence_revisions (1:1)
|
||||
-- [FIX 1] เพิ่มการ Join ตาราง disciplines
|
||||
LEFT JOIN disciplines d ON c.discipline_id = d.id
|
||||
INNER JOIN projects p ON c.project_id = p.id
|
||||
INNER JOIN organizations org ON c.originator_id = org.id
|
||||
INNER JOIN rfa_status_codes rsc ON rr.rfa_status_code_id = rsc.id
|
||||
LEFT JOIN rfa_approve_codes rac ON rr.rfa_approve_code_id = rac.id
|
||||
LEFT JOIN users u ON cr.created_by = u.user_id
|
||||
WHERE cr.is_current = TRUE
|
||||
AND c.deleted_at IS NULL;
|
||||
|
||||
-- View แสดงความสัมพันธ์ทั้งหมดระหว่าง Contract, Project, และ Organization
|
||||
CREATE VIEW v_contract_parties_all AS
|
||||
SELECT c.id AS contract_id,
|
||||
c.uuid AS contract_uuid,
|
||||
c.contract_code,
|
||||
c.contract_name,
|
||||
p.id AS project_id,
|
||||
p.uuid AS project_uuid,
|
||||
p.project_code,
|
||||
p.project_name,
|
||||
o.id AS organization_id,
|
||||
o.uuid AS organization_uuid,
|
||||
o.organization_code,
|
||||
o.organization_name,
|
||||
co.role_in_contract
|
||||
FROM contracts c
|
||||
INNER JOIN projects p ON c.project_id = p.id
|
||||
INNER JOIN contract_organizations co ON c.id = co.contract_id
|
||||
INNER JOIN organizations o ON co.organization_id = o.id
|
||||
WHERE c.is_active = TRUE;
|
||||
|
||||
-- ============================================================
|
||||
-- View: v_user_tasks (Unified Workflow Engine Edition)
|
||||
-- ============================================================
|
||||
-- หน้าที่: รวมรายการงานที่ยังค้างอยู่ (Status = ACTIVE) จากทุกระบบ (RFA, Circulation, Correspondence)
|
||||
-- เพื่อนำไปแสดงในหน้า Dashboard "My Tasks"
|
||||
-- ============================================================
|
||||
CREATE OR REPLACE VIEW v_user_tasks AS
|
||||
SELECT -- 1. Workflow Instance Info
|
||||
wi.id AS instance_id,
|
||||
wd.workflow_code,
|
||||
wi.current_state,
|
||||
wi.status AS workflow_status,
|
||||
wi.created_at AS assigned_at,
|
||||
-- 2. Entity Info (Polymorphic Identity)
|
||||
wi.entity_type,
|
||||
wi.entity_id,
|
||||
-- 3. Normalized Document Info (ดึงข้อมูลจริงจากตารางลูกตามประเภท)
|
||||
-- ใช้ CASE WHEN เพื่อรวมคอลัมน์ที่ชื่อต่างกันให้เป็นชื่อกลาง (document_number, subject)
|
||||
CASE
|
||||
WHEN wi.entity_type = 'rfa_revision' THEN rfa_corr.correspondence_number
|
||||
WHEN wi.entity_type = 'circulation' THEN circ.circulation_no
|
||||
WHEN wi.entity_type = 'correspondence_revision' THEN corr_corr.correspondence_number
|
||||
ELSE 'N/A'
|
||||
END AS document_number,
|
||||
CASE
|
||||
WHEN wi.entity_type = 'rfa_revision' THEN rfa_corr_rev.subject
|
||||
WHEN wi.entity_type = 'circulation' THEN circ.circulation_subject
|
||||
WHEN wi.entity_type = 'correspondence_revision' THEN corr_rev.subject
|
||||
ELSE 'Unknown Document'
|
||||
END AS subject,
|
||||
-- 4. Context Info (สำหรับ Filter สิทธิ์การมองเห็นที่ Backend)
|
||||
-- ดึงเป็น JSON String เพื่อให้ Backend ไป Parse หรือใช้ JSON_CONTAINS
|
||||
JSON_UNQUOTE(JSON_EXTRACT(wi.context, '$.ownerId')) AS owner_id,
|
||||
JSON_EXTRACT(wi.context, '$.assigneeIds') AS assignee_ids_json
|
||||
FROM workflow_instances wi
|
||||
JOIN workflow_definitions wd ON wi.definition_id = wd.id -- 5. Joins for RFA (ซับซ้อนหน่อยเพราะ RFA ผูกกับ Correspondence อีกที)
|
||||
LEFT JOIN rfa_revisions rfa_rev ON wi.entity_type = 'rfa_revision'
|
||||
AND wi.entity_id = CAST(rfa_rev.id AS CHAR)
|
||||
LEFT JOIN correspondence_revisions rfa_corr_rev ON rfa_rev.id = rfa_corr_rev.id
|
||||
LEFT JOIN correspondences rfa_corr ON rfa_corr_rev.correspondence_id = rfa_corr.id -- 6. Joins for Circulation
|
||||
LEFT JOIN circulations circ ON wi.entity_type = 'circulation'
|
||||
AND wi.entity_id = CAST(circ.id AS CHAR) -- 7. Joins for Correspondence
|
||||
LEFT JOIN correspondence_revisions corr_rev ON wi.entity_type = 'correspondence_revision'
|
||||
AND wi.entity_id = CAST(corr_rev.id AS CHAR)
|
||||
LEFT JOIN correspondences corr_corr ON corr_rev.correspondence_id = corr_corr.id -- 8. Filter เฉพาะงานที่ยัง Active อยู่
|
||||
WHERE wi.status = 'ACTIVE';
|
||||
|
||||
-- View แสดง audit_logs พร้อมข้อมูล username และ email ของผู้กระทำ
|
||||
CREATE VIEW v_audit_log_details AS
|
||||
SELECT al.audit_id,
|
||||
al.user_id,
|
||||
u.username,
|
||||
u.email,
|
||||
u.first_name,
|
||||
u.last_name,
|
||||
al.action,
|
||||
al.entity_type,
|
||||
al.entity_id,
|
||||
al.details_json,
|
||||
al.ip_address,
|
||||
al.user_agent,
|
||||
al.created_at
|
||||
FROM audit_logs al
|
||||
LEFT JOIN users u ON al.user_id = u.user_id;
|
||||
|
||||
-- View รวมสิทธิ์ทั้งหมด (Global + Project) ของผู้ใช้ทุกคน
|
||||
CREATE VIEW v_user_all_permissions AS -- Global Permissions
|
||||
SELECT ua.user_id,
|
||||
ua.role_id,
|
||||
r.role_name,
|
||||
rp.permission_id,
|
||||
p.permission_name,
|
||||
p.module,
|
||||
p.scope_level,
|
||||
ua.organization_id,
|
||||
NULL AS project_id,
|
||||
NULL AS contract_id,
|
||||
'GLOBAL' AS permission_scope
|
||||
FROM user_assignments ua
|
||||
INNER JOIN roles r ON ua.role_id = r.role_id
|
||||
INNER JOIN role_permissions rp ON ua.role_id = rp.role_id
|
||||
INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Global scope
|
||||
WHERE p.is_active = 1
|
||||
AND ua.organization_id IS NULL
|
||||
AND ua.project_id IS NULL
|
||||
AND ua.contract_id IS NULL
|
||||
UNION ALL
|
||||
-- Organization-specific Permissions
|
||||
SELECT ua.user_id,
|
||||
ua.role_id,
|
||||
r.role_name,
|
||||
rp.permission_id,
|
||||
p.permission_name,
|
||||
p.module,
|
||||
p.scope_level,
|
||||
ua.organization_id,
|
||||
NULL AS project_id,
|
||||
NULL AS contract_id,
|
||||
'ORGANIZATION' AS permission_scope
|
||||
FROM user_assignments ua
|
||||
INNER JOIN roles r ON ua.role_id = r.role_id
|
||||
INNER JOIN role_permissions rp ON ua.role_id = rp.role_id
|
||||
INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Organization scope
|
||||
WHERE p.is_active = 1
|
||||
AND ua.organization_id IS NOT NULL
|
||||
AND ua.project_id IS NULL
|
||||
AND ua.contract_id IS NULL
|
||||
UNION ALL
|
||||
-- Project-specific Permissions
|
||||
SELECT ua.user_id,
|
||||
ua.role_id,
|
||||
r.role_name,
|
||||
rp.permission_id,
|
||||
p.permission_name,
|
||||
p.module,
|
||||
p.scope_level,
|
||||
ua.organization_id,
|
||||
ua.project_id,
|
||||
NULL AS contract_id,
|
||||
'PROJECT' AS permission_scope
|
||||
FROM user_assignments ua
|
||||
INNER JOIN roles r ON ua.role_id = r.role_id
|
||||
INNER JOIN role_permissions rp ON ua.role_id = rp.role_id
|
||||
INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Project scope
|
||||
WHERE p.is_active = 1
|
||||
AND ua.project_id IS NOT NULL
|
||||
AND ua.contract_id IS NULL
|
||||
UNION ALL
|
||||
-- Contract-specific Permissions
|
||||
SELECT ua.user_id,
|
||||
ua.role_id,
|
||||
r.role_name,
|
||||
rp.permission_id,
|
||||
p.permission_name,
|
||||
p.module,
|
||||
p.scope_level,
|
||||
ua.organization_id,
|
||||
ua.project_id,
|
||||
ua.contract_id,
|
||||
'CONTRACT' AS permission_scope
|
||||
FROM user_assignments ua
|
||||
INNER JOIN roles r ON ua.role_id = r.role_id
|
||||
INNER JOIN role_permissions rp ON ua.role_id = rp.role_id
|
||||
INNER JOIN permissions p ON rp.permission_id = p.permission_id -- Contract scope
|
||||
WHERE p.is_active = 1
|
||||
AND ua.contract_id IS NOT NULL;
|
||||
|
||||
-- =====================================================
|
||||
-- Additional Useful Views
|
||||
-- =====================================================
|
||||
-- View แสดงเอกสารทั้งหมดที่มีไฟล์แนบ
|
||||
CREATE VIEW v_documents_with_attachments AS
|
||||
SELECT 'CORRESPONDENCE' AS document_type,
|
||||
c.id AS document_id,
|
||||
c.uuid AS document_uuid,
|
||||
c.correspondence_number AS document_number,
|
||||
c.project_id,
|
||||
p.uuid AS project_uuid,
|
||||
p.project_code,
|
||||
p.project_name,
|
||||
COUNT(cra.attachment_id) AS attachment_count,
|
||||
MAX(a.created_at) AS latest_attachment_date
|
||||
FROM correspondences c
|
||||
INNER JOIN projects p ON c.project_id = p.id -- [FIX] JOIN ผ่าน correspondence_revisions เพราะไฟล์ผูกกับ revision ไม่ใช่ correspondence master
|
||||
LEFT JOIN correspondence_revisions cr ON c.id = cr.correspondence_id
|
||||
AND cr.is_current = TRUE
|
||||
LEFT JOIN correspondence_revision_attachments cra ON cr.id = cra.correspondence_revision_id
|
||||
LEFT JOIN attachments a ON cra.attachment_id = a.id
|
||||
WHERE c.deleted_at IS NULL
|
||||
GROUP BY c.id,
|
||||
c.uuid,
|
||||
c.correspondence_number,
|
||||
c.project_id,
|
||||
p.uuid,
|
||||
p.project_code,
|
||||
p.project_name
|
||||
UNION ALL
|
||||
SELECT 'CIRCULATION' AS document_type,
|
||||
circ.id AS document_id,
|
||||
circ.uuid AS document_uuid,
|
||||
circ.circulation_no AS document_number,
|
||||
corr.project_id,
|
||||
p.uuid AS project_uuid,
|
||||
p.project_code,
|
||||
p.project_name,
|
||||
COUNT(ca.attachment_id) AS attachment_count,
|
||||
MAX(a.created_at) AS latest_attachment_date
|
||||
FROM circulations circ
|
||||
INNER JOIN correspondences corr ON circ.correspondence_id = corr.id
|
||||
INNER JOIN projects p ON corr.project_id = p.id
|
||||
LEFT JOIN circulation_attachments ca ON circ.id = ca.circulation_id
|
||||
LEFT JOIN attachments a ON ca.attachment_id = a.id
|
||||
GROUP BY circ.id,
|
||||
circ.circulation_no,
|
||||
corr.project_id,
|
||||
p.project_code,
|
||||
p.project_name
|
||||
UNION ALL
|
||||
SELECT 'SHOP_DRAWING' AS document_type,
|
||||
sdr.id AS document_id,
|
||||
sdr.uuid AS document_uuid,
|
||||
sd.drawing_number AS document_number,
|
||||
sd.project_id,
|
||||
p.uuid AS project_uuid,
|
||||
p.project_code,
|
||||
p.project_name,
|
||||
COUNT(sdra.attachment_id) AS attachment_count,
|
||||
MAX(a.created_at) AS latest_attachment_date
|
||||
FROM shop_drawing_revisions sdr
|
||||
INNER JOIN shop_drawings sd ON sdr.shop_drawing_id = sd.id
|
||||
INNER JOIN projects p ON sd.project_id = p.id
|
||||
LEFT JOIN shop_drawing_revision_attachments sdra ON sdr.id = sdra.shop_drawing_revision_id
|
||||
LEFT JOIN attachments a ON sdra.attachment_id = a.id
|
||||
WHERE sd.deleted_at IS NULL
|
||||
GROUP BY sdr.id,
|
||||
sd.drawing_number,
|
||||
sd.project_id,
|
||||
p.project_code,
|
||||
p.project_name
|
||||
UNION ALL
|
||||
SELECT 'CONTRACT_DRAWING' AS document_type,
|
||||
cd.id AS document_id,
|
||||
cd.uuid AS document_uuid,
|
||||
cd.condwg_no AS document_number,
|
||||
cd.project_id,
|
||||
p.uuid AS project_uuid,
|
||||
p.project_code,
|
||||
p.project_name,
|
||||
COUNT(cda.attachment_id) AS attachment_count,
|
||||
MAX(a.created_at) AS latest_attachment_date
|
||||
FROM contract_drawings cd
|
||||
INNER JOIN projects p ON cd.project_id = p.id
|
||||
LEFT JOIN contract_drawing_attachments cda ON cd.id = cda.contract_drawing_id
|
||||
LEFT JOIN attachments a ON cda.attachment_id = a.id
|
||||
WHERE cd.deleted_at IS NULL
|
||||
GROUP BY cd.id,
|
||||
cd.condwg_no,
|
||||
cd.project_id,
|
||||
p.project_code,
|
||||
p.project_name;
|
||||
|
||||
-- View แสดงสถิติเอกสารตามประเภทและสถานะ
|
||||
CREATE VIEW v_document_statistics AS
|
||||
SELECT p.id AS project_id,
|
||||
p.project_code,
|
||||
p.project_name,
|
||||
ct.id AS correspondence_type_id,
|
||||
ct.type_code,
|
||||
ct.type_name,
|
||||
cs.id AS status_id,
|
||||
cs.status_code,
|
||||
cs.status_name,
|
||||
COUNT(DISTINCT c.id) AS document_count,
|
||||
COUNT(DISTINCT cr.id) AS revision_count
|
||||
FROM projects p
|
||||
CROSS JOIN correspondence_types ct
|
||||
CROSS JOIN correspondence_status cs
|
||||
LEFT JOIN correspondences c ON p.id = c.project_id
|
||||
AND ct.id = c.correspondence_type_id
|
||||
LEFT JOIN correspondence_revisions cr ON c.id = cr.correspondence_id
|
||||
AND cs.id = cr.correspondence_status_id
|
||||
AND cr.is_current = TRUE
|
||||
WHERE p.is_active = 1
|
||||
AND ct.is_active = 1
|
||||
AND cs.is_active = 1
|
||||
GROUP BY p.id,
|
||||
p.project_code,
|
||||
p.project_name,
|
||||
ct.id,
|
||||
ct.type_code,
|
||||
ct.type_name,
|
||||
cs.id,
|
||||
cs.status_code,
|
||||
cs.status_name;
|
||||
|
||||
-- =====================================================
|
||||
-- Indexes for View Performance Optimization
|
||||
-- =====================================================
|
||||
-- Indexes for v_current_correspondences performance
|
||||
CREATE INDEX idx_correspondences_type_project ON correspondences (correspondence_type_id, project_id);
|
||||
|
||||
CREATE INDEX idx_corr_revisions_current_status ON correspondence_revisions (is_current, correspondence_status_id);
|
||||
|
||||
CREATE INDEX idx_corr_revisions_correspondence_current ON correspondence_revisions (correspondence_id, is_current);
|
||||
|
||||
-- Indexes for v_current_rfas performance
|
||||
CREATE INDEX idx_rfa_revisions_status ON rfa_revisions (rfa_status_code_id);
|
||||
|
||||
-- Indexes for document statistics performance
|
||||
CREATE INDEX idx_correspondences_project_type ON correspondences (project_id, correspondence_type_id);
|
||||
|
||||
CREATE INDEX idx_corr_revisions_status_current ON correspondence_revisions (correspondence_status_id, is_current);
|
||||
|
||||
CREATE INDEX IDX_AUDIT_DOC_ID ON document_number_audit (document_id);
|
||||
|
||||
CREATE INDEX IDX_AUDIT_STATUS ON document_number_audit (STATUS);
|
||||
|
||||
CREATE INDEX IDX_AUDIT_OPERATION ON document_number_audit (operation);
|
||||
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -588,6 +588,7 @@ export class WorkflowEngineService {
|
||||
|---------|------|---------|--------|
|
||||
| 1.0 | 2026-02-24 | Initial version - DSL-based Unified Workflow Engine | ✅ Active |
|
||||
| 1.1 | 2026-05-02 | Production hardening: JSON Logic condition engine, optimistic lock concurrency, BullMQ dedicated queue, context schema two-phase validation, async-only auto-action rule | ✅ Active |
|
||||
| 1.9.0 | 2026-05-13 | Finalized integration with RFA Approval System and Integrated Workflow Context (ADR-021) | ✅ Active |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-017: Ollama Data Migration Architecture
|
||||
|
||||
**Status:** Accepted
|
||||
**Status:** Superseded by [ADR-023: Unified AI Architecture](./ADR-023-unified-ai-architecture.md)
|
||||
**Date:** 2026-02-26
|
||||
**Version:** 1.8.3 (Aligned with ADR-020)
|
||||
**Review Cycle:** Core ADR (Review every 6 months or Major Version upgrade)
|
||||
@@ -553,3 +553,4 @@ _สำหรับขั้นตอนปฏิบัติงานแบบ
|
||||
| 1.8.0 | 2026-02-26 | DevOps Team | Initial ADR — Ollama + n8n Migration Architecture |
|
||||
| 1.8.2 | 2026-04-03 | Tech Lead | **Updated** — Aligned with ADR-019 (UUID Strategy), changed AI Model to `gemma4:9b` (9.6 GB) |
|
||||
| 1.8.4 | 2026-04-04 | System Architect | **Enhanced** — Added Impact Analysis template, ADR Review Cycle process, Gap Linking to requirements, and Version Dependency tracking |
|
||||
| 1.8.5 | 2026-05-14 | System Architect | **Superseded** — Consolidated into master AI architecture [ADR-023](./ADR-023-unified-ai-architecture.md) |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-017B: AI Document Classification
|
||||
|
||||
**Status:** Accepted
|
||||
**Status:** Superseded by [ADR-023: Unified AI Architecture](./ADR-023-unified-ai-architecture.md)
|
||||
**Date:** 2026-03-27
|
||||
**Version:** 1.8.2 (Aligned with ADR-020)
|
||||
**Review Cycle:** Core ADR (Review every 6 months or Major Version upgrade)
|
||||
@@ -461,10 +461,11 @@ Phase 2: Final Commit (โดย Admin ผ่าน Frontend)
|
||||
| 1.8.1 | 2026-03-27 | Tech Lead | **Refactored to ADR format** — Aligned with ADR-017, ADR-018, and Project Specs |
|
||||
| 1.8.3 | 2026-04-04 | System Architect | **Renamed** — Changed from "Smart Legacy Document Digitization" to "AI Document Classification" for clarity and simplicity |
|
||||
| 1.8.4 | 2026-04-04 | System Architect | **Enhanced** — Added Impact Analysis template, ADR Review Cycle process, Gap Linking to requirements, and Version Dependency tracking |
|
||||
| 1.8.5 | 2026-05-14 | System Architect | **Superseded** — Consolidated into master AI architecture [ADR-023](./ADR-023-unified-ai-architecture.md) |
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-04-04
|
||||
**Status:** Accepted
|
||||
**Status:** Superseded by ADR-023
|
||||
**Next Review:** 2026-06-01 (Quarterly review)
|
||||
**Next 6-Month Review:** 2026-10-04 (regular review cycle)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-018: AI Boundary Policy (AI Isolation)
|
||||
|
||||
**Status:** Accepted
|
||||
**Status:** Superseded by [ADR-023: Unified AI Architecture](./ADR-023-unified-ai-architecture.md)
|
||||
**Date:** 2026-03-27
|
||||
**Version:** 1.8.2 (Aligned with ADR-020)
|
||||
**Review Cycle:** Core ADR (Review every 6 months or Major Version upgrade)
|
||||
@@ -548,10 +548,11 @@ Response:
|
||||
| 1.8.1 | 2026-03-27 | Security Lead| Initial ADR — AI Boundary Policy (Physical Isolation) |
|
||||
| 1.8.2 | 2026-04-03 | Tech Lead | Updated — Aligned AI Model spec with ADR-017/017B |
|
||||
| 1.8.3 | 2026-04-04 | System Architect | Enhanced — Added Impact Analysis template, ADR Review Cycle process, Gap Linking to requirements, and Version Dependency tracking |
|
||||
| 1.8.4 | 2026-05-14 | System Architect | Superseded — Consolidated into master AI architecture [ADR-023](./ADR-023-unified-ai-architecture.md) |
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-04-04
|
||||
**Status:** Accepted
|
||||
**Status:** Superseded by ADR-023
|
||||
**Next Review:** 2026-06-01 (Quarterly security review)
|
||||
**Next 6-Month Review:** 2026-10-04 (regular review cycle)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-020: AI Intelligence Integration Architecture
|
||||
|
||||
**Status:** Proposed
|
||||
**Status:** Superseded by [ADR-023: Unified AI Architecture](./ADR-023-unified-ai-architecture.md)
|
||||
**Date:** 2026-04-03
|
||||
**Version:** 1.8.5
|
||||
**Review Cycle:** Core ADR (Review every 6 months or Major Version upgrade)
|
||||
@@ -654,11 +654,12 @@ OUTPUT FORMAT:
|
||||
| 1.8.5 | 2026-04-03 | AI Integration Lead | Initial ADR — AI Intelligence Integration Architecture |
|
||||
| 1.8.6 | 2026-04-03 | Tech Lead | Updated — Aligned with detailed task specifications and implementation requirements |
|
||||
| 1.8.7 | 2026-04-04 | System Architect | Enhanced — Added Impact Analysis template, ADR Review Cycle process, Gap Linking to requirements, and Version Dependency tracking |
|
||||
| 1.8.8 | 2026-05-14 | System Architect | Superseded — Consolidated into master AI architecture [ADR-023](./ADR-023-unified-ai-architecture.md) |
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-04-04
|
||||
**Status:** Proposed
|
||||
**Status:** Superseded by ADR-023
|
||||
**Review Date:** 2026-04-10
|
||||
**Implementation Target:** v1.9.0
|
||||
**Next Review Date:** 2026-10-04 (6-month regular review)
|
||||
|
||||
+6
-5
@@ -1,9 +1,9 @@
|
||||
# ADR-021: Integrated Workflow Context & Step-specific Attachments
|
||||
|
||||
## 1. ข้อมูลทั่วไป (General Information)
|
||||
- **Status:** Proposed
|
||||
- **Version:** 1.8.6
|
||||
- **Date:** 2026-04-12
|
||||
- **Status:** Active
|
||||
- **Version:** 1.9.0
|
||||
- **Date:** 2026-05-13
|
||||
- **Author:** Senior Full Stack Developer (Windsurf AI)
|
||||
- **Reference:** ADR-001 (Workflow Engine), ADR-002 (Redlock), ADR-016 (Security)
|
||||
|
||||
@@ -115,7 +115,7 @@
|
||||
## 6. รายการไฟล์ที่ต้องอัพเดท (Impacted Files)
|
||||
|
||||
### 🔴 Backend Layer (Data & Logic)
|
||||
- `specs/03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql`
|
||||
- `specs/03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql`
|
||||
- *Action:* เพิ่ม FK `workflow_history_id` ในตาราง `attachments`
|
||||
- `backend/src/common/file-storage/entities/attachment.entity.ts`
|
||||
- *Action:* เพิ่ม Relation `@ManyToOne(() => WorkflowHistory)`
|
||||
@@ -249,4 +249,5 @@
|
||||
### Version History
|
||||
| Version | Date | Changes | Status |
|
||||
|---------|------|---------|--------|
|
||||
| 1.8.6 | 2026-04-12 | Initial version - Integrated Workflow Context & Step-specific Attachments | 🔄 Proposed |
|
||||
| 1.8.6 | 2026-04-12 | Initial version - Integrated Workflow Context & Step-specific Attachments | ✅ Active |
|
||||
| 1.9.0 | 2026-05-13 | Final integration with RFA Approval System and master schema v1.9.0 | ✅ Active |
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-022: Retrieval-Augmented Generation (RAG) System
|
||||
|
||||
**Status:** Accepted
|
||||
**Status:** Superseded by [ADR-023: Unified AI Architecture](./ADR-023-unified-ai-architecture.md)
|
||||
**Date:** 2026-04-19
|
||||
**Decision Makers:** Development Team, System Architect
|
||||
**Related Documents:**
|
||||
|
||||
@@ -0,0 +1,250 @@
|
||||
# ADR-023: Unified AI Architecture (สถาปัตยกรรม AI หลักแบบผสานรวม)
|
||||
|
||||
**Status:** Accepted
|
||||
**Date:** 2026-05-14
|
||||
**Decision Makers:** Development Team, System Architect, Security Team, AI Integration Lead
|
||||
**Related Documents:**
|
||||
- [Glossary](../00-Overview/00-02-glossary.md)
|
||||
- [Data Dictionary](../03-Data-and-Storage/03-01-data-dictionary.md)
|
||||
- [Legacy Data Migration Plan](../03-Data-and-Storage/03-04-legacy-data-migration.md)
|
||||
- [n8n Migration Setup Guide](../03-Data-and-Storage/03-05-n8n-migration-setup-guide.md)
|
||||
- [ADR-016: Security & Authentication](./ADR-016-security-authentication.md)
|
||||
- [ADR-019: Hybrid Identifier Strategy](./ADR-019-hybrid-identifier-strategy.md)
|
||||
- [RAG Implementation Guide v1.1.2](../08-Tasks/ADR-022-Retrieval-Augmented-Generation/LCBP3-RAG-Implementation-Guide-v1.1.2.md)
|
||||
|
||||
> **หมายเหตุ:** ADR-023 เป็นเอกสารสถาปัตยกรรม AI หลักของระบบ (Master AI Architecture) ที่ทำการยุบรวมและแทนที่ (Supersede) เอกสาร ADR-017, ADR-017B, ADR-018, ADR-020, และ ADR-022 เพื่อให้มี Single Source of Truth เพียงที่เดียวในการควบคุมทั้งโครงสร้างพื้นฐาน, ความปลอดภัย, การนำเข้าข้อมูลเก่า, การสกัดข้อมูลอัตโนมัติ, และระบบสืบค้น RFA/Correspondence แบบถาม-ตอบเชิงลึก
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Gap Analysis & Purpose
|
||||
|
||||
### ปิด Gap จากเอกสาร:
|
||||
- **Product Vision v1.8.5** - Section 3.1, 3.2, 3.3: ความต้องการยกระดับประสิทธิภาพการนำเข้าเอกสารเก่าและการจัดการเอกสารใหม่ด้วย AI Intelligence
|
||||
- เหตุผล: การทำงานด้วยมือ (Manual) มีต้นทุนเวลาสูงและเกิดความผิดพลาดได้ง่าย จำเป็นต้องมี AI ช่วยตรวจสอบและจัดหมวดหมู่โดยอัตโนมัติ
|
||||
- **Security Requirements & Risk Assessment** - Section 3.1, 4.2: ความเสี่ยงด้านข้อมูลรั่วไหลและการเข้าถึงฐานข้อมูลโดยตรงจากเซอร์วิส AI
|
||||
- เหตุผล: ข้อมูลเอกสารก่อสร้างท่าเรือแหลมฉบัง เฟส 3 เป็นความลับระดับสูง (Confidential) ต้องมีขอบเขตความปลอดภัย (AI Boundary) ที่รัดกุม
|
||||
|
||||
### แก้ไขความขัดแย้งและการกระจายตัวของเอกสาร:
|
||||
- **ADR-017, ADR-017B, ADR-018, ADR-020, ADR-022** มีความทับซ้อนในเชิงสถาปัตยกรรมและข้อกำหนด
|
||||
- การตัดสินใจนี้ช่วยแก้ไขโดย: ยุบรวมข้อกำหนดทั้งหมดเข้าสู่ร่มใหญ่ฉบับเดียว (Consolidation) เพื่อลดปัญหา Revision Drift และทำให้การทบทวนสถาปัตยกรรม (Review Cycle) เป็นไปอย่างสอดคล้องกันทั้งระบบ
|
||||
|
||||
---
|
||||
|
||||
## Context and Problem Statement
|
||||
|
||||
โครงการ LCBP3-DMS มีความต้องการประยุกต์ใช้ AI ในการเพิ่มประสิทธิภาพการบริหารจัดการเอกสารวิศวกรรมโยธาขนาดใหญ่ โดยเผชิญกับโจทย์และความท้าทายหลัก 5 ด้าน:
|
||||
|
||||
1. **Legacy Document Migration:** เอกสาร PDF เก่ากว่า 20,000 ฉบับ ต้องนำเข้าระบบพร้อมตรวจสอบความสอดคล้องกับ Metadata ใน Excel
|
||||
2. **Real-time Ingestion & Classification:** เอกสารใหม่ที่ผู้ใช้อัปโหลดต้องการการสกัด Metadata และจัดหมวดหมู่แบบเรียลไทม์เพื่อลดภาระงานกรอกข้อมูล
|
||||
3. **Conversational Retrieval (RAG):** Full-text search บน MariaDB ไม่เข้าใจบริบท (Semantic) และการตัดคำภาษาไทย ทำให้สืบค้นข้อมูลเชิงลึกได้ยาก
|
||||
4. **Data Confidentiality & Privacy:** ห้ามส่งข้อมูลความลับออกนอกเครือข่ายองค์กรไปยัง Cloud AI Provider (เช่น OpenAI, Google)
|
||||
5. **System Stability & Isolation:** การรัน AI Inference ใช้ทรัพยากรสูง (GPU VRAM/CPU) ไม่ควรรันร่วมกับ Production Server หลัก (QNAP NAS) เพื่อไม่ให้กระทบประสิทธิภาพของระบบ
|
||||
|
||||
---
|
||||
|
||||
## Decision Drivers
|
||||
|
||||
- **Zero Trust & Physical Isolation:** AI ต้องถูกปฏิบัติเสมือน Untrusted Component รันแยกต่างหากบน Admin Desktop เท่านั้น
|
||||
- **RFA-First Approach:** มุ่งเน้นกระบวนการเอกสาร RFA (Request for Approval) ซึ่งซับซ้อนที่สุดเป็นแกนหลัก
|
||||
- **Data Integrity & Human-in-the-Loop:** ข้อมูลจาก AI ต้องผ่านการทวนสอบและยืนยันโดยมนุษย์ก่อน Commit ลงฐานข้อมูลจริงเสมอ
|
||||
- **Multi-tenant Isolation:** ต้องแยกขอบเขตข้อมูลของแต่ละโครงการอย่างเด็ดขาดในระดับ Vector Database Payload Filter
|
||||
- **Cost Effectiveness:** ประมวลผลภายในองค์กร (On-Premises) เพื่อหลีกเลี่ยงค่าใช้จ่ายแบบ Pay-per-use
|
||||
- **Two-Phase Storage Governance:** ควบคุมการย้ายไฟล์ทุกขั้นตอนผ่าน `StorageService` เพื่อให้สแกนไวรัสและเก็บ Audit Log ได้ครบถ้วน
|
||||
|
||||
---
|
||||
|
||||
## Considered Options
|
||||
|
||||
### Option 1: Fragmented AI Subsystems (แยกระบบ AI ตาม Use Case)
|
||||
**Pros:**
|
||||
- ออกแบบและพัฒนาง่ายในระยะสั้น แต่ละส่วนไม่พึ่งพากัน
|
||||
**Cons:**
|
||||
- ❌ เกิด Code Duplication สูง
|
||||
- ❌ มาตรฐานความปลอดภัยและการควบคุมสิทธิ์ไม่สม่ำเสมอ
|
||||
- ❌ บำรุงรักษายากเมื่อมีการเปลี่ยนโมเดลหรือโครงสร้าง Prompt
|
||||
|
||||
### Option 2: Cloud AI Platform Integration
|
||||
**Pros:**
|
||||
- โมเดลมีความฉลาดแม่นยำสูงมาก ไม่ต้องลงทุนและบำรุงรักษา Hardware
|
||||
**Cons:**
|
||||
- ❌ **ผิดข้อกำหนดด้าน Data Privacy** อย่างรุนแรงสำหรับเอกสาร Confidential
|
||||
- ❌ ค่าใช้จ่ายสูงมากเมื่อต้องประมวลผลเอกสารเก่ากว่า 20,000 ฉบับและรองรับ RAG
|
||||
|
||||
### Option 3: Unified Master AI Architecture (On-Premises Isolation + Multi-layer Gateways) ⭐ **SELECTED**
|
||||
**Pros:**
|
||||
- ✅ **Single Source of Truth:** รวมสถาปัตยกรรมและข้อกำหนดความปลอดภัยไว้ที่เดียว
|
||||
- ✅ **Absolute Isolation:** แยกส่วน AI Workload ไปยัง Admin Desktop ปลอดภัยและไม่กวน Production NAS
|
||||
- ✅ **High Reusability:** ใช้ Pipeline และ UI Components (เช่น `DocumentReviewForm`) ร่วมกันได้ทั้งหมด
|
||||
- ✅ **Thai Language Optimized:** ผสานการทำงานของ PaddleOCR และ PyThaiNLP เพื่อคุณภาพข้อความภาษาไทยสูงสุด
|
||||
- ✅ **Absolute Multitenancy:** ป้องกันข้อมูลข้ามโครงการด้วย Qdrant Payload Filter
|
||||
|
||||
**Cons:**
|
||||
- ❌ สถาปัตยกรรมมีความซับซ้อน ต้องใช้คิว (BullMQ) และการจัดการ State ที่รัดกุม
|
||||
|
||||
---
|
||||
|
||||
## Decision Outcome
|
||||
|
||||
**Chosen Option:** Option 3 — Unified Master AI Architecture
|
||||
|
||||
### Rationale
|
||||
การรวมสถาปัตยกรรม AI ไว้ภายใต้ร่มใหญ่เดียวช่วยลดความซ้ำซ้อนของโค้ดและการบำรุงรักษา พร้อมทั้งบังคับใช้นโยบายความปลอดภัยสูงสุด (Physical Isolation & No Direct DB Access) ได้กับทุกฟีเจอร์อย่างเสมอภาค ทำให้ระบบมีความเสถียร ปลอดภัย และพร้อมขยายขีดความสามารถในอนาคต
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Impact Analysis
|
||||
|
||||
### Affected Components
|
||||
|
||||
| Component | Level | Impact Description | Required Action |
|
||||
|-----------|-------|-------------------|-----------------|
|
||||
| **Security Layer** | 🔴 High | บังคับใช้ขอบเขตการเชื่อมต่อผ่าน API Gateway เท่านั้น | เพิ่ม permissions `ai.suggest`, `ai.rag_query`, `ai.migration_manage`, `ai.audit_log_delete` และ Assign ตาม Role Matrix ด้านล่าง |
|
||||
| **Backend (NestJS)** | 🔴 High | สร้าง `AiModule` เป็นศูนย์กลางควบคุม Pipeline และ RAG | พัฒนา Gateway Services และ Validation Layers |
|
||||
| **Database** | 🔴 High | ตารางจัดเก็บประวัติการทวนสอบและสถานะเวกเตอร์ | สร้าง `migration_review_queue` และ `ai_audit_logs` (แยก table, ไม่ใช่ Compliance — เป็น AI Development Feedback Log) |
|
||||
| **Frontend (Next.js)** | 🟡 Medium | หน้าจอแสดงผลลัพธ์จาก AI พร้อมค่า Confidence | พัฒนา Reusable Form Components และ Dashboard |
|
||||
| **Infrastructure** | 🔴 High | การตั้งค่า Admin Desktop (Desk-5439) สำหรับ AI | ติดตั้ง Ollama, Qdrant, n8n, PaddleOCR, PyThaiNLP |
|
||||
|
||||
### Cross-Module Dependencies
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph QNAP["🖥️ QNAP NAS (Production Host)"]
|
||||
BE[NestJS Backend API]
|
||||
N8N[n8n Workflow Orchestrator]
|
||||
end
|
||||
|
||||
subgraph DESK["🖥️ Desk-5439 (AI Isolation Host)"]
|
||||
OLLAMA["Ollama\ngemma4:9b + Typhoon Local\n+ nomic-embed-text"]
|
||||
QDRANT[Qdrant Vector Store]
|
||||
NLP[PaddleOCR + PyThaiNLP]
|
||||
end
|
||||
|
||||
BE --"HTTP API"--> N8N
|
||||
N8N --"Ollama REST API"--> OLLAMA
|
||||
N8N --"Qdrant REST API"--> QDRANT
|
||||
N8N --"HTTP"--> NLP
|
||||
N8N --"DMS API (MariaDB update)"--> BE
|
||||
BE --"RAG Query"--> QDRANT
|
||||
BE --"LLM Inference"--> OLLAMA
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Version Dependency Matrix
|
||||
|
||||
| ADR | Version | Dependency Type | Affected Version(s) | Implementation Status |
|
||||
|-----|---------|-----------------|---------------------|----------------------|
|
||||
| **ADR-023** | 1.0 | Master Core | v1.9.0+ | ✅ Consolidated |
|
||||
| **ADR-016** | 2.0 | Governs | v1.8.0+ | ✅ Active |
|
||||
| **ADR-019** | 1.5 | Governs | v1.8.0+ | ✅ Active |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Details (ข้อกำหนดเชิงลึกรายหมวด)
|
||||
|
||||
### 1. Security Isolation Policy (ขอบเขตความปลอดภัย)
|
||||
* **Physical Isolation:** เซอร์วิส AI ทั้งหมด (Ollama, Qdrant, PaddleOCR) **ต้องรันบน Admin Desktop (Desk-5439)** ที่มี GPU RTX 2060 Super 8GB เท่านั้น ห้ามรันบน QNAP NAS หลัก
|
||||
* **No Direct DB/Storage Access:** เครื่อง AI Host **ห้าม**มีการเชื่อมต่อฐานข้อมูล MariaDB หรือเมาท์ Storage ปลายทางโดยตรง การอ่าน/เขียนข้อมูลทั้งหมดต้องทำผ่าน **DMS Backend API**
|
||||
* **Validation Layer:** Backend ต้องตรวจสอบความถูกต้องของ Output จาก AI (Schema, System Enum, Confidence Threshold) ก่อนบันทึกลงฐานข้อมูลเสมอ
|
||||
|
||||
#### AI RBAC Permission Matrix
|
||||
|
||||
> Permission ใหม่ที่ต้องเพิ่มใน `lcbp3-v1.9.0-seed-permissions.sql` (module: `ai`, ID range: 181-190)
|
||||
|
||||
| Permission | คำอธิบาย | Superadmin (1) | Org Admin (2) | Document Control (3) | Editor (4) | Viewer (5) |
|
||||
|---|---|:---:|:---:|:---:|:---:|:---:|
|
||||
| `ai.suggest` | รับ AI Suggestion เมื่อสร้าง/แก้ไขเอกสาร | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| `ai.rag_query` | ใช้ RAG Q&A สืบค้นเอกสาร | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| `ai.migration_manage` | จัดการ Migration Batch (Review/Import/Reject) | ✅ | ✅ | ✅ | ❌ | ❌ |
|
||||
| `ai.audit_log_delete` | Hard Delete `ai_audit_logs` | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
|
||||
### 2. Core Infrastructure & Models
|
||||
|
||||
> **นโยบาย:** เอกสารทั้งหมดใน LCBP3 จัดชั้นเป็น **INTERNAL** — AI Inference ทั้งหมดต้องรันภายใน Physical Isolation Boundary บน Desk-5439 เท่านั้น ห้ามใช้ Cloud AI Provider โดยเด็ดขาด
|
||||
|
||||
* **Orchestrator:** ใช้ **n8n** เป็นตัวควบคุม Flow การนำเข้าและเตรียมข้อมูล
|
||||
* **LLM Engine (General Inference):** ใช้ **Ollama** บน Desk-5439 รันโมเดล `gemma4:9b` สำหรับงานทำความเข้าใจเอกสารและ RAG Q&A
|
||||
* **LLM Engine (OCR Post-processing & Extraction):** ใช้ **Typhoon Local Model** (Typhoon 2 series) รันผ่าน Ollama บน Desk-5439 สำหรับทำความสะอาดข้อความ (OCR Post-processing) และสกัด Metadata (Classification/Extraction) จากข้อความที่ PaddleOCR สกัดมาแล้ว
|
||||
* **Embedding Model:** ใช้ `nomic-embed-text` รันผ่าน Ollama บน Desk-5439 สำหรับแปลงเวกเตอร์ 768-มิติ
|
||||
* **OCR & NLP:** ใช้ **PaddleOCR** สกัดข้อความจาก Scanned PDF และใช้ **PyThaiNLP** ตัดคำ/เตรียมข้อความภาษาไทย — ทั้งคู่รันบน Desk-5439
|
||||
* ❌ **Typhoon Cloud API:** ไม่ใช้ — `rag/typhoon.service.ts` ต้องถูก Remove ออกจาก Codebase (Dead Code + Security Risk)
|
||||
|
||||
### 3. Legacy Data Migration (การนำเข้าข้อมูลเก่า)
|
||||
* **Staging Area:** ข้อมูลที่ประมวลผลผ่าน n8n จะถูกส่งเข้าตาราง `migration_review_queue` เสมอ
|
||||
* **Record Lifecycle:** Record ใน `migration_review_queue` **ไม่ถูกลบ** หลัง Import — เปลี่ยน `status` เป็น `IMPORTED` เก็บไว้ตลอดเพื่อ Debug และตรวจสอบ Batch ย้อนหลัง
|
||||
* Status transitions: `PENDING` → `IMPORTED` | `PENDING` → `REJECTED`
|
||||
* **Confidence Threshold Policy** (กำหนดผ่าน `.env` — ไม่ Hardcode, ไม่มี Admin UI):
|
||||
* `AI_THRESHOLD_HIGH=0.85` และ `is_valid = true` $\rightarrow$ สถานะ `PENDING` (พร้อม Import)
|
||||
* `AI_THRESHOLD_MID=0.60` ถึง `AI_THRESHOLD_HIGH-0.01` $\rightarrow$ สถานะ `PENDING` (ไฮไลต์เตือน Admin)
|
||||
* ต่ำกว่า `AI_THRESHOLD_MID` หรือ `is_valid = false` $\rightarrow$ สถานะ `REJECTED`
|
||||
* การเปลี่ยนค่าต้อง Restart service และมีร่องรอยใน deployment log
|
||||
* **Idempotency Header:** บังคับส่ง `Idempotency-Key: <doc_number>:<batch_id>` ป้องกันบันทึกซ้ำ
|
||||
* **Two-Phase Storage:** ไฟล์ถูกอัปโหลดเป็น Temp (`is_temporary = true`) และย้ายเข้า Storage จริงเมื่อเรียก API Commit เท่านั้น
|
||||
|
||||
### 4. Smart Classification & Real-time Ingestion
|
||||
* **Enum Enforcement:** ฟิลด์หมวดหมู่และประเภทเอกสารที่สกัดได้ ต้องนำไปทวนสอบกับ Master Data (`GET /api/meta/categories`) เสมอ ห้ามให้ AI สร้างประเภทเอกสารขึ้นมาเองโดยพลการ
|
||||
* **Human Override:** นำเสนอผลลัพธ์บนหน้าจอ RFA/Correspondence ให้ผู้ใช้กดยืนยันหรือแก้ไขก่อนบันทึก
|
||||
|
||||
### 5. Hybrid Retrieval-Augmented Generation (RAG)
|
||||
* **Hybrid Search Strategy:** ผสานคะแนน Vector Similarity (0.7) + Keyword Exact Match (0.3) และผ่าน Score-based Re-ranking
|
||||
* **Multi-tenant Isolation:** บังคับใช้ Qdrant Payload Filter กำหนด `project_public_id` เป็นเงื่อนไขในการสืบค้นทุกครั้งเพื่อป้องกันข้อมูลรั่วไหลข้ามโครงการ
|
||||
* **LLM สำหรับ RAG Q&A:** ใช้ **Local Ollama (`gemma4:9b`)** บน Desk-5439 เท่านั้น — ไม่มี Cloud Fallback เนื่องจากเอกสารทั้งหมดจัดชั้นเป็น INTERNAL
|
||||
* **Performance Target:** $p95 < 10s$ สำหรับการตอบคำถามผ่าน Local LLM
|
||||
|
||||
### 6. `ai_audit_logs` — AI Development Feedback Log
|
||||
|
||||
> **วัตถุประสงค์:** บันทึก AI Suggestion + การตัดสินใจของมนุษย์เพื่อใช้วิเคราะห์และปรับปรุงคุณภาพโมเดล AI — **ไม่ใช่ Compliance Audit Trail**
|
||||
> Compliance จริงๆ ถูกบันทึกอยู่ใน `audit_logs` แล้ว (Human Confirm Action)
|
||||
|
||||
* **Key Columns:** `document_public_id`, `model_name`, `ai_suggestion_json`, `human_override_json`, `confidence_score`, `confirmed_by_user_id`, `created_at`
|
||||
* **Retention:** ตลอดอายุโครงการ (~5-10 ปี) — Admin สามารถ **Hard Delete** ได้ผ่าน Frontend เพื่อจัดการ Test Data และ Storage
|
||||
* **RBAC:** เฉพาะ Role `SYSTEM_ADMIN` เท่านั้นที่ลบได้ — การลบทุกครั้งต้องบันทึกใน `audit_logs` (`action: 'AI_AUDIT_LOG_DELETED'`)
|
||||
* **ห้าม Merge:** ต้องเป็น Table แยกจาก `audit_logs` เพื่อให้ Query ด้วย Typed Columns ได้ (เช่น `WHERE confidence_score < 0.85`)
|
||||
|
||||
---
|
||||
|
||||
## Consequences
|
||||
|
||||
### Positive
|
||||
1. ✅ มีมาตรฐานสถาปัตยกรรม AI ที่เป็นหนึ่งเดียว ง่ายต่อการอ้างอิงและตรวจสอบสิทธิ์
|
||||
2. ✅ ปลอดภัยสูงสุดตามหลักการ Zero Trust ป้องกันฐานข้อมูลและไฟล์ระบบจากความเสี่ยงของเซอร์วิสภายนอก
|
||||
3. ✅ รองรับเอกสารภาษาไทยได้อย่างแม่นยำผ่านกระบวนการ NLP เฉพาะทาง
|
||||
4. ✅ ควบคุมการใช้งานทรัพยากรได้อย่างมีประสิทธิภาพ ไม่รบกวน Production NAS
|
||||
|
||||
### Negative
|
||||
1. ❌ ระบบมีความซับซ้อนในการตั้งค่าและเชื่อมต่อเครือข่ายระหว่าง NAS และ Admin Desktop
|
||||
2. ❌ มี Overhead ในการดูแลรักษาเครื่อง Desktop (GPU Temperature, Service Uptime)
|
||||
|
||||
### Mitigation Strategies
|
||||
* **Graceful Degradation (Desk-5439 ออฟไลน์):** DMS Core ยังทำงานได้ปกติทุก Feature — เฉพาะ AI Features ถูก Disable ชั่วคราว:
|
||||
* Backend ตรวจสอบ Health Check ของ Desk-5439 ทุก **60 วินาที** ผ่าน `/health` endpoint ของ Ollama และ Qdrant
|
||||
* เมื่อ Desk-5439 ออฟไลน์ → set `AI_AVAILABLE = false` ใน Redis Cache
|
||||
* Frontend แสดง **Global Banner:** "⚠️ ระบบ AI ไม่พร้อมใช้งานชั่วคราว กรุณากรอกข้อมูลด้วยตนเอง"
|
||||
* AI Classification form fields แสดงผล แต่ AI Suggestion ถูก hide — User กรอกเองได้ปกติ
|
||||
* RAG Q&A endpoint return `503 Service Unavailable` พร้อม error message ที่อ่านเข้าใจได้
|
||||
* **GPU Overload Prevention:** ใช้ BullMQ จัดคิวงาน AI แบบ Sequential เพื่อไม่ให้ VRAM 8GB โอเวอร์โหลด
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Review Cycle & Maintenance
|
||||
|
||||
### Review Schedule
|
||||
- **Next Review:** 2026-11-14 (6 months from creation)
|
||||
- **Review Type:** Scheduled Core Architecture Review
|
||||
- **Reviewers:** System Architect, Security Lead, AI Integration Lead
|
||||
|
||||
### Review History
|
||||
| Version | Date | Changes | Status |
|
||||
|---------|------|---------|--------|
|
||||
| 1.0 | 2026-05-14 | ยุบรวมและแทนที่ ADR-017, 017B, 018, 020, 022 เป็นฉบับเดียว | ✅ Active |
|
||||
| 1.1 | 2026-05-14 | Grilling Session: (1) ล็อค Local-only AI บน Desk-5439 ทั้งหมด (2) แยก Typhoon Local vs Cloud (3) ลบ Typhoon Cloud API ออก (4) กำหนด `ai_audit_logs` เป็น Development Feedback Log ไม่ใช่ Compliance (5) เพิ่ม Admin Hard Delete Policy | ✅ Active |
|
||||
|
||||
---
|
||||
|
||||
## Related ADRs (อดีตเอกสารที่ถูกแทนที่)
|
||||
|
||||
- [ADR-017: Ollama Data Migration Architecture](./ADR-017-ollama-data-migration.md) — **Superseded**
|
||||
- [ADR-017B: AI Document Classification](./ADR-017B-ai-document-classification.md) — **Superseded**
|
||||
- [ADR-018: AI Boundary Policy](./ADR-018-ai-boundary.md) — **Superseded**
|
||||
- [ADR-020: AI Intelligence Integration Architecture](./ADR-020-ai-intelligence-integration.md) — **Superseded**
|
||||
- [ADR-022: Retrieval-Augmented Generation (RAG) System](./ADR-022-retrieval-augmented-generation.md) — **Superseded**
|
||||
@@ -1,7 +1,7 @@
|
||||
# Architecture Decision Records (ADRs)
|
||||
|
||||
**Version:** 1.8.5
|
||||
**Last Updated:** 2026-04-10
|
||||
**Version:** 1.9.1
|
||||
**Last Updated:** 2026-05-14
|
||||
**Project:** LCBP3-DMS (Laem Chabang Port Phase 3 - Document Management System)
|
||||
|
||||
---
|
||||
@@ -89,10 +89,12 @@ Architecture Decision Records (ADRs) เป็นเอกสารที่บ
|
||||
|
||||
| ADR | Title | Status | Date | Summary |
|
||||
| ----------------------------------------------- | ---------------------------------- | ------------- | ---------- | ---------------------------------------------------------------------------- |
|
||||
| [ADR-017](./ADR-017-ollama-data-migration.md) | Ollama Data Migration Architecture | ✅ Accepted | 2026-02-26 | On-premise AI (Ollama) สำหรับ Migration 20,000+ PDFs พร้อม Validation Layer |
|
||||
| [ADR-017B](./ADR-017B-ai-document-classification.md) | AI Document Classification | ✅ Accepted | 2026-03-27 | AI-powered document classification ตาม ADR-018 (AI Isolation) |
|
||||
| [ADR-018](./ADR-018-ai-boundary.md) | AI Boundary Policy | ✅ Accepted | 2026-03-27 | Physical Isolation + API-only communication (Zero Trust for AI) |
|
||||
| [ADR-020](./ADR-020-ai-intelligence-integration.md) | AI Intelligence Integration Architecture | 🔄 Proposed | 2026-04-03 | Unified AI Pipeline สำหรับ RFA-First (Legacy Migration + New Ingestion) |
|
||||
| [ADR-017](./ADR-017-ollama-data-migration.md) | Ollama Data Migration Architecture | ❌ Superseded | 2026-02-26 | ถูกแทนที่โดย ADR-023: Unified AI Architecture |
|
||||
| [ADR-017B](./ADR-017B-ai-document-classification.md) | AI Document Classification | ❌ Superseded | 2026-03-27 | ถูกแทนที่โดย ADR-023: Unified AI Architecture |
|
||||
| [ADR-018](./ADR-018-ai-boundary.md) | AI Boundary Policy | ❌ Superseded | 2026-03-27 | ถูกแทนที่โดย ADR-023: Unified AI Architecture |
|
||||
| [ADR-020](./ADR-020-ai-intelligence-integration.md) | AI Intelligence Integration Architecture | ❌ Superseded | 2026-04-03 | ถูกแทนที่โดย ADR-023: Unified AI Architecture |
|
||||
| [ADR-022](./ADR-022-retrieval-augmented-generation.md) | Retrieval-Augmented Generation (RAG) | ❌ Superseded | 2026-04-20 | ถูกแทนที่โดย ADR-023: Unified AI Architecture |
|
||||
| [ADR-023](./ADR-023-unified-ai-architecture.md) | Unified AI Architecture | ✅ Accepted | 2026-05-14 | สถาปัตยกรรม AI หลักแบบรวมศูนย์ (Boundary, RAG, Workflows และ Isolation) |
|
||||
|
||||
---
|
||||
|
||||
@@ -144,10 +146,12 @@ Architecture Decision Records (ADRs) เป็นเอกสารที่บ
|
||||
|
||||
### 9. AI & Data Integration
|
||||
|
||||
- **ADR-017:** Ollama Data Migration - On-premise AI (Ollama) สำหรับ Migration 20,000+ PDFs
|
||||
- **ADR-017B:** Smart Document Digitization - AI-powered categorization สำหรับเอกสารเก่า
|
||||
- **ADR-018:** AI Boundary Policy - Physical Isolation + API-only communication (Zero Trust)
|
||||
- **ADR-020:** AI Intelligence Integration - Unified AI Pipeline สำหรับ RFA-First approach
|
||||
- **ADR-017:** Ollama Data Migration - (Superseded by ADR-023)
|
||||
- **ADR-017B:** Smart Document Digitization - (Superseded by ADR-023)
|
||||
- **ADR-018:** AI Boundary Policy - (Superseded by ADR-023)
|
||||
- **ADR-020:** AI Intelligence Integration - (Superseded by ADR-023)
|
||||
- **ADR-022:** Retrieval-Augmented Generation - (Superseded by ADR-023)
|
||||
- **ADR-023:** Unified AI Architecture - สถาปัตยกรรม AI หลักของระบบ ครอบคลุม Boundary, Workflows, RAG และ Hardware Isolation
|
||||
|
||||
---
|
||||
|
||||
@@ -375,8 +379,8 @@ graph TB
|
||||
|
||||
---
|
||||
|
||||
**Version:** 1.8.5 (Added ADR-003, ADR-004, ADR-007)
|
||||
**Last Review:** 2026-04-10
|
||||
**Version:** 1.9.1 (Consolidated AI ADRs into ADR-023)
|
||||
**Last Review:** 2026-05-14
|
||||
**Next Review:** 2026-10-10
|
||||
|
||||
---
|
||||
|
||||
@@ -1,160 +0,0 @@
|
||||
# Implementation Plan: RFA Approval System Refactor
|
||||
|
||||
**Branch**: `1-rfa-approval-refactor` | **Date**: 2026-05-11 | **Spec**: [spec.md](./spec.md)
|
||||
|
||||
**Input**: Refactor RFA approval system to TeamBinder/InEight-style with Review Teams, Response Codes, Delegation, Auto-Reminders, and Distribution Matrix
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
ปรับปรุงระบบการอนุมัติเอกสาร RFA ให้รองรับ:
|
||||
1. **Review Teams by Discipline** - กำหนผู้ตรวจสอบตามสาขาวิชาแทนรายบุคคล พร้อม Parallel Review
|
||||
2. **Master Approval Matrix** - Response Codes มาตรฐาน 1A-1G, 2, 3, 4 ตามหมวดงาน (Engineering, Material, Contract, Testing, ESG)
|
||||
3. **Delegation & Proxy** - มอบหมายงานแทนเมื่อไม่อยู่ พร้อมตรวจจับ Circular Delegation
|
||||
4. **Auto-Reminders & Escalation** - แจ้งเตือนอัตโนมัติตาม SLA และ Escalate 2 ระดับเมื่อ Overdue
|
||||
5. **Distribution Matrix** - กระจายเอกสารอัตโนมัติหลังอนุมัติผ่าน BullMQ
|
||||
|
||||
Technical approach: สร้าง Entities ใหม่ (ReviewTeam, ReviewTask, ResponseCodeMatrix, Delegation, DistributionMatrix) และ integrate กับ Unified Workflow Engine (ADR-001) และ BullMQ (ADR-008)
|
||||
|
||||
---
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript 5.x, NestJS 10.x (Backend), Next.js 14.x (Frontend)
|
||||
**Primary Dependencies**: TypeORM (Database), BullMQ (Queue/Reminders), CASL (Authorization), Zod (Validation), Redis (Cache/Locking)
|
||||
**Storage**: MariaDB 10.11 (Primary), Redis 7.x (Cache/Queue)
|
||||
**Testing**: Jest (Backend), Playwright (Frontend), Min 70% backend coverage, 80% business logic
|
||||
**Target Platform**: Web Application (NestJS API + Next.js Frontend)
|
||||
**Project Type**: Full-stack (backend + frontend)
|
||||
**Performance Goals**: Approval API response <500ms, Parallel Review aggregation <200ms, Distribution queue processing <5 min
|
||||
**Constraints**: ADR-019 UUID (no parseInt), ADR-009 No TypeORM Migrations, ADR-002 Document Numbering with Redlock, ADR-018 AI Boundary
|
||||
**Scale/Scope**: 100+ concurrent RFAs, 50+ Review Teams per project, 1000+ Distribution recipients
|
||||
|
||||
---
|
||||
|
||||
## Constitution Check
|
||||
|
||||
| Gate | Status | Notes |
|
||||
|------|--------|-------|
|
||||
| **ADR-019 UUID** | ✅ PASS | All new entities use publicId (string UUID), internal id (number) with @Exclude() |
|
||||
| **ADR-009 No Migrations** | ✅ PASS | Schema changes via SQL files in `specs/03-Data-and-Storage/` |
|
||||
| **ADR-002 Document Numbering** | ✅ PASS | Existing RFA numbering reused, no new numbering needed |
|
||||
| **ADR-008 BullMQ** | ✅ PASS | Reminders, Distribution, Escalation all use BullMQ |
|
||||
| **ADR-016 CASL** | ✅ PASS | Reviewer permissions via CASL ability checks |
|
||||
| **ADR-018 AI Boundary** | ✅ PASS | No AI involvement in approval workflow |
|
||||
| **ADR-007 Error Handling** | ✅ PASS | BusinessException/WorkflowException for approval errors |
|
||||
| **No `any` types** | ✅ PASS | Strict TypeScript enforced |
|
||||
| **No `console.log`** | ✅ PASS | NestJS Logger for backend, removed for frontend commits |
|
||||
|
||||
**Post-Design Re-check**: Required after data-model.md and contracts generated
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/1-rfa-approval-refactor/
|
||||
├── plan.md # This file
|
||||
├── spec.md # Feature specification
|
||||
├── research.md # Phase 0 research
|
||||
├── data-model.md # Phase 1 data model
|
||||
├── quickstart.md # Phase 1 setup guide
|
||||
├── contracts/ # Phase 1 API contracts
|
||||
│ ├── review-team-api.yaml
|
||||
│ ├── response-code-api.yaml
|
||||
│ ├── delegation-api.yaml
|
||||
│ └── distribution-api.yaml
|
||||
└── tasks.md # Phase 2 (generated by /speckit-tasks)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
backend/
|
||||
├── src/
|
||||
│ ├── modules/
|
||||
│ │ ├── review-team/ # NEW: Review Teams & Tasks
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ ├── dto/
|
||||
│ │ │ ├── review-team.service.ts
|
||||
│ │ │ ├── review-team.controller.ts
|
||||
│ │ │ └── review-task.service.ts
|
||||
│ │ ├── response-code/ # NEW: Master Approval Matrix
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ ├── dto/
|
||||
│ │ │ └── response-code.service.ts
|
||||
│ │ ├── delegation/ # NEW: Delegation & Proxy
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ ├── dto/
|
||||
│ │ │ └── delegation.service.ts
|
||||
│ │ ├── reminder/ # NEW: Auto-Reminders
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ ├── processors/
|
||||
│ │ │ └── reminder.service.ts
|
||||
│ │ ├── distribution/ # NEW: Distribution Matrix
|
||||
│ │ │ ├── entities/
|
||||
│ │ │ ├── processors/
|
||||
│ │ │ └── distribution.service.ts
|
||||
│ │ └── workflow-engine/ # EXISTING: Modified for Parallel Review
|
||||
│ └── rfa/ # EXISTING: Modified for Response Codes
|
||||
└── tests/
|
||||
├── unit/review-team/
|
||||
├── unit/delegation/
|
||||
├── integration/distribution/
|
||||
└── e2e/rfa-workflow/
|
||||
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── app/
|
||||
│ │ ├── (dashboard)/
|
||||
│ │ │ ├── review-teams/ # NEW: Review Team management UI
|
||||
│ │ │ ├── response-codes/ # NEW: Master Matrix admin UI
|
||||
│ │ │ ├── delegation/ # NEW: Delegation settings UI
|
||||
│ │ │ └── rfa/
|
||||
│ │ │ └── [id]/
|
||||
│ │ │ └── review/ # MODIFIED: Parallel Review UI with Response Codes
|
||||
│ │ └── api/
|
||||
│ │ └── review-team/ # NEW: API routes
|
||||
│ ├── components/
|
||||
│ │ ├── review-task/ # NEW: Review Task cards, Aggregate status
|
||||
│ │ ├── response-code/ # NEW: Response Code selector
|
||||
│ │ └── delegation/ # NEW: Delegation form
|
||||
│ └── hooks/
|
||||
│ ├── use-review-teams.ts # NEW
|
||||
│ ├── use-response-codes.ts # NEW
|
||||
│ └── use-delegation.ts # NEW
|
||||
```
|
||||
|
||||
**Structure Decision**: Full-stack implementation with 5 new backend modules (review-team, response-code, delegation, reminder, distribution) + modifications to existing rfa and workflow-engine modules. Frontend adds management UIs and enhances existing RFA review page.
|
||||
|
||||
---
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
||||
|-----------|------------|--------------------------------------|
|
||||
| 5 new backend modules | Each domain (Review Teams, Response Codes, Delegation, Reminders, Distribution) has distinct business logic, lifecycle, and scaling needs | Single combined module would create tight coupling and maintenance burden |
|
||||
| Parallel Review in Workflow Engine | Requires significant DSL extension to support concurrent tasks with consensus rules | Sequential review would not meet industry standard (TeamBinder/InEight) efficiency requirements |
|
||||
| Master Approval Matrix with inheritance | Global + Project override needed for standardization while allowing project flexibility | Single global matrix wouldn't accommodate project-specific requirements (e.g., ESG varies by project type) |
|
||||
|
||||
---
|
||||
|
||||
## Phase Overview
|
||||
|
||||
| Phase | Output | Purpose |
|
||||
|-------|--------|---------|
|
||||
| **Phase 0** | research.md | Research technical patterns, validate Workflow Engine DSL extension |
|
||||
| **Phase 1** | data-model.md, contracts/, quickstart.md | Design entities, API contracts, setup guide |
|
||||
| **Phase 2** | tasks.md | Break into actionable tasks (via /speckit-tasks) |
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Phase 0**: Generate research.md - Validate Workflow Engine DSL can support Parallel Review
|
||||
2. **Phase 1**: Generate data-model.md and API contracts
|
||||
3. **Run**: `/speckit-tasks` to create tasks.md
|
||||
4. **Run**: `/speckit-analyze` to validate cross-artifact consistency
|
||||
@@ -0,0 +1,102 @@
|
||||
# Implementation Plan: RFA Approval System Refactor
|
||||
|
||||
**Feature Branch**: `204-rfa-approval-refactor`
|
||||
**Parent Feature**: RFA Approval Refactor
|
||||
**Version**: 1.0.0
|
||||
**Status**: Planning
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ Architectural Overview
|
||||
|
||||
เราจะปรับปรุงระบบ RFA Approval โดยใช้ **Domain-Driven Design (DDD)** และ **Unified Workflow Engine (ADR-001)** โดยเน้นไปที่ความยืดหยุ่นของ Review Teams, มาตรฐาน Response Codes, และระบบ Delegation/Escalation ที่อัตโนมัติ
|
||||
|
||||
### Core Components
|
||||
|
||||
1. **Review Team Module**: จัดการสมาชิกตาม Discipline และ Default Rules
|
||||
2. **Approval Matrix Service**: ประมวลผลกฎ Response Codes และ Implications (Cost/Schedule)
|
||||
3. **Task Orchestrator**: ควบคุม Parallel Review และ Lead Consolidation logic
|
||||
4. **Delegation Manager**: ตรวจสอบสิทธิ์และสลับผู้รับผิดชอบงานตามระยะเวลา
|
||||
5. **Reminder & Escalation Engine**: ใช้ BullMQ ในการส่งการแจ้งเตือนแบบ Progressive (3-strike)
|
||||
6. **Distribution Engine**: ทำงานแบบ Async เพื่อกระจายเอกสารตาม Matrix หลังการอนุมัติ
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ Security & Integrity (Tier 1)
|
||||
|
||||
| Gate | Status | Notes |
|
||||
| :--- | :--- | :--- |
|
||||
| **ADR-019 UUID** | ✅ PASS | All new entities use publicId (string UUID), internal id (number) with @Exclude() |
|
||||
| **ADR-009 No Migrations** | ✅ PASS | Schema changes via SQL files in `specs/03-Data-and-Storage/` |
|
||||
| **ADR-002 Document Numbering** | ✅ PASS | Existing RFA numbering reused, no new numbering needed |
|
||||
| **ADR-008 BullMQ** | ✅ PASS | Reminders, Distribution, Escalation all use BullMQ |
|
||||
| **ADR-016 CASL** | ✅ PASS | Reviewer permissions via CASL ability checks |
|
||||
| **ADR-018 AI Boundary** | ✅ PASS | No AI involvement in approval workflow |
|
||||
| **ADR-007 Error Handling** | ✅ PASS | BusinessException/WorkflowException for approval errors |
|
||||
| **No `any` types** | ✅ PASS | Strict TypeScript enforced |
|
||||
| **No `console.log`** | ✅ PASS | NestJS Logger for backend, removed for frontend commits |
|
||||
|
||||
---
|
||||
|
||||
## 🗺️ Implementation Roadmap
|
||||
|
||||
### Phase 0: Research & Foundation (Validated)
|
||||
- [x] วิเคราะห์ความแตกต่างระหว่างระบบปัจจุบันกับ TeamBinder/InEight
|
||||
- [x] ตรวจสอบความพร้อมของ Workflow Engine สำหรับ Parallel Tasks
|
||||
- [x] กำหนด Schema พื้นฐานสำหรับ Entities ใหม่
|
||||
|
||||
### Phase 1: Data Model & Contracts (Next Step)
|
||||
- [ ] สร้าง SQL schema สำหรับ `ReviewTeam`, `ReviewTask`, `Delegation`, `ApprovalMatrix`
|
||||
- [ ] อัปเดต Data Dictionary ใน `specs/03-Data-and-Storage/`
|
||||
- [ ] นิยาม API DTOs และ Response Interfaces ใน NestJS
|
||||
|
||||
### Phase 2: Core Logic & Services
|
||||
- [ ] พัฒนา `ReviewTeamService` และ `DelegationService`
|
||||
- [ ] พัฒนา `ApprovalMatrixService` (Logic: Lead Consolidation, Category Filtering)
|
||||
- [ ] พัฒนา `RemindersProcessor` (Logic: 3-Strike Escalation via BullMQ)
|
||||
|
||||
### Phase 3: Workflow Integration
|
||||
- [ ] อัปเดต RFA Workflow DSL ให้รองรับ Parallel Review Step
|
||||
- [ ] เชื่อมต่อ Event Hooks สำหรับ Auto-Distribution (Transmittal generation)
|
||||
- [ ] พัฒนา Proxy Logic ใน Task Manager สำหรับ Delegation
|
||||
|
||||
### Phase 4: Frontend UI/UX
|
||||
- [ ] พัฒนา Horizontal Stepper สำหรับ Parallel Review visualization
|
||||
- [ ] พัฒนา Delegation Settings UI และ Matrix Management Dashboard
|
||||
- [ ] ปรับปรุง RFA Detail page ให้รองรับ Side Panel layout (Discipline details)
|
||||
|
||||
### Phase 5: Testing & Validation
|
||||
- [ ] Unit Tests สำหรับ Lead Consolidation rules
|
||||
- [ ] E2E Tests สำหรับ Delegation expiry และ Escalation flow
|
||||
- [ ] โหลดเทสต์สำหรับ Distribution Matrix (Concurrent approvals)
|
||||
|
||||
---
|
||||
|
||||
## 📐 Technical Decisions
|
||||
|
||||
### Decision 1: Lead Consolidation Logic (Parallel Review)
|
||||
เราจะใช้ **Lead Consolidation** แทน Majority Vote เพื่อให้สอดคล้องกับมาตรฐานอุตสาหกรรม โดย Lead Discipline จะได้รับสิทธิ์ในการ "สรุปผล" หลังจากทุก Discipline ทำงานเสร็จ หรือสามารถ override ได้ตามสิทธิ์ PM
|
||||
|
||||
### Decision 2: Single-Level Delegation
|
||||
เพื่อป้องกันความซับซ้อนของ Circular Dependency และ Audit Trail เราจะจำกัดการมอบหมายงานให้เพียง **1 ระดับ** เท่านั้น (A -> B) โดย B ไม่สามารถส่งต่อให้ C ได้ในฐานะผู้ใช้ปกติ
|
||||
|
||||
### Decision 3: 3-Strike Progressive Escalation
|
||||
การยกระดับจะใช้ระบบ 3 ครั้ง (Reminders) ต่อระดับความสำคัญ ก่อนจะย้ายงานไปให้ผู้บังคับบัญชา (L1 -> L2) และเมื่อถึงระดับสูงสุดจะส่ง Daily Reminder จนกว่าจะจบงาน
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Potential Blockers
|
||||
|
||||
- **Workflow DSL Flexibility**: ความสามารถของ Engine ปัจจุบันในการจัดการ Parallel Tasks ที่รอการ Consolidation
|
||||
- **Migration of Existing RFAs**: แผนการจัดการ RFA ที่กำลังอยู่ในกระบวนการ (In-flight) ระหว่างการเปลี่ยนผ่านระบบ
|
||||
- **CASL Matrix Overlap**: ความซับซ้อนของสิทธิ์เมื่อมีทั้ง Delegation และ Review Team roles ซ้อนทับกัน
|
||||
|
||||
---
|
||||
|
||||
## 🔗 References
|
||||
|
||||
- **Spec File**: `specs/200-fullstacks/204-rfa-approval-refactor/spec.md`
|
||||
- **Research File**: `specs/200-fullstacks/204-rfa-approval-refactor/research.md`
|
||||
- **ADR-001**: Unified Workflow Engine
|
||||
- **ADR-019**: Hybrid Identifier Strategy
|
||||
- **ADR-008**: BullMQ Notification Strategy
|
||||
+31
-43
@@ -11,30 +11,31 @@
|
||||
|
||||
**Research Task**: Can Unified Workflow Engine (ADR-001) support parallel tasks with consensus rules?
|
||||
|
||||
**Decision**: ✅ **Yes, with DSL Extension**
|
||||
**Decision**: ✅ **Yes, with DSL Extension (Lead Consolidation)**
|
||||
|
||||
**Rationale**:
|
||||
- Current DSL supports sequential states and transitions
|
||||
- Parallel review requires: (a) Task splitting on state entry, (b) Task aggregation before transition, (c) Consensus rule evaluation
|
||||
- Pattern: `ParallelGateState` - enters sub-workflows for each Discipline, aggregates on completion
|
||||
- Parallel review requires: (a) Task splitting on state entry, (b) Task completion by all Disciplines, (c) **Lead Discipline Consolidation** - Lead reviews all comments and makes the final decision
|
||||
- Pattern: `ParallelReviewState` - enters sub-workflows for each Discipline, aggregates on completion of all, then enables Lead review step
|
||||
|
||||
**Implementation Pattern**:
|
||||
```typescript
|
||||
// DSL Extension: Parallel Gateway
|
||||
// DSL Extension: Parallel Review with Consolidation
|
||||
{
|
||||
type: 'parallel_gateway',
|
||||
type: 'parallel_review',
|
||||
config: {
|
||||
discriminator: 'discipline', // Split by discipline
|
||||
minCompletion: 'majority', // Consensus rule
|
||||
vetoConditions: [{ field: 'responseCode', value: '3' }] // Veto triggers
|
||||
splitBy: 'discipline',
|
||||
requiredDisciplines: 'all', // Must wait for all to finish
|
||||
consolidator: 'leadDiscipline', // Final decision maker
|
||||
visibility: 'full' // Transparency enabled
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Alternatives Considered**:
|
||||
- Option A: Multiple Workflow Instances per RFA (rejected - too complex, hard to aggregate)
|
||||
- Option A: Majority with Veto (rejected - too rigid for complex engineering projects)
|
||||
- Option B: Sequential with fast-forward (rejected - doesn't truly parallelize)
|
||||
- Option C: **Parallel Gateway in DSL** (selected - clean abstraction, reusable pattern)
|
||||
- Option C: **Lead Consolidation in DSL** (selected - provides expert review and flexibility)
|
||||
|
||||
**References**:
|
||||
- BPMN 2.0 Parallel Gateway pattern
|
||||
@@ -81,34 +82,26 @@ response_code_rules (
|
||||
|
||||
**Research Task**: Best approach for delegation with chain depth limit and circular detection?
|
||||
|
||||
**Decision**: **Adjacency List with Path Enumeration, Max Depth = 3**
|
||||
**Decision**: **Simple Adjacency List, Max Depth = 1 (Single Level Only)**
|
||||
|
||||
**Rationale**:
|
||||
- Adjacency List: Simple, fast for immediate lookup (`delegator_id → delegatee_id`)
|
||||
- Path Enumeration: Store full chain as array for circular detection
|
||||
- Max Depth 3: Prevents runaway chains while supporting realistic use cases
|
||||
- **Single Level Only**: Prevents accountability loss and complex chain management
|
||||
- Circular Detection: Trivial check (`A -> B -> A`)
|
||||
|
||||
**Circular Detection Algorithm**:
|
||||
**Circular Detection Logic**:
|
||||
```typescript
|
||||
function detectCircularDelegation(delegatorId: string, proposedDelegateeId: string): boolean {
|
||||
// BFS/DFS from proposedDelegatee, check if can reach delegatorId
|
||||
const visited = new Set<string>();
|
||||
const queue = [proposedDelegateeId];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const current = queue.shift()!;
|
||||
if (current === delegatorId) return true; // Circular!
|
||||
if (visited.has(current)) continue;
|
||||
visited.add(current);
|
||||
|
||||
// Add all delegatees of current
|
||||
const delegatees = getActiveDelegations(current);
|
||||
queue.push(...delegatees.map(d => d.delegateeId));
|
||||
}
|
||||
return false;
|
||||
// Check if proposedDelegatee has already delegated to delegatorId
|
||||
const existing = getActiveDelegation(proposedDelegateeId);
|
||||
return existing?.delegateeId === delegatorId;
|
||||
}
|
||||
```
|
||||
|
||||
**Alternatives Considered**:
|
||||
- Max Depth 3 (rejected - too complex for standard accountability)
|
||||
- Closure Table (rejected - overkill for simple chains)
|
||||
|
||||
**Alternatives Considered**:
|
||||
- Nested Set Model (rejected - overkill for simple chains)
|
||||
- Closure Table (rejected - requires maintenance on delegation expiry)
|
||||
@@ -179,36 +172,31 @@ SELECT
|
||||
rfa_revision_id,
|
||||
COUNT(*) as total_disciplines,
|
||||
SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) as completed,
|
||||
SUM(CASE WHEN response_code = '3' THEN 1 ELSE 0 END) as rejected_count
|
||||
GROUP_CONCAT(discipline_id ORDER BY discipline_id) as discipline_list
|
||||
FROM review_tasks
|
||||
GROUP BY rfa_revision_id;
|
||||
|
||||
-- Real-time consensus check
|
||||
SELECT CASE
|
||||
WHEN rejected_count > 0 THEN 'REJECTED' -- Veto triggered
|
||||
WHEN completed = total_disciplines THEN
|
||||
CASE
|
||||
WHEN approved_count / total_disciplines >= 0.5 THEN 'APPROVED'
|
||||
ELSE 'NEEDS_REVIEW'
|
||||
END
|
||||
ELSE 'IN_PROGRESS'
|
||||
END as consensus_status
|
||||
FROM review_task_summary;
|
||||
-- Lead Consolidation Check
|
||||
-- RFA moves to 'Lead Consolidation' step only when completed = total_disciplines
|
||||
```
|
||||
|
||||
**Alternatives Considered**:
|
||||
- Calculate on-demand (rejected - slow with many disciplines)
|
||||
- Application-level cache (rejected - stale data risk)
|
||||
|
||||
**Alternatives Considered**:
|
||||
- Calculate on-demand (rejected - slow with many disciplines)
|
||||
- Application-level cache (rejected - stale data risk)
|
||||
|
||||
---
|
||||
|
||||
## Summary of Decisions
|
||||
|
||||
| Topic | Decision | Key Rationale |
|
||||
|-------|----------|---------------|
|
||||
| Parallel Review | DSL Parallel Gateway | Clean abstraction, reusable |
|
||||
| Parallel Review | DSL Lead Consolidation | Expert-driven summaries, flexibility |
|
||||
| Response Code Storage | Normalized + JSON | Balance of structure and flexibility |
|
||||
| Delegation | Adjacency List + Path Enum | Simple, sufficient for depth ≤3 |
|
||||
| Delegation | Adjacency List (Level 1) | Accountability, simple circular detection |
|
||||
| Queue Pattern | BullMQ Delayed + Flows | Industry standard, reliable |
|
||||
| Status Aggregation | Materialized View + Counter | Fast reads, real-time updates |
|
||||
|
||||
+60
-27
@@ -1,6 +1,6 @@
|
||||
# Feature Specification: RFA Approval System Refactor (TeamBinder/InEight-Style)
|
||||
|
||||
**Feature Branch**: `1-rfa-approval-refactor`
|
||||
**Feature Branch**: `204-rfa-approval-refactor`
|
||||
**Created**: 2026-05-11
|
||||
**Status**: Draft
|
||||
**Input**: User description: "refactor ระบบการอนุมัติเอกสาร, RFA ให้เหมือนกับ TeamBinder หรือ InEight"
|
||||
@@ -110,7 +110,7 @@
|
||||
|
||||
---
|
||||
|
||||
### Edge Cases
|
||||
## Edge Cases
|
||||
|
||||
1. **Race Condition**: สอง Reviewer ในทีมเดียวกันกดอนุมัติพร้อมกัน - ระบบต้องจัดการด้วย Optimistic Locking หรือ Redlock
|
||||
2. **Circular Delegation**: ผู้ใช้ A Delegate ให้ B, B Delegate ให้ C, C พยายาม Delegate ให้ A - ระบบต้องตรวจจับและป้องกัน
|
||||
@@ -124,92 +124,114 @@
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
**Review Teams & Disciplines**
|
||||
#### Review Teams & Disciplines
|
||||
|
||||
- **FR-001**: ระบบ MUST รองรับการสร้าง Review Teams ที่มีหลาย Disciplines
|
||||
- **FR-002**: Review Teams MUST สามารถกำหนดเป็น Default ตามประเภท RFA ได้
|
||||
- **FR-002.1**: เมื่อมีการส่ง Revision ใหม่ของ RFA เดิม, ระบบ MUST ดึงรายชื่อ Review Team และ Disciplines จาก Revision ล่าสุดมาเป็น Default เพื่อความต่อเนื่อง (Inherit Previous Team) แต่ผู้ส่งยังคงสามารถปรับเปลี่ยนได้หากจำเป็น
|
||||
- **FR-003**: เมื่อ RFA เข้า workflow ที่ใช้ Review Team, ระบบ MUST สร้าง Review Tasks สำหรับแต่ละ Discipline พร้อมกัน (Parallel Review)
|
||||
- **FR-004**: Review Tasks MUST แสดงสถานะรวม (Aggregate Status) เช่น "2 of 3 Disciplines Approved"
|
||||
- **FR-004.5**: Parallel Review MUST ใช้กฎ Majority with Veto - หากส่วนใหญ่อนุมัติให้ผ่าน แต่หากมี Discipline ใดให้ Code 3 (Rejected) ต้องหยุด workflow และส่งกลับให้แก้ไข
|
||||
- **FR-004.5**: Parallel Review MUST ใช้กฎ **Lead Consolidation** - ระบบอนุญาตให้ทุก Disciplines ตรวจสอบจนครบ โดยที่ Lead Discipline จะเป็นผู้พิจารณาความเห็นจากทุกฝ่ายและเป็นผู้สรุปสถานะสุดท้าย (Final Response Code) ของ RFA นั้นๆ
|
||||
- **FR-004.6**: ในระหว่าง Parallel Review, Reviewers ทุกคน MUST สามารถเห็นคอมเมนต์และสถานะเบื้องต้นของกันและกันได้ (Full Transparency) เพื่อช่วยในการประสานงานข้ามสายงาน (Cross-discipline coordination)
|
||||
|
||||
#### Response Codes & Master Approval Matrix
|
||||
|
||||
**Response Codes & Master Approval Matrix**
|
||||
- **FR-005**: ระบบ MUST ใช้ Master Approval Matrix ตามมาตรฐานที่กำหนด
|
||||
- **FR-006**: Response Codes MUST แสดงตาม Category ของเอกสาร (Engineering, Material, Contract, Testing, ESG)
|
||||
- **FR-006**: Response Codes MUST แสดงตาม Category ของเอกสาร (Engineering, Material, Contract, Testing, ESG) และระบบ MUST จำกัดตัวเลือก (Filter) ให้ทั้ง Reviewer และ Lead เห็นเฉพาะโค้ดที่อยู่ในหมวดหมู่นั้นๆ เท่านั้น
|
||||
- **FR-006.1**: หาก Final Response Code มีผลกระทบต่อ Cost หรือ Schedule (ตาม Matrix), ระบบ MUST ทำการ Flag และแจ้งเตือนบน Management Dashboard ทันที เพื่อให้ฝ่ายบริหารดำเนินการต่อภายนอกระบบได้ทันท่วงที
|
||||
- **FR-007**: Code 1C (Change Order), 1D (Alternative), 3 (Rejected) MUST trigger การแจ้งเตือนไปยังฝ่ายที่เกี่ยวข้องโดยอัตโนมัติ
|
||||
- **FR-008**: ระบบ MUST บันทึกประวัติการเปลี่ยน Response Code (Audit Trail)
|
||||
- **FR-009**: Reviewer MUST สามารถเพิ่ม Comments พร้อม Response Code ได้
|
||||
|
||||
**Delegation & Proxy**
|
||||
#### Delegation & Proxy
|
||||
|
||||
- **FR-010**: ผู้ใช้ MUST สามารถตั้งค่า Delegation ได้ พร้อมกำหนดระยะเวลาเริ่มต้น-สิ้นสุด
|
||||
- **FR-011**: Delegation MUST รองรับการกำหนด Scope (เฉพาะบางประเภทเอกสาร หรือทั้งหมด)
|
||||
- **FR-012**: ระบบ MUST ตรวจจับ Circular Delegation และป้องกัน
|
||||
- **FR-012**: ระบบ MUST ตรวจจับและป้องกัน Circular Delegation และไม่อนุญาตให้มีการมอบหมายงานต่อ (Sub-delegation) ในระดับผู้ใช้งานปกติ (Single Level Only) ยกเว้น Admin ที่มีสิทธิ์ในการ Reassign หรืองานจัดการมอบหมายงานใหม่ได้เสมอ
|
||||
- **FR-013**: เมื่อ Delegation หมดอายุ, งานใหม่ MUST กลับไปยังผู้ใช้เดิมโดยอัตโนมัติ
|
||||
- **FR-014**: Delegatee MUST เห็นงานที่ได้รับมอบหมายแยกจากงานของตนเอง (Badge "Delegated")
|
||||
|
||||
**Auto-Reminders & Escalation**
|
||||
#### Auto-Reminders & Escalation
|
||||
|
||||
- **FR-015**: ระบบ MUST ส่ง Reminder ตาม Schedule ที่กำหนด (1 วันก่อน Due, วัน Due, ทุกวันหลัง Due)
|
||||
- **FR-016**: Escalation MUST ส่งแจ้งเตือนไปยัง Manager เมื่องาน Overdue ตามเกณฑ์ที่ตั้งไว้
|
||||
- **FR-016**: Escalation MUST ดำเนินการแบบลำดับชั้น (L1 -> L2) โดยที่แต่ละระดับจะมีการแจ้งเตือน 3 ครั้งก่อนจะยกระดับขึ้นไปตามเกณฑ์ที่ตั้งไว้ และเมื่อถึงระดับสูงสุด (L2) ระบบ MUST ส่งแจ้งเตือนรายวัน (Daily) จนกว่าจะมีการดำเนินการ
|
||||
- **FR-016.1**: ระบบ MUST บันทึกประวัติการ Escalation และการตอบสนองของผู้บริหารใน Audit Trail
|
||||
- **FR-017**: Admin MUST สามารถตั้งค่า Reminder Rules ต่อโครงการ/ประเภทเอกสารได้
|
||||
- **FR-018**: ระบบ MUST บันทึกประวัติการส่ง Reminder (ส่งเมื่อไหร่, ใครได้รับ)
|
||||
|
||||
**Frontend Workflow Visualization**
|
||||
#### Frontend Workflow Visualization
|
||||
|
||||
- **FR-019.1**: Parallel Review MUST แสดงผลด้วย **Horizontal Stepper** - แถบความคืบหนันแนวนอนแสดงสถานะแต่ละ Discipline [Structural ▓▓▓░] [Civil ▓▓▓▓] [MEP ▓░░░]
|
||||
- **FR-019.2**: เมื่อมี Code 3 (Rejected) จาก Discipline ใด MUST แสดง **Modal Dialog** แจ้งการ Veto พร้อมรายละเอียด Discipline ที่ reject
|
||||
- **FR-019.3**: Navigation ระหว่าง Discipline details MUST ใช้ **Side Panel** layout - ซ้ายรายการ Disciplines, ขวาแสดงรายละเอียด Comments + Attachments ของ Discipline ที่เลือก
|
||||
- **FR-019.4**: Project Manager MUST สามารถ **Override Veto** ได้ - บังคับผ่าน RFA แม้มี Code 3 จาก Discipline พร้อมบันทึกเหตุผล, Audit trail ว่าเป็น forced approval, และแจ้งเตือนทุก stakeholder ที่เกี่ยวข้อง
|
||||
|
||||
**Distribution Matrix**
|
||||
- **FR-019**: Distribution Matrix MUST กำหนดผู้รับตาม: ประเภทเอกสาร + Response Code + สถานะเอกสาร
|
||||
#### Distribution Matrix
|
||||
|
||||
- **FR-019**: Distribution Matrix MUST กำหนดผู้รับตาม: ประเภทเอกสาร + Response Code + สถานะเอกสาร และระบบ MUST อนุญาตให้ผู้สรุปผล (Lead/PM) เพิ่มผู้รับเพิ่มเติมแบบ Manual ได้ก่อนการส่งออกเฉพาะครั้งนั้นๆ
|
||||
- **FR-020**: ระบบ MUST สร้าง Transmittal Records อัตโนมัติหลังการอนุมัติตาม Distribution Matrix ผ่าน BullMQ Queue (Async, ภายใน 5 นาที)
|
||||
- **FR-021**: Distribution Matrix MUST รองรับเงื่อนไข "Send Only If" (เช่น Code 1A, 1B เท่านั้น)
|
||||
- **FR-022**: ระบบ MUST แสดงรายงาน Distribution Status (ส่งแล้วกี่ราย, ค้างกี่ราย)
|
||||
|
||||
**Integration with Existing Systems**
|
||||
#### Integration with Existing Systems
|
||||
|
||||
- **FR-023**: ต้องใช้ Unified Workflow Engine (ADR-001) ที่มีอยู่แล้ว
|
||||
- **FR-024**: ต้องใช้ BullMQ (ADR-008) สำหรับ Reminders และ Distribution Jobs
|
||||
- **FR-025**: ต้องใช้ CASL (ADR-016) สำหรับสิทธิ์ Reviewer และ Delegation
|
||||
|
||||
---
|
||||
|
||||
### Key Entities
|
||||
|
||||
**ReviewTeam**
|
||||
#### ReviewTeam
|
||||
|
||||
- ตัวแทนกลุ่มผู้ตรวจสอบที่จัดการตามสาขาวิชา
|
||||
- Attributes: name, description, projectId, defaultForRfaTypes, isActive
|
||||
- Relationships: has many ReviewTeamMember, has many ReviewTask
|
||||
|
||||
**ReviewTeamMember**
|
||||
#### ReviewTeamMember
|
||||
|
||||
- สมาชิกใน Review Team พร้อม Discipline ที่รับผิดชอบ
|
||||
- Attributes: teamId, userId, disciplineId, role, priorityOrder
|
||||
- Relationships: belongs to ReviewTeam, belongs to User, belongs to Discipline
|
||||
|
||||
**ReviewTask**
|
||||
#### ReviewTask
|
||||
|
||||
- งานตรวจสอบที่สร้างเมื่อ RFA เข้า workflow
|
||||
- Attributes: rfaRevisionId, teamId, disciplineId, assignedToUserId, status, dueDate, responseCode, comments
|
||||
- Relationships: belongs to RfaRevision, belongs to ReviewTeam
|
||||
|
||||
**ResponseCodeMatrix**
|
||||
#### ResponseCodeMatrix
|
||||
|
||||
- Master Approval Matrix ที่กำหนด Response Codes ตาม Category
|
||||
- Attributes: code, subStatus, category, descriptionTh, descriptionEn, implications, requiresNotificationTo
|
||||
- Relationships: has many ResponseCodeRule
|
||||
|
||||
**ResponseCodeRule**
|
||||
#### ResponseCodeRule
|
||||
|
||||
- กฎการใช้ Response Code ตามประเภทเอกสาร
|
||||
- Attributes: matrixId, documentTypeId, isEnabled, requiresComments, triggersNotification
|
||||
|
||||
**Delegation**
|
||||
#### Delegation
|
||||
|
||||
- การมอบหมายอำนาจจากผู้ใช้หนึ่งไปอีกผู้ใช้
|
||||
- Attributes: delegatorId, delegateeId, startDate, endDate, scope, documentTypes, isActive
|
||||
- Relationships: belongs to User (delegator), belongs to User (delegatee)
|
||||
|
||||
**ReminderRule**
|
||||
#### ReminderRule
|
||||
|
||||
- กฎการส่ง Reminder ตาม SLA
|
||||
- Attributes: name, projectId, documentTypeId, triggerDays, reminderType, recipients, messageTemplate
|
||||
- Relationships: has many ReminderSchedule
|
||||
|
||||
**DistributionMatrix**
|
||||
#### DistributionMatrix
|
||||
|
||||
- กำหนดการกระจายเอกสารหลังอนุมัติ
|
||||
- Attributes: name, documentTypeId, responseCode, status, recipients, conditions, isActive
|
||||
- Relationships: has many DistributionRecipient
|
||||
|
||||
**DistributionRecipient**
|
||||
#### DistributionRecipient
|
||||
|
||||
- ผู้รับเอกสารใน Distribution Matrix
|
||||
- Attributes: matrixId, recipientType (USER/ORGANIZATION/TEAM), recipientId, deliveryMethod
|
||||
|
||||
@@ -231,16 +253,27 @@
|
||||
|
||||
## Clarifications
|
||||
|
||||
### Session 2026-05-13
|
||||
|
||||
- **Q1**: Parallel Review consensus model for conflicting decisions? → **A**: Lead Consolidation - Allow all disciplines to complete their review, then the Lead Discipline consolidates all comments and makes the final decision.
|
||||
- **Q2**: Delegation Depth and sub-delegation rules? → **A**: Single Level Only (No sub-delegation) for regular users, but Admin retains full override power to reassign or manage delegations as needed.
|
||||
- **Q3**: Manual overrides in Distribution Matrix? → **A**: Flexible Recipients - Allow Lead/PM to manually add additional recipients during the final approval step for a specific RFA.
|
||||
- **Q4**: Escalation Cycle and notification frequency? → **A**: 3-Strike Escalation - Notify current escalation level 3 times before moving up to the next level (Progressive). Once the highest level (L2) is reached, send daily reminders until resolved.
|
||||
- **Q5**: Transparency between Disciplines during Parallel Review? → **A**: Full Transparency - All reviewers can see each other's comments and partial results in real-time.
|
||||
- **Q6**: Handling Cost/Schedule Implications from Response Codes? → **A**: Dashboard Flagging - Record the impact and notify management via dashboard/reporting without blocking the RFA flow.
|
||||
- **Q7**: Review Team persistence on resubmissions? → **A**: Inherit Previous Team - Automatically default to the same team and disciplines from the previous version for continuity.
|
||||
- **Q8**: Response Code visibility constraints for Reviewers? → **A**: Category Scoped - Filter the available codes based on the RFA category for both Reviewers and Leads to ensure consistency.
|
||||
|
||||
### Session 2026-05-11
|
||||
|
||||
- **Q1**: Master Approval Matrix scope and inheritance model? → **A**: Global base + Project overrides - Default Matrix inherited organization-wide, projects can override specific codes/rules as needed (Option B)
|
||||
- **Q2**: Parallel Review consensus model when Disciplines have conflicting decisions? → **A**: Majority with veto - All Disciplines submit responses, majority determines outcome, but Code 3 (Rejected) from any Discipline vetoes approval and requires revision (Option C)
|
||||
- **Q2**: Parallel Review consensus model when Disciplines have conflicting decisions? → **A**: Lead Consolidation (Updated 2026-05-13) - Formerly Majority with Veto.
|
||||
- **Q3**: Escalation chain depth for overdue RFA reviews? → **A**: 2 levels - Direct manager first, then Project Manager/Director if still unresolved after additional delay (Option B)
|
||||
- **Q4**: Distribution Matrix execution timing relative to approval? → **A**: Async after approval - Approval returns immediately, distribution queued via BullMQ and processed automatically within 5 minutes (Option C)
|
||||
- **Q5**: Frontend pattern for displaying Parallel Review progress? → **A**: Horizontal Stepper - แถบความคืบหนันแนวนอนแสดงสถานะแต่ละ Discipline (Option A)
|
||||
- **Q6**: How to display Veto/Consensus status when Code 3 triggered? → **A**: Modal Dialog - Popup แจ้งเมื่อมีการ Veto พร้อมรายละเอียด Discipline ที่ reject (Option B)
|
||||
- **Q6**: How to display Veto/Consensus status เมื่อมีการ Reject? → **A**: Modal Dialog - Popup แจ้งเมื่อมีการ Reject พร้อมรายละเอียด Discipline ที่ระบุ (Option B)
|
||||
- **Q7**: Navigation pattern between Discipline details for Reviewer? → **A**: Side Panel - ซ้ายรายการ Disciplines, ขวาแสดงรายละเอียดที่เลือก (Option D)
|
||||
- **Q8**: Can Veto be overridden and by whom? → **A**: Project Manager Override - PM สามารถบังคับผ่าน Veto ได้ พร้อมบันทึกเหตุผล และแจ้งเตือนทุก stakeholder (Option B)
|
||||
- **Q8**: Can a decision be overridden and by whom? → **A**: Project Manager Override - PM สามารถบังคับผ่านการตัดสินใจของ Lead ได้ พร้อมบันทึกเหตุผล และแจ้งเตือนทุก stakeholder (Option B)
|
||||
|
||||
---
|
||||
|
||||
@@ -267,7 +300,7 @@
|
||||
## Risks & Mitigations
|
||||
|
||||
| Risk | Impact | Mitigation |
|
||||
|------|--------|------------|
|
||||
| :--- | :--- | :--- |
|
||||
| ผู้ใช้ไม่คุ้นเคยกับ Response Codes ใหม่ | High | ทำ Training Workshop พร้อม Quick Reference Guide |
|
||||
| Workflow Engine ไม่รองรับ Parallel Review | High | ประเมิน DSL ก่อนเริ่ม, อาจต้อง Refactor Engine |
|
||||
| Performance ช้าจาก Complex Matrix Lookup | Medium | ทำ Caching สำหรับ Matrix และ Rules |
|
||||
+92
-82
@@ -15,11 +15,12 @@ Initialize project structure and shared infrastructure for all modules.
|
||||
|
||||
---
|
||||
|
||||
- [x] T001 [P] Create SQL schema file `specs/03-Data-and-Storage/lcbp3-v1.9.0-rfa-approval-schema.sql` with all 9 new entities
|
||||
- [x] T002 [P] Create Response Code seeder `backend/src/modules/response-code/seeders/response-code.seed.ts`
|
||||
- [x] T003 Create BullMQ queue configuration `backend/src/config/bullmq.config.ts`
|
||||
- [x] T004 [P] Setup Redis connection for BullMQ and Redlock `backend/src/config/redis.config.ts`
|
||||
- [x] T005 Create shared DTOs and enums `backend/src/modules/review-team/dto/shared/` (ReviewTaskStatus, ResponseCodeCategory, etc.)
|
||||
- [X] T001 [P] Create SQL schema file `specs/03-Data-and-Storage/lcbp3-v1.9.0-rfa-approval-schema.sql` with all 9 new entities
|
||||
- [X] T002 [P] Create Response Code seeder `backend/src/modules/response-code/seeders/response-code.seed.ts`
|
||||
- [X] T003 Create BullMQ queue configuration `backend/src/config/bullmq.config.ts`
|
||||
- [X] T004 [P] Setup Redis connection for BullMQ and Redlock `backend/src/config/redis.config.ts`
|
||||
- [X] T005 Create shared DTOs and enums `backend/src/modules/review-team/dto/shared/` (ReviewTaskStatus, ResponseCodeCategory, etc.)
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -32,14 +33,15 @@ Core entities required by multiple user stories. Must complete before US1-US6.
|
||||
|
||||
---
|
||||
|
||||
- [x] T006 [P] Create ReviewTeam entity `backend/src/modules/review-team/entities/review-team.entity.ts`
|
||||
- [x] T007 [P] Create ReviewTeamMember entity `backend/src/modules/review-team/entities/review-team-member.entity.ts`
|
||||
- [x] T008 Create ResponseCode entity `backend/src/modules/response-code/entities/response-code.entity.ts`
|
||||
- [x] T009 [P] Create ResponseCodeRule entity `backend/src/modules/response-code/entities/response-code-rule.entity.ts`
|
||||
- [x] T010 [P] Create ReviewTask entity `backend/src/modules/review-team/entities/review-task.entity.ts`
|
||||
- [x] T011 Create ResponseCodeModule with service `backend/src/modules/response-code/response-code.service.ts`
|
||||
- [x] T012 Create ResponseCodeController with basic CRUD `backend/src/modules/response-code/response-code.controller.ts`
|
||||
- [x] T013 Create ReviewTeamModule base structure `backend/src/modules/review-team/review-team.module.ts`
|
||||
- [X] T006 [P] Create ReviewTeam entity `backend/src/modules/review-team/entities/review-team.entity.ts`
|
||||
- [X] T007 [P] Create ReviewTeamMember entity `backend/src/modules/review-team/entities/review-team-member.entity.ts`
|
||||
- [X] T008 Create ResponseCode entity `backend/src/modules/response-code/entities/response-code.entity.ts`
|
||||
- [X] T009 [P] Create ResponseCodeRule entity `backend/src/modules/response-code/entities/response-code-rule.entity.ts`
|
||||
- [X] T010 [P] Create ReviewTask entity `backend/src/modules/review-team/entities/review-task.entity.ts`
|
||||
- [X] T011 Create ResponseCodeModule with service `backend/src/modules/response-code/response-code.service.ts`
|
||||
- [X] T012 Create ResponseCodeController with basic CRUD `backend/src/modules/response-code/response-code.controller.ts`
|
||||
- [X] T013 Create ReviewTeamModule base structure `backend/src/modules/review-team/review-team.module.ts`
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -55,16 +57,17 @@ Users can create Review Teams with multiple Disciplines, and teams auto-assign t
|
||||
|
||||
---
|
||||
|
||||
- [x] T014 [US1] Create ReviewTeamService with CRUD and member management `backend/src/modules/review-team/review-team.service.ts`
|
||||
- [x] T015 [P] [US1] Create ReviewTeamController endpoints `backend/src/modules/review-team/review-team.controller.ts`
|
||||
- [x] T016 [US1] Create ReviewTaskService with assignment logic `backend/src/modules/review-team/review-task.service.ts`
|
||||
- [x] T017 [P] [US1] Integrate Review Team selection in RFA submission flow `backend/src/modules/rfa/rfa.service.ts`
|
||||
- [x] T018 [US1] Implement parallel task creation on RFA submit `backend/src/modules/review-team/services/task-creation.service.ts`
|
||||
- [x] T019 [P] [US1] Create Review Team management UI page `frontend/src/app/(dashboard)/review-teams/page.tsx`
|
||||
- [x] T020 [P] [US1] Create Review Team form component `frontend/src/components/review-team/ReviewTeamForm.tsx`
|
||||
- [x] T021 [US1] Create Team Member assignment component `frontend/src/components/review-team/TeamMemberManager.tsx`
|
||||
- [x] T022 [P] [US1] Create useReviewTeams hook `frontend/src/hooks/use-review-teams.ts`
|
||||
- [x] T023 [US1] Add Review Team selector to RFA submission form `frontend/src/app/(dashboard)/rfa/[id]/submit/page.tsx`
|
||||
- [X] T014 [US1] Create ReviewTeamService with CRUD and member management `backend/src/modules/review-team/review-team.service.ts`
|
||||
- [X] T015 [P] [US1] Create ReviewTeamController endpoints `backend/src/modules/review-team/review-team.controller.ts`
|
||||
- [X] T016 [US1] Create ReviewTaskService with assignment logic `backend/src/modules/review-team/review-task.service.ts`
|
||||
- [X] T017 [P] [US1] Integrate Review Team selection in RFA submission flow `backend/src/modules/rfa/rfa.service.ts`
|
||||
- [X] T018 [US1] Implement parallel task creation on RFA submit `backend/src/modules/review-team/services/task-creation.service.ts`
|
||||
- [X] T019 [P] [US1] Create Review Team management UI page `frontend/src/app/(dashboard)/review-teams/page.tsx`
|
||||
- [X] T020 [P] [US1] Create Review Team form component `frontend/src/components/review-team/ReviewTeamForm.tsx`
|
||||
- [X] T021 [US1] Create Team Member assignment component `frontend/src/components/review-team/TeamMemberManager.tsx`
|
||||
- [X] T022 [P] [US1] Create useReviewTeams hook `frontend/src/hooks/use-review-teams.ts`
|
||||
- [X] T023 [US1] Add Review Team selector to RFA submission form `frontend/src/app/(dashboard)/rfa/[id]/submit/page.tsx`
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -80,16 +83,17 @@ Response Codes display by document category, Code 1C/1D/3 trigger notifications,
|
||||
|
||||
---
|
||||
|
||||
- [x] T024 [US2] Extend ResponseCodeService with category filtering `backend/src/modules/response-code/response-code.service.ts`
|
||||
- [x] T025 [P] [US2] Create ResponseCode lookup endpoint by document type `backend/src/modules/response-code/response-code.controller.ts`
|
||||
- [x] T026 [US2] Implement Response Code implications evaluator `backend/src/modules/response-code/services/implications.service.ts`
|
||||
- [x] T027 [P] [US2] Create notification trigger service for critical codes `backend/src/modules/response-code/services/notification-trigger.service.ts`
|
||||
- [x] T028 [US2] Add audit logging for Response Code changes `backend/src/modules/response-code/services/audit.service.ts`
|
||||
- [x] T029 [P] [US2] Create Response Code selector component with category filtering `frontend/src/components/response-code/ResponseCodeSelector.tsx`
|
||||
- [x] T030 [US2] Create Response Code implications display `frontend/src/components/response-code/CodeImplications.tsx`
|
||||
- [x] T031 [P] [US2] Create Master Approval Matrix admin UI `frontend/src/app/(dashboard)/response-codes/page.tsx`
|
||||
- [x] T032 [US2] Create useResponseCodes hook with category filter `frontend/src/hooks/use-response-codes.ts`
|
||||
- [x] T033 [P] [US2] Integrate Response Code selector in Review Task completion UI `frontend/src/components/review-task/CompleteReviewForm.tsx`
|
||||
- [X] T024 [US2] Extend ResponseCodeService with category filtering `backend/src/modules/response-code/response-code.service.ts`
|
||||
- [X] T025 [P] [US2] Create ResponseCode lookup endpoint by document type `backend/src/modules/response-code/response-code.controller.ts`
|
||||
- [X] T026 [US2] Implement Response Code implications evaluator `backend/src/modules/response-code/services/implications.service.ts`
|
||||
- [X] T027 [P] [US2] Create notification trigger service for critical codes `backend/src/modules/response-code/services/notification-trigger.service.ts`
|
||||
- [X] T028 [US2] Add audit logging for Response Code changes `backend/src/modules/response-code/services/audit.service.ts`
|
||||
- [X] T029 [P] [US2] Create Response Code selector component with category filtering `frontend/src/components/response-code/ResponseCodeSelector.tsx`
|
||||
- [X] T030 [US2] Create Response Code implications display `frontend/src/components/response-code/CodeImplications.tsx`
|
||||
- [X] T031 [P] [US2] Create Master Approval Matrix admin UI `frontend/src/app/(dashboard)/response-codes/page.tsx`
|
||||
- [X] T032 [US2] Create useResponseCodes hook with category filter `frontend/src/hooks/use-response-codes.ts`
|
||||
- [X] T033 [P] [US2] Integrate Response Code selector in Review Task completion UI `frontend/src/components/review-task/CompleteReviewForm.tsx`
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -105,15 +109,16 @@ Users can delegate review tasks with date range, circular detection prevents loo
|
||||
|
||||
---
|
||||
|
||||
- [x] T034 [US3] Create Delegation entity `backend/src/modules/delegation/entities/delegation.entity.ts`
|
||||
- [x] T035 [P] [US3] Create DelegationService with CRUD `backend/src/modules/delegation/delegation.service.ts`
|
||||
- [x] T036 [US3] Implement circular delegation detection algorithm `backend/src/modules/delegation/services/circular-detection.service.ts`
|
||||
- [x] T037 [P] [US3] Create DelegationController endpoints `backend/src/modules/delegation/delegation.controller.ts`
|
||||
- [x] T038 [US3] Integrate delegation resolution in ReviewTaskService `backend/src/modules/review-team/review-task.service.ts`
|
||||
- [x] T039 [P] [US3] Create Delegation settings UI page `frontend/src/app/(dashboard)/delegation/page.tsx`
|
||||
- [x] T040 [US3] Create Delegation form with date picker `frontend/src/components/delegation/DelegationForm.tsx`
|
||||
- [x] T041 [P] [US3] Create delegated task indicator ("Delegated from X") `frontend/src/components/review-task/DelegatedBadge.tsx`
|
||||
- [x] T042 [P] [US3] Create useDelegation hook `frontend/src/hooks/use-delegation.ts`
|
||||
- [X] T034 [US3] Create Delegation entity `backend/src/modules/delegation/entities/delegation.entity.ts`
|
||||
- [X] T035 [P] [US3] Create DelegationService with CRUD `backend/src/modules/delegation/delegation.service.ts`
|
||||
- [X] T036 [US3] Implement circular delegation detection algorithm `backend/src/modules/delegation/services/circular-detection.service.ts`
|
||||
- [X] T037 [P] [US3] Create DelegationController endpoints `backend/src/modules/delegation/delegation.controller.ts`
|
||||
- [X] T038 [US3] Integrate delegation resolution in ReviewTaskService `backend/src/modules/review-team/review-task.service.ts`
|
||||
- [X] T039 [P] [US3] Create Delegation settings UI page `frontend/src/app/(dashboard)/delegation/page.tsx`
|
||||
- [X] T040 [US3] Create Delegation form with date picker `frontend/src/components/delegation/DelegationForm.tsx`
|
||||
- [X] T041 [P] [US3] Create delegated task indicator ("Delegated from X") `frontend/src/components/review-task/DelegatedBadge.tsx`
|
||||
- [X] T042 [P] [US3] Create useDelegation hook `frontend/src/hooks/use-delegation.ts`
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -129,14 +134,16 @@ Scheduled reminders via BullMQ, 2-level escalation when overdue.
|
||||
|
||||
---
|
||||
|
||||
- [x] T043 [US4] Create ReminderRule entity `backend/src/modules/reminder/entities/reminder-rule.entity.ts`
|
||||
- [x] T044 [P] [US4] Create ReminderService with BullMQ integration `backend/src/modules/reminder/reminder.service.ts`
|
||||
- [x] T045 [US4] Implement reminder scheduling on RFA submit `backend/src/modules/reminder/services/scheduler.service.ts`
|
||||
- [x] T046 [P] [US4] Create ReminderProcessor for queue workers `backend/src/modules/reminder/processors/reminder.processor.ts`
|
||||
- [x] T047 [US4] Implement 2-level escalation logic `backend/src/modules/reminder/services/escalation.service.ts`
|
||||
- [x] T048 [P] [US4] Create ReminderRuleController admin endpoints `backend/src/modules/reminder/reminder.controller.ts`
|
||||
- [x] T049 [P] [US4] Create ReminderRule admin UI `frontend/src/app/(dashboard)/reminder-rules/page.tsx`
|
||||
- [x] T050 [US4] Create reminder history viewer `frontend/src/components/reminder/ReminderHistory.tsx`
|
||||
- [X] T043 [US4] Create ReminderRule entity `backend/src/modules/reminder/entities/reminder-rule.entity.ts`
|
||||
- [X] T044 [P] [US4] Create ReminderService with BullMQ integration `backend/src/modules/reminder/reminder.service.ts`
|
||||
- [X] T045 [US4] Implement reminder scheduling on RFA submit `backend/src/modules/reminder/services/scheduler.service.ts`
|
||||
- [X] T046 [P] [US4] Create ReminderProcessor for queue workers `backend/src/modules/reminder/processors/reminder.processor.ts`
|
||||
- [X] T047 [US4] Implement 2-level escalation logic `backend/src/modules/reminder/services/escalation.service.ts`
|
||||
- [X] T048 [P] [US4] Create ReminderRuleController admin endpoints `backend/src/modules/reminder/reminder.controller.ts`
|
||||
- [X] T049 [P] [US4] Create ReminderRule admin UI `frontend/src/app/(dashboard)/reminder-rules/page.tsx`
|
||||
- [X] T050 [US4] Create reminder history viewer `frontend/src/components/reminder/ReminderHistory.tsx`
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -152,16 +159,17 @@ Async distribution after approval, Transmittal records created via BullMQ.
|
||||
|
||||
---
|
||||
|
||||
- [x] T051 [US5] Create DistributionMatrix entity `backend/src/modules/distribution/entities/distribution-matrix.entity.ts`
|
||||
- [x] T052 [P] [US5] Create DistributionRecipient entity `backend/src/modules/distribution/entities/distribution-recipient.entity.ts`
|
||||
- [x] T053 [US5] Create DistributionMatrixService with CRUD `backend/src/modules/distribution/distribution-matrix.service.ts`
|
||||
- [x] T054 [P] [US5] Create DistributionService with BullMQ integration `backend/src/modules/distribution/distribution.service.ts`
|
||||
- [x] T055 [US5] Implement distribution triggering on approval `backend/src/modules/distribution/services/approval-listener.service.ts`
|
||||
- [x] T056 [P] [US5] Create DistributionProcessor for queue workers `backend/src/modules/distribution/processors/distribution.processor.ts`
|
||||
- [x] T057 [US5] Create Transmittal records from distribution `backend/src/modules/distribution/services/transmittal-creator.service.ts`
|
||||
- [x] T058 [P] [US5] Create DistributionMatrixController `backend/src/modules/distribution/distribution.controller.ts`
|
||||
- [x] T059 [P] [US5] Create Distribution Matrix admin UI `frontend/src/app/(dashboard)/distribution-matrices/page.tsx`
|
||||
- [x] T060 [US5] Create distribution status dashboard `frontend/src/components/distribution/DistributionStatus.tsx`
|
||||
- [X] T051 [US5] Create DistributionMatrix entity `backend/src/modules/distribution/entities/distribution-matrix.entity.ts`
|
||||
- [X] T052 [P] [US5] Create DistributionRecipient entity `backend/src/modules/distribution/entities/distribution-recipient.entity.ts`
|
||||
- [X] T053 [US5] Create DistributionMatrixService with CRUD `backend/src/modules/distribution/distribution-matrix.service.ts`
|
||||
- [X] T054 [P] [US5] Create DistributionService with BullMQ integration `backend/src/modules/distribution/distribution.service.ts`
|
||||
- [X] T055 [US5] Implement distribution triggering on approval `backend/src/modules/distribution/services/approval-listener.service.ts`
|
||||
- [X] T056 [P] [US5] Create DistributionProcessor for queue workers `backend/src/modules/distribution/processors/distribution.processor.ts`
|
||||
- [X] T057 [US5] Create Transmittal records from distribution `backend/src/modules/distribution/services/transmittal-creator.service.ts`
|
||||
- [X] T058 [P] [US5] Create DistributionMatrixController `backend/src/modules/distribution/distribution.controller.ts`
|
||||
- [X] T059 [P] [US5] Create Distribution Matrix admin UI `frontend/src/app/(dashboard)/distribution-matrices/page.tsx`
|
||||
- [X] T060 [US5] Create distribution status dashboard `frontend/src/components/distribution/DistributionStatus.tsx`
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -177,11 +185,12 @@ Admin UI for managing Matrix, project overrides with inheritance tracking.
|
||||
|
||||
---
|
||||
|
||||
- [x] T061 [US6] Extend ResponseCodeService with project overrides `backend/src/modules/response-code/services/matrix-management.service.ts`
|
||||
- [x] T062 [P] [US6] Create Matrix inheritance resolver `backend/src/modules/response-code/services/inheritance.service.ts`
|
||||
- [x] T063 [US6] Add Matrix management endpoints to ResponseCodeController `backend/src/modules/response-code/response-code.controller.ts`
|
||||
- [x] T064 [P] [US6] Create Master Approval Matrix visual editor `frontend/src/components/response-code/MatrixEditor.tsx`
|
||||
- [x] T065 [US6] Create project override management UI `frontend/src/components/response-code/ProjectOverrideManager.tsx`
|
||||
- [X] T061 [US6] Extend ResponseCodeService with project overrides `backend/src/modules/response-code/services/matrix-management.service.ts`
|
||||
- [X] T062 [P] [US6] Create Matrix inheritance resolver `backend/src/modules/response-code/services/inheritance.service.ts`
|
||||
- [X] T063 [US6] Add Matrix management endpoints to ResponseCodeController `backend/src/modules/response-code/response-code.controller.ts`
|
||||
- [X] T064 [P] [US6] Create Master Approval Matrix visual editor `frontend/src/components/response-code/MatrixEditor.tsx`
|
||||
- [X] T065 [US6] Create project override management UI `frontend/src/components/response-code/ProjectOverrideManager.tsx`
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -196,23 +205,24 @@ Workflow Engine integration, aggregate status, edge case handling, testing.
|
||||
|
||||
---
|
||||
|
||||
- [x] T066 Extend WorkflowEngine DSL with Parallel Gateway support `backend/src/modules/workflow-engine/dsl/parallel-gateway.handler.ts`
|
||||
- [x] T067 [P] Implement Review Task aggregate status calculator `backend/src/modules/review-team/services/aggregate-status.service.ts`
|
||||
- [x] T068 [P] Create consensus evaluation service `backend/src/modules/review-team/services/consensus.service.ts`
|
||||
- [x] T068.5 Implement Veto Override for Project Manager `backend/src/modules/review-team/services/veto-override.service.ts` - พร้อม audit trail และ notification
|
||||
- [x] T069 Implement race condition handling (Redlock) in ReviewTask completion `backend/src/modules/review-team/review-task.service.ts`
|
||||
- [x] T070 [P] Add optimistic locking to ReviewTask entity `backend/src/modules/review-team/entities/review-task.entity.ts`
|
||||
- [x] T071 Create Review Task inbox UI with aggregate status `frontend/src/components/review-task/ReviewTaskInbox.tsx`
|
||||
- [x] T072 [P] Create parallel review progress indicator `frontend/src/components/review-task/ParallelProgress.tsx`
|
||||
- [x] T072.5 Create Veto Override button and modal for PM `frontend/src/components/review-task/VetoOverrideDialog.tsx` - พร้อม input สำหรับ justification reason
|
||||
- [x] T073 Add validation for all edge cases in service layer `backend/src/common/validators/review-validators.ts`
|
||||
- [x] T074 [P] Create unit tests for ResponseCodeService `backend/tests/unit/response-code/response-code.service.spec.ts`
|
||||
- [x] T075 [P] Create unit tests for Delegation circular detection `backend/tests/unit/delegation/circular-detection.service.spec.ts`
|
||||
- [x] T076 [P] Create integration tests for parallel review consensus `backend/tests/integration/review-team/parallel-review.spec.ts`
|
||||
- [x] T077 Create e2e tests for complete RFA workflow `backend/tests/e2e/rfa-workflow.e2e-spec.ts`
|
||||
- [x] T078 [P] Add frontend tests for ResponseCodeSelector `frontend/tests/components/ResponseCodeSelector.test.tsx`
|
||||
- [x] T079 Update quickstart.md with final setup instructions `specs/1-rfa-approval-refactor/quickstart.md`
|
||||
- [x] T080 [P] Run full test suite and fix any failures `npm test`
|
||||
- [X] T066 Extend WorkflowEngine DSL with Parallel Gateway support `backend/src/modules/workflow-engine/dsl/parallel-gateway.handler.ts`
|
||||
- [X] T067 [P] Implement Review Task aggregate status calculator `backend/src/modules/review-team/services/aggregate-status.service.ts`
|
||||
- [X] T068 [P] Create consensus evaluation service `backend/src/modules/review-team/services/consensus.service.ts`
|
||||
- [X] T068.5 Implement Veto Override for Project Manager `backend/src/modules/review-team/services/veto-override.service.ts` - พร้อม audit trail และ notification
|
||||
- [X] T069 Implement race condition handling (Redlock) in ReviewTask completion `backend/src/modules/review-team/review-task.service.ts`
|
||||
- [X] T070 [P] Add optimistic locking to ReviewTask entity `backend/src/modules/review-team/entities/review-task.entity.ts`
|
||||
- [X] T071 Create Review Task inbox UI with aggregate status `frontend/src/components/review-task/ReviewTaskInbox.tsx`
|
||||
- [X] T072 [P] Create parallel review progress indicator `frontend/src/components/review-task/ParallelProgress.tsx`
|
||||
- [X] T072.5 Create Veto Override button and modal for PM `frontend/src/components/review-task/VetoOverrideDialog.tsx` - พร้อม input สำหรับ justification reason
|
||||
- [X] T073 Add validation for all edge cases in service layer `backend/src/common/validators/review-validators.ts`
|
||||
- [X] T074 [P] Create unit tests for ResponseCodeService `backend/tests/unit/response-code/response-code.service.spec.ts`
|
||||
- [X] T075 [P] Create unit tests for Delegation circular detection `backend/tests/unit/delegation/circular-detection.service.spec.ts`
|
||||
- [X] T076 [P] Create integration tests for parallel review consensus `backend/tests/integration/review-team/parallel-review.spec.ts`
|
||||
- [X] T077 Create e2e tests for complete RFA workflow `backend/tests/e2e/rfa-workflow.e2e-spec.ts`
|
||||
- [X] T078 [P] Add frontend tests for ResponseCodeSelector `frontend/tests/components/ResponseCodeSelector.test.tsx`
|
||||
- [X] T079 Update quickstart.md with final setup instructions `specs/1-rfa-approval-refactor/quickstart.md`
|
||||
- [X] T080 [P] Run full test suite and fix any failures `npm test`
|
||||
|
||||
|
||||
---
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# Specification Quality Checklist: Unified AI Architecture
|
||||
|
||||
**Purpose**: Validate specification completeness and quality before proceeding to planning
|
||||
**Created**: 2026-05-14
|
||||
**Feature**: [spec.md](./spec.md)
|
||||
|
||||
## Content Quality
|
||||
|
||||
- [x] No implementation details (languages, frameworks, APIs)
|
||||
- [x] Focused on user value and business needs
|
||||
- [x] Written for non-technical stakeholders
|
||||
- [x] All mandatory sections completed
|
||||
|
||||
## Requirement Completeness
|
||||
|
||||
- [x] No [NEEDS CLARIFICATION] markers remain
|
||||
- [x] Requirements are testable and unambiguous
|
||||
- [x] Success criteria are measurable
|
||||
- [x] Success criteria are technology-agnostic (no implementation details)
|
||||
- [x] All acceptance scenarios are defined
|
||||
- [x] Edge cases are identified
|
||||
- [x] Scope is clearly bounded
|
||||
- [x] Dependencies and assumptions identified
|
||||
|
||||
## Feature Readiness
|
||||
|
||||
- [x] All functional requirements have clear acceptance criteria
|
||||
- [x] User scenarios cover primary flows
|
||||
- [x] Feature meets measurable outcomes defined in Success Criteria
|
||||
- [x] No implementation details leak into specification
|
||||
|
||||
## Notes
|
||||
|
||||
- All checklist items pass based on initial generation.
|
||||
@@ -0,0 +1,71 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: LCBP3-DMS AI API
|
||||
version: 1.0.0
|
||||
paths:
|
||||
/api/ai/legacy-migration/ingest:
|
||||
post:
|
||||
summary: Upload legacy documents to the AI Pipeline
|
||||
security:
|
||||
- BearerAuth: []
|
||||
requestBody:
|
||||
content:
|
||||
multipart/form-data:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
files:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
format: binary
|
||||
responses:
|
||||
'202':
|
||||
description: Accepted and queued for processing
|
||||
|
||||
/api/ai/legacy-migration/queue:
|
||||
get:
|
||||
summary: List documents in the staging queue
|
||||
responses:
|
||||
'200':
|
||||
description: Returns a list of migration review records
|
||||
|
||||
/api/ai/legacy-migration/queue/{publicId}/approve:
|
||||
post:
|
||||
summary: Approve a document and import to DB
|
||||
parameters:
|
||||
- in: path
|
||||
name: publicId
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
format: uuid
|
||||
responses:
|
||||
'200':
|
||||
description: Document successfully imported
|
||||
|
||||
/api/ai/rag/query:
|
||||
post:
|
||||
summary: Submit a conversational query to the local LLM
|
||||
requestBody:
|
||||
required: true
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
projectPublicId:
|
||||
type: string
|
||||
format: uuid
|
||||
query:
|
||||
type: string
|
||||
responses:
|
||||
'202':
|
||||
description: Query queued via BullMQ, returns a Job ID
|
||||
|
||||
/api/ai/audit-logs:
|
||||
delete:
|
||||
summary: Hard delete AI audit logs
|
||||
responses:
|
||||
'204':
|
||||
description: Logs deleted successfully (Requires SYSTEM_ADMIN)
|
||||
@@ -0,0 +1,34 @@
|
||||
# Data Model: Unified AI Architecture
|
||||
|
||||
## Entity: `migration_review_queue`
|
||||
Stores legacy document processing results waiting for human-in-the-loop validation.
|
||||
|
||||
**Table Structure**:
|
||||
- `id` (INT, PK, Auto Increment) - Internal ID
|
||||
- `public_id` (BINARY(16), UUIDv7, Unique, Indexed) - Exposed to API (ADR-019)
|
||||
- `batch_id` (VARCHAR) - Groups documents from a single run
|
||||
- `original_file_name` (VARCHAR) - Name of the uploaded PDF
|
||||
- `extracted_metadata` (JSON) - The AI-extracted fields (title, date, categories, etc.)
|
||||
- `confidence_score` (DECIMAL) - Overall AI confidence score (0.0 to 1.0)
|
||||
- `status` (ENUM) - `PENDING`, `IMPORTED`, `REJECTED`
|
||||
- `error_reason` (TEXT, Nullable) - Populated if status is `REJECTED`
|
||||
- `version` (INT) - OptLocking via `@VersionColumn`
|
||||
- `created_at` (TIMESTAMP)
|
||||
- `updated_at` (TIMESTAMP)
|
||||
|
||||
## Entity: `ai_audit_logs`
|
||||
Development feedback logs for recording AI vs Human decisions.
|
||||
|
||||
**Table Structure**:
|
||||
- `id` (INT, PK, Auto Increment) - Internal ID
|
||||
- `public_id` (BINARY(16), UUIDv7, Unique, Indexed) - Exposed to API
|
||||
- `document_public_id` (BINARY(16), Nullable) - References the document if successfully imported
|
||||
- `model_name` (VARCHAR) - LLM Model used (e.g., "gemma4:9b")
|
||||
- `ai_suggestion_json` (JSON) - What the AI suggested
|
||||
- `human_override_json` (JSON) - What the human actually saved
|
||||
- `confidence_score` (DECIMAL)
|
||||
- `confirmed_by_user_id` (INT) - FK to users table
|
||||
- `created_at` (TIMESTAMP)
|
||||
|
||||
## Entity: `Project` (Existing)
|
||||
*Added Context*: The `publicId` of this table is injected natively into Qdrant payload filters as `project_public_id` for multi-tenant isolation.
|
||||
@@ -0,0 +1,72 @@
|
||||
# Implementation Plan: Unified AI Architecture
|
||||
|
||||
**Branch**: `301-unified-ai-arch` | **Date**: 2026-05-14 | **Spec**: [spec.md](./spec.md)
|
||||
**Input**: Feature specification from `specs/300-others/301-unified-ai-arch/spec.md`
|
||||
|
||||
## Summary
|
||||
|
||||
Implement a Master AI Architecture enforcing strict physical isolation of AI workloads on a dedicated Admin Desktop (Desk-5439). The system supports secure legacy document migration via a staging queue, context-aware conversational RAG queries, and detailed AI audit logging, all orchestrated through robust backend queues (BullMQ) and multi-tenant security filters.
|
||||
|
||||
## Technical Context
|
||||
|
||||
**Language/Version**: TypeScript (Node.js v24.15.0) for Backend/Frontend
|
||||
**Primary Dependencies**: NestJS 11, Next.js 16, BullMQ, Qdrant Node Client, n8n
|
||||
**Storage**: MariaDB 11.8 (Relational), Qdrant (Vector), Redis (Queue/Cache)
|
||||
**Testing**: Jest (Backend), Vitest (Frontend), E2E with Playwright
|
||||
**Target Platform**: QNAP Container Station (Production), Desk-5439 (AI Host)
|
||||
**Project Type**: Monorepo Web Application (Backend + Frontend)
|
||||
**Performance Goals**: RAG Response < 10s (p95), Migration throughput 1000 pages/hour
|
||||
**Constraints**: AI host has limited VRAM (8GB), necessitating concurrency limit of 1 for LLM generation.
|
||||
**Scale/Scope**: 20,000+ legacy documents, project-wide deployment.
|
||||
|
||||
## Constitution Check
|
||||
|
||||
_GATE: Must pass before Phase 0 research. Re-check after Phase 1 design._
|
||||
|
||||
- **ADR-019 UUID**: `publicId` used exclusively. No INT primary keys exposed.
|
||||
- **ADR-009 Database**: Schema changes via raw SQL deltas.
|
||||
- **ADR-016 Security**: CASL RBAC strictly enforced (`@UseGuards(CaslAbilityGuard)`). Idempotency-Key headers required.
|
||||
- **ADR-008 BullMQ**: Heavy AI orchestration and RAG queuing managed via BullMQ.
|
||||
- **ADR-018/023 AI Boundary**: AI host connects via DMS API. No direct database access.
|
||||
- **TypeScript Strict**: Explicit types, no `any`, proper error handling via `BusinessException`.
|
||||
|
||||
## Project Structure
|
||||
|
||||
### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/300-others/301-unified-ai-arch/
|
||||
├── spec.md
|
||||
├── plan.md # This file
|
||||
├── research.md
|
||||
├── data-model.md
|
||||
├── quickstart.md
|
||||
├── contracts/
|
||||
└── tasks.md # (To be created)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
backend/
|
||||
├── src/
|
||||
│ ├── ai/
|
||||
│ │ ├── ai.module.ts
|
||||
│ │ ├── ai.controller.ts
|
||||
│ │ ├── ai.service.ts
|
||||
│ │ ├── qdrant.service.ts
|
||||
│ │ ├── rag.processor.ts
|
||||
│ │ └── dto/
|
||||
│ └── database/
|
||||
│ └── sql/
|
||||
|
||||
frontend/
|
||||
├── src/
|
||||
│ ├── app/(dashboard)/ai-staging/
|
||||
│ ├── components/ai/
|
||||
│ │ ├── AiStatusBanner.tsx
|
||||
│ │ └── RagChatWidget.tsx
|
||||
│ └── lib/api/ai.ts
|
||||
```
|
||||
|
||||
**Structure Decision**: Integrated into the existing Next.js / NestJS monorepo architecture, utilizing a dedicated `AiModule` in the backend to centralize all external AI API calls and queue management.
|
||||
@@ -0,0 +1,21 @@
|
||||
# Quickstart: Unified AI Architecture
|
||||
|
||||
## 1. Setup the AI Host (Desk-5439)
|
||||
1. Install Ollama and pull `gemma4:9b` and `nomic-embed-text`.
|
||||
2. Start the Qdrant container with persistent storage.
|
||||
3. Start n8n and configure the API key to connect to the DMS backend.
|
||||
|
||||
## 2. Environment Variables (Backend)
|
||||
Add the following to your `.env`:
|
||||
```bash
|
||||
AI_HOST_URL=http://<desk-5439-ip>
|
||||
AI_QDRANT_URL=http://<desk-5439-ip>:6333
|
||||
AI_N8N_WEBHOOK_URL=http://<desk-5439-ip>:5678
|
||||
AI_N8N_SERVICE_TOKEN=your-secure-token
|
||||
```
|
||||
|
||||
## 3. Usage Flow (RAG)
|
||||
1. User submits a query via the Next.js `RagChatWidget`.
|
||||
2. Backend validates JWT and creates a BullMQ job on `rag-query-queue`.
|
||||
3. Worker retrieves the job, injects the `projectPublicId` filter into Qdrant.
|
||||
4. Worker fetches context, queries Ollama, and streams/returns the response.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Technical Research: Unified AI Architecture
|
||||
|
||||
**Feature**: Unified AI Architecture (ADR-023)
|
||||
**Date**: 2026-05-14
|
||||
|
||||
## Unknown 1: Integration Auth for n8n AI Pipeline
|
||||
**Decision**: Create a dedicated `ServiceAccount` API token mechanism for n8n to communicate with the DMS Backend API.
|
||||
**Rationale**: n8n runs on the isolated AI Host (Desk-5439) and requires programmatic access to upload legacy documents and update staging queue statuses. Standard user JWTs expire too quickly, so a long-lived, restricted-scope service account token is required.
|
||||
**Alternatives considered**:
|
||||
- Using a standard Admin user account (rejected due to token expiry and audit trail mingling).
|
||||
- Unauthenticated internal webhook (rejected due to ADR-016 Zero Trust policy).
|
||||
|
||||
## Unknown 2: Qdrant Multi-tenant Payload Filters
|
||||
**Decision**: Enforce `project_public_id` natively at the NestJS `QdrantService` layer for every search query.
|
||||
**Rationale**: Ensures that RAG queries absolutely cannot leak data across projects, satisfying SC-004. The backend will automatically inject the user's currently active project ID into the Qdrant filter condition before executing the search.
|
||||
**Alternatives considered**:
|
||||
- Having the LLM filter the context (rejected due to high risk of hallucination and leakage).
|
||||
|
||||
## Unknown 3: BullMQ RAG Queue Configuration
|
||||
**Decision**: Implement a dedicated `rag-query-queue` in BullMQ with a concurrency limit of `1`.
|
||||
**Rationale**: The local LLM (gemma4:9b) on Desk-5439 has limited VRAM (8GB). Processing more than one RAG query at a time will cause Out-Of-Memory (OOM) crashes. Queuing guarantees stability.
|
||||
**Alternatives considered**:
|
||||
- Load balancing across multiple GPUs (rejected: hardware constraints, only one RTX 2060 Super available).
|
||||
|
||||
## Unknown 4: UI/UX for Graceful AI Fallback
|
||||
**Decision**: Use React Context (`AiStatusProvider`) in Next.js to globally distribute the AI Host health status. If offline, AI-specific form fields (like auto-suggest chips) and the RAG Chat widget will conditionally render a disabled state or hide entirely.
|
||||
**Rationale**: Provides a seamless graceful degradation experience without requiring individual components to implement repetitive health-check logic.
|
||||
@@ -0,0 +1,106 @@
|
||||
# Feature Specification: Unified AI Architecture
|
||||
|
||||
**Feature Branch**: `301-unified-ai-arch`
|
||||
**Created**: 2026-05-14
|
||||
**Status**: Draft
|
||||
**Input**: User description: "ADR-023: Unified AI Architecture"
|
||||
|
||||
## Clarifications
|
||||
|
||||
### Session 2026-05-14
|
||||
- Q: Legacy Migration Input Mechanism (How are legacy PDFs fed into the AI pipeline?) → A: Both a secure Admin API endpoint and a watched network folder.
|
||||
- Q: Failure Handling for Corrupted PDFs (How does the system handle documents that fail AI processing?) → A: Mark as "Rejected" in the staging queue with an error reason.
|
||||
- Q: AI Audit Log Retention Policy (How long are AI audit logs retained?) → A: Infinite retention until manually deleted by a System Admin.
|
||||
- Q: RAG Concurrency Handling (How are concurrent RAG queries managed to prevent LLM timeouts?) → A: Request Queuing via BullMQ for sequential processing.
|
||||
|
||||
## User Scenarios & Testing _(mandatory)_
|
||||
|
||||
### User Story 1 - Legacy Document Migration and Review (Priority: P1)
|
||||
|
||||
As a System Administrator, I want to process legacy PDF documents through an AI pipeline and review the extracted metadata in a staging queue so that I can ensure data integrity before importing them into the main database.
|
||||
|
||||
**Why this priority**: Legacy document migration is the most critical immediate need for the LCBP3 project, as 20,000+ documents need to be imported securely and accurately.
|
||||
|
||||
**Independent Test**: Can be fully tested by uploading a batch of PDF documents, verifying they appear in the staging queue with AI-extracted metadata, and approving/rejecting them, which then commits them to the permanent storage.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a batch of legacy PDF documents, **When** processed by the AI pipeline, **Then** they appear in the staging queue with extracted metadata and confidence scores.
|
||||
2. **Given** a document in the staging queue with a high confidence score, **When** reviewed by an Admin, **Then** it is marked as ready for import without warnings.
|
||||
3. **Given** a document in the staging queue, **When** the Admin approves it, **Then** the file is moved to permanent storage and committed to the main database.
|
||||
|
||||
---
|
||||
|
||||
### User Story 2 - RAG Conversational Q&A (Priority: P2)
|
||||
|
||||
As a Document Controller or Org Admin, I want to ask natural language questions about project documents (RFA/Correspondence) and get context-aware answers so that I can quickly find information without manually reading full texts.
|
||||
|
||||
**Why this priority**: Enhances the user experience significantly by allowing conversational search over complex engineering documents.
|
||||
|
||||
**Independent Test**: Can be tested by asking a specific project-related question and receiving an answer generated by the AI, accompanied by citations to the relevant documents.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** a user has access to a specific project, **When** they ask a question via the RAG interface, **Then** the system returns an answer based ONLY on documents within that project's scope.
|
||||
2. **Given** the RAG system is queried, **When** generating an answer, **Then** the response is returned in under 10 seconds.
|
||||
|
||||
---
|
||||
|
||||
### User Story 3 - AI Audit Log Management (Priority: P3)
|
||||
|
||||
As a System Admin, I want to view and manage AI audit logs so that I can provide feedback to the development team to improve the AI models and clean up test data.
|
||||
|
||||
**Why this priority**: Essential for continuous improvement of the AI models and keeping the database clean, but not a blocker for the core user workflows.
|
||||
|
||||
**Independent Test**: Can be tested by generating AI suggestions, verifying they are logged in the audit table, and then having a System Admin perform a hard delete on those logs.
|
||||
|
||||
**Acceptance Scenarios**:
|
||||
|
||||
1. **Given** an AI suggestion is presented to a user, **When** the user confirms or overrides it, **Then** the action is recorded in the AI audit log with confidence scores and user feedback.
|
||||
2. **Given** a System Admin is viewing the AI audit logs, **When** they select records to delete, **Then** the records are hard-deleted and this deletion is recorded in the main compliance audit log.
|
||||
|
||||
### Edge Cases
|
||||
|
||||
- What happens when the isolated AI host is offline?
|
||||
- The system must fail gracefully, hiding AI suggestions and disabling RAG features, while allowing normal manual document operations to continue.
|
||||
- How does the system handle concurrent processing of a massive number of documents?
|
||||
- The system must queue tasks sequentially to prevent memory/resource overload on the isolated machine.
|
||||
- What happens if the AI suggests an invalid category or enum value?
|
||||
- The validation layer must reject invalid values before they are presented to the user or saved to the database.
|
||||
- What happens if a legacy document fails AI processing (e.g., corruption or timeout)?
|
||||
- The document must be marked as "Rejected" in the staging queue with a specific error reason, keeping it visible for Admin review.
|
||||
- How does the system handle concurrent RAG queries from multiple users?
|
||||
- RAG queries must be placed in a BullMQ queue and processed sequentially (or with strict concurrency limits) to prevent LLM crashes, potentially showing a "Waiting in queue..." state to the user.
|
||||
|
||||
## Requirements _(mandatory)_
|
||||
|
||||
### Functional Requirements
|
||||
|
||||
- **FR-001**: System MUST process AI workloads exclusively on a physically isolated machine without direct database or storage access.
|
||||
- **FR-001b**: System MUST support legacy document ingestion via BOTH a secure Admin API upload endpoint and a watched network folder.
|
||||
- **FR-002**: System MUST enforce a multi-tenant boundary by filtering vector search queries by project identifier.
|
||||
- **FR-003**: System MUST provide a staging queue for legacy document migration with human-in-the-loop review.
|
||||
- **FR-004**: System MUST record AI suggestions and human overrides in a dedicated AI audit log.
|
||||
- **FR-005**: System MUST allow System Admins to hard-delete AI audit logs, logging the deletion action in the main compliance audit log.
|
||||
- **FR-005b**: System MUST retain AI audit logs indefinitely until a System Admin explicitly performs a manual hard-delete.
|
||||
- **FR-006**: System MUST gracefully disable AI features when the AI host is offline, allowing manual data entry to continue uninterrupted.
|
||||
- **FR-007**: System MUST validate the final payload (whether AI-suggested or human-overridden) against defined master data before database commitment.
|
||||
- **FR-008**: System MUST utilize BullMQ for asynchronous vector deletion in Qdrant when documents are deleted, ensuring eventual consistency via retries to prevent orphaned vectors.
|
||||
- **FR-009**: System MUST limit users to 1 active concurrent RAG query, blocking new queries until the active one completes.
|
||||
- **FR-010**: System MUST enforce a rate limit of maximum 5 RAG queries per minute per user.
|
||||
- **FR-011**: System SHOULD attempt to abort the underlying LLM generation if a client disconnects during a streaming response, requiring the user to requeue upon reconnecting.
|
||||
- **FR-012**: For all constrained master data fields, the UI MUST enforce selection via predefined lists (Dropdowns) and prohibit arbitrary free-text input during the human review process.
|
||||
|
||||
### Key Entities
|
||||
|
||||
- **Migration Review Record**: Represents a document in the staging queue, containing extracted metadata, confidence score, and review status (Pending, Imported, Rejected).
|
||||
- **AI Audit Log**: Represents a record of AI suggestions vs human decisions for model feedback and training purposes.
|
||||
|
||||
## Success Criteria _(mandatory)_
|
||||
|
||||
### Measurable Outcomes
|
||||
|
||||
- **SC-001**: Legacy document migration pipeline can process 1,000 pages per hour without system degradation.
|
||||
- **SC-002**: The RAG Q&A system answers user queries with a response time (p95) of under 10 seconds.
|
||||
- **SC-003**: 100% of AI-processed documents pass validation against master data schemas before being committed to the database.
|
||||
- **SC-004**: Zero cross-project data leakage incidents during RAG queries.
|
||||
@@ -0,0 +1,110 @@
|
||||
# Tasks: Unified AI Architecture
|
||||
|
||||
**Input**: Design documents from `/specs/300-others/301-unified-ai-arch/`
|
||||
**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/openapi.yaml
|
||||
**Tests**: Tests are OPTIONAL for this implementation phase unless specifically requested during PR review.
|
||||
|
||||
## Phase 1: Setup (Shared Infrastructure)
|
||||
|
||||
**Purpose**: Project initialization and basic structure
|
||||
|
||||
- [ ] T001 Initialize `AiModule` inside `backend/src/ai/ai.module.ts`
|
||||
- [ ] T002 [P] Install `qdrant-js` client dependency in the backend workspace
|
||||
- [ ] T003 Add `AI_HOST_URL`, `AI_QDRANT_URL`, `AI_N8N_SERVICE_TOKEN` to backend `.env` configuration
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Foundational (Blocking Prerequisites)
|
||||
|
||||
**Purpose**: Core infrastructure that MUST be complete before ANY user story can be implemented
|
||||
**⚠️ CRITICAL**: No user story work can begin until this phase is complete
|
||||
|
||||
- [ ] T004 Setup `QdrantService` in `backend/src/ai/qdrant.service.ts` to manage vector DB connections
|
||||
- [ ] T005 [P] Setup BullMQ infrastructure in `AiModule` (configure `AiQueueService`)
|
||||
- [ ] T006 [P] Implement `ServiceAccountGuard` to validate n8n service tokens for internal API routes
|
||||
- [ ] T007 Implement SQL Schema Deltas for `migration_review_queue` and `ai_audit_logs` in MariaDB
|
||||
- [ ] T008 Implement TypeORM base entities mapping to the created SQL tables
|
||||
|
||||
**Checkpoint**: Foundation ready - user story implementation can now begin
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: User Story 1 - Legacy Document Migration and Review (Priority: P1) 🎯 MVP
|
||||
|
||||
**Goal**: Process legacy PDFs through an AI pipeline and review extracted metadata in a staging queue before DB commit.
|
||||
**Independent Test**: Upload PDF batch via n8n/endpoint, verify they appear in the UI queue, and approve them successfully.
|
||||
|
||||
### Implementation for User Story 1
|
||||
|
||||
- [ ] T009 [P] [US1] Create `MigrationReviewRecord` TypeORM Entity in `backend/src/ai/entities/migration-review.entity.ts`
|
||||
- [ ] T010 [US1] Implement `AiIngestService` to handle batch ingestion and queue creation
|
||||
- [ ] T011 [US1] Implement `POST /api/ai/legacy-migration/ingest` in `AiController` using `ServiceAccountGuard`
|
||||
- [ ] T011b [P] [US1] Export n8n workflow definition to `backend/src/ai/workflows/folder-watcher.json` to monitor the network directory and POST to the ingest API (FR-001b)
|
||||
- [ ] T012 [US1] Implement `GET /api/ai/legacy-migration/queue` in `AiController`
|
||||
- [ ] T013 [US1] Implement `POST /api/ai/legacy-migration/queue/{publicId}/approve` with Zod/class-validator payload checking (FR-007)
|
||||
- [ ] T014 [P] [US1] Create Frontend API hooks for staging queue in `frontend/src/lib/api/ai.ts`
|
||||
- [ ] T015 [US1] Build Frontend Staging Queue Table UI in `frontend/src/app/(dashboard)/ai-staging/page.tsx`
|
||||
- [ ] T016 [US1] Implement UI Form dropdown constraints for master data fields in the approval modal (FR-012)
|
||||
- [ ] T017 [US1] Build `AiStatusBanner.tsx` component in `frontend/src/components/ai/AiStatusBanner.tsx` to handle offline graceful degradation
|
||||
|
||||
**Checkpoint**: At this point, User Story 1 should be fully functional.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: User Story 2 - RAG Conversational Q&A (Priority: P2)
|
||||
|
||||
**Goal**: Ask natural language questions about project documents with context-aware RAG answers.
|
||||
**Independent Test**: Submit a RAG query for a specific project; verify response < 10s and accurate isolation.
|
||||
|
||||
### Implementation for User Story 2
|
||||
|
||||
- [ ] T018 [P] [US2] Create BullMQ Processor `rag.processor.ts` with strict concurrency limit = 1 (FR-009)
|
||||
- [ ] T019 [US2] Implement `AiRagService` containing Ollama LLM integration logic
|
||||
- [ ] T020 [US2] Enforce `projectPublicId` filtering natively in Qdrant search payload inside `AiRagService`
|
||||
- [ ] T021 [US2] Implement `POST /api/ai/rag/query` to push jobs to BullMQ and apply rate limiting (5 per min) (FR-010)
|
||||
- [ ] T022 [US2] Add AbortController logic to backend processor to cancel LLM generation on client disconnect (FR-011)
|
||||
- [ ] T023 [P] [US2] Build `RagChatWidget.tsx` component with streaming/polling UI for queue wait status
|
||||
|
||||
**Checkpoint**: RAG capability is fully implemented and throttled safely.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: User Story 3 - AI Audit Log Management (Priority: P3)
|
||||
|
||||
**Goal**: View and manage AI audit logs for model feedback, with safe deletion capabilities.
|
||||
**Independent Test**: Generate AI suggestions, verify logs exist, and test hard delete as a System Admin.
|
||||
|
||||
### Implementation for User Story 3
|
||||
|
||||
- [ ] T024 [P] [US3] Create `AiAuditLog` TypeORM Entity in `backend/src/ai/entities/ai-audit-log.entity.ts`
|
||||
- [ ] T025 [US3] Inject Audit Log creation logic into the `/approve` endpoint (capture Human vs AI differences)
|
||||
- [ ] T026 [US3] Implement `DELETE /api/ai/audit-logs` endpoint with `@UseGuards(CaslAbilityGuard)` checking for `SYSTEM_ADMIN`
|
||||
- [ ] T027 [US3] Create BullMQ Processor `vector-deletion.processor.ts` to handle asynchronous vector cleanup (FR-008)
|
||||
- [ ] T028 [US3] Integrate `vector-deletion-queue` dispatch into the main Document Deletion service
|
||||
|
||||
**Checkpoint**: AI Audit and safe vector cleanup are complete.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Polish & Cross-Cutting Concerns
|
||||
|
||||
**Purpose**: Improvements that affect multiple user stories
|
||||
|
||||
- [ ] T029 Code cleanup and CASL RBAC matrix review for all AI endpoints
|
||||
- [ ] T030 E2E Validation of the BullMQ concurrency limit (stress test 10 concurrent requests)
|
||||
- [ ] T031 Finalize `README.md` and `quickstart.md` documentation for Desk-5439 setup
|
||||
|
||||
---
|
||||
|
||||
## Dependencies & Execution Order
|
||||
|
||||
### Phase Dependencies
|
||||
- **Setup (Phase 1)**: Start immediately
|
||||
- **Foundational (Phase 2)**: Depends on Phase 1 - BLOCKS US1, US2, US3
|
||||
- **User Stories (Phases 3-5)**: Depend on Phase 2. Should be executed sequentially (US1 -> US2 -> US3) due to shared services, but frontend/backend tasks within each story can run in parallel.
|
||||
- **Polish (Phase 6)**: Depends on all stories completing.
|
||||
|
||||
### Parallel Opportunities
|
||||
- Database schema changes (T007) and Backend Auth Setup (T006)
|
||||
- Frontend UI components (T015, T017, T023) can be stubbed concurrently with backend API creation.
|
||||
- Entity creation (T009, T024) and BullMQ Processor setup (T018)
|
||||
@@ -0,0 +1,218 @@
|
||||
-- =============================================================================
|
||||
-- LCBP3-DMS v1.9.0 — RFA Approval System Refactor Schema
|
||||
-- Feature Branch: 1-rfa-approval-refactor
|
||||
-- ADR-009: No TypeORM migrations — edit SQL schema directly
|
||||
-- Created: 2026-05-12
|
||||
-- =============================================================================
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 1. review_teams — ทีมตรวจสอบแยกตาม Discipline
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `review_teams` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`project_id` INT NOT NULL,
|
||||
`name` VARCHAR(100) NOT NULL,
|
||||
`description` VARCHAR(255) NULL,
|
||||
`default_for_rfa_types` TEXT NULL COMMENT 'Comma-separated RFA type codes e.g. SDW,DDW',
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_review_teams_uuid` (`uuid`),
|
||||
KEY `idx_review_teams_project` (`project_id`, `is_active`),
|
||||
CONSTRAINT `fk_review_teams_project` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 2. review_team_members — สมาชิกในทีมแยกตาม Discipline
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `review_team_members` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`team_id` INT NOT NULL,
|
||||
`user_id` INT NOT NULL,
|
||||
`discipline_id` INT NOT NULL,
|
||||
`role` ENUM('REVIEWER','LEAD','MANAGER') NOT NULL DEFAULT 'REVIEWER',
|
||||
`priority_order` INT NOT NULL DEFAULT 0,
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_review_team_members_uuid` (`uuid`),
|
||||
UNIQUE KEY `uq_team_user_discipline` (`team_id`, `user_id`, `discipline_id`),
|
||||
KEY `idx_rtm_team` (`team_id`),
|
||||
KEY `idx_rtm_user` (`user_id`),
|
||||
CONSTRAINT `fk_rtm_team` FOREIGN KEY (`team_id`) REFERENCES `review_teams` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_rtm_user` FOREIGN KEY (`user_id`) REFERENCES `users` (`user_id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_rtm_discipline` FOREIGN KEY (`discipline_id`) REFERENCES `disciplines` (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 3. response_codes — รหัสตอบกลับมาตรฐาน (Master Approval Matrix)
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `response_codes` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`code` VARCHAR(10) NOT NULL COMMENT '1A, 1B, 1C, 1D, 1E, 1F, 1G, 2, 3, 4',
|
||||
`sub_status` VARCHAR(10) NULL,
|
||||
`category` ENUM('ENGINEERING','MATERIAL','CONTRACT','TESTING','ESG') NOT NULL,
|
||||
`description_th` TEXT NOT NULL,
|
||||
`description_en` TEXT NOT NULL,
|
||||
`implications` JSON NULL COMMENT '{"affectsSchedule":bool,"affectsCost":bool,"requiresContractReview":bool}',
|
||||
`notify_roles` TEXT NULL COMMENT 'Comma-separated roles e.g. CONTRACT_MANAGER,QS_MANAGER',
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`is_system` TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'System default — cannot delete',
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_response_codes_uuid` (`uuid`),
|
||||
UNIQUE KEY `uq_response_code_category` (`code`, `category`),
|
||||
KEY `idx_rc_category_active` (`category`, `is_active`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 4. response_code_rules — กฎการใช้รหัสต่อโครงการ/ประเภทเอกสาร
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `response_code_rules` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`project_id` INT NULL COMMENT 'NULL = global default',
|
||||
`document_type_id` INT NOT NULL,
|
||||
`response_code_id` INT NOT NULL,
|
||||
`is_enabled` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`requires_comments` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`triggers_notification` TINYINT(1) NOT NULL DEFAULT 0,
|
||||
`parent_rule_id` INT NULL COMMENT 'For inheritance tracking',
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_response_code_rules_uuid` (`uuid`),
|
||||
UNIQUE KEY `uq_rule_per_project_doctype_code` (`project_id`, `document_type_id`, `response_code_id`),
|
||||
KEY `idx_response_rules_lookup` (`project_id`, `document_type_id`, `is_enabled`),
|
||||
CONSTRAINT `fk_rcr_response_code` FOREIGN KEY (`response_code_id`) REFERENCES `response_codes` (`id`),
|
||||
CONSTRAINT `fk_rcr_parent` FOREIGN KEY (`parent_rule_id`) REFERENCES `response_code_rules` (`id`) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 5. review_tasks — งานตรวจสอบสำหรับแต่ละ Discipline (Parallel Review)
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `review_tasks` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`rfa_revision_id` INT NOT NULL,
|
||||
`team_id` INT NOT NULL,
|
||||
`discipline_id` INT NOT NULL,
|
||||
`assigned_to_user_id` INT NULL COMMENT 'NULL = auto-assign by discipline',
|
||||
`status` ENUM('PENDING','IN_PROGRESS','COMPLETED','DELEGATED','EXPIRED','CANCELLED') NOT NULL DEFAULT 'PENDING',
|
||||
`due_date` DATE NULL,
|
||||
`response_code_id` INT NULL,
|
||||
`comments` TEXT NULL,
|
||||
`attachments` JSON NULL COMMENT 'Array of attachment publicIds',
|
||||
`delegated_from_user_id` INT NULL COMMENT 'Original assignee when delegated',
|
||||
`completed_at` TIMESTAMP NULL,
|
||||
`version` INT NOT NULL DEFAULT 1 COMMENT 'Optimistic locking (ADR-002)',
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_review_tasks_uuid` (`uuid`),
|
||||
UNIQUE KEY `uq_review_task_per_revision_discipline` (`rfa_revision_id`, `team_id`, `discipline_id`),
|
||||
KEY `idx_review_tasks_rfa_revision` (`rfa_revision_id`),
|
||||
KEY `idx_review_tasks_status` (`status`),
|
||||
KEY `idx_review_tasks_assigned` (`assigned_to_user_id`, `status`),
|
||||
CONSTRAINT `fk_rt_rfa_revision` FOREIGN KEY (`rfa_revision_id`) REFERENCES `rfa_revisions` (`id`) ON DELETE CASCADE,
|
||||
CONSTRAINT `fk_rt_team` FOREIGN KEY (`team_id`) REFERENCES `review_teams` (`id`),
|
||||
CONSTRAINT `fk_rt_discipline` FOREIGN KEY (`discipline_id`) REFERENCES `disciplines` (`id`),
|
||||
CONSTRAINT `fk_rt_user` FOREIGN KEY (`assigned_to_user_id`) REFERENCES `users` (`user_id`) ON DELETE SET NULL,
|
||||
CONSTRAINT `fk_rt_response_code` FOREIGN KEY (`response_code_id`) REFERENCES `response_codes` (`id`) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 6. delegations — การมอบหมายงาน
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `delegations` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`delegator_user_id` INT NOT NULL COMMENT 'ผู้มอบหมาย (FK → users.user_id)',
|
||||
`delegate_user_id` INT NOT NULL COMMENT 'ผู้รับมอบหมาย (FK → users.user_id)',
|
||||
`start_date` DATE NOT NULL,
|
||||
`end_date` DATE NULL COMMENT 'BullMQ job flips is_active=0 when end_date < NOW() (ADR-008)',
|
||||
`scope` ENUM('ALL','RFA_ONLY','CORRESPONDENCE_ONLY','SPECIFIC_TYPES') NOT NULL DEFAULT 'ALL',
|
||||
`document_types` TEXT NULL COMMENT 'Comma-separated doc type codes when scope=SPECIFIC_TYPES',
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1 COMMENT 'Managed by BullMQ scheduler — do not flip manually',
|
||||
`reason` TEXT NULL,
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_delegations_uuid` (`uuid`),
|
||||
KEY `idx_delegations_active` (`delegator_user_id`, `is_active`, `start_date`, `end_date`),
|
||||
KEY `idx_delegations_delegate` (`delegate_user_id`, `is_active`),
|
||||
CONSTRAINT `fk_del_delegator` FOREIGN KEY (`delegator_user_id`) REFERENCES `users` (`user_id`),
|
||||
CONSTRAINT `fk_del_delegate` FOREIGN KEY (`delegate_user_id`) REFERENCES `users` (`user_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 7. reminder_rules — กฎการแจ้งเตือน
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `reminder_rules` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`name` VARCHAR(100) NOT NULL,
|
||||
`project_id` INT NULL COMMENT 'NULL = global',
|
||||
`document_type_id` INT NULL COMMENT 'NULL = all types',
|
||||
`trigger_days_before_due` INT NOT NULL DEFAULT 2,
|
||||
`escalation_days_after_due` INT NOT NULL DEFAULT 1,
|
||||
`reminder_type` ENUM('DUE_SOON','ON_DUE','OVERDUE','ESCALATION_L1','ESCALATION_L2') NOT NULL,
|
||||
`recipients` TEXT NOT NULL COMMENT 'Comma-separated: ASSIGNEE,MANAGER,PROJECT_MANAGER',
|
||||
`message_template_th` TEXT NOT NULL,
|
||||
`message_template_en` TEXT NOT NULL,
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
`updated_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_reminder_rules_uuid` (`uuid`),
|
||||
KEY `idx_reminder_rules_active` (`is_active`, `project_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 8. distribution_matrices — ตารางกระจายเอกสาร
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `distribution_matrices` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`uuid` UUID NOT NULL DEFAULT (UUID()),
|
||||
`name` VARCHAR(100) NOT NULL,
|
||||
`project_id` INT NULL COMMENT 'NULL = global',
|
||||
`document_type_id` INT NOT NULL,
|
||||
`response_code_id` INT NULL COMMENT 'NULL = applies to all codes',
|
||||
`conditions` JSON NULL COMMENT '{"codes":["1A","1B"],"excludeCodes":["3","4"]}',
|
||||
`is_active` TINYINT(1) NOT NULL DEFAULT 1,
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uq_distribution_matrices_uuid` (`uuid`),
|
||||
KEY `idx_distribution_lookup` (`document_type_id`, `response_code_id`, `is_active`),
|
||||
CONSTRAINT `fk_dm_response_code` FOREIGN KEY (`response_code_id`) REFERENCES `response_codes` (`id`) ON DELETE SET NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- -----------------------------------------------------------------------------
|
||||
-- 9. distribution_recipients — ผู้รับเอกสารใน Distribution Matrix
|
||||
-- -----------------------------------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `distribution_recipients` (
|
||||
`id` INT NOT NULL AUTO_INCREMENT,
|
||||
`matrix_id` INT NOT NULL,
|
||||
`recipient_type` ENUM('USER','ORGANIZATION','TEAM','ROLE') NOT NULL,
|
||||
`recipient_public_id` UUID NOT NULL COMMENT 'publicId of target: USER=users.uuid | ORGANIZATION=organizations.uuid | TEAM=review_teams.uuid | ROLE=roles.role_id (roles has no uuid — store as string of role_id, query confirmed Q4B)',
|
||||
`delivery_method` ENUM('EMAIL','IN_APP','BOTH') NOT NULL DEFAULT 'BOTH',
|
||||
`sequence` INT NULL COMMENT 'For ordered delivery',
|
||||
`created_at` DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_dr_matrix` (`matrix_id`),
|
||||
KEY `idx_dr_type_recipient` (`recipient_type`, `recipient_public_id`),
|
||||
CONSTRAINT `fk_dr_matrix` FOREIGN KEY (`matrix_id`) REFERENCES `distribution_matrices` (`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
|
||||
COMMENT='Polymorphic recipients — no FK on recipient_public_id (by design). ROLE type uses roles.role_id cast to UUID string until roles table gains uuid column (pending delta-11)';
|
||||
|
||||
-- =============================================================================
|
||||
-- Additional Indexes (Performance)
|
||||
-- =============================================================================
|
||||
-- (all created inline above with KEY statements)
|
||||
|
||||
-- =============================================================================
|
||||
-- END OF SCHEMA v1.9.0
|
||||
-- =============================================================================
|
||||
+10
-10
@@ -11,7 +11,7 @@
|
||||
|
||||
---
|
||||
|
||||
## 📂 Directory Structure (v1.8.9)
|
||||
## 📂 Directory Structure (v1.9.0)
|
||||
|
||||
```text
|
||||
specs/
|
||||
@@ -48,11 +48,11 @@ specs/
|
||||
│ ├── 03-04-legacy-data-migration.md # Legacy Data Migration จาก Excel (ADR-017)
|
||||
│ ├── 03-05-n8n-migration-setup-guide.md # n8n Workflow Setup + Ollama Integration
|
||||
│ ├── 03-06-migration-business-scope.md # ★ Gap 7: Migration Scope, 3 Tiers, Go/No-Go Gates
|
||||
│ ├── lcbp3-v1.8.0-schema-01-drop.sql # Schema: DROP statements
|
||||
│ ├── lcbp3-v1.8.0-schema-02-tables.sql # Schema: CREATE TABLE (Source of Truth)
|
||||
│ ├── lcbp3-v1.8.0-schema-03-views-indexes.sql # Schema: Views + Indexes
|
||||
│ ├── lcbp3-v1.8.0-seed-basic.sql # Seed: Master Data
|
||||
│ ├── lcbp3-v1.8.0-seed-permissions.sql # Seed: CASL Permission Matrix
|
||||
│ ├── lcbp3-v1.9.0-schema-01-drop.sql # Schema: DROP statements
|
||||
│ ├── lcbp3-v1.9.0-schema-02-tables.sql # Schema: CREATE TABLE (Source of Truth)
|
||||
│ ├── lcbp3-v1.9.0-schema-03-views-indexes.sql # Schema: Views + Indexes
|
||||
│ ├── lcbp3-v1.9.0-seed-basic.sql # Seed: Master Data
|
||||
│ ├── lcbp3-v1.9.0-seed-permissions.sql # Seed: CASL Permission Matrix
|
||||
│ └── README.md # ภาพรวม Data Strategy
|
||||
│
|
||||
├── 04-Infrastructure-OPS/ # โครงสร้างพื้นฐานและการปฏิบัติการ
|
||||
@@ -135,9 +135,9 @@ specs/
|
||||
|
||||
| เอกสาร | Path | ใช้เมื่อ |
|
||||
| -------------------- | ----------------------------------------------------------- | ----------------------------------- |
|
||||
| **Schema Tables** | `03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql` | ก่อนเขียน Query ทุกครั้ง |
|
||||
| **Schema Tables** | `03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql` | ก่อนเขียน Query ทุกครั้ง |
|
||||
| **Data Dictionary** | `03-Data-and-Storage/03-01-data-dictionary.md` | ตรวจ Field Meaning + Business Rules |
|
||||
| **Seed Permissions** | `03-Data-and-Storage/lcbp3-v1.8.0-seed-permissions.sql` | ตรวจ CASL Permission Matrix |
|
||||
| **Seed Permissions** | `03-Data-and-Storage/lcbp3-v1.9.0-seed-permissions.sql` | ตรวจ CASL Permission Matrix |
|
||||
| **Edge Cases** | `01-Requirements/01-06-edge-cases-and-rules.md` | 37 Rules ป้องกัน Bug |
|
||||
| **Migration Scope** | `03-Data-and-Storage/03-06-migration-business-scope.md` | งาน Migration Bot |
|
||||
| **Release Policy** | `04-Infrastructure-OPS/04-08-release-management-policy.md` | ก่อน Deploy / Hotfix |
|
||||
@@ -153,13 +153,13 @@ specs/
|
||||
|
||||
1. **The Specs are the Source of Truth:** ก่อนเริ่มงานเสมอ ให้อ่าน Requirement, Architecture และ ADR ถ้าเจอประโยคไหนที่คุณคิดไว้แล้วขัดแย้งกับ Specs ให้ยึดจากใน `specs/` เป็นคำตอบสุดท้าย
|
||||
|
||||
2. **Never Invent Tables / Columns:** ห้ามสร้างคอลัมน์ใหม่ในหัวเอง ให้ดู Schema ใน `03-Data-and-Storage/lcbp3-v1.8.0-schema-02-tables.sql` สำหรับโครงสร้าง Table ทั้งหลัก และ Reference — Schema แบ่งเป็น 3 ไฟล์ (01-drop / 02-tables / 03-views-indexes)
|
||||
2. **Never Invent Tables / Columns:** ห้ามสร้างคอลัมน์ใหม่ในหัวเอง ให้ดู Schema ใน `03-Data-and-Storage/lcbp3-v1.9.0-schema-02-tables.sql` สำหรับโครงสร้าง Table ทั้งหลัก และ Reference — Schema แบ่งเป็น 3 ไฟล์ (01-drop / 02-tables / 03-views-indexes)
|
||||
|
||||
3. **Double-Lock Numbering:** ระบบออกเลขเอกสารมีความอ่อนไหวสูง เนื่องจากมีหลาย User พร้อมกัน ต้องใช้ **Redis Redlock** ควบคู่กับ **DB Optimistic Lock** เพื่อแก้ Race Condition (ADR-002)
|
||||
|
||||
4. **Follow Blue-Green Deployment:** โปรเจกต์พึ่งพาการทำ Blue-Green Environment เพื่อ Downtime ขั้นต่ำ ต้องผ่าน **5 Release Gates** ก่อน Deploy ทุกครั้ง — ดู `04-08-release-management-policy.md`
|
||||
|
||||
5. **No `any` Types:** ไม่อนุญาตให้ใช้ `any` ในโค้ด พยายามใช้ Validation ผ่าน DTO / Zod แบบ Strongly-typed เสมอ — **Enforced ✅** (0 remaining in backend as of v1.8.1, ดูเทคนิคที่ `05-02-backend-guidelines.md`)
|
||||
5. **No `any` Types:** ไม่อนุญาตให้ใช้ `any` ในโค้ด พยายามใช้ Validation ผ่าน DTO / Zod แบบ Strongly-typed เสมอ — **Enforced ✅** (0 remaining in backend as of v1.9.0, ดูเทคนิคที่ `05-02-backend-guidelines.md`)
|
||||
|
||||
6. **AI Isolation (ADR-018):** Ollama ต้องรันบน **Admin Desktop** (i9-9900K, RTX 2060 SUPER 8GB) เท่านั้น — ห้ามรันบน QNAP/Production Server ห้ามมี Direct DB Access โดยเด็ดขาด AI Output ต้องผ่าน Backend Validation ก่อน Write ทุกครั้ง
|
||||
|
||||
|
||||
Reference in New Issue
Block a user