feat: 일일 작업 보고서 생성 API 구조 개선 (C-S-M 패턴 적용)

- dailyWorkReportController의 생성 로직을 Service와 Model로 분리
 - Service: 데이터 유효성 검사 등 비즈니스 로직 담당
 - Model: 트랜잭션을 사용한 DB 쿼리 담당
 - Controller: HTTP 요청/응답 처리만 담당하도록 단순화
This commit is contained in:
2025-07-28 11:14:48 +09:00
parent 5539b09fd8
commit 97e32a7057
3 changed files with 160 additions and 106 deletions

View File

@@ -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
});
}
};
/**
* 📊 누적 현황 조회 (새로운 기능)
* <EFBFBD><EFBFBD> 누적 현황 조회 (새로운 기능)
*/
const getAccumulatedReports = (req, res) => {
const { date, worker_id } = req.query;