diff --git a/api.hyungi.net/controllers/dailyWorkReportController.js b/api.hyungi.net/controllers/dailyWorkReportController.js index 3d49134..2227fd8 100644 --- a/api.hyungi.net/controllers/dailyWorkReportController.js +++ b/api.hyungi.net/controllers/dailyWorkReportController.js @@ -1,122 +1,38 @@ // controllers/dailyWorkReportController.js - 권한별 전체 조회 지원 버전 const dailyWorkReportModel = require('../models/dailyWorkReportModel'); +const dailyWorkReportService = require('../services/dailyWorkReportService'); /** - * 📝 작업보고서 생성 (누적 방식 - 덮어쓰기 없음!) + * 📝 작업보고서 생성 (V2 - Service Layer 사용) */ -const createDailyWorkReport = (req, res) => { - const { report_date, worker_id, work_entries } = req.body; - const created_by = req.user?.user_id || req.user?.id; - const created_by_name = req.user?.name || req.user?.username || '알 수 없는 사용자'; +const createDailyWorkReport = async (req, res) => { + try { + const reportData = { + ...req.body, + created_by: req.user?.user_id || req.user?.id, + created_by_name: req.user?.name || req.user?.username || '알 수 없는 사용자' + }; - // 1. 기본 유효성 검사 - if (!report_date || !worker_id || !work_entries) { - return res.status(400).json({ - error: '필수 필드가 누락되었습니다.', - required: ['report_date', 'worker_id', 'work_entries'], - received: { - report_date: !!report_date, - worker_id: !!worker_id, - work_entries: !!work_entries - } - }); - } + const result = await dailyWorkReportService.createDailyWorkReportService(reportData); - if (!Array.isArray(work_entries) || work_entries.length === 0) { - return res.status(400).json({ - error: '최소 하나의 작업 항목이 필요합니다.', - received_entries: work_entries?.length || 0 - }); - } - - if (!created_by) { - return res.status(401).json({ - error: '사용자 인증 정보가 없습니다.' - }); - } - - // 2. 작업 항목 유효성 검사 - for (let i = 0; i < work_entries.length; i++) { - const entry = work_entries[i]; - const requiredFields = ['project_id', 'work_type_id', 'work_status_id', 'work_hours']; - - for (const field of requiredFields) { - if (entry[field] === undefined || entry[field] === null || entry[field] === '') { - return res.status(400).json({ - error: `작업 항목 ${i + 1}의 ${field}가 누락되었습니다.`, - entry_index: i, - missing_field: field - }); - } - } - - // 에러 상태인 경우 에러 타입 필수 - if (entry.work_status_id === 2 && (!entry.error_type_id)) { - return res.status(400).json({ - error: `작업 항목 ${i + 1}이 에러 상태인 경우 error_type_id가 필요합니다.`, - entry_index: i - }); - } - - // 시간 유효성 검사 - const hours = parseFloat(entry.work_hours); - if (isNaN(hours) || hours < 0 || hours > 24) { - return res.status(400).json({ - error: `작업 항목 ${i + 1}의 작업시간이 유효하지 않습니다. (0-24시간)`, - entry_index: i, - received_hours: entry.work_hours - }); - } - } - - // 3. 총 시간 계산 - const total_hours = work_entries.reduce((sum, entry) => sum + (parseFloat(entry.work_hours) || 0), 0); - - // 4. 요청 데이터 구성 - const reportData = { - report_date, - worker_id: parseInt(worker_id), - work_entries, - created_by, - created_by_name, - total_hours, - is_update: false - }; - - console.log('📝 작업보고서 누적 추가 요청:', { - date: report_date, - worker: worker_id, - creator: created_by_name, - creator_id: created_by, - entries: work_entries.length, - total_hours - }); - - // 5. 누적 추가 실행 (덮어쓰기 없음!) - dailyWorkReportModel.createDailyReport(reportData, (err, result) => { - if (err) { - console.error('작업보고서 생성 오류:', err); - return res.status(500).json({ - error: '작업보고서 생성 중 오류가 발생했습니다.', - details: err.message, - timestamp: new Date().toISOString() - }); - } - - console.log('✅ 작업보고서 누적 추가 성공:', result); res.status(201).json({ - message: '작업보고서가 성공적으로 누적 추가되었습니다.', - report_date, - worker_id, - created_by: created_by_name, + success: true, timestamp: new Date().toISOString(), ...result }); - }); + + } catch (error) { + console.error('💥 작업보고서 생성 컨트롤러 오류:', error.message); + res.status(400).json({ + success: false, + error: '작업보고서 생성에 실패했습니다.', + details: error.message + }); + } }; /** - * 📊 누적 현황 조회 (새로운 기능) + * �� 누적 현황 조회 (새로운 기능) */ const getAccumulatedReports = (req, res) => { const { date, worker_id } = req.query; diff --git a/api.hyungi.net/models/dailyWorkReportModel.js b/api.hyungi.net/models/dailyWorkReportModel.js index 992d716..67e5cd8 100644 --- a/api.hyungi.net/models/dailyWorkReportModel.js +++ b/api.hyungi.net/models/dailyWorkReportModel.js @@ -794,6 +794,57 @@ const getStatistics = async (start_date, end_date, callback) => { } }; +/** + * [V2] 여러 작업 보고서 항목을 트랜잭션으로 생성합니다. (Promise 기반) + * @param {object} modelData - 서비스 레이어에서 전달된 데이터 + * @returns {Promise} 삽입된 항목의 ID 배열과 개수 + */ +const createReportEntries = async ({ report_date, worker_id, entries }) => { + const db = await getDb(); + const conn = await db.getConnection(); + try { + await conn.beginTransaction(); + + const insertedIds = []; + const sql = ` + INSERT INTO daily_work_reports + (report_date, worker_id, project_id, task_id, work_hours, is_error, error_type_code_id, created_by_user_id) + VALUES (?, ?, ?, ?, ?, ?, ?, ?) + `; + + for (const entry of entries) { + const { project_id, task_id, work_hours, is_error, error_type_code_id, created_by_user_id } = entry; + const [result] = await conn.query(sql, [ + report_date, + worker_id, + project_id, + task_id, + work_hours, + is_error, + error_type_code_id, + created_by_user_id + ]); + insertedIds.push(result.insertId); + } + + await conn.commit(); + + console.log(`[Model] ${insertedIds.length}개 작업 항목 생성 완료.`); + return { + inserted_ids: insertedIds, + inserted_count: insertedIds.length + }; + + } catch (err) { + await conn.rollback(); + console.error('[Model] 작업 보고서 생성 중 오류 발생:', err); + // 여기서 발생한 에러는 서비스 레이어로 전파됩니다. + throw new Error('데이터베이스에 작업 보고서를 생성하는 중 오류가 발생했습니다.'); + } finally { + conn.release(); + } +}; + // 모든 함수 내보내기 (기존 기능 + 누적 기능) module.exports = { // 📋 마스터 데이터 @@ -826,5 +877,8 @@ module.exports = { updateById, removeById, removeByDateAndWorker, - getStatistics + getStatistics, + + // 새로 추가된 V2 함수 + createReportEntries }; \ No newline at end of file diff --git a/api.hyungi.net/services/dailyWorkReportService.js b/api.hyungi.net/services/dailyWorkReportService.js new file mode 100644 index 0000000..f08ec6f --- /dev/null +++ b/api.hyungi.net/services/dailyWorkReportService.js @@ -0,0 +1,84 @@ +const dailyWorkReportModel = require('../models/dailyWorkReportModel'); + +/** + * 일일 작업 보고서를 생성하는 비즈니스 로직을 처리합니다. + * @param {object} reportData - 컨트롤러에서 전달된 보고서 데이터 + * @returns {Promise} 생성 결과 또는 에러 + */ +const createDailyWorkReportService = async (reportData) => { + const { report_date, worker_id, work_entries, created_by, created_by_name } = reportData; + + // 1. 기본 유효성 검사 + if (!report_date || !worker_id || !work_entries) { + throw new Error('필수 필드(report_date, worker_id, work_entries)가 누락되었습니다.'); + } + + if (!Array.isArray(work_entries) || work_entries.length === 0) { + throw new Error('최소 하나의 작업 항목(work_entries)이 필요합니다.'); + } + + if (!created_by) { + throw new Error('사용자 인증 정보(created_by)가 없습니다.'); + } + + // 2. 작업 항목 유효성 검사 + for (let i = 0; i < work_entries.length; i++) { + const entry = work_entries[i]; + const requiredFields = ['project_id', 'task_id', 'work_hours']; + + for (const field of requiredFields) { + if (entry[field] === undefined || entry[field] === null || entry[field] === '') { + throw new Error(`작업 항목 ${i + 1}의 '${field}' 필드가 누락되었습니다.`); + } + } + + // is_error가 true일 때 error_type_code_id 필수 검사 + if (entry.is_error === true && !entry.error_type_code_id) { + throw new Error(`작업 항목 ${i + 1}이 에러 상태인 경우 'error_type_code_id'가 필요합니다.`); + } + + const hours = parseFloat(entry.work_hours); + if (isNaN(hours) || hours <= 0 || hours > 24) { + throw new Error(`작업 항목 ${i + 1}의 작업 시간이 유효하지 않습니다 (0 초과 24 이하).`); + } + } + + // 3. 모델에 전달할 데이터 준비 + const modelData = { + report_date, + worker_id: parseInt(worker_id), + entries: work_entries.map(entry => ({ + project_id: entry.project_id, + task_id: entry.task_id, + work_hours: parseFloat(entry.work_hours), + is_error: entry.is_error || false, + error_type_code_id: entry.error_type_code_id || null, + created_by_user_id: created_by + })) + }; + + console.log('📝 [Service] 작업보고서 생성 요청:', { + date: report_date, + worker: worker_id, + creator: created_by_name, + entries_count: modelData.entries.length + }); + + // 4. 모델 함수 호출 + try { + const result = await dailyWorkReportModel.createReportEntries(modelData); + console.log('✅ [Service] 작업보고서 생성 성공:', result); + return { + message: '작업보고서가 성공적으로 생성되었습니다.', + ...result + }; + } catch (error) { + console.error('[Service] 작업보고서 생성 중 오류 발생:', error); + // 모델에서 발생한 오류를 그대로 상위로 전달 + throw error; + } +}; + +module.exports = { + createDailyWorkReportService, +}; \ No newline at end of file