// models/tbmModel.js - TBM 시스템 모델 const db = require('../db/connection'); const TbmModel = { // ==================== TBM 세션 관련 ==================== /** * TBM 세션 생성 */ createSession: (sessionData, callback) => { const sql = ` INSERT INTO tbm_sessions (session_date, leader_id, project_id, work_location, work_description, safety_notes, start_time, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `; const values = [ sessionData.session_date, sessionData.leader_id, sessionData.project_id, sessionData.work_location, sessionData.work_description, sessionData.safety_notes, sessionData.start_time, sessionData.created_by ]; db.query(sql, values, callback); }, /** * 특정 날짜의 TBM 세션 조회 */ getSessionsByDate: (date, callback) => { const sql = ` SELECT s.*, w.worker_name as leader_name, w.job_type as leader_job_type, p.project_name, p.job_no, u.username as created_by_username, COUNT(DISTINCT ta.worker_id) as team_member_count FROM tbm_sessions s LEFT JOIN workers w ON s.leader_id = w.worker_id LEFT JOIN projects p ON s.project_id = p.project_id LEFT JOIN users u ON s.created_by = u.user_id LEFT JOIN tbm_team_assignments ta ON s.session_id = ta.session_id WHERE s.session_date = ? GROUP BY s.session_id ORDER BY s.start_time DESC `; db.query(sql, [date], callback); }, /** * TBM 세션 상세 조회 */ getSessionById: (sessionId, callback) => { const sql = ` SELECT s.*, w.worker_name as leader_name, w.job_type as leader_job_type, w.phone_number as leader_phone, p.project_name, p.job_no, p.site, u.username as created_by_username, u.name as created_by_name FROM tbm_sessions s LEFT JOIN workers w ON s.leader_id = w.worker_id LEFT JOIN projects p ON s.project_id = p.project_id LEFT JOIN users u ON s.created_by = u.user_id WHERE s.session_id = ? `; db.query(sql, [sessionId], callback); }, /** * TBM 세션 수정 */ updateSession: (sessionId, sessionData, callback) => { const sql = ` UPDATE tbm_sessions SET project_id = ?, work_location = ?, work_description = ?, safety_notes = ?, status = ?, updated_at = NOW() WHERE session_id = ? `; const values = [ sessionData.project_id, sessionData.work_location, sessionData.work_description, sessionData.safety_notes, sessionData.status, sessionId ]; db.query(sql, values, callback); }, /** * TBM 세션 완료 처리 */ completeSession: (sessionId, endTime, callback) => { const sql = ` UPDATE tbm_sessions SET status = 'completed', end_time = ?, updated_at = NOW() WHERE session_id = ? `; db.query(sql, [endTime, sessionId], callback); }, // ==================== 팀 구성 관련 ==================== /** * 팀원 추가 */ addTeamMember: (assignmentData, callback) => { const sql = ` INSERT INTO tbm_team_assignments (session_id, worker_id, assigned_role, work_detail, is_present, absence_reason) VALUES (?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE assigned_role = VALUES(assigned_role), work_detail = VALUES(work_detail), is_present = VALUES(is_present), absence_reason = VALUES(absence_reason) `; const values = [ assignmentData.session_id, assignmentData.worker_id, assignmentData.assigned_role, assignmentData.work_detail, assignmentData.is_present !== undefined ? assignmentData.is_present : true, assignmentData.absence_reason ]; db.query(sql, values, callback); }, /** * 팀 구성 일괄 추가 */ addTeamMembers: (sessionId, members, callback) => { if (!members || members.length === 0) { return callback(null, { affectedRows: 0 }); } const values = members.map(m => [ sessionId, m.worker_id, m.assigned_role || null, m.work_detail || null, m.is_present !== undefined ? m.is_present : true, m.absence_reason || null ]); const sql = ` INSERT INTO tbm_team_assignments (session_id, worker_id, assigned_role, work_detail, is_present, absence_reason) VALUES ? `; db.query(sql, [values], callback); }, /** * TBM 세션의 팀 구성 조회 */ getTeamMembers: (sessionId, callback) => { const sql = ` SELECT ta.*, w.worker_name, w.job_type, w.phone_number, w.department FROM tbm_team_assignments ta INNER JOIN workers w ON ta.worker_id = w.worker_id WHERE ta.session_id = ? ORDER BY ta.assigned_at DESC `; db.query(sql, [sessionId], callback); }, /** * 팀원 제거 */ removeTeamMember: (sessionId, workerId, callback) => { const sql = ` DELETE FROM tbm_team_assignments WHERE session_id = ? AND worker_id = ? `; db.query(sql, [sessionId, workerId], callback); }, // ==================== 안전 체크리스트 관련 ==================== /** * 모든 안전 체크 항목 조회 */ getAllSafetyChecks: (callback) => { const sql = ` SELECT * FROM tbm_safety_checks WHERE is_active = 1 ORDER BY check_category, display_order `; db.query(sql, callback); }, /** * 카테고리별 안전 체크 항목 조회 */ getSafetyChecksByCategory: (category, callback) => { const sql = ` SELECT * FROM tbm_safety_checks WHERE check_category = ? AND is_active = 1 ORDER BY display_order `; db.query(sql, [category], callback); }, /** * TBM 세션의 안전 체크 기록 조회 */ getSafetyRecords: (sessionId, callback) => { const sql = ` SELECT sr.*, sc.check_category, sc.check_item, sc.description, sc.is_required, u.username as checked_by_username, u.name as checked_by_name FROM tbm_safety_records sr INNER JOIN tbm_safety_checks sc ON sr.check_id = sc.check_id LEFT JOIN users u ON sr.checked_by = u.user_id WHERE sr.session_id = ? ORDER BY sc.check_category, sc.display_order `; db.query(sql, [sessionId], callback); }, /** * 안전 체크 기록 저장/업데이트 */ saveSafetyRecord: (recordData, callback) => { const sql = ` INSERT INTO tbm_safety_records (session_id, check_id, is_checked, notes, checked_by, checked_at) VALUES (?, ?, ?, ?, ?, NOW()) ON DUPLICATE KEY UPDATE is_checked = VALUES(is_checked), notes = VALUES(notes), checked_by = VALUES(checked_by), checked_at = NOW() `; const values = [ recordData.session_id, recordData.check_id, recordData.is_checked, recordData.notes, recordData.checked_by ]; db.query(sql, values, callback); }, /** * 안전 체크 일괄 저장 */ saveSafetyRecords: (sessionId, records, checkedBy, callback) => { if (!records || records.length === 0) { return callback(null, { affectedRows: 0 }); } const values = records.map(r => [ sessionId, r.check_id, r.is_checked, r.notes || null, checkedBy ]); const sql = ` INSERT INTO tbm_safety_records (session_id, check_id, is_checked, notes, checked_by, checked_at) VALUES ? ON DUPLICATE KEY UPDATE is_checked = VALUES(is_checked), notes = VALUES(notes), checked_by = VALUES(checked_by), checked_at = NOW() `; db.query(sql, [values], callback); }, // ==================== 작업 인계 관련 ==================== /** * 작업 인계 생성 */ createHandover: (handoverData, callback) => { const sql = ` INSERT INTO team_handovers (session_id, from_leader_id, to_leader_id, handover_date, handover_time, reason, handover_notes, worker_ids) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `; const values = [ handoverData.session_id, handoverData.from_leader_id, handoverData.to_leader_id, handoverData.handover_date, handoverData.handover_time, handoverData.reason, handoverData.handover_notes, JSON.stringify(handoverData.worker_ids || []) ]; db.query(sql, values, callback); }, /** * 작업 인계 확인 */ confirmHandover: (handoverId, confirmedBy, callback) => { const sql = ` UPDATE team_handovers SET is_confirmed = 1, confirmed_at = NOW(), confirmed_by = ? WHERE handover_id = ? `; db.query(sql, [confirmedBy, handoverId], callback); }, /** * 특정 날짜의 작업 인계 목록 조회 */ getHandoversByDate: (date, callback) => { const sql = ` SELECT h.*, w1.worker_name as from_leader_name, w2.worker_name as to_leader_name, u.username as confirmed_by_username, u.name as confirmed_by_name FROM team_handovers h INNER JOIN workers w1 ON h.from_leader_id = w1.worker_id INNER JOIN workers w2 ON h.to_leader_id = w2.worker_id LEFT JOIN users u ON h.confirmed_by = u.user_id WHERE h.handover_date = ? ORDER BY h.handover_time DESC `; db.query(sql, [date], callback); }, /** * 인수자가 받은 미확인 인계 건 조회 */ getPendingHandovers: (toLeaderId, callback) => { const sql = ` SELECT h.*, w1.worker_name as from_leader_name, w1.phone_number as from_leader_phone, s.work_location, s.work_description FROM team_handovers h INNER JOIN workers w1 ON h.from_leader_id = w1.worker_id LEFT JOIN tbm_sessions s ON h.session_id = s.session_id WHERE h.to_leader_id = ? AND h.is_confirmed = 0 ORDER BY h.handover_date DESC, h.handover_time DESC `; db.query(sql, [toLeaderId], callback); }, // ==================== 통계 및 리포트 ==================== /** * 특정 기간의 TBM 통계 */ getTbmStatistics: (startDate, endDate, callback) => { const sql = ` SELECT DATE(session_date) as date, COUNT(DISTINCT session_id) as session_count, COUNT(DISTINCT leader_id) as leader_count, SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed_count FROM tbm_sessions WHERE session_date BETWEEN ? AND ? GROUP BY DATE(session_date) ORDER BY date DESC `; db.query(sql, [startDate, endDate], callback); }, /** * 리더별 TBM 진행 현황 */ getLeaderStatistics: (startDate, endDate, callback) => { const sql = ` SELECT s.leader_id, w.worker_name as leader_name, COUNT(DISTINCT s.session_id) as total_sessions, SUM(CASE WHEN s.status = 'completed' THEN 1 ELSE 0 END) as completed_sessions, COUNT(DISTINCT ta.worker_id) as total_team_members FROM tbm_sessions s INNER JOIN workers w ON s.leader_id = w.worker_id LEFT JOIN tbm_team_assignments ta ON s.session_id = ta.session_id WHERE s.session_date BETWEEN ? AND ? GROUP BY s.leader_id ORDER BY total_sessions DESC `; db.query(sql, [startDate, endDate], callback); } }; module.exports = TbmModel;