feat(rfa-ai): Complete RFA Approval Refactor and AI Model Revision
CI / CD Pipeline / build (push) Successful in 4m54s
CI / CD Pipeline / deploy (push) Failing after 12m9s

This commit is contained in:
2026-05-16 10:59:53 +07:00
parent 6cb3ae10ee
commit 1a162bf320
105 changed files with 5088 additions and 1083 deletions
@@ -1,9 +1,9 @@
---
title: 'Data & Storage: Data Dictionary and Data Model Architecture'
version: 1.9.0
version: 1.9.1
status: released
owner: Nattanin Peancharoen
last_updated: 2026-04-14
last_updated: 2026-05-16
related:
- specs/01-requirements/02-architecture.md
- specs/01-requirements/03-functional-requirements.md
@@ -1372,6 +1372,7 @@ erDiagram
| checksum | VARCHAR(64) | NULL | SHA-256 Checksum สำหรับ Verify File Integrity [Req 3.9.3] |
| reference_date | DATE | NULL | Date used for folder structure (e.g. Issue Date) to prevent broken paths |
| workflow_history_id | VARCHAR(36) | NULL, FK | **[ADR-021]** อ้างอิง workflow_histories.publicId — NULL = ไฟล์แนบหลักของเอกสาร; NOT NULL = ไฟล์หลักฐานประจำ Workflow Step |
| ai_processing_status | ENUM | NOT NULL, DEFAULT 'PENDING' | **[ADR-023A]** สถานะ AI job ของไฟล์เอกสาร: PENDING / PROCESSING / DONE / FAILED |
**Indexes**:
@@ -1384,6 +1385,7 @@ erDiagram
- INDEX (created_at)
- INDEX (reference_date)
- INDEX (workflow_history_id)
- INDEX idx_attachments_ai_status (ai_processing_status)
**Relationships**:
@@ -2286,29 +2288,86 @@ PENDING_REVIEW ──→ VERIFIED ──→ IMPORTED (terminal)
---
### 19.2 `ai_audit_logs`
### 19.1 `migration_review_queue`
**วัตถุประสงค์:** บันทึก Audit Trail ของ AI Interaction ทุกครั้ง ตาม ADR-018 Rule 5 — ห้ามลบ record ออก
**วัตถุประสงค์:** ตาราง staging queue สำหรับ AI migration ตาม ADR-023A — เก็บข้อมูลเอกสาร legacy ที่ AI ประมวลผลแล้วรอ Admin ตรวจสอบ
| Column | Type | Nullable | Description |
|--------|------|----------|-------------|
| `id` | INT AUTO_INCREMENT | NO | Internal PK — ห้าม expose ใน API (ADR-019) |
| `uuid` | UUID | NO | Public Identifier (UUIDv7) |
| `document_public_id` | UUID | YES | UUID ของ `migration_logs` ที่เกี่ยวข้อง (Soft Reference — ไม่ใช่ FK ในระดับ TypeORM) |
| `ai_model` | VARCHAR(50) | NO | ชื่อ AI Model เช่น `gemma4`, `paddleocr`, `gemma4:27b` |
| `processing_time_ms` | INT | YES | เวลาประมวลผล (milliseconds) — เป้าหมาย < 15,000ms ตาม ADR-020 |
| `confidence_score` | DECIMAL(3,2) | YES | คะแนนความมั่นใจ (0.00–1.00) |
| `input_hash` | VARCHAR(64) | YES | SHA-256 hash ของ Input — เพื่อ Integrity Verification |
| `output_hash` | VARCHAR(64) | YES | SHA-256 hash ของ Output — เพื่อ Integrity Verification |
| `status` | ENUM | NO | ผลการประมวลผล: SUCCESS / FAILED / TIMEOUT |
| `error_message` | TEXT | YES | รายละเอียด Error (เมื่อ status = FAILED หรือ TIMEOUT) |
| `created_at` | TIMESTAMP | NO | วันที่สร้าง — เรียงตาม timestamp เพื่อ Audit |
| `batch_id` | VARCHAR(100) | NO | n8n batch identifier |
| `idempotency_key` | VARCHAR(200) | NO | Idempotency-Key สำหรับป้องกัน queue ซ้ำ |
| `original_filename` | VARCHAR(500) | NO | ชื่อไฟล์ต้นฉบับจาก legacy source |
| `storage_temp_path` | VARCHAR(1000) | NO | temp storage path ก่อน import |
| `ai_metadata_json` | JSON | NO | AI suggestion payload เต็มสำหรับ human review |
| `confidence_score` | DECIMAL(5,4) | NO | AI confidence score 0.0000-1.0000 |
| `ocr_used` | TINYINT(1) | NO | ระบุว่าใช้ OCR path หรือไม่ (default: 0) |
| `status` | ENUM | NO | สถานะ: PENDING / APPROVED / IMPORTED / REJECTED |
| `reviewed_by` | INT | YES | Internal users.user_id ของผู้ review |
| `reviewed_at` | DATETIME | YES | เวลาที่ review record |
| `rejection_reason` | VARCHAR(500) | YES | เหตุผลเมื่อ reject |
| `version` | INT | NO | Optimistic locking version |
| `created_at` | DATETIME | NO | วันที่สร้าง |
| `updated_at` | DATETIME | NO | วันที่แก้ไขล่าสุด |
**Indexes**:
- PRIMARY KEY (id)
- UNIQUE KEY uq_migration_review_uuid (uuid)
- UNIQUE KEY uq_migration_review_idempotency (idempotency_key)
- KEY idx_migration_review_status_created (status, created_at)
- KEY idx_migration_review_batch (batch_id)
- KEY idx_migration_review_reviewed_by (reviewed_by)
- CONSTRAINT fk_migration_review_reviewed_by FOREIGN KEY (reviewed_by) REFERENCES users (user_id) ON DELETE SET NULL
#### Business Rules
1. **Immutable Records** — ห้ามแก้ไขหรือลบ `ai_audit_logs` หลังสร้าง (Audit Trail)
2. **Data Retention** — เก็บไว้อย่างน้อย 90 วัน ตาม ADR-020 Data Privacy
3. **No FK Constraint**`document_public_id` เป็น Soft Reference เพื่อให้ Log ยังคงอยู่แม้ MigrationLog ถูกลบ
1. **Staging Area** — ข้อมูลที่ประมวลผลผ่าน n8n จะถูกส่งเข้าตารางนี้เสมอ
2. **Record Lifecycle** — Record ไม่ถูกลบหลัง Import — เปลี่ยน status เป็น IMPORTED เก็บไว้ตลอดเพื่อ Debug
3. **Status Transitions** — PENDING → IMPORTED หรือ PENDING → REJECTED
4. **Confidence Threshold** — กำหนดผ่าน .env (AI_THRESHOLD_HIGH=0.85, AI_THRESHOLD_MID=0.60)
---
### 19.2 `ai_audit_logs`
**วัตถุประสงค์:** บันทึก AI Development Feedback Log ตาม ADR-023/ADR-023A — เก็บ AI Suggestion + การตัดสินใจของมนุษย์เพื่อวิเคราะห์และปรับปรุงคุณภาพโมเดล (ไม่ใช่ Compliance Audit Trail)
| Column | Type | Nullable | Description |
|--------|------|----------|-------------|
| `id` | INT AUTO_INCREMENT | NO | Internal PK — ห้าม expose ใน API (ADR-019) |
| `uuid` | UUID | NO | Public Identifier (UUIDv7) |
| `document_public_id` | UUID | YES | Imported document publicId when available |
| `ai_model` | VARCHAR(50) | NO | Legacy AI model column used by current gateway service (default: gemma4) |
| `model_name` | VARCHAR(100) | NO | Local model name used by ADR-023 AI pipeline |
| `ai_suggestion_json` | JSON | YES | AI suggested metadata |
| `human_override_json` | JSON | YES | Human approved or overridden metadata |
| `processing_time_ms` | INT | YES | Legacy processing duration field |
| `confidence_score` | DECIMAL(4,3) | YES | AI confidence score 0.000-1.000 |
| `input_hash` | VARCHAR(64) | YES | Legacy SHA-256 input hash |
| `output_hash` | VARCHAR(64) | YES | Legacy SHA-256 output hash |
| `status` | ENUM | NO | Legacy processing status field: SUCCESS / FAILED / TIMEOUT |
| `error_message` | TEXT | YES | Legacy processing error field |
| `confirmed_by_user_id` | INT | YES | Internal users.user_id that confirmed the record |
| `created_at` | TIMESTAMP | NO | วันที่สร้าง |
**Indexes**:
- PRIMARY KEY (id)
- UNIQUE KEY idx_ai_audit_logs_uuid (uuid)
- KEY idx_ai_audit_document (document_public_id)
- KEY idx_ai_audit_model (ai_model)
- KEY idx_ai_audit_model_name (model_name)
- KEY idx_ai_audit_status (status)
- KEY idx_ai_audit_confirmed_by (confirmed_by_user_id)
- CONSTRAINT fk_ai_audit_confirmed_by_user FOREIGN KEY (confirmed_by_user_id) REFERENCES users (user_id) ON DELETE SET NULL
#### Business Rules
1. **Development Feedback Log** — เป็น log สำหรับวิเคราะห์และปรับปรุงคุณภาพโมเดล AI ไม่ใช่ Compliance Audit Trail
2. **Data Retention** — เก็บไว้ตลอดอายุโครงการ (~5-10 ปี) — Admin สามารถ Hard Delete ได้ผ่าน Frontend
3. **RBAC** — เฉพาะ Role SYSTEM_ADMIN เท่านั้นที่ลบได้ — การลบทุกครั้งต้องบันทึกใน audit_logs (action: 'AI_AUDIT_LOG_DELETED')
---
+126
View File
@@ -0,0 +1,126 @@
# Data and Storage Specifications
เอกสารและสคริปต์ที่เกี่ยวข้องกับการจัดการข้อมูลและการจัดเก็บสำหรับระบบ NAP-DMS (LCBP3)
## 📁 โครงสร้างไดเรกทอรี
### Schema Files (สคริปต์ฐานข้อมูล)
| ไฟล์ | คำอธิบาย |
|------|-----------|
| `lcbp3-v1.9.0-schema-01-drop.sql` | สคริปต์ลบตารางทั้งหมด (ใช้สำหรับ reset schema) |
| `lcbp3-v1.9.0-schema-02-tables.sql` | สคริปต์สร้างตารางทั้งหมด (CREATE TABLE) |
| `lcbp3-v1.9.0-schema-03-views-indexes.sql` | สคริปต์สร้าง Views และ Indexes |
| `lcbp3-v1.9.0-migration.sql` | สคริปต์ migration สำหรับการอัปเกรดจากเวอร์ชันก่อนหน้า |
| `lcbp3-v1.9.0-rfa-approval-schema.sql` | Schema เฉพาะสำหรับระบบ RFA Approval System |
### Seed Files (ข้อมูลเริ่มต้น)
| ไฟล์ | คำอธิบาย |
|------|-----------|
| `lcbp3-v1.9.0-seed-basic.sql` | ข้อมูลเริ่มต้นพื้นฐาน (Organizations, Users, Roles, Permissions) |
| `lcbp3-v1.9.0-seed-permissions.sql` | ข้อมูล RBAC Permissions ตาม ADR-016 |
| `lcbp3-v1.9.0-seed-contractdrawing.sql` | ข้อมูล Contract Drawings ตัวอย่าง |
| `lcbp3-v1.9.0-seed-shopdrawing.sql` | ข้อมูล Shop Drawings ตัวอย่าง |
### Delta Files (การเปลี่ยนแปลง Schema แบบ Incremental)
ตั้งอยู่ใน `deltas/` - เก็บ SQL delta สำหรับการเปลี่ยนแปลง schema ตาม ADR-009 (ไม่ใช้ TypeORM migrations)
| ไฟล์ | คำอธิบาย |
|------|-----------|
| `12-unified-ai-architecture.sql` | เพิ่มตาราง AI: migration_review_queue, ai_audit_logs (ADR-023) |
| `14-add-migration-review-queue.sql` | เพิ่มคอลัมน์ใหม่ใน migration_review_queue (ADR-023A) |
| `15-add-ai-processing-status.sql` | เพิ่ม ai_processing_status ใน attachments (ADR-023A) |
### Documentation
| ไฟล์ | คำอธิบาย |
|------|-----------|
| `0.md` | ภาพรวมและแนวทางการจัดการข้อมูล |
| `03-01-data-dictionary.md` | Data Dictionary คำอธิบายฟิลด์ทั้งหมด |
| `03-02-db-indexing.md` | กลยุทธ์การสร้าง Indexes |
| `03-03-file-storage.md` | กลยุทธ์การจัดเก็บไฟล์ (Two-Phase Upload) |
| `03-04-legacy-data-migration.md` | แผนการนำเข้าข้อมูลเก่า (Legacy Migration) |
| `03-05-n8n-migration-setup-guide.md` | คู่มือติดตั้ง n8n สำหรับ Migration Phase |
| `03-06-migration-business-scope.md` | ขอบเขตการทำ Migration ตาม ADR-009 |
| `03-07-OpenRAG.md` | เอกสาร RAG Implementation Guide |
### Configuration Files
| ไฟล์ | คำอธิบาย |
|------|-----------|
| `n8n.workflow.json` | Workflow n8n สำหรับ Legacy Document Migration |
| `permissions-verification.sql` | สคริปต์ตรวจสอบ Permissions ที่กำหนดไว้ |
## 🚀 การใช้งาน
### การ Setup ฐานข้อมูลใหม่ (Fresh Install)
```bash
# 1. ลบตารางทั้งหมด (ถ้ามี)
mysql < lcbp3-v1.9.0-schema-01-drop.sql
# 2. สร้างตารางทั้งหมด
mysql < lcbp3-v1.9.0-schema-02-tables.sql
# 3. สร้าง Views และ Indexes
mysql < lcbp3-v1.9.0-schema-03-views-indexes.sql
# 4. เพิ่มข้อมูลเริ่มต้นพื้นฐาน
mysql < lcbp3-v1.9.0-seed-basic.sql
# 5. เพิ่ม Permissions
mysql < lcbp3-v1.9.0-seed-permissions.sql
# 6. เพิ่มข้อมูลตัวอย่าง (Optional)
mysql < lcbp3-v1.9.0-seed-contractdrawing.sql
mysql < lcbp3-v1.9.0-seed-shopdrawing.sql
```
### การอัปเกรดจากเวอร์ชันก่อนหน้า
```bash
# รัน migration script
mysql < lcbp3-v1.9.0-migration.sql
# รัน delta files ที่ยังไม่ได้ใช้ (ตามลำดับเลข)
mysql < deltas/12-unified-ai-architecture.sql
mysql < deltas/14-add-migration-review-queue.sql
mysql < deltas/15-add-ai-processing-status.sql
```
## 🔗 เอกสารที่เกี่ยวข้อง
### ADRs (Architecture Decision Records)
- **ADR-009**: Database Migration Strategy - ใช้ SQL delta โดยตรง ห้ามใช้ TypeORM migrations
- **ADR-016**: Security & Authentication - RBAC Matrix
- **ADR-019**: Hybrid Identifier Strategy - UUID Strategy
- **ADR-023**: Unified AI Architecture - สถาปัตยกรรม AI หลัก
- **ADR-023A**: Unified AI Architecture (Model Revision) - อัปเดตโมเดล AI
### Engineering Guidelines
- `specs/05-Engineering-Guidelines/05-02-backend-guidelines.md` - Backend patterns
- `specs/05-Engineering-Guidelines/05-03-frontend-guidelines.md` - Frontend patterns
## ⚠️ ข้อควรระวัง
1. **ADR-009**: ห้ามใช้ TypeORM migrations - ต้องแก้ SQL schema โดยตรงและสร้าง delta file
2. **UUID Handling**: ใช้ UUIDv7 (MariaDB native) ตาม ADR-019 - ห้ามใช้ `parseInt()` บน UUID
3. **AI Boundary**: ตาราง AI (migration_review_queue, ai_audit_logs) ต้องถูกจัดการผ่าน DMS API เท่านั้น (ADR-023)
4. **File Upload**: ต้องใช้ Two-Phase Storage (Temp → Commit) ตาม ADR-016
5. **Schema Changes**: ทุกการเปลี่ยนแปลงต้องอัปเดต Data Dictionary พร้อมกัน
## 📝 Change Log
- **2026-05-15**: เพิ่ม AI-related tables (migration_review_queue, ai_audit_logs) และ ai_processing_status column ตาม ADR-023A
- **2026-05-14**: อัปเดต schema เป็น v1.9.0 เพื่อรองรับ RFA Approval System และ Unified AI Architecture
## 👥 ผู้รับผิดชอบ
- Database Schema: System Architect
- Data Dictionary: Business Analyst
- Migration Scripts: Backend Team
- AI Integration: AI Integration Lead
@@ -0,0 +1,56 @@
-- File: specs/03-Data-and-Storage/deltas/14-add-migration-review-queue.sql
-- Change Log
-- - 2026-05-15: เพิ่ม delta สำหรับ migration_review_queue ตาม ADR-023A โดยไม่ลบ/rename column เดิม.
-- ADR-009: ใช้ SQL delta โดยตรง ห้ามใช้ TypeORM migration
SET NAMES utf8mb4;
CREATE TABLE IF NOT EXISTS migration_review_queue (
id INT NOT NULL AUTO_INCREMENT COMMENT 'Internal PK (ห้าม expose ใน API)',
uuid UUID NOT NULL DEFAULT UUID() COMMENT 'UUID Public Identifier (ADR-019)',
batch_id VARCHAR(100) NOT NULL COMMENT 'n8n batch identifier',
idempotency_key VARCHAR(200) NOT NULL COMMENT 'Idempotency-Key สำหรับป้องกัน queue ซ้ำ',
original_filename VARCHAR(500) NOT NULL COMMENT 'ชื่อไฟล์ต้นฉบับจาก legacy source',
storage_temp_path VARCHAR(1000) NOT NULL COMMENT 'temp storage path ก่อน import',
ai_metadata_json JSON NOT NULL COMMENT 'AI suggestion payload เต็มสำหรับ human review',
confidence_score DECIMAL(5, 4) NOT NULL COMMENT 'AI confidence score 0.0000-1.0000',
ocr_used TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'ระบุว่าใช้ OCR path หรือไม่',
status ENUM('PENDING', 'IMPORTED', 'REJECTED') NOT NULL DEFAULT 'PENDING',
reviewed_by INT NULL COMMENT 'Internal users.user_id ของผู้ review',
reviewed_at DATETIME NULL COMMENT 'เวลาที่ review record',
rejection_reason VARCHAR(500) NULL COMMENT 'เหตุผลเมื่อ reject',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_migration_review_uuid (uuid),
UNIQUE KEY uq_migration_review_idempotency (idempotency_key),
KEY idx_migration_review_status_created (status, created_at),
KEY idx_migration_review_batch (batch_id),
KEY idx_migration_review_reviewed_by (reviewed_by)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ADR-023A AI migration review staging queue';
ALTER TABLE migration_review_queue
ADD COLUMN IF NOT EXISTS idempotency_key VARCHAR(200) NULL COMMENT 'Idempotency-Key สำหรับป้องกัน queue ซ้ำ',
ADD COLUMN IF NOT EXISTS original_filename VARCHAR(500) NULL COMMENT 'ชื่อไฟล์ต้นฉบับจาก legacy source',
ADD COLUMN IF NOT EXISTS storage_temp_path VARCHAR(1000) NULL COMMENT 'temp storage path ก่อน import',
ADD COLUMN IF NOT EXISTS ai_metadata_json JSON NULL COMMENT 'AI suggestion payload เต็มสำหรับ human review',
ADD COLUMN IF NOT EXISTS ocr_used TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'ระบุว่าใช้ OCR path หรือไม่',
ADD COLUMN IF NOT EXISTS reviewed_by INT NULL COMMENT 'Internal users.user_id ของผู้ review',
ADD COLUMN IF NOT EXISTS reviewed_at DATETIME NULL COMMENT 'เวลาที่ review record',
ADD COLUMN IF NOT EXISTS rejection_reason VARCHAR(500) NULL COMMENT 'เหตุผลเมื่อ reject';
UPDATE migration_review_queue
SET
idempotency_key = COALESCE(idempotency_key, CONCAT(batch_id, ':', uuid)),
original_filename = COALESCE(original_filename, original_file_name),
ai_metadata_json = COALESCE(ai_metadata_json, extracted_metadata),
rejection_reason = COALESCE(rejection_reason, error_reason)
WHERE idempotency_key IS NULL
OR original_filename IS NULL
OR ai_metadata_json IS NULL
OR rejection_reason IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS uq_migration_review_idempotency ON migration_review_queue (idempotency_key);
CREATE INDEX IF NOT EXISTS idx_migration_review_status_created ON migration_review_queue (status, created_at);
CREATE INDEX IF NOT EXISTS idx_migration_review_batch ON migration_review_queue (batch_id);
CREATE INDEX IF NOT EXISTS idx_migration_review_reviewed_by ON migration_review_queue (reviewed_by);
@@ -0,0 +1,14 @@
-- File: specs/03-Data-and-Storage/deltas/15-add-ai-processing-status.sql
-- Change Log
-- - 2026-05-15: เพิ่มสถานะประมวลผล AI สำหรับเอกสารตาม ADR-023A FR-018.
-- ADR-009: ใช้ SQL delta โดยตรง ห้ามใช้ TypeORM migration
SET NAMES utf8mb4;
-- หมายเหตุ: schema v1.9.0 ยังไม่มีตาราง documents กลาง จึงเพิ่มให้ตาราง attachments
-- ซึ่งเป็นตารางไฟล์เอกสารรวมที่มีอยู่จริงใน canonical schema ปัจจุบัน
ALTER TABLE attachments
ADD COLUMN IF NOT EXISTS ai_processing_status ENUM('PENDING', 'PROCESSING', 'DONE', 'FAILED')
NOT NULL DEFAULT 'PENDING' COMMENT 'สถานะ AI job ของไฟล์เอกสารตาม ADR-023A';
CREATE INDEX IF NOT EXISTS idx_attachments_ai_status ON attachments (ai_processing_status);
@@ -871,13 +871,15 @@ CREATE TABLE attachments (
CHECKSUM VARCHAR(64) NULL COMMENT 'SHA-256 Checksum',
reference_date DATE NULL COMMENT 'Date used for folder structure (e.g. Issue Date) to prevent broken paths',
workflow_history_id CHAR(36) NULL COMMENT 'FK to workflow_histories.id for step-specific attachments (ADR-021). NULL = main document',
ai_processing_status ENUM('PENDING', 'PROCESSING', 'DONE', 'FAILED') NOT NULL DEFAULT 'PENDING' COMMENT 'สถานะ AI job ของไฟล์เอกสารตาม ADR-023A',
FOREIGN KEY (uploaded_by_user_id) REFERENCES users (user_id) ON DELETE CASCADE,
FOREIGN KEY (workflow_history_id) REFERENCES workflow_histories (id) ON DELETE
SET NULL ON UPDATE CASCADE,
INDEX idx_attachments_reference_date (reference_date),
INDEX idx_att_wfhist_created (workflow_history_id, created_at),
INDEX idx_attachments_ai_status (ai_processing_status),
UNIQUE INDEX idx_attachments_uuid (uuid)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = 'ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ';
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ตาราง "กลาง" เก็บไฟล์แนบทั้งหมดของระบบ';
-- ตารางเชื่อม correspondence_revisions กับ attachments (M:N)
-- [FIX] FK เปลี่ยนจาก correspondences.id → correspondence_revisions.id
@@ -1447,60 +1449,98 @@ CREATE TABLE migration_logs (
SET NULL
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ตารางเก็บบันทึก Migration เอกสารที่ผ่าน AI Processing (Task BE-AI-02)';
-- ตาราง Audit Log สำหรับการทำงานของ AI ทุกครั้ง (ADR-018 Rule 5)
-- =====================================================
-- 12. 🤖 AI Migration Review Queue (ADR-023, ADR-023A)
-- =====================================================
-- ตาราง staging queue สำหรับ AI migration ตาม ADR-023A
CREATE TABLE migration_review_queue (
id INT NOT NULL AUTO_INCREMENT COMMENT 'Internal PK (ห้าม expose ใน API)',
uuid UUID NOT NULL DEFAULT UUID() COMMENT 'UUID Public Identifier (ADR-019)',
batch_id VARCHAR(100) NOT NULL COMMENT 'n8n batch identifier',
idempotency_key VARCHAR(200) NOT NULL COMMENT 'Idempotency-Key สำหรับป้องกัน queue ซ้ำ',
original_filename VARCHAR(500) NOT NULL COMMENT 'ชื่อไฟล์ต้นฉบับจาก legacy source',
storage_temp_path VARCHAR(1000) NOT NULL COMMENT 'temp storage path ก่อน import',
ai_metadata_json JSON NOT NULL COMMENT 'AI suggestion payload เต็มสำหรับ human review',
confidence_score DECIMAL(5, 4) NOT NULL COMMENT 'AI confidence score 0.0000-1.0000',
ocr_used TINYINT(1) NOT NULL DEFAULT 0 COMMENT 'ระบุว่าใช้ OCR path หรือไม่',
STATUS ENUM('PENDING', 'APPROVED', 'IMPORTED', 'REJECTED') NOT NULL DEFAULT 'PENDING',
reviewed_by INT NULL COMMENT 'Internal users.user_id ของผู้ review',
reviewed_at DATETIME NULL COMMENT 'เวลาที่ review record',
rejection_reason VARCHAR(500) NULL COMMENT 'เหตุผลเมื่อ reject',
version INT NOT NULL DEFAULT 1 COMMENT 'Optimistic locking version',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
UNIQUE KEY uq_migration_review_uuid (uuid),
UNIQUE KEY uq_migration_review_idempotency (idempotency_key),
KEY idx_migration_review_status_created (STATUS, created_at),
KEY idx_migration_review_batch (batch_id),
KEY idx_migration_review_reviewed_by (reviewed_by),
CONSTRAINT fk_migration_review_reviewed_by FOREIGN KEY (reviewed_by) REFERENCES users (user_id) ON DELETE
SET NULL
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ADR-023A AI migration review staging queue';
-- ตาราง Audit Log สำหรับการทำงานของ AI ทุกครั้ง (ADR-023, ADR-023A)
CREATE TABLE ai_audit_logs (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'Internal PK (ห้าม expose ใน API)',
uuid UUID NOT NULL DEFAULT UUID() COMMENT 'UUID Public Identifier (ADR-019)',
document_public_id UUID NULL COMMENT 'UUID ของ migration_logs ที่เกี่ยวข้อง',
ai_model VARCHAR(50) NOT NULL COMMENT 'ชื่อ AI Model ที่ใช้ประมวลผล เช่น gemma4',
processing_time_ms INT NULL COMMENT 'เวลาประมวลผล (milliseconds)',
confidence_score DECIMAL(3, 2) NULL COMMENT 'คะแนนความมั่นใจ AI (0.00-1.00)',
input_hash VARCHAR(64) NULL COMMENT 'SHA-256 hash ของ Input เพื่อ Audit',
output_hash VARCHAR(64) NULL COMMENT 'SHA-256 hash ของ Output เพื่อ Audit',
STATUS ENUM('SUCCESS', 'FAILED', 'TIMEOUT') NOT NULL COMMENT 'สถานะการประมวลผล',
error_message TEXT NULL COMMENT 'ข้อความ Error (ถ้ามี)',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'วันที่สร้าง',
UNIQUE INDEX idx_ai_audit_logs_uuid (uuid),
INDEX idx_ai_audit_document (document_public_id),
INDEX idx_ai_audit_model (ai_model),
INDEX idx_ai_audit_status (STATUS)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ตาราง Audit Log การทำงาน AI ทุกครั้ง (ADR-018 Rule 5 Audit Logging)';
document_public_id UUID NULL COMMENT 'Imported document publicId when available',
ai_model VARCHAR(50) NOT NULL DEFAULT 'gemma4' COMMENT 'Legacy AI model column used by current gateway service',
model_name VARCHAR(100) NOT NULL COMMENT 'Local model name used by ADR-023 AI pipeline',
ai_suggestion_json JSON NULL COMMENT 'AI suggested metadata',
human_override_json JSON NULL COMMENT 'Human approved or overridden metadata',
processing_time_ms INT NULL COMMENT 'Legacy processing duration field',
confidence_score DECIMAL(4, 3) NULL COMMENT 'AI confidence score 0.000-1.000',
input_hash VARCHAR(64) NULL COMMENT 'Legacy SHA-256 input hash',
output_hash VARCHAR(64) NULL COMMENT 'Legacy SHA-256 output hash',
STATUS ENUM('SUCCESS', 'FAILED', 'TIMEOUT') NOT NULL DEFAULT 'SUCCESS' COMMENT 'Legacy processing status field',
error_message TEXT NULL COMMENT 'Legacy processing error field',
confirmed_by_user_id INT NULL COMMENT 'Internal users.user_id that confirmed the record',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY idx_ai_audit_logs_uuid (uuid),
KEY idx_ai_audit_document (document_public_id),
KEY idx_ai_audit_model (ai_model),
KEY idx_ai_audit_model_name (model_name),
KEY idx_ai_audit_status (STATUS),
KEY idx_ai_audit_confirmed_by (confirmed_by_user_id),
CONSTRAINT fk_ai_audit_confirmed_by_user FOREIGN KEY (confirmed_by_user_id) REFERENCES users (user_id) ON DELETE
SET NULL
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ADR-023 AI development feedback log';
-- =============================================================================
-- 20. RFA Approval System (v1.9.0)
-- =============================================================================
-- -----------------------------------------------------------------------------
-- 20.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),
`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;
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.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),
`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`),
@@ -1509,73 +1549,91 @@ CREATE TABLE IF NOT EXISTS `review_team_members` (
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;
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.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),
`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;
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.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),
`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`),
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;
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;
-- -----------------------------------------------------------------------------
-- 20.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),
`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`),
@@ -1585,95 +1643,117 @@ 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` (`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;
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;
-- -----------------------------------------------------------------------------
-- 20.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),
`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_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;
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.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),
`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;
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
-- -----------------------------------------------------------------------------
-- 20.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),
`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;
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;
-- -----------------------------------------------------------------------------
-- 20.9 distribution_recipients — ผู้รับเอกสารใน Distribution Matrix
-- -----------------------------------------------------------------------------
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 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),
`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 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
COMMENT='Polymorphic recipients — no FK on recipient_public_id (by design). ROLE type uses roles.uuid (ADR-019)';
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'Polymorphic recipients — no FK on recipient_public_id (by design). ROLE type uses roles.uuid (ADR-019)';
-- =============================================================================
-- END OF SCHEMA v1.9.0
@@ -1066,8 +1066,6 @@ VALUES -- Contract Management
-- ==========================================================
-- VERIFICATION: Run permissions-verification.sql after this
-- ==========================================================
-- ==========================================================
-- MERGED FROM fix-project-permissions.sql (v1.9.0 Update)
-- ==========================================================
@@ -1088,15 +1086,19 @@ VALUES (
'project',
1
);
-- 2. Grant project.view to Superadmin (Role 1)
INSERT IGNORE INTO role_permissions (role_id, permission_id)
VALUES (1, 202);
-- 3. Grant project.view to Organization Admin (Role 2)
INSERT IGNORE INTO role_permissions (role_id, permission_id)
VALUES (2, 202);
-- 4. Grant project.view to Project Manager (Role 6)
INSERT IGNORE INTO role_permissions (role_id, permission_id)
VALUES (6, 202);
-- 5. Grant project.view to Viewer (Role 5)
INSERT IGNORE INTO role_permissions (role_id, permission_id)
VALUES (5, 202);
@@ -1139,25 +1141,44 @@ VALUES (
'Hard Delete ai_audit_logs (Superadmin Only)',
'ai',
1
),
(
185,
'ai.read_analytics',
'ดู AI Analytics Summary (Confidence, Override Rate, Rejected Rate)',
'ai',
1
),
(
186,
'ai.delete_audit',
'ลบ AiAuditLog เดี่ยวโดย publicId (Superadmin Only)',
'ai',
1
);
-- Role 1: Superadmin — ได้รับทุก permission โดยอัตโนมัติผ่าน SELECT-all pattern (บรรทัด 825-829)
-- Role 2: Org Admin — ai.suggest, ai.rag_query, ai.migration_manage
-- Role 2: Org Admin — ai.suggest, ai.rag_query, ai.migration_manage, ai.read_analytics
INSERT IGNORE INTO role_permissions (role_id, permission_id)
VALUES (2, 181),
-- ai.suggest
(2, 182),
-- ai.rag_query
(2, 183);
(2, 183),
-- ai.migration_manage
(2, 185);
-- ai.migration_manage
-- Role 3: Document Control — ai.suggest, ai.rag_query, ai.migration_manage
-- ai.read_analytics
-- Role 3: Document Control — ai.suggest, ai.rag_query, ai.migration_manage, ai.read_analytics
INSERT IGNORE INTO role_permissions (role_id, permission_id)
VALUES (3, 181),
-- ai.suggest
(3, 182),
-- ai.rag_query
(3, 183);
(3, 183),
-- ai.migration_manage
(3, 185);
-- ai.read_analytics
-- ai.migration_manage
-- ai.audit_log_delete (184) — Superadmin เท่านั้น, ไม่ grant ให้ Role อื่น