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
|
// Code Quality & Error Handling
|
||||||
"usernamehw.errorlens",
|
"usernamehw.errorlens",
|
||||||
"yoavbls.pretty-typescript-errors",
|
"yoavbls.pretty-typescript-errors",
|
||||||
"wix.vscode-import-cost",
|
|
||||||
|
|
||||||
// Comments & Documentation
|
|
||||||
"aaron-bond.better-comments",
|
"aaron-bond.better-comments",
|
||||||
"gruntfuggly.todo-tree",
|
"gruntfuggly.todo-tree",
|
||||||
|
|
||||||
@@ -23,21 +20,12 @@
|
|||||||
|
|
||||||
// API Testing
|
// API Testing
|
||||||
"rangav.vscode-thunder-client",
|
"rangav.vscode-thunder-client",
|
||||||
"humao.rest-client",
|
|
||||||
|
|
||||||
// Auto Tags
|
|
||||||
"formulahendry.auto-close-tag",
|
"formulahendry.auto-close-tag",
|
||||||
"formulahendry.auto-rename-tag",
|
"formulahendry.auto-rename-tag",
|
||||||
|
|
||||||
// Docker & DevOps
|
// Docker & DevOps
|
||||||
"ms-azuretools.vscode-docker",
|
"ms-azuretools.vscode-docker",
|
||||||
"ms-kubernetes-tools.vscode-kubernetes-tools",
|
|
||||||
|
|
||||||
// Database
|
|
||||||
"mtxr.sqltools",
|
"mtxr.sqltools",
|
||||||
"mongodb.mongodb-vscode",
|
|
||||||
|
|
||||||
// YAML & Config
|
|
||||||
"redhat.vscode-yaml",
|
"redhat.vscode-yaml",
|
||||||
"mikestead.dotenv",
|
"mikestead.dotenv",
|
||||||
"editorconfig.editorconfig",
|
"editorconfig.editorconfig",
|
||||||
@@ -61,7 +49,7 @@
|
|||||||
"wallabyjs.console-ninja",
|
"wallabyjs.console-ninja",
|
||||||
|
|
||||||
// Icons & Theme
|
// Icons & Theme
|
||||||
"pkief.material-icon-theme",
|
"pkief.material-icon-theme"
|
||||||
|
|
||||||
// AI Assistance (Optional - เลือก 1 อัน)
|
// AI Assistance (Optional - เลือก 1 อัน)
|
||||||
// "github.copilot",
|
// "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/
|
📁frontend
|
||||||
├── (auth)/
|
├── .env.local
|
||||||
│ ├── login/
|
├── .eslintrc.json
|
||||||
│ └── register/
|
├── .gitignore
|
||||||
├── (dashboard)/
|
├── 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
|
│ ├── layout.tsx
|
||||||
│ ├── page.tsx
|
│ └── page.tsx
|
||||||
│ └── components/
|
├── 📁components
|
||||||
├── admin/
|
│ ├── 📁custom
|
||||||
│ ├── users/
|
│ │ ├── file-upload-zone.tsx
|
||||||
│ ├── roles/
|
│ │ ├── responsive-data-table.tsx
|
||||||
│ └── numbering-formats/
|
│ │ └── workflow-visualizer.tsx
|
||||||
├── correspondences/
|
│ ├── 📁dashboard
|
||||||
│ ├── page.tsx
|
│ │ └── recent-activity.tsx
|
||||||
│ ├── [id]/
|
│ ├── 📁forms
|
||||||
│ └── new/
|
│ │ └── file-upload.tsx
|
||||||
├── rfas/
|
│ ├── 📁layout
|
||||||
│ ├── page.tsx
|
│ │ ├── dashboard-shell.tsx
|
||||||
│ ├── [id]/
|
│ │ ├── navbar.tsx
|
||||||
│ └── new/
|
│ │ ├── sidebar.tsx
|
||||||
├── drawings/
|
│ │ └── user-nav.tsx
|
||||||
├── circulations/
|
│ ├── 📁tables
|
||||||
├── transmittals/
|
│ └── 📁ui
|
||||||
├── search/
|
│ ├── avatar.tsx
|
||||||
└── profile/
|
│ ├── badge.tsx
|
||||||
|
│ ├── button.tsx
|
||||||
components/
|
│ ├── calendar.tsx
|
||||||
├── ui/ # shadcn/ui components
|
│ ├── card.tsx
|
||||||
├── forms/ # Dynamic form components
|
│ ├── checkbox.tsx
|
||||||
├── tables/ # Responsive data tables
|
│ ├── dropdown-menu.tsx
|
||||||
├── workflow/ # Workflow visualization
|
│ ├── input.tsx
|
||||||
├── file-upload/ # File upload with security
|
│ ├── label.tsx
|
||||||
├── notifications/ # Notification system
|
│ ├── popover.tsx
|
||||||
└── layout/ # App layout components
|
│ ├── progress.tsx
|
||||||
|
│ ├── scroll-area.tsx
|
||||||
lib/
|
│ ├── select.tsx
|
||||||
├── api/ # API clients & interceptors
|
│ ├── switch.tsx
|
||||||
├── auth/ # Authentication utilities
|
│ ├── table.tsx
|
||||||
├── stores/ # Zustand stores
|
│ ├── tabs.tsx
|
||||||
├── hooks/ # Custom React hooks
|
│ └── textarea.tsx
|
||||||
├── utils/ # Utility functions
|
├── 📁config
|
||||||
├── constants/ # App constants
|
│ └── menu.ts
|
||||||
└── types/ # TypeScript type definitions
|
├── 📁lib
|
||||||
|
│ ├── 📁api
|
||||||
styles/
|
│ │ └── client.ts
|
||||||
├── globals.css
|
│ ├── 📁auth
|
||||||
└── components/
|
│ ├── 📁hooks
|
||||||
|
│ ├── 📁services
|
||||||
__tests__/
|
│ │ ├── circulation.service.ts
|
||||||
├── unit/
|
│ │ ├── contract-drawing.service.ts
|
||||||
├── integration/
|
│ │ ├── correspondence.service.ts
|
||||||
└── e2e/
|
│ │ ├── 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**
|
## 🗓️ **แผนการพัฒนาแบบ Phase-Based**
|
||||||
|
|
||||||
### **Dependency Diagram (ภาพรวม)**
|
### **Phase 0: Foundation & Configuration (สัปดาห์ที่ 1)**
|
||||||
|
|
||||||
```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)**
|
|
||||||
|
|
||||||
**Milestone:** โครงสร้างพื้นฐานพร้อม รองรับ Development Workflow ที่มีประสิทธิภาพ
|
**Milestone:** โครงสร้างพื้นฐานพร้อม รองรับ Development Workflow ที่มีประสิทธิภาพ
|
||||||
|
|
||||||
### **Phase 0: Tasks**
|
### **Phase 0: Tasks**
|
||||||
|
|
||||||
- **[ ] F0.1 Project Setup & Tooling**
|
- **[ ] F0.1 Project Setup & Tooling**
|
||||||
|
|
||||||
- [ ] Initialize Next.js 14+ project with TypeScript
|
- [ ] Initialize Next.js 14+ project with TypeScript
|
||||||
- [ ] Configure pnpm workspace
|
- [ ] Configure pnpm workspace
|
||||||
- [ ] Setup ESLint, Prettier, and pre-commit hooks
|
- [ ] Setup ESLint, Prettier, and pre-commit hooks
|
||||||
@@ -269,6 +217,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** None
|
- [ ] **Dependencies:** None
|
||||||
|
|
||||||
- **[ ] F0.2 Design System & UI Components**
|
- **[ ] F0.2 Design System & UI Components**
|
||||||
|
|
||||||
- [ ] Setup color palette and design tokens
|
- [ ] Setup color palette and design tokens
|
||||||
- [ ] Create responsive design breakpoints
|
- [ ] Create responsive design breakpoints
|
||||||
- [ ] Implement core shadcn/ui components:
|
- [ ] Implement core shadcn/ui components:
|
||||||
@@ -284,6 +233,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F0.1
|
- [ ] **Dependencies:** F0.1
|
||||||
|
|
||||||
- **[ ] F0.3 API Client & Authentication**
|
- **[ ] F0.3 API Client & Authentication**
|
||||||
|
|
||||||
- [ ] Setup Axios client with interceptors:
|
- [ ] Setup Axios client with interceptors:
|
||||||
- [ ] Idempotency-Key header injection
|
- [ ] Idempotency-Key header injection
|
||||||
- [ ] Authentication token management
|
- [ ] 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 ทำงานได้
|
**Milestone:** Layout หลักพร้อมใช้งาน การนำทางและ Authentication ทำงานได้
|
||||||
|
|
||||||
### **Phase 1: Tasks**
|
### **Phase 1: Tasks**
|
||||||
|
|
||||||
- **[ ] F1.1 Main Layout & Navigation**
|
- **[ ] F1.1 Main Layout & Navigation**
|
||||||
|
|
||||||
- [ ] Create App Shell layout:
|
- [ ] Create App Shell layout:
|
||||||
- [ ] Navbar with user menu and notifications
|
- [ ] Navbar with user menu and notifications
|
||||||
- [ ] Collapsible sidebar with navigation
|
- [ ] Collapsible sidebar with navigation
|
||||||
@@ -348,6 +299,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F0.2, F0.3
|
- [ ] **Dependencies:** F0.2, F0.3
|
||||||
|
|
||||||
- **[ ] F1.2 Authentication Pages**
|
- **[ ] F1.2 Authentication Pages**
|
||||||
|
|
||||||
- [ ] Create login page with form validation
|
- [ ] Create login page with form validation
|
||||||
- [ ] Implement forgot password flow
|
- [ ] Implement forgot password flow
|
||||||
- [ ] Create registration page (admin-only)
|
- [ ] Create registration page (admin-only)
|
||||||
@@ -358,6 +310,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F0.3, F1.1
|
- [ ] **Dependencies:** F0.3, F1.1
|
||||||
|
|
||||||
- **[ ] F1.3 Dashboard & Landing**
|
- **[ ] F1.3 Dashboard & Landing**
|
||||||
|
|
||||||
- [ ] Create public landing page for non-authenticated users
|
- [ ] Create public landing page for non-authenticated users
|
||||||
- [ ] Implement main dashboard with:
|
- [ ] Implement main dashboard with:
|
||||||
- [ ] KPI cards (document counts, pending tasks)
|
- [ ] KPI cards (document counts, pending tasks)
|
||||||
@@ -382,13 +335,13 @@ F10_3 --> F10_4
|
|||||||
|
|
||||||
### **Phase 1: Testing - Core Structure**
|
### **Phase 1: Testing - Core Structure**
|
||||||
|
|
||||||
#### **F1.T1 Layout Test Suite**
|
- **[ ] F1.T1 Layout Test Suite**
|
||||||
|
|
||||||
- [ ] **Unit Tests:** Navigation components, layout responsiveness
|
- [ ] **Unit Tests:** Navigation components, layout responsiveness
|
||||||
- [ ] **Integration Tests:** Route protection, permission-based navigation
|
- [ ] **Integration Tests:** Route protection, permission-based navigation
|
||||||
- [ ] **E2E Tests:** Complete user navigation flow
|
- [ ] **E2E Tests:** Complete user navigation flow
|
||||||
|
|
||||||
#### **F1.T2 Dashboard Test Suite**
|
- **[ ] F1.T2 Dashboard Test Suite**
|
||||||
|
|
||||||
- [ ] **Unit Tests:** Dashboard components, data formatting
|
- [ ] **Unit Tests:** Dashboard components, data formatting
|
||||||
- [ ] **Integration Tests:** Data fetching and display, real-time updates
|
- [ ] **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:** การจัดการผู้ใช้และสิทธิ์แบบสมบูรณ์
|
**Milestone:** การจัดการผู้ใช้และสิทธิ์แบบสมบูรณ์
|
||||||
|
|
||||||
### **Phase 2: Tasks**
|
### **Phase 2: Tasks**
|
||||||
|
|
||||||
- **[ ] F2.1 User Profile & Settings**
|
- **[ ] F2.1 User Profile & Settings**
|
||||||
|
|
||||||
- [ ] Create user profile page:
|
- [ ] Create user profile page:
|
||||||
- [ ] Personal information display/edit
|
- [ ] Personal information display/edit
|
||||||
- [ ] Password change functionality
|
- [ ] Password change functionality
|
||||||
@@ -414,6 +368,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F1.1, F0.4
|
- [ ] **Dependencies:** F1.1, F0.4
|
||||||
|
|
||||||
- **[ ] F2.2 Admin Panel - User Management**
|
- **[ ] F2.2 Admin Panel - User Management**
|
||||||
|
|
||||||
- [ ] Create user list with search and filters
|
- [ ] Create user list with search and filters
|
||||||
- [ ] Implement user creation form
|
- [ ] Implement user creation form
|
||||||
- [ ] Create user edit interface
|
- [ ] Create user edit interface
|
||||||
@@ -424,6 +379,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F1.1, F2.1
|
- [ ] **Dependencies:** F1.1, F2.1
|
||||||
|
|
||||||
- **[ ] F2.3 Admin Panel - Role Management**
|
- **[ ] F2.3 Admin Panel - Role Management**
|
||||||
|
|
||||||
- [ ] Create role list and management interface
|
- [ ] Create role list and management interface
|
||||||
- [ ] Implement role creation and editing
|
- [ ] Implement role creation and editing
|
||||||
- [ ] Create permission assignment interface
|
- [ ] Create permission assignment interface
|
||||||
@@ -446,22 +402,22 @@ F10_3 --> F10_4
|
|||||||
|
|
||||||
### **Phase 2: User Management & Admin Panel (สัปดาห์ที่ 3)**
|
### **Phase 2: User Management & Admin Panel (สัปดาห์ที่ 3)**
|
||||||
|
|
||||||
* **[ ] F2.5 Admin Panel - Master Data Management (Req 6B)** (New)
|
- **[ ] F2.5 Admin Panel - Master Data Management (Req 6B)** (New)
|
||||||
* [ ] Create "Disciplines Management" page (CRUD)
|
- [ ] Create "Disciplines Management" page (CRUD)
|
||||||
* [ ] Create "Sub-Types Management" page (CRUD + Mapping Number)
|
- [ ] Create "Sub-Types Management" page (CRUD + Mapping Number)
|
||||||
* [ ] Create "Numbering Format" configuration page (Template Editor)
|
- [ ] Create "Numbering Format" configuration page (Template Editor)
|
||||||
* [ ] **Deliverable:** UI for Admin to configure system master data
|
- [ ] **Deliverable:** UI for Admin to configure system master data
|
||||||
* [ ] **Dependencies:** F2.1
|
- [ ] **Dependencies:** F2.1
|
||||||
|
|
||||||
### **Phase 2: Testing - User Management**
|
### **Phase 2: Testing - User Management**
|
||||||
|
|
||||||
#### **F2.T1 User Management Test Suite**
|
- **[ ] F2.T1 User Management Test Suite**
|
||||||
|
|
||||||
- [ ] **Unit Tests:** User CRUD operations, form validation
|
- [ ] **Unit Tests:** User CRUD operations, form validation
|
||||||
- [ ] **Integration Tests:** User-role assignment, permission propagation
|
- [ ] **Integration Tests:** User-role assignment, permission propagation
|
||||||
- [ ] **Security Tests:** Permission escalation attempts, admin access control
|
- [ ] **Security Tests:** Permission escalation attempts, admin access control
|
||||||
|
|
||||||
#### **F2.T2 RBAC Test Suite**
|
- **[ ] F2.T2 RBAC Test Suite**
|
||||||
|
|
||||||
- [ ] **Unit Tests:** Permission checks, role validation
|
- [ ] **Unit Tests:** Permission checks, role validation
|
||||||
- [ ] **Integration Tests:** Multi-level permission enforcement, UI element protection
|
- [ ] **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:** การจัดการโครงสร้างโปรเจกต์และองค์กร
|
**Milestone:** การจัดการโครงสร้างโปรเจกต์และองค์กร
|
||||||
|
|
||||||
### **Phase 3: Tasks**
|
### **Phase 3: Tasks**
|
||||||
|
|
||||||
- **[ ] F3.1 Project Management UI**
|
- **[ ] F3.1 Project Management UI**
|
||||||
|
|
||||||
- [ ] Create project list with search and filters
|
- [ ] Create project list with search and filters
|
||||||
- [ ] Implement project creation and editing
|
- [ ] Implement project creation and editing
|
||||||
- [ ] Create project detail view
|
- [ ] Create project detail view
|
||||||
@@ -486,6 +443,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F1.1, F2.4
|
- [ ] **Dependencies:** F1.1, F2.4
|
||||||
|
|
||||||
- **[ ] F3.2 Organization Management**
|
- **[ ] F3.2 Organization Management**
|
||||||
|
|
||||||
- [ ] Create organization list and management
|
- [ ] Create organization list and management
|
||||||
- [ ] Implement organization creation and editing
|
- [ ] Implement organization creation and editing
|
||||||
- [ ] Create organization detail view
|
- [ ] Create organization detail view
|
||||||
@@ -507,21 +465,21 @@ F10_3 --> F10_4
|
|||||||
|
|
||||||
### **Phase 3: Testing - Project Structure**
|
### **Phase 3: Testing - Project Structure**
|
||||||
|
|
||||||
#### **F3.T1 Project Management Test Suite**
|
- **[ ] F3.T1 Project Management Test Suite**
|
||||||
|
- [ ] **Unit Tests:** Project CRUD operations, validation
|
||||||
- [ ] **Unit Tests:** Project CRUD operations, validation
|
- [ ] **Integration Tests:** Project-organization relationships, member management
|
||||||
- [ ] **Integration Tests:** Project-organization relationships, member management
|
- [ ] **Business Logic Tests:** Project hierarchy, access control
|
||||||
- [ ] **Business Logic Tests:** Project hierarchy, access control
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Phase 4: Correspondence System (สัปดาห์ที่ 5-6)**
|
### **Phase 4: Correspondence System (สัปดาห์ที่ 5-6)**
|
||||||
|
|
||||||
**Milestone:** ระบบจัดการเอกสารโต้ตอบแบบสมบูรณ์
|
**Milestone:** ระบบจัดการเอกสารโต้ตอบแบบสมบูรณ์
|
||||||
|
|
||||||
### **Phase 4: Tasks**
|
### **Phase 4: Tasks**
|
||||||
|
|
||||||
- **[ ] F4.1 Correspondence List & Search**
|
- **[ ] F4.1 Correspondence List & Search**
|
||||||
|
|
||||||
- [ ] Create correspondence list with advanced filtering:
|
- [ ] Create correspondence list with advanced filtering:
|
||||||
- [ ] Filter by type, status, project, organization
|
- [ ] Filter by type, status, project, organization
|
||||||
- [ ] Search by title, document number, content
|
- [ ] Search by title, document number, content
|
||||||
@@ -536,6 +494,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F1.1, F3.1
|
- [ ] **Dependencies:** F1.1, F3.1
|
||||||
|
|
||||||
- **[ ] F4.2 Correspondence Creation Form**
|
- **[ ] F4.2 Correspondence Creation Form**
|
||||||
|
|
||||||
- [ ] Create dynamic form generator based on JSON schema
|
- [ ] Create dynamic form generator based on JSON schema
|
||||||
- [ ] Implement form with multiple sections:
|
- [ ] Implement form with multiple sections:
|
||||||
- [ ] Basic information (type, title, recipients)
|
- [ ] Basic information (type, title, recipients)
|
||||||
@@ -553,6 +512,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F0.4, F4.1
|
- [ ] **Dependencies:** F0.4, F4.1
|
||||||
|
|
||||||
- **[ ] F4.3 Correspondence Detail View**
|
- **[ ] F4.3 Correspondence Detail View**
|
||||||
|
|
||||||
- [ ] Create comprehensive detail page:
|
- [ ] Create comprehensive detail page:
|
||||||
- [ ] Document header with metadata
|
- [ ] Document header with metadata
|
||||||
- [ ] Content display based on type
|
- [ ] Content display based on type
|
||||||
@@ -583,27 +543,27 @@ F10_3 --> F10_4
|
|||||||
|
|
||||||
### **Phase 4: Testing - Correspondence System**
|
### **Phase 4: Testing - Correspondence System**
|
||||||
|
|
||||||
#### **F4.T1 Correspondence Test Suite**
|
- **[ ] F4.T1 Correspondence Test Suite**
|
||||||
|
|
||||||
- [ ] **Unit Tests:** Form validation, file upload components
|
- [ ] **Unit Tests:** Form validation, file upload components
|
||||||
- [ ] **Integration Tests:** Complete document lifecycle, file attachment flow
|
- [ ] **Integration Tests:** Complete document lifecycle, file attachment flow
|
||||||
- [ ] **E2E Tests:** End-to-end correspondence creation and management
|
- [ ] **E2E Tests:** End-to-end correspondence creation and management
|
||||||
|
|
||||||
#### **F4.T2 File Upload Test Suite**
|
- **[ ] F4.T2 File Upload Test Suite**
|
||||||
|
- [ ] **Unit Tests:** File validation, type checking
|
||||||
- [ ] **Unit Tests:** File validation, type checking
|
- [ ] **Integration Tests:** Two-phase upload process, virus scan integration
|
||||||
- [ ] **Integration Tests:** Two-phase upload process, virus scan integration
|
- [ ] **Security Tests:** Malicious file upload attempts, security feedback
|
||||||
- [ ] **Security Tests:** Malicious file upload attempts, security feedback
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Phase 5: Workflow Management (สัปดาห์ที่ 7)**
|
### **Phase 5: Workflow Management (สัปดาห์ที่ 7)**
|
||||||
|
|
||||||
**Milestone:** ระบบ Visualization และจัดการ Workflow
|
**Milestone:** ระบบ Visualization และจัดการ Workflow
|
||||||
|
|
||||||
### **Phase 5: Tasks**
|
### **Phase 5: Tasks**
|
||||||
|
|
||||||
- **[ ] F5.1 Workflow Visualization Component**
|
- **[ ] F5.1 Workflow Visualization Component**
|
||||||
|
|
||||||
- [ ] Create horizontal workflow progress visualization
|
- [ ] Create horizontal workflow progress visualization
|
||||||
- [ ] Implement step status indicators (pending, active, completed, skipped)
|
- [ ] Implement step status indicators (pending, active, completed, skipped)
|
||||||
- [ ] Add due date and assignee information
|
- [ ] Add due date and assignee information
|
||||||
@@ -614,6 +574,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F4.3
|
- [ ] **Dependencies:** F4.3
|
||||||
|
|
||||||
- **[ ] F5.2 Routing Template Management**
|
- **[ ] F5.2 Routing Template Management**
|
||||||
|
|
||||||
- [ ] Create routing template list and editor
|
- [ ] Create routing template list and editor
|
||||||
- [ ] Implement drag-and-drop step configuration
|
- [ ] Implement drag-and-drop step configuration
|
||||||
- [ ] Add step configuration (purpose, duration, assignee rules)
|
- [ ] Add step configuration (purpose, duration, assignee rules)
|
||||||
@@ -624,6 +585,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F3.1, F4.2
|
- [ ] **Dependencies:** F3.1, F4.2
|
||||||
|
|
||||||
- **[ ] F5.3 Workflow Step Actions**
|
- **[ ] F5.3 Workflow Step Actions**
|
||||||
|
|
||||||
- [ ] Create step action interface:
|
- [ ] Create step action interface:
|
||||||
- [ ] Approve, reject, request changes
|
- [ ] Approve, reject, request changes
|
||||||
- [ ] Add comments and attachments
|
- [ ] Add comments and attachments
|
||||||
@@ -647,21 +609,21 @@ F10_3 --> F10_4
|
|||||||
|
|
||||||
### **Phase 5: Testing - Workflow Management**
|
### **Phase 5: Testing - Workflow Management**
|
||||||
|
|
||||||
#### **F5.T1 Workflow Test Suite**
|
- **[ ] F5.T1 Workflow Test Suite**
|
||||||
|
- [ ] **Unit Tests:** Workflow visualization, step status logic
|
||||||
- [ ] **Unit Tests:** Workflow visualization, step status logic
|
- [ ] **Integration Tests:** Complete workflow execution, real-time updates
|
||||||
- [ ] **Integration Tests:** Complete workflow execution, real-time updates
|
- [ ] **E2E Tests:** Multi-step workflow with different user roles
|
||||||
- [ ] **E2E Tests:** Multi-step workflow with different user roles
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Phase 6: Drawing System (สัปดาห์ที่ 8)**
|
### **Phase 6: Drawing System (สัปดาห์ที่ 8)**
|
||||||
|
|
||||||
**Milestone:** ระบบจัดการแบบแปลนแบบสมบูรณ์
|
**Milestone:** ระบบจัดการแบบแปลนแบบสมบูรณ์
|
||||||
|
|
||||||
### **Phase 6: Tasks**
|
### **Phase 6: Tasks**
|
||||||
|
|
||||||
- **[ ] F6.1 Contract Drawings Management**
|
- **[ ] F6.1 Contract Drawings Management**
|
||||||
|
|
||||||
- [ ] Create contract drawing list with categorization
|
- [ ] Create contract drawing list with categorization
|
||||||
- [ ] Implement drawing upload and metadata management
|
- [ ] Implement drawing upload and metadata management
|
||||||
- [ ] Create drawing preview and viewer
|
- [ ] Create drawing preview and viewer
|
||||||
@@ -672,6 +634,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F3.1, F4.4
|
- [ ] **Dependencies:** F3.1, F4.4
|
||||||
|
|
||||||
- **[ ] F6.2 Shop Drawings Management**
|
- **[ ] F6.2 Shop Drawings Management**
|
||||||
|
|
||||||
- [ ] Create shop drawing list with revision tracking
|
- [ ] Create shop drawing list with revision tracking
|
||||||
- [ ] Implement shop drawing creation and revision system
|
- [ ] Implement shop drawing creation and revision system
|
||||||
- [ ] Create drawing comparison interface
|
- [ ] Create drawing comparison interface
|
||||||
@@ -682,6 +645,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F6.1
|
- [ ] **Dependencies:** F6.1
|
||||||
|
|
||||||
- **[ ] F6.3 Drawing Revision System**
|
- **[ ] F6.3 Drawing Revision System**
|
||||||
|
|
||||||
- [ ] Create revision history interface
|
- [ ] Create revision history interface
|
||||||
- [ ] Implement revision comparison functionality
|
- [ ] Implement revision comparison functionality
|
||||||
- [ ] Add revision notes and change tracking
|
- [ ] Add revision notes and change tracking
|
||||||
@@ -703,21 +667,21 @@ F10_3 --> F10_4
|
|||||||
|
|
||||||
### **Phase 6: Testing - Drawing System**
|
### **Phase 6: Testing - Drawing System**
|
||||||
|
|
||||||
#### **F6.T1 Drawing Management Test Suite**
|
- **[ ] F6.T1 Drawing Management Test Suite**
|
||||||
|
- [ ] **Unit Tests:** Drawing CRUD operations, revision logic
|
||||||
- [ ] **Unit Tests:** Drawing CRUD operations, revision logic
|
- [ ] **Integration Tests:** Drawing approval workflows, reference management
|
||||||
- [ ] **Integration Tests:** Drawing approval workflows, reference management
|
- [ ] **E2E Tests:** Complete drawing lifecycle with revisions
|
||||||
- [ ] **E2E Tests:** Complete drawing lifecycle with revisions
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Phase 7: RFA System (สัปดาห์ที่ 9-10)**
|
### **Phase 7: RFA System (สัปดาห์ที่ 9-10)**
|
||||||
|
|
||||||
**Milestone:** ระบบขออนุมัติแบบสมบูรณ์พร้อม Dynamic Forms
|
**Milestone:** ระบบขออนุมัติแบบสมบูรณ์พร้อม Dynamic Forms
|
||||||
|
|
||||||
### **Phase 7: Tasks**
|
### **Phase 7: Tasks**
|
||||||
|
|
||||||
- **[ ] F7.1 RFA List & Dashboard**
|
- **[ ] F7.1 RFA List & Dashboard**
|
||||||
|
|
||||||
- [ ] Create RFA dashboard with status overview
|
- [ ] Create RFA dashboard with status overview
|
||||||
- [ ] Implement advanced RFA filtering and search
|
- [ ] Implement advanced RFA filtering and search
|
||||||
- [ ] Create RFA calendar view for deadlines
|
- [ ] Create RFA calendar view for deadlines
|
||||||
@@ -728,6 +692,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F4.1, F5.1
|
- [ ] **Dependencies:** F4.1, F5.1
|
||||||
|
|
||||||
- **[ ] F7.2 RFA Creation with Dynamic Forms**
|
- **[ ] F7.2 RFA Creation with Dynamic Forms**
|
||||||
|
|
||||||
- [ ] Create RFA type-specific form generator
|
- [ ] Create RFA type-specific form generator
|
||||||
- [ ] Implement dynamic form fields based on RFA type:
|
- [ ] Implement dynamic form fields based on RFA type:
|
||||||
- [ ] RFA_DWG: Shop drawing selection
|
- [ ] RFA_DWG: Shop drawing selection
|
||||||
@@ -747,6 +712,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F4.2, F6.2
|
- [ ] **Dependencies:** F4.2, F6.2
|
||||||
|
|
||||||
- **[ ] F7.3 RFA Workflow Integration**
|
- **[ ] F7.3 RFA Workflow Integration**
|
||||||
|
|
||||||
- [ ] Integrate RFA with unified workflow engine
|
- [ ] Integrate RFA with unified workflow engine
|
||||||
- [ ] Create RFA-specific workflow steps and actions
|
- [ ] Create RFA-specific workflow steps and actions
|
||||||
- [ ] Implement RFA approval interface
|
- [ ] Implement RFA approval interface
|
||||||
@@ -768,21 +734,21 @@ F10_3 --> F10_4
|
|||||||
|
|
||||||
### **Phase 7: Testing - RFA System**
|
### **Phase 7: Testing - RFA System**
|
||||||
|
|
||||||
#### **F7.T1 RFA Test Suite**
|
- **[ ] F7.T1 RFA Test Suite**
|
||||||
|
- [ ] **Unit Tests:** RFA form generation, validation logic
|
||||||
- [ ] **Unit Tests:** RFA form generation, validation logic
|
- [ ] **Integration Tests:** Complete RFA lifecycle, workflow integration
|
||||||
- [ ] **Integration Tests:** Complete RFA lifecycle, workflow integration
|
- [ ] **E2E Tests:** Multi-type RFA creation and approval workflows
|
||||||
- [ ] **E2E Tests:** Multi-type RFA creation and approval workflows
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Phase 8: Internal Workflows (สัปดาห์ที่ 11)**
|
### **Phase 8: Internal Workflows (สัปดาห์ที่ 11)**
|
||||||
|
|
||||||
**Milestone:** ระบบใบเวียนและการจัดการงานภายใน
|
**Milestone:** ระบบใบเวียนและการจัดการงานภายใน
|
||||||
|
|
||||||
### **Phase 8: Tasks**
|
### **Phase 8: Tasks**
|
||||||
|
|
||||||
- **[ ] F8.1 Circulation Management**
|
- **[ ] F8.1 Circulation Management**
|
||||||
|
|
||||||
- [ ] Create circulation list and management interface
|
- [ ] Create circulation list and management interface
|
||||||
- [ ] Implement circulation creation from correspondence
|
- [ ] Implement circulation creation from correspondence
|
||||||
- [ ] Create circulation template management
|
- [ ] Create circulation template management
|
||||||
@@ -793,6 +759,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F4.1, F5.2
|
- [ ] **Dependencies:** F4.1, F5.2
|
||||||
|
|
||||||
- **[ ] F8.2 Task Assignment Interface**
|
- **[ ] F8.2 Task Assignment Interface**
|
||||||
|
|
||||||
- [ ] Create task assignment interface with user selection
|
- [ ] Create task assignment interface with user selection
|
||||||
- [ ] Implement task priority and deadline setting
|
- [ ] Implement task priority and deadline setting
|
||||||
- [ ] Add task dependency management
|
- [ ] Add task dependency management
|
||||||
@@ -814,21 +781,21 @@ F10_3 --> F10_4
|
|||||||
|
|
||||||
### **Phase 8: Testing - Internal Workflows**
|
### **Phase 8: Testing - Internal Workflows**
|
||||||
|
|
||||||
#### **F8.T1 Circulation Test Suite**
|
- **[ ] F8.T1 Circulation Test Suite**
|
||||||
|
- [ ] **Unit Tests:** Circulation creation, task assignment logic
|
||||||
- [ ] **Unit Tests:** Circulation creation, task assignment logic
|
- [ ] **Integration Tests:** Complete circulation workflow, internal approvals
|
||||||
- [ ] **Integration Tests:** Complete circulation workflow, internal approvals
|
- [ ] **E2E Tests:** End-to-end circulation with task completion
|
||||||
- [ ] **E2E Tests:** End-to-end circulation with task completion
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Phase 9: Advanced Features (สัปดาห์ที่ 12)**
|
### **Phase 9: Advanced Features (สัปดาห์ที่ 12)**
|
||||||
|
|
||||||
**Milestone:** ฟีเจอร์ขั้นสูงและการปรับปรุงประสบการณ์ผู้ใช้
|
**Milestone:** ฟีเจอร์ขั้นสูงและการปรับปรุงประสบการณ์ผู้ใช้
|
||||||
|
|
||||||
### **Phase 9: Tasks**
|
### **Phase 9: Tasks**
|
||||||
|
|
||||||
- **[ ] F9.1 Advanced Search Interface**
|
- **[ ] F9.1 Advanced Search Interface**
|
||||||
|
|
||||||
- [ ] Create unified search interface across all document types
|
- [ ] Create unified search interface across all document types
|
||||||
- [ ] Implement faceted search with multiple filters
|
- [ ] Implement faceted search with multiple filters
|
||||||
- [ ] Add search result highlighting and relevance scoring
|
- [ ] Add search result highlighting and relevance scoring
|
||||||
@@ -839,6 +806,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F4.1, F7.1
|
- [ ] **Dependencies:** F4.1, F7.1
|
||||||
|
|
||||||
- **[ ] F9.2 Notification System**
|
- **[ ] F9.2 Notification System**
|
||||||
|
|
||||||
- [ ] Create notification center with real-time updates
|
- [ ] Create notification center with real-time updates
|
||||||
- [ ] Implement notification preferences management
|
- [ ] Implement notification preferences management
|
||||||
- [ ] Add notification grouping and digest views
|
- [ ] Add notification grouping and digest views
|
||||||
@@ -849,6 +817,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F1.3, F5.4
|
- [ ] **Dependencies:** F1.3, F5.4
|
||||||
|
|
||||||
- **[ ] F9.3 Reporting & Analytics**
|
- **[ ] F9.3 Reporting & Analytics**
|
||||||
|
|
||||||
- [ ] Create reporting dashboard with customizable widgets
|
- [ ] Create reporting dashboard with customizable widgets
|
||||||
- [ ] Implement data visualization components (charts, graphs)
|
- [ ] Implement data visualization components (charts, graphs)
|
||||||
- [ ] Add report scheduling and export
|
- [ ] Add report scheduling and export
|
||||||
@@ -870,21 +839,21 @@ F10_3 --> F10_4
|
|||||||
|
|
||||||
### **Phase 9: Testing - Advanced Features**
|
### **Phase 9: Testing - Advanced Features**
|
||||||
|
|
||||||
#### **F9.T1 Advanced Features Test Suite**
|
- **[ ] F9.T1 Advanced Features Test Suite**
|
||||||
|
- [ ] **Unit Tests:** Search algorithms, notification logic
|
||||||
- [ ] **Unit Tests:** Search algorithms, notification logic
|
- [ ] **Integration Tests:** Cross-module search, real-time notifications
|
||||||
- [ ] **Integration Tests:** Cross-module search, real-time notifications
|
- [ ] **Performance Tests:** Search performance, mobile responsiveness
|
||||||
- [ ] **Performance Tests:** Search performance, mobile responsiveness
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **Phase 10: Testing & Polish (สัปดาห์ที่ 13-14)**
|
### **Phase 10: Testing & Polish (สัปดาห์ที่ 13-14)**
|
||||||
|
|
||||||
**Milestone:** แอปพลิเคชันที่ผ่านการทดสอบและปรับปรุงอย่างสมบูรณ์
|
**Milestone:** แอปพลิเคชันที่ผ่านการทดสอบและปรับปรุงอย่างสมบูรณ์
|
||||||
|
|
||||||
### **Phase 10: Tasks**
|
### **Phase 10: Tasks**
|
||||||
|
|
||||||
- **[ ] F10.1 Comprehensive Testing**
|
- **[ ] F10.1 Comprehensive Testing**
|
||||||
|
|
||||||
- [ ] Idempotency Testing: เพิ่มการทดสอบเฉพาะสำหรับ Axios Interceptor เพื่อจำลองการส่ง Request POST/PUT/DELETE ที่มี Idempotency-Key ซ้ำไปยัง Mock API (MSW) เพื่อยืนยันว่า Client-side ไม่ส่ง Key ซ้ำในการทำงานปกติ และไม่เกิด Side Effect จากการ Replay Attack.
|
- [ ] 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
|
- [ ] Write unit tests for all components and utilities
|
||||||
- [ ] Create integration tests for critical user flows
|
- [ ] Create integration tests for critical user flows
|
||||||
@@ -896,6 +865,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** All previous phases
|
- [ ] **Dependencies:** All previous phases
|
||||||
|
|
||||||
- **[ ] F10.2 Performance Optimization**
|
- **[ ] F10.2 Performance Optimization**
|
||||||
|
|
||||||
- [ ] Implement code splitting and lazy loading
|
- [ ] Implement code splitting and lazy loading
|
||||||
- [ ] Optimize bundle size and asset delivery
|
- [ ] Optimize bundle size and asset delivery
|
||||||
- [ ] Add performance monitoring and metrics
|
- [ ] Add performance monitoring and metrics
|
||||||
@@ -906,6 +876,7 @@ F10_3 --> F10_4
|
|||||||
- [ ] **Dependencies:** F10.1
|
- [ ] **Dependencies:** F10.1
|
||||||
|
|
||||||
- **[ ] F10.3 Security Hardening**
|
- **[ ] F10.3 Security Hardening**
|
||||||
|
|
||||||
- [ ] Conduct security audit and penetration testing
|
- [ ] Conduct security audit and penetration testing
|
||||||
- [ ] Implement Content Security Policy (CSP)
|
- [ ] Implement Content Security Policy (CSP)
|
||||||
- [ ] Add security headers and protections
|
- [ ] Add security headers and protections
|
||||||
@@ -927,19 +898,18 @@ F10_3 --> F10_4
|
|||||||
|
|
||||||
### **Phase 10: Testing - Final Validation**
|
### **Phase 10: Testing - Final Validation**
|
||||||
|
|
||||||
#### **F10.T1 Final Test Suite**
|
- **[ ] F10.T1 Final Test Suite**
|
||||||
|
- [ ] **Performance Tests:** Load testing, stress testing
|
||||||
- [ ] **Performance Tests:** Load testing, stress testing
|
- [ ] **Security Tests:** Final security audit, vulnerability assessment
|
||||||
- [ ] **Security Tests:** Final security audit, vulnerability assessment
|
- [ ] **User Acceptance Tests:** Real user testing, feedback incorporation
|
||||||
- [ ] **User Acceptance Tests:** Real user testing, feedback incorporation
|
- [ ] **Compatibility Tests:** Cross-browser, cross-device testing
|
||||||
- [ ] **Compatibility Tests:** Cross-browser, cross-device testing
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📊 **สรุป Timeline**
|
## 📊 **สรุป Timeline**
|
||||||
|
|
||||||
| Phase | ระยะเวลา | จำนวนงาน | Output หลัก |
|
| Phase | ระยะเวลา | จำนวนงาน | Output หลัก |
|
||||||
| -------- | ------------ | ------------ | ------------------------------------ |
|
| -------- | -------------- | ------------ | ------------------------------------ |
|
||||||
| Phase 0 | 1 สัปดาห์ | 4 | Foundation & Tooling Ready |
|
| Phase 0 | 1 สัปดาห์ | 4 | Foundation & Tooling Ready |
|
||||||
| Phase 1 | 1 สัปดาห์ | 4 | Core Application Structure |
|
| Phase 1 | 1 สัปดาห์ | 4 | Core Application Structure |
|
||||||
| Phase 2 | 1 สัปดาห์ | 4 | User Management & Security |
|
| Phase 2 | 1 สัปดาห์ | 4 | User Management & Security |
|
||||||
|
|||||||
@@ -485,26 +485,29 @@
|
|||||||
|
|
||||||
**Purpose**: Child table storing revision history of correspondences (1:N)
|
**Purpose**: Child table storing revision history of correspondences (1:N)
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| ------------------------ | ------------ | --------------------------------- | -------------------------------------------------------- |
|
| ------------------------ | ------------ | --------------------------------- | ------------------------------------------------------------ |
|
||||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
|
||||||
| correspondence_id | INT | NOT NULL, FK | Master correspondence ID |
|
| correspondence_id | INT | NOT NULL, FK | Master correspondence ID |
|
||||||
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
|
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
|
||||||
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) |
|
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) |
|
||||||
| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag |
|
| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag |
|
||||||
| correspondence_status_id | INT | NOT NULL, FK | Current status of this revision |
|
| correspondence_status_id | INT | NOT NULL, FK | Current status of this revision |
|
||||||
| title | VARCHAR(255) | NOT NULL | Document title |
|
| title | VARCHAR(255) | NOT NULL | Document title |
|
||||||
| document_date | DATE | NULL | Document date |
|
| document_date | DATE | NULL | Document date |
|
||||||
| issued_date | DATETIME | NULL | Issue date |
|
| issued_date | DATETIME | NULL | Issue date |
|
||||||
| received_date | DATETIME | NULL | Received date |
|
| received_date | DATETIME | NULL | Received date |
|
||||||
| due_date | DATETIME | NULL | Due date for response |
|
| due_date | DATETIME | NULL | Due date for response |
|
||||||
| description | TEXT | NULL | Revision description |
|
| description | TEXT | NULL | Revision description |
|
||||||
| details | JSON | NULL | Type-specific details (e.g., RFI questions) |
|
| details | JSON | NULL | Type-specific details (e.g., RFI questions) |
|
||||||
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
|
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
|
||||||
| created_by | INT | NULL, FK | User who created revision |
|
| created_by | INT | NULL, FK | User who created revision |
|
||||||
| updated_by | INT | NULL, FK | User who last updated |
|
| 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_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**:
|
**Indexes**:
|
||||||
|
|
||||||
@@ -521,8 +524,9 @@
|
|||||||
- INDEX (issued_date)
|
- INDEX (issued_date)
|
||||||
- INDEX (v_ref_project_id)
|
- INDEX (v_ref_project_id)
|
||||||
- INDEX (v_ref_type)
|
- INDEX (v_ref_type)
|
||||||
|
- INDEX (v_doc_subtype)
|
||||||
|
|
||||||
**Relationships**:
|
- **Relationships**:
|
||||||
|
|
||||||
- Parent: correspondences, correspondence_status, users
|
- Parent: correspondences, correspondence_status, users
|
||||||
|
|
||||||
@@ -638,16 +642,16 @@
|
|||||||
|
|
||||||
**Purpose**: เก็บข้อมูลแม่แบบ (Template) ของสายงานการส่งต่อเอกสารเพื่อขออนุมัติ ทำให้ไม่ต้องกำหนดขั้นตอนซ้ำทุกครั้ง สามารถสร้างเป็นแม่แบบทั่วไป หรือเฉพาะสำหรับโครงการใดโครงการหนึ่งได้
|
**Purpose**: เก็บข้อมูลแม่แบบ (Template) ของสายงานการส่งต่อเอกสารเพื่อขออนุมัติ ทำให้ไม่ต้องกำหนดขั้นตอนซ้ำทุกครั้ง สามารถสร้างเป็นแม่แบบทั่วไป หรือเฉพาะสำหรับโครงการใดโครงการหนึ่งได้
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| --------------- | ------------ | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------ |
|
| --------------- | ------------ | --------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
|
||||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลัก (Primary Key) ของแม่แบบ รันค่าอัตโนมัติ |
|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลัก (Primary Key) ของแม่แบบ รันค่าอัตโนมัติ |
|
||||||
| template_name | VARCHAR(255) | NOT NULL | ชื่อของแม่แบบ เช่น "เสนอโครงการ", "ขออนุมัติจัดซื้อ" |
|
| template_name | VARCHAR(255) | NOT NULL | ชื่อของแม่แบบ เช่น "เสนอโครงการ", "ขออนุมัติจัดซื้อ" |
|
||||||
| description | TEXT | NULL | คำอธิบายรายละเอียดเกี่ยวกับแม่แบบนี้ |
|
| description | TEXT | NULL | คำอธิบายรายละเอียดเกี่ยวกับแม่แบบนี้ |
|
||||||
| project_id | INT | NULL | ID ของโครงการที่แม่แบบนี้สังกัดอยู่ (ถ้ามี) **ค่า NULL หมายถึง** เป็น "แม่แบบทั่วไป" ที่สามารถใช้กับทุกโครงการได้ |
|
| project_id | INT | NULL | ID ของโครงการที่แม่แบบนี้สังกัดอยู่ (ถ้ามี) **ค่า NULL หมายถึง** เป็น "แม่แบบทั่วไป" ที่สามารถใช้กับทุกโครงการได้ |
|
||||||
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | วันที่และเวลาที่สร้างแม่แบบนี้ |
|
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | วันที่และเวลาที่สร้างแม่แบบนี้ |
|
||||||
| updated_at | TIMESTAMP | NOT NULL,`DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | วันที่และเวลาที่แก้ไขข้อมูลในแม่แบบนี้ล่าสุด |
|
| updated_at | TIMESTAMP | NOT NULL,`DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP | วันที่และเวลาที่แก้ไขข้อมูลในแม่แบบนี้ล่าสุด |
|
||||||
| is_active | BOOLEAN | DEFAULT TRUE | สถานะใช้งาน |
|
| is_active | BOOLEAN | DEFAULT TRUE | สถานะใช้งาน |
|
||||||
| workflow_config | JSON | NULL | เก็บ State Machine Configuration หรือ Rules เพิ่มเติมที่ซับซ้อนกว่า Column ปกติ |
|
| workflow_config | JSON | NULL | เก็บ State Machine Configuration หรือ Rules เพิ่มเติมที่ซับซ้อนกว่า Column ปกติ |
|
||||||
|
|
||||||
**Indexes**:
|
**Indexes**:
|
||||||
|
|
||||||
@@ -664,14 +668,14 @@
|
|||||||
|
|
||||||
**Purpose**: เก็บรายละเอียดของแต่ละขั้นตอน (Steps) ภายในแม่แบบสายงาน (correspondence_routing_templates) กำหนดว่าจะส่งไปที่องค์กรไหน ลำดับเป็นเท่าไร และเพื่อวัตถุประสงค์อะไร
|
**Purpose**: เก็บรายละเอียดของแต่ละขั้นตอน (Steps) ภายในแม่แบบสายงาน (correspondence_routing_templates) กำหนดว่าจะส่งไปที่องค์กรไหน ลำดับเป็นเท่าไร และเพื่อวัตถุประสงค์อะไร
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| :----------------- | --------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
|
| :----------------- | --------- | --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลักของขั้นตอน |
|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลักของขั้นตอน |
|
||||||
| template_id | INT | NOT NULL | ID ของแม่แบบที่ขั้นตอนนี้สังกัดอยู่ |
|
| template_id | INT | NOT NULL | ID ของแม่แบบที่ขั้นตอนนี้สังกัดอยู่ |
|
||||||
| sequence | INT | NOT NULL | ลำดับของขั้นตอน (1, 2, 3, ...) |
|
| sequence | INT | NOT NULL | ลำดับของขั้นตอน (1, 2, 3, ...) |
|
||||||
| to_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: เพื่อทราบ] |
|
| step_purpose | ENUM | NOT NULL,DEFAULT FOR_REVIEW | วัตถุประสงค์ของการส่งต่อในขั้นตอนนี้ **ค่าที่เป็นไปได้:** [FOR_APPROVAL: เพื่ออนุมัติ, FOR_REVIEW: เพื่อตรวจสอบ/พิจารณา, FOR_INFORMATION: เพื่อทราบ] |
|
||||||
| expected_days | INT | NULL | วันที่คาดหวัง |
|
| expected_days | INT | NULL | วันที่คาดหวัง |
|
||||||
|
|
||||||
**Indexes**:
|
**Indexes**:
|
||||||
|
|
||||||
@@ -690,22 +694,22 @@
|
|||||||
|
|
||||||
**Purpose**: เป็นตารางที่เก็บข้อมูลการส่งต่อเอกสารจริง (Instance/Run-time) ติดตามประวัติการเคลื่อนย้ายของแต่ละเอกสาร ว่าผ่านใครมาบ้าง อยู่ที่ใคร และสถานะปัจจุบันคืออะไร
|
**Purpose**: เป็นตารางที่เก็บข้อมูลการส่งต่อเอกสารจริง (Instance/Run-time) ติดตามประวัติการเคลื่อนย้ายของแต่ละเอกสาร ว่าผ่านใครมาบ้าง อยู่ที่ใคร และสถานะปัจจุบันคืออะไร
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| -------------------- | --------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| -------------------- | --------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลักของรายการส่งต่อ |
|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | ID หลักของรายการส่งต่อ |
|
||||||
| correspondence_id | INT | NOT NUL | ID ของเอกสาร (FK ไปยัง correspondence_revisions) |
|
| correspondence_id | INT | NOT NUL | ID ของเอกสาร (FK ไปยัง correspondence_revisions) |
|
||||||
| template_id | INT | NULL | ID ของแม่แบบที่ใช้สร้างสายงานนี้ (เก็บไว้เป็นข้อมูลอ้างอิง) |
|
| template_id | INT | NULL | ID ของแม่แบบที่ใช้สร้างสายงานนี้ (เก็บไว้เป็นข้อมูลอ้างอิง) |
|
||||||
| sequence | INT | NOT NULL | ลำดับของขั้นตอนการส่งต่อจริง |
|
| sequence | INT | NOT NULL | ลำดับของขั้นตอนการส่งต่อจริง |
|
||||||
| from_organization_id | INT | NOT NULL | ID ขององค์กรผู้ส่ง |
|
| from_organization_id | INT | NOT NULL | ID ขององค์กรผู้ส่ง |
|
||||||
| to_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: เพื่อดำเนินการ] |
|
| 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: ตอบกลับแล้ว] |
|
| status | ENUM | NOT NULL, DEFAULT SENT | [ACTIONED: ดำเนินการแล้ว, FORWARDED: ส่งต่อแล้ว, REPLIE: ตอบกลับแล้ว] |
|
||||||
| comments | TEXT | NULL | หมายเหตุหรือความคิดเห็นในการส่งต่อ |
|
| comments | TEXT | NULL | หมายเหตุหรือความคิดเห็นในการส่งต่อ |
|
||||||
| due_date | DATETIME | NULL | วันที่ครบกำหนดที่ต้องดำเนินการในขั้นตอนนี้ |
|
| due_date | DATETIME | NULL | วันที่ครบกำหนดที่ต้องดำเนินการในขั้นตอนนี้ |
|
||||||
| processed_by_user_id | INT | NULL | ID ของผู้ใช้ที่ดำเนินการในขั้นตอนนี้จริงๆ |
|
| processed_by_user_id | INT | NULL | ID ของผู้ใช้ที่ดำเนินการในขั้นตอนนี้จริงๆ |
|
||||||
| processed_at | TIMESTAMP | NULL | เวลาที่ผู้ใช้ดำเนินการเสร็จสิ้น |
|
| processed_at | TIMESTAMP | NULL | เวลาที่ผู้ใช้ดำเนินการเสร็จสิ้น |
|
||||||
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | เวลาที่สร้างรายการส่งต่อนี้ |
|
| created_at | TIMESTAMP | NOT NULL, DEFAULT CURRENT_TIMESTAMP | เวลาที่สร้างรายการส่งต่อนี้ |
|
||||||
| state_context | JSON | NULL | เก็บข้อมูล Context ของ Workflow ณ ขณะนั้น (Snapshot) |
|
| state_context | JSON | NULL | Snapshot ของตัวแปรต่างๆ ใน Workflow ณ จุดนี้ (เช่น ผู้อนุมัติ, เงื่อนไขที่ผ่าน) |
|
||||||
|
|
||||||
**Indexes**:
|
**Indexes**:
|
||||||
|
|
||||||
@@ -730,11 +734,11 @@
|
|||||||
|
|
||||||
**Purpose**: ตารางนี้ใช้กำหนดกฎ (State Machine) ว่าสถานะใดสามารถเปลี่ยนไปเป็นสถานะใดได้บ้าง โดยขึ้นอยู่กับประเภทของหนังสือ เพื่อควบคุมการไหลของสถานะให้ถูกต้องตามข้อบังคับ
|
**Purpose**: ตารางนี้ใช้กำหนดกฎ (State Machine) ว่าสถานะใดสามารถเปลี่ยนไปเป็นสถานะใดได้บ้าง โดยขึ้นอยู่กับประเภทของหนังสือ เพื่อควบคุมการไหลของสถานะให้ถูกต้องตามข้อบังคับ
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| -------------- | --------- | ----------- | ----------------------------------------------- |
|
| -------------- | --------- | ----------- | ------------------------------------------------------ |
|
||||||
| type_id | INT | PRIMARY KEY | ID ของประเภทหนังสือ (เช่น หนังสือภายใน, หนังสือภายนอก) |
|
| type_id | INT | PRIMARY KEY | ID ของประเภทหนังสือ (เช่น หนังสือภายใน, หนังสือภายนอก) |
|
||||||
| from_status_id | INT | PRIMARY KEY | ID ของสถานะต้นทาง (เช่น ร่าง) |
|
| from_status_id | INT | PRIMARY KEY | ID ของสถานะต้นทาง (เช่น ร่าง) |
|
||||||
| to_status_id | INT | PRIMARY KEY | ID ของสถานะปลายทาง (เช่น รออนุมัติ) |
|
| to_status_id | INT | PRIMARY KEY | ID ของสถานะปลายทาง (เช่น รออนุมัติ) |
|
||||||
|
|
||||||
**คีย์หลัก (Primary Key):**
|
**คีย์หลัก (Primary Key):**
|
||||||
|
|
||||||
@@ -890,25 +894,28 @@
|
|||||||
|
|
||||||
**Purpose**: Child table storing revision history of RFAs (1:N)
|
**Purpose**: Child table storing revision history of RFAs (1:N)
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| ------------------- | ------------ | --------------------------- | ---------------------------------------------- |
|
| ------------------- | ------------ | --------------------------------- | --------------------------------------------------------------- |
|
||||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique revision ID |
|
||||||
| correspondence_id | INT | NOT NULL, FK | Link to correspondence (RFA as correspondence) |
|
| correspondence_id | INT | NOT NULL, FK | Link to correspondence (RFA as correspondence) |
|
||||||
| rfa_id | INT | NOT NULL, FK | Master RFA ID |
|
| rfa_id | INT | NOT NULL, FK | Master RFA ID |
|
||||||
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
|
| revision_number | INT | NOT NULL | Revision sequence (0, 1, 2...) |
|
||||||
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) |
|
| revision_label | VARCHAR(10) | NULL | Display revision (A, B, 1.1...) |
|
||||||
| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag |
|
| is_current | BOOLEAN | DEFAULT FALSE | Current revision flag |
|
||||||
| rfa_status_code_id | INT | NOT NULL, FK | Current RFA status |
|
| rfa_status_code_id | INT | NOT NULL, FK | Current RFA status |
|
||||||
| rfa_approve_code_id | INT | NULL, FK | Approval result code |
|
| rfa_approve_code_id | INT | NULL, FK | Approval result code |
|
||||||
| title | VARCHAR(255) | NOT NULL | RFA title |
|
| title | VARCHAR(255) | NOT NULL | RFA title |
|
||||||
| document_date | DATE | NULL | Document date |
|
| document_date | DATE | NULL | Document date |
|
||||||
| issued_date | DATE | NULL | Issue date for approval |
|
| issued_date | DATE | NULL | Issue date for approval |
|
||||||
| received_date | DATETIME | NULL | Received date |
|
| received_date | DATETIME | NULL | Received date |
|
||||||
| approved_date | DATE | NULL | Approval date |
|
| approved_date | DATE | NULL | Approval date |
|
||||||
| description | TEXT | NULL | Revision description |
|
| description | TEXT | NULL | Revision description |
|
||||||
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
|
| created_at | DATETIME | DEFAULT CURRENT_TIMESTAMP | Revision creation timestamp |
|
||||||
| created_by | INT | NULL, FK | User who created revision |
|
| created_by | INT | NULL, FK | User who created revision |
|
||||||
| updated_by | INT | NULL, FK | User who last updated |
|
| 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**:
|
**Indexes**:
|
||||||
|
|
||||||
@@ -924,6 +931,7 @@
|
|||||||
- INDEX (rfa_status_code_id)
|
- INDEX (rfa_status_code_id)
|
||||||
- INDEX (rfa_approve_code_id)
|
- INDEX (rfa_approve_code_id)
|
||||||
- INDEX (is_current)
|
- INDEX (is_current)
|
||||||
|
- INDEX (v_ref_drawing_count): ตัวอย่างการ Index ข้อมูลตัวเลขใน JSON
|
||||||
|
|
||||||
**Relationships**:
|
**Relationships**:
|
||||||
|
|
||||||
@@ -970,14 +978,14 @@
|
|||||||
|
|
||||||
**Purpose**: Master table for RFA approval workflow templates
|
**Purpose**: Master table for RFA approval workflow templates
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| --------------- | ------------ | ----------------------------------- | -------------------------------------------------------------------- |
|
| --------------- | ------------ | ----------------------------------- | ------------------------------------------------------------------------------- |
|
||||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique template ID |
|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique template ID |
|
||||||
| template_name | VARCHAR(100) | NOT NULL | Template name |
|
| template_name | VARCHAR(100) | NOT NULL | Template name |
|
||||||
| description | TEXT | NULL | Template description |
|
| description | TEXT | NULL | Template description |
|
||||||
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
|
| is_active | TINYINT(1) | DEFAULT 1 | Active status |
|
||||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
|
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
|
||||||
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
|
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
|
||||||
| workflow_config | JSON | NULL | เก็บ State Machine Configuration หรือ Rules เพิ่มเติมที่ซับซ้อนกว่า Column ปกติ |
|
| workflow_config | JSON | NULL | เก็บ State Machine Configuration หรือ Rules เพิ่มเติมที่ซับซ้อนกว่า Column ปกติ |
|
||||||
|
|
||||||
**Indexes**:
|
**Indexes**:
|
||||||
@@ -1037,20 +1045,20 @@
|
|||||||
|
|
||||||
**Purpose**: Transaction log table tracking actual RFA approval workflow execution
|
**Purpose**: Transaction log table tracking actual RFA approval workflow execution
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| --------------- | --------- | ----------------------------------- | ------------------------------------------------- |
|
| --------------- | --------- | ----------------------------------- | ---------------------------------------------------- |
|
||||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique workflow log ID |
|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique workflow log ID |
|
||||||
| rfa_revision_id | INT | NOT NULL, FK | Reference to RFA revision |
|
| rfa_revision_id | INT | NOT NULL, FK | Reference to RFA revision |
|
||||||
| step_number | INT | NOT NULL | Current step number |
|
| step_number | INT | NOT NULL | Current step number |
|
||||||
| organization_id | INT | NOT NULL, FK | Organization responsible |
|
| organization_id | INT | NOT NULL, FK | Organization responsible |
|
||||||
| assigned_to | INT | NULL, FK | Assigned user ID |
|
| assigned_to | INT | NULL, FK | Assigned user ID |
|
||||||
| action_type | ENUM | NULL | Action type: REVIEW, APPROVE, ACKNOWLEDGE |
|
| action_type | ENUM | NULL | Action type: REVIEW, APPROVE, ACKNOWLEDGE |
|
||||||
| status | ENUM | NULL | Status: PENDING, IN_PROGRESS, COMPLETED, REJECTED |
|
| status | ENUM | NULL | Status: PENDING, IN_PROGRESS, COMPLETED, REJECTED |
|
||||||
| comments | TEXT | NULL | Comments/remarks |
|
| comments | TEXT | NULL | Comments/remarks |
|
||||||
| completed_at | DATETIME | NULL | Completion timestamp |
|
| completed_at | DATETIME | NULL | Completion timestamp |
|
||||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
|
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Record creation timestamp |
|
||||||
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
|
| updated_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP ON UPDATE | Last update timestamp |
|
||||||
| state_context | JSON* | NULL | เก็บข้อมูล Context ของ Workflow ณ ขณะนั้น (Snapshot) |
|
| state_context | JSON\* | NULL | เก็บข้อมูล Context ของ Workflow ณ ขณะนั้น (Snapshot) |
|
||||||
|
|
||||||
**Indexes**:
|
**Indexes**:
|
||||||
|
|
||||||
@@ -1124,6 +1132,7 @@
|
|||||||
|
|
||||||
- Parent: contracts, correspondence_types
|
- Parent: contracts, correspondence_types
|
||||||
- Referenced by: (Used in logic for Document Numbering)
|
- Referenced by: (Used in logic for Document Numbering)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## **5. 📐 Drawings Tables (แบบ, หมวดหมู่)**
|
## **5. 📐 Drawings Tables (แบบ, หมวดหมู่)**
|
||||||
@@ -1737,20 +1746,20 @@
|
|||||||
|
|
||||||
**Purpose**: Central repository for all file attachments in the system
|
**Purpose**: Central repository for all file attachments in the system
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| ------------------- | ------------ | --------------------------- | -------------------------------------------------------------- |
|
| ------------------- | ------------ | --------------------------- | -------------------------------------------------------------------------- |
|
||||||
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique attachment ID |
|
| id | INT | PRIMARY KEY, AUTO_INCREMENT | Unique attachment ID |
|
||||||
| original_filename | VARCHAR(255) | NOT NULL | Original filename from upload |
|
| original_filename | VARCHAR(255) | NOT NULL | Original filename from upload |
|
||||||
| stored_filename | VARCHAR(255) | NOT NULL | System-generated unique filename |
|
| stored_filename | VARCHAR(255) | NOT NULL | System-generated unique filename |
|
||||||
| file_path | VARCHAR(500) | NOT NULL | Full file path on server (/share/dms-data/) |
|
| 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.) |
|
| mime_type | VARCHAR(100) | NOT NULL | MIME type (application/pdf, image/jpeg, etc.) |
|
||||||
| file_size | INT | NOT NULL | File size in bytes |
|
| file_size | INT | NOT NULL | File size in bytes |
|
||||||
| uploaded_by_user_id | INT | NOT NULL, FK | User who uploaded file |
|
| uploaded_by_user_id | INT | NOT NULL, FK | User who uploaded file |
|
||||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Upload timestamp |
|
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Upload timestamp |
|
||||||
| is_temporary | BOOLEAN | DEFAULT TRUE | ระบุว่าเป็นไฟล์ชั่วคราว (ยังไม่ได้ Commit) |
|
| is_temporary | BOOLEAN | DEFAULT TRUE | ระบุว่าเป็นไฟล์ชั่วคราว (ยังไม่ได้ Commit) |
|
||||||
| temp_id* | VARCHAR(100) | NULL | ID ชั่วคราวสำหรับอ้างอิงตอน Upload Phase 1 (อาจใช้ร่วมกับ id หรือแยกก็ได้) |
|
| temp_id\* | VARCHAR(100) | NULL | ID ชั่วคราวสำหรับอ้างอิงตอน Upload Phase 1 (อาจใช้ร่วมกับ id หรือแยกก็ได้) |
|
||||||
| expires_at | DATETIME | NULL | เวลาหมดอายุของไฟล์ Temp (เพื่อให้ Cron Job ลบออก) |
|
| expires_at | DATETIME | NULL | เวลาหมดอายุของไฟล์ Temp (เพื่อให้ Cron Job ลบออก) |
|
||||||
| checksum | VARCHAR(64) | NULL | SHA-256 Checksum สำหรับ Verify File Integrity [Req 3.9.3] |
|
| checksum | VARCHAR(64) | NULL | SHA-256 Checksum สำหรับ Verify File Integrity [Req 3.9.3] |
|
||||||
|
|
||||||
**Indexes**:
|
**Indexes**:
|
||||||
|
|
||||||
@@ -1929,15 +1938,15 @@
|
|||||||
|
|
||||||
**Purpose**: Transaction table maintaining running sequence numbers for document numbering
|
**Purpose**: Transaction table maintaining running sequence numbers for document numbering
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| -------------------------- | --------- | --------------- | ----------------------------------------------- |
|
| -------------------------- | --------- | --------------- | ---------------------------------------------------- |
|
||||||
| project_id | INT | PRIMARY KEY, FK | Reference to projects |
|
| project_id | INT | PRIMARY KEY, FK | Reference to projects |
|
||||||
| originator_organization_id | INT | PRIMARY KEY, FK | Originating organization |
|
| originator_organization_id | INT | PRIMARY KEY, FK | Originating organization |
|
||||||
| correspondence_type_id | INT | PRIMARY KEY, FK | Reference to correspondence types |
|
| correspondence_type_id | INT | PRIMARY KEY, FK | Reference to correspondence types |
|
||||||
| **discipline_id** | **INT** | **PRIMARY KEY** | **Discipline ID (0 if not applicable)** |
|
| **discipline_id** | **INT** | **PRIMARY KEY** | **Discipline ID (0 if not applicable)** |
|
||||||
| current_year | INT | PRIMARY KEY | Year (Buddhist calendar) |
|
| current_year | INT | PRIMARY KEY | Year (Buddhist calendar) |
|
||||||
| version | INT | DEFAULT 0 | ใช้สำหรับ Optimistic Locking (ตรวจสอบค่าก่อน Update) |
|
| 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**:
|
**Indexes**:
|
||||||
|
|
||||||
@@ -1970,20 +1979,20 @@
|
|||||||
|
|
||||||
**Purpose**: Comprehensive audit trail for all significant system actions
|
**Purpose**: Comprehensive audit trail for all significant system actions
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| ---------------- | ----------------------------------------- | --------------------------------- | -------------------------------------------------------- |
|
| ---------------- | ----------------------------------------- | --------------------------------- | ------------------------------------------------------------ |
|
||||||
| audit_id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | Unique audit log ID |
|
| audit_id | BIGINT | PRIMARY KEY, AUTO_INCREMENT | Unique audit log ID |
|
||||||
| user_id | INT | NULL, FK | User who performed action |
|
| user_id | INT | NULL, FK | User who performed action |
|
||||||
| action | VARCHAR(100) | NOT NULL | Action code (e.g., 'rfa.create', 'login.success') |
|
| 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_type | VARCHAR(50) | NULL | Entity/module affected (e.g., 'rfa', 'correspondence') |
|
||||||
| entity_id | VARCHAR(50) | NULL | Primary ID of affected record |
|
| entity_id | VARCHAR(50) | NULL | Primary ID of affected record |
|
||||||
| details_json | JSON | NULL | Additional context/details in JSON format |
|
| details_json | JSON | NULL | Additional context/details in JSON format |
|
||||||
| ip_address | VARCHAR(45) | NULL | Client IP address (supports IPv6) |
|
| ip_address | VARCHAR(45) | NULL | Client IP address (supports IPv6) |
|
||||||
| user_agent | VARCHAR(255) | NULL | Browser user agent string |
|
| user_agent | VARCHAR(255) | NULL | Browser user agent string |
|
||||||
| created_at | TIMESTAMP | DEFAULT CURRENT_TIMESTAMP | Action timestamp |
|
| 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_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 |
|
||||||
| request_id | VARCHAR(100) | NULL | Request ID/Trace ID เพื่อเชื่อมโยงกับ App Logs |
|
| request_id | VARCHAR(100) | NULL | Request ID/Trace ID เพื่อเชื่อมโยงกับ App Logs |
|
||||||
| severity | ENUM('INFO', 'WARN', 'ERROR', 'CRITICAL') | NULL | ระดับความรุนแรงของเหตุการณ์ |
|
| severity | ENUM('INFO', 'WARN', 'ERROR', 'CRITICAL') | NULL | ระดับความรุนแรงของเหตุการณ์ |
|
||||||
|
|
||||||
**Indexes**:
|
**Indexes**:
|
||||||
@@ -2153,27 +2162,36 @@
|
|||||||
|
|
||||||
**Purpose**: องรับ **Centralized JSON Schema Registry** เพื่อ Validate ข้อมูล JSON Details ของเอกสารแต่ละประเภท ตาม Requirements 6.11.1 และ Backend Plan T2.5.1
|
**Purpose**: องรับ **Centralized JSON Schema Registry** เพื่อ Validate ข้อมูล JSON Details ของเอกสารแต่ละประเภท ตาม Requirements 6.11.1 และ Backend Plan T2.5.1
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| :-------------------- | :------------- | :--------------- | :------------------------------------------------- |
|
| :-------------------- | :------------- | :--------------- | :----------------------------------------------------------- |
|
||||||
| **id** | `INT` | PK, AI | Unique Identifier |
|
| **id** | `INT` | PK, AI | Unique Identifier |
|
||||||
| **schema_code** | `VARCHAR(100)` | UNIQUE, NOT NULL | รหัส Schema (เช่น `RFA_DWG_V1`, `CORR_RFI_V1`) |
|
| **schema_code** | `VARCHAR(100)` | UNIQUE, NOT NULL | รหัส Schema (เช่น `RFA_DWG_V1`, `CORR_RFI_V1`) |
|
||||||
| **version** | `INT` | NOT NULL | เวอร์ชันของ Schema |
|
| **version** | `INT` | NOT NULL | เวอร์ชันของ Schema |
|
||||||
| **schema_definition** | `JSON` | NOT NULL | โครงสร้าง JSON Schema (Standard JSON Schema format) |
|
| **table_name** | `VARCHAR(100)` | NOT NULL | ชื่อตารางที่ Schema นี้ผูกอยู่ (เช่น rfa_revisions) |
|
||||||
| **is_active** | `BOOLEAN` | DEFAULT TRUE | สถานะการใช้งาน |
|
| **schema_definition** | `JSON` | NOT NULL | โครงสร้าง JSON Schema (Standard JSON Schema format) |
|
||||||
| **created_at** | `TIMESTAMP` | | วันที่สร้าง |
|
| **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
|
### 10.6 user_preferences
|
||||||
|
|
||||||
**Purpose**: แยกข้อมูลการตั้งค่าส่วนตัว (เช่น Notification Settings) ออกจากตาราง Users เพื่อความยืดหยุ่น ตาม Requirements 5.5 และ 6.8.3
|
**Purpose**: แยกข้อมูลการตั้งค่าส่วนตัว (เช่น Notification Settings) ออกจากตาราง Users เพื่อความยืดหยุ่น ตาม Requirements 5.5 และ 6.8.3
|
||||||
|
|
||||||
| Column Name | Data Type | Constraints | Description |
|
| Column Name | Data Type | Constraints | Description |
|
||||||
| :--------------- | :------------ | :-------------- | :------------------------------------- |
|
| :--------------- | :------------ | :-------------- | :---------------------------------------- |
|
||||||
| **user_id** | `INT` | PK, FK | อ้างอิงตาราง users |
|
| **user_id** | `INT` | PK, FK | อ้างอิงตาราง users |
|
||||||
| **notify_email** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนทางอีเมล |
|
| **notify_email** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนทางอีเมล |
|
||||||
| **notify_line** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนทาง LINE |
|
| **notify_line** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนทาง LINE |
|
||||||
| **digest_mode** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนแบบรวม (Digest) แทน Real-time |
|
| **digest_mode** | `BOOLEAN` | DEFAULT TRUE | รับแจ้งเตือนแบบรวม (Digest) แทน Real-time |
|
||||||
| **ui_theme** | `VARCHAR(20)` | DEFAULT 'light' | ธีมหน้าจอ (Light/Dark) |
|
| **ui_theme** | `VARCHAR(20)` | DEFAULT 'light' | ธีมหน้าจอ (Light/Dark) |
|
||||||
| **updated_at** | `TIMESTAMP` | | วันที่แก้ไขล่าสุด |
|
| **updated_at** | `TIMESTAMP` | | วันที่แก้ไขล่าสุด |
|
||||||
|
|
||||||
## **11. 📊 Views & Procedures (วิว และ โปรซีเดอร์)**
|
## **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
|
-- 1.1 JSON Schemas Registry
|
||||||
-- รองรับ: Backend Plan T2.5.1, Req 6.11.1
|
-- รองรับ: Backend Plan T2.5.1, Req 6.11.1
|
||||||
-- เหตุผล: เพื่อ Validate โครงสร้าง JSON Details ของเอกสารแต่ละประเภทแบบ Centralized
|
-- เหตุผล: เพื่อ Validate โครงสร้าง JSON Details ของเอกสารแต่ละประเภทแบบ Centralized
|
||||||
CREATE TABLE IF NOT EXISTS json_schemas (
|
CREATE TABLE json_schemas (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
schema_code VARCHAR(100) NOT NULL UNIQUE COMMENT 'รหัส Schema เช่น RFA_DWG_V1,
|
schema_code VARCHAR(100) NOT NULL COMMENT 'รหัส Schema (เช่น RFA_DWG)',
|
||||||
CORR_GENERIC',
|
|
||||||
version INT NOT NULL DEFAULT 1 COMMENT 'เวอร์ชันของ Schema',
|
version INT NOT NULL DEFAULT 1 COMMENT 'เวอร์ชันของ Schema',
|
||||||
schema_definition JSON NOT NULL COMMENT 'โครงสร้าง JSON Schema (Standard Format)',
|
table_name VARCHAR(100) NOT NULL COMMENT 'ชื่อตารางเป้าหมาย (เช่น rfa_revisions)',
|
||||||
is_active BOOLEAN DEFAULT TRUE,
|
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,
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
INDEX idx_schema_code (schema_code)
|
-- ป้องกัน Schema Code ซ้ำกันใน Version เดียวกัน
|
||||||
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_unicode_ci;
|
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
|
-- 1.2 User Preferences
|
||||||
-- รองรับ: Req 5.5, 6.8.3
|
-- รองรับ: Req 5.5, 6.8.3
|
||||||
-- เหตุผล: แยกการตั้งค่า Notification และ UI ออกจากตาราง Users หลัก
|
-- เหตุผล: แยกการตั้งค่า Notification และ UI ออกจากตาราง Users หลัก
|
||||||
CREATE TABLE IF NOT EXISTS user_preferences (
|
CREATE TABLE user_preferences (
|
||||||
user_id INT PRIMARY KEY,
|
user_id INT PRIMARY KEY,
|
||||||
notify_email BOOLEAN DEFAULT TRUE,
|
notify_email BOOLEAN DEFAULT TRUE,
|
||||||
notify_line 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
|
ALTER TABLE correspondence_revisions
|
||||||
ADD COLUMN v_doc_subtype VARCHAR(50) GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(details, '$.subType'))) VIRTUAL,
|
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);
|
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
|
-- ทำแบบเดียวกันกับ RFA Revisions หากมีการเก็บ JSON details
|
||||||
ALTER TABLE rfa_revisions
|
ALTER TABLE rfa_revisions
|
||||||
ADD COLUMN details JSON NULL COMMENT 'RFA Specific Details'
|
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 (
|
ADD COLUMN v_ref_drawing_count INT GENERATED ALWAYS AS (
|
||||||
JSON_UNQUOTE(JSON_EXTRACT(details, '$.drawingCount'))
|
JSON_UNQUOTE(JSON_EXTRACT(details, '$.drawingCount'))
|
||||||
) VIRTUAL;
|
) VIRTUAL;
|
||||||
|
CREATE INDEX idx_rfa_rev_v_drawing_count ON rfa_revisions(v_ref_drawing_count);
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
-- 5. PARTITIONING PREPARATION (Advance - Optional)
|
-- 5. PARTITIONING PREPARATION (Advance - Optional)
|
||||||
-- ============================================================
|
-- ============================================================
|
||||||
|
|||||||
@@ -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
|
// File: src/common/services/crypto.service.ts
|
||||||
// บันทึกการแก้ไข: Encryption/Decryption Utility (T1.1)
|
// บันทึกการแก้ไข: Encryption/Decryption Utility (T1.1)
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CryptoService {
|
export class CryptoService {
|
||||||
|
private readonly logger = new Logger(CryptoService.name);
|
||||||
private readonly algorithm = 'aes-256-cbc';
|
private readonly algorithm = 'aes-256-cbc';
|
||||||
private readonly key: Buffer;
|
private readonly key: Buffer;
|
||||||
private readonly ivLength = 16;
|
private readonly ivLength = 16;
|
||||||
@@ -19,22 +20,42 @@ export class CryptoService {
|
|||||||
this.key = crypto.scryptSync(secret, 'salt', 32);
|
this.key = crypto.scryptSync(secret, 'salt', 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
encrypt(text: string): string {
|
encrypt(text: string | number | boolean): string {
|
||||||
const iv = crypto.randomBytes(this.ivLength);
|
if (text === null || text === undefined) return text as any;
|
||||||
const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);
|
|
||||||
let encrypted = cipher.update(text, 'utf8', 'hex');
|
try {
|
||||||
encrypted += cipher.final('hex');
|
const stringValue = String(text);
|
||||||
return `${iv.toString('hex')}:${encrypted}`;
|
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 {
|
decrypt(text: string): string {
|
||||||
const [ivHex, encryptedHex] = text.split(':');
|
if (!text || typeof text !== 'string' || !text.includes(':')) return text;
|
||||||
if (!ivHex || !encryptedHex) return text;
|
|
||||||
|
|
||||||
const iv = Buffer.from(ivHex, 'hex');
|
try {
|
||||||
const decipher = crypto.createDecipheriv(this.algorithm, this.key, iv);
|
const [ivHex, encryptedHex] = text.split(':');
|
||||||
let decrypted = decipher.update(encryptedHex, 'hex', 'utf8');
|
if (!ivHex || !encryptedHex) return text;
|
||||||
decrypted += decipher.final('utf8');
|
|
||||||
return decrypted;
|
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 {
|
import {
|
||||||
Entity,
|
Entity,
|
||||||
Column,
|
Column,
|
||||||
@@ -5,12 +6,16 @@ import {
|
|||||||
ManyToOne,
|
ManyToOne,
|
||||||
JoinColumn,
|
JoinColumn,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
|
Index,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { Correspondence } from './correspondence.entity.js';
|
import { Correspondence } from './correspondence.entity.js';
|
||||||
import { CorrespondenceStatus } from './correspondence-status.entity.js';
|
import { CorrespondenceStatus } from './correspondence-status.entity.js';
|
||||||
import { User } from '../../user/entities/user.entity.js';
|
import { User } from '../../user/entities/user.entity.js';
|
||||||
|
|
||||||
@Entity('correspondence_revisions')
|
@Entity('correspondence_revisions')
|
||||||
|
// ✅ เพิ่ม Index สำหรับ Virtual Columns เพื่อให้ Search เร็วขึ้น
|
||||||
|
@Index('idx_corr_rev_v_project', ['vRefProjectId'])
|
||||||
|
@Index('idx_corr_rev_v_type', ['vRefType'])
|
||||||
export class CorrespondenceRevision {
|
export class CorrespondenceRevision {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id!: number;
|
id!: number;
|
||||||
@@ -39,6 +44,27 @@ export class CorrespondenceRevision {
|
|||||||
@Column({ type: 'json', nullable: true })
|
@Column({ type: 'json', nullable: true })
|
||||||
details?: any; // เก็บข้อมูลแบบ Dynamic ตาม Type
|
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
|
// Dates
|
||||||
@Column({ name: 'document_date', type: 'date', nullable: true })
|
@Column({ name: 'document_date', type: 'date', nullable: true })
|
||||||
documentDate?: Date;
|
documentDate?: Date;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// File: src/modules/correspondence/entities/correspondence-routing.entity.ts
|
||||||
import {
|
import {
|
||||||
Entity,
|
Entity,
|
||||||
Column,
|
Column,
|
||||||
@@ -9,7 +10,7 @@ import {
|
|||||||
import { CorrespondenceRevision } from './correspondence-revision.entity.js';
|
import { CorrespondenceRevision } from './correspondence-revision.entity.js';
|
||||||
import { Organization } from '../../project/entities/organization.entity.js';
|
import { Organization } from '../../project/entities/organization.entity.js';
|
||||||
import { User } from '../../user/entities/user.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')
|
@Entity('correspondence_routings')
|
||||||
export class CorrespondenceRouting {
|
export class CorrespondenceRouting {
|
||||||
@@ -49,6 +50,10 @@ export class CorrespondenceRouting {
|
|||||||
@Column({ name: 'processed_at', type: 'datetime', nullable: true })
|
@Column({ name: 'processed_at', type: 'datetime', nullable: true })
|
||||||
processedAt?: Date;
|
processedAt?: Date;
|
||||||
|
|
||||||
|
// ✅ [New] เพิ่ม State Context เพื่อเก็บ Snapshot ข้อมูล Workflow ณ จุดนั้น
|
||||||
|
@Column({ name: 'state_context', type: 'json', nullable: true })
|
||||||
|
stateContext?: any;
|
||||||
|
|
||||||
@CreateDateColumn({ name: 'created_at' })
|
@CreateDateColumn({ name: 'created_at' })
|
||||||
createdAt!: Date;
|
createdAt!: Date;
|
||||||
|
|
||||||
@@ -57,7 +62,7 @@ export class CorrespondenceRouting {
|
|||||||
@JoinColumn({ name: 'correspondence_id' })
|
@JoinColumn({ name: 'correspondence_id' })
|
||||||
correspondenceRevision?: CorrespondenceRevision;
|
correspondenceRevision?: CorrespondenceRevision;
|
||||||
|
|
||||||
@ManyToOne(() => RoutingTemplate) // ตอนนี้ TypeScript จะรู้จัก RoutingTemplate แล้ว
|
@ManyToOne(() => RoutingTemplate)
|
||||||
@JoinColumn({ name: 'template_id' })
|
@JoinColumn({ name: 'template_id' })
|
||||||
template?: RoutingTemplate;
|
template?: RoutingTemplate;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
|
// File: src/modules/correspondence/entities/routing-template-step.entity.ts
|
||||||
import {
|
import {
|
||||||
Entity,
|
Entity,
|
||||||
Column,
|
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
JoinColumn,
|
JoinColumn,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { RoutingTemplate } from './routing-template.entity.js';
|
import { RoutingTemplate } from './routing-template.entity.js';
|
||||||
import { Organization } from '../../project/entities/organization.entity.js';
|
import { Organization } from '../../project/entities/organization.entity.js';
|
||||||
|
import { Role } from '../../user/entities/role.entity.js';
|
||||||
|
|
||||||
@Entity('correspondence_routing_template_steps')
|
@Entity('correspondence_routing_template_steps')
|
||||||
export class RoutingTemplateStep {
|
export class RoutingTemplateStep {
|
||||||
@@ -22,17 +24,27 @@ export class RoutingTemplateStep {
|
|||||||
@Column({ name: 'to_organization_id' })
|
@Column({ name: 'to_organization_id' })
|
||||||
toOrganizationId!: number;
|
toOrganizationId!: number;
|
||||||
|
|
||||||
|
@Column({ name: 'role_id', nullable: true })
|
||||||
|
roleId?: number;
|
||||||
|
|
||||||
@Column({ name: 'step_purpose', default: 'FOR_REVIEW' })
|
@Column({ name: 'step_purpose', default: 'FOR_REVIEW' })
|
||||||
stepPurpose!: string; // FOR_APPROVAL, FOR_REVIEW
|
stepPurpose!: string;
|
||||||
|
|
||||||
@Column({ name: 'expected_days', nullable: true })
|
@Column({ name: 'expected_days', nullable: true })
|
||||||
expectedDays?: number;
|
expectedDays?: number;
|
||||||
|
|
||||||
@ManyToOne(() => RoutingTemplate, (t) => t.steps, { onDelete: 'CASCADE' })
|
// Relations
|
||||||
|
@ManyToOne(() => RoutingTemplate, (template) => template.steps, {
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
})
|
||||||
@JoinColumn({ name: 'template_id' })
|
@JoinColumn({ name: 'template_id' })
|
||||||
template?: RoutingTemplate;
|
template?: RoutingTemplate;
|
||||||
|
|
||||||
@ManyToOne(() => Organization)
|
@ManyToOne(() => Organization)
|
||||||
@JoinColumn({ name: 'to_organization_id' })
|
@JoinColumn({ name: 'to_organization_id' })
|
||||||
toOrganization?: Organization;
|
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 {
|
import {
|
||||||
IsString,
|
IsString,
|
||||||
IsNotEmpty,
|
IsNotEmpty,
|
||||||
@@ -5,22 +6,65 @@ import {
|
|||||||
IsOptional,
|
IsOptional,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsObject,
|
IsObject,
|
||||||
|
IsArray,
|
||||||
|
ValidateNested,
|
||||||
} from 'class-validator';
|
} 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 {
|
export class CreateJsonSchemaDto {
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
schemaCode!: string; // รหัส Schema (ต้องไม่ซ้ำ เช่น 'RFA_DWG_V1')
|
schemaCode!: string;
|
||||||
|
|
||||||
|
@IsString() // ✅ เพิ่ม Validation
|
||||||
|
@IsNotEmpty()
|
||||||
|
tableName!: string;
|
||||||
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
version?: number; // เวอร์ชัน (Default: 1)
|
version?: number;
|
||||||
|
|
||||||
@IsObject()
|
@IsObject()
|
||||||
@IsNotEmpty()
|
@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()
|
@IsBoolean()
|
||||||
@IsOptional()
|
@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 {
|
import {
|
||||||
Entity,
|
Entity,
|
||||||
Column,
|
Column,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
|
Index,
|
||||||
} from 'typeorm';
|
} 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')
|
@Entity('json_schemas')
|
||||||
|
@Index(['schemaCode', 'version'], { unique: true })
|
||||||
export class JsonSchema {
|
export class JsonSchema {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id!: number;
|
id!: number;
|
||||||
|
|
||||||
@Column({ name: 'schema_code', unique: true, length: 100 })
|
@Column({ name: 'schema_code', length: 100 })
|
||||||
schemaCode!: string; // เช่น 'RFA_DWG_V1'
|
schemaCode!: string;
|
||||||
|
|
||||||
@Column({ default: 1 })
|
@Column({ default: 1 })
|
||||||
version!: number;
|
version!: number;
|
||||||
|
|
||||||
|
@Column({ name: 'table_name', length: 100, nullable: false }) // ✅ เพิ่ม: ระบุตารางเป้าหมาย
|
||||||
|
tableName!: string;
|
||||||
|
|
||||||
@Column({ name: 'schema_definition', type: 'json' })
|
@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 })
|
@Column({ name: 'is_active', default: true })
|
||||||
isActive!: boolean;
|
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';
|
// File: src/modules/json-schema/json-schema.controller.ts
|
||||||
import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger';
|
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';
|
import { JsonSchemaService } from './json-schema.service';
|
||||||
// ✅ FIX: Import DTO
|
import { SchemaMigrationService } from './services/schema-migration.service';
|
||||||
|
|
||||||
import { CreateJsonSchemaDto } from './dto/create-json-schema.dto';
|
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 { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { RbacGuard } from '../../common/guards/rbac.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()
|
@ApiBearerAuth()
|
||||||
@UseGuards(JwtAuthGuard, RbacGuard)
|
@UseGuards(JwtAuthGuard, RbacGuard)
|
||||||
@Controller('json-schemas')
|
@Controller('json-schemas')
|
||||||
export class JsonSchemaController {
|
export class JsonSchemaController {
|
||||||
constructor(private readonly schemaService: JsonSchemaService) {}
|
constructor(
|
||||||
|
private readonly jsonSchemaService: JsonSchemaService,
|
||||||
|
private readonly migrationService: SchemaMigrationService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
// Schema Management (CRUD)
|
||||||
|
// ----------------------------------------------------------------------
|
||||||
|
|
||||||
@Post()
|
@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
|
@RequirePermission('system.manage_all') // Admin Only
|
||||||
create(@Body() createDto: CreateJsonSchemaDto) {
|
create(@Body() createDto: CreateJsonSchemaDto) {
|
||||||
return this.schemaService.createOrUpdate(
|
return this.jsonSchemaService.create(createDto);
|
||||||
createDto.schemaCode,
|
|
||||||
createDto.schemaDefinition,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post(':code/validate')
|
@Get()
|
||||||
@ApiOperation({ summary: 'Test validation against a schema' })
|
@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')
|
@RequirePermission('document.view')
|
||||||
async validate(@Param('code') code: string, @Body() data: any) {
|
async validate(@Param('code') code: string, @Body() data: any) {
|
||||||
const isValid = await this.schemaService.validate(code, data);
|
// Note: Validation API นี้ใช้สำหรับ Test หรือ Pre-check เท่านั้น
|
||||||
return { valid: isValid, message: 'Validation passed' };
|
// การ 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 { Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
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 { 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({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([JsonSchema]), UserModule],
|
imports: [
|
||||||
|
TypeOrmModule.forFeature([JsonSchema]),
|
||||||
|
ConfigModule,
|
||||||
|
// UserModule,
|
||||||
|
],
|
||||||
controllers: [JsonSchemaController],
|
controllers: [JsonSchemaController],
|
||||||
providers: [JsonSchemaService],
|
providers: [
|
||||||
exports: [JsonSchemaService],
|
JsonSchemaService,
|
||||||
|
VirtualColumnService,
|
||||||
|
UiSchemaService,
|
||||||
|
SchemaMigrationService,
|
||||||
|
JsonSecurityService,
|
||||||
|
CryptoService,
|
||||||
|
],
|
||||||
|
exports: [JsonSchemaService, SchemaMigrationService, JsonSecurityService],
|
||||||
})
|
})
|
||||||
export class JsonSchemaModule {}
|
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 {
|
import {
|
||||||
Injectable,
|
Body,
|
||||||
OnModuleInit,
|
Controller,
|
||||||
BadRequestException,
|
Delete,
|
||||||
NotFoundException,
|
Get,
|
||||||
Logger,
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Param,
|
||||||
|
ParseIntPipe,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
Query,
|
||||||
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import {
|
||||||
import { Repository } from 'typeorm';
|
ApiBearerAuth,
|
||||||
import Ajv from 'ajv';
|
ApiOperation,
|
||||||
import addFormats from 'ajv-formats';
|
ApiParam,
|
||||||
import { JsonSchema } from './entities/json-schema.entity'; // ลบ .js
|
ApiResponse,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
|
||||||
@Injectable()
|
import { JsonSchemaService } from './json-schema.service';
|
||||||
export class JsonSchemaService implements OnModuleInit {
|
import { SchemaMigrationService } from './services/schema-migration.service';
|
||||||
private ajv: Ajv;
|
|
||||||
private validators = new Map<string, any>();
|
|
||||||
private readonly logger = new Logger(JsonSchemaService.name);
|
|
||||||
|
|
||||||
|
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(
|
constructor(
|
||||||
@InjectRepository(JsonSchema)
|
private readonly jsonSchemaService: JsonSchemaService,
|
||||||
private schemaRepo: Repository<JsonSchema>,
|
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 });
|
return this.jsonSchemaService.update(id, updateDto);
|
||||||
addFormats(this.ajv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async onModuleInit() {
|
@Delete(':id')
|
||||||
// Pre-load schemas (Optional for performance)
|
@ApiOperation({ summary: 'Delete a schema version (Hard Delete)' })
|
||||||
// const schemas = await this.schemaRepo.find({ where: { isActive: true } });
|
@RequirePermission('system.manage_all')
|
||||||
// schemas.forEach(s => this.createValidator(s.schemaCode, s.schemaDefinition));
|
remove(@Param('id', ParseIntPipe) id: number) {
|
||||||
|
return this.jsonSchemaService.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// ----------------------------------------------------------------------
|
||||||
* ตรวจสอบข้อมูล JSON ว่าถูกต้องตาม Schema หรือไม่
|
// Validation & Security
|
||||||
*/
|
// ----------------------------------------------------------------------
|
||||||
async validate(schemaCode: string, data: any): Promise<boolean> {
|
|
||||||
let validate = this.validators.get(schemaCode);
|
|
||||||
|
|
||||||
if (!validate) {
|
@Post('validate/:code')
|
||||||
const schema = await this.schemaRepo.findOne({
|
@HttpCode(HttpStatus.OK)
|
||||||
where: { schemaCode, isActive: true },
|
@ApiOperation({ summary: 'Validate data against the latest schema version' })
|
||||||
});
|
@ApiResponse({
|
||||||
|
status: 200,
|
||||||
if (!schema) {
|
description: 'Validation result including errors and sanitized data',
|
||||||
throw new NotFoundException(`JSON Schema '${schemaCode}' not found`);
|
})
|
||||||
}
|
@RequirePermission('document.view')
|
||||||
|
async validate(@Param('code') code: string, @Body() data: any) {
|
||||||
try {
|
// Note: Validation API นี้ใช้สำหรับ Test หรือ Pre-check เท่านั้น
|
||||||
validate = this.ajv.compile(schema.schemaDefinition);
|
// การ Save จริงจะเรียกผ่าน Service ภายใน
|
||||||
this.validators.set(schemaCode, validate);
|
return this.jsonSchemaService.validateData(code, data);
|
||||||
} 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('read/:code')
|
||||||
* สร้างหรืออัปเดต Schema
|
@HttpCode(HttpStatus.OK)
|
||||||
*/
|
@ApiOperation({
|
||||||
async createOrUpdate(schemaCode: string, definition: Record<string, any>) {
|
summary: 'Process read data (Decrypt & Filter) based on user roles',
|
||||||
// 1. ตรวจสอบว่า Definition เป็น JSON Schema ที่ถูกต้องไหม
|
})
|
||||||
try {
|
@RequirePermission('document.view')
|
||||||
this.ajv.compile(definition);
|
async processReadData(
|
||||||
} catch (error: any) {
|
@Param('code') code: string,
|
||||||
throw new BadRequestException(
|
@Body() data: any,
|
||||||
`Invalid JSON Schema format: ${error.message}`,
|
@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
|
return this.jsonSchemaService.processReadData(code, data, { userRoles });
|
||||||
let schema = await this.schemaRepo.findOne({ where: { schemaCode } });
|
}
|
||||||
|
|
||||||
if (schema) {
|
// ----------------------------------------------------------------------
|
||||||
schema.schemaDefinition = definition;
|
// Data Migration
|
||||||
schema.version += 1;
|
// ----------------------------------------------------------------------
|
||||||
} else {
|
|
||||||
schema = this.schemaRepo.create({
|
|
||||||
schemaCode,
|
|
||||||
schemaDefinition: definition,
|
|
||||||
version: 1,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const savedSchema = await this.schemaRepo.save(schema);
|
@Post('migrate/:table/:id')
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
// 3. Clear Cache เพื่อให้ครั้งหน้าโหลดตัวใหม่
|
@ApiOperation({
|
||||||
this.validators.delete(schemaCode);
|
summary: 'Migrate specific entity data to target schema version',
|
||||||
this.logger.log(`Schema '${schemaCode}' updated (v${savedSchema.version})`);
|
})
|
||||||
|
@ApiParam({ name: 'table', description: 'Table Name (e.g. rfa_revisions)' })
|
||||||
return savedSchema;
|
@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 {
|
import {
|
||||||
IsInt,
|
|
||||||
IsString,
|
IsString,
|
||||||
|
IsNotEmpty,
|
||||||
|
IsInt,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
IsDateString,
|
IsDateString,
|
||||||
|
IsObject,
|
||||||
IsArray,
|
IsArray,
|
||||||
IsNotEmpty,
|
|
||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
|
|
||||||
export class CreateRfaDto {
|
export class CreateRfaRevisionDto {
|
||||||
@IsInt()
|
|
||||||
@IsNotEmpty()
|
|
||||||
projectId!: number;
|
|
||||||
|
|
||||||
@IsInt()
|
|
||||||
@IsNotEmpty()
|
|
||||||
rfaTypeId!: number;
|
|
||||||
|
|
||||||
@IsInt()
|
|
||||||
@IsOptional()
|
|
||||||
disciplineId?: number; // [Req 6B] สาขางาน (จำเป็นสำหรับการรันเลข RFA)
|
|
||||||
|
|
||||||
@IsString()
|
@IsString()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
title!: string;
|
title!: string;
|
||||||
|
|
||||||
@IsInt()
|
@IsInt()
|
||||||
@IsNotEmpty()
|
@IsNotEmpty()
|
||||||
toOrganizationId!: number; // ส่งถึงใคร (สำหรับ Routing Step 1)
|
rfaStatusCodeId!: number;
|
||||||
|
|
||||||
@IsString()
|
@IsInt()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
description?: string;
|
rfaApproveCodeId?: number;
|
||||||
|
|
||||||
@IsDateString()
|
@IsDateString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@@ -39,10 +28,25 @@ export class CreateRfaDto {
|
|||||||
|
|
||||||
@IsDateString()
|
@IsDateString()
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
dueDate?: string; // กำหนดวันตอบกลับ
|
issuedDate?: string;
|
||||||
|
|
||||||
|
@IsDateString()
|
||||||
|
@IsOptional()
|
||||||
|
receivedDate?: string;
|
||||||
|
|
||||||
|
@IsDateString()
|
||||||
|
@IsOptional()
|
||||||
|
approvedDate?: string;
|
||||||
|
|
||||||
|
@IsString()
|
||||||
|
@IsOptional()
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
@IsObject()
|
||||||
|
@IsOptional()
|
||||||
|
details?: Record<string, any>;
|
||||||
|
|
||||||
@IsArray()
|
@IsArray()
|
||||||
@IsInt({ each: true })
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
shopDrawingRevisionIds?: number[]; // Shop Drawings ที่แนบมา
|
shopDrawingRevisionIds?: number[]; // IDs of linked Shop Drawings
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { PartialType } from '@nestjs/swagger';
|
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 {
|
import {
|
||||||
Entity,
|
Entity,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
@@ -8,6 +9,7 @@ import {
|
|||||||
JoinColumn,
|
JoinColumn,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
Unique,
|
Unique,
|
||||||
|
Index,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { Rfa } from './rfa.entity';
|
import { Rfa } from './rfa.entity';
|
||||||
import { Correspondence } from '../../correspondence/entities/correspondence.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 { RfaApproveCode } from './rfa-approve-code.entity';
|
||||||
import { User } from '../../user/entities/user.entity';
|
import { User } from '../../user/entities/user.entity';
|
||||||
import { RfaItem } from './rfa-item.entity';
|
import { RfaItem } from './rfa-item.entity';
|
||||||
import { RfaWorkflow } from './rfa-workflow.entity'; // Import เพิ่ม
|
import { RfaWorkflow } from './rfa-workflow.entity';
|
||||||
|
|
||||||
@Entity('rfa_revisions')
|
@Entity('rfa_revisions')
|
||||||
@Unique(['rfaId', 'revisionNumber'])
|
@Unique(['rfaId', 'revisionNumber'])
|
||||||
@@ -63,6 +65,20 @@ export class RfaRevision {
|
|||||||
@Column({ type: 'text', nullable: true })
|
@Column({ type: 'text', nullable: true })
|
||||||
description?: string;
|
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' })
|
@CreateDateColumn({ name: 'created_at' })
|
||||||
createdAt!: Date;
|
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
|
// File: src/modules/workflow-engine/workflow-dsl.service.ts
|
||||||
|
|
||||||
import { Injectable, BadRequestException } from '@nestjs/common';
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
export interface WorkflowState {
|
export interface WorkflowState {
|
||||||
initial?: boolean;
|
initial?: boolean;
|
||||||
@@ -17,7 +17,7 @@ export interface TransitionRule {
|
|||||||
export interface RequirementRule {
|
export interface RequirementRule {
|
||||||
role?: string;
|
role?: string;
|
||||||
user?: string;
|
user?: string;
|
||||||
condition?: string; // e.g. "amount > 5000" (Advanced)
|
condition?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EventRule {
|
export interface EventRule {
|
||||||
@@ -36,11 +36,12 @@ export interface CompiledWorkflow {
|
|||||||
export class WorkflowDslService {
|
export class WorkflowDslService {
|
||||||
/**
|
/**
|
||||||
* คอมไพล์ DSL Input ให้เป็น Standard Execution Tree
|
* คอมไพล์ DSL Input ให้เป็น Standard Execution Tree
|
||||||
* @param dsl ข้อมูลดิบจาก User (JSON/Object)
|
|
||||||
* @returns CompiledWorkflow Object ที่พร้อมใช้งาน
|
|
||||||
*/
|
*/
|
||||||
compile(dsl: any): CompiledWorkflow {
|
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)) {
|
if (!dsl.states || !Array.isArray(dsl.states)) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
'DSL syntax error: "states" array is required.',
|
'DSL syntax error: "states" array is required.',
|
||||||
@@ -55,7 +56,6 @@ export class WorkflowDslService {
|
|||||||
|
|
||||||
const stateMap = new Set<string>();
|
const stateMap = new Set<string>();
|
||||||
|
|
||||||
// 2. First Pass: Collect all state names and normalize structure
|
|
||||||
for (const rawState of dsl.states) {
|
for (const rawState of dsl.states) {
|
||||||
if (!rawState.name) {
|
if (!rawState.name) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
@@ -71,7 +71,6 @@ export class WorkflowDslService {
|
|||||||
transitions: {},
|
transitions: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Normalize transitions "on:"
|
|
||||||
if (rawState.on) {
|
if (rawState.on) {
|
||||||
for (const [action, rule] of Object.entries(rawState.on)) {
|
for (const [action, rule] of Object.entries(rawState.on)) {
|
||||||
const rawRule = rule as any;
|
const rawRule = rule as any;
|
||||||
@@ -86,15 +85,11 @@ export class WorkflowDslService {
|
|||||||
compiled.states[rawState.name] = normalizedState;
|
compiled.states[rawState.name] = normalizedState;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. Second Pass: Validate Integrity
|
|
||||||
this.validateIntegrity(compiled, stateMap);
|
this.validateIntegrity(compiled, stateMap);
|
||||||
|
|
||||||
return compiled;
|
return compiled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ตรวจสอบความสมบูรณ์ของ Workflow Logic
|
|
||||||
*/
|
|
||||||
private validateIntegrity(compiled: CompiledWorkflow, stateMap: Set<string>) {
|
private validateIntegrity(compiled: CompiledWorkflow, stateMap: Set<string>) {
|
||||||
let hasInitial = false;
|
let hasInitial = false;
|
||||||
|
|
||||||
@@ -107,19 +102,13 @@ export class WorkflowDslService {
|
|||||||
hasInitial = true;
|
hasInitial = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ตรวจสอบ Transitions
|
|
||||||
if (state.transitions) {
|
if (state.transitions) {
|
||||||
for (const [action, rule] of Object.entries(state.transitions)) {
|
for (const [action, rule] of Object.entries(state.transitions)) {
|
||||||
// 1. ปลายทางต้องมีอยู่จริง
|
|
||||||
if (!stateMap.has(rule.to)) {
|
if (!stateMap.has(rule.to)) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
`DSL Error: State "${stateName}" transitions via "${action}" to unknown state "${rule.to}".`,
|
`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(
|
evaluate(
|
||||||
compiled: CompiledWorkflow,
|
compiled: CompiledWorkflow,
|
||||||
currentState: string,
|
currentState: string,
|
||||||
action: string,
|
action: string,
|
||||||
context: any,
|
context: any = {}, // Default empty object
|
||||||
): { nextState: string; events: EventRule[] } {
|
): { nextState: string; events: EventRule[] } {
|
||||||
const stateConfig = compiled.states[currentState];
|
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) {
|
if (transition.requirements && transition.requirements.length > 0) {
|
||||||
this.checkRequirements(transition.requirements, context);
|
this.checkRequirements(transition.requirements, context);
|
||||||
}
|
}
|
||||||
@@ -175,22 +156,19 @@ export class WorkflowDslService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* ตรวจสอบเงื่อนไขสิทธิ์ (Requirements)
|
|
||||||
*/
|
|
||||||
private checkRequirements(requirements: RequirementRule[], context: any) {
|
private checkRequirements(requirements: RequirementRule[], context: any) {
|
||||||
const userRoles = context.roles || [];
|
const safeContext = context || {};
|
||||||
const userId = context.userId;
|
const userRoles = safeContext.roles || [];
|
||||||
|
const userId = safeContext.userId;
|
||||||
|
|
||||||
const isAllowed = requirements.some((req) => {
|
const isAllowed = requirements.some((req) => {
|
||||||
// กรณีเช็ค Role
|
|
||||||
if (req.role) {
|
if (req.role) {
|
||||||
return userRoles.includes(req.role);
|
return userRoles.includes(req.role);
|
||||||
}
|
}
|
||||||
// กรณีเช็ค Specific User
|
|
||||||
if (req.user) {
|
if (req.user) {
|
||||||
return userId === req.user;
|
return userId === req.user;
|
||||||
}
|
}
|
||||||
|
// Future: Add Condition Logic Evaluation here
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,27 @@
|
|||||||
// File: src/modules/workflow-engine/workflow-engine.controller.ts
|
// File: src/modules/workflow-engine/workflow-engine.controller.ts
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Controller,
|
|
||||||
Post,
|
|
||||||
Body,
|
Body,
|
||||||
|
Controller,
|
||||||
Get,
|
Get,
|
||||||
Query,
|
|
||||||
Patch,
|
|
||||||
Param,
|
Param,
|
||||||
|
ParseUUIDPipe,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
Query,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from '@nestjs/common'; // เพิ่ม Patch, Param
|
} from '@nestjs/common';
|
||||||
import { ApiTags, ApiOperation, ApiResponse } from '@nestjs/swagger';
|
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||||
import { WorkflowEngineService } from './workflow-engine.service';
|
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
||||||
import { CreateWorkflowDefinitionDto } from './dto/create-workflow-definition.dto';
|
import { CreateWorkflowDefinitionDto } from './dto/create-workflow-definition.dto';
|
||||||
import { EvaluateWorkflowDto } from './dto/evaluate-workflow.dto';
|
import { EvaluateWorkflowDto } from './dto/evaluate-workflow.dto';
|
||||||
import { GetAvailableActionsDto } from './dto/get-available-actions.dto'; // [NEW]
|
import { GetAvailableActionsDto } from './dto/get-available-actions.dto';
|
||||||
import { UpdateWorkflowDefinitionDto } from './dto/update-workflow-definition.dto'; // [NEW]
|
import { UpdateWorkflowDefinitionDto } from './dto/update-workflow-definition.dto';
|
||||||
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
|
import { WorkflowEngineService } from './workflow-engine.service';
|
||||||
|
|
||||||
@ApiTags('Workflow Engine (DSL)')
|
@ApiTags('Workflow Engine (DSL)')
|
||||||
@Controller('workflow-engine')
|
@Controller('workflow-engine')
|
||||||
@UseGuards(JwtAuthGuard) // Protect all endpoints
|
@UseGuards(JwtAuthGuard)
|
||||||
export class WorkflowEngineController {
|
export class WorkflowEngineController {
|
||||||
constructor(private readonly workflowService: WorkflowEngineService) {}
|
constructor(private readonly workflowService: WorkflowEngineService) {}
|
||||||
|
|
||||||
@@ -42,24 +43,20 @@ export class WorkflowEngineController {
|
|||||||
@Get('actions')
|
@Get('actions')
|
||||||
@ApiOperation({ summary: 'Get available actions for current state' })
|
@ApiOperation({ summary: 'Get available actions for current state' })
|
||||||
async getAvailableActions(@Query() query: GetAvailableActionsDto) {
|
async getAvailableActions(@Query() query: GetAvailableActionsDto) {
|
||||||
// [UPDATED] ใช้ DTO แทนแยก Query
|
|
||||||
return this.workflowService.getAvailableActions(
|
return this.workflowService.getAvailableActions(
|
||||||
query.workflow_code,
|
query.workflow_code,
|
||||||
query.current_state,
|
query.current_state,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// [OPTIONAL/RECOMMENDED] เพิ่ม Endpoint สำหรับ Update (PATCH)
|
|
||||||
@Patch('definitions/:id')
|
@Patch('definitions/:id')
|
||||||
@ApiOperation({
|
@ApiOperation({
|
||||||
summary: 'Update workflow status or details (e.g. Deactivate)',
|
summary: 'Update workflow status or details (DSL Re-compile)',
|
||||||
})
|
})
|
||||||
async updateDefinition(
|
async updateDefinition(
|
||||||
@Param('id') id: string,
|
@Param('id', ParseUUIDPipe) id: string, // เพิ่ม ParseUUIDPipe เพื่อ Validate ID
|
||||||
@Body() dto: UpdateWorkflowDefinitionDto, // [NEW] ใช้ Update DTO
|
@Body() dto: UpdateWorkflowDefinitionDto,
|
||||||
) {
|
) {
|
||||||
// *หมายเหตุ: คุณต้องไปเพิ่ม method update() ใน Service ด้วยถ้าจะใช้ Endpoint นี้
|
return this.workflowService.update(id, dto);
|
||||||
// return this.workflowService.update(id, dto);
|
|
||||||
return { message: 'Update logic not implemented yet', id, ...dto };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,21 +2,23 @@
|
|||||||
|
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
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 { 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({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
// เชื่อมต่อกับตาราง workflow_definitions
|
TypeOrmModule.forFeature([
|
||||||
TypeOrmModule.forFeature([WorkflowDefinition]),
|
WorkflowDefinition,
|
||||||
|
WorkflowInstance, // [New]
|
||||||
|
WorkflowHistory, // [New]
|
||||||
|
]),
|
||||||
],
|
],
|
||||||
controllers: [WorkflowEngineController], // เพิ่ม Controller สำหรับรับ API
|
controllers: [WorkflowEngineController],
|
||||||
providers: [
|
providers: [WorkflowEngineService, WorkflowDslService],
|
||||||
WorkflowEngineService, // Service หลัก
|
exports: [WorkflowEngineService],
|
||||||
WorkflowDslService, // [New] Service สำหรับ Compile/Validate DSL
|
|
||||||
],
|
|
||||||
exports: [WorkflowEngineService], // Export ให้ module อื่นใช้เหมือนเดิม
|
|
||||||
})
|
})
|
||||||
export class WorkflowEngineModule {}
|
export class WorkflowEngineModule {}
|
||||||
|
|||||||
@@ -1,20 +1,29 @@
|
|||||||
// File: src/modules/workflow-engine/workflow-engine.service.ts
|
// File: src/modules/workflow-engine/workflow-engine.service.ts
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Injectable,
|
|
||||||
NotFoundException,
|
|
||||||
BadRequestException,
|
BadRequestException,
|
||||||
|
Injectable,
|
||||||
Logger,
|
Logger,
|
||||||
|
NotFoundException,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { DataSource, Repository } from 'typeorm';
|
||||||
|
|
||||||
|
// Entities
|
||||||
import { WorkflowDefinition } from './entities/workflow-definition.entity';
|
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 { CreateWorkflowDefinitionDto } from './dto/create-workflow-definition.dto';
|
||||||
import { EvaluateWorkflowDto } from './dto/evaluate-workflow.dto';
|
import { EvaluateWorkflowDto } from './dto/evaluate-workflow.dto';
|
||||||
import { UpdateWorkflowDefinitionDto } from './dto/update-workflow-definition.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 {
|
export enum WorkflowAction {
|
||||||
APPROVE = 'APPROVE',
|
APPROVE = 'APPROVE',
|
||||||
REJECT = 'REJECT',
|
REJECT = 'REJECT',
|
||||||
@@ -35,11 +44,16 @@ export class WorkflowEngineService {
|
|||||||
constructor(
|
constructor(
|
||||||
@InjectRepository(WorkflowDefinition)
|
@InjectRepository(WorkflowDefinition)
|
||||||
private readonly workflowDefRepo: Repository<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 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(
|
async createDefinition(
|
||||||
dto: CreateWorkflowDefinitionDto,
|
dto: CreateWorkflowDefinitionDto,
|
||||||
): Promise<WorkflowDefinition> {
|
): Promise<WorkflowDefinition> {
|
||||||
|
// 1. Compile & Validate DSL
|
||||||
const compiled = this.dslService.compile(dto.dsl);
|
const compiled = this.dslService.compile(dto.dsl);
|
||||||
|
|
||||||
|
// 2. Check latest version
|
||||||
const latest = await this.workflowDefRepo.findOne({
|
const latest = await this.workflowDefRepo.findOne({
|
||||||
where: { workflow_code: dto.workflow_code },
|
where: { workflow_code: dto.workflow_code },
|
||||||
order: { version: 'DESC' },
|
order: { version: 'DESC' },
|
||||||
@@ -57,6 +73,7 @@ export class WorkflowEngineService {
|
|||||||
|
|
||||||
const nextVersion = latest ? latest.version + 1 : 1;
|
const nextVersion = latest ? latest.version + 1 : 1;
|
||||||
|
|
||||||
|
// 3. Save new version
|
||||||
const entity = this.workflowDefRepo.create({
|
const entity = this.workflowDefRepo.create({
|
||||||
workflow_code: dto.workflow_code,
|
workflow_code: dto.workflow_code,
|
||||||
version: nextVersion,
|
version: nextVersion,
|
||||||
@@ -65,9 +82,16 @@ export class WorkflowEngineService {
|
|||||||
is_active: dto.is_active ?? true,
|
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(
|
async update(
|
||||||
id: string,
|
id: string,
|
||||||
dto: UpdateWorkflowDefinitionDto,
|
dto: UpdateWorkflowDefinitionDto,
|
||||||
@@ -95,33 +119,9 @@ export class WorkflowEngineService {
|
|||||||
return this.workflowDefRepo.save(definition);
|
return this.workflowDefRepo.save(definition);
|
||||||
}
|
}
|
||||||
|
|
||||||
async evaluate(dto: EvaluateWorkflowDto): Promise<any> {
|
/**
|
||||||
const definition = await this.workflowDefRepo.findOne({
|
* ดึง Action ที่ทำได้ ณ State ปัจจุบัน
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
async getAvailableActions(
|
async getAvailableActions(
|
||||||
workflowCode: string,
|
workflowCode: string,
|
||||||
currentState: string,
|
currentState: string,
|
||||||
@@ -140,25 +140,206 @@ export class WorkflowEngineService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// =================================================================
|
// =================================================================
|
||||||
// [LEGACY] Backward Compatibility for Correspondence/RFA Modules
|
// [PART 2] Runtime Engine (Phase 3.1)
|
||||||
// คืนค่า Logic เดิมเพื่อไม่ให้ Module อื่น Error (TS2339)
|
// =================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* เริ่มต้น 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 เดิม)
|
* คำนวณสถานะถัดไปแบบ Linear Sequence (Logic เดิม)
|
||||||
* ใช้สำหรับ CorrespondenceService และ RfaService ที่ยังไม่ได้ Refactor
|
* @deprecated แนะนำให้เปลี่ยนไปใช้ processTransition แทนเมื่อ Refactor เสร็จ
|
||||||
*/
|
*/
|
||||||
processAction(
|
processAction(
|
||||||
currentSequence: number,
|
currentSequence: number,
|
||||||
totalSteps: number,
|
totalSteps: number,
|
||||||
action: string, // รับเป็น string เพื่อความยืดหยุ่น
|
action: string,
|
||||||
returnToSequence?: number,
|
returnToSequence?: number,
|
||||||
): TransitionResult {
|
): TransitionResult {
|
||||||
// Map string action to enum logic
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case WorkflowAction.APPROVE:
|
case WorkflowAction.APPROVE:
|
||||||
case WorkflowAction.ACKNOWLEDGE:
|
case WorkflowAction.ACKNOWLEDGE:
|
||||||
case 'APPROVE': // Case sensitive handling fallback
|
case 'APPROVE':
|
||||||
case 'ACKNOWLEDGE':
|
case 'ACKNOWLEDGE':
|
||||||
if (currentSequence >= totalSteps) {
|
if (currentSequence >= totalSteps) {
|
||||||
return {
|
return {
|
||||||
@@ -193,7 +374,6 @@ export class WorkflowEngineService {
|
|||||||
};
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// กรณีส่ง Action อื่นมา ให้ถือว่าเป็น Approve (หรือจะ Throw Error ก็ได้)
|
|
||||||
this.logger.warn(
|
this.logger.warn(
|
||||||
`Unknown legacy action: ${action}, treating as next step.`,
|
`Unknown legacy action: ${action}, treating as next step.`,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,6 +22,27 @@
|
|||||||
"forceConsistentCasingInFileNames": true,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"noImplicitAny": true, // ห้ามใช้ Any โดยไม่จำเป็น
|
"noImplicitAny": true, // ห้ามใช้ Any โดยไม่จำเป็น
|
||||||
"strictBindCallApply": true,
|
"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
|
## VSCode Shortcut
|
||||||
|
|
||||||
Markdown preview Ctrl+Shift+V
|
Markdown preview Ctrl+Shift+V
|
||||||
|
|
||||||
## สร้างโครงสร้างโฟลเดอร์สำหรับ lcbp3-backend
|
## สร้างโครงสร้างโฟลเดอร์สำหรับ lcbp3-backend
|
||||||
|
|
||||||
@@ -80,14 +80,16 @@ git push -u origin main
|
|||||||
|
|
||||||
## **สร้าง NestJS Project ใหม่**
|
## **สร้าง NestJS Project ใหม่**
|
||||||
|
|
||||||
* ขั้นตอนที่ 1: ติดตั้ง NestJS CLI (ถ้ายังไม่ได้ติดตั้ง)
|
- ขั้นตอนที่ 1: ติดตั้ง NestJS CLI (ถ้ายังไม่ได้ติดตั้ง)
|
||||||
* npm install -g @nestjs/cli
|
|
||||||
|
|
||||||
* ขั้นตอนที่ 2: สร้างโปรเจกต์ใหม่
|
- npm install -g @nestjs/cli
|
||||||
* nest new backend
|
|
||||||
* nest new . /อยู่ในโฟลเดอร์ที่สร้างไว้แล้ว และต้องการสร้างโปรเจกต์ลงในโฟลเดอร์นั้นโดยตรง:
|
|
||||||
|
|
||||||
* ขั้นตอนที่ 3: ติดตั้ง Dependencies เพิ่มเติมสำหรับ DMS
|
- ขั้นตอนที่ 2: สร้างโปรเจกต์ใหม่
|
||||||
|
|
||||||
|
- nest new backend
|
||||||
|
- nest new . /อยู่ในโฟลเดอร์ที่สร้างไว้แล้ว และต้องการสร้างโปรเจกต์ลงในโฟลเดอร์นั้นโดยตรง:
|
||||||
|
|
||||||
|
- ขั้นตอนที่ 3: ติดตั้ง Dependencies เพิ่มเติมสำหรับ DMS
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Core & Database
|
# Core & Database
|
||||||
@@ -123,302 +125,143 @@ npm install --save-dev @nestjs/testing jest @types/jest @types/passport-jwt @typ
|
|||||||
การควบคุมพฤติกรรมของผมต้องทำผ่าน **"คำสั่ง (Prompt)"** ที่คุณพิมพ์เข้ามาเท่านั้นครับ เพื่อป้องกันไม่ให้เกิดเหตุการณ์แบบนี้อีก คุณสามารถใช้เทคนิคการสั่งงานดังนี้เพื่อ **"บังคับ"** ให้ผมตรวจสอบข้อมูลครับ:
|
การควบคุมพฤติกรรมของผมต้องทำผ่าน **"คำสั่ง (Prompt)"** ที่คุณพิมพ์เข้ามาเท่านั้นครับ เพื่อป้องกันไม่ให้เกิดเหตุการณ์แบบนี้อีก คุณสามารถใช้เทคนิคการสั่งงานดังนี้เพื่อ **"บังคับ"** ให้ผมตรวจสอบข้อมูลครับ:
|
||||||
|
|
||||||
1. **สั่งให้ "อ่านและสรุปก่อน" (Verify First):**
|
1. **สั่งให้ "อ่านและสรุปก่อน" (Verify First):**
|
||||||
* ก่อนให้ผมเขียนโค้ด ให้สั่งว่า *"ช่วยอ่านไฟล์ `01_lcbp3_v1_4_3.sql` แล้วสรุปโครงสร้างตาราง audit_logs ที่มีอยู่จริงให้ดูก่อน"*
|
|
||||||
* วิธีนี้จะบังคับให้ผมต้องไปดึงข้อมูลจากไฟล์มาประมวลผลก่อนที่จะเริ่ม "จินตนาการ" หรือเขียนโค้ดใหม่ครับ
|
- ก่อนให้ผมเขียนโค้ด ให้สั่งว่า _"ช่วยอ่านไฟล์ `01_lcbp3_v1_4_3.sql` แล้วสรุปโครงสร้างตาราง audit_logs ที่มีอยู่จริงให้ดูก่อน"_
|
||||||
|
- วิธีนี้จะบังคับให้ผมต้องไปดึงข้อมูลจากไฟล์มาประมวลผลก่อนที่จะเริ่ม "จินตนาการ" หรือเขียนโค้ดใหม่ครับ
|
||||||
|
|
||||||
2. **ใช้คำสั่ง "ห้ามเดา" (Strict Constraints):**
|
2. **ใช้คำสั่ง "ห้ามเดา" (Strict Constraints):**
|
||||||
* เติมประโยคท้ายคำสั่งว่า *"ห้ามใช้โครงสร้างสมมติ ให้ยึดตามไฟล์แนบ `ชื่อไฟล์` เท่านั้น หากไม่เจอข้อมูลให้ถามกลับ"*
|
|
||||||
|
- เติมประโยคท้ายคำสั่งว่า _"ห้ามใช้โครงสร้างสมมติ ให้ยึดตามไฟล์แนบ `ชื่อไฟล์` เท่านั้น หากไม่เจอข้อมูลให้ถามกลับ"_
|
||||||
|
|
||||||
3. **อ้างอิงชื่อไฟล์ชัดเจน:**
|
3. **อ้างอิงชื่อไฟล์ชัดเจน:**
|
||||||
* ระบุชื่อไฟล์ในคำสั่งเสมอ เช่น *"อ้างอิงจาก `Backend Plan`..."* หรือ *"ตาม `Data Dictionary`..."* (ซึ่งคุณทำได้ดีแล้วครับ แต่ผมประมวลผลพลาดเองในรอบนี้)
|
- ระบุชื่อไฟล์ในคำสั่งเสมอ เช่น _"อ้างอิงจาก `Backend Plan`..."_ หรือ _"ตาม `Data Dictionary`..."_ (ซึ่งคุณทำได้ดีแล้วครับ แต่ผมประมวลผลพลาดเองในรอบนี้)
|
||||||
|
|
||||||
**สรุป:**
|
**สรุป:**
|
||||||
คุณไม่ต้องไปตั้งค่าที่ไหนครับ แต่ใช้การ **"สั่งให้ผมทวนข้อมูลจากไฟล์"** เป็นขั้นตอนแรกก่อนเริ่มงาน จะช่วยลดโอกาสผิดพลาดได้มากที่สุดครับ
|
คุณไม่ต้องไปตั้งค่าที่ไหนครับ แต่ใช้การ **"สั่งให้ผมทวนข้อมูลจากไฟล์"** เป็นขั้นตอนแรกก่อนเริ่มงาน จะช่วยลดโอกาสผิดพลาดได้มากที่สุดครับ
|
||||||
|
|
||||||
ตอนนี้ผมพร้อมทำงานต่อโดยยึดข้อมูลจากไฟล์ `01_lcbp3_v1_4_3.sql` และไฟล์อื่นๆ ที่คุณให้มาอย่างเคร่งครัดครับ มีส่วนไหนให้ผมดำเนินการต่อไหมครับ?
|
ตอนนี้ผมพร้อมทำงานต่อโดยยึดข้อมูลจากไฟล์ `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)
|
||||||
|
|
||||||
จากการตรวจสอบเปรียบเทียบ:
|
เราจำเป็นต้องสร้าง `JsonSchemaModule` ขึ้นมาใหม่ โดยภายในจะประกอบด้วย Services ย่อยตามหน้าที่งาน เพื่อให้เป็นไปตามหลัก Single Responsibility Principle ครับ
|
||||||
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
|
|
||||||
|
|
||||||
**จุดที่ต้องเน้นย้ำ:** โครงสร้างไฟล์ (File Structure) มีไฟล์ `.ts` ปรากฏอยู่ครบ แต่สิ่งที่ผม "มองไม่เห็น" คือ **เนื้อหาโค้ดภายใน (Internal Logic)** ว่าได้ Implement ตาม Logic ที่ซับซ้อน (เช่น Redlock, Two-Phase Commit, DSL Parser) ไว้ถูกต้องหรือไม่
|
**Path:** `src/modules/json-schema/`
|
||||||
|
|
||||||
---
|
```text
|
||||||
|
src/modules/json-schema
|
||||||
### 🧐 **2. การตรวจสอบรายละเอียด (Detailed Review)**
|
├── 📁 dto
|
||||||
|
│ ├── create-json-schema.dto.ts # DTO สำหรับสร้าง Schema ใหม่
|
||||||
#### **✅ ส่วนที่มีความครบถ้วนสมบูรณ์ (Well-Implemented Areas)**
|
│ ├── update-json-schema.dto.ts # DTO สำหรับแก้ไข Schema
|
||||||
|
│ ├── validate-data.dto.ts # DTO สำหรับรับข้อมูลมา Validate
|
||||||
1. **Database Partitioning & Optimization:**
|
│ └── migrate-data.dto.ts # DTO สำหรับสั่ง Migrate Data
|
||||||
* SQL มีการทำ `PARTITION BY RANGE` ให้กับ `audit_logs` และ `notifications` แล้ว (ตาม Req 6.2)
|
├── 📁 entities
|
||||||
* มี `VIRTUAL COLUMNS` สำหรับดึงค่า JSON มาทำ Index (ตาม Req 3.11.3)
|
│ └── json-schema.entity.ts # Entity หลักเก็บ Definition (T2.5.1)
|
||||||
2. **Security & Identity:**
|
├── 📁 services
|
||||||
* ตาราง `users`, `roles`, `permissions`, `user_assignments` รองรับ RBAC 4 ระดับ (Global, Org, Project, Contract) ครบถ้วน
|
│ ├── json-schema.service.ts # Core Service (CRUD, Validation wrapper)
|
||||||
* Folder Structure มี `auth`, `guards`, `decorators` ที่จำเป็นครบ (`jwt-auth.guard.ts`, `rbac.guard.ts`, `require-permission.decorator.ts`)
|
│ ├── virtual-column.service.ts # จัดการ Generated Columns ใน DB (T2.5.3)
|
||||||
3. **File Management:**
|
│ ├── schema-migration.service.ts # จัดการ Data Migration ระหว่าง Version (T2.5.5)
|
||||||
* SQL มีตาราง `attachments` ที่มี field `is_temporary`, `temp_id`, `checksum` รองรับ Two-Phase Storage
|
│ └── json-security.service.ts # จัดการ Security & Encryption (T2.5.6)
|
||||||
* Folder มี `file-storage` module และ `file-cleanup.service.ts` (Cron job) เตรียมไว้แล้ว
|
├── 📁 interfaces
|
||||||
4. **Workflow & RFA:**
|
│ ├── ui-schema.interface.ts # Type definition สำหรับ UI Schema (T2.5.4)
|
||||||
* SQL มีตารางเก็บ Template และ Instance (`..._routings`, `..._workflows`) แยกกันชัดเจน และมี field `workflow_config` (JSON) สำหรับ DSL
|
│ └── virtual-column.interface.ts # Type definition สำหรับ Virtual Column Config
|
||||||
* Folder มี `workflow-engine` module และ `workflow-dsl.service.ts` (ตาม Phase 6A)
|
├── 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)
|
||||||
|
|
||||||
#### ⚠️ **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;
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### 2.2 ปรับปรุงตาราง `rfas` และ `document_number_counters`
|
---
|
||||||
|
|
||||||
เนื่องจาก RFA/RFI ต้องวิ่งเลขตาม Discipline เราจึงต้องเก็บ Discipline ไว้ใน RFA และต้องใช้เป็นส่วนหนึ่งของ Key ในการนับเลข
|
### 📝 2. รายละเอียดงานในแต่ละ Task (Tasks Breakdown)
|
||||||
|
|
||||||
```sql
|
#### **[ ] T2.5.1 JSON Schema Registry & Versioning**
|
||||||
-- เพิ่ม 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);
|
|
||||||
|
|
||||||
-- เพิ่ม discipline_id ในตาราง Correspondences (สำหรับ RFI)
|
- **สิ่งที่ต้องทำ:** สร้าง `JsonSchema` Entity
|
||||||
ALTER TABLE correspondences
|
- **รายละเอียด:**
|
||||||
ADD COLUMN discipline_id INT NULL AFTER correspondence_type_id,
|
- เก็บ `schema_definition` (AJV format)
|
||||||
ADD CONSTRAINT fk_corr_discipline FOREIGN KEY (discipline_id) REFERENCES disciplines(id);
|
- เก็บ `ui_schema` (Form configuration)
|
||||||
|
- รองรับ `version` เพื่อทำ Schema Evolution
|
||||||
|
- เก็บ config ของ `virtual_columns`
|
||||||
|
|
||||||
-- ปรับปรุงตาราง Counter ให้รองรับ Discipline และ SubType (ใช้เป็น Dynamic Key หรือเพิ่ม Column)
|
#### **[ ] T2.5.2 Schema Validation & Transformation Engine**
|
||||||
-- เพื่อความ 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 ทั่วไป
|
|
||||||
```
|
|
||||||
|
|
||||||
-----
|
- **สิ่งที่ต้องทำ:** 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}`
|
#### **[ ] T2.5.5 Data Migration & Version Compatibility**
|
||||||
* Transmittal: `{ORG}-{ORG}-{SUBTYPE_NUM}-{SEQ:4}-{YEAR}`
|
|
||||||
* RFA: `{CONTRACT}-{TYPE}-{DISCIPLINE}-{SUBTYPE}-{SEQ:4}-{REV}`
|
|
||||||
|
|
||||||
### 3.2 Logic การ Generate (Pseudo Code)
|
- **สิ่งที่ต้องทำ:** สร้าง `SchemaMigrationService`
|
||||||
|
- **รายละเอียด:**
|
||||||
|
- Logic การแปลงข้อมูลเก่าให้เข้ากับ Schema ใหม่ (Field Rename, Transform Value)
|
||||||
|
- ฟังก์ชัน `migrateData(entityType, entityId, targetVersion)`
|
||||||
|
|
||||||
```typescript
|
#### **[ ] T2.5.6 Security & Access Control for JSON Data**
|
||||||
// File: src/modules/document-numbering/document-numbering.service.ts
|
|
||||||
|
|
||||||
/**
|
- **สิ่งที่ต้องทำ:** สร้าง `JsonSecurityService`
|
||||||
* Strategy:
|
- **รายละเอียด:**
|
||||||
* 1. รับค่า Context (Project, Org, Type, Discipline, SubType, Year)
|
- **Field-level Security:** ตรวจสอบ Role ว่าเห็น Field นี้ได้ไหม (Masking/Removal)
|
||||||
* 2. ดึง Format Template จาก DB ตาม Type
|
- **Encryption:** เข้ารหัส Field ที่ Sensitive ก่อนบันทึกลง JSON
|
||||||
* 3. ถ้าเป็น Transmittal ต้องเช็คเงื่อนไขพิเศษเพื่อเลือก Template (Owner vs Contractor)
|
|
||||||
* 4. Parse Template เพื่อหาว่าต้องใช้ Key ไหนบ้างในการ Lock และ Count
|
|
||||||
* 5. Run Redlock + Optimistic Lock
|
|
||||||
* 6. Replace Tokens
|
|
||||||
*/
|
|
||||||
|
|
||||||
async generateNextNumber(ctx: GenerateNumberContext): Promise<string> {
|
#### **[ ] T2.5.7 API Design & Integration**
|
||||||
// 1. Get Template
|
|
||||||
let template = await this.formatRepo.findOne({ ... });
|
|
||||||
|
|
||||||
// 2. Handle Special Logic (Requirement 2.2 Transmittal)
|
- **สิ่งที่ต้องทำ:** สร้าง `JsonSchemaController`
|
||||||
if (ctx.typeCode === 'TRANSMITTAL') {
|
- **Endpoints:**
|
||||||
// Logic: ถ้าส่งให้ Owner ใช้ Template A, ถ้า Contractor ใช้ Template B
|
- `POST /json-schema/schemas`: สร้าง Schema
|
||||||
// หรือดึง sub_type_number มาเตรียมไว้
|
- `POST /json-schema/validate/:name`: ตรวจสอบข้อมูล
|
||||||
}
|
- `POST /json-schema/migrate/:type/:id`: สั่ง Migrate
|
||||||
|
- `GET /json-schema/ui-schema/:name`: ดึง UI Config
|
||||||
|
|
||||||
// 3. Resolve Tokens Values
|
#### **[ ] T2.5.8 Integration with Document Modules**
|
||||||
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),
|
|
||||||
};
|
|
||||||
|
|
||||||
// 4. Construct Counter Key
|
- **สิ่งที่ต้องทำ:** แก้ไข Service อื่นๆ (Correspondence, RFA)
|
||||||
// ถ้า Template มี {DISCIPLINE} ต้องเอา discipline_id มาเป็นส่วนหนึ่งของ Composite Key ใน DB Counter
|
- **รายละเอียด:**
|
||||||
const counterKey = {
|
- Inject `JsonSchemaService` เข้าไปใน `CorrespondenceService` และ `RfaService`
|
||||||
projectId: ctx.projectId,
|
- เรียก `validateData` ก่อน create/update
|
||||||
orgId: ctx.orgId,
|
- เรียก `setupVirtualColumns` หลัง create schema ใหม่
|
||||||
typeId: ctx.typeId,
|
|
||||||
disciplineId: template.includes('{DISCIPLINE}') ? ctx.disciplineId : null,
|
|
||||||
year: template.includes('{YEAR}') ? ctx.year : 0 // ถ้าไม่ใช้ปี ให้ใช้ 0
|
|
||||||
};
|
|
||||||
|
|
||||||
// 5. Execute Double-Lock Mechanism (Redis + DB Optimistic)
|
#### **[ ] T2.5.9 Testing Strategy**
|
||||||
const seq = await this.runDistributedCounter(counterKey);
|
|
||||||
|
|
||||||
// 6. Final Replace
|
- **สิ่งที่ต้องทำ:** เขียน Unit Test
|
||||||
let finalNumber = template.formatTemplate;
|
- **รายละเอียด:**
|
||||||
for (const [key, value] of Object.entries(tokens)) {
|
- ทดสอบ Validation Logic (ผ่าน/ไม่ผ่าน)
|
||||||
finalNumber = finalNumber.replace(key, value || '00'); // Fallback if missing
|
- ทดสอบ Virtual Column Generation (SQL ถูกต้องไหม)
|
||||||
}
|
- ทดสอบ Security Masking
|
||||||
// Replace SEQ with padding
|
|
||||||
finalNumber = finalNumber.replace(/{SEQ:(\d+)}/, (_, width) =>
|
|
||||||
String(seq).padStart(Number(width), '0')
|
|
||||||
);
|
|
||||||
|
|
||||||
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:**
|
ท่านต้องการให้ผมเริ่มเขียนโค้ดส่วนไหนก่อนหรือไม่ครับ? (เช่น Entity หรือ VirtualColumnService)
|
||||||
* รัน 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
|
|
||||||
|
|||||||
3
frontend/.vscode/settings.json
vendored
Normal file
3
frontend/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"jest.rootPath": "/"
|
||||||
|
}
|
||||||
@@ -19,7 +19,16 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"paths": {
|
"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"],
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
|
|||||||
@@ -123,36 +123,51 @@
|
|||||||
],
|
],
|
||||||
"eslint.alwaysShowStatus": true,
|
"eslint.alwaysShowStatus": true,
|
||||||
"eslint.format.enable": false,
|
"eslint.format.enable": false,
|
||||||
"eslint.workingDirectories": ["./backend", "./frontend"],
|
"eslint.lintTask.enable": true,
|
||||||
|
"eslint.workingDirectories": [
|
||||||
|
"./backend",
|
||||||
|
"./frontend"
|
||||||
|
],
|
||||||
// ========================================
|
// ========================================
|
||||||
// PATH INTELLISENSE - ไม่มี src folder
|
// PATH INTELLISENSE - ไม่มี src folder
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
"path-intellisense.mappings": {
|
"path-intellisense.mappings": {
|
||||||
// Backend paths (ไม่มี src)
|
// Backend paths
|
||||||
"@backend": "${workspaceFolder:🔧 Backend}",
|
"@backend": "${workspaceFolder:🔧 Backend (NestJS)}/src",
|
||||||
"@backend/*": "${workspaceFolder:🔧 Backend}/*",
|
"@backend/*": "${workspaceFolder:🔧 Backend (NestJS)}/src/*",
|
||||||
"@modules": "${workspaceFolder:🔧 Backend}/modules",
|
"@modules": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules",
|
||||||
"@common": "${workspaceFolder:🔧 Backend}/common",
|
"@common": "${workspaceFolder:🔧 Backend (NestJS)}/src/common",
|
||||||
"@config": "${workspaceFolder:🔧 Backend}/config",
|
"@config": "${workspaceFolder:🔧 Backend (NestJS)}/src/common/config",
|
||||||
"@utils": "${workspaceFolder:🔧 Backend}/utils",
|
"@circulation": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/circulation",
|
||||||
"@entities": "${workspaceFolder:🔧 Backend}/entities",
|
"@correspondence": "${workspaceFolder:🔧 Backend (NestJS)}/src/modules/correspondence",
|
||||||
"@dto": "${workspaceFolder:🔧 Backend}/dto",
|
"@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)
|
// Frontend paths (ไม่มี src)
|
||||||
"@": "${workspaceFolder:🎨 Frontend}",
|
"@": "${workspaceFolder:🎨 Frontend (React/Next.js)}/app",
|
||||||
"@/*": "${workspaceFolder:🎨 Frontend}/*",
|
"@/*": "${workspaceFolder:🎨 Frontend (React/Next.js)}/app/*",
|
||||||
"@components": "${workspaceFolder:🎨 Frontend}/components",
|
"@app": "${workspaceFolder:🎨 Frontend (React/Next.js)}/app",
|
||||||
"@hooks": "${workspaceFolder:🎨 Frontend}/hooks",
|
"@components": "${workspaceFolder:🎨 Frontend (React/Next.js)}/components",
|
||||||
"@utils": "${workspaceFolder:🎨 Frontend}/utils",
|
"@config": "${workspaceFolder:🎨 Frontend (React/Next.js)}/config",
|
||||||
"@lib": "${workspaceFolder:🎨 Frontend}/lib",
|
"@lib": "${workspaceFolder:🎨 Frontend (React/Next.js)}/lib",
|
||||||
"@types": "${workspaceFolder:🎨 Frontend}/types",
|
"@hooks": "${workspaceFolder:🎨 Frontend (React/Next.js)}/app/hooks",
|
||||||
"@api": "${workspaceFolder:🎨 Frontend}/api",
|
"@utils": "${workspaceFolder:🎨 Frontend (React/Next.js)}/utils",
|
||||||
"@styles": "${workspaceFolder:🎨 Frontend}/styles",
|
"@providers": "${workspaceFolder:🎨 Frontend (React/Next.js)}/providers",
|
||||||
"@assets": "${workspaceFolder:🎨 Frontend}/assets",
|
"@public": "${workspaceFolder:🎨 Frontend (React/Next.js)}/public",
|
||||||
"@store": "${workspaceFolder:🎨 Frontend}/store",
|
"@styles": "${workspaceFolder:🎨 Frontend (React/Next.js)}/styles",
|
||||||
"@contexts": "${workspaceFolder:🎨 Frontend}/contexts"
|
"@types": "${workspaceFolder:🎨 Frontend (React/Next.js)}/types",
|
||||||
|
"@api": "${workspaceFolder:🎨 Frontend (React/Next.js)}/app/api",
|
||||||
},
|
},
|
||||||
"path-intellisense.autoSlashAfterDirectory": true,
|
"path-intellisense.autoSlashAfterDirectory": true,
|
||||||
"path-intellisense.extensionOnImport": false,
|
"path-intellisense.extensionOnImport": false,
|
||||||
@@ -187,7 +202,7 @@
|
|||||||
|
|
||||||
// ระบุที่ตั้งของ tailwind.config (ถ้ามี)
|
// ระบุที่ตั้งของ tailwind.config (ถ้ามี)
|
||||||
"tailwindCSS.experimental.configFile": {
|
"tailwindCSS.experimental.configFile": {
|
||||||
"frontend/tailwind.config.js": "frontend/**",
|
//"backend/tailwind.config.js": "backend/**",
|
||||||
"frontend/tailwind.config.ts": "frontend/**"
|
"frontend/tailwind.config.ts": "frontend/**"
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -261,9 +276,18 @@
|
|||||||
// TODO TREE
|
// 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.highlights.enabled": true,
|
||||||
"todo-tree.tree.showInExplorer": true,
|
"todo-tree.tree.showScanModeButton": true,
|
||||||
"todo-tree.filtering.excludeGlobs": [
|
"todo-tree.filtering.excludeGlobs": [
|
||||||
"**/node_modules",
|
"**/node_modules",
|
||||||
"**/dist",
|
"**/dist",
|
||||||
@@ -290,6 +314,11 @@
|
|||||||
"icon": "note",
|
"icon": "note",
|
||||||
"iconColour": "#3498DB",
|
"iconColour": "#3498DB",
|
||||||
"foreground": "#3498DB"
|
"foreground": "#3498DB"
|
||||||
|
},
|
||||||
|
"HACK": {
|
||||||
|
"icon": "tools",
|
||||||
|
"iconColour": "#FFA500",
|
||||||
|
"foreground": "#FFA500"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -302,7 +331,13 @@
|
|||||||
"gitlens.currentLine.format": "${author}, ${agoOrDate}",
|
"gitlens.currentLine.format": "${author}, ${agoOrDate}",
|
||||||
"gitlens.codeLens.enabled": true,
|
"gitlens.codeLens.enabled": true,
|
||||||
"gitlens.codeLens.authors.enabled": false,
|
"gitlens.codeLens.authors.enabled": false,
|
||||||
|
"gitlens.codeLens.recentChange.enabled": true,
|
||||||
"gitlens.hovers.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
|
// GIT
|
||||||
@@ -313,6 +348,8 @@
|
|||||||
"git.autofetchPeriod": 180,
|
"git.autofetchPeriod": 180,
|
||||||
"git.confirmSync": false,
|
"git.confirmSync": false,
|
||||||
"git.enableSmartCommit": true,
|
"git.enableSmartCommit": true,
|
||||||
|
"git.postCommitCommand": "none",
|
||||||
|
"git.untrackedChanges": "separate",
|
||||||
"git.openRepositoryInParentFolders": "always",
|
"git.openRepositoryInParentFolders": "always",
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -323,6 +360,9 @@
|
|||||||
"importCost.largePackageSize": 100,
|
"importCost.largePackageSize": 100,
|
||||||
"importCost.mediumPackageSize": 50,
|
"importCost.mediumPackageSize": 50,
|
||||||
"importCost.smallPackageSize": 20,
|
"importCost.smallPackageSize": 20,
|
||||||
|
"importCost.largePackageColor": "#FF2D00",
|
||||||
|
"importCost.mediumPackageColor": "#FF8C00",
|
||||||
|
"importCost.smallPackageColor": "#98C379",
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// JAVASCRIPT/TYPESCRIPT
|
// JAVASCRIPT/TYPESCRIPT
|
||||||
@@ -332,12 +372,15 @@
|
|||||||
"javascript.updateImportsOnFileMove.enabled": "always",
|
"javascript.updateImportsOnFileMove.enabled": "always",
|
||||||
"javascript.inlayHints.parameterNames.enabled": "all",
|
"javascript.inlayHints.parameterNames.enabled": "all",
|
||||||
"javascript.inlayHints.functionLikeReturnTypes.enabled": true,
|
"javascript.inlayHints.functionLikeReturnTypes.enabled": true,
|
||||||
|
"javascript.inlayHints.variableTypes.enabled": true,
|
||||||
"javascript.preferences.importModuleSpecifier": "relative",
|
"javascript.preferences.importModuleSpecifier": "relative",
|
||||||
|
|
||||||
"typescript.suggest.autoImports": true,
|
"typescript.suggest.autoImports": true,
|
||||||
"typescript.updateImportsOnFileMove.enabled": "always",
|
"typescript.updateImportsOnFileMove.enabled": "always",
|
||||||
"typescript.inlayHints.parameterNames.enabled": "all",
|
"typescript.inlayHints.parameterNames.enabled": "all",
|
||||||
"typescript.inlayHints.functionLikeReturnTypes.enabled": true,
|
"typescript.inlayHints.functionLikeReturnTypes.enabled": true,
|
||||||
|
"typescript.inlayHints.variableTypes.enabled": false,
|
||||||
|
"typescript.inlayHints.propertyDeclarationTypes.enabled": true,
|
||||||
"typescript.preferences.importModuleSpecifier": "relative",
|
"typescript.preferences.importModuleSpecifier": "relative",
|
||||||
"typescript.tsdk": "node_modules/typescript/lib",
|
"typescript.tsdk": "node_modules/typescript/lib",
|
||||||
|
|
||||||
@@ -350,19 +393,21 @@
|
|||||||
"typescript": "typescriptreact"
|
"typescript": "typescriptreact"
|
||||||
},
|
},
|
||||||
"emmet.triggerExpansionOnTab": true,
|
"emmet.triggerExpansionOnTab": true,
|
||||||
|
"emmet.showSuggestionsAsSnippets": true,
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// FILES
|
// FILES
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
"files.autoSave": "onFocusChange",
|
//"files.autoSave": "onFocusChange",
|
||||||
"files.trimTrailingWhitespace": true,
|
"files.trimTrailingWhitespace": true,
|
||||||
"files.insertFinalNewline": true,
|
"files.insertFinalNewline": true,
|
||||||
"files.encoding": "utf8",
|
"files.encoding": "utf8",
|
||||||
"files.eol": "\n",
|
"files.eol": "\n",
|
||||||
"files.associations": {
|
"files.associations": {
|
||||||
"*.css": "tailwindcss",
|
"*.css": "tailwindcss",
|
||||||
".env*": "dotenv"
|
".env*": "dotenv",
|
||||||
|
"*.md": "markdown"
|
||||||
},
|
},
|
||||||
|
|
||||||
"files.exclude": {
|
"files.exclude": {
|
||||||
@@ -373,15 +418,19 @@
|
|||||||
"**/dist": true,
|
"**/dist": true,
|
||||||
"**/build": true,
|
"**/build": true,
|
||||||
"**/.turbo": true,
|
"**/.turbo": true,
|
||||||
"**/coverage": true
|
"**/coverage": true,
|
||||||
|
"**/.nyc_output": true,
|
||||||
|
"**/*.log": true
|
||||||
},
|
},
|
||||||
|
|
||||||
"files.watcherExclude": {
|
"files.watcherExclude": {
|
||||||
"**/.git/objects/**": true,
|
"**/.git/objects/**": true,
|
||||||
|
"**/.git/subtree-cache/**": true,
|
||||||
"**/node_modules/**": true,
|
"**/node_modules/**": true,
|
||||||
"**/.next/**": true,
|
"**/.next/**": true,
|
||||||
"**/dist/**": true,
|
"**/dist/**": true,
|
||||||
"**/build/**": true
|
"**/build/**": true,
|
||||||
|
"**/.turbo/**": true
|
||||||
},
|
},
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -393,11 +442,15 @@
|
|||||||
"**/dist": true,
|
"**/dist": true,
|
||||||
"**/build": true,
|
"**/build": true,
|
||||||
"**/.next": true,
|
"**/.next": true,
|
||||||
|
"**/.turbo": true,
|
||||||
"**/coverage": true,
|
"**/coverage": true,
|
||||||
|
"**/.nyc_output": true,
|
||||||
"**/yarn.lock": true,
|
"**/yarn.lock": true,
|
||||||
"**/package-lock.json": true,
|
"**/package-lock.json": true,
|
||||||
"**/pnpm-lock.yaml": true
|
"**/pnpm-lock.yaml": true,
|
||||||
|
"**/*.log": true
|
||||||
},
|
},
|
||||||
|
"search.followSymlinks": false,
|
||||||
"search.useIgnoreFiles": true,
|
"search.useIgnoreFiles": true,
|
||||||
"search.smartCase": true,
|
"search.smartCase": true,
|
||||||
|
|
||||||
@@ -405,10 +458,12 @@
|
|||||||
// TERMINAL
|
// TERMINAL
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
"terminal.integrated.fontSize": 13,
|
"terminal.integrated.fontSize": 14,
|
||||||
"terminal.integrated.lineHeight": 1.2,
|
"terminal.integrated.lineHeight": 1.2,
|
||||||
"terminal.integrated.smoothScrolling": true,
|
"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}",
|
"terminal.integrated.cwd": "${workspaceFolder}",
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -420,8 +475,20 @@
|
|||||||
"workbench.tree.indent": 15,
|
"workbench.tree.indent": 15,
|
||||||
"workbench.list.smoothScrolling": true,
|
"workbench.list.smoothScrolling": true,
|
||||||
"workbench.editor.enablePreview": false,
|
"workbench.editor.enablePreview": false,
|
||||||
|
"workbench.editor.limit.enabled": true,
|
||||||
|
"workbench.editor.limit.value": 10,
|
||||||
"workbench.startupEditor": "welcomePage",
|
"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
|
// EXPLORER
|
||||||
// ========================================
|
// ========================================
|
||||||
@@ -443,29 +510,44 @@
|
|||||||
"*.jsx": "${capture}.test.jsx, ${capture}.spec.jsx"
|
"*.jsx": "${capture}.test.jsx, ${capture}.spec.jsx"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// BREADCRUMBS
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
"breadcrumbs.enabled": true,
|
||||||
|
"breadcrumbs.filePath": "on",
|
||||||
|
"breadcrumbs.symbolPath": "on",
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// JEST
|
// JEST
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
"jest.autoRun": "off",
|
"jest.autoRun": "off",
|
||||||
"jest.showCoverageOnLoad": false,
|
"jest.showCoverageOnLoad": false,
|
||||||
|
"jest.rootPath": "backend",
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// DOCKER
|
// DOCKER
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
"docker.languageserver.formatter.ignoreMultilineInstructions": true,
|
"docker.languageserver.formatter.ignoreMultilineInstructions": true,
|
||||||
|
"docker.showStartPage": false,
|
||||||
// ========================================
|
// ========================================
|
||||||
// YAML
|
// YAML
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
"yaml.schemas": {
|
"yaml.schemas": {
|
||||||
"https://json.schemastore.org/github-workflow.json": ".github/workflows/*.{yml,yaml}",
|
"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}"
|
"https://json.schemastore.org/docker-compose.json": "docker-compose*.{yml,yaml}"
|
||||||
},
|
},
|
||||||
"yaml.format.enable": true,
|
"yaml.format.enable": true,
|
||||||
|
"yaml.format.singleQuote": false,
|
||||||
|
"yaml.format.bracketSpacing": true,
|
||||||
"yaml.validate": true,
|
"yaml.validate": true,
|
||||||
|
"yaml.hover": true,
|
||||||
|
"yaml.completion": true,
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// REST CLIENT
|
// REST CLIENT
|
||||||
@@ -481,15 +563,32 @@
|
|||||||
"apiUrl": "http://localhost:3000"
|
"apiUrl": "http://localhost:3000"
|
||||||
},
|
},
|
||||||
"production": {
|
"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
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
"material-icon-theme.folders.theme": "specific",
|
"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": {
|
"material-icon-theme.folders.associations": {
|
||||||
"hooks": "Custom",
|
"hooks": "Custom",
|
||||||
"utils": "Helper",
|
"utils": "Helper",
|
||||||
@@ -503,13 +602,32 @@
|
|||||||
"config": "Config"
|
"config": "Config"
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// ========================================
|
||||||
|
// NPM INTELLISENSE
|
||||||
|
// ========================================
|
||||||
|
|
||||||
|
"npm-intellisense.importES6": true,
|
||||||
|
"npm-intellisense.importQuotes": "'",
|
||||||
|
"npm-intellisense.importLinebreak": ";\n",
|
||||||
|
"npm-intellisense.importDeclarationType": "const",
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// PERFORMANCE
|
// PERFORMANCE
|
||||||
// ========================================
|
// ========================================
|
||||||
|
|
||||||
"files.maxMemoryForLargeFilesMB": 4096,
|
"files.maxMemoryForLargeFilesMB": 4096,
|
||||||
"telemetry.telemetryLevel": "off",
|
"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",
|
"formulahendry.auto-rename-tag",
|
||||||
"ms-azuretools.vscode-docker",
|
"ms-azuretools.vscode-docker",
|
||||||
"mtxr.sqltools",
|
"mtxr.sqltools",
|
||||||
"mongodb.mongodb-vscode",
|
|
||||||
"redhat.vscode-yaml",
|
"redhat.vscode-yaml",
|
||||||
"mikestead.dotenv",
|
"mikestead.dotenv",
|
||||||
"editorconfig.editorconfig",
|
"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