Files

148 lines
3.8 KiB
JavaScript

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,
};