feat: DH-HIS Web Giao Ban (Node.js & Express) complete codebase
This commit is contained in:
+147
@@ -0,0 +1,147 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const DATA_DIR = path.join(__dirname, 'data');
|
||||
const UPLOADS_DIR = path.join(__dirname, 'uploads', 'issues');
|
||||
const ISSUES_FILE = path.join(DATA_DIR, 'issues.json');
|
||||
|
||||
function ensureDirs() {
|
||||
if (!fs.existsSync(DATA_DIR)) {
|
||||
fs.mkdirSync(DATA_DIR, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(UPLOADS_DIR)) {
|
||||
fs.mkdirSync(UPLOADS_DIR, { recursive: true });
|
||||
}
|
||||
if (!fs.existsSync(ISSUES_FILE)) {
|
||||
fs.writeFileSync(ISSUES_FILE, JSON.stringify([], null, 2), 'utf-8');
|
||||
}
|
||||
}
|
||||
|
||||
function getAllIssues() {
|
||||
ensureDirs();
|
||||
try {
|
||||
const raw = fs.readFileSync(ISSUES_FILE, 'utf-8');
|
||||
const list = JSON.parse(raw);
|
||||
return Array.isArray(list) ? list : [];
|
||||
} catch (err) {
|
||||
console.error('Error reading issues.json:', err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function saveIssues(issues) {
|
||||
ensureDirs();
|
||||
const tmpFile = `${ISSUES_FILE}.tmp`;
|
||||
fs.writeFileSync(tmpFile, JSON.stringify(issues, null, 2), 'utf-8');
|
||||
fs.renameSync(tmpFile, ISSUES_FILE);
|
||||
}
|
||||
|
||||
function generateIssueId() {
|
||||
const now = new Date();
|
||||
const y = now.getFullYear();
|
||||
const m = String(now.getMonth() + 1).padStart(2, '0');
|
||||
const d = String(now.getDate()).padStart(2, '0');
|
||||
const prefix = `ISS-${y}${m}${d}`;
|
||||
|
||||
const all = getAllIssues();
|
||||
const todayCount = all.filter(item => item.id && item.id.startsWith(prefix)).length + 1;
|
||||
const seq = String(todayCount).padStart(3, '0');
|
||||
return `${prefix}-${seq}`;
|
||||
}
|
||||
|
||||
function createIssue(data, file) {
|
||||
ensureDirs();
|
||||
const all = getAllIssues();
|
||||
const id = generateIssueId();
|
||||
const now = new Date().toISOString();
|
||||
|
||||
let attachment = null;
|
||||
if (file) {
|
||||
attachment = {
|
||||
filename: file.filename,
|
||||
originalname: file.originalname,
|
||||
size: file.size,
|
||||
mimetype: file.mimetype,
|
||||
url: `/uploads/issues/${file.filename}`,
|
||||
};
|
||||
}
|
||||
|
||||
const newIssue = {
|
||||
id,
|
||||
title: (data.title || 'Không có tiêu đề').trim(),
|
||||
category: data.category || 'SAI_LECH_SO_LIEU', // SAI_LECH_SO_LIEU, LOI_GIAO_DIEN, LOI_KET_NOI, DE_XUAT, YEU_CAU_MOI, KHAC
|
||||
module: data.module || 'CHUNG', // NGOAI_TRU, NOI_TRU, CHUYEN_VIEN, THEO_DOI, CHUNG
|
||||
priority: data.priority || 'BINH_THUONG', // THAP, BINH_THUONG, CAO, KHAN_CAP
|
||||
database: data.database || '',
|
||||
databaseName: data.databaseName || data.database || '',
|
||||
period: data.period || '',
|
||||
reporter: (data.reporter || 'Cán bộ giao ban').trim(),
|
||||
department: (data.department || '').trim(),
|
||||
description: (data.description || '').trim(),
|
||||
status: 'CHO_XU_LY', // CHO_XU_LY, DANG_XU_LY, DA_XU_LY, DONG
|
||||
handlerNotes: '',
|
||||
resolvedBy: '',
|
||||
resolvedAt: null,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
attachment,
|
||||
};
|
||||
|
||||
all.unshift(newIssue);
|
||||
saveIssues(all);
|
||||
return newIssue;
|
||||
}
|
||||
|
||||
function updateIssue(id, updates) {
|
||||
const all = getAllIssues();
|
||||
const idx = all.findIndex(item => item.id === id);
|
||||
if (idx === -1) {
|
||||
throw new Error(`Không tìm thấy Issue với mã: ${id}`);
|
||||
}
|
||||
|
||||
const now = new Date().toISOString();
|
||||
const cur = all[idx];
|
||||
|
||||
const updated = {
|
||||
...cur,
|
||||
...updates,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
if (updates.status === 'DA_XU_LY' && cur.status !== 'DA_XU_LY') {
|
||||
updated.resolvedAt = now;
|
||||
}
|
||||
|
||||
all[idx] = updated;
|
||||
saveIssues(all);
|
||||
return updated;
|
||||
}
|
||||
|
||||
function deleteIssue(id) {
|
||||
const all = getAllIssues();
|
||||
const idx = all.findIndex(item => item.id === id);
|
||||
if (idx === -1) {
|
||||
throw new Error(`Không tìm thấy Issue với mã: ${id}`);
|
||||
}
|
||||
|
||||
const item = all[idx];
|
||||
if (item.attachment && item.attachment.filename) {
|
||||
const fPath = path.join(UPLOADS_DIR, item.attachment.filename);
|
||||
if (fs.existsSync(fPath)) {
|
||||
try { fs.unlinkSync(fPath); } catch (e) { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
all.splice(idx, 1);
|
||||
saveIssues(all);
|
||||
return true;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
UPLOADS_DIR,
|
||||
ensureDirs,
|
||||
getAllIssues,
|
||||
createIssue,
|
||||
updateIssue,
|
||||
deleteIssue,
|
||||
};
|
||||
Reference in New Issue
Block a user