251118:1300 ปรับปรุง Project requirement
This commit is contained in:
@@ -1856,7 +1856,6 @@ CREATE INDEX idx_notifications_created_at ON notifications(created_at);
|
|||||||
-- Indexes for search_indices
|
-- Indexes for search_indices
|
||||||
CREATE INDEX idx_search_indices_entity ON search_indices(entity_type, entity_id);
|
CREATE INDEX idx_search_indices_entity ON search_indices(entity_type, entity_id);
|
||||||
CREATE INDEX idx_search_indices_indexed_at ON search_indices(indexed_at);
|
CREATE INDEX idx_search_indices_indexed_at ON search_indices(indexed_at);
|
||||||
CREATE FULLTEXT INDEX idx_search_indices_content ON search_indices(content);
|
|
||||||
-- Indexes for backup_logs
|
-- Indexes for backup_logs
|
||||||
CREATE INDEX idx_backup_logs_type ON backup_logs(backup_type);
|
CREATE INDEX idx_backup_logs_type ON backup_logs(backup_type);
|
||||||
CREATE INDEX idx_backup_logs_status ON backup_logs(status);
|
CREATE INDEX idx_backup_logs_status ON backup_logs(status);
|
||||||
|
|||||||
483
Documnets/0.html
Normal file
483
Documnets/0.html
Normal file
@@ -0,0 +1,483 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>LCBP3-DMS V1.4.1 Project Infographic</title>
|
||||||
|
<script src="https://cdn.tailwindcss.com"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background-color: #f4f7f6;
|
||||||
|
}
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 600px;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
height: 350px;
|
||||||
|
max-height: 400px;
|
||||||
|
}
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.chart-container {
|
||||||
|
height: 300px;
|
||||||
|
max-height: 350px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.stat-card {
|
||||||
|
background-color: #004AAD;
|
||||||
|
color: white;
|
||||||
|
border-left: 5px solid #FFC107;
|
||||||
|
}
|
||||||
|
.kpi-value {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
.section-card {
|
||||||
|
background-color: white;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.section-header {
|
||||||
|
background-color: #002F6C;
|
||||||
|
color: white;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
.section-content {
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
.flow-box {
|
||||||
|
border: 2px solid #007BFF;
|
||||||
|
background-color: #B3D7FF;
|
||||||
|
color: #002F6C;
|
||||||
|
padding: 0.75rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
text-align: center;
|
||||||
|
font-weight: 600;
|
||||||
|
box-shadow: 0 4px 6px rgba(0, 75, 173, 0.1);
|
||||||
|
}
|
||||||
|
.flow-arrow {
|
||||||
|
font-size: 2rem;
|
||||||
|
color: #004AAD;
|
||||||
|
align-self: center;
|
||||||
|
margin: 0 0.5rem;
|
||||||
|
}
|
||||||
|
.timeline-item {
|
||||||
|
position: relative;
|
||||||
|
padding-left: 2.5rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
border-left: 4px solid #007BFF;
|
||||||
|
}
|
||||||
|
.timeline-dot {
|
||||||
|
position: absolute;
|
||||||
|
left: -11px;
|
||||||
|
top: 0;
|
||||||
|
height: 20px;
|
||||||
|
width: 20px;
|
||||||
|
background-color: #FFC107;
|
||||||
|
border: 3px solid #004AAD;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
.rbac-level {
|
||||||
|
border: 2px dashed;
|
||||||
|
padding: 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="antialiased">
|
||||||
|
|
||||||
|
<header class="bg-white shadow-md sticky top-0 z-50">
|
||||||
|
<nav class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="flex justify-between items-center h-16">
|
||||||
|
<div class="flex-shrink-0 flex items-center">
|
||||||
|
<span class="text-2xl font-bold text-[#002F6C]">LCBP3-DMS</span>
|
||||||
|
<span class="ml-2 text-xl font-semibold text-[#007BFF]">V1.4.1 Project Overview</span>
|
||||||
|
</div>
|
||||||
|
<div class="hidden md:block">
|
||||||
|
<div class="ml-10 flex items-baseline space-x-4">
|
||||||
|
<a href="#kpi" class="text-gray-600 hover:text-[#007BFF] px-3 py-2 rounded-md text-sm font-medium">KPIs</a>
|
||||||
|
<a href="#architecture" class="text-gray-600 hover:text-[#007BFF] px-3 py-2 rounded-md text-sm font-medium">Architecture</a>
|
||||||
|
<a href="#roadmap" class="text-gray-600 hover:text-[#007BFF] px-3 py-2 rounded-md text-sm font-medium">Roadmap</a>
|
||||||
|
<a href="#security" class="text-gray-600 hover:text-[#007BFF] px-3 py-2 rounded-md text-sm font-medium">Security</a>
|
||||||
|
<a href="#stats" class="text-gray-600 hover:text-[#007BFF] px-3 py-2 rounded-md text-sm font-medium">Stats</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<main class="max-w-7xl mx-auto p-4 md:p-8 mt-8">
|
||||||
|
|
||||||
|
<section id="intro" class="section-card">
|
||||||
|
<div class="section-header">
|
||||||
|
<h1 class="text-3xl font-bold">Project Overview: LCBP3 Document Management System (V1.4.1)</h1>
|
||||||
|
</div>
|
||||||
|
<div class="section-content">
|
||||||
|
<p class="text-lg text-gray-700">
|
||||||
|
นี่คือภาพรวมของระบบบริหารจัดการเอกสารโครงการ (DMS) V1.4.1
|
||||||
|
ที่กำลังพัฒนาสำหรับโครงการแหลมฉบังเฟส 3 (LCBP3)
|
||||||
|
เป้าหมายหลักคือการสร้างเว็บแอปพลิเคชั่นที่ทันสมัย ปลอดภัย
|
||||||
|
และมีประสิทธิภาพสูงเพื่อจัดการและควบคุมการสื่อสารด้วยเอกสารที่ซับซ้อน
|
||||||
|
ลดการใช้กระดาษ และเพิ่มความสะดวกในการทำงานร่วมกันระหว่างองค์กร
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="kpi" class="mb-8">
|
||||||
|
<h2 class="text-3xl font-bold text-gray-800 mb-6">Key Performance Indicators (KPIs)</h2>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
|
<div class="stat-card p-6 rounded-lg shadow-lg">
|
||||||
|
<h3 class="text-lg font-semibold uppercase tracking-wider">API Response Time</h3>
|
||||||
|
<p class="kpi-value font-bold mt-2">< 200<span class="text-xl">ms</span></p>
|
||||||
|
<p class="text-sm opacity-90">(90th Percentile)</p>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card p-6 rounded-lg shadow-lg">
|
||||||
|
<h3 class="text-lg font-semibold uppercase tracking-wider">Search Performance</h3>
|
||||||
|
<p class="kpi-value font-bold mt-2">< 500<span class="text-xl">ms</span></p>
|
||||||
|
<p class="text-sm opacity-90">(Elasticsearch)</p>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card p-6 rounded-lg shadow-lg">
|
||||||
|
<h3 class="text-lg font-semibold uppercase tracking-wider">File Upload (50MB)</h3>
|
||||||
|
<p class="kpi-value font-bold mt-2">< 30<span class="text-xl">s</span></p>
|
||||||
|
<p class="text-sm opacity-90">(Inc. Virus Scan)</p>
|
||||||
|
</div>
|
||||||
|
<div class="stat-card p-6 rounded-lg shadow-lg">
|
||||||
|
<h3 class="text-lg font-semibold uppercase tracking-wider">Cache Hit Ratio</h3>
|
||||||
|
<p class="kpi-value font-bold mt-2">> 80<span class="text-xl">%</span></p>
|
||||||
|
<p class="text-sm opacity-90">(Redis)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="architecture" class="section-card">
|
||||||
|
<div class="section-header">
|
||||||
|
<h2 class="text-3xl font-bold">System Architecture & Technology Stack</h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-content">
|
||||||
|
<p class="text-lg text-gray-700 mb-6">
|
||||||
|
สถาปัตยกรรมระบบเป็นแบบ API-First ที่ทำงานบน QNAP Container Station (Docker)
|
||||||
|
โดยมีการแบ่งส่วนบริการ (Services) อย่างชัดเจน
|
||||||
|
เพื่อความสะดวกในการจัดการและบำรุงรักษา
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="border-2 border-gray-200 rounded-lg p-4">
|
||||||
|
<div class="flex flex-col md:flex-row md:items-center justify-center space-y-4 md:space-y-0 md:space-x-4">
|
||||||
|
<div class="flow-box bg-[#FFC107] border-[#004AAD]">Internet User</div>
|
||||||
|
<div class="flow-arrow hidden md:block">→</div>
|
||||||
|
<div class="flow-arrow md:hidden text-center">↓</div>
|
||||||
|
<div class="flow-box">QNAP (WAN)</div>
|
||||||
|
<div class="flow-arrow hidden md:block">→</div>
|
||||||
|
<div class="flow-arrow md:hidden text-center">↓</div>
|
||||||
|
<div class="flow-box bg-green-100 border-green-500 text-green-800">Nginx Proxy Manager</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border-t-2 border-dashed border-gray-400 my-6"></div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-xl font-semibold text-center text-[#002F6C] mb-4">Public Facing Services (ผ่าน NPM)</h3>
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div class="flow-box">Frontend (Next.js)</div>
|
||||||
|
<div class="flow-box">Backend (NestJS)</div>
|
||||||
|
<div class="flow-box">Gitea (Git)</div>
|
||||||
|
<div class="flow-box">n8n (Automation)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 class="text-xl font-semibold text-center text-[#002F6C] mb-4">Internal Services (Backend เรียกใช้)</h3>
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div class="flow-box bg-gray-200 border-gray-400 text-gray-800">MariaDB (Database)</div>
|
||||||
|
<div class="flow-box bg-gray-200 border-gray-400 text-gray-800">Redis (Cache)</div>
|
||||||
|
<div class="flow-box bg-gray-200 border-gray-400 text-gray-800">Elasticsearch (Search)</div>
|
||||||
|
<div class="flow-box bg-gray-200 border-gray-400 text-gray-800">ClamAV (Virus Scan)</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
|
<section id="roadmap" class="section-card">
|
||||||
|
<div class="section-header">
|
||||||
|
<h2 class="text-3xl font-bold">Development Roadmap</h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-content">
|
||||||
|
<p class="text-lg text-gray-700 mb-6">
|
||||||
|
แผนการพัฒนาถูกแบ่งออกเป็น Phase (Backend) และ Sprints (Frontend)
|
||||||
|
เพื่อให้สามารถส่งมอบงานได้อย่างต่อเนื่องและเป็นระบบ
|
||||||
|
</p>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-xl font-semibold text-[#002F6C] mb-4">Backend (NestJS)</h3>
|
||||||
|
<div class="relative">
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-dot"></div>
|
||||||
|
<h4 class="font-bold">Phase 0-1: Setup & Core</h4>
|
||||||
|
<p class="text-sm text-gray-600">Infrastructure, DB Schema, ORM</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-dot"></div>
|
||||||
|
<h4 class="font-bold">Phase 2-3: Auth & RBAC</h4>
|
||||||
|
<p class="text-sm text-gray-600">JWT, Passport, CASL 4-Level</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-dot"></div>
|
||||||
|
<h4 class="font-bold">Phase 4-5: Core Features</h4>
|
||||||
|
<p class="text-sm text-gray-600">Document Upload, RFA Workflow</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-dot"></div>
|
||||||
|
<h4 class="font-bold">Phase 6-8: Integration & Deploy</h4>
|
||||||
|
<p class="text-sm text-gray-600">Search, Cache, Notification, Deploy</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-xl font-semibold text-[#002F6C] mb-4">Frontend (Next.js)</h3>
|
||||||
|
<div class="relative">
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-dot"></div>
|
||||||
|
<h4 class="font-bold">Sprint 1-2: Setup & Auth</h4>
|
||||||
|
<p class="text-sm text-gray-600">shadcn/ui, NextAuth, Layout</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-dot"></div>
|
||||||
|
<h4 class="font-bold">Sprint 3: Dashboard</h4>
|
||||||
|
<p class="text-sm text-gray-600">Charts (Recharts), KPIs</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-dot"></div>
|
||||||
|
<h4 class="font-bold">Sprint 4-5: Document Module</h4>
|
||||||
|
<p class="text-sm text-gray-600">TanStack Table, Upload, Search</p>
|
||||||
|
</div>
|
||||||
|
<div class="timeline-item">
|
||||||
|
<div class="timeline-dot"></div>
|
||||||
|
<h4 class="font-bold">Sprint 6-7: Workflow & Deploy</h4>
|
||||||
|
<p class="text-sm text-gray-600">RFA Forms, Testing, Deploy</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="security" class="section-card">
|
||||||
|
<div class="section-header">
|
||||||
|
<h2 class="text-3xl font-bold">Feature Focus: 4-Level RBAC</h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-content">
|
||||||
|
<p class="text-lg text-gray-700 mb-6">
|
||||||
|
ระบบควบคุมสิทธิ์ (RBAC) เป็นหัวใจสำคัญของความปลอดภัย
|
||||||
|
โดยใช้สถาปัตยกรรม 4 ระดับ (4-Level) เพื่อการควบคุมที่ละเอียดสูงสุด
|
||||||
|
</p>
|
||||||
|
<div class="rbac-level border-[#002F6C]">
|
||||||
|
<span class="font-bold text-lg text-[#002F6C]">🌍 Level 1: Global</span>
|
||||||
|
<p class="text-sm text-gray-600">(Super Admin, System Settings)</p>
|
||||||
|
|
||||||
|
<div class="rbac-level border-[#004AAD]">
|
||||||
|
<span class="font-bold text-lg text-[#004AAD]">🏢 Level 2: Organization</span>
|
||||||
|
<p class="text-sm text-gray-600">(Org Admin, Manage Users & Projects)</p>
|
||||||
|
|
||||||
|
<div class="rbac-level border-[#007BFF]">
|
||||||
|
<span class="font-bold text-lg text-[#007BFF]">🏗️ Level 3: Project</span>
|
||||||
|
<p class="text-sm text-gray-600">(Project Manager, View All Project Docs)</p>
|
||||||
|
|
||||||
|
<div class="rbac-level border-[#B3D7FF]">
|
||||||
|
<span class="font-bold text-lg text-[#002F6C]">📄 Level 4: Contract</span>
|
||||||
|
<p class="text-sm text-gray-600">(Contractor, Access Own Contract Docs Only)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section id="stats" class="section-card">
|
||||||
|
<div class="section-header">
|
||||||
|
<h2 class="text-3xl font-bold">Document Statistics (Mockup Data)</h2>
|
||||||
|
</div>
|
||||||
|
<div class="section-content">
|
||||||
|
<p class="text-lg text-gray-700 mb-6">
|
||||||
|
Dashboard จะแสดงสถิติเอกสารแบบ Real-time
|
||||||
|
(อ้างอิงจาก View: `v_document_statistics` ในฐานข้อมูล)
|
||||||
|
เพื่อช่วยในการติดตามและบริหารจัดการโครงการ
|
||||||
|
</p>
|
||||||
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||||
|
<div>
|
||||||
|
<h3 class="text-xl font-semibold text-center text-[#002F6C] mb-4">เอกสารตามประเภท (By Type)</h3>
|
||||||
|
<p class="text-sm text-center text-gray-600 mb-4">
|
||||||
|
แสดงจำนวนเอกสารทั้งหมดโดยแบ่งตามประเภทหลัก
|
||||||
|
ช่วยให้เห็นภาพรวมของเอกสารในระบบ
|
||||||
|
</p>
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas id="docTypeChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="text-xl font-semibold text-center text-[#002F6C] mb-4">สถานะเอกสาร (By Status)</h3>
|
||||||
|
<p class="text-sm text-center text-gray-600 mb-4">
|
||||||
|
แสดงสัดส่วนของสถานะเอกสารในปัจจุบัน
|
||||||
|
(เช่น ร่าง, รออนุมัติ, อนุมัติแล้ว) เพื่อติดตาม Workflow
|
||||||
|
</p>
|
||||||
|
<div class="chart-container">
|
||||||
|
<canvas id="docStatusChart"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer class="text-center p-6 text-gray-600 text-sm">
|
||||||
|
LCBP3-DMS V1.4.1 Infographic | Generated on: <span id="generation-date"></span>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.getElementById('generation-date').textContent = new Date().toLocaleDateString('en-US', {
|
||||||
|
year: 'numeric', month: 'long', day: 'numeric'
|
||||||
|
});
|
||||||
|
|
||||||
|
const wrapLabel = (label) => {
|
||||||
|
const maxChars = 16;
|
||||||
|
if (typeof label === 'string' && label.length > maxChars) {
|
||||||
|
const words = label.split(' ');
|
||||||
|
const lines = [];
|
||||||
|
let currentLine = '';
|
||||||
|
for (const word of words) {
|
||||||
|
if ((currentLine + word).length > maxChars) {
|
||||||
|
lines.push(currentLine.trim());
|
||||||
|
currentLine = '';
|
||||||
|
}
|
||||||
|
currentLine += word + ' ';
|
||||||
|
}
|
||||||
|
lines.push(currentLine.trim());
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
return label;
|
||||||
|
};
|
||||||
|
|
||||||
|
const tooltipTitleCallback = (tooltipItems) => {
|
||||||
|
const item = tooltipItems[0];
|
||||||
|
let label = item.chart.data.labels[item.dataIndex];
|
||||||
|
if (Array.isArray(label)) {
|
||||||
|
return label.join(' ');
|
||||||
|
} else {
|
||||||
|
return label;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sharedChartOptions = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
title: tooltipTitleCallback
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
font: {
|
||||||
|
family: "'Inter', sans-serif"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
ticks: {
|
||||||
|
font: {
|
||||||
|
family: "'Inter', sans-serif"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
ticks: {
|
||||||
|
font: {
|
||||||
|
family: "'Inter', sans-serif"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const docTypeCtx = document.getElementById('docTypeChart').getContext('2d');
|
||||||
|
const docTypeChart = new Chart(docTypeCtx, {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: [
|
||||||
|
'Correspondence (RFA)',
|
||||||
|
'Shop Drawing',
|
||||||
|
'Contract Drawing',
|
||||||
|
wrapLabel('Technical Specification'),
|
||||||
|
'Method Statement'
|
||||||
|
],
|
||||||
|
datasets: [{
|
||||||
|
label: 'จำนวนเอกสาร',
|
||||||
|
data: [120, 190, 75, 45, 88],
|
||||||
|
backgroundColor: [
|
||||||
|
'#004AAD',
|
||||||
|
'#007BFF',
|
||||||
|
'#B3D7FF',
|
||||||
|
'#002F6C',
|
||||||
|
'#FFC107'
|
||||||
|
],
|
||||||
|
borderColor: '#ffffff',
|
||||||
|
borderWidth: 1
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: { ...sharedChartOptions }
|
||||||
|
});
|
||||||
|
|
||||||
|
const docStatusCtx = document.getElementById('docStatusChart').getContext('2d');
|
||||||
|
const docStatusChart = new Chart(docStatusCtx, {
|
||||||
|
type: 'doughnut',
|
||||||
|
data: {
|
||||||
|
labels: ['Draft', 'Pending Approval', 'Approved', 'Rejected'],
|
||||||
|
datasets: [{
|
||||||
|
label: 'สถานะเอกสาร',
|
||||||
|
data: [30, 45, 150, 15],
|
||||||
|
backgroundColor: [
|
||||||
|
'#B3D7FF',
|
||||||
|
'#FFC107',
|
||||||
|
'#007BFF',
|
||||||
|
'#D9363E'
|
||||||
|
],
|
||||||
|
hoverOffset: 4
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
title: tooltipTitleCallback
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
position: 'bottom',
|
||||||
|
labels: {
|
||||||
|
font: {
|
||||||
|
family: "'Inter', sans-serif"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
220
Documnets/Git_command.md
Normal file
220
Documnets/Git_command.md
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
# **คำสั่งตั้งค่า Gitea ใหม่ทั้งหมด + คำสั่งใช้งานประจำวัน / แก้ปัญหา / branch”**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
📘 Git + Gitea (QNAP / Container Station) – Cheat Sheet
|
||||||
|
คู่มือนี้รวบรวม:
|
||||||
|
|
||||||
|
- คำสั่งตั้งค่า Gitea ใหม่ทั้งหมด
|
||||||
|
- คำสั่งใช้งาน Git ประจำวัน
|
||||||
|
- การแก้ไขปัญหา repository
|
||||||
|
- การทำงานกับ branch
|
||||||
|
- การ reset / clone / merge / rebase
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 SECTION 1 – การตั้งค่า Gitea ใหม่ทั้งหมด
|
||||||
|
|
||||||
|
🔹 1) เคลียร์ host key เดิม ใช้เมื่อ Gitea ถูก reset ใหม่ หรือ IP / key เปลี่ยน
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh-keygen -R "[git.np-dms.work]:2222"
|
||||||
|
```
|
||||||
|
|
||||||
|
🔹 2) เชื่อมต่อครั้งแรก (จะมีคำถาม fingerprint)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -T git@git.np-dms.work -p 2222
|
||||||
|
```
|
||||||
|
|
||||||
|
🔹 3) แสดง SSH public key เพื่อเพิ่มใน Gitea
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cat /root/.ssh/id_ed25519.pub
|
||||||
|
cat /root/.ssh/id_rsa.pub
|
||||||
|
```
|
||||||
|
|
||||||
|
🔹 4) เพิ่ม remote ใหม่ (หากยังไม่ได้เพิ่ม)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote add origin ssh://git@git.np-dms.work:2222/np-dms/lcbp3.git
|
||||||
|
```
|
||||||
|
|
||||||
|
🔹 5) ลบ remote เดิมหากผิด
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote remove origin
|
||||||
|
```
|
||||||
|
|
||||||
|
🔹 6) Push ครั้งแรกหลังตั้งค่า
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push -u origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
🔹 7) Clone repo ใหม่ทั้งหมด
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone ssh://git@git.np-dms.work:2222/np-dms/lcbp3.git
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 SECTION 2 – คำสั่ง Git ใช้งานประจำวัน
|
||||||
|
|
||||||
|
🟦 ตรวจสอบสถานะงาน
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git status
|
||||||
|
```
|
||||||
|
|
||||||
|
🟦 ดูว่าแก้ไฟล์อะไรไป
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git diff
|
||||||
|
```
|
||||||
|
|
||||||
|
🟦 เพิ่มไฟล์ทั้งหมด
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add .
|
||||||
|
```
|
||||||
|
|
||||||
|
🟦 Commit การแก้ไข
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "message"
|
||||||
|
```
|
||||||
|
|
||||||
|
🟦 Push
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
🟦 Pull (ดึงงานล่าสุด)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git pull
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
## 🧩 SECTION 3 – ทำงานกับ Branch
|
||||||
|
|
||||||
|
### ดู branch ทั้งหมด
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git branch
|
||||||
|
```
|
||||||
|
|
||||||
|
### สร้าง branch ใหม่
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout -b feature/login-page
|
||||||
|
```
|
||||||
|
|
||||||
|
### สลับ branch
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout main
|
||||||
|
```
|
||||||
|
|
||||||
|
### ส่ง branch ขึ้น Gitea
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push -u origin feature/login-page
|
||||||
|
```
|
||||||
|
|
||||||
|
### ลบ branch ในเครื่อง
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git branch -d feature/login-page
|
||||||
|
```
|
||||||
|
|
||||||
|
### ลบ branch บน Gitea
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git push origin --delete feature/login-page
|
||||||
|
```
|
||||||
|
|
||||||
|
### Merge branch → main
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout main
|
||||||
|
git pull
|
||||||
|
git merge feature/login-page
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rebase เพื่อให้ history สวย
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git checkout feature/login-page
|
||||||
|
git rebase main
|
||||||
|
git checkout main
|
||||||
|
git merge feature/login-page
|
||||||
|
git push
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧩 SECTION 4 – แก้ไขปัญหา Repo
|
||||||
|
|
||||||
|
🔴 (1) Reset repo ทั้งหมดให้เหมือน remote
|
||||||
|
|
||||||
|
⚠ ใช้เมื่อไฟล์ในเครื่องพัง หรือแก้จนเละ
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git fetch --all
|
||||||
|
git reset --hard origin/main
|
||||||
|
```
|
||||||
|
|
||||||
|
🔴 (2) แก้ปัญหา conflict ตอน pull
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git pull --rebase
|
||||||
|
```
|
||||||
|
|
||||||
|
🔴 (3) ดู remote ว่าชี้ไปทางไหน
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote -v
|
||||||
|
```
|
||||||
|
|
||||||
|
🔴 (4) เปลี่ยน remote ใหม่
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git remote remove origin
|
||||||
|
git remote add origin ssh://git@git.np-dms.work:2222/np-dms/lcbp3.git
|
||||||
|
```
|
||||||
|
|
||||||
|
🔴 (5) Commit message ผิด แก้ใหม่
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit --amend
|
||||||
|
```
|
||||||
|
|
||||||
|
🔴 (6) ย้อน commit ล่าสุด (ไม่ลบไฟล์)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git reset --soft HEAD~1
|
||||||
|
```
|
||||||
|
|
||||||
|
🔴 (7) ดู log แบบสรุป
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git log --oneline --graph
|
||||||
|
```
|
||||||
|
|
||||||
|
🔴 (8) Clone repo ใหม่ทั้งหมด (เมื่อพังหนัก)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rm -rf lcbp3
|
||||||
|
git clone ssh://git@git.np-dms.work:2222/np-dms/lcbp3.git
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📌 END
|
||||||
|
|
||||||
|
```
|
||||||
91
Documnets/Gitea_setting.md
Normal file
91
Documnets/Gitea_setting.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# การติดตั้ง Gitea ใน Docker
|
||||||
|
|
||||||
|
* user id ของ gites:
|
||||||
|
|
||||||
|
* uid=1000(git) gid=1000(git) groups=1000(git)
|
||||||
|
|
||||||
|
## กำหนดสิทธิ
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chown -R 1000:1000 /share/Container/gitea/
|
||||||
|
[/share/Container/git] # ls -l /share/Container/gitea/etc/app.ini
|
||||||
|
[/share/Container/git] # setfacl -R -m u:1000:rwx /share/Container/gitea/
|
||||||
|
[/share/Container/git] # setfacl -R -m u:70:rwx /share/Container/git/postgres/
|
||||||
|
getfacl /share/Container/git/etc/app.ini
|
||||||
|
chown -R 1000:1000 /share/Container/gitea/
|
||||||
|
ล้าง
|
||||||
|
setfacl -R -b /share/Container/gitea/
|
||||||
|
|
||||||
|
chgrp -R administrators /share/Container/gitea/
|
||||||
|
chown -R 1000:1000 /share/Container/gitea/etc /share/Container/gitea/lib /share/Container/gitea/backup
|
||||||
|
setfacl -m u:1000:rwx -m g:1000:rwx /share/Container/gitea/etc /share/Container/gitea/lib /share/Container/gitea/backup
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker file
|
||||||
|
|
||||||
|
```yml
|
||||||
|
# File: share/Container/git/docker-compose.yml
|
||||||
|
# DMS Container v1_4_1 : แยก service และ folder, Application name: git, Servive:gitea
|
||||||
|
networks:
|
||||||
|
lcbp3:
|
||||||
|
external: true
|
||||||
|
giteanet:
|
||||||
|
external: true
|
||||||
|
name: gitnet
|
||||||
|
|
||||||
|
services:
|
||||||
|
gitea:
|
||||||
|
image: gitea/gitea:latest-rootless
|
||||||
|
container_name: gitea
|
||||||
|
restart: always
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
environment:
|
||||||
|
# ---- File ownership in QNAP ----
|
||||||
|
USER_UID: "1000"
|
||||||
|
USER_GID: "1000"
|
||||||
|
TZ: Asia/Bangkok
|
||||||
|
# ---- Server / Reverse proxy (NPM) ----
|
||||||
|
GITEA__server__ROOT_URL: https://git.np-dms.work/
|
||||||
|
GITEA__server__DOMAIN: git.np-dms.work
|
||||||
|
GITEA__server__SSH_DOMAIN: git.np-dms.work
|
||||||
|
GITEA__server__START_SSH_SERVER: "true"
|
||||||
|
GITEA__server__SSH_PORT: "22"
|
||||||
|
GITEA__server__SSH_LISTEN_PORT: "22"
|
||||||
|
GITEA__server__LFS_START_SERVER: "true"
|
||||||
|
GITEA__server__HTTP_ADDR: "0.0.0.0"
|
||||||
|
GITEA__server__HTTP_PORT: "3000"
|
||||||
|
GITEA__server__TRUSTED_PROXIES: "127.0.0.1/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16"
|
||||||
|
# --- การตั้งค่าฐานข้อมูล
|
||||||
|
GITEA__database__DB_TYPE: mysql
|
||||||
|
GITEA__database__HOST: mariadb:3306
|
||||||
|
GITEA__database__NAME: "gitea"
|
||||||
|
GITEA__database__USER: "gitea"
|
||||||
|
GITEA__database__PASSWD: "Center#2025"
|
||||||
|
# --- repos
|
||||||
|
GITEA__repository__ROOT: /var/lib/gitea/git/repositories
|
||||||
|
DISABLE_HTTP_GIT: "false"
|
||||||
|
ENABLE_BASIC_AUTHENTICATION: "true"
|
||||||
|
# --- Enable Package Registry ---
|
||||||
|
GITEA__packages__ENABLED: "true"
|
||||||
|
GITEA__packages__REGISTRY__ENABLED: "true"
|
||||||
|
GITEA__packages__REGISTRY__STORAGE_TYPE: local
|
||||||
|
GITEA__packages__REGISTRY__STORAGE_PATH: /data/registry
|
||||||
|
# Optional: lock install after setup (เปลี่ยนเป็น true เมื่อจบ onboarding)
|
||||||
|
GITEA__security__INSTALL_LOCK: "true"
|
||||||
|
volumes:
|
||||||
|
- /share/Container/gitea/backup:/backup
|
||||||
|
- /share/Container/gitea/etc:/etc/gitea
|
||||||
|
- /share/Container/gitea/lib:/var/lib/gitea
|
||||||
|
# ให้ repo root ใช้จาก /share/dms-data/gitea_repos
|
||||||
|
- /share/dms-data/gitea_repos:/var/lib/gitea/git/repositories
|
||||||
|
- /share/dms-data/gitea_registry:/data/registry
|
||||||
|
- /etc/timezone:/etc/timezone:ro
|
||||||
|
- /etc/localtime:/etc/localtime:ro
|
||||||
|
ports:
|
||||||
|
- "3003:3000" # HTTP (ไปหลัง NPM)
|
||||||
|
- "2222:22" # SSH สำหรับ git clone/push
|
||||||
|
networks:
|
||||||
|
- lcbp3
|
||||||
|
- giteanet
|
||||||
|
```
|
||||||
138
Documnets/MariaDB_setting.md
Normal file
138
Documnets/MariaDB_setting.md
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
# การติดตั้ง MAriaDB และ PHPMyAdmin ใน Docker
|
||||||
|
|
||||||
|
* user id ของ mariadb:
|
||||||
|
|
||||||
|
* uid=0(root) gid=0(root) groups=0(root)
|
||||||
|
|
||||||
|
## กำหนดสิทธิ
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chown -R 999:999 /share/Container/mariadb/init
|
||||||
|
chmod 755 /share/Container/mariadb/init
|
||||||
|
setfacl -R -m u:999:r-x /share/Container/mariadb/init
|
||||||
|
setfacl -R -d -m u:999:r-x /share/Container/mariadb/init
|
||||||
|
|
||||||
|
chown -R 33:33 /share/Container/pma/tmp
|
||||||
|
chmod 755 /share/Container/pma/tmp
|
||||||
|
setfacl -R -m u:33:rwx /share/Container/pma/tmp
|
||||||
|
setfacl -R -d -m u:33:rwx /share/Container/pma/tmp
|
||||||
|
|
||||||
|
chown -R 33:33 /share/dms-data/logs/pma
|
||||||
|
chmod 755 /share/dms-data/logs/pma
|
||||||
|
setfacl -R -m u:33:rwx /share/dms-data/logs/pma
|
||||||
|
setfacl -R -d -m u:33:rwx /share/dms-data/logs/pma
|
||||||
|
|
||||||
|
setfacl -R -m u:1000:rwx /share/Container/gitea
|
||||||
|
setfacl -R -m u:1000:rwx /share/dms-data/gitea_repos
|
||||||
|
setfacl -R -m u:1000:rwx /share/dms-data/gitea_registry
|
||||||
|
```
|
||||||
|
|
||||||
|
## เพิ่ม database & user สำหรับ Nginx Proxy Manager (NPM)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it mariadb mysql -u root -p
|
||||||
|
CREATE DATABASE npm;
|
||||||
|
CREATE USER 'npm'@'%' IDENTIFIED BY 'npm';
|
||||||
|
GRANT ALL PRIVILEGES ON npm.* TO 'npm'@'%';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
```
|
||||||
|
|
||||||
|
## เพิ่ม database & user สำหรับ Gitea
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it mariadb mysql -u root -p
|
||||||
|
CREATE DATABASE gitea CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_unicode_ci';
|
||||||
|
CREATE USER 'gitea'@'%' IDENTIFIED BY 'Center#2025';
|
||||||
|
GRANT ALL PRIVILEGES ON gitea.* TO 'gitea'@'%';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker file
|
||||||
|
|
||||||
|
```yml
|
||||||
|
# File: share/Container/mariadb/docker-compose.yml
|
||||||
|
# DMS Container v1_4_1 : แยก service และ folder,Application name: lcbp3-db, Servive: mariadb, pma
|
||||||
|
x-restart: &restart_policy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
x-logging: &default_logging
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "5"
|
||||||
|
|
||||||
|
services:
|
||||||
|
mariadb:
|
||||||
|
<<: [*restart_policy, *default_logging]
|
||||||
|
image: mariadb:11.8
|
||||||
|
container_name: mariadb
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: "2.0"
|
||||||
|
memory: 4G
|
||||||
|
reservations:
|
||||||
|
cpus: "0.5"
|
||||||
|
memory: 1G
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: "Center#2025"
|
||||||
|
MYSQL_DATABASE: "lcbp3"
|
||||||
|
MYSQL_USER: "center"
|
||||||
|
MYSQL_PASSWORD: "Center#2025"
|
||||||
|
TZ: "Asia/Bangkok"
|
||||||
|
ports:
|
||||||
|
- "3306:3306"
|
||||||
|
volumes:
|
||||||
|
- "/share/Container/mariadb/data:/var/lib/mysql"
|
||||||
|
- "/share/Container/mariadb/my.cnf:/etc/mysql/conf.d/my.cnf:ro"
|
||||||
|
- "/share/Container/mariadb/init:/docker-entrypoint-initdb.d:ro"
|
||||||
|
- "/share/dms-data/mariadb/backup:/backup"
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
["CMD-SHELL", "mysqladmin ping -h 127.0.0.1 -pCenter#2025 || exit 1"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 15
|
||||||
|
networks:
|
||||||
|
lcbp3: {}
|
||||||
|
|
||||||
|
pma:
|
||||||
|
<<: [*restart_policy, *default_logging]
|
||||||
|
image: phpmyadmin:5-apache
|
||||||
|
container_name: pma
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: "0.25"
|
||||||
|
memory: 256M
|
||||||
|
environment:
|
||||||
|
TZ: "Asia/Bangkok"
|
||||||
|
PMA_HOST: "mariadb"
|
||||||
|
PMA_PORT: "3306"
|
||||||
|
PMA_ABSOLUTE_URI: "https://pma.np-dms.work/"
|
||||||
|
UPLOAD_LIMIT: "1G"
|
||||||
|
MEMORY_LIMIT: "512M"
|
||||||
|
ports:
|
||||||
|
- "89:80"
|
||||||
|
# expose:
|
||||||
|
# - "80"
|
||||||
|
volumes:
|
||||||
|
- "/share/Container/pma/config.user.inc.php:/etc/phpmyadmin/config.user.inc.php:ro"
|
||||||
|
- "/share/Container/pma/zzz-custom.ini:/usr/local/etc/php/conf.d/zzz-custom.ini:ro"
|
||||||
|
- "/share/Container/pma/tmp:/var/lib/phpmyadmin/tmp:rw"
|
||||||
|
- "/share/dms-data/logs/pma:/var/log/apache2"
|
||||||
|
depends_on:
|
||||||
|
mariadb:
|
||||||
|
condition: service_healthy
|
||||||
|
networks:
|
||||||
|
lcbp3: {}
|
||||||
|
|
||||||
|
networks:
|
||||||
|
lcbp3:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
99
Documnets/NPM_setting.md
Normal file
99
Documnets/NPM_setting.md
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
# การติดตั้ง Nginx Proxy Manager (NPM) ใน Docker
|
||||||
|
|
||||||
|
* ค่าเริ่มต้นคือ:Email: [admin@example.com] Password: changeme
|
||||||
|
|
||||||
|
* user id ของ NPM:
|
||||||
|
|
||||||
|
* uid=0(root) gid=0(root) groups=0(root)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## กำหนดสิทธิ
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# ตรวจสอบ user id ของ NPM
|
||||||
|
docker exec -it npm id
|
||||||
|
chown -R 0:0 /share/Container/npm
|
||||||
|
setfacl -R -m u:0:rwx /share/Container/npm
|
||||||
|
```
|
||||||
|
|
||||||
|
## Note: Configurations
|
||||||
|
|
||||||
|
| Domain Names | Forward Hostname | IP Forward Port | Cache Assets | Block Common Exploits | Websockets | Force SSL | HTTP/2 | SupportHSTS Enabled |
|
||||||
|
| :----------------------------- | :--------------- | :-------------- | :----------- | :-------------------- | :--------- | :-------- | :----- | :------------------ |
|
||||||
|
| backend.np-dms.work | backend | 3000 | [ ] | [x] | [ ] | [x] | [x] | [ ] |
|
||||||
|
| lcbp3.np-dms.work | frontend | 3000 | [x] | [x] | [x] | [x] | [x] | [ ] |
|
||||||
|
| db.np-dms.work | mariadb | 3306 | [x] | [x] | [x] | [x] | [x] | [ ] |
|
||||||
|
| git.np-dms.work | gitea | 3000 | [x] | [x] | [x] | [x] | [x] | [ ] |
|
||||||
|
| n8n.np-dms.work | n8n | 5678 | [x] | [x] | [x] | [x] | [x] | [ ] |
|
||||||
|
| npm.np-dms.work | npm | 81 | [ ] | [x] | [x] | [x] | [x] | [ ] |
|
||||||
|
| pma.np-dms.work | pma | 80 | [x] | [x] | [ ] | [x] | [x] | [ ] |
|
||||||
|
| np-dms.work, [www.np-dms.work] | localhost | 80 | [x] | [x] | [ ] | [x] | [x] | [ ] |
|
||||||
|
|
||||||
|
## Docker file
|
||||||
|
|
||||||
|
```yml
|
||||||
|
# File: share/Container/npm/docker-compose-npm.yml
|
||||||
|
# DMS Container v1_4_1 แยก service และ folder, Application name: lcbp3-npm, Servive:npm
|
||||||
|
x-restart: &restart_policy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
x-logging: &default_logging
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "5"
|
||||||
|
services:
|
||||||
|
npm:
|
||||||
|
<<: [*restart_policy, *default_logging]
|
||||||
|
image: jc21/nginx-proxy-manager:latest
|
||||||
|
container_name: npm
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: "1.0" # 50% CPU
|
||||||
|
memory: 512M
|
||||||
|
ports:
|
||||||
|
- "80:80" # HTTP
|
||||||
|
- "443:443" # HTTPS
|
||||||
|
- "81:81" # NPM Admin UI
|
||||||
|
environment:
|
||||||
|
TZ: "Asia/Bangkok"
|
||||||
|
DB_MYSQL_HOST: "mariadb"
|
||||||
|
DB_MYSQL_PORT: 3306
|
||||||
|
DB_MYSQL_USER: "npm"
|
||||||
|
DB_MYSQL_PASSWORD: "npm"
|
||||||
|
DB_MYSQL_NAME: "npm"
|
||||||
|
# Uncomment this if IPv6 is not enabled on your host
|
||||||
|
DISABLE_IPV6: "true"
|
||||||
|
networks:
|
||||||
|
- lcbp3
|
||||||
|
- giteanet
|
||||||
|
volumes:
|
||||||
|
- "/share/Container/npm/data:/data"
|
||||||
|
- "/share/dms-data/logs/npm:/data/logs" # <-- เพิ่ม logging volume
|
||||||
|
- "/share/Container/npm/letsencrypt:/etc/letsencrypt"
|
||||||
|
- "/share/Container/npm/custom:/data/nginx/custom" # <-- สำคัญสำหรับ http_top.conf
|
||||||
|
# - "/share/Container/lcbp3/npm/landing:/data/landing:ro"
|
||||||
|
landing:
|
||||||
|
image: nginx:1.27-alpine
|
||||||
|
container_name: landing
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- "/share/Container/npm/landing:/usr/share/nginx/html:ro"
|
||||||
|
networks:
|
||||||
|
- lcbp3
|
||||||
|
networks:
|
||||||
|
lcbp3:
|
||||||
|
external: true
|
||||||
|
giteanet:
|
||||||
|
external: true
|
||||||
|
name: gitnet
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
119
Documnets/Securities.md
Normal file
119
Documnets/Securities.md
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
สวัสดีครับ! การตั้งค่า Network Segmentation และ Firewall Rules เป็นขั้นตอนที่ฉลาดมากครับ โดยเฉพาะเมื่อคุณมี Services ที่ต้องเปิดสู่ Public (เช่น `lcbp3.np-dms.work`) และ Services ภายใน (เช่น `db.np-dms.work`)
|
||||||
|
|
||||||
|
สำหรับอุปกรณ์ Omada (ER7206 + OC200) กลยุทธ์หลักคือการใช้ **VLANs (Virtual LANs)** เพื่อแบ่งกลุ่มอุปกรณ์ และใช้ **Firewall ACLs (Access Control Lists)** เพื่อควบคุมการจราจรระหว่างกลุ่มเหล่านั้น
|
||||||
|
|
||||||
|
นี่คือคำแนะนำตามแนวทาง "Zero Trust" ที่ปรับให้เข้ากับสถาปัตยกรรมของคุณครับ
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 🌐 การแบ่งส่วนเครือข่าย (VLAN Segmentation)
|
||||||
|
|
||||||
|
ใน Omada Controller (OC200) ให้คุณไปที่ `Settings > Wired Networks > LAN` และสร้างเครือข่ายย่อย (VLANs) ดังนี้:
|
||||||
|
|
||||||
|
* **VLAN 1 (Default): Management**
|
||||||
|
* **IP Range:** 192.168.1.x
|
||||||
|
* **วัตถุประสงค์:** ใช้สำหรับอุปกรณ์ Network (ER7206, OC200, Switches) และ PC ของผู้ดูแลระบบ (Admin) เท่านั้น
|
||||||
|
|
||||||
|
* **VLAN 10: Servers (DMZ)**
|
||||||
|
* **IP Range:** 192.168.10.x
|
||||||
|
* **วัตถุประสงค์:** นี่คือ VLAN ที่คุณจะเสียบสาย LAN ของ **QNAP NAS** ครับ QNAP จะได้รับ IP ในกลุ่มนี้ (เช่น `192.168.10.100`)
|
||||||
|
|
||||||
|
* **VLAN 20: Office / Trusted**
|
||||||
|
* **IP Range:** 192.168.20.x
|
||||||
|
* **วัตถุประสงค์:** สำหรับ PC, Notebook, และ Wi-Fi ของพนักงานทั่วไปที่ต้องเข้าใช้งานระบบ (เช่น `lcbp3.np-dms.work`)
|
||||||
|
|
||||||
|
* **VLAN 30: Guests / Untrusted**
|
||||||
|
* **IP Range:** 192.168.30.x
|
||||||
|
* **วัตถุประสงค์:** สำหรับ Wi-Fi แขก (Guest) ห้ามเข้าถึงเครือข่ายภายในโดยเด็ดขาด
|
||||||
|
|
||||||
|
**การตั้งค่า Port Switch:**
|
||||||
|
หลังจากสร้าง VLANs แล้ว ให้ไปที่ `Devices` > เลือก Switch ของคุณ > `Ports` > กำหนด Port Profile:
|
||||||
|
|
||||||
|
* Port ที่เสียบ QNAP NAS: ตั้งค่า Profile เป็น **VLAN 10**
|
||||||
|
* Port ที่เสียบ PC พนักงาน: ตั้งค่า Profile เป็น **VLAN 20**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 🔥 Firewall Rules (ACLs)
|
||||||
|
|
||||||
|
นี่คือหัวใจสำคัญครับ ไปที่ `Settings > Network Security > ACL (Access Control)`
|
||||||
|
|
||||||
|
กฎของ Firewall จะทำงานจากบนลงล่าง (ข้อ 1 ทำก่อนข้อ 2)
|
||||||
|
|
||||||
|
### A. กฎการห้าม (Deny Rules) - สำคัญที่สุด
|
||||||
|
|
||||||
|
**กฎข้อ 1: บล็อก Guest (VLAN 30) ไม่ให้ยุ่งกับใคร**
|
||||||
|
|
||||||
|
* **Name:** Isolate-Guests
|
||||||
|
* **Policy:** Deny
|
||||||
|
* **Source:** `Network` -> `VLAN 30`
|
||||||
|
* **Destination:** `Network` -> `VLAN 1`, `VLAN 10`, `VLAN 20`
|
||||||
|
* *(กฎนี้จะทำให้ Guest ออกอินเทอร์เน็ตได้อย่างเดียว แต่คุยข้าม VLAN ไม่ได้)*
|
||||||
|
|
||||||
|
**กฎข้อ 2: บล็อก Server (VLAN 10) ไม่ให้โจมตีคนอื่น**
|
||||||
|
|
||||||
|
* **Name:** Isolate-Servers
|
||||||
|
* **Policy:** Deny
|
||||||
|
* **Source:** `Network` -> `VLAN 10`
|
||||||
|
* **Destination:** `Network` -> `VLAN 20`
|
||||||
|
* *(กฎนี้ป้องกันไม่ให้ Server (QNAP) ที่อาจถูกแฮก เริ่มเชื่อมต่อไปยัง PC ของพนักงาน (VLAN 20) เพื่อแพร่กระจาย Malware)*
|
||||||
|
|
||||||
|
**กฎข้อ 3: บล็อก Office ไม่ให้เข้าหน้า Admin**
|
||||||
|
|
||||||
|
* **Name:** Block-Office-to-Management
|
||||||
|
* **Policy:** Deny
|
||||||
|
* **Source:** `Network` -> `VLAN 20`
|
||||||
|
* **Destination:** `Network` -> `VLAN 1`
|
||||||
|
* *(ป้องกันไม่ให้พนักงานทั่วไปเข้าหน้าตั้งค่า Router หรือ Controller)*
|
||||||
|
|
||||||
|
### B. กฎการอนุญาต (Allow Rules)
|
||||||
|
|
||||||
|
**กฎข้อ 4: อนุญาตให้ Office (VLAN 20) ใช้งาน Services ที่จำเป็น**
|
||||||
|
|
||||||
|
* **Name:** Allow-Office-to-Services
|
||||||
|
* **Policy:** Allow
|
||||||
|
* **Source:** `Network` -> `VLAN 20`
|
||||||
|
* **Destination:** `IP Group` -> (สร้าง Group ชื่อ `QNAP_Services` ชี้ไปที่ `192.168.10.100` (IP ของ QNAP))
|
||||||
|
* **Port:** `Service` -> (สร้าง Port Group ชื่อ `Web_Services`):
|
||||||
|
* TCP 443 (HTTPS - สำหรับทุก Service เช่น lcbp3, git, pma)
|
||||||
|
* TCP 80 (HTTP - สำหรับ NPM redirect)
|
||||||
|
* TCP 81 (NPM Admin UI)
|
||||||
|
* TCP 2222 (Gitea SSH)
|
||||||
|
* (ไม่จำเป็นต้องเปิด Port 3000, 3003, 5678, 89 เพราะ NPM จัดการให้แล้ว)
|
||||||
|
|
||||||
|
### C. กฎสุดท้าย (Default)
|
||||||
|
|
||||||
|
Omada มักจะมีกฎ "Allow All" อยู่ล่างสุด ให้ปล่อยไว้ หรือถ้าคุณต้องการความปลอดภัยสูงสุด (Zero Trust) ให้เปลี่ยนกฎสุดท้ายเป็น "Deny All" (แต่ต้องมั่นใจว่ากฎ Allow ของคุณครอบคลุมทั้งหมดแล้ว)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 🚪 Port Forwarding (การเปิด Service สู่สาธารณะ)
|
||||||
|
|
||||||
|
ส่วนนี้ไม่ใช่ Firewall ACL แต่จำเป็นเพื่อให้คนนอกเข้าใช้งานได้ครับ
|
||||||
|
ไปที่ `Settings > Transmission > Port Forwarding`
|
||||||
|
|
||||||
|
สร้างกฎเพื่อส่งต่อการจราจรจาก WAN (อินเทอร์เน็ต) ไปยัง Nginx Proxy Manager (NPM) ที่อยู่บน QNAP (VLAN 10)
|
||||||
|
|
||||||
|
* **Name:** Allow-NPM-HTTPS
|
||||||
|
* **External Port:** 443
|
||||||
|
* **Internal Port:** 443
|
||||||
|
* **Internal IP:** `192.168.10.100` (IP ของ QNAP)
|
||||||
|
* **Protocol:** TCP
|
||||||
|
|
||||||
|
* **Name:** Allow-NPM-HTTP (สำหรับ Let's Encrypt)
|
||||||
|
* **External Port:** 80
|
||||||
|
* **Internal Port:** 80
|
||||||
|
* **Internal IP:** `192.168.10.100` (IP ของ QNAP)
|
||||||
|
* **Protocol:** TCP
|
||||||
|
|
||||||
|
### สรุปผังการเชื่อมต่อ
|
||||||
|
|
||||||
|
1. **ผู้ใช้ภายนอก** -> `https://lcbp3.np-dms.work`
|
||||||
|
2. **ER7206** รับที่ Port 443
|
||||||
|
3. **Port Forwarding** ส่งต่อไปยัง `192.168.10.100:443` (QNAP NPM)
|
||||||
|
4. **NPM** (บน QNAP) ส่งต่อไปยัง `backend:3000` หรือ `frontend:3000` ภายใน Docker
|
||||||
|
5. **ผู้ใช้ภายใน (Office)** -> `https://lcbp3.np-dms.work`
|
||||||
|
6. **Firewall ACL** (กฎข้อ 4) อนุญาตให้ VLAN 20 คุยกับ `192.168.10.100:443`
|
||||||
|
7. (ขั้นตอนที่ 3-4 ทำงานเหมือนเดิม)
|
||||||
|
|
||||||
|
การตั้งค่าตามนี้จะช่วยแยกส่วน Server ของคุณออกจากเครือข่ายพนักงานอย่างชัดเจน ซึ่งปลอดภัยกว่าการวางทุกอย่างไว้ในวง LAN เดียวกันมากครับ
|
||||||
143
Documnets/Service_setting.md
Normal file
143
Documnets/Service_setting.md
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
# การติดตั้ง Nginx Proxy Manager (NPM) ใน Docker
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## **📝 คำอธิบายและข้อควรพิจารณา**
|
||||||
|
|
||||||
|
* 1 Redis (Service: cache)
|
||||||
|
|
||||||
|
* Image: redis:7-alpine มีขนาดเล็กและทันสมัย
|
||||||
|
|
||||||
|
* Port: ไม่ได้ expose port 6379 ออกมาที่ Host QNAP เพราะตามสถาปัตยกรรม Service backend (NestJS) จะคุยกับ cache (Redis) ผ่าน lcbp3 network ภายในโดยตรง ซึ่งปลอดภัยกว่าครับ
|
||||||
|
|
||||||
|
* Volume: map data ไปที่ /share/Container/cache/data เผื่อใช้ Redis ในการทำ Persistent Cache (ถ้าต้องการแค่ Locking อาจจะไม่จำเป็นต้อง map volume ก็ได้ครับ)
|
||||||
|
|
||||||
|
* User ID: Image redis:7-alpine รันด้วย user redis (UID 999)
|
||||||
|
|
||||||
|
* 2 Elasticsearch (Service: search)
|
||||||
|
|
||||||
|
* Image: elasticsearch:8.11.1 ผมเลือกเวอร์ชัน 8 ที่ใหม่และระบุชัดเจน (ไม่ใช้ latest) เพื่อความเสถียรครับ
|
||||||
|
|
||||||
|
* Port: ไม่ได้ expose port 9200 ออกมาที่ Host เช่นกัน เพราะ NPM_setting.md ระบุว่า npm (Nginx Proxy Manager) จะ forward search.np-dms.work ไปยัง service search ที่ port 9200 ผ่าน lcbp3 network ครับ
|
||||||
|
|
||||||
|
* Environment (สำคัญมาก):
|
||||||
|
|
||||||
|
* discovery.type: "single-node": ต้องมี ไม่อย่างนั้น Elasticsearch V.8 จะไม่ยอม start ถ้าไม่พบ node อื่นใน cluster
|
||||||
|
|
||||||
|
* xpack.security.enabled: "false": เพื่อความสะดวกในการพัฒนาระยะแรก NestJS จะได้เชื่อมต่อ API port 9200 ได้เลย (หากเปิดใช้งานจะต้องตั้งค่า SSL และ Token ซึ่งซับซ้อนกว่ามาก)
|
||||||
|
|
||||||
|
* ES_JAVA_OPTS: "-Xms1g -Xmx1g": เป็น Best Practice ที่ต้องกำหนด Heap Size ให้ Elasticsearch (ในที่นี้คือ 1GB)
|
||||||
|
|
||||||
|
* User ID: Image elasticsearch รันด้วย user elasticsearch (UID 1000)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## กำหนดสิทธิ
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# สร้าง Directory
|
||||||
|
mkdir -p /share/Container/services/cache/data
|
||||||
|
mkdir -p /share/Container/services/search/data
|
||||||
|
|
||||||
|
# กำหนดสิทธิ์ให้ตรงกับ User ID ใน Container
|
||||||
|
# Redis (UID 999)
|
||||||
|
chown -R 999:999 /share/Container/services/cache/data
|
||||||
|
chmod -R 750 /share/Container/services/cache/data
|
||||||
|
|
||||||
|
# Elasticsearch (UID 1000)
|
||||||
|
chown -R 1000:1000 /share/Container/services/search/data
|
||||||
|
chmod -R 750 /share/Container/services/search/data
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker file
|
||||||
|
|
||||||
|
```yml
|
||||||
|
# File: /share/Container/services/docker-compose.yml (หรือไฟล์ที่คุณใช้รวม)
|
||||||
|
# DMS Container v1_4_1: เพิ่ม Application name: services, Services 'cache' (Redis) และ 'search' (Elasticsearch)
|
||||||
|
|
||||||
|
x-restart: &restart_policy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
x-logging: &default_logging
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "5"
|
||||||
|
|
||||||
|
networks:
|
||||||
|
lcbp3:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
services:
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# 1. Redis (สำหรับ Caching และ Distributed Lock)
|
||||||
|
# Service Name: cache (ตามที่ NPM และ Backend Plan อ้างอิง)
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
cache:
|
||||||
|
<<: [*restart_policy, *default_logging]
|
||||||
|
image: redis:7-alpine # ใช้ Alpine image เพื่อให้มีขนาดเล็ก
|
||||||
|
container_name: cache
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: "1.0"
|
||||||
|
memory: 2G # Redis เป็น in-memory, ให้ memory เพียงพอต่อการใช้งาน
|
||||||
|
reservations:
|
||||||
|
cpus: "0.25"
|
||||||
|
memory: 512M
|
||||||
|
environment:
|
||||||
|
TZ: "Asia/Bangkok"
|
||||||
|
networks:
|
||||||
|
- lcbp3 # เชื่อมต่อ network ภายในเท่านั้น
|
||||||
|
volumes:
|
||||||
|
- "/share/Container/cache/data:/data" # Map volume สำหรับเก็บข้อมูล (ถ้าต้องการ persistence)
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "redis-cli", "ping"] # ตรวจสอบว่า service พร้อมใช้งาน
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
# 2. Elasticsearch (สำหรับ Advanced Search)
|
||||||
|
# Service Name: search (ตามที่ NPM และ Backend Plan อ้างอิง)
|
||||||
|
# ----------------------------------------------------------------
|
||||||
|
search:
|
||||||
|
<<: [*restart_policy, *default_logging]
|
||||||
|
image: elasticsearch:8.11.1 # แนะนำให้ระบุเวอร์ชันชัดเจน (V.8)
|
||||||
|
container_name: search
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: "2.0" # Elasticsearch ใช้ CPU และ Memory ค่อนข้างหนัก
|
||||||
|
memory: 4G
|
||||||
|
reservations:
|
||||||
|
cpus: "0.5"
|
||||||
|
memory: 2G
|
||||||
|
environment:
|
||||||
|
TZ: "Asia/Bangkok"
|
||||||
|
# --- Critical Settings for Single-Node ---
|
||||||
|
discovery.type: "single-node" # สำคัญมาก: กำหนดให้รันแบบ 1 node
|
||||||
|
# --- Security (Disable for Development) ---
|
||||||
|
# ปิด xpack security เพื่อให้ NestJS เชื่อมต่อง่าย (backend -> search:9200)
|
||||||
|
# หากเป็น Production จริง ควรเปิดใช้งานและตั้งค่า token/cert ครับ
|
||||||
|
xpack.security.enabled: "false"
|
||||||
|
# --- Performance Tuning ---
|
||||||
|
# กำหนด Heap size (1GB) ให้เหมาะสมกับ memory limit (4GB)
|
||||||
|
ES_JAVA_OPTS: "-Xms1g -Xmx1g"
|
||||||
|
networks:
|
||||||
|
- lcbp3 # เชื่อมต่อ network ภายใน (NPM จะ proxy port 9200 จากภายนอก)
|
||||||
|
volumes:
|
||||||
|
- "/share/Container/search/data:/usr/share/elasticsearch/data" # Map volume สำหรับเก็บ data/indices
|
||||||
|
healthcheck:
|
||||||
|
# รอจนกว่า cluster health จะเป็น yellow หรือ green
|
||||||
|
test: ["CMD-SHELL", "curl -s http://localhost:9200/_cluster/health | grep -q '\"status\":\"green\"\\|\\\"status\":\"yellow\"'"]
|
||||||
|
interval: 30s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
|
```
|
||||||
91
Documnets/n8n_setting.md
Normal file
91
Documnets/n8n_setting.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# การติดตั้ง n8n ใน Docker
|
||||||
|
|
||||||
|
* user id ของ gites:
|
||||||
|
|
||||||
|
* uid=1000(node) gid=1000(node) groups=1000(node)
|
||||||
|
|
||||||
|
## กำหนดสิทธิ
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# สำหรับ n8n volumes
|
||||||
|
chown -R 1000:1000 /share/Container/n8n
|
||||||
|
chmod -R 755 /share/Container/n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker file
|
||||||
|
|
||||||
|
```yml
|
||||||
|
# File: share/Container/n8n/docker-compose.yml
|
||||||
|
# DMS Container v1_4_1 แยก service และ folder, Application name:n8n service n8n
|
||||||
|
x-restart: &restart_policy
|
||||||
|
restart: unless-stopped
|
||||||
|
|
||||||
|
x-logging: &default_logging
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "10m"
|
||||||
|
max-file: "5"
|
||||||
|
services:
|
||||||
|
n8n:
|
||||||
|
<<: [*restart_policy, *default_logging]
|
||||||
|
image: n8nio/n8n:latest
|
||||||
|
container_name: n8n
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
limits:
|
||||||
|
cpus: "1.5"
|
||||||
|
memory: 2G
|
||||||
|
reservations:
|
||||||
|
cpus: "0.25"
|
||||||
|
memory: 512M
|
||||||
|
environment:
|
||||||
|
TZ: "Asia/Bangkok"
|
||||||
|
NODE_ENV: "production"
|
||||||
|
# N8N_PATH: "/n8n/"
|
||||||
|
N8N_PUBLIC_URL: "https://n8n.np-dms.work/"
|
||||||
|
WEBHOOK_URL: "https://n8n.np-dms.work/"
|
||||||
|
N8N_EDITOR_BASE_URL: "https://n8n.np-dms.work/"
|
||||||
|
N8N_PROTOCOL: "https"
|
||||||
|
N8N_HOST: "n8n.np-dms.work"
|
||||||
|
N8N_PORT: 5678
|
||||||
|
N8N_PROXY_HOPS: "1"
|
||||||
|
N8N_DIAGNOSTICS_ENABLED: 'false'
|
||||||
|
N8N_SECURE_COOKIE: 'true'
|
||||||
|
N8N_ENCRYPTION_KEY: "9AAIB7Da9DW1qAhJE5/Bz4SnbQjeAngI"
|
||||||
|
N8N_BASIC_AUTH_ACTIVE: 'true'
|
||||||
|
N8N_BASIC_AUTH_USER: admin
|
||||||
|
N8N_BASIC_AUTH_PASSWORD: Center#2025
|
||||||
|
N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: 'true'
|
||||||
|
GENERIC_TIMEZONE: "Asia/Bangkok"
|
||||||
|
DB_TYPE: mysqldb
|
||||||
|
DB_MYSQLDB_DATABASE: "n8n"
|
||||||
|
DB_MYSQLDB_USER: "center"
|
||||||
|
DB_MYSQLDB_PASSWORD: "Center#2025"
|
||||||
|
DB_MYSQLDB_HOST: "mariadb"
|
||||||
|
DB_MYSQLDB_PORT: 3306
|
||||||
|
|
||||||
|
ports:
|
||||||
|
- "5678:5678"
|
||||||
|
networks:
|
||||||
|
lcbp3: {}
|
||||||
|
volumes:
|
||||||
|
- "/share/Container/n8n:/home/node/.n8n"
|
||||||
|
- "/share/Container/n8n/cache:/home/node/.cache"
|
||||||
|
- "/share/Container/n8n/scripts:/scripts"
|
||||||
|
- "/share/Container/n8n/data:/data"
|
||||||
|
- "/var/run/docker.sock:/var/run/docker.sock"
|
||||||
|
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "-qO-", "http://127.0.0.1:5678/"]
|
||||||
|
# test: ["CMD", "curl", "-f", "http://127.0.0.1:5678/ || exit 1"]
|
||||||
|
interval: 15s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 30
|
||||||
|
|
||||||
|
networks:
|
||||||
|
lcbp3:
|
||||||
|
external: true
|
||||||
|
```
|
||||||
120
Documnets/แผนผัง Network.md
Normal file
120
Documnets/แผนผัง Network.md
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
# **🗺️ แผนผัง Network Architecture & Firewall (LCBP3-DMS)**
|
||||||
|
|
||||||
|
แผนผังนี้แสดงการแบ่งส่วนเครือข่าย (VLANs) และกฎ Firewall (ACLs) สำหรับ TP-Link Omada (ER7206/OC200) เพื่อรักษาความปลอดภัยของ QNAP NAS และ Docker Services
|
||||||
|
|
||||||
|
## **1\. แผนผังการเชื่อมต่อ (Connection Flow Diagram)**
|
||||||
|
|
||||||
|
graph TD
|
||||||
|
direction TB
|
||||||
|
|
||||||
|
subgraph Flow1 \[\<b\>การเชื่อมต่อจากภายนอก (Public WAN)\</b\>\]
|
||||||
|
User\[ผู้ใช้งานภายนอก (Internet)\]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Router \[\<b\>Router (ER7206)\</b\> \- Gateway\]
|
||||||
|
User \-- "Port 80/443 (HTTPS/HTTP)" \--\> ER7206
|
||||||
|
ER7206(\<b\>Port Forwarding\</b\>\<br/\>TCP 80 \-\> 192.168.10.100:80\<br/\>TCP 443 \-\> 192.168.10.100:443)
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph VLANs \[\<b\>เครือข่ายภายใน (VLANs & Firewall Rules)\</b\>\]
|
||||||
|
direction LR
|
||||||
|
|
||||||
|
subgraph VLAN10 \[\<b\>VLAN 10: Servers (DMZ)\</b\>\<br/\>192.168.10.x\]
|
||||||
|
QNAP\[\<b\>QNAP NAS (192.168.10.100)\</b\>\]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph VLAN20 \[\<b\>VLAN 20: Office\</b\>\<br/\>192.168.20.x\]
|
||||||
|
OfficePC\[PC พนักงาน/Wi-Fi\]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph VLAN30 \[\<b\>VLAN 30: Guests\</b\>\<br/\>192.168.30.x\]
|
||||||
|
GuestPC\[Guest Wi-Fi\]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Firewall \[\<b\>Firewall ACLs (ควบคุมโดย OC200)\</b\>\]
|
||||||
|
direction TB
|
||||||
|
rule1(\<b\>Rule 1: DENY\</b\>\<br/\>Guest (VLAN 30\) \-\> All VLANs)
|
||||||
|
rule2(\<b\>Rule 2: DENY\</b\>\<br/\>Server (VLAN 10\) \-\> Office (VLAN 20))
|
||||||
|
rule3(\<b\>Rule 3: ALLOW\</b\>\<br/\>Office (VLAN 20\) \-\> QNAP (192.168.10.100)\<br/\>Ports: 443, 80, 81, 2222\)
|
||||||
|
end
|
||||||
|
|
||||||
|
%% \--- แสดงผล Firewall Rules \---
|
||||||
|
GuestPC \-.x|rule1| QNAP
|
||||||
|
QNAP \-.x|rule2| OfficePC
|
||||||
|
OfficePC \-- "\[https://lcbp3.np-dms.work\](https://lcbp3.np-dms.work)" \--\>|rule3| QNAP
|
||||||
|
end
|
||||||
|
|
||||||
|
%% \--- เชื่อมต่อ Router กับ QNAP \---
|
||||||
|
ER7206 \--\> QNAP
|
||||||
|
|
||||||
|
subgraph Docker \[\<b\>Docker Network 'lcbp3' (ภายใน QNAP)\</b\>\]
|
||||||
|
direction TB
|
||||||
|
|
||||||
|
subgraph PublicServices \[Services ที่ NPM เปิดสู่ภายนอก\]
|
||||||
|
direction LR
|
||||||
|
NPM\[\<b\>NPM (Nginx Proxy Manager)\</b\>\<br/\>รับการจราจรจาก QNAP\]
|
||||||
|
Frontend(frontend:3000)
|
||||||
|
Backend(backend:3000)
|
||||||
|
Gitea(gitea:3000)
|
||||||
|
PMA(pma:80)
|
||||||
|
N8N(n8n:5678)
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph InternalServices \[Internal Services (Backend เรียกใช้เท่านั้น)\]
|
||||||
|
direction LR
|
||||||
|
DB(mariadb:3306)
|
||||||
|
Cache(cache:6379)
|
||||||
|
Search(search:9200)
|
||||||
|
end
|
||||||
|
|
||||||
|
%% \--- การเชื่อมต่อภายใน Docker \---
|
||||||
|
NPM \-- "lcbp3.np-dms.work" \--\> Frontend
|
||||||
|
NPM \-- "backend.np-dms.work" \--\> Backend
|
||||||
|
NPM \-- "git.np-dms.work" \--\> Gitea
|
||||||
|
NPM \-- "pma.np-dms.work" \--\> PMA
|
||||||
|
NPM \-- "n8n.np-dms.work" \--\> N8N
|
||||||
|
|
||||||
|
Backend \-- "lcbp3 Network" \--\> DB
|
||||||
|
Backend \-- "lcbp3 Network" \--\> Cache
|
||||||
|
Backend \-- "lcbp3 Network" \--\> Search
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
%% \--- เชื่อมต่อ QNAP กับ Docker \---
|
||||||
|
QNAP \--\> NPM
|
||||||
|
|
||||||
|
%% \--- Styling \---
|
||||||
|
classDef default fill:\#f9f9f9,stroke:\#333,stroke-width:2px;
|
||||||
|
classDef router fill:\#e6f7ff,stroke:\#0056b3,stroke-width:2px;
|
||||||
|
classDef vlan fill:\#fffbe6,stroke:\#d46b08,stroke-width:2px;
|
||||||
|
classDef docker fill:\#e6ffed,stroke:\#096dd9,stroke-width:2px;
|
||||||
|
classDef internal fill:\#f0f0f0,stroke:\#595959,stroke-width:2px,stroke-dasharray: 5 5;
|
||||||
|
classDef fw fill:\#fff0f0,stroke:\#d9363e,stroke-width:2px,stroke-dasharray: 3 3;
|
||||||
|
|
||||||
|
class Router,ER7206 router;
|
||||||
|
class VLANs,VLAN10,VLAN20,VLAN30 vlan;
|
||||||
|
class Docker,PublicServices,InternalServices docker;
|
||||||
|
class DB,Cache,Search internal;
|
||||||
|
class Firewall,rule1,rule2,rule3 fw;
|
||||||
|
|
||||||
|
## **2\. สรุปการตั้งค่า Firewall ACLs (สำหรับ Omada OC200)**
|
||||||
|
|
||||||
|
นี่คือรายการกฎ (Rules) ที่คุณต้องสร้างใน Settings \> Network Security \> ACL (เรียงลำดับจากบนลงล่าง):
|
||||||
|
|
||||||
|
| ลำดับ | Name | Policy | Source | Destination | Ports |
|
||||||
|
| :---- | :---- | :---- | :---- | :---- | :---- |
|
||||||
|
| **1** | Isolate-Guests | **Deny** | Network \-\> VLAN 30 (Guests) | Network \-\> VLAN 1, 10, 20 | All |
|
||||||
|
| **2** | Isolate-Servers | **Deny** | Network \-\> VLAN 10 (Servers) | Network \-\> VLAN 20 (Office) | All |
|
||||||
|
| **3** | Block-Office-to-Mgmt | **Deny** | Network \-\> VLAN 20 (Office) | Network \-\> VLAN 1 (Mgmt) | All |
|
||||||
|
| **4** | Allow-Office-to-Services | **Allow** | Network \-\> VLAN 20 (Office) | IP Group \-\> QNAP\_Services (192.168.10.100) | Port Group \-\> Web\_Services (443, 80, 81, 2222\) |
|
||||||
|
| **5** | (Default) | Allow | Any | Any | All |
|
||||||
|
|
||||||
|
## **3\. สรุปการตั้งค่า Port Forwarding (สำหรับ Omada ER7206)**
|
||||||
|
|
||||||
|
นี่คือรายการกฎที่คุณต้องสร้างใน Settings \> Transmission \> Port Forwarding:
|
||||||
|
|
||||||
|
| Name | External Port | Internal IP | Internal Port | Protocol |
|
||||||
|
| :---- | :---- | :---- | :---- | :---- |
|
||||||
|
| Allow-NPM-HTTPS | 443 | 192.168.10.100 | 443 | TCP |
|
||||||
|
| Allow-NPM-HTTP | 80 | 192.168.10.100 | 80 | TCP |
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,480 +1,480 @@
|
|||||||
# **Documents Management Sytem Version 1.4.0: แนวทางการพัฒนา FullStackJS**
|
# **Documents Management Sytem Version 1.4.0: แนวทางการพัฒนา FullStackJS**
|
||||||
|
|
||||||
## **🧠 ปรัชญาทั่วไป**
|
## **🧠 ปรัชญาทั่วไป**
|
||||||
|
|
||||||
แนวทางปฏิบัติที่ดีที่สุดแบบครบวงจรสำหรับการพัฒนา NestJS Backend, NextJS Frontend และ Tailwind-based UI/UX ในสภาพแวดล้อม TypeScript มุ่งเน้นที่ ความชัดเจน (clarity), ความง่ายในการบำรุงรักษา (maintainability), ความสอดคล้องกัน (consistency) และ การเข้าถึงได้ (accessibility) ตลอดทั้งสแต็ก
|
แนวทางปฏิบัติที่ดีที่สุดแบบครบวงจรสำหรับการพัฒนา NestJS Backend, NextJS Frontend และ Tailwind-based UI/UX ในสภาพแวดล้อม TypeScript มุ่งเน้นที่ ความชัดเจน (clarity), ความง่ายในการบำรุงรักษา (maintainability), ความสอดคล้องกัน (consistency) และ การเข้าถึงได้ (accessibility) ตลอดทั้งสแต็ก
|
||||||
|
|
||||||
## **⚙️ แนวทางทั่วไปสำหรับ TypeScript**
|
## **⚙️ แนวทางทั่วไปสำหรับ TypeScript**
|
||||||
|
|
||||||
### **หลักการพื้นฐาน**
|
### **หลักการพื้นฐาน**
|
||||||
|
|
||||||
* ใช้ **ภาษาอังกฤษ** สำหรับโค้ด
|
* ใช้ **ภาษาอังกฤษ** สำหรับโค้ด
|
||||||
* ใช้ **ภาษาไทย** สำหรับ comment และเอกสารทั้งหมด
|
* ใช้ **ภาษาไทย** สำหรับ comment และเอกสารทั้งหมด
|
||||||
* กำหนดไทป์ (type) อย่างชัดเจนสำหรับตัวแปร, พารามิเตอร์ และค่าที่ส่งกลับ (return values) ทั้งหมด
|
* กำหนดไทป์ (type) อย่างชัดเจนสำหรับตัวแปร, พารามิเตอร์ และค่าที่ส่งกลับ (return values) ทั้งหมด
|
||||||
* หลีกเลี่ยงการใช้ any; ให้สร้างไทป์ (types) หรืออินเทอร์เฟซ (interfaces) ที่กำหนดเอง
|
* หลีกเลี่ยงการใช้ any; ให้สร้างไทป์ (types) หรืออินเทอร์เฟซ (interfaces) ที่กำหนดเอง
|
||||||
* ใช้ **JSDoc** สำหรับคลาส (classes) และเมธอด (methods) ที่เป็น public
|
* ใช้ **JSDoc** สำหรับคลาส (classes) และเมธอด (methods) ที่เป็น public
|
||||||
* ส่งออก (Export) **สัญลักษณ์หลัก (main symbol) เพียงหนึ่งเดียว** ต่อไฟล์
|
* ส่งออก (Export) **สัญลักษณ์หลัก (main symbol) เพียงหนึ่งเดียว** ต่อไฟล์
|
||||||
* หลีกเลี่ยงบรรทัดว่างภายในฟังก์ชัน
|
* หลีกเลี่ยงบรรทัดว่างภายในฟังก์ชัน
|
||||||
* ระบุ // File: path/filename ในบรรทัดแรกของทุกไฟล์
|
* ระบุ // File: path/filename ในบรรทัดแรกของทุกไฟล์
|
||||||
* ระบุ // บันทึกการแก้ไข, หากมีการแก้ไขเพิ่มในอนาคต ให้เพิ่มบันทึก
|
* ระบุ // บันทึกการแก้ไข, หากมีการแก้ไขเพิ่มในอนาคต ให้เพิ่มบันทึก
|
||||||
|
|
||||||
### **ข้อตกลงในการตั้งชื่อ (Naming Conventions)**
|
### **ข้อตกลงในการตั้งชื่อ (Naming Conventions)**
|
||||||
|
|
||||||
| Entity (สิ่งที่ตั้งชื่อ) | Convention (รูปแบบ) | Example (ตัวอย่าง) |
|
| Entity (สิ่งที่ตั้งชื่อ) | Convention (รูปแบบ) | Example (ตัวอย่าง) |
|
||||||
| :---- | :---- | :---- |
|
| :---- | :---- | :---- |
|
||||||
| Classes | PascalCase | UserService |
|
| Classes | PascalCase | UserService |
|
||||||
| Property | snake_sase | user_id |
|
| Property | snake_sase | user_id |
|
||||||
| Variables & Functions | camelCase | getUserInfo |
|
| Variables & Functions | camelCase | getUserInfo |
|
||||||
| Files & Folders | kebab-case | user-service.ts |
|
| Files & Folders | kebab-case | user-service.ts |
|
||||||
| Environment Variables | UPPERCASE | DATABASE\URL |
|
| Environment Variables | UPPERCASE | DATABASE\URL |
|
||||||
| Booleans | Verb \+ Noun | isActive, canDelete, hasPermission |
|
| Booleans | Verb \+ Noun | isActive, canDelete, hasPermission |
|
||||||
|
|
||||||
ใช้คำเต็ม — ไม่ใช้อักษรย่อ — ยกเว้นคำมาตรฐาน (เช่น API, URL, req, res, err, ctx)
|
ใช้คำเต็ม — ไม่ใช้อักษรย่อ — ยกเว้นคำมาตรฐาน (เช่น API, URL, req, res, err, ctx)
|
||||||
|
|
||||||
## **🧩 ฟังก์ชัน (Functions)**
|
## **🧩 ฟังก์ชัน (Functions)**
|
||||||
|
|
||||||
* เขียนฟังก์ชันให้สั้น และทำ **หน้าที่เพียงอย่างเดียว** (single-purpose) (\< 20 บรรทัด)
|
* เขียนฟังก์ชันให้สั้น และทำ **หน้าที่เพียงอย่างเดียว** (single-purpose) (\< 20 บรรทัด)
|
||||||
* ใช้ **early returns** เพื่อลดการซ้อน (nesting) ของโค้ด
|
* ใช้ **early returns** เพื่อลดการซ้อน (nesting) ของโค้ด
|
||||||
* ใช้ **map**, **filter**, **reduce** แทนการใช้ loops เมื่อเหมาะสม
|
* ใช้ **map**, **filter**, **reduce** แทนการใช้ loops เมื่อเหมาะสม
|
||||||
* ควรใช้ **arrow functions** สำหรับตรรกะสั้นๆ, และใช้ **named functions** ในกรณีอื่น
|
* ควรใช้ **arrow functions** สำหรับตรรกะสั้นๆ, และใช้ **named functions** ในกรณีอื่น
|
||||||
* ใช้ **default parameters** แทนการตรวจสอบค่า null
|
* ใช้ **default parameters** แทนการตรวจสอบค่า null
|
||||||
* จัดกลุ่มพารามิเตอร์หลายตัวให้เป็นอ็อบเจกต์เดียว (RO-RO pattern)
|
* จัดกลุ่มพารามิเตอร์หลายตัวให้เป็นอ็อบเจกต์เดียว (RO-RO pattern)
|
||||||
* ส่งค่ากลับ (Return) เป็นอ็อบเจกต์ที่มีไทป์กำหนด (typed objects) ไม่ใช่ค่าพื้นฐาน (primitives)
|
* ส่งค่ากลับ (Return) เป็นอ็อบเจกต์ที่มีไทป์กำหนด (typed objects) ไม่ใช่ค่าพื้นฐาน (primitives)
|
||||||
* รักษาระดับของสิ่งที่เป็นนามธรรม (abstraction level) ให้เป็นระดับเดียวในแต่ละฟังก์ชัน
|
* รักษาระดับของสิ่งที่เป็นนามธรรม (abstraction level) ให้เป็นระดับเดียวในแต่ละฟังก์ชัน
|
||||||
|
|
||||||
## **🧱 การจัดการข้อมูล (Data Handling)**
|
## **🧱 การจัดการข้อมูล (Data Handling)**
|
||||||
|
|
||||||
* ห่อหุ้มข้อมูล (Encapsulate) ในไทป์แบบผสม (composite types)
|
* ห่อหุ้มข้อมูล (Encapsulate) ในไทป์แบบผสม (composite types)
|
||||||
* ใช้ **immutability** (การไม่เปลี่ยนแปลงค่า) ด้วย readonly และ as const
|
* ใช้ **immutability** (การไม่เปลี่ยนแปลงค่า) ด้วย readonly และ as const
|
||||||
* ทำการตรวจสอบความถูกต้องของข้อมูล (Validations) ในคลาสหรือ DTOs ไม่ใช่ภายในฟังก์ชันทางธุรกิจ
|
* ทำการตรวจสอบความถูกต้องของข้อมูล (Validations) ในคลาสหรือ DTOs ไม่ใช่ภายในฟังก์ชันทางธุรกิจ
|
||||||
* ตรวจสอบความถูกต้องของข้อมูลโดยใช้ DTOs ที่มีไทป์กำหนดเสมอ
|
* ตรวจสอบความถูกต้องของข้อมูลโดยใช้ DTOs ที่มีไทป์กำหนดเสมอ
|
||||||
|
|
||||||
## **🧰 คลาส (Classes)**
|
## **🧰 คลาส (Classes)**
|
||||||
|
|
||||||
* ปฏิบัติตามหลักการ **SOLID**
|
* ปฏิบัติตามหลักการ **SOLID**
|
||||||
* ควรใช้ **composition มากกว่า inheritance** (Prefer composition over inheritance)
|
* ควรใช้ **composition มากกว่า inheritance** (Prefer composition over inheritance)
|
||||||
* กำหนด **interfaces** สำหรับสัญญา (contracts)
|
* กำหนด **interfaces** สำหรับสัญญา (contracts)
|
||||||
* ให้คลาสมุ่งเน้นการทำงานเฉพาะอย่างและมีขนาดเล็ก (\< 200 บรรทัด, \< 10 เมธอด, \< 10 properties)
|
* ให้คลาสมุ่งเน้นการทำงานเฉพาะอย่างและมีขนาดเล็ก (\< 200 บรรทัด, \< 10 เมธอด, \< 10 properties)
|
||||||
|
|
||||||
## **🚨 การจัดการข้อผิดพลาด (Error Handling)**
|
## **🚨 การจัดการข้อผิดพลาด (Error Handling)**
|
||||||
|
|
||||||
* ใช้ Exceptions สำหรับข้อผิดพลาดที่ไม่คาดคิด
|
* ใช้ Exceptions สำหรับข้อผิดพลาดที่ไม่คาดคิด
|
||||||
* ดักจับ (Catch) ข้อผิดพลาดเพื่อแก้ไขหรือเพิ่มบริบท (context) เท่านั้น; หากไม่เช่นนั้น ให้ใช้ global error handlers
|
* ดักจับ (Catch) ข้อผิดพลาดเพื่อแก้ไขหรือเพิ่มบริบท (context) เท่านั้น; หากไม่เช่นนั้น ให้ใช้ global error handlers
|
||||||
* ระบุข้อความข้อผิดพลาด (error messages) ที่มีความหมายเสมอ
|
* ระบุข้อความข้อผิดพลาด (error messages) ที่มีความหมายเสมอ
|
||||||
|
|
||||||
## **🧪 การทดสอบ (ทั่วไป) (Testing (General))**
|
## **🧪 การทดสอบ (ทั่วไป) (Testing (General))**
|
||||||
|
|
||||||
* ใช้รูปแบบ **Arrange–Act–Assert**
|
* ใช้รูปแบบ **Arrange–Act–Assert**
|
||||||
* ใช้ชื่อตัวแปรในการทดสอบที่สื่อความหมาย (inputData, expectedOutput)
|
* ใช้ชื่อตัวแปรในการทดสอบที่สื่อความหมาย (inputData, expectedOutput)
|
||||||
* เขียน **unit tests** สำหรับ public methods ทั้งหมด
|
* เขียน **unit tests** สำหรับ public methods ทั้งหมด
|
||||||
* จำลอง (Mock) การพึ่งพาภายนอก (external dependencies)
|
* จำลอง (Mock) การพึ่งพาภายนอก (external dependencies)
|
||||||
* เพิ่ม **acceptance tests** ต่อโมดูลโดยใช้รูปแบบ Given–When-Then
|
* เพิ่ม **acceptance tests** ต่อโมดูลโดยใช้รูปแบบ Given–When-Then
|
||||||
|
|
||||||
## **🏗️ แบ็กเอนด์ (NestJS) (Backend (NestJS))**
|
## **🏗️ แบ็กเอนด์ (NestJS) (Backend (NestJS))**
|
||||||
|
|
||||||
### **หลักการ**
|
### **หลักการ**
|
||||||
|
|
||||||
* **สถาปัตยกรรมแบบโมดูลาร์ (Modular architecture)**:
|
* **สถาปัตยกรรมแบบโมดูลาร์ (Modular architecture)**:
|
||||||
* หนึ่งโมดูลต่อหนึ่งโดเมน
|
* หนึ่งโมดูลต่อหนึ่งโดเมน
|
||||||
* โครงสร้างแบบ Controller → Service → Repository (Model)
|
* โครงสร้างแบบ Controller → Service → Repository (Model)
|
||||||
* API-First: มุ่งเน้นการสร้าง API ที่มีคุณภาพสูง มีเอกสารประกอบ (Swagger) ที่ชัดเจนสำหรับ Frontend Team
|
* API-First: มุ่งเน้นการสร้าง API ที่มีคุณภาพสูง มีเอกสารประกอบ (Swagger) ที่ชัดเจนสำหรับ Frontend Team
|
||||||
* DTOs ที่ตรวจสอบความถูกต้องด้วย **class-validator**
|
* DTOs ที่ตรวจสอบความถูกต้องด้วย **class-validator**
|
||||||
* ใช้ **MikroORM** (หรือ TypeORM/Prisma) สำหรับการคงอยู่ของข้อมูล (persistence) ซึ่งสอดคล้องกับสคีมา MariaDB
|
* ใช้ **MikroORM** (หรือ TypeORM/Prisma) สำหรับการคงอยู่ของข้อมูล (persistence) ซึ่งสอดคล้องกับสคีมา MariaDB
|
||||||
* ห่อหุ้มโค้ดที่ใช้ซ้ำได้ไว้ใน **common module** (@app/common):
|
* ห่อหุ้มโค้ดที่ใช้ซ้ำได้ไว้ใน **common module** (@app/common):
|
||||||
* Configs, decorators, DTOs, guards, interceptors, notifications, shared services, types, validators
|
* Configs, decorators, DTOs, guards, interceptors, notifications, shared services, types, validators
|
||||||
|
|
||||||
### **ฟังก์ชันหลัก (Core Functionalities)**
|
### **ฟังก์ชันหลัก (Core Functionalities)**
|
||||||
|
|
||||||
* Global **filters** สำหรับการจัดการ exception
|
* Global **filters** สำหรับการจัดการ exception
|
||||||
* **Middlewares** สำหรับการจัดการ request
|
* **Middlewares** สำหรับการจัดการ request
|
||||||
* **Guards** สำหรับการอนุญาต (permissions) และ RBAC
|
* **Guards** สำหรับการอนุญาต (permissions) และ RBAC
|
||||||
* **Interceptors** สำหรับการแปลงข้อมูล response และการบันทึก log
|
* **Interceptors** สำหรับการแปลงข้อมูล response และการบันทึก log
|
||||||
|
|
||||||
### **ข้อจำกัดในการ Deploy (QNAP Container Station)**
|
### **ข้อจำกัดในการ Deploy (QNAP Container Station)**
|
||||||
|
|
||||||
* **ห้ามใช้ไฟล์ .env** ในการตั้งค่า Environment Variables [cite: 2.1]
|
* **ห้ามใช้ไฟล์ .env** ในการตั้งค่า Environment Variables [cite: 2.1]
|
||||||
* การตั้งค่าทั้งหมด (เช่น Database connection string, JWT secret) **จะต้องถูกกำหนดผ่าน Environment Variable ใน docker-compose.yml โดยตรง** [cite: 6.5] ซึ่งจะจัดการผ่าน UI ของ QNAP Container Station [cite: 2.1]
|
* การตั้งค่าทั้งหมด (เช่น Database connection string, JWT secret) **จะต้องถูกกำหนดผ่าน Environment Variable ใน docker-compose.yml โดยตรง** [cite: 6.5] ซึ่งจะจัดการผ่าน UI ของ QNAP Container Station [cite: 2.1]
|
||||||
|
|
||||||
### **โครงสร้างโมดูลตามโดเมน (Domain-Driven Module Structure)**
|
### **โครงสร้างโมดูลตามโดเมน (Domain-Driven Module Structure)**
|
||||||
|
|
||||||
เพื่อให้สอดคล้องกับสคีมา SQL (LCBP3-DMS) เราจะใช้โครงสร้างโมดูลแบบ **Domain-Driven (แบ่งตามขอบเขตธุรกิจ)** แทนการแบ่งตามฟังก์ชัน:
|
เพื่อให้สอดคล้องกับสคีมา SQL (LCBP3-DMS) เราจะใช้โครงสร้างโมดูลแบบ **Domain-Driven (แบ่งตามขอบเขตธุรกิจ)** แทนการแบ่งตามฟังก์ชัน:
|
||||||
|
|
||||||
1. **CommonModule:**
|
1. **CommonModule:**
|
||||||
* เก็บ Services ที่ใช้ร่วมกัน เช่น DatabaseModule, FileStorageService (จัดการไฟล์ใน QNAP), AuditLogService, NotificationService
|
* เก็บ Services ที่ใช้ร่วมกัน เช่น DatabaseModule, FileStorageService (จัดการไฟล์ใน QNAP), AuditLogService, NotificationService
|
||||||
* จัดการ audit_logs
|
* จัดการ audit_logs
|
||||||
* NotificationService ต้องรองรับ Triggers ที่ระบุใน Requirement 6.7 [cite: 6.7]
|
* NotificationService ต้องรองรับ Triggers ที่ระบุใน Requirement 6.7 [cite: 6.7]
|
||||||
2. **AuthModule:**
|
2. **AuthModule:**
|
||||||
* จัดการะการยืนยันตัวตน (JWT, Guards)
|
* จัดการะการยืนยันตัวตน (JWT, Guards)
|
||||||
* **(สำคัญ)** ต้องรับผิดชอบการตรวจสอบสิทธิ์ **4 ระดับ** [cite: 4.2]: สิทธิ์ระดับระบบ (Global Role), สิทธิ์ระดับองกรณ์ (Organization Role), สิทธิ์ระดับโปรเจกต์ (Project Role), และ สิทธิ์ระดับสัญญา (Contract Role)
|
* **(สำคัญ)** ต้องรับผิดชอบการตรวจสอบสิทธิ์ **4 ระดับ** [cite: 4.2]: สิทธิ์ระดับระบบ (Global Role), สิทธิ์ระดับองกรณ์ (Organization Role), สิทธิ์ระดับโปรเจกต์ (Project Role), และ สิทธิ์ระดับสัญญา (Contract Role)
|
||||||
* **(สำคัญ)** ต้องมี API สำหรับ **Admin Panel** เพื่อ:
|
* **(สำคัญ)** ต้องมี API สำหรับ **Admin Panel** เพื่อ:
|
||||||
* สร้างและจัดการ Role และการจับคู่ Permission แบบไดนามิก [cite: 4.3]
|
* สร้างและจัดการ Role และการจับคู่ Permission แบบไดนามิก [cite: 4.3]
|
||||||
* ให้ Superadmin สร้าง Organizations และกำหนด Org Admin ได้ [cite: 4.6]
|
* ให้ Superadmin สร้าง Organizations และกำหนด Org Admin ได้ [cite: 4.6]
|
||||||
* ให้ Superadmin/Admin จัดการ document_number_formats (รูปแบบเลขที่เอกสาร), document_number_counters (Running Number) [cite: 3.10]
|
* ให้ Superadmin/Admin จัดการ document_number_formats (รูปแบบเลขที่เอกสาร), document_number_counters (Running Number) [cite: 3.10]
|
||||||
3. **UserModule:**
|
3. **UserModule:**
|
||||||
* จัดการ users, roles, permissions, global_default_roles, role_permissions, user_roles, user_project_roles
|
* จัดการ users, roles, permissions, global_default_roles, role_permissions, user_roles, user_project_roles
|
||||||
* **(สำคัญ)** ต้องมี API สำหรับ **Admin Panel** เพื่อ:
|
* **(สำคัญ)** ต้องมี API สำหรับ **Admin Panel** เพื่อ:
|
||||||
* สร้างและจัดการ Role และการจับคู่ Permission แบบไดนามิก [cite: 4.3]
|
* สร้างและจัดการ Role และการจับคู่ Permission แบบไดนามิก [cite: 4.3]
|
||||||
4. **ProjectModule:**
|
4. **ProjectModule:**
|
||||||
* จัดการ projects, organizations, contracts, project_parties, contract_parties
|
* จัดการ projects, organizations, contracts, project_parties, contract_parties
|
||||||
5. **MasterModule:**
|
5. **MasterModule:**
|
||||||
* จัดการ master data (correspondence_types, rfa_types, rfa_status_codes, rfa_approve_codes, circulation_status_codes, correspondence_types, correspondence_status, tags) [cite: 4.5]
|
* จัดการ master data (correspondence_types, rfa_types, rfa_status_codes, rfa_approve_codes, circulation_status_codes, correspondence_types, correspondence_status, tags) [cite: 4.5]
|
||||||
6. **CorrespondenceModule (โมดูลศูนย์กลาง):**
|
6. **CorrespondenceModule (โมดูลศูนย์กลาง):**
|
||||||
* จัดการ correspondences, correspondence_revisions, correspondence_tags
|
* จัดการ correspondences, correspondence_revisions, correspondence_tags
|
||||||
|
|
||||||
* **(สำคัญ)** Service นี้ต้อง Inject DocumentNumberingService เพื่อขอเลขที่เอกสารใหม่ก่อนการสร้าง
|
* **(สำคัญ)** Service นี้ต้อง Inject DocumentNumberingService เพื่อขอเลขที่เอกสารใหม่ก่อนการสร้าง
|
||||||
* **(สำคัญ)** ตรรกะการสร้าง/อัปเดต Revision จะอยู่ใน Service นี้
|
* **(สำคัญ)** ตรรกะการสร้าง/อัปเดต Revision จะอยู่ใน Service นี้
|
||||||
* จัดการ correspondence_attachments (ตารางเชื่อมไฟล์แนบ)
|
* จัดการ correspondence_attachments (ตารางเชื่อมไฟล์แนบ)
|
||||||
* รับผิดชอบ Routing **Correspondence Routing** (correspondence_routings, correspondence_routing_template_steps, correspondence_routing_templates, correspondence_status_transitions) สำหรับการส่งต่อเอกสารทั่วไประหว่างองค์กร
|
* รับผิดชอบ Routing **Correspondence Routing** (correspondence_routings, correspondence_routing_template_steps, correspondence_routing_templates, correspondence_status_transitions) สำหรับการส่งต่อเอกสารทั่วไประหว่างองค์กร
|
||||||
7. **RfaModule:**
|
7. **RfaModule:**
|
||||||
* จัดการ rfas, rfa_revisions, rfa_items
|
* จัดการ rfas, rfa_revisions, rfa_items
|
||||||
* รับผิดชอบเวิร์กโฟลว์ **"RFA Workflows"** (rfa_workflows, rfa_workflow_templates, rfa_workflow_template_steps, rfa_status_transitions) สำหรับการอนุมัติเอกสารทางเทคนิค
|
* รับผิดชอบเวิร์กโฟลว์ **"RFA Workflows"** (rfa_workflows, rfa_workflow_templates, rfa_workflow_template_steps, rfa_status_transitions) สำหรับการอนุมัติเอกสารทางเทคนิค
|
||||||
8. **DrawingModule:**
|
8. **DrawingModule:**
|
||||||
* จัดการ shop_drawings, shop_drawing_revisions, contract_drawings, contract_drawing_volumes, contract_drawing_cats, contract_drawing_sub_cats, shop_drawing_main_categories, shop_drawing_sub_categories, contract_drawing_subcat_cat_maps, shop_drawing_revision_contract_refs
|
* จัดการ shop_drawings, shop_drawing_revisions, contract_drawings, contract_drawing_volumes, contract_drawing_cats, contract_drawing_sub_cats, shop_drawing_main_categories, shop_drawing_sub_categories, contract_drawing_subcat_cat_maps, shop_drawing_revision_contract_refs
|
||||||
* จัดการ shop_drawing_revision_attachments และ contract_drawing_attachments(ตารางเชื่อมไฟล์แนบ)
|
* จัดการ shop_drawing_revision_attachments และ contract_drawing_attachments(ตารางเชื่อมไฟล์แนบ)
|
||||||
9. **CirculationModule:**
|
9. **CirculationModule:**
|
||||||
* จัดการ circulations, circulation_templates, circulation_assignees
|
* จัดการ circulations, circulation_templates, circulation_assignees
|
||||||
* จัดการ circulation_attachments (ตารางเชื่อมไฟล์แนบ)
|
* จัดการ circulation_attachments (ตารางเชื่อมไฟล์แนบ)
|
||||||
* รับผิดชอบเวิร์กโฟลว์ **"Circulations"** (circulation_status_transitions, circulation_template_assignees, circulation_assignees, circulation_recipients, circulation_actions, circulation_action_documents)สำหรับการเวียนเอกสาร **ภายในองค์กร**
|
* รับผิดชอบเวิร์กโฟลว์ **"Circulations"** (circulation_status_transitions, circulation_template_assignees, circulation_assignees, circulation_recipients, circulation_actions, circulation_action_documents)สำหรับการเวียนเอกสาร **ภายในองค์กร**
|
||||||
10. **TransmittalModule:**
|
10. **TransmittalModule:**
|
||||||
* จัดการ transmittals และ transmittal_items
|
* จัดการ transmittals และ transmittal_items
|
||||||
11. **SearchModule:**
|
11. **SearchModule:**
|
||||||
* ให้บริการค้นหาขั้นสูง (Advanced Search) [cite: 6.2] โดยใช้ **Elasticsearch** เพื่อรองรับการค้นหาแบบ Full-text จากชื่อเรื่อง, รายละเอียด, เลขที่เอกสาร, ประเภท, วันที่, และ Tags
|
* ให้บริการค้นหาขั้นสูง (Advanced Search) [cite: 6.2] โดยใช้ **Elasticsearch** เพื่อรองรับการค้นหาแบบ Full-text จากชื่อเรื่อง, รายละเอียด, เลขที่เอกสาร, ประเภท, วันที่, และ Tags
|
||||||
* ระบบจะใช้ Elasticsearch Engine ในการจัดทำดัชนีเพื่อการค้นหาข้อมูลเชิงลึกจากเนื้อหาของเอกสาร โดยข้อมูลจะถูกส่งไปทำดัชนีจาก Backend (NestJS) ทุกครั้งที่มีการสร้างหรือแก้ไขเอกสาร
|
* ระบบจะใช้ Elasticsearch Engine ในการจัดทำดัชนีเพื่อการค้นหาข้อมูลเชิงลึกจากเนื้อหาของเอกสาร โดยข้อมูลจะถูกส่งไปทำดัชนีจาก Backend (NestJS) ทุกครั้งที่มีการสร้างหรือแก้ไขเอกสาร
|
||||||
12. **DocumentNumberingModule:**
|
12. **DocumentNumberingModule:**
|
||||||
* **สถานะ:** เป็น Module ภายใน (Internal Module) ไม่เปิด API สู่ภายนอก
|
* **สถานะ:** เป็น Module ภายใน (Internal Module) ไม่เปิด API สู่ภายนอก
|
||||||
* **หน้าที่:** ให้บริการ DocumentNumberingService ที่ Module อื่น (เช่น CorrespondenceModule) จะ Inject ไปใช้งาน
|
* **หน้าที่:** ให้บริการ DocumentNumberingService ที่ Module อื่น (เช่น CorrespondenceModule) จะ Inject ไปใช้งาน
|
||||||
* **ตรรกะ:** รับผิดชอบการสร้างเลขที่เอกสาร โดยการเรียกใช้ Stored Procedure *sp_get_next_document_number** เพื่อป้องกัน Race Condition
|
* **ตรรกะ:** รับผิดชอบการสร้างเลขที่เอกสาร โดยการเรียกใช้ Stored Procedure *sp_get_next_document_number** เพื่อป้องกัน Race Condition
|
||||||
|
|
||||||
### **สถาปัตยกรรมระบบ (System Architecture)**
|
### **สถาปัตยกรรมระบบ (System Architecture)**
|
||||||
|
|
||||||
โครงสร้างโมดูล (Module Structure)
|
โครงสร้างโมดูล (Module Structure)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
📁 src
|
📁 src
|
||||||
├── 📄 app.module.ts
|
├── 📄 app.module.ts
|
||||||
├── 📄 main.ts
|
├── 📄 main.ts
|
||||||
├── 📁 common # @app/common (โมดูลส่วนกลาง)
|
├── 📁 common # @app/common (โมดูลส่วนกลาง)
|
||||||
│ ├── 📁 auth # AuthModule (JWT, Guards)
|
│ ├── 📁 auth # AuthModule (JWT, Guards)
|
||||||
│ ├── 📁 config # Configuration
|
│ ├── 📁 config # Configuration
|
||||||
│ ├── 📁 decorators # Custom Decorators (เช่น @RequirePermission)
|
│ ├── 📁 decorators # Custom Decorators (เช่น @RequirePermission)
|
||||||
│ ├── 📁 entities # Shared Entities (User, Role, Permission)
|
│ ├── 📁 entities # Shared Entities (User, Role, Permission)
|
||||||
│ ├── 📁 exceptions # Global Exception Filters
|
│ ├── 📁 exceptions # Global Exception Filters
|
||||||
│ ├── 📁 file-storage # FileStorageService
|
│ ├── 📁 file-storage # FileStorageService
|
||||||
│ ├── 📁 guards # Custom Guards (RBAC Guard)
|
│ ├── 📁 guards # Custom Guards (RBAC Guard)
|
||||||
│ ├── 📁 interceptors # Interceptors (Audit Log, Transform)
|
│ ├── 📁 interceptors # Interceptors (Audit Log, Transform)
|
||||||
│ └── 📁 services # Shared Services (NotificationService)
|
│ └── 📁 services # Shared Services (NotificationService)
|
||||||
├── 📁 modules
|
├── 📁 modules
|
||||||
│ ├── 📁 user # UserModule (จัดการ Users, Roles, Permissions)
|
│ ├── 📁 user # UserModule (จัดการ Users, Roles, Permissions)
|
||||||
│ ├── 📁 project # ProjectModule (จัดการ Projects, Organizations, Contracts)
|
│ ├── 📁 project # ProjectModule (จัดการ Projects, Organizations, Contracts)
|
||||||
│ ├── 📁 correspondence # CorrespondenceModule (จัดการเอกสารโต้ตอบ)
|
│ ├── 📁 correspondence # CorrespondenceModule (จัดการเอกสารโต้ตอบ)
|
||||||
│ ├── 📁 rfa # RfaModule (จัดการเอกสารขออนุมัติ)
|
│ ├── 📁 rfa # RfaModule (จัดการเอกสารขออนุมัติ)
|
||||||
│ ├── 📁 drawing # DrawingModule (จัดการแบบแปลน)
|
│ ├── 📁 drawing # DrawingModule (จัดการแบบแปลน)
|
||||||
│ ├── 📁 circulation # CirculationModule (จัดการใบเวียน)
|
│ ├── 📁 circulation # CirculationModule (จัดการใบเวียน)
|
||||||
│ ├── 📁 transmittal # TransmittalModule (จัดการเอกสารนำส่ง)
|
│ ├── 📁 transmittal # TransmittalModule (จัดการเอกสารนำส่ง)
|
||||||
│ ├── 📁 search # SearchModule (ค้นหาขั้นสูงด้วย Elasticsearch)
|
│ ├── 📁 search # SearchModule (ค้นหาขั้นสูงด้วย Elasticsearch)
|
||||||
│ └── 📁 document-numbering # DocumentNumberingModule (Internal Module)
|
│ └── 📁 document-numbering # DocumentNumberingModule (Internal Module)
|
||||||
└── 📁 database # Database Migration & Seeding Scripts
|
└── 📁 database # Database Migration & Seeding Scripts
|
||||||
```
|
```
|
||||||
|
|
||||||
### **เเทคโนโลยีที่ใช้ (Technology Stack)**
|
### **เเทคโนโลยีที่ใช้ (Technology Stack)**
|
||||||
|
|
||||||
| ส่วน | Library/Tool | หมายเหตุ |
|
| ส่วน | Library/Tool | หมายเหตุ |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| **Framework** | `@nestjs/core`, `@nestjs/common` | Core Framework |
|
| **Framework** | `@nestjs/core`, `@nestjs/common` | Core Framework |
|
||||||
| **Language** | `TypeScript` | ใช้ TypeScript ทั้งระบบ |
|
| **Language** | `TypeScript` | ใช้ TypeScript ทั้งระบบ |
|
||||||
| **Database** | `MariaDB 10.11` | ฐานข้อมูลหลัก |
|
| **Database** | `MariaDB 10.11` | ฐานข้อมูลหลัก |
|
||||||
| **ORM** | `@nestjs/typeorm`, `typeorm` | 🗃️จัดการการเชื่อมต่อและ Query ฐานข้อมูล |
|
| **ORM** | `@nestjs/typeorm`, `typeorm` | 🗃️จัดการการเชื่อมต่อและ Query ฐานข้อมูล |
|
||||||
| **Validation** | `class-validator`, `class-transformer` | 📦ตรวจสอบและแปลงข้อมูลใน DTO |
|
| **Validation** | `class-validator`, `class-transformer` | 📦ตรวจสอบและแปลงข้อมูลใน DTO |
|
||||||
| **Auth** | `@nestjs/jwt`, `@nestjs/passport`, `passport-jwt` | 🔐การยืนยันตัวตนด้วย JWT |
|
| **Auth** | `@nestjs/jwt`, `@nestjs/passport`, `passport-jwt` | 🔐การยืนยันตัวตนด้วย JWT |
|
||||||
|**Authorization** | `casl` | 🔐จัดการสิทธิ์แบบ RBAC |
|
|**Authorization** | `casl` | 🔐จัดการสิทธิ์แบบ RBAC |
|
||||||
| **File Upload** | `multer` | 📁จัดการการอัปโหลดไฟล์ |
|
| **File Upload** | `multer` | 📁จัดการการอัปโหลดไฟล์ |
|
||||||
| **Search** | `@nestjs/elasticsearch` | 🔍สำหรับการค้นหาขั้นสูง |
|
| **Search** | `@nestjs/elasticsearch` | 🔍สำหรับการค้นหาขั้นสูง |
|
||||||
| **Notification** | `nodemailer` | 📬ส่งอีเมลแจ้งเตือน |
|
| **Notification** | `nodemailer` | 📬ส่งอีเมลแจ้งเตือน |
|
||||||
| **Scheduling** | `@nestjs/schedule` | 📬สำหรับ Cron Jobs (เช่น แจ้งเตือน Deadline) |
|
| **Scheduling** | `@nestjs/schedule` | 📬สำหรับ Cron Jobs (เช่น แจ้งเตือน Deadline) |
|
||||||
| **Logging** | `winston` | 📊บันทึก Log ที่มีประสิทธิภาพ |
|
| **Logging** | `winston` | 📊บันทึก Log ที่มีประสิทธิภาพ |
|
||||||
| **Testing** | `@nestjs/testing`, `jest`, `supertest` | 🧪ทดสอบ Unit, Integration และ E2E |
|
| **Testing** | `@nestjs/testing`, `jest`, `supertest` | 🧪ทดสอบ Unit, Integration และ E2E |
|
||||||
| **Documentation** | `@nestjs/swagger` | 🌐สร้าง API Documentation อัตโนมัติ |
|
| **Documentation** | `@nestjs/swagger` | 🌐สร้าง API Documentation อัตโนมัติ |
|
||||||
| **Security** | `helmet`, `rate-limiter-flexible` | 🛡️เพิ่มความปลอดภัยให้ API |
|
| **Security** | `helmet`, `rate-limiter-flexible` | 🛡️เพิ่มความปลอดภัยให้ API |
|
||||||
|
|
||||||
เราจะแบ่งการทดสอบเป็น 3 ระดับ โดยใช้ **Jest** และ @nestjs/testing:
|
เราจะแบ่งการทดสอบเป็น 3 ระดับ โดยใช้ **Jest** และ @nestjs/testing:
|
||||||
|
|
||||||
* **Unit Tests (การทดสอบหน่วยย่อย):**
|
* **Unit Tests (การทดสอบหน่วยย่อย):**
|
||||||
* **เป้าหมาย:** ทดสอบ Logic ภายใน Service, Guard, หรือ Pipe โดยจำลอง (Mock) Dependencies ทั้งหมด
|
* **เป้าหมาย:** ทดสอบ Logic ภายใน Service, Guard, หรือ Pipe โดยจำลอง (Mock) Dependencies ทั้งหมด
|
||||||
* **สิ่งที่ต้องทดสอบ:** Business Logic (เช่น การเปลี่ยนสถานะ Workflow, การตรวจสอบ Deadline) [cite: 2.9.1], ตรรกะการตรวจสอบสิทธิ์ (Auth Guard) ทั้ง 4 ระดับ
|
* **สิ่งที่ต้องทดสอบ:** Business Logic (เช่น การเปลี่ยนสถานะ Workflow, การตรวจสอบ Deadline) [cite: 2.9.1], ตรรกะการตรวจสอบสิทธิ์ (Auth Guard) ทั้ง 4 ระดับ
|
||||||
* **Integration Tests (การทดสอบการบูรณาการ):**
|
* **Integration Tests (การทดสอบการบูรณาการ):**
|
||||||
* **เป้าหมาย:** ทดสอบการทำงานร่วมกันของ Controller -> Service -> Repository (Database)
|
* **เป้าหมาย:** ทดสอบการทำงานร่วมกันของ Controller -> Service -> Repository (Database)
|
||||||
* **เทคนิค:** ใช้ **Test Database แยกต่างหาก** (ห้ามใช้ Dev DB) และใช้ supertest เพื่อยิง HTTP Request จริงไปยัง App
|
* **เทคนิค:** ใช้ **Test Database แยกต่างหาก** (ห้ามใช้ Dev DB) และใช้ supertest เพื่อยิง HTTP Request จริงไปยัง App
|
||||||
* **สิ่งที่ต้องทดสอบ:** การเรียก sp\get\next\document\number [cite: 2.9.3] และการทำงานของ Views (เช่น v_user_tasks)
|
* **สิ่งที่ต้องทดสอบ:** การเรียก sp\get\next\document\number [cite: 2.9.3] และการทำงานของ Views (เช่น v_user_tasks)
|
||||||
* **E2E (End-to-End) Tests:**
|
* **E2E (End-to-End) Tests:**
|
||||||
* **เป้าหมาย:** ทดสอบ API Contract ว่า Response Body Shape ตรงตามเอกสาร Swagger เพื่อรับประกันทีม Frontend
|
* **เป้าหมาย:** ทดสอบ API Contract ว่า Response Body Shape ตรงตามเอกสาร Swagger เพื่อรับประกันทีม Frontend
|
||||||
|
|
||||||
### **🗄️ Backend State Management**
|
### **🗄️ Backend State Management**
|
||||||
|
|
||||||
Backend (NestJS) ควรเป็น **Stateless** (ไม่เก็บสถานะ) "State" ทั้งหมดจะถูกจัดเก็บใน MariaDB
|
Backend (NestJS) ควรเป็น **Stateless** (ไม่เก็บสถานะ) "State" ทั้งหมดจะถูกจัดเก็บใน MariaDB
|
||||||
|
|
||||||
* **Request-Scoped State (สถานะภายใน Request เดียว):**
|
* **Request-Scoped State (สถานะภายใน Request เดียว):**
|
||||||
* **ปัญหา:** จะส่งต่อข้อมูล (เช่น User ที่ล็อกอิน) ระหว่าง Guard และ Service ใน Request เดียวกันได้อย่างไร?
|
* **ปัญหา:** จะส่งต่อข้อมูล (เช่น User ที่ล็อกอิน) ระหว่าง Guard และ Service ใน Request เดียวกันได้อย่างไร?
|
||||||
* **วิธีแก้:** ใช้ **Request-Scoped Providers** ของ NestJS (เช่น AuthContextService) เพื่อเก็บข้อมูล User ปัจจุบันที่ได้จาก AuthGuard และให้ Service อื่น Inject ไปใช้
|
* **วิธีแก้:** ใช้ **Request-Scoped Providers** ของ NestJS (เช่น AuthContextService) เพื่อเก็บข้อมูล User ปัจจุบันที่ได้จาก AuthGuard และให้ Service อื่น Inject ไปใช้
|
||||||
* **Application-Scoped State (การ Caching):**
|
* **Application-Scoped State (การ Caching):**
|
||||||
* **ปัญหา:** ข้อมูล Master (เช่น roles, permissions, organizations) ถูกเรียกใช้บ่อย
|
* **ปัญหา:** ข้อมูล Master (เช่น roles, permissions, organizations) ถูกเรียกใช้บ่อย
|
||||||
* **วิธีแก้:** ใช้ **Caching** (เช่น @nestjs/cache-manager) เพื่อ Caching ข้อมูลเหล่านี้ และลดภาระ Database
|
* **วิธีแก้:** ใช้ **Caching** (เช่น @nestjs/cache-manager) เพื่อ Caching ข้อมูลเหล่านี้ และลดภาระ Database
|
||||||
|
|
||||||
### **การไหลของข้อมูล (Data Flow)**
|
### **การไหลของข้อมูล (Data Flow)**
|
||||||
|
|
||||||
1. Request: ผ่าน Nginx Proxy Manager -> NestJS Controller
|
1. Request: ผ่าน Nginx Proxy Manager -> NestJS Controller
|
||||||
2. Authentication: JWT Guard ตรวจสอบ Token และดึงข้อมูล User
|
2. Authentication: JWT Guard ตรวจสอบ Token และดึงข้อมูล User
|
||||||
3. Authorization: RBAC Guard (ใช้ CASL) ตรวจสอบสิทธิ์จาก Decorators (@RequirePermission)
|
3. Authorization: RBAC Guard (ใช้ CASL) ตรวจสอบสิทธิ์จาก Decorators (@RequirePermission)
|
||||||
4. Validation: Validation Pipe (ใช้ class-validator) ตรวจสอบ DTO
|
4. Validation: Validation Pipe (ใช้ class-validator) ตรวจสอบ DTO
|
||||||
5. Business Logic: Service Layer ประมวลผลตรรกะทางธุรกิจ
|
5. Business Logic: Service Layer ประมวลผลตรรกะทางธุรกิจ
|
||||||
6. Data Access: Repository Layer (ใช้ TypeORM) ติดต่อกับฐานข้อมูล MariaDB
|
6. Data Access: Repository Layer (ใช้ TypeORM) ติดต่อกับฐานข้อมูล MariaDB
|
||||||
7. Response: ส่งกลับไปยัง Frontend พร้อมสถานะและข้อมูลที่เหมาะสม
|
7. Response: ส่งกลับไปยัง Frontend พร้อมสถานะและข้อมูลที่เหมาะสม
|
||||||
|
|
||||||
# **🖥️ ฟรอนต์เอนด์ (NextJS / React / UI) (Frontend (NextJS / React / UI))**
|
# **🖥️ ฟรอนต์เอนด์ (NextJS / React / UI) (Frontend (NextJS / React / UI))**
|
||||||
|
|
||||||
### **โปรไฟล์นักพัฒนา (Developer Profile)**
|
### **โปรไฟล์นักพัฒนา (Developer Profile)**
|
||||||
|
|
||||||
วิศวกร TypeScript + React/NextJS ระดับ Senior
|
วิศวกร TypeScript + React/NextJS ระดับ Senior
|
||||||
เชี่ยวชาญ TailwindCSS, Shadcn/UI, และ Radix สำหรับการพัฒนา UI
|
เชี่ยวชาญ TailwindCSS, Shadcn/UI, และ Radix สำหรับการพัฒนา UI
|
||||||
|
|
||||||
### **แนวทางการพัฒนาโค้ด (Code Implementation Guidelines)**
|
### **แนวทางการพัฒนาโค้ด (Code Implementation Guidelines)**
|
||||||
|
|
||||||
* ใช้ **early returns** เพื่อความชัดเจน
|
* ใช้ **early returns** เพื่อความชัดเจน
|
||||||
* ใช้คลาสของ **TailwindCSS** ในการกำหนดสไตล์เสมอ
|
* ใช้คลาสของ **TailwindCSS** ในการกำหนดสไตล์เสมอ
|
||||||
* ควรใช้ class: syntax แบบมีเงื่อนไข (หรือ utility clsx) มากกว่าการใช้ ternary operators ใน class strings
|
* ควรใช้ class: syntax แบบมีเงื่อนไข (หรือ utility clsx) มากกว่าการใช้ ternary operators ใน class strings
|
||||||
* ใช้ **const arrow functions** สำหรับ components และ handlers
|
* ใช้ **const arrow functions** สำหรับ components และ handlers
|
||||||
* Event handlers ให้ขึ้นต้นด้วย handle... (เช่น handleClick, handleSubmit)
|
* Event handlers ให้ขึ้นต้นด้วย handle... (เช่น handleClick, handleSubmit)
|
||||||
* รวมแอตทริบิวต์สำหรับการเข้าถึง (accessibility) ด้วย:
|
* รวมแอตทริบิวต์สำหรับการเข้าถึง (accessibility) ด้วย:
|
||||||
tabIndex="0", aria-label, onKeyDown, ฯลฯ
|
tabIndex="0", aria-label, onKeyDown, ฯลฯ
|
||||||
* ตรวจสอบให้แน่ใจว่าโค้ดทั้งหมด **สมบูรณ์**, **ผ่านการทดสอบ**, และ **ไม่ซ้ำซ้อน (DRY)**
|
* ตรวจสอบให้แน่ใจว่าโค้ดทั้งหมด **สมบูรณ์**, **ผ่านการทดสอบ**, และ **ไม่ซ้ำซ้อน (DRY)**
|
||||||
* ต้อง import โมดูลที่จำเป็นต้องใช้อย่างชัดเจนเสมอ
|
* ต้อง import โมดูลที่จำเป็นต้องใช้อย่างชัดเจนเสมอ
|
||||||
|
|
||||||
### **UI/UX ด้วย React**
|
### **UI/UX ด้วย React**
|
||||||
|
|
||||||
* ใช้ **semantic HTML**
|
* ใช้ **semantic HTML**
|
||||||
* ใช้คลาสของ **Tailwind** ที่รองรับ responsive (sm:, md:, lg:)
|
* ใช้คลาสของ **Tailwind** ที่รองรับ responsive (sm:, md:, lg:)
|
||||||
* รักษาลำดับชั้นของการมองเห็น (visual hierarchy) ด้วยการใช้ typography และ spacing
|
* รักษาลำดับชั้นของการมองเห็น (visual hierarchy) ด้วยการใช้ typography และ spacing
|
||||||
* ใช้ **Shadcn** components (Button, Input, Card, ฯลฯ) เพื่อ UI ที่สอดคล้องกัน
|
* ใช้ **Shadcn** components (Button, Input, Card, ฯลฯ) เพื่อ UI ที่สอดคล้องกัน
|
||||||
* ทำให้ components มีขนาดเล็กและมุ่งเน้นการทำงานเฉพาะอย่าง
|
* ทำให้ components มีขนาดเล็กและมุ่งเน้นการทำงานเฉพาะอย่าง
|
||||||
* ใช้ utility classes สำหรับการจัดสไตล์อย่างรวดเร็ว (spacing, colors, text, ฯลฯ)
|
* ใช้ utility classes สำหรับการจัดสไตล์อย่างรวดเร็ว (spacing, colors, text, ฯลฯ)
|
||||||
* ตรวจสอบให้แน่ใจว่าสอดคล้องกับ **ARIA** และใช้ semantic markup
|
* ตรวจสอบให้แน่ใจว่าสอดคล้องกับ **ARIA** และใช้ semantic markup
|
||||||
|
|
||||||
### **การตรวจสอบฟอร์มและข้อผิดพลาด (Form Validation & Errors)**
|
### **การตรวจสอบฟอร์มและข้อผิดพลาด (Form Validation & Errors)**
|
||||||
|
|
||||||
* ใช้ไลบรารีฝั่ง client เช่น zod และ react-hook-form
|
* ใช้ไลบรารีฝั่ง client เช่น zod และ react-hook-form
|
||||||
* แสดงข้อผิดพลาดด้วย **alert components** หรือข้อความ inline
|
* แสดงข้อผิดพลาดด้วย **alert components** หรือข้อความ inline
|
||||||
* ต้องมี labels, placeholders, และข้อความ feedback
|
* ต้องมี labels, placeholders, และข้อความ feedback
|
||||||
|
|
||||||
### **🧪 Frontend Testing**
|
### **🧪 Frontend Testing**
|
||||||
|
|
||||||
เราจะใช้ **React Testing Library (RTL)** สำหรับการทดสอบ Component และ **Playwright** สำหรับ E2E:
|
เราจะใช้ **React Testing Library (RTL)** สำหรับการทดสอบ Component และ **Playwright** สำหรับ E2E:
|
||||||
|
|
||||||
* **Unit Tests (การทดสอบหน่วยย่อย):**
|
* **Unit Tests (การทดสอบหน่วยย่อย):**
|
||||||
* **เครื่องมือ:** Vitest + RTL
|
* **เครื่องมือ:** Vitest + RTL
|
||||||
* **เป้าหมาย:** ทดสอบ Component ขนาดเล็ก (เช่น Buttons, Inputs) หรือ Utility functions
|
* **เป้าหมาย:** ทดสอบ Component ขนาดเล็ก (เช่น Buttons, Inputs) หรือ Utility functions
|
||||||
* **Integration Tests (การทดสอบการบูรณาการ):**
|
* **Integration Tests (การทดสอบการบูรณาการ):**
|
||||||
* **เครื่องมือ:** RTL + **Mock Service Worker (MSW)**
|
* **เครื่องมือ:** RTL + **Mock Service Worker (MSW)**
|
||||||
* **เป้าหมาย:** ทดสอบว่า Component หรือ Page ทำงานกับ API (ที่จำลองขึ้น) ได้ถูกต้อง
|
* **เป้าหมาย:** ทดสอบว่า Component หรือ Page ทำงานกับ API (ที่จำลองขึ้น) ได้ถูกต้อง
|
||||||
* **เทคนิค:** ใช้ MSW เพื่อจำลอง NestJS API และทดสอบว่า Component แสดงผลข้อมูลจำลองได้ถูกต้องหรือไม่ (เช่น ทดสอบหน้า Dashboard [cite: 5.3] ที่ดึงข้อมูลจาก v_user_tasks)
|
* **เทคนิค:** ใช้ MSW เพื่อจำลอง NestJS API และทดสอบว่า Component แสดงผลข้อมูลจำลองได้ถูกต้องหรือไม่ (เช่น ทดสอบหน้า Dashboard [cite: 5.3] ที่ดึงข้อมูลจาก v_user_tasks)
|
||||||
* **E2E (End-to-End) Tests:**
|
* **E2E (End-to-End) Tests:**
|
||||||
* **เครื่องมือ:** **Playwright**
|
* **เครื่องมือ:** **Playwright**
|
||||||
* **เป้าหมาย:** ทดสอบ User Flow ทั้งระบบโดยอัตโนมัติ (เช่น ล็อกอิน -> สร้าง RFA -> ตรวจสอบ Workflow Visualization [cite: 5.6])
|
* **เป้าหมาย:** ทดสอบ User Flow ทั้งระบบโดยอัตโนมัติ (เช่น ล็อกอิน -> สร้าง RFA -> ตรวจสอบ Workflow Visualization [cite: 5.6])
|
||||||
|
|
||||||
### **🗄️ Frontend State Management**
|
### **🗄️ Frontend State Management**
|
||||||
|
|
||||||
สำหรับ Next.js App Router เราจะแบ่ง State เป็น 4 ระดับ:
|
สำหรับ Next.js App Router เราจะแบ่ง State เป็น 4 ระดับ:
|
||||||
|
|
||||||
1. **Local UI State (สถานะ UI ชั่วคราว):**
|
1. **Local UI State (สถานะ UI ชั่วคราว):**
|
||||||
* **เครื่องมือ:** useState, useReducer
|
* **เครื่องมือ:** useState, useReducer
|
||||||
* **ใช้เมื่อ:** จัดการสถานะเล็กๆ ที่จบใน Component เดียว (เช่น Modal เปิด/ปิด, ค่าใน Input)
|
* **ใช้เมื่อ:** จัดการสถานะเล็กๆ ที่จบใน Component เดียว (เช่น Modal เปิด/ปิด, ค่าใน Input)
|
||||||
2. **Server State (สถานะข้อมูลจากเซิร์ฟเวอร์):**
|
2. **Server State (สถานะข้อมูลจากเซิร์ฟเวอร์):**
|
||||||
* **เครื่องมือ:** **React Query (TanStack Query)** หรือ SWR
|
* **เครื่องมือ:** **React Query (TanStack Query)** หรือ SWR
|
||||||
* **ใช้เมื่อ:** จัดการข้อมูลที่ดึงมาจาก NestJS API (เช่น รายการ correspondences, rfas, drawings)
|
* **ใช้เมื่อ:** จัดการข้อมูลที่ดึงมาจาก NestJS API (เช่น รายการ correspondences, rfas, drawings)
|
||||||
* **ทำไม:** React Query เป็น "Cache" ที่จัดการ Caching, Re-fetching, และ Invalidation ให้โดยอัตโนมัติ
|
* **ทำไม:** React Query เป็น "Cache" ที่จัดการ Caching, Re-fetching, และ Invalidation ให้โดยอัตโนมัติ
|
||||||
3. **Global Client State (สถานะส่วนกลางฝั่ง Client):**
|
3. **Global Client State (สถานะส่วนกลางฝั่ง Client):**
|
||||||
* **เครื่องมือ:** **Zustand** (แนะนำ) หรือ Context API
|
* **เครื่องมือ:** **Zustand** (แนะนำ) หรือ Context API
|
||||||
* **ใช้เมื่อ:** จัดการข้อมูลที่ต้องใช้ร่วมกันทั่วทั้งแอป และ *ไม่ใช่* ข้อมูลจากเซิร์ฟเวอร์ (เช่น ข้อมูล User ที่ล็อกอิน, สิทธิ์ Permissions)
|
* **ใช้เมื่อ:** จัดการข้อมูลที่ต้องใช้ร่วมกันทั่วทั้งแอป และ *ไม่ใช่* ข้อมูลจากเซิร์ฟเวอร์ (เช่น ข้อมูล User ที่ล็อกอิน, สิทธิ์ Permissions)
|
||||||
4. **Form State (สถานะของฟอร์ม):**
|
4. **Form State (สถานะของฟอร์ม):**
|
||||||
* **เครื่องมือ:** **React Hook Form** + **Zod**
|
* **เครื่องมือ:** **React Hook Form** + **Zod**
|
||||||
* **ใช้เมื่อ:** จัดการฟอร์มที่ซับซ้อน (เช่น ฟอร์มสร้าง RFA, ฟอร์ม Circulation [cite: 3.7])
|
* **ใช้เมื่อ:** จัดการฟอร์มที่ซับซ้อน (เช่น ฟอร์มสร้าง RFA, ฟอร์ม Circulation [cite: 3.7])
|
||||||
|
|
||||||
# **🔗 แนวทางการบูรณาการ Full Stack (Full Stack Integration Guidelines)**
|
# **🔗 แนวทางการบูรณาการ Full Stack (Full Stack Integration Guidelines)**
|
||||||
|
|
||||||
| Aspect (แง่มุม) | Backend (NestJS) | Frontend (NextJS) | UI Layer (Tailwind/Shadcn) |
|
| Aspect (แง่มุม) | Backend (NestJS) | Frontend (NextJS) | UI Layer (Tailwind/Shadcn) |
|
||||||
| :---- | :---- | :---- | :---- |
|
| :---- | :---- | :---- | :---- |
|
||||||
| API | REST / GraphQL Controllers | API hooks ผ่าน fetch/axios/SWR | Components ที่รับข้อมูล |
|
| API | REST / GraphQL Controllers | API hooks ผ่าน fetch/axios/SWR | Components ที่รับข้อมูล |
|
||||||
| Validation (การตรวจสอบ) | class-validator DTOs | zod / react-hook-form | สถานะของฟอร์ม/input ใน Shadcn |
|
| Validation (การตรวจสอบ) | class-validator DTOs | zod / react-hook-form | สถานะของฟอร์ม/input ใน Shadcn |
|
||||||
| Auth (การยืนยันตัวตน) | Guards, JWT | NextAuth / cookies | สถานะ UI ของ Auth (loading, signed in) |
|
| Auth (การยืนยันตัวตน) | Guards, JWT | NextAuth / cookies | สถานะ UI ของ Auth (loading, signed in) |
|
||||||
| Errors (ข้อผิดพลาด) | Global filters | Toasts / modals | Alerts / ข้อความ feedback |
|
| Errors (ข้อผิดพลาด) | Global filters | Toasts / modals | Alerts / ข้อความ feedback |
|
||||||
| Testing (การทดสอบ) | Jest (unit/e2e) | Vitest / Playwright | Visual regression |
|
| Testing (การทดสอบ) | Jest (unit/e2e) | Vitest / Playwright | Visual regression |
|
||||||
| Styles (สไตล์) | Scoped modules (ถ้าจำเป็น) | Tailwind / Shadcn | Tailwind utilities |
|
| Styles (สไตล์) | Scoped modules (ถ้าจำเป็น) | Tailwind / Shadcn | Tailwind utilities |
|
||||||
| Accessibility (การเข้าถึง) | Guards + filters | ARIA attributes | Semantic HTML |
|
| Accessibility (การเข้าถึง) | Guards + filters | ARIA attributes | Semantic HTML |
|
||||||
|
|
||||||
## **🗂️ ข้อตกลงเฉพาะสำหรับ DMS (LCBP3-DMS)**
|
## **🗂️ ข้อตกลงเฉพาะสำหรับ DMS (LCBP3-DMS)**
|
||||||
|
|
||||||
ส่วนนี้ขยายแนวทาง FullStackJS ทั่วไปสำหรับโปรเจกต์ **LCBP3-DMS** โดยมุ่งเน้นไปที่เวิร์กโฟลว์การอนุมัติเอกสาร (Correspondence, RFA, Drawing, Contract, Transmittal, Circulation)
|
ส่วนนี้ขยายแนวทาง FullStackJS ทั่วไปสำหรับโปรเจกต์ **LCBP3-DMS** โดยมุ่งเน้นไปที่เวิร์กโฟลว์การอนุมัติเอกสาร (Correspondence, RFA, Drawing, Contract, Transmittal, Circulation)
|
||||||
|
|
||||||
### **🧩 RBAC และการควบคุมสิทธิ์ (RBAC & Permission Control)**
|
### **🧩 RBAC และการควบคุมสิทธิ์ (RBAC & Permission Control)**
|
||||||
|
|
||||||
ใช้ Decorators เพื่อบังคับใช้สิทธิ์การเข้าถึง โดยอ้างอิงสิทธิ์จากตาราง permissions
|
ใช้ Decorators เพื่อบังคับใช้สิทธิ์การเข้าถึง โดยอ้างอิงสิทธิ์จากตาราง permissions
|
||||||
|
|
||||||
@RequirePermission('rfas.respond') // ต้องตรงกับ 'permission\code'
|
@RequirePermission('rfas.respond') // ต้องตรงกับ 'permission\code'
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
updateRFA(@Param('id') id: string) {
|
updateRFA(@Param('id') id: string) {
|
||||||
return this.rfaService.update(id);
|
return this.rfaService.update(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
### **Roles (บทบาท)**
|
### **Roles (บทบาท)**
|
||||||
|
|
||||||
* **Superadmin**: ไม่มีข้อจำกัดใดๆ [cite: 4.3]
|
* **Superadmin**: ไม่มีข้อจำกัดใดๆ [cite: 4.3]
|
||||||
* **Admin**: มีสิทธิ์เต็มที่ในองค์กร [cite: 4.3]
|
* **Admin**: มีสิทธิ์เต็มที่ในองค์กร [cite: 4.3]
|
||||||
* **Document Control**: เพิ่ม/แก้ไข/ลบ เอกสารในองค์กร [cite: 4.3]
|
* **Document Control**: เพิ่ม/แก้ไข/ลบ เอกสารในองค์กร [cite: 4.3]
|
||||||
* **Editor**: สามารถ เพิ่ม/แก้ไข เอกสารที่กำหนด [cite: 4.3]
|
* **Editor**: สามารถ เพิ่ม/แก้ไข เอกสารที่กำหนด [cite: 4.3]
|
||||||
* **Viewer**: สามารถดู เอกสาร [cite: 4.3]
|
* **Viewer**: สามารถดู เอกสาร [cite: 4.3]
|
||||||
|
|
||||||
### **ตัวอย่าง Permissions (จากตาราง permissions)**
|
### **ตัวอย่าง Permissions (จากตาราง permissions)**
|
||||||
|
|
||||||
* rfas.view, rfas.create, rfas.respond, rfas.delete
|
* rfas.view, rfas.create, rfas.respond, rfas.delete
|
||||||
* drawings.view, drawings.upload, drawings.delete
|
* drawings.view, drawings.upload, drawings.delete
|
||||||
* corr.view, corr.manage
|
* corr.view, corr.manage
|
||||||
* transmittals.manage
|
* transmittals.manage
|
||||||
* cirs.manage
|
* cirs.manage
|
||||||
* project\parties.manage
|
* project\parties.manage
|
||||||
|
|
||||||
การจับคู่ระหว่าง roles และ permissions **เริ่มต้น** จะถูก seed ผ่านสคริปต์ (ดังที่เห็นในไฟล์ SQL)**อย่างไรก็ตาม AuthModule/UserModule ต้องมี API สำหรับ Admin เพื่อสร้าง Role ใหม่และกำหนดสิทธิ์ (Permissions) เพิ่มเติมได้ในภายหลัง** [cite: 4.3]
|
การจับคู่ระหว่าง roles และ permissions **เริ่มต้น** จะถูก seed ผ่านสคริปต์ (ดังที่เห็นในไฟล์ SQL)**อย่างไรก็ตาม AuthModule/UserModule ต้องมี API สำหรับ Admin เพื่อสร้าง Role ใหม่และกำหนดสิทธิ์ (Permissions) เพิ่มเติมได้ในภายหลัง** [cite: 4.3]
|
||||||
|
|
||||||
## **🧾 มาตรฐาน AuditLog (AuditLog Standard)**
|
## **🧾 มาตรฐาน AuditLog (AuditLog Standard)**
|
||||||
|
|
||||||
บันทึกการดำเนินการ CRUD และการจับคู่ทั้งหมดลงในตาราง audit_logs
|
บันทึกการดำเนินการ CRUD และการจับคู่ทั้งหมดลงในตาราง audit_logs
|
||||||
|
|
||||||
| Field (ฟิลด์) | Type (จาก SQL) | Description (คำอธิบาย) |
|
| Field (ฟิลด์) | Type (จาก SQL) | Description (คำอธิบาย) |
|
||||||
| :---- | :---- | :---- |
|
| :---- | :---- | :---- |
|
||||||
| audit_id | BIGINT | Primary Key |
|
| audit_id | BIGINT | Primary Key |
|
||||||
| user_id | INT | ผู้ใช้ที่ดำเนินการ (FK -> users) |
|
| user_id | INT | ผู้ใช้ที่ดำเนินการ (FK -> users) |
|
||||||
| action | VARCHAR(100) | rfa.create, correspondence.update, login.success |
|
| action | VARCHAR(100) | rfa.create, correspondence.update, login.success |
|
||||||
| entity_type | VARCHAR(50) | ชื่อตาราง/โมดูล เช่น 'rfa', 'correspondence' |
|
| entity_type | VARCHAR(50) | ชื่อตาราง/โมดูล เช่น 'rfa', 'correspondence' |
|
||||||
| entity_id | VARCHAR(50) | Primary ID ของระเบียนที่ได้รับผลกระทบ |
|
| entity_id | VARCHAR(50) | Primary ID ของระเบียนที่ได้รับผลกระทบ |
|
||||||
| details_json | JSON | ข้อมูลบริบท (เช่น ฟิลด์ที่มีการเปลี่ยนแปลง) |
|
| details_json | JSON | ข้อมูลบริบท (เช่น ฟิลด์ที่มีการเปลี่ยนแปลง) |
|
||||||
| ip_address | VARCHAR(45) | IP address ของผู้ดำเนินการ |
|
| ip_address | VARCHAR(45) | IP address ของผู้ดำเนินการ |
|
||||||
| user_agent | VARCHAR(255) | User Agent ของผู้ดำเนินการ |
|
| user_agent | VARCHAR(255) | User Agent ของผู้ดำเนินการ |
|
||||||
| created_at | TIMESTAMP | Timestamp (UTC) |
|
| created_at | TIMESTAMP | Timestamp (UTC) |
|
||||||
|
|
||||||
## **📂 การจัดการไฟล์ (File Handling) (ปรับปรุงใหม่)**
|
## **📂 การจัดการไฟล์ (File Handling) (ปรับปรุงใหม่)**
|
||||||
|
|
||||||
### **มาตรฐานการอัปโหลดไฟล์ (File Upload Standard)**
|
### **มาตรฐานการอัปโหลดไฟล์ (File Upload Standard)**
|
||||||
|
|
||||||
* **ตรรกะใหม่:** การอัปโหลดไฟล์ทั้งหมดจะถูกจัดการโดย FileStorageService และบันทึกข้อมูลไฟล์ลงในตาราง attachments (ตารางกลาง)
|
* **ตรรกะใหม่:** การอัปโหลดไฟล์ทั้งหมดจะถูกจัดการโดย FileStorageService และบันทึกข้อมูลไฟล์ลงในตาราง attachments (ตารางกลาง)
|
||||||
* ไฟล์จะถูกเชื่อมโยงไปยัง Entity ที่ถูกต้องผ่าน **ตารางเชื่อม (Junction Tables)** เท่านั้น:
|
* ไฟล์จะถูกเชื่อมโยงไปยัง Entity ที่ถูกต้องผ่าน **ตารางเชื่อม (Junction Tables)** เท่านั้น:
|
||||||
* correspondence_attachments (เชื่อม Correspondence กับ Attachments)
|
* correspondence_attachments (เชื่อม Correspondence กับ Attachments)
|
||||||
* circulation_attachments (เชื่อม Circulation กับ Attachments)
|
* circulation_attachments (เชื่อม Circulation กับ Attachments)
|
||||||
* shop_drawing_revision_attachments (เชื่อม Shop Drawing Revision กับ Attachments)
|
* shop_drawing_revision_attachments (เชื่อม Shop Drawing Revision กับ Attachments)
|
||||||
* contract_drawing_attachments (เชื่อม Contract Drawing กับ Attachments)
|
* contract_drawing_attachments (เชื่อม Contract Drawing กับ Attachments)
|
||||||
* เส้นทางจัดเก็บไฟล์ (Upload path): อ้างอิงจาก Requirement 2.1 คือ /share/dms-data [cite: 2.1] โดย FileStorageService จะสร้างโฟลเดอร์ย่อยแบบรวมศูนย์ (เช่น /share/dms-data/uploads/{YYYY}/{MM}/[stored\filename])
|
* เส้นทางจัดเก็บไฟล์ (Upload path): อ้างอิงจาก Requirement 2.1 คือ /share/dms-data [cite: 2.1] โดย FileStorageService จะสร้างโฟลเดอร์ย่อยแบบรวมศูนย์ (เช่น /share/dms-data/uploads/{YYYY}/{MM}/[stored\filename])
|
||||||
* ประเภทไฟล์ที่อนุญาต: pdf, dwg, docx, xlsx, zip
|
* ประเภทไฟล์ที่อนุญาต: pdf, dwg, docx, xlsx, zip
|
||||||
* ขนาดสูงสุด: **50 MB**
|
* ขนาดสูงสุด: **50 MB**
|
||||||
* จัดเก็บนอก webroot
|
* จัดเก็บนอก webroot
|
||||||
* ให้บริการไฟล์ผ่าน endpoint ที่ปลอดภัย /files/:attachment_id/download
|
* ให้บริการไฟล์ผ่าน endpoint ที่ปลอดภัย /files/:attachment_id/download
|
||||||
|
|
||||||
### **การควบคุมการเข้าถึง (Access Control)**
|
### **การควบคุมการเข้าถึง (Access Control)**
|
||||||
|
|
||||||
การเข้าถึงไฟล์ไม่ใช่การเข้าถึงโดยตรง endpoint /files/:attachment_id/download จะต้อง:
|
การเข้าถึงไฟล์ไม่ใช่การเข้าถึงโดยตรง endpoint /files/:attachment_id/download จะต้อง:
|
||||||
|
|
||||||
1. ค้นหาระเบียน attachment
|
1. ค้นหาระเบียน attachment
|
||||||
2. ตรวจสอบว่า attachment_id นี้ เชื่อมโยงกับ Entity ใด (เช่น correspondence, circulation, shop_drawing_revision, contract_drawing) ผ่านตารางเชื่อม
|
2. ตรวจสอบว่า attachment_id นี้ เชื่อมโยงกับ Entity ใด (เช่น correspondence, circulation, shop_drawing_revision, contract_drawing) ผ่านตารางเชื่อม
|
||||||
3. ตรวจสอบว่าผู้ใช้มีสิทธิ์ (permission) ในการดู Entity ต้นทางนั้นๆ หรือไม่
|
3. ตรวจสอบว่าผู้ใช้มีสิทธิ์ (permission) ในการดู Entity ต้นทางนั้นๆ หรือไม่
|
||||||
|
|
||||||
## **🔟 การจัดการเลขที่เอกสาร (Document Numbering) [cite: 3.10]**
|
## **🔟 การจัดการเลขที่เอกสาร (Document Numbering) [cite: 3.10]**
|
||||||
|
|
||||||
* **เป้าหมาย:** สร้างเลขที่เอกสาร (เช่น correspondence\number) โดยอัตโนมัติ ตามรูปแบบที่กำหนด
|
* **เป้าหมาย:** สร้างเลขที่เอกสาร (เช่น correspondence\number) โดยอัตโนมัติ ตามรูปแบบที่กำหนด
|
||||||
* **ตรรกะการนับ:** การนับ Running number (SEQ) จะนับแยกตาม Key: **Project + Originator Organization + Document Type + Year**
|
* **ตรรกะการนับ:** การนับ Running number (SEQ) จะนับแยกตาม Key: **Project + Originator Organization + Document Type + Year**
|
||||||
* **ตาราง SQL:**
|
* **ตาราง SQL:**
|
||||||
* document_number_formats: Admin ใช้กำหนด "รูปแบบ" (Template) ของเลขที่ (เช่น {ORG\CODE}-{TYPE\CODE}-{YEAR\SHORT}-{SEQ:4}) โดยกำหนดตาม **Project** และ **Document Type** [cite: 4.5]
|
* document_number_formats: Admin ใช้กำหนด "รูปแบบ" (Template) ของเลขที่ (เช่น {ORG\CODE}-{TYPE\CODE}-{YEAR\SHORT}-{SEQ:4}) โดยกำหนดตาม **Project** และ **Document Type** [cite: 4.5]
|
||||||
* document_number_counters: ระบบใช้เก็บ "ตัวนับ" ล่าสุดของ Key (Project+Org+Type+Year)
|
* document_number_counters: ระบบใช้เก็บ "ตัวนับ" ล่าสุดของ Key (Project+Org+Type+Year)
|
||||||
* **การทำงาน (Backend):**
|
* **การทำงาน (Backend):**
|
||||||
* DocumentNumberingModule จะให้บริการ DocumentNumberingService
|
* DocumentNumberingModule จะให้บริการ DocumentNumberingService
|
||||||
* เมื่อ CorrespondenceModule ต้องการสร้างเอกสารใหม่, มันจะเรียก documentNumberingService.generateNextNumber(...)
|
* เมื่อ CorrespondenceModule ต้องการสร้างเอกสารใหม่, มันจะเรียก documentNumberingService.generateNextNumber(...)
|
||||||
* Service นี้จะเรียกใช้ Stored Procedure **sp_get_next_document_number** [cite: 2.9.3] ซึ่ง Procedure นี้จะจัดการ Database Transaction และ Row Lock (FOR UPDATE) ภายใน DB เพื่อรับประกันการป้องกัน Race Condition
|
* Service นี้จะเรียกใช้ Stored Procedure **sp_get_next_document_number** [cite: 2.9.3] ซึ่ง Procedure นี้จะจัดการ Database Transaction และ Row Lock (FOR UPDATE) ภายใน DB เพื่อรับประกันการป้องกัน Race Condition
|
||||||
|
|
||||||
## **📊 การรายงานและการส่งออก (Reporting & Exports)**
|
## **📊 การรายงานและการส่งออก (Reporting & Exports)**
|
||||||
|
|
||||||
### **วิวสำหรับการรายงาน (Reporting Views) (จาก SQL)**
|
### **วิวสำหรับการรายงาน (Reporting Views) (จาก SQL)**
|
||||||
|
|
||||||
การรายงานควรสร้างขึ้นจาก Views ที่กำหนดไว้ล่วงหน้าในฐานข้อมูลเป็นหลัก:
|
การรายงานควรสร้างขึ้นจาก Views ที่กำหนดไว้ล่วงหน้าในฐานข้อมูลเป็นหลัก:
|
||||||
|
|
||||||
* v_current_correspondences: สำหรับ revision ปัจจุบันทั้งหมดของเอกสารที่ไม่ใช่ RFA
|
* v_current_correspondences: สำหรับ revision ปัจจุบันทั้งหมดของเอกสารที่ไม่ใช่ RFA
|
||||||
* v_current_rfas: สำหรับ revision ปัจจุบันทั้งหมดของ RFA และข้อมูล master
|
* v_current_rfas: สำหรับ revision ปัจจุบันทั้งหมดของ RFA และข้อมูล master
|
||||||
* v_contract_parties_all: สำหรับการตรวจสอบความสัมพันธ์ของ project/contract/organization
|
* v_contract_parties_all: สำหรับการตรวจสอบความสัมพันธ์ของ project/contract/organization
|
||||||
* v_user_tasks: สำหรับ Dashboard "งานของฉัน"
|
* v_user_tasks: สำหรับ Dashboard "งานของฉัน"
|
||||||
* v_audit_log_details: สำหรับ Activity Feed
|
* v_audit_log_details: สำหรับ Activity Feed
|
||||||
|
|
||||||
Views เหล่านี้ทำหน้าที่เป็นแหล่งข้อมูลหลักสำหรับการรายงานฝั่งเซิร์ฟเวอร์และการส่งออกข้อมูล
|
Views เหล่านี้ทำหน้าที่เป็นแหล่งข้อมูลหลักสำหรับการรายงานฝั่งเซิร์ฟเวอร์และการส่งออกข้อมูล
|
||||||
|
|
||||||
### **กฎการส่งออก (Export Rules)**
|
### **กฎการส่งออก (Export Rules)**
|
||||||
|
|
||||||
* Export formats: CSV, Excel, PDF.
|
* Export formats: CSV, Excel, PDF.
|
||||||
* จัดเตรียมมุมมองสำหรับพิมพ์ (Print view).
|
* จัดเตรียมมุมมองสำหรับพิมพ์ (Print view).
|
||||||
* รวมลิงก์ไปยังต้นทาง (เช่น /rfas/:id).
|
* รวมลิงก์ไปยังต้นทาง (เช่น /rfas/:id).
|
||||||
|
|
||||||
## **🧮 ฟรอนต์เอนด์: รูปแบบ DataTable และฟอร์ม (Frontend: DataTable & Form Patterns)**
|
## **🧮 ฟรอนต์เอนด์: รูปแบบ DataTable และฟอร์ม (Frontend: DataTable & Form Patterns)**
|
||||||
|
|
||||||
### **DataTable (Server‑Side)**
|
### **DataTable (Server‑Side)**
|
||||||
|
|
||||||
* Endpoint: /api/{module}?page=1\&pageSize=20\&sort=...\&filter=...
|
* Endpoint: /api/{module}?page=1\&pageSize=20\&sort=...\&filter=...
|
||||||
* ต้องรองรับ: การแบ่งหน้า (pagination), การเรียงลำดับ (sorting), การค้นหา (search), การกรอง (filters)
|
* ต้องรองรับ: การแบ่งหน้า (pagination), การเรียงลำดับ (sorting), การค้นหา (search), การกรอง (filters)
|
||||||
* แสดง revision ล่าสุดแบบ inline เสมอ (สำหรับ RFA/Drawing)
|
* แสดง revision ล่าสุดแบบ inline เสมอ (สำหรับ RFA/Drawing)
|
||||||
|
|
||||||
### **มาตรฐานฟอร์ม (Form Standards)**
|
### **มาตรฐานฟอร์ม (Form Standards)**
|
||||||
|
|
||||||
* ต้องมีการใช้งาน Dropdowns แบบขึ้นต่อกัน (Dependent dropdowns) (ตามที่สคีมารองรับ):
|
* ต้องมีการใช้งาน Dropdowns แบบขึ้นต่อกัน (Dependent dropdowns) (ตามที่สคีมารองรับ):
|
||||||
* Project → Contract Drawing Volumes
|
* Project → Contract Drawing Volumes
|
||||||
* Contract Drawing Category → Sub-Category
|
* Contract Drawing Category → Sub-Category
|
||||||
* RFA (ประเภท Shop Drawing) → Shop Drawing Revisions ที่เชื่อมโยงได้
|
* RFA (ประเภท Shop Drawing) → Shop Drawing Revisions ที่เชื่อมโยงได้
|
||||||
* **(ใหม่)** การอัปโหลดไฟล์: ต้องรองรับ **Multi-file upload (Drag-and-Drop)** [cite: 5.7]
|
* **(ใหม่)** การอัปโหลดไฟล์: ต้องรองรับ **Multi-file upload (Drag-and-Drop)** [cite: 5.7]
|
||||||
* **(ใหม่)** UI ต้องอนุญาตให้ผู้ใช้กำหนดว่าไฟล์ใดเป็น **"เอกสารหลัก"** หรือ "เอกสารแนบประกอบ" [cite: 5.7]
|
* **(ใหม่)** UI ต้องอนุญาตให้ผู้ใช้กำหนดว่าไฟล์ใดเป็น **"เอกสารหลัก"** หรือ "เอกสารแนบประกอบ" [cite: 5.7]
|
||||||
* ส่ง (Submit) ผ่าน API พร้อม feedback แบบ toast
|
* ส่ง (Submit) ผ่าน API พร้อม feedback แบบ toast
|
||||||
|
|
||||||
### **ข้อกำหนด Component เฉพาะ (Specific UI Requirements)**
|
### **ข้อกำหนด Component เฉพาะ (Specific UI Requirements)**
|
||||||
|
|
||||||
* **Dashboard \- My Tasks:** ต้องพัฒนา Component ตาราง "งานของฉัน" (My Tasks)ซึ่งดึงข้อมูลงานที่ผู้ใช้ล็อกอินอยู่ต้องรับผิดชอบ (Main/Action) จาก v\user\tasks [cite: 5.3]
|
* **Dashboard \- My Tasks:** ต้องพัฒนา Component ตาราง "งานของฉัน" (My Tasks)ซึ่งดึงข้อมูลงานที่ผู้ใช้ล็อกอินอยู่ต้องรับผิดชอบ (Main/Action) จาก v\user\tasks [cite: 5.3]
|
||||||
* **Workflow Visualization:** ต้องพัฒนา Component สำหรับแสดงผล Workflow (โดยเฉพาะ RFA)ที่แสดงขั้นตอนทั้งหมดเป็นลำดับ โดยขั้นตอนปัจจุบัน (active) เท่านั้นที่ดำเนินการได้ และขั้นตอนอื่นเป็น disabled [cite: 5.6] ต้องมีตรรกะสำหรับ Admin ในการ override หรือย้อนกลับขั้นตอนได้ [cite: 5.6]
|
* **Workflow Visualization:** ต้องพัฒนา Component สำหรับแสดงผล Workflow (โดยเฉพาะ RFA)ที่แสดงขั้นตอนทั้งหมดเป็นลำดับ โดยขั้นตอนปัจจุบัน (active) เท่านั้นที่ดำเนินการได้ และขั้นตอนอื่นเป็น disabled [cite: 5.6] ต้องมีตรรกะสำหรับ Admin ในการ override หรือย้อนกลับขั้นตอนได้ [cite: 5.6]
|
||||||
* ** Admin Panel:** ต้องมีหน้า UI สำหรับ Superadmin/Admin เพื่อจัดการข้อมูลหลัก (Master Data [cite: 4.5]), การเริ่มต้นใช้งาน (Onboarding [cite: 4.6]), และ **รูปแบบเลขที่เอกสาร (Numbering Formats [cite: 3.10])**
|
* ** Admin Panel:** ต้องมีหน้า UI สำหรับ Superadmin/Admin เพื่อจัดการข้อมูลหลัก (Master Data [cite: 4.5]), การเริ่มต้นใช้งาน (Onboarding [cite: 4.6]), และ **รูปแบบเลขที่เอกสาร (Numbering Formats [cite: 3.10])**
|
||||||
|
|
||||||
## **🧭 แดชบอร์ดและฟีดกิจกรรม (Dashboard & Activity Feed)**
|
## **🧭 แดชบอร์ดและฟีดกิจกรรม (Dashboard & Activity Feed)**
|
||||||
|
|
||||||
### **การ์ดบนแดชบอร์ด (Dashboard Cards)**
|
### **การ์ดบนแดชบอร์ด (Dashboard Cards)**
|
||||||
|
|
||||||
* แสดง Correspondences, RFAs, Circulations, Shop Drawing Revision ล่าสุด
|
* แสดง Correspondences, RFAs, Circulations, Shop Drawing Revision ล่าสุด
|
||||||
* รวมสรุป KPI (เช่น "RFAs ที่รอการอนุมัติ", "Shop Drawing ที่รอการอนุมัติ") [cite: 5.3]
|
* รวมสรุป KPI (เช่น "RFAs ที่รอการอนุมัติ", "Shop Drawing ที่รอการอนุมัติ") [cite: 5.3]
|
||||||
* รวมลิงก์ด่วนไปยังโมดูลต่างๆ
|
* รวมลิงก์ด่วนไปยังโมดูลต่างๆ
|
||||||
|
|
||||||
### **ฟีดกิจกรรม (Activity Feed)**
|
### **ฟีดกิจกรรม (Activity Feed)**
|
||||||
|
|
||||||
* แสดงรายการ v\audit\log\details ล่าสุด (10 รายการ) ที่เกี่ยวข้องกับผู้ใช้
|
* แสดงรายการ v\audit\log\details ล่าสุด (10 รายการ) ที่เกี่ยวข้องกับผู้ใช้
|
||||||
|
|
||||||
// ตัวอย่าง API response
|
// ตัวอย่าง API response
|
||||||
[
|
[
|
||||||
{ user: 'editor01', action: 'Updated RFA (LCBP3-RFA-001)', time: '2025-11-04T09:30Z' }
|
{ user: 'editor01', action: 'Updated RFA (LCBP3-RFA-001)', time: '2025-11-04T09:30Z' }
|
||||||
]
|
]
|
||||||
|
|
||||||
## **🛡️ ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน (Non-Functional Requirements)**
|
## **🛡️ ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน (Non-Functional Requirements)**
|
||||||
|
|
||||||
ส่วนนี้สรุปข้อกำหนด Non-Functional จาก requirements.md เพื่อให้ทีมพัฒนาทราบ
|
ส่วนนี้สรุปข้อกำหนด Non-Functional จาก requirements.md เพื่อให้ทีมพัฒนาทราบ
|
||||||
|
|
||||||
* **Audit Log [cite: 6.1]:** ทุกการกระทำที่สำคัญ (C/U/D) ต้องถูกบันทึกใน audit_logs
|
* **Audit Log [cite: 6.1]:** ทุกการกระทำที่สำคัญ (C/U/D) ต้องถูกบันทึกใน audit_logs
|
||||||
* **Performance [cite: 6.4]:** ต้องใช้ Caching สำหรับข้อมูลที่เรียกบ่อย และใช้ Pagination
|
* **Performance [cite: 6.4]:** ต้องใช้ Caching สำหรับข้อมูลที่เรียกบ่อย และใช้ Pagination
|
||||||
* **Security [cite: 6.5]:** ต้องมี Rate Limiting และจัดการ Secret ผ่าน docker-compose.yml (ไม่ใช่ .env)
|
* **Security [cite: 6.5]:** ต้องมี Rate Limiting และจัดการ Secret ผ่าน docker-compose.yml (ไม่ใช่ .env)
|
||||||
* **(ใหม่) Backup & Recovery [cite: 6.6]:** ต้องมีแผนสำรองข้อมูลทั้ง Database (MariaDB) และ File Storage (/share/dms-data) อย่างน้อยวันละ 1 ครั้ง
|
* **(ใหม่) Backup & Recovery [cite: 6.6]:** ต้องมีแผนสำรองข้อมูลทั้ง Database (MariaDB) และ File Storage (/share/dms-data) อย่างน้อยวันละ 1 ครั้ง
|
||||||
* **(ใหม่) Notification Strategy [cite: 6.7]:** ระบบแจ้งเตือน (Email/Line) ต้องถูก Trigger เมื่อมีเอกสารใหม่ส่งถึง, มีการมอบหมายงานใหม่ (Circulation), หรือ (ทางเลือก) เมื่องานเสร็จ/ใกล้ถึงกำหนด
|
* **(ใหม่) Notification Strategy [cite: 6.7]:** ระบบแจ้งเตือน (Email/Line) ต้องถูก Trigger เมื่อมีเอกสารใหม่ส่งถึง, มีการมอบหมายงานใหม่ (Circulation), หรือ (ทางเลือก) เมื่องานเสร็จ/ใกล้ถึงกำหนด
|
||||||
|
|
||||||
## **✅ มาตรฐานที่นำไปใช้แล้ว (จาก SQL v1.1.0) (Implemented Standards (from SQL v1.1.0))**
|
## **✅ มาตรฐานที่นำไปใช้แล้ว (จาก SQL v1.1.0) (Implemented Standards (from SQL v1.1.0))**
|
||||||
|
|
||||||
ส่วนนี้ยืนยันว่าแนวทางปฏิบัติที่ดีที่สุดต่อไปนี้เป็นส่วนหนึ่งของการออกแบบฐานข้อมูลอยู่แล้ว และควรถูกนำไปใช้ประโยชน์ ไม่ใช่สร้างขึ้นใหม่
|
ส่วนนี้ยืนยันว่าแนวทางปฏิบัติที่ดีที่สุดต่อไปนี้เป็นส่วนหนึ่งของการออกแบบฐานข้อมูลอยู่แล้ว และควรถูกนำไปใช้ประโยชน์ ไม่ใช่สร้างขึ้นใหม่
|
||||||
|
|
||||||
* ✅ **Soft Delete:** นำไปใช้แล้วผ่านคอลัมน์ deleted_at ในตารางสำคัญ (เช่น correspondences, rfas, project_parties) ตรรกะการดึงข้อมูลต้องกรอง deleted_at IS NULL
|
* ✅ **Soft Delete:** นำไปใช้แล้วผ่านคอลัมน์ deleted_at ในตารางสำคัญ (เช่น correspondences, rfas, project_parties) ตรรกะการดึงข้อมูลต้องกรอง deleted_at IS NULL
|
||||||
* ✅ **Database Indexes:** สคีมาได้มีการทำ index ไว้อย่างหนักหน่วงบน foreign keys และคอลัมน์ที่ใช้ค้นหาบ่อย (เช่น idx_rr_rfa, idx_cor_project, idx_cr_is_current) เพื่อประสิทธิภาพ
|
* ✅ **Database Indexes:** สคีมาได้มีการทำ index ไว้อย่างหนักหน่วงบน foreign keys และคอลัมน์ที่ใช้ค้นหาบ่อย (เช่น idx_rr_rfa, idx_cor_project, idx_cr_is_current) เพื่อประสิทธิภาพ
|
||||||
* ✅ **โครงสร้าง RBAC:** มีระบบ users, roles, permissions, user_roles, และ user_project_roles ที่ครอบคลุมอยู่แล้ว
|
* ✅ **โครงสร้าง RBAC:** มีระบบ users, roles, permissions, user_roles, และ user_project_roles ที่ครอบคลุมอยู่แล้ว
|
||||||
* ✅ **Data Seeding:** ข้อมูล Master (roles, permissions, organization_roles, initial users, project parties) ถูกรวมอยู่ในสคริปต์สคีมาแล้ว
|
* ✅ **Data Seeding:** ข้อมูล Master (roles, permissions, organization_roles, initial users, project parties) ถูกรวมอยู่ในสคริปต์สคีมาแล้ว
|
||||||
|
|
||||||
## **🧩 การปรับปรุงที่แนะนำ (สำหรับอนาคต) (Recommended Enhancements (Future))**
|
## **🧩 การปรับปรุงที่แนะนำ (สำหรับอนาคต) (Recommended Enhancements (Future))**
|
||||||
|
|
||||||
* ✅ สร้าง Background job (โดยใช้ **n8n** เพื่อเชื่อมต่อกับ **Line** [cite: 2.7] และ/หรือใช้สำหรับการแจ้งเตือน RFA ที่ใกล้ถึงกำหนด due_date [cite: 6.7])
|
* ✅ สร้าง Background job (โดยใช้ **n8n** เพื่อเชื่อมต่อกับ **Line** [cite: 2.7] และ/หรือใช้สำหรับการแจ้งเตือน RFA ที่ใกล้ถึงกำหนด due_date [cite: 6.7])
|
||||||
* ✅ เพิ่ม job ล้างข้อมูลเป็นระยะสำหรับ attachments ที่ไม่ถูกเชื่อมโยงกับ Entity ใดๆ เลย (ไฟล์กำพร้า)
|
* ✅ เพิ่ม job ล้างข้อมูลเป็นระยะสำหรับ attachments ที่ไม่ถูกเชื่อมโยงกับ Entity ใดๆ เลย (ไฟล์กำพร้า)
|
||||||
@@ -1,245 +1,245 @@
|
|||||||
# **📝 Documents Management Sytem Version 1.4.0: Application Requirements Specification**
|
# **📝 Documents Management Sytem Version 1.4.0: Application Requirements Specification**
|
||||||
|
|
||||||
## **📌 1. วัตถุประสงค์**
|
## **📌 1. วัตถุประสงค์**
|
||||||
|
|
||||||
สร้างเว็บแอปพลิเคชั่นสำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System)ที่สามารถจัดการและควบคุม การสื่อสารด้วยเอกสารที่ซับซ้อน อย่างมีประสิทธิภาพ
|
สร้างเว็บแอปพลิเคชั่นสำหรับระบบบริหารจัดการเอกสารโครงการ (Document Management System)ที่สามารถจัดการและควบคุม การสื่อสารด้วยเอกสารที่ซับซ้อน อย่างมีประสิทธิภาพ
|
||||||
|
|
||||||
- มีฟังก์ชันหลักในการอัปโหลด จัดเก็บ ค้นหา แชร์ และควบคุมสิทธิ์การเข้าถึงเอกสาร
|
- มีฟังก์ชันหลักในการอัปโหลด จัดเก็บ ค้นหา แชร์ และควบคุมสิทธิ์การเข้าถึงเอกสาร
|
||||||
- ช่วยลดการใช้เอกสารกระดาษ เพิ่มความปลอดภัยในการจัดเก็บข้อมูล
|
- ช่วยลดการใช้เอกสารกระดาษ เพิ่มความปลอดภัยในการจัดเก็บข้อมูล
|
||||||
- เพิ่มความสะดวกในการทำงานร่วมกันระหว่างองกรณ์
|
- เพิ่มความสะดวกในการทำงานร่วมกันระหว่างองกรณ์
|
||||||
|
|
||||||
## **🛠️ 2. สถาปัตยกรรมและเทคโนโลยี (System Architecture & Technology Stack)**
|
## **🛠️ 2. สถาปัตยกรรมและเทคโนโลยี (System Architecture & Technology Stack)**
|
||||||
|
|
||||||
ใช้สถาปัตยกรรมแบบ Headless/API-First ที่ทันสมัย ทำงานทั้งหมดบน QNAP Server ผ่าน Container Station เพื่อความสะดวกในการจัดการและบำรุงรักษา, Domain: np-dms.work, มี fix ip, รัน docker command ใน application ของ Container Station ได้โดยตรง, ประกอบด้วย
|
ใช้สถาปัตยกรรมแบบ Headless/API-First ที่ทันสมัย ทำงานทั้งหมดบน QNAP Server ผ่าน Container Station เพื่อความสะดวกในการจัดการและบำรุงรักษา, Domain: np-dms.work, มี fix ip, รัน docker command ใน application ของ Container Station ได้โดยตรง, ประกอบด้วย
|
||||||
|
|
||||||
- **2.1. Infrastructure & Environment:**
|
- **2.1. Infrastructure & Environment:**
|
||||||
- Server: QNAP (Model: TS-473A, RAM: 32GB, CPU: AMD Ryzen V1500B)
|
- Server: QNAP (Model: TS-473A, RAM: 32GB, CPU: AMD Ryzen V1500B)
|
||||||
- Containerization: Container Station (Docker & Docker Compose) ใช้ UI ของ Container Station เป็นหลัก ในการ configuration และการรัน docker command
|
- Containerization: Container Station (Docker & Docker Compose) ใช้ UI ของ Container Station เป็นหลัก ในการ configuration และการรัน docker command
|
||||||
- Development Environment: VS Code on Windows 11
|
- Development Environment: VS Code on Windows 11
|
||||||
- Domain: np-dms.work, www.np-dms.work
|
- Domain: np-dms.work, www.np-dms.work
|
||||||
- ip: 159.192.126.103
|
- ip: 159.192.126.103
|
||||||
- Docker Network: ทุก Service จะเชื่อมต่อผ่านเครือข่ายกลางชื่อ lcbp3 เพื่อให้สามารถสื่อสารกันได้
|
- Docker Network: ทุก Service จะเชื่อมต่อผ่านเครือข่ายกลางชื่อ lcbp3 เพื่อให้สามารถสื่อสารกันได้
|
||||||
- Data Storage: /share/dms-data บน QNAP
|
- Data Storage: /share/dms-data บน QNAP
|
||||||
- ข้อจำกัด: ไม่สามารถใช้ .env ในการกำหนดตัวแปรภายนอกได้ ต้องกำหนดใน docker-compose.yml เท่านั้น
|
- ข้อจำกัด: ไม่สามารถใช้ .env ในการกำหนดตัวแปรภายนอกได้ ต้องกำหนดใน docker-compose.yml เท่านั้น
|
||||||
- **2.2. Code Hosting:**
|
- **2.2. Code Hosting:**
|
||||||
- Application name: git
|
- Application name: git
|
||||||
- Service: Gitea (Self-hosted on QNAP)
|
- Service: Gitea (Self-hosted on QNAP)
|
||||||
- Service name: gitea
|
- Service name: gitea
|
||||||
- Domain: git.np-dms.work
|
- Domain: git.np-dms.work
|
||||||
- หน้าที่: เป็นศูนย์กลางในการเก็บและจัดการเวอร์ชันของโค้ด (Source Code) สำหรับทุกส่วน
|
- หน้าที่: เป็นศูนย์กลางในการเก็บและจัดการเวอร์ชันของโค้ด (Source Code) สำหรับทุกส่วน
|
||||||
- **2.3. Backend / Data Platform:**
|
- **2.3. Backend / Data Platform:**
|
||||||
- Application name: lcbp3-backend
|
- Application name: lcbp3-backend
|
||||||
- Service: NestJS
|
- Service: NestJS
|
||||||
- Service name: backend
|
- Service name: backend
|
||||||
- Domain: backend.np-dms.work
|
- Domain: backend.np-dms.work
|
||||||
- Framework: NestJS (Node.js, TypeScript, ESM)
|
- Framework: NestJS (Node.js, TypeScript, ESM)
|
||||||
- หน้าที่: จัดการโครงสร้างข้อมูล (Data Models), สร้าง API, จัดการสิทธิ์ผู้ใช้ (Roles & Permissions), และสร้าง Workflow ทั้งหมดของระบบ
|
- หน้าที่: จัดการโครงสร้างข้อมูล (Data Models), สร้าง API, จัดการสิทธิ์ผู้ใช้ (Roles & Permissions), และสร้าง Workflow ทั้งหมดของระบบ
|
||||||
- **2.4. Database:**
|
- **2.4. Database:**
|
||||||
- Application name: lcbp3-db
|
- Application name: lcbp3-db
|
||||||
- Service: mariadb:10.11
|
- Service: mariadb:10.11
|
||||||
- Service name: mariadb
|
- Service name: mariadb
|
||||||
- Domain: db.np-dms.work
|
- Domain: db.np-dms.work
|
||||||
- หน้าที่: ฐานข้อมูลหลักสำหรับเก็บข้อมูลทั้งหมด
|
- หน้าที่: ฐานข้อมูลหลักสำหรับเก็บข้อมูลทั้งหมด
|
||||||
- Tooling: DBeaver (Community Edition), phpmyadmin สำหรับการออกแบบและจัดการฐานข้อมูล
|
- Tooling: DBeaver (Community Edition), phpmyadmin สำหรับการออกแบบและจัดการฐานข้อมูล
|
||||||
- **2.5. Database management:**
|
- **2.5. Database management:**
|
||||||
- Application name: lcbp3-db
|
- Application name: lcbp3-db
|
||||||
- Service: phpmyadmin:5-apache
|
- Service: phpmyadmin:5-apache
|
||||||
- Service name: pma
|
- Service name: pma
|
||||||
- Domain: pma.np-dms.work
|
- Domain: pma.np-dms.work
|
||||||
- หน้าที่: จัดการฐานข้อมูล mariadb ผ่าน Web UI
|
- หน้าที่: จัดการฐานข้อมูล mariadb ผ่าน Web UI
|
||||||
- **2.6. Frontend:**
|
- **2.6. Frontend:**
|
||||||
- Application name: lcbp3-frontend
|
- Application name: lcbp3-frontend
|
||||||
- Service: next.js
|
- Service: next.js
|
||||||
- Service name: frontend
|
- Service name: frontend
|
||||||
- Domain: lcbp3.np-dms.work
|
- Domain: lcbp3.np-dms.work
|
||||||
- Framework: Next.js (App Router, React, TypeScript, ESM)
|
- Framework: Next.js (App Router, React, TypeScript, ESM)
|
||||||
- Styling: Tailwind CSS + PostCSS
|
- Styling: Tailwind CSS + PostCSS
|
||||||
- Component Library: shadcn/ui
|
- Component Library: shadcn/ui
|
||||||
- หน้าที่: สร้างหน้าตาเว็บแอปพลิเคชันสำหรับให้ผู้ใช้งานเข้ามาดู Dashboard, จัดการเอกสาร, และติดตามงาน โดยจะสื่อสารกับ Backend ผ่าน API
|
- หน้าที่: สร้างหน้าตาเว็บแอปพลิเคชันสำหรับให้ผู้ใช้งานเข้ามาดู Dashboard, จัดการเอกสาร, และติดตามงาน โดยจะสื่อสารกับ Backend ผ่าน API
|
||||||
- **2.7. Workflow automation:**
|
- **2.7. Workflow automation:**
|
||||||
- Application name: lcbp3-n8n
|
- Application name: lcbp3-n8n
|
||||||
- Service: n8nio/n8n:latest
|
- Service: n8nio/n8n:latest
|
||||||
- Service name: n8n
|
- Service name: n8n
|
||||||
- Domain: n8n.np-dms.work
|
- Domain: n8n.np-dms.work
|
||||||
- หน้าที่: จัดการ workflow ระหว่าง Backend และ Line
|
- หน้าที่: จัดการ workflow ระหว่าง Backend และ Line
|
||||||
- **2.8. Reverse Proxy:**
|
- **2.8. Reverse Proxy:**
|
||||||
- Application name: lcbp3-npm
|
- Application name: lcbp3-npm
|
||||||
- Service: Nginx Proxy Manager (nginx-proxy-manage: latest)
|
- Service: Nginx Proxy Manager (nginx-proxy-manage: latest)
|
||||||
- Service name: npm
|
- Service name: npm
|
||||||
- Domain: npm.np-dms.work
|
- Domain: npm.np-dms.work
|
||||||
- หน้าที่: เป็นด่านหน้าในการรับ-ส่งข้อมูล จัดการโดเมนทั้งหมด, ทำหน้าที่เป็น Proxy ชี้ไปยัง Service ที่ถูกต้อง, และจัดการ SSL Certificate (HTTPS) ให้อัตโนมัติ
|
- หน้าที่: เป็นด่านหน้าในการรับ-ส่งข้อมูล จัดการโดเมนทั้งหมด, ทำหน้าที่เป็น Proxy ชี้ไปยัง Service ที่ถูกต้อง, และจัดการ SSL Certificate (HTTPS) ให้อัตโนมัติ
|
||||||
- **2.9. การจัดการตรรกะทางธุรกิจ (Business Logic Implementation):**
|
- **2.9. การจัดการตรรกะทางธุรกิจ (Business Logic Implementation):**
|
||||||
- 2.9.1. ตรรกะทางธุรกิจที่ซับซ้อนทั้งหมด (เช่น การเปลี่ยนสถานะ Workflow [cite: 3.5.4, 3.6.5], การบังคับใช้สิทธิ์ [cite: 4.4], การตรวจสอบ Deadline [cite: 3.2.5]) **จะถูกจัดการในฝั่ง Backend (NestJS)** [cite: 2.3] เพื่อให้สามารถบำรุงรักษาและทดสอบได้ง่าย (Testability)
|
- 2.9.1. ตรรกะทางธุรกิจที่ซับซ้อนทั้งหมด (เช่น การเปลี่ยนสถานะ Workflow [cite: 3.5.4, 3.6.5], การบังคับใช้สิทธิ์ [cite: 4.4], การตรวจสอบ Deadline [cite: 3.2.5]) **จะถูกจัดการในฝั่ง Backend (NestJS)** [cite: 2.3] เพื่อให้สามารถบำรุงรักษาและทดสอบได้ง่าย (Testability)
|
||||||
- 2.9.2. **จะไม่มีการใช้ SQL Triggers** เพื่อป้องกันตรรกะซ่อนเร้น (Hidden Logic) และความซับซ้อนในการดีบัก
|
- 2.9.2. **จะไม่มีการใช้ SQL Triggers** เพื่อป้องกันตรรกะซ่อนเร้น (Hidden Logic) และความซับซ้อนในการดีบัก
|
||||||
- 2.9.3. **ข้อยกเว้น:** ตรรกะเดียวที่จะอยู่ในฐานข้อมูลคือ **Stored Procedure** สำหรับการสร้างเลขที่เอกสาร (Document Numbering) [cite: 3.10] เพื่อป้องกันการซ้ำซ้อนของข้อมูล (Race Condition)
|
- 2.9.3. **ข้อยกเว้น:** ตรรกะเดียวที่จะอยู่ในฐานข้อมูลคือ **Stored Procedure** สำหรับการสร้างเลขที่เอกสาร (Document Numbering) [cite: 3.10] เพื่อป้องกันการซ้ำซ้อนของข้อมูล (Race Condition)
|
||||||
|
|
||||||
## **📦 3. ข้อกำหนดด้านฟังก์ชันการทำงาน (Functional Requirements)**
|
## **📦 3. ข้อกำหนดด้านฟังก์ชันการทำงาน (Functional Requirements)**
|
||||||
|
|
||||||
- **3.1. การจัดการโครงสร้างโครงการและองค์กร**
|
- **3.1. การจัดการโครงสร้างโครงการและองค์กร**
|
||||||
- 3.1.1. โครงการ (Projects): ระบบต้องสามารถจัดการเอกสารภายในหลายโครงการได้ (ปัจจุบันมี 4 โครงการ และจะเพิ่มขึ้นในอนาคต)
|
- 3.1.1. โครงการ (Projects): ระบบต้องสามารถจัดการเอกสารภายในหลายโครงการได้ (ปัจจุบันมี 4 โครงการ และจะเพิ่มขึ้นในอนาคต)
|
||||||
- 3.1.2. สัญญา (Contracts): ระบบต้องสามารถจัดการเอกสารภายในแต่ละสัญญาได้ ในแต่ละโครงการ มีได้หลายสัญญา หรืออย่างน้อย 1 สัญญา
|
- 3.1.2. สัญญา (Contracts): ระบบต้องสามารถจัดการเอกสารภายในแต่ละสัญญาได้ ในแต่ละโครงการ มีได้หลายสัญญา หรืออย่างน้อย 1 สัญญา
|
||||||
- 3.1.3. องค์กร (Organizations):
|
- 3.1.3. องค์กร (Organizations):
|
||||||
- มีหลายองค์กรในโครงการ องค์กรณ์ที่เป็น Owner, Designer และ Consultant สามารถอยู่ในหลายโครงการและหลายสัญญาได้
|
- มีหลายองค์กรในโครงการ องค์กรณ์ที่เป็น Owner, Designer และ Consultant สามารถอยู่ในหลายโครงการและหลายสัญญาได้
|
||||||
- Contractor จะถือ 1 สัญญา และอยู่ใน 1 โครงการเท่านั้น
|
- Contractor จะถือ 1 สัญญา และอยู่ใน 1 โครงการเท่านั้น
|
||||||
- **3.2. การจัดการเอกสารโต้ตอบ (Correspondence Management)**
|
- **3.2. การจัดการเอกสารโต้ตอบ (Correspondence Management)**
|
||||||
- 3.2.1. วัตถุประสงค์: เอกสารโต้ตอบ (correspondences) ระหว่างองกรณื-องกรณ์ ภายใน โครงการ (Projects) และระหว่าง องค์กร-องค์กร ภายนอก โครงการ (Projects), รองรับ To (ผู้รับหลัก) และ CC (ผู้รับสำเนา) หลายองค์กร
|
- 3.2.1. วัตถุประสงค์: เอกสารโต้ตอบ (correspondences) ระหว่างองกรณื-องกรณ์ ภายใน โครงการ (Projects) และระหว่าง องค์กร-องค์กร ภายนอก โครงการ (Projects), รองรับ To (ผู้รับหลัก) และ CC (ผู้รับสำเนา) หลายองค์กร
|
||||||
- 3.2.2. ประเภทเอกสาร: ระบบต้องรองรับเอกสารรูปแบบ ไฟล์ PDF หลายประเภท (Types) เช่น จดหมาย (Letter), อีเมล์ (Email), Request for Information (RFI), และสามารถเพิ่มประเภทใหม่ได้ในภายหลัง
|
- 3.2.2. ประเภทเอกสาร: ระบบต้องรองรับเอกสารรูปแบบ ไฟล์ PDF หลายประเภท (Types) เช่น จดหมาย (Letter), อีเมล์ (Email), Request for Information (RFI), และสามารถเพิ่มประเภทใหม่ได้ในภายหลัง
|
||||||
- 3.2.3. การสร้างเอกสาร (Correspondence):
|
- 3.2.3. การสร้างเอกสาร (Correspondence):
|
||||||
- ผู้ใช้ที่มีสิทธิ์ (เช่น Document Control) สามารถสร้างเอกสารรอไว้ในสถานะ ฉบับร่าง" (Draft) ได้ ซึ่งผู้ใช้งานต่างองค์กรจะมองไม่เห็น
|
- ผู้ใช้ที่มีสิทธิ์ (เช่น Document Control) สามารถสร้างเอกสารรอไว้ในสถานะ ฉบับร่าง" (Draft) ได้ ซึ่งผู้ใช้งานต่างองค์กรจะมองไม่เห็น
|
||||||
- เมื่อกด "Submitted" แล้ว การแก้ไข, ถอนเอกสารกลับไปสถานะ Draft, หรือยกเลิก (Cancel) จะต้องทำโดยผู้ใช้ระดับ Admin ขึ้นไป พร้อมระบุเหตุผล
|
- เมื่อกด "Submitted" แล้ว การแก้ไข, ถอนเอกสารกลับไปสถานะ Draft, หรือยกเลิก (Cancel) จะต้องทำโดยผู้ใช้ระดับ Admin ขึ้นไป พร้อมระบุเหตุผล
|
||||||
- 3.2.4. การอ้างอิงและจัดกลุ่ม:
|
- 3.2.4. การอ้างอิงและจัดกลุ่ม:
|
||||||
- เอกสารสามารถอ้างถึง (Reference) เอกสารฉบับก่อนหน้าได้หลายฉบับ
|
- เอกสารสามารถอ้างถึง (Reference) เอกสารฉบับก่อนหน้าได้หลายฉบับ
|
||||||
- สามารถกำหนด Tag ได้หลาย Tag เพื่อจัดกลุ่มและใช้ในการค้นหาขั้นสูง
|
- สามารถกำหนด Tag ได้หลาย Tag เพื่อจัดกลุ่มและใช้ในการค้นหาขั้นสูง
|
||||||
- 3.2.5. Routings : ต้องรองรับกระบวนการส่งต่อเอกสาร (Routing) ตามลำดับ เช่น
|
- 3.2.5. Routings : ต้องรองรับกระบวนการส่งต่อเอกสาร (Routing) ตามลำดับ เช่น
|
||||||
- ส่งจาก Originator -> Organization 1 -> Organization 2 -> Organization 3 แล้วส่งผลกลับตามลำดับเดิม (โดยถ้า องกรณ์ใดใน Wouting ให้ส่งกลับ ก็สามารถส่งผลกลับตามลำดับเดิมโดยไม่ต้องรอให้ถึง องกรณืในลำดับถัดไป)
|
- ส่งจาก Originator -> Organization 1 -> Organization 2 -> Organization 3 แล้วส่งผลกลับตามลำดับเดิม (โดยถ้า องกรณ์ใดใน Wouting ให้ส่งกลับ ก็สามารถส่งผลกลับตามลำดับเดิมโดยไม่ต้องรอให้ถึง องกรณืในลำดับถัดไป)
|
||||||
- 3.2.6. การจัดการ: มีการจัดการอย่างน้อยดังนี้
|
- 3.2.6. การจัดการ: มีการจัดการอย่างน้อยดังนี้
|
||||||
- สามารถกำหนดวันแล้วเสร็จ (Deadline) สำหรับผู้รับผิดชอบของ องกรณ์ ที่เป็นผู้รับได้
|
- สามารถกำหนดวันแล้วเสร็จ (Deadline) สำหรับผู้รับผิดชอบของ องกรณ์ ที่เป็นผู้รับได้
|
||||||
- มีระบบแจ้งเตือน ให้ผู้รับผิดชอบขององกรณ์ที่เป็น ผู้รับ/ผู้ส่ง ทราบ เมื่อมีเอกสารใหม่ หรือมีการเปลี่ยนสถานะ
|
- มีระบบแจ้งเตือน ให้ผู้รับผิดชอบขององกรณ์ที่เป็น ผู้รับ/ผู้ส่ง ทราบ เมื่อมีเอกสารใหม่ หรือมีการเปลี่ยนสถานะ
|
||||||
- **3.3. การจัดกาแบบคู่สัญญา (Contract Drawing)**
|
- **3.3. การจัดกาแบบคู่สัญญา (Contract Drawing)**
|
||||||
- 3.3.1. วัตถุประสงค์: แบบคู่สัญญา (Contract Drawing) ใช้เพื่ออ้างอิงและใช้ในการตรวจสอบ
|
- 3.3.1. วัตถุประสงค์: แบบคู่สัญญา (Contract Drawing) ใช้เพื่ออ้างอิงและใช้ในการตรวจสอบ
|
||||||
- 3.3.2. ประเภทเอกสาร: ไฟล์ PDF
|
- 3.3.2. ประเภทเอกสาร: ไฟล์ PDF
|
||||||
- 3.3.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้
|
- 3.3.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้
|
||||||
- 3.3.4. การอ้างอิงและจัดกลุ่ม: ใช้สำหรับอ้างอิง ใน Shop Drawings, มีการจัดหมวดหมู่ของ Contract Drawing
|
- 3.3.4. การอ้างอิงและจัดกลุ่ม: ใช้สำหรับอ้างอิง ใน Shop Drawings, มีการจัดหมวดหมู่ของ Contract Drawing
|
||||||
- **3.4. การจัดกาแบบก่อสร้าง (Shop Drawing)**
|
- **3.4. การจัดกาแบบก่อสร้าง (Shop Drawing)**
|
||||||
- 3.4.1. วัตถุประสงค์: แบบก่อสร้าง (Shop Drawing) ใช้เในการตรวจสอบ โดยจัดส่งด้วย Request for Approval (RFA)
|
- 3.4.1. วัตถุประสงค์: แบบก่อสร้าง (Shop Drawing) ใช้เในการตรวจสอบ โดยจัดส่งด้วย Request for Approval (RFA)
|
||||||
- 3.4.2. ประเภทเอกสาร: ไฟล์ PDF
|
- 3.4.2. ประเภทเอกสาร: ไฟล์ PDF
|
||||||
- 3.4.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้
|
- 3.4.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้
|
||||||
- 3.4.4. การอ้างอิงและจัดกลุ่ม: ช้สำหรับอ้างอิง ใน Shop Drawings, มีการจัดหมวดหมู่ของ Shop Drawings
|
- 3.4.4. การอ้างอิงและจัดกลุ่ม: ช้สำหรับอ้างอิง ใน Shop Drawings, มีการจัดหมวดหมู่ของ Shop Drawings
|
||||||
- **3.5. การจัดการเอกสารขออนุมัติ (Request for Approval & Workflow)**
|
- **3.5. การจัดการเอกสารขออนุมัติ (Request for Approval & Workflow)**
|
||||||
- 3.5.1. วัตถุประสงค์: เอกสารขออนุมัติ (Request for Approval) ใช้ในการส่งเอกสารเพิอขออนุมัติ
|
- 3.5.1. วัตถุประสงค์: เอกสารขออนุมัติ (Request for Approval) ใช้ในการส่งเอกสารเพิอขออนุมัติ
|
||||||
- 3.5.2. ประเภทเอกสาร: Request for Approval (RFA) เป็นชนิดหนึ่งของ Correspondence ที่มีลักษณะเฉพาะที่ต้องได้รับการอนุมัติ มีประเภทดังนี้:
|
- 3.5.2. ประเภทเอกสาร: Request for Approval (RFA) เป็นชนิดหนึ่งของ Correspondence ที่มีลักษณะเฉพาะที่ต้องได้รับการอนุมัติ มีประเภทดังนี้:
|
||||||
- Request for Drawing Approval (RFA_DWG)
|
- Request for Drawing Approval (RFA_DWG)
|
||||||
- Request for Document Approval (RFA_DOC)
|
- Request for Document Approval (RFA_DOC)
|
||||||
- Request for Method statement Approval (RFA_MES)
|
- Request for Method statement Approval (RFA_MES)
|
||||||
- Request for Material Approval (RFA_MAT)
|
- Request for Material Approval (RFA_MAT)
|
||||||
- 3.5.2. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้
|
- 3.5.2. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้
|
||||||
- 3.5.4. การอ้างอิงและจัดกลุ่ม: การจัดการ Drawing (RFA_DWG):
|
- 3.5.4. การอ้างอิงและจัดกลุ่ม: การจัดการ Drawing (RFA_DWG):
|
||||||
- เอกสาร RFA_DWG จะประกอบไปด้วย Shop Drawing (shop_drawings) หลายแผ่น ซึ่งแต่ละแผ่นมี Revision ของตัวเอง
|
- เอกสาร RFA_DWG จะประกอบไปด้วย Shop Drawing (shop_drawings) หลายแผ่น ซึ่งแต่ละแผ่นมี Revision ของตัวเอง
|
||||||
- Shop Drawing แต่ละ Revision สามารถอ้างอิงถึง Contract Drawing (Ccontract_drawings) หลายแผ่น หรือไม่อ้างถึงก็ได้
|
- Shop Drawing แต่ละ Revision สามารถอ้างอิงถึง Contract Drawing (Ccontract_drawings) หลายแผ่น หรือไม่อ้างถึงก็ได้
|
||||||
- ระบบต้องมีส่วนสำหรับจัดการข้อมูล Master Data ของทั้ง Shop Drawing และ Contract Drawing แยกจากกัน
|
- ระบบต้องมีส่วนสำหรับจัดการข้อมูล Master Data ของทั้ง Shop Drawing และ Contract Drawing แยกจากกัน
|
||||||
- 3.6.5. Workflow การอนุมัติ: ต้องรองรับกระบวนการอนุมัติที่ซับซ้อนและเป็นลำดับ เช่น
|
- 3.6.5. Workflow การอนุมัติ: ต้องรองรับกระบวนการอนุมัติที่ซับซ้อนและเป็นลำดับ เช่น
|
||||||
- ส่งจาก Originator -> Organization 1 -> Organization 2 -> Organization 3 แล้วส่งผลกลับตามลำดับเดิม (โดยถ้า องกรณ์ใดใน Workflow ให้ส่งกลับ ก็สามารถส่งผลกลับตามลำดับเดิมโดยไม่ต้องรอให้ถึง องกรณืในลำดับถัดไป)
|
- ส่งจาก Originator -> Organization 1 -> Organization 2 -> Organization 3 แล้วส่งผลกลับตามลำดับเดิม (โดยถ้า องกรณ์ใดใน Workflow ให้ส่งกลับ ก็สามารถส่งผลกลับตามลำดับเดิมโดยไม่ต้องรอให้ถึง องกรณืในลำดับถัดไป)
|
||||||
- 3.6.6. การจัดการ: มีการจัดการอย่างน้อยดังนี้
|
- 3.6.6. การจัดการ: มีการจัดการอย่างน้อยดังนี้
|
||||||
- สามารถกำหนดวันแล้วเสร็จ (Deadline) สำหรับผู้รับผิดชอบของ องกรณ์ ที่อยู่ใน Workflow ได้
|
- สามารถกำหนดวันแล้วเสร็จ (Deadline) สำหรับผู้รับผิดชอบของ องกรณ์ ที่อยู่ใน Workflow ได้
|
||||||
- มีระบบแจ้งเตือน ให้ผู้รับผิดชอบของ องกรณ์ ที่อยู่ใน Workflow ทราบ เมื่อมี RFA ใหม่ หรือมีการเปลี่ยนสถานะ
|
- มีระบบแจ้งเตือน ให้ผู้รับผิดชอบของ องกรณ์ ที่อยู่ใน Workflow ทราบ เมื่อมี RFA ใหม่ หรือมีการเปลี่ยนสถานะ
|
||||||
- **3.6.การจัดการเอกสารนำส่ง (Transmittals)**
|
- **3.6.การจัดการเอกสารนำส่ง (Transmittals)**
|
||||||
- 3.6.1. วัตถุประสงค์: เอกสารนำส่ง ใช้สำหรับ นำส่ง Request for Approval (RFAS) หลายฉบับ ไปยังองค์กรอื่น
|
- 3.6.1. วัตถุประสงค์: เอกสารนำส่ง ใช้สำหรับ นำส่ง Request for Approval (RFAS) หลายฉบับ ไปยังองค์กรอื่น
|
||||||
- 3.6.2. ประเภทเอกสาร: ไฟล์ PDF
|
- 3.6.2. ประเภทเอกสาร: ไฟล์ PDF
|
||||||
- 3.6.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้
|
- 3.6.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ สามารถสร้างและแก้ไขได้
|
||||||
- 3.6.4. การอ้างอิงและจัดกลุ่ม: เอกสารนำส่ง เป็นส่วนหนึ่งใน Correspondence
|
- 3.6.4. การอ้างอิงและจัดกลุ่ม: เอกสารนำส่ง เป็นส่วนหนึ่งใน Correspondence
|
||||||
- **3.7. ใบเวียนเอกสาร (Circulation Sheet)**
|
- **3.7. ใบเวียนเอกสาร (Circulation Sheet)**
|
||||||
- 3.7.1. วัตถุประสงค์: การสื่อสาร เอกสาร (Correspondence) ทุกฉบับ จะมีใบเวียนเอกสารเพื่อควบคุมและมอบหมายงานภายในองค์กร (สามารถดูและแก้ไขได้เฉพาะคนในองค์กร)
|
- 3.7.1. วัตถุประสงค์: การสื่อสาร เอกสาร (Correspondence) ทุกฉบับ จะมีใบเวียนเอกสารเพื่อควบคุมและมอบหมายงานภายในองค์กร (สามารถดูและแก้ไขได้เฉพาะคนในองค์กร)
|
||||||
- 3.7.2. ประเภทเอกสาร: ไฟล์ PDF
|
- 3.7.2. ประเภทเอกสาร: ไฟล์ PDF
|
||||||
- 3.7.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ในองค์กรนั้น สามารถสร้างและแก้ไขได้
|
- 3.7.3. การสร้างเอกสาร: ผู้ใช้ที่มีสิทธิ์ในองค์กรนั้น สามารถสร้างและแก้ไขได้
|
||||||
- 3.7.4. การอ้างอิงและจัดกลุ่ม: การระบุผู้รับผิดชอบ:
|
- 3.7.4. การอ้างอิงและจัดกลุ่ม: การระบุผู้รับผิดชอบ:
|
||||||
- ผู้รับผิดชอบหลัก (Main): มีได้หลายคน
|
- ผู้รับผิดชอบหลัก (Main): มีได้หลายคน
|
||||||
- ผู้ร่วมปฏิบัติงาน (Action): มีได้หลายคน
|
- ผู้ร่วมปฏิบัติงาน (Action): มีได้หลายคน
|
||||||
- ผู้ที่ต้องรับทราบ (Information): มีได้หลายคน
|
- ผู้ที่ต้องรับทราบ (Information): มีได้หลายคน
|
||||||
- 3.7.5. การติดตามงาน:
|
- 3.7.5. การติดตามงาน:
|
||||||
- สามารถกำหนดวันแล้วเสร็จ (Deadline) สำหรับผู้รับผิดชอบประเภท Main และ Action ได้
|
- สามารถกำหนดวันแล้วเสร็จ (Deadline) สำหรับผู้รับผิดชอบประเภท Main และ Action ได้
|
||||||
- มีระบบแจ้งเตือนเมื่อมี Circulation ใหม่ และแจ้งเตือนล่วงหน้าก่อนถึงวันแล้วเสร็จ
|
- มีระบบแจ้งเตือนเมื่อมี Circulation ใหม่ และแจ้งเตือนล่วงหน้าก่อนถึงวันแล้วเสร็จ
|
||||||
- สามารถปิด Circulation ได้เมื่อดำเนินการตอบกลับไปยังองค์กรผู้ส่ง (Originator) แล้ว หรือ รับทราบแล้ว (For Information)
|
- สามารถปิด Circulation ได้เมื่อดำเนินการตอบกลับไปยังองค์กรผู้ส่ง (Originator) แล้ว หรือ รับทราบแล้ว (For Information)
|
||||||
- **3.8. ประวัติการแก้ไข (Revisions):** ระบบจะเก็บประวัติการสร้างและแก้ไข เอกสารทั้งหมด
|
- **3.8. ประวัติการแก้ไข (Revisions):** ระบบจะเก็บประวัติการสร้างและแก้ไข เอกสารทั้งหมด
|
||||||
- **3.9. การจัดเก็บ: (ปรับปรุงตามสถาปัตยกรรมใหม่)**
|
- **3.9. การจัดเก็บ: (ปรับปรุงตามสถาปัตยกรรมใหม่)**
|
||||||
- เอกสารและไฟล์แนบทั้งหมดจะถูกจัดเก็บในโฟลเดอร์บน Server (/share/dms-data/) [cite: 2.1]
|
- เอกสารและไฟล์แนบทั้งหมดจะถูกจัดเก็บในโฟลเดอร์บน Server (/share/dms-data/) [cite: 2.1]
|
||||||
- ข้อมูล Metadata ของไฟล์ (เช่น ชื่อไฟล์, ขนาด, path) จะถูกเก็บในตาราง attachments (ตารางกลาง)
|
- ข้อมูล Metadata ของไฟล์ (เช่น ชื่อไฟล์, ขนาด, path) จะถูกเก็บในตาราง attachments (ตารางกลาง)
|
||||||
- ไฟล์จะถูกเชื่อมโยงกับเอกสารประเภทต่างๆ ผ่านตารางเชื่อม (Junction tables) เช่น correspondence_attachments, circulation_attachments, shop_drawing_revision_attachments ,และ contracy_drawing_attachments
|
- ไฟล์จะถูกเชื่อมโยงกับเอกสารประเภทต่างๆ ผ่านตารางเชื่อม (Junction tables) เช่น correspondence_attachments, circulation_attachments, shop_drawing_revision_attachments ,และ contracy_drawing_attachments
|
||||||
- สถาปัตยกรรมแบบรวมศูนย์นี้ _แทนที่_ แนวคิดเดิมที่จะแยกโฟลเดอร์ตามประเภทเอกสาร เพื่อรองรับการขยายระบบที่ดีกว่า
|
- สถาปัตยกรรมแบบรวมศูนย์นี้ _แทนที่_ แนวคิดเดิมที่จะแยกโฟลเดอร์ตามประเภทเอกสาร เพื่อรองรับการขยายระบบที่ดีกว่า
|
||||||
- **3.10. การจัดการเลขที่เอกสาร (Document Numbering):**
|
- **3.10. การจัดการเลขที่เอกสาร (Document Numbering):**
|
||||||
- 3.10.1. ระบบต้องสามารถสร้างเลขที่เอกสาร (เช่น correspondence_number) ได้โดยอัตโนมัติ
|
- 3.10.1. ระบบต้องสามารถสร้างเลขที่เอกสาร (เช่น correspondence_number) ได้โดยอัตโนมัติ
|
||||||
- 3.10.2. การนับเลข Running Number (SEQ) จะต้องนับแยกตาม Key ดังนี้: **โครงการ (Project)**, **องค์กรผู้ส่ง (Originator Organization)**, **ประเภทเอกสาร (Document Type)** และ **ปีปัจจุบัน (Year)**
|
- 3.10.2. การนับเลข Running Number (SEQ) จะต้องนับแยกตาม Key ดังนี้: **โครงการ (Project)**, **องค์กรผู้ส่ง (Originator Organization)**, **ประเภทเอกสาร (Document Type)** และ **ปีปัจจุบัน (Year)**
|
||||||
- 3.10.3. ผู้ดูแลระบบ (Admin) ต้องสามารถกำหนด "รูปแบบ" (Format Template) ของเลขที่เอกสารได้ (เช่น {ORG_CODE}-{TYPE_CODE}-{YEAR_SHORT}-{SEQ:4}) โดยกำหนดแยกตามโครงการและประเภทเอกสาร
|
- 3.10.3. ผู้ดูแลระบบ (Admin) ต้องสามารถกำหนด "รูปแบบ" (Format Template) ของเลขที่เอกสารได้ (เช่น {ORG_CODE}-{TYPE_CODE}-{YEAR_SHORT}-{SEQ:4}) โดยกำหนดแยกตามโครงการและประเภทเอกสาร
|
||||||
|
|
||||||
## **🔐 4. ข้อกำหนดด้านสิทธิ์และการเข้าถึง (Access Control Requirements)**
|
## **🔐 4. ข้อกำหนดด้านสิทธิ์และการเข้าถึง (Access Control Requirements)**
|
||||||
|
|
||||||
- **4.1. ภาพรวม:** ผู้ใช้และองค์กรสามารถดูและแก้ไขเอกสารได้ตามสิทธิ์ที่ได้รับ โดยระบบสิทธิ์จะเป็นแบบ Role-Based Access Control (RBAC)
|
- **4.1. ภาพรวม:** ผู้ใช้และองค์กรสามารถดูและแก้ไขเอกสารได้ตามสิทธิ์ที่ได้รับ โดยระบบสิทธิ์จะเป็นแบบ Role-Based Access Control (RBAC)
|
||||||
- **4.2. ลำดับชั้นของสิทธิ์ (Permission Hierarchy)**
|
- **4.2. ลำดับชั้นของสิทธิ์ (Permission Hierarchy)**
|
||||||
|
|
||||||
- Global: สิทธิ์สูงสุดของระบบ
|
- Global: สิทธิ์สูงสุดของระบบ
|
||||||
- Organization: สิทธิ์ภายในองค์กร เป็นสิทธิ์พื้นฐานของผู้ใช้
|
- Organization: สิทธิ์ภายในองค์กร เป็นสิทธิ์พื้นฐานของผู้ใช้
|
||||||
- Project: สิทธิ์เฉพาะในโครงการ จะถูกพิจารณาเมื่อผู้ใช้อยู่ในโครงการนั้น
|
- Project: สิทธิ์เฉพาะในโครงการ จะถูกพิจารณาเมื่อผู้ใช้อยู่ในโครงการนั้น
|
||||||
- Contract: สิทธิ์เฉพาะในสัญญา จะถูกพิจารณาเมื่อผู้ใช้อยู่ในสัญญานั้น (สัญญาเป็นส่วนหนึ่งของโครงการ)
|
- Contract: สิทธิ์เฉพาะในสัญญา จะถูกพิจารณาเมื่อผู้ใช้อยู่ในสัญญานั้น (สัญญาเป็นส่วนหนึ่งของโครงการ)
|
||||||
|
|
||||||
กฎการบังคับใช้: เมื่อตรวจสอบสิทธิ์ ระบบจะพิจารณาสิทธิ์จากทุกระดับที่ผู้ใช้มี และใช้ สิทธิ์ที่มากที่สุด (Most Permissive) เป็นตัวตัดสิน
|
กฎการบังคับใช้: เมื่อตรวจสอบสิทธิ์ ระบบจะพิจารณาสิทธิ์จากทุกระดับที่ผู้ใช้มี และใช้ สิทธิ์ที่มากที่สุด (Most Permissive) เป็นตัวตัดสิน
|
||||||
|
|
||||||
ตัวอย่าง: ผู้ใช้ A เป็น Viewer ในองค์กร แต่ถูกมอบหมายเป็น Editor ในโครงการ X เมื่ออยู่ในโครงการ X ผู้ใช้ A จะมีสิทธิ์แก้ไขได้
|
ตัวอย่าง: ผู้ใช้ A เป็น Viewer ในองค์กร แต่ถูกมอบหมายเป็น Editor ในโครงการ X เมื่ออยู่ในโครงการ X ผู้ใช้ A จะมีสิทธิ์แก้ไขได้
|
||||||
|
|
||||||
- **4.3. การกำหนดบทบาท (Roles) และขอบเขต (Scope)**
|
- **4.3. การกำหนดบทบาท (Roles) และขอบเขต (Scope)**
|
||||||
|
|
||||||
| บทบาท (Role) | ขอบเขต (Scope) | คำอธิบาย | สิทธิ์หลัก (Key Permissions) |
|
| บทบาท (Role) | ขอบเขต (Scope) | คำอธิบาย | สิทธิ์หลัก (Key Permissions) |
|
||||||
| :------------------- | :------------- | :---------------------- | :------------------------------------------------------------------------------------- |
|
| :------------------- | :------------- | :---------------------- | :------------------------------------------------------------------------------------- |
|
||||||
| **Superadmin** | Global | ผู้ดูแลระบบสูงสุด | ทำทุกอย่างในระบบ, จัดการองค์กร, จัดการข้อมูลหลักระดับ Global |
|
| **Superadmin** | Global | ผู้ดูแลระบบสูงสุด | ทำทุกอย่างในระบบ, จัดการองค์กร, จัดการข้อมูลหลักระดับ Global |
|
||||||
| **Org Admin** | Organization | ผู้ดูแลองค์กร | จัดการผู้ใช้ในองค์กร, จัดการบทบาท/สิทธิ์ภายในองค์กร, ดูรายงานขององค์กร |
|
| **Org Admin** | Organization | ผู้ดูแลองค์กร | จัดการผู้ใช้ในองค์กร, จัดการบทบาท/สิทธิ์ภายในองค์กร, ดูรายงานขององค์กร |
|
||||||
| **Document Control** | Organization | ควบคุมเอกสารขององค์กร | เพิ่ม/แก้ไข/ลบเอกสาร, กำหนดสิทธิ์เอกสารภายในองค์กร |
|
| **Document Control** | Organization | ควบคุมเอกสารขององค์กร | เพิ่ม/แก้ไข/ลบเอกสาร, กำหนดสิทธิ์เอกสารภายในองค์กร |
|
||||||
| **Editor** | Organization | ผู้แก้ไขเอกสารขององค์กร | เพิ่ม/แก้ไขเอกสารที่ได้รับมอบหมาย |
|
| **Editor** | Organization | ผู้แก้ไขเอกสารขององค์กร | เพิ่ม/แก้ไขเอกสารที่ได้รับมอบหมาย |
|
||||||
| **Viewer** | Organization | ผู้ดูเอกสารขององค์กร | ดูเอกสารที่มีสิทธิ์เข้าถึง |
|
| **Viewer** | Organization | ผู้ดูเอกสารขององค์กร | ดูเอกสารที่มีสิทธิ์เข้าถึง |
|
||||||
| **Project Manager** | Project | ผู้จัดการโครงการ | จัดการสมาชิกในโครงการ (เพิ่ม/ลบ/มอบบทบาท), สร้าง/จัดการสัญญาในโครงการ, ดูรายงานโครงการ |
|
| **Project Manager** | Project | ผู้จัดการโครงการ | จัดการสมาชิกในโครงการ (เพิ่ม/ลบ/มอบบทบาท), สร้าง/จัดการสัญญาในโครงการ, ดูรายงานโครงการ |
|
||||||
| **Contract Admin** | Contract | ผู้ดูแลสัญญา | จัดการสมาชิกในสัญญา, สร้าง/จัดการข้อมูลหลักเฉพาะสัญญา (ถ้ามี), อนุมัติเอกสารในสัญญา |
|
| **Contract Admin** | Contract | ผู้ดูแลสัญญา | จัดการสมาชิกในสัญญา, สร้าง/จัดการข้อมูลหลักเฉพาะสัญญา (ถ้ามี), อนุมัติเอกสารในสัญญา |
|
||||||
|
|
||||||
- **4.4. กระบวนการเริ่มต้นใช้งาน (Onboarding Workflow) ที่สมบูรณ์**
|
- **4.4. กระบวนการเริ่มต้นใช้งาน (Onboarding Workflow) ที่สมบูรณ์**
|
||||||
|
|
||||||
- 4.1. **สร้างองค์กร (Organization)**
|
- 4.1. **สร้างองค์กร (Organization)**
|
||||||
|
|
||||||
- **Superadmin** สร้างองค์กรใหม่ (เช่น บริษัท A)
|
- **Superadmin** สร้างองค์กรใหม่ (เช่น บริษัท A)
|
||||||
- **Superadmin** แต่งตั้งผู้ใช้อย่างน้อย 1 คนให้เป็น **Org Admin** หรือ **Document Control** ของบริษัท A
|
- **Superadmin** แต่งตั้งผู้ใช้อย่างน้อย 1 คนให้เป็น **Org Admin** หรือ **Document Control** ของบริษัท A
|
||||||
|
|
||||||
- 4.2. **เพิ่มผู้ใช้ในองค์กร**
|
- 4.2. **เพิ่มผู้ใช้ในองค์กร**
|
||||||
|
|
||||||
- **Org Admin** ของบริษัท A เพิ่มผู้ใช้อื่นๆ (Editor, Viewer) เข้ามาในองค์กรของตน
|
- **Org Admin** ของบริษัท A เพิ่มผู้ใช้อื่นๆ (Editor, Viewer) เข้ามาในองค์กรของตน
|
||||||
|
|
||||||
- 4.3. **มอบหมายผู้ใช้ให้กับโครงการ (Project)**
|
- 4.3. **มอบหมายผู้ใช้ให้กับโครงการ (Project)**
|
||||||
|
|
||||||
- **Project Manager** ของโครงการ X (ซึ่งอาจมาจากบริษัท A หรือบริษัทอื่น) ทำการ "เชิญ" หรือ "มอบหมาย" ผู้ใช้จากองค์กรต่างๆ ที่เกี่ยวข้องเข้ามาในโครงการ X
|
- **Project Manager** ของโครงการ X (ซึ่งอาจมาจากบริษัท A หรือบริษัทอื่น) ทำการ "เชิญ" หรือ "มอบหมาย" ผู้ใช้จากองค์กรต่างๆ ที่เกี่ยวข้องเข้ามาในโครงการ X
|
||||||
- ในขั้นตอนนี้ **Project Manager** จะกำหนด **บทบาทระดับโครงการ** (เช่น Project Member, หรืออาจไม่มีบทบาทพิเศษ ให้ใช้สิทธิ์จากระดับองค์กรไปก่อน)
|
- ในขั้นตอนนี้ **Project Manager** จะกำหนด **บทบาทระดับโครงการ** (เช่น Project Member, หรืออาจไม่มีบทบาทพิเศษ ให้ใช้สิทธิ์จากระดับองค์กรไปก่อน)
|
||||||
|
|
||||||
- 4.4. **เมอบหมายผู้ใช้ให้กับสัญญา (Contract)**
|
- 4.4. **เมอบหมายผู้ใช้ให้กับสัญญา (Contract)**
|
||||||
- **Contract Admin** ของสัญญา Y (ซึ่งเป็นส่วนหนึ่งของโครงการ X) ทำการเลือกผู้ใช้ที่อยู่ในโครงการ X แล้ว มอบหมายให้เข้ามาในสัญญา Y
|
- **Contract Admin** ของสัญญา Y (ซึ่งเป็นส่วนหนึ่งของโครงการ X) ทำการเลือกผู้ใช้ที่อยู่ในโครงการ X แล้ว มอบหมายให้เข้ามาในสัญญา Y
|
||||||
- ในขั้นตอนนี้ **Contract Admin** จะกำหนด **บทบาทระดับสัญญา** (เช่น Contract Member) และสิทธิ์เฉพาะที่จำเป็น
|
- ในขั้นตอนนี้ **Contract Admin** จะกำหนด **บทบาทระดับสัญญา** (เช่น Contract Member) และสิทธิ์เฉพาะที่จำเป็น
|
||||||
|
|
||||||
- **4.5. การจัดการข้อมูลหลัก (Master Data Management) ที่แบ่งตามระดับ**
|
- **4.5. การจัดการข้อมูลหลัก (Master Data Management) ที่แบ่งตามระดับ**
|
||||||
|
|
||||||
| ข้อมูลหลัก | ผู้มีสิทธิ์จัดการ | ระดับ |
|
| ข้อมูลหลัก | ผู้มีสิทธิ์จัดการ | ระดับ |
|
||||||
| :---------------------------------- | :------------------------------ | :--------------------------------- |
|
| :---------------------------------- | :------------------------------ | :--------------------------------- |
|
||||||
| ประเภทเอกสาร (Correspondence, RFA) | **Superadmin** | Global |
|
| ประเภทเอกสาร (Correspondence, RFA) | **Superadmin** | Global |
|
||||||
| สถานะเอกสาร (Draft, Approved, etc.) | **Superadmin** | Global |
|
| สถานะเอกสาร (Draft, Approved, etc.) | **Superadmin** | Global |
|
||||||
| หมวดหมู่แบบ (Shop Drawing) | **Project Manager** | Project (สร้างใหม่ได้ภายในโครงการ) |
|
| หมวดหมู่แบบ (Shop Drawing) | **Project Manager** | Project (สร้างใหม่ได้ภายในโครงการ) |
|
||||||
| Tags | **Org Admin / Project Manager** | Organization / Project |
|
| Tags | **Org Admin / Project Manager** | Organization / Project |
|
||||||
| บทบาทและสิทธิ์ (Custom Roles) | **Superadmin / Org Admin** | Global / Organization |
|
| บทบาทและสิทธิ์ (Custom Roles) | **Superadmin / Org Admin** | Global / Organization |
|
||||||
|
|
||||||
## **👥 5. ข้อกำหนดด้านผู้ใช้งาน (User Interface & Experience)**
|
## **👥 5. ข้อกำหนดด้านผู้ใช้งาน (User Interface & Experience)**
|
||||||
|
|
||||||
- **5.1. Layout หลัก:** หน้าเว็บใช้รูปแบบ App Shell ที่ประกอบด้วย:
|
- **5.1. Layout หลัก:** หน้าเว็บใช้รูปแบบ App Shell ที่ประกอบด้วย:
|
||||||
- Navbar (ส่วนบน): แสดงชื่อระบบ, เมนูผู้ใช้ (Profile), เมนูสำหรับ Document Control/เมนูสำหรับ Admin/Superadmin (จัดการผู้ใช้, จัดการสิทธิ์), และปุ่ม Login/Logout
|
- Navbar (ส่วนบน): แสดงชื่อระบบ, เมนูผู้ใช้ (Profile), เมนูสำหรับ Document Control/เมนูสำหรับ Admin/Superadmin (จัดการผู้ใช้, จัดการสิทธิ์), และปุ่ม Login/Logout
|
||||||
- Sidebar (ด้านข้าง): เป็นเมนูหลักสำหรับเข้าถึงส่วนที่เกี่ยวกับเอกสารทั้งหมด เช่น Dashboard, Correspondences, RFA, Drawings
|
- Sidebar (ด้านข้าง): เป็นเมนูหลักสำหรับเข้าถึงส่วนที่เกี่ยวกับเอกสารทั้งหมด เช่น Dashboard, Correspondences, RFA, Drawings
|
||||||
- Main Content Area: พื้นที่สำหรับแสดงเนื้อหาหลักของหน้าที่เลือก
|
- Main Content Area: พื้นที่สำหรับแสดงเนื้อหาหลักของหน้าที่เลือก
|
||||||
- **5.2. หน้า Landing Page:** เป็นหน้าแรกที่แสดงข้อมูลบางส่วนของโครงการสำหรับผู้ใช้ที่ยังไม่ได้ล็อกอิน
|
- **5.2. หน้า Landing Page:** เป็นหน้าแรกที่แสดงข้อมูลบางส่วนของโครงการสำหรับผู้ใช้ที่ยังไม่ได้ล็อกอิน
|
||||||
- **5.3. หน้า Dashboard:** เป็นหน้าแรกหลังจากล็อกอิน ประกอบด้วย:
|
- **5.3. หน้า Dashboard:** เป็นหน้าแรกหลังจากล็อกอิน ประกอบด้วย:
|
||||||
- การ์ดสรุปภาพรวม (KPI Cards): แสดงข้อมูลสรุปที่สำคัญขององค์กร เช่น จำนวนเอกสาร, งานที่เกินกำหนด
|
- การ์ดสรุปภาพรวม (KPI Cards): แสดงข้อมูลสรุปที่สำคัญขององค์กร เช่น จำนวนเอกสาร, งานที่เกินกำหนด
|
||||||
- ตาราง "งานของฉัน" (My Tasks Table): แสดงรายการงานทั้งหมดจาก Circulation ที่ผู้ใช้ต้องดำเนินการ
|
- ตาราง "งานของฉัน" (My Tasks Table): แสดงรายการงานทั้งหมดจาก Circulation ที่ผู้ใช้ต้องดำเนินการ
|
||||||
- **5.4. การติดตามสถานะ:** องค์กรสามารถติดตามสถานะเอกสารทั้งของตนเอง (Originator) และสถานะเอกสารที่ส่งมาถึงตนเอง (Recipient)
|
- **5.4. การติดตามสถานะ:** องค์กรสามารถติดตามสถานะเอกสารทั้งของตนเอง (Originator) และสถานะเอกสารที่ส่งมาถึงตนเอง (Recipient)
|
||||||
- **5.5. การจัดการข้อมูลส่วนตัว (Profile Page):** ผู้ใช้สามารถจัดการข้อมูลส่วนตัวและเปลี่ยนรหัสผ่านของตนเองได้
|
- **5.5. การจัดการข้อมูลส่วนตัว (Profile Page):** ผู้ใช้สามารถจัดการข้อมูลส่วนตัวและเปลี่ยนรหัสผ่านของตนเองได้
|
||||||
- **5.6. การจัดการเอกสารทางเทคนิค (RFA & Workflow):** ผู้ใช้สามารถดู RFA ในรูปแบบ Workflow ทั้งหมดได้ในหน้าเดียว, ขั้นตอนที่ยังไม่ถึงหรือผ่านไปแล้วจะเป็นรูปแบบ diable, สามารถดำเนินการได้เฉพาะในขั้นตอนที่ได้รับมอบหมายงาน (active) เช่น ตรวจสอบแล้ว เพื่อไปยังขั้นตอนต่อไป, สิทธิ์ Document Control ขึ้นไป สามรถกด ไปยังขั้นตอนต่อไป ได้ทุกขั้นตอน, การย้อนกลับ ไปขั้นตอนก่อนหน้า สามารถทำได้โดย สิทธิ์ Document Control ขึ้นไป
|
- **5.6. การจัดการเอกสารทางเทคนิค (RFA & Workflow):** ผู้ใช้สามารถดู RFA ในรูปแบบ Workflow ทั้งหมดได้ในหน้าเดียว, ขั้นตอนที่ยังไม่ถึงหรือผ่านไปแล้วจะเป็นรูปแบบ diable, สามารถดำเนินการได้เฉพาะในขั้นตอนที่ได้รับมอบหมายงาน (active) เช่น ตรวจสอบแล้ว เพื่อไปยังขั้นตอนต่อไป, สิทธิ์ Document Control ขึ้นไป สามรถกด ไปยังขั้นตอนต่อไป ได้ทุกขั้นตอน, การย้อนกลับ ไปขั้นตอนก่อนหน้า สามารถทำได้โดย สิทธิ์ Document Control ขึ้นไป
|
||||||
- **5.7. การจัดการใบเวียนเอกสาร (Circulation):** ผู้ใช้สามารถดู Circulation ในรูปแบบ Workflow ทั้งหมดได้ในหน้าเดียว,ขั้นตอนที่ยังไม่ถึงหรือผ่านไปแล้วจะเป็นรูปแบบ diable, สามารถดำเนินการได้เฉพาะในขั้นตอนที่ได้รับมอบหมายงาน (active) เช่น ตรวจสอบแล้ว เพื่อไปยังขั้นตอนต่อไป, สิทธิ์ Document Control ขึ้นไป สามรถกด ไปยังขั้นตอนต่อไป ได้ทุกขั้นตอน, การย้อนกลับ ไปขั้นตอนก่อนหน้า สามารถทำได้โดย สิทธิ์ Document Control ขึ้นไป
|
- **5.7. การจัดการใบเวียนเอกสาร (Circulation):** ผู้ใช้สามารถดู Circulation ในรูปแบบ Workflow ทั้งหมดได้ในหน้าเดียว,ขั้นตอนที่ยังไม่ถึงหรือผ่านไปแล้วจะเป็นรูปแบบ diable, สามารถดำเนินการได้เฉพาะในขั้นตอนที่ได้รับมอบหมายงาน (active) เช่น ตรวจสอบแล้ว เพื่อไปยังขั้นตอนต่อไป, สิทธิ์ Document Control ขึ้นไป สามรถกด ไปยังขั้นตอนต่อไป ได้ทุกขั้นตอน, การย้อนกลับ ไปขั้นตอนก่อนหน้า สามารถทำได้โดย สิทธิ์ Document Control ขึ้นไป
|
||||||
- **5.8. การจัดการเอกสารนำส่ง (Transmittals):** ผู้ใช้สามารถดู Transmittals ในรูปแบบรายการทั้งหมดได้ในหน้าเดียว
|
- **5.8. การจัดการเอกสารนำส่ง (Transmittals):** ผู้ใช้สามารถดู Transmittals ในรูปแบบรายการทั้งหมดได้ในหน้าเดียว
|
||||||
- **5.9. ข้อกำหนด UI/UX การแนบไฟล์ (File Attachment UX):**
|
- **5.9. ข้อกำหนด UI/UX การแนบไฟล์ (File Attachment UX):**
|
||||||
- ระบบต้องรองรับการอัปโหลดไฟล์หลายไฟล์พร้อมกัน (Multi-file upload) เช่น การลากและวาง (Drag-and-Drop)
|
- ระบบต้องรองรับการอัปโหลดไฟล์หลายไฟล์พร้อมกัน (Multi-file upload) เช่น การลากและวาง (Drag-and-Drop)
|
||||||
- ในหน้าอัปโหลด (เช่น สร้าง RFA หรือ Correspondence) ผู้ใช้ต้องสามารถกำหนดได้ว่าไฟล์ใดเป็น "เอกสารหลัก" (Main Document เช่น PDF) และไฟล์ใดเป็น "เอกสารแนบประกอบ" (Supporting Attachments เช่น .dwg, .docx, .zip)
|
- ในหน้าอัปโหลด (เช่น สร้าง RFA หรือ Correspondence) ผู้ใช้ต้องสามารถกำหนดได้ว่าไฟล์ใดเป็น "เอกสารหลัก" (Main Document เช่น PDF) และไฟล์ใดเป็น "เอกสารแนบประกอบ" (Supporting Attachments เช่น .dwg, .docx, .zip)
|
||||||
|
|
||||||
## **6. ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน (Non-Functional Requirements)**
|
## **6. ข้อกำหนดที่ไม่ใช่ฟังก์ชันการทำงาน (Non-Functional Requirements)**
|
||||||
|
|
||||||
- **6.1. การบันทึกการกระทำ (Audit Log):** ทุกการกระทำที่สำคัญของผู้ใช้ (สร้าง, แก้ไข, ลบ, ส่ง) จะถูกบันทึกไว้ใน audit_logs เพื่อการตรวจสอบย้อนหลัง
|
- **6.1. การบันทึกการกระทำ (Audit Log):** ทุกการกระทำที่สำคัญของผู้ใช้ (สร้าง, แก้ไข, ลบ, ส่ง) จะถูกบันทึกไว้ใน audit_logs เพื่อการตรวจสอบย้อนหลัง
|
||||||
- **6.2. การค้นหา (Search):** ระบบต้องมีฟังก์ชันการค้นหาขั้นสูง ที่สามารถค้นหาเอกสาร **correspondence**, **rfa**, **shop_drawing**, **contract-drawing**, **transmittal** และ **ใบเวียน (Circulations)** จากหลายเงื่อนไขพร้อมกันได้ เช่น ค้นหาจากชื่อเรื่อง, ประเภท, วันที่, และ Tag
|
- **6.2. การค้นหา (Search):** ระบบต้องมีฟังก์ชันการค้นหาขั้นสูง ที่สามารถค้นหาเอกสาร **correspondence**, **rfa**, **shop_drawing**, **contract-drawing**, **transmittal** และ **ใบเวียน (Circulations)** จากหลายเงื่อนไขพร้อมกันได้ เช่น ค้นหาจากชื่อเรื่อง, ประเภท, วันที่, และ Tag
|
||||||
- **6.3. การทำรายงาน (Reporting):** สามารถจัดทำรายงานสรุปแยกประเภทของ Correspondence ประจำวัน, สัปดาห์, เดือน, และปีได้
|
- **6.3. การทำรายงาน (Reporting):** สามารถจัดทำรายงานสรุปแยกประเภทของ Correspondence ประจำวัน, สัปดาห์, เดือน, และปีได้
|
||||||
- **6.4. ประสิทธิภาพ (Performance):** มีการใช้ Caching กับข้อมูลที่เรียกใช้บ่อย และใช้ Pagination ในตารางข้อมูลเพื่อจัดการข้อมูลจำนวนมาก
|
- **6.4. ประสิทธิภาพ (Performance):** มีการใช้ Caching กับข้อมูลที่เรียกใช้บ่อย และใช้ Pagination ในตารางข้อมูลเพื่อจัดการข้อมูลจำนวนมาก
|
||||||
- **6.5. ความปลอดภัย (Security):**
|
- **6.5. ความปลอดภัย (Security):**
|
||||||
- มีระบบ Rate Limiting เพื่อป้องกันการโจมตีแบบ Brute-force
|
- มีระบบ Rate Limiting เพื่อป้องกันการโจมตีแบบ Brute-force
|
||||||
- การจัดการ Secret (เช่น รหัสผ่าน DB, JWT Secret) จะต้องทำผ่าน Environment Variable ของ Docker เพื่อความปลอดภัยสูงสุด
|
- การจัดการ Secret (เช่น รหัสผ่าน DB, JWT Secret) จะต้องทำผ่าน Environment Variable ของ Docker เพื่อความปลอดภัยสูงสุด
|
||||||
- **6.6. การสำรองข้อมูลและการกู้คืน (Backup & Recovery):**
|
- **6.6. การสำรองข้อมูลและการกู้คืน (Backup & Recovery):**
|
||||||
- ระบบจะต้องมีกลไกการสำรองข้อมูลอัตโนมัติสำหรับฐานข้อมูล MariaDB [cite: 2.4] และไฟล์เอกสารทั้งหมดใน /share/dms-data [cite: 2.1] (เช่น ใช้ HBS 3 ของ QNAP หรือสคริปต์สำรองข้อมูล) อย่างน้อยวันละ 1 ครั้ง
|
- ระบบจะต้องมีกลไกการสำรองข้อมูลอัตโนมัติสำหรับฐานข้อมูล MariaDB [cite: 2.4] และไฟล์เอกสารทั้งหมดใน /share/dms-data [cite: 2.1] (เช่น ใช้ HBS 3 ของ QNAP หรือสคริปต์สำรองข้อมูล) อย่างน้อยวันละ 1 ครั้ง
|
||||||
- ต้องมีแผนการกู้คืนระบบ (Disaster Recovery Plan) ในกรณีที่ Server หลัก (QNAP) ใช้งานไม่ได้
|
- ต้องมีแผนการกู้คืนระบบ (Disaster Recovery Plan) ในกรณีที่ Server หลัก (QNAP) ใช้งานไม่ได้
|
||||||
- **6.7. กลยุทธ์การแจ้งเตือน (Notification Strategy):**
|
- **6.7. กลยุทธ์การแจ้งเตือน (Notification Strategy):**
|
||||||
- ระบบจะส่งการแจ้งเตือน (ผ่าน Email หรือ Line [cite: 2.7]) เมื่อมีการกระทำที่สำคัญ ดังนี้:
|
- ระบบจะส่งการแจ้งเตือน (ผ่าน Email หรือ Line [cite: 2.7]) เมื่อมีการกระทำที่สำคัญ ดังนี้:
|
||||||
1. เมื่อมีเอกสารใหม่ (Correspondence, RFA) ถูกส่งมาถึงองค์กรณ์ของเรา
|
1. เมื่อมีเอกสารใหม่ (Correspondence, RFA) ถูกส่งมาถึงองค์กรณ์ของเรา
|
||||||
2. เมื่อมีใบเวียน (Circulation) ใหม่ มอบหมายงานมาที่เรา
|
2. เมื่อมีใบเวียน (Circulation) ใหม่ มอบหมายงานมาที่เรา
|
||||||
3. (ทางเลือก) เมื่อเอกสารที่เราส่งไป ถูกดำเนินการ (เช่น อนุมัติ/ปฏิเสธ)
|
3. (ทางเลือก) เมื่อเอกสารที่เราส่งไป ถูกดำเนินการ (เช่น อนุมัติ/ปฏิเสธ)
|
||||||
4. (ทางเลือก) เมื่อใกล้ถึงวันครบกำหนด (Deadline) [cite: 3.2.5, 3.6.6, 3.7.5]
|
4. (ทางเลือก) เมื่อใกล้ถึงวันครบกำหนด (Deadline) [cite: 3.2.5, 3.6.6, 3.7.5]
|
||||||
Reference in New Issue
Block a user