5개 테이블(risk_projects/processes/items/mitigations/templates) + 마스터 시딩, 프로젝트·항목·감소대책 CRUD API, ExcelJS 평가표 내보내기, 프로젝트 목록·평가 수행 페이지, 사진 업로드(multer), 네비게이션·CSS 추가. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
202 lines
8.2 KiB
JavaScript
202 lines
8.2 KiB
JavaScript
const riskModel = require('../models/riskModel');
|
|
|
|
// ==================== 공정 템플릿 ====================
|
|
|
|
exports.getTemplates = async (req, res) => {
|
|
try {
|
|
const templates = await riskModel.getTemplates(req.query.product_type);
|
|
res.json({ success: true, data: templates });
|
|
} catch (err) {
|
|
console.error('템플릿 조회 오류:', err);
|
|
res.status(500).json({ success: false, error: '템플릿 조회 실패' });
|
|
}
|
|
};
|
|
|
|
// ==================== 프로젝트 CRUD ====================
|
|
|
|
exports.createProject = async (req, res) => {
|
|
try {
|
|
const { title, product_type } = req.body;
|
|
if (!title || !product_type) {
|
|
return res.status(400).json({ success: false, error: '제목과 제품유형은 필수입니다' });
|
|
}
|
|
const data = { ...req.body, created_by: req.user.user_id || req.user.id };
|
|
const projectId = await riskModel.createProject(data);
|
|
res.status(201).json({ success: true, message: '프로젝트가 생성되었습니다', data: { id: projectId } });
|
|
} catch (err) {
|
|
console.error('프로젝트 생성 오류:', err);
|
|
res.status(500).json({ success: false, error: '프로젝트 생성 실패' });
|
|
}
|
|
};
|
|
|
|
exports.getAllProjects = async (req, res) => {
|
|
try {
|
|
const projects = await riskModel.getAllProjects(req.query);
|
|
|
|
// 대시보드 요약
|
|
const summary = {
|
|
total: projects.length,
|
|
risk_distribution: { high: 0, substantial: 0, moderate: 0, low: 0 }
|
|
};
|
|
for (const p of projects) {
|
|
summary.risk_distribution.high += p.high_risk_count || 0;
|
|
}
|
|
|
|
res.json({ success: true, data: { projects, summary } });
|
|
} catch (err) {
|
|
console.error('프로젝트 목록 조회 오류:', err);
|
|
res.status(500).json({ success: false, error: '프로젝트 목록 조회 실패' });
|
|
}
|
|
};
|
|
|
|
exports.getProjectById = async (req, res) => {
|
|
try {
|
|
const project = await riskModel.getProjectById(req.params.id);
|
|
if (!project) return res.status(404).json({ success: false, error: '프로젝트를 찾을 수 없습니다' });
|
|
res.json({ success: true, data: project });
|
|
} catch (err) {
|
|
console.error('프로젝트 상세 조회 오류:', err);
|
|
res.status(500).json({ success: false, error: '프로젝트 상세 조회 실패' });
|
|
}
|
|
};
|
|
|
|
exports.updateProject = async (req, res) => {
|
|
try {
|
|
const result = await riskModel.updateProject(req.params.id, req.body);
|
|
if (result.affectedRows === 0) return res.status(404).json({ success: false, error: '프로젝트를 찾을 수 없습니다' });
|
|
res.json({ success: true, message: '프로젝트가 수정되었습니다' });
|
|
} catch (err) {
|
|
console.error('프로젝트 수정 오류:', err);
|
|
res.status(500).json({ success: false, error: '프로젝트 수정 실패' });
|
|
}
|
|
};
|
|
|
|
exports.deleteProject = async (req, res) => {
|
|
try {
|
|
const result = await riskModel.deleteProject(req.params.id);
|
|
if (result.affectedRows === 0) return res.status(404).json({ success: false, error: '프로젝트를 찾을 수 없습니다' });
|
|
res.json({ success: true, message: '프로젝트가 삭제되었습니다' });
|
|
} catch (err) {
|
|
console.error('프로젝트 삭제 오류:', err);
|
|
res.status(500).json({ success: false, error: '프로젝트 삭제 실패' });
|
|
}
|
|
};
|
|
|
|
// ==================== 세부 공정 ====================
|
|
|
|
exports.addProcess = async (req, res) => {
|
|
try {
|
|
const { process_name } = req.body;
|
|
if (!process_name) return res.status(400).json({ success: false, error: '공정명은 필수입니다' });
|
|
const processId = await riskModel.addProcess(req.params.id, req.body);
|
|
res.status(201).json({ success: true, message: '공정이 추가되었습니다', data: { id: processId } });
|
|
} catch (err) {
|
|
console.error('공정 추가 오류:', err);
|
|
res.status(500).json({ success: false, error: '공정 추가 실패' });
|
|
}
|
|
};
|
|
|
|
// ==================== 평가 항목 CRUD ====================
|
|
|
|
exports.createItem = async (req, res) => {
|
|
try {
|
|
const itemId = await riskModel.createItem(req.params.processId, req.body);
|
|
res.status(201).json({ success: true, message: '항목이 추가되었습니다', data: { id: itemId } });
|
|
} catch (err) {
|
|
console.error('항목 추가 오류:', err);
|
|
res.status(500).json({ success: false, error: '항목 추가 실패' });
|
|
}
|
|
};
|
|
|
|
exports.updateItem = async (req, res) => {
|
|
try {
|
|
const result = await riskModel.updateItem(req.params.itemId, req.body);
|
|
if (result.affectedRows === 0) return res.status(404).json({ success: false, error: '항목을 찾을 수 없습니다' });
|
|
res.json({ success: true, message: '항목이 수정되었습니다' });
|
|
} catch (err) {
|
|
console.error('항목 수정 오류:', err);
|
|
res.status(500).json({ success: false, error: '항목 수정 실패' });
|
|
}
|
|
};
|
|
|
|
exports.deleteItem = async (req, res) => {
|
|
try {
|
|
const result = await riskModel.deleteItem(req.params.itemId);
|
|
if (result.affectedRows === 0) return res.status(404).json({ success: false, error: '항목을 찾을 수 없습니다' });
|
|
res.json({ success: true, message: '항목이 삭제되었습니다' });
|
|
} catch (err) {
|
|
console.error('항목 삭제 오류:', err);
|
|
res.status(500).json({ success: false, error: '항목 삭제 실패' });
|
|
}
|
|
};
|
|
|
|
// ==================== 감소대책 CRUD ====================
|
|
|
|
exports.getMitigations = async (req, res) => {
|
|
try {
|
|
const mitigations = await riskModel.getMitigationsByProject(req.params.id);
|
|
res.json({ success: true, data: mitigations });
|
|
} catch (err) {
|
|
console.error('감소대책 조회 오류:', err);
|
|
res.status(500).json({ success: false, error: '감소대책 조회 실패' });
|
|
}
|
|
};
|
|
|
|
exports.createMitigation = async (req, res) => {
|
|
try {
|
|
const { mitigation_no } = req.body;
|
|
if (!mitigation_no) return res.status(400).json({ success: false, error: '대책 번호는 필수입니다' });
|
|
const mitigationId = await riskModel.createMitigation(req.params.id, req.body);
|
|
res.status(201).json({ success: true, message: '감소대책이 추가되었습니다', data: { id: mitigationId } });
|
|
} catch (err) {
|
|
if (err.code === 'ER_DUP_ENTRY') {
|
|
return res.status(409).json({ success: false, error: '이미 존재하는 대책 번호입니다' });
|
|
}
|
|
console.error('감소대책 생성 오류:', err);
|
|
res.status(500).json({ success: false, error: '감소대책 생성 실패' });
|
|
}
|
|
};
|
|
|
|
exports.updateMitigation = async (req, res) => {
|
|
try {
|
|
const result = await riskModel.updateMitigation(req.params.mitigationId, req.body);
|
|
if (result.affectedRows === 0) return res.status(404).json({ success: false, error: '감소대책을 찾을 수 없습니다' });
|
|
res.json({ success: true, message: '감소대책이 수정되었습니다' });
|
|
} catch (err) {
|
|
console.error('감소대책 수정 오류:', err);
|
|
res.status(500).json({ success: false, error: '감소대책 수정 실패' });
|
|
}
|
|
};
|
|
|
|
exports.uploadPhoto = async (req, res) => {
|
|
try {
|
|
if (!req.file) return res.status(400).json({ success: false, error: '사진 파일이 없습니다' });
|
|
const photoPath = '/uploads/risk/' + req.file.filename;
|
|
await riskModel.updateMitigationPhoto(req.params.mitigationId, photoPath);
|
|
res.json({ success: true, message: '사진이 업로드되었습니다', data: { photo_url: photoPath } });
|
|
} catch (err) {
|
|
console.error('사진 업로드 오류:', err);
|
|
res.status(500).json({ success: false, error: '사진 업로드 실패' });
|
|
}
|
|
};
|
|
|
|
// ==================== Excel 내보내기 ====================
|
|
|
|
exports.exportExcel = async (req, res) => {
|
|
try {
|
|
const project = await riskModel.getProjectById(req.params.id);
|
|
if (!project) return res.status(404).json({ success: false, error: '프로젝트를 찾을 수 없습니다' });
|
|
|
|
const { generateRiskExcel } = require('../utils/riskExcelExport');
|
|
const buffer = await generateRiskExcel(project);
|
|
|
|
const filename = encodeURIComponent(`위험성평가_${project.title}_${project.year}.xlsx`);
|
|
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${filename}`);
|
|
res.send(buffer);
|
|
} catch (err) {
|
|
console.error('Excel 내보내기 오류:', err);
|
|
res.status(500).json({ success: false, error: 'Excel 내보내기 실패' });
|
|
}
|
|
};
|