// controllers/workAnalysisController.js const WorkAnalysis = require('../models/WorkAnalysis'); const { getDb } = require('../dbPool'); // 기존 프로젝트의 DB 연결 방식 사용 class WorkAnalysisController { constructor() { // 메서드 바인딩 this.getStats = this.getStats.bind(this); this.getDailyTrend = this.getDailyTrend.bind(this); this.getWorkerStats = this.getWorkerStats.bind(this); this.getProjectStats = this.getProjectStats.bind(this); this.getWorkTypeStats = this.getWorkTypeStats.bind(this); this.getRecentWork = this.getRecentWork.bind(this); this.getWeekdayPattern = this.getWeekdayPattern.bind(this); this.getErrorAnalysis = this.getErrorAnalysis.bind(this); this.getMonthlyComparison = this.getMonthlyComparison.bind(this); this.getWorkerSpecialization = this.getWorkerSpecialization.bind(this); } // 날짜 유효성 검사 validateDateRange(startDate, endDate) { if (!startDate || !endDate) { throw new Error('시작일과 종료일을 입력해주세요.'); } const start = new Date(startDate); const end = new Date(endDate); if (isNaN(start.getTime()) || isNaN(end.getTime())) { throw new Error('올바른 날짜 형식을 입력해주세요. (YYYY-MM-DD)'); } if (start > end) { throw new Error('시작일이 종료일보다 늦을 수 없습니다.'); } // 너무 긴 기간 방지 (1년 제한) const diffTime = Math.abs(end - start); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); if (diffDays > 365) { throw new Error('조회 기간은 1년을 초과할 수 없습니다.'); } return { start, end }; } // 기본 통계 조회 async getStats(req, res) { try { const { start, end } = req.query; this.validateDateRange(start, end); const db = await getDb(); const workAnalysis = new WorkAnalysis(db); const stats = await workAnalysis.getBasicStats(start, end); res.status(200).json({ success: true, data: stats, message: '기본 통계 조회 완료' }); } catch (error) { console.error('기본 통계 조회 오류:', error); res.status(400).json({ success: false, error: error.message }); } } // 일별 작업시간 추이 async getDailyTrend(req, res) { try { const { start, end } = req.query; this.validateDateRange(start, end); const db = await getDb(); const workAnalysis = new WorkAnalysis(db); const trendData = await workAnalysis.getDailyTrend(start, end); res.status(200).json({ success: true, data: trendData, message: '일별 추이 조회 완료' }); } catch (error) { console.error('일별 추이 조회 오류:', error); res.status(400).json({ success: false, error: error.message }); } } // 작업자별 통계 async getWorkerStats(req, res) { try { const { start, end } = req.query; this.validateDateRange(start, end); const db = await getDb(); const workAnalysis = new WorkAnalysis(db); const workerStats = await workAnalysis.getWorkerStats(start, end); res.status(200).json({ success: true, data: workerStats, message: '작업자별 통계 조회 완료' }); } catch (error) { console.error('작업자별 통계 조회 오류:', error); res.status(400).json({ success: false, error: error.message }); } } // 프로젝트별 통계 async getProjectStats(req, res) { try { const { start, end } = req.query; this.validateDateRange(start, end); const db = await getDb(); const workAnalysis = new WorkAnalysis(db); const projectStats = await workAnalysis.getProjectStats(start, end); res.status(200).json({ success: true, data: projectStats, message: '프로젝트별 통계 조회 완료' }); } catch (error) { console.error('프로젝트별 통계 조회 오류:', error); res.status(400).json({ success: false, error: error.message }); } } // 작업유형별 통계 async getWorkTypeStats(req, res) { try { const { start, end } = req.query; this.validateDateRange(start, end); const db = await getDb(); const workAnalysis = new WorkAnalysis(db); const workTypeStats = await workAnalysis.getWorkTypeStats(start, end); res.status(200).json({ success: true, data: workTypeStats, message: '작업유형별 통계 조회 완료' }); } catch (error) { console.error('작업유형별 통계 조회 오류:', error); res.status(400).json({ success: false, error: error.message }); } } // 최근 작업 현황 async getRecentWork(req, res) { try { const { start, end, limit = 10 } = req.query; this.validateDateRange(start, end); // limit 유효성 검사 const limitNum = parseInt(limit); if (isNaN(limitNum) || limitNum < 1 || limitNum > 100) { throw new Error('limit은 1~100 사이의 숫자여야 합니다.'); } const db = await getDb(); const workAnalysis = new WorkAnalysis(db); const recentWork = await workAnalysis.getRecentWork(start, end, limitNum); res.status(200).json({ success: true, data: recentWork, message: '최근 작업 현황 조회 완료' }); } catch (error) { console.error('최근 작업 현황 조회 오류:', error); res.status(400).json({ success: false, error: error.message }); } } // 요일별 패턴 분석 async getWeekdayPattern(req, res) { try { const { start, end } = req.query; this.validateDateRange(start, end); const db = await getDb(); const workAnalysis = new WorkAnalysis(db); const weekdayPattern = await workAnalysis.getWeekdayPattern(start, end); res.status(200).json({ success: true, data: weekdayPattern, message: '요일별 패턴 분석 완료' }); } catch (error) { console.error('요일별 패턴 분석 오류:', error); res.status(400).json({ success: false, error: error.message }); } } // 에러 분석 async getErrorAnalysis(req, res) { try { const { start, end } = req.query; this.validateDateRange(start, end); const db = await getDb(); const workAnalysis = new WorkAnalysis(db); const errorAnalysis = await workAnalysis.getErrorAnalysis(start, end); res.status(200).json({ success: true, data: errorAnalysis, message: '에러 분석 완료' }); } catch (error) { console.error('에러 분석 오류:', error); res.status(400).json({ success: false, error: error.message }); } } // 월별 비교 분석 async getMonthlyComparison(req, res) { try { const { year = new Date().getFullYear() } = req.query; const yearNum = parseInt(year); if (isNaN(yearNum) || yearNum < 2000 || yearNum > 2050) { throw new Error('올바른 연도를 입력해주세요. (2000-2050)'); } const db = await getDb(); const workAnalysis = new WorkAnalysis(db); const monthlyData = await workAnalysis.getMonthlyComparison(yearNum); res.status(200).json({ success: true, data: monthlyData, message: '월별 비교 분석 완료' }); } catch (error) { console.error('월별 비교 분석 오류:', error); res.status(400).json({ success: false, error: error.message }); } } // 작업자별 전문분야 분석 async getWorkerSpecialization(req, res) { try { const { start, end } = req.query; this.validateDateRange(start, end); const db = await getDb(); const workAnalysis = new WorkAnalysis(db); const specializationData = await workAnalysis.getWorkerSpecialization(start, end); // 작업자별로 그룹화하여 정리 const groupedData = specializationData.reduce((acc, item) => { if (!acc[item.worker_id]) { acc[item.worker_id] = []; } acc[item.worker_id].push({ work_type_id: item.work_type_id, project_id: item.project_id, totalHours: item.totalHours, totalReports: item.totalReports, percentage: item.percentage }); return acc; }, {}); res.status(200).json({ success: true, data: groupedData, message: '작업자별 전문분야 분석 완료' }); } catch (error) { console.error('작업자별 전문분야 분석 오류:', error); res.status(400).json({ success: false, error: error.message }); } } // 대시보드용 종합 데이터 async getDashboardData(req, res) { try { const { start, end } = req.query; this.validateDateRange(start, end); const db = await getDb(); const workAnalysis = new WorkAnalysis(db); // 병렬로 여러 데이터 조회 const [ stats, dailyTrend, workerStats, projectStats, workTypeStats, recentWork ] = await Promise.all([ workAnalysis.getBasicStats(start, end), workAnalysis.getDailyTrend(start, end), workAnalysis.getWorkerStats(start, end), workAnalysis.getProjectStats(start, end), workAnalysis.getWorkTypeStats(start, end), workAnalysis.getRecentWork(start, end, 10) ]); res.status(200).json({ success: true, data: { stats, dailyTrend, workerStats, projectStats, workTypeStats, recentWork }, message: '대시보드 데이터 조회 완료' }); } catch (error) { console.error('대시보드 데이터 조회 오류:', error); res.status(400).json({ success: false, error: error.message }); } } } module.exports = new WorkAnalysisController();