690519:1719 224 to 226 AI #02
CI / CD Pipeline / build (push) Successful in 4m47s
CI / CD Pipeline / deploy (push) Failing after 7m3s

This commit is contained in:
2026-05-19 17:19:24 +07:00
parent ea5499123e
commit 7259cbf67a
30 changed files with 783 additions and 878 deletions
@@ -1,4 +0,0 @@
ALTER TABLE `attachments`
ADD COLUMN `reference_date` DATE NULL COMMENT 'Date used for folder structure (e.g. Issue Date) to prevent broken paths';
ALTER TABLE `attachments`
ADD INDEX `idx_attachments_reference_date` (`reference_date`);
@@ -1,12 +0,0 @@
-- Add permission for Bulk RBAC Update
-- Fix: Use existing 'user.manage_assignments' instead of creating new invalid permission
-- This permission (ID 25) is for assigning roles/projects
-- Grant to ADMIN (ID 2) and DC (ID 3) roles if not already present
-- Use INSERT IGNORE to avoid duplicates
INSERT IGNORE INTO `role_permissions` (`role_id`, `permission_id`)
SELECT r.role_id,
p.permission_id
FROM `roles` r,
`permissions` p
WHERE r.role_name IN ('ADMIN', 'Org Admin', 'DC', 'Document Control')
AND p.permission_name = 'user.manage_assignments';
@@ -1,30 +0,0 @@
-- Delta 03: Fix ENUM mismatches in document_number_audit and document_number_errors
-- Issue: 'GENERATE' operation used by backend but not in DB ENUM → INSERT fails → correspondence creation blocked
-- Date: 2026-03-19
-- Applies to: lcbp3-v1.8.0-schema-02-tables.sql
-- 1. Add 'VOID' and 'GENERATE' to document_number_audit.operation ENUM
ALTER TABLE document_number_audit
MODIFY COLUMN operation ENUM(
'RESERVE',
'CONFIRM',
'MANUAL_OVERRIDE',
'VOID_REPLACE',
'CANCEL',
'VOID',
'GENERATE'
) NOT NULL DEFAULT 'CONFIRM' COMMENT 'ประเภทการดำเนินการ';
-- 2. Add missing error_type values to document_number_errors
ALTER TABLE document_number_errors
MODIFY COLUMN error_type ENUM(
'LOCK_TIMEOUT',
'VERSION_CONFLICT',
'DB_ERROR',
'REDIS_ERROR',
'VALIDATION_ERROR',
'SEQUENCE_EXHAUSTED',
'RESERVATION_EXPIRED',
'DUPLICATE_NUMBER',
'GENERATE_ERROR'
) NOT NULL COMMENT 'ประเภท error (9 types)';
@@ -1,25 +0,0 @@
-- ============================================================
-- Delta 04: ADR-021 — Step-specific Attachments
-- เพิ่ม FK workflow_history_id ใน attachments table
-- ============================================================
-- ข้อควรระวัง: ค่า NULL = ไฟล์แนบหลัก (Main Document)
-- ค่าไม่ NULL = ไฟล์ประจำ Workflow Step นั้น
ALTER TABLE attachments
ADD COLUMN workflow_history_id CHAR(36) NULL
COMMENT 'FK to workflow_histories.id สำหรับไฟล์แนบประจำ Step (ADR-021). NULL = ไฟล์แนบหลัก',
ADD CONSTRAINT fk_attachments_workflow_history
FOREIGN KEY (workflow_history_id)
REFERENCES workflow_histories (id)
ON DELETE SET NULL
ON UPDATE CASCADE;
-- Index สำหรับ optimize การดึงไฟล์แนบตาม Step + เรียงตามวันที่
CREATE INDEX idx_att_wfhist_created
ON attachments (workflow_history_id, created_at);
-- ============================================================
-- Rollback:
-- ALTER TABLE attachments DROP FOREIGN KEY fk_attachments_workflow_history;
-- ALTER TABLE attachments DROP COLUMN workflow_history_id;
-- ============================================================
@@ -1,13 +0,0 @@
-- Delta 05: Add deadline_date to circulations (v1.8.7 — EC-CIRC-003)
-- Purpose: เพิ่มคอลัมน์ deadline_date ที่ตาราง circulations
-- เพื่อรองรับ EC-CIRC-003 (Overdue Badge) และการตั้งค่ากำหนดเวลา
-- Applied: เพิ่มทีหลัง Schema v1.8.0
-- Author: NAP-DMS v1.8.7
ALTER TABLE circulations
ADD COLUMN deadline_date DATE NULL
COMMENT 'วันครบกำหนดส่งงาน (nullable — ถ้า NULL = ไม่มีกำหนด)'
AFTER closed_at;
-- Index สำหรับ query Overdue Circulations ที่ Dashboard
CREATE INDEX idx_circulations_deadline ON circulations (deadline_date);
@@ -1,87 +0,0 @@
-- ==========================================================
-- Delta 06: Add CIRCULATION_FLOW_V1 Workflow Definition
-- ==========================================================
-- Purpose : สร้าง Workflow Definition สำหรับใบเวียนภายใน (Circulation)
-- Required : ก่อน delta นี้ ตาราง workflow_definitions ต้องมีอยู่แล้ว (ADR-001)
-- Renamed : CIRCULATION_INTERNAL_V1 → CIRCULATION_FLOW_V1 (ตาม naming convention)
-- States :
-- DRAFT (initial) → START → ROUTING
-- ROUTING → COMPLETE → COMPLETED (terminal)
-- ROUTING → FORCE_CLOSE → CANCELLED (terminal)
-- ==========================================================
INSERT INTO `workflow_definitions` (
`id`,
`workflow_code`,
`version`,
`description`,
`dsl`,
`compiled`,
`is_active`,
`created_at`,
`updated_at`
)
VALUES (
UUID(),
'CIRCULATION_FLOW_V1',
1,
'Circulation Workflow — DRAFT → ROUTING → COMPLETED | CANCELLED',
JSON_OBJECT(
'workflow', 'CIRCULATION_FLOW_V1',
'version', 1,
'states', JSON_ARRAY(
JSON_OBJECT(
'name', 'DRAFT',
'initial', TRUE,
'on', JSON_OBJECT(
'START', JSON_OBJECT('to', 'ROUTING')
)
),
JSON_OBJECT(
'name', 'ROUTING',
'on', JSON_OBJECT(
'COMPLETE', JSON_OBJECT('to', 'COMPLETED'),
'FORCE_CLOSE', JSON_OBJECT('to', 'CANCELLED')
)
),
JSON_OBJECT('name', 'COMPLETED', 'terminal', TRUE),
JSON_OBJECT('name', 'CANCELLED', 'terminal', TRUE)
)
),
JSON_OBJECT(
'initialState', 'DRAFT',
'states', JSON_OBJECT(
'DRAFT', JSON_OBJECT(
'initial', TRUE,
'terminal', FALSE,
'transitions', JSON_OBJECT(
'START', JSON_OBJECT('to', 'ROUTING', 'events', JSON_ARRAY())
)
),
'ROUTING', JSON_OBJECT(
'initial', FALSE,
'terminal', FALSE,
'transitions', JSON_OBJECT(
'COMPLETE', JSON_OBJECT('to', 'COMPLETED', 'events', JSON_ARRAY()),
'FORCE_CLOSE', JSON_OBJECT('to', 'CANCELLED', 'events', JSON_ARRAY())
)
),
'COMPLETED', JSON_OBJECT(
'initial', FALSE,
'terminal', TRUE,
'transitions', JSON_OBJECT()
),
'CANCELLED', JSON_OBJECT(
'initial', FALSE,
'terminal', TRUE,
'transitions', JSON_OBJECT()
)
)
),
TRUE,
NOW(),
NOW()
);
-- Verify
-- SELECT workflow_code, version, is_active FROM workflow_definitions WHERE workflow_code = 'CIRCULATION_FLOW_V1';
@@ -1,22 +0,0 @@
-- ==========================================================
-- Delta 07: Add contract_id to workflow_instances (C3 Contract Scoping)
-- ==========================================================
-- Purpose : เพิ่ม contract_id FK ใน workflow_instances เพื่อให้ Workflow Engine
-- แยก scope ตาม Contract ได้ (C3 ตาม analysis ADR-021)
-- Why : RFA / Correspondence / Transmittal → contract-scoped (contractId ถูกตั้ง)
-- Circulation → organization-scoped (contractId = NULL, ใช้ Level 2 org check)
-- Guard Level 2.5 ตรวจ contract_organizations membership เฉพาะ contractId ≠ NULL
-- ADR : ADR-009 (no migrations — direct SQL only)
-- ==========================================================
ALTER TABLE workflow_instances
ADD COLUMN contract_id INT NULL COMMENT 'Contract ที่ Workflow นี้เป็นส่วนหนึ่ง (NULL = global/legacy)'
AFTER definition_id,
ADD CONSTRAINT fk_wf_inst_contract FOREIGN KEY (contract_id) REFERENCES contracts (id) ON DELETE
SET NULL;
-- Index สำหรับ Guard lookup: "Instances ทั้งหมดในสัญญา X"
CREATE INDEX idx_wf_inst_contract ON workflow_instances (contract_id, entity_type, STATUS);
-- Verify
-- DESCRIBE workflow_instances;
-- SHOW INDEX FROM workflow_instances WHERE Key_name = 'idx_wf_inst_contract';
@@ -1,11 +0,0 @@
-- Delta 08: ADR-022 RAG — เพิ่ม rag_status และ rag_last_error ในตาราง attachments
-- Apply: 2026-04-19
-- Ref: specs/08-Tasks/ADR-022-Retrieval-Augmented-Generation/data-model.md §1.1
ALTER TABLE attachments
ADD COLUMN rag_status ENUM('PENDING', 'PROCESSING', 'INDEXED', 'FAILED')
NOT NULL DEFAULT 'PENDING'
COMMENT 'สถานะ RAG ingestion ระดับ file',
ADD COLUMN rag_last_error TEXT NULL
COMMENT 'Error message ล่าสุดเมื่อ rag_status = FAILED',
ADD INDEX idx_attachments_rag_status (rag_status);
@@ -1,25 +0,0 @@
-- Delta 08b: ADR-022 RAG — สร้างตาราง document_chunks สำหรับเก็บ vector metadata
-- Apply: 2026-04-19
-- Ref: specs/08-Tasks/ADR-022-Retrieval-Augmented-Generation/data-model.md §1.2
CREATE TABLE document_chunks (
id CHAR(36) NOT NULL PRIMARY KEY COMMENT 'UUID = Qdrant point ID',
document_id CHAR(36) NOT NULL COMMENT 'FK → attachments.public_id (UUIDv7)',
chunk_index INT NOT NULL COMMENT 'ลำดับ chunk ภายใน document',
content TEXT NOT NULL COMMENT 'เนื้อหา chunk หลัง PyThaiNLP normalize',
doc_type VARCHAR(20) NOT NULL COMMENT 'CORR, RFA, DRAWING, CONTRACT, RPT, TRANS',
doc_number VARCHAR(100) NULL COMMENT 'หมายเลขเอกสาร เช่น REF-2026-001',
revision VARCHAR(20) NULL COMMENT 'Revision เช่น Rev.A',
project_code VARCHAR(50) NOT NULL COMMENT 'รหัสโครงการ (ใช้ filter)',
project_public_id CHAR(36) NOT NULL COMMENT 'UUIDv7 ของโครงการ (Qdrant tenant key)',
version VARCHAR(20) NULL COMMENT 'เวอร์ชันเอกสาร เช่น 1.0, 2.1 (ถ้ามี)',
classification ENUM('PUBLIC', 'INTERNAL', 'CONFIDENTIAL')
NOT NULL DEFAULT 'INTERNAL',
embedding_model VARCHAR(100) NOT NULL DEFAULT 'nomic-embed-text',
created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
INDEX idx_chunks_document_id (document_id),
INDEX idx_chunks_doc_number_rev (doc_number, revision),
INDEX idx_chunks_project (project_public_id),
FULLTEXT INDEX ft_chunks_content (content)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
@@ -1,8 +0,0 @@
-- Delta 08c: ADR-022 RAG — เพิ่ม permissions สำหรับ RAG feature
-- Apply: 2026-04-19
-- Ref: specs/08-Tasks/ADR-022-Retrieval-Augmented-Generation/tasks.md T014
INSERT IGNORE INTO permissions (permission_name, description, module, created_at, updated_at)
VALUES
('rag.query', 'ใช้งาน RAG Q&A เพื่อค้นหาคำตอบจากเอกสาร', 'rag', NOW(), NOW()),
('rag.manage', 'จัดการ RAG ingestion, re-index, ลบ vectors', 'rag', NOW(), NOW());
@@ -1,17 +0,0 @@
-- ============================================================
-- Delta 09: ADR-001 v1.1 — Optimistic Lock for Workflow Transitions
-- เพิ่ม version_no ใน workflow_instances สำหรับ Optimistic Concurrency Control
-- ============================================================
-- Feature: 003-unified-workflow-engine (FR-002)
-- Date: 2026-05-03
-- ข้อควรระวัง: Existing rows จะได้ค่า DEFAULT 1 อัตโนมัติ — ไม่มี Data Loss
-- Rollback: ALTER TABLE workflow_instances DROP INDEX idx_wf_inst_version;
-- ALTER TABLE workflow_instances DROP COLUMN version_no;
ALTER TABLE workflow_instances
ADD COLUMN version_no INT NOT NULL DEFAULT 1
COMMENT 'Optimistic lock counter — incremented on every successful transition (ADR-001 v1.1 FR-002). Client sends current value; server rejects with 409 if mismatch.';
-- Index เพื่อรองรับ CAS check: WHERE id = ? AND version_no = ?
CREATE INDEX idx_wf_inst_version
ON workflow_instances (id, version_no);
@@ -1,14 +0,0 @@
-- ============================================================
-- Delta 10: ADR-001 v1.1 / ADR-019 UUID Compliance
-- เพิ่ม action_by_user_uuid ใน workflow_histories
-- เพื่อ expose User identity ผ่าน API โดยไม่ต้องเปิดเผย INT PK (ADR-019)
-- ============================================================
-- Feature: 003-unified-workflow-engine (FR-003)
-- Date: 2026-05-03
-- ข้อควรระวัง: NULL สำหรับ Historical records ที่สร้างก่อน delta นี้ — เป็น Acceptable
-- NULL ในบริบทนี้ = "System Action" หรือ "Pre-migration record"
-- Rollback: ALTER TABLE workflow_histories DROP COLUMN action_by_user_uuid;
ALTER TABLE workflow_histories
ADD COLUMN action_by_user_uuid VARCHAR(36) NULL
COMMENT 'UUID ของ User ผู้ดำเนินการ — ใช้ใน API Response แทน INT FK (ADR-019). NULL = System Action หรือ Pre-migration record';
@@ -1,25 +0,0 @@
-- =============================================================================
-- 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
-- =============================================================================
@@ -1,88 +0,0 @@
-- File: specs/03-Data-and-Storage/deltas/12-unified-ai-architecture.sql
-- Change Log
-- - 2026-05-14: เพิ่ม schema delta สำหรับ ADR-023 Unified AI Architecture.
-- ADR-009: ใช้ SQL delta โดยตรง ห้ามใช้ TypeORM migration
SET NAMES utf8mb4;
CREATE TABLE IF NOT EXISTS migration_review_queue (
id INT AUTO_INCREMENT PRIMARY KEY COMMENT 'Internal PK (ห้าม expose ใน API)',
uuid UUID NOT NULL DEFAULT UUID() COMMENT 'UUID Public Identifier (ADR-019)',
batch_id VARCHAR(100) NOT NULL COMMENT 'Batch ingestion identifier',
original_file_name VARCHAR(255) NOT NULL COMMENT 'Original uploaded legacy filename',
source_attachment_public_id UUID NULL COMMENT 'Temp attachment publicId from two-phase upload',
temp_attachment_id INT NULL COMMENT 'Internal temp attachment id used during commit only',
extracted_metadata JSON NULL COMMENT 'AI extracted metadata before human validation',
confidence_score DECIMAL(4, 3) NULL COMMENT 'Overall AI confidence score 0.000-1.000',
status ENUM('PENDING', 'IMPORTED', 'REJECTED') NOT NULL DEFAULT 'PENDING',
error_reason TEXT NULL COMMENT 'Reason when AI processing rejected the record',
version INT NOT NULL DEFAULT 1 COMMENT 'Optimistic locking version',
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY idx_migration_review_uuid (uuid),
KEY idx_migration_review_batch (batch_id),
KEY idx_migration_review_status (status)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ADR-023 AI migration review staging queue';
ALTER TABLE migration_review_queue
ADD COLUMN IF NOT EXISTS uuid UUID NOT NULL DEFAULT UUID() COMMENT 'UUID Public Identifier (ADR-019)',
ADD COLUMN IF NOT EXISTS batch_id VARCHAR(100) NULL COMMENT 'Batch ingestion identifier',
ADD COLUMN IF NOT EXISTS original_file_name VARCHAR(255) NULL COMMENT 'Original uploaded legacy filename',
ADD COLUMN IF NOT EXISTS source_attachment_public_id UUID NULL COMMENT 'Temp attachment publicId from two-phase upload',
ADD COLUMN IF NOT EXISTS temp_attachment_id INT NULL COMMENT 'Internal temp attachment id used during commit only',
ADD COLUMN IF NOT EXISTS extracted_metadata JSON NULL COMMENT 'AI extracted metadata before human validation',
ADD COLUMN IF NOT EXISTS confidence_score DECIMAL(4, 3) NULL COMMENT 'Overall AI confidence score 0.000-1.000',
ADD COLUMN IF NOT EXISTS error_reason TEXT NULL COMMENT 'Reason when AI processing rejected the record',
ADD COLUMN IF NOT EXISTS version INT NOT NULL DEFAULT 1 COMMENT 'Optimistic locking version',
ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
ALTER TABLE migration_review_queue
MODIFY COLUMN status ENUM('PENDING', 'APPROVED', 'IMPORTED', 'REJECTED') NOT NULL DEFAULT 'PENDING';
CREATE UNIQUE INDEX IF NOT EXISTS idx_migration_review_uuid ON migration_review_queue (uuid);
CREATE INDEX IF NOT EXISTS idx_migration_review_batch ON migration_review_queue (batch_id);
CREATE INDEX IF NOT EXISTS idx_migration_review_status ON migration_review_queue (status);
CREATE TABLE IF NOT EXISTS 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 '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';
ALTER TABLE ai_audit_logs
ADD COLUMN IF NOT EXISTS ai_model VARCHAR(50) NOT NULL DEFAULT 'gemma4' COMMENT 'Legacy AI model column used by current gateway service',
ADD COLUMN IF NOT EXISTS model_name VARCHAR(100) NULL COMMENT 'Local model name used by ADR-023 AI pipeline',
ADD COLUMN IF NOT EXISTS ai_suggestion_json JSON NULL COMMENT 'AI suggested metadata',
ADD COLUMN IF NOT EXISTS human_override_json JSON NULL COMMENT 'Human approved or overridden metadata',
ADD COLUMN IF NOT EXISTS processing_time_ms INT NULL COMMENT 'Legacy processing duration field',
ADD COLUMN IF NOT EXISTS input_hash VARCHAR(64) NULL COMMENT 'Legacy SHA-256 input hash',
ADD COLUMN IF NOT EXISTS output_hash VARCHAR(64) NULL COMMENT 'Legacy SHA-256 output hash',
ADD COLUMN IF NOT EXISTS status ENUM('SUCCESS', 'FAILED', 'TIMEOUT') NOT NULL DEFAULT 'SUCCESS' COMMENT 'Legacy processing status field',
ADD COLUMN IF NOT EXISTS error_message TEXT NULL COMMENT 'Legacy processing error field',
ADD COLUMN IF NOT EXISTS confirmed_by_user_id INT NULL COMMENT 'Internal users.user_id that confirmed the record';
UPDATE ai_audit_logs
SET model_name = ai_model
WHERE model_name IS NULL AND ai_model IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_ai_audit_model_name ON ai_audit_logs (model_name);
CREATE INDEX IF NOT EXISTS idx_ai_audit_confirmed_by ON ai_audit_logs (confirmed_by_user_id);
@@ -1,56 +0,0 @@
-- 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);
@@ -1,14 +0,0 @@
-- 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);
@@ -1,60 +0,0 @@
-- Delta 16: Add Intent Classification Tables (ADR-024)
-- Feature: 224-intent-classification
-- Created: 2026-05-19
-- เพิ่มตาราง ai_intent_definitions และ ai_intent_patterns สำหรับ Hybrid Intent Classifier
-- Intent Definitions Table
CREATE TABLE IF NOT EXISTS ai_intent_definitions (
id INT AUTO_INCREMENT PRIMARY KEY,
public_id UUID NOT NULL DEFAULT UUID(),
intent_code VARCHAR(50) NOT NULL,
description_th VARCHAR(255) NOT NULL,
description_en VARCHAR(255) NOT NULL,
category ENUM('read', 'suggest', 'utility') NOT NULL,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_intent_public_id (public_id),
UNIQUE KEY uk_intent_code (intent_code),
INDEX idx_intent_active (is_active, category)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Intent Patterns Table
CREATE TABLE IF NOT EXISTS ai_intent_patterns (
id INT AUTO_INCREMENT PRIMARY KEY,
public_id UUID NOT NULL DEFAULT UUID(),
intent_code VARCHAR(50) NOT NULL,
language ENUM('th', 'en', 'any') NOT NULL DEFAULT 'any',
pattern_type ENUM('keyword', 'regex') NOT NULL DEFAULT 'keyword',
pattern_value VARCHAR(255) NOT NULL,
priority INT NOT NULL DEFAULT 100,
is_active BOOLEAN NOT NULL DEFAULT TRUE,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_pattern_public_id (public_id),
INDEX idx_pattern_intent_code (intent_code),
INDEX idx_pattern_active_priority (is_active, priority ASC),
CONSTRAINT fk_intent_pattern_definition
FOREIGN KEY (intent_code) REFERENCES ai_intent_definitions(intent_code)
ON UPDATE CASCADE ON DELETE RESTRICT
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- Seed Intent Definitions (v1) — 12 รายการตาม ADR-024
INSERT IGNORE INTO ai_intent_definitions (intent_code, description_th, description_en, category) VALUES
-- Read Intents
('RAG_QUERY', 'ถามคำถามธรรมชาติ ตอบจาก vector + doc context', 'Natural language query from vector DB + document context', 'read'),
('GET_RFA', 'ดึง RFA ตาม filter', 'Get RFA by filters', 'read'),
('GET_DRAWING', 'ดึง Drawing revision', 'Get Drawing revision', 'read'),
('GET_TRANSMITTAL', 'ดึง Transmittal', 'Get Transmittal', 'read'),
('GET_CORRESPONDENCE','ดึง Correspondence ทั่วไป', 'Get Correspondence', 'read'),
('GET_CIRCULATION', 'ดึง Circulation', 'Get Circulation', 'read'),
('GET_RFA_DRAWINGS', 'ดึง Drawings ที่ผูกกับ RFA', 'Get Drawings linked to RFA', 'read'),
('SUMMARIZE_DOCUMENT','สรุปเอกสารที่เปิดอยู่', 'Summarize current document', 'read'),
('LIST_OVERDUE', 'รายการ cross-entity ที่เกินกำหนด', 'List overdue items across entities', 'read'),
-- Suggest Intents
('SUGGEST_METADATA', 'แนะนำ metadata สำหรับเอกสารที่อัปโหลด', 'Suggest metadata for uploaded document', 'suggest'),
('SUGGEST_ACTION', 'แจ้งเตือนว่าควรทำอะไรต่อ', 'Suggest next actions', 'suggest'),
-- Utility Intents
('FALLBACK', 'ไม่เข้า intent ไหน / ไม่เกี่ยวกับระบบ', 'No matching intent / unrelated to system', 'utility');
@@ -1,89 +0,0 @@
-- Delta 17: Seed Intent Patterns (v1) for ADR-024 Intent Classification
-- Feature: 224-intent-classification
-- Created: 2026-05-19
-- เพิ่ม patterns เริ่มต้นสำหรับ 12 Intent Definitions (keyword + regex)
-- RAG_QUERY patterns
INSERT IGNORE INTO ai_intent_patterns (intent_code, language, pattern_type, pattern_value, priority) VALUES
('RAG_QUERY', 'th', 'keyword', 'ค้นหา', 10),
('RAG_QUERY', 'th', 'keyword', 'หาข้อมูล', 10),
('RAG_QUERY', 'en', 'keyword', 'search', 10),
('RAG_QUERY', 'en', 'keyword', 'find', 10),
('RAG_QUERY', 'any', 'regex', '(?i)(what|where|who|when|how|why).*\\?', 50);
-- GET_RFA patterns
INSERT IGNORE INTO ai_intent_patterns (intent_code, language, pattern_type, pattern_value, priority) VALUES
('GET_RFA', 'th', 'keyword', 'rfa', 10),
('GET_RFA', 'th', 'keyword', 'อาร์เอฟเอ', 10),
('GET_RFA', 'en', 'keyword', 'request for approval', 15),
('GET_RFA', 'any', 'regex', '(?i)rfa[- ]?\\d+', 5);
-- GET_DRAWING patterns
INSERT IGNORE INTO ai_intent_patterns (intent_code, language, pattern_type, pattern_value, priority) VALUES
('GET_DRAWING', 'th', 'keyword', 'แบบ', 20),
('GET_DRAWING', 'th', 'keyword', 'drawing', 10),
('GET_DRAWING', 'en', 'keyword', 'drawing', 10),
('GET_DRAWING', 'en', 'keyword', 'revision', 20),
('GET_DRAWING', 'any', 'regex', '(?i)(shop.?draw|dwg|rev\\.?\\s*\\d)', 5);
-- GET_TRANSMITTAL patterns
INSERT IGNORE INTO ai_intent_patterns (intent_code, language, pattern_type, pattern_value, priority) VALUES
('GET_TRANSMITTAL', 'th', 'keyword', 'transmittal', 10),
('GET_TRANSMITTAL', 'th', 'keyword', 'ทรานส์มิตทอล', 10),
('GET_TRANSMITTAL', 'en', 'keyword', 'transmittal', 10),
('GET_TRANSMITTAL', 'any', 'regex', '(?i)tr[- ]?\\d+', 5);
-- GET_CORRESPONDENCE patterns
INSERT IGNORE INTO ai_intent_patterns (intent_code, language, pattern_type, pattern_value, priority) VALUES
('GET_CORRESPONDENCE', 'th', 'keyword', 'จดหมาย', 10),
('GET_CORRESPONDENCE', 'th', 'keyword', 'หนังสือ', 15),
('GET_CORRESPONDENCE', 'en', 'keyword', 'correspondence', 10),
('GET_CORRESPONDENCE', 'en', 'keyword', 'letter', 15);
-- GET_CIRCULATION patterns
INSERT IGNORE INTO ai_intent_patterns (intent_code, language, pattern_type, pattern_value, priority) VALUES
('GET_CIRCULATION', 'th', 'keyword', 'เวียน', 10),
('GET_CIRCULATION', 'th', 'keyword', 'circulation', 10),
('GET_CIRCULATION', 'en', 'keyword', 'circulation', 10),
('GET_CIRCULATION', 'en', 'keyword', 'distribute', 15);
-- GET_RFA_DRAWINGS patterns
INSERT IGNORE INTO ai_intent_patterns (intent_code, language, pattern_type, pattern_value, priority) VALUES
('GET_RFA_DRAWINGS', 'th', 'keyword', 'แบบใน rfa', 5),
('GET_RFA_DRAWINGS', 'en', 'keyword', 'drawings in rfa', 5),
('GET_RFA_DRAWINGS', 'any', 'regex', '(?i)(draw|แบบ).*(rfa|อาร์เอฟเอ)', 5);
-- SUMMARIZE_DOCUMENT patterns
INSERT IGNORE INTO ai_intent_patterns (intent_code, language, pattern_type, pattern_value, priority) VALUES
('SUMMARIZE_DOCUMENT', 'th', 'keyword', 'สรุป', 10),
('SUMMARIZE_DOCUMENT', 'th', 'keyword', 'สรุปเอกสาร', 5),
('SUMMARIZE_DOCUMENT', 'en', 'keyword', 'summarize', 10),
('SUMMARIZE_DOCUMENT', 'en', 'keyword', 'summary', 10),
('SUMMARIZE_DOCUMENT', 'any', 'regex', '(?i)(สรุป|summar|tldr|tl;dr)', 5);
-- LIST_OVERDUE patterns
INSERT IGNORE INTO ai_intent_patterns (intent_code, language, pattern_type, pattern_value, priority) VALUES
('LIST_OVERDUE', 'th', 'keyword', 'เกินกำหนด', 10),
('LIST_OVERDUE', 'th', 'keyword', 'ค้าง', 15),
('LIST_OVERDUE', 'th', 'keyword', 'overdue', 10),
('LIST_OVERDUE', 'en', 'keyword', 'overdue', 10),
('LIST_OVERDUE', 'en', 'keyword', 'late', 20),
('LIST_OVERDUE', 'any', 'regex', '(?i)(overdue|เกินกำหนด|ล่าช้า)', 5);
-- SUGGEST_METADATA patterns
INSERT IGNORE INTO ai_intent_patterns (intent_code, language, pattern_type, pattern_value, priority) VALUES
('SUGGEST_METADATA', 'th', 'keyword', 'แนะนำ metadata', 5),
('SUGGEST_METADATA', 'th', 'keyword', 'แท็ก', 15),
('SUGGEST_METADATA', 'en', 'keyword', 'suggest metadata', 5),
('SUGGEST_METADATA', 'en', 'keyword', 'tag', 15),
('SUGGEST_METADATA', 'any', 'regex', '(?i)(suggest|แนะนำ).*(tag|meta|ประเภท)', 5);
-- SUGGEST_ACTION patterns
INSERT IGNORE INTO ai_intent_patterns (intent_code, language, pattern_type, pattern_value, priority) VALUES
('SUGGEST_ACTION', 'th', 'keyword', 'ทำอะไรต่อ', 10),
('SUGGEST_ACTION', 'th', 'keyword', 'แนะนำ', 20),
('SUGGEST_ACTION', 'en', 'keyword', 'what should i do', 10),
('SUGGEST_ACTION', 'en', 'keyword', 'next step', 10),
('SUGGEST_ACTION', 'any', 'regex', '(?i)(next.?step|ทำอะไร|ควรทำ|what.*do)', 10);
-- FALLBACK: ไม่ต้อง seed pattern — ใช้เป็น default เมื่อไม่ match อะไรเลย