260322:1648 Correct Coresspondence / Doing RFA / Correct CI
CI Pipeline / build (push) Failing after 12m41s
Build and Deploy / deploy (push) Failing after 2m44s

This commit is contained in:
admin
2026-03-22 16:48:12 +07:00
parent e5deedb42e
commit 11984bfa29
683 changed files with 105251 additions and 29068 deletions
@@ -5,11 +5,13 @@
**Version:** 1.8.0
**Decision Makers:** Development Team, DevOps Engineer
**Related Documents:**
- [Legacy Data Migration Plan](../03-Data-and-Storage/03-04-legacy-data-migration.md)
- [n8n Migration Setup Guide](../03-Data-and-Storage/03-05-n8n-migration-setup-guide.md)
- [Software Architecture](../02-Architecture/02-02-software-architecture.md)
- [Data Dictionary](../03-Data-and-Storage/03-01-data-dictionary.md)
> **Note:** ADR-017 is clarified and hardened by ADR-018 regarding AI physical isolation. Category Enum system-driven, Idempotency Contract, Duplicate Handling Clarification, Storage Enforcement, Audit Log Enhancement, Review Queue Integration, Revision Drift Protection, Execution Time, Encoding Normalization, Security Hardening, Orchestrator on QNAP, AI Physical Isolation (Desktop Desk-5439).
> **Note:** ADR-017 is clarified and hardened by ADR-018 regarding AI physical isolation. Category Enum system-driven, Idempotency Contract, Duplicate Handling Clarification, Storage Enforcement, Audit Log Enhancement, Review Queue Integration, Revision Drift Protection, Execution Time, Encoding Normalization, Security Hardening, Orchestrator on QNAP, AI Physical Isolation (Desktop Desk-5439).
---
## Context and Problem Statement
@@ -19,6 +21,7 @@
ความท้าทายหลักคือ **Data Integrity และความถูกต้องของ Metadata** เนื่องจากข้อมูลเก่ามีโอกาสเกิด Human Error เราจึงต้องการ AI ช่วย Validate ก่อนนำเข้า
การส่งข้อมูลขึ้น Cloud AI Provider มีปัญหา 2 ประการ:
1. **Data Privacy:** เอกสารก่อสร้างท่าเรือเป็นความลับ ห้ามออกนอกเครือข่าย
2. **Cost:** ~$0.010.03 ต่อ Record = อาจสูงถึง $600 สำหรับ 20,000 records
@@ -44,6 +47,7 @@
**Pros:** ไม่ต้องจัดหา Hardware เพิ่ม, AI ฉลาดสูง
**Cons:**
- ❌ ผิดนโยบาย Data Privacy
- ❌ ค่าใช้จ่ายสูง (~$600)
- ❌ Code สกปรก ปะปนกับ Source Code หลัก
@@ -53,12 +57,14 @@
**Pros:** เร็ว ไม่มีค่าใช้จ่าย
**Cons:**
- ❌ ความแม่นยำต่ำ ตรวจได้แค่ Format
- ❌ ต้องใช้ Manual Review จำนวนมาก
### Option 3: Local AI Model (Ollama) + n8n ⭐ (Selected)
**Pros:**
- ✅ Privacy Guaranteed
- ✅ Zero Cost
- ✅ Clean Architecture
@@ -67,6 +73,7 @@
- ✅ Structured Output ด้วย JSON Schema
**Cons:**
- ❌ ต้องเปิด Desktop ทิ้งไว้ดูแล GPU Temperature
- ❌ Model เล็กอาจแม่นน้อยกว่า Cloud AI → ต้องมี Human Review Queue
@@ -82,19 +89,19 @@
## Implementation Summary
| Component | รายละเอียด |
| ---------------------- | ------------------------------------------------------------------------------- |
| Migration Orchestrator | n8n (Docker บน QNAP NAS) |
| AI Model Primary | Ollama `llama3.2:3b` (Validation, Summarization, Tagging) |
| AI Model Fallback | Ollama `mistral:7b-instruct-q4_K_M` |
| Hardware | QNAP NAS (Orchestrator) + Desktop Desk-5439 (AI Processing, RTX 2060 SUPER 8GB) |
| DB Lookup (n8n) | n8n ทำการ Query `project_id`, `organization_id` และดึง `Tags` จาก DB ให้ AI |
| Component | รายละเอียด |
| ---------------------- | ------------------------------------------------------------------------------------------------------------ |
| Migration Orchestrator | n8n (Docker บน QNAP NAS) |
| AI Model Primary | Ollama `llama3.2:3b` (Validation, Summarization, Tagging) |
| AI Model Fallback | Ollama `mistral:7b-instruct-q4_K_M` |
| Hardware | QNAP NAS (Orchestrator) + Desktop Desk-5439 (AI Processing, RTX 2060 SUPER 8GB) |
| DB Lookup (n8n) | n8n ทำการ Query `project_id`, `organization_id` และดึง `Tags` จาก DB ให้ AI |
| Data Ingestion | 1. Staging ลง `migration_review_queue` -> 2. กดยืนยันผ่าน Frontend Management UI -> 3. Final Commit ผ่าน API |
| Concurrency (n8n) | Sequential — Batch Size 50-100 ป้องกัน DB Connection Overload |
| Checkpoint | MariaDB `migration_progress` และการใช้ `ON DUPLICATE KEY UPDATE` ใน Staging |
| Fallback | Auto-switch Model เมื่อ Error ≥ Threshold |
| Storage | Two-Phase Storage: 1. `POST /api/storage/upload` (Temp) -> 2. Commit ภายหลัง |
| Expected Runtime | ~16.6 ชั่วโมง (~3–4 คืน) สำหรับ 20,000 records |
| Concurrency (n8n) | Sequential — Batch Size 50-100 ป้องกัน DB Connection Overload |
| Checkpoint | MariaDB `migration_progress` และการใช้ `ON DUPLICATE KEY UPDATE` ใน Staging |
| Fallback | Auto-switch Model เมื่อ Error ≥ Threshold |
| Storage | Two-Phase Storage: 1. `POST /api/storage/upload` (Temp) -> 2. Commit ภายหลัง |
| Expected Runtime | ~16.6 ชั่วโมง (~3–4 คืน) สำหรับ 20,000 records |
---
@@ -115,14 +122,14 @@
}
```
| Field | Type | คำอธิบาย |
| -------------------- | ------------------------- | --------------------------- |
| `is_valid` | boolean | เอกสารผ่านการตรวจสอบหรือไม่ (เปรียบเทียบ subject vs pdf) |
| `confidence` | float (0.01.0) | ความมั่นใจของ AI |
| `suggested_category` | string (enum จาก Backend) | หมวดหมู่ที่ AI แนะนำ |
| `detected_issues` | string[] | รายการปัญหา (array ว่างถ้าไม่มี) |
| `suggested_title` | string \| null | Title ที่แก้ไขแล้ว หรือ null |
| `summary` | string | สรุปเนื้อหา 4-5 ประโยค สำหรับใส่ใน `body` |
| Field | Type | คำอธิบาย |
| -------------------- | ------------------------- | ---------------------------------------------------------------- |
| `is_valid` | boolean | เอกสารผ่านการตรวจสอบหรือไม่ (เปรียบเทียบ subject vs pdf) |
| `confidence` | float (0.01.0) | ความมั่นใจของ AI |
| `suggested_category` | string (enum จาก Backend) | หมวดหมู่ที่ AI แนะนำ |
| `detected_issues` | string[] | รายการปัญหา (array ว่างถ้าไม่มี) |
| `suggested_title` | string \| null | Title ที่แก้ไขแล้ว หรือ null |
| `summary` | string | สรุปเนื้อหา 4-5 ประโยค สำหรับใส่ใน `body` |
| `suggested_tags` | array of objects | รายการ Tags ที่จับคู่ได้ หรือ แนะนำให้สร้างใหม่ (`is_new: true`) |
> ⚠️ **Patch:** `suggested_category` ต้องตรงกับ System Enum จาก `GET /api/meta/categories` เท่านั้น — ห้าม hardcode Category List ใน Prompt
@@ -133,13 +140,13 @@
**ข้อมูลทุกชุดจาก n8n จะต้องถูกส่งเข้าตาราง `migration_review_queue` เสมอ** โดยจัดสถานะเบื้องต้นตาม Confidence:
| ระดับ Confidence | สถานะใน Review Queue |
| ------------------------------- | --------------------------------------- |
| `>= 0.85` และ `is_valid = true` | `PENDING` (พร้อมให้ Admin เลือก Batch Import) |
| `0.600.84` | `PENDING` (ไฮไลต์แจ้งให้ Admin ตรวจสอบข้อมูลก่อน) |
| `< 0.60` หรือ `is_valid = false` | `REJECTED` (รอให้ Admin แก้ไขข้อมูล Manual) |
| AI Parse Error | ส่งไป Error Log + Trigger Fallback Logic |
| Revision Drift | `PENDING` พร้อมระบุ reason: "Revision drift" |
| ระดับ Confidence | สถานะใน Review Queue |
| -------------------------------- | ------------------------------------------------- |
| `>= 0.85` และ `is_valid = true` | `PENDING` (พร้อมให้ Admin เลือก Batch Import) |
| `0.600.84` | `PENDING` (ไฮไลต์แจ้งให้ Admin ตรวจสอบข้อมูลก่อน) |
| `< 0.60` หรือ `is_valid = false` | `REJECTED` (รอให้ Admin แก้ไขข้อมูล Manual) |
| AI Parse Error | ส่งไป Error Log + Trigger Fallback Logic |
| Revision Drift | `PENDING` พร้อมระบุ reason: "Revision drift" |
> ⚠️ **Tag Review:** ข้อมูลใดที่มี `is_new: true` ใน `suggested_tags` จะถูกบังคับให้ Admin ตรวจสอบบน Frontend UI ก่อน เพื่อป้องกัน AI สร้าง Tags ขยะซ้ำซ้อน
@@ -148,11 +155,13 @@
## Idempotency Contract
**HTTP Header ที่ต้องส่งทุก Request:**
```
Idempotency-Key: <document_number>:<batch_id>
```
**Backend Logic:**
```
IF idempotency_key EXISTS in import_transactions → RETURN HTTP 200 (no action)
ELSE → Process normally → INSERT import_transactions → RETURN HTTP 201
@@ -167,6 +176,7 @@ ELSE → Process normally → INSERT import_transactions → RETURN HTTP 201
Bypass Duplicate **Validation Error**
Hard Rules:
- ❌ Migration Token ไม่สามารถ Overwrite Revision ที่มีอยู่
- ❌ Migration Token ไม่สามารถ Delete Revision ก่อนหน้า
- ✅ Migration Token trigger Revision increment logic ตามปกติเท่านั้น
@@ -176,6 +186,7 @@ Hard Rules:
## Storage Governance (Two-Phase Storage)
**ข้อห้าม:**
```
❌ mv /data/dms/staging_ai/TCC-COR-0001.pdf /final/path/...
```
@@ -183,6 +194,7 @@ Hard Rules:
**ข้อบังคับ (Two-Phase Strategy):**
**Phase 1: Temp Upload (โดย n8n)**
```
✅ POST /api/storage/upload
(Upload ไฟล์ PDF ได้ผลลัพธ์เป็น attachment_id เช่น 1024)
@@ -190,12 +202,14 @@ Hard Rules:
```
**Phase 2: Final Commit (โดย Frontend UI -> Backend API)**
```
✅ POST /api/migration/commit_batch
body: { queue_ids: [1, 2, 3] }
```
Backend จะทำหน้าที่:
1. อ่านข้อมูลจาก `migration_review_queue` ซึ่งมี `temp_attachment_id` อยู่
2. นำ `temp_attachment_id` ไปเชื่อมกับเอกสาร (Link to `correspondence_attachments`)
3. เปลี่ยนสถานะอัพเดต `is_temporary = FALSE`
@@ -206,8 +220,8 @@ Backend จะทำหน้าที่:
## Review Queue Contract & Frontend UI
- `migration_review_queue` เป็น **Staging Table หลัก** (ไม่ auto-ingest ข้ามขั้นตอนนี้)
- ห้ามสร้าง Correspondence record จนกว่า Admin จะสั่ง Execute การ Import จากหน้าจอ
- **Approval Flow:**
- ห้ามสร้าง Correspondence record จนกว่า Admin จะสั่ง Execute การ Import จากหน้าจอ
- **Approval Flow:**
1. N8N Insert เข้า `migration_review_queue` (พร้อม `temp_attachment_id`)
2. Admin Review บน Frontend UI (ให้ความสำคัญกับการเช็ค `is_new: true` Tags)
3. Admin เลือก Rows แล้วกด **"Execute Import"**
@@ -218,6 +232,7 @@ Backend จะทำหน้าที่:
## Revision Drift Protection
ถ้า Excel มี revision column:
```
IF excel_revision != current_db_revision + 1
→ ROUTE ไป Review Queue พร้อม reason: "Revision drift"
@@ -227,20 +242,21 @@ IF excel_revision != current_db_revision + 1
## Execution Time Estimate
| Parameter | ค่า |
| -------------------- | ---------------------------- |
| Delay ระหว่าง Request | 2 วินาที |
| Inference Time (avg) | ~1 วินาที |
| เวลาต่อ Record | ~3 วินาที |
| จำนวน Record | 20,000 |
| เวลารวม | ~60,000 วินาที (~16.6 ชั่วโมง) |
| **จำนวนคืนที่ต้องใช้** | **~34 คืน** (รัน 22:0006:00) |
| Parameter | ค่า |
| ---------------------- | ------------------------------ |
| Delay ระหว่าง Request | 2 วินาที |
| Inference Time (avg) | ~1 วินาที |
| เวลาต่อ Record | ~3 วินาที |
| จำนวน Record | 20,000 |
| เวลารวม | ~60,000 วินาที (~16.6 ชั่วโมง) |
| **จำนวนคืนที่ต้องใช้** | **~34 คืน** (รัน 22:0006:00) |
---
## Encoding Normalization
ก่อน Ingestion ทุกครั้ง:
- Excel data → Convert เป็น **UTF-8**
- Filename → Normalize เป็น **NFC UTF-8** ป้องกันปัญหาภาษาไทยเพี้ยนข้าม OS
@@ -274,7 +290,7 @@ IF excel_revision != current_db_revision + 1
### 🟢 A. Infrastructure Validation
| Check | Expected | ✅ |
| Check | Expected | ✅ |
| ---------------------------- | ------------- | --- |
| Ollama `/api/tags` reachable | HTTP 200 | |
| Backend `/health` OK | HTTP 200 | |
@@ -286,7 +302,7 @@ IF excel_revision != current_db_revision + 1
### 🟢 B. Security Validation
| Check | Expected | ✅ |
| Check | Expected | ✅ |
| -------------------------------------- | -------- | --- |
| Migration Token expiry ≤ 7 days | Verified | |
| Token IP Whitelist = NAS IP only | Verified | |
@@ -298,7 +314,7 @@ IF excel_revision != current_db_revision + 1
### 🟢 C. Data Integrity Validation
| Check | Expected | ✅ |
| Check | Expected | ✅ |
| ---------------------------------------------- | -------------- | --- |
| Enum fetched from `/api/meta/categories` | Not hardcoded | |
| `Idempotency-Key` header enforced | Verified | |
@@ -309,7 +325,7 @@ IF excel_revision != current_db_revision + 1
### 🟢 D. Workflow Validation (Dry Run 20 Records)
| Check | Expected | ✅ |
| Check | Expected | ✅ |
| ---------------------------------------- | ------------ | --- |
| JSON parse success rate | > 95% | |
| Confidence distribution reasonable | Mean 0.70.9 | |
@@ -321,7 +337,7 @@ IF excel_revision != current_db_revision + 1
### 🟢 E. Performance Validation
| Check | Expected | ✅ |
| Check | Expected | ✅ |
| ------------------------------- | -------- | --- |
| 10 records processed < 1 minute | Verified | |
| GPU temp < 80°C | Verified | |
@@ -330,7 +346,7 @@ IF excel_revision != current_db_revision + 1
### 🟢 F. Rollback Test (Mandatory)
| Check | Expected | ✅ |
| Check | Expected | ✅ |
| ------------------------------------ | ----------------- | --- |
| Disable token works | is_active = false | |
| Delete `SYSTEM_IMPORT` records works | COUNT = 0 | |
@@ -343,12 +359,14 @@ IF excel_revision != current_db_revision + 1
## GO / NO-GO Criteria
**GO ถ้า:**
- A, B, C ทุก Check = PASS
- Dry run error rate < 10%
- JSON parse failure < 5%
- Revision conflict < 3%
**NO-GO ถ้า:**
- Enum mismatch (Category hardcoded)
- Idempotency ไม่ได้ implement
- Storage bypass (move file โดยตรง)
@@ -358,8 +376,8 @@ IF excel_revision != current_db_revision + 1
## Final Architectural Assessment
| Area | Status |
| ------------------ | ------------------------------------------------ |
| Area | Status |
| ------------------ | ------------------------------------------------- |
| ADR Compliance | ✅ Fully aligned |
| Security | ✅ Hardened (IP Whitelist, Rate Limit, Docker) |
| Data Integrity | ✅ Controlled (Idempotency, Revision Drift, Enum) |
@@ -368,4 +386,4 @@ IF excel_revision != current_db_revision + 1
---
*สำหรับขั้นตอนปฏิบัติงานแบบละเอียด ดูที่ `03-04-legacy-data-migration.md` และ `03-05-n8n-migration-setup-guide.md`*
_สำหรับขั้นตอนปฏิบัติงานแบบละเอียด ดูที่ `03-04-legacy-data-migration.md` และ `03-05-n8n-migration-setup-guide.md`_