75 KiB
📋 แผนการพัฒนา Backend (NestJS) - LCBP3-DMS v1.4.4 (ฉบับปรับปรุง)
สถานะ: FINAL GUIDELINE Rev.04 วันที่: 2025-11-26 อ้างอิง: Requirements v1.4.3 & FullStackJS Guidelines v1.4.3 Classification: Internal Technical Documentation การแก้ไข: เพิ่ม T2.5.1-T2.5.9, T3.1.1-T3.1.8,
🎯 ภาพรวมโครงการ
พัฒนา Backend สำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System) ที่มีความปลอดภัยสูง รองรับการทำงานพร้อมกัน (Concurrency) ได้อย่างถูกต้องแม่นยำ มีสถาปัตยกรรมที่ยืดหยุ่นต่อการขยายตัว และรองรับการจัดการเอกสารที่ซับซ้อน มีระบบ Workflow การอนุมัติ และการควบคุมสิทธิ์แบบ RBAC 4 ระดับ พร้อมมาตรการความปลอดภัยที่ทันสมัย
📐 สถาปัตยกรรมระบบ
Technology Stack (Updated)
- Framework: NestJS (TypeScript, ESM)
- Database: MariaDB 10.11 (ใช้ Virtual Columns)
- ORM: TypeORM (ใช้ Optimistic Locking)
- Authentication: JWT + Passport
- Authorization: CASL (RBAC 4-level)
- File Upload: Multer + Virus Scanning (ClamAV) + Two-Phase Storage
- Search: Elasticsearch
- Notification: Nodemailer + n8n (Line Integration) + BullMQ Queue
- Caching/Locking: Redis (Redlock) สำหรับ Distributed Locking
- Queue: BullMQ (Redis) สำหรับ Notification Batching และ Async Jobs
- Resilience: Circuit Breaker, Retry Patterns
- Security: Helmet, CSRF Protection, Rate Limiting, Idempotency
- Monitoring: Winston, Health Checks, Metrics
- Scheduling: @nestjs/schedule (Cron Jobs)
- Documentation: Swagger
- Validation: Zod / Class-validator
โครงสร้างโมดูล (Domain-Driven)
📁backend
├── .editorconfig
├── .env
├── .gitignore
├── .prettierrc
├── docker-compose.override.yml.example
├── docker-compose.yml
├── eslint.config.mjs
├── Infrastructure Setup.yml
├── nest-cli.json
├── package-lock.json
├── package.json
├── pnpm-lock.yaml
├── README.md
├── tsconfig.build.json
├── tsconfig.json
├── 📁scripts
│ ├── debug-db.ts
│ └── verify-workflow.ts
├── 📁test
│ ├── app.e2e-spec.ts
│ ├── jest-e2e.json
│ ├── phase3-workflow.e2e-spec.ts
│ └── simple.e2e-spec.ts
├── 📁uploads
│ └── 📁temp
│ ├── 5a6d4c26-84b2-4c8a-b177-9fa267651a93.pdf
│ └── d60d9807-a22d-4ca0-b99a-5d5d8b81b3e8.pdf
└── 📁src
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
├── main.ts
├── redlock.d.ts
├── 📁common
│ ├── 📁auth
│ │ ├── 📁dto
│ │ │ ├── login.dto.ts
│ │ │ └── register.dto.ts
│ │ ├── 📁strategies
│ │ │ ├── jwt-refresh.strategy.ts
│ │ │ └── jwt.strategy.ts
│ │ ├── auth.controller.spec.ts
│ │ ├── auth.controller.ts
│ │ ├── auth.module.ts
│ │ ├── auth.service.spec.ts
│ │ └── auth.service.ts
│ ├── 📁config
│ │ ├── env.validation.ts
│ │ └── redis.config.ts
│ ├── 📁decorators
│ │ ├── audit.decorator.ts
│ │ ├── bypass-maintenance.decorator.ts
│ │ ├── circuit-breaker.decorator.ts
│ │ ├── current-user.decorator.ts
│ │ ├── idempotency.decorator.ts
│ │ ├── require-permission.decorator.ts
│ │ └── retry.decorator.ts
│ ├── 📁entities
│ │ ├── audit-log.entity.ts
│ │ └── base.entity.ts
│ ├── 📁exceptions
│ │ └── http-exception.filter.ts
│ ├── 📁file-storage
│ │ ├── 📁entities
│ │ │ └── attachment.entity.ts
│ │ ├── file-cleanup.service.ts
│ │ ├── file-storage.controller.spec.ts
│ │ ├── file-storage.controller.ts
│ │ ├── file-storage.module.ts
│ │ ├── file-storage.service.spec.ts
│ │ └── file-storage.service.ts
│ ├── 📁guards
│ │ ├── jwt-auth.guard.ts
│ │ ├── jwt-refresh.guard.ts
│ │ ├── maintenance-mode.guard.ts
│ │ └── rbac.guard.ts
│ ├── 📁idempotency
│ │ ├── idempotency.interceptor.ts
│ │ └── performance.interceptor.ts
│ ├── 📁interceptors
│ │ ├── audit-log.interceptor.ts
│ │ ├── idempotency.interceptor.ts
│ │ ├── performance.interceptor.ts
│ │ └── transform.interceptor.ts
│ ├── 📁maintenance
│ ├── 📁resilience
│ │ └── resilience.module.ts
│ └── 📁security
│ ├── 📁services
│ │ ├── crypto.service.ts
│ │ └── request-context.service.ts
│ └── common.module.ts
├── 📁database
│ ├── 📁migrations
│ └── 📁seeds
│ └── workflow-definitions.seed.ts
└── 📁modules
├── 📁circulation
│ ├── 📁dto
│ │ ├── create-circulation.dto.ts
│ │ ├── search-circulation.dto.ts
│ │ └── update-circulation-routing.dto.ts
│ ├── 📁entities
│ │ ├── circulation-routing.entity.ts
│ │ ├── circulation-status-code.entity.ts
│ │ └── circulation.entity.ts
│ ├── circulation.controller.ts
│ ├── circulation.module.ts
│ └── circulation.service.ts
├── 📁correspondence
│ ├── 📁dto
│ │ ├── add-reference.dto.ts
│ │ ├── create-correspondence.dto.ts
│ │ ├── search-correspondence.dto.ts
│ │ ├── submit-correspondence.dto.ts
│ │ └── workflow-action.dto.ts
│ ├── 📁entities
│ │ ├── correspondence-reference.entity.ts
│ │ ├── correspondence-revision.entity.ts
│ │ ├── correspondence-routing.entity.ts
│ │ ├── correspondence-status.entity.ts
│ │ ├── correspondence-sub-type.entity.ts
│ │ ├── correspondence-type.entity.ts
│ │ ├── correspondence.entity.ts
│ │ ├── routing-template-step.entity.ts
│ │ └── routing-template.entity.ts
│ ├── correspondence.controller.spec.ts
│ ├── correspondence.controller.ts
│ ├── correspondence.module.ts
│ ├── correspondence.service.spec.ts
│ └── correspondence.service.ts
├── 📁document-numbering
│ ├── 📁entities
│ │ ├── document-number-counter.entity.ts
│ │ └── document-number-format.entity.ts
│ ├── 📁interfaces
│ │ └── document-numbering.interface.ts
│ ├── document-numbering.module.ts
│ ├── document-numbering.service.spec.ts
│ └── document-numbering.service.ts
├── 📁drawing
│ ├── 📁dto
│ │ ├── create-contract-drawing.dto.ts
│ │ ├── create-shop-drawing-revision.dto.ts
│ │ ├── create-shop-drawing.dto.ts
│ │ ├── search-contract-drawing.dto.ts
│ │ ├── search-shop-drawing.dto.ts
│ │ └── update-contract-drawing.dto.ts
│ ├── 📁entities
│ │ ├── contract-drawing-sub-category.entity.ts
│ │ ├── contract-drawing-volume.entity.ts
│ │ ├── contract-drawing.entity.ts
│ │ ├── shop-drawing-main-category.entity.ts
│ │ ├── shop-drawing-revision.entity.ts
│ │ ├── shop-drawing-sub-category.entity.ts
│ │ └── shop-drawing.entity.ts
│ ├── contract-drawing.controller.ts
│ ├── contract-drawing.service.ts
│ ├── drawing-master-data.controller.ts
│ ├── drawing-master-data.service.ts
│ ├── drawing.module.ts
│ ├── shop-drawing.controller.ts
│ └── shop-drawing.service.ts
├── 📁json-schema
│ ├── 📁dto
│ │ ├── create-json-schema.dto.ts
│ │ ├── search-json-schema.dto.ts
│ │ └── update-json-schema.dto.ts
│ ├── 📁entities
│ │ └── json-schema.entity.ts
│ ├── json-schema.controller.spec.ts
│ ├── json-schema.controller.ts
│ ├── json-schema.module.ts
│ ├── json-schema.service.spec.ts
│ └── json-schema.service.ts
├── 📁master
│ ├── 📁dto
│ │ ├── create-discipline.dto.ts
│ │ ├── create-sub-type.dto.ts
│ │ ├── create-tag.dto.ts
│ │ ├── save-number-format.dto.ts
│ │ ├── search-tag.dto.ts
│ │ └── update-tag.dto.ts
│ ├── 📁entities
│ │ ├── discipline.entity.ts
│ │ └── tag.entity.ts
│ ├── master.controller.ts
│ ├── master.module.ts
│ └── master.service.ts
├── 📁monitoring
│ ├── 📁controllers
│ │ └── health.controller.ts
│ ├── 📁dto
│ │ └── set-maintenance.dto.ts
│ ├── 📁logger
│ │ └── winston.config.ts
│ ├── 📁services
│ │ └── metrics.service.ts
│ ├── monitoring.controller.ts
│ ├── monitoring.module.ts
│ └── monitoring.service.ts
├── 📁notification
│ ├── 📁dto
│ │ ├── create-notification.dto.ts
│ │ └── search-notification.dto.ts
│ ├── 📁entities
│ │ └── notification.entity.ts
│ ├── notification-cleanup.service.ts
│ ├── notification.controller.ts
│ ├── notification.gateway.ts
│ ├── notification.module.ts
│ ├── notification.processor.ts
│ └── notification.service.ts
├── 📁project
│ ├── 📁dto
│ │ ├── create-project.dto.ts
│ │ ├── search-project.dto.ts
│ │ └── update-project.dto.ts
│ ├── 📁entities
│ │ ├── contract-organization.entity.ts
│ │ ├── contract.entity.ts
│ │ ├── organization.entity.ts
│ │ ├── project-organization.entity.ts
│ │ └── project.entity.ts
│ ├── project.controller.spec.ts
│ ├── project.controller.ts
│ ├── project.module.ts
│ ├── project.service.spec.ts
│ └── project.service.ts
├── 📁rfa
│ ├── 📁dto
│ │ ├── create-rfa.dto.ts
│ │ ├── search-rfa.dto.ts
│ │ └── update-rfa.dto.ts
│ ├── 📁entities
│ │ ├── rfa-approve-code.entity.ts
│ │ ├── rfa-item.entity.ts
│ │ ├── rfa-revision.entity.ts
│ │ ├── rfa-status-code.entity.ts
│ │ ├── rfa-type.entity.ts
│ │ ├── rfa-workflow-template-step.entity.ts
│ │ ├── rfa-workflow-template.entity.ts
│ │ ├── rfa-workflow.entity.ts
│ │ └── rfa.entity.ts
│ ├── rfa.controller.ts
│ ├── rfa.module.ts
│ └── rfa.service.ts
├── 📁search
│ ├── 📁dto
│ │ └── search-query.dto.ts
│ ├── search.controller.ts
│ ├── search.module.ts
│ └── search.service.ts
├── 📁transmittal
│ ├── 📁dto
│ │ ├── create-transmittal.dto.ts
│ │ ├── search-transmittal.dto.ts
│ │ └── update-transmittal.dto.ts
│ ├── 📁entities
│ │ ├── transmittal-item.entity.ts
│ │ └── transmittal.entity.ts
│ ├── transmittal.controller.ts
│ ├── transmittal.module.ts
│ └── transmittal.service.ts
├── 📁user
│ ├── 📁dto
│ │ ├── assign-role.dto.ts
│ │ ├── create-user.dto.ts
│ │ ├── update-preference.dto.ts
│ │ └── update-user.dto.ts
│ ├── 📁entities
│ │ ├── permission.entity.ts
│ │ ├── role.entity.ts
│ │ ├── user-assignment.entity.ts
│ │ ├── user-preference.entity.ts
│ │ └── user.entity.ts
│ ├── user-assignment.service.ts
│ ├── user-preference.service.ts
│ ├── user.controller.ts
│ ├── user.module.ts
│ ├── user.service.spec.ts
│ └── user.service.ts
└── 📁workflow-engine
├── 📁dto
│ ├── create-workflow-definition.dto.ts
│ ├── evaluate-workflow.dto.ts
│ ├── get-available-actions.dto.ts
│ └── update-workflow-definition.dto.ts
├── 📁entities
│ └── workflow-definition.entity.ts
├── 📁interfaces
│ └── workflow.interface.ts
├── workflow-dsl.service.ts
├── workflow-engine.controller.ts
├── workflow-engine.module.ts
├── workflow-engine.service.spec.ts
└── workflow-engine.service.ts
🗓️ แผนการพัฒนาแบบ Phase-Based
- (Dependency Diagram ถูกละไว้เพื่อประหยัดพื้นที่ เนื่องจากมีการอ้างอิงจากแผนเดิม)
Phase 0: Infrastructure & Configuration (สัปดาห์ที่ 1)
Milestone: โครงสร้างพื้นฐานพร้อม รองรับ Secrets ที่ปลอดภัย และ Redis พร้อมใช้งาน
Phase 0: Tasks
-
[ ] T0.1 Secure Configuration Setup
- ปรับปรุง
ConfigModuleให้รองรับการอ่านค่าจาก Environment Variables - สร้าง Template
docker-compose.override.yml.exampleสำหรับ Dev - Validate Config ด้วย Joi/Zod ตอน Start App (Throw error ถ้าขาด Secrets)
- Security: Setup network segmentation และ firewall rules
- Deliverable: Configuration Management พร้อมใช้งานอย่างปลอดภัย
- Dependencies: None (Task เริ่มต้น)
- ปรับปรุง
-
[ ] T0.2 Redis & Queue Infrastructure
- Setup Redis Container
- Setup BullMQ Module ใน NestJS สำหรับจัดการ Background Jobs
- Setup Redis Client สำหรับ Distributed Lock (Redlock)
- Security: Setup Redis authentication และ encryption
- Deliverable: Redis และ Queue System พร้อมใช้งาน
- Dependencies: T0.1
-
[ ] T0.3 Setup Database Connection
- Import SQL Schema v1.4.2 เข้า MariaDB
- Run Seed Data (organizations, users, roles, permissions)
- Configure TypeORM ใน AppModule
- Security: Setup database connection encryption
- ทดสอบ Connection
- Deliverable: Database พร้อมใช้งาน, มี Seed Data
- Dependencies: T0.1
-
[ ] T0.4 Setup Git Repository
- สร้าง Repository ใน Gitea (git.np-dms.work)
- Setup .gitignore, README.md, SECURITY.md
- Commit Initial Project
- Deliverable: Code อยู่ใน Version Control
- Dependencies: T0.1, T0.2, T0.3
Phase 1: Core Foundation & Security (สัปดาห์ที่ 2-3)
Milestone: ระบบ Authentication, Authorization, Idempotency พื้นฐาน และ Security Baseline
Phase 1: Tasks
-
[ ] T1.1 CommonModule - Base Infrastructure
- สร้าง Base Entity (id, created_at, updated_at, deleted_at)
- สร้าง Global Exception Filter (ไม่เปิดเผย sensitive information)
- สร้าง Response Transform Interceptor
- สร้าง Audit Log Interceptor
- Idempotency Interceptor: ตรวจสอบ Header
Idempotency-Keyและ Cache Response เดิมใน Redis - Maintenance Mode Middleware: ตรวจสอบ Flag ใน Redis Key เพื่อ Block API ระหว่างปรับปรุงระบบ (Admin ใช้ Redis/Admin UI ในการ Toggle สถานะ)
- สร้าง RequestContextService - สำหรับเก็บข้อมูลระหว่าง Request
- สร้าง ConfigService - Centralized configuration management
- สร้าง CryptoService - สำหรับ encryption/decryption
- Security: Implement input validation pipeline
- Deliverable: Common Services พร้อมใช้ รวมถึง Idempotency และ Maintenance Mode
- Dependencies: T0.2, T0.3
-
[ ] T1.2 AuthModule - JWT Authentication
- สร้าง Entity: User
- สร้าง AuthService:
- login(username, password) → JWT Token
- validateUser(username, password) → User | null
- Password Hashing (bcrypt) + salt
- สร้าง JWT Strategy (Passport)
- สร้าง JwtAuthGuard
- สร้าง Controllers:
- POST /auth/login → { access_token, refresh_token }
- POST /auth/register → Create User (Admin only)
- POST /auth/refresh → Refresh token
- POST /auth/logout → Revoke token
- GET /auth/profile (Protected)
- Security: Implement rate limiting สำหรับ authentication endpoints
- Deliverable: ล็อกอิน/ล็อกเอาต์ทำงานได้อย่างปลอดภัย
- Dependencies: T1.1, T0.3
-
[ ] T1.3 UserModule - User Management
- สร้าง Entities: User, Role, Permission, UserRole, UserAssignment, UserPreference
- สร้าง UserService CRUD (พร้อม soft delete)
- สร้าง RoleService CRUD
- สร้าง PermissionService (Read-Only, จาก Seed)
- สร้าง UserAssignmentService - สำหรับจัดการ user assignments ตาม scope
- สร้าง UserPreferenceService - สำหรับจัดการการตั้งค่า Notification และ UI
- สร้าง Controllers:
- GET /users → List Users (Paginated)
- GET /users/:id → User Detail
- POST /users → Create User (ต้องบังคับเปลี่ยน password ครั้งแรก)
- PUT /users/:id → Update User
- DELETE /users/:id → Soft Delete
- GET /roles → List Roles
- POST /roles → Create Role (Admin)
- PUT /roles/:id/permissions → Assign Permissions
- Security: Implement permission checks สำหรับ user management
- Deliverable: จัดการผู้ใช้, Role, และ Preferences ได้
- Dependencies: T1.1, T1.2
-
[ ] T1.4 RBAC Guard - 4-Level Authorization
- สร้าง @RequirePermission() Decorator
- สร้าง RbacGuard ที่ตรวจสอบ 4 ระดับ:
- Global Permissions
- Organization Permissions
- Project Permissions
- Contract Permissions
- Permission Hierarchy Logic
- Integration กับ CASL
- Security: Implement audit logging สำหรับ permission checks
- Deliverable: ระบบสิทธิ์ทำงานได้ทั้ง 4 ระดับ
- Dependencies: T1.1, T1.3
-
[ ] T1.5 ProjectModule - Base Structures
- สร้าง Entities:
- Organization
- Project
- Contract
- ProjectOrganization (Junction)
- ContractOrganization (Junction)
- สร้าง Services & Controllers:
- GET /organizations → List
- POST /projects → Create (Superadmin)
- GET /projects/:id/contracts → List Contracts
- POST /projects/:id/contracts → Create Contract
- Security: Implement data isolation ระหว่าง organizations
- Deliverable: จัดการโครงสร้างโปรเจกต์ได้
- Dependencies: T1.1, T1.2, T0.3
- สร้าง Entities:
Phase 2: High-Integrity Data & File Management (สัปดาห์ที่ 4)
Milestone: Master Data, ระบบจัดการไฟล์แบบ Transactional, Document Numbering ที่ไม่มี Race Condition, JSON details system พร้อมใช้งาน
Phase 2: Tasks
-
[ ] T2.1 Virtual Columns for JSON
- ออกแบบ Migration Script สำหรับตารางที่มี JSON Details
- เพิ่ม Generated Columns (Virtual) สำหรับฟิลด์ที่ใช้ Search บ่อยๆ (เช่น
project_id,type) พร้อม Index - Security: Implement admin-only access สำหรับ master data
- Deliverable: JSON Data Search Performance ดีขึ้น
- Dependencies: T0.3, T1.1, T1.5
-
[ ] T2.2 FileStorageService - Two-Phase Storage
- สร้าง Attachment Entity
- สร้าง FileStorageService:
- Phase 1 (Upload): API รับไฟล์ → Scan Virus → Save ลง
temp/→ Returntemp_id - Phase 2 (Commit): Method
commitFiles(tempIds[])→ ย้ายจากtemp/ไปpermanent/{YYYY}/{MM}/→ Update DB - File type validation (white-list: PDF, DWG, DOCX, XLSX, PPTX, ZIP)
- File size check (max 50MB)
- Generate checksum (SHA-256)
- Cleanup Job: สร้าง Cron Job ลบไฟล์ใน
temp/ที่ค้างเกิน 24 ชม. โดยตรวจสอบจากคอลัมน์expires_atในตารางattachments
- Phase 1 (Upload): API รับไฟล์ → Scan Virus → Save ลง
- สร้าง Controller:
- POST /files/upload → { temp_id } (Protected)
- POST /files/commit → { attachment_id, url } (Protected)
- GET /files/:id/download → File Stream (Protected + Expiration)
- Security: Access Control - ตรวจสอบสิทธิ์ผ่าน Junction Table
- Deliverable: อัปโหลด/ดาวน์โหลดไฟล์ได้อย่างปลอดภัย แบบ Transactional
- Dependencies: T1.1, T1.4
-
[ ] T2.3 DocumentNumberingModule - Double-Lock Mechanism
- สร้าง Entities:
- DocumentNumberFormat
- DocumentNumberCounter
- สร้าง DocumentNumberingService:
- generateNextNumber(projectId, orgId, typeId, year) → string
- ใช้ Double-Lock Mechanism: 1. Acquire Redis Lock (Key:
doc_num:{project}:{type}) 2. Read DB & Calculate Next Number 3. Update DB with Optimistic Lock Check (ใช้@VersionColumn()) 4. Release Redis Lock 5. Retry on Failure ด้วย exponential backoff - Fallback mechanism เมื่อการขอเลขล้มเหลว
- Format ตาม Template: {ORG_CODE}-{TYPE_CODE}-{YEAR_SHORT}-{SEQ:4}
- ไม่มี Controller (Internal Service เท่านั้น)
- Security: Implement audit log ทุกครั้งที่มีการ generate เลขที่
- Deliverable: Service สร้างเลขที่เอกสารได้ถูกต้องและปลอดภัย ไม่มี Race Condition
- Dependencies: T1.1, T0.3
- สร้าง Entities:
- [ ] T2.3 DocumentNumberingModule - Token-Based & Double-Lock (Updated)
- Update Entity:
DocumentNumberCounter(Adddiscipline_idto PK) - Implement Token Parser & Replacer Logic (
{DISCIPLINE},{SUBTYPE_NUM}) - Update
generateNextNumberto handle optional keys (Discipline/SubType) - Deliverable: Flexible Numbering System
- Update Entity:
-
[ ] T2.4 SecurityModule - Enhanced Security
- สร้าง Input Validation Service:
- XSS Prevention
- SQL Injection Prevention
- CSRF Protection
- สร้าง RateLimitGuard:
- Implement rate limiting ตาม strategy (anonymous: 100/hr, authenticated: 500-5000/hr)
- Different limits สำหรับ endpoints ต่างๆ
- สร้าง Security Headers Middleware
- Security: Implement content security policy (CSP)
- Deliverable: Security layers ทำงานได้
- Dependencies: T1.1
- สร้าง Input Validation Service:
-
[ ] T2.5 JSON Details & Schema Management
- T2.5.1 JsonSchemaModule - Schema Management: สร้าง Service สำหรับ Validate, get, register JSON schemas
- T2.5.2 DetailsService - Data Processing: สร้าง Service สำหรับ sanitize, transform, compress/decompress JSON
- T2.5.3 JSON Security & Validation: Implement security checks และ validation rules
- Deliverable: JSON schema system ทำงานได้
- Dependencies: T1.1
🚀 T2.5 JSON Details & Schema Management - Enhanced Implementation Plan
📋 Overview
สร้างระบบจัดการ JSON Schema ที่ครอบคลุมสำหรับ dynamic document details, validation, transformation และ performance optimization
🎯 Enhanced Task Breakdown for T2.5
[ ] T2.5.1 JSON Schema Registry & Versioning System
- Schema Entity Design สำหรับเก็บ JSON schemas ทุกประเภท
- Version Control System สำหรับ schema evolution
- Migration Strategy สำหรับ backward-compatible changes
- Schema Inheritance สำหรับ shared field definitions
// Schema Entity Design
@Entity()
export class JsonSchema {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string; // 'CORRESPONDENCE_GENERIC', 'RFA_DWG', 'CIRCULATION_INTERNAL'
@Column()
entity_type: string; // 'correspondence', 'rfa', 'circulation'
@Column()
version: number;
@Column('json')
schema_definition: any; // AJV JSON Schema
@Column('json')
ui_schema: any; // UI configuration for form generation
@Column({ default: true })
is_active: boolean;
@Column('json', { nullable: true })
migration_script: any; // Data transformation rules
@CreateDateColumn()
created_at: Date;
@UpdateDateColumn()
updated_at: Date;
// Virtual columns configuration for performance
@Column('json', { nullable: true })
virtual_columns: VirtualColumnConfig[];
}
interface VirtualColumnConfig {
json_path: string; // '$.projectId'
column_name: string; // 'ref_project_id'
data_type: 'INT' | 'VARCHAR' | 'BOOLEAN' | 'DATE';
index_type?: 'INDEX' | 'UNIQUE' | 'FULLTEXT';
is_required: boolean;
}
[ ] T2.5.2 Schema Validation & Transformation Engine
- AJV Integration สำหรับ high-performance JSON validation
- Custom Validators สำหรับ business rule validation
- Data Transformation สำหรับ schema version migration
- Sanitization Service สำหรับ data cleansing
@Injectable()
export class JsonSchemaService {
private ajv: Ajv;
constructor() {
this.ajv = new Ajv({
allErrors: true,
coerceTypes: true,
useDefaults: true,
removeAdditional: true,
formats: {
'date-time': true,
email: true,
uri: true,
'document-number': this.documentNumberFormat,
},
});
// Register custom formats and keywords
this.registerCustomValidators();
}
async validateData(
schemaName: string,
data: any,
options: ValidationOptions = {}
): Promise<ValidationResult> {
const schema = await this.getSchema(schemaName);
const validate = this.ajv.compile(schema);
const isValid = validate(data);
if (!isValid) {
return {
isValid: false,
errors: validate.errors,
sanitizedData: null,
};
}
// Apply data transformation if needed
const sanitizedData = await this.sanitizeData(data, schema, options);
return {
isValid: true,
errors: [],
sanitizedData,
};
}
private async sanitizeData(
data: any,
schema: any,
options: ValidationOptions
): Promise<any> {
const sanitized = { ...data };
// Remove unknown properties if not allowed
if (options.removeAdditional !== false) {
const allowedProperties = this.extractPropertyNames(schema);
Object.keys(sanitized).forEach((key) => {
if (!allowedProperties.includes(key)) {
delete sanitized[key];
}
});
}
// Apply custom sanitizers based on field type
await this.applyFieldSanitizers(sanitized, schema);
return sanitized;
}
private registerCustomValidators(): void {
// Custom format for document numbers
this.ajv.addFormat('document-number', {
type: 'string',
validate: (value: string) => {
return /^[A-Z]{3,5}-[A-Z]{2,4}-\d{4}-\d{3,5}$/.test(value);
},
});
// Custom keyword for role-based access
this.ajv.addKeyword({
keyword: 'requiredRole',
type: 'string',
compile: (requiredRole: string) => {
return (data: any, dataPath: string, parentData: any) => {
// Check if user has required role for this field
const userContext = this.getUserContext();
return userContext.roles.includes(requiredRole);
};
},
});
}
}
[ ] T2.5.3 Virtual Columns & Performance Optimization
- Virtual Column Generator สำหรับ JSON field indexing
- Migration Scripts สำหรับสร้าง generated columns
- Query Optimizer สำหรับใช้ virtual columns ใน search
- Performance Monitoring สำหรับ JSON query performance
@Injectable()
export class VirtualColumnService {
constructor(
private dataSource: DataSource,
private configService: ConfigService
) {}
async setupVirtualColumns(
tableName: string,
schemaConfig: VirtualColumnConfig[]
): Promise<void> {
const connection = this.dataSource.manager.connection;
for (const config of schemaConfig) {
await this.createVirtualColumn(tableName, config);
}
}
private async createVirtualColumn(
tableName: string,
config: VirtualColumnConfig
): Promise<void> {
const columnDefinition = this.generateColumnDefinition(config);
const sql = `
ALTER TABLE ${tableName}
ADD COLUMN ${config.column_name} ${columnDefinition}
`;
await this.dataSource.query(sql);
// Create index if specified
if (config.index_type) {
await this.createIndex(tableName, config);
}
}
private generateColumnDefinition(config: VirtualColumnConfig): string {
const dataType = this.mapDataType(config.data_type);
const jsonPath = this.escapeJsonPath(config.json_path);
return `${dataType} GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '${jsonPath}'))) VIRTUAL`;
}
private async createIndex(
tableName: string,
config: VirtualColumnConfig
): Promise<void> {
const indexName = `idx_${tableName}_${config.column_name}`;
const sql = `
CREATE ${config.index_type} INDEX ${indexName}
ON ${tableName} (${config.column_name})
`;
await this.dataSource.query(sql);
}
}
// Example virtual column configuration for correspondence
const correspondenceVirtualColumns: VirtualColumnConfig[] = [
{
json_path: '$.projectId',
column_name: 'ref_project_id',
data_type: 'INT',
index_type: 'INDEX',
is_required: true,
},
{
json_path: '$.priority',
column_name: 'ref_priority',
data_type: 'VARCHAR',
index_type: 'INDEX',
is_required: false,
},
{
json_path: '$.dueDate',
column_name: 'ref_due_date',
data_type: 'DATE',
index_type: 'INDEX',
is_required: false,
},
];
[ ] T2.5.4 Dynamic Form Schema Management
- UI Schema Definition สำหรับ frontend form generation
- Field Dependency System สำหรับ conditional fields
- Validation Rule Sync ระหว่าง backend-frontend
- Form Template Registry สำหรับ reusable form patterns
interface UiSchema {
type: 'object';
properties: {
[key: string]: UiSchemaField;
};
required?: string[];
layout?: {
type: 'tabs' | 'sections' | 'steps';
groups: LayoutGroup[];
};
}
interface UiSchemaField {
type: 'string' | 'number' | 'boolean' | 'array' | 'object';
widget?: 'text' | 'textarea' | 'select' | 'radio' | 'checkbox' | 'date';
title: string;
description?: string;
placeholder?: string;
enum?: string[];
enumNames?: string[];
dependencies?: FieldDependency[];
conditions?: FieldCondition[];
properties?: { [key: string]: UiSchemaField }; // for nested objects
items?: UiSchemaField; // for arrays
}
interface FieldDependency {
field: string;
condition: {
operator: 'equals' | 'notEquals' | 'contains' | 'greaterThan';
value: any;
};
actions: {
visibility?: boolean;
required?: boolean;
options?: string[];
};
}
// Example: RFA Drawing Schema
const rfaDwgSchema: UiSchema = {
type: 'object',
layout: {
type: 'tabs',
groups: [
{
title: 'Basic Information',
fields: ['title', 'description', 'discipline'],
},
{
title: 'Drawing Details',
fields: ['drawingReferences', 'revision', 'approvalType'],
},
{
title: 'Technical Specifications',
fields: ['materials', 'dimensions', 'tolerances'],
},
],
},
properties: {
title: {
type: 'string',
widget: 'text',
title: 'Drawing Title',
placeholder: 'Enter drawing title...',
required: true,
},
discipline: {
type: 'string',
widget: 'select',
title: 'Discipline',
enum: ['CIVIL', 'STRUCTURAL', 'MECHANICAL', 'ELECTRICAL', 'PLUMBING'],
enumNames: [
'Civil',
'Structural',
'Mechanical',
'Electrical',
'Plumbing',
],
},
drawingReferences: {
type: 'array',
title: 'Related Contract Drawings',
items: {
type: 'string',
widget: 'select',
title: 'Drawing Number',
},
},
approvalType: {
type: 'string',
widget: 'radio',
title: 'Approval Type',
enum: ['FULL_APPROVAL', 'PARTIAL_APPROVAL', 'COMMENTS_ONLY'],
enumNames: ['Full Approval', 'Partial Approval', 'Comments Only'],
},
},
required: ['title', 'discipline', 'approvalType'],
};
[ ] T2.5.5 Data Migration & Version Compatibility
- Schema Migration Service สำหรับอัพเกรด data ระหว่าง versions
- Data Transformation Pipeline สำหรับ backward compatibility
- Version Rollback Mechanism สำหรับ emergency situations
- Migration Testing Framework สำหรับ 确保 data integrity
@Injectable()
export class SchemaMigrationService {
async migrateData(
entityType: string,
entityId: string,
targetVersion: number
): Promise<MigrationResult> {
const currentData = await this.getCurrentData(entityType, entityId);
const currentVersion = await this.getCurrentSchemaVersion(
entityType,
entityId
);
const migrationPath = await this.findMigrationPath(
currentVersion,
targetVersion
);
let migratedData = currentData;
for (const migrationStep of migrationPath) {
migratedData = await this.applyMigrationStep(migrationStep, migratedData);
}
// Validate migrated data against target schema
const validationResult = await this.validateAgainstSchema(
migratedData,
targetVersion
);
if (!validationResult.isValid) {
throw new MigrationError(
'MIGRATION_VALIDATION_FAILED',
validationResult.errors
);
}
await this.saveMigratedData(
entityType,
entityId,
migratedData,
targetVersion
);
return {
success: true,
fromVersion: currentVersion,
toVersion: targetVersion,
migratedFields: this.getMigratedFields(currentData, migratedData),
};
}
private async applyMigrationStep(
step: MigrationStep,
data: any
): Promise<any> {
switch (step.type) {
case 'FIELD_RENAME':
return this.renameField(data, step.config);
case 'FIELD_TRANSFORM':
return this.transformField(data, step.config);
case 'FIELD_ADD':
return this.addField(data, step.config);
case 'FIELD_REMOVE':
return this.removeField(data, step.config);
case 'STRUCTURE_CHANGE':
return this.restructureData(data, step.config);
default:
throw new MigrationError('UNKNOWN_MIGRATION_TYPE');
}
}
}
// Example migration configuration
const migrationSteps = [
{
from_version: 1,
to_version: 2,
type: 'FIELD_RENAME',
config: {
old_field: 'project_id',
new_field: 'ref_project_id',
},
},
{
from_version: 2,
to_version: 3,
type: 'FIELD_TRANSFORM',
config: {
field: 'priority',
transform: 'MAP_VALUES',
mapping: {
HIGH: 'URGENT',
MEDIUM: 'NORMAL',
LOW: 'LOW',
},
},
},
];
[ ] T2.5.6 Security & Access Control for JSON Data
- Field-level Security 基于 user roles
- Data Encryption สำหรับ sensitive fields
- Audit Logging สำหรับ JSON data changes
- Input Sanitization สำหรับป้องกัน XSS และ injection
@Injectable()
export class JsonSecurityService {
async applyFieldLevelSecurity(
data: any,
schema: any,
userContext: UserContext
): Promise<any> {
const securedData = { ...data };
const securityRules = await this.getSecurityRules(schema.name);
for (const [fieldPath, fieldConfig] of Object.entries(schema.properties)) {
const fieldRules = securityRules[fieldPath];
if (fieldRules && !this.hasFieldAccess(fieldRules, userContext)) {
// Remove or mask field based on security rules
if (fieldRules.on_deny === 'REMOVE') {
this.deleteField(securedData, fieldPath);
} else if (fieldRules.on_deny === 'MASK') {
this.maskField(securedData, fieldPath, fieldRules.mask_pattern);
}
}
}
return securedData;
}
async encryptSensitiveFields(data: any, schema: any): Promise<any> {
const encryptedData = { ...data };
const sensitiveFields = this.getSensitiveFields(schema);
for (const fieldPath of sensitiveFields) {
const fieldValue = this.getFieldValue(data, fieldPath);
if (fieldValue) {
const encrypted = await this.cryptoService.encrypt(
fieldValue,
'field-level'
);
this.setFieldValue(encryptedData, fieldPath, encrypted);
}
}
return encryptedData;
}
private getSensitiveFields(schema: any): string[] {
const sensitiveFields: string[] = [];
const traverseSchema = (obj: any, path: string = '') => {
if (obj.properties) {
for (const [key, value] of Object.entries(obj.properties)) {
const currentPath = path ? `${path}.${key}` : key;
if (value.sensitive) {
sensitiveFields.push(currentPath);
}
if (value.properties || value.items) {
traverseSchema(value, currentPath);
}
}
}
};
traverseSchema(schema);
return sensitiveFields;
}
}
[ ] T2.5.7 API Design & Integration
- Schema Management API สำหรับ CRUD operations
- Validation API สำหรับ validate data against schemas
- Migration API สำหรับ manage data migrations
- Integration Hooks สำหรับ other modules
@Controller('json-schema')
export class JsonSchemaController {
@Post('validate/:schemaName')
@RequirePermission('schema.validate')
async validateData(
@Param('schemaName') schemaName: string,
@Body() dto: ValidateDataDto
): Promise<ValidationResult> {
return this.jsonSchemaService.validateData(
schemaName,
dto.data,
dto.options
);
}
@Post('schemas')
@RequirePermission('schema.manage')
async createSchema(@Body() dto: CreateSchemaDto): Promise<JsonSchema> {
return this.jsonSchemaService.createSchema(dto);
}
@Post('migrate/:entityType/:entityId')
@RequirePermission('data.migrate')
async migrateData(
@Param('entityType') entityType: string,
@Param('entityId') entityId: string,
@Body() dto: MigrateDataDto
): Promise<MigrationResult> {
return this.migrationService.migrateData(
entityType,
entityId,
dto.targetVersion
);
}
@Get('ui-schema/:schemaName')
@RequirePermission('schema.view')
async getUiSchema(
@Param('schemaName') schemaName: string
): Promise<UiSchema> {
return this.schemaService.getUiSchema(schemaName);
}
}
[ ] T2.5.8 Integration with Document Modules
- Correspondence Module Integration
- RFA Module Integration
- Circulation Module Integration
- Drawing Module Integration
// Example: Correspondence Service Integration
@Injectable()
export class CorrespondenceService {
constructor(
private jsonSchemaService: JsonSchemaService,
private detailsService: DetailsService
) {}
async createCorrespondence(
dto: CreateCorrespondenceDto
): Promise<Correspondence> {
// 1. Validate details against schema
const validationResult = await this.jsonSchemaService.validateData(
`CORRESPONDENCE_${dto.type}`,
dto.details
);
if (!validationResult.isValid) {
throw new ValidationError('INVALID_DETAILS', validationResult.errors);
}
// 2. Apply security and sanitization
const secureDetails = await this.detailsService.sanitizeDetails(
validationResult.sanitizedData,
dto.type
);
// 3. Create correspondence entity
const correspondence = this.correspondenceRepository.create({
...dto,
details: secureDetails,
schema_version: await this.getCurrentSchemaVersion(
`CORRESPONDENCE_${dto.type}`
),
});
// 4. Setup virtual columns for performance
await this.setupVirtualColumns(correspondence);
return this.correspondenceRepository.save(correspondence);
}
async searchCorrespondences(
filters: SearchFilters
): Promise<Correspondence[]> {
// Use virtual columns for efficient filtering
const query = this.correspondenceRepository.createQueryBuilder('c');
if (filters.projectId) {
query.andWhere('c.ref_project_id = :projectId', {
projectId: filters.projectId,
});
}
if (filters.priority) {
query.andWhere('c.ref_priority = :priority', {
priority: filters.priority,
});
}
return query.getMany();
}
}
[ ] T2.5.9 Testing Strategy
- Unit Tests สำหรับ schema validation และ transformation
- Integration Tests สำหรับ end-to-end data flow
- Performance Tests สำหรับ virtual columns และ large datasets
- Security Tests สำหรับ field-level security
describe('JsonSchemaService', () => {
describe('validateData', () => {
it('should validate correct RFA_DWG data successfully', async () => {
const testData = {
title: 'Structural Beam Details',
discipline: 'STRUCTURAL',
drawingReferences: ['CD-STR-001', 'CD-STR-002'],
approvalType: 'FULL_APPROVAL',
materials: ['STEEL_A36', 'CONCRETE_40MPA'],
};
const result = await jsonSchemaService.validateData('RFA_DWG', testData);
expect(result.isValid).toBe(true);
expect(result.errors).toHaveLength(0);
});
it('should reject invalid discipline value', async () => {
const testData = {
title: 'Test Drawing',
discipline: 'INVALID_DISCIPLINE', // Not in enum
approvalType: 'FULL_APPROVAL',
};
const result = await jsonSchemaService.validateData('RFA_DWG', testData);
expect(result.isValid).toBe(false);
expect(result.errors[0].message).toContain('discipline');
});
});
});
describe('VirtualColumnService', () => {
it('should improve search performance with virtual columns', async () => {
// Create test data with JSON details
await createTestCorrespondences(1000);
// Search without virtual column (JSON_EXTRACT)
const startTime1 = Date.now();
const result1 = await correspondenceRepository
.createQueryBuilder('c')
.where("JSON_EXTRACT(c.details, '$.projectId') = :projectId", {
projectId: 123,
})
.getMany();
const time1 = Date.now() - startTime1;
// Search with virtual column
const startTime2 = Date.now();
const result2 = await correspondenceRepository
.createQueryBuilder('c')
.where('c.ref_project_id = :projectId', { projectId: 123 })
.getMany();
const time2 = Date.now() - startTime2;
expect(time2).toBeLessThan(time1 * 0.5); // At least 2x faster
expect(result1.length).toEqual(result2.length);
});
});
🔗 Critical Dependencies
- T1.1 (Common Module) - สำหรับ base entities และ shared services
- T1.4 (RBAC Guard) - สำหรับ field-level security
- T0.3 (Database) - สำหรับ virtual columns implementation
- T3.2 (Correspondence) - สำหรับ integration testing
🎯 Success Metrics
- ✅ JSON validation performance: < 10ms ต่อ request
- ✅ Virtual columns improve search performance 5x+
- ✅ Support schema evolution without data loss
- ✅ Field-level security enforced across all modules
- ✅ 100% test coverage สำหรับ core validation logic
- [ ] T2.6 MasterModule - Advanced Data (Req 6B) (New)
- Update Entities:
Discipline,CorrespondenceSubType - Create Services/Controllers for CRUD Operations (Admin Panel Support)
- Implement Seeding Logic for initial 6B data
- Deliverable: API for managing Disciplines and Sub-types
- Dependencies: T1.1, T0.3
- Update Entities:
Phase 3: Unified Workflow Engine (สัปดาห์ที่ 5-6)
Milestone: ระบบ Workflow กลางที่รองรับทั้ง Routing ปกติ และ RFA
Phase 3: Tasks
-
[ ] T3.1 WorkflowEngineModule (New)
- ออกแบบ Generic Schema สำหรับ Workflow State Machine
- Implement Service:
initializeWorkflow(),processAction(),getNextStep() - รองรับ Logic การ "ข้ามขั้นตอน" และ "ส่งกลับ" ภายใน Engine เดียว
- Security: Implement audit logging สำหรับ workflow actions
- Deliverable: Unified Workflow Engine พร้อมใช้งาน
- Dependencies: T1.1
-
[ ] T3.1.1 Workflow DSL Specification & Grammar
- Define EBNF Grammar สำหรับ Workflow DSL
- Create YAML Schema สำหรับ human-friendly workflow definitions
- Design JSON Schema สำหรับ compiled workflow representations
# ตัวอย่าง DSL Structure
workflow: RFA_APPROVAL
version: 1.0
description: "RFA Approval Workflow with Parallel Reviews"
states:
- name: DRAFT
initial: true
metadata:
color: "gray"
icon: "draft"
on:
SUBMIT:
to: TECHNICAL_REVIEW
conditions: - expression: "user.hasRole('ENGINEER')"
requirements: - role: "ENGINEER"
events: - type: "notify"
target: "reviewers"
template: "NEW_RFA_SUBMITTED" - type: "assign"
target: "technical_lead"
- name: TECHNICAL_REVIEW
metadata:
color: "blue"
icon: "review"
on:
APPROVE:
to: MANAGERIAL_REVIEW
conditions: - expression: "user.department === context.document.department"
REQUEST_CHANGES:
to: DRAFT
events: - type: "notify"
target: "creator"
template: "CHANGES_REQUESTED"
ESCALATE:
to: ESCALATED_REVIEW
conditions: - expression: "document.priority === 'HIGH'"
- name: PARALLEL_APPROVAL
parallel: true
branches:
- MANAGERIAL_REVIEW
- FINANCIAL_REVIEW
on:
ALL_APPROVED:
to: APPROVED
ANY_REJECTED:
to: REJECTED
- name: APPROVED
terminal: true
metadata:
color: "green"
icon: "approved"
Versioning Strategy สำหรับ workflow definitions
-
[ ] T3.1.2 Workflow Core Entities & Database Schema
- WorkflowDefinition Entity
- WorkflowInstance Entity
- WorkflowHistory Entity
- WorkflowTransition Entity
// Core Entities Design
@Entity()
export class WorkflowDefinition {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column()
version: number;
@Column('json')
dsl_raw: any; // YAML/JSON DSL
@Column('json')
compiled_schema: any; // Normalized JSON
@Column({ default: true })
is_active: boolean;
@CreateDateColumn()
created_at: Date;
@VersionColumn()
version: number;
}
@Entity()
export class WorkflowInstance {
@PrimaryGeneratedColumn('uuid')
id: string;
@ManyToOne(() => WorkflowDefinition)
definition: WorkflowDefinition;
@Column()
entity_type: string; // 'correspondence', 'rfa', 'circulation'
@Column()
entity_id: string;
@Column()
current_state: string;
@Column('json')
context: any; // Workflow-specific data
@Column('json')
history: any[]; // State transition history
@CreateDateColumn()
created_at: Date;
@UpdateDateColumn()
updated_at: Date;
}
-
[ ] T3.1.3 DSL Parser & Compiler Service
- YAML Parser สำหรับอ่าน DSL definitions
- Syntax Validator สำหรับ compile-time validation
- Schema Compiler สำหรับแปลง DSL → Normalized JSON
- Version Migration สำหรับอัพเกรด workflow definitions
@Injectable()
export class WorkflowDslService {
async parseAndValidate(dslContent: string): Promise<CompiledWorkflow> {
// 1. Parse YAML
const rawDefinition = yaml.parse(dslContent);
// 2. Validate Syntax
await this.validateSyntax(rawDefinition);
// 3. Compile to Normalized JSON
const compiled = await this.compileDefinition(rawDefinition);
// 4. Validate Business Rules
await this.validateBusinessRules(compiled);
return compiled;
}
private async validateSyntax(definition: any): Promise<void> {
const rules = [
// ต้องมีอย่างน้อย 1 initial state
() => definition.states.some((s) => s.initial),
// Terminal states ต้องไม่มี transitions
() => !definition.states.filter((s) => s.terminal).some((s) => s.on),
// State names must be unique
() =>
new Set(definition.states.map((s) => s.name)).size ===
definition.states.length,
// Transition targets must exist
() => this.validateTransitionTargets(definition),
];
for (const rule of rules) {
if (!rule()) {
throw new WorkflowValidationError('DSL_VALIDATION_FAILED');
}
}
}
}
- [ ] T3.1.4 Workflow Runtime Engine
- State Machine Engine สำหรับจัดการ state transitions
- Condition Evaluator สำหรับประเมิน conditional transitions
- Permission Checker สำหรับตรวจสอบสิทธิ์ในการเปลี่ยนสถานะ
- Event Dispatcher สำหรับจัดการ workflow events
****@Injectable()
export class WorkflowEngineService {
async processTransition(
instanceId: string,
action: string,
context: WorkflowContext
): Promise<TransitionResult> {
// 1. Load Workflow Instance
const instance = await this.findInstance(instanceId);
const definition = await this.getCompiledDefinition(instance.definition_id);
// 2. Validate Current State & Action
const currentState = definition.states[instance.current_state];
const transition = currentState.transitions[action];
if (!transition) {
throw new WorkflowError('INVALID_TRANSITION');
}
// 3. Check Permissions & Conditions
await this.validatePermissions(transition, context);
await this.validateConditions(transition, context);
// 4. Execute Pre-Transition Hooks
await this.executeHooks('pre_transition', instance, transition, context);
// 5. Perform State Transition
const previousState = instance.current_state;
instance.current_state = transition.to;
// 6. Record History
await this.recordTransitionHistory(instance, {
from: previousState,
to: transition.to,
action,
user: context.userId,
timestamp: new Date(),
metadata: context.metadata
});
// 7. Execute Post-Transition Events
await this.executeEvents(transition.events, instance, context);
// 8. Execute Post-Transition Hooks
await this.executeHooks('post_transition', instance, transition, context);
// 9. Save Instance
await this.saveInstance(instance);
return {
success: true,
previousState,
newState: transition.to,
instanceId: instance.id
};
}
async getAvailableActions(
instanceId: string,
context: WorkflowContext
): Promise<string[]> {
const instance = await this.findInstance(instanceId);
const definition = await this.getCompiledDefinition(instance.definition_id);
const currentState = definition.states[instance.current_state];
return Object.keys(currentState.transitions).filter(action => {
const transition = currentState.transitions[action];
return this.isActionAvailable(transition, context);
});
}
}
- [ ] T3.1.5 Advanced Feature Implementation
- Parallel Approval Flows สำหรับ multi-department approvals
- Conditional Transitions ด้วย expression evaluation
- Timeout & Escalation สำหรับขั้นตอนที่เกินกำหนด
- Rollback & Compensation สำหรับย้อนกลับสถานะ
// Parallel Workflow Support
interface ParallelState {
parallel: true;
branches: string[];
completion_policy: 'ALL' | 'ANY' | 'MAJORITY';
on: {
[completionType: string]: {
to: string;
conditions?: Condition[];
};
};
}
// Conditional Transition Support
interface ConditionalTransition {
to: string;
conditions: Array<{
expression: string; // "user.role === 'MANAGER' && document.value > 10000"
evaluator?: 'javascript' | 'jsonlogic';
}>;
requirements?: PermissionRequirement[];
}
// Timeout & Escalation
interface StateWithTimeout {
timeout_seconds: number;
on_timeout: {
action: string;
escalate_to?: string;
notify?: string[];
};
}
- [ ] T3.1.6 Event System & Integration
- Event Types (notify, assign, webhook, auto_action)
- Template Engine สำหรับ dynamic messages
- Webhook Support สำหรับ external integrations
- Notification Service Integration
@Injectable()
export class WorkflowEventService {
async executeEvents(
events: WorkflowEvent[],
instance: WorkflowInstance,
context: WorkflowContext
): Promise<void> {
for (const event of events) {
switch (event.type) {
case 'notify':
await this.handleNotifyEvent(event, instance, context);
break;
case 'assign':
await this.handleAssignEvent(event, instance, context);
break;
case 'webhook':
await this.handleWebhookEvent(event, instance, context);
break;
case 'auto_action':
await this.handleAutoActionEvent(event, instance, context);
break;
}
}
}
private async handleNotifyEvent(
event: NotifyEvent,
instance: WorkflowInstance,
context: WorkflowContext
): Promise<void> {
const recipients = await this.resolveRecipients(
event.target,
instance,
context
);
const message = await this.renderTemplate(
event.template,
instance,
context
);
await this.notificationService.send({
type: 'workflow',
recipients,
subject: message.subject,
body: message.body,
metadata: {
workflow_instance_id: instance.id,
state: instance.current_state,
action: context.action,
},
});
}
}
- [ ] T3.1.7 API Design & Controllers
- REST API Endpoints สำหรับ workflow management
- WebSocket Support สำหรับ real-time updates
- Admin API สำหรับ workflow definition management
- Integration Hooks สำหรับ external systems
@Controller('workflow')
export class WorkflowEngineController {
@Post('instances/:id/transition')
@RequirePermission('workflow.execute')
async processTransition(
@Param('id') instanceId: string,
@Body() dto: WorkflowTransitionDto
): Promise<TransitionResult> {
return this.workflowEngine.processTransition(
instanceId,
dto.action,
dto.context
);
}
@Get('instances/:id/actions')
@RequirePermission('workflow.view')
async getAvailableActions(
@Param('id') instanceId: string,
@Query() context: WorkflowContext
): Promise<string[]> {
return this.workflowEngine.getAvailableActions(instanceId, context);
}
@Post('definitions')
@RequirePermission('workflow.manage')
async createWorkflowDefinition(
@Body() dto: CreateWorkflowDefinitionDto
): Promise<WorkflowDefinition> {
return this.workflowDslService.compileAndSave(dto.dslContent);
}
@Get('instances/:id/history')
@RequirePermission('workflow.view')
async getWorkflowHistory(
@Param('id') instanceId: string
): Promise<WorkflowHistory[]> {
return this.workflowHistoryService.getHistory(instanceId);
}
}
- [ ] T3.1.8 Integration with Existing Modules
- Correspondence Module Integration
- RFA Module Integration
- Circulation Module Integration
- Notification Module Integration
// Correspondence Integration Example
@Injectable()
export class CorrespondenceWorkflowService {
constructor(
private workflowEngine: WorkflowEngineService,
private correspondenceService: CorrespondenceService
) {}
async submitCorrespondence(
correspondenceId: string,
userId: string
): Promise<void> {
const correspondence = await this.correspondenceService.findById(
correspondenceId
);
// Create workflow instance
const instance = await this.workflowEngine.createInstance({
definition: 'CORRESPONDENCE_ROUTING',
entity_type: 'correspondence',
entity_id: correspondenceId,
context: {
document: correspondence,
user: userId,
},
});
// Process initial transition
await this.workflowEngine.processTransition(instance.id, 'SUBMIT', {
userId,
metadata: { correspondenceId },
});
}
}
- [ ] T3.1.9 Testing Strategy
- Unit Tests สำหรับ DSL parser และ state machine
- Integration Tests สำหรับ end-to-end workflow execution
- Performance Tests สำหรับ high-concurrency scenarios
- Security Tests สำหรับ permission validation.
describe('WorkflowEngineService', () => {
describe('processTransition', () => {
it('should successfully transition state with valid permissions', async () => {
// Arrange
const instance = await createTestInstance();
const context = { userId: 'user1', roles: ['APPROVER'] };
// Act
const result = await workflowEngine.processTransition(
instance.id,
'APPROVE',
context
);
// Assert
expect(result.success).toBe(true);
expect(result.newState).toBe('APPROVED');
});
it('should reject transition without required permissions', async () => {
// Arrange
const instance = await createTestInstance();
const context = { userId: 'user2', roles: ['VIEWER'] };
// Act & Assert
await expect(
workflowEngine.processTransition(instance.id, 'APPROVE', context)
).rejects.toThrow(WorkflowError);
});
});
});
-
🔗 Critical Dependencies of T3.1.1-T3.1.8
- T1.1 (Common Module) - สำหรับ base entities และ shared services
- T1.4 (RBAC Guard) - สำหรับ permission checking
- T2.5 (JSON Schema) - สำหรับ DSL validation
- T6.2 (Notification) - สำหรับ event handling
-
🎯 Success Metrics
- ✅ Support ทั้ง Correspondence Routing และ RFA Workflow
- ✅ DSL ที่ human-readable และ editable โดยไม่ต้องแก้โค้ด
- ✅ Performance: < 50ms ต่อ state transition
- ✅ 100% test coverage สำหรับ core workflow logic
- ✅ Complete audit trail สำหรับทุก workflow instance
-
[ ] T3.2 CorrespondenceModule - Basic CRUD
- สร้าง Entities (Correspondence, Revision, Recipient, Tag, Reference, Attachment)
- สร้าง CorrespondenceService (Create with Document Numbering, Update with new Revision, Soft Delete)
- สร้าง Controllers (POST/GET/PUT/DELETE /correspondences)
- Implement Impersonation Logic: ตรวจสอบ originatorId ใน DTO หากมีการส่งมา ต้องเช็คว่า User ปัจจุบันมีสิทธิ์กระทำการแทนหรือไม่ (Superadmin)
- Security: Implement permission checks สำหรับ document access
- Deliverable: สร้าง/แก้ไข/ดูเอกสารได้
- Dependencies: T1.1, T1.2, T1.3, T1.4, T1.5, T2.3, T2.2, T2.5
-
[ ] T3.3 CorrespondenceModule - Advanced Features
- Implement Status Transitions (DRAFT → SUBMITTED)
- Implement References (Link Documents)
- Implement Search (Basic)
- Security: Implement state transition validation
- Deliverable: Workflow พื้นฐานทำงานได้
- Dependencies: T3.2
-
[ ] T3.4 Correspondence Integration with Workflow
- เชื่อมต่อ
CorrespondenceServiceเข้ากับWorkflowEngineModule - ย้าย Logic การ Routing เดิมมาใช้ Engine ใหม่
- สร้าง API endpoints สำหรับ Frontend (Templates, Pending Tasks, Bulk Action)
- Security: Implement permission checks สำหรับ workflow operations
- Deliverable: ระบบส่งต่อเอกสารทำงานได้สมบูรณ์ด้วย Unified Engine
- Dependencies: T3.1, T3.2
- เชื่อมต่อ
Phase 4: Drawing & Advanced Workflows (สัปดาห์ที่ 7-8)
Milestone: การจัดการแบบและ RFA โดยใช้ Unified Engine
Phase 4: Tasks
-
[ ] T4.1 DrawingModule - Contract Drawings
- สร้าง Entities (ContractDrawing, Volume, Category, SubCategory, Attachment)
- สร้าง ContractDrawingService CRUD
- สร้าง Controllers (GET/POST /drawings/contract)
- Security: Implement access control สำหรับ contract drawings
- Deliverable: จัดการ Contract Drawings ได้
- Dependencies: T1.1, T1.2, T1.4, T1.5, T2.2
-
[ ] T4.2 DrawingModule - Shop Drawings
- สร้าง Entities (ShopDrawing, Revision, Main/SubCategory, ContractRef, RevisionAttachment)
- สร้าง ShopDrawingService CRUD (รวมการสร้าง Revision)
- สร้าง Controllers (GET/POST /drawings/shop, /drawings/shop/:id/revisions)
- Link Shop Drawing Revision → Contract Drawings
- Security: Implement virus scanning สำหรับ drawing files
- Deliverable: จัดการ Shop Drawings และ Revisions ได้
- Dependencies: T4.1
-
[ ] T5.1 RfaModule with Unified Workflow
- สร้าง Entities (Rfa, RfaRevision, RfaItem, RfaWorkflowTemplate/Step)
- สร้าง RfaService (Create RFA, Link Shop Drawings)
- Implement RFA Workflow โดยใช้ Configuration ของ
WorkflowEngineModule - สร้าง Controllers (POST/GET /rfas, POST /rfas/:id/workflow/...)
- Resilience: Implement circuit breaker สำหรับ notification services
- Deliverable: RFA Workflow ทำงานได้ด้วย Unified Engine
- Dependencies: T3.2, T4.2, T2.5, T6.2
Phase 5: Workflow Systems & Resilience (สัปดาห์ที่ 8-9)
Milestone: ระบบ Workflow ทั้งหมดพร้อม Resilience Patterns
Phase 5: Tasks
-
[ ] T5.2 CirculationModule - Internal Routing
- สร้าง Entities (Circulation, Template, Routing, Attachment)
- สร้าง CirculationService (Create 1:1 with Correspondence, Assign User, Complete/Close Step)
- สร้าง Controllers (POST/GET /circulations, POST /circulations/:id/steps/...)
- Resilience: Implement retry mechanism สำหรับ assignment notifications
- Deliverable: ใบเวียนภายในองค์กรทำงานได้
- Dependencies: T3.2, T2.5, T6.2
-
[ ] T5.3 TransmittalModule - Document Forwarding
- สร้าง Entities (Transmittal, TransmittalItem)
- สร้าง TransmittalService (Create Correspondence + Transmittal, Link Multiple Correspondences)
- สร้าง Controllers (POST/GET /transmittals)
- Security: Implement access control สำหรับ transmittal items
- Deliverable: สร้าง Transmittal ได้
- Dependencies: T3.2
Phase 6: Notification & Resilience (สัปดาห์ที่ 9)
Milestone: ระบบแจ้งเตือนแบบ Digest และการจัดการข้อมูลขนาดใหญ่
Phase 6: Tasks
-
[ ] T6.1 SearchModule - Elasticsearch Integration
- Setup Elasticsearch Container
- สร้าง SearchService (index/update/delete documents, search)
- Index ทุก Document Type
- สร้าง Controllers (GET /search)
- Resilience: Implement circuit breaker สำหรับ Elasticsearch
- Deliverable: ค้นหาขั้นสูงทำงานได้
- Dependencies: T3.2, T5.1, T4.2, T5.2, T5.3
-
[ ] T6.2 Notification Queue & Digest
- สร้าง NotificationService (sendEmail/Line/System)
- Producer: Push Event ลง BullMQ Queue
- Consumer: จัดกลุ่ม Notification (Digest Message) และส่งผ่าน Email/Line
- Integrate กับ Workflow Events (แจ้ง Recipients, Assignees, Deadline)
- สร้าง Controllers (GET /notifications, PUT /notifications/:id/read)
- Resilience: Implement retry mechanism ด้วย exponential backoff
- Deliverable: ระบบแจ้งเตือนทำงานได้แบบ Digest
- Dependencies: T1.1, T6.4
-
[ ] T6.3 MonitoringModule - Observability
- สร้าง Health Check Controller (GET /health)
- สร้าง Metrics Service (API response times, Error rates)
- สร้าง Performance Interceptor (Track request duration)
- สร้าง Logging Service (Structured logging)
- Deliverable: Monitoring system ทำงานได้
- Dependencies: T1.1
-
[ ] T6.4 ResilienceModule - Circuit Breaker & Retry
- สร้าง Circuit Breaker Service (@CircuitBreaker() decorator)
- สร้าง Retry Service (@Retry() decorator)
- สร้าง Fallback Strategies
- Implement สำหรับ Email, LINE, Elasticsearch, Virus Scanning
- Deliverable: Resilience patterns ทำงานได้
- Dependencies: T1.1
-
[ ] T6.5 Data Partitioning Strategy
- ออกแบบ Table Partitioning สำหรับ
audit_logsและnotifications(แบ่งตาม Range: Year) - เขียน Raw SQL Migration สำหรับสร้าง Partition Table
- Deliverable: Database Performance และ Scalability ดีขึ้น
- Dependencies: T0.3
- ออกแบบ Table Partitioning สำหรับ
Phase 7: Testing & Hardening (สัปดาห์ที่ 10-12)
Milestone: ทดสอบความทนทานต่อ Race Condition, Security, และ Performance
Phase 7: Tasks
-
[ ] T7.1 Concurrency Testing
- เขียน Test Scenarios ยิง Request ขอเลขที่เอกสารพร้อมกัน 100 Request (ต้องไม่ซ้ำและไม่ข้าม)
- ทดสอบ Optimistic Lock ทำงานถูกต้องเมื่อ Redis ถูกปิด
- ทดสอบ File Upload พร้อมกันหลายไฟล์
- Deliverable: ระบบทนทานต่อ Concurrency Issues
-
[ ] T7.2 Transaction Integrity Testing
- ทดสอบ Upload ไฟล์แล้ว Kill Process ก่อน Commit
- ทดสอบ Two-Phase File Storage ทำงานถูกต้อง
- ทดสอบ Database Transaction Rollback Scenarios
- Deliverable: Data Integrity รับประกันได้
-
[ ] T7.3 Security & Idempotency Test
- ทดสอบ Replay Attack โดยใช้
Idempotency-Keyซ้ำ - ทดสอบ Maintenance Mode Block API ได้จริง
- ทดสอบ RBAC 4-Level ทำงานถูกต้อง 100%
- Deliverable: Security และ Idempotency ทำงานได้ตาม 设计要求
- ทดสอบ Replay Attack โดยใช้
-
[ ] T7.4 Unit Testing (80% Coverage)
-
[ ] T7.5 Integration Testing
-
[ ] T7.6 E2E Testing
-
[ ] T7.7 Performance Testing
- Load Testing: 100 concurrent users
- (สำคัญ) การจูนและทดสอบ Load Test จะต้องทำในสภาพแวดล้อมที่จำลอง Spec ของ QNAP Server (TS-473A, AMD Ryzen V1500B) เพื่อให้ได้ค่า Response Time และ Connection Pool ที่เที่ยงตรง
- Stress Testing
- Endurance Testing
- Deliverable: Performance targets บรรลุ
-
[ ] T7.8 Security Testing
- Penetration Testing (OWASP Top 10)
- Security Audit (Code review, Dependency scanning)
- File Upload Security Testing
- Deliverable: Security tests ผ่าน
-
[ ] T7.9 Performance Optimization
- Implement Caching (Master Data, User Permissions, Search Results)
- Database Optimization (Review Indexes, Query Optimization, Pagination)
- Deliverable: Response Time < 200ms (90th percentile)
Phase 8: Documentation & Deployment (สัปดาห์ที่ 14)
Milestone: เอกสารและ Deploy สู่ Production พร้อม Security Hardening
Phase 8: Tasks
- [ ] T8.1 API Documentation (Swagger)
- [ ] T8.2 Technical Documentation
- [ ] T8.3 Security Hardening
- [ ] T8.4 Deployment Preparation (QNAP Setup, Nginx Proxy Manager)
- [ ] T8.5 Production Deployment
- [ ] T8.6 Handover to Frontend Team
📊 สรุป Timeline
| Phase | ระยะเวลา | จำนวนงาน | Output หลัก |
|---|---|---|---|
| Phase 0 | 1 สัปดาห์ | 4 | Infrastructure Ready + Security Base |
| Phase 1 | 2 สัปดาห์ | 5 | Auth & User Management + RBAC + Idempotency |
| Phase 2 | 1 สัปดาห์ | 5 | High-Integrity Data & File Management |
| Phase 3 | 2 สัปดาห์ | 4 | Unified Workflow Engine + Correspondence |
| Phase 4 | 2 สัปดาห์ | 3 | Drawing Management + RFA with Unified Workflow |
| Phase 5 | 2 สัปดาห์ | 2 | Workflow Systems + Resilience |
| Phase 6 | 1 สัปดาห์ | 5 | Notification & Resilience + Data Partitioning |
| Phase 7 | 3 สัปดาห์ | 9 | Testing & Hardening |
| Phase 8 | 1 สัปดาห์ | 6 | Documentation & Deploy |
| รวม | 15 สัปดาห์ | 39 Tasks | Production-Ready Backend v1.4.2 |
Document Control:
- Document: Backend Development Plan v1.4.4
- Version: 1.4
- Date: 2025-11-28
- Author: NAP LCBP3-DMS & Gemini
- Status: FINAL-Rev.04
- Classification: Internal Technical Documentation
- Approved By: Nattanin
End of Backend Development Plan v1.4.4