/** * 대시보드 컨트롤러 * Sprint 003 — 개인 요약 API */ const DashboardModel = require('../models/dashboardModel'); const logger = require('../../shared/utils/logger'); const DashboardController = { /** * GET /api/dashboard/my-summary * 연차 잔여 + 월간 연장근로 + 접근 가능 페이지 */ getMySummary: async (req, res) => { try { const userId = req.user.user_id || req.user.id; const now = new Date(); const year = now.getFullYear(); const month = now.getMonth() + 1; // 1단계: 사용자 정보 먼저 조회 (worker_id 필요) const userInfo = await DashboardModel.getUserInfo(userId); if (!userInfo) { return res.status(404).json({ success: false, message: '사용자 정보를 찾을 수 없습니다.' }); } const departmentId = userInfo.department_id; const role = userInfo.role; // 2단계: 나머지 3개 병렬 조회 (연차: sp_vacation_balances from user_id) const [vacationRows, overtime, quickAccess] = await Promise.all([ DashboardModel.getVacationBalance(userId, year), DashboardModel.getMonthlyOvertime(userId, year, month), DashboardModel.getQuickAccess(userId, departmentId, role) ]); // 연차 응답 가공 const details = vacationRows.map(v => ({ type_name: v.type_name, type_code: v.type_code, balance_type: v.balance_type || 'AUTO', expires_at: v.expires_at || null, total: parseFloat(v.total_days) || 0, used: parseFloat(v.used_days) || 0, remaining: parseFloat(v.remaining_days) || 0 })); // 만료되지 않은 balance만 합산 (만료된 이월연차 제외) const today = new Date().toISOString().substring(0, 10); const activeRows = vacationRows.filter(v => !v.expires_at || v.expires_at >= today); const totalDays = activeRows.reduce((s, v) => s + (parseFloat(v.total_days) || 0), 0); const usedDays = activeRows.reduce((s, v) => s + (parseFloat(v.used_days) || 0), 0); const remainingDays = totalDays - usedDays; res.json({ success: true, data: { user: { user_id: userInfo.user_id, name: userInfo.name, worker_name: userInfo.worker_name || userInfo.name, job_type: userInfo.job_type || '', department_name: userInfo.department_name, department_id: userInfo.department_id, role: userInfo.role }, vacation: { year, total_days: totalDays, used_days: usedDays, remaining_days: remainingDays, details }, overtime: { year, month, total_overtime_hours: parseFloat(overtime.total_overtime_hours) || 0, overtime_days: parseInt(overtime.overtime_days) || 0, total_work_days: parseInt(overtime.total_work_days) || 0, total_work_hours: parseFloat(overtime.total_work_hours) || 0, avg_daily_hours: parseFloat(parseFloat(overtime.avg_daily_hours || 0).toFixed(1)) }, quick_access: quickAccess } }); } catch (err) { logger.error('대시보드 요약 조회 오류:', err); res.status(500).json({ success: false, message: '대시보드 데이터 조회 중 오류가 발생했습니다.', error: err.message }); } } }; module.exports = DashboardController;