// controllers/tbmController.js - TBM 시스템 컨트롤러 const TbmModel = require('../models/tbmModel'); const TbmController = { // ==================== TBM 세션 관련 ==================== /** * TBM 세션 생성 */ createSession: (req, res) => { const sessionData = { session_date: req.body.session_date, leader_id: req.body.leader_id || null, project_id: req.body.project_id || null, work_location: req.body.work_location || null, work_description: req.body.work_description || null, safety_notes: req.body.safety_notes || null, start_time: req.body.start_time || null, created_by: req.user.user_id }; // 필수 필드 검증 (날짜만 필수, leader_id는 관리자의 경우 null 허용) if (!sessionData.session_date) { return res.status(400).json({ success: false, message: 'TBM 날짜는 필수입니다.' }); } TbmModel.createSession(sessionData, (err, result) => { if (err) { console.error('TBM 세션 생성 오류:', err); return res.status(500).json({ success: false, message: 'TBM 세션 생성 중 오류가 발생했습니다.', error: err.message }); } res.status(201).json({ success: true, message: 'TBM 세션이 생성되었습니다.', data: { session_id: result.insertId, ...sessionData } }); }); }, /** * 특정 날짜의 TBM 세션 목록 조회 */ getSessionsByDate: (req, res) => { const { date } = req.params; if (!date) { return res.status(400).json({ success: false, message: '날짜 정보가 필요합니다.' }); } TbmModel.getSessionsByDate(date, (err, results) => { if (err) { console.error('TBM 세션 조회 오류:', err); return res.status(500).json({ success: false, message: 'TBM 세션 조회 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, data: results }); }); }, /** * TBM 세션 상세 조회 */ getSessionById: (req, res) => { const { sessionId } = req.params; TbmModel.getSessionById(sessionId, (err, results) => { if (err) { console.error('TBM 세션 상세 조회 오류:', err); return res.status(500).json({ success: false, message: 'TBM 세션 상세 조회 중 오류가 발생했습니다.', error: err.message }); } if (results.length === 0) { return res.status(404).json({ success: false, message: 'TBM 세션을 찾을 수 없습니다.' }); } res.json({ success: true, data: results[0] }); }); }, /** * TBM 세션 수정 */ updateSession: (req, res) => { const { sessionId } = req.params; const sessionData = { project_id: req.body.project_id, work_location: req.body.work_location, work_description: req.body.work_description, safety_notes: req.body.safety_notes, status: req.body.status || 'draft' }; TbmModel.updateSession(sessionId, sessionData, (err, result) => { if (err) { console.error('TBM 세션 수정 오류:', err); return res.status(500).json({ success: false, message: 'TBM 세션 수정 중 오류가 발생했습니다.', error: err.message }); } if (result.affectedRows === 0) { return res.status(404).json({ success: false, message: 'TBM 세션을 찾을 수 없습니다.' }); } res.json({ success: true, message: 'TBM 세션이 수정되었습니다.' }); }); }, /** * TBM 세션 완료 처리 */ completeSession: (req, res) => { const { sessionId } = req.params; const endTime = req.body.end_time || new Date().toTimeString().slice(0, 8); TbmModel.completeSession(sessionId, endTime, (err, result) => { if (err) { console.error('TBM 세션 완료 처리 오류:', err); return res.status(500).json({ success: false, message: 'TBM 세션 완료 처리 중 오류가 발생했습니다.', error: err.message }); } if (result.affectedRows === 0) { return res.status(404).json({ success: false, message: 'TBM 세션을 찾을 수 없습니다.' }); } res.json({ success: true, message: 'TBM 세션이 완료되었습니다.' }); }); }, // ==================== 팀 구성 관련 ==================== /** * 팀원 추가 (작업자별 상세 정보 포함) */ addTeamMember: (req, res) => { const assignmentData = { session_id: req.params.sessionId, worker_id: req.body.worker_id, assigned_role: req.body.assigned_role || null, work_detail: req.body.work_detail || null, is_present: req.body.is_present, absence_reason: req.body.absence_reason || null, project_id: req.body.project_id || null, work_type_id: req.body.work_type_id || null, task_id: req.body.task_id || null, workplace_category_id: req.body.workplace_category_id || null, workplace_id: req.body.workplace_id || null }; if (!assignmentData.worker_id) { return res.status(400).json({ success: false, message: '작업자 ID가 필요합니다.' }); } TbmModel.addTeamMember(assignmentData, (err, result) => { if (err) { console.error('팀원 추가 오류:', err); return res.status(500).json({ success: false, message: '팀원 추가 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, message: '팀원이 추가되었습니다.' }); }); }, /** * 팀 구성 일괄 추가 */ addTeamMembers: (req, res) => { const { sessionId } = req.params; const { members } = req.body; if (!Array.isArray(members) || members.length === 0) { return res.status(400).json({ success: false, message: '팀원 목록이 필요합니다.' }); } TbmModel.addTeamMembers(sessionId, members, (err, result) => { if (err) { console.error('팀 구성 일괄 추가 오류:', err); return res.status(500).json({ success: false, message: '팀 구성 추가 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, message: `${members.length}명의 팀원이 추가되었습니다.`, data: { count: members.length } }); }); }, /** * TBM 세션의 팀 구성 조회 */ getTeamMembers: (req, res) => { const { sessionId } = req.params; TbmModel.getTeamMembers(sessionId, (err, results) => { if (err) { console.error('팀 구성 조회 오류:', err); return res.status(500).json({ success: false, message: '팀 구성 조회 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, data: results }); }); }, /** * 팀원 제거 */ removeTeamMember: (req, res) => { const { sessionId, workerId } = req.params; TbmModel.removeTeamMember(sessionId, workerId, (err, result) => { if (err) { console.error('팀원 제거 오류:', err); return res.status(500).json({ success: false, message: '팀원 제거 중 오류가 발생했습니다.', error: err.message }); } if (result.affectedRows === 0) { return res.status(404).json({ success: false, message: '팀원을 찾을 수 없습니다.' }); } res.json({ success: true, message: '팀원이 제거되었습니다.' }); }); }, /** * 세션의 모든 팀원 삭제 (수정 시 사용) */ clearAllTeamMembers: (req, res) => { const { sessionId } = req.params; TbmModel.clearAllTeamMembers(sessionId, (err, result) => { if (err) { console.error('팀원 전체 삭제 오류:', err); return res.status(500).json({ success: false, message: '팀원 전체 삭제 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, message: '모든 팀원이 삭제되었습니다.', data: { deletedCount: result.affectedRows } }); }); }, // ==================== 안전 체크리스트 관련 ==================== /** * 모든 안전 체크 항목 조회 */ getAllSafetyChecks: (req, res) => { TbmModel.getAllSafetyChecks((err, results) => { if (err) { console.error('안전 체크 항목 조회 오류:', err); return res.status(500).json({ success: false, message: '안전 체크 항목 조회 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, data: results }); }); }, /** * TBM 세션의 안전 체크 기록 조회 */ getSafetyRecords: (req, res) => { const { sessionId } = req.params; TbmModel.getSafetyRecords(sessionId, (err, results) => { if (err) { console.error('안전 체크 기록 조회 오류:', err); return res.status(500).json({ success: false, message: '안전 체크 기록 조회 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, data: results }); }); }, /** * 안전 체크 일괄 저장 */ saveSafetyRecords: (req, res) => { const { sessionId } = req.params; const { records } = req.body; if (!Array.isArray(records) || records.length === 0) { return res.status(400).json({ success: false, message: '안전 체크 기록이 필요합니다.' }); } const checkedBy = req.user.user_id; TbmModel.saveSafetyRecords(sessionId, records, checkedBy, (err, result) => { if (err) { console.error('안전 체크 저장 오류:', err); return res.status(500).json({ success: false, message: '안전 체크 저장 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, message: '안전 체크가 저장되었습니다.', data: { count: records.length } }); }); }, // ==================== 필터링된 안전 체크리스트 (확장) ==================== /** * 세션에 맞는 필터링된 안전 체크 항목 조회 * 기본 + 날씨 + 작업별 체크항목 통합 */ getFilteredSafetyChecks: async (req, res) => { const { sessionId } = req.params; try { // 날씨 정보 확인 (이미 저장된 경우 사용, 없으면 새로 조회) const weatherService = require('../services/weatherService'); let weatherRecord = await weatherService.getWeatherRecord(sessionId); let weatherConditions = []; if (weatherRecord && weatherRecord.weather_conditions) { weatherConditions = weatherRecord.weather_conditions; } else { // 날씨 정보가 없으면 현재 날씨 조회 const currentWeather = await weatherService.getCurrentWeather(); weatherConditions = await weatherService.determineWeatherConditions(currentWeather); // 날씨 기록 저장 await weatherService.saveWeatherRecord(sessionId, currentWeather, weatherConditions); } TbmModel.getFilteredSafetyChecks(sessionId, weatherConditions, (err, results) => { if (err) { console.error('필터링된 안전 체크 조회 오류:', err); return res.status(500).json({ success: false, message: '안전 체크리스트 조회 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, data: results }); }); } catch (error) { console.error('필터링된 안전 체크 조회 오류:', error); res.status(500).json({ success: false, message: '안전 체크리스트 조회 중 오류가 발생했습니다.', error: error.message }); } }, /** * 현재 날씨 조회 */ getCurrentWeather: async (req, res) => { try { const weatherService = require('../services/weatherService'); const { nx, ny } = req.query; const weatherData = await weatherService.getCurrentWeather(nx, ny); const conditions = await weatherService.determineWeatherConditions(weatherData); const conditionList = await weatherService.getWeatherConditionList(); // 현재 조건의 상세 정보 매핑 const activeConditions = conditionList.filter(c => conditions.includes(c.condition_code)); res.json({ success: true, data: { ...weatherData, conditions, conditionDetails: activeConditions } }); } catch (error) { console.error('날씨 조회 오류:', error); res.status(500).json({ success: false, message: '날씨 조회 중 오류가 발생했습니다.', error: error.message }); } }, /** * 세션 날씨 정보 저장 */ saveSessionWeather: async (req, res) => { const { sessionId } = req.params; const { weatherConditions } = req.body; try { const weatherService = require('../services/weatherService'); // 현재 날씨 조회 const weatherData = await weatherService.getCurrentWeather(); const conditions = weatherConditions || await weatherService.determineWeatherConditions(weatherData); // 저장 await weatherService.saveWeatherRecord(sessionId, weatherData, conditions); res.json({ success: true, message: '날씨 정보가 저장되었습니다.', data: { conditions } }); } catch (error) { console.error('날씨 저장 오류:', error); res.status(500).json({ success: false, message: '날씨 저장 중 오류가 발생했습니다.', error: error.message }); } }, /** * 세션 날씨 정보 조회 */ getSessionWeather: async (req, res) => { const { sessionId } = req.params; try { const weatherService = require('../services/weatherService'); const weatherRecord = await weatherService.getWeatherRecord(sessionId); if (!weatherRecord) { return res.status(404).json({ success: false, message: '날씨 기록이 없습니다.' }); } res.json({ success: true, data: weatherRecord }); } catch (error) { console.error('날씨 조회 오류:', error); res.status(500).json({ success: false, message: '날씨 조회 중 오류가 발생했습니다.', error: error.message }); } }, /** * 날씨 조건 목록 조회 */ getWeatherConditions: async (req, res) => { try { const weatherService = require('../services/weatherService'); const conditions = await weatherService.getWeatherConditionList(); res.json({ success: true, data: conditions }); } catch (error) { console.error('날씨 조건 조회 오류:', error); res.status(500).json({ success: false, message: '날씨 조건 조회 중 오류가 발생했습니다.', error: error.message }); } }, // ==================== 안전 체크항목 관리 (관리자용) ==================== /** * 안전 체크 항목 생성 */ createSafetyCheck: (req, res) => { const checkData = req.body; if (!checkData.check_category || !checkData.check_item) { return res.status(400).json({ success: false, message: '카테고리와 체크 항목은 필수입니다.' }); } TbmModel.createSafetyCheck(checkData, (err, result) => { if (err) { console.error('안전 체크 항목 생성 오류:', err); return res.status(500).json({ success: false, message: '안전 체크 항목 생성 중 오류가 발생했습니다.', error: err.message }); } res.status(201).json({ success: true, message: '안전 체크 항목이 생성되었습니다.', data: { check_id: result.insertId } }); }); }, /** * 안전 체크 항목 수정 */ updateSafetyCheck: (req, res) => { const { checkId } = req.params; const checkData = req.body; TbmModel.updateSafetyCheck(checkId, checkData, (err, result) => { if (err) { console.error('안전 체크 항목 수정 오류:', err); return res.status(500).json({ success: false, message: '안전 체크 항목 수정 중 오류가 발생했습니다.', error: err.message }); } if (result.affectedRows === 0) { return res.status(404).json({ success: false, message: '안전 체크 항목을 찾을 수 없습니다.' }); } res.json({ success: true, message: '안전 체크 항목이 수정되었습니다.' }); }); }, /** * 안전 체크 항목 삭제 (비활성화) */ deleteSafetyCheck: (req, res) => { const { checkId } = req.params; TbmModel.deleteSafetyCheck(checkId, (err, result) => { if (err) { console.error('안전 체크 항목 삭제 오류:', err); return res.status(500).json({ success: false, message: '안전 체크 항목 삭제 중 오류가 발생했습니다.', error: err.message }); } if (result.affectedRows === 0) { return res.status(404).json({ success: false, message: '안전 체크 항목을 찾을 수 없습니다.' }); } res.json({ success: true, message: '안전 체크 항목이 삭제되었습니다.' }); }); }, // ==================== 작업 인계 관련 ==================== /** * 작업 인계 생성 */ createHandover: (req, res) => { const handoverData = { session_id: req.body.session_id, from_leader_id: req.body.from_leader_id, to_leader_id: req.body.to_leader_id, handover_date: req.body.handover_date, handover_time: req.body.handover_time || null, reason: req.body.reason, handover_notes: req.body.handover_notes || null, worker_ids: req.body.worker_ids || [] }; // 필수 필드 검증 if (!handoverData.session_id || !handoverData.from_leader_id || !handoverData.to_leader_id || !handoverData.handover_date || !handoverData.reason) { return res.status(400).json({ success: false, message: '필수 정보가 누락되었습니다.' }); } TbmModel.createHandover(handoverData, (err, result) => { if (err) { console.error('작업 인계 생성 오류:', err); return res.status(500).json({ success: false, message: '작업 인계 생성 중 오류가 발생했습니다.', error: err.message }); } res.status(201).json({ success: true, message: '작업 인계가 생성되었습니다.', data: { handover_id: result.insertId } }); }); }, /** * 작업 인계 확인 */ confirmHandover: (req, res) => { const { handoverId } = req.params; const confirmedBy = req.user.user_id; TbmModel.confirmHandover(handoverId, confirmedBy, (err, result) => { if (err) { console.error('작업 인계 확인 오류:', err); return res.status(500).json({ success: false, message: '작업 인계 확인 중 오류가 발생했습니다.', error: err.message }); } if (result.affectedRows === 0) { return res.status(404).json({ success: false, message: '작업 인계 건을 찾을 수 없습니다.' }); } res.json({ success: true, message: '작업 인계가 확인되었습니다.' }); }); }, /** * 특정 날짜의 작업 인계 목록 조회 */ getHandoversByDate: (req, res) => { const { date } = req.params; TbmModel.getHandoversByDate(date, (err, results) => { if (err) { console.error('작업 인계 목록 조회 오류:', err); return res.status(500).json({ success: false, message: '작업 인계 목록 조회 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, data: results }); }); }, /** * 나에게 온 미확인 인계 건 조회 */ getMyPendingHandovers: (req, res) => { // worker_id는 req.user에서 가져옴 const toLeaderId = req.user.worker_id; if (!toLeaderId) { return res.status(400).json({ success: false, message: '작업자 정보를 찾을 수 없습니다.' }); } TbmModel.getPendingHandovers(toLeaderId, (err, results) => { if (err) { console.error('미확인 인계 건 조회 오류:', err); return res.status(500).json({ success: false, message: '미확인 인계 건 조회 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, data: results }); }); }, // ==================== 통계 및 리포트 ==================== /** * TBM 통계 조회 */ getTbmStatistics: (req, res) => { const { startDate, endDate } = req.query; if (!startDate || !endDate) { return res.status(400).json({ success: false, message: '시작일과 종료일이 필요합니다.' }); } TbmModel.getTbmStatistics(startDate, endDate, (err, results) => { if (err) { console.error('TBM 통계 조회 오류:', err); return res.status(500).json({ success: false, message: 'TBM 통계 조회 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, data: results }); }); }, /** * 리더별 TBM 진행 현황 조회 */ getLeaderStatistics: (req, res) => { const { startDate, endDate } = req.query; if (!startDate || !endDate) { return res.status(400).json({ success: false, message: '시작일과 종료일이 필요합니다.' }); } TbmModel.getLeaderStatistics(startDate, endDate, (err, results) => { if (err) { console.error('리더 통계 조회 오류:', err); return res.status(500).json({ success: false, message: '리더 통계 조회 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, data: results }); }); }, /** * 작업보고서가 작성되지 않은 TBM 팀 배정 조회 */ getIncompleteWorkReports: (req, res) => { const userId = req.user.user_id; const accessLevel = req.user.access_level; // 관리자는 모든 TBM 조회, 일반 사용자는 본인이 작성한 것만 조회 const filterUserId = (accessLevel === 'system' || accessLevel === 'admin') ? null : userId; TbmModel.getIncompleteWorkReports(filterUserId, (err, results) => { if (err) { console.error('미완료 작업보고서 조회 오류:', err); return res.status(500).json({ success: false, message: '미완료 작업보고서 조회 중 오류가 발생했습니다.', error: err.message }); } res.json({ success: true, data: results }); }); } }; module.exports = TbmController;