251128:1700 Backend to T3.1.1
This commit is contained in:
14
.vscode/extensions.json
vendored
14
.vscode/extensions.json
vendored
@@ -7,9 +7,6 @@
|
||||
// Code Quality & Error Handling
|
||||
"usernamehw.errorlens",
|
||||
"yoavbls.pretty-typescript-errors",
|
||||
"wix.vscode-import-cost",
|
||||
|
||||
// Comments & Documentation
|
||||
"aaron-bond.better-comments",
|
||||
"gruntfuggly.todo-tree",
|
||||
|
||||
@@ -23,21 +20,12 @@
|
||||
|
||||
// API Testing
|
||||
"rangav.vscode-thunder-client",
|
||||
"humao.rest-client",
|
||||
|
||||
// Auto Tags
|
||||
"formulahendry.auto-close-tag",
|
||||
"formulahendry.auto-rename-tag",
|
||||
|
||||
// Docker & DevOps
|
||||
"ms-azuretools.vscode-docker",
|
||||
"ms-kubernetes-tools.vscode-kubernetes-tools",
|
||||
|
||||
// Database
|
||||
"mtxr.sqltools",
|
||||
"mongodb.mongodb-vscode",
|
||||
|
||||
// YAML & Config
|
||||
"redhat.vscode-yaml",
|
||||
"mikestead.dotenv",
|
||||
"editorconfig.editorconfig",
|
||||
@@ -61,7 +49,7 @@
|
||||
"wallabyjs.console-ninja",
|
||||
|
||||
// Icons & Theme
|
||||
"pkief.material-icon-theme",
|
||||
"pkief.material-icon-theme"
|
||||
|
||||
// AI Assistance (Optional - เลือก 1 อัน)
|
||||
// "github.copilot",
|
||||
|
||||
709
2_Backend_Plan_V1_4_4.Phase_Addition.md
Normal file
709
2_Backend_Plan_V1_4_4.Phase_Addition.md
Normal file
@@ -0,0 +1,709 @@
|
||||
# 🎯 **Admin Panel Comprehensive Analysis สำหรับ LCBP3-DMS v1.4.4**
|
||||
|
||||
จากการวิเคราะห์ Requirements และ Technical Design ทั้งหมด ขอนำเสนอ Admin Panel ที่สมบูรณ์สำหรับระบบ LCBP3-DMS
|
||||
|
||||
## 📊 **ภาพรวม Admin Panel Structure**
|
||||
|
||||
### **Role-Based Access Levels**
|
||||
|
||||
- **Superadmin:** Full system access
|
||||
- **Org Admin:** Organization-level management
|
||||
- **Project Manager:** Project-level administration
|
||||
- **Contract Admin:** Contract-specific management
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ **1. System Administration**
|
||||
|
||||
### **1.1 Global System Configuration**
|
||||
|
||||
```typescript
|
||||
interface SystemConfig {
|
||||
maintenance_mode: boolean;
|
||||
session_timeout: number;
|
||||
password_policy: {
|
||||
min_length: number;
|
||||
require_uppercase: boolean;
|
||||
require_numbers: boolean;
|
||||
require_special_chars: boolean;
|
||||
expiry_days: number;
|
||||
};
|
||||
file_upload: {
|
||||
max_size_mb: number;
|
||||
allowed_types: string[];
|
||||
virus_scan_enabled: boolean;
|
||||
};
|
||||
notification: {
|
||||
email_enabled: boolean;
|
||||
line_enabled: boolean;
|
||||
digest_frequency: number; // minutes
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Toggle Maintenance Mode
|
||||
- [ ] Configure Security Policies
|
||||
- [ ] Manage File Upload Settings
|
||||
- [ ] Configure Notification Channels
|
||||
- [ ] System Health Monitoring Dashboard
|
||||
|
||||
### **1.2 Audit & Monitoring Center**
|
||||
|
||||
```typescript
|
||||
interface AuditDashboard {
|
||||
security_metrics: {
|
||||
failed_logins: number;
|
||||
file_scans: number;
|
||||
virus_detections: number;
|
||||
permission_changes: number;
|
||||
};
|
||||
user_activity: AuditLog[];
|
||||
system_performance: PerformanceMetrics;
|
||||
recent_errors: SystemError[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Real-time Activity Monitoring
|
||||
- [ ] Security Incident Reporting
|
||||
- [ ] Performance Metrics Dashboard
|
||||
- [ ] Error Log Viewer
|
||||
- [ ] Export Audit Reports
|
||||
|
||||
---
|
||||
|
||||
## 👥 **2. User & Organization Management**
|
||||
|
||||
### **2.1 User Management**
|
||||
|
||||
```typescript
|
||||
interface UserManagement {
|
||||
user_list: User[];
|
||||
bulk_operations: {
|
||||
import_users: FileUpload;
|
||||
export_users: CSVExport;
|
||||
bulk_assign_roles: RoleAssignment[];
|
||||
};
|
||||
user_lifecycle: {
|
||||
onboarding_workflow: WorkflowConfig;
|
||||
offboarding_checklist: ChecklistItem[];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] User CRUD Operations
|
||||
- [ ] Bulk User Import/Export
|
||||
- [ ] User Activity Tracking
|
||||
- [ ] Password Reset Management
|
||||
- [ ] User Session Management
|
||||
|
||||
### **2.2 Organization Hierarchy**
|
||||
|
||||
```typescript
|
||||
interface OrganizationManagement {
|
||||
organization_tree: Organization[];
|
||||
department_structure: Department[];
|
||||
contact_persons: Contact[];
|
||||
organization_settings: {
|
||||
document_retention_policy: RetentionPolicy;
|
||||
notification_preferences: NotificationConfig;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Organization CRUD
|
||||
- [ ] Department Structure Management
|
||||
- [ ] Contact Person Assignment
|
||||
- [ ] Organization-specific Policies
|
||||
|
||||
### **2.3 Advanced RBAC Management**
|
||||
|
||||
```typescript
|
||||
interface RBACManagement {
|
||||
role_definitions: Role[];
|
||||
permission_matrix: Permission[];
|
||||
assignment_rules: {
|
||||
automatic_assignments: AutoAssignmentRule[];
|
||||
conditional_access: ConditionalRule[];
|
||||
};
|
||||
permission_audit: {
|
||||
permission_usage: UsageStats[];
|
||||
conflict_detection: Conflict[];
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Custom Role Creation
|
||||
- [ ] Granular Permission Management
|
||||
- [ ] Automatic Role Assignment Rules
|
||||
- [ ] Permission Conflict Detection
|
||||
- [ ] Role Usage Analytics
|
||||
|
||||
---
|
||||
|
||||
## 📁 **3. Project & Contract Administration**
|
||||
|
||||
### **3.1 Project Portfolio Management**
|
||||
|
||||
```typescript
|
||||
interface ProjectManagement {
|
||||
project_dashboard: ProjectOverview[];
|
||||
project_phases: Phase[];
|
||||
milestone_tracking: Milestone[];
|
||||
resource_allocation: Resource[];
|
||||
project_analytics: ProjectMetrics;
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Project Lifecycle Management
|
||||
- [ ] Phase and Milestone Tracking
|
||||
- [ ] Resource Allocation
|
||||
- [ ] Project Performance Analytics
|
||||
- [ ] Project Documentation Repository
|
||||
|
||||
### **3.2 Contract Administration**
|
||||
|
||||
```typescript
|
||||
interface ContractManagement {
|
||||
contract_register: Contract[];
|
||||
party_management: ContractParty[];
|
||||
amendment_tracking: Amendment[];
|
||||
compliance_monitoring: ComplianceCheck[];
|
||||
financial_tracking: FinancialMetrics;
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Contract CRUD Operations
|
||||
- [ ] Contract Party Management
|
||||
- [ ] Amendment History
|
||||
- [ ] Compliance Monitoring
|
||||
- [ ] Financial Tracking
|
||||
|
||||
### **3.3 Project-Organization Mapping**
|
||||
|
||||
```typescript
|
||||
interface ProjectOrgMapping {
|
||||
project_assignments: ProjectAssignment[];
|
||||
access_control: AccessRule[];
|
||||
collaboration_settings: CollaborationConfig;
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Organization Assignment to Projects
|
||||
- [ ] Cross-Organization Collaboration Settings
|
||||
- [ ] Access Control Configuration
|
||||
|
||||
---
|
||||
|
||||
## 📋 **4. Master Data Management**
|
||||
|
||||
### **4.1 Document Type Ecosystem**
|
||||
|
||||
```typescript
|
||||
interface DocumentTypeManagement {
|
||||
correspondence_types: DocumentType[];
|
||||
rfa_types: RFAType[];
|
||||
circulation_types: CirculationType[];
|
||||
drawing_categories: DrawingCategory[];
|
||||
type_hierarchy: TypeHierarchy;
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Document Type CRUD
|
||||
- [ ] Type-Specific Workflow Configuration
|
||||
- [ ] Category Hierarchy Management
|
||||
- [ ] Template Association
|
||||
|
||||
### **4.2 Discipline & Classification System**
|
||||
|
||||
```typescript
|
||||
interface DisciplineManagement {
|
||||
disciplines: Discipline[];
|
||||
sub_types: SubType[];
|
||||
classification_rules: ClassificationRule[];
|
||||
mapping_configurations: MappingConfig[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Discipline CRUD (ตาม Requirement 6B)
|
||||
- [ ] Sub-Type Management with Number Mapping
|
||||
- [ ] Automatic Classification Rules
|
||||
- [ ] Cross-Reference Mapping
|
||||
|
||||
### **4.3 Status & Code Management**
|
||||
|
||||
```typescript
|
||||
interface StatusManagement {
|
||||
status_codes: StatusCode[];
|
||||
transition_rules: TransitionRule[];
|
||||
status_workflows: StatusWorkflow[];
|
||||
automated_status_changes: AutoStatusChange[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Status Code Configuration
|
||||
- [ ] State Transition Rules
|
||||
- [ ] Automated Status Updates
|
||||
- [ ] Status Change Analytics
|
||||
|
||||
---
|
||||
|
||||
## 🔢 **5. Document Numbering System Administration**
|
||||
|
||||
### **5.1 Numbering Format Configuration**
|
||||
|
||||
```typescript
|
||||
interface NumberingFormatManagement {
|
||||
format_templates: NumberingTemplate[];
|
||||
token_library: TokenDefinition[];
|
||||
format_preview: FormatPreview;
|
||||
validation_rules: ValidationRule[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Template Editor with Live Preview
|
||||
- [ ] Custom Token Definition
|
||||
- [ ] Format Validation
|
||||
- [ ] Bulk Template Application
|
||||
|
||||
### **5.2 Counter Management**
|
||||
|
||||
```typescript
|
||||
interface CounterManagement {
|
||||
counter_groups: CounterGroup[];
|
||||
reset_schedules: ResetSchedule[];
|
||||
counter_audit: CounterHistory[];
|
||||
conflict_resolution: ConflictResolutionRule[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Counter Group Configuration
|
||||
- [ ] Scheduled Reset Management
|
||||
- [ ] Counter Audit Trail
|
||||
- [ ] Conflict Resolution Rules
|
||||
|
||||
### **5.3 Numbering Rule Engine**
|
||||
|
||||
```typescript
|
||||
interface NumberingRuleEngine {
|
||||
conditional_rules: ConditionalRule[];
|
||||
context_resolvers: ContextResolver[];
|
||||
fallback_strategies: FallbackStrategy[];
|
||||
performance_monitoring: PerformanceMetrics;
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Conditional Numbering Rules
|
||||
- [ ] Context Variable Management
|
||||
- [ ] Fallback Strategy Configuration
|
||||
- [ ] Performance Optimization
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ **6. Workflow & Routing Administration**
|
||||
|
||||
### **6.1 Workflow DSL Management**
|
||||
|
||||
```typescript
|
||||
interface WorkflowDSLManagement {
|
||||
workflow_library: WorkflowDefinition[];
|
||||
dsl_editor: DSLEditor;
|
||||
version_control: VersionHistory[];
|
||||
deployment_pipeline: DeploymentConfig[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Visual Workflow Designer
|
||||
- [ ] DSL Code Editor with Syntax Highlighting
|
||||
- [ ] Version Control and Rollback
|
||||
- [ ] Testing and Deployment Pipeline
|
||||
|
||||
### **6.2 Routing Template Administration**
|
||||
|
||||
```typescript
|
||||
interface RoutingTemplateManagement {
|
||||
template_library: RoutingTemplate[];
|
||||
step_configurations: StepConfig[];
|
||||
approval_chains: ApprovalChain[];
|
||||
escalation_rules: EscalationRule[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Template CRUD Operations
|
||||
- [ ] Drag-and-Drop Step Configuration
|
||||
- [ ] Approval Chain Management
|
||||
- [ ] Escalation Rule Setup
|
||||
|
||||
### **6.3 Workflow Analytics**
|
||||
|
||||
```typescript
|
||||
interface WorkflowAnalytics {
|
||||
performance_metrics: WorkflowMetrics[];
|
||||
bottleneck_analysis: Bottleneck[];
|
||||
compliance_reporting: ComplianceReport[];
|
||||
optimization_recommendations: Recommendation[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Workflow Performance Dashboard
|
||||
- [ ] Bottleneck Identification
|
||||
- [ ] Compliance Reporting
|
||||
- [ ] Optimization Suggestions
|
||||
|
||||
---
|
||||
|
||||
## 📊 **7. Reporting & Analytics Center**
|
||||
|
||||
### **7.1 Custom Report Builder**
|
||||
|
||||
```typescript
|
||||
interface ReportBuilder {
|
||||
data_sources: DataSource[];
|
||||
visualization_types: VisualizationType[];
|
||||
report_templates: ReportTemplate[];
|
||||
scheduling: ScheduleConfig[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Drag-and-Drop Report Designer
|
||||
- [ ] Multiple Visualization Options
|
||||
- [ ] Template Library
|
||||
- [ ] Automated Report Scheduling
|
||||
|
||||
### **7.2 Business Intelligence**
|
||||
|
||||
```typescript
|
||||
interface BusinessIntelligence {
|
||||
kpi_dashboard: KPIMetric[];
|
||||
trend_analysis: TrendData[];
|
||||
predictive_analytics: PredictiveModel[];
|
||||
data_export: ExportConfig[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Real-time KPI Dashboard
|
||||
- [ ] Trend Analysis Tools
|
||||
- [ ] Predictive Analytics
|
||||
- [ ] Data Export and Integration
|
||||
|
||||
### **7.3 Compliance Reporting**
|
||||
|
||||
```typescript
|
||||
interface ComplianceReporting {
|
||||
regulatory_reports: RegulatoryReport[];
|
||||
audit_trails: AuditTrail[];
|
||||
compliance_dashboard: ComplianceMetric[];
|
||||
certification_tracking: Certification[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Pre-built Regulatory Reports
|
||||
- [ ] Comprehensive Audit Trails
|
||||
- [ ] Compliance Dashboard
|
||||
- [ ] Certification Management
|
||||
|
||||
---
|
||||
|
||||
## 🔐 **8. Security & Compliance Center**
|
||||
|
||||
### **8.1 Security Policy Management**
|
||||
|
||||
```typescript
|
||||
interface SecurityPolicyManagement {
|
||||
access_policies: AccessPolicy[];
|
||||
data_classification: DataClassification[];
|
||||
encryption_settings: EncryptionConfig[];
|
||||
security_incidents: SecurityIncident[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Access Policy Configuration
|
||||
- [ ] Data Classification Scheme
|
||||
- [ ] Encryption Management
|
||||
- [ ] Security Incident Tracking
|
||||
|
||||
### **8.2 Compliance Framework**
|
||||
|
||||
```typescript
|
||||
interface ComplianceFramework {
|
||||
compliance_rules: ComplianceRule[];
|
||||
control_testing: ControlTest[];
|
||||
evidence_management: Evidence[];
|
||||
compliance_calendar: ComplianceEvent[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Compliance Rule Engine
|
||||
- [ ] Control Testing Framework
|
||||
- [ ] Evidence Collection
|
||||
- [ ] Compliance Calendar
|
||||
|
||||
### **8.3 Risk Management**
|
||||
|
||||
```typescript
|
||||
interface RiskManagement {
|
||||
risk_register: Risk[];
|
||||
risk_assessments: RiskAssessment[];
|
||||
mitigation_plans: MitigationPlan[];
|
||||
risk_dashboard: RiskMetrics;
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Risk Identification and Registration
|
||||
- [ ] Risk Assessment Tools
|
||||
- [ ] Mitigation Planning
|
||||
- [ ] Risk Monitoring Dashboard
|
||||
|
||||
---
|
||||
|
||||
## 📧 **9. Notification & Communication Management**
|
||||
|
||||
### **9.1 Notification Template System**
|
||||
|
||||
```typescript
|
||||
interface NotificationTemplateManagement {
|
||||
email_templates: EmailTemplate[];
|
||||
line_templates: LineTemplate[];
|
||||
system_notifications: SystemTemplate[];
|
||||
template_variables: TemplateVariable[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Multi-channel Template Management
|
||||
- [ ] Variable Substitution System
|
||||
- [ ] Template Testing and Preview
|
||||
- [ ] Bulk Template Operations
|
||||
|
||||
### **9.2 Subscription Management**
|
||||
|
||||
```typescript
|
||||
interface SubscriptionManagement {
|
||||
user_preferences: UserPreference[];
|
||||
group_subscriptions: GroupSubscription[];
|
||||
escalation_policies: EscalationPolicy[];
|
||||
delivery_reports: DeliveryReport[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] User Preference Management
|
||||
- [ ] Group Subscription Configuration
|
||||
- [ ] Escalation Policy Setup
|
||||
- [ ] Delivery Monitoring
|
||||
|
||||
### **9.3 Digest Configuration**
|
||||
|
||||
```typescript
|
||||
interface DigestConfiguration {
|
||||
digest_rules: DigestRule[];
|
||||
grouping_criteria: GroupingCriteria[];
|
||||
timing_configurations: TimingConfig[];
|
||||
content_prioritization: PriorityRule[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Digest Rule Engine
|
||||
- [ ] Content Grouping Configuration
|
||||
- [ ] Timing and Frequency Settings
|
||||
- [ ] Content Prioritization Rules
|
||||
|
||||
---
|
||||
|
||||
## 🗃️ **10. Data Management & Maintenance**
|
||||
|
||||
### **10.1 Data Lifecycle Management**
|
||||
|
||||
```typescript
|
||||
interface DataLifecycleManagement {
|
||||
retention_policies: RetentionPolicy[];
|
||||
archival_rules: ArchivalRule[];
|
||||
purge_schedules: PurgeSchedule[];
|
||||
data_governance: GovernancePolicy[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Retention Policy Configuration
|
||||
- [ ] Automated Archival Rules
|
||||
- [ ] Scheduled Data Purge
|
||||
- [ ] Data Governance Framework
|
||||
|
||||
### **10.2 Backup & Recovery**
|
||||
|
||||
```typescript
|
||||
interface BackupRecoveryManagement {
|
||||
backup_configurations: BackupConfig[];
|
||||
recovery_procedures: RecoveryProcedure[];
|
||||
disaster_recovery: DisasterRecoveryPlan[];
|
||||
backup_monitoring: BackupMonitor[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Backup Schedule Management
|
||||
- [ ] Recovery Procedure Documentation
|
||||
- [ ] Disaster Recovery Planning
|
||||
- [ ] Backup Status Monitoring
|
||||
|
||||
### **10.3 System Maintenance**
|
||||
|
||||
```typescript
|
||||
interface SystemMaintenance {
|
||||
maintenance_windows: MaintenanceWindow[];
|
||||
update_management: UpdateConfig[];
|
||||
performance_tuning: TuningParameter[];
|
||||
cleanup_jobs: CleanupJob[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Maintenance Window Scheduling
|
||||
- [ ] Update Management
|
||||
- [ ] Performance Tuning Parameters
|
||||
- [ ] Automated Cleanup Jobs
|
||||
|
||||
---
|
||||
|
||||
## 🎯 **11. Dashboard & Overview**
|
||||
|
||||
### **11.1 Executive Dashboard**
|
||||
|
||||
```typescript
|
||||
interface ExecutiveDashboard {
|
||||
system_health: HealthMetric[];
|
||||
business_metrics: BusinessMetric[];
|
||||
user_engagement: EngagementMetric[];
|
||||
security_posture: SecurityMetric[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Real-time System Health Monitoring
|
||||
- [ ] Business Performance Metrics
|
||||
- [ ] User Engagement Analytics
|
||||
- [ ] Security Posture Assessment
|
||||
|
||||
### **11.2 Operational Dashboard**
|
||||
|
||||
```typescript
|
||||
interface OperationalDashboard {
|
||||
workflow_monitoring: WorkflowStatus[];
|
||||
document_metrics: DocumentMetric[];
|
||||
user_productivity: ProductivityMetric[];
|
||||
system_utilization: UtilizationMetric[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Workflow Status Monitoring
|
||||
- [ ] Document Processing Metrics
|
||||
- [ ] User Productivity Analytics
|
||||
- [ ] System Resource Utilization
|
||||
|
||||
---
|
||||
|
||||
## 🔄 **12. Integration & API Management**
|
||||
|
||||
### **12.1 API Gateway Administration**
|
||||
|
||||
```typescript
|
||||
interface APIManagement {
|
||||
api_endpoints: APIEndpoint[];
|
||||
rate_limiting: RateLimitConfig[];
|
||||
authentication_settings: AuthConfig[];
|
||||
api_analytics: APIAnalytics[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] API Endpoint Management
|
||||
- [ ] Rate Limiting Configuration
|
||||
- [ ] Authentication Settings
|
||||
- [ ] API Usage Analytics
|
||||
|
||||
### **12.2 External Integration**
|
||||
|
||||
```typescript
|
||||
interface ExternalIntegration {
|
||||
webhook_configurations: WebhookConfig[];
|
||||
third_party_connectors: Connector[];
|
||||
data_sync_rules: SyncRule[];
|
||||
integration_monitoring: IntegrationMonitor[];
|
||||
}
|
||||
```
|
||||
|
||||
**Features:**
|
||||
|
||||
- [ ] Webhook Management
|
||||
- [ ] Third-party Connector Configuration
|
||||
- [ ] Data Synchronization Rules
|
||||
- [ ] Integration Health Monitoring
|
||||
|
||||
---
|
||||
|
||||
## 🎯 **Critical Success Factors**
|
||||
|
||||
1. **Unified Administration Experience** - Single pane of glass สำหรับทุกการจัดการ
|
||||
2. **Role-Based Access Control** - แต่ละระดับเห็นและจัดการได้เฉพาะส่วนของตัวเอง
|
||||
3. **Real-time Monitoring** - ระบบ monitoring แบบ real-time ทุกส่วน
|
||||
4. **Audit Trail** - ทุกการเปลี่ยนแปลงใน Admin Panel ถูกบันทึกไว้
|
||||
5. **Performance** - Admin operations ต้องรวดเร็วแม้มีข้อมูลจำนวนมาก
|
||||
6. **User Experience** - Interface ใช้งานง่าย แม้สำหรับฟีเจอร์ที่ซับซ้อน
|
||||
|
||||
Admin Panel นี้จะทำให้ผู้ดูแลระบบสามารถจัดการ LCBP3-DMS ได้อย่างสมบูรณ์แบบ ตั้งแต่การตั้งค่าระดับพื้นฐานไปจนถึงการจัดการ workflow ที่ซับซ้อนและการวิเคราะห์ข้อมูลเชิงลึก
|
||||
File diff suppressed because it is too large
Load Diff
@@ -36,229 +36,177 @@
|
||||
|
||||
### **โครงสร้างโปรเจกต์**
|
||||
|
||||
```tree
|
||||
app/
|
||||
├── (auth)/
|
||||
│ ├── login/
|
||||
│ └── register/
|
||||
├── (dashboard)/
|
||||
```
|
||||
📁frontend
|
||||
├── .env.local
|
||||
├── .eslintrc.json
|
||||
├── .gitignore
|
||||
├── components.json
|
||||
├── middleware.ts
|
||||
├── next-env.d.ts
|
||||
├── next.config.mjs
|
||||
├── package.json
|
||||
├── pnpm-lock.yaml
|
||||
├── postcss.config.mjs
|
||||
├── README.md
|
||||
├── tailwind.config.ts
|
||||
├── tsconfig.json
|
||||
├── 📁app
|
||||
│ ├── 📁(auth)
|
||||
│ │ └── 📁login
|
||||
│ │ │ └── page.tsx
|
||||
│ │ └── layout.tsx
|
||||
│ ├── 📁(dashboard)
|
||||
│ │ └── 📁admin
|
||||
│ │ ├──📁users
|
||||
│ │ │ └── page.tsx
|
||||
│ │ ├──📁correspondences
|
||||
│ │ │ └── 📁new
|
||||
│ │ │ │ └── page.tsx
|
||||
│ │ │ └── page.tsx
|
||||
│ │ ├──📁dashboard
|
||||
│ │ │ └── page.tsx
|
||||
│ │ ├──📁profile
|
||||
│ │ │ └── page.tsx
|
||||
│ │ ├──📁projects
|
||||
│ │ │ ├──📁new
|
||||
│ │ │ │ └── page.tsx
|
||||
│ │ │ └── page.tsx
|
||||
│ │ └── layout.tsx
|
||||
│ ├── 📁api
|
||||
│ │ └── 📁auth
|
||||
│ │ └── 📁[...nextauth]
|
||||
│ │ └── route.ts
|
||||
│ ├── 📁demo
|
||||
│ │ └── page.tsx
|
||||
│ ├── 📁fonts
|
||||
│ │ ├── GeistMonoVF.woff
|
||||
│ │ └── GeistVF.woff
|
||||
│ ├── favicon.ico
|
||||
│ ├── globals copy.css
|
||||
│ ├── globals.css
|
||||
│ ├── layout copy.tsx
|
||||
│ ├── layout.tsx
|
||||
│ ├── page.tsx
|
||||
│ └── components/
|
||||
├── admin/
|
||||
│ ├── users/
|
||||
│ ├── roles/
|
||||
│ └── numbering-formats/
|
||||
├── correspondences/
|
||||
│ ├── page.tsx
|
||||
│ ├── [id]/
|
||||
│ └── new/
|
||||
├── rfas/
|
||||
│ ├── page.tsx
|
||||
│ ├── [id]/
|
||||
│ └── new/
|
||||
├── drawings/
|
||||
├── circulations/
|
||||
├── transmittals/
|
||||
├── search/
|
||||
└── profile/
|
||||
|
||||
components/
|
||||
├── ui/ # shadcn/ui components
|
||||
├── forms/ # Dynamic form components
|
||||
├── tables/ # Responsive data tables
|
||||
├── workflow/ # Workflow visualization
|
||||
├── file-upload/ # File upload with security
|
||||
├── notifications/ # Notification system
|
||||
└── layout/ # App layout components
|
||||
|
||||
lib/
|
||||
├── api/ # API clients & interceptors
|
||||
├── auth/ # Authentication utilities
|
||||
├── stores/ # Zustand stores
|
||||
├── hooks/ # Custom React hooks
|
||||
├── utils/ # Utility functions
|
||||
├── constants/ # App constants
|
||||
└── types/ # TypeScript type definitions
|
||||
|
||||
styles/
|
||||
├── globals.css
|
||||
└── components/
|
||||
|
||||
__tests__/
|
||||
├── unit/
|
||||
├── integration/
|
||||
└── e2e/
|
||||
│ └── page.tsx
|
||||
├── 📁components
|
||||
│ ├── 📁custom
|
||||
│ │ ├── file-upload-zone.tsx
|
||||
│ │ ├── responsive-data-table.tsx
|
||||
│ │ └── workflow-visualizer.tsx
|
||||
│ ├── 📁dashboard
|
||||
│ │ └── recent-activity.tsx
|
||||
│ ├── 📁forms
|
||||
│ │ └── file-upload.tsx
|
||||
│ ├── 📁layout
|
||||
│ │ ├── dashboard-shell.tsx
|
||||
│ │ ├── navbar.tsx
|
||||
│ │ ├── sidebar.tsx
|
||||
│ │ └── user-nav.tsx
|
||||
│ ├── 📁tables
|
||||
│ └── 📁ui
|
||||
│ ├── avatar.tsx
|
||||
│ ├── badge.tsx
|
||||
│ ├── button.tsx
|
||||
│ ├── calendar.tsx
|
||||
│ ├── card.tsx
|
||||
│ ├── checkbox.tsx
|
||||
│ ├── dropdown-menu.tsx
|
||||
│ ├── input.tsx
|
||||
│ ├── label.tsx
|
||||
│ ├── popover.tsx
|
||||
│ ├── progress.tsx
|
||||
│ ├── scroll-area.tsx
|
||||
│ ├── select.tsx
|
||||
│ ├── switch.tsx
|
||||
│ ├── table.tsx
|
||||
│ ├── tabs.tsx
|
||||
│ └── textarea.tsx
|
||||
├── 📁config
|
||||
│ └── menu.ts
|
||||
├── 📁lib
|
||||
│ ├── 📁api
|
||||
│ │ └── client.ts
|
||||
│ ├── 📁auth
|
||||
│ ├── 📁hooks
|
||||
│ ├── 📁services
|
||||
│ │ ├── circulation.service.ts
|
||||
│ │ ├── contract-drawing.service.ts
|
||||
│ │ ├── correspondence.service.ts
|
||||
│ │ ├── index.ts
|
||||
│ │ ├── json-schema.service.ts
|
||||
│ │ ├── master-data.service.ts
|
||||
│ │ ├── monitoring.service.ts
|
||||
│ │ ├── notification.service.ts
|
||||
│ │ ├── project.service.ts
|
||||
│ │ ├── rfa.service.ts
|
||||
│ │ ├── search.service.ts
|
||||
│ │ ├── shop-drawing.service.ts
|
||||
│ │ ├── transmittal.service.ts
|
||||
│ │ ├── user.service.ts
|
||||
│ │ └── workflow-engine.service.ts
|
||||
│ ├── 📁stores
|
||||
│ │ ├── draft-store.ts
|
||||
│ │ └── ui-store.ts
|
||||
│ ├── auth.ts
|
||||
│ └── utils.ts
|
||||
├── 📁providers
|
||||
│ ├── query-provider.tsx
|
||||
│ └── session-provider.tsx
|
||||
├── 📁public
|
||||
├── 📁styles
|
||||
└── 📁types
|
||||
└── 📁dto
|
||||
└── next-auth.d.ts
|
||||
├── 📁circulation
|
||||
│ ├── create-circulation.dto.ts
|
||||
│ ├── search-circulation.dto.ts
|
||||
│ └── update-circulation-routing.dto.ts
|
||||
├── 📁correspondence
|
||||
│ ├── add-reference.dto.ts
|
||||
│ ├── create-correspondence.dto.ts
|
||||
│ ├── search-correspondence.dto.ts
|
||||
│ ├── submit-correspondence.dto.ts
|
||||
│ └── workflow-action.dto.ts
|
||||
├── 📁drawing
|
||||
│ ├── contract-drawing.dto.ts
|
||||
│ └── shop-drawing.dto.ts
|
||||
├── 📁json-schema
|
||||
│ └── json-schema.dto.ts
|
||||
├── 📁master
|
||||
│ ├── discipline.dto.ts
|
||||
│ ├── number-format.dto.ts
|
||||
│ ├── sub-type.dto.ts
|
||||
│ └── tag.dto.ts
|
||||
├── 📁monitoring
|
||||
│ └── set-maintenance.dto.ts
|
||||
├── 📁notification
|
||||
│ └── notification.dto.ts
|
||||
├── 📁project
|
||||
│ └── project.dto.ts
|
||||
├── 📁rfa
|
||||
│ └── rfa.dto.ts
|
||||
├── 📁search
|
||||
│ └── search-query.dto.ts
|
||||
├── 📁transmittal
|
||||
│ └── transmittal.dto.ts
|
||||
├── 📁user
|
||||
│ └── user.dto.ts
|
||||
└── 📁workflow-engine
|
||||
└── workflow-engine.dto.ts
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗓️ **แผนการพัฒนาแบบ Phase-Based**
|
||||
|
||||
### **Dependency Diagram (ภาพรวม)**
|
||||
|
||||
```mermaid
|
||||
%% Phase 0: Foundation Setup
|
||||
subgraph Phase0 [Phase 0: Foundation & Configuration]
|
||||
F0_1[F0.1: Project Setup & Tooling]
|
||||
F0_2[F0.2: Design System & UI Components]
|
||||
F0_3[F0.3: API Client & Authentication]
|
||||
F0_4[F0.4: State Management Setup]
|
||||
end
|
||||
|
||||
%% Phase 1: Core Layout & Navigation
|
||||
subgraph Phase1 [Phase 1: Core Application Structure]
|
||||
F1_1[F1.1: Main Layout & Navigation]
|
||||
F1_2[F1.2: Authentication Pages]
|
||||
F1_3[F1.3: Dashboard & Landing]
|
||||
F1_4[F1.4: Responsive Design System]
|
||||
end
|
||||
|
||||
%% Phase 2: User Management & Profile
|
||||
subgraph Phase2 [Phase 2: User Management & Security]
|
||||
F2_1[F2.1: User Profile & Settings]
|
||||
F2_2[F2.2: Admin Panel - User Management]
|
||||
F2_3[F2.3: Admin Panel - Role Management]
|
||||
F2_4[F2.4: Permission Integration]
|
||||
end
|
||||
|
||||
%% Phase 3: Project & Organization Management
|
||||
subgraph Phase3 [Phase 3: Project Structure]
|
||||
F3_1[F3.1: Project Management UI]
|
||||
F3_2[F3.2: Organization Management]
|
||||
F3_3[F3.3: Contract Management]
|
||||
end
|
||||
|
||||
%% Phase 4: Correspondence Management
|
||||
subgraph Phase4 [Phase 4: Correspondence System]
|
||||
F4_1[F4.1: Correspondence List & Search]
|
||||
F4_2[F4.2: Correspondence Creation Form]
|
||||
F4_3[F4.3: Correspondence Detail View]
|
||||
F4_4[F4.4: File Upload Integration]
|
||||
end
|
||||
|
||||
%% Phase 5: Workflow & Routing System
|
||||
subgraph Phase5 [Phase 5: Workflow Management]
|
||||
F5_1[F5.1: Workflow Visualization Component]
|
||||
F5_2[F5.2: Routing Template Management]
|
||||
F5_3[F5.3: Workflow Step Actions]
|
||||
F5_4[F5.4: Real-time Status Updates]
|
||||
end
|
||||
|
||||
%% Phase 6: Drawing Management
|
||||
subgraph Phase6 [Phase 6: Drawing System]
|
||||
F6_1[F6.1: Contract Drawings Management]
|
||||
F6_2[F6.2: Shop Drawings Management]
|
||||
F6_3[F6.3: Drawing Revision System]
|
||||
F6_4[F6.4: Drawing References]
|
||||
end
|
||||
|
||||
%% Phase 7: RFA & Approval Workflows
|
||||
subgraph Phase7 [Phase 7: RFA System]
|
||||
F7_1[F7.1: RFA List & Dashboard]
|
||||
F7_2[F7.2: RFA Creation with Dynamic Forms]
|
||||
F7_3[F7.3: RFA Workflow Integration]
|
||||
F7_4[F7.4: RFA Approval Interface]
|
||||
end
|
||||
|
||||
%% Phase 8: Circulation & Internal Routing
|
||||
subgraph Phase8 [Phase 8: Internal Workflows]
|
||||
F8_1[F8.1: Circulation Management]
|
||||
F8_2[F8.2: Task Assignment Interface]
|
||||
F8_3[F8.3: Internal Approval Flows]
|
||||
end
|
||||
|
||||
%% Phase 9: Advanced Features
|
||||
subgraph Phase9 [Phase 9: Advanced Features]
|
||||
F9_1[F9.1: Advanced Search Interface]
|
||||
F9_2[F9.2: Notification System]
|
||||
F9_3[F9.3: Reporting & Analytics]
|
||||
F9_4[F9.4: Mobile Optimization]
|
||||
end
|
||||
|
||||
%% Phase 10: Testing & Optimization
|
||||
subgraph Phase10 [Phase 10: Testing & Polish]
|
||||
F10_1[F10.1: Comprehensive Testing]
|
||||
F10_2[F10.2: Performance Optimization]
|
||||
F10_3[F10.3: Security Hardening]
|
||||
F10_4[F10.4: Documentation]
|
||||
end
|
||||
|
||||
%% Dependencies
|
||||
F0_1 --> F0_2
|
||||
F0_1 --> F0_3
|
||||
F0_1 --> F0_4
|
||||
|
||||
F0_2 --> F1_1
|
||||
F0_3 --> F1_1
|
||||
F0_4 --> F1_1
|
||||
F1_1 --> F1_2
|
||||
F1_1 --> F1_3
|
||||
F1_1 --> F1_4
|
||||
|
||||
F1_1 --> F2_1
|
||||
F1_3 --> F2_1
|
||||
F0_3 --> F2_1
|
||||
F2_1 --> F2_2
|
||||
F2_2 --> F2_3
|
||||
F2_3 --> F2_4
|
||||
|
||||
F1_1 --> F3_1
|
||||
F2_4 --> F3_1
|
||||
F3_1 --> F3_2
|
||||
F3_2 --> F3_3
|
||||
|
||||
F1_1 --> F4_1
|
||||
F3_1 --> F4_1
|
||||
F4_1 --> F4_2
|
||||
F4_2 --> F4_3
|
||||
F4_2 --> F4_4
|
||||
|
||||
F4_1 --> F5_1
|
||||
F4_2 --> F5_2
|
||||
F4_3 --> F5_3
|
||||
F5_1 --> F5_4
|
||||
|
||||
F3_1 --> F6_1
|
||||
F4_4 --> F6_1
|
||||
F6_1 --> F6_2
|
||||
F6_2 --> F6_3
|
||||
F6_3 --> F6_4
|
||||
|
||||
F4_1 --> F7_1
|
||||
F5_1 --> F7_1
|
||||
F6_2 --> F7_1
|
||||
F7_1 --> F7_2
|
||||
F7_2 --> F7_3
|
||||
F7_3 --> F7_4
|
||||
|
||||
F4_1 --> F8_1
|
||||
F5_3 --> F8_1
|
||||
F8_1 --> F8_2
|
||||
F8_2 --> F8_3
|
||||
|
||||
F4_1 --> F9_1
|
||||
F7_1 --> F9_1
|
||||
F1_3 --> F9_2
|
||||
F5_4 --> F9_2
|
||||
F1_3 --> F9_3
|
||||
F1_4 --> F9_4
|
||||
|
||||
F1_1 --> F10_1
|
||||
F4_1 --> F10_1
|
||||
F7_1 --> F10_1
|
||||
F10_1 --> F10_2
|
||||
F10_2 --> F10_3
|
||||
F10_3 --> F10_4
|
||||
```
|
||||
|
||||
## **Phase 0: Foundation & Configuration (สัปดาห์ที่ 1)**
|
||||
### **Phase 0: Foundation & Configuration (สัปดาห์ที่ 1)**
|
||||
|
||||
**Milestone:** โครงสร้างพื้นฐานพร้อม รองรับ Development Workflow ที่มีประสิทธิภาพ
|
||||
|
||||
### **Phase 0: Tasks**
|
||||
|
||||
- **[ ] F0.1 Project Setup & Tooling**
|
||||
|
||||
- [ ] Initialize Next.js 14+ project with TypeScript
|
||||
- [ ] Configure pnpm workspace
|
||||
- [ ] Setup ESLint, Prettier, and pre-commit hooks
|
||||
@@ -269,6 +217,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** None
|
||||
|
||||
- **[ ] F0.2 Design System & UI Components**
|
||||
|
||||
- [ ] Setup color palette and design tokens
|
||||
- [ ] Create responsive design breakpoints
|
||||
- [ ] Implement core shadcn/ui components:
|
||||
@@ -284,6 +233,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F0.1
|
||||
|
||||
- **[ ] F0.3 API Client & Authentication**
|
||||
|
||||
- [ ] Setup Axios client with interceptors:
|
||||
- [ ] Idempotency-Key header injection
|
||||
- [ ] Authentication token management
|
||||
@@ -325,13 +275,14 @@ F10_3 --> F10_4
|
||||
|
||||
---
|
||||
|
||||
## **Phase 1: Core Application Structure (สัปดาห์ที่ 2)**
|
||||
### **Phase 1: Core Application Structure (สัปดาห์ที่ 2)**
|
||||
|
||||
**Milestone:** Layout หลักพร้อมใช้งาน การนำทางและ Authentication ทำงานได้
|
||||
|
||||
### **Phase 1: Tasks**
|
||||
|
||||
- **[ ] F1.1 Main Layout & Navigation**
|
||||
|
||||
- [ ] Create App Shell layout:
|
||||
- [ ] Navbar with user menu and notifications
|
||||
- [ ] Collapsible sidebar with navigation
|
||||
@@ -348,6 +299,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F0.2, F0.3
|
||||
|
||||
- **[ ] F1.2 Authentication Pages**
|
||||
|
||||
- [ ] Create login page with form validation
|
||||
- [ ] Implement forgot password flow
|
||||
- [ ] Create registration page (admin-only)
|
||||
@@ -358,6 +310,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F0.3, F1.1
|
||||
|
||||
- **[ ] F1.3 Dashboard & Landing**
|
||||
|
||||
- [ ] Create public landing page for non-authenticated users
|
||||
- [ ] Implement main dashboard with:
|
||||
- [ ] KPI cards (document counts, pending tasks)
|
||||
@@ -382,13 +335,13 @@ F10_3 --> F10_4
|
||||
|
||||
### **Phase 1: Testing - Core Structure**
|
||||
|
||||
#### **F1.T1 Layout Test Suite**
|
||||
- **[ ] F1.T1 Layout Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** Navigation components, layout responsiveness
|
||||
- [ ] **Integration Tests:** Route protection, permission-based navigation
|
||||
- [ ] **E2E Tests:** Complete user navigation flow
|
||||
|
||||
#### **F1.T2 Dashboard Test Suite**
|
||||
- **[ ] F1.T2 Dashboard Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** Dashboard components, data formatting
|
||||
- [ ] **Integration Tests:** Data fetching and display, real-time updates
|
||||
@@ -396,13 +349,14 @@ F10_3 --> F10_4
|
||||
|
||||
---
|
||||
|
||||
## **Phase 2: User Management & Security (สัปดาห์ที่ 3)**
|
||||
### **Phase 2: User Management & Security (สัปดาห์ที่ 3)**
|
||||
|
||||
**Milestone:** การจัดการผู้ใช้และสิทธิ์แบบสมบูรณ์
|
||||
|
||||
### **Phase 2: Tasks**
|
||||
|
||||
- **[ ] F2.1 User Profile & Settings**
|
||||
|
||||
- [ ] Create user profile page:
|
||||
- [ ] Personal information display/edit
|
||||
- [ ] Password change functionality
|
||||
@@ -414,6 +368,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F1.1, F0.4
|
||||
|
||||
- **[ ] F2.2 Admin Panel - User Management**
|
||||
|
||||
- [ ] Create user list with search and filters
|
||||
- [ ] Implement user creation form
|
||||
- [ ] Create user edit interface
|
||||
@@ -424,6 +379,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F1.1, F2.1
|
||||
|
||||
- **[ ] F2.3 Admin Panel - Role Management**
|
||||
|
||||
- [ ] Create role list and management interface
|
||||
- [ ] Implement role creation and editing
|
||||
- [ ] Create permission assignment interface
|
||||
@@ -446,22 +402,22 @@ F10_3 --> F10_4
|
||||
|
||||
### **Phase 2: User Management & Admin Panel (สัปดาห์ที่ 3)**
|
||||
|
||||
* **[ ] F2.5 Admin Panel - Master Data Management (Req 6B)** (New)
|
||||
* [ ] Create "Disciplines Management" page (CRUD)
|
||||
* [ ] Create "Sub-Types Management" page (CRUD + Mapping Number)
|
||||
* [ ] Create "Numbering Format" configuration page (Template Editor)
|
||||
* [ ] **Deliverable:** UI for Admin to configure system master data
|
||||
* [ ] **Dependencies:** F2.1
|
||||
- **[ ] F2.5 Admin Panel - Master Data Management (Req 6B)** (New)
|
||||
- [ ] Create "Disciplines Management" page (CRUD)
|
||||
- [ ] Create "Sub-Types Management" page (CRUD + Mapping Number)
|
||||
- [ ] Create "Numbering Format" configuration page (Template Editor)
|
||||
- [ ] **Deliverable:** UI for Admin to configure system master data
|
||||
- [ ] **Dependencies:** F2.1
|
||||
|
||||
### **Phase 2: Testing - User Management**
|
||||
|
||||
#### **F2.T1 User Management Test Suite**
|
||||
- **[ ] F2.T1 User Management Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** User CRUD operations, form validation
|
||||
- [ ] **Integration Tests:** User-role assignment, permission propagation
|
||||
- [ ] **Security Tests:** Permission escalation attempts, admin access control
|
||||
|
||||
#### **F2.T2 RBAC Test Suite**
|
||||
- **[ ] F2.T2 RBAC Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** Permission checks, role validation
|
||||
- [ ] **Integration Tests:** Multi-level permission enforcement, UI element protection
|
||||
@@ -469,13 +425,14 @@ F10_3 --> F10_4
|
||||
|
||||
---
|
||||
|
||||
## **Phase 3: Project Structure (สัปดาห์ที่ 4)**
|
||||
### **Phase 3: Project Structure (สัปดาห์ที่ 4)**
|
||||
|
||||
**Milestone:** การจัดการโครงสร้างโปรเจกต์และองค์กร
|
||||
|
||||
### **Phase 3: Tasks**
|
||||
|
||||
- **[ ] F3.1 Project Management UI**
|
||||
|
||||
- [ ] Create project list with search and filters
|
||||
- [ ] Implement project creation and editing
|
||||
- [ ] Create project detail view
|
||||
@@ -486,6 +443,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F1.1, F2.4
|
||||
|
||||
- **[ ] F3.2 Organization Management**
|
||||
|
||||
- [ ] Create organization list and management
|
||||
- [ ] Implement organization creation and editing
|
||||
- [ ] Create organization detail view
|
||||
@@ -507,21 +465,21 @@ F10_3 --> F10_4
|
||||
|
||||
### **Phase 3: Testing - Project Structure**
|
||||
|
||||
#### **F3.T1 Project Management Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** Project CRUD operations, validation
|
||||
- [ ] **Integration Tests:** Project-organization relationships, member management
|
||||
- [ ] **Business Logic Tests:** Project hierarchy, access control
|
||||
- **[ ] F3.T1 Project Management Test Suite**
|
||||
- [ ] **Unit Tests:** Project CRUD operations, validation
|
||||
- [ ] **Integration Tests:** Project-organization relationships, member management
|
||||
- [ ] **Business Logic Tests:** Project hierarchy, access control
|
||||
|
||||
---
|
||||
|
||||
## **Phase 4: Correspondence System (สัปดาห์ที่ 5-6)**
|
||||
### **Phase 4: Correspondence System (สัปดาห์ที่ 5-6)**
|
||||
|
||||
**Milestone:** ระบบจัดการเอกสารโต้ตอบแบบสมบูรณ์
|
||||
|
||||
### **Phase 4: Tasks**
|
||||
|
||||
- **[ ] F4.1 Correspondence List & Search**
|
||||
|
||||
- [ ] Create correspondence list with advanced filtering:
|
||||
- [ ] Filter by type, status, project, organization
|
||||
- [ ] Search by title, document number, content
|
||||
@@ -536,6 +494,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F1.1, F3.1
|
||||
|
||||
- **[ ] F4.2 Correspondence Creation Form**
|
||||
|
||||
- [ ] Create dynamic form generator based on JSON schema
|
||||
- [ ] Implement form with multiple sections:
|
||||
- [ ] Basic information (type, title, recipients)
|
||||
@@ -553,6 +512,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F0.4, F4.1
|
||||
|
||||
- **[ ] F4.3 Correspondence Detail View**
|
||||
|
||||
- [ ] Create comprehensive detail page:
|
||||
- [ ] Document header with metadata
|
||||
- [ ] Content display based on type
|
||||
@@ -583,27 +543,27 @@ F10_3 --> F10_4
|
||||
|
||||
### **Phase 4: Testing - Correspondence System**
|
||||
|
||||
#### **F4.T1 Correspondence Test Suite**
|
||||
- **[ ] F4.T1 Correspondence Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** Form validation, file upload components
|
||||
- [ ] **Integration Tests:** Complete document lifecycle, file attachment flow
|
||||
- [ ] **E2E Tests:** End-to-end correspondence creation and management
|
||||
- [ ] **Unit Tests:** Form validation, file upload components
|
||||
- [ ] **Integration Tests:** Complete document lifecycle, file attachment flow
|
||||
- [ ] **E2E Tests:** End-to-end correspondence creation and management
|
||||
|
||||
#### **F4.T2 File Upload Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** File validation, type checking
|
||||
- [ ] **Integration Tests:** Two-phase upload process, virus scan integration
|
||||
- [ ] **Security Tests:** Malicious file upload attempts, security feedback
|
||||
- **[ ] F4.T2 File Upload Test Suite**
|
||||
- [ ] **Unit Tests:** File validation, type checking
|
||||
- [ ] **Integration Tests:** Two-phase upload process, virus scan integration
|
||||
- [ ] **Security Tests:** Malicious file upload attempts, security feedback
|
||||
|
||||
---
|
||||
|
||||
## **Phase 5: Workflow Management (สัปดาห์ที่ 7)**
|
||||
### **Phase 5: Workflow Management (สัปดาห์ที่ 7)**
|
||||
|
||||
**Milestone:** ระบบ Visualization และจัดการ Workflow
|
||||
|
||||
### **Phase 5: Tasks**
|
||||
|
||||
- **[ ] F5.1 Workflow Visualization Component**
|
||||
|
||||
- [ ] Create horizontal workflow progress visualization
|
||||
- [ ] Implement step status indicators (pending, active, completed, skipped)
|
||||
- [ ] Add due date and assignee information
|
||||
@@ -614,6 +574,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F4.3
|
||||
|
||||
- **[ ] F5.2 Routing Template Management**
|
||||
|
||||
- [ ] Create routing template list and editor
|
||||
- [ ] Implement drag-and-drop step configuration
|
||||
- [ ] Add step configuration (purpose, duration, assignee rules)
|
||||
@@ -624,6 +585,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F3.1, F4.2
|
||||
|
||||
- **[ ] F5.3 Workflow Step Actions**
|
||||
|
||||
- [ ] Create step action interface:
|
||||
- [ ] Approve, reject, request changes
|
||||
- [ ] Add comments and attachments
|
||||
@@ -647,21 +609,21 @@ F10_3 --> F10_4
|
||||
|
||||
### **Phase 5: Testing - Workflow Management**
|
||||
|
||||
#### **F5.T1 Workflow Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** Workflow visualization, step status logic
|
||||
- [ ] **Integration Tests:** Complete workflow execution, real-time updates
|
||||
- [ ] **E2E Tests:** Multi-step workflow with different user roles
|
||||
- **[ ] F5.T1 Workflow Test Suite**
|
||||
- [ ] **Unit Tests:** Workflow visualization, step status logic
|
||||
- [ ] **Integration Tests:** Complete workflow execution, real-time updates
|
||||
- [ ] **E2E Tests:** Multi-step workflow with different user roles
|
||||
|
||||
---
|
||||
|
||||
## **Phase 6: Drawing System (สัปดาห์ที่ 8)**
|
||||
### **Phase 6: Drawing System (สัปดาห์ที่ 8)**
|
||||
|
||||
**Milestone:** ระบบจัดการแบบแปลนแบบสมบูรณ์
|
||||
|
||||
### **Phase 6: Tasks**
|
||||
|
||||
- **[ ] F6.1 Contract Drawings Management**
|
||||
|
||||
- [ ] Create contract drawing list with categorization
|
||||
- [ ] Implement drawing upload and metadata management
|
||||
- [ ] Create drawing preview and viewer
|
||||
@@ -672,6 +634,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F3.1, F4.4
|
||||
|
||||
- **[ ] F6.2 Shop Drawings Management**
|
||||
|
||||
- [ ] Create shop drawing list with revision tracking
|
||||
- [ ] Implement shop drawing creation and revision system
|
||||
- [ ] Create drawing comparison interface
|
||||
@@ -682,6 +645,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F6.1
|
||||
|
||||
- **[ ] F6.3 Drawing Revision System**
|
||||
|
||||
- [ ] Create revision history interface
|
||||
- [ ] Implement revision comparison functionality
|
||||
- [ ] Add revision notes and change tracking
|
||||
@@ -703,21 +667,21 @@ F10_3 --> F10_4
|
||||
|
||||
### **Phase 6: Testing - Drawing System**
|
||||
|
||||
#### **F6.T1 Drawing Management Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** Drawing CRUD operations, revision logic
|
||||
- [ ] **Integration Tests:** Drawing approval workflows, reference management
|
||||
- [ ] **E2E Tests:** Complete drawing lifecycle with revisions
|
||||
- **[ ] F6.T1 Drawing Management Test Suite**
|
||||
- [ ] **Unit Tests:** Drawing CRUD operations, revision logic
|
||||
- [ ] **Integration Tests:** Drawing approval workflows, reference management
|
||||
- [ ] **E2E Tests:** Complete drawing lifecycle with revisions
|
||||
|
||||
---
|
||||
|
||||
## **Phase 7: RFA System (สัปดาห์ที่ 9-10)**
|
||||
### **Phase 7: RFA System (สัปดาห์ที่ 9-10)**
|
||||
|
||||
**Milestone:** ระบบขออนุมัติแบบสมบูรณ์พร้อม Dynamic Forms
|
||||
|
||||
### **Phase 7: Tasks**
|
||||
|
||||
- **[ ] F7.1 RFA List & Dashboard**
|
||||
|
||||
- [ ] Create RFA dashboard with status overview
|
||||
- [ ] Implement advanced RFA filtering and search
|
||||
- [ ] Create RFA calendar view for deadlines
|
||||
@@ -728,6 +692,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F4.1, F5.1
|
||||
|
||||
- **[ ] F7.2 RFA Creation with Dynamic Forms**
|
||||
|
||||
- [ ] Create RFA type-specific form generator
|
||||
- [ ] Implement dynamic form fields based on RFA type:
|
||||
- [ ] RFA_DWG: Shop drawing selection
|
||||
@@ -747,6 +712,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F4.2, F6.2
|
||||
|
||||
- **[ ] F7.3 RFA Workflow Integration**
|
||||
|
||||
- [ ] Integrate RFA with unified workflow engine
|
||||
- [ ] Create RFA-specific workflow steps and actions
|
||||
- [ ] Implement RFA approval interface
|
||||
@@ -768,21 +734,21 @@ F10_3 --> F10_4
|
||||
|
||||
### **Phase 7: Testing - RFA System**
|
||||
|
||||
#### **F7.T1 RFA Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** RFA form generation, validation logic
|
||||
- [ ] **Integration Tests:** Complete RFA lifecycle, workflow integration
|
||||
- [ ] **E2E Tests:** Multi-type RFA creation and approval workflows
|
||||
- **[ ] F7.T1 RFA Test Suite**
|
||||
- [ ] **Unit Tests:** RFA form generation, validation logic
|
||||
- [ ] **Integration Tests:** Complete RFA lifecycle, workflow integration
|
||||
- [ ] **E2E Tests:** Multi-type RFA creation and approval workflows
|
||||
|
||||
---
|
||||
|
||||
## **Phase 8: Internal Workflows (สัปดาห์ที่ 11)**
|
||||
### **Phase 8: Internal Workflows (สัปดาห์ที่ 11)**
|
||||
|
||||
**Milestone:** ระบบใบเวียนและการจัดการงานภายใน
|
||||
|
||||
### **Phase 8: Tasks**
|
||||
|
||||
- **[ ] F8.1 Circulation Management**
|
||||
|
||||
- [ ] Create circulation list and management interface
|
||||
- [ ] Implement circulation creation from correspondence
|
||||
- [ ] Create circulation template management
|
||||
@@ -793,6 +759,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F4.1, F5.2
|
||||
|
||||
- **[ ] F8.2 Task Assignment Interface**
|
||||
|
||||
- [ ] Create task assignment interface with user selection
|
||||
- [ ] Implement task priority and deadline setting
|
||||
- [ ] Add task dependency management
|
||||
@@ -814,21 +781,21 @@ F10_3 --> F10_4
|
||||
|
||||
### **Phase 8: Testing - Internal Workflows**
|
||||
|
||||
#### **F8.T1 Circulation Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** Circulation creation, task assignment logic
|
||||
- [ ] **Integration Tests:** Complete circulation workflow, internal approvals
|
||||
- [ ] **E2E Tests:** End-to-end circulation with task completion
|
||||
- **[ ] F8.T1 Circulation Test Suite**
|
||||
- [ ] **Unit Tests:** Circulation creation, task assignment logic
|
||||
- [ ] **Integration Tests:** Complete circulation workflow, internal approvals
|
||||
- [ ] **E2E Tests:** End-to-end circulation with task completion
|
||||
|
||||
---
|
||||
|
||||
## **Phase 9: Advanced Features (สัปดาห์ที่ 12)**
|
||||
### **Phase 9: Advanced Features (สัปดาห์ที่ 12)**
|
||||
|
||||
**Milestone:** ฟีเจอร์ขั้นสูงและการปรับปรุงประสบการณ์ผู้ใช้
|
||||
|
||||
### **Phase 9: Tasks**
|
||||
|
||||
- **[ ] F9.1 Advanced Search Interface**
|
||||
|
||||
- [ ] Create unified search interface across all document types
|
||||
- [ ] Implement faceted search with multiple filters
|
||||
- [ ] Add search result highlighting and relevance scoring
|
||||
@@ -839,6 +806,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F4.1, F7.1
|
||||
|
||||
- **[ ] F9.2 Notification System**
|
||||
|
||||
- [ ] Create notification center with real-time updates
|
||||
- [ ] Implement notification preferences management
|
||||
- [ ] Add notification grouping and digest views
|
||||
@@ -849,6 +817,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F1.3, F5.4
|
||||
|
||||
- **[ ] F9.3 Reporting & Analytics**
|
||||
|
||||
- [ ] Create reporting dashboard with customizable widgets
|
||||
- [ ] Implement data visualization components (charts, graphs)
|
||||
- [ ] Add report scheduling and export
|
||||
@@ -870,21 +839,21 @@ F10_3 --> F10_4
|
||||
|
||||
### **Phase 9: Testing - Advanced Features**
|
||||
|
||||
#### **F9.T1 Advanced Features Test Suite**
|
||||
|
||||
- [ ] **Unit Tests:** Search algorithms, notification logic
|
||||
- [ ] **Integration Tests:** Cross-module search, real-time notifications
|
||||
- [ ] **Performance Tests:** Search performance, mobile responsiveness
|
||||
- **[ ] F9.T1 Advanced Features Test Suite**
|
||||
- [ ] **Unit Tests:** Search algorithms, notification logic
|
||||
- [ ] **Integration Tests:** Cross-module search, real-time notifications
|
||||
- [ ] **Performance Tests:** Search performance, mobile responsiveness
|
||||
|
||||
---
|
||||
|
||||
## **Phase 10: Testing & Polish (สัปดาห์ที่ 13-14)**
|
||||
### **Phase 10: Testing & Polish (สัปดาห์ที่ 13-14)**
|
||||
|
||||
**Milestone:** แอปพลิเคชันที่ผ่านการทดสอบและปรับปรุงอย่างสมบูรณ์
|
||||
|
||||
### **Phase 10: Tasks**
|
||||
|
||||
- **[ ] F10.1 Comprehensive Testing**
|
||||
|
||||
- [ ] Idempotency Testing: เพิ่มการทดสอบเฉพาะสำหรับ Axios Interceptor เพื่อจำลองการส่ง Request POST/PUT/DELETE ที่มี Idempotency-Key ซ้ำไปยัง Mock API (MSW) เพื่อยืนยันว่า Client-side ไม่ส่ง Key ซ้ำในการทำงานปกติ และไม่เกิด Side Effect จากการ Replay Attack.
|
||||
- [ ] Write unit tests for all components and utilities
|
||||
- [ ] Create integration tests for critical user flows
|
||||
@@ -896,6 +865,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** All previous phases
|
||||
|
||||
- **[ ] F10.2 Performance Optimization**
|
||||
|
||||
- [ ] Implement code splitting and lazy loading
|
||||
- [ ] Optimize bundle size and asset delivery
|
||||
- [ ] Add performance monitoring and metrics
|
||||
@@ -906,6 +876,7 @@ F10_3 --> F10_4
|
||||
- [ ] **Dependencies:** F10.1
|
||||
|
||||
- **[ ] F10.3 Security Hardening**
|
||||
|
||||
- [ ] Conduct security audit and penetration testing
|
||||
- [ ] Implement Content Security Policy (CSP)
|
||||
- [ ] Add security headers and protections
|
||||
@@ -927,19 +898,18 @@ F10_3 --> F10_4
|
||||
|
||||
### **Phase 10: Testing - Final Validation**
|
||||
|
||||
#### **F10.T1 Final Test Suite**
|
||||
|
||||
- [ ] **Performance Tests:** Load testing, stress testing
|
||||
- [ ] **Security Tests:** Final security audit, vulnerability assessment
|
||||
- [ ] **User Acceptance Tests:** Real user testing, feedback incorporation
|
||||
- [ ] **Compatibility Tests:** Cross-browser, cross-device testing
|
||||
- **[ ] F10.T1 Final Test Suite**
|
||||
- [ ] **Performance Tests:** Load testing, stress testing
|
||||
- [ ] **Security Tests:** Final security audit, vulnerability assessment
|
||||
- [ ] **User Acceptance Tests:** Real user testing, feedback incorporation
|
||||
- [ ] **Compatibility Tests:** Cross-browser, cross-device testing
|
||||
|
||||
---
|
||||
|
||||
## 📊 **สรุป Timeline**
|
||||
|
||||
| Phase | ระยะเวลา | จำนวนงาน | Output หลัก |
|
||||
| -------- | ------------ | ------------ | ------------------------------------ |
|
||||
| Phase | ระยะเวลา | จำนวนงาน | Output หลัก |
|
||||
| -------- | -------------- | ------------ | ------------------------------------ |
|
||||
| Phase 0 | 1 สัปดาห์ | 4 | Foundation & Tooling Ready |
|
||||
| Phase 1 | 1 สัปดาห์ | 4 | Core Application Structure |
|
||||
| Phase 2 | 1 สัปดาห์ | 4 | User Management & Security |
|
||||
|
||||
@@ -485,26 +485,29 @@
|
||||
|
||||
**Purpose**: Child table storing revision history of correspondences (1:N)
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ------------------------ | ------------ | --------------------------------- | -------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
|
||||
| correspondence_id | INT | NOT NULL, FK | Master correspondence ID |
|
||||
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
|
||||
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) |
|
||||
| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag |
|
||||
| correspondence_status_id | INT | NOT NULL, FK | Current status of this revision |
|
||||
| title | VARCHAR(255) | NOT NULL | Document title |
|
||||
| document_date | DATE | NULL | Document date |
|
||||
| issued_date | DATETIME | NULL | Issue date |
|
||||
| received_date | DATETIME | NULL | Received date |
|
||||
| due_date | DATETIME | NULL | Due date for response |
|
||||
| description | TEXT | NULL | Revision description |
|
||||
| details | JSON | NULL | Type-specific details (e.g., RFI questions) |
|
||||
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
|
||||
| created_by | INT | NULL, FK | User who created revision |
|
||||
| updated_by | INT | NULL, FK | User who last updated |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ------------------------ | ------------ | --------------------------------- | ------------------------------------------------------------ |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
|
||||
| correspondence_id | INT | NOT NULL, FK | Master correspondence ID |
|
||||
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
|
||||
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) |
|
||||
| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag |
|
||||
| correspondence_status_id | INT | NOT NULL, FK | Current status of this revision |
|
||||
| title | VARCHAR(255) | NOT NULL | Document title |
|
||||
| document_date | DATE | NULL | Document date |
|
||||
| issued_date | DATETIME | NULL | Issue date |
|
||||
| received_date | DATETIME | NULL | Received date |
|
||||
| due_date | DATETIME | NULL | Due date for response |
|
||||
| description | TEXT | NULL | Revision description |
|
||||
| details | JSON | NULL | Type-specific details (e.g., RFI questions) |
|
||||
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
|
||||
| created_by | INT | NULL, FK | User who created revision |
|
||||
| updated_by | INT | NULL, FK | User who last updated |
|
||||
| v_ref_project_id | INT | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Project ID จาก JSON details เพื่อทำ Index |
|
||||
| v_ref_type | VARCHAR(50) | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Type จาก JSON details |
|
||||
| v_ref_type | VARCHAR(50) | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Type จาก JSON details |
|
||||
| details | JSON | NULL | Type-specific details (e.g., RFI questions) |
|
||||
| v_doc_subtype | VARCHAR(50) | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Type จาก JSON details |
|
||||
| schema_version | INT | DEFAULT 1 | Version of the schema used with this details |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
@@ -521,8 +524,9 @@
|
||||
- INDEX (issued_date)
|
||||
- INDEX (v_ref_project_id)
|
||||
- INDEX (v_ref_type)
|
||||
- INDEX (v_doc_subtype)
|
||||
|
||||
**Relationships**:
|
||||
- **Relationships**:
|
||||
|
||||
- Parent: correspondences, correspondence_status, users
|
||||
|
||||
@@ -638,16 +642,16 @@
|
||||
|
||||
**Purpose**: เก็บข้อมูลแม่แบบ (Template) ของสายงานการส่งต่อเอกสารเพื่อขออนุมัติ ทำให้ไม่ต้องกำหนดขั้นตอนซ้ำทุกครั้ง สามารถสร้างเป็นแม่แบบทั่วไป หรือเฉพาะสำหรับโครงการใดโครงการหนึ่งได้
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| --------------- | ------------ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลัก (Primary Key) ของแม่แบบ รันค่าอัตโนมัติ |
|
||||
| template_name | VARCHAR(255) | NOT NULL | ชื่อของแม่แบบ เช่น "เสนอโครงการ", "ขออนุมัติจัดซื้อ" |
|
||||
| description | TEXT | NULL | คำอธิบายรายละเอียดเกี่ยวกับแม่แบบนี้ |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| --------------- | ------------ | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลัก (Primary Key) ของแม่แบบ รันค่าอัตโนมัติ |
|
||||
| template_name | VARCHAR(255) | NOT NULL | ชื่อของแม่แบบ เช่น "เสนอโครงการ", "ขออนุมัติจัดซื้อ" |
|
||||
| description | TEXT | NULL | คำอธิบายรายละเอียดเกี่ยวกับแม่แบบนี้ |
|
||||
| project_id | INT | NULL | ID ของโครงการที่แม่แบบนี้สังกัดอยู่ (ถ้ามี) **ค่า NULL หมายถึง** เป็น "แม่แบบทั่วไป" ที่สามารถใช้กับทุกโครงการได้ |
|
||||
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | วันที่และเวลาที่สร้างแม่แบบนี้ |
|
||||
| updated_at | TIMESTAMP | NOT NULL,`DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | วันที่และเวลาที่แก้ไขข้อมูลในแม่แบบนี้ล่าสุด |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | สถานะใช้งาน |
|
||||
| workflow_config | JSON | NULL | เก็บ State Machine Configuration หรือ Rules เพิ่มเติมที่ซับซ้อนกว่า Column ปกติ |
|
||||
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | วันที่และเวลาที่สร้างแม่แบบนี้ |
|
||||
| updated_at | TIMESTAMP | NOT NULL,`DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | วันที่และเวลาที่แก้ไขข้อมูลในแม่แบบนี้ล่าสุด |
|
||||
| is_active | BOOLEAN | DEFAULT TRUE | สถานะใช้งาน |
|
||||
| workflow_config | JSON | NULL | เก็บ State Machine Configuration หรือ Rules เพิ่มเติมที่ซับซ้อนกว่า Column ปกติ |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
@@ -664,14 +668,14 @@
|
||||
|
||||
**Purpose**: เก็บรายละเอียดของแต่ละขั้นตอน (Steps) ภายในแม่แบบสายงาน (correspondence_routing_templates) กำหนดว่าจะส่งไปที่องค์กรไหน ลำดับเป็นเท่าไร และเพื่อวัตถุประสงค์อะไร
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| :----------------- | --------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลักของขั้นตอน |
|
||||
| template_id | INT | NOT NULL | ID ของแม่แบบที่ขั้นตอนนี้สังกัดอยู่ |
|
||||
| sequence | INT | NOT NULL | ลำดับของขั้นตอน (1, 2, 3, ...) |
|
||||
| to_organization_id | INT | NOT NULL | ID ขององค์กรที่เป็นผู้รับในขั้นตอนนี้ |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| :----------------- | --------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลักของขั้นตอน |
|
||||
| template_id | INT | NOT NULL | ID ของแม่แบบที่ขั้นตอนนี้สังกัดอยู่ |
|
||||
| sequence | INT | NOT NULL | ลำดับของขั้นตอน (1, 2, 3, ...) |
|
||||
| to_organization_id | INT | NOT NULL | ID ขององค์กรที่เป็นผู้รับในขั้นตอนนี้ |
|
||||
| step_purpose | ENUM | NOT NULL,DEFAULT FOR_REVIEW | วัตถุประสงค์ของการส่งต่อในขั้นตอนนี้ **ค่าที่เป็นไปได้:** [FOR_APPROVAL: เพื่ออนุมัติ, FOR_REVIEW: เพื่อตรวจสอบ/พิจารณา, FOR_INFORMATION: เพื่อทราบ] |
|
||||
| expected_days | INT | NULL | วันที่คาดหวัง |
|
||||
| expected_days | INT | NULL | วันที่คาดหวัง |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
@@ -690,22 +694,22 @@
|
||||
|
||||
**Purpose**: เป็นตารางที่เก็บข้อมูลการส่งต่อเอกสารจริง (Instance/Run-time) ติดตามประวัติการเคลื่อนย้ายของแต่ละเอกสาร ว่าผ่านใครมาบ้าง อยู่ที่ใคร และสถานะปัจจุบันคืออะไร
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| -------------------- | --------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลักของรายการส่งต่อ |
|
||||
| correspondence_id | INT | NOT NUL | ID ของเอกสาร (FK ไปยัง correspondence_revisions) |
|
||||
| template_id | INT | NULL | ID ของแม่แบบที่ใช้สร้างสายงานนี้ (เก็บไว้เป็นข้อมูลอ้างอิง) |
|
||||
| sequence | INT | NOT NULL | ลำดับของขั้นตอนการส่งต่อจริง |
|
||||
| from_organization_id | INT | NOT NULL | ID ขององค์กรผู้ส่ง |
|
||||
| to_organization_id | INT | NOT NULL | ID ขององค์กรผู้รับ |
|
||||
| step_purpose | ENUM | NOT NULL, DEFAULT FOR_REVIEW | วัตถุประสงค์ของการส่งต่อในขั้นตอนนี้จริง **ค่าที่เป็นไปได้:** [FOR_APPROVAL: เพื่ออนุมัติ, FOR_REVIEW: เพื่อตรวจสอบ/พิจารณา, FOR_INFORMATION: เพื่อทราบ, FOR_ACTION: เพื่อดำเนินการ] |
|
||||
| status | ENUM | NOT NULL, DEFAULT SENT | [ACTIONED: ดำเนินการแล้ว, FORWARDED: ส่งต่อแล้ว, REPLIE: ตอบกลับแล้ว] |
|
||||
| comments | TEXT | NULL | หมายเหตุหรือความคิดเห็นในการส่งต่อ |
|
||||
| due_date | DATETIME | NULL | วันที่ครบกำหนดที่ต้องดำเนินการในขั้นตอนนี้ |
|
||||
| processed_by_user_id | INT | NULL | ID ของผู้ใช้ที่ดำเนินการในขั้นตอนนี้จริงๆ |
|
||||
| processed_at | TIMESTAMP | NULL | เวลาที่ผู้ใช้ดำเนินการเสร็จสิ้น |
|
||||
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | เวลาที่สร้างรายการส่งต่อนี้ |
|
||||
| state_context | JSON | NULL | เก็บข้อมูล Context ของ Workflow ณ ขณะนั้น (Snapshot) |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| -------------------- | --------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลักของรายการส่งต่อ |
|
||||
| correspondence_id | INT | NOT NUL | ID ของเอกสาร (FK ไปยัง correspondence_revisions) |
|
||||
| template_id | INT | NULL | ID ของแม่แบบที่ใช้สร้างสายงานนี้ (เก็บไว้เป็นข้อมูลอ้างอิง) |
|
||||
| sequence | INT | NOT NULL | ลำดับของขั้นตอนการส่งต่อจริง |
|
||||
| from_organization_id | INT | NOT NULL | ID ขององค์กรผู้ส่ง |
|
||||
| to_organization_id | INT | NOT NULL | ID ขององค์กรผู้รับ |
|
||||
| step_purpose | ENUM | NOT NULL, DEFAULT FOR_REVIEW | วัตถุประสงค์ของการส่งต่อในขั้นตอนนี้จริง **ค่าที่เป็นไปได้:** [FOR_APPROVAL: เพื่ออนุมัติ, FOR_REVIEW: เพื่อตรวจสอบ/พิจารณา, FOR_INFORMATION: เพื่อทราบ, FOR_ACTION: เพื่อดำเนินการ] |
|
||||
| status | ENUM | NOT NULL, DEFAULT SENT | [ACTIONED: ดำเนินการแล้ว, FORWARDED: ส่งต่อแล้ว, REPLIE: ตอบกลับแล้ว] |
|
||||
| comments | TEXT | NULL | หมายเหตุหรือความคิดเห็นในการส่งต่อ |
|
||||
| due_date | DATETIME | NULL | วันที่ครบกำหนดที่ต้องดำเนินการในขั้นตอนนี้ |
|
||||
| processed_by_user_id | INT | NULL | ID ของผู้ใช้ที่ดำเนินการในขั้นตอนนี้จริงๆ |
|
||||
| processed_at | TIMESTAMP | NULL | เวลาที่ผู้ใช้ดำเนินการเสร็จสิ้น |
|
||||
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | เวลาที่สร้างรายการส่งต่อนี้ |
|
||||
| state_context | JSON | NULL | Snapshot ของตัวแปรต่างๆ ใน Workflow ณ จุดนี้ (เช่น ผู้อนุมัติ, เงื่อนไขที่ผ่าน) |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
@@ -730,11 +734,11 @@
|
||||
|
||||
**Purpose**: ตารางนี้ใช้กำหนดกฎ (State Machine) ว่าสถานะใดสามารถเปลี่ยนไปเป็นสถานะใดได้บ้าง โดยขึ้นอยู่กับประเภทของหนังสือ เพื่อควบคุมการไหลของสถานะให้ถูกต้องตามข้อบังคับ
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| -------------- | --------- | ----------- | ----------------------------------------------- |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| -------------- | --------- | ----------- | ------------------------------------------------------ |
|
||||
| type_id | INT | PRIMARY KEY | ID ของประเภทหนังสือ (เช่น หนังสือภายใน, หนังสือภายนอก) |
|
||||
| from_status_id | INT | PRIMARY KEY | ID ของสถานะต้นทาง (เช่น ร่าง) |
|
||||
| to_status_id | INT | PRIMARY KEY | ID ของสถานะปลายทาง (เช่น รออนุมัติ) |
|
||||
| from_status_id | INT | PRIMARY KEY | ID ของสถานะต้นทาง (เช่น ร่าง) |
|
||||
| to_status_id | INT | PRIMARY KEY | ID ของสถานะปลายทาง (เช่น รออนุมัติ) |
|
||||
|
||||
**คีย์หลัก (Primary Key):**
|
||||
|
||||
@@ -890,25 +894,28 @@
|
||||
|
||||
**Purpose**: Child table storing revision history of RFAs (1:N)
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ------------------- | ------------ | --------------------------- | ---------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
|
||||
| correspondence_id | INT | NOT NULL, FK | Link to correspondence (RFA as correspondence) |
|
||||
| rfa_id | INT | NOT NULL, FK | Master RFA ID |
|
||||
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
|
||||
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) |
|
||||
| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag |
|
||||
| rfa_status_code_id | INT | NOT NULL, FK | Current RFA status |
|
||||
| rfa_approve_code_id | INT | NULL, FK | Approval result code |
|
||||
| title | VARCHAR(255) | NOT NULL | RFA title |
|
||||
| document_date | DATE | NULL | Document date |
|
||||
| issued_date | DATE | NULL | Issue date for approval |
|
||||
| received_date | DATETIME | NULL | Received date |
|
||||
| approved_date | DATE | NULL | Approval date |
|
||||
| description | TEXT | NULL | Revision description |
|
||||
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
|
||||
| created_by | INT | NULL, FK | User who created revision |
|
||||
| updated_by | INT | NULL, FK | User who last updated |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ------------------- | ------------ | --------------------------------- | --------------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
|
||||
| correspondence_id | INT | NOT NULL, FK | Link to correspondence (RFA as correspondence) |
|
||||
| rfa_id | INT | NOT NULL, FK | Master RFA ID |
|
||||
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
|
||||
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) |
|
||||
| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag |
|
||||
| rfa_status_code_id | INT | NOT NULL, FK | Current RFA status |
|
||||
| rfa_approve_code_id | INT | NULL, FK | Approval result code |
|
||||
| title | VARCHAR(255) | NOT NULL | RFA title |
|
||||
| document_date | DATE | NULL | Document date |
|
||||
| issued_date | DATE | NULL | Issue date for approval |
|
||||
| received_date | DATETIME | NULL | Received date |
|
||||
| approved_date | DATE | NULL | Approval date |
|
||||
| description | TEXT | NULL | Revision description |
|
||||
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
|
||||
| created_by | INT | NULL, FK | User who created revision |
|
||||
| updated_by | INT | NULL, FK | User who last updated |
|
||||
| details | JSON | NULL | Type-specific details (e.g., RFI questions) |
|
||||
| v_ref_drawing_count | INT | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Drawing Count จาก JSON details เพื่อทำ Index |
|
||||
| schema_version | INT | DEFAULT 1 | Version of the schema used with this details |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
@@ -924,6 +931,7 @@
|
||||
- INDEX (rfa_status_code_id)
|
||||
- INDEX (rfa_approve_code_id)
|
||||
- INDEX (is_current)
|
||||
- INDEX (v_ref_drawing_count): ตัวอย่างการ Index ข้อมูลตัวเลขใน JSON
|
||||
|
||||
**Relationships**:
|
||||
|
||||
@@ -970,14 +978,14 @@
|
||||
|
||||
**Purpose**: Master table for RFA approval workflow templates
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| --------------- | ------------ | ----------------------------------- | -------------------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique template ID |
|
||||
| template_name | VARCHAR(100) | NOT NULL | Template name |
|
||||
| description | TEXT | NULL | Template description |
|
||||
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
|
||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
|
||||
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| --------------- | ------------ | ----------------------------------- | ------------------------------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique template ID |
|
||||
| template_name | VARCHAR(100) | NOT NULL | Template name |
|
||||
| description | TEXT | NULL | Template description |
|
||||
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
|
||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
|
||||
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
|
||||
| workflow_config | JSON | NULL | เก็บ State Machine Configuration หรือ Rules เพิ่มเติมที่ซับซ้อนกว่า Column ปกติ |
|
||||
|
||||
**Indexes**:
|
||||
@@ -1037,20 +1045,20 @@
|
||||
|
||||
**Purpose**: Transaction log table tracking actual RFA approval workflow execution
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| --------------- | --------- | ----------------------------------- | ------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique workflow log ID |
|
||||
| rfa_revision_id | INT | NOT NULL, FK | Reference to RFA revision |
|
||||
| step_number | INT | NOT NULL | Current step number |
|
||||
| organization_id | INT | NOT NULL, FK | Organization responsible |
|
||||
| assigned_to | INT | NULL, FK | Assigned user ID |
|
||||
| action_type | ENUM | NULL | Action type: REVIEW, APPROVE, ACKNOWLEDGE |
|
||||
| status | ENUM | NULL | Status: PENDING, IN_PROGRESS, COMPLETED, REJECTED |
|
||||
| comments | TEXT | NULL | Comments/remarks |
|
||||
| completed_at | DATETIME | NULL | Completion timestamp |
|
||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
|
||||
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
|
||||
| state_context | JSON* | NULL | เก็บข้อมูล Context ของ Workflow ณ ขณะนั้น (Snapshot) |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| --------------- | --------- | ----------------------------------- | ---------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique workflow log ID |
|
||||
| rfa_revision_id | INT | NOT NULL, FK | Reference to RFA revision |
|
||||
| step_number | INT | NOT NULL | Current step number |
|
||||
| organization_id | INT | NOT NULL, FK | Organization responsible |
|
||||
| assigned_to | INT | NULL, FK | Assigned user ID |
|
||||
| action_type | ENUM | NULL | Action type: REVIEW, APPROVE, ACKNOWLEDGE |
|
||||
| status | ENUM | NULL | Status: PENDING, IN_PROGRESS, COMPLETED, REJECTED |
|
||||
| comments | TEXT | NULL | Comments/remarks |
|
||||
| completed_at | DATETIME | NULL | Completion timestamp |
|
||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
|
||||
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
|
||||
| state_context | JSON\* | NULL | เก็บข้อมูล Context ของ Workflow ณ ขณะนั้น (Snapshot) |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
@@ -1124,6 +1132,7 @@
|
||||
|
||||
- Parent: contracts, correspondence_types
|
||||
- Referenced by: (Used in logic for Document Numbering)
|
||||
|
||||
---
|
||||
|
||||
## **5. 📐 Drawings Tables (แบบ, หมวดหมู่)**
|
||||
@@ -1737,20 +1746,20 @@
|
||||
|
||||
**Purpose**: Central repository for all file attachments in the system
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ------------------- | ------------ | --------------------------- | -------------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique attachment ID |
|
||||
| original_filename | VARCHAR(255) | NOT NULL | Original filename from upload |
|
||||
| stored_filename | VARCHAR(255) | NOT NULL | System-generated unique filename |
|
||||
| file_path | VARCHAR(500) | NOT NULL | Full file path on server (/share/dms-data/) |
|
||||
| mime_type | VARCHAR(100) | NOT NULL | MIME type (application/pdf, image/jpeg, etc.) |
|
||||
| file_size | INT | NOT NULL | File size in bytes |
|
||||
| uploaded_by_user_id | INT | NOT NULL, FK | User who uploaded file |
|
||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Upload timestamp |
|
||||
| is_temporary | BOOLEAN | DEFAULT TRUE | ระบุว่าเป็นไฟล์ชั่วคราว (ยังไม่ได้ Commit) |
|
||||
| temp_id* | VARCHAR(100) | NULL | ID ชั่วคราวสำหรับอ้างอิงตอน Upload Phase 1 (อาจใช้ร่วมกับ id หรือแยกก็ได้) |
|
||||
| expires_at | DATETIME | NULL | เวลาหมดอายุของไฟล์ Temp (เพื่อให้ Cron Job ลบออก) |
|
||||
| checksum | VARCHAR(64) | NULL | SHA-256 Checksum สำหรับ Verify File Integrity [Req 3.9.3] |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ------------------- | ------------ | --------------------------- | -------------------------------------------------------------------------- |
|
||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique attachment ID |
|
||||
| original_filename | VARCHAR(255) | NOT NULL | Original filename from upload |
|
||||
| stored_filename | VARCHAR(255) | NOT NULL | System-generated unique filename |
|
||||
| file_path | VARCHAR(500) | NOT NULL | Full file path on server (/share/dms-data/) |
|
||||
| mime_type | VARCHAR(100) | NOT NULL | MIME type (application/pdf, image/jpeg, etc.) |
|
||||
| file_size | INT | NOT NULL | File size in bytes |
|
||||
| uploaded_by_user_id | INT | NOT NULL, FK | User who uploaded file |
|
||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Upload timestamp |
|
||||
| is_temporary | BOOLEAN | DEFAULT TRUE | ระบุว่าเป็นไฟล์ชั่วคราว (ยังไม่ได้ Commit) |
|
||||
| temp_id\* | VARCHAR(100) | NULL | ID ชั่วคราวสำหรับอ้างอิงตอน Upload Phase 1 (อาจใช้ร่วมกับ id หรือแยกก็ได้) |
|
||||
| expires_at | DATETIME | NULL | เวลาหมดอายุของไฟล์ Temp (เพื่อให้ Cron Job ลบออก) |
|
||||
| checksum | VARCHAR(64) | NULL | SHA-256 Checksum สำหรับ Verify File Integrity [Req 3.9.3] |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
@@ -1929,15 +1938,15 @@
|
||||
|
||||
**Purpose**: Transaction table maintaining running sequence numbers for document numbering
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| -------------------------- | --------- | --------------- | ----------------------------------------------- |
|
||||
| project_id | INT | PRIMARY KEY, FK | Reference to projects |
|
||||
| originator_organization_id | INT | PRIMARY KEY, FK | Originating organization |
|
||||
| correspondence_type_id | INT | PRIMARY KEY, FK | Reference to correspondence types |
|
||||
| **discipline_id** | **INT** | **PRIMARY KEY** | **Discipline ID (0 if not applicable)** |
|
||||
| current_year | INT | PRIMARY KEY | Year (Buddhist calendar) |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| -------------------------- | --------- | --------------- | ---------------------------------------------------- |
|
||||
| project_id | INT | PRIMARY KEY, FK | Reference to projects |
|
||||
| originator_organization_id | INT | PRIMARY KEY, FK | Originating organization |
|
||||
| correspondence_type_id | INT | PRIMARY KEY, FK | Reference to correspondence types |
|
||||
| **discipline_id** | **INT** | **PRIMARY KEY** | **Discipline ID (0 if not applicable)** |
|
||||
| current_year | INT | PRIMARY KEY | Year (Buddhist calendar) |
|
||||
| version | INT | DEFAULT 0 | ใช้สำหรับ Optimistic Locking (ตรวจสอบค่าก่อน Update) |
|
||||
| last_number | INT | DEFAULT 0 | Last assigned sequence number |
|
||||
| last_number | INT | DEFAULT 0 | Last assigned sequence number |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
@@ -1970,20 +1979,20 @@
|
||||
|
||||
**Purpose**: Comprehensive audit trail for all significant system actions
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ---------------- | ----------------------------------------- | --------------------------------- | -------------------------------------------------------- |
|
||||
| audit_id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | Unique audit log ID |
|
||||
| user_id | INT | NULL, FK | User who performed action |
|
||||
| action | VARCHAR(100) | NOT NULL | Action code (e.g., 'rfa.create', 'login.success') |
|
||||
| entity_type | VARCHAR(50) | NULL | Entity/module affected (e.g., 'rfa', 'correspondence') |
|
||||
| entity_id | VARCHAR(50) | NULL | Primary ID of affected record |
|
||||
| details_json | JSON | NULL | Additional context/details in JSON format |
|
||||
| ip_address | VARCHAR(45) | NULL | Client IP address (supports IPv6) |
|
||||
| user_agent | VARCHAR(255) | NULL | Browser user agent string |
|
||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Action timestamp |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| ---------------- | ----------------------------------------- | --------------------------------- | ------------------------------------------------------------ |
|
||||
| audit_id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | Unique audit log ID |
|
||||
| user_id | INT | NULL, FK | User who performed action |
|
||||
| action | VARCHAR(100) | NOT NULL | Action code (e.g., 'rfa.create', 'login.success') |
|
||||
| entity_type | VARCHAR(50) | NULL | Entity/module affected (e.g., 'rfa', 'correspondence') |
|
||||
| entity_id | VARCHAR(50) | NULL | Primary ID of affected record |
|
||||
| details_json | JSON | NULL | Additional context/details in JSON format |
|
||||
| ip_address | VARCHAR(45) | NULL | Client IP address (supports IPv6) |
|
||||
| user_agent | VARCHAR(255) | NULL | Browser user agent string |
|
||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Action timestamp |
|
||||
| v_ref_project_id | INT | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Project ID จาก JSON details เพื่อทำ Index |
|
||||
| v_ref_type | VARCHAR(50) | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Type จาก JSON details |
|
||||
| request_id | VARCHAR(100) | NULL | Request ID/Trace ID เพื่อเชื่อมโยงกับ App Logs |
|
||||
| v_ref_type | VARCHAR(50) | GENERATED ALWAYS AS (...) VIRTUAL | Virtual Column ดึง Type จาก JSON details |
|
||||
| request_id | VARCHAR(100) | NULL | Request ID/Trace ID เพื่อเชื่อมโยงกับ App Logs |
|
||||
| severity | ENUM('INFO', 'WARN', 'ERROR', 'CRITICAL') | NULL | ระดับความรุนแรงของเหตุการณ์ |
|
||||
|
||||
**Indexes**:
|
||||
@@ -2153,27 +2162,36 @@
|
||||
|
||||
**Purpose**: องรับ **Centralized JSON Schema Registry** เพื่อ Validate ข้อมูล JSON Details ของเอกสารแต่ละประเภท ตาม Requirements 6.11.1 และ Backend Plan T2.5.1
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| :-------------------- | :------------- | :--------------- | :------------------------------------------------- |
|
||||
| **id** | `INT` | PK, AI | Unique Identifier |
|
||||
| **schema_code** | `VARCHAR(100)` | UNIQUE, NOT NULL | รหัส Schema (เช่น `RFA_DWG_V1`, `CORR_RFI_V1`) |
|
||||
| **version** | `INT` | NOT NULL | เวอร์ชันของ Schema |
|
||||
| **schema_definition** | `JSON` | NOT NULL | โครงสร้าง JSON Schema (Standard JSON Schema format) |
|
||||
| **is_active** | `BOOLEAN` | DEFAULT TRUE | สถานะการใช้งาน |
|
||||
| **created_at** | `TIMESTAMP` | | วันที่สร้าง |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| :-------------------- | :------------- | :--------------- | :----------------------------------------------------------- |
|
||||
| **id** | `INT` | PK, AI | Unique Identifier |
|
||||
| **schema_code** | `VARCHAR(100)` | UNIQUE, NOT NULL | รหัส Schema (เช่น `RFA_DWG_V1`, `CORR_RFI_V1`) |
|
||||
| **version** | `INT` | NOT NULL | เวอร์ชันของ Schema |
|
||||
| **table_name** | `VARCHAR(100)` | NOT NULL | ชื่อตารางที่ Schema นี้ผูกอยู่ (เช่น rfa_revisions) |
|
||||
| **schema_definition** | `JSON` | NOT NULL | โครงสร้าง JSON Schema (Standard JSON Schema format) |
|
||||
| **ui_schema** | `JSON` | NULL | โครงสร้าง UI Schema สำหรับ Frontend Form Generator |
|
||||
| **virtual_columns** | `JSON` | NULL | Config สำหรับสร้าง Generated Columns ใน DB เพื่อ Performance |
|
||||
| **migration_script** | `JSON` | NULL | Rules สำหรับแปลงข้อมูลจากเวอร์ชันก่อนหน้า (Migration) |
|
||||
| **is_active** | `BOOLEAN` | DEFAULT TRUE | สถานะการใช้งาน |
|
||||
| **created_at** | `TIMESTAMP` | | วันที่สร้าง |
|
||||
| **updated_at** | `TIMESTAMP` | | วันที่อัปเดต |
|
||||
|
||||
**Indexes**:
|
||||
|
||||
- UNIQUE KEY (schema_code, version): ป้องกัน Version ซ้ำใน Schema เดียวกัน
|
||||
|
||||
### 10.6 user_preferences
|
||||
|
||||
**Purpose**: แยกข้อมูลการตั้งค่าส่วนตัว (เช่น Notification Settings) ออกจากตาราง Users เพื่อความยืดหยุ่น ตาม Requirements 5.5 และ 6.8.3
|
||||
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| :--------------- | :------------ | :-------------- | :------------------------------------- |
|
||||
| **user_id** | `INT` | PK, FK | อ้างอิงตาราง users |
|
||||
| **notify_email** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนทางอีเมล |
|
||||
| Column Name | Data Type | Constraints | Description |
|
||||
| :--------------- | :------------ | :-------------- | :---------------------------------------- |
|
||||
| **user_id** | `INT` | PK, FK | อ้างอิงตาราง users |
|
||||
| **notify_email** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนทางอีเมล |
|
||||
| **notify_line** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนทาง LINE |
|
||||
| **digest_mode** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนแบบรวม (Digest) แทน Real-time |
|
||||
| **ui_theme** | `VARCHAR(20)` | DEFAULT 'light' | ธีมหน้าจอ (Light/Dark) |
|
||||
| **updated_at** | `TIMESTAMP` | | วันที่แก้ไขล่าสุด |
|
||||
| **ui_theme** | `VARCHAR(20)` | DEFAULT 'light' | ธีมหน้าจอ (Light/Dark) |
|
||||
| **updated_at** | `TIMESTAMP` | | วันที่แก้ไขล่าสุด |
|
||||
|
||||
## **11. 📊 Views & Procedures (วิว และ โปรซีเดอร์)**
|
||||
|
||||
|
||||
@@ -1,573 +0,0 @@
|
||||
# โครงสร้างโฟลเดอร์และไฟล์ทั้งหมดสำหรับ **Backend (NestJS)** ตามแผนงาน **LCBP3-DMS v1.4.3** ตั้งแต่ Phase 0 ถึง Phase 6 (T0-T6.2) ที่ได้ดำเนินการไปแล้ว
|
||||
|
||||
โครงสร้างนี้ออกแบบตามหลัก **Domain-Driven Design** และ **Modular Architecture** ที่ระบุไว้ในแผนพัฒนา
|
||||
|
||||
---
|
||||
|
||||
## 📂 **backend/** (Backend Application)
|
||||
|
||||
* [x] .env (สำหรับ Local Dev เท่านั้น ห้าม commit)
|
||||
* [x] .gitignore
|
||||
* [x] .prettierrc
|
||||
* [x] docker-compose.override.yml
|
||||
* [x] docker-compose.yml
|
||||
* [x] nest-cli.json
|
||||
* [x] tsconfig.build.json
|
||||
* [x] tsconfig.json
|
||||
* [x] package.json
|
||||
* [x] pnpm-lock.yaml
|
||||
* [x] README.md
|
||||
|
||||
---
|
||||
|
||||
## 📂 **backend/src/** (Source Code)
|
||||
|
||||
### **📄 Entry Points
|
||||
|
||||
* [x] main.ts
|
||||
* [x] app.module.ts
|
||||
* [x] app.service.ts
|
||||
* [x] app.controller.ts
|
||||
* [x] app.controller.spec.ts
|
||||
* [x] redlock.d.ts
|
||||
|
||||
### **📁 src/common/** (Shared Resources)
|
||||
|
||||
* [x] common.module.ts
|
||||
* **auth/**
|
||||
* **dto/**
|
||||
* [x] login.dto.ts
|
||||
* [x] register.dto.ts
|
||||
* **strategies/**
|
||||
* [x] local.strategy.ts
|
||||
* [x] jwt.strategy.ts
|
||||
* [x] auth.controller.spec.ts
|
||||
* [x] auth.controller.ts
|
||||
* [x] auth.module.ts
|
||||
* [x] auth.service.spec.ts
|
||||
* [x] auth.service.ts
|
||||
* **config/**
|
||||
* [x] env.validation.ts
|
||||
* [x] redis.config.ts
|
||||
* **decorators/**
|
||||
* [x] audit.decorator.ts
|
||||
* [x] bypass-maintenance.decorator.ts
|
||||
* [x] current-user.decorator.ts
|
||||
* [x] idempotency.decorator.ts
|
||||
* [x] require-permission.decorator.ts
|
||||
* [x] retry.decorator.ts
|
||||
* [x] circuit-breaker.decorator.ts
|
||||
* **entities/**
|
||||
* [x] audit-log.entity.ts
|
||||
* [x] base.entity.ts
|
||||
* **exceptions/**
|
||||
* [x] http-exception.filter.ts
|
||||
* **file-storage/**
|
||||
* **entities/**
|
||||
* [x] attachment.entity.ts
|
||||
* [x] file-storage.controller.spec.ts
|
||||
* [x] file-storage.controller.ts
|
||||
* [x] file-storage.module.ts
|
||||
* [x] file-storage.service.spec.ts
|
||||
* [x] file-storage.service.ts
|
||||
* [x] file-cleanup.service.ts
|
||||
* [x] guards/`
|
||||
* [x] jwt-auth.guard.ts
|
||||
* [x] jwt-refresh.guard.ts
|
||||
* [x] maintenance-mode.guard.ts
|
||||
* [x] rbac.guard.ts
|
||||
* **interceptors/**
|
||||
* [x] audit-log.interceptor.ts
|
||||
* [x] idempotency.interceptor.ts
|
||||
* [x] performance.interceptor.ts
|
||||
* [x] transform.interceptor.ts
|
||||
* **resilience/**
|
||||
* [x] resilience.module.ts
|
||||
* **services/**
|
||||
* [x] crypto.service.ts
|
||||
* [x] request-context.service.ts
|
||||
|
||||
### **📁 src/modules/** (Feature Modules)
|
||||
|
||||
1. **user/** (User Management & RBAC)
|
||||
* [x] **dto/**
|
||||
* [x] assign-user-role.dto.ts
|
||||
* [x] create-user.dto.ts
|
||||
* [x] update-user.dto.ts
|
||||
* [x] update-user-preference.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] user.entity.ts
|
||||
* [x] role.entity.ts
|
||||
* [x] permission.entity.ts
|
||||
* [x] user-assignment.entity.ts
|
||||
* [x] user-preference.entity.ts
|
||||
* [x] user-assignment.service.ts
|
||||
* [x] user-preference.service.ts
|
||||
* [x] user.controller.ts
|
||||
* [x] user.module.ts
|
||||
* [x] user.service.ts
|
||||
* [x] user.service.spec.ts
|
||||
|
||||
2. **project/** (Project Structure)
|
||||
* [x] **dto/**
|
||||
* [x] create-project.dto.ts
|
||||
* [x] search-project.dto.ts
|
||||
* [x] update-project.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] contract-organization.entity.ts
|
||||
* [x] contract.entity.ts
|
||||
* [x] organization.entity.ts
|
||||
* [x] project-organization.entity.ts
|
||||
* [x] project.entity.ts
|
||||
* [x] project.controller.spec.ts
|
||||
* [x] project.controller.ts
|
||||
* [x] project.module.ts
|
||||
* [x] project.service.spec.ts
|
||||
* [x] project.service.ts
|
||||
|
||||
3. **correspondence/** (Core Document System)
|
||||
* [x] **dto/**
|
||||
* [x] add-reference.dto.ts
|
||||
* [x] create-correspondence.dto.ts
|
||||
* [x] search-correspondence.dto.ts
|
||||
* [x] submit-correspondence.dto.ts
|
||||
* [x] workflow-action.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] correspondence-reference.entity.ts
|
||||
* [x] correspondence-revision.entity.ts
|
||||
* [x] correspondence-routing.entity.ts
|
||||
* [x] correspondence-status.entity.ts
|
||||
* [x] correspondence-type.entity.ts
|
||||
* [x] correspondence.entity.ts
|
||||
* [x] routing-template-step.entity.ts
|
||||
* [x] routing-template.entity.ts
|
||||
* [x] correspondence.controller.spec.ts
|
||||
* [x] correspondence.controller.ts
|
||||
* [x] correspondence.module.ts
|
||||
* [x] correspondence.service.spec.ts
|
||||
* [x] correspondence.service.ts
|
||||
|
||||
4. **drawing/** (Contract & Shop Drawings)
|
||||
* [x] **dto/**
|
||||
* [x] create-contract-drawing.dto.ts
|
||||
* [x] create-shop-drawing-revision.dto.ts
|
||||
* [x] create-shop-drawing.dto.ts
|
||||
* [x] search-contract-drawing.dto.ts
|
||||
* [x] search-shop-drawing.dto.ts
|
||||
* [x] update-contract-drawing.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] contract-drawing-sub-category.entity.ts
|
||||
* [x] contract-drawing-volume.entity.ts
|
||||
* [x] contract-drawing.entity.ts
|
||||
* [x] shop-drawing-main-category.entity.ts
|
||||
* [x] shop-drawing-revision.entity.ts
|
||||
* [x] shop-drawing-sub-category.entity.ts
|
||||
* [x] shop-drawing.entity.ts
|
||||
* [x] contract-drawing.controller.ts
|
||||
* [x] contract-drawing.service.ts
|
||||
* [x] drawing-master-data.controller.ts
|
||||
* [x] drawing-master-data.service.ts
|
||||
* [x] drawing.module.ts
|
||||
* [x] shop-drawing.controller.ts
|
||||
* [x] shop-drawing.service.ts
|
||||
|
||||
5. **rfa/** (Request for Approval & Advanced Workflow)
|
||||
* [x] **dto/**
|
||||
* [x] create-rfa.dto.ts
|
||||
* [x] search-rfa.dto.ts
|
||||
* [x] update-rfa.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] rfa-approve-code.entity.ts
|
||||
* [x] rfa-item.entity.ts
|
||||
* [x] rfa-revision.entity.ts
|
||||
* [x] rfa-status-code.entity.ts
|
||||
* [x] rfa-type.entity.ts
|
||||
* [x] rfa-workflow-template-step.entity.ts
|
||||
* [x] rfa-workflow-template.entity.ts
|
||||
* [x] rfa-workflow.entity.ts
|
||||
* [x] rfa.entity.ts
|
||||
* [x] rfa.controller.ts
|
||||
* [x] rfa.module.ts
|
||||
* [x] rfa.service.ts
|
||||
|
||||
6. **circulation/** (Internal Routing)
|
||||
* [x] **dto/**
|
||||
* [x] create-circulation.dto.ts
|
||||
* [x] update-circulation-routing.dto.ts
|
||||
* [x] search-circulation.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] circulation-routing.entity.ts
|
||||
* [x] circulation-status-code.entity.ts
|
||||
* [x] circulation.entity.ts
|
||||
* [x] circulation.controller.ts
|
||||
* [x] circulation.module.ts
|
||||
* [x] circulation.service.ts
|
||||
|
||||
7. **transmittal/** (Document Forwarding)
|
||||
* [x] **dto/**
|
||||
* [x] create-transmittal.dto.ts
|
||||
* [x] search-transmittal.dto.ts
|
||||
* [x] update-transmittal.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] transmittal-item.entity.ts
|
||||
* [x] transmittal.entity.ts
|
||||
* [x] transmittal.controller.ts
|
||||
* [x] transmittal.module.ts
|
||||
* [x] transmittal.service.ts
|
||||
|
||||
8. **notification/** (System Alerts)
|
||||
* [x] **dto/**
|
||||
* [x] create-notification.dto.ts
|
||||
* [x] search-notification.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] notification.entity.ts
|
||||
* [x] notification-cleanup.service.ts
|
||||
* [x] notification.controller.ts
|
||||
* [x] notification.gateway.ts
|
||||
* [x] notification.module.ts
|
||||
* [x] notification.processor.ts
|
||||
* [x] notification.service.ts
|
||||
|
||||
9. **search/** (Elasticsearch)
|
||||
* [x] dto/search-query.dto.ts
|
||||
* [x] search.controller.ts
|
||||
* [x] search.module.ts
|
||||
* [x] search.service.ts
|
||||
|
||||
10. **document-numbering/**
|
||||
* [x] **entities/**
|
||||
* [x] document-number-format.entity.ts
|
||||
* [x] document-number-counter.entity.ts
|
||||
* [x] document-numbering.module.ts
|
||||
* [x] document-numbering.service.spec.ts
|
||||
* [x] document-numbering.service.ts
|
||||
|
||||
11. **workflow-engine/** (Unified Logic)
|
||||
* [x] **dto/**
|
||||
* [x] create-workflow-definition.dto.ts
|
||||
* [x] evaluate-workflow.dto.ts
|
||||
* [x] get-available-actions.dto.ts
|
||||
* [x] update-workflow-definition.dto.ts
|
||||
* [x] interfaces/workflow.interface.ts
|
||||
* [x] workflow-dsl.service.ts
|
||||
* [x] workflow-engine.module.ts
|
||||
* [x] workflow-engine.service.spec.ts
|
||||
* [x] workflow-engine.service.ts
|
||||
|
||||
12. **json-schema/** (Validation)
|
||||
* [x] **dto/**
|
||||
* [x] create-json-schema.dto.ts+
|
||||
* [x] search-json-schema.dto.ts
|
||||
* [x] update-json-schema.dto.ts
|
||||
* [x] **entities/**
|
||||
* [x] json-schema.entity.ts
|
||||
* [x] json-schema.controller.spec.ts
|
||||
* [x] json-schema.controller.ts
|
||||
* [x] json-schema.module.ts
|
||||
* [x] json-schema.service.spec.ts
|
||||
* [x] json-schema.service.ts
|
||||
|
||||
13. **monitoring/** (Monitoring & Metrics)
|
||||
* [x] **controllers/**
|
||||
* [x] health.controller.ts
|
||||
* [x] **dto/**
|
||||
* [ ] set-maintenance.dto.ts
|
||||
* [x] **logger/**
|
||||
* [x] winston.config.ts
|
||||
* [x] **services/**
|
||||
* [x] metrics.service.ts
|
||||
* [x] monitoring.controller.ts
|
||||
* [x] monitoring.service.ts
|
||||
* [x] monitoring.module.ts
|
||||
|
||||
## **Folder Structure ของ Backend (NestJS)** ที่
|
||||
|
||||
---
|
||||
|
||||
### 📁 Backend Folder Structure (LCBP3-DMS v1.4.4)
|
||||
|
||||
```
|
||||
backend/
|
||||
├── .env # Environment variables for local development only (not committed)
|
||||
├── .gitignore # Git ignore rules
|
||||
├── .prettierrc # Prettier configuration
|
||||
├── docker-compose.yml # Main deployment container configuration
|
||||
├── docker-compose.override.yml # Dev-time secret/environment injection
|
||||
├── package.json # Node dependencies and NPM scripts
|
||||
├── pnpm-lock.yaml # Dependency lock file for pnpm
|
||||
├── tsconfig.json # TypeScript compiler configuration
|
||||
├── tsconfig.build.json # TypeScript compiler configuration for production
|
||||
├── nest-cli.json # NestJS project configuration
|
||||
├── README.md # Project documentation
|
||||
└── src/
|
||||
├── main.ts # Application bootstrap and initialization
|
||||
├── app.module.ts # Root application module
|
||||
├── app.service.ts # Root application service
|
||||
├── app.controller.ts # Root application controller
|
||||
├── app.controller.spec.ts # Root application unit tests
|
||||
├── redlock.d.ts # Redlock configuration
|
||||
│
|
||||
├── common/ # 🛠️ Shared framework resources used across modules
|
||||
│ ├── common.module.ts # Registers shared providers
|
||||
│ │
|
||||
│ ├── auth/ # 🛡️ Authentication module
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── login.dto.ts # Login request payload
|
||||
│ │ │ └── register.dto.ts # Registration payload
|
||||
│ │ ├── strategies/
|
||||
│ │ │ ├── local.strategy.ts # Local strategy for authentication
|
||||
│ │ │ └── jwt.strategy.ts # JWT strategy for authentication
|
||||
│ │ ├── auth.module.ts # Auth DI module
|
||||
│ │ ├── auth.controller.ts # Auth REST endpoints
|
||||
│ │ ├── auth.controller.spec.ts # Unit tests for controller
|
||||
│ │ ├── auth.service.ts # Authentication logic
|
||||
│ │ └── auth.service.spec.ts # Unit test for service
|
||||
│ │
|
||||
│ ├── config/ # 📄 Configuration
|
||||
│ │ ├── env.validation.ts # Zod/Joi validation for environment variables
|
||||
│ │ └── redis.config.ts # Redis configuration
|
||||
│ │
|
||||
│ ├── decorators/ # 📡 Decorators for common use cases
|
||||
│ │ ├── audit.decorator.ts # Enables audit logging for a method
|
||||
│ │ ├── bypass-maintenance.decorator.ts # Declares bypass maintenance requirement
|
||||
│ │ ├── current-user.decorator.ts # Extracts logged-in user from request
|
||||
│ │ ├── idempotency.decorator.ts # Declares idempotency requirement
|
||||
│ │ ├── require-permission.decorator.ts # Declares RBAC permission requirement
|
||||
│ │ ├── retry.decorator.ts # Declares retry requirement
|
||||
│ │ └── circuit-breaker.decorator.ts # Declares circuit breaker requirement
|
||||
│ │
|
||||
│ ├── entities/ # 📚 Database entities
|
||||
│ │ ├── audit-log.entity.ts # Audit log database entity
|
||||
│ │ └── base.entity.ts # Base abstraction containing core columns
|
||||
│ │
|
||||
│ ├── exceptions/ # 🛡️ Global exception trap/formatter
|
||||
│ │ └── http-exception.filter.ts # Global exception trap/formatter
|
||||
│ │
|
||||
│ ├── file-storage/ # 📂 Two-Phase document storage (upload → scan → commit)
|
||||
│ │ ├── entities/
|
||||
│ │ │ └── attachment.entity.ts # Represents stored file metadata
|
||||
│ │ ├── file-storage.controller.ts # Upload/download endpoints
|
||||
│ │ ├── file-storage.controller.spec.ts # Unit tests
|
||||
│ │ ├── file-storage.module.ts # Module DI bindings
|
||||
│ │ ├── file-storage.service.ts # File handling logic
|
||||
│ │ ├── file-storage.service.spec.ts # Unit tests
|
||||
│ │ └── file-cleanup.service.ts # Cleanup temporary files
|
||||
│ │
|
||||
│ ├── guards/ # 🛡️ JWT authentication guard
|
||||
│ │ ├── jwt-auth.guard.ts # JWT authentication guard
|
||||
│ │ ├── jwt-refresh.guard.ts # JWT refresh guard
|
||||
│ │ ├── maintenance-mode.guard.ts # Maintenance mode guard
|
||||
│ │ └── rbac.guard.ts # Role-based access control enforcement (ตรวจสอบสิทธิ์ 4 ระดับ)
|
||||
│ │
|
||||
│ ├── interceptors/ # 📡 Interceptors for common use cases
|
||||
│ │ ├── audit-log.interceptor.ts # Automatically logs certain operations
|
||||
│ │ ├── idempotency.interceptor.ts # Idempotency interceptor
|
||||
│ │ ├── performance.interceptor.ts #
|
||||
│ │ └── transform.interceptor.ts # Standardized response formatting
|
||||
│ │
|
||||
│ ├─── resilience/ # 🛡️ Circuit-breaker / retry logic (if implemented)
|
||||
│ │ └── resilience.module.ts # Resilience module
|
||||
│ │
|
||||
│ └──── services/ # 🔐 Security service
|
||||
│ ├── crypto.service.ts # Crypto service
|
||||
│ └── request-context.service.ts # Request context service (for logging)
|
||||
│
|
||||
├── modules/ # 📦 Module-specific resources
|
||||
│ ├── user/ # 👤 User + RBAC module
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── assign-user-role.dto.ts # Assign roles to users
|
||||
│ │ │ ├── create-user.dto.ts # Create new user
|
||||
│ │ │ ├── update-user.dto.ts # Update user details
|
||||
│ │ │ └── update-user-preference.dto.ts # Update user preferences
|
||||
│ │ ├── entities/
|
||||
│ │ │ ├── user.entity.ts # User table definition
|
||||
│ │ │ ├── role.entity.ts # Role definition
|
||||
│ │ │ ├── permission.entity.ts # Permission entity
|
||||
│ │ │ ├── user-assignment.entity.ts # User assignment entity
|
||||
│ │ │ └── user-preference.entity.ts # User preference settings
|
||||
│ │ ├── user-assignment.service.ts # User assignment service
|
||||
│ │ ├── user-preference.service.ts # User preference service
|
||||
│ │ ├── user.controller.ts # REST endpoints
|
||||
│ │ ├── user.module.ts # Module DI container
|
||||
│ │ ├── user.service.ts # Business logic
|
||||
│ │ └── user.service.spec.ts # Unit tests
|
||||
│ │
|
||||
│ ├── project/ # 🏢 Project/Organization/Contract structure
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── create-project.dto.ts # Create new project
|
||||
│ │ │ ├── search-project.dto.ts # Search projects
|
||||
│ │ │ └── update-project.dto.ts # Update project
|
||||
│ │ ├── entities/
|
||||
│ │ │ ├── project.entity.ts # Project table definition
|
||||
│ │ │ ├── contract.entity.ts # Contract table definition
|
||||
│ │ │ ├── organization.entity.ts # Organization table definition
|
||||
│ │ │ ├── project-organization.entity.ts # Project organization entity
|
||||
│ │ │ └── contract-organization.entity.ts # Contract organization entity
|
||||
│ │ ├── project.controller.ts # REST endpoints
|
||||
│ │ ├── project.controller.spec.ts # Unit tests
|
||||
│ │ ├── project.module.ts # Module DI container
|
||||
│ │ ├── project.service.ts # Business logic
|
||||
│ │ └── project.service.spec.ts # Unit tests
|
||||
│ │
|
||||
│ ├── correspondence/ # ✉️ Formal letters with routing workflow
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── add-reference.dto.ts # Add reference to correspondence
|
||||
│ │ │ ├── create-correspondence.dto.ts # Create new correspondence
|
||||
│ │ │ ├── search-correspondence.dto.ts # Search correspondences
|
||||
│ │ │ ├── submit-correspondence.dto.ts # Submit correspondence
|
||||
│ │ │ └── workflow-action.dto.ts # Workflow action
|
||||
│ │ ├── entities/
|
||||
│ │ │ ├── correspondence.entity.ts # Correspondence table definition
|
||||
│ │ │ ├── correspondence-revision.entity.ts # Correspondence revision entity
|
||||
│ │ │ ├── correspondence-routing.entity.ts # Correspondence routing entity (Unified Workflow)
|
||||
│ │ │ ├── correspondence-status.entity.ts # Correspondence status entity
|
||||
│ │ │ ├── correspondence-type.entity.ts # Correspondence type entity
|
||||
│ │ │ ├── correspondence-reference.entity.ts # Correspondence reference entity
|
||||
│ │ │ ├── routing-template.entity.ts # Routing template entity
|
||||
│ │ │ └── routing-template-step.entity.ts # Routing template step entity
|
||||
│ │ ├── correspondence.controller.ts # REST endpoints
|
||||
│ │ ├── correspondence.controller.spec.ts # Unit tests
|
||||
│ │ ├── correspondence.module.ts # Module DI container
|
||||
│ │ ├── correspondence.service.ts # Business logic
|
||||
│ │ └── correspondence.service.spec.ts # Unit tests
|
||||
│ │
|
||||
│ ├── drawing/ # 📐Contract & Shop drawing tracking
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── create-contract-drawing.dto.ts # Create new contract drawing
|
||||
│ │ │ ├── create-shop-drawing.dto.ts # Create new shop drawing
|
||||
│ │ │ ├── create-shop-drawing-revision.dto.ts # Create new shop drawing revision
|
||||
│ │ │ ├── search-contract-drawing.dto.ts # Search contract drawings
|
||||
│ │ │ ├── search-shop-drawing.dto.ts # Search shop drawings
|
||||
│ │ │ └── update-contract-drawing.dto.ts # Update contract drawing
|
||||
│ │ ├── entities/
|
||||
│ │ │ ├── contract-drawing.entity.ts # Contract drawing entity
|
||||
│ │ │ ├── contract-drawing-volume.entity.ts # Contract drawing volume entity
|
||||
│ │ │ ├── contract-drawing-sub-category.entity.ts # Contract drawing sub category entity
|
||||
│ │ │ ├── shop-drawing.entity.ts # Shop drawing entity
|
||||
│ │ │ ├── shop-drawing-revision.entity.ts # Shop drawing revision entity
|
||||
│ │ │ ├── shop-drawing-main-category.entity.ts # Shop drawing main category entity
|
||||
│ │ │ └── shop-drawing-sub-category.entity.ts # Shop drawing sub category entity
|
||||
│ │ ├── drawing.module.ts # Module DI container
|
||||
│ │ ├── contract-drawing.controller.ts # REST endpoints
|
||||
│ │ ├── contract-drawing.service.ts # Business logic
|
||||
│ │ ├── drawing-master-data.controller.ts # REST endpoints
|
||||
│ │ ├── drawing-master-data.service.ts # Business logic
|
||||
│ │ ├── shop-drawing.controller.ts # REST endpoints
|
||||
│ │ └── shop-drawing.service.ts # Business logic
|
||||
│ │
|
||||
│ ├── rfa/ # ✅ Request for Approval (multi-step workflow)
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── create-rfa.dto.ts # Create new RFA
|
||||
│ │ │ ├── search-rfa.dto.ts # Search RFAs
|
||||
│ │ │ └── update-rfa.dto.ts # Update RFA
|
||||
│ │ ├── entities/
|
||||
│ │ │ ├── rfa.entity.ts # RFA entity
|
||||
│ │ │ ├── rfa-revision.entity.ts # RFA revision entity
|
||||
│ │ │ ├── rfa-item.entity.ts # RFA item entity
|
||||
│ │ │ ├── rfa-type.entity.ts # RFA type entity
|
||||
│ │ │ ├── rfa-status-code.entity.ts # RFA status code entity
|
||||
│ │ │ ├── rfa-approve-code.entity.ts # RFA approve code entity
|
||||
│ │ │ ├── rfa-workflow.entity.ts # RFA workflow entity
|
||||
│ │ │ ├── rfa-workflow-template.entity.ts # RFA workflow template entity
|
||||
│ │ │ └── rfa-workflow-template-step.entity.ts # RFA workflow template step entity
|
||||
│ │ ├── rfa.controller.ts # REST endpoints
|
||||
│ │ ├── rfa.module.ts # Module DI container
|
||||
│ │ └── rfa.service.ts # Business logic
|
||||
│ │
|
||||
│ ├── circulation/ # 🔄 Internal routing workflow
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── create-circulation.dto.ts # Create new circulation
|
||||
│ │ │ ├── update-circulation-routing.dto.ts # Update circulation routing
|
||||
│ │ │ └── search-circulation.dto.ts # Search circulation
|
||||
│ │ ├── entities/
|
||||
│ │ │ ├── circulation.entity.ts # Circulation entity
|
||||
│ │ │ ├── circulation-routing.entity.ts # Circulation routing entity
|
||||
│ │ │ └── circulation-status-code.entity.ts # Circulation status code entity
|
||||
│ │ ├── circulation.controller.ts # REST endpoints
|
||||
│ │ ├── circulation.module.ts # Module DI container
|
||||
│ │ └── circulation.service.ts # Business logic
|
||||
│ │
|
||||
│ ├── transmittal/ # 📤 Document forwarding
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── create-transmittal.dto.ts # Create new transmittal
|
||||
│ │ │ ├── search-transmittal.dto.ts # Search transmittal
|
||||
│ │ │ └── update-transmittal.dto.ts # Update transmittal
|
||||
│ │ ├── entities/
|
||||
│ │ │ ├── transmittal.entity.ts # Transmittal entity
|
||||
│ │ │ └── transmittal-item.entity.ts # Transmittal item entity
|
||||
│ │ ├── transmittal.controller.ts # REST endpoints
|
||||
│ │ ├── transmittal.module.ts # Module DI container
|
||||
│ │ └── transmittal.service.ts # Business logic
|
||||
│ │
|
||||
│ ├── notification/ # 🔔 Real-Time notification system
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── create-notification.dto.ts # Create new notification
|
||||
│ │ │ └── search-notification.dto.ts # Search notification
|
||||
│ │ ├── entities/
|
||||
│ │ │ └── notification.entity.ts # Notification entity
|
||||
│ │ ├── notification.module.ts # WebSocket + Processor registration
|
||||
│ │ ├── notification.controller.ts # REST endpoints
|
||||
│ │ ├── notification.gateway.ts # WebSocket gateway
|
||||
│ │ ├── notification.processor.ts # Message consumer (Consumer/Worker for Email & Line)
|
||||
│ │ ├── notification.service.ts # Business logic
|
||||
│ │ └── notification-cleanup.service.ts # Cron-based cleanup job
|
||||
│ │
|
||||
│ ├── search/ # 🔍 Elasticsearch integration
|
||||
│ │ ├── dto/
|
||||
│ │ │ └── search-query.dto.ts # Search query
|
||||
│ │ ├── search.module.ts # Module DI container
|
||||
│ │ ├── search.controller.ts # REST endpoints
|
||||
│ │ └── search.service.ts # Indexing/search logic
|
||||
│ │
|
||||
│ ├── document-numbering/ # 🔢 Auto-increment controlled ID generation
|
||||
│ │ ├── entities/
|
||||
│ │ │ ├── document-number-format.entity.ts # Document number format entity
|
||||
│ │ │ └── document-number-counter.entity.ts # Document number counter entity
|
||||
│ │ ├── document-numbering.module.ts # Module DI container
|
||||
│ │ ├── document-numbering.service.ts # Business logic
|
||||
│ │ └── document-numbering.service.spec.ts # Unit tests
|
||||
│ │
|
||||
│ ├── workflow-engine/ # ⚙️ Unified state-machine workflow engine
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── create-workflow-definition.dto.ts # Create new workflow definition
|
||||
│ │ │ ├── evaluate-workflow.dto.ts # Evaluate workflow
|
||||
│ │ │ ├── get-available-actions.dto.ts # Get available actions
|
||||
│ │ │ └── update-workflow-definition.dto.ts # Update workflow definition
|
||||
│ │ ├── entities/
|
||||
│ │ │ └── workflow-definition.entity.ts # Workflow definition entity
|
||||
│ │ ├── interfaces/
|
||||
│ │ │ └── workflow.interface.ts # Workflow interface
|
||||
│ │ ├── workflow-engine.controller.ts # REST endpoints
|
||||
│ │ ├── workflow-engine.module.ts # Module DI container
|
||||
│ │ ├── workflow-engine.service.ts # State Machine Logic
|
||||
│ │ └── workflow-engine.service.spec.ts # Unit tests
|
||||
│ │
|
||||
│ ├── json-schema/ # 📋 Dynamic request schema validation
|
||||
│ │ ├── dto/
|
||||
│ │ │ ├── create-json-schema.dto.ts # Create new JSON schema
|
||||
│ │ │ ├── update-json-schema.dto.ts # Update JSON schema
|
||||
│ │ │ └── search-json-schema.dto.ts # Search JSON schema
|
||||
│ │ ├── entities/
|
||||
│ │ │ └── json-schema.entity.ts # JSON schema entity
|
||||
│ │ ├── json-schema.module.ts # Module DI container
|
||||
│ │ ├── json-schema.controller.ts # REST endpoints
|
||||
│ │ ├── json-schema.controller.spec.ts # Unit tests
|
||||
│ │ ├── json-schema.service.ts # Business logic
|
||||
│ │ └── json-schema.service.spec.ts # Unit tests
|
||||
│ │
|
||||
│ └── monitoring/ # 📋 Dynamic request schema validation
|
||||
│ ├── controllers/
|
||||
│ │ └── health.controller.ts # Create new JSON schema
|
||||
│ ├── dto/
|
||||
│ │ └── set-maintenance.dto.ts # Create new JSON schema
|
||||
│ ├── logger/
|
||||
│ │ └── winston.config.ts # JSON schema entity
|
||||
│ ├── services/
|
||||
│ │ └── metrics.service.ts # JSON schema entity
|
||||
│ ├── monitoring.controller.ts # REST endpoints
|
||||
│ ├── monitoring.service.ts # Business logic
|
||||
│ └── monitoring.module.ts # Module DI container
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
@@ -1001,21 +1001,25 @@ CREATE TABLE document_number_counters (
|
||||
-- 1.1 JSON Schemas Registry
|
||||
-- รองรับ: Backend Plan T2.5.1, Req 6.11.1
|
||||
-- เหตุผล: เพื่อ Validate โครงสร้าง JSON Details ของเอกสารแต่ละประเภทแบบ Centralized
|
||||
CREATE TABLE IF NOT EXISTS json_schemas (
|
||||
CREATE TABLE json_schemas (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
schema_code VARCHAR(100) NOT NULL UNIQUE COMMENT 'รหัส Schema เช่น RFA_DWG_V1,
|
||||
CORR_GENERIC',
|
||||
schema_code VARCHAR(100) NOT NULL COMMENT 'รหัส Schema (เช่น RFA_DWG)',
|
||||
version INT NOT NULL DEFAULT 1 COMMENT 'เวอร์ชันของ Schema',
|
||||
schema_definition JSON NOT NULL COMMENT 'โครงสร้าง JSON Schema (Standard Format)',
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
table_name VARCHAR(100) NOT NULL COMMENT 'ชื่อตารางเป้าหมาย (เช่น rfa_revisions)',
|
||||
schema_definition JSON NOT NULL COMMENT 'โครงสร้าง Data Schema (AJV Standard)',
|
||||
ui_schema JSON NULL COMMENT 'โครงสร้าง UI Schema สำหรับ Frontend',
|
||||
virtual_columns JSON NULL COMMENT 'Config สำหรับสร้าง Virtual Columns',
|
||||
migration_script JSON NULL COMMENT 'Script สำหรับแปลงข้อมูลจากเวอร์ชันก่อนหน้า',
|
||||
is_active BOOLEAN DEFAULT TRUE COMMENT 'สถานะการใช้งาน',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
INDEX idx_schema_code (schema_code)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
||||
-- ป้องกัน Schema Code ซ้ำกันใน Version เดียวกัน
|
||||
UNIQUE KEY uk_schema_version (schema_code, version)
|
||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'ตารางเก็บ JSON Schema และ Configuration';
|
||||
-- 1.2 User Preferences
|
||||
-- รองรับ: Req 5.5, 6.8.3
|
||||
-- เหตุผล: แยกการตั้งค่า Notification และ UI ออกจากตาราง Users หลัก
|
||||
CREATE TABLE IF NOT EXISTS user_preferences (
|
||||
CREATE TABLE user_preferences (
|
||||
user_id INT PRIMARY KEY,
|
||||
notify_email BOOLEAN DEFAULT TRUE,
|
||||
notify_line BOOLEAN DEFAULT TRUE,
|
||||
@@ -1145,6 +1149,11 @@ ADD COLUMN v_ref_project_id INT GENERATED ALWAYS AS (
|
||||
ALTER TABLE correspondence_revisions
|
||||
ADD COLUMN v_doc_subtype VARCHAR(50) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '$.subType'))) VIRTUAL,
|
||||
ADD INDEX idx_corr_rev_v_subtype (v_doc_subtype);
|
||||
-- 2. ปรับปรุงตาราง correspondence_revisions
|
||||
-- เพิ่ม Virtual Columns และ Schema Version
|
||||
ALTER TABLE correspondence_revisions
|
||||
ADD COLUMN schema_version INT DEFAULT 1 COMMENT 'เวอร์ชันของ Schema ที่ใช้กับ details'
|
||||
AFTER details;
|
||||
-- ทำแบบเดียวกันกับ RFA Revisions หากมีการเก็บ JSON details
|
||||
ALTER TABLE rfa_revisions
|
||||
ADD COLUMN details JSON NULL COMMENT 'RFA Specific Details'
|
||||
@@ -1153,6 +1162,7 @@ ALTER TABLE rfa_revisions
|
||||
ADD COLUMN v_ref_drawing_count INT GENERATED ALWAYS AS (
|
||||
JSON_UNQUOTE(JSON_EXTRACT(details, '$.drawingCount'))
|
||||
) VIRTUAL;
|
||||
CREATE INDEX idx_rfa_rev_v_drawing_count ON rfa_revisions(v_ref_drawing_count);
|
||||
-- ============================================================
|
||||
-- 5. PARTITIONING PREPARATION (Advance - Optional)
|
||||
-- ============================================================
|
||||
@@ -1592,4 +1602,4 @@ CREATE INDEX idx_circulation_routings_circulation_status ON circulation_routings
|
||||
-- Indexes for document statistics performance
|
||||
CREATE INDEX idx_correspondences_project_type ON correspondences(project_id, correspondence_type_id);
|
||||
CREATE INDEX idx_corr_revisions_status_current ON correspondence_revisions(correspondence_status_id, is_current);
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
SET FOREIGN_KEY_CHECKS = 1;
|
||||
|
||||
@@ -1,301 +0,0 @@
|
||||
```
|
||||
└── 📁backend
|
||||
├── 📁scripts # Script files
|
||||
│ ├── debug-db.ts # Debug database script
|
||||
│ └── verify-workflow.ts # Verify workflow script
|
||||
├── 📁src # Source files
|
||||
│ ├── 📁common # Common files
|
||||
│ │ ├── 📁auth # Authentication files
|
||||
│ │ │ ├── 📁dto # Data transfer objects
|
||||
│ │ │ │ ├── login.dto.ts # Login data transfer object
|
||||
│ │ │ │ └── register.dto.ts # Register data transfer object
|
||||
│ │ │ ├── 📁strategies # Authentication strategies
|
||||
│ │ │ │ ├── jwt-refresh.strategy.ts # JWT refresh strategy
|
||||
│ │ │ │ └── jwt.strategy.ts # JWT strategy
|
||||
│ │ │ ├── auth.controller.spec.ts # Authentication controller spec
|
||||
│ │ │ ├── auth.controller.ts # Authentication controller
|
||||
│ │ │ ├── auth.module.ts # Authentication module
|
||||
│ │ │ ├── auth.service.spec.ts # Authentication service spec
|
||||
│ │ │ └── auth.service.ts # Authentication service
|
||||
│ │ ├── 📁config # Configuration files
|
||||
│ │ │ ├── env.validation.ts # Environment validation
|
||||
│ │ │ └── redis.config.ts # Redis configuration
|
||||
│ │ ├── 📁decorators # Decorators files
|
||||
│ │ │ ├── audit.decorator.ts # Audit decorator
|
||||
│ │ │ ├── bypass-maintenance.decorator.ts # Bypass maintenance decorator
|
||||
│ │ │ ├── circuit-breaker.decorator.ts # Circuit breaker decorator
|
||||
│ │ │ ├── current-user.decorator.ts # Current user decorator
|
||||
│ │ │ ├── idempotency.decorator.ts # Idempotency decorator
|
||||
│ │ │ ├── require-permission.decorator.ts # Require permission decorator
|
||||
│ │ │ └── retry.decorator.ts # Retry decorator
|
||||
│ │ ├── 📁entities # Entities files
|
||||
│ │ │ ├── audit-log.entity.ts # Audit log entity
|
||||
│ │ │ └── base.entity.ts # Base entity
|
||||
│ │ ├── 📁exceptions # Exceptions files
|
||||
│ │ │ └── http-exception.filter.ts # HTTP exception filter
|
||||
│ │ ├── 📁file-storage # File storage files
|
||||
│ │ │ ├── 📁entities # Entities files
|
||||
│ │ │ │ └── attachment.entity.ts # Attachment entity
|
||||
│ │ │ ├── file-cleanup.service.ts # File cleanup service
|
||||
│ │ │ ├── file-storage.controller.spec.ts # File storage controller spec
|
||||
│ │ │ ├── file-storage.controller.ts # File storage controller
|
||||
│ │ │ ├── file-storage.module.ts # File storage module
|
||||
│ │ │ ├── file-storage.service.spec.ts # File storage service spec
|
||||
│ │ │ └── file-storage.service.ts # File storage service
|
||||
│ │ ├── 📁guards # Guards files
|
||||
│ │ │ ├── jwt-auth.guard.ts # JWT authentication guard
|
||||
│ │ │ ├── jwt-refresh.guard.ts # JWT refresh guard
|
||||
│ │ │ ├── maintenance-mode.guard.ts # Maintenance mode guard
|
||||
│ │ │ └── rbac.guard.ts # Role-based access control guard
|
||||
│ │ ├── 📁idempotency # Idempotency files
|
||||
│ │ │ ├── idempotency.interceptor.ts # Idempotency interceptor
|
||||
│ │ │ └── performance.interceptor.ts # Performance interceptor
|
||||
│ │ ├── 📁interceptors # Interceptors files
|
||||
│ │ │ ├── audit-log.interceptor.ts # Audit log interceptor
|
||||
│ │ │ ├── idempotency.interceptor.ts # Idempotency interceptor
|
||||
│ │ │ ├── performance.interceptor.ts # Performance interceptor
|
||||
│ │ │ └── transform.interceptor.ts # Transform interceptor
|
||||
│ │ ├── 📁maintenance # Maintenance files
|
||||
│ │ ├── 📁resilience # Resilience files
|
||||
│ │ │ └── resilience.module.ts # Resilience module
|
||||
│ │ ├── 📁security # Security files
|
||||
│ │ ├── 📁services # Services files
|
||||
│ │ │ ├── crypto.service.ts # Crypto service
|
||||
│ │ │ └── request-context.service.ts # Request context service
|
||||
│ │ └── common.module.ts # Common module
|
||||
│ ├── 📁database # Database files
|
||||
│ │ ├── 📁migrations # Migrations files
|
||||
│ │ ├── 📁seeds # Seeds files
|
||||
│ │ │ └── workflow-definitions.seed.ts # Workflow definitions seed
|
||||
│ ├── 📁modules # Modules files
|
||||
│ │ ├── 📁circulation # Circulation files
|
||||
│ │ │ ├── 📁dto # DTO files
|
||||
│ │ │ │ ├── create-circulation.dto.ts # Create circulation DTO
|
||||
│ │ │ │ ├── search-circulation.dto.ts # Search circulation DTO
|
||||
│ │ │ │ └── update-circulation-routing.dto.ts # Update circulation routing DTO
|
||||
│ │ │ ├── 📁entities # Entities files
|
||||
│ │ │ │ ├── circulation-routing.entity.ts # Circulation routing entity
|
||||
│ │ │ │ ├── circulation-status-code.entity.ts # Circulation status code entity
|
||||
│ │ │ │ └── circulation.entity.ts # Circulation entity
|
||||
│ │ │ ├── circulation.controller.ts # Circulation controller
|
||||
│ │ │ ├── circulation.module.ts # Circulation module
|
||||
│ │ │ └── circulation.service.ts # Circulation service
|
||||
│ │ ├── 📁correspondence # Correspondence files
|
||||
│ │ │ ├── 📁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
|
||||
│ ├── app.controller.spec.ts
|
||||
│ ├── app.controller.ts
|
||||
│ ├── app.module.ts
|
||||
│ ├── app.service.ts
|
||||
│ ├── main.ts
|
||||
│ └── redlock.d.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
|
||||
├── .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
|
||||
```
|
||||
@@ -1,159 +0,0 @@
|
||||
```
|
||||
└── 📁frontend
|
||||
└── 📁app
|
||||
└── 📁(auth)
|
||||
└── 📁login
|
||||
└── page.tsx
|
||||
└── layout.tsx
|
||||
└── 📁(dashboard)
|
||||
└── 📁admin
|
||||
└── 📁users
|
||||
└── page.tsx
|
||||
└── 📁correspondences
|
||||
└── 📁new
|
||||
└── page.tsx
|
||||
└── page.tsx
|
||||
└── 📁dashboard
|
||||
└── page.tsx
|
||||
└── 📁profile
|
||||
└── page.tsx
|
||||
└── 📁projects
|
||||
└── 📁new
|
||||
└── page.tsx
|
||||
└── page.tsx
|
||||
└── layout.tsx
|
||||
└── 📁admin
|
||||
└── 📁api
|
||||
└── 📁auth
|
||||
└── 📁[...nextauth]
|
||||
└── route.ts
|
||||
└── 📁demo
|
||||
└── page.tsx
|
||||
└── 📁fonts
|
||||
├── GeistMonoVF.woff
|
||||
└── GeistVF.woff
|
||||
├── favicon.ico
|
||||
├── globals copy.css
|
||||
├── globals.css
|
||||
├── layout copy.tsx
|
||||
├── layout.tsx
|
||||
├── page.tsx
|
||||
└── 📁components
|
||||
└── 📁custom
|
||||
├── file-upload-zone.tsx
|
||||
├── responsive-data-table.tsx
|
||||
└── workflow-visualizer.tsx
|
||||
└── 📁dashboard
|
||||
└── recent-activity.tsx
|
||||
└── 📁forms
|
||||
└── file-upload.tsx
|
||||
└── 📁layout
|
||||
├── dashboard-shell.tsx
|
||||
├── navbar.tsx
|
||||
├── sidebar.tsx
|
||||
└── user-nav.tsx
|
||||
└── 📁tables
|
||||
└── 📁ui
|
||||
├── avatar.tsx
|
||||
├── badge.tsx
|
||||
├── button.tsx
|
||||
├── calendar.tsx
|
||||
├── card.tsx
|
||||
├── checkbox.tsx
|
||||
├── dropdown-menu.tsx
|
||||
├── input.tsx
|
||||
├── label.tsx
|
||||
├── popover.tsx
|
||||
├── progress.tsx
|
||||
├── scroll-area.tsx
|
||||
├── select.tsx
|
||||
├── switch.tsx
|
||||
├── table.tsx
|
||||
├── tabs.tsx
|
||||
└── textarea.tsx
|
||||
└── 📁config
|
||||
└── menu.ts
|
||||
└── 📁lib
|
||||
└── 📁api
|
||||
└── client.ts
|
||||
└── 📁auth
|
||||
└── 📁hooks
|
||||
└── 📁services
|
||||
├── circulation.service.ts
|
||||
├── contract-drawing.service.ts
|
||||
├── correspondence.service.ts
|
||||
├── index.ts
|
||||
├── json-schema.service.ts
|
||||
├── master-data.service.ts
|
||||
├── monitoring.service.ts
|
||||
├── notification.service.ts
|
||||
├── project.service.ts
|
||||
├── rfa.service.ts
|
||||
├── search.service.ts
|
||||
├── shop-drawing.service.ts
|
||||
├── transmittal.service.ts
|
||||
├── user.service.ts
|
||||
└── workflow-engine.service.ts
|
||||
└── 📁stores
|
||||
├── draft-store.ts
|
||||
└── ui-store.ts
|
||||
├── auth.ts
|
||||
└── utils.ts
|
||||
└── 📁providers
|
||||
├── query-provider.tsx
|
||||
└── session-provider.tsx
|
||||
└── 📁public
|
||||
└── 📁styles
|
||||
└── 📁types
|
||||
└── 📁dto
|
||||
└── 📁circulation
|
||||
├── create-circulation.dto.ts
|
||||
├── search-circulation.dto.ts
|
||||
└── update-circulation-routing.dto.ts
|
||||
└── 📁correspondence
|
||||
├── add-reference.dto.ts
|
||||
├── create-correspondence.dto.ts
|
||||
├── search-correspondence.dto.ts
|
||||
├── submit-correspondence.dto.ts
|
||||
└── workflow-action.dto.ts
|
||||
└── 📁drawing
|
||||
├── contract-drawing.dto.ts
|
||||
└── shop-drawing.dto.ts
|
||||
└── 📁json-schema
|
||||
└── json-schema.dto.ts
|
||||
└── 📁master
|
||||
├── discipline.dto.ts
|
||||
├── number-format.dto.ts
|
||||
├── sub-type.dto.ts
|
||||
└── tag.dto.ts
|
||||
└── 📁monitoring
|
||||
└── set-maintenance.dto.ts
|
||||
└── 📁notification
|
||||
└── notification.dto.ts
|
||||
└── 📁project
|
||||
└── project.dto.ts
|
||||
└── 📁rfa
|
||||
└── rfa.dto.ts
|
||||
└── 📁search
|
||||
└── search-query.dto.ts
|
||||
└── 📁transmittal
|
||||
└── transmittal.dto.ts
|
||||
└── 📁user
|
||||
└── user.dto.ts
|
||||
└── 📁workflow-engine
|
||||
└── workflow-engine.dto.ts
|
||||
└── next-auth.d.ts
|
||||
├── .env.local
|
||||
├── .eslintrc.json
|
||||
├── .gitignore
|
||||
├── components.json
|
||||
├── middleware.ts
|
||||
├── next-env.d.ts
|
||||
├── next.config.mjs
|
||||
├── package.json
|
||||
├── pnpm-lock.yaml
|
||||
├── postcss.config.mjs
|
||||
├── README.md
|
||||
├── tailwind.config.ts
|
||||
└── tsconfig.json
|
||||
```
|
||||
@@ -1,12 +1,13 @@
|
||||
// File: src/common/services/crypto.service.ts
|
||||
// บันทึกการแก้ไข: Encryption/Decryption Utility (T1.1)
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
@Injectable()
|
||||
export class CryptoService {
|
||||
private readonly logger = new Logger(CryptoService.name);
|
||||
private readonly algorithm = 'aes-256-cbc';
|
||||
private readonly key: Buffer;
|
||||
private readonly ivLength = 16;
|
||||
@@ -19,22 +20,42 @@ export class CryptoService {
|
||||
this.key = crypto.scryptSync(secret, 'salt', 32);
|
||||
}
|
||||
|
||||
encrypt(text: string): string {
|
||||
const iv = crypto.randomBytes(this.ivLength);
|
||||
const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);
|
||||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
return `${iv.toString('hex')}:${encrypted}`;
|
||||
encrypt(text: string | number | boolean): string {
|
||||
if (text === null || text === undefined) return text as any;
|
||||
|
||||
try {
|
||||
const stringValue = String(text);
|
||||
const iv = crypto.randomBytes(this.ivLength);
|
||||
const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);
|
||||
let encrypted = cipher.update(stringValue, 'utf8', 'hex');
|
||||
encrypted += cipher.final('hex');
|
||||
return `${iv.toString('hex')}:${encrypted}`;
|
||||
} catch (error: any) {
|
||||
// Fix TS18046: Cast error to any or Error to access .message
|
||||
this.logger.error(`Encryption failed: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
decrypt(text: string): string {
|
||||
const [ivHex, encryptedHex] = text.split(':');
|
||||
if (!ivHex || !encryptedHex) return text;
|
||||
if (!text || typeof text !== 'string' || !text.includes(':')) return text;
|
||||
|
||||
const iv = Buffer.from(ivHex, 'hex');
|
||||
const decipher = crypto.createDecipheriv(this.algorithm, this.key, iv);
|
||||
let decrypted = decipher.update(encryptedHex, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
return decrypted;
|
||||
try {
|
||||
const [ivHex, encryptedHex] = text.split(':');
|
||||
if (!ivHex || !encryptedHex) return text;
|
||||
|
||||
const iv = Buffer.from(ivHex, 'hex');
|
||||
const decipher = crypto.createDecipheriv(this.algorithm, this.key, iv);
|
||||
let decrypted = decipher.update(encryptedHex, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
return decrypted;
|
||||
} catch (error: any) {
|
||||
// Fix TS18046: Cast error to any or Error to access .message
|
||||
this.logger.warn(
|
||||
`Decryption failed for value. Returning original text. Error: ${error.message}`,
|
||||
);
|
||||
// กรณี Decrypt ไม่ได้ ให้คืนค่าเดิมเพื่อป้องกัน App Crash
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// File: src/modules/correspondence/dto/create-routing-template.dto.ts
|
||||
import {
|
||||
IsString,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsInt,
|
||||
IsArray,
|
||||
ValidateNested,
|
||||
IsEnum,
|
||||
IsBoolean,
|
||||
} from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class CreateRoutingTemplateStepDto {
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
sequence!: number;
|
||||
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
toOrganizationId!: number;
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
roleId?: number;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
stepPurpose?: string = 'FOR_REVIEW';
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
expectedDays?: number;
|
||||
}
|
||||
|
||||
export class CreateRoutingTemplateDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
templateName!: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
projectId?: number;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
isActive?: boolean;
|
||||
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => CreateRoutingTemplateStepDto)
|
||||
steps!: CreateRoutingTemplateStepDto[];
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// File: src/modules/correspondence/entities/correspondence-revision.entity.ts
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
@@ -5,12 +6,16 @@ import {
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
CreateDateColumn,
|
||||
Index,
|
||||
} from 'typeorm';
|
||||
import { Correspondence } from './correspondence.entity.js';
|
||||
import { CorrespondenceStatus } from './correspondence-status.entity.js';
|
||||
import { User } from '../../user/entities/user.entity.js';
|
||||
|
||||
@Entity('correspondence_revisions')
|
||||
// ✅ เพิ่ม Index สำหรับ Virtual Columns เพื่อให้ Search เร็วขึ้น
|
||||
@Index('idx_corr_rev_v_project', ['vRefProjectId'])
|
||||
@Index('idx_corr_rev_v_type', ['vRefType'])
|
||||
export class CorrespondenceRevision {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
@@ -39,6 +44,27 @@ export class CorrespondenceRevision {
|
||||
@Column({ type: 'json', nullable: true })
|
||||
details?: any; // เก็บข้อมูลแบบ Dynamic ตาม Type
|
||||
|
||||
// ✅ [New] Virtual Column: ดึง Project ID จาก JSON details
|
||||
@Column({
|
||||
name: 'v_ref_project_id',
|
||||
type: 'int',
|
||||
generatedType: 'VIRTUAL',
|
||||
asExpression: "JSON_UNQUOTE(JSON_EXTRACT(details, '$.projectId'))",
|
||||
nullable: true,
|
||||
})
|
||||
vRefProjectId?: number;
|
||||
|
||||
// ✅ [New] Virtual Column: ดึง Document SubType จาก JSON details
|
||||
@Column({
|
||||
name: 'v_doc_subtype',
|
||||
type: 'varchar',
|
||||
length: 50,
|
||||
generatedType: 'VIRTUAL',
|
||||
asExpression: "JSON_UNQUOTE(JSON_EXTRACT(details, '$.subType'))",
|
||||
nullable: true,
|
||||
})
|
||||
vRefType?: string;
|
||||
|
||||
// Dates
|
||||
@Column({ name: 'document_date', type: 'date', nullable: true })
|
||||
documentDate?: Date;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// File: src/modules/correspondence/entities/correspondence-routing.entity.ts
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
@@ -9,7 +10,7 @@ import {
|
||||
import { CorrespondenceRevision } from './correspondence-revision.entity.js';
|
||||
import { Organization } from '../../project/entities/organization.entity.js';
|
||||
import { User } from '../../user/entities/user.entity.js';
|
||||
import { RoutingTemplate } from './routing-template.entity.js'; // <--- ✅ เพิ่ม Import นี้ครับ
|
||||
import { RoutingTemplate } from './routing-template.entity.js';
|
||||
|
||||
@Entity('correspondence_routings')
|
||||
export class CorrespondenceRouting {
|
||||
@@ -49,6 +50,10 @@ export class CorrespondenceRouting {
|
||||
@Column({ name: 'processed_at', type: 'datetime', nullable: true })
|
||||
processedAt?: Date;
|
||||
|
||||
// ✅ [New] เพิ่ม State Context เพื่อเก็บ Snapshot ข้อมูล Workflow ณ จุดนั้น
|
||||
@Column({ name: 'state_context', type: 'json', nullable: true })
|
||||
stateContext?: any;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt!: Date;
|
||||
|
||||
@@ -57,7 +62,7 @@ export class CorrespondenceRouting {
|
||||
@JoinColumn({ name: 'correspondence_id' })
|
||||
correspondenceRevision?: CorrespondenceRevision;
|
||||
|
||||
@ManyToOne(() => RoutingTemplate) // ตอนนี้ TypeScript จะรู้จัก RoutingTemplate แล้ว
|
||||
@ManyToOne(() => RoutingTemplate)
|
||||
@JoinColumn({ name: 'template_id' })
|
||||
template?: RoutingTemplate;
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
// File: src/modules/correspondence/entities/routing-template-step.entity.ts
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
ManyToOne,
|
||||
JoinColumn,
|
||||
} from 'typeorm';
|
||||
import { RoutingTemplate } from './routing-template.entity.js';
|
||||
import { Organization } from '../../project/entities/organization.entity.js';
|
||||
import { Role } from '../../user/entities/role.entity.js';
|
||||
|
||||
@Entity('correspondence_routing_template_steps')
|
||||
export class RoutingTemplateStep {
|
||||
@@ -22,17 +24,27 @@ export class RoutingTemplateStep {
|
||||
@Column({ name: 'to_organization_id' })
|
||||
toOrganizationId!: number;
|
||||
|
||||
@Column({ name: 'role_id', nullable: true })
|
||||
roleId?: number;
|
||||
|
||||
@Column({ name: 'step_purpose', default: 'FOR_REVIEW' })
|
||||
stepPurpose!: string; // FOR_APPROVAL, FOR_REVIEW
|
||||
stepPurpose!: string;
|
||||
|
||||
@Column({ name: 'expected_days', nullable: true })
|
||||
expectedDays?: number;
|
||||
|
||||
@ManyToOne(() => RoutingTemplate, (t) => t.steps, { onDelete: 'CASCADE' })
|
||||
// Relations
|
||||
@ManyToOne(() => RoutingTemplate, (template) => template.steps, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'template_id' })
|
||||
template?: RoutingTemplate;
|
||||
|
||||
@ManyToOne(() => Organization)
|
||||
@JoinColumn({ name: 'to_organization_id' })
|
||||
toOrganization?: Organization;
|
||||
|
||||
@ManyToOne(() => Role)
|
||||
@JoinColumn({ name: 'role_id' })
|
||||
role?: Role;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// File: src/modules/json-schema/dto/create-json-schema.dto.ts
|
||||
import {
|
||||
IsString,
|
||||
IsNotEmpty,
|
||||
@@ -5,22 +6,65 @@ import {
|
||||
IsOptional,
|
||||
IsBoolean,
|
||||
IsObject,
|
||||
IsArray,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class VirtualColumnConfigDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
json_path!: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
column_name!: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
data_type!: 'INT' | 'VARCHAR' | 'BOOLEAN' | 'DATE' | 'DECIMAL' | 'DATETIME';
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
index_type?: 'INDEX' | 'UNIQUE' | 'FULLTEXT';
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
is_required?: boolean;
|
||||
}
|
||||
|
||||
export class CreateJsonSchemaDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
schemaCode!: string; // รหัส Schema (ต้องไม่ซ้ำ เช่น 'RFA_DWG_V1')
|
||||
schemaCode!: string;
|
||||
|
||||
@IsString() // ✅ เพิ่ม Validation
|
||||
@IsNotEmpty()
|
||||
tableName!: string;
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
version?: number; // เวอร์ชัน (Default: 1)
|
||||
version?: number;
|
||||
|
||||
@IsObject()
|
||||
@IsNotEmpty()
|
||||
schemaDefinition!: Record<string, any>; // โครงสร้าง JSON Schema (Standard Format)
|
||||
schemaDefinition!: Record<string, any>;
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
uiSchema?: Record<string, any>;
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => VirtualColumnConfigDto)
|
||||
virtualColumns?: VirtualColumnConfigDto[];
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
migrationScript?: Record<string, any>;
|
||||
|
||||
@IsBoolean()
|
||||
@IsOptional()
|
||||
isActive?: boolean; // สถานะการใช้งาน
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
19
backend/src/modules/json-schema/dto/migrate-data.dto.ts
Normal file
19
backend/src/modules/json-schema/dto/migrate-data.dto.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
// File: src/modules/json-schema/dto/migrate-data.dto.ts
|
||||
import { IsString, IsNotEmpty, IsInt, IsOptional } from 'class-validator';
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class MigrateDataDto {
|
||||
@ApiProperty({ description: 'The schema code to migrate to (e.g., RFA_DWG)' })
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
targetSchemaCode!: string;
|
||||
|
||||
@ApiProperty({
|
||||
description: 'Target version. If omitted, migrates to latest.',
|
||||
required: false,
|
||||
})
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
targetVersion?: number;
|
||||
}
|
||||
|
||||
@@ -1,24 +1,47 @@
|
||||
// File: src/modules/json-schema/entities/json-schema.entity.ts
|
||||
import {
|
||||
Entity,
|
||||
Column,
|
||||
PrimaryGeneratedColumn,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Index,
|
||||
} from 'typeorm';
|
||||
|
||||
export interface VirtualColumnConfig {
|
||||
json_path: string;
|
||||
column_name: string;
|
||||
data_type: 'INT' | 'VARCHAR' | 'BOOLEAN' | 'DATE' | 'DECIMAL' | 'DATETIME';
|
||||
index_type?: 'INDEX' | 'UNIQUE' | 'FULLTEXT';
|
||||
is_required: boolean;
|
||||
}
|
||||
|
||||
@Entity('json_schemas')
|
||||
@Index(['schemaCode', 'version'], { unique: true })
|
||||
export class JsonSchema {
|
||||
@PrimaryGeneratedColumn()
|
||||
id!: number;
|
||||
|
||||
@Column({ name: 'schema_code', unique: true, length: 100 })
|
||||
schemaCode!: string; // เช่น 'RFA_DWG_V1'
|
||||
@Column({ name: 'schema_code', length: 100 })
|
||||
schemaCode!: string;
|
||||
|
||||
@Column({ default: 1 })
|
||||
version!: number;
|
||||
|
||||
@Column({ name: 'table_name', length: 100, nullable: false }) // ✅ เพิ่ม: ระบุตารางเป้าหมาย
|
||||
tableName!: string;
|
||||
|
||||
@Column({ name: 'schema_definition', type: 'json' })
|
||||
schemaDefinition!: any; // เก็บ JSON Schema มาตรฐาน (Draft 7/2019-09)
|
||||
schemaDefinition!: any;
|
||||
|
||||
@Column({ name: 'ui_schema', type: 'json', nullable: true })
|
||||
uiSchema?: any;
|
||||
|
||||
@Column({ name: 'virtual_columns', type: 'json', nullable: true })
|
||||
virtualColumns?: VirtualColumnConfig[];
|
||||
|
||||
@Column({ name: 'migration_script', type: 'json', nullable: true })
|
||||
migrationScript?: any;
|
||||
|
||||
@Column({ name: 'is_active', default: true })
|
||||
isActive!: boolean;
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
// File: src/modules/json-schema/interfaces/ui-schema.interface.ts
|
||||
|
||||
export type WidgetType =
|
||||
| 'text'
|
||||
| 'textarea'
|
||||
| 'number'
|
||||
| 'select'
|
||||
| 'radio'
|
||||
| 'checkbox'
|
||||
| 'date'
|
||||
| 'datetime'
|
||||
| 'file-upload'
|
||||
| 'document-ref'; // Custom widget สำหรับอ้างอิงเอกสารอื่น
|
||||
|
||||
export type Operator =
|
||||
| 'equals'
|
||||
| 'notEquals'
|
||||
| 'contains'
|
||||
| 'greaterThan'
|
||||
| 'lessThan'
|
||||
| 'in';
|
||||
|
||||
export interface FieldCondition {
|
||||
field: string;
|
||||
operator: Operator;
|
||||
value: any;
|
||||
}
|
||||
|
||||
export interface FieldDependency {
|
||||
condition: FieldCondition;
|
||||
actions: {
|
||||
visibility?: boolean; // true = show, false = hide
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
filterOptions?: Record<string, any>; // เช่น กรอง Dropdown ตามค่าที่เลือก
|
||||
};
|
||||
}
|
||||
|
||||
export interface UiSchemaField {
|
||||
type: 'string' | 'number' | 'boolean' | 'array' | 'object';
|
||||
widget?: WidgetType;
|
||||
title: string;
|
||||
description?: string;
|
||||
placeholder?: string;
|
||||
enum?: any[]; // กรณีเป็น static options
|
||||
enumNames?: string[]; // label สำหรับ options
|
||||
dataSource?: string; // กรณีดึง options จาก API (เช่น 'master-data/disciplines')
|
||||
defaultValue?: any;
|
||||
readOnly?: boolean;
|
||||
hidden?: boolean;
|
||||
|
||||
// Validation & Rules
|
||||
required?: boolean;
|
||||
dependencies?: FieldDependency[];
|
||||
|
||||
// For Nested Structures
|
||||
properties?: { [key: string]: UiSchemaField };
|
||||
items?: UiSchemaField; // For arrays
|
||||
|
||||
// UI Grid Layout (Tailwind classes equivalent)
|
||||
colSpan?: number; // 1-12
|
||||
}
|
||||
|
||||
export interface LayoutGroup {
|
||||
id: string;
|
||||
title: string;
|
||||
description?: string;
|
||||
type: 'group' | 'section';
|
||||
fields: string[]; // Field keys ที่จะอยู่ในกลุ่มนี้
|
||||
}
|
||||
|
||||
export interface LayoutConfig {
|
||||
type: 'stack' | 'grid' | 'tabs' | 'steps' | 'wizard';
|
||||
groups: LayoutGroup[];
|
||||
options?: Record<string, any>; // Config เพิ่มเติมเฉพาะ Layout type
|
||||
}
|
||||
|
||||
export interface UiSchema {
|
||||
layout: LayoutConfig;
|
||||
fields: {
|
||||
[key: string]: UiSchemaField;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// File: src/modules/json-schema/interfaces/validation-result.interface.ts
|
||||
|
||||
export interface ValidationOptions {
|
||||
/**
|
||||
* ลบ field ที่ไม่ได้ระบุใน Schema ออกอัตโนมัติหรือไม่
|
||||
* Default: true
|
||||
*/
|
||||
removeAdditional?: boolean;
|
||||
|
||||
/**
|
||||
* แปลงชนิดข้อมูลอัตโนมัติถ้าเป็นไปได้ (เช่น "123" -> 123)
|
||||
* Default: true
|
||||
*/
|
||||
coerceTypes?: boolean;
|
||||
|
||||
/**
|
||||
* ใช้ค่า Default จาก Schema ถ้าข้อมูลไม่ถูกส่งมา
|
||||
* Default: true
|
||||
*/
|
||||
useDefaults?: boolean;
|
||||
}
|
||||
|
||||
export interface ValidationErrorDetail {
|
||||
field: string;
|
||||
message: string;
|
||||
value?: any;
|
||||
}
|
||||
|
||||
export interface ValidationResult {
|
||||
isValid: boolean;
|
||||
errors: ValidationErrorDetail[];
|
||||
sanitizedData: any;
|
||||
}
|
||||
|
||||
@@ -1,36 +1,172 @@
|
||||
import { Controller, Post, Body, Param, UseGuards } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
||||
// File: src/modules/json-schema/json-schema.controller.ts
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiOperation,
|
||||
ApiParam,
|
||||
ApiResponse,
|
||||
ApiTags,
|
||||
} from '@nestjs/swagger';
|
||||
|
||||
import { JsonSchemaService } from './json-schema.service';
|
||||
// ✅ FIX: Import DTO
|
||||
import { SchemaMigrationService } from './services/schema-migration.service';
|
||||
|
||||
import { CreateJsonSchemaDto } from './dto/create-json-schema.dto';
|
||||
// ✅ FIX: แก้ไข Path ของ Guards
|
||||
import { MigrateDataDto } from './dto/migrate-data.dto';
|
||||
import { SearchJsonSchemaDto } from './dto/search-json-schema.dto';
|
||||
import { UpdateJsonSchemaDto } from './dto/update-json-schema.dto';
|
||||
|
||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { RbacGuard } from '../../common/guards/rbac.guard';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
import { User } from '../user/entities/user.entity';
|
||||
|
||||
@ApiTags('JSON Schemas') // ✅ Add Swagger Tag
|
||||
@ApiTags('JSON Schemas Management')
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard, RbacGuard)
|
||||
@Controller('json-schemas')
|
||||
export class JsonSchemaController {
|
||||
constructor(private readonly schemaService: JsonSchemaService) {}
|
||||
constructor(
|
||||
private readonly jsonSchemaService: JsonSchemaService,
|
||||
private readonly migrationService: SchemaMigrationService,
|
||||
) {}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Schema Management (CRUD)
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Post()
|
||||
@ApiOperation({ summary: 'Create or Update JSON Schema' })
|
||||
@ApiOperation({
|
||||
summary: 'Create a new schema or new version of existing schema',
|
||||
})
|
||||
@ApiResponse({
|
||||
status: 201,
|
||||
description: 'The schema has been successfully created.',
|
||||
})
|
||||
@RequirePermission('system.manage_all') // Admin Only
|
||||
create(@Body() createDto: CreateJsonSchemaDto) {
|
||||
return this.schemaService.createOrUpdate(
|
||||
createDto.schemaCode,
|
||||
createDto.schemaDefinition,
|
||||
);
|
||||
return this.jsonSchemaService.create(createDto);
|
||||
}
|
||||
|
||||
@Post(':code/validate')
|
||||
@ApiOperation({ summary: 'Test validation against a schema' })
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'List all schemas with pagination and filtering' })
|
||||
@RequirePermission('document.view') // Viewer+ can see schemas
|
||||
findAll(@Query() searchDto: SearchJsonSchemaDto) {
|
||||
return this.jsonSchemaService.findAll(searchDto);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get a specific schema version by ID' })
|
||||
@RequirePermission('document.view')
|
||||
findOne(@Param('id', ParseIntPipe) id: number) {
|
||||
return this.jsonSchemaService.findOne(id);
|
||||
}
|
||||
|
||||
@Get('latest/:code')
|
||||
@ApiOperation({
|
||||
summary: 'Get the latest active version of a schema by code',
|
||||
})
|
||||
@ApiParam({ name: 'code', description: 'Schema Code (e.g., RFA_DWG)' })
|
||||
@RequirePermission('document.view')
|
||||
findLatest(@Param('code') code: string) {
|
||||
return this.jsonSchemaService.findLatestByCode(code);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@ApiOperation({
|
||||
summary: 'Update a specific schema (Not recommended for active schemas)',
|
||||
})
|
||||
@RequirePermission('system.manage_all')
|
||||
update(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() updateDto: UpdateJsonSchemaDto,
|
||||
) {
|
||||
return this.jsonSchemaService.update(id, updateDto);
|
||||
}
|
||||
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: 'Delete a schema version (Hard Delete)' })
|
||||
@RequirePermission('system.manage_all')
|
||||
remove(@Param('id', ParseIntPipe) id: number) {
|
||||
return this.jsonSchemaService.remove(id);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Validation & Security
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Post('validate/:code')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: 'Validate data against the latest schema version' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Validation result including errors and sanitized data',
|
||||
})
|
||||
@RequirePermission('document.view')
|
||||
async validate(@Param('code') code: string, @Body() data: any) {
|
||||
const isValid = await this.schemaService.validate(code, data);
|
||||
return { valid: isValid, message: 'Validation passed' };
|
||||
// Note: Validation API นี้ใช้สำหรับ Test หรือ Pre-check เท่านั้น
|
||||
// การ Save จริงจะเรียกผ่าน Service ภายใน
|
||||
return this.jsonSchemaService.validateData(code, data);
|
||||
}
|
||||
|
||||
@Post('read/:code')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({
|
||||
summary: 'Process read data (Decrypt & Filter) based on user roles',
|
||||
})
|
||||
@RequirePermission('document.view')
|
||||
async processReadData(
|
||||
@Param('code') code: string,
|
||||
@Body() data: any,
|
||||
@CurrentUser() user: User,
|
||||
) {
|
||||
// แปลง User Entity เป็น Security Context
|
||||
// ใช้ as any เพื่อ bypass type checking ชั่วคราว เนื่องจาก roles มักจะถูก inject เข้ามาใน request.user
|
||||
// โดย Strategy หรือ Guard แม้จะไม่มีใน Entity หลัก
|
||||
const userWithRoles = user as any;
|
||||
const userRoles = userWithRoles.roles
|
||||
? userWithRoles.roles.map((r: any) => r.roleName || r) // รองรับทั้ง Object Role และ String Role
|
||||
: [];
|
||||
|
||||
return this.jsonSchemaService.processReadData(code, data, { userRoles });
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Data Migration
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Post('migrate/:table/:id')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({
|
||||
summary: 'Migrate specific entity data to target schema version',
|
||||
})
|
||||
@ApiParam({ name: 'table', description: 'Table Name (e.g. rfa_revisions)' })
|
||||
@ApiParam({ name: 'id', description: 'Entity ID' })
|
||||
@RequirePermission('system.manage_all') // Dangerous Op -> Admin Only
|
||||
async migrateData(
|
||||
@Param('table') tableName: string,
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() dto: MigrateDataDto,
|
||||
) {
|
||||
return this.migrationService.migrateData(
|
||||
tableName,
|
||||
id,
|
||||
dto.targetSchemaCode,
|
||||
dto.targetVersion,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,37 @@
|
||||
// File: src/modules/json-schema/json-schema.module.ts
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { JsonSchemaService } from './json-schema.service';
|
||||
import { JsonSchemaController } from './json-schema.controller';
|
||||
|
||||
import { JsonSchema } from './entities/json-schema.entity';
|
||||
import { UserModule } from '../user/user.module';
|
||||
import { JsonSchemaController } from './json-schema.controller';
|
||||
import { JsonSchemaService } from './json-schema.service';
|
||||
|
||||
import { JsonSecurityService } from './services/json-security.service';
|
||||
import { SchemaMigrationService } from './services/schema-migration.service';
|
||||
import { UiSchemaService } from './services/ui-schema.service';
|
||||
import { VirtualColumnService } from './services/virtual-column.service';
|
||||
// Fix TS2307: Correct path to CryptoService
|
||||
import { CryptoService } from '../../common/services/crypto.service';
|
||||
|
||||
// Import Module อื่นๆ ที่จำเป็นสำหรับ Guard (ถ้า Guards อยู่ใน Common อาจจะไม่ต้อง import ที่นี่โดยตรง)
|
||||
// import { UserModule } from '../user/user.module';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([JsonSchema]), UserModule],
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([JsonSchema]),
|
||||
ConfigModule,
|
||||
// UserModule,
|
||||
],
|
||||
controllers: [JsonSchemaController],
|
||||
providers: [JsonSchemaService],
|
||||
exports: [JsonSchemaService],
|
||||
providers: [
|
||||
JsonSchemaService,
|
||||
VirtualColumnService,
|
||||
UiSchemaService,
|
||||
SchemaMigrationService,
|
||||
JsonSecurityService,
|
||||
CryptoService,
|
||||
],
|
||||
exports: [JsonSchemaService, SchemaMigrationService, JsonSecurityService],
|
||||
})
|
||||
export class JsonSchemaModule {}
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { JsonSchemaService } from './json-schema.service';
|
||||
|
||||
describe('JsonSchemaService', () => {
|
||||
let service: JsonSchemaService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [JsonSchemaService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<JsonSchemaService>(JsonSchemaService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -1,107 +1,172 @@
|
||||
// File: src/modules/json-schema/json-schema.controller.ts
|
||||
import {
|
||||
Injectable,
|
||||
OnModuleInit,
|
||||
BadRequestException,
|
||||
NotFoundException,
|
||||
Logger,
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import Ajv from 'ajv';
|
||||
import addFormats from 'ajv-formats';
|
||||
import { JsonSchema } from './entities/json-schema.entity'; // ลบ .js
|
||||
import {
|
||||
ApiBearerAuth,
|
||||
ApiOperation,
|
||||
ApiParam,
|
||||
ApiResponse,
|
||||
ApiTags,
|
||||
} from '@nestjs/swagger';
|
||||
|
||||
@Injectable()
|
||||
export class JsonSchemaService implements OnModuleInit {
|
||||
private ajv: Ajv;
|
||||
private validators = new Map<string, any>();
|
||||
private readonly logger = new Logger(JsonSchemaService.name);
|
||||
import { JsonSchemaService } from './json-schema.service';
|
||||
import { SchemaMigrationService } from './services/schema-migration.service';
|
||||
|
||||
import { CreateJsonSchemaDto } from './dto/create-json-schema.dto';
|
||||
import { MigrateDataDto } from './dto/migrate-data.dto';
|
||||
import { SearchJsonSchemaDto } from './dto/search-json-schema.dto';
|
||||
import { UpdateJsonSchemaDto } from './dto/update-json-schema.dto';
|
||||
|
||||
import { CurrentUser } from '../../common/decorators/current-user.decorator';
|
||||
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { RbacGuard } from '../../common/guards/rbac.guard';
|
||||
import { User } from '../user/entities/user.entity';
|
||||
|
||||
@ApiTags('JSON Schemas Management')
|
||||
@ApiBearerAuth()
|
||||
@UseGuards(JwtAuthGuard, RbacGuard)
|
||||
@Controller('json-schemas')
|
||||
export class JsonSchemaController {
|
||||
constructor(
|
||||
@InjectRepository(JsonSchema)
|
||||
private schemaRepo: Repository<JsonSchema>,
|
||||
private readonly jsonSchemaService: JsonSchemaService,
|
||||
private readonly migrationService: SchemaMigrationService,
|
||||
) {}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Schema Management (CRUD)
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Post()
|
||||
@ApiOperation({
|
||||
summary: 'Create a new schema or new version of existing schema',
|
||||
})
|
||||
@ApiResponse({
|
||||
status: 201,
|
||||
description: 'The schema has been successfully created.',
|
||||
})
|
||||
@RequirePermission('system.manage_all') // Admin Only
|
||||
create(@Body() createDto: CreateJsonSchemaDto) {
|
||||
return this.jsonSchemaService.create(createDto);
|
||||
}
|
||||
|
||||
@Get()
|
||||
@ApiOperation({ summary: 'List all schemas with pagination and filtering' })
|
||||
@RequirePermission('document.view') // Viewer+ can see schemas
|
||||
findAll(@Query() searchDto: SearchJsonSchemaDto) {
|
||||
return this.jsonSchemaService.findAll(searchDto);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
@ApiOperation({ summary: 'Get a specific schema version by ID' })
|
||||
@RequirePermission('document.view')
|
||||
findOne(@Param('id', ParseIntPipe) id: number) {
|
||||
return this.jsonSchemaService.findOne(id);
|
||||
}
|
||||
|
||||
@Get('latest/:code')
|
||||
@ApiOperation({
|
||||
summary: 'Get the latest active version of a schema by code',
|
||||
})
|
||||
@ApiParam({ name: 'code', description: 'Schema Code (e.g., RFA_DWG)' })
|
||||
@RequirePermission('document.view')
|
||||
findLatest(@Param('code') code: string) {
|
||||
return this.jsonSchemaService.findLatestByCode(code);
|
||||
}
|
||||
|
||||
@Patch(':id')
|
||||
@ApiOperation({
|
||||
summary: 'Update a specific schema (Not recommended for active schemas)',
|
||||
})
|
||||
@RequirePermission('system.manage_all')
|
||||
update(
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() updateDto: UpdateJsonSchemaDto,
|
||||
) {
|
||||
this.ajv = new Ajv({ allErrors: true, strict: false });
|
||||
addFormats(this.ajv);
|
||||
return this.jsonSchemaService.update(id, updateDto);
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
// Pre-load schemas (Optional for performance)
|
||||
// const schemas = await this.schemaRepo.find({ where: { isActive: true } });
|
||||
// schemas.forEach(s => this.createValidator(s.schemaCode, s.schemaDefinition));
|
||||
@Delete(':id')
|
||||
@ApiOperation({ summary: 'Delete a schema version (Hard Delete)' })
|
||||
@RequirePermission('system.manage_all')
|
||||
remove(@Param('id', ParseIntPipe) id: number) {
|
||||
return this.jsonSchemaService.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* ตรวจสอบข้อมูล JSON ว่าถูกต้องตาม Schema หรือไม่
|
||||
*/
|
||||
async validate(schemaCode: string, data: any): Promise<boolean> {
|
||||
let validate = this.validators.get(schemaCode);
|
||||
// ----------------------------------------------------------------------
|
||||
// Validation & Security
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
if (!validate) {
|
||||
const schema = await this.schemaRepo.findOne({
|
||||
where: { schemaCode, isActive: true },
|
||||
});
|
||||
|
||||
if (!schema) {
|
||||
throw new NotFoundException(`JSON Schema '${schemaCode}' not found`);
|
||||
}
|
||||
|
||||
try {
|
||||
validate = this.ajv.compile(schema.schemaDefinition);
|
||||
this.validators.set(schemaCode, validate);
|
||||
} catch (error: any) {
|
||||
throw new BadRequestException(
|
||||
`Invalid Schema Definition for '${schemaCode}': ${error.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const valid = validate(data);
|
||||
|
||||
if (!valid) {
|
||||
const errors = validate.errors
|
||||
?.map((e: any) => `${e.instancePath} ${e.message}`)
|
||||
.join(', ');
|
||||
// โยน Error กลับไปเพื่อให้ Controller/Service ปลายทางจัดการ
|
||||
throw new BadRequestException(`JSON Validation Failed: ${errors}`);
|
||||
}
|
||||
|
||||
return true;
|
||||
@Post('validate/:code')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({ summary: 'Validate data against the latest schema version' })
|
||||
@ApiResponse({
|
||||
status: 200,
|
||||
description: 'Validation result including errors and sanitized data',
|
||||
})
|
||||
@RequirePermission('document.view')
|
||||
async validate(@Param('code') code: string, @Body() data: any) {
|
||||
// Note: Validation API นี้ใช้สำหรับ Test หรือ Pre-check เท่านั้น
|
||||
// การ Save จริงจะเรียกผ่าน Service ภายใน
|
||||
return this.jsonSchemaService.validateData(code, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* สร้างหรืออัปเดต Schema
|
||||
*/
|
||||
async createOrUpdate(schemaCode: string, definition: Record<string, any>) {
|
||||
// 1. ตรวจสอบว่า Definition เป็น JSON Schema ที่ถูกต้องไหม
|
||||
try {
|
||||
this.ajv.compile(definition);
|
||||
} catch (error: any) {
|
||||
throw new BadRequestException(
|
||||
`Invalid JSON Schema format: ${error.message}`,
|
||||
);
|
||||
}
|
||||
@Post('read/:code')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({
|
||||
summary: 'Process read data (Decrypt & Filter) based on user roles',
|
||||
})
|
||||
@RequirePermission('document.view')
|
||||
async processReadData(
|
||||
@Param('code') code: string,
|
||||
@Body() data: any,
|
||||
@CurrentUser() user: User,
|
||||
) {
|
||||
// แปลง User Entity เป็น Security Context
|
||||
// แก้ไข TS2339 & TS7006: Type Casting เพื่อให้เข้าถึง roles ได้โดยไม่ error
|
||||
// เนื่องจาก User Entity ปกติไม่มี property roles (แต่อาจถูก Inject มาตอน Runtime หรือผ่าน Assignments)
|
||||
const userWithRoles = user as any;
|
||||
const userRoles = userWithRoles.roles
|
||||
? userWithRoles.roles.map((r: any) => r.roleName)
|
||||
: [];
|
||||
|
||||
// 2. บันทึกลง DB
|
||||
let schema = await this.schemaRepo.findOne({ where: { schemaCode } });
|
||||
return this.jsonSchemaService.processReadData(code, data, { userRoles });
|
||||
}
|
||||
|
||||
if (schema) {
|
||||
schema.schemaDefinition = definition;
|
||||
schema.version += 1;
|
||||
} else {
|
||||
schema = this.schemaRepo.create({
|
||||
schemaCode,
|
||||
schemaDefinition: definition,
|
||||
version: 1,
|
||||
});
|
||||
}
|
||||
// ----------------------------------------------------------------------
|
||||
// Data Migration
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const savedSchema = await this.schemaRepo.save(schema);
|
||||
|
||||
// 3. Clear Cache เพื่อให้ครั้งหน้าโหลดตัวใหม่
|
||||
this.validators.delete(schemaCode);
|
||||
this.logger.log(`Schema '${schemaCode}' updated (v${savedSchema.version})`);
|
||||
|
||||
return savedSchema;
|
||||
@Post('migrate/:table/:id')
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@ApiOperation({
|
||||
summary: 'Migrate specific entity data to target schema version',
|
||||
})
|
||||
@ApiParam({ name: 'table', description: 'Table Name (e.g. rfa_revisions)' })
|
||||
@ApiParam({ name: 'id', description: 'Entity ID' })
|
||||
@RequirePermission('system.manage_all') // Dangerous Op -> Admin Only
|
||||
async migrateData(
|
||||
@Param('table') tableName: string,
|
||||
@Param('id', ParseIntPipe) id: number,
|
||||
@Body() dto: MigrateDataDto,
|
||||
) {
|
||||
return this.migrationService.migrateData(
|
||||
tableName,
|
||||
id,
|
||||
dto.targetSchemaCode,
|
||||
dto.targetVersion,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
// File: src/modules/json-schema/services/json-security.service.ts
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { CryptoService } from '../../../common/services/crypto.service';
|
||||
|
||||
export interface SecurityContext {
|
||||
userRoles: string[]; // Role ของ user ปัจจุบัน (เช่น ['EDITOR', 'viewer'])
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class JsonSecurityService {
|
||||
constructor(private readonly cryptoService: CryptoService) {}
|
||||
|
||||
/**
|
||||
* ขาเข้า (Write): เข้ารหัสข้อมูล Sensitive ก่อนบันทึก
|
||||
*/
|
||||
encryptFields(data: any, schemaDefinition: any): any {
|
||||
if (!data || typeof data !== 'object') return data;
|
||||
const processed = Array.isArray(data) ? [...data] : { ...data };
|
||||
|
||||
// Traverse schema properties
|
||||
if (schemaDefinition.properties) {
|
||||
for (const [key, propSchema] of Object.entries<any>(
|
||||
schemaDefinition.properties,
|
||||
)) {
|
||||
if (data[key] !== undefined) {
|
||||
// 1. Check encryption flag
|
||||
if (propSchema['x-encrypt'] === true) {
|
||||
processed[key] = this.cryptoService.encrypt(data[key]);
|
||||
}
|
||||
|
||||
// 2. Recursive for nested objects/arrays
|
||||
if (propSchema.type === 'object' && propSchema.properties) {
|
||||
processed[key] = this.encryptFields(data[key], propSchema);
|
||||
} else if (propSchema.type === 'array' && propSchema.items) {
|
||||
if (Array.isArray(data[key])) {
|
||||
processed[key] = data[key].map((item: any) =>
|
||||
this.encryptFields(item, propSchema.items),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
|
||||
/**
|
||||
* ขาออก (Read): ถอดรหัส และ กรองข้อมูลตามสิทธิ์
|
||||
*/
|
||||
decryptAndFilterFields(
|
||||
data: any,
|
||||
schemaDefinition: any,
|
||||
context: SecurityContext,
|
||||
): any {
|
||||
if (!data || typeof data !== 'object') return data;
|
||||
|
||||
// Clone data to avoid mutation
|
||||
const processed = Array.isArray(data) ? [...data] : { ...data };
|
||||
|
||||
if (schemaDefinition.properties) {
|
||||
for (const [key, propSchema] of Object.entries<any>(
|
||||
schemaDefinition.properties,
|
||||
)) {
|
||||
if (data[key] !== undefined) {
|
||||
// 1. Decrypt (ถ้ามีค่าและถูกเข้ารหัสไว้)
|
||||
if (propSchema['x-encrypt'] === true) {
|
||||
processed[key] = this.cryptoService.decrypt(data[key]);
|
||||
}
|
||||
|
||||
// 2. Security Check (Role-based Access Control)
|
||||
if (propSchema['x-security']) {
|
||||
const rule = propSchema['x-security'];
|
||||
const requiredRoles = rule.roles || [];
|
||||
const hasPermission = requiredRoles.some(
|
||||
(role: string) =>
|
||||
context.userRoles.includes(role) ||
|
||||
context.userRoles.includes('SUPERADMIN'),
|
||||
);
|
||||
|
||||
if (!hasPermission) {
|
||||
if (rule.onDeny === 'REMOVE') {
|
||||
delete processed[key];
|
||||
continue; // ข้ามไป field ถัดไป
|
||||
} else {
|
||||
// Default: MASK
|
||||
processed[key] = '********';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Recursive for nested objects/arrays
|
||||
// (ทำต่อเมื่อ Field ยังไม่ถูกลบ)
|
||||
if (processed[key] !== undefined) {
|
||||
if (propSchema.type === 'object' && propSchema.properties) {
|
||||
processed[key] = this.decryptAndFilterFields(
|
||||
processed[key],
|
||||
propSchema,
|
||||
context,
|
||||
);
|
||||
} else if (propSchema.type === 'array' && propSchema.items) {
|
||||
if (Array.isArray(processed[key])) {
|
||||
processed[key] = processed[key].map((item: any) =>
|
||||
this.decryptAndFilterFields(item, propSchema.items, context),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return processed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
// File: src/modules/json-schema/services/schema-migration.service.ts
|
||||
import { Injectable, Logger, BadRequestException } from '@nestjs/common';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { JsonSchemaService } from '../json-schema.service';
|
||||
|
||||
export interface MigrationStep {
|
||||
type:
|
||||
| 'FIELD_RENAME'
|
||||
| 'FIELD_TRANSFORM'
|
||||
| 'FIELD_ADD'
|
||||
| 'FIELD_REMOVE'
|
||||
| 'STRUCTURE_CHANGE';
|
||||
config: any;
|
||||
}
|
||||
|
||||
export interface MigrationResult {
|
||||
success: boolean;
|
||||
fromVersion: number;
|
||||
toVersion: number;
|
||||
migratedFields: string[];
|
||||
error?: string;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class SchemaMigrationService {
|
||||
private readonly logger = new Logger(SchemaMigrationService.name);
|
||||
|
||||
constructor(
|
||||
private readonly dataSource: DataSource,
|
||||
private readonly jsonSchemaService: JsonSchemaService,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Migrate data for a specific entity to a target schema version
|
||||
*/
|
||||
async migrateData(
|
||||
entityType: string, // e.g., 'rfa_revisions', 'correspondence_revisions'
|
||||
entityId: number,
|
||||
targetSchemaCode: string,
|
||||
targetVersion?: number,
|
||||
): Promise<MigrationResult> {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
// 1. Get Target Schema
|
||||
let targetSchema;
|
||||
if (targetVersion) {
|
||||
targetSchema = await this.jsonSchemaService.findOneByCodeAndVersion(
|
||||
targetSchemaCode,
|
||||
targetVersion,
|
||||
);
|
||||
} else {
|
||||
targetSchema =
|
||||
await this.jsonSchemaService.findLatestByCode(targetSchemaCode);
|
||||
}
|
||||
|
||||
// 2. Fetch Entity Data & Current Version
|
||||
// Note: This assumes the entity table has 'details' (json) and 'schema_version' (int) columns
|
||||
// If schema_version is not present, we assume version 1
|
||||
const entity = await queryRunner.manager.query(
|
||||
`SELECT details, schema_version FROM ${entityType} WHERE id = ?`,
|
||||
[entityId],
|
||||
);
|
||||
|
||||
if (!entity || entity.length === 0) {
|
||||
throw new BadRequestException(
|
||||
`Entity ${entityType} with ID ${entityId} not found.`,
|
||||
);
|
||||
}
|
||||
|
||||
const currentData = entity[0].details || {};
|
||||
const currentVersion = entity[0].schema_version || 1;
|
||||
|
||||
if (currentVersion >= targetSchema.version) {
|
||||
return {
|
||||
success: true,
|
||||
fromVersion: currentVersion,
|
||||
toVersion: currentVersion,
|
||||
migratedFields: [], // No migration needed
|
||||
};
|
||||
}
|
||||
|
||||
// 3. Find Migration Path (Iterative Upgrade)
|
||||
let migratedData = JSON.parse(JSON.stringify(currentData));
|
||||
const migratedFields: string[] = [];
|
||||
|
||||
// Loop from current version up to target version
|
||||
for (let v = currentVersion + 1; v <= targetSchema.version; v++) {
|
||||
const schemaVer = await this.jsonSchemaService.findOneByCodeAndVersion(
|
||||
targetSchemaCode,
|
||||
v,
|
||||
);
|
||||
|
||||
if (schemaVer && schemaVer.migrationScript) {
|
||||
this.logger.log(
|
||||
`Applying migration script for ${targetSchemaCode} v${v}...`,
|
||||
);
|
||||
|
||||
const script = schemaVer.migrationScript;
|
||||
|
||||
// Apply steps defined in migrationScript
|
||||
if (Array.isArray(script.steps)) {
|
||||
for (const step of script.steps) {
|
||||
migratedData = await this.applyMigrationStep(step, migratedData);
|
||||
if (step.config.field || step.config.new_field) {
|
||||
migratedFields.push(step.config.new_field || step.config.field);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Validate Migrated Data against Target Schema
|
||||
const validation = await this.jsonSchemaService.validateData(
|
||||
targetSchema.schemaCode,
|
||||
migratedData,
|
||||
);
|
||||
|
||||
if (!validation.isValid) {
|
||||
throw new BadRequestException(
|
||||
`Migration failed: Resulting data does not match target schema v${targetSchema.version}. Errors: ${JSON.stringify(validation.errors)}`,
|
||||
);
|
||||
}
|
||||
|
||||
// 5. Save Migrated Data
|
||||
// Update details AND schema_version
|
||||
await queryRunner.manager.query(
|
||||
`UPDATE ${entityType} SET details = ?, schema_version = ? WHERE id = ?`,
|
||||
[
|
||||
JSON.stringify(validation.sanitizedData),
|
||||
targetSchema.version,
|
||||
entityId,
|
||||
],
|
||||
);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return {
|
||||
success: true,
|
||||
fromVersion: currentVersion,
|
||||
toVersion: targetSchema.version,
|
||||
migratedFields: [...new Set(migratedFields)],
|
||||
};
|
||||
} catch (err: any) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
this.logger.error(`Migration failed: ${err.message}`, err.stack);
|
||||
throw err;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply a single migration step
|
||||
*/
|
||||
private async applyMigrationStep(
|
||||
step: MigrationStep,
|
||||
data: any,
|
||||
): Promise<any> {
|
||||
const newData = { ...data };
|
||||
|
||||
switch (step.type) {
|
||||
case 'FIELD_RENAME':
|
||||
if (newData[step.config.old_field] !== undefined) {
|
||||
newData[step.config.new_field] = newData[step.config.old_field];
|
||||
delete newData[step.config.old_field];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'FIELD_ADD':
|
||||
if (newData[step.config.field] === undefined) {
|
||||
newData[step.config.field] = step.config.default_value;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'FIELD_REMOVE':
|
||||
delete newData[step.config.field];
|
||||
break;
|
||||
|
||||
case 'FIELD_TRANSFORM':
|
||||
if (newData[step.config.field] !== undefined) {
|
||||
// Simple transform logic (e.g., map values)
|
||||
if (step.config.transform === 'MAP_VALUES' && step.config.mapping) {
|
||||
const oldVal = newData[step.config.field];
|
||||
newData[step.config.field] = step.config.mapping[oldVal] || oldVal;
|
||||
}
|
||||
// Type casting
|
||||
else if (step.config.transform === 'TO_NUMBER') {
|
||||
newData[step.config.field] = Number(newData[step.config.field]);
|
||||
} else if (step.config.transform === 'TO_STRING') {
|
||||
newData[step.config.field] = String(newData[step.config.field]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
this.logger.warn(`Unknown migration step type: ${step.type}`);
|
||||
}
|
||||
|
||||
return newData;
|
||||
}
|
||||
}
|
||||
|
||||
115
backend/src/modules/json-schema/services/ui-schema.service.ts
Normal file
115
backend/src/modules/json-schema/services/ui-schema.service.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
// File: src/modules/json-schema/services/ui-schema.service.ts
|
||||
import { Injectable, BadRequestException, Logger } from '@nestjs/common';
|
||||
import { UiSchema, UiSchemaField } from '../interfaces/ui-schema.interface';
|
||||
|
||||
@Injectable()
|
||||
export class UiSchemaService {
|
||||
private readonly logger = new Logger(UiSchemaService.name);
|
||||
|
||||
/**
|
||||
* ตรวจสอบความถูกต้องของ UI Schema
|
||||
*/
|
||||
validateUiSchema(uiSchema: UiSchema, dataSchema: any): boolean {
|
||||
if (!uiSchema) return true; // Optional field
|
||||
|
||||
// 1. Validate Structure เบื้องต้น
|
||||
if (!uiSchema.layout || !uiSchema.fields) {
|
||||
throw new BadRequestException(
|
||||
'UI Schema must contain "layout" and "fields" properties.',
|
||||
);
|
||||
}
|
||||
|
||||
// 2. ตรวจสอบว่า Fields ใน Layout มีคำนิยามครบถ้วน
|
||||
const definedFields = new Set(Object.keys(uiSchema.fields));
|
||||
const layoutFields = new Set<string>();
|
||||
|
||||
uiSchema.layout.groups.forEach((group) => {
|
||||
group.fields.forEach((fieldKey) => {
|
||||
layoutFields.add(fieldKey);
|
||||
if (!definedFields.has(fieldKey)) {
|
||||
throw new BadRequestException(
|
||||
`Field "${fieldKey}" used in layout "${group.title}" is not defined in "fields".`,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 3. (Optional) ตรวจสอบว่า Fields ใน Data Schema (AJV) มีครบใน UI Schema หรือไม่
|
||||
// ถ้า Data Schema บอกว่ามี field 'title' แต่ UI Schema ไม่มี -> Frontend อาจจะไม่เรนเดอร์
|
||||
if (dataSchema && dataSchema.properties) {
|
||||
const dataKeys = Object.keys(dataSchema.properties);
|
||||
const missingFields = dataKeys.filter((key) => !definedFields.has(key));
|
||||
|
||||
if (missingFields.length > 0) {
|
||||
this.logger.warn(
|
||||
`Data schema properties [${missingFields.join(', ')}] are missing from UI Schema.`,
|
||||
);
|
||||
// ไม่ Throw Error เพราะบางทีเราอาจตั้งใจซ่อน Field (Hidden field)
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* สร้าง UI Schema พื้นฐานจาก Data Schema (AJV) อัตโนมัติ
|
||||
* ใช้กรณี user ไม่ได้ส่ง UI Schema มาให้
|
||||
*/
|
||||
generateDefaultUiSchema(dataSchema: any): UiSchema {
|
||||
if (!dataSchema || !dataSchema.properties) {
|
||||
return {
|
||||
layout: { type: 'stack', groups: [] },
|
||||
fields: {},
|
||||
};
|
||||
}
|
||||
|
||||
const fields: { [key: string]: UiSchemaField } = {};
|
||||
const groupFields: string[] = [];
|
||||
|
||||
for (const [key, value] of Object.entries<any>(dataSchema.properties)) {
|
||||
groupFields.push(key);
|
||||
|
||||
fields[key] = {
|
||||
type: value.type || 'string',
|
||||
title: value.title || this.humanize(key),
|
||||
description: value.description,
|
||||
required: (dataSchema.required || []).includes(key),
|
||||
widget: this.guessWidget(value),
|
||||
colSpan: 12, // Default full width
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
layout: {
|
||||
type: 'stack',
|
||||
groups: [
|
||||
{
|
||||
id: 'default',
|
||||
title: 'General Information',
|
||||
type: 'section',
|
||||
fields: groupFields,
|
||||
},
|
||||
],
|
||||
},
|
||||
fields,
|
||||
};
|
||||
}
|
||||
|
||||
// Helpers
|
||||
private humanize(str: string): string {
|
||||
return str
|
||||
.replace(/([A-Z])/g, ' $1')
|
||||
.replace(/^./, (str) => str.toUpperCase())
|
||||
.trim();
|
||||
}
|
||||
|
||||
private guessWidget(schemaProp: any): any {
|
||||
if (schemaProp.enum) return 'select';
|
||||
if (schemaProp.type === 'boolean') return 'checkbox';
|
||||
if (schemaProp.format === 'date') return 'date';
|
||||
if (schemaProp.format === 'date-time') return 'datetime';
|
||||
if (schemaProp.format === 'binary') return 'file-upload';
|
||||
return 'text';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
// File: src/modules/json-schema/services/virtual-column.service.ts
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { DataSource, QueryRunner } from 'typeorm';
|
||||
import { VirtualColumnConfig } from '../entities/json-schema.entity';
|
||||
|
||||
@Injectable()
|
||||
export class VirtualColumnService {
|
||||
private readonly logger = new Logger(VirtualColumnService.name);
|
||||
|
||||
constructor(private readonly dataSource: DataSource) {}
|
||||
|
||||
/**
|
||||
* สร้าง/อัปเดต Virtual Columns และ Index บน Database จริง
|
||||
*/
|
||||
async setupVirtualColumns(tableName: string, configs: VirtualColumnConfig[]) {
|
||||
if (!configs || configs.length === 0) return;
|
||||
|
||||
// ใช้ QueryRunner เพื่อให้จัดการ Transaction หรือ Connection ได้ละเอียด
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
|
||||
try {
|
||||
this.logger.log(
|
||||
`Start setting up virtual columns for table '${tableName}'...`,
|
||||
);
|
||||
|
||||
// 1. ตรวจสอบว่าตารางมีอยู่จริงไหม
|
||||
const tableExists = await queryRunner.hasTable(tableName);
|
||||
if (!tableExists) {
|
||||
this.logger.warn(
|
||||
`Table '${tableName}' not found. Skipping virtual columns.`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const config of configs) {
|
||||
await this.ensureVirtualColumn(queryRunner, tableName, config);
|
||||
|
||||
if (config.index_type) {
|
||||
await this.ensureIndex(queryRunner, tableName, config);
|
||||
}
|
||||
}
|
||||
|
||||
this.logger.log(
|
||||
`Finished setting up virtual columns for '${tableName}'.`,
|
||||
);
|
||||
} catch (err: any) {
|
||||
this.logger.error(
|
||||
`Failed to setup virtual columns: ${err.message}`,
|
||||
err.stack,
|
||||
);
|
||||
throw err;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* สร้าง Column ถ้ายังไม่มี
|
||||
*/
|
||||
private async ensureVirtualColumn(
|
||||
queryRunner: QueryRunner,
|
||||
tableName: string,
|
||||
config: VirtualColumnConfig,
|
||||
) {
|
||||
const hasColumn = await queryRunner.hasColumn(
|
||||
tableName,
|
||||
config.column_name,
|
||||
);
|
||||
|
||||
if (!hasColumn) {
|
||||
const sql = this.generateAddColumnSql(tableName, config);
|
||||
this.logger.log(`Executing: ${sql}`);
|
||||
await queryRunner.query(sql);
|
||||
} else {
|
||||
// TODO: (Advance) ถ้ามี Column แล้ว แต่ Definition เปลี่ยน อาจต้อง ALTER MODIFY
|
||||
this.logger.debug(
|
||||
`Column '${config.column_name}' already exists in '${tableName}'.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* สร้าง Index ถ้ายังไม่มี
|
||||
*/
|
||||
private async ensureIndex(
|
||||
queryRunner: QueryRunner,
|
||||
tableName: string,
|
||||
config: VirtualColumnConfig,
|
||||
) {
|
||||
const indexName = `idx_${tableName}_${config.column_name}`;
|
||||
|
||||
// ตรวจสอบว่า Index มีอยู่จริงไหม (Query จาก information_schema เพื่อความชัวร์)
|
||||
const checkIndexSql = `
|
||||
SELECT COUNT(1) as count
|
||||
FROM information_schema.STATISTICS
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = ?
|
||||
AND index_name = ?
|
||||
`;
|
||||
const result = await queryRunner.query(checkIndexSql, [
|
||||
tableName,
|
||||
indexName,
|
||||
]);
|
||||
|
||||
if (result[0].count == 0) {
|
||||
const sql = `CREATE ${config.index_type === 'UNIQUE' ? 'UNIQUE' : ''} INDEX ${indexName} ON ${tableName} (${config.column_name})`;
|
||||
this.logger.log(`Creating Index: ${sql}`);
|
||||
await queryRunner.query(sql);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate SQL สำหรับ MariaDB 10.11 Virtual Column
|
||||
* Syntax: ADD COLUMN name type GENERATED ALWAYS AS (expr) VIRTUAL
|
||||
*/
|
||||
private generateAddColumnSql(
|
||||
tableName: string,
|
||||
config: VirtualColumnConfig,
|
||||
): string {
|
||||
const dbType = this.mapDataTypeToSql(config.data_type);
|
||||
// JSON_UNQUOTE(JSON_EXTRACT(details, '$.path'))
|
||||
// ใช้ 'details' เป็นชื่อ column JSON หลัก (ต้องตรงกับ Database Schema ที่ออกแบบไว้)
|
||||
const expression = `JSON_UNQUOTE(JSON_EXTRACT(details, '${config.json_path}'))`;
|
||||
|
||||
// Handle Type Casting inside expression if needed,
|
||||
// but usually MariaDB handles string return from JSON_EXTRACT.
|
||||
// For INT/DATE, virtual column type definition enforces it.
|
||||
|
||||
return `ALTER TABLE ${tableName} ADD COLUMN ${config.column_name} ${dbType} GENERATED ALWAYS AS (${expression}) VIRTUAL`;
|
||||
}
|
||||
|
||||
private mapDataTypeToSql(type: string): string {
|
||||
switch (type) {
|
||||
case 'INT':
|
||||
return 'INT';
|
||||
case 'VARCHAR':
|
||||
return 'VARCHAR(255)';
|
||||
case 'BOOLEAN':
|
||||
return 'TINYINT(1)';
|
||||
case 'DATE':
|
||||
return 'DATE';
|
||||
case 'DATETIME':
|
||||
return 'DATETIME';
|
||||
case 'DECIMAL':
|
||||
return 'DECIMAL(10,2)';
|
||||
default:
|
||||
return 'VARCHAR(255)';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
52
backend/src/modules/rfa/dto/create-rfa-revision.dto.ts
Normal file
52
backend/src/modules/rfa/dto/create-rfa-revision.dto.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
// File: src/modules/rfa/dto/create-rfa-revision.dto.ts
|
||||
import {
|
||||
IsString,
|
||||
IsNotEmpty,
|
||||
IsInt,
|
||||
IsOptional,
|
||||
IsDateString,
|
||||
IsObject,
|
||||
IsArray,
|
||||
} from 'class-validator';
|
||||
|
||||
export class CreateRfaRevisionDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
title!: string;
|
||||
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
rfaStatusCodeId!: number;
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
rfaApproveCodeId?: number;
|
||||
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
documentDate?: string;
|
||||
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
issuedDate?: string;
|
||||
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
receivedDate?: string;
|
||||
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
approvedDate?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
details?: Record<string, any>;
|
||||
|
||||
@IsArray()
|
||||
@IsOptional()
|
||||
shopDrawingRevisionIds?: number[]; // IDs of linked Shop Drawings
|
||||
}
|
||||
37
backend/src/modules/rfa/dto/create-rfa-workflow.dto.ts
Normal file
37
backend/src/modules/rfa/dto/create-rfa-workflow.dto.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
// File: src/modules/rfa/dto/create-rfa-workflow.dto.ts
|
||||
import {
|
||||
IsInt,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsEnum,
|
||||
IsString,
|
||||
} from 'class-validator';
|
||||
|
||||
export enum RfaActionType {
|
||||
REVIEW = 'REVIEW',
|
||||
APPROVE = 'APPROVE',
|
||||
ACKNOWLEDGE = 'ACKNOWLEDGE',
|
||||
}
|
||||
|
||||
export class CreateRfaWorkflowDto {
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
stepNumber!: number;
|
||||
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
organizationId!: number;
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
assignedTo?: number;
|
||||
|
||||
@IsEnum(RfaActionType)
|
||||
@IsOptional()
|
||||
actionType?: RfaActionType;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
comments?: string;
|
||||
}
|
||||
|
||||
@@ -1,37 +1,26 @@
|
||||
// File: src/modules/rfa/dto/create-rfa.dto.ts
|
||||
// File: src/modules/rfa/dto/create-rfa-revision.dto.ts
|
||||
import {
|
||||
IsInt,
|
||||
IsString,
|
||||
IsNotEmpty,
|
||||
IsInt,
|
||||
IsOptional,
|
||||
IsDateString,
|
||||
IsObject,
|
||||
IsArray,
|
||||
IsNotEmpty,
|
||||
} from 'class-validator';
|
||||
|
||||
export class CreateRfaDto {
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
projectId!: number;
|
||||
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
rfaTypeId!: number;
|
||||
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
disciplineId?: number; // [Req 6B] สาขางาน (จำเป็นสำหรับการรันเลข RFA)
|
||||
|
||||
export class CreateRfaRevisionDto {
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
title!: string;
|
||||
|
||||
@IsInt()
|
||||
@IsNotEmpty()
|
||||
toOrganizationId!: number; // ส่งถึงใคร (สำหรับ Routing Step 1)
|
||||
rfaStatusCodeId!: number;
|
||||
|
||||
@IsString()
|
||||
@IsInt()
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
rfaApproveCodeId?: number;
|
||||
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
@@ -39,10 +28,25 @@ export class CreateRfaDto {
|
||||
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
dueDate?: string; // กำหนดวันตอบกลับ
|
||||
issuedDate?: string;
|
||||
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
receivedDate?: string;
|
||||
|
||||
@IsDateString()
|
||||
@IsOptional()
|
||||
approvedDate?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
description?: string;
|
||||
|
||||
@IsObject()
|
||||
@IsOptional()
|
||||
details?: Record<string, any>;
|
||||
|
||||
@IsArray()
|
||||
@IsInt({ each: true })
|
||||
@IsOptional()
|
||||
shopDrawingRevisionIds?: number[]; // Shop Drawings ที่แนบมา
|
||||
}
|
||||
shopDrawingRevisionIds?: number[]; // IDs of linked Shop Drawings
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { PartialType } from '@nestjs/swagger';
|
||||
import { CreateRfaDto } from './create-rfa.dto';
|
||||
import { CreateRfaRevisionDto } from './create-rfa-revision.dto';
|
||||
|
||||
export class UpdateRfaDto extends PartialType(CreateRfaDto) {}
|
||||
export class UpdateRfaDto extends PartialType(CreateRfaRevisionDto) {}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
// File: src/modules/rfa/entities/rfa-revision.entity.ts
|
||||
import {
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
@@ -8,6 +9,7 @@ import {
|
||||
JoinColumn,
|
||||
OneToMany,
|
||||
Unique,
|
||||
Index,
|
||||
} from 'typeorm';
|
||||
import { Rfa } from './rfa.entity';
|
||||
import { Correspondence } from '../../correspondence/entities/correspondence.entity';
|
||||
@@ -15,7 +17,7 @@ import { RfaStatusCode } from './rfa-status-code.entity';
|
||||
import { RfaApproveCode } from './rfa-approve-code.entity';
|
||||
import { User } from '../../user/entities/user.entity';
|
||||
import { RfaItem } from './rfa-item.entity';
|
||||
import { RfaWorkflow } from './rfa-workflow.entity'; // Import เพิ่ม
|
||||
import { RfaWorkflow } from './rfa-workflow.entity';
|
||||
|
||||
@Entity('rfa_revisions')
|
||||
@Unique(['rfaId', 'revisionNumber'])
|
||||
@@ -63,6 +65,20 @@ export class RfaRevision {
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description?: string;
|
||||
|
||||
// ✅ [New] เพิ่ม field details สำหรับเก็บข้อมูล Dynamic ของ RFA (เช่น Method Statement Details)
|
||||
@Column({ type: 'json', nullable: true })
|
||||
details?: any;
|
||||
|
||||
// ✅ [New] Virtual Column: ดึงจำนวนแบบที่แนบ (drawingCount) จาก JSON
|
||||
@Column({
|
||||
name: 'v_ref_drawing_count',
|
||||
type: 'int',
|
||||
generatedType: 'VIRTUAL',
|
||||
asExpression: "JSON_UNQUOTE(JSON_EXTRACT(details, '$.drawingCount'))",
|
||||
nullable: true,
|
||||
})
|
||||
vRefDrawingCount?: number;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt!: Date;
|
||||
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
// File: src/modules/workflow-engine/entities/workflow-history.entity.ts
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
} from 'typeorm';
|
||||
import { WorkflowInstance } from './workflow-instance.entity';
|
||||
|
||||
@Entity('workflow_histories')
|
||||
export class WorkflowHistory {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string;
|
||||
|
||||
@ManyToOne(() => WorkflowInstance, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'instance_id' })
|
||||
instance!: WorkflowInstance;
|
||||
|
||||
@Column({ name: 'instance_id' })
|
||||
instanceId!: string;
|
||||
|
||||
@Column({ name: 'from_state', length: 50 })
|
||||
fromState!: string;
|
||||
|
||||
@Column({ name: 'to_state', length: 50 })
|
||||
toState!: string;
|
||||
|
||||
@Column({ length: 50 })
|
||||
action!: string;
|
||||
|
||||
@Column({ name: 'action_by_user_id', nullable: true })
|
||||
actionByUserId?: number; // User ID ของผู้ดำเนินการ
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
comment?: string;
|
||||
|
||||
@Column({ type: 'json', nullable: true })
|
||||
metadata?: Record<string, any>; // เก็บข้อมูลเพิ่มเติม เช่น Snapshot ของ Context ณ ตอนนั้น
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt!: Date;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// File: src/modules/workflow-engine/entities/workflow-instance.entity.ts
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
import { WorkflowDefinition } from './workflow-definition.entity';
|
||||
|
||||
export enum WorkflowStatus {
|
||||
ACTIVE = 'ACTIVE',
|
||||
COMPLETED = 'COMPLETED',
|
||||
CANCELLED = 'CANCELLED',
|
||||
TERMINATED = 'TERMINATED',
|
||||
}
|
||||
|
||||
@Entity('workflow_instances')
|
||||
@Index(['entityType', 'entityId']) // Index สำหรับค้นหาตามเอกสาร
|
||||
@Index(['currentState']) // Index สำหรับ Filter ตามสถานะ
|
||||
export class WorkflowInstance {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id!: string;
|
||||
|
||||
// เชื่อมโยงกับ Definition ที่ใช้ตอนสร้าง Instance นี้
|
||||
@ManyToOne(() => WorkflowDefinition)
|
||||
@JoinColumn({ name: 'definition_id' })
|
||||
definition!: WorkflowDefinition;
|
||||
|
||||
@Column({ name: 'definition_id' })
|
||||
definitionId!: string;
|
||||
|
||||
// Polymorphic Relation: เชื่อมกับเอกสารได้หลายประเภท (RFA, CORR, etc.)
|
||||
@Column({ name: 'entity_type', length: 50 })
|
||||
entityType!: string;
|
||||
|
||||
@Column({ name: 'entity_id', length: 50 })
|
||||
entityId!: string; // รองรับทั้ง ID แบบ Int และ UUID (เก็บเป็น String)
|
||||
|
||||
@Column({ name: 'current_state', length: 50 })
|
||||
currentState!: string;
|
||||
|
||||
@Column({
|
||||
type: 'enum',
|
||||
enum: WorkflowStatus,
|
||||
default: WorkflowStatus.ACTIVE,
|
||||
})
|
||||
status!: WorkflowStatus;
|
||||
|
||||
// Context เฉพาะของ Instance นี้ (เช่น ตัวแปรที่ส่งต่อระหว่าง State)
|
||||
@Column({ type: 'json', nullable: true })
|
||||
context?: Record<string, any>;
|
||||
|
||||
@CreateDateColumn({ name: 'created_at' })
|
||||
createdAt!: Date;
|
||||
|
||||
@UpdateDateColumn({ name: 'updated_at' })
|
||||
updatedAt!: Date;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
// File: src/modules/workflow-engine/workflow-dsl.service.ts
|
||||
|
||||
import { Injectable, BadRequestException } from '@nestjs/common';
|
||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||
|
||||
export interface WorkflowState {
|
||||
initial?: boolean;
|
||||
@@ -17,7 +17,7 @@ export interface TransitionRule {
|
||||
export interface RequirementRule {
|
||||
role?: string;
|
||||
user?: string;
|
||||
condition?: string; // e.g. "amount > 5000" (Advanced)
|
||||
condition?: string;
|
||||
}
|
||||
|
||||
export interface EventRule {
|
||||
@@ -36,11 +36,12 @@ export interface CompiledWorkflow {
|
||||
export class WorkflowDslService {
|
||||
/**
|
||||
* คอมไพล์ DSL Input ให้เป็น Standard Execution Tree
|
||||
* @param dsl ข้อมูลดิบจาก User (JSON/Object)
|
||||
* @returns CompiledWorkflow Object ที่พร้อมใช้งาน
|
||||
*/
|
||||
compile(dsl: any): CompiledWorkflow {
|
||||
// 1. Basic Structure Validation
|
||||
if (!dsl || typeof dsl !== 'object') {
|
||||
throw new BadRequestException('DSL must be a valid JSON object.');
|
||||
}
|
||||
|
||||
if (!dsl.states || !Array.isArray(dsl.states)) {
|
||||
throw new BadRequestException(
|
||||
'DSL syntax error: "states" array is required.',
|
||||
@@ -55,7 +56,6 @@ export class WorkflowDslService {
|
||||
|
||||
const stateMap = new Set<string>();
|
||||
|
||||
// 2. First Pass: Collect all state names and normalize structure
|
||||
for (const rawState of dsl.states) {
|
||||
if (!rawState.name) {
|
||||
throw new BadRequestException(
|
||||
@@ -71,7 +71,6 @@ export class WorkflowDslService {
|
||||
transitions: {},
|
||||
};
|
||||
|
||||
// Normalize transitions "on:"
|
||||
if (rawState.on) {
|
||||
for (const [action, rule] of Object.entries(rawState.on)) {
|
||||
const rawRule = rule as any;
|
||||
@@ -86,15 +85,11 @@ export class WorkflowDslService {
|
||||
compiled.states[rawState.name] = normalizedState;
|
||||
}
|
||||
|
||||
// 3. Second Pass: Validate Integrity
|
||||
this.validateIntegrity(compiled, stateMap);
|
||||
|
||||
return compiled;
|
||||
}
|
||||
|
||||
/**
|
||||
* ตรวจสอบความสมบูรณ์ของ Workflow Logic
|
||||
*/
|
||||
private validateIntegrity(compiled: CompiledWorkflow, stateMap: Set<string>) {
|
||||
let hasInitial = false;
|
||||
|
||||
@@ -107,19 +102,13 @@ export class WorkflowDslService {
|
||||
hasInitial = true;
|
||||
}
|
||||
|
||||
// ตรวจสอบ Transitions
|
||||
if (state.transitions) {
|
||||
for (const [action, rule] of Object.entries(state.transitions)) {
|
||||
// 1. ปลายทางต้องมีอยู่จริง
|
||||
if (!stateMap.has(rule.to)) {
|
||||
throw new BadRequestException(
|
||||
`DSL Error: State "${stateName}" transitions via "${action}" to unknown state "${rule.to}".`,
|
||||
);
|
||||
}
|
||||
// 2. Action name convention (Optional but recommended)
|
||||
if (!/^[A-Z0-9_]+$/.test(action)) {
|
||||
// Warning or Strict Error could be here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,18 +118,11 @@ export class WorkflowDslService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ประเมินผล (Evaluate) การเปลี่ยนสถานะ
|
||||
* @param compiled ข้อมูล Workflow ที่ Compile แล้ว
|
||||
* @param currentState สถานะปัจจุบัน
|
||||
* @param action การกระทำ
|
||||
* @param context ข้อมูลประกอบ (User roles, etc.)
|
||||
*/
|
||||
evaluate(
|
||||
compiled: CompiledWorkflow,
|
||||
currentState: string,
|
||||
action: string,
|
||||
context: any,
|
||||
context: any = {}, // Default empty object
|
||||
): { nextState: string; events: EventRule[] } {
|
||||
const stateConfig = compiled.states[currentState];
|
||||
|
||||
@@ -164,7 +146,6 @@ export class WorkflowDslService {
|
||||
);
|
||||
}
|
||||
|
||||
// Check Requirements (RBAC Logic inside Engine)
|
||||
if (transition.requirements && transition.requirements.length > 0) {
|
||||
this.checkRequirements(transition.requirements, context);
|
||||
}
|
||||
@@ -175,22 +156,19 @@ export class WorkflowDslService {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* ตรวจสอบเงื่อนไขสิทธิ์ (Requirements)
|
||||
*/
|
||||
private checkRequirements(requirements: RequirementRule[], context: any) {
|
||||
const userRoles = context.roles || [];
|
||||
const userId = context.userId;
|
||||
const safeContext = context || {};
|
||||
const userRoles = safeContext.roles || [];
|
||||
const userId = safeContext.userId;
|
||||
|
||||
const isAllowed = requirements.some((req) => {
|
||||
// กรณีเช็ค Role
|
||||
if (req.role) {
|
||||
return userRoles.includes(req.role);
|
||||
}
|
||||
// กรณีเช็ค Specific User
|
||||
if (req.user) {
|
||||
return userId === req.user;
|
||||
}
|
||||
// Future: Add Condition Logic Evaluation here
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
// File: src/modules/workflow-engine/workflow-engine.controller.ts
|
||||
|
||||
import {
|
||||
Controller,
|
||||
Post,
|
||||
Body,
|
||||
Controller,
|
||||
Get,
|
||||
Query,
|
||||
Patch,
|
||||
Param,
|
||||
ParseUUIDPipe,
|
||||
Patch,
|
||||
Post,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from '@nestjs/common'; // เพิ่ม Patch, Param
|
||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
||||
import { WorkflowEngineService } from './workflow-engine.service';
|
||||
} from '@nestjs/common';
|
||||
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { CreateWorkflowDefinitionDto } from './dto/create-workflow-definition.dto';
|
||||
import { EvaluateWorkflowDto } from './dto/evaluate-workflow.dto';
|
||||
import { GetAvailableActionsDto } from './dto/get-available-actions.dto'; // [NEW]
|
||||
import { UpdateWorkflowDefinitionDto } from './dto/update-workflow-definition.dto'; // [NEW]
|
||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||
import { GetAvailableActionsDto } from './dto/get-available-actions.dto';
|
||||
import { UpdateWorkflowDefinitionDto } from './dto/update-workflow-definition.dto';
|
||||
import { WorkflowEngineService } from './workflow-engine.service';
|
||||
|
||||
@ApiTags('Workflow Engine (DSL)')
|
||||
@Controller('workflow-engine')
|
||||
@UseGuards(JwtAuthGuard) // Protect all endpoints
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class WorkflowEngineController {
|
||||
constructor(private readonly workflowService: WorkflowEngineService) {}
|
||||
|
||||
@@ -42,24 +43,20 @@ export class WorkflowEngineController {
|
||||
@Get('actions')
|
||||
@ApiOperation({ summary: 'Get available actions for current state' })
|
||||
async getAvailableActions(@Query() query: GetAvailableActionsDto) {
|
||||
// [UPDATED] ใช้ DTO แทนแยก Query
|
||||
return this.workflowService.getAvailableActions(
|
||||
query.workflow_code,
|
||||
query.current_state,
|
||||
);
|
||||
}
|
||||
|
||||
// [OPTIONAL/RECOMMENDED] เพิ่ม Endpoint สำหรับ Update (PATCH)
|
||||
@Patch('definitions/:id')
|
||||
@ApiOperation({
|
||||
summary: 'Update workflow status or details (e.g. Deactivate)',
|
||||
summary: 'Update workflow status or details (DSL Re-compile)',
|
||||
})
|
||||
async updateDefinition(
|
||||
@Param('id') id: string,
|
||||
@Body() dto: UpdateWorkflowDefinitionDto, // [NEW] ใช้ Update DTO
|
||||
@Param('id', ParseUUIDPipe) id: string, // เพิ่ม ParseUUIDPipe เพื่อ Validate ID
|
||||
@Body() dto: UpdateWorkflowDefinitionDto,
|
||||
) {
|
||||
// *หมายเหตุ: คุณต้องไปเพิ่ม method update() ใน Service ด้วยถ้าจะใช้ Endpoint นี้
|
||||
// return this.workflowService.update(id, dto);
|
||||
return { message: 'Update logic not implemented yet', id, ...dto };
|
||||
return this.workflowService.update(id, dto);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,21 +2,23 @@
|
||||
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { WorkflowDefinition } from './entities/workflow-definition.entity';
|
||||
import { WorkflowHistory } from './entities/workflow-history.entity'; // [New]
|
||||
import { WorkflowInstance } from './entities/workflow-instance.entity'; // [New]
|
||||
import { WorkflowDslService } from './workflow-dsl.service';
|
||||
import { WorkflowEngineController } from './workflow-engine.controller';
|
||||
import { WorkflowEngineService } from './workflow-engine.service';
|
||||
import { WorkflowDslService } from './workflow-dsl.service'; // [New] ต้องสร้างไฟล์นี้ตามแผน Phase 6A
|
||||
import { WorkflowEngineController } from './workflow-engine.controller'; // [New] ต้องสร้างไฟล์นี้ตามแผน Phase 6A
|
||||
import { WorkflowDefinition } from './entities/workflow-definition.entity'; // [New] ต้องสร้างไฟล์นี้ตามแผน Phase 6A
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
// เชื่อมต่อกับตาราง workflow_definitions
|
||||
TypeOrmModule.forFeature([WorkflowDefinition]),
|
||||
TypeOrmModule.forFeature([
|
||||
WorkflowDefinition,
|
||||
WorkflowInstance, // [New]
|
||||
WorkflowHistory, // [New]
|
||||
]),
|
||||
],
|
||||
controllers: [WorkflowEngineController], // เพิ่ม Controller สำหรับรับ API
|
||||
providers: [
|
||||
WorkflowEngineService, // Service หลัก
|
||||
WorkflowDslService, // [New] Service สำหรับ Compile/Validate DSL
|
||||
],
|
||||
exports: [WorkflowEngineService], // Export ให้ module อื่นใช้เหมือนเดิม
|
||||
controllers: [WorkflowEngineController],
|
||||
providers: [WorkflowEngineService, WorkflowDslService],
|
||||
exports: [WorkflowEngineService],
|
||||
})
|
||||
export class WorkflowEngineModule {}
|
||||
|
||||
@@ -1,20 +1,29 @@
|
||||
// File: src/modules/workflow-engine/workflow-engine.service.ts
|
||||
|
||||
import {
|
||||
Injectable,
|
||||
NotFoundException,
|
||||
BadRequestException,
|
||||
Injectable,
|
||||
Logger,
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { DataSource, Repository } from 'typeorm';
|
||||
|
||||
// Entities
|
||||
import { WorkflowDefinition } from './entities/workflow-definition.entity';
|
||||
import { WorkflowDslService, CompiledWorkflow } from './workflow-dsl.service';
|
||||
import { WorkflowHistory } from './entities/workflow-history.entity';
|
||||
import {
|
||||
WorkflowInstance,
|
||||
WorkflowStatus,
|
||||
} from './entities/workflow-instance.entity';
|
||||
|
||||
// Services & Interfaces
|
||||
import { CreateWorkflowDefinitionDto } from './dto/create-workflow-definition.dto';
|
||||
import { EvaluateWorkflowDto } from './dto/evaluate-workflow.dto';
|
||||
import { UpdateWorkflowDefinitionDto } from './dto/update-workflow-definition.dto';
|
||||
import { CompiledWorkflow, WorkflowDslService } from './workflow-dsl.service';
|
||||
|
||||
// Interface สำหรับ Backward Compatibility (Logic เดิม)
|
||||
// Legacy Interface (Backward Compatibility)
|
||||
export enum WorkflowAction {
|
||||
APPROVE = 'APPROVE',
|
||||
REJECT = 'REJECT',
|
||||
@@ -35,11 +44,16 @@ export class WorkflowEngineService {
|
||||
constructor(
|
||||
@InjectRepository(WorkflowDefinition)
|
||||
private readonly workflowDefRepo: Repository<WorkflowDefinition>,
|
||||
@InjectRepository(WorkflowInstance)
|
||||
private readonly instanceRepo: Repository<WorkflowInstance>,
|
||||
@InjectRepository(WorkflowHistory)
|
||||
private readonly historyRepo: Repository<WorkflowHistory>,
|
||||
private readonly dslService: WorkflowDslService,
|
||||
private readonly dataSource: DataSource, // ใช้สำหรับ Transaction
|
||||
) {}
|
||||
|
||||
// =================================================================
|
||||
// [NEW] DSL & Workflow Engine (Phase 6A)
|
||||
// [PART 1] Definition Management (Phase 6A)
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
@@ -48,8 +62,10 @@ export class WorkflowEngineService {
|
||||
async createDefinition(
|
||||
dto: CreateWorkflowDefinitionDto,
|
||||
): Promise<WorkflowDefinition> {
|
||||
// 1. Compile & Validate DSL
|
||||
const compiled = this.dslService.compile(dto.dsl);
|
||||
|
||||
// 2. Check latest version
|
||||
const latest = await this.workflowDefRepo.findOne({
|
||||
where: { workflow_code: dto.workflow_code },
|
||||
order: { version: 'DESC' },
|
||||
@@ -57,6 +73,7 @@ export class WorkflowEngineService {
|
||||
|
||||
const nextVersion = latest ? latest.version + 1 : 1;
|
||||
|
||||
// 3. Save new version
|
||||
const entity = this.workflowDefRepo.create({
|
||||
workflow_code: dto.workflow_code,
|
||||
version: nextVersion,
|
||||
@@ -65,9 +82,16 @@ export class WorkflowEngineService {
|
||||
is_active: dto.is_active ?? true,
|
||||
});
|
||||
|
||||
return this.workflowDefRepo.save(entity);
|
||||
const saved = await this.workflowDefRepo.save(entity);
|
||||
this.logger.log(
|
||||
`Created Workflow Definition: ${saved.workflow_code} v${saved.version}`,
|
||||
);
|
||||
return saved;
|
||||
}
|
||||
|
||||
/**
|
||||
* อัปเดต Definition (Re-compile DSL)
|
||||
*/
|
||||
async update(
|
||||
id: string,
|
||||
dto: UpdateWorkflowDefinitionDto,
|
||||
@@ -95,33 +119,9 @@ export class WorkflowEngineService {
|
||||
return this.workflowDefRepo.save(definition);
|
||||
}
|
||||
|
||||
async evaluate(dto: EvaluateWorkflowDto): Promise<any> {
|
||||
const definition = await this.workflowDefRepo.findOne({
|
||||
where: { workflow_code: dto.workflow_code, is_active: true },
|
||||
order: { version: 'DESC' },
|
||||
});
|
||||
|
||||
if (!definition) {
|
||||
throw new NotFoundException(
|
||||
`No active workflow definition found for "${dto.workflow_code}"`,
|
||||
);
|
||||
}
|
||||
|
||||
const compiled: CompiledWorkflow = definition.compiled;
|
||||
const result = this.dslService.evaluate(
|
||||
compiled,
|
||||
dto.current_state,
|
||||
dto.action,
|
||||
dto.context || {},
|
||||
);
|
||||
|
||||
this.logger.log(
|
||||
`Workflow Evaluated: ${dto.workflow_code} [${dto.current_state}] --${dto.action}--> [${result.nextState}]`,
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* ดึง Action ที่ทำได้ ณ State ปัจจุบัน
|
||||
*/
|
||||
async getAvailableActions(
|
||||
workflowCode: string,
|
||||
currentState: string,
|
||||
@@ -140,25 +140,206 @@ export class WorkflowEngineService {
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// [LEGACY] Backward Compatibility for Correspondence/RFA Modules
|
||||
// คืนค่า Logic เดิมเพื่อไม่ให้ Module อื่น Error (TS2339)
|
||||
// [PART 2] Runtime Engine (Phase 3.1)
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* เริ่มต้น Workflow Instance ใหม่สำหรับเอกสาร
|
||||
*/
|
||||
async createInstance(
|
||||
workflowCode: string,
|
||||
entityType: string,
|
||||
entityId: string,
|
||||
initialContext: Record<string, any> = {},
|
||||
): Promise<WorkflowInstance> {
|
||||
// 1. หา Definition ล่าสุด
|
||||
const definition = await this.workflowDefRepo.findOne({
|
||||
where: { workflow_code: workflowCode, is_active: true },
|
||||
order: { version: 'DESC' },
|
||||
});
|
||||
|
||||
if (!definition) {
|
||||
throw new NotFoundException(
|
||||
`Workflow "${workflowCode}" not found or inactive.`,
|
||||
);
|
||||
}
|
||||
|
||||
// 2. หา Initial State จาก Compiled Structure
|
||||
const compiled: CompiledWorkflow = definition.compiled;
|
||||
const initialState = Object.keys(compiled.states).find(
|
||||
(key) => compiled.states[key].initial,
|
||||
);
|
||||
|
||||
if (!initialState) {
|
||||
throw new BadRequestException(
|
||||
`Workflow "${workflowCode}" has no initial state defined.`,
|
||||
);
|
||||
}
|
||||
|
||||
// 3. สร้าง Instance
|
||||
const instance = this.instanceRepo.create({
|
||||
definition,
|
||||
entityType,
|
||||
entityId,
|
||||
currentState: initialState,
|
||||
status: WorkflowStatus.ACTIVE,
|
||||
context: initialContext,
|
||||
});
|
||||
|
||||
const savedInstance = await this.instanceRepo.save(instance);
|
||||
this.logger.log(
|
||||
`Started Workflow Instance: ${workflowCode} for ${entityType}:${entityId}`,
|
||||
);
|
||||
return savedInstance;
|
||||
}
|
||||
|
||||
/**
|
||||
* ดำเนินการเปลี่ยนสถานะ (Transition) ของ Instance จริงแบบ Transactional
|
||||
*/
|
||||
async processTransition(
|
||||
instanceId: string,
|
||||
action: string,
|
||||
userId: number,
|
||||
comment?: string,
|
||||
payload: Record<string, any> = {},
|
||||
) {
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
try {
|
||||
// 1. Lock Instance เพื่อป้องกัน Race Condition (Pessimistic Write Lock)
|
||||
const instance = await queryRunner.manager.findOne(WorkflowInstance, {
|
||||
where: { id: instanceId },
|
||||
relations: ['definition'],
|
||||
lock: { mode: 'pessimistic_write' },
|
||||
});
|
||||
|
||||
if (!instance) {
|
||||
throw new NotFoundException(
|
||||
`Workflow Instance "${instanceId}" not found.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (instance.status !== WorkflowStatus.ACTIVE) {
|
||||
throw new BadRequestException(
|
||||
`Workflow is not active (Status: ${instance.status}).`,
|
||||
);
|
||||
}
|
||||
|
||||
// 2. Evaluate Logic ผ่าน DSL Service
|
||||
const compiled: CompiledWorkflow = instance.definition.compiled;
|
||||
const context = { ...instance.context, userId, ...payload }; // Merge Context
|
||||
|
||||
// * DSL Service จะ throw error ถ้า action ไม่ถูกต้อง หรือสิทธิ์ไม่พอ
|
||||
const evaluation = this.dslService.evaluate(
|
||||
compiled,
|
||||
instance.currentState,
|
||||
action,
|
||||
context,
|
||||
);
|
||||
|
||||
const fromState = instance.currentState;
|
||||
const toState = evaluation.nextState;
|
||||
|
||||
// 3. อัปเดต Instance
|
||||
instance.currentState = toState;
|
||||
instance.context = context; // อัปเดต Context ด้วย
|
||||
|
||||
// เช็คว่าเป็น Terminal State หรือไม่?
|
||||
if (compiled.states[toState].terminal) {
|
||||
instance.status = WorkflowStatus.COMPLETED;
|
||||
}
|
||||
|
||||
await queryRunner.manager.save(instance);
|
||||
|
||||
// 4. บันทึก History (Audit Trail)
|
||||
const history = this.historyRepo.create({
|
||||
instanceId: instance.id,
|
||||
fromState,
|
||||
toState,
|
||||
action,
|
||||
actionByUserId: userId,
|
||||
comment,
|
||||
metadata: {
|
||||
events: evaluation.events,
|
||||
payload,
|
||||
},
|
||||
});
|
||||
await queryRunner.manager.save(history);
|
||||
|
||||
// 5. Trigger Events (Integration Point)
|
||||
// ในอนาคตสามารถ Inject NotificationService มาเรียกตรงนี้ได้
|
||||
if (evaluation.events && evaluation.events.length > 0) {
|
||||
this.logger.log(
|
||||
`Triggering ${evaluation.events.length} events for instance ${instanceId}`,
|
||||
);
|
||||
// await this.eventHandler.handle(evaluation.events);
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
this.logger.log(
|
||||
`Transition: ${instanceId} [${fromState}] --${action}--> [${toState}] by User:${userId}`,
|
||||
);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
nextState: toState,
|
||||
events: evaluation.events,
|
||||
isCompleted: instance.status === WorkflowStatus.COMPLETED,
|
||||
};
|
||||
} catch (err) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
this.logger.error(
|
||||
`Transition Failed for ${instanceId}: ${(err as Error).message}`,
|
||||
);
|
||||
throw err;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* (Utility) Evaluate แบบไม่บันทึกผล (Dry Run) สำหรับ Test หรือ Preview
|
||||
*/
|
||||
async evaluate(dto: EvaluateWorkflowDto): Promise<any> {
|
||||
const definition = await this.workflowDefRepo.findOne({
|
||||
where: { workflow_code: dto.workflow_code, is_active: true },
|
||||
order: { version: 'DESC' },
|
||||
});
|
||||
|
||||
if (!definition) {
|
||||
throw new NotFoundException(`Workflow "${dto.workflow_code}" not found`);
|
||||
}
|
||||
|
||||
return this.dslService.evaluate(
|
||||
definition.compiled,
|
||||
dto.current_state,
|
||||
dto.action,
|
||||
dto.context || {},
|
||||
);
|
||||
}
|
||||
|
||||
// =================================================================
|
||||
// [PART 3] Legacy Support (Backward Compatibility)
|
||||
// รักษา Logic เดิมไว้เพื่อให้ Module อื่น (Correspondence/RFA) ทำงานต่อได้
|
||||
// =================================================================
|
||||
|
||||
/**
|
||||
* คำนวณสถานะถัดไปแบบ Linear Sequence (Logic เดิม)
|
||||
* ใช้สำหรับ CorrespondenceService และ RfaService ที่ยังไม่ได้ Refactor
|
||||
* @deprecated แนะนำให้เปลี่ยนไปใช้ processTransition แทนเมื่อ Refactor เสร็จ
|
||||
*/
|
||||
processAction(
|
||||
currentSequence: number,
|
||||
totalSteps: number,
|
||||
action: string, // รับเป็น string เพื่อความยืดหยุ่น
|
||||
action: string,
|
||||
returnToSequence?: number,
|
||||
): TransitionResult {
|
||||
// Map string action to enum logic
|
||||
switch (action) {
|
||||
case WorkflowAction.APPROVE:
|
||||
case WorkflowAction.ACKNOWLEDGE:
|
||||
case 'APPROVE': // Case sensitive handling fallback
|
||||
case 'APPROVE':
|
||||
case 'ACKNOWLEDGE':
|
||||
if (currentSequence >= totalSteps) {
|
||||
return {
|
||||
@@ -193,7 +374,6 @@ export class WorkflowEngineService {
|
||||
};
|
||||
|
||||
default:
|
||||
// กรณีส่ง Action อื่นมา ให้ถือว่าเป็น Approve (หรือจะ Throw Error ก็ได้)
|
||||
this.logger.warn(
|
||||
`Unknown legacy action: ${action}, treating as next step.`,
|
||||
);
|
||||
|
||||
@@ -22,6 +22,27 @@
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitAny": true, // ห้ามใช้ Any โดยไม่จำเป็น
|
||||
"strictBindCallApply": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"paths": {
|
||||
"@backend": ["./src"],
|
||||
"@backend/*": ["./src/*"],
|
||||
"@modules": ["./src/modules"],
|
||||
"@common": ["./src/common"],
|
||||
"@config": ["./src/common/config"],
|
||||
"@circulation": ["./src/modules/circulation"],
|
||||
"@correspondence": ["./src/modules/correspondence"],
|
||||
"@document-numbering": ["./src/modules/document-numbering"],
|
||||
"@drawing": ["./src/modules/drawing"],
|
||||
"@json-schema": ["./src/modules/json-schema"],
|
||||
"@master": ["./src/modules/master"],
|
||||
"@monitoring": ["./src/modules/monitoring"],
|
||||
"@notification": ["./src/modules/notification"],
|
||||
"@project": ["./src/modules/project"],
|
||||
"@rfa": ["./src/modules/rfa"],
|
||||
"@search": ["./src/modules/search"],
|
||||
"@transmittal": ["./src/modules/transmittal"],
|
||||
"@users": ["./src/modules/users"],
|
||||
"@workflow-engine": ["./src/modules/workflow-engine"]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
58
docs/GEM.md
Normal file
58
docs/GEM.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# บทบาท: คุณคือ Programmer ที่เชี่ยวชาญ
|
||||
|
||||
- การจัดการฐานข้อมูล (SQL, Relational Databases, Data Analysis)
|
||||
- การวิเคราะห์ฐานข้อมูล (Database Analysis)
|
||||
- การพัฒนา Backend (NodeJS, NestJS)
|
||||
- การพัฒนา Frontend (NextJS)
|
||||
- ระบบควบคุมการเข้าถึง (RBAC, ABAC)
|
||||
- การ debug และแก้ไข error ในโค้ด
|
||||
|
||||
# Basic data:
|
||||
|
||||
1. Application Requirements file: 0_Requirements_V1_4_4.md
|
||||
2. Full Stack JS file: 1_FullStackJS_V1_4_4.md
|
||||
3. Backend Development Plan:
|
||||
- 2_Backend_Plan_V1_4_4.md
|
||||
- 2_Backend_Plan_Phase6A_V1_4_3.md
|
||||
- 2_Backend_Plan_V1_4_4.Phase_Addition.md
|
||||
4. Frontend Development Plan: 3_Frontend_Plan_V1_4_4.md
|
||||
5. Data files:
|
||||
- 4_Data_Dictionary_V1_4_4.md
|
||||
- 8_lcbp3_v1_4_4.sql
|
||||
- 8_lcbp3_v1_4_4_seed.sql
|
||||
|
||||
# rules:
|
||||
|
||||
- ใช้ภาษาไทยใน comments
|
||||
- เขียนโค้ดให้อ่านง่าย, ใส่ path/filename ในบรรทัดแรกของโค้ด
|
||||
- การอัพเดทโค้ด ให้แก้ไขจากต้นฉบับเป็น โค้ดที่สมบูรณ์
|
||||
- เขียน documentation สำหรับ function สำคัญ
|
||||
- ใช้ข้อมูลพื้นฐานที่ให้มา (Basic data) เพื่อให้คำแนะนำและโค้ดที่สอดคล้องกับเอกสารโครงการ
|
||||
- ต้องตรวจสอบกับ 8_lcbp3_v1_4_4_seed.sql ก่อน สร้างข้อมูลสมมุติ (Mockup data) ถ้ามีข้อมูลอยู่แล้วให้ใช้ข้อมูลที่มีอยู่
|
||||
|
||||
# เป้าหมายและจุดประสงค์:
|
||||
|
||||
- ให้ความช่วยเหลือผู้ใช้ในงานที่เกี่ยวข้องกับการพัฒนาซอฟต์แวร์ โดยเฉพาะอย่างยิ่งในส่วนของ JavaScript (NodeJS, NestJS, NextJS) และฐานข้อมูล (SQL, Relational Databases)
|
||||
- ให้คำปรึกษาเกี่ยวกับการจัดการข้อมูล, การออกแบบฐานข้อมูลเชิงสัมพันธ์, และการใช้โมเดลการควบคุมการเข้าถึง (RBAC, ABAC)
|
||||
- ให้คำแนะนำเกี่ยวกับการแก้ไขปัญหาหรือการพัฒนาซอฟต์แวร์
|
||||
- ช่วยเหลือในการวิเคราะห์และแก้ไขข้อผิดพลาด (debug และ error) ในโค้ดตามที่ผู้ใช้ระบุ
|
||||
|
||||
# พฤติกรรมและกฎเพิ่มเติม:
|
||||
|
||||
1. การเริ่มต้นและการโต้ตอบ:
|
||||
a) ทักทายผู้ใช้ด้วยภาษาไทยอย่างเป็นมิตร และสอบถามเกี่ยวกับปัญหาหรือความช่วยเหลือที่ต้องการในด้านการเขียนโปรแกรมหรือฐานข้อมูล
|
||||
b) ตอบคำถามทางเทคนิคอย่างแม่นยำและเป็นมืออาชีพ โดยใช้ศัพท์เฉพาะทางที่ถูกต้อง
|
||||
c) จำกัดจำนวนประโยคในการตอบกลับแต่ละครั้งให้กระชับและตรงประเด็นเพื่อความรวดเร็วในการสื่อสาร
|
||||
d) ให้วิเคราะห์ปัญกาอย่างละเอียดและรอบคอบ ไม่ต้องรีบตอบ
|
||||
|
||||
2. การจัดการโค้ดและข้อมูล:
|
||||
a) เมื่อผู้ใช้ขอให้อัพเดทโค้ด ให้ทำการแสดงโค้ดฉบับเสมบูรณ์ที่ได้รับการแก้ไขแล้ว
|
||||
b) ต้องแน่ใจว่าโค้ดที่สร้างขึ้นมานั้นอ่านง่ายและมี comments เป็นภาษาไทยตามที่ระบุใน rules
|
||||
c) สำหรับฟังก์ชันที่มีความซับซ้อนหรือมีความสำคัญต่อระบบ ต้องเขียน documentation อธิบายวัตถุประสงค์, พารามิเตอร์, และผลลัพธ์ของฟังก์ชันนั้นๆ ด้วยภาษาไทย
|
||||
d) หากต้องอ้างอิงถึงโครงสร้างข้อมูลหรือข้อกำหนดใดๆ ให้ตรวจสอบจากไฟล์ Basic data ที่ผู้ใช้ให้มาก่อนเสมอ ถ้าไม่พบ ให้แจ้งผู้ใช้ทราบ
|
||||
e) ถ้ามีการอ้างอิงถึงโค้ดที่อยู่ใน Phase หรือ Task ก่อนหน้า ให้สอบถามผู้ใช้เพื่อให้ upload ไฟล์โค้ดที่อ้างอิง (ไม่สร้างใหม่ เพื่อประหยัดเวลา)
|
||||
|
||||
3. โทนโดยรวม:
|
||||
- ใช้ภาษาไทยในการสื่อสารเป็นหลัก ยกเว้นศัพท์เทคนิค
|
||||
- มีความมั่นใจและแสดงออกถึงความเชี่ยวชาญในฐานะโปรแกรมเมอร์ผู้เชี่ยวชาญ
|
||||
- มีความเป็นระเบียบและให้คำแนะนำที่เป็นขั้นตอน
|
||||
@@ -1,162 +0,0 @@
|
||||
# **🌳 โครงสร้างไฟล์และโฟลเดอร์ทั้งหมด \- DMS v1.3.0 Backend**
|
||||
|
||||
DMS\_Backend\_Project/
|
||||
├── Dockerfile
|
||||
├── docker-compose.yml
|
||||
├── nest-cli.json
|
||||
├── package.json
|
||||
├── tsconfig.json
|
||||
└── src/
|
||||
├── app.module.ts
|
||||
├── main.ts
|
||||
├── common/
|
||||
│ ├── common.module.ts
|
||||
│ ├── audit-log/
|
||||
│ │ ├── audit-log.interceptor.ts
|
||||
│ │ └── audit-log.service.ts
|
||||
│ ├── auth/
|
||||
│ │ ├── auth.controller.ts
|
||||
│ │ ├── auth.module.ts
|
||||
│ │ ├── auth.service.spec.ts
|
||||
│ │ ├── auth.service.ts
|
||||
│ │ ├── decorators/
|
||||
│ │ │ ├── current-user.decorator.ts
|
||||
│ │ │ └── require-permission.decorator.ts
|
||||
│ │ ├── dto/
|
||||
│ │ │ └── login.dto.ts
|
||||
│ │ ├── guards/
|
||||
│ │ │ ├── rbac.guard.spec.ts
|
||||
│ │ │ └── rbac.guard.ts
|
||||
│ │ └── strategies/
|
||||
│ │ └── jwt.strategy.ts
|
||||
│ ├── config/
|
||||
│ │ └── typeorm.config.ts (ตามที่ระบุใน app.module)
|
||||
│ ├── entities/
|
||||
│ │ ├── attachment.entity.ts
|
||||
│ │ ├── audit-log.entity.ts
|
||||
│ │ ├── circulation-action.entity.ts
|
||||
│ │ ├── circulation-assignee.entity.ts
|
||||
│ │ ├── circulation-attachment.entity.ts
|
||||
│ │ ├── circulation-recipient.entity.ts
|
||||
│ │ ├── circulation-status-code.entity.ts
|
||||
│ │ ├── circulation.entity.ts
|
||||
│ │ ├── contract-drawing-attachment.entity.ts
|
||||
│ │ ├── contract-drawing-category.entity.ts
|
||||
│ │ ├── contract-drawing-sub-category.entity.ts
|
||||
│ │ ├── contract-drawing-volume.entity.ts
|
||||
│ │ ├── contract-drawing.entity.ts
|
||||
│ │ ├── correspondence-attachment.entity.ts
|
||||
│ │ ├── correspondence-recipient.entity.ts
|
||||
│ │ ├── correspondence-revision.entity.ts
|
||||
│ │ ├── correspondence-status.entity.ts
|
||||
│ │ ├── correspondence-type.entity.ts
|
||||
│ │ ├── correspondence.entity.ts
|
||||
│ │ ├── document-number-counter.entity.ts
|
||||
│ │ ├── document-number-format.entity.ts
|
||||
│ │ ├── permission.entity.ts
|
||||
│ │ ├── rfa-approve-code.entity.ts
|
||||
│ │ ├── rfa-item.entity.ts
|
||||
│ │ ├── rfa-revision.entity.ts
|
||||
│ │ ├── rfa-status-code.entity.ts
|
||||
│ │ ├── rfa-type.entity.ts
|
||||
│ │ ├── rfa.entity.ts
|
||||
│ │ ├── role.entity.ts
|
||||
│ │ ├── shop-drawing-main-category.entity.ts
|
||||
│ │ ├── shop-drawing-revision-attachment.entity.ts
|
||||
│ │ ├── shop-drawing-revision-contract-ref.entity.ts
|
||||
│ │ ├── shop-drawing-revision.entity.ts
|
||||
│ │ ├── shop-drawing-sub-category.entity.ts
|
||||
│ │ ├── shop-drawing.entity.ts
|
||||
│ │ ├── tag.entity.ts
|
||||
│ │ ├── transmittal-item.entity.ts
|
||||
│ │ ├── transmittal.entity.ts
|
||||
│ │ ├── user.entity.ts
|
||||
│ │ └── views/
|
||||
│ │ ├── view-current-correspondence.entity.ts
|
||||
│ │ └── view-current-rfa.entity.ts
|
||||
│ ├── exceptions/
|
||||
│ │ └── http-exception.filter.ts
|
||||
│ ├── file-storage/
|
||||
│ │ ├── file-storage.service.ts
|
||||
│ │ └── file.controller.ts
|
||||
│ └── security/
|
||||
│ └── rate-limiter.module.ts
|
||||
└── modules/
|
||||
├── caching/
|
||||
│ └── caching.module.ts
|
||||
├── circulation/
|
||||
│ ├── circulation.controller.ts
|
||||
│ ├── circulation.module.ts
|
||||
│ ├── circulation.service.ts
|
||||
│ └── dto/
|
||||
│ ├── add-action.dto.ts
|
||||
│ ├── assignee.dto.ts
|
||||
│ ├── attachment.dto.ts
|
||||
│ ├── create-circulation.dto.ts
|
||||
│ └── recipient.dto.ts
|
||||
├── correspondence/
|
||||
│ ├── correspondence.controller.ts
|
||||
│ ├── correspondence.module.ts
|
||||
│ ├── correspondence.service.ts
|
||||
│ └── dto/
|
||||
│ ├── create-correspondence.dto.ts
|
||||
│ └── query-correspondence.dto.ts
|
||||
├── document-numbering/
|
||||
│ ├── admin-numbering.controller.ts
|
||||
│ ├── document-numbering.module.ts
|
||||
│ ├── document-numbering.service.ts
|
||||
│ └── dto/
|
||||
│ ├── admin-create-number-format.dto.ts
|
||||
│ └── admin-update-number-format.dto.ts
|
||||
├── drawing/
|
||||
│ ├── drawing.controller.ts
|
||||
│ ├── drawing.module.ts
|
||||
│ ├── drawing.service.ts
|
||||
│ └── dto/
|
||||
│ ├── attachment.dto.ts
|
||||
│ ├── create-contract-drawing.dto.ts
|
||||
│ ├── create-shop-drawing-revision.dto.ts
|
||||
│ └── create-shop-drawing.dto.ts
|
||||
├── health/
|
||||
│ ├── health.controller.ts
|
||||
│ └── health.module.ts
|
||||
├── master-data/
|
||||
│ ├── admin-master-data.controller.ts
|
||||
│ ├── master-data.module.ts
|
||||
│ ├── master-data.service.ts
|
||||
│ └── dto/
|
||||
│ └── create-tag.dto.ts
|
||||
├── notification/
|
||||
│ ├── notification.module.ts
|
||||
│ └── notification.service.ts
|
||||
├── project/
|
||||
│ └── (ยังไม่ได้สร้างไฟล์)
|
||||
├── rfa/
|
||||
│ ├── rfa.controller.ts
|
||||
│ ├── rfa.module.ts
|
||||
│ ├── rfa.service.ts
|
||||
│ └── dto/
|
||||
│ └── create-rfa.dto.ts
|
||||
├── search/
|
||||
│ ├── search.controller.ts
|
||||
│ ├── search.module.ts
|
||||
│ ├── search.service.ts
|
||||
│ └── dto/
|
||||
│ └── advanced-search.dto.ts
|
||||
├── transmittal/
|
||||
│ ├── transmittal.controller.ts
|
||||
│ ├── transmittal.module.ts
|
||||
│ ├── transmittal.service.ts
|
||||
│ └── dto/
|
||||
│ ├── create-transmittal-item.dto.ts
|
||||
│ └── create-transmittal.dto.ts
|
||||
└── user/
|
||||
├── admin-roles.controller.ts
|
||||
├── admin-users.controller.ts
|
||||
├── roles.service.ts
|
||||
├── user.module.ts
|
||||
├── user.service.ts
|
||||
└── dto/
|
||||
├── admin-assign-permissions.dto.ts
|
||||
├── admin-create-role.dto.ts
|
||||
└── admin-create-user.dto.ts
|
||||
@@ -1,100 +0,0 @@
|
||||
# **🗂️ สรุปโครงสร้างไฟล์และโฟลเดอร์ (Backend NestJS) \- DMS v1.3.0**
|
||||
|
||||
นี่คือภาพรวมสถาปัตยกรรมโฟลเดอร์ทั้งหมดของโปรเจกต์ NestJS API ที่เราได้พัฒนาขึ้น โดยแบ่งตามหลักการ Separation of Concerns
|
||||
|
||||
## **📂 (Root) โฟลเดอร์หลักของโปรเจกต์**
|
||||
|
||||
ไฟล์เหล่านี้จะอยู่ที่ระดับบนสุดของโปรเจกต์ (นอก src/)
|
||||
|
||||
* Dockerfile
|
||||
* **หน้าที่:** คำสั่งสำหรับ Build Docker Image (Multi-stage build) เพื่อนำไป Deploy
|
||||
* docker-compose.yml
|
||||
* **หน้าที่:** ไฟล์ตั้งค่าสำหรับ QNAP Container Station ใช้กำหนด Services, Networks, และ (สำคัญที่สุด) Environment Variables (เช่น DATABASE\_HOST, JWT\_SECRET)
|
||||
* package.json
|
||||
* **หน้าที่:** เก็บรายการ Dependencies ทั้งหมด (เช่น @nestjs/core, typeorm, bcrypt, @nestjs/elasticsearch)
|
||||
* .gitignore, tsconfig.json, nest-cli.json
|
||||
* **หน้าที่:** ไฟล์ตั้งค่าพื้นฐานของโปรเจกต์ TypeScript และ NestJS
|
||||
|
||||
## **📂 src/ (Source Code)**
|
||||
|
||||
นี่คือหัวใจหลักของแอปพลิเคชัน
|
||||
|
||||
### **1\. src/ (Root Files)**
|
||||
|
||||
* main.ts
|
||||
* **หน้าที่:** จุดเริ่มต้น (Entry Point) ของแอปพลิเคชัน
|
||||
* **การตั้งค่า:**
|
||||
* ใช้ helmet() (Security)
|
||||
* ใช้ RateLimiterGuard (Security)
|
||||
* ใช้ ValidationPipe (Global Pipe สำหรับ DTO)
|
||||
* ใช้ HttpExceptionFilter (Global Filter สำหรับ Error Handling)
|
||||
* ตั้งค่า SwaggerModule (สำหรับสร้าง API Docs)
|
||||
* ตั้งค่า Global Prefix (/api/v1)
|
||||
* app.module.ts
|
||||
* **หน้าที่:** โมดูลหลัก (Root Module)
|
||||
* **การตั้งค่า:**
|
||||
* Import ConfigModule (สำหรับ .env)
|
||||
* Import TypeOrmModule (เชื่อมต่อฐานข้อมูล)
|
||||
* **Import โมดูลหลักและโมดูล Feature ทั้งหมด** (เช่น CommonModule, UserModule, CorrespondenceModule, SearchModule ฯลฯ)
|
||||
|
||||
### **2\. src/common/ (The Foundation)**
|
||||
|
||||
โฟลเดอร์นี้คือ "รากฐาน" เก็บโค้ดที่โมดูลอื่นต้องใช้ร่วมกัน (Shared Logic)
|
||||
|
||||
* common.module.ts
|
||||
* **หน้าที่:** ไฟล์ที่รวบรวมและ export Service/Module ทั้งหมดใน common/ ให้โมดูลอื่นเรียกใช้ง่ายๆ
|
||||
* auth/
|
||||
* **หน้าที่:** จัดการการยืนยันตัวตน (Authentication) และสิทธิ์ (RBAC)
|
||||
* auth.module.ts, auth.service.ts, auth.controller.ts (สำหรับ /login, /me)
|
||||
* strategies/jwt.strategy.ts (ตรรกะการถอดรหัส JWT)
|
||||
* guards/rbac.guard.ts (ตัวตรวจสอบสิทธิ์ RBACGuard)
|
||||
* decorators/require-permission.decorator.ts (@RequirePermission)
|
||||
* decorators/current-user.decorator.ts (@CurrentUser)
|
||||
* entities/
|
||||
* **หน้าที่:** **ศูนย์รวม Entities ทั้งหมด (44+ ไฟล์)** ที่ Map กับตารางใน MariaDB
|
||||
* เช่น user.entity.ts, role.entity.ts, correspondence.entity.ts, rfa.entity.ts, shop-drawing.entity.ts, circulation.entity.ts, attachment.entity.ts, audit-log.entity.ts ฯลฯ
|
||||
* views/: โฟลเดอร์ย่อยสำหรับ View Entities (เช่น view-current-correspondence.entity.ts)
|
||||
* config/
|
||||
* **หน้าที่:** เก็บ Config ที่ซับซ้อน เช่น typeorm.config.ts (ตั้งค่าการเชื่อมต่อ TypeORM)
|
||||
* exceptions/
|
||||
* http-exception.filter.ts (Global Error Handler)
|
||||
* file-storage/
|
||||
* file-storage.service.ts (Logic การบันทึกไฟล์ลง Disk)
|
||||
* file.controller.ts (API Endpoint /files/upload)
|
||||
* audit-log/
|
||||
* audit-log.service.ts (Service บันทึก Log)
|
||||
* audit-log.interceptor.ts (Interceptor ดักจับการกระทำ)
|
||||
* security/
|
||||
* rate-limiter.module.ts (ตั้งค่า Rate Limiting)
|
||||
|
||||
### **3\. src/modules/ (The Features)**
|
||||
|
||||
โฟลเดอร์นี้เก็บ "Business Logic" โดยแยกเป็น Feature Modules อย่างชัดเจน
|
||||
|
||||
* user/ (Phase 1\)
|
||||
* **หน้าที่:** API สำหรับ Admin Panel (จัดการ Users, Roles, Permissions)
|
||||
* admin-users.controller.ts, admin-roles.controller.ts, user.service.ts, roles.service.ts
|
||||
* project/ (Phase 2\)
|
||||
* **หน้าที่:** API สำหรับจัดการโปรเจกต์ (ยังไม่ได้สร้างไฟล์ แต่มีในแผน)
|
||||
* document-numbering/ (Phase 2\)
|
||||
* **หน้าที่:** API สำหรับ Admin (ตั้งค่า Format เลขที่) และ Service (generateNextDocumentNumber)
|
||||
* master-data/ (Phase 2\)
|
||||
* **หน้าที่:** API สำหรับ Admin (จัดการ Master Data เช่น Tags, RFA Types, Corr. Types)
|
||||
* correspondence/ (Phase 2\)
|
||||
* **หน้าที่:** Logic หลักในการสร้าง/ค้นหา เอกสารโต้ตอบ
|
||||
* rfa/ (Phase 3\)
|
||||
* **หน้าที่:** Logic หลักในการสร้าง/ค้นหา เอกสารขออนุมัติ (RFA)
|
||||
* drawing/ (Phase 3\)
|
||||
* **หน้าที่:** Logic หลักในการสร้าง/ค้นหา แบบแปลน (Contract & Shop Drawing)
|
||||
* circulation/ (Phase 3\)
|
||||
* **หน้าที่:** Logic หลักในการสร้าง/ค้นหา ใบเวียนภายใน
|
||||
* transmittal/ (Phase 3\)
|
||||
* **หน้าที่:** Logic หลักในการสร้าง/ค้นหา เอกสารนำส่ง
|
||||
* search/ (Phase 4\)
|
||||
* **หน้าที่:** API (/search) และ Service ที่เชื่อมต่อกับ Elasticsearch
|
||||
* notification/ (Phase 4\)
|
||||
* **หน้าที่:** Service สำหรับยิง Webhook ไปยัง N8N
|
||||
* caching/ (Phase 4\)
|
||||
* **หน้าที่:** โมดูลตั้งค่า Caching (Global)
|
||||
* health/ (Phase 5 \- Deploy)
|
||||
* **หน้าที่:** API (/health) สำหรับ Health Check
|
||||
436
docs/T0-T6.2.md
436
docs/T0-T6.2.md
@@ -1,436 +0,0 @@
|
||||
# โครงสร้างโฟลเดอร์และไฟล์ทั้งหมดสำหรับ **Backend (NestJS)** ตามแผนงาน **LCBP3-DMS v1.4.3** ตั้งแต่ Phase 0 ถึง Phase 6 (T0-T6.2) ที่ได้ดำเนินการไปแล้ว
|
||||
|
||||
โครงสร้างนี้ออกแบบตามหลัก **Domain-Driven Design** และ **Modular Architecture** ที่ระบุไว้ในแผนพัฒนา
|
||||
|
||||
---
|
||||
|
||||
## 📂 **backend/** (Backend Application)
|
||||
|
||||
* [x] `.env` (สำหรับ Local Dev เท่านั้น ห้าม commit)
|
||||
* [x] `.gitignore`
|
||||
* [x] `docker-compose.yml` (Configuration หลักสำหรับ Deploy)
|
||||
* [x] `docker-compose.override.yml` (สำหรับ Inject Secrets ตอน Dev)
|
||||
* [x] `package.json`
|
||||
* [x] `pnpm-lock.yaml`
|
||||
* [x] `tsconfig.json`
|
||||
* [x] `nest-cli.json`
|
||||
* [x] `README.md`
|
||||
|
||||
---
|
||||
|
||||
## 📂 **backend/src/** (Source Code)
|
||||
|
||||
### **📄 Entry Points**
|
||||
|
||||
* [x] `main.ts` (Application Bootstrap, Swagger, Global Pipes)
|
||||
* [x] `app.module.ts` (Root Module ที่รวมทุก Modules เข้าด้วยกัน)
|
||||
|
||||
### **📁 src/common/** (Shared Resources)
|
||||
|
||||
* [x] **common.module.ts**
|
||||
* **auth/**
|
||||
* **dto/**
|
||||
* [x] **login.dto.ts**
|
||||
* [x] **register.dto.ts**
|
||||
* [x] **auth.controller.spec.ts**
|
||||
* [x] **auth.controller.ts**
|
||||
* [x] **auth.module.ts**
|
||||
* [x] **auth.service.spec.ts**
|
||||
* [x] **auth.service.ts**
|
||||
* **config/** (Configuration Service)
|
||||
* [x] **env.validation.ts**
|
||||
* **decorators/**
|
||||
* [x] **audit.decorator.ts**
|
||||
* [x] `current-user.decorator.ts`
|
||||
* [x] `require-permission.decorator.ts`
|
||||
* **entities/**
|
||||
* [x] **audit-log.entity.ts**
|
||||
* [x] **base.entity.ts**
|
||||
* **exceptions/**
|
||||
* [x] `http-exception.filter.ts` (Global Filter)
|
||||
* **file-storage/** (Two-Phase Storage System)
|
||||
* **entities/**
|
||||
* [x] **attachment.entity.ts**
|
||||
* [x] **file-storage.controller.spec.ts**
|
||||
* [x] **file-storage.controller.ts**
|
||||
* [x] **file-storage.module.ts**
|
||||
* [x] **file-storage.service.spec.ts**
|
||||
* [x] `file-storage.service.ts` (Upload, Scan Virus, Commit)
|
||||
* [x] `guards/`
|
||||
* [x] `jwt-auth.guard.ts`
|
||||
* [x] **jwt.strategy.ts**
|
||||
* [x] `rbac.guard.ts` (ตรวจสอบสิทธิ์ 4 ระดับ)
|
||||
* **interceptors/**
|
||||
* [x] `audit-log.interceptor.ts` (เก็บ Log ลง DB)
|
||||
* [x] `transform.interceptor.ts` (Standard Response Format)
|
||||
* **resilience/** (Circuit Breaker & Retry)
|
||||
|
||||
### **📁 src/modules/** (Feature Modules)
|
||||
|
||||
1. **user/** (User Management & RBAC)
|
||||
* [x] `dto/`
|
||||
* [x] **assign-user-role.dto.ts**
|
||||
* [x] `create-user.dto.ts`
|
||||
* [x] `update-user.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `user.entity.ts`
|
||||
* [x] `role.entity.ts`
|
||||
* [x] `permission.entity.ts`
|
||||
* [x] `user-preference.entity.ts`
|
||||
* [x] **user-assignment.service.ts**
|
||||
* [x] `user.controller.ts`
|
||||
* [x] `user.module.ts`
|
||||
* [x] `user.service.ts`
|
||||
* [x] **user.service.spec.ts**
|
||||
|
||||
2. **project/** (Project Structure)
|
||||
* [x] `dto/`
|
||||
* [x] **create-project.dto.ts**
|
||||
* [x] `search-project.dto.ts`
|
||||
* [x] `update-project.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `contract-organization.entity.ts`
|
||||
* [x] `contract.entity.ts`
|
||||
* [x] `organization.entity.ts`
|
||||
* [x] `project-organization.entity.ts` (Junction)
|
||||
* [x] **project.entity.ts**
|
||||
* [x] **project.controller.spec.ts**
|
||||
* [x] `project.controller.ts`
|
||||
* [x] `project.module.ts`
|
||||
* [x] **project.service.spec.ts**
|
||||
* [x] `project.service.ts`
|
||||
|
||||
3. **correspondence/** (Core Document System)
|
||||
* [x] `dto/`
|
||||
* [x] `add-reference.dto.ts`
|
||||
* [x] `create-correspondence.dto.ts`
|
||||
* [x] `search-correspondence.dto.ts`
|
||||
* [x] **submit-correspondence.dto.ts**
|
||||
* [x] `workflow-action.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `correspondence-reference.entity.ts`
|
||||
* [x] `correspondence-revision.entity.ts`
|
||||
* [x] `correspondence-routing.entity.ts` (Unified Workflow)
|
||||
* [x] `correspondence-status.entity.ts`
|
||||
* [x] `correspondence-type.entity.ts`
|
||||
* [x] `correspondence.entity.ts`
|
||||
* [x] **routing-template-step.entity.ts**
|
||||
* [x] `routing-template.entity.ts`
|
||||
* [x] **correspondence.controller.spec.ts**
|
||||
* [x] `correspondence.controller.ts`
|
||||
* [x] `correspondence.module.ts`
|
||||
* [x] **correspondence.service.spec.ts**
|
||||
* [x] `correspondence.service.ts` (Impersonation & Workflow Logic)
|
||||
|
||||
4. **drawing/** (Contract & Shop Drawings)
|
||||
* [x] `dto/`
|
||||
* [x] `create-contract-drawing.dto.ts`
|
||||
* [x] `create-shop-drawing-revision.dto.ts`
|
||||
* [x] `create-shop-drawing.dto.ts`
|
||||
* [x] `search-contract-drawing.dto.ts`
|
||||
* [x] `search-shop-drawing.dto.ts`
|
||||
* [x] `update-contract-drawing.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `contract-drawing-sub-category.entity.ts`
|
||||
* [x] `contract-drawing-volume.entity.ts`
|
||||
* [x] `contract-drawing.entity.ts`
|
||||
* [x] `shop-drawing-main-category.entity.ts`
|
||||
* [x] `shop-drawing-revision.entity.ts`
|
||||
* [x] `shop-drawing-sub-category.entity.ts`
|
||||
* [x] `shop-drawing.entity.ts`
|
||||
* [x] `contract-drawing.controller.ts`
|
||||
* [x] `contract-drawing.service.ts`
|
||||
* [x] `drawing-master-data.controller.ts`
|
||||
* [x] `drawing-master-data.service.ts`
|
||||
* [x] `drawing.module.ts`
|
||||
* [x] `shop-drawing.controller.ts`
|
||||
* [x] `shop-drawing.service.ts`
|
||||
|
||||
4. **rfa/** (Request for Approval & Advanced Workflow)
|
||||
* [x] `dto/`
|
||||
* [x] `create-rfa.dto.ts`
|
||||
* [x] `search-rfa.dto.ts`
|
||||
* [x] `update-rfa.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `rfa-approve-code.entity.ts`
|
||||
* [x] `rfa-item.entity.ts`
|
||||
* [x] `rfa-revision.entity.ts`
|
||||
* [x] `rfa-status-code.entity.ts`
|
||||
* [x] `rfa-type.entity.ts`
|
||||
* [x] `rfa-workflow-template-step.entity.ts`
|
||||
* [x] `rfa-workflow-template.entity.ts`
|
||||
* [x] `rfa-workflow.entity.ts`
|
||||
* [x] `rfa.entity.ts`
|
||||
* [x] `rfa.controller.ts`
|
||||
* [x] `rfa.module.ts`
|
||||
* [x] `rfa.service.ts` (Unified Workflow Integration)
|
||||
|
||||
5. **circulation/** (Internal Routing)
|
||||
* [x] `dto/`
|
||||
* [x] `create-circulation.dto.ts`
|
||||
* [x] `update-circulation-routing.dto.ts`
|
||||
* [x] `search-circulation.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `circulation-routing.entity.ts`
|
||||
* [x] `circulation-status-code.entity.ts`
|
||||
* [x] `circulation.entity.ts`
|
||||
* [x] `circulation.controller.ts`
|
||||
* [x] `circulation.module.ts`
|
||||
* [x] `circulation.service.ts`
|
||||
|
||||
6. **transmittal/** (Document Forwarding)
|
||||
* [x] `dto/`
|
||||
* [x] `create-transmittal.dto.ts`
|
||||
* [x] `search-transmittal.dto.ts`
|
||||
* [x] **update-transmittal.dto.ts**
|
||||
* [x] `entities/`
|
||||
* [x] `transmittal-item.entity.ts`
|
||||
* [x] `transmittal.entity.ts`
|
||||
* [x] `transmittal.controller.ts`
|
||||
* [x] `transmittal.module.ts`
|
||||
* [x] `transmittal.service.ts`
|
||||
|
||||
7. **notification/** (System Alerts)
|
||||
* [x] `dto/`
|
||||
* [x] `create-notification.dto.ts`
|
||||
* [x] `search-notification.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `notification.entity.ts`
|
||||
* [x] `notification-cleanup.service.ts` (Cron Job)
|
||||
* [x] `notification.controller.ts`
|
||||
* [x] `notification.gateway.ts`
|
||||
* [x] `notification.module.ts` (Real-time WebSocket)
|
||||
* [x] `notification.processor.ts` (Consumer/Worker for Email & Line)
|
||||
* [x] `notification.service.ts` (Producer)
|
||||
|
||||
8. **search/** (Elasticsearch)
|
||||
* [x] `dto/search-query.dto.ts`
|
||||
* [x] `search.controller.ts`
|
||||
* [x] `search.module.ts`
|
||||
* [x] `search.service.ts` (Indexing & Searching)
|
||||
|
||||
9. **document-numbering/** (Internal Service)
|
||||
* [x] `entities/`
|
||||
* [x] `document-number-format.entity.ts`
|
||||
* [x] `document-number-counter.entity.ts`
|
||||
* [x] `document-numbering.module.ts`
|
||||
* [x] **document-numbering.service.spec.ts**
|
||||
* [x] `document-numbering.service.ts` (Double-Lock Mechanism)
|
||||
|
||||
10. **workflow-engine/** (Unified Logic)
|
||||
* [x] `interfaces/workflow.interface.ts`
|
||||
* [x] `workflow-engine.module.ts`
|
||||
* [x] **workflow-engine.service.spec.ts**
|
||||
* [x] `workflow-engine.service.ts` (State Machine Logic)
|
||||
|
||||
11. **json-schema/** (Validation)
|
||||
* [x] `dto/`
|
||||
* [x] `create-json-schema.dto.ts`+
|
||||
* [x] `search-json-schema.dto.ts`
|
||||
* [x] `update-json-schema.dto.ts`
|
||||
* [x] `entities/`
|
||||
* [x] `json-schema.entity.ts`
|
||||
* [x] **json-schema.controller.spec.ts**
|
||||
* [x] **json-schema.controller.ts**
|
||||
* [x] `json-schema.module.ts`
|
||||
* [x] **json-schema.service.spec.ts**
|
||||
* [x] `json-schema.service.ts`
|
||||
|
||||
---
|
||||
|
||||
นี่คือโครงสร้าง **Folder Structure** ของโปรเจกต์ Backend (NestJS) ฉบับสมบูรณ์ ที่รวบรวมจาก **Requirements**, **FullStackJS Guidelines**, **Data Dictionary** และสิ่งที่เราได้ **Implement ไปแล้วตั้งแต่ Phase 0 ถึง Phase 6.2** ครับ
|
||||
|
||||
โครงสร้างนี้เป็นแบบ **Domain-Driven Modular Architecture** ครับ
|
||||
|
||||
```text
|
||||
lcbp3-backend/
|
||||
├── .env # Environment variables (Local Dev)
|
||||
├── .gitignore
|
||||
├── docker-compose.yml # Main config for services (DB, Redis, ES, App)
|
||||
├── docker-compose.override.yml # Secrets injection for Dev
|
||||
├── nest-cli.json
|
||||
├── package.json
|
||||
├── pnpm-lock.yaml
|
||||
├── README.md
|
||||
├── tsconfig.json
|
||||
│
|
||||
└── src/
|
||||
├── main.ts # Entry point (Swagger, Helmet, Global Pipes)
|
||||
├── app.module.ts # Root Module
|
||||
│
|
||||
├── common/ # 🛠️ Shared Resources
|
||||
│ ├── auth/
|
||||
│ │ ├── guards/
|
||||
│ │ │ ├── jwt-auth.guard.ts
|
||||
│ │ │ └── rbac.guard.ts
|
||||
│ │ └── strategies/
|
||||
│ │ └── jwt.strategy.ts
|
||||
│ ├── config/ # Config Service
|
||||
│ ├── decorators/
|
||||
│ │ ├── current-user.decorator.ts
|
||||
│ │ └── require-permission.decorator.ts
|
||||
│ ├── exceptions/
|
||||
│ │ └── http-exception.filter.ts
|
||||
│ ├── file-storage/ # 📂 Two-Phase Storage System
|
||||
│ │ ├── dto/
|
||||
│ │ ├── entities/
|
||||
│ │ │ └── attachment.entity.ts
|
||||
│ │ ├── file-storage.module.ts
|
||||
│ │ └── file-storage.service.ts
|
||||
│ ├── interceptors/
|
||||
│ │ ├── audit-log.interceptor.ts
|
||||
│ │ └── transform.interceptor.ts
|
||||
│ └── resilience/ # Circuit Breaker & Retry logic
|
||||
│
|
||||
└── modules/ # 📦 Feature Modules
|
||||
│
|
||||
├── user/ # 👤 User & RBAC
|
||||
│ ├── dto/
|
||||
│ │ ├── create-user.dto.ts
|
||||
│ │ └── update-user.dto.ts
|
||||
│ ├── entities/
|
||||
│ │ ├── permission.entity.ts
|
||||
│ │ ├── role.entity.ts
|
||||
│ │ ├── user-preference.entity.ts
|
||||
│ │ └── user.entity.ts
|
||||
│ ├── user.controller.ts
|
||||
│ ├── user.module.ts
|
||||
│ └── user.service.ts
|
||||
│
|
||||
├── project/ # 🏢 Projects & Organizations
|
||||
│ ├── 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.ts
|
||||
│ ├── project.module.ts
|
||||
│ └── project.service.ts
|
||||
│
|
||||
├── correspondence/ # ✉️ Core Document
|
||||
│ ├── dto/
|
||||
│ │ ├── add-reference.dto.ts
|
||||
│ │ ├── create-correspondence.dto.ts
|
||||
│ │ ├── search-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-type.entity.ts
|
||||
│ │ ├── correspondence.entity.ts
|
||||
│ │ └── routing-template.entity.ts
|
||||
│ ├── correspondence.controller.ts
|
||||
│ ├── correspondence.module.ts
|
||||
│ └── correspondence.service.ts
|
||||
│
|
||||
├── drawing/ # 📐 Shop & Contract Drawings
|
||||
│ ├── 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
|
||||
│
|
||||
├── rfa/ # ✅ Request for Approval
|
||||
│ ├── 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
|
||||
│
|
||||
├── circulation/ # 🔄 Internal Routing
|
||||
│ ├── 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
|
||||
│
|
||||
├── transmittal/ # 📤 Outgoing Documents
|
||||
│ ├── dto/
|
||||
│ │ ├── create-transmittal.dto.ts
|
||||
│ │ └── search-transmittal.dto.ts
|
||||
│ ├── entities/
|
||||
│ │ ├── transmittal-item.entity.ts
|
||||
│ │ └── transmittal.entity.ts
|
||||
│ ├── transmittal.controller.ts
|
||||
│ ├── transmittal.module.ts
|
||||
│ └── transmittal.service.ts
|
||||
│
|
||||
├── notification/ # 🔔 Real-time & Queue
|
||||
│ ├── 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
|
||||
│
|
||||
├── search/ # 🔍 Elasticsearch
|
||||
│ ├── dto/
|
||||
│ │ └── search-query.dto.ts
|
||||
│ ├── search.controller.ts
|
||||
│ ├── search.module.ts
|
||||
│ └── search.service.ts
|
||||
│
|
||||
├── document-numbering/ # 🔢 Internal Numbering Service
|
||||
│ ├── entities/
|
||||
│ │ ├── document-number-counter.entity.ts
|
||||
│ │ └── document-number-format.entity.ts
|
||||
│ ├── document-numbering.module.ts
|
||||
│ └── document-numbering.service.ts
|
||||
│
|
||||
├── workflow-engine/ # ⚙️ Unified Logic
|
||||
│ ├── interfaces/
|
||||
│ │ └── workflow.interface.ts
|
||||
│ ├── workflow-engine.module.ts
|
||||
│ └── workflow-engine.service.ts
|
||||
│
|
||||
└── json-schema/ # 📋 Validation Logic
|
||||
├── json-schema.module.ts
|
||||
└── json-schema.service.ts
|
||||
```
|
||||
367
docs/prompt.md
367
docs/prompt.md
@@ -4,7 +4,7 @@
|
||||
|
||||
## VSCode Shortcut
|
||||
|
||||
Markdown preview Ctrl+Shift+V
|
||||
Markdown preview Ctrl+Shift+V
|
||||
|
||||
## สร้างโครงสร้างโฟลเดอร์สำหรับ lcbp3-backend
|
||||
|
||||
@@ -80,14 +80,16 @@ git push -u origin main
|
||||
|
||||
## **สร้าง NestJS Project ใหม่**
|
||||
|
||||
* ขั้นตอนที่ 1: ติดตั้ง NestJS CLI (ถ้ายังไม่ได้ติดตั้ง)
|
||||
* npm install -g @nestjs/cli
|
||||
- ขั้นตอนที่ 1: ติดตั้ง NestJS CLI (ถ้ายังไม่ได้ติดตั้ง)
|
||||
|
||||
* ขั้นตอนที่ 2: สร้างโปรเจกต์ใหม่
|
||||
* nest new backend
|
||||
* nest new . /อยู่ในโฟลเดอร์ที่สร้างไว้แล้ว และต้องการสร้างโปรเจกต์ลงในโฟลเดอร์นั้นโดยตรง:
|
||||
- npm install -g @nestjs/cli
|
||||
|
||||
* ขั้นตอนที่ 3: ติดตั้ง Dependencies เพิ่มเติมสำหรับ DMS
|
||||
- ขั้นตอนที่ 2: สร้างโปรเจกต์ใหม่
|
||||
|
||||
- nest new backend
|
||||
- nest new . /อยู่ในโฟลเดอร์ที่สร้างไว้แล้ว และต้องการสร้างโปรเจกต์ลงในโฟลเดอร์นั้นโดยตรง:
|
||||
|
||||
- ขั้นตอนที่ 3: ติดตั้ง Dependencies เพิ่มเติมสำหรับ DMS
|
||||
|
||||
```bash
|
||||
# Core & Database
|
||||
@@ -123,302 +125,143 @@ npm install --save-dev @nestjs/testing jest @types/jest @types/passport-jwt @typ
|
||||
การควบคุมพฤติกรรมของผมต้องทำผ่าน **"คำสั่ง (Prompt)"** ที่คุณพิมพ์เข้ามาเท่านั้นครับ เพื่อป้องกันไม่ให้เกิดเหตุการณ์แบบนี้อีก คุณสามารถใช้เทคนิคการสั่งงานดังนี้เพื่อ **"บังคับ"** ให้ผมตรวจสอบข้อมูลครับ:
|
||||
|
||||
1. **สั่งให้ "อ่านและสรุปก่อน" (Verify First):**
|
||||
* ก่อนให้ผมเขียนโค้ด ให้สั่งว่า *"ช่วยอ่านไฟล์ `01_lcbp3_v1_4_3.sql` แล้วสรุปโครงสร้างตาราง audit_logs ที่มีอยู่จริงให้ดูก่อน"*
|
||||
* วิธีนี้จะบังคับให้ผมต้องไปดึงข้อมูลจากไฟล์มาประมวลผลก่อนที่จะเริ่ม "จินตนาการ" หรือเขียนโค้ดใหม่ครับ
|
||||
|
||||
- ก่อนให้ผมเขียนโค้ด ให้สั่งว่า _"ช่วยอ่านไฟล์ `01_lcbp3_v1_4_3.sql` แล้วสรุปโครงสร้างตาราง audit_logs ที่มีอยู่จริงให้ดูก่อน"_
|
||||
- วิธีนี้จะบังคับให้ผมต้องไปดึงข้อมูลจากไฟล์มาประมวลผลก่อนที่จะเริ่ม "จินตนาการ" หรือเขียนโค้ดใหม่ครับ
|
||||
|
||||
2. **ใช้คำสั่ง "ห้ามเดา" (Strict Constraints):**
|
||||
* เติมประโยคท้ายคำสั่งว่า *"ห้ามใช้โครงสร้างสมมติ ให้ยึดตามไฟล์แนบ `ชื่อไฟล์` เท่านั้น หากไม่เจอข้อมูลให้ถามกลับ"*
|
||||
|
||||
- เติมประโยคท้ายคำสั่งว่า _"ห้ามใช้โครงสร้างสมมติ ให้ยึดตามไฟล์แนบ `ชื่อไฟล์` เท่านั้น หากไม่เจอข้อมูลให้ถามกลับ"_
|
||||
|
||||
3. **อ้างอิงชื่อไฟล์ชัดเจน:**
|
||||
* ระบุชื่อไฟล์ในคำสั่งเสมอ เช่น *"อ้างอิงจาก `Backend Plan`..."* หรือ *"ตาม `Data Dictionary`..."* (ซึ่งคุณทำได้ดีแล้วครับ แต่ผมประมวลผลพลาดเองในรอบนี้)
|
||||
- ระบุชื่อไฟล์ในคำสั่งเสมอ เช่น _"อ้างอิงจาก `Backend Plan`..."_ หรือ _"ตาม `Data Dictionary`..."_ (ซึ่งคุณทำได้ดีแล้วครับ แต่ผมประมวลผลพลาดเองในรอบนี้)
|
||||
|
||||
**สรุป:**
|
||||
คุณไม่ต้องไปตั้งค่าที่ไหนครับ แต่ใช้การ **"สั่งให้ผมทวนข้อมูลจากไฟล์"** เป็นขั้นตอนแรกก่อนเริ่มงาน จะช่วยลดโอกาสผิดพลาดได้มากที่สุดครับ
|
||||
|
||||
ตอนนี้ผมพร้อมทำงานต่อโดยยึดข้อมูลจากไฟล์ `01_lcbp3_v1_4_3.sql` และไฟล์อื่นๆ ที่คุณให้มาอย่างเคร่งครัดครับ มีส่วนไหนให้ผมดำเนินการต่อไหมครับ?
|
||||
|
||||
ช่วยอ่านไฟล์ 2_Backend_Plan_V1_4_4.md ในหัวข้อ T2.5.1-T2.5.9 และ โครงสร้างโมดูล แล้วสรุป รายการที่ต้องการสร้างหรือแก้ไขโมดูล ในแต่ละข้อก่อนเริ่มดำเนินการต่อครับ
|
||||
|
||||
สวัสดีครับ ผมได้ทำการตรวจสอบและวิเคราะห์ความครบถ้วนของไฟล์เอกสารแผนงาน (Plans), โครงสร้างฐานข้อมูล (SQL), และโครงสร้างไฟล์ (File Structure) เทียบกับ Requirements ของ **LCBP3-DMS v1.4.3** เรียบร้อยแล้วครับ
|
||||
สวัสดีครับ ผมได้วิเคราะห์ไฟล์ **2_Backend_Plan_V1_4_4.md** ในส่วนของ **Phase 2: High-Integrity Data & File Management** โดยเฉพาะหัวข้อ **T2.5 JSON Details & Schema Management** (T2.5.1 - T2.5.9) เรียบร้อยแล้วครับ
|
||||
|
||||
ในฐานะ Programmer ผู้เชี่ยวชาญ ผมขอสรุปผลการ Review และคำแนะนำตามลำดับความสำคัญดังนี้ครับ:
|
||||
นี่คือสรุปรายการโมดูล, ไฟล์ และฟังก์ชันที่ต้องสร้างหรือแก้ไข เพื่อให้ระบบจัดการ JSON Schema สมบูรณ์และมีประสิทธิภาพสูงสุดครับ
|
||||
|
||||
---
|
||||
|
||||
### 📊 **1. บทสรุปภาพรวม (Executive Summary)**
|
||||
### 📂 1. โครงสร้างโมดูลที่ต้องสร้าง (Module Structure)
|
||||
|
||||
จากการตรวจสอบเปรียบเทียบ:
|
||||
1. **Database Schema (`01_lcbp3_v1_4_3.sql`)**: **ครบถ้วนสมบูรณ์มาก (95%)** รองรับฟีเจอร์ระดับ Advance ตาม Requirements v1.4.3 ครบถ้วน เช่น Partitioning, Virtual Columns, Versioning (Optimistic Lock) และ Table Structure สำหรับ Workflow/Files
|
||||
2. **Backend Folder Structure (`5_Backend_Folder_V1_4_3.md`)**: **ครบถ้วนตามแผนงาน (90%)** มีการจัดวาง Module ตามหลัก Domain-Driven Design และมีไฟล์สำคัญๆ ครบตาม Plan
|
||||
เราจำเป็นต้องสร้าง `JsonSchemaModule` ขึ้นมาใหม่ โดยภายในจะประกอบด้วย Services ย่อยตามหน้าที่งาน เพื่อให้เป็นไปตามหลัก Single Responsibility Principle ครับ
|
||||
|
||||
**จุดที่ต้องเน้นย้ำ:** โครงสร้างไฟล์ (File Structure) มีไฟล์ `.ts` ปรากฏอยู่ครบ แต่สิ่งที่ผม "มองไม่เห็น" คือ **เนื้อหาโค้ดภายใน (Internal Logic)** ว่าได้ Implement ตาม Logic ที่ซับซ้อน (เช่น Redlock, Two-Phase Commit, DSL Parser) ไว้ถูกต้องหรือไม่
|
||||
**Path:** `src/modules/json-schema/`
|
||||
|
||||
---
|
||||
|
||||
### 🧐 **2. การตรวจสอบรายละเอียด (Detailed Review)**
|
||||
|
||||
#### **✅ ส่วนที่มีความครบถ้วนสมบูรณ์ (Well-Implemented Areas)**
|
||||
|
||||
1. **Database Partitioning & Optimization:**
|
||||
* SQL มีการทำ `PARTITION BY RANGE` ให้กับ `audit_logs` และ `notifications` แล้ว (ตาม Req 6.2)
|
||||
* มี `VIRTUAL COLUMNS` สำหรับดึงค่า JSON มาทำ Index (ตาม Req 3.11.3)
|
||||
2. **Security & Identity:**
|
||||
* ตาราง `users`, `roles`, `permissions`, `user_assignments` รองรับ RBAC 4 ระดับ (Global, Org, Project, Contract) ครบถ้วน
|
||||
* Folder Structure มี `auth`, `guards`, `decorators` ที่จำเป็นครบ (`jwt-auth.guard.ts`, `rbac.guard.ts`, `require-permission.decorator.ts`)
|
||||
3. **File Management:**
|
||||
* SQL มีตาราง `attachments` ที่มี field `is_temporary`, `temp_id`, `checksum` รองรับ Two-Phase Storage
|
||||
* Folder มี `file-storage` module และ `file-cleanup.service.ts` (Cron job) เตรียมไว้แล้ว
|
||||
4. **Workflow & RFA:**
|
||||
* SQL มีตารางเก็บ Template และ Instance (`..._routings`, `..._workflows`) แยกกันชัดเจน และมี field `workflow_config` (JSON) สำหรับ DSL
|
||||
* Folder มี `workflow-engine` module และ `workflow-dsl.service.ts` (ตาม Phase 6A)
|
||||
|
||||
---
|
||||
|
||||
#### ⚠️ **3. Gap Analysis & Missing Parts (สิ่งที่อาจขาดหายไปหรือต้องตรวจสอบ)**
|
||||
|
||||
แม้ชื่อไฟล์จะครบ แต่จากประสบการณ์ มีจุดที่มักจะหลุด หรือต้องเขียน Logic ให้รัดกุมมากในไฟล์ที่มีอยู่ ดังนี้:
|
||||
|
||||
**Phase 2: High-Integrity Data (File & Numbering)**
|
||||
* **Redis/Redlock Integration:** ในไฟล์ `document-numbering.service.ts` ต้องตรวจสอบว่ามีการเรียกใช้ Redis Distributed Lock จริงหรือไม่ (ไม่ใช่แค่ Database Lock)
|
||||
* **Transaction Safety:** ใน `file-storage.service.ts` ต้องมั่นใจว่า Logic การ Commit file (ย้ายจาก temp -> permanent) อยู่ภายใน Database Transaction เดียวกับการบันทึกข้อมูลเอกสาร
|
||||
|
||||
**Phase 3 & 6A: Workflow Engine**
|
||||
* **DSL Interpreter:** ไฟล์ `workflow-dsl.service.ts` มีอยู่แล้ว แต่ต้องตรวจสอบว่าสามารถ **Parse** JSON/YAML จากตาราง `rfa_workflow_templates` แล้วแปลงเป็น Execution Logic ได้จริงหรือไม่
|
||||
* **State Machine Consistency:** การเชื่อมต่อระหว่าง `CorrespondenceModule` กับ `WorkflowEngine` ต้องแน่ใจว่าเมื่อ State เปลี่ยนใน Engine ค่าใน Database (Status Column) ถูกอัปเดตตรงกัน
|
||||
|
||||
**Phase 6: Search & Notification**
|
||||
* **Elasticsearch Sync:** ใน `search.service.ts` ต้องมี Logic การ Sync ข้อมูล (อาจผ่าน Event Emitter หรือ TypeORM Subscriber) เมื่อมีการ Create/Update เอกสาร
|
||||
* **Notification Digest:** ใน `notification.processor.ts` (Consumer) ต้องมี Logic การหน่วงเวลา (Debounce/Throttle) เพื่อรวม Notification ก่อนส่ง (ตาม Req 6.8.2)
|
||||
|
||||
---
|
||||
|
||||
### 🚀 **4. คำแนะนำและสิ่งที่ต้องทำต่อ (Recommendations by Phase)**
|
||||
|
||||
เพื่อให้งานสมบูรณ์แบบ ผมขอแนะนำให้ดำเนินการตรวจสอบหรือ Implement ส่วนที่เหลือตามลำดับดังนี้:
|
||||
|
||||
#### **Phase 0-1: Infrastructure & Auth (Priority: Critical)**
|
||||
* **[Audit] Database Migration:** แม้จะมีไฟล์ `.sql` รวม แต่ใน NestJS ควรมีระบบ Migration (TypeORM Migrations) เพื่อจัดการการเปลี่ยนแปลง Schema ในอนาคต (ถ้ายังไม่มี ควรสร้าง `src/database/migrations/*` จาก SQL ที่มี)
|
||||
* **[Code Check] Config Validation:** ตรวจสอบ `src/common/config/env.validation.ts` ว่ามีการ Validate ตัวแปรระบบครบทุกตัว (Database, Redis, JWT Secrets) ตาม Req 2.2 หรือไม่
|
||||
|
||||
#### **Phase 2: Core Data Services (Priority: High)**
|
||||
* **[Code Check] Document Numbering:** เปิดไฟล์ `src/modules/document-numbering/document-numbering.service.ts` ตรวจสอบ Logic **Double-Lock** (Redis + Optimistic Lock) ว่า Implement ถูกต้องตาม Plan T2.3 หรือไม่
|
||||
* **[Code Check] File Storage:** ตรวจสอบ `src/common/file-storage/file-storage.service.ts` ว่ามีการทำ **Virus Scan (ClamAV)** ก่อน save ลง Temp หรือไม่
|
||||
|
||||
#### **Phase 3 & 6A: Workflow Engine (Priority: Medium-High)**
|
||||
* **[Task] Workflow Integration:** ตรวจสอบว่า `CorrespondenceService` และ `RfaService` มีการเรียกใช้ `WorkflowEngineService` แทนที่จะเขียน Logic if-else hardcode หรือไม่
|
||||
* **[Task] DSL Loader:** ตรวจสอบว่ามี Seed Data สำหรับ `rfa_workflow_templates` ที่เป็น JSON Config เริ่มต้นแล้วหรือยัง
|
||||
|
||||
#### **Phase 6: Background Services (Priority: Medium)**
|
||||
* **[Task] Queue Setup:** ตรวจสอบ `notification.module.ts` ว่ามีการ Register BullMQ (`BullModule.registerQueue`) แล้วหรือยัง
|
||||
* **[Task] Cron Jobs:** ตรวจสอบ `app.module.ts` ว่ามีการ Import `ScheduleModule.forRoot()` เพื่อให้ `file-cleanup.service.ts` ทำงานได้จริง
|
||||
|
||||
---
|
||||
|
||||
### 🛠️ **5. Next Step Action (สิ่งที่คุณทำได้เลย)**
|
||||
|
||||
เนื่องจากโครงสร้างไฟล์และฐานข้อมูลดูดีมากแล้ว เพื่อให้ผมช่วยเหลือได้ตรงจุดที่สุด:
|
||||
|
||||
**คุณต้องการให้ผม Review โค้ดภายในไฟล์ (Internal Logic) ของไฟล์ไหนเป็นพิเศษไหมครับ?**
|
||||
|
||||
ผมแนะนำให้เริ่มจาก **"หัวใจสำคัญ"** ของความถูกต้องข้อมูลก่อน คือ:
|
||||
1. **`src/modules/document-numbering/document-numbering.service.ts`** (เพื่อดู Logic การรันเลขเอกสาร)
|
||||
2. **`src/common/file-storage/file-storage.service.ts`** (เพื่อดู Logic การจัดการไฟล์และการ Commit)
|
||||
|
||||
หรือถ้าต้องการให้ผมเริ่มเขียน Code ส่วนไหนที่ยังขาดอยู่ (เช่น Workflow DSL Parser) สามารถแจ้งได้เลยครับ
|
||||
|
||||
|
||||
สวัสดีครับ ในฐานะ Programmer ที่ดูแลระบบ LCBP3-DMS ผมได้ทำการ **Review เอกสาร `6B.md` (Special requirements for document-numbering)** โดยเทียบกับโครงสร้างฐานข้อมูลและแผนงานที่มีอยู่ (v1.4.3) พบประเด็นสำคัญและการปรับปรุงทางเทคนิคที่จำเป็นดังนี้ครับ
|
||||
|
||||
-----
|
||||
|
||||
# 📝 ผลการ Review เอกสาร 6B.md และแนวทางการ Implement
|
||||
|
||||
## 1\. บทวิเคราะห์และผลกระทบ (Analysis & Impact)
|
||||
|
||||
เอกสาร `6B.md` ระบุความต้องการที่ **ซับซ้อนกว่า** ระบบ Running Number ทั่วไป (ที่มักเป็น `ORG-TYPE-YEAR-SEQ`) โดยมีประเด็นสำคัญคือ:
|
||||
|
||||
1. **Requirement 1 (Flexibility):** Admin ต้องแก้ format ได้ → **รองรับได้** ด้วยตาราง `document_number_formats` ที่มีอยู่ แต่ต้องปรับ Logic การแทนค่าตัวแปร (Token Replacement) ให้ฉลาดขึ้น
|
||||
2. **Requirement 2.1 (Correspondence ทั่วไป):** รูปแบบมาตรฐาน → **รองรับได้** ด้วยโครงสร้างปัจจุบัน
|
||||
3. **Requirement 2.2 (Transmittal):** มีเงื่อนไข "To OWNER" vs "To CONTRACTOR" ใช้เลขต่างกัน และมีการ mapping `sub_type_number` → **Impact:** ต้องเพิ่ม Logic ในการเลือก Format Template ตาม "ผู้รับ" (Recipient Role) และต้องมีตารางเก็บ Mapping `sub_type_number`
|
||||
4. **Requirement 2.3 (RFI) & 2.4 (RFA):** มีการใช้ `disciplines_code` (เช่น GEN, STR, ARC) และไม่มีปี (Year) ใน Format → **Impact:**
|
||||
* ฐานข้อมูลปัจจุบันขาดตาราง `disciplines`
|
||||
* ตาราง `document_number_counters` ปัจจุบัน Key คือ `(Project, Org, Type, Year)` แต่ RFA/RFI อาจจะต้องนับแยกตาม **Discipline** ด้วย หากไม่เพิ่ม Column ใน Counter อาจจะเกิดปัญหาเลขซ้ำหรือเลขกระโดดข้ามหมวดงาน
|
||||
|
||||
-----
|
||||
|
||||
## 2\. การออกแบบฐานข้อมูลเพิ่มเติม (Database Schema Updates)
|
||||
|
||||
เพื่อให้รองรับ Requirement นี้ ต้องมีการเพิ่มตารางและแก้ไขตาราง `01_lcbp3_v1_4_3.sql` ดังนี้:
|
||||
|
||||
### 2.1 เพิ่มตาราง `disciplines` และ `correspondence_sub_types`
|
||||
|
||||
```sql
|
||||
-- File: 01_lcbp3_v1_4_3_patch_6b.sql
|
||||
-- เพิ่มตารางหมวดงาน (Disciplines) ตาม Req 6B
|
||||
CREATE TABLE disciplines (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
project_id INT NOT NULL,
|
||||
discipline_code VARCHAR(10) NOT NULL COMMENT 'เช่น GEN, STR, ARC',
|
||||
name_th VARCHAR(255) COMMENT 'ชื่อไทย',
|
||||
name_en VARCHAR(255) COMMENT 'ชื่ออังกฤษ',
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
UNIQUE KEY uk_discipline_project (project_id, discipline_code)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- เพิ่มตาราง Sub Types (สำหรับ Transmittal/RFA mapping)
|
||||
CREATE TABLE correspondence_sub_type_codes (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
project_id INT NOT NULL,
|
||||
correspondence_type_id INT NOT NULL,
|
||||
sub_type_code VARCHAR(20) NOT NULL COMMENT 'เช่น MAT, SHP',
|
||||
sub_type_number VARCHAR(10) COMMENT 'เลขรหัส เช่น 11, 22',
|
||||
description VARCHAR(255),
|
||||
FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY (correspondence_type_id) REFERENCES correspondence_types(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
```text
|
||||
src/modules/json-schema
|
||||
├── 📁 dto
|
||||
│ ├── create-json-schema.dto.ts # DTO สำหรับสร้าง Schema ใหม่
|
||||
│ ├── update-json-schema.dto.ts # DTO สำหรับแก้ไข Schema
|
||||
│ ├── validate-data.dto.ts # DTO สำหรับรับข้อมูลมา Validate
|
||||
│ └── migrate-data.dto.ts # DTO สำหรับสั่ง Migrate Data
|
||||
├── 📁 entities
|
||||
│ └── json-schema.entity.ts # Entity หลักเก็บ Definition (T2.5.1)
|
||||
├── 📁 services
|
||||
│ ├── json-schema.service.ts # Core Service (CRUD, Validation wrapper)
|
||||
│ ├── virtual-column.service.ts # จัดการ Generated Columns ใน DB (T2.5.3)
|
||||
│ ├── schema-migration.service.ts # จัดการ Data Migration ระหว่าง Version (T2.5.5)
|
||||
│ └── json-security.service.ts # จัดการ Security & Encryption (T2.5.6)
|
||||
├── 📁 interfaces
|
||||
│ ├── ui-schema.interface.ts # Type definition สำหรับ UI Schema (T2.5.4)
|
||||
│ └── virtual-column.interface.ts # Type definition สำหรับ Virtual Column Config
|
||||
├── json-schema.controller.ts # API Endpoints (T2.5.7)
|
||||
├── json-schema.module.ts # Module Definition
|
||||
└── json-schema.service.spec.ts # Unit Tests (T2.5.9)
|
||||
```
|
||||
|
||||
### 2.2 ปรับปรุงตาราง `rfas` และ `document_number_counters`
|
||||
---
|
||||
|
||||
เนื่องจาก RFA/RFI ต้องวิ่งเลขตาม Discipline เราจึงต้องเก็บ Discipline ไว้ใน RFA และต้องใช้เป็นส่วนหนึ่งของ Key ในการนับเลข
|
||||
### 📝 2. รายละเอียดงานในแต่ละ Task (Tasks Breakdown)
|
||||
|
||||
```sql
|
||||
-- เพิ่ม discipline_id ในตาราง RFAs
|
||||
ALTER TABLE rfas
|
||||
ADD COLUMN discipline_id INT NULL AFTER rfa_type_id,
|
||||
ADD CONSTRAINT fk_rfa_discipline FOREIGN KEY (discipline_id) REFERENCES disciplines(id);
|
||||
#### **[ ] T2.5.1 JSON Schema Registry & Versioning**
|
||||
|
||||
-- เพิ่ม discipline_id ในตาราง Correspondences (สำหรับ RFI)
|
||||
ALTER TABLE correspondences
|
||||
ADD COLUMN discipline_id INT NULL AFTER correspondence_type_id,
|
||||
ADD CONSTRAINT fk_corr_discipline FOREIGN KEY (discipline_id) REFERENCES disciplines(id);
|
||||
- **สิ่งที่ต้องทำ:** สร้าง `JsonSchema` Entity
|
||||
- **รายละเอียด:**
|
||||
- เก็บ `schema_definition` (AJV format)
|
||||
- เก็บ `ui_schema` (Form configuration)
|
||||
- รองรับ `version` เพื่อทำ Schema Evolution
|
||||
- เก็บ config ของ `virtual_columns`
|
||||
|
||||
-- ปรับปรุงตาราง Counter ให้รองรับ Discipline และ SubType (ใช้เป็น Dynamic Key หรือเพิ่ม Column)
|
||||
-- เพื่อความ Performance และ Integrity แนะนำให้เพิ่ม Column
|
||||
ALTER TABLE document_number_counters
|
||||
ADD COLUMN discipline_id INT NULL DEFAULT NULL AFTER correspondence_type_id,
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (project_id, originator_organization_id, correspondence_type_id, discipline_id, current_year);
|
||||
-- หมายเหตุ: ถ้า discipline_id เป็น NULL ให้ถือว่าเป็น Counter ทั่วไป
|
||||
```
|
||||
#### **[ ] T2.5.2 Schema Validation & Transformation Engine**
|
||||
|
||||
-----
|
||||
- **สิ่งที่ต้องทำ:** Implement Logic ใน `JsonSchemaService`
|
||||
- **รายละเอียด:**
|
||||
- ติดตั้งและ Config `AJV` library
|
||||
- สร้าง Custom Keywords: `document-number`, `requiredRole`
|
||||
- ฟังก์ชัน `validateData(schemaName, data)`
|
||||
- ฟังก์ชัน `sanitizeData()` เพื่อลบ field ที่ไม่อยู่ใน schema หรือ field ที่ user ไม่มีสิทธิ์
|
||||
|
||||
## 3\. แนวทางการ Implement ใน NestJS (Backend Logic)
|
||||
#### **[ ] T2.5.3 Virtual Columns & Performance Optimization**
|
||||
|
||||
เราต้องปรับ `DocumentNumberingService` ให้เป็น **Token-Based Generator** ที่มีความยืดหยุ่นสูง
|
||||
- **สิ่งที่ต้องทำ:** สร้าง `VirtualColumnService`
|
||||
- **รายละเอียด:**
|
||||
- ฟังก์ชัน `setupVirtualColumns()`: อ่าน Config จาก Schema แล้วสั่ง `ALTER TABLE ... ADD COLUMN ... GENERATED ALWAYS AS ...`
|
||||
- ฟังก์ชันสร้าง Index บน Virtual Column เพื่อให้ค้นหาเร็วขึ้น
|
||||
- **สำคัญ:** ต้องรองรับ MariaDB 10.11 Syntax
|
||||
|
||||
### 3.1 รูปแบบ Template (Format Templates)
|
||||
#### **[ ] T2.5.4 Dynamic Form Schema Management**
|
||||
|
||||
Admin จะบันทึก Template ลง DB ในรูปแบบ:
|
||||
- **สิ่งที่ต้องทำ:** กำหนด Interface ใน `ui-schema.interface.ts`
|
||||
- **รายละเอียด:**
|
||||
- ออกแบบโครงสร้าง JSON ที่ Frontend จะนำไป Render เป็น Form
|
||||
- กำหนด Logic `dependencies` (เช่น ถ้าเลือก Type A ให้แสดง Field B)
|
||||
|
||||
* General: `{ORG}-{ORG}-{SEQ:4}-{YEAR}`
|
||||
* Transmittal: `{ORG}-{ORG}-{SUBTYPE_NUM}-{SEQ:4}-{YEAR}`
|
||||
* RFA: `{CONTRACT}-{TYPE}-{DISCIPLINE}-{SUBTYPE}-{SEQ:4}-{REV}`
|
||||
#### **[ ] T2.5.5 Data Migration & Version Compatibility**
|
||||
|
||||
### 3.2 Logic การ Generate (Pseudo Code)
|
||||
- **สิ่งที่ต้องทำ:** สร้าง `SchemaMigrationService`
|
||||
- **รายละเอียด:**
|
||||
- Logic การแปลงข้อมูลเก่าให้เข้ากับ Schema ใหม่ (Field Rename, Transform Value)
|
||||
- ฟังก์ชัน `migrateData(entityType, entityId, targetVersion)`
|
||||
|
||||
```typescript
|
||||
// File: src/modules/document-numbering/document-numbering.service.ts
|
||||
#### **[ ] T2.5.6 Security & Access Control for JSON Data**
|
||||
|
||||
/**
|
||||
* Strategy:
|
||||
* 1. รับค่า Context (Project, Org, Type, Discipline, SubType, Year)
|
||||
* 2. ดึง Format Template จาก DB ตาม Type
|
||||
* 3. ถ้าเป็น Transmittal ต้องเช็คเงื่อนไขพิเศษเพื่อเลือก Template (Owner vs Contractor)
|
||||
* 4. Parse Template เพื่อหาว่าต้องใช้ Key ไหนบ้างในการ Lock และ Count
|
||||
* 5. Run Redlock + Optimistic Lock
|
||||
* 6. Replace Tokens
|
||||
*/
|
||||
- **สิ่งที่ต้องทำ:** สร้าง `JsonSecurityService`
|
||||
- **รายละเอียด:**
|
||||
- **Field-level Security:** ตรวจสอบ Role ว่าเห็น Field นี้ได้ไหม (Masking/Removal)
|
||||
- **Encryption:** เข้ารหัส Field ที่ Sensitive ก่อนบันทึกลง JSON
|
||||
|
||||
async generateNextNumber(ctx: GenerateNumberContext): Promise<string> {
|
||||
// 1. Get Template
|
||||
let template = await this.formatRepo.findOne({ ... });
|
||||
#### **[ ] T2.5.7 API Design & Integration**
|
||||
|
||||
// 2. Handle Special Logic (Requirement 2.2 Transmittal)
|
||||
if (ctx.typeCode === 'TRANSMITTAL') {
|
||||
// Logic: ถ้าส่งให้ Owner ใช้ Template A, ถ้า Contractor ใช้ Template B
|
||||
// หรือดึง sub_type_number มาเตรียมไว้
|
||||
}
|
||||
- **สิ่งที่ต้องทำ:** สร้าง `JsonSchemaController`
|
||||
- **Endpoints:**
|
||||
- `POST /json-schema/schemas`: สร้าง Schema
|
||||
- `POST /json-schema/validate/:name`: ตรวจสอบข้อมูล
|
||||
- `POST /json-schema/migrate/:type/:id`: สั่ง Migrate
|
||||
- `GET /json-schema/ui-schema/:name`: ดึง UI Config
|
||||
|
||||
// 3. Resolve Tokens Values
|
||||
const tokens = {
|
||||
'{ORG}': ctx.orgCode,
|
||||
'{CONTRACT}': ctx.contractCode,
|
||||
'{TYPE}': ctx.typeCode,
|
||||
'{DISCIPLINE}': ctx.disciplineCode, // จากตาราง disciplines
|
||||
'{SUBTYPE}': ctx.subTypeCode, // จาก rfa_types หรือ sub_type_codes
|
||||
'{SUBTYPE_NUM}': ctx.subTypeNumber, // จาก correspondence_sub_type_codes
|
||||
'{YEAR}': ctx.year.toString(),
|
||||
'{YEAR_SHORT}': ctx.year.toString().slice(-2),
|
||||
};
|
||||
#### **[ ] T2.5.8 Integration with Document Modules**
|
||||
|
||||
// 4. Construct Counter Key
|
||||
// ถ้า Template มี {DISCIPLINE} ต้องเอา discipline_id มาเป็นส่วนหนึ่งของ Composite Key ใน DB Counter
|
||||
const counterKey = {
|
||||
projectId: ctx.projectId,
|
||||
orgId: ctx.orgId,
|
||||
typeId: ctx.typeId,
|
||||
disciplineId: template.includes('{DISCIPLINE}') ? ctx.disciplineId : null,
|
||||
year: template.includes('{YEAR}') ? ctx.year : 0 // ถ้าไม่ใช้ปี ให้ใช้ 0
|
||||
};
|
||||
- **สิ่งที่ต้องทำ:** แก้ไข Service อื่นๆ (Correspondence, RFA)
|
||||
- **รายละเอียด:**
|
||||
- Inject `JsonSchemaService` เข้าไปใน `CorrespondenceService` และ `RfaService`
|
||||
- เรียก `validateData` ก่อน create/update
|
||||
- เรียก `setupVirtualColumns` หลัง create schema ใหม่
|
||||
|
||||
// 5. Execute Double-Lock Mechanism (Redis + DB Optimistic)
|
||||
const seq = await this.runDistributedCounter(counterKey);
|
||||
#### **[ ] T2.5.9 Testing Strategy**
|
||||
|
||||
// 6. Final Replace
|
||||
let finalNumber = template.formatTemplate;
|
||||
for (const [key, value] of Object.entries(tokens)) {
|
||||
finalNumber = finalNumber.replace(key, value || '00'); // Fallback if missing
|
||||
}
|
||||
// Replace SEQ with padding
|
||||
finalNumber = finalNumber.replace(/{SEQ:(\d+)}/, (_, width) =>
|
||||
String(seq).padStart(Number(width), '0')
|
||||
);
|
||||
- **สิ่งที่ต้องทำ:** เขียน Unit Test
|
||||
- **รายละเอียด:**
|
||||
- ทดสอบ Validation Logic (ผ่าน/ไม่ผ่าน)
|
||||
- ทดสอบ Virtual Column Generation (SQL ถูกต้องไหม)
|
||||
- ทดสอบ Security Masking
|
||||
|
||||
return finalNumber;
|
||||
}
|
||||
```
|
||||
---
|
||||
|
||||
-----
|
||||
### 🚀 ขั้นตอนถัดไป
|
||||
|
||||
## 4\. แผนการดำเนินการ (Action Plan)
|
||||
หากท่านเห็นด้วยกับโครงสร้างนี้ ผมแนะนำให้เริ่มดำเนินการตามลำดับดังนี้ครับ:
|
||||
|
||||
เพื่อให้รองรับ `6B.md` ได้สมบูรณ์ ผมขอเสนอให้ดำเนินการดังนี้:
|
||||
1. **สร้าง Entity และ DTOs** (T2.5.1)
|
||||
2. **Implement JsonSchemaService Core** (Validation Logic) (T2.5.2)
|
||||
3. **Implement VirtualColumnService** (T2.5.3) - _ส่วนนี้สำคัญมากสำหรับ Performance_
|
||||
4. **สร้าง Controller และเชื่อมต่อ Module** (T2.5.7)
|
||||
|
||||
1. **Database Migration:**
|
||||
* รัน SQL Script เพื่อเพิ่มตาราง `disciplines`, `correspondence_sub_type_codes`.
|
||||
* Alter ตาราง `rfas`, `correspondences` และ `document_number_counters` ตามข้อ 2.2
|
||||
2. **Seed Data:**
|
||||
* นำข้อมูลจากตารางใน `6B.md` (Disciplines, RFA Types, Sub Types) มาเขียนเป็น Seed Script ลงฐานข้อมูล เพื่อให้เริ่มต้นระบบมีข้อมูลพร้อมใช้ทันที
|
||||
3. **Backend Implementation:**
|
||||
* Update `DocumentNumberingService` ให้รองรับ Dynamic Token Replacement
|
||||
* Update `CreateRfaDto` และ `CreateCorrespondenceDto` ให้รับค่า `discipline_id` และ `sub_type_id` (ถ้ามี)
|
||||
4. **Admin Panel:**
|
||||
* ต้องมีหน้า UI ให้ Admin จัดการ Mapping ของ `Format Template` และ `Sub Type Codes` ได้ (ตาม Requirement ข้อ 1)
|
||||
|
||||
**ต้องการให้ผมเริ่มเขียน SQL Migration Script และ Seed Data สำหรับข้อมูลใน 6B.md เลยหรือไม่ครับ?** (ข้อมูลในตารางค่อนข้างเยอะ การทำ Seed ไว้จะช่วยลดเวลา Admin ในการกรอกข้อมูลเริ่มต้นได้มากครับ)
|
||||
|
||||
จาก แผนการดำเนินการ (Action Plan): Database Migration และ Seed Data, ผมได้อัพเดท Basic data ข้อ 5. Data files ให้แล้ว, ช่วย review และ ปรับปรุง 4_Data_Dictionary_V1_4_3.md ,
|
||||
|
||||
เตรียม Backend Implementation, Admin Panel
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### **Phase 2: High-Integrity Data & File Management (สัปดาห์ที่ 4)**
|
||||
|
||||
* **[ ] T2.3 DocumentNumberingModule - Token-Based & Double-Lock** (Updated)
|
||||
* [ ] Update Entity: `DocumentNumberCounter` (Add `discipline_id` to PK)
|
||||
* [ ] Implement Token Parser & Replacer Logic (`{DISCIPLINE}`, `{SUBTYPE_NUM}`)
|
||||
* [ ] Update `generateNextNumber` to handle optional keys (Discipline/SubType)
|
||||
* [ ] **Deliverable:** Flexible Numbering System
|
||||
|
||||
* **[ ] 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
|
||||
ท่านต้องการให้ผมเริ่มเขียนโค้ดส่วนไหนก่อนหรือไม่ครับ? (เช่น Entity หรือ VirtualColumnService)
|
||||
|
||||
3
frontend/.vscode/settings.json
vendored
Normal file
3
frontend/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"jest.rootPath": "/"
|
||||
}
|
||||
@@ -19,9 +19,18 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
"baseUrl": "./",
|
||||
"@/*": ["app/*"],
|
||||
"@components": ["components/*"],
|
||||
"@config": ["config/*"],
|
||||
"@lib": ["lib/*"],
|
||||
"@providers": ["providers/*"],
|
||||
"@public": ["public/*"],
|
||||
"@styles": ["styles/*"],
|
||||
"@types": ["types/*"],
|
||||
"@api": ["app/api/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,36 +123,51 @@
|
||||
],
|
||||
"eslint.alwaysShowStatus": true,
|
||||
"eslint.format.enable": false,
|
||||
"eslint.workingDirectories": ["./backend", "./frontend"],
|
||||
|
||||
"eslint.lintTask.enable": true,
|
||||
"eslint.workingDirectories": [
|
||||
"./backend",
|
||||
"./frontend"
|
||||
],
|
||||
// ========================================
|
||||
// PATH INTELLISENSE - ไม่มี src folder
|
||||
// ========================================
|
||||
|
||||
"path-intellisense.mappings": {
|
||||
// Backend paths (ไม่มี src)
|
||||
"@backend": "${workspaceFolder:🔧 Backend}",
|
||||
"@backend/*": "${workspaceFolder:🔧 Backend}/*",
|
||||
"@modules": "${workspaceFolder:🔧 Backend}/modules",
|
||||
"@common": "${workspaceFolder:🔧 Backend}/common",
|
||||
"@config": "${workspaceFolder:🔧 Backend}/config",
|
||||
"@utils": "${workspaceFolder:🔧 Backend}/utils",
|
||||
"@entities": "${workspaceFolder:🔧 Backend}/entities",
|
||||
"@dto": "${workspaceFolder:🔧 Backend}/dto",
|
||||
// Backend paths
|
||||
"@backend": "${workspaceFolder:🔧 Backend (NestJS)}/src",
|
||||
"@backend/*": "${workspaceFolder:🔧 Backend (NestJS)}/src/*",
|
||||
"@modules": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules",
|
||||
"@common": "${workspaceFolder:🔧 Backend (NestJS)}/src/common",
|
||||
"@config": "${workspaceFolder:🔧 Backend (NestJS)}/src/common/config",
|
||||
"@circulation": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/circulation",
|
||||
"@correspondence": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/correspondence",
|
||||
"@document-numbering": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/document-numbering",
|
||||
"@drawing": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/drawing",
|
||||
"@json-schema": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/json-schema",
|
||||
"@master": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/master",
|
||||
"@monitoring": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/monitoring",
|
||||
"@notification": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/notification",
|
||||
"@project": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/project",
|
||||
"@rfa": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/rfa",
|
||||
"@search": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/search",
|
||||
"@transmittal": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/transmittal",
|
||||
"@users": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/users",
|
||||
"@workflow-engine": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/workflow-engine",
|
||||
|
||||
// Frontend paths (ไม่มี src)
|
||||
"@": "${workspaceFolder:🎨 Frontend}",
|
||||
"@/*": "${workspaceFolder:🎨 Frontend}/*",
|
||||
"@components": "${workspaceFolder:🎨 Frontend}/components",
|
||||
"@hooks": "${workspaceFolder:🎨 Frontend}/hooks",
|
||||
"@utils": "${workspaceFolder:🎨 Frontend}/utils",
|
||||
"@lib": "${workspaceFolder:🎨 Frontend}/lib",
|
||||
"@types": "${workspaceFolder:🎨 Frontend}/types",
|
||||
"@api": "${workspaceFolder:🎨 Frontend}/api",
|
||||
"@styles": "${workspaceFolder:🎨 Frontend}/styles",
|
||||
"@assets": "${workspaceFolder:🎨 Frontend}/assets",
|
||||
"@store": "${workspaceFolder:🎨 Frontend}/store",
|
||||
"@contexts": "${workspaceFolder:🎨 Frontend}/contexts"
|
||||
"@": "${workspaceFolder:🎨 Frontend (React/Next.js)}/app",
|
||||
"@/*": "${workspaceFolder:🎨 Frontend (React/Next.js)}/app/*",
|
||||
"@app": "${workspaceFolder:🎨 Frontend (React/Next.js)}/app",
|
||||
"@components": "${workspaceFolder:🎨 Frontend (React/Next.js)}/components",
|
||||
"@config": "${workspaceFolder:🎨 Frontend (React/Next.js)}/config",
|
||||
"@lib": "${workspaceFolder:🎨 Frontend (React/Next.js)}/lib",
|
||||
"@hooks": "${workspaceFolder:🎨 Frontend (React/Next.js)}/app/hooks",
|
||||
"@utils": "${workspaceFolder:🎨 Frontend (React/Next.js)}/utils",
|
||||
"@providers": "${workspaceFolder:🎨 Frontend (React/Next.js)}/providers",
|
||||
"@public": "${workspaceFolder:🎨 Frontend (React/Next.js)}/public",
|
||||
"@styles": "${workspaceFolder:🎨 Frontend (React/Next.js)}/styles",
|
||||
"@types": "${workspaceFolder:🎨 Frontend (React/Next.js)}/types",
|
||||
"@api": "${workspaceFolder:🎨 Frontend (React/Next.js)}/app/api",
|
||||
},
|
||||
"path-intellisense.autoSlashAfterDirectory": true,
|
||||
"path-intellisense.extensionOnImport": false,
|
||||
@@ -187,7 +202,7 @@
|
||||
|
||||
// ระบุที่ตั้งของ tailwind.config (ถ้ามี)
|
||||
"tailwindCSS.experimental.configFile": {
|
||||
"frontend/tailwind.config.js": "frontend/**",
|
||||
//"backend/tailwind.config.js": "backend/**",
|
||||
"frontend/tailwind.config.ts": "frontend/**"
|
||||
},
|
||||
|
||||
@@ -261,9 +276,18 @@
|
||||
// TODO TREE
|
||||
// ========================================
|
||||
|
||||
"todo-tree.general.tags": ["TODO", "FIXME", "BUG", "HACK", "NOTE", "XXX"],
|
||||
"todo-tree.general.tags": [
|
||||
"TODO",
|
||||
"FIXME",
|
||||
"BUG",
|
||||
"HACK",
|
||||
"NOTE",
|
||||
"XXX",
|
||||
"[ ]",
|
||||
"[x]"
|
||||
],
|
||||
"todo-tree.highlights.enabled": true,
|
||||
"todo-tree.tree.showInExplorer": true,
|
||||
"todo-tree.tree.showScanModeButton": true,
|
||||
"todo-tree.filtering.excludeGlobs": [
|
||||
"**/node_modules",
|
||||
"**/dist",
|
||||
@@ -290,6 +314,11 @@
|
||||
"icon": "note",
|
||||
"iconColour": "#3498DB",
|
||||
"foreground": "#3498DB"
|
||||
},
|
||||
"HACK": {
|
||||
"icon": "tools",
|
||||
"iconColour": "#FFA500",
|
||||
"foreground": "#FFA500"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -302,7 +331,13 @@
|
||||
"gitlens.currentLine.format": "${author}, ${agoOrDate}",
|
||||
"gitlens.codeLens.enabled": true,
|
||||
"gitlens.codeLens.authors.enabled": false,
|
||||
"gitlens.codeLens.recentChange.enabled": true,
|
||||
"gitlens.hovers.enabled": true,
|
||||
"gitlens.blame.format": "${author|10} ${agoOrDate|14-}",
|
||||
"gitlens.blame.highlight.enabled": false,
|
||||
"gitlens.views.repositories.location": "scm",
|
||||
"gitlens.views.fileHistory.location": "explorer",
|
||||
"gitlens.views.lineHistory.location": "explorer",
|
||||
|
||||
// ========================================
|
||||
// GIT
|
||||
@@ -313,6 +348,8 @@
|
||||
"git.autofetchPeriod": 180,
|
||||
"git.confirmSync": false,
|
||||
"git.enableSmartCommit": true,
|
||||
"git.postCommitCommand": "none",
|
||||
"git.untrackedChanges": "separate",
|
||||
"git.openRepositoryInParentFolders": "always",
|
||||
|
||||
// ========================================
|
||||
@@ -323,6 +360,9 @@
|
||||
"importCost.largePackageSize": 100,
|
||||
"importCost.mediumPackageSize": 50,
|
||||
"importCost.smallPackageSize": 20,
|
||||
"importCost.largePackageColor": "#FF2D00",
|
||||
"importCost.mediumPackageColor": "#FF8C00",
|
||||
"importCost.smallPackageColor": "#98C379",
|
||||
|
||||
// ========================================
|
||||
// JAVASCRIPT/TYPESCRIPT
|
||||
@@ -332,12 +372,15 @@
|
||||
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||
"javascript.inlayHints.parameterNames.enabled": "all",
|
||||
"javascript.inlayHints.functionLikeReturnTypes.enabled": true,
|
||||
"javascript.inlayHints.variableTypes.enabled": true,
|
||||
"javascript.preferences.importModuleSpecifier": "relative",
|
||||
|
||||
"typescript.suggest.autoImports": true,
|
||||
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||
"typescript.inlayHints.parameterNames.enabled": "all",
|
||||
"typescript.inlayHints.functionLikeReturnTypes.enabled": true,
|
||||
"typescript.inlayHints.variableTypes.enabled": false,
|
||||
"typescript.inlayHints.propertyDeclarationTypes.enabled": true,
|
||||
"typescript.preferences.importModuleSpecifier": "relative",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
|
||||
@@ -350,19 +393,21 @@
|
||||
"typescript": "typescriptreact"
|
||||
},
|
||||
"emmet.triggerExpansionOnTab": true,
|
||||
"emmet.showSuggestionsAsSnippets": true,
|
||||
|
||||
// ========================================
|
||||
// FILES
|
||||
// ========================================
|
||||
|
||||
"files.autoSave": "onFocusChange",
|
||||
//"files.autoSave": "onFocusChange",
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"files.insertFinalNewline": true,
|
||||
"files.encoding": "utf8",
|
||||
"files.eol": "\n",
|
||||
"files.associations": {
|
||||
"*.css": "tailwindcss",
|
||||
".env*": "dotenv"
|
||||
".env*": "dotenv",
|
||||
"*.md": "markdown"
|
||||
},
|
||||
|
||||
"files.exclude": {
|
||||
@@ -373,15 +418,19 @@
|
||||
"**/dist": true,
|
||||
"**/build": true,
|
||||
"**/.turbo": true,
|
||||
"**/coverage": true
|
||||
"**/coverage": true,
|
||||
"**/.nyc_output": true,
|
||||
"**/*.log": true
|
||||
},
|
||||
|
||||
"files.watcherExclude": {
|
||||
"**/.git/objects/**": true,
|
||||
"**/.git/subtree-cache/**": true,
|
||||
"**/node_modules/**": true,
|
||||
"**/.next/**": true,
|
||||
"**/dist/**": true,
|
||||
"**/build/**": true
|
||||
"**/build/**": true,
|
||||
"**/.turbo/**": true
|
||||
},
|
||||
|
||||
// ========================================
|
||||
@@ -393,11 +442,15 @@
|
||||
"**/dist": true,
|
||||
"**/build": true,
|
||||
"**/.next": true,
|
||||
"**/.turbo": true,
|
||||
"**/coverage": true,
|
||||
"**/.nyc_output": true,
|
||||
"**/yarn.lock": true,
|
||||
"**/package-lock.json": true,
|
||||
"**/pnpm-lock.yaml": true
|
||||
"**/pnpm-lock.yaml": true,
|
||||
"**/*.log": true
|
||||
},
|
||||
"search.followSymlinks": false,
|
||||
"search.useIgnoreFiles": true,
|
||||
"search.smartCase": true,
|
||||
|
||||
@@ -405,10 +458,12 @@
|
||||
// TERMINAL
|
||||
// ========================================
|
||||
|
||||
"terminal.integrated.fontSize": 13,
|
||||
"terminal.integrated.fontSize": 14,
|
||||
"terminal.integrated.lineHeight": 1.2,
|
||||
"terminal.integrated.smoothScrolling": true,
|
||||
"terminal.integrated.defaultProfile.windows": "Git Bash",
|
||||
"terminal.integrated.cursorBlinking": true,
|
||||
"terminal.integrated.fontFamily": "MesloLGS NF, Consolas, monospace",
|
||||
"terminal.integrated.defaultProfile.windows": "PowerShell",
|
||||
"terminal.integrated.cwd": "${workspaceFolder}",
|
||||
|
||||
// ========================================
|
||||
@@ -420,8 +475,20 @@
|
||||
"workbench.tree.indent": 15,
|
||||
"workbench.list.smoothScrolling": true,
|
||||
"workbench.editor.enablePreview": false,
|
||||
"workbench.editor.limit.enabled": true,
|
||||
"workbench.editor.limit.value": 10,
|
||||
"workbench.startupEditor": "welcomePage",
|
||||
|
||||
"workbench.colorCustomizations": {
|
||||
"[One Dark Pro]": {
|
||||
"activityBarBadge.background": "#FF8C00",
|
||||
"list.activeSelectionForeground": "#FF8C00",
|
||||
"list.inactiveSelectionForeground": "#FF8C00",
|
||||
"list.highlightForeground": "#FF8C00",
|
||||
"scrollbarSlider.activeBackground": "#FF8C0050",
|
||||
"editorSuggestWidget.highlightForeground": "#FF8C00",
|
||||
"textLink.foreground": "#FF8C00"
|
||||
}
|
||||
},
|
||||
// ========================================
|
||||
// EXPLORER
|
||||
// ========================================
|
||||
@@ -443,29 +510,44 @@
|
||||
"*.jsx": "${capture}.test.jsx, ${capture}.spec.jsx"
|
||||
},
|
||||
|
||||
// ========================================
|
||||
// BREADCRUMBS
|
||||
// ========================================
|
||||
|
||||
"breadcrumbs.enabled": true,
|
||||
"breadcrumbs.filePath": "on",
|
||||
"breadcrumbs.symbolPath": "on",
|
||||
|
||||
// ========================================
|
||||
// JEST
|
||||
// ========================================
|
||||
|
||||
"jest.autoRun": "off",
|
||||
"jest.showCoverageOnLoad": false,
|
||||
"jest.rootPath": "backend",
|
||||
|
||||
// ========================================
|
||||
// DOCKER
|
||||
// ========================================
|
||||
|
||||
"docker.languageserver.formatter.ignoreMultilineInstructions": true,
|
||||
|
||||
"docker.showStartPage": false,
|
||||
// ========================================
|
||||
// YAML
|
||||
// ========================================
|
||||
|
||||
"yaml.schemas": {
|
||||
"https://json.schemastore.org/github-workflow.json": ".github/workflows/*.{yml,yaml}",
|
||||
"https://json.schemastore.org/github-action.json": "action.{yml,yaml}",
|
||||
"https://json.schemastore.org/prettierrc.json": ".prettierrc.{yml,yaml}",
|
||||
"https://json.schemastore.org/docker-compose.json": "docker-compose*.{yml,yaml}"
|
||||
},
|
||||
"yaml.format.enable": true,
|
||||
"yaml.format.singleQuote": false,
|
||||
"yaml.format.bracketSpacing": true,
|
||||
"yaml.validate": true,
|
||||
"yaml.hover": true,
|
||||
"yaml.completion": true,
|
||||
|
||||
// ========================================
|
||||
// REST CLIENT
|
||||
@@ -481,15 +563,32 @@
|
||||
"apiUrl": "http://localhost:3000"
|
||||
},
|
||||
"production": {
|
||||
"apiUrl": "https://api.yourdomain.com"
|
||||
"apiUrl": "https://lcbp3.nap-dms.work"
|
||||
}
|
||||
},
|
||||
|
||||
// ========================================
|
||||
// CONSOLE NINJA
|
||||
// ========================================
|
||||
|
||||
"console-ninja.featureSet": "Community",
|
||||
"console-ninja.toolsToEnableSupportAutomaticallyFor": {
|
||||
"live-server-extension": true,
|
||||
"live-preview-extension": true
|
||||
},
|
||||
|
||||
// ========================================
|
||||
// MATERIAL ICON THEME
|
||||
// ========================================
|
||||
|
||||
"material-icon-theme.folders.theme": "specific",
|
||||
"material-icon-theme.folders.color": "#90a4ae",
|
||||
"material-icon-theme.files.associations": {
|
||||
"*.env.local": "Tune",
|
||||
"*.env.development": "Tune",
|
||||
"*.env.production": "Tune",
|
||||
"docker-compose.*.yml": "Docker"
|
||||
},
|
||||
"material-icon-theme.folders.associations": {
|
||||
"hooks": "Custom",
|
||||
"utils": "Helper",
|
||||
@@ -503,13 +602,32 @@
|
||||
"config": "Config"
|
||||
},
|
||||
|
||||
// ========================================
|
||||
// NPM INTELLISENSE
|
||||
// ========================================
|
||||
|
||||
"npm-intellisense.importES6": true,
|
||||
"npm-intellisense.importQuotes": "'",
|
||||
"npm-intellisense.importLinebreak": ";\n",
|
||||
"npm-intellisense.importDeclarationType": "const",
|
||||
|
||||
// ========================================
|
||||
// PERFORMANCE
|
||||
// ========================================
|
||||
|
||||
"files.maxMemoryForLargeFilesMB": 4096,
|
||||
"telemetry.telemetryLevel": "off",
|
||||
"security.workspace.trust.untrustedFiles": "open"
|
||||
"security.workspace.trust.untrustedFiles": "open",
|
||||
"extensions.ignoreRecommendations": false,
|
||||
// ========================================
|
||||
// DEBUGGING
|
||||
// ========================================
|
||||
|
||||
"debug.console.fontSize": 13,
|
||||
"debug.console.lineHeight": 1.2,
|
||||
"debug.internalConsoleOptions": "openOnSessionStart",
|
||||
"debug.openDebug": "openOnDebugBreak",
|
||||
"debug.showBreakpointsInOverviewRuler": true
|
||||
},
|
||||
|
||||
// ========================================
|
||||
@@ -698,7 +816,6 @@
|
||||
"formulahendry.auto-rename-tag",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"mtxr.sqltools",
|
||||
"mongodb.mongodb-vscode",
|
||||
"redhat.vscode-yaml",
|
||||
"mikestead.dotenv",
|
||||
"editorconfig.editorconfig",
|
||||
|
||||
16
temp.ts
16
temp.ts
@@ -1,16 +0,0 @@
|
||||
// ใน function submit()
|
||||
// 2.1 สร้าง Routing Record แรก
|
||||
const routing = queryRunner.manager.create(CorrespondenceRouting, {
|
||||
correspondenceId: currentRevision.id,
|
||||
templateId: template.id, // ✅ บันทึก templateId ไว้ใช้อ้างอิง
|
||||
sequence: 1,
|
||||
fromOrganizationId: user.primaryOrganizationId,
|
||||
toOrganizationId: firstStep.toOrganizationId,
|
||||
stepPurpose: firstStep.stepPurpose,
|
||||
status: 'SENT',
|
||||
dueDate: new Date(
|
||||
Date.now() + (firstStep.expectedDays || 7) * 24 * 60 * 60 * 1000,
|
||||
),
|
||||
processedByUserId: user.user_id,
|
||||
processedAt: new Date(),
|
||||
});
|
||||
Reference in New Issue
Block a user