feat: 페이지 구조 재구성 및 사이드바 네비게이션 구현
- 페이지 폴더 재구성: safety/, attendance/ 폴더 신규 생성 - work/ → safety/: 이슈 신고, 출입 신청 관련 페이지 이동 - common/ → attendance/: 근태/휴가 관련 페이지 이동 - admin/ 정리: safety-* 파일들을 safety/로 이동 - 사이드바 네비게이션 메뉴 구현 - 카테고리별 메뉴: 작업관리, 안전관리, 근태관리, 시스템관리 - 접기/펼치기 기능 및 상태 저장 - 관리자 전용 메뉴 자동 표시/숨김 - 날씨 API 연동 (기상청 단기예보) - TBM 및 navbar에 현재 날씨 표시 - weatherService.js 추가 - 안전 체크리스트 확장 - 기본/날씨별/작업별 체크 유형 추가 - checklist-manage.html 페이지 추가 - 이슈 신고 시스템 구현 - workIssueController, workIssueModel, workIssueRoutes 추가 - DB 마이그레이션 파일 추가 (실행 대기) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
const workReportModel = require('../models/workReportModel');
|
||||
const { ValidationError, NotFoundError, DatabaseError } = require('../utils/errors');
|
||||
const logger = require('../utils/logger');
|
||||
const { getDb } = require('../dbPool');
|
||||
|
||||
/**
|
||||
* 작업 보고서 생성 (단일 또는 다중)
|
||||
@@ -269,6 +270,170 @@ const getSummaryService = async (year, month) => {
|
||||
}
|
||||
};
|
||||
|
||||
// ========== 부적합 원인 관리 서비스 ==========
|
||||
|
||||
/**
|
||||
* 작업 보고서의 부적합 원인 목록 조회
|
||||
*/
|
||||
const getReportDefectsService = async (reportId) => {
|
||||
const db = await getDb();
|
||||
try {
|
||||
const [rows] = await db.execute(`
|
||||
SELECT
|
||||
d.defect_id,
|
||||
d.report_id,
|
||||
d.error_type_id,
|
||||
d.defect_hours,
|
||||
d.note,
|
||||
d.created_at,
|
||||
et.name as error_type_name,
|
||||
et.severity
|
||||
FROM work_report_defects d
|
||||
JOIN error_types et ON d.error_type_id = et.id
|
||||
WHERE d.report_id = ?
|
||||
ORDER BY d.created_at
|
||||
`, [reportId]);
|
||||
|
||||
return rows;
|
||||
} catch (error) {
|
||||
logger.error('부적합 원인 조회 실패', { reportId, error: error.message });
|
||||
throw new DatabaseError('부적합 원인 조회 중 오류가 발생했습니다');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 부적합 원인 저장 (전체 교체)
|
||||
*/
|
||||
const saveReportDefectsService = async (reportId, defects) => {
|
||||
const db = await getDb();
|
||||
try {
|
||||
await db.query('START TRANSACTION');
|
||||
|
||||
// 기존 부적합 원인 삭제
|
||||
await db.execute('DELETE FROM work_report_defects WHERE report_id = ?', [reportId]);
|
||||
|
||||
// 새 부적합 원인 추가
|
||||
if (defects && defects.length > 0) {
|
||||
for (const defect of defects) {
|
||||
await db.execute(`
|
||||
INSERT INTO work_report_defects (report_id, error_type_id, defect_hours, note)
|
||||
VALUES (?, ?, ?, ?)
|
||||
`, [reportId, defect.error_type_id, defect.defect_hours || 0, defect.note || null]);
|
||||
}
|
||||
}
|
||||
|
||||
// 총 부적합 시간 계산 및 daily_work_reports 업데이트
|
||||
const totalErrorHours = defects
|
||||
? defects.reduce((sum, d) => sum + (parseFloat(d.defect_hours) || 0), 0)
|
||||
: 0;
|
||||
|
||||
await db.execute(`
|
||||
UPDATE daily_work_reports
|
||||
SET error_hours = ?,
|
||||
error_type_id = ?,
|
||||
work_status_id = ?
|
||||
WHERE id = ?
|
||||
`, [
|
||||
totalErrorHours,
|
||||
defects && defects.length > 0 ? defects[0].error_type_id : null,
|
||||
totalErrorHours > 0 ? 2 : 1,
|
||||
reportId
|
||||
]);
|
||||
|
||||
await db.query('COMMIT');
|
||||
|
||||
logger.info('부적합 원인 저장 성공', { reportId, count: defects?.length || 0 });
|
||||
return { success: true, count: defects?.length || 0, totalErrorHours };
|
||||
} catch (error) {
|
||||
await db.query('ROLLBACK');
|
||||
logger.error('부적합 원인 저장 실패', { reportId, error: error.message });
|
||||
throw new DatabaseError('부적합 원인 저장 중 오류가 발생했습니다');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 부적합 원인 추가 (단일)
|
||||
*/
|
||||
const addReportDefectService = async (reportId, defectData) => {
|
||||
const db = await getDb();
|
||||
try {
|
||||
const [result] = await db.execute(`
|
||||
INSERT INTO work_report_defects (report_id, error_type_id, defect_hours, note)
|
||||
VALUES (?, ?, ?, ?)
|
||||
`, [reportId, defectData.error_type_id, defectData.defect_hours || 0, defectData.note || null]);
|
||||
|
||||
// 총 부적합 시간 업데이트
|
||||
await updateTotalErrorHours(reportId);
|
||||
|
||||
logger.info('부적합 원인 추가 성공', { reportId, defectId: result.insertId });
|
||||
return { success: true, defect_id: result.insertId };
|
||||
} catch (error) {
|
||||
logger.error('부적합 원인 추가 실패', { reportId, error: error.message });
|
||||
throw new DatabaseError('부적합 원인 추가 중 오류가 발생했습니다');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 부적합 원인 삭제
|
||||
*/
|
||||
const removeReportDefectService = async (defectId) => {
|
||||
const db = await getDb();
|
||||
try {
|
||||
// report_id 먼저 조회
|
||||
const [defect] = await db.execute('SELECT report_id FROM work_report_defects WHERE defect_id = ?', [defectId]);
|
||||
if (defect.length === 0) {
|
||||
throw new NotFoundError('부적합 원인을 찾을 수 없습니다');
|
||||
}
|
||||
|
||||
const reportId = defect[0].report_id;
|
||||
|
||||
// 삭제
|
||||
await db.execute('DELETE FROM work_report_defects WHERE defect_id = ?', [defectId]);
|
||||
|
||||
// 총 부적합 시간 업데이트
|
||||
await updateTotalErrorHours(reportId);
|
||||
|
||||
logger.info('부적합 원인 삭제 성공', { defectId, reportId });
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
if (error instanceof NotFoundError) throw error;
|
||||
logger.error('부적합 원인 삭제 실패', { defectId, error: error.message });
|
||||
throw new DatabaseError('부적합 원인 삭제 중 오류가 발생했습니다');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 총 부적합 시간 업데이트 헬퍼
|
||||
*/
|
||||
const updateTotalErrorHours = async (reportId) => {
|
||||
const db = await getDb();
|
||||
const [result] = await db.execute(`
|
||||
SELECT COALESCE(SUM(defect_hours), 0) as total
|
||||
FROM work_report_defects
|
||||
WHERE report_id = ?
|
||||
`, [reportId]);
|
||||
|
||||
const totalErrorHours = result[0].total || 0;
|
||||
|
||||
// 첫 번째 부적합 원인의 error_type_id를 대표값으로 사용
|
||||
const [firstDefect] = await db.execute(`
|
||||
SELECT error_type_id FROM work_report_defects WHERE report_id = ? ORDER BY created_at LIMIT 1
|
||||
`, [reportId]);
|
||||
|
||||
await db.execute(`
|
||||
UPDATE daily_work_reports
|
||||
SET error_hours = ?,
|
||||
error_type_id = ?,
|
||||
work_status_id = ?
|
||||
WHERE id = ?
|
||||
`, [
|
||||
totalErrorHours,
|
||||
firstDefect.length > 0 ? firstDefect[0].error_type_id : null,
|
||||
totalErrorHours > 0 ? 2 : 1,
|
||||
reportId
|
||||
]);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
createWorkReportService,
|
||||
getWorkReportsByDateService,
|
||||
@@ -276,5 +441,10 @@ module.exports = {
|
||||
getWorkReportByIdService,
|
||||
updateWorkReportService,
|
||||
removeWorkReportService,
|
||||
getSummaryService
|
||||
getSummaryService,
|
||||
// 부적합 원인 관리
|
||||
getReportDefectsService,
|
||||
saveReportDefectsService,
|
||||
addReportDefectService,
|
||||
removeReportDefectService
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user