167 KiB
title, version, status, owner, last_updated, related
| title | version | status | owner | last_updated | related | ||
|---|---|---|---|---|---|---|---|
| Data & Storage: Data Dictionary and Data Model Architecture | 1.9.2 | released | Nattanin Peancharoen | 2026-06-19 |
|
1. Data Model Architecture Overview
📋 1.1 Overview
เอกสารนี้อธิบายสถาปัตยกรรมของ Data Model สำหรับระบบ LCBP3-DMS โดยครอบคลุมโครงสร้างฐานข้อมูล, ความสัมพันธ์ระหว่างตาราง, และหลักการออกแบบที่สำคัญ
🎯 1.2 Design Principles
1. Separation of Concerns
- Master-Revision Pattern: แยกข้อมูลที่ไม่เปลี่ยนแปลง (Master) จากข้อมูลที่มีการแก้ไข (Revisions)
correspondences(Master) ↔correspondence_revisions(Revisions)rfas(Master) ↔rfa_revisions(Revisions)shop_drawings(Master) ↔shop_drawing_revisions(Revisions)
2. Data Integrity
- Foreign Key Constraints: ใช้ FK ทุกความสัมพันธ์เพื่อรักษาความสมบูรณ์ของข้อมูล
- Soft Delete: ใช้
deleted_atแทนการลบข้อมูลจริง เพื่อรักษาประวัติ - Optimistic Locking: ใช้
versioncolumn ในdocument_number_countersป้องกัน Race Condition
3. Flexibility & Extensibility
- JSON Details Field: เก็บข้อมูลเฉพาะประเภทใน
correspondence_revisions.details - Virtual Columns: สร้าง Index จาก JSON fields สำหรับ Performance
- Master Data Tables: แยกข้อมูล Master (Types, Status, Codes) เพื่อความยืดหยุ่น
4. Security & Audit
- RBAC (Role-Based Access Control): ระบบสิทธิ์แบบ Hierarchical Scope
- Audit Trail: บันทึกผู้สร้าง/แก้ไข และเวลาในทุกตาราง
- Two-Phase File Upload: ป้องกันไฟล์ขยะด้วย Temporary Storage
2. Database Schema Overview (ERD)
Entity Relationship Diagram
erDiagram
%% Core Entities
organizations ||--o{ users : "employs"
projects ||--o{ contracts : "contains"
projects ||--o{ correspondences : "manages"
%% RBAC
users ||--o{ user_assignments : "has"
roles ||--o{ user_assignments : "assigned_to"
roles ||--o{ role_permissions : "has"
permissions ||--o{ role_permissions : "granted_by"
%% Correspondences
correspondences ||--o{ correspondence_revisions : "has_revisions"
correspondence_types ||--o{ correspondences : "categorizes"
correspondence_status ||--o{ correspondence_revisions : "defines_state"
disciplines ||--o{ correspondences : "classifies"
%% RFAs
rfas ||--o{ rfa_revisions : "has_revisions"
rfa_types ||--o{ rfas : "categorizes"
rfa_status_codes ||--o{ rfa_revisions : "defines_state"
rfa_approve_codes ||--o{ rfa_revisions : "defines_result"
disciplines ||--o{ rfas : "classifies"
%% Drawings
shop_drawings ||--o{ shop_drawing_revisions : "has_revisions"
shop_drawing_main_categories ||--o{ shop_drawings : "categorizes"
shop_drawing_sub_categories ||--o{ shop_drawings : "sub_categorizes"
%% Attachments
attachments ||--o{ correspondence_revision_attachments : "attached_to"
correspondence_revisions ||--o{ correspondence_revision_attachments : "has"
3. Data Dictionary V1.9.0
หมายเหตุ: PK = Primary Key, FK = Foreign Key, AI = AUTO_INCREMENT. รูปแบบ Soft Delete จะปรากฏ Column
deleted_at DATETIME NULLเป็นมาตรฐาน
1. 🏢 Core & Master Data Tables (องค์กร, โครงการ, สัญญา)
1.1 organization_roles
-
- Purpose **: MASTER TABLE FOR organization role TYPES IN the system | COLUMN Name | Data TYPE | Constraints | Description | | ----------- | ----------- | --------------------------- | ---------------------------------------------------------------- | | id | INT | PRIMARY KEY, AUTO_INCREMENT | UNIQUE identifier FOR organization role | | role_name | VARCHAR(20) | NOT NULL, UNIQUE | Role name ( CONTRACTOR, THIRD PARTY ) | | 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 (id) - UNIQUE (role_name) ** Business Rules **: - Predefined system roles FOR organization TYPES - Cannot be deleted IF referenced by organizations ---
1.2 organizations
-
-
Purpose **: MASTER TABLE storing ALL organizations involved IN the system | COLUMN Name | Data TYPE | Constraints | Description | | ----------------- | ------------ | ----------------------------------- | ---------------------------------------- | | id | INT | PRIMARY KEY, AUTO_INCREMENT | UNIQUE identifier FOR organization | | uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID() | UUID Public Identifier (ADR-019) | | organization_code | VARCHAR(20) | NOT NULL, UNIQUE | Organization code (e.g., 'กทท.', 'TEAM') | | organization_name | VARCHAR(255) | NOT NULL | FULL organization name | | is_active | BOOLEAN | DEFAULT TRUE | Active STATUS (1 = active, 0 = inactive) | | 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 (id) - UNIQUE (organization_code) - INDEX (is_active) ** Relationships **: - Referenced by: users, project_organizations, contract_organizations, correspondences, circulations ---
1.3 projects
-
- Purpose **: MASTER TABLE FOR ALL projects IN the system | COLUMN Name | Data TYPE | Constraints | Description | | ------------ | ------------ | --------------------------- | ----------------------------- | | id | INT | PRIMARY KEY, AUTO_INCREMENT | UNIQUE identifier FOR project | | uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID() | UUID Public Identifier (ADR-019) | | project_code | VARCHAR(50) | NOT NULL, UNIQUE | Project code (e.g., 'LCBP3') | | project_name | VARCHAR(255) | NOT NULL | FULL project name | | is_active | TINYINT(1) | DEFAULT 1 | Active STATUS | | 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 (id) - UNIQUE (project_code) - INDEX (is_active) ** Relationships **: - Referenced by: contracts, correspondences, document_number_formats, drawings ---
1.4 contracts
-
- Purpose **: MASTER TABLE FOR contracts within projects | COLUMN Name | Data TYPE | Constraints | Description | | ------------- | ------------ | ----------------------------------- | ------------------------------ | | id | INT | PRIMARY KEY, AUTO_INCREMENT | UNIQUE identifier FOR contract | | uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID() | UUID Public Identifier (ADR-019) | | project_id | INT | NOT NULL, FK | Reference TO projects TABLE | | contract_code | VARCHAR(50) | NOT NULL, UNIQUE | Contract code | | contract_name | VARCHAR(255) | NOT NULL | FULL contract name | | description | TEXT | NULL | Contract description | | start_date | DATE | NULL | Contract START date | | end_date | DATE | NULL | Contract END date | | is_active | BOOLEAN | DEFAULT TRUE | Active STATUS | | 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 (id) - UNIQUE (contract_code) - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE - INDEX (project_id, is_active) ** Relationships **: - Parent: projects - Referenced by: contract_organizations, user_assignments ---
1.5 disciplines (NEW v1.5.1)
-
- Purpose **: เก็บข้อมูลสาขางาน (Disciplines) แยกตามสัญญา (Req 6B) | COLUMN Name | Data TYPE | Constraints | Description | |: -------------- | :----------- | :----------- | :--------------------- | | id | INT | PK, AI | UNIQUE identifier | | contract_id | INT | FK, NOT NULL | ผูกกับสัญญา | | discipline_code | VARCHAR(10) | NOT NULL | รหัสสาขา (เช่น GEN, STR) | | code_name_th | VARCHAR(255) | NULL | ชื่อไทย | | code_name_en | VARCHAR(255) | NULL | ชื่ออังกฤษ | | is_active | TINYINT(1) | DEFAULT 1 | สถานะการใช้งาน | ** INDEXES **: - UNIQUE (contract_id, discipline_code) ---
1.6 system_settings (NEW v1.9.0)
-
- Purpose **: ตารางเก็บข้อมูลการตั้งค่าระบบแบบไดนามิก (เช่น สถานะการเปิด/ปิด AI features ทั่วทั้งระบบ) | COLUMN Name | Data TYPE | Constraints | Description | |: -------------- | :----------- | :----------- | :--------------------- |
| id | INT | PK, AI | ID ของตาราง | | setting_key | VARCHAR(100) | NOT NULL, UNIQUE | คีย์การตั้งค่าระบบ (เช่น 'AI_FEATURES_ENABLED') | | setting_value | TEXT | NOT NULL | ค่าที่บันทึก (stringified) | | data_type | ENUM | NOT NULL, DEFAULT 'string' | ประเภทข้อมูลสำหรับ validation ('string', 'number', 'boolean', 'json') | | category | VARCHAR(50) | NULL | หมวดหมู่การตั้งค่า (เช่น 'ai') | | is_encrypted | TINYINT(1) | DEFAULT 0 | เข้ารหัสค่า sensitive (1 = เข้ารหัส, 0 = ไม่เข้ารหัส) | | validation_rules | JSON | NULL | กฎ validation (min, max, allowed_values) | | description | TEXT | NULL | คำอธิบายข้อมูลการตั้งค่า | | is_public | TINYINT(1) | DEFAULT 0 | เผยแพร่ให้ frontend อ่านได้หรือ admin เท่านั้น (1 = เผยแพร่, 0 = แอดมินเท่านั้น) | | updated_by | INT | FK, NULL | ผู้แก้ไขล่าสุด | | created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | เวลาสร้างเรคคอร์ด | | updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | เวลาอัปเดตเรคคอร์ดล่าสุด | ** INDEXES **: - PRIMARY KEY (id) - UNIQUE (setting_key) - FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL - INDEX idx_system_settings_category (category) - INDEX idx_system_settings_is_public (is_public) ** Business Rules **: - ใช้เก็บค่าการตั้งค่าระดับระบบที่ต้องการปรับเปลี่ยนแบบไดนามิกผ่านหน้า Admin Console - คีย์
AI_FEATURES_ENABLEDควบคุมสถานะการทำงานของฟีเจอร์ AI สำหรับผู้ใช้ทั่วไป ---
- Purpose **: ตารางเก็บข้อมูลการตั้งค่าระบบแบบไดนามิก (เช่น สถานะการเปิด/ปิด AI features ทั่วทั้งระบบ) | COLUMN Name | Data TYPE | Constraints | Description | |: -------------- | :----------- | :----------- | :--------------------- |
| id | INT | PK, AI | ID ของตาราง | | setting_key | VARCHAR(100) | NOT NULL, UNIQUE | คีย์การตั้งค่าระบบ (เช่น 'AI_FEATURES_ENABLED') | | setting_value | TEXT | NOT NULL | ค่าที่บันทึก (stringified) | | data_type | ENUM | NOT NULL, DEFAULT 'string' | ประเภทข้อมูลสำหรับ validation ('string', 'number', 'boolean', 'json') | | category | VARCHAR(50) | NULL | หมวดหมู่การตั้งค่า (เช่น 'ai') | | is_encrypted | TINYINT(1) | DEFAULT 0 | เข้ารหัสค่า sensitive (1 = เข้ารหัส, 0 = ไม่เข้ารหัส) | | validation_rules | JSON | NULL | กฎ validation (min, max, allowed_values) | | description | TEXT | NULL | คำอธิบายข้อมูลการตั้งค่า | | is_public | TINYINT(1) | DEFAULT 0 | เผยแพร่ให้ frontend อ่านได้หรือ admin เท่านั้น (1 = เผยแพร่, 0 = แอดมินเท่านั้น) | | updated_by | INT | FK, NULL | ผู้แก้ไขล่าสุด | | created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | เวลาสร้างเรคคอร์ด | | updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | เวลาอัปเดตเรคคอร์ดล่าสุด | ** INDEXES **: - PRIMARY KEY (id) - UNIQUE (setting_key) - FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL - INDEX idx_system_settings_category (category) - INDEX idx_system_settings_is_public (is_public) ** Business Rules **: - ใช้เก็บค่าการตั้งค่าระดับระบบที่ต้องการปรับเปลี่ยนแบบไดนามิกผ่านหน้า Admin Console - คีย์
2. 👥 Users & RBAC Tables (ผู้ใช้, สิทธิ์, บทบาท)
2.1 users
-
- Purpose **: MASTER TABLE storing ALL system users | COLUMN Name | Data TYPE | Constraints | Description | | ----------------------- | ------------ | ----------------------------------- | -------------------------------- | | user_id | INT | PRIMARY KEY, AUTO_INCREMENT | UNIQUE identifier FOR user | | uuid | UUID | NOT NULL, UNIQUE, DEFAULT UUID() | UUID Public Identifier (ADR-019) | | username | VARCHAR(50) | NOT NULL, UNIQUE | Login username | | password_hash | VARCHAR(255) | NOT NULL | Hashed PASSWORD (bcrypt) | | first_name | VARCHAR(50) | NULL | User 's first name | | last_name | VARCHAR(50) | NULL | User' s last name | | email | VARCHAR(100) | NOT NULL, UNIQUE | Email address | | line_id | VARCHAR(100) | NULL | LINE messenger ID | | primary_organization_id | INT | NULL, FK | PRIMARY organization affiliation | | is_active | TINYINT(1) | DEFAULT 1 | Active STATUS | | failed_attempts | INT | DEFAULT 0 | Failed login attempts counter | | locked_until | DATETIME | NULL | Account LOCK expiration time | | last_login_at | TIMESTAMP | NULL | Last successful login timestamp | | created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp | | updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last UPDATE timestamp | | deleted_at | DATETIME | NULL | Deleted at | ** INDEXES **: - PRIMARY KEY (user_id) - UNIQUE (username) - UNIQUE (email) - FOREIGN KEY (primary_organization_id) REFERENCES organizations(id) ON DELETE SET NULL - INDEX (is_active) - INDEX (email) ** Relationships **: - Parent: organizations (primary_organization_id) - Referenced by: user_assignments, audit_logs, notifications, circulation_routings ---
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 | | 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) - UNIQUE INDEX idx_roles_uuid (uuid) [delta-11] - INDEX (scope) ** Relationships **: - Referenced by: role_permissions, user_assignments ---
2.3 permissions
-
- Purpose **: MASTER TABLE defining ALL system permissions | COLUMN Name | Data TYPE | Constraints | Description | | --------------- | ------------ | --------------------------- | ------------------------------------------------------ | | permission_id | INT | PRIMARY KEY, AUTO_INCREMENT | UNIQUE identifier FOR permission | | permission_name | VARCHAR(100) | NOT NULL, UNIQUE | Permission code (e.g., 'rfas.create', 'document.view') | | description | TEXT | NULL | Permission description | | module | VARCHAR(50) | NULL | Related module name | | scope_level | ENUM | NULL | Scope: GLOBAL, ORG, PROJECT | | is_active | TINYINT(1) | DEFAULT 1 | Active STATUS | | 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 (permission_id) - UNIQUE (permission_name) - INDEX (module) - INDEX (scope_level) - INDEX (is_active) ** Relationships **: - Referenced by: role_permissions ---
2.4 role_permissions
-
- Purpose **: Junction TABLE mapping roles TO permissions (M :N) | COLUMN Name | Data TYPE | Constraints | Description | | ------------- | --------- | --------------- | ------------------------------ | | role_id | INT | PRIMARY KEY, FK | Reference TO roles TABLE | | permission_id | INT | PRIMARY KEY, FK | Reference TO permissions TABLE | ** INDEXES **: - PRIMARY KEY (role_id, permission_id) - FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE - FOREIGN KEY (permission_id) REFERENCES permissions(permission_id) ON DELETE CASCADE - INDEX (permission_id) ** Relationships **: - Parent: roles, permissions ---
2.5 user_assignments
-
- Purpose **: Junction TABLE assigning users TO roles WITH scope context | COLUMN Name | Data TYPE | Constraints | Description | | ------------------- | --------- | --------------------------- | ---------------------------------- | | id | INT | PRIMARY KEY, AUTO_INCREMENT | UNIQUE identifier | | user_id | INT | NOT NULL, FK | Reference TO users TABLE | | role_id | INT | NOT NULL, FK | Reference TO roles TABLE | | organization_id | INT | NULL, FK | Organization scope (IF applicable) | | project_id | INT | NULL, FK | Project scope (IF applicable) | | contract_id | INT | NULL, FK | Contract scope (IF applicable) | | assigned_by_user_id | INT | NULL, FK | User who made the assignment | | assigned_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Assignment timestamp | ** INDEXES **: - PRIMARY KEY (id) - FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE - FOREIGN KEY (role_id) REFERENCES roles(role_id) ON DELETE CASCADE - FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE - FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE - FOREIGN KEY (assigned_by_user_id) REFERENCES users(user_id) - INDEX (user_id, role_id) - INDEX (organization_id) - INDEX (project_id) - INDEX (contract_id) ** Relationships **: - Parent: users, roles, organizations, projects, contracts ---
2.6 project_organizations
-
- Purpose **: Junction TABLE linking projects TO participating organizations (M :N) | COLUMN Name | Data TYPE | Constraints | Description | | --------------- | --------- | --------------- | -------------------------------- | | project_id | INT | PRIMARY KEY, FK | Reference TO projects TABLE | | organization_id | INT | PRIMARY KEY, FK | Reference TO organizations TABLE | ** INDEXES **: - PRIMARY KEY (project_id, organization_id) - FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE - FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE - INDEX (organization_id) ** Relationships **: - Parent: projects, organizations ---
2.7 contract_organizations
-
- Purpose **: Junction TABLE linking contracts TO participating organizations WITH roles (M :N) | COLUMN Name | Data TYPE | Constraints | Description | | ---------------- | ------------ | --------------- | ------------------------------------------------------------------------- | | contract_id | INT | PRIMARY KEY, FK | Reference TO contracts TABLE | | organization_id | INT | PRIMARY KEY, FK | Reference TO organizations TABLE | | role_in_contract | VARCHAR(100) | NULL | Organization 's role in contract (Owner, Designer, Consultant, Contractor) |
-
-
Indexes:
- PRIMARY KEY (contract_id, organization_id)
- FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE
- FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE
- INDEX (organization_id)
- INDEX (role_in_contract)
Relationships:
- Parent: contracts, organizations
2.8 user_preferences (NEW v1.5.1)
Purpose: เก็บการตั้งค่าส่วนตัวของผู้ใช้ (Req 5.5, 6.8.3)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| user_id | INT | PK, FK | User ID |
| notify_email | BOOLEAN | DEFAULT TRUE | รับอีเมลแจ้งเตือน |
| notify_line | BOOLEAN | DEFAULT TRUE | รับไลน์แจ้งเตือน |
| digest_mode | BOOLEAN | DEFAULT FALSE | รับแจ้งเตือนแบบรวม |
| ui_theme | VARCHAR(20) | DEFAULT ' light ' | UI Theme |
2.9 refresh_tokens (UPDATE v1.8.1)
Purpose: เก็บ Refresh Tokens สำหรับการทำ Authentication และ Token Rotation (รองรับ Grace Period)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| token_id | INT | PK, AI | Unique Token ID |
| user_id | INT | FK, NOT NULL | เจ้าของ Token |
| token_hash | VARCHAR(255) | NOT NULL | Hash ของ Refresh Token (Security) |
| expires_at | DATETIME | NOT NULL | วันหมดอายุของ Token |
| is_revoked | BOOLEAN | DEFAULT FALSE | สถานะถูกยกเลิก (True = ใช้งานไม่ได้) |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | เวลาที่สร้าง |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | เวลาที่แก้ไขล่าสุด (ใช้ตรวจสอบ Grace Period ขณะหมุน Token) |
| replaced_by_token | VARCHAR(255) | NULL | Token ใหม่ที่มาแทนที่ (กรณี Token Rotation) |
Indexes:
- PRIMARY KEY (token_id)
- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
- INDEX (user_id)
Relationships:
- Parent: users
3. ✉️ Correspondences Tables (เอกสารหลัก, Revisions, Workflows)
3.1 correspondence_types
Purpose: Master table for correspondence document types
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier |
| type_code | VARCHAR(50) | NOT NULL, UNIQUE | Type code (e.g., ' RFA ', ' RFI ', ' TRANSMITTAL ') |
| type_name | VARCHAR(255) | NOT NULL | Full type name |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Last update timestamp |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
Indexes:
- PRIMARY KEY (id)
- UNIQUE (type_code)
- INDEX (is_active)
- INDEX (sort_order)
Relationships:
- Referenced by: correspondences, document_number_formats, document_number_counters
3.2 correspondence_sub_types (NEW v1.5.1)
Purpose: เก็บประเภทหนังสือย่อย (Sub Types) สำหรับ Mapping เลขรหัส (Req 6B)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PK, AI | Unique identifier |
| contract_id | INT | FK, NOT NULL | ผูกกับสัญญา |
| correspondence_type_id | INT | FK, NOT NULL | ผูกกับประเภทเอกสารหลัก |
| sub_type_code | VARCHAR(20) | NOT NULL | รหัสย่อย (เช่น MAT, SHP) |
| sub_type_name | VARCHAR(255) | NULL | ชื่อประเภทหนังสือย่อย |
| sub_type_number | VARCHAR(10) | NULL | เลขรหัสสำหรับ Running Number |
3.3 correspondences (UPDATE v1.7.0)
Purpose: Master table for correspondence documents (non-revisioned data)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Master correspondence ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT | UUID Public Identifier (ADR-019) |
| correspondence_number | VARCHAR(100) | NOT NULL | Document number (from numbering system) |
| correspondence_type_id | INT | NOT NULL, FK | Reference to correspondence_types |
| discipline_id | INT | NULL, FK | [NEW] สาขางาน (ถ้ามี) |
| is_internal_communication | TINYINT(1) | DEFAULT 0 | Internal (1) or external (0) communication |
| project_id | INT | NOT NULL, FK | Reference to projects table |
| originator_id | INT | NULL, FK | Originating organization |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| created_by | INT | NULL, FK | User who created the record |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE RESTRICT
- FOREIGN KEY (discipline_id) REFERENCES disciplines(id) ON DELETE SET NULL
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (originator_id) REFERENCES organizations(id) ON DELETE SET NULL
- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL
- UNIQUE KEY (project_id, correspondence_number)
- UNIQUE INDEX idx_correspondences_uuid (uuid)
- INDEX (correspondence_type_id)
- INDEX (originator_id)
- INDEX (deleted_at)
Relationships:
- Parent: correspondence_types, disciplines, projects, organizations, users
- Children: correspondence_revisions, correspondence_recipients, correspondence_tags, correspondence_references, circulations, transmittals
3.4 correspondence_revisions (UPDATE v1.7.0)
Purpose: Child table storing revision history of correspondences (1:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT | UUID Public Identifier (ADR-019) |
| correspondence_id | INT | NOT NULL, FK | Master correspondence ID |
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) |
| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag |
| correspondence_status_id | INT | NOT NULL, FK | Current status of this revision |
| title | VARCHAR(255) | NOT NULL | Document title |
| document_date | DATE | NULL | Document date |
| issued_date | DATETIME | NULL | Issue date |
| received_date | DATETIME | NULL | Received date |
| due_date | DATETIME | NULL | Due date for response |
| description | TEXT | NULL | Revision description |
| details | JSON | NULL | Type-specific details (e.g., RFI questions) |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
| created_by | INT | NULL, FK | User who created revision |
| updated_by | INT | NULL, FK | User who last updated |
| v_ref_project_id | INT | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Project ID จาก JSON details เพื่อทำ Index |
| v_doc_subtype | VARCHAR(50) | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Type จาก JSON details | | schema_version | INT | DEFAULT 1 | Version of the schema used with this details |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- FOREIGN KEY (correspondence_status_id) REFERENCES correspondence_status(id) ON DELETE RESTRICT
- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL
- FOREIGN KEY (updated_by) REFERENCES users(user_id) ON DELETE SET NULL
- UNIQUE KEY (correspondence_id, revision_number)
- UNIQUE KEY (correspondence_id, is_current)
- INDEX (correspondence_status_id)
- INDEX (is_current)
- INDEX (document_date)
- INDEX (issued_date)
- INDEX (v_ref_project_id)
- INDEX (v_doc_subtype)
3.5 correspondence_recipients
Purpose: Junction table for correspondence recipients (TO/CC) (M:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences |
| recipient_organization_id | INT | PRIMARY KEY, FK | Recipient organization |
| recipient_type | ENUM(' TO ', ' CC ') | PRIMARY KEY | Recipient type |
Indexes:
- PRIMARY KEY (correspondence_id, recipient_organization_id, recipient_type)
- FOREIGN KEY (correspondence_id) REFERENCES correspondence_revisions(correspondence_id) ON DELETE CASCADE
- FOREIGN KEY (recipient_organization_id) REFERENCES organizations(id) ON DELETE RESTRICT
- INDEX (recipient_organization_id)
- INDEX (recipient_type)
Relationships:
- Parent: correspondences, organizations
3.6 tags (UPDATE v1.8.0)
Purpose: Master table for document tagging system (Supports multi-tenant per project)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique tag ID |
| project_id | INT | NULL, FK | [NEW] Project scope (NULL = Global) |
| tag_name | VARCHAR(100) | NOT NULL | Tag name |
| color_code | VARCHAR(30) | DEFAULT 'default' | [NEW] UI Color/Class Code |
| description | TEXT | NULL | Tag description |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
| created_by | INT | NULL, FK | [NEW] User who created the tag |
| deleted_at | DATETIME | NULL | [NEW] Soft delete timestamp |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL
- UNIQUE KEY (project_id, tag_name)
- INDEX (deleted_at)
Relationships:
- Parent: projects, users
- Referenced by: correspondence_tags
3.7 correspondence_tags (UPDATE v1.8.0)
Purpose: Junction table linking correspondences to tags (M:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences |
| tag_id | INT | PRIMARY KEY, FK | Reference to tags |
Indexes:
- PRIMARY KEY (correspondence_id, tag_id)
- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE
- INDEX idx_tag_lookup (tag_id) - For reverse lookup (Find documents by tag)
Relationships:
- Parent: correspondences, tags
3.8 correspondence_references
Purpose: Junction table for cross-referencing correspondences (M:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| src_correspondence_id | INT | PRIMARY KEY, FK | Source correspondence ID |
| tgt_correspondence_id | INT | PRIMARY KEY, FK | Target (referenced) correspondence ID |
Indexes:
- PRIMARY KEY (src_correspondence_id, tgt_correspondence_id)
- FOREIGN KEY (src_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- FOREIGN KEY (tgt_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- INDEX (tgt_correspondence_id)
Relationships:
- Parent: correspondences (both sides)
4. 📐 approval: RFA Tables (เอกสารขออนุมัติ, Workflows)
4.1 rfa_types (UPDATE v1.7.0)
Purpose: Master table for RFA (Request for Approval) types
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier |
| contract_id | INT | NOT NULL, FK | Contract reference |
| type_code | VARCHAR(20) | NOT NULL | Type code (DDW, SDW, ADW, DOC, MAT, etc.) |
| type_name_th | VARCHAR(100) | NOT NULL | Full type name (TH) |
| type_name_en | VARCHAR(100) | NOT NULL | Full type name (EN) |
| remark | TEXT | NULL | Remark |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
Indexes:
- PRIMARY KEY (id)
- UNIQUE (contract_id, type_code)
- FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE CASCADE
- INDEX (is_active)
Relationships:
- Referenced by: rfas
4.2 rfa_status_codes
Purpose: Master table for RFA status codes
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier |
| status_code | VARCHAR(20) | NOT NULL, UNIQUE | Status code (DFT, FAP, FRE, etc.) |
| status_name | VARCHAR(100) | NOT NULL | Full status name |
| description | TEXT | NULL | Status description |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
Indexes:
- PRIMARY KEY (id)
- UNIQUE (status_code)
- INDEX (is_active)
- INDEX (sort_order)
Relationships:
- Referenced by: rfa_revisions
4.3 rfa_approve_codes
Purpose: Master table for RFA approval result codes
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique identifier |
| approve_code | VARCHAR(20) | NOT NULL, UNIQUE | Approval code (1A, 1C, 3R, etc.) |
| approve_name | VARCHAR(100) | NOT NULL | Full approval name |
| description | TEXT | NULL | Code description |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
Indexes:
- PRIMARY KEY (id)
- UNIQUE (approve_code)
- INDEX (is_active)
- INDEX (sort_order)
Relationships:
- Referenced by: rfa_revisions
4.4 rfas (UPDATE v1.7.0)
Purpose: Master table for RFA documents (non-revisioned data)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PK, FK | Master RFA ID (Shared with correspondences) |
| rfa_type_id | INT | NOT NULL, FK | Reference to rfa_types |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| created_by | INT | NULL, FK | User who created the record |
| deleted_at | DATETIME | NULL | Soft delete timestamp |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (id) REFERENCES correspondences(id) ON DELETE CASCADE
- FOREIGN KEY (rfa_type_id) REFERENCES rfa_types(id)
- FOREIGN KEY (created_by) REFERENCES users(user_id) ON DELETE SET NULL
- INDEX (rfa_type_id)
- INDEX (deleted_at)
Relationships:
- Parent: correspondences, rfa_types, users
- Children: rfa_revisions
4.5 rfa_revisions (UPDATE v1.7.0)
Purpose: Child table storing revision history of RFAs (1:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PK, FK | Master Revision ID (Shared with correspondence_revisions) |
| rfa_status_code_id | INT | NOT NULL, FK | Current RFA status |
| rfa_approve_code_id | INT | NULL, FK | Approval result code |
| details | JSON | NULL | Type-specific details (e.g., RFI questions) |
| v_ref_drawing_count | INT | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Drawing Count จาก JSON details เพื่อทำ Index |
| schema_version | INT | DEFAULT 1 | Version of the schema used with this details |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (id) REFERENCES correspondence_revisions(id) ON DELETE CASCADE
- FOREIGN KEY (rfa_status_code_id) REFERENCES rfa_status_codes(id)
- FOREIGN KEY (rfa_approve_code_id) REFERENCES rfa_approve_codes(id) ON DELETE SET NULL
- INDEX (rfa_status_code_id)
- INDEX (rfa_approve_code_id)
- INDEX (v_ref_drawing_count): ตัวอย่างการ Index ข้อมูลตัวเลขใน JSON
Relationships:
- Parent: correspondence_revisions, rfas, rfa_status_codes, rfa_approve_codes
- Children: rfa_items
4.6 rfa_items
Purpose: Child table linking RFA revisions to drawing revisions that require approval
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AI | Unique identifier |
| rfa_revision_id | INT | NOT NULL, FK | RFA Revision ID |
| item_type | ENUM('SHOP','AS_BUILT') | NOT NULL | Drawing reference type |
| shop_drawing_revision_id | INT | NULL, FK | Shop drawing revision ID |
| asbuilt_drawing_revision_id | INT | NULL, FK | As-Built drawing revision ID |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (rfa_revision_id) REFERENCES rfa_revisions(id) ON DELETE CASCADE
- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
- FOREIGN KEY (asbuilt_drawing_revision_id) REFERENCES asbuilt_drawing_revisions(id) ON DELETE CASCADE
- UNIQUE KEY (rfa_revision_id, shop_drawing_revision_id)
- UNIQUE KEY (rfa_revision_id, asbuilt_drawing_revision_id)
- INDEX (item_type)
- INDEX (shop_drawing_revision_id)
- INDEX (asbuilt_drawing_revision_id)
Relationships:
- Parent: rfa_revisions, shop_drawing_revisions, asbuilt_drawing_revisions
Business Rules:
correspondences.correspondence_type_idfor an RFA must always point tocorrespondence_types.type_code = 'RFA'rfas.rfa_type_idstores the selected RFA subtypeDDWandSDWRFA types must referenceshop_drawing_revisionsADWRFA types must referenceasbuilt_drawing_revisions- Each
rfa_itemsrow must reference exactly one drawing revision target according toitem_type - One RFA can contain multiple drawing references
- One drawing revision can be referenced by multiple RFAs
5. 📐 Drawings Tables (แบบ, หมวดหมู่)
5.1 contract_drawing_volumes
Purpose: Master table for contract drawing volume classification
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique volume ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| volume_code | VARCHAR(50) | NOT NULL | Volume code |
| volume_name | VARCHAR(255) | NOT NULL | Volume name |
| description | TEXT | NULL | Volume description |
| sort_order | INT | DEFAULT 0 | Display order |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- UNIQUE KEY (project_id, volume_code)
- INDEX (sort_order)
Relationships:
- Parent: projects
- Referenced by: contract_drawings
Business Rules:
- Volume codes must be unique within a project
- Used for organizing large sets of contract drawings
5.2 contract_drawing_cats
Purpose: Master table for contract drawing main categories
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique category ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| cat_code | VARCHAR(50) | NOT NULL | Category code |
| cat_name | VARCHAR(255) | NOT NULL | Category name |
| description | TEXT | NULL | Category description |
| sort_order | INT | DEFAULT 0 | Display order |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- UNIQUE KEY (project_id, cat_code)
- INDEX (sort_order)
Relationships:
- Parent: projects
- Referenced by: contract_drawing_subcat_cat_maps
Business Rules:
- Category codes must be unique within a project
- Hierarchical relationship with sub-categories via mapping table
5.3 contract_drawing_sub_cats
Purpose: Master table for contract drawing sub-categories
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique sub-category ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| sub_cat_code | VARCHAR(50) | NOT NULL | Sub-category code |
| sub_cat_name | VARCHAR(255) | NOT NULL | Sub-category name |
| description | TEXT | NULL | Sub-category description |
| sort_order | INT | DEFAULT 0 | Display order |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- UNIQUE KEY (project_id, sub_cat_code)
- INDEX (sort_order)
Relationships:
- Parent: projects
- Referenced by: contract_drawings, contract_drawing_subcat_cat_maps
Business Rules:
- Sub-category codes must be unique within a project
- Can be mapped to multiple main categories via mapping table
5.4 contract_drawing_subcat_cat_maps (UPDATE v1.7.0)
Purpose: Junction table mapping sub-categories to main categories (M:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique mapping ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| sub_cat_id | INT | NOT NULL, FK | Reference to sub-category |
| cat_id | INT | NOT NULL, FK | Reference to main category |
Indexes:
- PRIMARY KEY (id)
- UNIQUE KEY (project_id, sub_cat_id, cat_id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (sub_cat_id) REFERENCES contract_drawing_sub_cats(id) ON DELETE CASCADE
- FOREIGN KEY (cat_id) REFERENCES contract_drawing_cats(id) ON DELETE CASCADE
- INDEX (sub_cat_id)
- INDEX (cat_id)
Relationships:
- Parent: projects, contract_drawing_sub_cats, contract_drawing_cats
- Referenced by: contract_drawings
Business Rules:
- Allows flexible categorization
- One sub-category can belong to multiple main categories
- Composite uniqueness enforced via UNIQUE constraint
5.5 contract_drawings (UPDATE v1.7.0)
Purpose: Master table for contract drawings (from contract specifications)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique drawing ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT | UUID Public Identifier (ADR-019) |
| project_id | INT | NOT NULL, FK | Reference to projects |
| condwg_no | VARCHAR(255) | NOT NULL | Contract drawing number |
| title | VARCHAR(255) | NOT NULL | Drawing title |
| map_cat_id | INT | NULL, FK | [CHANGED] Reference to mapping table |
| volume_id | INT | NULL, FK | Reference to volume |
| volume_page | INT | NULL | [NEW] Page number within volume |
| 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 |
| updated_by | INT | NULL, FK | User who last updated |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (map_cat_id) REFERENCES contract_drawing_subcat_cat_maps(id) ON DELETE RESTRICT
- FOREIGN KEY (volume_id) REFERENCES contract_drawing_volumes(id) ON DELETE RESTRICT
- FOREIGN KEY (updated_by) REFERENCES users(user_id)
- UNIQUE KEY (project_id, condwg_no)
- UNIQUE INDEX idx_contract_drawings_uuid (uuid)
- INDEX (map_cat_id)
- INDEX (volume_id)
- INDEX (deleted_at)
Relationships:
- Parent: projects, contract_drawing_subcat_cat_maps, contract_drawing_volumes, users
- Referenced by: shop_drawing_revision_contract_refs, contract_drawing_attachments
Business Rules:
- Drawing numbers must be unique within a project
- Represents baseline/contract drawings
- Referenced by shop drawings for compliance tracking
- Soft delete preserves history
- map_cat_id references the mapping table for flexible categorization
5.6 shop_drawing_main_categories (UPDATE v1.7.0)
Purpose: Master table for shop drawing main categories (discipline-level)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique category ID |
| project_id | INT | NOT NULL, FK | [NEW] Reference to projects |
| main_category_code | VARCHAR(50) | NOT NULL, UNIQUE | Category code (ARCH, STR, MEP, etc.) |
| main_category_name | VARCHAR(255) | NOT NULL | Category name |
| description | TEXT | NULL | Category description |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id)
- UNIQUE (main_category_code)
- INDEX (is_active)
- INDEX (sort_order)
Relationships:
- Parent: projects
- Referenced by: shop_drawings, asbuilt_drawings
Business Rules:
- [CHANGED] Project-specific categories (was global)
- Typically represents engineering disciplines
5.7 shop_drawing_sub_categories (UPDATE v1.7.0)
Purpose: Master table for shop drawing sub-categories (component-level)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique sub-category ID |
| project_id | INT | NOT NULL, FK | [NEW] Reference to projects |
| sub_category_code | VARCHAR(50) | NOT NULL, UNIQUE | Sub-category code (STR-COLUMN, ARCH-DOOR, etc.) |
| sub_category_name | VARCHAR(255) | NOT NULL | Sub-category name |
| description | TEXT | NULL | Sub-category description |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id)
- UNIQUE (sub_category_code)
- INDEX (is_active)
- INDEX (sort_order)
Relationships:
- Parent: projects
- Referenced by: shop_drawings, asbuilt_drawings
Business Rules:
- [CHANGED] Project-specific sub-categories (was global)
- [REMOVED] No longer hierarchical under main categories
- Represents specific drawing types or components
5.8 shop_drawings (UPDATE v1.7.0)
Purpose: Master table for shop drawings (contractor-submitted)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique drawing ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT | UUID Public Identifier (ADR-019) |
| project_id | INT | NOT NULL, FK | Reference to projects |
| drawing_number | VARCHAR(100) | NOT NULL, UNIQUE | Shop drawing number |
| main_category_id | INT | NOT NULL, FK | Reference to main category |
| sub_category_id | INT | NOT NULL, FK | Reference to sub-category |
| 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 |
| updated_by | INT | NULL, FK | User who last updated |
Indexes:
- PRIMARY KEY (id)
- UNIQUE (drawing_number)
- UNIQUE INDEX idx_shop_drawings_uuid (uuid)
- FOREIGN KEY (project_id) REFERENCES projects(id)
- FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id)
- FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id)
- FOREIGN KEY (updated_by) REFERENCES users(user_id)
- INDEX (project_id)
- INDEX (main_category_id)
- INDEX (sub_category_id)
- INDEX (deleted_at)
Relationships:
- Parent: projects, shop_drawing_main_categories, shop_drawing_sub_categories, users
- Children: shop_drawing_revisions
Business Rules:
- Drawing numbers are globally unique across all projects
- Represents contractor shop drawings
- Can have multiple revisions
- Soft delete preserves history
- [CHANGED] Title moved to shop_drawing_revisions table
5.9 shop_drawing_revisions (UPDATE v1.7.0)
Purpose: Child table storing revision history of shop drawings (1:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT | UUID Public Identifier (ADR-019) |
| shop_drawing_id | INT | NOT NULL, FK | Master shop drawing ID |
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, C...) |
| revision_date | DATE | NULL | Revision date |
| title | VARCHAR(500) | NOT NULL | [NEW] Drawing title |
| description | TEXT | NULL | Revision description/changes |
| legacy_drawing_number | VARCHAR(100) | NULL | [NEW] Original/legacy drawing number |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (shop_drawing_id) REFERENCES shop_drawings(id) ON DELETE CASCADE
- UNIQUE KEY (shop_drawing_id, revision_number)
- UNIQUE INDEX idx_shop_drawing_revisions_uuid (uuid)
- INDEX (revision_date)
Relationships:
- Parent: shop_drawings
- Referenced by: rfa_items, shop_drawing_revision_contract_refs, shop_drawing_revision_attachments, asbuilt_revision_shop_revisions_refs
Business Rules:
- Revision numbers are sequential starting from 0
- Each revision can reference multiple contract drawings
- Each revision can have multiple file attachments
- Linked to RFAs for approval tracking
- [NEW] Title stored at revision level for version-specific naming
- [NEW] legacy_drawing_number supports data migration from old systems
5.10 shop_drawing_revision_contract_refs
Purpose: Junction table linking shop drawing revisions to referenced contract drawings (M:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to shop drawing revision |
| contract_drawing_id | INT | PRIMARY KEY, FK | Reference to contract drawing |
Indexes:
- PRIMARY KEY (shop_drawing_revision_id, contract_drawing_id)
- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
- FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE
- INDEX (contract_drawing_id)
Relationships:
- Parent: shop_drawing_revisions, contract_drawings
Business Rules:
- Tracks which contract drawings each shop drawing revision is based on
- Ensures compliance with contract specifications
- One shop drawing revision can reference multiple contract drawings
5.11 asbuilt_drawings (NEW v1.7.0)
Purpose: Master table for AS Built drawings (final construction records)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique drawing ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT | UUID Public Identifier (ADR-019) |
| project_id | INT | NOT NULL, FK | Reference to projects |
| drawing_number | VARCHAR(100) | NOT NULL, UNIQUE | AS Built drawing number |
| main_category_id | INT | NOT NULL, FK | Reference to main category |
| sub_category_id | INT | NOT NULL, FK | Reference to sub-category |
| 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 |
| updated_by | INT | NULL, FK | User who last updated |
Indexes:
- PRIMARY KEY (id)
- UNIQUE (drawing_number)
- UNIQUE INDEX idx_asbuilt_drawings_uuid (uuid)
- FOREIGN KEY (project_id) REFERENCES projects(id)
- FOREIGN KEY (main_category_id) REFERENCES shop_drawing_main_categories(id)
- FOREIGN KEY (sub_category_id) REFERENCES shop_drawing_sub_categories(id)
- FOREIGN KEY (updated_by) REFERENCES users(user_id)
- INDEX (project_id)
- INDEX (main_category_id)
- INDEX (sub_category_id)
- INDEX (deleted_at)
Relationships:
- Parent: projects, shop_drawing_main_categories, shop_drawing_sub_categories, users
- Children: asbuilt_drawing_revisions
Business Rules:
- Drawing numbers are globally unique across all projects
- Represents final as-built construction drawings
- Can have multiple revisions
- Soft delete preserves history
- Uses same category structure as shop drawings
5.12 asbuilt_drawing_revisions (NEW v1.7.0)
Purpose: Child table storing revision history of AS Built drawings (1:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT | UUID Public Identifier (ADR-019) |
| asbuilt_drawing_id | INT | NOT NULL, FK | Master AS Built drawing ID |
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, C...) |
| revision_date | DATE | NULL | Revision date |
| title | VARCHAR(500) | NOT NULL | Drawing title |
| description | TEXT | NULL | Revision description/changes |
| legacy_drawing_number | VARCHAR(100) | NULL | Original/legacy drawing number |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (asbuilt_drawing_id) REFERENCES asbuilt_drawings(id) ON DELETE CASCADE
- UNIQUE KEY (asbuilt_drawing_id, revision_number)
- UNIQUE INDEX idx_asbuilt_drawing_revisions_uuid (uuid)
- INDEX (revision_date)
Relationships:
- Parent: asbuilt_drawings
- Referenced by: asbuilt_revision_shop_revisions_refs, asbuilt_drawing_revision_attachments
Business Rules:
- Revision numbers are sequential starting from 0
- Each revision can reference multiple shop drawing revisions
- Each revision can have multiple file attachments
- Title stored at revision level for version-specific naming
- legacy_drawing_number supports data migration from old systems
5.13 asbuilt_revision_shop_revisions_refs (NEW v1.7.0)
Purpose: Junction table linking AS Built drawing revisions to shop drawing revisions (M:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| asbuilt_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to AS Built revision |
| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to shop drawing revision |
Indexes:
- PRIMARY KEY (asbuilt_drawing_revision_id, shop_drawing_revision_id)
- FOREIGN KEY (asbuilt_drawing_revision_id) REFERENCES asbuilt_drawing_revisions(id) ON DELETE CASCADE
- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
- INDEX (shop_drawing_revision_id)
Relationships:
- Parent: asbuilt_drawing_revisions, shop_drawing_revisions
Business Rules:
- Tracks which shop drawings each AS Built drawing revision is based on
- Maintains construction document lineage
- One AS Built revision can reference multiple shop drawing revisions
- Supports traceability from final construction to approved shop drawings
5.14 asbuilt_drawing_revision_attachments (NEW v1.7.0)
Purpose: Junction table linking AS Built drawing revisions to file attachments (M:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| asbuilt_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to AS Built revision |
| attachment_id | INT | PRIMARY KEY, FK | Reference to attachment file |
| file_type | ENUM('PDF', 'DWG', 'SOURCE', 'OTHER') | NULL | File type classification |
| is_main_document | BOOLEAN | DEFAULT FALSE | Main document flag (1 = primary file) |
Indexes:
- PRIMARY KEY (asbuilt_drawing_revision_id, attachment_id)
- FOREIGN KEY (asbuilt_drawing_revision_id) REFERENCES asbuilt_drawing_revisions(id) ON DELETE CASCADE
- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE
- INDEX (attachment_id)
Relationships:
- Parent: asbuilt_drawing_revisions, attachments
Business Rules:
- Each AS Built revision can have multiple file attachments
- File types: PDF (documents), DWG (CAD files), SOURCE (source files), OTHER (miscellaneous)
- One attachment can be marked as main document per revision
- Cascade delete when revision is deleted
6. 🔄 Circulations Tables (ใบเวียนภายใน)
6.1 circulation_status_codes
Purpose: Master table for circulation workflow status codes
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique status ID |
| code | VARCHAR(20) | NOT NULL, UNIQUE | Status code (OPEN, IN_REVIEW, COMPLETED, CANCELLED) |
| description | VARCHAR(50) | NOT NULL | Status description |
| sort_order | INT | DEFAULT 0 | Display order |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
Indexes:
- PRIMARY KEY (id)
- UNIQUE (code)
- INDEX (is_active)
- INDEX (sort_order)
Relationships:
- Referenced by: circulations
Seed Data: 4 status codes
- OPEN: Initial status when created
- IN_REVIEW: Under review by recipients
- COMPLETED: All recipients have responded
- CANCELLED: Withdrawn/cancelled
6.2 circulations
Purpose: Master table for internal circulation sheets (document routing)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique circulation ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT | UUID Public Identifier (ADR-019) |
| correspondence_id | INT | UNIQUE, FK | Link to correspondence (1:1 relationship) |
| organization_id | INT | NOT NULL, FK | Organization that owns this circulation |
| circulation_no | VARCHAR(100) | NOT NULL | Circulation sheet number |
| circulation_subject | VARCHAR(500) | NOT NULL | Subject/title |
| circulation_status_code | VARCHAR(20) | NOT NULL, FK | Current status code |
| created_by_user_id | INT | NOT NULL, FK | User who created circulation |
| submitted_at | TIMESTAMP | NULL | Submission timestamp |
| closed_at | TIMESTAMP | NULL | Closure timestamp |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
Indexes:
- PRIMARY KEY (id)
- UNIQUE (correspondence_id)
- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id)
- FOREIGN KEY (organization_id) REFERENCES organizations(id)
- FOREIGN KEY (circulation_status_code) REFERENCES circulation_status_codes(code)
- FOREIGN KEY (created_by_user_id) REFERENCES users(user_id)
- INDEX (organization_id)
- UNIQUE INDEX idx_circulations_uuid (uuid)
- INDEX (circulation_status_code)
- INDEX (created_by_user_id)
Relationships:
- Parent: correspondences, organizations, circulation_status_codes, users
- Children: circulation_routings, circulation_attachments
Business Rules:
- Internal document routing within organization
- One-to-one relationship with correspondences
- Tracks document review/approval workflow
- Status progression: OPEN → IN_REVIEW → COMPLETED/CANCELLED
7. 📤 Transmittals Tables (เอกสารนำส่ง)
7.1 transmittals
Purpose: Child table for transmittal-specific data (1:1 with correspondences)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| correspondence_id | INT | PRIMARY KEY, FK | Reference to correspondences (1:1) |
| purpose | ENUM | NULL | Purpose: FOR_APPROVAL, FOR_INFORMATION, FOR_REVIEW, OTHER |
| remarks | TEXT | NULL | Additional remarks |
Indexes:
- PRIMARY KEY (correspondence_id)
- FOREIGN KEY (correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- INDEX (purpose)
Relationships:
- Parent: correspondences
- Children: transmittal_items
Business Rules:
- One-to-one relationship with correspondences
- Transmittal is a correspondence type for forwarding documents
- Contains metadata about the transmission
7.2 transmittal_items
Purpose: Junction table listing documents included in transmittal (M:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique item ID |
| transmittal_id | INT | NOT NULL, FK | Reference to transmittal |
| item_correspondence_id | INT | NOT NULL, FK | Reference to document being transmitted |
| quantity | INT | DEFAULT 1 | Number of copies |
| remarks | VARCHAR(255) | NULL | Item-specific remarks |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (transmittal_id) REFERENCES transmittals(correspondence_id) ON DELETE CASCADE
- FOREIGN KEY (item_correspondence_id) REFERENCES correspondences(id) ON DELETE CASCADE
- UNIQUE KEY (transmittal_id, item_correspondence_id)
- INDEX (item_correspondence_id)
Relationships:
- Parent: transmittals, correspondences
Business Rules:
- One transmittal can contain multiple documents
- Tracks quantity of physical copies (if applicable)
- Links to any type of correspondence document
8. 📎 File Management Tables (ไฟล์แนบ)
8.1 attachments
Purpose: Central repository for all file attachments in the system
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique attachment ID |
| uuid | UUID | NOT NULL, UNIQUE, DEFAULT | UUID Public Identifier (ADR-019) |
| original_filename | VARCHAR(255) | NOT NULL | Original filename from upload |
| stored_filename | VARCHAR(255) | NOT NULL | System-generated unique filename |
| file_path | VARCHAR(500) | NOT NULL | Full file path on server (/share/dms-data/) |
| mime_type | VARCHAR(100) | NOT NULL | MIME type (application/pdf, image/jpeg, etc.) |
| file_size | INT | NOT NULL | File size in bytes |
| uploaded_by_user_id | INT | NOT NULL, FK | User who uploaded file |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Upload timestamp |
| is_temporary | BOOLEAN | DEFAULT TRUE | ระบุว่าเป็นไฟล์ชั่วคราว (ยังไม่ได้ Commit) |
| temp_id* | VARCHAR(100) | NULL | ID ชั่วคราวสำหรับอ้างอิงตอน Upload Phase 1 (อาจใช้ร่วมกับ id หรือแยกก็ได้) |
| expires_at | DATETIME | NULL | เวลาหมดอายุของไฟล์ Temp (เพื่อให้ Cron Job ลบออก) |
| 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:
- PRIMARY KEY (id)
- FOREIGN KEY (uploaded_by_user_id) REFERENCES users(user_id) ON DELETE CASCADE
- INDEX (stored_filename)
- INDEX (mime_type)
- INDEX (uploaded_by_user_id)
- UNIQUE INDEX idx_attachments_uuid (uuid)
- INDEX (created_at)
- INDEX (reference_date)
- INDEX (workflow_history_id)
- INDEX idx_attachments_ai_status (ai_processing_status)
Relationships:
- Parent: users, workflow_histories (via workflow_history_id)
- Referenced by: correspondence_revision_attachments, circulation_attachments, shop_drawing_revision_attachments, contract_drawing_attachments
Business Rules:
- Central storage prevents file duplication
- Stored filename prevents naming conflicts
- File path points to QNAP NAS storage
- Original filename preserved for download
- One file record can be linked to multiple documents
- [ADR-021]
workflow_history_id = NULL→ Main Document attachment (linked via junction tables) - [ADR-021]
workflow_history_id IS NOT NULL→ Step-evidence attachment — linked exclusively to a single workflow transition history record (Approve/Reject/Return evidence) - Step-evidence attachments are committed (is_temporary = false) during
processTransition()in the same transaction as the history record - Cleanup jobs MUST NOT delete attachments where
workflow_history_id IS NOT NULL
8.2 correspondence_revision_attachments (UPDATE v1.8.1)
Purpose: Junction table linking correspondence_revisions to file attachments (M:N)
[FIX v1.8.1] เปลี่ยนจาก
correspondence_attachments(FK →correspondences.id) →correspondence_revision_attachments(FK →correspondence_revisions.id) เหตุผล: ไฟล์แนบผูกกับ revision ไม่ใช่ correspondence master เพราะแต่ละ revision อาจมีไฟล์แนบต่างกัน
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| correspondence_revision_id | INT | PRIMARY KEY, FK | Reference to correspondence_revisions |
| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments |
| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag of revision |
Indexes:
- PRIMARY KEY (correspondence_revision_id, attachment_id)
- FOREIGN KEY (correspondence_revision_id) REFERENCES correspondence_revisions(id) ON DELETE CASCADE
- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE
- INDEX idx_corr_rev_att_revision (correspondence_revision_id)
Relationships:
- Parent: correspondence_revisions, attachments
- correspondence_revisions has
attachmentLinksOneToMany relation
Business Rules:
- One revision can have multiple attachments
- One attachment can be linked to multiple revisions
- is_main_document identifies primary file of that revision (typically PDF)
- เมื่อ create/update correspondence → commit attachments → บันทึก revision_id ลงตารางนี้
8.3 circulation_attachments
Purpose: Junction table linking circulations to file attachments (M:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| circulation_id | INT | PRIMARY KEY, FK | Reference to circulations |
| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments |
| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag |
Indexes:
- PRIMARY KEY (circulation_id, attachment_id)
- FOREIGN KEY (circulation_id) REFERENCES circulations(id) ON DELETE CASCADE
- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE
- INDEX (attachment_id)
- INDEX (is_main_document)
Relationships:
- Parent: circulations, attachments
8.4 shop_drawing_revision_attachments
Purpose: Junction table linking shop drawing revisions to file attachments (M:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| shop_drawing_revision_id | INT | PRIMARY KEY, FK | Reference to shop drawing revision |
| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments |
| file_type | ENUM | NULL | File type: PDF, DWG, SOURCE, OTHER |
| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag |
Indexes:
- PRIMARY KEY (shop_drawing_revision_id, attachment_id)
- FOREIGN KEY (shop_drawing_revision_id) REFERENCES shop_drawing_revisions(id) ON DELETE CASCADE
- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE
- INDEX (attachment_id)
- INDEX (file_type)
- INDEX (is_main_document)
Relationships:
- Parent: shop_drawing_revisions, attachments
Business Rules:
- file_type categorizes drawing file formats
- Typically includes PDF for viewing and DWG for editing
- SOURCE may include native CAD files
8.5 contract_drawing_attachments
Purpose: Junction table linking contract drawings to file attachments (M:N)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| contract_drawing_id | INT | PRIMARY KEY, FK | Reference to contract drawing |
| attachment_id | INT | PRIMARY KEY, FK | Reference to attachments |
| file_type | ENUM | NULL | File type: PDF, DWG, SOURCE, OTHER |
| is_main_document | BOOLEAN | DEFAULT FALSE | Main/primary document flag |
Indexes:
- PRIMARY KEY (contract_drawing_id, attachment_id)
- FOREIGN KEY (contract_drawing_id) REFERENCES contract_drawings(id) ON DELETE CASCADE
- FOREIGN KEY (attachment_id) REFERENCES attachments(id) ON DELETE CASCADE
- INDEX (attachment_id)
- INDEX (file_type)
- INDEX (is_main_document)
Relationships:
- Parent: contract_drawings, attachments
9. 🔢 Document Numbering System Tables (ระบบเลขที่เอกสาร)
9.1 document_number_formats
Purpose: Master table defining numbering formats for each document type
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique format ID |
| project_id | INT | NOT NULL, FK | Reference to projects |
| correspondence_type_id | INT | NULL, FK | Reference to correspondence_types |
| discipline_id | INT | DEFAULT 0, FK | Reference to disciplines (0 = all) |
| format_string | VARCHAR(100) | NOT NULL | Format pattern (e.g., {ORG}-{TYPE}-{YYYY}-#) |
| description | TEXT | NULL | Format description |
| reset_annually | BOOLEAN | DEFAULT TRUE | Start sequence new every year |
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE
- UNIQUE KEY (project_id, correspondence_type_id, discipline_id)
- INDEX (is_active)
Relationships:
- Parent: projects, correspondence_types
Business Rules:
- Defines how document numbers are constructed
- Supports placeholders: {PROJ}, {ORG}, {TYPE}, {YYYY}, {MM}, {#}
9.2 document_number_counters (UPDATE v1.7.0)
Purpose: Transaction table tracking running numbers (High Concurrency)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| project_id | INT | PK, NOT NULL | โครงการ |
| originator_organization_id | INT | PK, NOT NULL | องค์กรผู้ส่ง |
| recipient_organization_id | INT | PK, NOT NULL | องค์กรผู้รับ (0 = no recipient / RFA) |
| correspondence_type_id | INT | PK, NULL | ประเภทเอกสาร (NULL = default) |
| sub_type_id | INT | PK, DEFAULT 0 | ประเภทย่อย สำหรับ TRANSMITTAL (0 = ไม่ระบุ) |
| rfa_type_id | INT | PK, DEFAULT 0 | ประเภท RFA (0 = ไม่ใช่ RFA) |
| discipline_id | INT | PK, DEFAULT 0 | สาขางาน (0 = ไม่ระบุ) |
| reset_scope | VARCHAR(20) | PK, NOT NULL | Scope of reset (YEAR_2024, MONTH_2024_01, NONE) |
| last_number | INT | DEFAULT 0 | เลขล่าสุดที่ถูกใช้งานไปแล้ว |
| version | INT | DEFAULT 0 | Optimistic Lock Version |
| updated_at | DATETIME(6) | ON UPDATE | เวลาที่อัปเดตล่าสุด |
Indexes:
- PRIMARY KEY (project_id, originator_organization_id, recipient_organization_id, correspondence_type_id, sub_type_id, rfa_type_id, discipline_id, reset_scope)
- INDEX idx_counter_lookup (project_id, correspondence_type_id, reset_scope)
- INDEX idx_counter_org (originator_organization_id, reset_scope)
Business Rules:
- Composite Primary Key 8 Columns: เพื่อรองรับการรันเลขที่ซับซ้อนและ Reset Scope ที่หลากหลาย
- Concurrency Control: ใช้ Redis Lock หรือ Optimistic Locking (version)
- Reset Scope: ใช้ Field
reset_scopeควบคุมการ Reset แทนcurrent_yearแบบเดิม
9.3 document_number_audit (UPDATE v1.7.0)
Purpose: Audit log for document number generation (Debugging & Tracking)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PK, AI | ID ของ audit record |
| document_id | INT | NULL, FK | ID ของเอกสารที่สร้างเลขที่ (NULL if failed) |
| document_type | VARCHAR(50) | NULL | ประเภทเอกสาร |
| document_number | VARCHAR(100) | NOT NULL | เลขที่เอกสารที่สร้าง (ผลลัพธ์) |
| operation | ENUM | DEFAULT 'CONFIRM' | RESERVE, CONFIRM, MANUAL_OVERRIDE, etc. |
| status | ENUM | DEFAULT 'RESERVED' | RESERVED, CONFIRMED, CANCELLED, VOID |
| counter_key | JSON | NOT NULL | Counter key ที่ใช้ (JSON 8 fields) |
| reservation_token | VARCHAR(36) | NULL | Token การจอง |
| idempotency_key | VARCHAR(128) | NULL | Idempotency Key from request |
| originator_organization_id | INT | NULL | องค์กรผู้ส่ง |
| recipient_organization_id | INT | NULL | องค์กรผู้รับ |
| template_used | VARCHAR(200) | NOT NULL | Template ที่ใช้ในการสร้าง |
| old_value | TEXT | NULL | Previous value |
| new_value | TEXT | NULL | New value |
| user_id | INT | NULL, FK | ผู้ขอสร้างเลขที่ |
| is_success | BOOLEAN | DEFAULT TRUE | สถานะความสำเร็จ |
| created_at | TIMESTAMP | DEFAULT NOW | วันที่/เวลาที่สร้าง |
| total_duration_ms | INT | NULL | เวลารวมทั้งหมดในการสร้าง (ms) |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (document_id) REFERENCES correspondences(id) ON DELETE CASCADE
- FOREIGN KEY (user_id) REFERENCES users(user_id)
- INDEX (document_id)
- INDEX (user_id)
- INDEX (status)
- INDEX (operation)
- INDEX (document_number)
- INDEX (reservation_token)
- INDEX (created_at)
9.4 document_number_errors (UPDATE v1.7.0)
Purpose: Error log for failed document number generation
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PK, AI | ID ของ error record |
| error_type | ENUM | NOT NULL | LOCK_TIMEOUT, VERSION_CONFLICT, DB_ERROR, etc. |
| error_message | TEXT | NULL | ข้อความ error |
| stack_trace | TEXT | NULL | Stack trace สำหรับ debugging |
| context_data | JSON | NULL | Context ของ request |
| user_id | INT | NULL | ผู้ที่เกิด error |
| created_at | TIMESTAMP | DEFAULT NOW | วันที่เกิด error |
| resolved_at | TIMESTAMP | NULL | วันที่แก้ไขแล้ว |
Indexes:
- PRIMARY KEY (id)
- INDEX (error_type)
- INDEX (created_at)
- INDEX (user_id)
- INDEX (resolved_at)
9.5 document_number_reservations (NEW v1.7.0)
Purpose: Two-Phase Commit table for document number reservation
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PK, AI | Unique ID |
| token | VARCHAR(36) | UNIQUE, NOT NULL | UUID v4 Reservation Token |
| document_number | VARCHAR(100) | UNIQUE, NOT NULL | เลขที่เอกสารที่จอง |
| document_number_status | ENUM | DEFAULT RESERVED | RESERVED, CONFIRMED, CANCELLED, VOID |
| document_id | INT | NULL, FK | ID ของเอกสาร (เมื่อ Confirm แล้ว) |
| expires_at | DATETIME(6) | NOT NULL | เวลาหมดอายุการจอง |
| project_id | INT | NOT NULL, FK | Project Context |
| user_id | INT | NOT NULL, FK | User Context |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (document_id) REFERENCES correspondence_revisions(id) ON DELETE SET NULL
- FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
- FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE
- FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
- INDEX idx_token (token)
- INDEX idx_status (document_number_status)
- INDEX idx_status_expires (document_number_status, expires_at)
- INDEX idx_document_id (document_id)
- INDEX idx_user_id (user_id)
- INDEX idx_reserved_at (reserved_at)
10. ⚙️ Unified Workflow Engine Tables (UPDATE v1.7.0)
10.1 workflow_definitions
Purpose: เก็บแม่แบบ (Template) ของ Workflow (Definition / DSL)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | CHAR(36) | PK, UUID | Unique Workflow Definition ID |
| workflow_code | VARCHAR(50) | NOT NULL | รหัส Workflow (เช่น RFA_FLOW_V1) |
| version | INT | DEFAULT 1 | หมายเลข Version |
| description | TEXT | NULL | คำอธิบาย Workflow |
| dsl | JSON | NOT NULL | นิยาม Workflow ต้นฉบับ (YAML/JSON Format) |
| compiled | JSON | NOT NULL | โครงสร้าง Execution Tree ที่ Compile แล้ว |
| is_active | BOOLEAN | DEFAULT TRUE | สถานะการใช้งาน |
| created_at | TIMESTAMP | DEFAULT NOW | วันที่สร้าง |
| updated_at | TIMESTAMP | ON UPDATE | วันที่แก้ไขล่าสุด |
Indexes:
- PRIMARY KEY (id)
- UNIQUE KEY (workflow_code, version)
- INDEX (is_active)
10.2 workflow_instances
Purpose: เก็บสถานะของ Workflow ที่กำลังรันอยู่จริง (Runtime)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | CHAR(36) | PK, UUID | Unique Instance ID |
| definition_id | CHAR(36) | FK, NOT NULL | อ้างอิง Definition ที่ใช้ |
| contract_id | INT | NULL, FK | [delta-07 / ADR-021 C3] Contract ที่ Workflow นี้สังกัด (NULL = org-scoped เช่น Circulation) |
| entity_type | VARCHAR(50) | NOT NULL | ประเภทเอกสาร (rfa, correspondence, transmittal, circulation) |
| entity_id | VARCHAR(50) | NOT NULL | ID ของเอกสาร |
| current_state | VARCHAR(50) | NOT NULL | สถานะปัจจุบัน |
| status | ENUM | DEFAULT 'ACTIVE' | ACTIVE, COMPLETED, CANCELLED, TERMINATED |
| context | JSON | NULL | ตัวแปร Context สำหรับตัดสินใจ (รวม contractId, projectId, initiatorId เป็นต้น) |
| version_no | INT | NOT NULL, DEFAULT 1 | [ADR-001 v1.1 FR-002] Optimistic lock counter — incremented on every successful transition. Client sends current value; server rejects with 409 if mismatch. |
| created_at | TIMESTAMP | DEFAULT NOW | เวลาที่สร้าง |
| updated_at | TIMESTAMP | ON UPDATE | เวลาที่อัปเดตล่าสุด |
Business Rules:
contract_id= NOT NULL → contract-scoped workflow (RFA, Correspondence, Transmittal) — Guard Level 2.5 ตรวจ membershipcontract_id= NULL → org-scoped workflow (Circulation) — Guard Level 2 (org match only)context.contractIdmirrorscontract_idcolumn — redundant by design (ฟิลเตอร์ได้ทั้งสองทาง)
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (definition_id) REFERENCES workflow_definitions(id) ON DELETE CASCADE
- FOREIGN KEY (contract_id) REFERENCES contracts(id) ON DELETE SET NULL
- INDEX (entity_type, entity_id)
- INDEX (current_state)
- INDEX (contract_id, entity_type, status)
- INDEX (id, version_no) —
idx_wf_inst_version[ADR-001 v1.1 FR-002] — Supports CAS check: WHERE id = ? AND version_no = ?
10.3 workflow_histories
Purpose: เก็บประวัติการดำเนินการในแต่ละ Step (Audit Trail)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | CHAR(36) | PK, UUID | Unique ID |
| instance_id | CHAR(36) | FK, NOT NULL | อ้างอิง Instance |
| from_state | VARCHAR(50) | NOT NULL | สถานะต้นทาง |
| to_state | VARCHAR(50) | NOT NULL | สถานะปลายทาง |
| action | VARCHAR(50) | NOT NULL | Action ที่กระทำ |
| action_by_user_id | INT | FK, NULL | User ID ผู้กระทำ |
| action_by_user_uuid | VARCHAR(36) | NULL | [ADR-019 FR-003] UUID ของ User ผู้ดำเนินการ — ใช้ใน API Response แทน INT FK. NULL = System Action หรือ Pre-migration record |
| comment | TEXT | NULL | ความเห็น |
| metadata | JSON | NULL | Snapshot ข้อมูล ณ ขณะนั้น |
| created_at | TIMESTAMP | DEFAULT NOW | เวลาที่กระทำ |
Indexes:
- PRIMARY KEY (id)
- FOREIGN KEY (instance_id) REFERENCES workflow_instances(id) ON DELETE CASCADE
- INDEX (instance_id)
- INDEX (action_by_user_id)
11. 🖥️ System & Logs Tables (ระบบ, บันทึก)
Audit Logging Architecture:
1. Audit Logging
Table: audit_logs
บันทึกการเปลี่ยนแปลงสำคัญ:
- User actions (CREATE, UPDATE, DELETE)
- Entity type และ Entity ID
- Old/New values (JSON)
- IP Address, User Agent
2. User Preferences
Table: user_preferences
เก็บการตั้งค่าส่วนตัว:
- Language preference
- Notification settings
- UI preferences (JSON)
3. JSON Schema Validation
Table: json_schemas
เก็บ Schema สำหรับ Validate JSON fields:
correspondence_revisions.detailsuser_preferences.preferences
11.1 json_schemas (UPDATE v1.7.0)
Purpose: เก็บ Schema สำหรับ Validate JSON Columns (Req 3.12)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PK, AI | Unique ID |
| schema_code | VARCHAR(100) | NOT NULL | รหัส Schema (เช่น RFA_DWG) |
| version | INT | DEFAULT 1 | เวอร์ชันของ Schema |
| table_name | VARCHAR(100) | NOT NULL | ชื่อตารางเป้าหมาย |
| schema_definition | JSON | NOT NULL | JSON Schema Definition |
| ui_schema | JSON | NULL | โครงสร้าง UI Schema สำหรับ Frontend |
| virtual_columns | JSON | NULL | Config สำหรับสร้าง Virtual Columns |
| migration_script | JSON | NULL | Script สำหรับแปลงข้อมูล |
| is_active | BOOLEAN | DEFAULT TRUE | สถานะการใช้งาน |
11.2 audit_logs (UPDATE v1.7.0)
Purpose: Centralized audit logging for all system actions (Req 6.1)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| audit_id | BIGINT | PK, AI | Unique log ID |
| request_id | VARCHAR(100) | NULL | Trace ID linking to app logs |
| user_id | INT | NULL, FK | User who performed action |
| action | VARCHAR(100) | NOT NULL | Action name (e.g. rfa.create) |
| severity | ENUM | DEFAULT 'INFO' | INFO, WARN, ERROR, CRITICAL |
| entity_type | VARCHAR(50) | NULL | Module/Table name (e.g. rfa, correspondence) |
| entity_id | VARCHAR(50) | NULL | ID of affected entity |
| details_json | JSON | NULL | Context data / Old & New values |
| ip_address | VARCHAR(45) | NULL | User IP address |
| user_agent | VARCHAR(255) | NULL | User browser/client info |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Log timestamp |
Indexes:
- PRIMARY KEY (audit_id, created_at) -- Partition Key
- INDEX idx_audit_user (user_id)
- INDEX idx_audit_action (action)
- INDEX idx_audit_entity (entity_type, entity_id)
- INDEX idx_audit_created (created_at)
Partitioning:
- PARTITION BY RANGE (YEAR(created_at)): แบ่ง Partition รายปี เพื่อประสิทธิภาพในการเก็บข้อมูลระยะยาว
11.3 notifications (UPDATE v1.7.0)
Purpose: System notifications for users
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique notification ID |
| uuid | UUID | NOT NULL, DEFAULT | UUID Public Identifier (ADR-019) |
| user_id | INT | NOT NULL, FK | Recipient user ID |
| title | VARCHAR(255) | NOT NULL | Notification title |
| message | TEXT | NOT NULL | Notification body |
| notification_type | ENUM | NOT NULL | Type: EMAIL, LINE, SYSTEM |
| is_read | BOOLEAN | DEFAULT FALSE | Read status |
| entity_type | VARCHAR(50) | NULL | Related Entity Type |
| entity_id | INT | NULL | Related Entity ID |
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Notification timestamp |
Indexes:
- PRIMARY KEY (id, created_at) -- Partition Key
- INDEX idx_notif_user (user_id)
- INDEX idx_notif_type (notification_type)
- INDEX idx_notif_read (is_read)
- INDEX idx_notif_created (created_at)
- INDEX idx_notifications_uuid (uuid)
Partitioning:
- PARTITION BY RANGE (YEAR(created_at)): แบ่ง Partition รายปี
12. 🔍 Views (มุมมองข้อมูล)
12.1 v_current_correspondences
Purpose: แสดงข้อมูล Correspondence Revision ล่าสุด (is_current = TRUE)
12.2 v_current_rfas
Purpose: แสดงข้อมูล RFA Revision ล่าสุด พร้อม Status และ Approve Code
12.3 v_user_tasks (Unified Workflow)
Purpose: รวมรายการงานที่ยังค้างอยู่ (Status = ACTIVE) จากทุกระบบ (RFA, Circulation, Correspondence) เพื่อนำไปแสดงใน Dashboard
12.4 v_audit_log_details
Purpose: แสดง audit_logs พร้อมข้อมูล username และ email ของผู้กระทำ
12.5 v_user_all_permissions
Purpose: รวมสิทธิ์ทั้งหมด (Global + Project + Organization) ของผู้ใช้ทุกคน
12.6 v_documents_with_attachments
Purpose: แสดงเอกสารทั้งหมดที่มีไฟล์แนบ (Correspondence, Circulation, Drawings)
12.7 v_document_statistics
Purpose: แสดงสถิติเอกสารตามประเภทและสถานะ
13. 📊 Index Summaries (สรุป Index)
Performance Optimization Strategy:
1. Indexing Strategy
Primary Indexes:
- Primary Keys (AUTO_INCREMENT)
- Foreign Keys (automatic in InnoDB)
- Unique Constraints (business keys)
Secondary Indexes:
-- Correspondence search
CREATE INDEX idx_corr_type_status ON correspondence_revisions(correspondence_type_id, correspondence_status_id);
CREATE INDEX idx_corr_date ON correspondence_revisions(document_date);
-- Virtual columns for JSON
CREATE INDEX idx_v_ref_project ON correspondence_revisions(v_ref_project_id);
CREATE INDEX idx_v_doc_subtype ON correspondence_revisions(v_doc_subtype);
-- User lookup
CREATE INDEX idx_user_email ON users(email);
CREATE INDEX idx_user_org ON users(primary_organization_id, is_active);
2. Virtual Columns
ใช้ Virtual Columns สำหรับ Index JSON fields:
ALTER TABLE correspondence_revisions
ADD COLUMN v_ref_project_id INT GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '$.ref_project_id'))) VIRTUAL,
ADD INDEX idx_v_ref_project(v_ref_project_id);
3. Partitioning (Future)
พิจารณา Partition ตาราง audit_logs ตามปี:
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
);
13.1 Performance Indexes
| Table Name | Index Columns | Purpose |
|---|---|---|
| correspondences | (project_id, correspondence_number) | Fast lookup by document number |
| correspondences | (correspondence_type_id) | Filter by type |
| correspondence_revisions | (correspondence_id, is_current) | Get current revision |
| rfas | (rfa_type_id) | Filter by RFA type |
| rfa_revisions | (rfa_id, is_current) | Get current RFA revision |
| rfa_revisions | (rfa_status_code_id) | Filter by status |
| audit_logs | (created_at) | Date range queries |
| audit_logs | (user_id) | User activity history |
| audit_logs | (module, action) | Action type analysis |
| notifications | (user_id, is_read) | Unread notifications query |
| document_number_counters | (project_id, correspondence_type_id, reset_scope) | Running number generation |
| workflow_instances | (entity_type, entity_id) | Workflow lookup by document ID |
| workflow_instances | (current_state) | Monitor active workflows |
| workflow_instances | (contract_id, entity_type, status) | [delta-07] Guard contract-membership + dashboard filter |
13.2 Unique Constraints
| Table Name | Columns | Description |
|---|---|---|
| users | (username) | Unique login name |
| users | (email) | Unique email address |
| organizations | (organization_code) | Unique organization code |
| projects | (project_code) | Unique project code |
| contracts | (contract_code) | Unique contract code |
| correspondences | (project_id, correspondence_number) | Unique document number per project |
| shop_drawings | (drawing_number) | Unique shop drawing number |
| document_number_formats | (project_id, correspondence_type_id) | One format per type per project |
| workflow_definitions | (workflow_code, version) | Unique workflow code per version |
14. 🛡️ Data Integrity Constraints (ความถูกต้องของข้อมูล)
14.1 Soft Delete Policy
- Tables with
deleted_at:- users
- organizations
- projects
- contracts
- correspondences
- rfas
- shop_drawings
- contract_drawings
- Rule: Records are never physically deleted.
deleted_atis set to timestamp. - Query Rule: All standard queries MUST include
WHERE deleted_at IS NULL.
14.2 Foreign Key Cascades
- ON DELETE CASCADE:
- Used for child tables that cannot exist without parent (e.g.,
correspondence_revisions,rfa_revisions,correspondence_attachments).
- Used for child tables that cannot exist without parent (e.g.,
- ON DELETE RESTRICT:
- Used for master data references to prevent accidental deletion of used data (e.g.,
correspondence_types,organizations).
- Used for master data references to prevent accidental deletion of used data (e.g.,
- ON DELETE SET NULL:
- Used for optional references (e.g.,
created_by,originator_id).
- Used for optional references (e.g.,
15. 🔐 Security & Permissions Model (ความปลอดภัย)
15.1 Row-Level Security (RLS) Logic
- Organization Scope: Users can only see documents where
originator_idORrecipient_organization_idmatches their organization. - Project Scope: Users can only see documents within projects they are assigned to.
- Confidentiality: Documents marked
is_confidentialare visible ONLY to specific roles or users.
15.2 Role-Based Access Control (RBAC)
- Permissions are granular (e.g.,
correspondence.view,correspondence.create). - Roles aggregate permissions (e.g.,
Document Controller=view+create+edit). - Assignments link Users to Roles within a Context (Global, Project, or Organization).
16. 🔄 Data Migration & Seeding (การย้ายข้อมูล)
16.1 Initial Seeding (V1.7.0)
- Master Data:
organizations: Owner, Consultant, Contractorprojects: LCBP3correspondence_types: LETTER, MEMO, TRANSMITTAL, RFArfa_types: DWG, MAT, DOC, RFIrfa_status_codes: DFT, PEND, APPR, REJdisciplines: GEN, STR, ARC, MEP
- System Users:
admin: Super Adminsystem: System Bot for automated tasks
16.2 Migration Strategy
- Schema Migration: Use TypeORM Migrations or raw SQL scripts (versioned).
- Data Migration:
- V1.6.0 -> V1.7.0:
- Run SQL script
9_lcbp3_v1_7_0.sql - Migrate
document_number_countersto 8-col composite PK. - Initialize
document_number_reservations. - Update
json_schemaswith new columns.
- Run SQL script
- V1.6.0 -> V1.7.0:
16.3 Temporary Migration Tracking Tables (V1.8.0 n8n Migration)
ตารางเหล่านี้ถูกใช้ชั่วคราวระหว่างกระบวนการ Migrate เอกสาร PDF 20,000 ฉบับด้วย n8n (ดูรายละเอียดใน �3-05-n8n-migration-setup-guide.md) และไม่ใช่ตาราง Business หลักของระบบ
16.3.1 migration_progress
Purpose: เก็บ Checkpoint สถานะการ Migrate
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| batch_id | VARCHAR(50) | PRIMARY KEY | รหัสชุดการ Migrate |
| last_processed_index | INT | DEFAULT 0 | ลำดับล่าสุดที่ประมวลผลผ่าน |
| status | ENUM | DEFAULT 'RUNNING' | สถานะ (RUNNING, COMPLETED, FAILED) |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | เวลาอัปเดตล่าสุด |
16.3.2 migration_review_queue
Purpose: คิวเอกสารที่ต้องการให้เจ้าหน้าที่ตรวจสอบ (Confidence ต่ำกว่าเกณฑ์) หมายเหตุ: เมื่อตรวจสอบผ่านและสร้าง Correspondence จริงแล้ว ข้อมูลในนี้อาจถูกลบหรือเก็บเป็น Log ได้
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique ID |
| document_number | VARCHAR(100) | NOT NULL, UNIQUE | เลขที่เอกสาร (จาก OCR) |
| title | TEXT | ชื่อเรื่อง | |
| original_title | TEXT | ชื่อเรื่องต้นฉบับก่อนตรวจสอบ | |
| ai_suggested_category | VARCHAR(50) | หมวดหมู่ที่ AI แนะนำ | |
| ai_confidence | DECIMAL(4,3) | ค่าความมั่นใจของ AI (0.000 - 1.000) | |
| ai_issues | JSON | รายละเอียดปัญหาที่ AI พบ | |
| review_reason | VARCHAR(255) | เหตุผลที่ต้องตรวจสอบ (เช่น Confidence ต่ำ) | |
| status | ENUM | DEFAULT 'PENDING' | PENDING, APPROVED, REJECTED |
| reviewed_by | VARCHAR(100) | ผู้ตรวจสอบ | |
| reviewed_at | TIMESTAMP | NULL | เวลาที่ตรวจสอบ |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | วันที่บันทึกเข้าคิว |
16.3.3 migration_errors
Purpose: บันทึกข้อผิดพลาด (Errors) ระหว่างการทำงานของ n8n workflow
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique ID |
| batch_id | VARCHAR(50) | INDEX | รหัสชุดการ Migrate |
| document_number | VARCHAR(100) | เลขที่เอกสาร | |
| error_type | ENUM | INDEX | ประเภท Error (FILE_NOT_FOUND, AI_PARSE_ERROR, etc.) |
| error_message | TEXT | รายละเอียด Error | |
| raw_ai_response | TEXT | Raw response จาก AI กรณีแปลผลไม่ได้ | |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | วันที่บันทึก |
16.3.4 migration_fallback_state
Purpose: ติดตามสถานะ Fallback ของ AI (เช่น เปลี่ยน Model เมื่อ Error ถี่)
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique ID |
| batch_id | VARCHAR(50) | UNIQUE | รหัสชุดการ Migrate |
| recent_error_count | INT | DEFAULT 0 | จำนวน Error รวดล่าสุด |
| is_fallback_active | BOOLEAN | DEFAULT FALSE | สถานะการใช้งาน Fallback Model |
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | เวลาอัปเดตล่าสุด |
16.3.5 import_transactions
Purpose: ป้องกันข้อมูลซ้ำ (Idempotency) ระหว่างการ Patch ข้อมูล
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique ID |
| idempotency_key | VARCHAR(255) | UNIQUE, NOT NULL | Key สำหรับเช็คซ้ำ |
| document_number | VARCHAR(100) | เลขที่เอกสาร | |
| batch_id | VARCHAR(100) | รหัสชุดการ Migrate | |
| status_code | INT | DEFAULT 201 | HTTP Status ของการ Import |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | วันที่บันทึก |
16.3.6 migration_daily_summary
Purpose: สรุปยอดการทำงานรายวันแยกตาม Batch
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique ID |
| batch_id | VARCHAR(50) | UNIQUE KEY PART 1 | รหัสชุดการ Migrate |
| summary_date | DATE | UNIQUE KEY PART 2 | วันที่สรุป |
| total_processed | INT | DEFAULT 0 | จำนวนที่ประมวลผลรวม |
| auto_ingested | INT | DEFAULT 0 | จำนวนที่เข้าสู่ระบบสำเร็จ |
| sent_to_review | INT | DEFAULT 0 | จำนวนที่ส่งคิวตรวจสอบ |
| rejected | INT | DEFAULT 0 | จำนวนที่ถูกปฏิเสธ |
| errors | INT | DEFAULT 0 | จำนวนที่เกิด Error |
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | วันที่บันทึก |
17. 📈 Monitoring & Maintenance (การดูแลรักษา)
17.1 Database Maintenance
- Daily: Incremental Backup.
- Weekly: Full Backup +
OPTIMIZE TABLEfor heavy tables (audit_logs,notifications). - Monthly: Archive old
audit_logspartitions to cold storage.
17.2 Health Checks
- Monitor
document_number_errorsfor numbering failures. - Monitor
workflow_instancesfor stuck workflows (status = ' IN_PROGRESS '> 7 days). - Check
document_number_countersfor gaps or resets.
18. 📚 Best Practices
1. Naming Conventions
- Tables:
snake_case, plural (e.g.,correspondences,users) - Columns:
snake_case(e.g.,correspondence_number,created_at) - Foreign Keys:
{referenced_table_singular}_id(e.g.,project_id,user_id) - Junction Tables:
{table1}_{table2}(e.g.,correspondence_tags)
2. Timestamp Columns
ทุกตารางควรมี:
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMPupdated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
3. Soft Delete
ใช้ deleted_at DATETIME NULL แทนการลบจริง:
-- Soft delete
UPDATE correspondences SET deleted_at = NOW() WHERE id = 1;
-- Query active records
SELECT * FROM correspondences WHERE deleted_at IS NULL;
4. JSON Field Guidelines
- ใช้สำหรับข้อมูลที่ไม่ต้อง Query บ่อย
- สร้าง Virtual Columns สำหรับ fields ที่ต้อง Index
- Validate ด้วย JSON Schema
- Document structure ใน Data Dictionary
19. 🤖 AI Gateway Tables (ADR-018, ADR-020)
เพิ่มใน v1.9.0 — Task BE-AI-02 | ตารางสำหรับ AI Intelligence Integration
19.1 migration_logs
วัตถุประสงค์: ติดตามสถานะการประมวลผล AI สำหรับเอกสารแต่ละรายการ ใช้ในกระบวนการ Legacy Migration และ New Ingestion
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
INT AUTO_INCREMENT | NO | Internal PK — ห้าม expose ใน API (ADR-019) |
uuid |
UUID | NO | Public Identifier (UUIDv7) — ใช้ใน API response เป็น publicId |
source_file |
VARCHAR(255) | NO | Path หรือ publicId ของไฟล์ต้นทาง |
source_metadata |
JSON | YES | Metadata จากแหล่งต้นทาง (Excel, manual input) |
ai_extracted_metadata |
JSON | YES | Metadata ที่ AI สกัดได้ — ประกอบด้วย subject, date, discipline, drawingReference, contractNumber |
confidence_score |
DECIMAL(3,2) | YES | คะแนนความมั่นใจของ AI (0.00–1.00) ตาม ADR-020 Confidence Strategy |
status |
ENUM | NO | สถานะปัจจุบัน (ดู State Machine ด้านล่าง) |
admin_feedback |
TEXT | YES | ความเห็นของ Admin ผู้ตรวจสอบ |
reviewed_by |
INT | YES | FK → users.user_id — Admin ที่ทำการตรวจสอบ |
reviewed_at |
TIMESTAMP | YES | เวลาที่ Admin ตรวจสอบ |
created_at |
TIMESTAMP | NO | วันที่สร้าง record |
updated_at |
TIMESTAMP | NO | วันที่แก้ไขล่าสุด (Auto-update) |
Status Enum Values
| Status | Description | Action Required |
|---|---|---|
PENDING_REVIEW |
รอ Admin ตรวจสอบ (Default) | Admin ต้องตรวจสอบและเปลี่ยนสถานะ |
VERIFIED |
Admin ยืนยันแล้ว พร้อม Import | ระบบนำเข้าหรือรอ Batch Import |
IMPORTED |
นำเข้าสู่ระบบสำเร็จ (Terminal State) | ไม่สามารถแก้ไขได้อีก |
FAILED |
ล้มเหลว — AI หรือ Validation ไม่ผ่าน | สามารถ Retry ได้ (→ PENDING_REVIEW) |
State Machine (Status Transitions)
PENDING_REVIEW ──→ VERIFIED ──→ IMPORTED (terminal)
│ │
↓ ↓
FAILED ──────→ PENDING_REVIEW (retry)
กฎ:
IMPORTEDเป็น Terminal State — ห้ามเปลี่ยนสถานะ- Admin สามารถเปลี่ยนได้เฉพาะ
PENDING_REVIEW → VERIFIEDหรือPENDING_REVIEW → FAILED FAILED → PENDING_REVIEWสำหรับ Retry
ai_extracted_metadata JSON Schema
{
"subject": "string — หัวเรื่องเอกสาร",
"date": "YYYY-MM-DD — วันที่เอกสาร",
"discipline": "Civil | Mechanical | Electrical | Architectural",
"drawingReference": "string — รหัสแบบอ้างอิง",
"contractNumber": "string — เลขสัญญา",
"discrepancies": ["string — รายการที่ไม่สอดคล้องกับต้นทาง"]
}
19.1 migration_review_queue
วัตถุประสงค์: ตาราง 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) |
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
- Staging Area — ข้อมูลที่ประมวลผลผ่าน n8n จะถูกส่งเข้าตารางนี้เสมอ
- Record Lifecycle — Record ไม่ถูกลบหลัง Import — เปลี่ยน status เป็น IMPORTED เก็บไว้ตลอดเพื่อ Debug
- Status Transitions — PENDING → IMPORTED หรือ PENDING → REJECTED
- 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 |
effective_profile |
VARCHAR(50) | YES | ExecutionProfile ที่ backend กำหนด: interactive|standard|quality|deep-analysis (Feature-235) |
canonical_model |
VARCHAR(50) | YES | Canonical model identity: np-dms-ai หรือ np-dms-ocr (Feature-235, ADR-023) |
snapshot_params_json |
LONGTEXT | YES | Runtime parameters snapshot ณ เวลา dispatch — ใช้จริงใน Ollama call (FR-A09, Feature-235) |
model_type |
VARCHAR(50) | YES | ประเภท OCR/LLM model ที่ใช้ เช่น tesseract, typhoon-ocr-3b |
vram_usage_mb |
INT | YES | VRAM ที่ใช้จริง (MB) ณ เวลาประมวลผล |
cache_hit |
TINYINT(1) | NO | 1 = ผลลัพธ์มาจาก Redis cache, 0 = OCR ใหม่ |
ai_suggestion_json |
LONGTEXT | YES | AI suggested metadata |
human_override_json |
LONGTEXT | 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)
- KEY idx_ai_audit_model_type (model_type)
- KEY idx_ai_audit_effective_profile (effective_profile)
- KEY idx_ai_audit_canonical_model (canonical_model)
- CONSTRAINT fk_ai_audit_confirmed_by_user FOREIGN KEY (confirmed_by_user_id) REFERENCES users (user_id) ON DELETE SET NULL
Business Rules
- Development Feedback Log — เป็น log สำหรับวิเคราะห์และปรับปรุงคุณภาพโมเดล AI ไม่ใช่ Compliance Audit Trail
- Data Retention — เก็บไว้ตลอดอายุโครงการ (~5-10 ปี) — Admin สามารถ Hard Delete ได้ผ่าน Frontend
- RBAC — เฉพาะ Role SYSTEM_ADMIN เท่านั้นที่ลบได้ — การลบทุกครั้งต้องบันทึกใน audit_logs (action: 'AI_AUDIT_LOG_DELETED')
19.3 ai_available_models
วัตถุประสงค์: เก็บรายการโมเดล AI ที่ให้เลือกใช้งานในระบบ (ADR-027, ADR-034)
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
INT AUTO_INCREMENT | NO | ID ของตาราง |
model_name |
VARCHAR(100) | NO | ชื่อโมเดล เช่น gemma4:e2b, gemma4:e4b |
model_version |
VARCHAR(50) | NO | เวอร์ชั่นของโมเดล |
description |
VARCHAR(500) | YES | รายละเอียดโมเดล |
vram_gb |
DECIMAL(4,2) | YES | VRAM ที่ใช้โดยประมาณ (GB) |
is_active |
TINYINT(1) | NO | สถานะใช้งาน |
is_default |
TINYINT(1) | NO | โมเดลเริ่มต้น |
created_by |
INT | YES | ผู้สร้าง |
updated_by |
INT | YES | ผู้แก้ไขล่าสุด |
created_at |
DATETIME(3) | NO | วันที่สร้าง |
updated_at |
DATETIME(3) | NO | วันที่แก้ไขล่าสุด |
deleted_at |
DATETIME(3) | YES | วันที่ลบ (Soft Delete) |
Indexes:
- PRIMARY KEY (id)
- UNIQUE KEY uk_model_name (model_name)
- KEY idx_is_active (is_active)
- KEY idx_is_default (is_default)
19.4 ai_prompts
วัตถุประสงค์: ตาราง versioned prompt templates สำหรับ OCR extraction (ADR-029)
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
INT AUTO_INCREMENT | NO | ID ภายใน (ไม่ expose ใน API) |
public_id |
UUID | NO | UUID Public Identifier (ADR-019) |
prompt_type |
VARCHAR(50) | NO | ประเภท prompt เช่น ocr_extraction |
version_number |
INT | NO | เลข version ต่อเนื่องต่อ prompt_type (1, 2, 3...) |
template |
TEXT | NO | prompt template ที่มี {{ocr_text}} placeholder บังคับ |
field_schema |
LONGTEXT | YES | definition ของ fields ที่คาดหวังในผลลัพธ์ JSON |
is_active |
TINYINT(1) | NO | 1 = version นี้ใช้งานจริงทั้ง sandbox และ migrate-document (1 per prompt_type) |
test_result_json |
LONGTEXT | YES | ผลลัพธ์ JSON จาก sandbox run ล่าสุด (auto-save โดย processor) |
manual_note |
TEXT | YES | หมายเหตุ/annotation จาก admin (manual input) |
last_tested_at |
TIMESTAMP | YES | เวลาที่ sandbox รันครั้งล่าสุดสำหรับ version นี้ |
activated_at |
TIMESTAMP | YES | เวลาที่ version นี้ถูก activate เป็น active |
created_by |
INT | NO | user_id ของผู้สร้าง version นี้ |
created_at |
TIMESTAMP | NO | วันที่สร้าง |
context_config |
LONGTEXT | YES | Configuration สำหรับ context ที่ backend ต้องส่งให้ AI (filter, pageSize, language, etc.) |
version |
INT | NO | Optimistic locking version |
Indexes:
- PRIMARY KEY (id)
- UNIQUE KEY uk_type_version (prompt_type, version_number)
- KEY idx_prompt_type_active (prompt_type, is_active)
- KEY created_by (created_by)
- CONSTRAINT ai_prompts_ibfk_1 FOREIGN KEY (created_by) REFERENCES users (user_id)
19.5 ai_execution_profiles
วัตถุประสงค์: ตาราง execution profile parameters สำหรับ np-dms-ai (ADR-025, ADR-027)
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
INT AUTO_INCREMENT | NO | ID ภายใน |
profile_name |
VARCHAR(50) | NO | ชื่อ profile |
canonical_model |
VARCHAR(20) | NO | Model identity (default: np-dms-ai) |
temperature |
DECIMAL(4,3) | NO | LLM temperature |
top_p |
DECIMAL(4,3) | NO | LLM top_p |
max_tokens |
INT | YES | Maximum tokens |
num_ctx |
INT | YES | Context window size |
repeat_penalty |
DECIMAL(5,3) | NO | Repeat penalty |
keep_alive_seconds |
INT | NO | Model keep_alive in seconds |
is_active |
TINYINT(1) | NO | 1 = active; 0 = disabled |
updated_by |
INT | YES | user_id |
updated_at |
TIMESTAMP | NO | วันที่แก้ไขล่าสุด |
created_at |
TIMESTAMP | NO | วันที่สร้าง |
Indexes:
- PRIMARY KEY (id)
- UNIQUE KEY uk_profile_name (profile_name)
- KEY idx_profile_active (profile_name, is_active)
- KEY updated_by (updated_by)
- CONSTRAINT ai_execution_profiles_ibfk_1 FOREIGN KEY (updated_by) REFERENCES users (user_id)
19.6 ai_sandbox_profiles
วัตถุประสงค์: ตาราง sandbox profile parameters
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
INT AUTO_INCREMENT | NO | ID ภายใน |
profile_name |
VARCHAR(50) | NO | ชื่อ profile |
canonical_model |
VARCHAR(20) | NO | Model identity (default: np-dms-ai) |
temperature |
DECIMAL(4,3) | NO | LLM temperature |
top_p |
DECIMAL(4,3) | NO | LLM top_p |
max_tokens |
INT | YES | Maximum tokens |
num_ctx |
INT | NO | Context window size |
repeat_penalty |
DECIMAL(5,3) | NO | Repeat penalty |
keep_alive_seconds |
INT | NO | Model keep_alive in seconds |
updated_by |
INT | YES | user_id |
updated_at |
TIMESTAMP | NO | วันที่แก้ไขล่าสุด |
created_at |
TIMESTAMP | NO | วันที่สร้าง |
Indexes:
- PRIMARY KEY (id)
- UNIQUE KEY uk_profile_name (profile_name)
- KEY idx_ai_sandbox_profile_model (canonical_model)
- KEY updated_by (updated_by)
- CONSTRAINT ai_sandbox_profiles_ibfk_1 FOREIGN KEY (updated_by) REFERENCES users (user_id)
19.7 migration_errors
วัตถุประสงค์: ตาราง Error Log สำหรับ Migration (ลบได้หลัง Migration เสร็จ) (ADR-028)
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
INT AUTO_INCREMENT | NO | ID |
batch_id |
VARCHAR(50) | YES | n8n batch identifier |
document_number |
VARCHAR(100) | YES | เลขที่เอกสาร |
error_type |
ENUM | YES | FILE_NOT_FOUND, MISSING_FILENAME, FILE_ERROR, AI_PARSE_ERROR, API_ERROR, DB_ERROR, SECURITY, UNKNOWN |
error_message |
TEXT | YES | ข้อความ error |
job_id |
VARCHAR(100) | YES | BullMQ Job ID |
raw_ai_response |
TEXT | YES | AI response ดิบ |
created_at |
TIMESTAMP | NO | วันที่สร้าง |
Indexes:
- PRIMARY KEY (id)
- KEY idx_batch_id (batch_id)
- KEY idx_job_id (job_id)
- KEY idx_error_type (error_type)
19.8 document_chunks
วัตถุประสงค์: เก็บ vector metadata สำหรับ RAG ingestion ตาม ADR-022
| Column | Type | Nullable | Description |
|---|---|---|---|
id |
CHAR(36) | NO | UUID = Qdrant point ID |
document_id |
CHAR(36) | NO | FK → attachments.public_id (UUIDv7) |
chunk_index |
INT | NO | ลำดับ chunk ภายใน document |
content |
TEXT | NO | เนื้อหา chunk หลัง PyThaiNLP normalize |
doc_type |
VARCHAR(20) | NO | CORR, RFA, DRAWING, CONTRACT, RPT, TRANS |
doc_number |
VARCHAR(100) | YES | หมายเลขเอกสาร เช่น REF-2026-001 |
revision |
VARCHAR(20) | YES | Revision เช่น Rev.A |
project_code |
VARCHAR(50) | NO | รหัสโครงการ (ใช้ filter) |
project_public_id |
CHAR(36) | NO | UUIDv7 ของโครงการ (Qdrant tenant key) |
version |
VARCHAR(20) | YES | เวอร์ชันเอกสาร เช่น 1.0, 2.1 (ถ้ามี) |
classification |
ENUM | NO | PUBLIC, INTERNAL, CONFIDENTIAL (DEFAULT: INTERNAL) |
embedding_model |
VARCHAR(100) | NO | nomic-embed-text |
created_at |
DATETIME(3) | NO | วันที่สร้าง |
Indexes:
- PRIMARY KEY (id)
- 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)
Business Rules:
- Project Isolation — Qdrant queries MUST include project_public_id filter (compile-time enforcement per ADR-023A)
- Chunk Size — 512 tokens with 64 tokens overlap (ADR-023A RAG embed scope)
- Embedding Model — nomic-embed-text only (ADR-023A 2-model stack)
19.4 Confidence Scoring Strategy (ADR-020)
| Score Range | Action | Description |
|---|---|---|
| 0.95–1.00 | auto_approve |
นำเข้าอัตโนมัติ (Migration mode เท่านั้น) |
| 0.85–0.94 | low_priority_review |
รอ Admin ตรวจสอบ — ลำดับต่ำ |
| 0.60–0.84 | high_priority_review |
รอ Admin ตรวจสอบ — ลำดับสูง |
| < 0.60 | reject |
ปฏิเสธ — Admin ต้องกรอกข้อมูลเอง |
19.4 Intent Classification Tables (ADR-024)
เพิ่มใน v1.9.0 — Feature 224-intent-classification | ตารางสำหรับ Hybrid Intent Classifier (Pattern Match + LLM Fallback)
19.4.1 ai_intent_definitions
วัตถุประสงค์: เก็บ Intent Definitions (ประเภทความตั้งใจของผู้ใช้) สำหรับ Hybrid Intent Classifier ตาม ADR-024
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal PK (ห้าม expose ใน API) |
| public_id | UUID | NOT NULL, UNIQUE, DEFAULT UUID() | UUID Public Identifier (ADR-019) |
| intent_code | VARCHAR(50) | NOT NULL, UNIQUE | รหัส Intent เช่น RAG_QUERY, GET_RFA |
| description_th | VARCHAR(255) | NOT NULL | คำอธิบายภาษาไทย |
| description_en | VARCHAR(255) | NOT NULL | คำอธิบายภาษาอังกฤษ |
| category | ENUM | NOT NULL | read, suggest, utility |
| is_active | BOOLEAN | NOT NULL, DEFAULT TRUE | สถานะการใช้งาน |
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | วันที่สร้าง |
| updated_at | TIMESTAMP | NOT NULL, ON UPDATE CURRENT_TIMESTAMP | วันที่แก้ไขล่าสุด |
Indexes:
- PRIMARY KEY (id)
- UNIQUE KEY uk_intent_public_id (public_id)
- UNIQUE KEY uk_intent_code (intent_code)
- INDEX idx_intent_active (is_active, category)
Business Rules:
- Intent Categories — read (ดึงข้อมูล), suggest (แนะนำ), utility (อื่นๆ)
- Active Status — Intent ที่ไม่ active จะไม่ถูกใช้ในการ classify
- Seed Data — 12 Intent Definitions พร้อม patterns v1 (RAG_QUERY, GET_RFA, GET_DRAWING, ฯลฯ)
19.4.2 ai_intent_patterns
วัตถุประสงค์: เก็บ Patterns (keyword และ regex) สำหรับ Pattern Matching ใน Hybrid Intent Classifier
| Column Name | Data Type | Constraints | Description |
|---|---|---|---|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Internal PK (ห้าม expose ใน API) |
| public_id | UUID | NOT NULL, UNIQUE, DEFAULT UUID() | UUID Public Identifier (ADR-019) |
| intent_code | VARCHAR(50) | NOT NULL, FK | รหัส Intent (FK to ai_intent_definitions) |
| language | ENUM | NOT NULL, DEFAULT 'any' | th, en, any |
| pattern_type | ENUM | NOT NULL, DEFAULT 'keyword' | keyword, regex |
| pattern_value | VARCHAR(255) | NOT NULL | ค่า pattern (keyword หรือ regex) |
| 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, ON UPDATE CURRENT_TIMESTAMP | วันที่แก้ไขล่าสุด |
Indexes:
- PRIMARY KEY (id)
- 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
Business Rules:
- Pattern Types — keyword (ตรงตัว), regex (regular expression)
- Priority — ยิ่งน้อยยิ่งสำคัญ (ตัวอย่าง: keyword=10, regex=5)
- Language — th (ไทย), en (อังกฤษ), any (ทุกภาษา)
- Seed Data — ~45 patterns สำหรับ 12 Intents (ยกเว้น FALLBACK)
- LLM Fallback — เมื่อไม่ match pattern ไหนเลย → ใช้ LLM (gemma4:e4b Q8_0) ตาม ADR-023A
20. 📖 Glossary (คำศัพท์)
- RFA: Request for Approval (เอกสารขออนุมัติ)
- Transmittal: Document Transmittal Sheet (ใบนำส่งเอกสาร)
- Shop Drawing: แบบก่อสร้างที่ผู้รับเหมาจัดทำ
- Contract Drawing: แบบสัญญา (แบบตั้งต้น)
- Revision: ฉบับแก้ไข (0, 1, 2, A, B, C)
- Originator: ผู้จัดทำ/ผู้ส่งเอกสาร
- Recipient: ผู้รับเอกสาร
- Workflow: กระบวนการทำงาน/อนุมัติ
- Discipline: สาขางาน (เช่น โยธา, สถาปัตย์, ไฟฟ้า)
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)
versioncolumn used for optimistic locking (ADR-002)attachmentsJSON 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()และ flipis_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