260223:1415 20260223 nextJS & nestJS Best pratices
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
All checks were successful
Build and Deploy / deploy (push) Successful in 4m44s
This commit is contained in:
@@ -0,0 +1,313 @@
|
||||
# 4. Access Control & RBAC Matrix (V1.8.0)
|
||||
|
||||
---
|
||||
title: 'Access Control & RBAC Matrix'
|
||||
version: 1.8.0
|
||||
status: APPROVED
|
||||
owner: Nattanin Peancharoen / Development Team
|
||||
last_updated: 2026-02-23
|
||||
related:
|
||||
- specs/02-architecture/02-01-system-architecture.md
|
||||
- specs/03-implementation/03-02-backend-guidelines.md
|
||||
- specs/07-database/07-01-data-dictionary-v1.8.0.md
|
||||
- specs/05-decisions/ADR-005-redis-usage-strategy.md
|
||||
- specs/05-decisions/ADR-001-unified-workflow-engine.md
|
||||
references:
|
||||
- [RBAC Implementation](../../99-archives/ADR-004-rbac-implementation.md)
|
||||
- [Access Control](../../99-archives/01-04-access-control.md)
|
||||
---
|
||||
|
||||
## 4.1. Overview and Problem Statement
|
||||
|
||||
LCBP3-DMS จัดการสิทธิ์การเข้าถึงข้อมูลที่ซับซ้อน ได้แก่:
|
||||
- **Multi-Organization**: หลายองค์กรใช้ระบบร่วมกัน แต่ต้องแยกข้อมูลตามบริบท
|
||||
- **Project-Based**: โครงการสามารถมีหลายสัญญาย่อย (Contracts)
|
||||
- **Hierarchical Permissions**: สิทธิ์ระดับบนสามารถครอบคลุมระดับล่าง
|
||||
- **Dynamic Roles**: สิทธิ์สามารถปรับเปลี่ยน Role หรือเพิ่ม Role พิเศษได้โดยไม่ต้อง Deploy ระบบใหม่.
|
||||
|
||||
Users และ Organizations สามารถเข้าดูหรือแก้ไขเอกสารได้จากสิทธิ์ที่ได้รับ ระบบออกแบบด้วย **4-Level Hierarchical Role-Based Access Control (RBAC)** ដើម្បីรองรับความซับซ้อนนี้.
|
||||
|
||||
### Key Requirements
|
||||
1. User หนึ่งคนสามารถมีหลาย Roles ในหลาย Scopes
|
||||
2. Permission Inheritance (Global → Organization → Project → Contract)
|
||||
3. Fine-grained Access Control (เช่น "อนุญาตให้ดู Correspondence เฉพาะใน Project A รวมถึง Contract ภายใต้ Project A เท่านั้น")
|
||||
4. Performance (Check permission ผ่าน Redis ต้องเร็ว < 10ms)
|
||||
|
||||
---
|
||||
|
||||
## 4.2. Permission Hierarchy & Enforcement
|
||||
|
||||
### 4-Level Scope Hierarchy
|
||||
|
||||
การออกแบบสิทธิ์ครอบคลุม Scope ลำดับขั้นดังนี้:
|
||||
|
||||
```text
|
||||
Global (ทั้งระบบ)
|
||||
│
|
||||
├─ Organization (ระดับองค์กร)
|
||||
│ ├─ Project (ระดับโครงการ)
|
||||
│ │ └─ Contract (ระดับสัญญา)
|
||||
│ │
|
||||
│ └─ Project B
|
||||
│ └─ Contract B
|
||||
│
|
||||
└─ Organization 2
|
||||
└─ Project C
|
||||
```
|
||||
|
||||
**Permission Enforcement:**
|
||||
- เมื่อตรวจสอบสิทธิ์ ระบบจะพิจารณาสิทธิ์จากทุก Level ที่ผู้ใช้มี และใช้สิทธิ์ที่ **"ครอบคลุมที่สุด (Most Permissive)"** ในบริบท (Context) นั้นมาเป็นเกณฑ์.
|
||||
- *Example*: User A เป็น `Viewer` ในองค์กร (ระดับ Organization Level) แต่มอบหมายหน้าที่ให้เป็น `Editor` ในตำแหน่งของ Project X. เมื่อ User A ดำเนินการในบริบท Context ของ Project X (หรือ Contract ที่อยู่ใต้ Project X), User A จะสามารถทำงานด้วยสิทธิ์ `Editor` ทันที.
|
||||
|
||||
---
|
||||
|
||||
## 4.3. Role and Scope Summary
|
||||
|
||||
| Role | Scope | Description | Key Permissions |
|
||||
| :------------------- | :----------- | :------------------------- | :-------------------------------------------------------------------------------------------------------------------- |
|
||||
| **Superadmin** | Global | System administrator | Do everything in the system, manage organizations, manage global data |
|
||||
| **Org Admin** | Organization | Organization administrator | Manage users in the organization, manage roles/permissions within the organization, view organization reports |
|
||||
| **Document Control** | Organization | Document controller | Add/edit/delete documents, set document permissions within the organization |
|
||||
| **Editor** | Organization | Document editor | Edit documents that have been assigned to them |
|
||||
| **Viewer** | Organization | Document viewer | View documents that have access permissions |
|
||||
| **Project Manager** | Project | Project manager | Manage members in the project (add/delete/assign roles), create/manage contracts in the project, view project reports |
|
||||
| **Contract Admin** | Contract | Contract administrator | Manage users in the contract, manage roles/permissions within the contract, view contract reports |
|
||||
|
||||
### Master Data Management Authority
|
||||
|
||||
| Master Data | Manager | Scope |
|
||||
| :-------------------------------------- | :------------------------------ | :------------------------------ |
|
||||
| Document Type (Correspondence, RFA) | **Superadmin** | Global |
|
||||
| Document Status (Draft, Approved, etc.) | **Superadmin** | Global |
|
||||
| Shop Drawing Category | **Project Manager** | Project (สร้างใหม่ได้ภายในโครงการ) |
|
||||
| Tags | **Org Admin / Project Manager** | Organization / Project |
|
||||
| Custom Roles | **Superadmin / Org Admin** | Global / Organization |
|
||||
| Document Numbering Formats | **Superadmin / Admin** | Global / Organization |
|
||||
|
||||
---
|
||||
|
||||
## 4.4. Onboarding Workflow
|
||||
|
||||
- **4.4.1. Create Organization**
|
||||
- **Superadmin** สร้าง Organization ใหม่ (e.g. Company A)
|
||||
- **Superadmin** แต่งตั้ง User อย่างน้อย 1 คน ให้เป็น **Org Admin** หรือ **Document Control**
|
||||
- **4.4.2. Add Users to Organization**
|
||||
- **Org Admin** เพิ่ม users (`Editor`, `Viewer`) เข้าสู่งองค์กร
|
||||
- **4.4.3. Assign Users to Project**
|
||||
- **Project Manager** เชิญ/กำหนดผู้ใช้เข้าสู่ Project. ในขั้นตอนนี้จะกำหนด **Project Role**
|
||||
- **4.4.4. Assign Users to Contract**
|
||||
- **Contract Admin** เลือก users จาก Project และมอบหมายหน้าที่เจาะจงใน Contract ขั้นตอนนี้จะกำหนด **Contract Role**
|
||||
- **4.4.5. Security Onboarding**
|
||||
- บังคับ Users to change password เป็นครั้งแรก
|
||||
- ฝึกอบรม (Security awareness training) สำหรับ users ที่มีสิทธิ์สูงระดับแอดมิน.
|
||||
- บันทึก Audit Log ทุกเหตุการณ์เกี่ยวกับการมอบหมาย/ตั้งค่า Permissions.
|
||||
|
||||
---
|
||||
|
||||
## 4.5. Implementation Details
|
||||
|
||||
### 4.5.1 Database Schema (RBAC Tables)
|
||||
|
||||
```sql
|
||||
-- Role Definitions with Scope
|
||||
CREATE TABLE roles (
|
||||
role_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
role_name VARCHAR(100) NOT NULL,
|
||||
scope ENUM('Global', 'Organization', 'Project', 'Contract') NOT NULL,
|
||||
description TEXT,
|
||||
is_system BOOLEAN DEFAULT FALSE
|
||||
);
|
||||
|
||||
-- Granular Permissions
|
||||
CREATE TABLE permissions (
|
||||
permission_id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
permission_name VARCHAR(100) NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
module VARCHAR(50),
|
||||
-- Scope Reference ENUM includes CONTRACT
|
||||
scope_level ENUM('GLOBAL', 'ORG', 'PROJECT', 'CONTRACT')
|
||||
);
|
||||
|
||||
-- Role -> Permissions Mapping
|
||||
CREATE TABLE role_permissions (
|
||||
role_id INT,
|
||||
permission_id INT,
|
||||
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
|
||||
);
|
||||
|
||||
-- User Role Assignments with Context Map
|
||||
CREATE TABLE user_assignments (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
user_id INT NOT NULL,
|
||||
role_id INT NOT NULL,
|
||||
organization_id INT NULL,
|
||||
project_id INT NULL,
|
||||
contract_id INT NULL,
|
||||
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
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,
|
||||
|
||||
-- ควบคุมโครงสร้าง: ไม่ให้ระบุ Scope เลื่อนลอยข้ามขั้นต้องมีชั้นเดียวชัดเจน
|
||||
CONSTRAINT chk_scope CHECK (
|
||||
(organization_id IS NOT NULL AND project_id IS NULL AND contract_id IS NULL) OR
|
||||
(organization_id IS NULL AND project_id IS NOT NULL AND contract_id IS NULL) OR
|
||||
(organization_id IS NULL AND project_id IS NULL AND contract_id IS NOT NULL) OR
|
||||
(organization_id IS NULL AND project_id IS NULL AND contract_id IS NULL)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
### 4.5.2 Setup CASL Ability Rules
|
||||
|
||||
เพื่อให้ **"การสืบทอดสิทธิ์ (Inheritance Logic)"** ทำงานได้ถูกต้อง เช่น บทบาทระดับ `Project Manager` ให้แผ่คลุมไปถึงทรัพยากรระดับ `Contract` ด้านในด้วย (Logic `project_id -> all embedded contracts`).
|
||||
|
||||
```typescript
|
||||
// ability.factory.ts
|
||||
import { AbilityBuilder, PureAbility } from '@casl/ability';
|
||||
|
||||
export type AppAbility = PureAbility<[string, any]>;
|
||||
|
||||
@Injectable()
|
||||
export class AbilityFactory {
|
||||
async createForUser(user: User): Promise<AppAbility> {
|
||||
const { can, cannot, build } = new AbilityBuilder<AppAbility>(PureAbility);
|
||||
|
||||
const assignments = await this.getUserAssignments(user.user_id);
|
||||
|
||||
for (const assignment of assignments) {
|
||||
const role = await this.getRole(assignment.role_id);
|
||||
const permissions = await this.getRolePermissions(role.role_id);
|
||||
|
||||
for (const permission of permissions) {
|
||||
// e.g. 'correspondence.create', 'project.view'
|
||||
const [subject, action] = permission.permission_name.split('.');
|
||||
|
||||
// Apply Scope conditions based on the Role's specified scope level
|
||||
switch (role.scope) {
|
||||
case 'Global':
|
||||
// ได้รับสิทธิ์กับองค์ประกอบทั้งหมด
|
||||
can(action, subject);
|
||||
break;
|
||||
|
||||
case 'Organization':
|
||||
// อนุญาตการ Action ทั้งในองค์กร และโปรเจกต์ภายใต้องค์กร
|
||||
can(action, subject, { organization_id: assignment.organization_id });
|
||||
// Advanced Case: In some queries mapped to Projects/Contracts, support fallback checks
|
||||
// can(action, subject, { '__orgIdFallback': assignment.organization_id });
|
||||
break;
|
||||
|
||||
case 'Project':
|
||||
// สืบทอดสิทธิ์ไปยัง Project ID นั้นๆ เสมอ และสิทธิ์ครอบคลุมถึงทุก Contracts ที่ผูกกับ Project นี้
|
||||
can(action, subject, { project_id: assignment.project_id });
|
||||
break;
|
||||
|
||||
case 'Contract':
|
||||
// จำกัดเฉพาะใน Contract นั้น ตรงเป้าหมาย
|
||||
can(action, subject, { contract_id: assignment.contract_id });
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return build();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.5.3 Token Management & Redis Permission Cache
|
||||
|
||||
- **Payload Optimization**: JWT Access Token ให้เก็บเฉพาะ `userId` และ ข้อมูล Sessions ขั้นต้น. โครงสร้าง Permissions List ปริมาณมากจะ**ไม่อยู่ใน Token**.
|
||||
- **Permission Caching**: นำโครงสร้างสิทธิ์ทั้งหมด (Ability Rules ที่ประกอบแล้วจาก 4.5.2) ไป Cache ไว้เป็น Key ภายใต้ฐานข้อมูล **Redis** โดยคงระยะการตั้ง TTL ประมาณ 30 นาที `1800` วินาที. ลดขนาด Payload ลง และเพื่อเป้าหมาย Performance ที่ `< 10ms`.
|
||||
|
||||
### 4.5.4 Permission Guard Enforcement (NestJS)
|
||||
|
||||
```typescript
|
||||
// permission.guard.ts
|
||||
@Injectable()
|
||||
export class PermissionGuard implements CanActivate {
|
||||
constructor(
|
||||
private reflector: Reflector,
|
||||
private abilityFactory: AbilityFactory,
|
||||
private redis: Redis
|
||||
) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const permission = this.reflector.get<string>('permission', context.getHandler());
|
||||
if (!permission) return true; // Route without specific permission requirements
|
||||
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const user = request.user;
|
||||
|
||||
// Check Redis cache first
|
||||
const cacheKey = `user:${user.user_id}:permissions`;
|
||||
let abilityRules = await this.redis.get(cacheKey);
|
||||
|
||||
if (!abilityRules) {
|
||||
const newAbility = await this.abilityFactory.createForUser(user);
|
||||
abilityRules = newAbility.rules;
|
||||
// Store in Cache (1800ms TTL)
|
||||
await this.redis.set(cacheKey, JSON.stringify(abilityRules), 'EX', 1800);
|
||||
} else {
|
||||
abilityRules = JSON.parse(abilityRules);
|
||||
}
|
||||
|
||||
const ability = new PureAbility(abilityRules);
|
||||
const [action, subject] = permission.split('.');
|
||||
|
||||
// Evaluate mapped payload / resource parameters directly into CASL engine
|
||||
const resource = { ...request.params, ...request.body };
|
||||
return ability.can(action, subject, resource);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Controller Example Usage:
|
||||
|
||||
```typescript
|
||||
@Controller('correspondences')
|
||||
@UseGuards(JwtAuthGuard, PermissionGuard)
|
||||
export class CorrespondenceController {
|
||||
|
||||
@Post()
|
||||
@RequirePermission('correspondence.create')
|
||||
async create(@Body() dto: CreateCorrespondenceDto) {
|
||||
// Accessible only when CASL Evaluate "correspondence.create" successfully fits the parameters limits (Project/Contract scope check)
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4.6. Cache Invalidation Strategy
|
||||
|
||||
เพื่อให้ระบบ Security Update ได้รวดเร็วและปลอดภัย ต้องมีกลไกสั่งรีเซ็ตสิทธิ์ (Invalidation):
|
||||
|
||||
1. **When Role Assignemnt Modified**: เมื่อช้อมูลในตาราง `user_assignments` (เช่นลบ/เพิ่ม user ระดับโปรเจกต์) หรือตาราง `role_permissions` (สลับสิทธิ์ของกลุ่ม) เกิดการแก้ไขในฐานข้อมูลแล้ว.
|
||||
2. **Execute Invalidation**: API Services ต้องทำการลบ Cache เก่าที่ฝั่งขัดข้องทันที ผ่านคำสั่ง `DEL user:{user_id}:permissions` ณ ฝั่ง Redis.
|
||||
3. ระบบจะทำงานโปรโตซ้ำ (Cold Boot) เข้ามาหยิบ DB -> สร้าง CASL Factory ใหม่ ใน First API Request อีกครั้งในทันที.
|
||||
|
||||
---
|
||||
|
||||
## 4.7. Appendix: ADR-004 Decision History & Justification
|
||||
|
||||
ส่วนอ้างอิงและประวัติศาสตร์การตัดสินใจพิจารณาในอดีต (Reference) ก่อนการนำมาปรับใช้ร่าง Architecture ฉบับ V1.8.0.
|
||||
|
||||
### Considered Options Before Version 1.5.0
|
||||
1. **Option 1**: Simple Role-Based (No Scope)
|
||||
- *Pros*: Very simple implementation, Easy to understand
|
||||
- *Cons*: ไม่รองรับ Multi-organization, Superadmin เห็นข้อมูลองค์กรอื่นมั่วข้ามกลุ่ม
|
||||
2. **Option 2**: Organization-Only Scope
|
||||
- *Pros*: แยกข้อมูลระหว่าง Organizations ได้ชัดเจน
|
||||
- *Cons*: ไม่รองรับระดับ Sub-level Permissions (Project/Contract) ไม่เพียงพอกับ Business Rule.
|
||||
3. **Option 3**: **4-Level Hierarchical RBAC (Selected)**
|
||||
- *Pros*: ครอบคลุม Use Case สูงสุด (Maximum Flexibility), รองรับ Hierarchy สืบทอดสิทธิ์ (Inheritance), ขอบเขตจำกัดข้อมูลอย่างรัดกุมระดับ Contract (Data Isolation).
|
||||
- *Cons*: Complexity ทางด้านการ Implement, Invalidate Caching หางานให้ฝั่ง Operations, Developer Learning Curve.
|
||||
|
||||
**Decision Rationale:**
|
||||
เลือกแนวทางที่ 3 รองรับความต้องการของ Construction Projects ที่มีการแบ่งกลุ่มรับเหมาช่วงย่อยระดับ Contracts ต่อเนื่อง (Future proof). แก้ไขปัญหา Performance Cons ด้วยแนวคิดการประจุ Redis Caching ข้ามขั้ว และล้าง Invalidation เฉพาะเมื่อ Triggering Database Updates (อ้างอิงหัวข้อ 4.6).
|
||||
@@ -0,0 +1,786 @@
|
||||
# 3.11 Document Numbering Management & Implementation (V1.8.0)
|
||||
|
||||
---
|
||||
title: 'Specifications & Implementation Guide: Document Numbering System'
|
||||
version: 1.8.0
|
||||
status: draft
|
||||
owner: Nattanin Peancharoen / Development Team
|
||||
last_updated: 2026-02-23
|
||||
related:
|
||||
- specs/01-requirements/01-01-objectives.md
|
||||
- specs/02-architecture/README.md
|
||||
- specs/03-implementation/03-02-backend-guidelines.md
|
||||
- specs/04-operations/04-08-document-numbering-operations.md
|
||||
- specs/07-database/07-01-data-dictionary-v1.8.0.md
|
||||
- specs/05-decisions/ADR-002-document-numbering-strategy.md
|
||||
Clean Version v1.8.0 – Scope of Changes:
|
||||
- รวม Functional Requirements เข้ากับ Implementation Guide
|
||||
- เลือกใช้ Single Numbering System (Option A) `document_number_counters` เป็น Authoritative Counter
|
||||
- เพิ่ม Idempotency Key, Reservation (Two-Phase Commit)
|
||||
- Number State Machine, Pattern Validate UTF-8, Cancellation Rule (Void/Replace)
|
||||
references:
|
||||
- [Document Numbering](../../99-archives/01-03.11-document-numbering.md)
|
||||
- [Document Numbering](../../99-archives/03-04-document-numbering.md)
|
||||
---
|
||||
|
||||
> **📖 เอกสารฉบับนี้เป็นการรวมรายละเอียดจาก `01-03.11-document-numbering.md` และ `03-04-document-numbering.md` ให้อยู่ในฉบับเดียวสำหรับใช้อ้างอิงการออกแบบเชิง Functional และการพัฒนา Technology Component**
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview & วัตถุประสงค์ (Purpose)
|
||||
|
||||
ระบบ Document Numbering สำหรับสร้างเลขที่เอกสารอัตโนมัติที่มีความเป็นเอกลักษณ์ (unique) และสามารถติดตามได้ (traceable) สำหรับเอกสารทุกประเภทในระบบ LCBP3-DMS
|
||||
|
||||
### 1.1 Requirements Summary & Scope
|
||||
- **Auto-generation**: สร้างเลขที่อัตโนมัติ ไม่ซ้ำ (Unique) ยืดหยุ่น
|
||||
- **Configurable Templates**: รองรับแบบฟอร์มกำหนดค่า สำหรับโปรเจกต์ ประเภทเอกสาร ฯลฯ
|
||||
- **Uniqueness Guarantee**: การันตี Uniqueness ใน Concurrent Environment (Race Conditions)
|
||||
- **Manual override**: รองรับการ Import หรือ Override สำหรับเอกสารเก่า
|
||||
- **Cancelled/Void Handling**: ไม่นำหมายเลขที่ใช้ หรือ Cancel/Void กลับมาใช้ใหม่ (No reuse)
|
||||
- **Audit Logging**: บันทึกเหตุการณ์ Operation ทั้งหมดอย่างละเอียดครบถ้วน 7 ปี
|
||||
|
||||
### 1.2 Technology Stack
|
||||
| Component | Technology |
|
||||
| ----------------- | -------------------- |
|
||||
| Backend Framework | NestJS 10.x |
|
||||
| ORM | TypeORM 0.3.x |
|
||||
| Database | MariaDB 11.8 |
|
||||
| Cache/Lock | Redis 7.x + Redlock |
|
||||
| Message Queue | BullMQ |
|
||||
| Monitoring | Prometheus + Grafana |
|
||||
|
||||
### 1.3 Architectural Decision (AD-DN-001)
|
||||
ระบบเลือกใช้ **Option A**:
|
||||
- `document_number_counters` เป็น Core / Authoritative Counter System.
|
||||
- `document_numbering_configs` (หรือ `document_number_formats`) ใช้เฉพาะกำหนดระเบียบเลข (Template format) และ Permission.
|
||||
- เหตุผล: ลดความซ้ำซ้อน, ป้องกัน Counter Mismatch, Debug ง่าย, Ops เคลียร์.
|
||||
|
||||
---
|
||||
|
||||
## 2. Business Rules & Logic
|
||||
|
||||
### 2.1 Counter Logic & Reset Policy
|
||||
|
||||
การนับเลขจะแยกตาม **Counter Key** ที่ประกอบด้วยหลายส่วน ซึ่งขึ้นกับประเภทเอกสาร
|
||||
* `(project_id, originator_organization_id, recipient_organization_id, correspondence_type_id, sub_type_id, rfa_type_id, discipline_id, reset_scope)`
|
||||
|
||||
| Document Type | Reset Policy | Counter Key Format / Details |
|
||||
| ---------------------------------- | ------------------ | ------------------------------------------------------------------------------ |
|
||||
| Correspondence (LETTER, MEMO, RFI) | Yearly reset | `(project_id, originator, recipient, type_id, 0, 0, 0, 'YEAR_2025')` |
|
||||
| Transmittal | Yearly reset | `(project_id, originator, recipient, type_id, sub_type_id, 0, 0, 'YEAR_2025')` |
|
||||
| RFA | No reset | `(project_id, originator, 0, type_id, 0, rfa_type_id, discipline_id, 'NONE')` |
|
||||
| Drawing | Separate Namespace | `DRAWING::<project>::<contract>` (ไม่ได้ใช้ counter rules เดียวกัน) |
|
||||
|
||||
### 2.2 Format Templates & Supported Tokens
|
||||
|
||||
**Supported Token Types**:
|
||||
* `{PROJECT}`: รหัสโครงการ (เช่น `LCBP3`)
|
||||
* `{ORIGINATOR}`: รหัสองค์กรส่ง (เช่น `คคง.`)
|
||||
* `{RECIPIENT}`: รหัสองค์กรรับหลัก (เช่น `สคฉ.3`) *ไม่ใช้กับ RFA
|
||||
* `{CORR_TYPE}`: รหัสประเภทเอกสาร (เช่น `RFA`, `LETTER`)
|
||||
* `{SUB_TYPE}`: ประเภทย่อย (สำหรับ Transmittal)
|
||||
* `{RFA_TYPE}`: รหัสประเภท RFA (เช่น `SDW`, `RPT`)
|
||||
* `{DISCIPLINE}`: รหัสสาขาวิชา (เช่น `STR`, `CV`)
|
||||
* `{SEQ:n}`: Running Number ตามจำนวนหลัก `n` ลบด้วยศูนย์นำหน้า
|
||||
* `{YEAR:B.E.}`, `{YEAR:A.D.}`, `{YYYY}`, `{YY}`, `{MM}`: สัญลักษณ์บอกเวลาและปฏิทิน.
|
||||
* `{REV}`: Revision Code (เช่น `A`, `B`)
|
||||
|
||||
**Token Validation Grammar**
|
||||
```ebnf
|
||||
TEMPLATE := TOKEN ("-" TOKEN)*
|
||||
TOKEN := SIMPLE | PARAM
|
||||
SIMPLE := "{PROJECT}" | "{ORIGINATOR}" | "{RECIPIENT}" | "{CORR_TYPE}" | "{DISCIPLINE}" | "{RFA_TYPE}" | "{REV}" | "{YYYY}" | "{YY}" | "{MM}"
|
||||
PARAM := "{SEQ:" DIGIT+ "}"
|
||||
DIGIT := "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
|
||||
```
|
||||
|
||||
### 2.3 Character & Format Rules (BR-DN-002, BR-DN-003)
|
||||
- Document number **must be printable UTF‑8** (Thai, English, Numbers, `-`, `_`, `.`). ไม่อนุญาต Control characters, newlines.
|
||||
- ต้องยาวระหว่าง 10 ถึง 50 ตัวอักษร
|
||||
- ต้องกำหนด Token `{SEQ:n}` ลำดับที่ exactly once. ห้ามเป็น Unknown token ใดๆ.
|
||||
|
||||
### 2.4 Number State Machine & Idempotency
|
||||
1. **States Lifecycle**: `RESERVED` (TTL 5 mins) → `CONFIRMED` → `VOID` / `CANCELLED`. Document ที่ Confirmed แล้วสามารถมีพฤติกรรม VOID ในอนาคตเพื่อแทนที่ด้วยเอกสารใหม่ได้ การ Request จะได้ Document ชุดใหม่ทดแทนต่อเนื่องทันที. ห้าม Reuse เลขเดิมโดยสิ้นเชิง.
|
||||
2. **Idempotency Key Support**: ทุก API ในการ Generator จำเป็นต้องระบุ HTTP Header `Idempotency-Key` ป้องกันระบบสร้างเลขเบิ้ล (Double Submission). ถ้าระบบได้รับคู่ Request + Key ชุดเดิม จะ Response เลขที่เอกสารเดิม.
|
||||
|
||||
---
|
||||
|
||||
## 3. Functional Requirements
|
||||
|
||||
* **FR-DN-001 (Sequential Auto-generation)**: ระบบตอบกลับความรวดเร็วที่ระดับ < 100ms โดยที่ทน Concurrent ได้ ทนต่อปัญหา Duplicate
|
||||
* **FR-DN-002 (Configurable)**: สามารถเปลี่ยนรูปแบบเทมเพลตผ่านระบบ Admin ได้ด้วยการ Validation ก่อน حفظ
|
||||
* **FR-DN-003 (Scope-based sequences)**: รองรับ Scope แยกระดับเอกสาร
|
||||
* **FR-DN-004 (Manual Override)**: ระบบรองรับการตั้งเลขด้วยตนเองสำหรับ Admin Level พร้อมระบุเหตุผลผ่าน Audit Trails เป็นหลักฐานสำคัญ (Import Legacy, Correction)
|
||||
* **FR-DN-005 (Bulk Import)**: รับเข้าระบบจากไฟล์ Excel/CSV และแก้ไข Counters Sequence ต่อเนื่องพร้อมเช็ค Duplicates.
|
||||
* **FR-DN-006 (Skip Cancelled)**: ไม่ให้สิทธิ์ดึงเอกสารยกเลิกใช้งานซ้ำ. คงรักษาสภาพ Audit ต่อไป.
|
||||
* **FR-DN-007 (Void & Replace)**: เปลี่ยน Status เลขเป็น VOID ไม่มีการ Delete. Reference Link เอกสารใหม่ที่เกิดขึ้นทดแทนอิงตาม `voided_from_id`.
|
||||
* **FR-DN-008 (Race Condition Prevention)**: จัดการ Race Condition (RedLock + Database Pessimistic Locking) เพื่อ Guarantee zero duplicate numbers ที่ Load 1000 req/s.
|
||||
* **FR-DN-009 (Two-phase Commit)**: แบ่งการออกเลขเป็นช่วง Reserve 5 นาที แล้วค่อยเรียก Confirm หลังจากได้เอกสารที่ Submit เรียบร้อย (ลดอาการเลขหาย/เลขฟันหลอที่ยังไม่ถูกใช้).
|
||||
* **FR-DN-010/011 (Audit / Metrics Alerting)**: Audit ทุกๆ Step / Transaction ไว้ใน DB ให้เสิร์ชได้เกิน 7 ปี. ส่งแจ้งเตือนถ้า Sequence เริ่มเต็ม (เกิน 90%) หรือ Rate error เริ่มสูง.
|
||||
|
||||
---
|
||||
|
||||
## 4. Module System & Code Architecture
|
||||
|
||||
### 4.1 Folder Structure
|
||||
```
|
||||
backend/src/modules/document-numbering/
|
||||
├── document-numbering.module.ts
|
||||
├── controllers/ # document-numbering.controller.ts, admin.controller.ts, metrics.controller.ts
|
||||
├── services/ # Main orchestration (document-numbering.service.ts), lock, counter, reserve, format, audit
|
||||
├── entities/ # DB Entities mappings
|
||||
├── dto/ # DTOs
|
||||
├── validators/ # template.validator.ts
|
||||
├── guards/ # manual-override.guard.ts
|
||||
└── jobs/ # counter-reset.job.ts (Cron)
|
||||
```
|
||||
|
||||
### 4.2 Sequence Process Architecture
|
||||
|
||||
**1. Number Generation Process Diagram**
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant S as NumberingService
|
||||
participant L as LockService
|
||||
participant CS as CounterService
|
||||
participant DB as Database
|
||||
participant R as Redis
|
||||
|
||||
C->>S: generateDocumentNumber(dto)
|
||||
S->>L: acquireLock(counterKey)
|
||||
L->>R: REDLOCK acquire
|
||||
R-->>L: lock acquired
|
||||
L-->>S: lock handle
|
||||
S->>CS: incrementCounter(counterKey)
|
||||
CS->>DB: BEGIN TRANSACTION
|
||||
CS->>DB: SELECT FOR UPDATE
|
||||
CS->>DB: UPDATE last_number
|
||||
CS->>DB: COMMIT
|
||||
DB-->>CS: newNumber
|
||||
CS-->>S: sequence
|
||||
S->>S: formatNumber(template, seq)
|
||||
S->>L: releaseLock()
|
||||
L->>R: REDLOCK release
|
||||
S-->>C: documentNumber
|
||||
```
|
||||
|
||||
**2. Two-Phase Commit Pattern (Reserve / Confirm)**
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant C as Client
|
||||
participant RS as ReservationService
|
||||
participant SS as SequenceService
|
||||
participant R as Redis
|
||||
|
||||
Note over C,R: Phase 1: Reserve
|
||||
C->>RS: reserve(documentType)
|
||||
RS->>SS: getNextSequence()
|
||||
SS-->>RS: documentNumber
|
||||
RS->>R: SETEX reservation:{token} (TTL: 5min)
|
||||
RS-->>C: {token, documentNumber, expiresAt}
|
||||
|
||||
Note over C,R: Phase 2: Confirm
|
||||
C->>RS: confirm(token)
|
||||
RS->>R: GET reservation:{token}
|
||||
R-->>RS: reservationData
|
||||
RS->>R: DEL reservation:{token}
|
||||
RS-->>C: documentNumber (confirmed)
|
||||
```
|
||||
|
||||
### 4.3 Lock Service (Redis Redlock Example)
|
||||
```typescript
|
||||
@Injectable()
|
||||
export class DocumentNumberingLockService {
|
||||
constructor(@InjectRedis() private readonly redis: Redis) {
|
||||
this.redlock = new Redlock([redis], { driftFactor: 0.01, retryCount: 5, retryDelay: 100, retryJitter: 50 });
|
||||
}
|
||||
|
||||
async acquireLock(counterKey: CounterKey): Promise<Redlock.Lock> {
|
||||
const lockKey = this.buildLockKey(counterKey);
|
||||
return await this.redlock.acquire([lockKey], /* ttl */ 5000); // 5 sec retention
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 Counter Service & Transaction Strategy (Optimistic Example)
|
||||
```typescript
|
||||
async incrementCounter(counterKey: CounterKey): Promise<number> {
|
||||
const MAX_RETRIES = 2;
|
||||
for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
|
||||
try {
|
||||
return await this.dataSource.transaction(async (manager) => {
|
||||
const counter = await manager.findOne(DocumentNumberCounter, { /* rules */ });
|
||||
if (!counter) {
|
||||
// Insert base 1
|
||||
return 1;
|
||||
}
|
||||
counter.lastNumber += 1;
|
||||
await manager.save(counter); // Trigger Optimistic Version Check
|
||||
return counter.lastNumber;
|
||||
});
|
||||
} catch (error) {
|
||||
// Loop if version mismatch
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Database Schema Details
|
||||
|
||||
### 5.1 Format Storage & Counters
|
||||
```sql
|
||||
-- Format Template Configuration
|
||||
CREATE TABLE document_number_formats (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
project_id INT NOT NULL,
|
||||
correspondence_type_id INT NULL,
|
||||
format_template VARCHAR(100) NOT NULL,
|
||||
reset_sequence_yearly TINYINT(1) DEFAULT 1,
|
||||
UNIQUE KEY idx_unique_project_type (project_id, correspondence_type_id),
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- Active Sequences
|
||||
CREATE TABLE document_number_counters (
|
||||
project_id INT NOT NULL,
|
||||
correspondence_type_id INT NULL,
|
||||
originator_organization_id INT NOT NULL,
|
||||
recipient_organization_id INT NOT NULL DEFAULT 0,
|
||||
sub_type_id INT DEFAULT 0,
|
||||
rfa_type_id INT DEFAULT 0,
|
||||
discipline_id INT DEFAULT 0,
|
||||
reset_scope VARCHAR(20) NOT NULL,
|
||||
last_number INT DEFAULT 0 NOT NULL,
|
||||
version INT DEFAULT 0 NOT NULL,
|
||||
PRIMARY KEY (... 8 fields combination ...),
|
||||
INDEX idx_counter_lookup (project_id, correspondence_type_id, reset_scope)
|
||||
);
|
||||
```
|
||||
|
||||
### 5.2 Two-Phase Commit Reservations
|
||||
```sql
|
||||
CREATE TABLE document_number_reservations (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
token VARCHAR(36) NOT NULL UNIQUE COMMENT 'UUID v4',
|
||||
document_number VARCHAR(100) NOT NULL UNIQUE,
|
||||
status ENUM('RESERVED', 'CONFIRMED', 'CANCELLED', 'VOID') NOT NULL DEFAULT 'RESERVED',
|
||||
document_id INT NULL COMMENT 'Link after confirmed',
|
||||
expires_at DATETIME(6) NOT NULL,
|
||||
... Context fields ...
|
||||
);
|
||||
```
|
||||
|
||||
### 5.3 Audit Trails & Error Logs
|
||||
```sql
|
||||
CREATE TABLE document_number_audit (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
document_number VARCHAR(100) NOT NULL,
|
||||
operation ENUM('RESERVE', 'CONFIRM', 'CANCEL', 'MANUAL_OVERRIDE', 'VOID', 'GENERATE') NOT NULL,
|
||||
counter_key JSON NOT NULL,
|
||||
is_success BOOLEAN DEFAULT TRUE,
|
||||
lock_wait_ms INT,
|
||||
... Extraneous Auditing fields ...
|
||||
);
|
||||
|
||||
CREATE TABLE document_number_errors (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
error_type ENUM('LOCK_TIMEOUT','VERSION_CONFLICT','DB_ERROR','REDIS_ERROR','VALIDATION_ERROR','SEQUENCE_EXHAUSTED') NOT NULL,
|
||||
stack_trace TEXT,
|
||||
context_data JSON
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Endpoints & API Definitions
|
||||
|
||||
| Endpoint | Method | Permission | Meaning |
|
||||
| -------------------------------------------- | -------- | ------------------------ | ------------------------------------------ |
|
||||
| `/document-numbering/preview` | POST | `correspondence.read` | Preview Formats |
|
||||
| `/document-numbering/reserve` | POST | `correspondence.create` | Reserve Token & Logic Number (2PC) |
|
||||
| `/document-numbering/confirm` | POST | `correspondence.create` | Confirm Reservation (2PC) |
|
||||
| `/document-numbering/cancel` | POST | `correspondence.create` | Manual or System Cancel Reservation |
|
||||
| `/admin/document-numbering/manual-override` | POST | `system.manage_settings` | Inject / Legacy specific number generation |
|
||||
| `/admin/document-numbering/void-and-replace` | POST | `system.manage_settings` | Replace document marking old logic as VOID |
|
||||
| `/admin/document-numbering/bulk-import` | POST | `system.manage_settings` | Batch Migration Numbers from legacy |
|
||||
| `/admin/document-numbering/templates` | GET/POST | `system.manage_settings` | Setting Pattern Configurations |
|
||||
|
||||
---
|
||||
|
||||
## 7. Security, Error Handling & Concurrency Checklists
|
||||
|
||||
**Fallback Strategy for Database Lock Failures**:
|
||||
1. System attempt to acquire `Redlock`.
|
||||
2. Redis Down? → **Fallback DB-only Lock** Transaction Layer.
|
||||
3. Redis Online but Timeout `>3 Times`? → Return HTTP 503 (Exponential Backoff).
|
||||
4. Save Failed via TypeORM Version Confilct? → Retry Loop `2 Times`, otherwise Return 409 Conflict.
|
||||
|
||||
**Rate Limiting Profiles**:
|
||||
* Single User Threshold: `10 requests/minute`.
|
||||
* Specific Request IP: `50 requests/minute`.
|
||||
|
||||
**Authorization Policies**:
|
||||
* `Super Admin` เท่านั้นที่บังคับสั่ง `Reset Counter` ให้เริ่มนับศูนย์ได้เมื่อฉุกเฉิน.
|
||||
* กฎ Audit Log System ระบุชัดเจนว่าต้อง Retain Information ไม่ต่ำกว่า 7 ปี.
|
||||
|
||||
## 8. Monitoring / Observability (Prometheus + Grafana)
|
||||
|
||||
| Condition Event | Prometheus Counter Target | Severity Reaction |
|
||||
| ---------------------- | -------------------------------- | ----------------------------------------------------------------- |
|
||||
| Utilization `>95%` | `numbering_sequence_utilization` | 🔴 **CRITICAL** (PagerDuty/Slack). Limit Maximum sequence reached. |
|
||||
| Redis Downtime `>1M` | Health System | 🔴 **CRITICAL** (PagerDuty/Slack) |
|
||||
| Lock Latency p95 `>1s` | `numbering_lock_wait_seconds` | 🟡 **WARNING** (Slack). Redis connection struggling. |
|
||||
| Error Rate Burst | `numbering_lock_failures_total` | 🟡 **WARNING** (Slack). Need investigation logs check |
|
||||
|
||||
---
|
||||
|
||||
## 9. Testing & Rollout Migration Strategies
|
||||
|
||||
### 9.1 Test Approach Requirements
|
||||
* **Unit Tests**: Template Tokens Validations, Error handling retry, Optimistic locking checks.
|
||||
* **Concurrency Integration Test**: Assert >1000 requests without double generating numbers simultaneously per `project_id`.
|
||||
* **E2E Load Sequence Flow**: Mocking bulk API loads over 500 requests per seconds via CI/CD load pipeline.
|
||||
|
||||
### 9.2 Data Rollout Plan (Legacy Legacy Import)
|
||||
1. Dump out existing Sequence numbers (Extracted Document Strings).
|
||||
2. Write validation script for Sequence Max Counts.
|
||||
3. Import to Table `document_number_counters` as Manual Override API Method context (`FR-DN-004`).
|
||||
4. Re-Verify next sequence logic output `+1` count seamlessly integrates to `nextNumber()`.
|
||||
|
||||
---
|
||||
|
||||
**Best Practices Checklist**
|
||||
- ✅ **DO**: Two-Phase Commit (`Reserve` + `Confirm`) ให้เป็น Routine System Concept.
|
||||
- ✅ **DO**: DB Fallback เมื่อ Redis ดาวน์. ให้ Availability สูงสุด ไม่หยุดทำงาน.
|
||||
- ✅ **DO**: ข้ามเลขที่ยกเลิกทั้งหมดห้ามมีการ Re-Use เด็ดขาด ไม่ว่าเจตนาใดๆ.
|
||||
- ❌ **DON'T**: ไม่แก้ Sequence จาก DB Console ตรงๆ โดยเด็ดขาด.
|
||||
- ❌ **DON'T**: ลืม Validate format หรือ Tokens แปลกๆ ในระบบ Template (ต้อง Check Grammar ตลอดไป).
|
||||
- ❌ **DON'T**: ลืมเขียน Idempotency-Key สำหรับ Request.
|
||||
|
||||
---
|
||||
**Document Version**: 1.8.0
|
||||
**Created By**: Development Team
|
||||
**End of Document**
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 10. Operations & Infrastructure Guidelines
|
||||
|
||||
### 1. Performance Requirements
|
||||
|
||||
### 1.1. Response Time Targets
|
||||
|
||||
| Metric | Target | Measurement |
|
||||
| ---------------- | -------- | ------------------------ |
|
||||
| 95th percentile | ≤ 2 วินาที | ตั้งแต่ request ถึง response |
|
||||
| 99th percentile | ≤ 5 วินาที | ตั้งแต่ request ถึง response |
|
||||
| Normal operation | ≤ 500ms | ไม่มี retry |
|
||||
|
||||
### 1.2. Throughput Targets
|
||||
|
||||
| Load Level | Target | Notes |
|
||||
| -------------- | ----------- | ------------------------ |
|
||||
| Normal load | ≥ 50 req/s | ใช้งานปกติ |
|
||||
| Peak load | ≥ 100 req/s | ช่วงเร่งงาน |
|
||||
| Burst capacity | ≥ 200 req/s | Short duration (< 1 min) |
|
||||
|
||||
### 1.3. Availability SLA
|
||||
|
||||
- **Uptime**: ≥ 99.5% (excluding planned maintenance)
|
||||
- **Maximum downtime**: ≤ 3.6 ชั่วโมง/เดือน (~ 8.6 นาที/วัน)
|
||||
- **Recovery Time Objective (RTO)**: ≤ 30 นาที
|
||||
- **Recovery Point Objective (RPO)**: ≤ 5 นาที
|
||||
|
||||
|
||||
### 2. Infrastructure Setup
|
||||
|
||||
### 2.1. Database Configuration
|
||||
|
||||
#### MariaDB Connection Pool
|
||||
|
||||
```typescript
|
||||
// ormconfig.ts
|
||||
{
|
||||
type: 'mysql',
|
||||
host: process.env.DB_HOST,
|
||||
port: parseInt(process.env.DB_PORT),
|
||||
username: process.env.DB_USERNAME,
|
||||
password: process.env.DB_PASSWORD,
|
||||
database: process.env.DB_DATABASE,
|
||||
extra: {
|
||||
connectionLimit: 20, // Pool size
|
||||
queueLimit: 0, // Unlimited queue
|
||||
acquireTimeout: 10000, // 10s timeout
|
||||
retryAttempts: 3,
|
||||
retryDelay: 1000
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### High Availability Setup
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
mariadb-master:
|
||||
image: mariadb:11.8
|
||||
environment:
|
||||
MYSQL_REPLICATION_MODE: master
|
||||
MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
|
||||
volumes:
|
||||
- mariadb-master-data:/var/lib/mysql
|
||||
networks:
|
||||
- backend
|
||||
|
||||
mariadb-replica:
|
||||
image: mariadb:11.8
|
||||
environment:
|
||||
MYSQL_REPLICATION_MODE: slave
|
||||
MYSQL_MASTER_HOST: mariadb-master
|
||||
MYSQL_MASTER_ROOT_PASSWORD: ${DB_ROOT_PASSWORD}
|
||||
volumes:
|
||||
- mariadb-replica-data:/var/lib/mysql
|
||||
networks:
|
||||
- backend
|
||||
```
|
||||
|
||||
### 2.2. Redis Configuration
|
||||
|
||||
#### Redis Sentinel for High Availability
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
redis-master:
|
||||
image: redis:7-alpine
|
||||
command: redis-server --appendonly yes
|
||||
volumes:
|
||||
- redis-master-data:/data
|
||||
networks:
|
||||
- backend
|
||||
|
||||
redis-replica:
|
||||
image: redis:7-alpine
|
||||
command: redis-server --replicaof redis-master 6379 --appendonly yes
|
||||
volumes:
|
||||
- redis-replica-data:/data
|
||||
networks:
|
||||
- backend
|
||||
|
||||
redis-sentinel:
|
||||
image: redis:7-alpine
|
||||
command: >
|
||||
redis-sentinel /etc/redis/sentinel.conf
|
||||
--sentinel monitor mymaster redis-master 6379 2
|
||||
--sentinel down-after-milliseconds mymaster 5000
|
||||
--sentinel failover-timeout mymaster 10000
|
||||
networks:
|
||||
- backend
|
||||
```
|
||||
|
||||
#### Redis Connection Pool
|
||||
|
||||
```typescript
|
||||
// redis.config.ts
|
||||
import IORedis from 'ioredis';
|
||||
|
||||
export const redisConfig = {
|
||||
host: process.env.REDIS_HOST || 'localhost',
|
||||
port: parseInt(process.env.REDIS_PORT) || 6379,
|
||||
password: process.env.REDIS_PASSWORD,
|
||||
maxRetriesPerRequest: 3,
|
||||
enableReadyCheck: true,
|
||||
lazyConnect: false,
|
||||
poolSize: 10,
|
||||
retryStrategy: (times: number) => {
|
||||
if (times > 3) {
|
||||
return null; // Stop retry
|
||||
}
|
||||
return Math.min(times * 100, 3000);
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
### 2.3. Load Balancing
|
||||
|
||||
#### Nginx Configuration
|
||||
|
||||
```nginx
|
||||
# nginx.conf
|
||||
upstream backend {
|
||||
least_conn; # Least connections algorithm
|
||||
server backend-1:3000 max_fails=3 fail_timeout=30s weight=1;
|
||||
server backend-2:3000 max_fails=3 fail_timeout=30s weight=1;
|
||||
server backend-3:3000 max_fails=3 fail_timeout=30s weight=1;
|
||||
|
||||
keepalive 32;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name api.lcbp3.local;
|
||||
|
||||
location /api/v1/document-numbering/ {
|
||||
proxy_pass http://backend;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
proxy_next_upstream error timeout;
|
||||
proxy_connect_timeout 10s;
|
||||
proxy_send_timeout 30s;
|
||||
proxy_read_timeout 30s;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Docker Compose Scaling
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
backend:
|
||||
image: lcbp3-backend:latest
|
||||
deploy:
|
||||
replicas: 3
|
||||
resources:
|
||||
limits:
|
||||
cpus: '1.0'
|
||||
memory: 1G
|
||||
reservations:
|
||||
cpus: '0.5'
|
||||
memory: 512M
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
delay: 5s
|
||||
max_attempts: 3
|
||||
environment:
|
||||
NODE_ENV: production
|
||||
DB_POOL_SIZE: 20
|
||||
networks:
|
||||
- backend
|
||||
```
|
||||
|
||||
|
||||
### 4. Troubleshooting Runbooks
|
||||
|
||||
### 4.1. Scenario: Redis Unavailable
|
||||
|
||||
**Symptoms:**
|
||||
- Alert: `RedisUnavailable`
|
||||
- System falls back to DB-only locking
|
||||
- Performance degraded 30-50%
|
||||
|
||||
**Action Steps:**
|
||||
|
||||
1. **Check Redis status:**
|
||||
```bash
|
||||
docker exec lcbp3-redis redis-cli ping
|
||||
# Expected: PONG
|
||||
```
|
||||
|
||||
2. **Check Redis logs:**
|
||||
```bash
|
||||
docker logs lcbp3-redis --tail=100
|
||||
```
|
||||
|
||||
3. **Restart Redis (if needed):**
|
||||
```bash
|
||||
docker restart lcbp3-redis
|
||||
```
|
||||
|
||||
4. **Verify failover (if using Sentinel):**
|
||||
```bash
|
||||
docker exec lcbp3-redis-sentinel redis-cli -p 26379 SENTINEL masters
|
||||
```
|
||||
|
||||
5. **Monitor recovery:**
|
||||
- Check metric: `docnum_redis_connection_status` returns to 1
|
||||
- Check performance: P95 latency returns to normal (< 500ms)
|
||||
|
||||
### 4.2. Scenario: High Lock Failure Rate
|
||||
|
||||
**Symptoms:**
|
||||
- Alert: `HighLockFailureRate` (> 10%)
|
||||
- Users report "ระบบกำลังยุ่ง" errors
|
||||
|
||||
**Action Steps:**
|
||||
|
||||
1. **Check concurrent load:**
|
||||
```bash
|
||||
# Check current request rate
|
||||
curl http://prometheus:9090/api/v1/query?query=rate(docnum_generation_duration_ms_count[1m])
|
||||
```
|
||||
|
||||
2. **Check database connections:**
|
||||
```sql
|
||||
SHOW PROCESSLIST;
|
||||
-- Look for waiting/locked queries
|
||||
```
|
||||
|
||||
3. **Check Redis memory:**
|
||||
```bash
|
||||
docker exec lcbp3-redis redis-cli INFO memory
|
||||
```
|
||||
|
||||
4. **Scale up if needed:**
|
||||
```bash
|
||||
# Increase backend replicas
|
||||
docker-compose up -d --scale backend=5
|
||||
```
|
||||
|
||||
5. **Check for deadlocks:**
|
||||
```sql
|
||||
SHOW ENGINE INNODB STATUS;
|
||||
-- Look for LATEST DETECTED DEADLOCK section
|
||||
```
|
||||
|
||||
### 4.3. Scenario: Slow Performance
|
||||
|
||||
**Symptoms:**
|
||||
- Alert: `SlowDocumentNumberGeneration`
|
||||
- P95 > 2 seconds
|
||||
|
||||
**Action Steps:**
|
||||
|
||||
1. **Check database query performance:**
|
||||
```sql
|
||||
SELECT * FROM document_number_counters USE INDEX (idx_counter_lookup)
|
||||
WHERE project_id = 2 AND correspondence_type_id = 6 AND current_year = 2025;
|
||||
|
||||
-- Check execution plan
|
||||
EXPLAIN SELECT ...;
|
||||
```
|
||||
|
||||
2. **Check for missing indexes:**
|
||||
```sql
|
||||
SHOW INDEX FROM document_number_counters;
|
||||
```
|
||||
|
||||
3. **Check Redis latency:**
|
||||
```bash
|
||||
docker exec lcbp3-redis redis-cli --latency
|
||||
```
|
||||
|
||||
4. **Check network latency:**
|
||||
```bash
|
||||
ping mariadb-master
|
||||
ping redis-master
|
||||
```
|
||||
|
||||
5. **Review slow query log:**
|
||||
```bash
|
||||
docker exec lcbp3-mariadb-master cat /var/log/mysql/slow.log
|
||||
```
|
||||
|
||||
### 4.4. Scenario: Version Conflicts
|
||||
|
||||
**Symptoms:**
|
||||
- High retry count
|
||||
- Users report "เลขที่เอกสารถูกเปลี่ยน" errors
|
||||
|
||||
**Action Steps:**
|
||||
|
||||
1. **Check concurrent requests to same counter:**
|
||||
```sql
|
||||
SELECT
|
||||
project_id,
|
||||
correspondence_type_id,
|
||||
COUNT(*) as concurrent_requests
|
||||
FROM document_number_audit
|
||||
WHERE created_at > NOW() - INTERVAL 5 MINUTE
|
||||
GROUP BY project_id, correspondence_type_id
|
||||
HAVING COUNT(*) > 10
|
||||
ORDER BY concurrent_requests DESC;
|
||||
```
|
||||
|
||||
2. **Investigate specific counter:**
|
||||
```sql
|
||||
SELECT * FROM document_number_counters
|
||||
WHERE project_id = X AND correspondence_type_id = Y;
|
||||
|
||||
-- Check audit trail
|
||||
SELECT * FROM document_number_audit
|
||||
WHERE counter_key LIKE '%project_id:X%'
|
||||
ORDER BY created_at DESC
|
||||
LIMIT 20;
|
||||
```
|
||||
|
||||
3. **Check for application bugs:**
|
||||
- Review error logs for stack traces
|
||||
- Check if retry logic is working correctly
|
||||
|
||||
4. **Temporary mitigation:**
|
||||
- Increase retry count in application config
|
||||
- Consider manual counter adjustment (last resort)
|
||||
|
||||
|
||||
### 5. Maintenance Procedures
|
||||
|
||||
### 5.1. Counter Reset (Manual)
|
||||
|
||||
**Requires:** SUPER_ADMIN role + 2-person approval
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. **Request approval via API:**
|
||||
```bash
|
||||
POST /api/v1/document-numbering/configs/{configId}/reset-counter
|
||||
{
|
||||
"reason": "เหตุผลที่ชัดเจน อย่างน้อย 20 ตัวอักษร",
|
||||
"approver_1": "user_id",
|
||||
"approver_2": "user_id"
|
||||
}
|
||||
```
|
||||
|
||||
2. **Verify in audit log:**
|
||||
```sql
|
||||
SELECT * FROM document_number_config_history
|
||||
WHERE config_id = X
|
||||
ORDER BY changed_at DESC
|
||||
LIMIT 1;
|
||||
```
|
||||
|
||||
### 5.2. Template Update
|
||||
|
||||
**Best Practices:**
|
||||
|
||||
1. Always test template in staging first
|
||||
2. Preview generated numbers before applying
|
||||
3. Document reason for change
|
||||
4. Template changes do NOT affect existing documents
|
||||
|
||||
**API Call:**
|
||||
```bash
|
||||
PUT /api/v1/document-numbering/configs/{configId}
|
||||
{
|
||||
"template": "{ORIGINATOR}-{RECIPIENT}-{SEQ:4}-{YEAR:B.E.}",
|
||||
"change_reason": "เหตุผลในการเปลี่ยนแปลง"
|
||||
}
|
||||
```
|
||||
|
||||
### 5.3. Database Maintenance
|
||||
|
||||
**Weekly Tasks:**
|
||||
- Check slow query log
|
||||
- Optimize tables if needed:
|
||||
```sql
|
||||
OPTIMIZE TABLE document_number_counters;
|
||||
OPTIMIZE TABLE document_number_audit;
|
||||
```
|
||||
|
||||
**Monthly Tasks:**
|
||||
- Review and archive old audit logs (> 2 years)
|
||||
- Check index usage:
|
||||
```sql
|
||||
SELECT * FROM sys.schema_unused_indexes
|
||||
WHERE object_schema = 'lcbp3_db';
|
||||
```
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
# 👥 Section 5: UI/UX Requirements (ข้อกำหนดด้านผู้ใช้งาน)
|
||||
|
||||
---
|
||||
|
||||
title: 'UI/UX Requirements'
|
||||
version: 1.8.0
|
||||
status: first-draft
|
||||
owner: Nattanin Peancharoen
|
||||
last_updated: 2026-02-23
|
||||
related:
|
||||
|
||||
- specs/02-architecture/data-model.md#correspondence
|
||||
- specs/03-implementation/backend-guidelines.md#correspondencemodule
|
||||
|
||||
---
|
||||
|
||||
## 5.1. Layout หลัก
|
||||
|
||||
- หน้าเว็บใช้รูปแบบ App Shell ที่ประกอบด้วย
|
||||
- Navbar (ส่วนบน): แสดงชื่อระบบ, เมนูผู้ใช้ (Profile), เมนูสำหรับ Document Control/เมนูสำหรับ Admin/Superadmin (จัดการผู้ใช้, จัดการสิทธิ์, และอื่นๆ), และปุ่ม Login/Logout
|
||||
- Sidebar (ด้านข้าง): เป็นเมนูหลักสำหรับเข้าถึงส่วนที่เกี่ยวข้องกับเอกสารทั้งหมด เช่น Dashboard, Correspondences, RFA, Drawings
|
||||
- Main Content Area: พื้นที่สำหรับแสดงเนื้อหาหลักของหน้าที่เลือก
|
||||
|
||||
## 5.2. หน้า Landing Page
|
||||
|
||||
- เป็นหน้าแรกที่แสดงข้อมูลบางส่วนของโครงการสำหรับผู้ใช้ที่ยังไม่ได้ล็อกอิน
|
||||
|
||||
## 5.3. หน้า Dashboard
|
||||
|
||||
- เป็นหน้าแรกหลังจากล็อกอิน ประกอบด้วย
|
||||
- การ์ดสรุปภาพรวม (KPI Cards): แสดงข้อมูลสรุปที่สำคัญขององค์กร เช่น จำนวนเอกสาร, งานที่เกินกำหนด
|
||||
- ตาราง "งานของฉัน" (My Tasks Table): แสดงรายการงานทั้งหมดจาก Circulation ที่ผู้ใช้ต้องดำเนินการ
|
||||
- Security Metrics: แสดงจำนวน files scanned, security incidents, failed login attempts
|
||||
|
||||
## 5.4. การติดตามสถานะ
|
||||
|
||||
- องค์กรสามารถติดตามสถานะเอกสารทั้งของตนเอง (Originator) และสถานะเอกสารที่ส่งมาถึงตนเอง (Recipient)
|
||||
|
||||
## 5.5. การจัดการข้อมูลส่วนตัว (Profile Page)
|
||||
|
||||
- ผู้ใช้สามารถจัดการข้อมูลส่วนตัวและเปลี่ยนรหัสผ่านของตนเองได้
|
||||
|
||||
## 5.6. การจัดการเอกสารทางเทคนิค (RFA)
|
||||
|
||||
- ผู้ใช้สามารถดู RFA ในรูปแบบ Workflow Diagram ทั้งหมดได้ในหน้าเดียว
|
||||
- Interactive History (เพิ่ม): ในแผนภาพ Workflow ผู้ใช้ต้องสามารถ คลิกที่ Node หรือ Step เก่าที่ผ่านมาแล้ว เพื่อดู Audit Log ย่อยของ Step นั้นได้ทันที (เช่น ใครเป็นคนกด Approve, เวลาไหน, มี Comment อะไร) โดยไม่ต้องสลับไปดูใน Tab History แยกต่างหาก
|
||||
- ขั้นตอนที่ยังไม่ถึงหรือผ่านไปแล้วจะเป็นรูปแบบ disabled
|
||||
- สามารถดำเนินการได้เฉพาะในขั้นตอนที่ได้รับมอบหมายงาน (active)
|
||||
- สิทธิ์ Document Control ขึ้นไป สามารถกด "Force Proceed" ไปยังขั้นตอนต่อไปได้ทุกขั้นตอน หรือ "Revert" กลับขั้นตอนก่อนหน้าได้
|
||||
|
||||
## 5.7. การจัดการใบเวียนเอกสาร (Circulation)
|
||||
|
||||
- ผู้ใช้สามารถดู Circulation ในรูปแบบ Workflow ทั้งหมดได้ในหน้าเดียว,ขั้นตอนที่ยังไม่ถึงหรือผ่านไปแล้วจะเป็นรูปแบบ diable, สามารถดำเนินการได้เฉพาะในขั้นตอนที่ได้รับมอบหมายงาน (active) เช่น ตรวจสอบแล้ว เพื่อไปยังขั้นตอนต่อไป, สิทธิ์ Document Control ขึ้นไป สามรถกด ไปยังขั้นตอนต่อไป ได้ทุกขั้นตอน, การย้อนกลับ ไปขั้นตอนก่อนหน้า สามารถทำได้โดย สิทธิ์ Document Control ขึ้นไป
|
||||
|
||||
## 5.8. การจัดการเอกสารนำส่ง (Transmittals)
|
||||
|
||||
- ผู้ใช้สามารถดู Transmittals ในรูปแบบรายการทั้งหมดได้ในหน้าเดียว
|
||||
|
||||
## 5.9. ข้อกำหนด UI/UX การแนบไฟล์ (File Attachment UX)
|
||||
|
||||
- ระบบต้องรองรับการอัปโหลดไฟล์หลายไฟล์พร้อมกัน (Multi-file upload) เช่น การลากและวาง (Drag-and-Drop)
|
||||
- ในหน้าอัปโหลด (เช่น สร้าง RFA หรือ Correspondence) ผู้ใช้ต้องสามารถกำหนดได้ว่าไฟล์ใดเป็น "เอกสารหลัก" (Main Document เช่น PDF) และไฟล์ใดเป็น "เอกสารแนบประกอบ" (Supporting Attachments เช่น .dwg, .docx, .zip)
|
||||
- **Security Feedback:** แสดง security warnings สำหรับ file types ที่เสี่ยงหรือ files ที่ fail virus scan
|
||||
- **File Type Indicators:** แสดง file type icons และ security status
|
||||
|
||||
## 5.10 Form & Interaction
|
||||
|
||||
- **Dynamic Form Generator:** ใช้ Component กลางที่รับ JSON Schema แล้ว Render Form ออกมาอัตโนมัติ เพื่อลดความซ้ำซ้อนของโค้ดหน้าบ้าน และรองรับเอกสารประเภทใหม่ๆ ได้ทันที
|
||||
- **Optimistic Updates:** การเปลี่ยนสถานะ (เช่น กด Approve, กด Read) ให้ UI เปลี่ยนสถานะทันทีให้ผู้ใช้เห็นก่อนรอ API Response (Rollback ถ้า Failed)
|
||||
|
||||
## 5.11 Mobile Responsiveness
|
||||
|
||||
- **Table Visualization:** บนหน้าจอมือถือ ตารางข้อมูลที่มีหลาย Column (เช่น Correspondence List) ต้องเปลี่ยนการแสดงผลเป็นแบบ **Card View** อัตโนมัติ
|
||||
- **Navigation:** Sidebar ต้องเป็นแบบ Collapsible Drawer
|
||||
|
||||
## 5.12 Resilience & Offline Support
|
||||
|
||||
- **Auto-Save Draft:** ระบบต้องบันทึกข้อมูลฟอร์มที่กำลังกรอกลง **LocalStorage** อัตโนมัติ เพื่อป้องกันข้อมูลหายกรณีเน็ตหลุดหรือปิด Browser โดยไม่ได้ตั้งใจ
|
||||
- **State Management:** ใช้ State Management ที่เหมาะสมและไม่ซับซ้อนเกินไป โดยเน้นการใช้ React Query สำหรับ Server State และ React Hook Form สำหรับ Form State
|
||||
- **Graceful Degradation:** หาก Service รอง (เช่น Search, Notification) ล่ม ระบบหลัก (CRUD) ต้องยังทำงานต่อได้
|
||||
|
||||
## 5.13. Secure In-App PDF Viewer (ใหม่)
|
||||
|
||||
- 5.13.1 Viewer Capabilities: ระบบต้องมี PDF Viewer ภายในแอปพลิเคชันที่สามารถเปิดดูไฟล์เอกสารหลัก (PDF) ได้ทันทีโดยไม่ต้องดาวน์โหลดลงเครื่อง เพื่อความสะดวกในการตรวจทาน (Review/Approve)
|
||||
- 5.13.2 Security: การแสดงผลไฟล์ต้อง ห้าม (Disable) การทำ Browser Cache สำหรับไฟล์ Sensitive เพื่อป้องกันการกู้คืนไฟล์จากเครื่อง Client ภายหลัง
|
||||
- 5.13.3 Performance: ต้องรองรับการส่งข้อมูลแบบ Streaming (Range Requests) เพื่อให้เปิดดูไฟล์ขนาดใหญ่ (เช่น แบบแปลน 50MB+) ได้รวดเร็วโดยไม่ต้องรอโหลดเสร็จทั้งไฟล์
|
||||
@@ -0,0 +1,212 @@
|
||||
# 🛡️ Section 6: Non-Functional Requirements (ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน)
|
||||
|
||||
---
|
||||
|
||||
title: 'Non-Functional Requirements'
|
||||
version: 1.8.0
|
||||
status: first-draft
|
||||
owner: Nattanin Peancharoen
|
||||
last_updated: 2026-02-23
|
||||
related:
|
||||
|
||||
- specs/02-architecture/data-model.md#correspondence
|
||||
- specs/03-implementation/backend-guidelines.md#correspondencemodule
|
||||
|
||||
---
|
||||
|
||||
## 6.1. การบันทึกการกระทำ (Audit Log)
|
||||
|
||||
- ทุกการกระทำที่สำคัญของผู้ใช้ (สร้าง, แก้ไข, ลบ, ส่ง) จะถูกบันทึกไว้ใน audit_logs เพื่อการตรวจสอบย้อนหลัง
|
||||
- ขอบเขตการบันทึก Audit Log:
|
||||
- ทุกการสร้าง/แก้ไข/ลบ ข้อมูลสำคัญ (correspondences, RFAs, drawings, users, permissions)
|
||||
- ทุกการเข้าถึงข้อมูล sensitive (user data, financial information)
|
||||
- ทุกการเปลี่ยนสถานะ workflow (status transitions)
|
||||
- ทุกการดาวน์โหลดไฟล์สำคัญ (contract documents, financial reports)
|
||||
- ทุกการเปลี่ยนแปลง permission และ role assignment
|
||||
- ทุกการล็อกอินที่สำเร็จและล้มเหลว
|
||||
- ทุกการส่งคำขอ API ที่สำคัญ
|
||||
- ข้อมูลที่ต้องบันทึกใน Audit Log:
|
||||
- ผู้ใช้งาน (user_id)
|
||||
- การกระทำ (action)
|
||||
- ชนิดของ entity (entity_type)
|
||||
- ID ของ entity (entity_id)
|
||||
- ข้อมูลก่อนการเปลี่ยนแปลง (old_values) - สำหรับ update operations
|
||||
- ข้อมูลหลังการเปลี่ยนแปลง (new_values) - สำหรับ update operations
|
||||
- IP address
|
||||
- User agent
|
||||
- Timestamp
|
||||
- Request ID สำหรับ tracing
|
||||
|
||||
## 6.2. Data Archiving & Partitioning
|
||||
|
||||
- สำหรับตารางที่มีขนาดใหญ่และโตเร็ว (เช่น `audit_logs`, `notifications`, `correspondence_revisions`) ต้องออกแบบโดยรองรับ **Table Partitioning** (แบ่งตาม Range วันที่ หรือ List) เพื่อประสิทธิภาพในระยะยาว
|
||||
|
||||
## 6.3. การค้นหา (Search):
|
||||
|
||||
- ระบบต้องมีฟังก์ชันการค้นหาขั้นสูง ที่สามารถค้นหาเอกสาร **correspondence**, **rfa**, **shop_drawing**, **contract-drawing**, **transmittal** และ **ใบเวียน (Circulations)** จากหลายเงื่อนไขพร้อมกันได้ เช่น ค้นหาจากชื่อเรื่อง, ประเภท, วันที่, และ Tag
|
||||
|
||||
## 6.4. การทำรายงาน (Reporting):
|
||||
|
||||
- สามารถจัดทำรายงานสรุปแยกประเภทของ Correspondence ประจำวัน, สัปดาห์, เดือน, และปีได้
|
||||
|
||||
## 6.5. ประสิทธิภาพ (Performance):
|
||||
|
||||
- มีการใช้ Caching กับข้อมูลที่เรียกใช้บ่อย และใช้ Pagination ในตารางข้อมูลเพื่อจัดการข้อมูลจำนวนมาก
|
||||
|
||||
- ตัวชี้วัดประสิทธิภาพ:
|
||||
|
||||
- **API Response Time:** < 200ms (90th percentile) สำหรับ operation ทั่วไป
|
||||
- **Search Query Performance:** < 500ms สำหรับการค้นหาขั้นสูง
|
||||
- **File Upload Performance:** < 30 seconds สำหรับไฟล์ขนาด 50MB
|
||||
- **Concurrent Users:** รองรับผู้ใช้พร้อมกันอย่างน้อย 100 คน
|
||||
- **Database Connection Pool:** ขนาดเหมาะสมกับ workload (default: min 5, max 20 connections)
|
||||
- **Cache Hit Ratio:** > 80% สำหรับ cached data
|
||||
- **Application Startup Time:** < 30 seconds
|
||||
|
||||
- Caching Strategy:
|
||||
|
||||
- **Master Data Cache:** Roles, Permissions, Organizations, Project metadata (TTL: 1 hour)
|
||||
- **User Session Cache:** User permissions และ profile data (TTL: 30 minutes)
|
||||
- **Search Result Cache:** Frequently searched queries (TTL: 15 minutes)
|
||||
- **File Metadata Cache:** Attachment metadata (TTL: 1 hour)
|
||||
- **Document Cache:** Frequently accessed document metadata (TTL: 30 minutes)
|
||||
- **ต้องมี cache invalidation strategy ที่ชัดเจน:**
|
||||
- Invalidate on update/delete operations
|
||||
- Time-based expiration
|
||||
- Manual cache clearance สำหรับ admin operations
|
||||
- ใช้ Redis เป็น distributed cache backend
|
||||
- ต้องมี cache monitoring (hit/miss ratios)
|
||||
|
||||
- Frontend Performance:
|
||||
- **Bundle Size Optimization:** ต้องควบคุมขนาด Bundle โดยรวมไม่เกิน 2MB
|
||||
- **State Management Efficiency:** ใช้ State Management Libraries อย่างเหมาะสม ไม่เกิน 2 ตัวหลัก
|
||||
- **Memory Management:** ต้องป้องกัน Memory Leak จาก State ที่ไม่จำเป็น
|
||||
|
||||
## 6.6. ความปลอดภัย (Security):
|
||||
|
||||
- มีระบบ Rate Limiting เพื่อป้องกันการโจมตีแบบ Brute-force
|
||||
- การจัดการ Secret (เช่น รหัสผ่าน DB, JWT Secret) จะต้องทำผ่าน Environment Variable ของ Docker เพื่อความปลอดภัยสูงสุด
|
||||
- Rate Limiting Strategy:
|
||||
- **Anonymous Endpoints:** 100 requests/hour ต่อ IP address
|
||||
- **Authenticated Endpoints:**
|
||||
- Viewer: 500 requests/hour
|
||||
- Editor: 1000 requests/hour
|
||||
- Document Control: 2000 requests/hour
|
||||
- Admin/Superadmin: 5000 requests/hour
|
||||
- **File Upload Endpoints:** 50 requests/hour ต่อ user
|
||||
- **Search Endpoints:** 500 requests/hour ต่อ user
|
||||
- **Authentication Endpoints:** 10 requests/minute ต่อ IP address
|
||||
- **ต้องมี mechanism สำหรับยกเว้น rate limiting สำหรับ trusted services**
|
||||
- ต้องบันทึก log เมื่อมีการ trigger rate limiting
|
||||
- Error Handling และ Resilience:
|
||||
- ต้องมี circuit breaker pattern สำหรับ external service calls
|
||||
- ต้องมี retry mechanism ด้วย exponential backoff
|
||||
- ต้องมี graceful degradation เมื่อบริการภายนอกล้มเหลว
|
||||
- Error messages ต้องไม่เปิดเผยข้อมูล sensitive
|
||||
- Input Validation:
|
||||
- ต้องมี input validation ทั้งฝั่ง client และ server (defense in depth)
|
||||
- ต้องป้องกัน OWASP Top 10 vulnerabilities:
|
||||
- SQL Injection (ใช้ parameterized queries ผ่าน ORM)
|
||||
- XSS (input sanitization และ output encoding)
|
||||
- CSRF (CSRF tokens สำหรับ state-changing operations)
|
||||
- ต้อง validate file uploads:
|
||||
- File type (white-list approach)
|
||||
- File size
|
||||
- File content (magic number verification)
|
||||
- ต้อง sanitize user inputs ก่อนแสดงผลใน UI
|
||||
- ต้องใช้ content security policy (CSP) headers
|
||||
- ต้องมี request size limits เพื่อป้องกัน DoS attacks
|
||||
- Session และ Token Management:
|
||||
- **JWT token expiration:** 8 hours สำหรับ access token
|
||||
- **Refresh token expiration:** 7 days
|
||||
- **Refresh token mechanism:** ต้องรองรับ token rotation และ revocation
|
||||
- **Token revocation on logout:** ต้องบันทึก revoked tokens จนกว่าจะ expire
|
||||
- **Concurrent session management:**
|
||||
- จำกัดจำนวน session พร้อมกันได้ (default: 5 devices)
|
||||
- ต้องแจ้งเตือนเมื่อมี login จาก device/location ใหม่
|
||||
- **Device fingerprinting:** สำหรับ security และ audit purposes
|
||||
- **Password policy:**
|
||||
- ความยาวขั้นต่ำ: 8 characters
|
||||
- ต้องมี uppercase, lowercase, number, special character
|
||||
- ต้องเปลี่ยน password ทุก 90 วัน
|
||||
- ต้องป้องกันการใช้ password ที่เคยใช้มาแล้ว 5 ครั้งล่าสุด
|
||||
|
||||
## 6.7. การสำรองข้อมูลและการกู้คืน (Backup & Recovery)
|
||||
|
||||
- ระบบจะต้องมีกลไกการสำรองข้อมูลอัตโนมัติสำหรับฐานข้อมูล MariaDB [cite: 2.4] และไฟล์เอกสารทั้งหมดใน /share/dms-data [cite: 2.1] (เช่น ใช้ HBS 3 ของ QNAP หรือสคริปต์สำรองข้อมูล) อย่างน้อยวันละ 1 ครั้ง
|
||||
- ต้องมีแผนการกู้คืนระบบ (Disaster Recovery Plan) ในกรณีที่ Server หลัก (QNAP) ใช้งานไม่ได้
|
||||
- ขั้นตอนการกู้คืน:
|
||||
- **Database Restoration Procedure:**
|
||||
- สร้างจาก full backup ล่าสุด
|
||||
- Apply transaction logs ถึง point-in-time ที่ต้องการ
|
||||
- Verify data integrity post-restoration
|
||||
- **File Storage Restoration Procedure:**
|
||||
- Restore จาก QNAP snapshot หรือ backup
|
||||
- Verify file integrity และ permissions
|
||||
- **Application Redeployment Procedure:**
|
||||
- Deploy จาก version ล่าสุดที่รู้ว่าทำงานได้
|
||||
- Verify application health
|
||||
- **Data Integrity Verification Post-Recovery:**
|
||||
- Run data consistency checks
|
||||
- Verify critical business data
|
||||
- **Recovery Time Objective (RTO):** < 4 ชั่วโมง
|
||||
- **Recovery Point Objective (RPO):** < 1 ชั่วโมง
|
||||
|
||||
## 6.8. กลยุทธ์การแจ้งเตือน (Notification Strategy - ปรับปรุง)
|
||||
|
||||
- ระบบจะส่งการแจ้งเตือน (ผ่าน Email หรือ Line [cite: 2.7]) เมื่อมีการกระทำที่สำคัญ\*\* ดังนี้:
|
||||
1. เมื่อมีเอกสารใหม่ (Correspondence, RFA) ถูกส่งมาถึงองค์กรณ์ของเรา
|
||||
2. เมื่อมีใบเวียน (Circulation) ใหม่ มอบหมายงานมาที่เรา
|
||||
3. (ทางเลือก) เมื่อเอกสารที่เราส่งไป ถูกดำเนินการ (เช่น อนุมัติ/ปฏิเสธ)
|
||||
4. (ทางเลือก) เมื่อใกล้ถึงวันครบกำหนด (Deadline) [cite: 3.2.5, 3.6.6, 3.7.5]
|
||||
- Grouping/Digest
|
||||
- กรณีมีการแจ้งเตือนประเภทเดียวกันจำนวนมากในช่วงเวลาสั้นๆ (เช่น Approve เอกสาร 10 ฉบับรวด) ระบบต้อง **รวม (Batch)** เป็น 1 Email/Line Notification เพื่อไม่ให้รบกวนผู้ใช้ (Spamming)
|
||||
- Notification Delivery Guarantees
|
||||
- **At-least-once delivery:** สำหรับ important notifications
|
||||
- **Retry mechanism:** ด้วย exponential backoff (max 3 reties)
|
||||
- **Dead letter queue:** สำหรับ notifications ที่ส่งไม่สำเร็จหลังจาก retries
|
||||
- **Delivery status tracking:** ต้องบันทึกสถานะการส่ง notifications
|
||||
- **Fallback channels:** ถ้า Email ล้มเหลว ให้ส่งผ่าน SYSTEM notification
|
||||
- **Notification preferences:** ผู้ใช้ต้องสามารถกำหนด channel preferences ได้
|
||||
|
||||
## 6.9. Maintenance Mode
|
||||
|
||||
- ระบบต้องมีกลไก **Maintenance Mode** ที่ Admin สามารถเปิดใช้งานได้
|
||||
- เมื่อเปิด: ผู้ใช้ทั่วไปจะเห็นหน้า "ปิดปรับปรุง" และไม่สามารถเรียก API ได้ (ยกเว้น Admin)
|
||||
- ใช้สำหรับช่วง Deploy Version ใหม่ หรือ Database Migration
|
||||
|
||||
## 6.10. Monitoring และ Observability
|
||||
|
||||
- Application Monitoring
|
||||
- **Health checks:** /health endpoint สำหรับ load balancer
|
||||
- **Metrics collection:** Response times, error rates, throughput
|
||||
- **Distributed tracing:** สำหรับ request tracing across services
|
||||
- **Log aggregation:** Structured logging ด้วย JSON format
|
||||
- **Alerting:** สำหรับ critical errors และ performance degradation
|
||||
- Business Metrics
|
||||
- จำนวน documents created ต่อวัน
|
||||
- Workflow completion rates
|
||||
- User activity metrics
|
||||
- System utilization rates
|
||||
- Search query performance
|
||||
- Security Monitoring
|
||||
- Failed login attempts
|
||||
- Rate limiting triggers
|
||||
- Virus scan results
|
||||
- File download activities
|
||||
- Permission changes
|
||||
|
||||
## 6.11. JSON Processing & Validation
|
||||
|
||||
- JSON Schema Management
|
||||
- ต้องมี centralized JSON schema registry
|
||||
- ต้องรองรับ schema versioning และ migration
|
||||
- ต้องมี schema validation during runtime
|
||||
- Performance Optimization
|
||||
- **Caching:** Cache parsed JSON structures
|
||||
- **Compression:** ใช้ compression สำหรับ JSON ขนาดใหญ่
|
||||
- **Indexing:** Support JSON path indexing สำหรับ query
|
||||
- Error Handling
|
||||
- ต้องมี graceful degradation เมื่อ JSON validation ล้มเหลว
|
||||
- ต้องมี default fallback values
|
||||
- ต้องบันทึก error logs สำหรับ validation failures
|
||||
@@ -0,0 +1,65 @@
|
||||
# Section 7: Testing Requirements (ข้อกำหนดด้านการทดสอบ)
|
||||
|
||||
---
|
||||
|
||||
title: 'Testing Requirements'
|
||||
version: 1.8.0
|
||||
status: first-draft
|
||||
owner: Nattanin Peancharoen
|
||||
last_updated: 2026-02-23
|
||||
related:
|
||||
|
||||
- specs/02-architecture/data-model.md#correspondence
|
||||
- specs/03-implementation/backend-guidelines.md#correspondencemodule
|
||||
|
||||
---
|
||||
|
||||
## 7.1 Unit Testing
|
||||
|
||||
- ต้องมี unit tests สำหรับ business logic ทั้งหมด
|
||||
- Code coverage อย่างน้อย 70% สำหรับ backend services
|
||||
- Business Logic: 80%+
|
||||
- Controllers: 70%+
|
||||
- Utilities: 90%+
|
||||
- ต้องทดสอบ RBAC permission logic ทุกระดับ
|
||||
|
||||
## 7.2 Integration Testing
|
||||
|
||||
- ทดสอบการทำงานร่วมกันของ modules
|
||||
- ทดสอบ database migrations และ data integrity
|
||||
- ทดสอบ API endpoints ด้วย realistic data
|
||||
|
||||
## 7.3 End-to-End Testing
|
||||
|
||||
- ทดสอบ complete user workflows
|
||||
- ทดสอบ document lifecycle จาก creation ถึง archival
|
||||
- ทดสอบ cross-module integrations
|
||||
|
||||
## 7.4 Security Testing
|
||||
|
||||
- Penetration Testing: ทดสอบ OWASP Top 10 vulnerabilities
|
||||
- Security Audit: Review code สำหรับ security flaws
|
||||
- Virus Scanning Test: ทดสอบ file upload security
|
||||
- Rate Limiting Test: ทดสอบ rate limiting functionality
|
||||
|
||||
## 7.5. Performance Testing
|
||||
|
||||
- Penetration Testing: ทดสอบ OWASP Top 10 vulnerabilities
|
||||
- Security Audit: Review code สำหรับ security flaws
|
||||
- Virus Scanning Test: ทดสอบ file upload security
|
||||
- **Rate Limiting Test:** ทดสอบ rate limiting functionality
|
||||
- **Load Testing:** ทดสอบด้วย realistic workloads
|
||||
- **Stress Testing:** หา breaking points ของระบบ
|
||||
- **Endurance Testing:** ทดสอบการทำงานต่อเนื่องเป็นเวลานาน
|
||||
|
||||
## 7.6. Disaster Recovery Testing
|
||||
|
||||
- ทดสอบ backup และ restoration procedures
|
||||
- ทดสอบ failover mechanisms
|
||||
- ทดสอบ data integrity หลังการ recovery
|
||||
|
||||
## 7.7 Specific Scenario Testing (เพิ่ม)
|
||||
|
||||
- **Race Condition Test:** ทดสอบยิง Request ขอเลขที่เอกสารพร้อมกัน 100 Request
|
||||
- **Transaction Test:** ทดสอบปิดเน็ตระหว่าง Upload ไฟล์ (ตรวจสอบว่าไม่มี Orphan File หรือ Broken Link)
|
||||
- **Permission Test:** ทดสอบ CASL Integration ทั้งฝั่ง Backend และ Frontend ให้ตรงกัน
|
||||
Reference in New Issue
Block a user