diff --git a/system1-factory/api/controllers/dailyWorkReportController.js b/system1-factory/api/controllers/dailyWorkReportController.js index 9c79629..e01621a 100644 --- a/system1-factory/api/controllers/dailyWorkReportController.js +++ b/system1-factory/api/controllers/dailyWorkReportController.js @@ -14,7 +14,7 @@ const { asyncHandler } = require('../middlewares/errorHandler'); const logger = require('../utils/logger'); /** - * πŸ“ μž‘μ—…λ³΄κ³ μ„œ 생성 (V2 - Service Layer μ‚¬μš©) + * μž‘μ—…λ³΄κ³ μ„œ 생성 (V2 - Service Layer μ‚¬μš©) */ const createDailyWorkReport = asyncHandler(async (req, res) => { const reportData = { @@ -33,75 +33,53 @@ const createDailyWorkReport = asyncHandler(async (req, res) => { }); /** - * πŸ“Š κΈ°μ—¬μžλ³„ μš”μ•½ 쑰회 (μƒˆλ‘œμš΄ κΈ°λŠ₯) + * κΈ°μ—¬μžλ³„ μš”μ•½ 쑰회 */ const getContributorsSummary = asyncHandler(async (req, res) => { const { date, worker_id } = req.query; if (!date || !worker_id) { - throw new ApiError('date와 worker_idκ°€ ν•„μš”ν•©λ‹ˆλ‹€.', 400); + return res.status(400).json({ error: 'date와 worker_idκ°€ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - console.log(`πŸ“Š κΈ°μ—¬μžλ³„ μš”μ•½ 쑰회: date=${date}, worker_id=${worker_id}`); + const data = await dailyWorkReportModel.getContributorsByDate(date, worker_id); - try { - const data = await new Promise((resolve, reject) => { - dailyWorkReportModel.getContributorsByDate(date, worker_id, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - - const totalHours = data.reduce((sum, contributor) => sum + parseFloat(contributor.total_hours || 0), 0); - - console.log(`πŸ“Š κΈ°μ—¬μžλ³„ μš”μ•½: ${data.length}λͺ…, 총 ${totalHours}μ‹œκ°„`); - - const result = { - date, - worker_id, - contributors: data, - total_contributors: data.length, - grand_total_hours: totalHours - }; - - res.success(result, 'κΈ°μ—¬μžλ³„ μš”μ•½ 쑰회 성곡'); - } catch (err) { - handleDatabaseError(err, 'κΈ°μ—¬μžλ³„ μš”μ•½ 쑰회'); - } + const totalHours = data.reduce((sum, contributor) => sum + parseFloat(contributor.total_hours || 0), 0); + + const result = { + date, + worker_id, + contributors: data, + total_contributors: data.length, + grand_total_hours: totalHours + }; + + res.success(result, 'κΈ°μ—¬μžλ³„ μš”μ•½ 쑰회 성곡'); }); /** - * πŸ“Š 개인 λˆ„μ  ν˜„ν™© 쑰회 (μƒˆλ‘œμš΄ κΈ°λŠ₯) + * 개인 λˆ„μ  ν˜„ν™© 쑰회 */ -const getMyAccumulatedData = (req, res) => { +const getMyAccumulatedData = async (req, res) => { const { date, worker_id } = req.query; const created_by = req.user?.user_id || req.user?.id; if (!date || !worker_id) { - return res.status(400).json({ + return res.status(400).json({ error: 'date와 worker_idκ°€ ν•„μš”ν•©λ‹ˆλ‹€.', example: 'date=2024-06-16&worker_id=1' }); } if (!created_by) { - return res.status(401).json({ - error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' + return res.status(401).json({ + error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' }); } - console.log(`πŸ“Š 개인 λˆ„μ  ν˜„ν™© 쑰회: date=${date}, worker_id=${worker_id}, created_by=${created_by}`); + try { + const data = await dailyWorkReportModel.getMyAccumulatedHours(date, worker_id, created_by); - dailyWorkReportModel.getMyAccumulatedHours(date, worker_id, created_by, (err, data) => { - if (err) { - console.error('개인 λˆ„μ  ν˜„ν™© 쑰회 였λ₯˜:', err); - return res.status(500).json({ - error: '개인 λˆ„μ  ν˜„ν™© 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - - console.log(`πŸ“Š 개인 λˆ„μ : ${data.my_entry_count}개 ν•­λͺ©, ${data.my_total_hours}μ‹œκ°„`); res.json({ date, worker_id, @@ -109,52 +87,55 @@ const getMyAccumulatedData = (req, res) => { my_data: data, timestamp: new Date().toISOString() }); - }); + } catch (err) { + logger.error('개인 λˆ„μ  ν˜„ν™© 쑰회 였λ₯˜:', err); + res.status(500).json({ + error: '개인 λˆ„μ  ν˜„ν™© 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', + details: err.message + }); + } }; /** - * πŸ—‘οΈ κ°œλ³„ ν•­λͺ© μ‚­μ œ (본인 μž‘μ„±λΆ„λ§Œ - μƒˆλ‘œμš΄ κΈ°λŠ₯) + * κ°œλ³„ ν•­λͺ© μ‚­μ œ (본인 μž‘μ„±λΆ„λ§Œ) */ -const removeMyEntry = (req, res) => { +const removeMyEntry = async (req, res) => { const { id } = req.params; const deleted_by = req.user?.user_id || req.user?.id; if (!deleted_by) { - return res.status(401).json({ - error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' + return res.status(401).json({ + error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' }); } - console.log(`πŸ—‘οΈ κ°œλ³„ ν•­λͺ© μ‚­μ œ μš”μ²­: id=${id}, μ‚­μ œμž=${deleted_by}`); + try { + const result = await dailyWorkReportModel.removeSpecificEntry(id, deleted_by); - dailyWorkReportModel.removeSpecificEntry(id, deleted_by, (err, result) => { - if (err) { - console.error('κ°œλ³„ ν•­λͺ© μ‚­μ œ 였λ₯˜:', err); - return res.status(500).json({ - error: 'ν•­λͺ© μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - - console.log(`βœ… κ°œλ³„ ν•­λͺ© μ‚­μ œ μ™„λ£Œ: id=${id}`); - res.json({ + res.json({ message: 'ν•­λͺ©μ΄ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', id: id, deleted_by, timestamp: new Date().toISOString(), ...result }); - }); + } catch (err) { + logger.error('κ°œλ³„ ν•­λͺ© μ‚­μ œ 였λ₯˜:', err); + res.status(500).json({ + error: 'ν•­λͺ© μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', + details: err.message + }); + } }; /** - * πŸ“Š μž‘μ—…λ³΄κ³ μ„œ 쑰회 (V2 - Service Layer μ‚¬μš©) + * μž‘μ—…λ³΄κ³ μ„œ 쑰회 (V2 - Service Layer μ‚¬μš©) */ const getDailyWorkReports = async (req, res) => { try { const userInfo = { user_id: req.user?.user_id || req.user?.id, - role: req.user?.role || 'user' // 기본값을 'user'둜 μ„€μ •ν•˜μ—¬ μ•ˆμ „ν•˜κ²Œ 처리 + role: req.user?.role || 'user' }; if (!userInfo.user_id) { @@ -162,73 +143,55 @@ const getDailyWorkReports = async (req, res) => { } const reports = await dailyWorkReportService.getDailyWorkReportsService(req.query, userInfo); - + res.json(reports); } catch (error) { - console.error('πŸ’₯ μž‘μ—…λ³΄κ³ μ„œ 쑰회 컨트둀러 였λ₯˜:', error.message); - res.status(400).json({ + logger.error('μž‘μ—…λ³΄κ³ μ„œ 쑰회 컨트둀러 였λ₯˜:', error.message); + res.status(400).json({ success: false, error: 'μž‘μ—…λ³΄κ³ μ„œ μ‘°νšŒμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.', - details: error.message + details: error.message }); } }; /** - * πŸ“Š λ‚ μ§œλ³„ μž‘μ—…λ³΄κ³ μ„œ 쑰회 (경둜 νŒŒλΌλ―Έν„° - κΆŒν•œλ³„ 전체 쑰회 지원) + * λ‚ μ§œλ³„ μž‘μ—…λ³΄κ³ μ„œ 쑰회 (경둜 νŒŒλΌλ―Έν„° - κΆŒν•œλ³„ 전체 쑰회 지원) */ -const getDailyWorkReportsByDate = (req, res) => { +const getDailyWorkReportsByDate = async (req, res) => { const { date } = req.params; const current_user_id = req.user?.user_id || req.user?.id; - const user_access_level = req.user?.access_level; - const user_job_type = req.user?.job_type; if (!current_user_id) { - return res.status(401).json({ - error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' + return res.status(401).json({ + error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' }); } - const isAdmin = user_access_level === 'system' || user_access_level === 'admin' || user_access_level === 'leader' || user_job_type === 'leader'; + try { + const data = await dailyWorkReportModel.getByDate(date); - console.log(`πŸ“Š λ‚ μ§œλ³„ 쑰회 (경둜): date=${date}, user=${current_user_id}, κΆŒν•œ=${user_access_level}, 직책=${user_job_type}, κ΄€λ¦¬μž=${isAdmin}`); - console.log(`πŸ” μ‚¬μš©μž 정보 상세:`, req.user); - - dailyWorkReportModel.getByDate(date, (err, data) => { - if (err) { - console.error('λ‚ μ§œλ³„ μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - error: 'μž‘μ—…λ³΄κ³ μ„œ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - - // 🎯 κΆŒν•œλ³„ 필터링 (μž„μ‹œλ‘œ λΉ„ν™œμ„±ν™”) - let finalData = data; - console.log(`πŸ“Š μž„μ‹œλ‘œ λͺ¨λ“  μ‚¬μš©μžμ—κ²Œ 전체 쑰회 ν—ˆμš©: ${data.length}개`); - console.log(`πŸ“Š κΆŒν•œ 정보: access_level=${user_access_level}, job_type=${user_job_type}, isAdmin=${isAdmin}`); - - // if (!isAdmin) { - // finalData = data.filter(report => report.created_by === current_user_id); - // console.log(`πŸ“Š κΆŒν•œ 필터링: 전체 ${data.length}개 β†’ ${finalData.length}개`); - // } else { - // console.log(`πŸ“Š κ΄€λ¦¬μž κΆŒν•œμœΌλ‘œ 전체 쑰회: ${data.length}개`); - // } - - res.json(finalData); - }); + // μž„μ‹œλ‘œ λͺ¨λ“  μ‚¬μš©μžμ—κ²Œ 전체 쑰회 ν—ˆμš© + res.json(data); + } catch (err) { + logger.error('λ‚ μ§œλ³„ μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); + res.status(500).json({ + error: 'μž‘μ—…λ³΄κ³ μ„œ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', + details: err.message + }); + } }; /** - * πŸ” μž‘μ—…λ³΄κ³ μ„œ 검색 (νŽ˜μ΄μ§€λ„€μ΄μ…˜ 포함) + * μž‘μ—…λ³΄κ³ μ„œ 검색 (νŽ˜μ΄μ§€λ„€μ΄μ…˜ 포함) */ -const searchWorkReports = (req, res) => { +const searchWorkReports = async (req, res) => { const { start_date, end_date, worker_id, project_id, work_status_id, page = 1, limit = 20 } = req.query; const created_by = req.user?.user_id || req.user?.id; - + if (!start_date || !end_date) { - return res.status(400).json({ + return res.status(400).json({ error: 'start_date와 end_dateκ°€ ν•„μš”ν•©λ‹ˆλ‹€.', example: 'start_date=2024-01-01&end_date=2024-01-31', optional: ['worker_id', 'project_id', 'work_status_id', 'page', 'limit'] @@ -236,8 +199,8 @@ const searchWorkReports = (req, res) => { } if (!created_by) { - return res.status(401).json({ - error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' + return res.status(401).json({ + error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' }); } @@ -247,53 +210,49 @@ const searchWorkReports = (req, res) => { worker_id: worker_id ? parseInt(worker_id) : null, project_id: project_id ? parseInt(project_id) : null, work_status_id: work_status_id ? parseInt(work_status_id) : null, - created_by, // μž‘μ„±μž 필터링 μΆ”κ°€ + created_by, page: parseInt(page), limit: parseInt(limit) }; - console.log('πŸ” μž‘μ—…λ³΄κ³ μ„œ 검색 μš”μ²­:', searchParams); - - dailyWorkReportModel.searchWithDetails(searchParams, (err, data) => { - if (err) { - console.error('μž‘μ—…λ³΄κ³ μ„œ 검색 였λ₯˜:', err); - return res.status(500).json({ - error: 'μž‘μ—…λ³΄κ³ μ„œ 검색 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - - console.log(`πŸ” 검색 κ²°κ³Ό: ${data.reports?.length || 0}개 (전체: ${data.total || 0}개)`); + try { + const data = await dailyWorkReportModel.searchWithDetails(searchParams); res.json(data); - }); + } catch (err) { + logger.error('μž‘μ—…λ³΄κ³ μ„œ 검색 였λ₯˜:', err); + res.status(500).json({ + error: 'μž‘μ—…λ³΄κ³ μ„œ 검색 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', + details: err.message + }); + } }; /** - * πŸ“ˆ 톡계 쑰회 (V2 - Service Layer μ‚¬μš©) + * 톡계 쑰회 (V2 - Service Layer μ‚¬μš©) */ const getWorkReportStats = async (req, res) => { try { const statsData = await dailyWorkReportService.getStatisticsService(req.query); res.json(statsData); } catch (error) { - console.error('πŸ’₯ 톡계 쑰회 컨트둀러 였λ₯˜:', error.message); - res.status(400).json({ + logger.error('톡계 쑰회 컨트둀러 였λ₯˜:', error.message); + res.status(400).json({ success: false, error: '톡계 μ‘°νšŒμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.', - details: error.message + details: error.message }); } }; /** - * πŸ“Š 일일 근무 μš”μ•½ 쑰회 (V2 - Service Layer μ‚¬μš©) + * 일일 근무 μš”μ•½ 쑰회 (V2 - Service Layer μ‚¬μš©) */ const getDailySummary = async (req, res) => { try { const summaryData = await dailyWorkReportService.getSummaryService(req.query); res.json(summaryData); } catch (error) { - console.error('πŸ’₯ 일일 μš”μ•½ 쑰회 컨트둀러 였λ₯˜:', error.message); + logger.error('일일 μš”μ•½ 쑰회 컨트둀러 였λ₯˜:', error.message); res.status(400).json({ success: false, error: '일일 μš”μ•½ μ‘°νšŒμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.', @@ -303,30 +262,22 @@ const getDailySummary = async (req, res) => { }; /** - * πŸ“… μ›”κ°„ μš”μ•½ 쑰회 + * μ›”κ°„ μš”μ•½ 쑰회 */ -const getMonthlySummary = (req, res) => { +const getMonthlySummary = async (req, res) => { const { year, month } = req.query; if (!year || !month) { - return res.status(400).json({ + return res.status(400).json({ error: 'year와 monthκ°€ ν•„μš”ν•©λ‹ˆλ‹€.', example: 'year=2024&month=01', note: 'monthλŠ” 01, 02, ..., 12 ν˜•μ‹μœΌλ‘œ μž…λ ₯ν•˜μ„Έμš”.' }); } - console.log(`πŸ“… μ›”κ°„ μš”μ•½ 쑰회: ${year}-${month}`); + try { + const data = await dailyWorkReportModel.getMonthlySummary(year, month); - dailyWorkReportModel.getMonthlySummary(year, month, (err, data) => { - if (err) { - console.error('μ›”κ°„ μš”μ•½ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - error: 'μ›”κ°„ μš”μ•½ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - res.json({ year: parseInt(year), month: parseInt(month), @@ -334,11 +285,17 @@ const getMonthlySummary = (req, res) => { total_entries: data.length, timestamp: new Date().toISOString() }); - }); + } catch (err) { + logger.error('μ›”κ°„ μš”μ•½ 쑰회 였λ₯˜:', err); + res.status(500).json({ + error: 'μ›”κ°„ μš”μ•½ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', + details: err.message + }); + } }; /** - * ✏️ μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • (V2 - Service Layer μ‚¬μš©) + * μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • (V2 - Service Layer μ‚¬μš©) */ const updateWorkReport = async (req, res) => { try { @@ -354,7 +311,7 @@ const updateWorkReport = async (req, res) => { } const result = await dailyWorkReportService.updateWorkReportService(reportId, updateData, userInfo); - + res.json({ success: true, timestamp: new Date().toISOString(), @@ -362,18 +319,18 @@ const updateWorkReport = async (req, res) => { }); } catch (error) { - console.error(`πŸ’₯ μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • 컨트둀러 였λ₯˜ (id: ${req.params.id}):`, error.message); + logger.error(`μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • 컨트둀러 였λ₯˜ (id: ${req.params.id}):`, error.message); const statusCode = error.statusCode || 400; - res.status(statusCode).json({ + res.status(statusCode).json({ success: false, error: 'μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ •μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.', - details: error.message + details: error.message }); } }; /** - * πŸ—‘οΈ νŠΉμ • μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ (V2 - Service Layer μ‚¬μš©) + * νŠΉμ • μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ (V2 - Service Layer μ‚¬μš©) * κΆŒν•œ: κ·Έλ£Ήμž₯(group_leader), μ‹œμŠ€ν…œ(system), κ΄€λ¦¬μž(admin)만 κ°€λŠ₯ */ const removeDailyWorkReport = async (req, res) => { @@ -391,14 +348,14 @@ const removeDailyWorkReport = async (req, res) => { // κΆŒν•œ 체크: κ·Έλ£Ήμž₯, μ‹œμŠ€ν…œ, κ΄€λ¦¬μžλ§Œ μ‚­μ œ κ°€λŠ₯ const allowedRoles = ['admin', 'system', 'group_leader']; if (!allowedRoles.includes(userInfo.access_level)) { - return res.status(403).json({ + return res.status(403).json({ error: 'μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.', details: 'κ·Έλ£Ήμž₯ μ΄μƒμ˜ κΆŒν•œμ΄ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - + const result = await dailyWorkReportService.removeDailyWorkReportService(reportId, userInfo); - + res.json({ success: true, timestamp: new Date().toISOString(), @@ -406,60 +363,51 @@ const removeDailyWorkReport = async (req, res) => { }); } catch (error) { - console.error(`πŸ’₯ μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ 컨트둀러 였λ₯˜ (id: ${req.params.id}):`, error.message); + logger.error(`μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ 컨트둀러 였λ₯˜ (id: ${req.params.id}):`, error.message); const statusCode = error.statusCode || 400; - res.status(statusCode).json({ + res.status(statusCode).json({ success: false, error: 'μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.', - details: error.message + details: error.message }); } }; /** - * ��️ μž‘μ—…μžμ˜ νŠΉμ • λ‚ μ§œ 전체 μ‚­μ œ + * μž‘μ—…μžμ˜ νŠΉμ • λ‚ μ§œ 전체 μ‚­μ œ */ -const removeDailyWorkReportByDateAndWorker = (req, res) => { +const removeDailyWorkReportByDateAndWorker = async (req, res) => { const { date, worker_id } = req.params; const deleted_by = req.user?.user_id || req.user?.id; const access_level = req.user?.access_level || req.user?.role; if (!deleted_by) { - return res.status(401).json({ - error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' + return res.status(401).json({ + error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' }); } // κΆŒν•œ 체크: κ·Έλ£Ήμž₯, μ‹œμŠ€ν…œ, κ΄€λ¦¬μžλ§Œ μ‚­μ œ κ°€λŠ₯ const allowedRoles = ['admin', 'system', 'group_leader']; if (!allowedRoles.includes(access_level)) { - return res.status(403).json({ + return res.status(403).json({ error: 'μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.', details: 'κ·Έλ£Ήμž₯ μ΄μƒμ˜ κΆŒν•œμ΄ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - console.log(`πŸ—‘οΈ λ‚ μ§œ+μž‘μ—…μžλ³„ 전체 μ‚­μ œ μš”μ²­: date=${date}, worker_id=${worker_id}, μ‚­μ œμž=${deleted_by}`); - - dailyWorkReportModel.removeByDateAndWorker(date, worker_id, deleted_by, (err, affectedRows) => { - if (err) { - console.error('μž‘μ—…λ³΄κ³ μ„œ 전체 μ‚­μ œ 였λ₯˜:', err); - return res.status(500).json({ - error: 'μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } + try { + const affectedRows = await dailyWorkReportModel.removeByDateAndWorker(date, worker_id, deleted_by); if (affectedRows === 0) { - return res.status(404).json({ + return res.status(404).json({ error: 'μ‚­μ œν•  μž‘μ—…λ³΄κ³ μ„œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.', date: date, - worker_id: worker_id + worker_id: worker_id }); } - console.log(`βœ… λ‚ μ§œ+μž‘μ—…μžλ³„ 전체 μ‚­μ œ μ™„λ£Œ: ${affectedRows}개`); - res.json({ + res.json({ message: `${date} λ‚ μ§œμ˜ μž‘μ—…μž ${worker_id} μž‘μ—…λ³΄κ³ μ„œ ${affectedRows}κ°œκ°€ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.`, date, worker_id, @@ -467,356 +415,245 @@ const removeDailyWorkReportByDateAndWorker = (req, res) => { deleted_by, timestamp: new Date().toISOString() }); - }); + } catch (err) { + logger.error('μž‘μ—…λ³΄κ³ μ„œ 전체 μ‚­μ œ 였λ₯˜:', err); + res.status(500).json({ + error: 'μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', + details: err.message + }); + } }; /** - * πŸ“‹ λ§ˆμŠ€ν„° 데이터 쑰회 ν•¨μˆ˜λ“€ + * λ§ˆμŠ€ν„° 데이터 쑰회 ν•¨μˆ˜λ“€ */ -const getWorkTypes = (req, res) => { - console.log('πŸ“‹ μž‘μ—… μœ ν˜• 쑰회 μš”μ²­'); - dailyWorkReportModel.getAllWorkTypes((err, data) => { - if (err) { - console.error('μž‘μ—… μœ ν˜• 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - error: { - message: 'μž‘μ—… μœ ν˜• 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - code: 'DATABASE_ERROR' - } - }); - } - console.log(`πŸ“‹ μž‘μ—… μœ ν˜• 쑰회 κ²°κ³Ό: ${data.length}개`); +const getWorkTypes = async (req, res) => { + try { + const data = await dailyWorkReportModel.getAllWorkTypes(); res.json({ success: true, data: data, message: 'μž‘μ—… μœ ν˜• 쑰회 성곡' }); - }); + } catch (err) { + logger.error('μž‘μ—… μœ ν˜• 쑰회 였λ₯˜:', err); + res.status(500).json({ + success: false, + error: { + message: 'μž‘μ—… μœ ν˜• 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', + code: 'DATABASE_ERROR' + } + }); + } }; -const getWorkStatusTypes = (req, res) => { - console.log('πŸ“‹ 업무 μƒνƒœ μœ ν˜• 쑰회 μš”μ²­'); - dailyWorkReportModel.getAllWorkStatusTypes((err, data) => { - if (err) { - console.error('업무 μƒνƒœ μœ ν˜• 쑰회 였λ₯˜:', err); - return res.status(500).json({ - error: '업무 μƒνƒœ μœ ν˜• 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - console.log(`πŸ“‹ 업무 μƒνƒœ μœ ν˜• 쑰회 κ²°κ³Ό: ${data.length}개`); +const getWorkStatusTypes = async (req, res) => { + try { + const data = await dailyWorkReportModel.getAllWorkStatusTypes(); res.json(data); - }); + } catch (err) { + logger.error('업무 μƒνƒœ μœ ν˜• 쑰회 였λ₯˜:', err); + res.status(500).json({ + error: '업무 μƒνƒœ μœ ν˜• 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', + details: err.message + }); + } }; -const getErrorTypes = (req, res) => { - console.log('πŸ“‹ μ—λŸ¬ μœ ν˜• 쑰회 μš”μ²­'); - dailyWorkReportModel.getAllErrorTypes((err, data) => { - if (err) { - console.error('μ—λŸ¬ μœ ν˜• 쑰회 였λ₯˜:', err); - return res.status(500).json({ - error: 'μ—λŸ¬ μœ ν˜• 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - console.log(`πŸ“‹ μ—λŸ¬ μœ ν˜• 쑰회 κ²°κ³Ό: ${data.length}개`); +const getErrorTypes = async (req, res) => { + try { + const data = await dailyWorkReportModel.getAllErrorTypes(); res.json(data); - }); + } catch (err) { + logger.error('μ—λŸ¬ μœ ν˜• 쑰회 였λ₯˜:', err); + res.status(500).json({ + error: 'μ—λŸ¬ μœ ν˜• 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', + details: err.message + }); + } }; // ========== μž‘μ—… μœ ν˜• CRUD ========== /** - * πŸ“ μž‘μ—… μœ ν˜• 생성 + * μž‘μ—… μœ ν˜• 생성 */ const createWorkType = asyncHandler(async (req, res) => { const { name, description, category } = req.body; - + if (!name) { - throw new ApiError('μž‘μ—… μœ ν˜• 이름이 ν•„μš”ν•©λ‹ˆλ‹€.', 400); - } - - console.log('πŸ“ μž‘μ—… μœ ν˜• 생성:', { name, description, category }); - - try { - const result = await new Promise((resolve, reject) => { - dailyWorkReportModel.createWorkType({ name, description, category }, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - - res.created(result, 'μž‘μ—… μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); - } catch (err) { - handleDatabaseError(err, 'μž‘μ—… μœ ν˜• 생성'); + return res.status(400).json({ error: 'μž‘μ—… μœ ν˜• 이름이 ν•„μš”ν•©λ‹ˆλ‹€.' }); } + + const result = await dailyWorkReportModel.createWorkType({ name, description, category }); + res.created(result, 'μž‘μ—… μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); }); /** - * ✏️ μž‘μ—… μœ ν˜• μˆ˜μ • + * μž‘μ—… μœ ν˜• μˆ˜μ • */ const updateWorkType = asyncHandler(async (req, res) => { const { id } = req.params; const { name, description, category } = req.body; - + if (!id) { - throw new ApiError('μž‘μ—… μœ ν˜• IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.', 400); + return res.status(400).json({ error: 'μž‘μ—… μœ ν˜• IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - - console.log('✏️ μž‘μ—… μœ ν˜• μˆ˜μ •:', { id, name, description, category }); - - try { - const result = await new Promise((resolve, reject) => { - dailyWorkReportModel.updateWorkType(id, { name, description, category }, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - - if (result.affectedRows === 0) { - throw new ApiError('μˆ˜μ •ν•  μž‘μ—… μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.', 404); - } - - res.success(result, 'μž‘μ—… μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); - } catch (err) { - handleDatabaseError(err, 'μž‘μ—… μœ ν˜• μˆ˜μ •'); + + const result = await dailyWorkReportModel.updateWorkType(id, { name, description, category }); + + if (result.affectedRows === 0) { + return res.status(404).json({ error: 'μˆ˜μ •ν•  μž‘μ—… μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } + + res.success(result, 'μž‘μ—… μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); }); /** - * πŸ—‘οΈ μž‘μ—… μœ ν˜• μ‚­μ œ + * μž‘μ—… μœ ν˜• μ‚­μ œ */ const deleteWorkType = asyncHandler(async (req, res) => { const { id } = req.params; - + if (!id) { - throw new ApiError('μž‘μ—… μœ ν˜• IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.', 400); + return res.status(400).json({ error: 'μž‘μ—… μœ ν˜• IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - - console.log('πŸ—‘οΈ μž‘μ—… μœ ν˜• μ‚­μ œ:', id); - - try { - const result = await new Promise((resolve, reject) => { - dailyWorkReportModel.deleteWorkType(id, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - - if (result.affectedRows === 0) { - throw new ApiError('μ‚­μ œν•  μž‘μ—… μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.', 404); - } - - res.success(result, 'μž‘μ—… μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); - } catch (err) { - handleDatabaseError(err, 'μž‘μ—… μœ ν˜• μ‚­μ œ'); + + const result = await dailyWorkReportModel.deleteWorkType(id); + + if (result.affectedRows === 0) { + return res.status(404).json({ error: 'μ‚­μ œν•  μž‘μ—… μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } + + res.success(result, 'μž‘μ—… μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); }); // ========== μž‘μ—… μƒνƒœ CRUD ========== /** - * πŸ“ μž‘μ—… μƒνƒœ 생성 + * μž‘μ—… μƒνƒœ 생성 */ const createWorkStatus = asyncHandler(async (req, res) => { const { name, description, is_error } = req.body; - + if (!name) { - throw new ApiError('μž‘μ—… μƒνƒœ 이름이 ν•„μš”ν•©λ‹ˆλ‹€.', 400); - } - - console.log('πŸ“ μž‘μ—… μƒνƒœ 생성:', { name, description, is_error }); - - try { - const result = await new Promise((resolve, reject) => { - dailyWorkReportModel.createWorkStatus({ name, description, is_error }, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - - res.created(result, 'μž‘μ—… μƒνƒœκ°€ μ„±κ³΅μ μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); - } catch (err) { - handleDatabaseError(err, 'μž‘μ—… μƒνƒœ 생성'); + return res.status(400).json({ error: 'μž‘μ—… μƒνƒœ 이름이 ν•„μš”ν•©λ‹ˆλ‹€.' }); } + + const result = await dailyWorkReportModel.createWorkStatus({ name, description, is_error }); + res.created(result, 'μž‘μ—… μƒνƒœκ°€ μ„±κ³΅μ μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); }); /** - * ✏️ μž‘μ—… μƒνƒœ μˆ˜μ • + * μž‘μ—… μƒνƒœ μˆ˜μ • */ const updateWorkStatus = asyncHandler(async (req, res) => { const { id } = req.params; const { name, description, is_error } = req.body; - + if (!id) { - throw new ApiError('μž‘μ—… μƒνƒœ IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.', 400); + return res.status(400).json({ error: 'μž‘μ—… μƒνƒœ IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - - console.log('✏️ μž‘μ—… μƒνƒœ μˆ˜μ •:', { id, name, description, is_error }); - - try { - const result = await new Promise((resolve, reject) => { - dailyWorkReportModel.updateWorkStatus(id, { name, description, is_error }, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - - if (result.affectedRows === 0) { - throw new ApiError('μˆ˜μ •ν•  μž‘μ—… μƒνƒœλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.', 404); - } - - res.success(result, 'μž‘μ—… μƒνƒœκ°€ μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); - } catch (err) { - handleDatabaseError(err, 'μž‘μ—… μƒνƒœ μˆ˜μ •'); + + const result = await dailyWorkReportModel.updateWorkStatus(id, { name, description, is_error }); + + if (result.affectedRows === 0) { + return res.status(404).json({ error: 'μˆ˜μ •ν•  μž‘μ—… μƒνƒœλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } + + res.success(result, 'μž‘μ—… μƒνƒœκ°€ μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); }); /** - * πŸ—‘οΈ μž‘μ—… μƒνƒœ μ‚­μ œ + * μž‘μ—… μƒνƒœ μ‚­μ œ */ const deleteWorkStatus = asyncHandler(async (req, res) => { const { id } = req.params; - + if (!id) { - throw new ApiError('μž‘μ—… μƒνƒœ IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.', 400); + return res.status(400).json({ error: 'μž‘μ—… μƒνƒœ IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - - console.log('πŸ—‘οΈ μž‘μ—… μƒνƒœ μ‚­μ œ:', id); - - try { - const result = await new Promise((resolve, reject) => { - dailyWorkReportModel.deleteWorkStatus(id, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - - if (result.affectedRows === 0) { - throw new ApiError('μ‚­μ œν•  μž‘μ—… μƒνƒœλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.', 404); - } - - res.success(result, 'μž‘μ—… μƒνƒœκ°€ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); - } catch (err) { - handleDatabaseError(err, 'μž‘μ—… μƒνƒœ μ‚­μ œ'); + + const result = await dailyWorkReportModel.deleteWorkStatus(id); + + if (result.affectedRows === 0) { + return res.status(404).json({ error: 'μ‚­μ œν•  μž‘μ—… μƒνƒœλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } + + res.success(result, 'μž‘μ—… μƒνƒœκ°€ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); }); // ========== 였λ₯˜ μœ ν˜• CRUD ========== /** - * πŸ“ 였λ₯˜ μœ ν˜• 생성 + * 였λ₯˜ μœ ν˜• 생성 */ const createErrorType = asyncHandler(async (req, res) => { const { name, description, severity } = req.body; - + if (!name) { - throw new ApiError('였λ₯˜ μœ ν˜• 이름이 ν•„μš”ν•©λ‹ˆλ‹€.', 400); - } - - console.log('πŸ“ 였λ₯˜ μœ ν˜• 생성:', { name, description, severity }); - - try { - const result = await new Promise((resolve, reject) => { - dailyWorkReportModel.createErrorType({ name, description, severity }, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - - res.created(result, '였λ₯˜ μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); - } catch (err) { - handleDatabaseError(err, '였λ₯˜ μœ ν˜• 생성'); + return res.status(400).json({ error: '였λ₯˜ μœ ν˜• 이름이 ν•„μš”ν•©λ‹ˆλ‹€.' }); } + + const result = await dailyWorkReportModel.createErrorType({ name, description, severity }); + res.created(result, '였λ₯˜ μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); }); /** - * ✏️ 였λ₯˜ μœ ν˜• μˆ˜μ • + * 였λ₯˜ μœ ν˜• μˆ˜μ • */ const updateErrorType = asyncHandler(async (req, res) => { const { id } = req.params; const { name, description, severity } = req.body; - + if (!id) { - throw new ApiError('였λ₯˜ μœ ν˜• IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.', 400); + return res.status(400).json({ error: '였λ₯˜ μœ ν˜• IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - - console.log('✏️ 였λ₯˜ μœ ν˜• μˆ˜μ •:', { id, name, description, severity }); - - try { - const result = await new Promise((resolve, reject) => { - dailyWorkReportModel.updateErrorType(id, { name, description, severity }, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - - if (result.affectedRows === 0) { - throw new ApiError('μˆ˜μ •ν•  였λ₯˜ μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.', 404); - } - - res.success(result, '였λ₯˜ μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); - } catch (err) { - handleDatabaseError(err, '였λ₯˜ μœ ν˜• μˆ˜μ •'); + + const result = await dailyWorkReportModel.updateErrorType(id, { name, description, severity }); + + if (result.affectedRows === 0) { + return res.status(404).json({ error: 'μˆ˜μ •ν•  였λ₯˜ μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } + + res.success(result, '였λ₯˜ μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); }); /** - * πŸ—‘οΈ 였λ₯˜ μœ ν˜• μ‚­μ œ + * 였λ₯˜ μœ ν˜• μ‚­μ œ */ const deleteErrorType = asyncHandler(async (req, res) => { const { id } = req.params; - + if (!id) { - throw new ApiError('였λ₯˜ μœ ν˜• IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.', 400); + return res.status(400).json({ error: '였λ₯˜ μœ ν˜• IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - - console.log('πŸ—‘οΈ 였λ₯˜ μœ ν˜• μ‚­μ œ:', id); - - try { - const result = await new Promise((resolve, reject) => { - dailyWorkReportModel.deleteErrorType(id, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - - if (result.affectedRows === 0) { - throw new ApiError('μ‚­μ œν•  였λ₯˜ μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.', 404); - } - - res.success(result, '였λ₯˜ μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); - } catch (err) { - handleDatabaseError(err, '였λ₯˜ μœ ν˜• μ‚­μ œ'); + + const result = await dailyWorkReportModel.deleteErrorType(id); + + if (result.affectedRows === 0) { + return res.status(404).json({ error: 'μ‚­μ œν•  였λ₯˜ μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } + + res.success(result, '였λ₯˜ μœ ν˜•μ΄ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.'); }); /** - * πŸ“Š λˆ„μ  ν˜„ν™© 쑰회 + * λˆ„μ  ν˜„ν™© 쑰회 */ -const getAccumulatedReports = (req, res) => { +const getAccumulatedReports = async (req, res) => { const { date, worker_id } = req.query; if (!date || !worker_id) { - return res.status(400).json({ + return res.status(400).json({ error: 'date와 worker_idκ°€ ν•„μš”ν•©λ‹ˆλ‹€.', example: 'date=2024-06-16&worker_id=1' }); } - console.log(`πŸ“Š λˆ„μ  ν˜„ν™© 쑰회: date=${date}, worker_id=${worker_id}`); + try { + const data = await dailyWorkReportModel.getAccumulatedReportsByDate(date, worker_id); - dailyWorkReportModel.getAccumulatedReportsByDate(date, worker_id, (err, data) => { - if (err) { - console.error('λˆ„μ  ν˜„ν™© 쑰회 였λ₯˜:', err); - return res.status(500).json({ - error: 'λˆ„μ  ν˜„ν™© 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - - console.log(`πŸ“Š λˆ„μ  ν˜„ν™© 쑰회 κ²°κ³Ό: ${data.length}개`); res.json({ date, worker_id, @@ -824,7 +661,13 @@ const getAccumulatedReports = (req, res) => { accumulated_data: data, timestamp: new Date().toISOString() }); - }); + } catch (err) { + logger.error('λˆ„μ  ν˜„ν™© 쑰회 였λ₯˜:', err); + res.status(500).json({ + error: 'λˆ„μ  ν˜„ν™© 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', + details: err.message + }); + } }; /** @@ -870,7 +713,7 @@ const createFromTbm = async (req, res) => { total_hours, error_hours: error_hours || 0, regular_hours, - work_status_id: work_status_id || (error_hours > 0 ? 2 : 1), // error_hoursκ°€ 있으면 μƒνƒœ 2 (뢀적합) + work_status_id: work_status_id || (error_hours > 0 ? 2 : 1), error_type_id, created_by: req.user.user_id }; @@ -884,31 +727,29 @@ const createFromTbm = async (req, res) => { }); } catch (err) { - console.error('TBM μž‘μ—…λ³΄κ³ μ„œ 생성 였λ₯˜:', err); - console.error('Error stack:', err.stack); + logger.error('TBM μž‘μ—…λ³΄κ³ μ„œ 생성 였λ₯˜:', err); res.status(500).json({ success: false, message: 'TBM μž‘μ—…λ³΄κ³ μ„œ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message, - stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + error: err.message }); } }; -// λͺ¨λ“  컨트둀러 ν•¨μˆ˜ 내보내기 (λ¦¬νŒ©ν† λ§λœ ν•¨μˆ˜ μœ„μ£Όλ‘œ μž¬κ΅¬μ„±) +// λͺ¨λ“  컨트둀러 ν•¨μˆ˜ 내보내기 module.exports = { - // πŸ“ V2 핡심 CRUD ν•¨μˆ˜ + // V2 핡심 CRUD ν•¨μˆ˜ createDailyWorkReport, getDailyWorkReports, updateWorkReport, removeDailyWorkReport, createFromTbm, - // πŸ“Š V2 톡계 및 μš”μ•½ ν•¨μˆ˜ + // V2 톡계 및 μš”μ•½ ν•¨μˆ˜ getWorkReportStats, getDailySummary, - // πŸ”½ 아직 λ¦¬νŒ©ν† λ§λ˜μ§€ μ•Šμ€ λ ˆκ±°μ‹œ ν•¨μˆ˜λ“€ + // λ ˆκ±°μ‹œ ν•¨μˆ˜ (콜백 제거 μ™„λ£Œ) getAccumulatedReports, getContributorsSummary, getMyAccumulatedData, @@ -921,7 +762,7 @@ module.exports = { getWorkStatusTypes, getErrorTypes, - // πŸ”½ λ§ˆμŠ€ν„° 데이터 CRUD + // λ§ˆμŠ€ν„° 데이터 CRUD createWorkType, updateWorkType, deleteWorkType, @@ -931,4 +772,4 @@ module.exports = { createErrorType, updateErrorType, deleteErrorType -}; \ No newline at end of file +}; diff --git a/system1-factory/api/controllers/equipmentController.js b/system1-factory/api/controllers/equipmentController.js index 896eb75..2ceb301 100644 --- a/system1-factory/api/controllers/equipmentController.js +++ b/system1-factory/api/controllers/equipmentController.js @@ -1,6 +1,7 @@ // controllers/equipmentController.js const EquipmentModel = require('../models/equipmentModel'); const imageUploadService = require('../services/imageUploadService'); +const logger = require('../utils/logger'); const EquipmentController = { // CREATE - μ„€λΉ„ 생성 @@ -8,7 +9,6 @@ const EquipmentController = { try { const equipmentData = req.body; - // ν•„μˆ˜ ν•„λ“œ 검증 if (!equipmentData.equipment_code || !equipmentData.equipment_name) { return res.status(400).json({ success: false, @@ -16,42 +16,22 @@ const EquipmentController = { }); } - // μ„€λΉ„ μ½”λ“œ 쀑볡 확인 - EquipmentModel.checkDuplicateCode(equipmentData.equipment_code, null, (error, isDuplicate) => { - if (error) { - console.error('μ„€λΉ„ μ½”λ“œ 쀑볡 확인 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ μ½”λ“œ 쀑볡 확인 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - if (isDuplicate) { - return res.status(409).json({ - success: false, - message: '이미 μ‚¬μš© 쀑인 μ„€λΉ„ μ½”λ“œμž…λ‹ˆλ‹€.' - }); - } - - // μ„€λΉ„ 생성 - EquipmentModel.create(equipmentData, (error, result) => { - if (error) { - console.error('μ„€λΉ„ 생성 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.status(201).json({ - success: true, - message: 'μ„€λΉ„κ°€ μ„±κ³΅μ μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: result - }); + const isDuplicate = await EquipmentModel.checkDuplicateCode(equipmentData.equipment_code, null); + if (isDuplicate) { + return res.status(409).json({ + success: false, + message: '이미 μ‚¬μš© 쀑인 μ„€λΉ„ μ½”λ“œμž…λ‹ˆλ‹€.' }); + } + + const result = await EquipmentModel.create(equipmentData); + res.status(201).json({ + success: true, + message: 'μ„€λΉ„κ°€ μ„±κ³΅μ μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: result }); - } catch (error) { - console.error('μ„€λΉ„ 생성 였λ₯˜:', error); + } catch (err) { + logger.error('μ„€λΉ„ 생성 였λ₯˜:', err); res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' @@ -59,8 +39,8 @@ const EquipmentController = { } }, - // READ ALL - λͺ¨λ“  μ„€λΉ„ 쑰회 (필터링 κ°€λŠ₯) - getAllEquipments: (req, res) => { + // READ ALL - λͺ¨λ“  μ„€λΉ„ 쑰회 + getAllEquipments: async (req, res) => { try { const filters = { workplace_id: req.query.workplace_id, @@ -69,114 +49,61 @@ const EquipmentController = { search: req.query.search }; - EquipmentModel.getAll(filters, (error, results) => { - if (error) { - console.error('μ„€λΉ„ 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: results - }); - }); - } catch (error) { - console.error('μ„€λΉ„ 쑰회 였λ₯˜:', error); + const results = await EquipmentModel.getAll(filters); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('μ„€λΉ„ 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, // READ ONE - νŠΉμ • μ„€λΉ„ 쑰회 - getEquipmentById: (req, res) => { + getEquipmentById: async (req, res) => { try { - const equipmentId = req.params.id; - - EquipmentModel.getById(equipmentId, (error, result) => { - if (error) { - console.error('μ„€λΉ„ 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - if (!result) { - return res.status(404).json({ - success: false, - message: 'μ„€λΉ„λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: result + const result = await EquipmentModel.getById(req.params.id); + if (!result) { + return res.status(404).json({ + success: false, + message: 'μ„€λΉ„λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); - }); - } catch (error) { - console.error('μ„€λΉ„ 쑰회 였λ₯˜:', error); + } + res.json({ success: true, data: result }); + } catch (err) { + logger.error('μ„€λΉ„ 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, // READ BY WORKPLACE - νŠΉμ • μž‘μ—…μž₯의 μ„€λΉ„ 쑰회 - getEquipmentsByWorkplace: (req, res) => { + getEquipmentsByWorkplace: async (req, res) => { try { - const workplaceId = req.params.workplaceId; - - EquipmentModel.getByWorkplace(workplaceId, (error, results) => { - if (error) { - console.error('μž‘μ—…μž₯ μ„€λΉ„ 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μž‘μ—…μž₯ μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: results - }); - }); - } catch (error) { - console.error('μž‘μ—…μž₯ μ„€λΉ„ 쑰회 였λ₯˜:', error); + const results = await EquipmentModel.getByWorkplace(req.params.workplaceId); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('μž‘μ—…μž₯ μ„€λΉ„ 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'μž‘μ—…μž₯ μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, // READ ACTIVE - ν™œμ„± μ„€λΉ„λ§Œ 쑰회 - getActiveEquipments: (req, res) => { + getActiveEquipments: async (req, res) => { try { - EquipmentModel.getActive((error, results) => { - if (error) { - console.error('ν™œμ„± μ„€λΉ„ 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'ν™œμ„± μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: results - }); - }); - } catch (error) { - console.error('ν™œμ„± μ„€λΉ„ 쑰회 였λ₯˜:', error); + const results = await EquipmentModel.getActive(); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('ν™œμ„± μ„€λΉ„ 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'ν™œμ„± μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, @@ -187,7 +114,6 @@ const EquipmentController = { const equipmentId = req.params.id; const equipmentData = req.body; - // ν•„μˆ˜ ν•„λ“œ 검증 if (!equipmentData.equipment_code || !equipmentData.equipment_name) { return res.status(400).json({ success: false, @@ -195,60 +121,30 @@ const EquipmentController = { }); } - // μ„€λΉ„ 쑴재 확인 - EquipmentModel.getById(equipmentId, (error, existingEquipment) => { - if (error) { - console.error('μ„€λΉ„ 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - if (!existingEquipment) { - return res.status(404).json({ - success: false, - message: 'μ„€λΉ„λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); - } - - // μ„€λΉ„ μ½”λ“œ 쀑볡 확인 (μžμ‹  μ œμ™Έ) - EquipmentModel.checkDuplicateCode(equipmentData.equipment_code, equipmentId, (error, isDuplicate) => { - if (error) { - console.error('μ„€λΉ„ μ½”λ“œ 쀑볡 확인 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ μ½”λ“œ 쀑볡 확인 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - if (isDuplicate) { - return res.status(409).json({ - success: false, - message: '이미 μ‚¬μš© 쀑인 μ„€λΉ„ μ½”λ“œμž…λ‹ˆλ‹€.' - }); - } - - // μ„€λΉ„ μˆ˜μ • - EquipmentModel.update(equipmentId, equipmentData, (error, result) => { - if (error) { - console.error('μ„€λΉ„ μˆ˜μ • 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - message: 'μ„€λΉ„κ°€ μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: result - }); - }); + const existingEquipment = await EquipmentModel.getById(equipmentId); + if (!existingEquipment) { + return res.status(404).json({ + success: false, + message: 'μ„€λΉ„λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); + } + + const isDuplicate = await EquipmentModel.checkDuplicateCode(equipmentData.equipment_code, equipmentId); + if (isDuplicate) { + return res.status(409).json({ + success: false, + message: '이미 μ‚¬μš© 쀑인 μ„€λΉ„ μ½”λ“œμž…λ‹ˆλ‹€.' + }); + } + + const result = await EquipmentModel.update(equipmentId, equipmentData); + res.json({ + success: true, + message: 'μ„€λΉ„κ°€ μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: result }); - } catch (error) { - console.error('μ„€λΉ„ μˆ˜μ • 였λ₯˜:', error); + } catch (err) { + logger.error('μ„€λΉ„ μˆ˜μ • 였λ₯˜:', err); res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' @@ -257,7 +153,7 @@ const EquipmentController = { }, // UPDATE MAP POSITION - 지도상 μœ„μΉ˜ μ—…λ°μ΄νŠΈ - updateMapPosition: (req, res) => { + updateMapPosition: async (req, res) => { try { const equipmentId = req.params.id; const positionData = { @@ -267,117 +163,71 @@ const EquipmentController = { map_height_percent: req.body.map_height_percent }; - // workplace_idκ°€ 있으면 포함 (μ„€λΉ„λ₯Ό λ‹€λ₯Έ μž‘μ—…μž₯으둜 이동 κ°€λŠ₯) if (req.body.workplace_id !== undefined) { positionData.workplace_id = req.body.workplace_id; } - EquipmentModel.updateMapPosition(equipmentId, positionData, (error, result) => { - if (error) { - console.error('μ„€λΉ„ μœ„μΉ˜ μ—…λ°μ΄νŠΈ 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ μœ„μΉ˜ μ—…λ°μ΄νŠΈ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - message: 'μ„€λΉ„ μœ„μΉ˜κ°€ μ„±κ³΅μ μœΌλ‘œ μ—…λ°μ΄νŠΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: result - }); + const result = await EquipmentModel.updateMapPosition(equipmentId, positionData); + res.json({ + success: true, + message: 'μ„€λΉ„ μœ„μΉ˜κ°€ μ„±κ³΅μ μœΌλ‘œ μ—…λ°μ΄νŠΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: result }); - } catch (error) { - console.error('μ„€λΉ„ μœ„μΉ˜ μ—…λ°μ΄νŠΈ 였λ₯˜:', error); + } catch (err) { + logger.error('μ„€λΉ„ μœ„μΉ˜ μ—…λ°μ΄νŠΈ 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'μ„€λΉ„ μœ„μΉ˜ μ—…λ°μ΄νŠΈ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, // DELETE - μ„€λΉ„ μ‚­μ œ - deleteEquipment: (req, res) => { + deleteEquipment: async (req, res) => { try { - const equipmentId = req.params.id; - - EquipmentModel.delete(equipmentId, (error, result) => { - if (error) { - console.error('μ„€λΉ„ μ‚­μ œ 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - message: 'μ„€λΉ„κ°€ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: result - }); + const result = await EquipmentModel.delete(req.params.id); + res.json({ + success: true, + message: 'μ„€λΉ„κ°€ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: result }); - } catch (error) { - console.error('μ„€λΉ„ μ‚­μ œ 였λ₯˜:', error); + } catch (err) { + logger.error('μ„€λΉ„ μ‚­μ œ 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'μ„€λΉ„ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, // GET EQUIPMENT TYPES - μ‚¬μš© 쀑인 μ„€λΉ„ μœ ν˜• λͺ©λ‘ 쑰회 - getEquipmentTypes: (req, res) => { + getEquipmentTypes: async (req, res) => { try { - EquipmentModel.getEquipmentTypes((error, results) => { - if (error) { - console.error('μ„€λΉ„ μœ ν˜• 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ μœ ν˜• 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: results - }); - }); - } catch (error) { - console.error('μ„€λΉ„ μœ ν˜• 쑰회 였λ₯˜:', error); + const results = await EquipmentModel.getEquipmentTypes(); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('μ„€λΉ„ μœ ν˜• 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'μ„€λΉ„ μœ ν˜• 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, // GET NEXT EQUIPMENT CODE - λ‹€μŒ κ΄€λ¦¬λ²ˆν˜Έ μžλ™ 생성 - getNextEquipmentCode: (req, res) => { + getNextEquipmentCode: async (req, res) => { try { const prefix = req.query.prefix || 'TKP'; - - EquipmentModel.getNextEquipmentCode(prefix, (error, nextCode) => { - if (error) { - console.error('λ‹€μŒ κ΄€λ¦¬λ²ˆν˜Έ 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'λ‹€μŒ κ΄€λ¦¬λ²ˆν˜Έ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: { - next_code: nextCode, - prefix: prefix - } - }); + const nextCode = await EquipmentModel.getNextEquipmentCode(prefix); + res.json({ + success: true, + data: { next_code: nextCode, prefix } }); - } catch (error) { - console.error('λ‹€μŒ κ΄€λ¦¬λ²ˆν˜Έ 쑰회 였λ₯˜:', error); + } catch (err) { + logger.error('λ‹€μŒ κ΄€λ¦¬λ²ˆν˜Έ 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'λ‹€μŒ κ΄€λ¦¬λ²ˆν˜Έ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, @@ -386,7 +236,6 @@ const EquipmentController = { // μ„€λΉ„ 사진 관리 // ========================================== - // ADD PHOTO - μ„€λΉ„ 사진 μΆ”κ°€ addPhoto: async (req, res) => { try { const equipmentId = req.params.id; @@ -399,13 +248,7 @@ const EquipmentController = { }); } - // Base64 이미지λ₯Ό 파일둜 μ €μž₯ - const photoPath = await imageUploadService.saveBase64Image( - photo_base64, - 'equipment', - 'equipments' - ); - + const photoPath = await imageUploadService.saveBase64Image(photo_base64, 'equipment', 'equipments'); if (!photoPath) { return res.status(500).json({ success: false, @@ -413,7 +256,6 @@ const EquipmentController = { }); } - // DB에 사진 정보 μ €μž₯ const photoData = { photo_path: photoPath, description: description || null, @@ -421,23 +263,14 @@ const EquipmentController = { uploaded_by: req.user?.user_id || null }; - EquipmentModel.addPhoto(equipmentId, photoData, (error, result) => { - if (error) { - console.error('사진 정보 μ €μž₯ 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: '사진 정보 μ €μž₯ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.status(201).json({ - success: true, - message: '사진이 μ„±κ³΅μ μœΌλ‘œ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: result - }); + const result = await EquipmentModel.addPhoto(equipmentId, photoData); + res.status(201).json({ + success: true, + message: '사진이 μ„±κ³΅μ μœΌλ‘œ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: result }); - } catch (error) { - console.error('사진 μΆ”κ°€ 였λ₯˜:', error); + } catch (err) { + logger.error('사진 μΆ”κ°€ 였λ₯˜:', err); res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' @@ -445,70 +278,43 @@ const EquipmentController = { } }, - // GET PHOTOS - μ„€λΉ„ 사진 쑰회 - getPhotos: (req, res) => { + getPhotos: async (req, res) => { try { - const equipmentId = req.params.id; - - EquipmentModel.getPhotos(equipmentId, (error, results) => { - if (error) { - console.error('사진 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: '사진 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: results - }); - }); - } catch (error) { - console.error('사진 쑰회 였λ₯˜:', error); + const results = await EquipmentModel.getPhotos(req.params.id); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('사진 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: '사진 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, - // DELETE PHOTO - μ„€λΉ„ 사진 μ‚­μ œ deletePhoto: async (req, res) => { try { - const photoId = req.params.photoId; + const result = await EquipmentModel.deletePhoto(req.params.photoId); - EquipmentModel.deletePhoto(photoId, async (error, result) => { - if (error) { - if (error.message === 'Photo not found') { - return res.status(404).json({ - success: false, - message: '사진을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); - } - console.error('사진 μ‚­μ œ 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: '사진 μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } + if (result.photo_path) { + await imageUploadService.deleteFile(result.photo_path); + } - // 파일 μ‹œμŠ€ν…œμ—μ„œ 사진 μ‚­μ œ - if (result.photo_path) { - await imageUploadService.deleteFile(result.photo_path); - } - - res.json({ - success: true, - message: '사진이 μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: { photo_id: photoId } - }); + res.json({ + success: true, + message: '사진이 μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: { photo_id: req.params.photoId } }); - } catch (error) { - console.error('사진 μ‚­μ œ 였λ₯˜:', error); + } catch (err) { + if (err.message === 'Photo not found') { + return res.status(404).json({ + success: false, + message: '사진을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' + }); + } + logger.error('사진 μ‚­μ œ 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: '사진 μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, @@ -517,8 +323,7 @@ const EquipmentController = { // μ„€λΉ„ μž„μ‹œ 이동 // ========================================== - // MOVE TEMPORARILY - μ„€λΉ„ μž„μ‹œ 이동 - moveTemporarily: (req, res) => { + moveTemporarily: async (req, res) => { try { const equipmentId = req.params.id; const moveData = { @@ -541,116 +346,66 @@ const EquipmentController = { }); } - EquipmentModel.moveTemporarily(equipmentId, moveData, (error, result) => { - if (error) { - console.error('μ„€λΉ„ 이동 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ 이동 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - message: 'μ„€λΉ„κ°€ μž„μ‹œ μ΄λ™λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: result - }); + const result = await EquipmentModel.moveTemporarily(equipmentId, moveData); + res.json({ + success: true, + message: 'μ„€λΉ„κ°€ μž„μ‹œ μ΄λ™λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: result }); - } catch (error) { - console.error('μ„€λΉ„ 이동 였λ₯˜:', error); + } catch (err) { + logger.error('μ„€λΉ„ 이동 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'μ„€λΉ„ 이동 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, - // RETURN TO ORIGINAL - μ„€λΉ„ μ›μœ„μΉ˜ 볡귀 - returnToOriginal: (req, res) => { + returnToOriginal: async (req, res) => { try { - const equipmentId = req.params.id; - const userId = req.user?.user_id || null; - - EquipmentModel.returnToOriginal(equipmentId, userId, (error, result) => { - if (error) { - if (error.message === 'Equipment not found') { - return res.status(404).json({ - success: false, - message: 'μ„€λΉ„λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); - } - console.error('μ„€λΉ„ 볡귀 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ 볡귀 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - message: 'μ„€λΉ„κ°€ μ›μœ„μΉ˜λ‘œ λ³΅κ·€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: result - }); + const result = await EquipmentModel.returnToOriginal(req.params.id, req.user?.user_id || null); + res.json({ + success: true, + message: 'μ„€λΉ„κ°€ μ›μœ„μΉ˜λ‘œ λ³΅κ·€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: result }); - } catch (error) { - console.error('μ„€λΉ„ 볡귀 였λ₯˜:', error); + } catch (err) { + if (err.message === 'Equipment not found') { + return res.status(404).json({ + success: false, + message: 'μ„€λΉ„λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' + }); + } + logger.error('μ„€λΉ„ 볡귀 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'μ„€λΉ„ 볡귀 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, - // GET TEMPORARILY MOVED - μž„μ‹œ μ΄λ™λœ μ„€λΉ„ λͺ©λ‘ - getTemporarilyMoved: (req, res) => { + getTemporarilyMoved: async (req, res) => { try { - EquipmentModel.getTemporarilyMoved((error, results) => { - if (error) { - console.error('μž„μ‹œ 이동 μ„€λΉ„ 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μž„μ‹œ 이동 μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: results - }); - }); - } catch (error) { - console.error('μž„μ‹œ 이동 μ„€λΉ„ 쑰회 였λ₯˜:', error); + const results = await EquipmentModel.getTemporarilyMoved(); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('μž„μ‹œ 이동 μ„€λΉ„ 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'μž„μ‹œ 이동 μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, - // GET MOVE LOGS - μ„€λΉ„ 이동 이λ ₯ 쑰회 - getMoveLogs: (req, res) => { + getMoveLogs: async (req, res) => { try { - const equipmentId = req.params.id; - - EquipmentModel.getMoveLogs(equipmentId, (error, results) => { - if (error) { - console.error('이동 이λ ₯ 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: '이동 이λ ₯ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: results - }); - }); - } catch (error) { - console.error('이동 이λ ₯ 쑰회 였λ₯˜:', error); + const results = await EquipmentModel.getMoveLogs(req.params.id); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('이동 이λ ₯ 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: '이동 이λ ₯ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, @@ -659,12 +414,10 @@ const EquipmentController = { // μ„€λΉ„ μ™ΈλΆ€ 반좜/λ°˜μž… // ========================================== - // EXPORT EQUIPMENT - μ„€λΉ„ μ™ΈλΆ€ 반좜 - exportEquipment: (req, res) => { + exportEquipment: async (req, res) => { try { - const equipmentId = req.params.id; const exportData = { - equipment_id: equipmentId, + equipment_id: req.params.id, export_date: req.body.export_date, expected_return_date: req.body.expected_return_date, destination: req.body.destination, @@ -674,34 +427,23 @@ const EquipmentController = { exported_by: req.user?.user_id || null }; - EquipmentModel.exportEquipment(exportData, (error, result) => { - if (error) { - console.error('μ„€λΉ„ 반좜 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ 반좜 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.status(201).json({ - success: true, - message: 'μ„€λΉ„κ°€ μ™ΈλΆ€λ‘œ λ°˜μΆœλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: result - }); + const result = await EquipmentModel.exportEquipment(exportData); + res.status(201).json({ + success: true, + message: 'μ„€λΉ„κ°€ μ™ΈλΆ€λ‘œ λ°˜μΆœλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: result }); - } catch (error) { - console.error('μ„€λΉ„ 반좜 였λ₯˜:', error); + } catch (err) { + logger.error('μ„€λΉ„ 반좜 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'μ„€λΉ„ 반좜 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, - // RETURN EQUIPMENT - μ„€λΉ„ λ°˜μž… (μ™ΈλΆ€μ—μ„œ 볡귀) - returnEquipment: (req, res) => { + returnEquipment: async (req, res) => { try { - const logId = req.params.logId; const returnData = { return_date: req.body.return_date, new_status: req.body.new_status || 'active', @@ -709,86 +451,49 @@ const EquipmentController = { returned_by: req.user?.user_id || null }; - EquipmentModel.returnEquipment(logId, returnData, (error, result) => { - if (error) { - if (error.message === 'Export log not found') { - return res.status(404).json({ - success: false, - message: '반좜 기둝을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); - } - console.error('μ„€λΉ„ λ°˜μž… 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: 'μ„€λΉ„ λ°˜μž… 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - message: 'μ„€λΉ„κ°€ λ°˜μž…λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: result - }); + const result = await EquipmentModel.returnEquipment(req.params.logId, returnData); + res.json({ + success: true, + message: 'μ„€λΉ„κ°€ λ°˜μž…λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: result }); - } catch (error) { - console.error('μ„€λΉ„ λ°˜μž… 였λ₯˜:', error); + } catch (err) { + if (err.message === 'Export log not found') { + return res.status(404).json({ + success: false, + message: '반좜 기둝을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' + }); + } + logger.error('μ„€λΉ„ λ°˜μž… 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: 'μ„€λΉ„ λ°˜μž… 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, - // GET EXTERNAL LOGS - μ„€λΉ„ μ™ΈλΆ€ 반좜 이λ ₯ 쑰회 - getExternalLogs: (req, res) => { + getExternalLogs: async (req, res) => { try { - const equipmentId = req.params.id; - - EquipmentModel.getExternalLogs(equipmentId, (error, results) => { - if (error) { - console.error('반좜 이λ ₯ 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: '반좜 이λ ₯ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: results - }); - }); - } catch (error) { - console.error('반좜 이λ ₯ 쑰회 였λ₯˜:', error); + const results = await EquipmentModel.getExternalLogs(req.params.id); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('반좜 이λ ₯ 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: '반좜 이λ ₯ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, - // GET EXPORTED EQUIPMENTS - ν˜„μž¬ μ™ΈλΆ€ 반좜 쀑인 μ„€λΉ„ λͺ©λ‘ - getExportedEquipments: (req, res) => { + getExportedEquipments: async (req, res) => { try { - EquipmentModel.getExportedEquipments((error, results) => { - if (error) { - console.error('반좜 쀑 μ„€λΉ„ 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: '반좜 쀑 μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: results - }); - }); - } catch (error) { - console.error('반좜 쀑 μ„€λΉ„ 쑰회 였λ₯˜:', error); + const results = await EquipmentModel.getExportedEquipments(); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('반좜 쀑 μ„€λΉ„ 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: '반좜 쀑 μ„€λΉ„ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, @@ -797,13 +502,11 @@ const EquipmentController = { // μ„€λΉ„ 수리 μ‹ μ²­ // ========================================== - // CREATE REPAIR REQUEST - 수리 μ‹ μ²­ createRepairRequest: async (req, res) => { try { const equipmentId = req.params.id; const { photo_base64_list, description, item_id, workplace_id } = req.body; - // 사진 μ €μž₯ (μžˆλŠ” 경우) let photoPaths = []; if (photo_base64_list && photo_base64_list.length > 0) { for (const base64 of photo_base64_list) { @@ -821,92 +524,54 @@ const EquipmentController = { reported_by: req.user?.user_id || null }; - EquipmentModel.createRepairRequest(requestData, (error, result) => { - if (error) { - if (error.message === 'μ„€λΉ„ 수리 μΉ΄ν…Œκ³ λ¦¬κ°€ μ—†μŠ΅λ‹ˆλ‹€') { - return res.status(400).json({ - success: false, - message: error.message - }); - } - console.error('수리 μ‹ μ²­ 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: '수리 μ‹ μ²­ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.status(201).json({ - success: true, - message: '수리 신청이 μ ‘μˆ˜λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: result - }); + const result = await EquipmentModel.createRepairRequest(requestData); + res.status(201).json({ + success: true, + message: '수리 신청이 μ ‘μˆ˜λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: result }); - } catch (error) { - console.error('수리 μ‹ μ²­ 였λ₯˜:', error); + } catch (err) { + if (err.message === 'μ„€λΉ„ 수리 μΉ΄ν…Œκ³ λ¦¬κ°€ μ—†μŠ΅λ‹ˆλ‹€') { + return res.status(400).json({ + success: false, + message: err.message + }); + } + logger.error('수리 μ‹ μ²­ 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: '수리 μ‹ μ²­ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, - // GET REPAIR HISTORY - μ„€λΉ„ 수리 이λ ₯ 쑰회 - getRepairHistory: (req, res) => { + getRepairHistory: async (req, res) => { try { - const equipmentId = req.params.id; - - EquipmentModel.getRepairHistory(equipmentId, (error, results) => { - if (error) { - console.error('수리 이λ ₯ 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: '수리 이λ ₯ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: results - }); - }); - } catch (error) { - console.error('수리 이λ ₯ 쑰회 였λ₯˜:', error); + const results = await EquipmentModel.getRepairHistory(req.params.id); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('수리 이λ ₯ 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: '수리 이λ ₯ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, - // GET REPAIR CATEGORIES - μ„€λΉ„ 수리 ν•­λͺ© λͺ©λ‘ 쑰회 - getRepairCategories: (req, res) => { + getRepairCategories: async (req, res) => { try { - EquipmentModel.getRepairCategories((error, results) => { - if (error) { - console.error('수리 ν•­λͺ© 쑰회 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: '수리 ν•­λͺ© 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.json({ - success: true, - data: results - }); - }); - } catch (error) { - console.error('수리 ν•­λͺ© 쑰회 였λ₯˜:', error); + const results = await EquipmentModel.getRepairCategories(); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('수리 ν•­λͺ© 쑰회 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: '수리 ν•­λͺ© 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }, - // ADD REPAIR CATEGORY - μƒˆ 수리 ν•­λͺ© μΆ”κ°€ - addRepairCategory: (req, res) => { + addRepairCategory: async (req, res) => { try { const { item_name } = req.body; @@ -917,26 +582,17 @@ const EquipmentController = { }); } - EquipmentModel.addRepairCategory(item_name.trim(), (error, result) => { - if (error) { - console.error('수리 ν•­λͺ© μΆ”κ°€ 였λ₯˜:', error); - return res.status(500).json({ - success: false, - message: '수리 ν•­λͺ© μΆ”κ°€ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' - }); - } - - res.status(201).json({ - success: true, - message: result.isNew ? 'μƒˆ 수리 μœ ν˜•μ΄ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' : 'κΈ°μ‘΄ 수리 μœ ν˜•μ„ μ‚¬μš©ν•©λ‹ˆλ‹€.', - data: result - }); + const result = await EquipmentModel.addRepairCategory(item_name.trim()); + res.status(201).json({ + success: true, + message: result.isNew ? 'μƒˆ 수리 μœ ν˜•μ΄ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' : 'κΈ°μ‘΄ 수리 μœ ν˜•μ„ μ‚¬μš©ν•©λ‹ˆλ‹€.', + data: result }); - } catch (error) { - console.error('수리 ν•­λͺ© μΆ”κ°€ 였λ₯˜:', error); + } catch (err) { + logger.error('수리 ν•­λͺ© μΆ”κ°€ 였λ₯˜:', err); res.status(500).json({ success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' + message: '수리 ν•­λͺ© μΆ”κ°€ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } } diff --git a/system1-factory/api/controllers/tbmController.js b/system1-factory/api/controllers/tbmController.js index 8baba23..52447ea 100644 --- a/system1-factory/api/controllers/tbmController.js +++ b/system1-factory/api/controllers/tbmController.js @@ -1,515 +1,299 @@ // controllers/tbmController.js - TBM μ‹œμŠ€ν…œ 컨트둀러 const TbmModel = require('../models/tbmModel'); const TbmTransferModel = require('../models/tbmTransferModel'); +const logger = require('../utils/logger'); 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 - }; + createSession: async (req, res) => { + try { + 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 - }); + if (!sessionData.session_date) { + return res.status(400).json({ success: false, message: 'TBM λ‚ μ§œλŠ” ν•„μˆ˜μž…λ‹ˆλ‹€.' }); } + const result = await TbmModel.createSession(sessionData); 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: 'λ‚ μ§œ 정보가 ν•„μš”ν•©λ‹ˆλ‹€.' + data: { session_id: result.insertId, ...sessionData } }); + } catch (err) { + logger.error('TBM μ„Έμ…˜ 생성 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'TBM μ„Έμ…˜ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.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 - }); + getSessionsByDate: async (req, res) => { + try { + const { date } = req.params; + if (!date) { + return res.status(400).json({ success: false, message: 'λ‚ μ§œ 정보가 ν•„μš”ν•©λ‹ˆλ‹€.' }); } + const results = await TbmModel.getSessionsByDate(date); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('TBM μ„Έμ…˜ 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'TBM μ„Έμ…˜ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } + }, + + getSessionById: async (req, res) => { + try { + const { sessionId } = req.params; + const results = await TbmModel.getSessionById(sessionId); + if (results.length === 0) { - return res.status(404).json({ - success: false, - message: 'TBM μ„Έμ…˜μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + 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); - const attendanceData = req.body.attendance_data; - - // κ·Όνƒœ 데이터가 있으면 μƒˆ λ©”μ„œλ“œ μ‚¬μš© - if (attendanceData && Array.isArray(attendanceData) && attendanceData.length > 0) { - const createdBy = req.user.user_id; - TbmModel.completeSessionWithAttendance(sessionId, endTime, attendanceData, createdBy, (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 μ„Έμ…˜μ΄ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); - return; + res.json({ success: true, data: results[0] }); + } catch (err) { + logger.error('TBM μ„Έμ…˜ 상세 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'TBM μ„Έμ…˜ 상세 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); } - - // κΈ°μ‘΄ 방식 (ν•˜μœ„ ν˜Έν™˜) - 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 μ„Έμ…˜μ΄ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); }, - /** - * TBM μ„Έμ…˜ μ‚­μ œ (draft μƒνƒœλ§Œ) - */ - deleteSession: (req, res) => { - const { sessionId } = req.params; + updateSession: async (req, res) => { + try { + 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.deleteSession(sessionId, (err, result) => { - if (err) { - console.error('TBM μ„Έμ…˜ μ‚­μ œ 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'TBM μ„Έμ…˜ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); + const result = await TbmModel.updateSession(sessionId, sessionData); + if (result.affectedRows === 0) { + return res.status(404).json({ success: false, message: 'TBM μ„Έμ…˜μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); + } + + res.json({ success: true, message: 'TBM μ„Έμ…˜μ΄ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('TBM μ„Έμ…˜ μˆ˜μ • 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'TBM μ„Έμ…˜ μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } + }, + + completeSession: async (req, res) => { + try { + const { sessionId } = req.params; + const endTime = req.body.end_time || new Date().toTimeString().slice(0, 8); + const attendanceData = req.body.attendance_data; + + let result; + if (attendanceData && Array.isArray(attendanceData) && attendanceData.length > 0) { + result = await TbmModel.completeSessionWithAttendance(sessionId, endTime, attendanceData, req.user.user_id); + } else { + result = await TbmModel.completeSession(sessionId, endTime); } if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'TBM μ„Έμ…˜μ„ 찾을 수 μ—†κ±°λ‚˜ 이미 μ™„λ£Œλœ μ„Έμ…˜μž…λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'TBM μ„Έμ…˜μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - res.json({ - success: true, - message: 'TBM μ„Έμ…˜μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); + res.json({ success: true, message: 'TBM μ„Έμ…˜μ΄ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('TBM μ„Έμ…˜ μ™„λ£Œ 처리 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'TBM μ„Έμ…˜ μ™„λ£Œ 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } + }, + + deleteSession: async (req, res) => { + try { + const { sessionId } = req.params; + const result = await TbmModel.deleteSession(sessionId); + + if (result.affectedRows === 0) { + return res.status(404).json({ success: false, message: 'TBM μ„Έμ…˜μ„ 찾을 수 μ—†κ±°λ‚˜ 이미 μ™„λ£Œλœ μ„Έμ…˜μž…λ‹ˆλ‹€.' }); + } + + res.json({ success: true, message: 'TBM μ„Έμ…˜μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('TBM μ„Έμ…˜ μ‚­μ œ 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'TBM μ„Έμ…˜ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, // ==================== νŒ€ ꡬ성 κ΄€λ ¨ ==================== - /** - * νŒ€μ› μΆ”κ°€ (μž‘μ—…μžλ³„ 상세 정보 포함) - */ - 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, - work_hours: req.body.work_hours !== undefined ? req.body.work_hours : undefined - }; + addTeamMember: async (req, res) => { + try { + 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, + work_hours: req.body.work_hours !== undefined ? req.body.work_hours : undefined + }; - 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 - }); + if (!assignmentData.worker_id) { + return res.status(400).json({ success: false, message: 'μž‘μ—…μž IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - res.json({ - success: true, - message: 'νŒ€μ›μ΄ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); + await TbmModel.addTeamMember(assignmentData); + res.json({ success: true, message: 'νŒ€μ›μ΄ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('νŒ€μ› μΆ”κ°€ 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'νŒ€μ› μΆ”κ°€ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, - /** - * λΆ„ν•  ν•­λͺ© μΆ”κ°€ (같은 μž‘μ—…μžμ˜ μΆ”κ°€ λ°°μ •) - */ - addSplitAssignment: (req, res) => { - const assignmentData = { - session_id: req.params.sessionId, - worker_id: req.body.worker_id, - work_hours: req.body.work_hours, - 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 - }; + addSplitAssignment: async (req, res) => { + try { + const assignmentData = { + session_id: req.params.sessionId, + worker_id: req.body.worker_id, + work_hours: req.body.work_hours, + 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 || !assignmentData.work_hours) { - return res.status(400).json({ success: false, message: 'μž‘μ—…μž ID와 μž‘μ—…μ‹œκ°„μ΄ ν•„μš”ν•©λ‹ˆλ‹€.' }); - } - - TbmModel.addSplitAssignment(assignmentData, (err, result) => { - if (err) { - console.error('λΆ„ν•  ν•­λͺ© μΆ”κ°€ 였λ₯˜:', err); - return res.status(500).json({ success: false, message: 'λΆ„ν•  ν•­λͺ© μΆ”κ°€ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + if (!assignmentData.worker_id || !assignmentData.work_hours) { + return res.status(400).json({ success: false, message: 'μž‘μ—…μž ID와 μž‘μ—…μ‹œκ°„μ΄ ν•„μš”ν•©λ‹ˆλ‹€.' }); } + + const result = await TbmModel.addSplitAssignment(assignmentData); res.json({ success: true, data: result }); - }); + } catch (err) { + logger.error('λΆ„ν•  ν•­λͺ© μΆ”κ°€ 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'λΆ„ν•  ν•­λͺ© μΆ”κ°€ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }, - /** - * νŒ€ ꡬ성 일괄 μΆ”κ°€ - */ - addTeamMembers: (req, res) => { - const { sessionId } = req.params; - const { members } = req.body; + addTeamMembers: async (req, res) => { + try { + 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 - }); + if (!Array.isArray(members) || members.length === 0) { + return res.status(400).json({ success: false, message: 'νŒ€μ› λͺ©λ‘μ΄ ν•„μš”ν•©λ‹ˆλ‹€.' }); } + await TbmModel.addTeamMembers(sessionId, members); res.json({ success: true, message: `${members.length}λͺ…μ˜ νŒ€μ›μ΄ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.`, data: { count: members.length } }); - }); + } catch (err) { + logger.error('νŒ€ ꡬ성 일괄 μΆ”κ°€ 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'νŒ€ ꡬ성 μΆ”κ°€ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, - /** - * 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 - }); - }); + getTeamMembers: async (req, res) => { + try { + const results = await TbmModel.getTeamMembers(req.params.sessionId); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('νŒ€ ꡬ성 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'νŒ€ ꡬ성 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, - /** - * νŒ€μ› 제거 - */ - 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 - }); - } + removeTeamMember: async (req, res) => { + try { + const { sessionId, workerId } = req.params; + const result = await TbmModel.removeTeamMember(sessionId, workerId); if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'νŒ€μ›μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'νŒ€μ›μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - res.json({ - success: true, - message: 'νŒ€μ›μ΄ μ œκ±°λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); + res.json({ success: true, message: 'νŒ€μ›μ΄ μ œκ±°λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('νŒ€μ› 제거 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'νŒ€μ› 제거 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.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 - }); - } - + clearAllTeamMembers: async (req, res) => { + try { + const result = await TbmModel.clearAllTeamMembers(req.params.sessionId); res.json({ success: true, message: 'λͺ¨λ“  νŒ€μ›μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', data: { deletedCount: result.affectedRows } }); - }); + } catch (err) { + logger.error('νŒ€μ› 전체 μ‚­μ œ 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'νŒ€μ› 전체 μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, // ==================== μ•ˆμ „ 체크리슀트 κ΄€λ ¨ ==================== - /** - * λͺ¨λ“  μ•ˆμ „ 체크 ν•­λͺ© 쑰회 - */ - 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: 'μ•ˆμ „ 체크 기둝이 ν•„μš”ν•©λ‹ˆλ‹€.' - }); + getAllSafetyChecks: async (req, res) => { + try { + const results = await TbmModel.getAllSafetyChecks(); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('μ•ˆμ „ 체크 ν•­λͺ© 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „ 체크 ν•­λͺ© 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); } + }, - const checkedBy = req.user.user_id; + getSafetyRecords: async (req, res) => { + try { + const results = await TbmModel.getSafetyRecords(req.params.sessionId); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('μ•ˆμ „ 체크 기둝 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „ 체크 기둝 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } + }, - TbmModel.saveSafetyRecords(sessionId, records, checkedBy, (err, result) => { - if (err) { - console.error('μ•ˆμ „ 체크 μ €μž₯ 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μ•ˆμ „ 체크 μ €μž₯ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); + saveSafetyRecords: async (req, res) => { + try { + const { sessionId } = req.params; + const { records } = req.body; + + if (!Array.isArray(records) || records.length === 0) { + return res.status(400).json({ success: false, message: 'μ•ˆμ „ 체크 기둝이 ν•„μš”ν•©λ‹ˆλ‹€.' }); } + await TbmModel.saveSafetyRecords(sessionId, records, req.user.user_id); res.json({ success: true, message: 'μ•ˆμ „ 체크가 μ €μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', data: { count: records.length } }); - }); + } catch (err) { + logger.error('μ•ˆμ „ 체크 μ €μž₯ 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „ 체크 μ €μž₯ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, // ==================== ν•„ν„°λ§λœ μ•ˆμ „ 체크리슀트 (ν™•μž₯) ==================== - /** - * μ„Έμ…˜μ— λ§žλŠ” ν•„ν„°λ§λœ μ•ˆμ „ 체크 ν•­λͺ© 쑰회 - * κΈ°λ³Έ + 날씨 + μž‘μ—…λ³„ 체크항λͺ© 톡합 - */ getFilteredSafetyChecks: async (req, res) => { - const { sessionId } = req.params; - try { - // 날씨 정보 확인 (이미 μ €μž₯된 경우 μ‚¬μš©, μ—†μœΌλ©΄ μƒˆλ‘œ 쑰회) + const { sessionId } = req.params; const weatherService = require('../services/weatherService'); let weatherRecord = await weatherService.getWeatherRecord(sessionId); let weatherConditions = []; @@ -517,41 +301,19 @@ const TbmController = { 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 - }); + const results = await TbmModel.getFilteredSafetyChecks(sessionId, weatherConditions); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('ν•„ν„°λ§λœ μ•ˆμ „ 체크 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „ 체크리슀트 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); } }, - /** - * ν˜„μž¬ 날씨 쑰회 - */ getCurrentWeather: async (req, res) => { try { const weatherService = require('../services/weatherService'); @@ -561,538 +323,290 @@ const TbmController = { 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 + data: { ...weatherData, conditions, conditionDetails: activeConditions } }); + } catch (err) { + logger.error('날씨 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: '날씨 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); } }, - /** - * μ„Έμ…˜ 날씨 정보 μ €μž₯ - */ saveSessionWeather: async (req, res) => { - const { sessionId } = req.params; - const { weatherConditions } = req.body; - try { + const { sessionId } = req.params; + const { weatherConditions } = req.body; 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 - }); + res.json({ success: true, message: '날씨 정보가 μ €μž₯λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', data: { conditions } }); + } catch (err) { + logger.error('날씨 μ €μž₯ 였λ₯˜:', err); + res.status(500).json({ success: false, message: '날씨 μ €μž₯ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); } }, - /** - * μ„Έμ…˜ 날씨 정보 쑰회 - */ getSessionWeather: async (req, res) => { - const { sessionId } = req.params; - try { const weatherService = require('../services/weatherService'); - const weatherRecord = await weatherService.getWeatherRecord(sessionId); + const weatherRecord = await weatherService.getWeatherRecord(req.params.sessionId); if (!weatherRecord) { - return res.status(404).json({ - success: false, - message: '날씨 기둝이 μ—†μŠ΅λ‹ˆλ‹€.' - }); + 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 - }); + res.json({ success: true, data: weatherRecord }); + } catch (err) { + logger.error('날씨 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: '날씨 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.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 - }); + res.json({ success: true, data: conditions }); + } catch (err) { + logger.error('날씨 쑰건 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: '날씨 쑰건 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.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 - }); + createSafetyCheck: async (req, res) => { + try { + const checkData = req.body; + if (!checkData.check_category || !checkData.check_item) { + return res.status(400).json({ success: false, message: 'μΉ΄ν…Œκ³ λ¦¬μ™€ 체크 ν•­λͺ©μ€ ν•„μˆ˜μž…λ‹ˆλ‹€.' }); } + const result = await TbmModel.createSafetyCheck(checkData); res.status(201).json({ success: true, message: 'μ•ˆμ „ 체크 ν•­λͺ©μ΄ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', data: { check_id: result.insertId } }); - }); + } catch (err) { + logger.error('μ•ˆμ „ 체크 ν•­λͺ© 생성 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „ 체크 ν•­λͺ© 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, - /** - * μ•ˆμ „ 체크 ν•­λͺ© μˆ˜μ • - */ - 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 - }); - } - + updateSafetyCheck: async (req, res) => { + try { + const result = await TbmModel.updateSafetyCheck(req.params.checkId, req.body); if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'μ•ˆμ „ 체크 ν•­λͺ©μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'μ•ˆμ „ 체크 ν•­λͺ©μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - - res.json({ - success: true, - message: 'μ•ˆμ „ 체크 ν•­λͺ©μ΄ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); + res.json({ success: true, message: 'μ•ˆμ „ 체크 ν•­λͺ©μ΄ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μ•ˆμ „ 체크 ν•­λͺ© μˆ˜μ • 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „ 체크 ν•­λͺ© μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.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 - }); - } - + deleteSafetyCheck: async (req, res) => { + try { + const result = await TbmModel.deleteSafetyCheck(req.params.checkId); if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'μ•ˆμ „ 체크 ν•­λͺ©μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'μ•ˆμ „ 체크 ν•­λͺ©μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - - res.json({ - success: true, - message: 'μ•ˆμ „ 체크 ν•­λͺ©μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); + res.json({ success: true, message: 'μ•ˆμ „ 체크 ν•­λͺ©μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μ•ˆμ „ 체크 ν•­λͺ© μ‚­μ œ 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „ 체크 ν•­λͺ© μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.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 || [] - }; + createHandover: async (req, res) => { + try { + 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 - }); + 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: 'ν•„μˆ˜ 정보가 λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); } + const result = await TbmModel.createHandover(handoverData); 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: 'μž‘μ—…μž 정보λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + } catch (err) { + logger.error('μž‘μ—… 인계 생성 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μž‘μ—… 인계 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); } + }, - TbmModel.getPendingHandovers(toLeaderId, (err, results) => { - if (err) { - console.error('미확인 인계 건 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: '미확인 인계 건 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); + confirmHandover: async (req, res) => { + try { + const result = await TbmModel.confirmHandover(req.params.handoverId, req.user.user_id); + if (result.affectedRows === 0) { + return res.status(404).json({ success: false, message: 'μž‘μ—… 인계 건을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); + } + res.json({ success: true, message: 'μž‘μ—… 인계가 ν™•μΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μž‘μ—… 인계 확인 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μž‘μ—… 인계 확인 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } + }, + + getHandoversByDate: async (req, res) => { + try { + const results = await TbmModel.getHandoversByDate(req.params.date); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('μž‘μ—… 인계 λͺ©λ‘ 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μž‘μ—… 인계 λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } + }, + + getMyPendingHandovers: async (req, res) => { + try { + const toLeaderId = req.user.worker_id; + if (!toLeaderId) { + return res.status(400).json({ success: false, message: 'μž‘μ—…μž 정보λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - res.json({ - success: true, - data: results - }); - }); + const results = await TbmModel.getPendingHandovers(toLeaderId); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('미확인 인계 건 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: '미확인 인계 건 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, // ==================== 톡계 및 리포트 ==================== - /** - * 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 - }); + getTbmStatistics: async (req, res) => { + try { + const { startDate, endDate } = req.query; + if (!startDate || !endDate) { + return res.status(400).json({ success: false, message: 'μ‹œμž‘μΌκ³Ό μ’…λ£ŒμΌμ΄ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - res.json({ - success: true, - data: results - }); - }); + const results = await TbmModel.getTbmStatistics(startDate, endDate); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('TBM 톡계 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'TBM 톡계 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, - /** - * 리더별 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 - }); + getLeaderStatistics: async (req, res) => { + try { + const { startDate, endDate } = req.query; + if (!startDate || !endDate) { + return res.status(400).json({ success: false, message: 'μ‹œμž‘μΌκ³Ό μ’…λ£ŒμΌμ΄ ν•„μš”ν•©λ‹ˆλ‹€.' }); } - res.json({ - success: true, - data: results - }); - }); + const results = await TbmModel.getLeaderStatistics(startDate, endDate); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('리더 톡계 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: '리더 톡계 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, // ==================== μž‘μ—…μž 이동 κ΄€λ ¨ ==================== - /** - * 이동 μ‹€ν–‰ (보내기/빼였기) - */ - createTransfer: (req, res) => { - const { transfer_type, worker_id, source_session_id, dest_session_id, hours, - project_id, work_type_id, task_id, workplace_category_id, workplace_id } = req.body; + createTransfer: async (req, res) => { + try { + const { transfer_type, worker_id, source_session_id, dest_session_id, hours, + project_id, work_type_id, task_id, workplace_category_id, workplace_id } = req.body; - if (!transfer_type || !worker_id || !source_session_id || !dest_session_id || !hours) { - return res.status(400).json({ - success: false, - message: 'ν•„μˆ˜ 정보가 λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); + if (!transfer_type || !worker_id || !source_session_id || !dest_session_id || !hours) { + return res.status(400).json({ success: false, message: 'ν•„μˆ˜ 정보가 λˆ„λ½λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } + + const today = new Date(); + const transferDate = today.getFullYear() + '-' + + String(today.getMonth() + 1).padStart(2, '0') + '-' + + String(today.getDate()).padStart(2, '0'); + + const transferData = { + transfer_type, worker_id, source_session_id, dest_session_id, + hours, initiated_by: req.user.user_id, transfer_date: transferDate, + project_id, work_type_id, task_id, workplace_category_id, workplace_id + }; + + const result = await TbmTransferModel.createTransfer(transferData); + if (!result.success) { + return res.status(400).json(result); + } + + res.json({ success: true, message: '이동이 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', data: result }); + } catch (err) { + logger.error('이동 μ‹€ν–‰ 였λ₯˜:', err); + res.status(500).json({ success: false, message: '이동 μ‹€ν–‰ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); } + }, - const today = new Date(); - const transferDate = today.getFullYear() + '-' + - String(today.getMonth() + 1).padStart(2, '0') + '-' + - String(today.getDate()).padStart(2, '0'); - - const transferData = { - transfer_type, worker_id, source_session_id, dest_session_id, - hours, initiated_by: req.user.user_id, transfer_date: transferDate, - project_id, work_type_id, task_id, workplace_category_id, workplace_id - }; - - TbmTransferModel.createTransfer(transferData, (err, result) => { - if (err) { - console.error('이동 μ‹€ν–‰ 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: '이동 μ‹€ν–‰ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } + getTransfersByDate: async (req, res) => { + try { + const results = await TbmTransferModel.getTransfersByDate(req.params.date); + res.json({ success: true, data: results }); + } catch (err) { + logger.error('이동 λ‚΄μ—­ 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: '이동 λ‚΄μ—­ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } + }, + cancelTransfer: async (req, res) => { + try { + const result = await TbmTransferModel.cancelTransfer(req.params.transferId); if (!result.success) { return res.status(400).json(result); } - - res.json({ - success: true, - message: '이동이 μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: result - }); - }); + res.json({ success: true, message: '이동이 μ·¨μ†Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('이동 μ·¨μ†Œ 였λ₯˜:', err); + res.status(500).json({ success: false, message: '이동 μ·¨μ†Œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, - /** - * 당일 이동 λ‚΄μ—­ 쑰회 - */ - getTransfersByDate: (req, res) => { - const { date } = req.params; - - TbmTransferModel.getTransfersByDate(date, (err, results) => { - if (err) { - console.error('이동 λ‚΄μ—­ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: '이동 λ‚΄μ—­ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - + getWorkerAssignmentsByDate: async (req, res) => { + try { + const results = await TbmTransferModel.getWorkerAssignmentsByDate(req.params.date); res.json({ success: true, data: results }); - }); + } catch (err) { + logger.error('λ°°μ • ν˜„ν™© 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'λ°°μ • ν˜„ν™© 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } }, - /** - * 이동 μ·¨μ†Œ (원볡) - */ - cancelTransfer: (req, res) => { - const { transferId } = req.params; - - TbmTransferModel.cancelTransfer(transferId, (err, result) => { - if (err) { - console.error('이동 μ·¨μ†Œ 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: '이동 μ·¨μ†Œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - - if (!result.success) { - return res.status(400).json(result); - } - - res.json({ - success: true, - message: '이동이 μ·¨μ†Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); - }, - - /** - * 당일 μ „ μž‘μ—…μž λ°°μ • ν˜„ν™© - */ - getWorkerAssignmentsByDate: (req, res) => { - const { date } = req.params; - - TbmTransferModel.getWorkerAssignmentsByDate(date, (err, results) => { - if (err) { - console.error('λ°°μ • ν˜„ν™© 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'λ°°μ • ν˜„ν™© 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } + getIncompleteWorkReports: async (req, res) => { + try { + const userId = req.user.user_id; + const accessLevel = req.user.access_level; + const filterUserId = (accessLevel === 'system' || accessLevel === 'admin') ? null : userId; + const results = await TbmModel.getIncompleteWorkReports(filterUserId); 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 - }); - }); + } catch (err) { + logger.error('λ―Έμ™„λ£Œ μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'λ―Έμ™„λ£Œ μž‘μ—…λ³΄κ³ μ„œ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', error: err.message }); + } } }; diff --git a/system1-factory/api/controllers/vacationBalanceController.js b/system1-factory/api/controllers/vacationBalanceController.js index 4eeaa3a..2231dc7 100644 --- a/system1-factory/api/controllers/vacationBalanceController.js +++ b/system1-factory/api/controllers/vacationBalanceController.js @@ -5,6 +5,7 @@ const vacationBalanceModel = require('../models/vacationBalanceModel'); const vacationTypeModel = require('../models/vacationTypeModel'); +const logger = require('../utils/logger'); const vacationBalanceController = { /** @@ -14,27 +15,11 @@ const vacationBalanceController = { async getByWorkerAndYear(req, res) { try { const { workerId, year } = req.params; - - vacationBalanceModel.getByWorkerAndYear(workerId, year, (err, results) => { - if (err) { - console.error('νœ΄κ°€ μž”μ•‘ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'νœ΄κ°€ μž”μ•‘μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - res.json({ - success: true, - data: results - }); - }); + const results = await vacationBalanceModel.getByWorkerAndYear(workerId, year); + res.json({ success: true, data: results }); } catch (error) { - console.error('getByWorkerAndYear 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('νœ΄κ°€ μž”μ•‘ 쑰회 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'νœ΄κ°€ μž”μ•‘μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -45,27 +30,11 @@ const vacationBalanceController = { async getAllByYear(req, res) { try { const { year } = req.params; - - vacationBalanceModel.getAllByYear(year, (err, results) => { - if (err) { - console.error('전체 νœ΄κ°€ μž”μ•‘ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: '전체 νœ΄κ°€ μž”μ•‘μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - res.json({ - success: true, - data: results - }); - }); + const results = await vacationBalanceModel.getAllByYear(year); + res.json({ success: true, data: results }); } catch (error) { - console.error('getAllByYear 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('전체 νœ΄κ°€ μž”μ•‘ 쑰회 였λ₯˜:', error); + res.status(500).json({ success: false, message: '전체 νœ΄κ°€ μž”μ•‘μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -75,17 +44,9 @@ const vacationBalanceController = { */ async createBalance(req, res) { try { - const { - worker_id, - vacation_type_id, - year, - total_days, - used_days, - notes - } = req.body; + const { worker_id, vacation_type_id, year, total_days, used_days, notes } = req.body; const created_by = req.user.user_id; - // ν•„μˆ˜ ν•„λ“œ 검증 if (!worker_id || !vacation_type_id || !year || total_days === undefined) { return res.status(400).json({ success: false, @@ -94,54 +55,33 @@ const vacationBalanceController = { } // 쀑볡 체크 - vacationBalanceModel.getByWorkerTypeYear(worker_id, vacation_type_id, year, (err, existing) => { - if (err) { - console.error('쀑볡 체크 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: '쀑볡 체크 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - if (existing && existing.length > 0) { - return res.status(400).json({ - success: false, - message: '이미 ν•΄λ‹Ή μž‘μ—…μžμ˜ ν•΄λ‹Ή 연도 νœ΄κ°€ μž”μ•‘μ΄ μ‘΄μž¬ν•©λ‹ˆλ‹€' - }); - } - - const balanceData = { - worker_id, - vacation_type_id, - year, - total_days, - used_days: used_days || 0, - notes: notes || null, - created_by - }; - - vacationBalanceModel.create(balanceData, (err, result) => { - if (err) { - console.error('νœ΄κ°€ μž”μ•‘ 생성 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'νœ΄κ°€ μž”μ•‘μ„ μƒμ„±ν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - res.status(201).json({ - success: true, - message: 'νœ΄κ°€ μž”μ•‘μ΄ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€', - data: { id: result.insertId } - }); + const existing = await vacationBalanceModel.getByWorkerTypeYear(worker_id, vacation_type_id, year); + if (existing && existing.length > 0) { + return res.status(400).json({ + success: false, + message: '이미 ν•΄λ‹Ή μž‘μ—…μžμ˜ ν•΄λ‹Ή 연도 νœ΄κ°€ μž”μ•‘μ΄ μ‘΄μž¬ν•©λ‹ˆλ‹€' }); + } + + const balanceData = { + worker_id, + vacation_type_id, + year, + total_days, + used_days: used_days || 0, + notes: notes || null, + created_by + }; + + const result = await vacationBalanceModel.create(balanceData); + res.status(201).json({ + success: true, + message: 'νœ΄κ°€ μž”μ•‘μ΄ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€', + data: { id: result.insertId } }); } catch (error) { - console.error('createBalance 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('νœ΄κ°€ μž”μ•‘ 생성 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -161,39 +101,18 @@ const vacationBalanceController = { updateData.updated_at = new Date(); if (Object.keys(updateData).length === 1) { - return res.status(400).json({ - success: false, - message: 'μˆ˜μ •ν•  데이터가 μ—†μŠ΅λ‹ˆλ‹€' - }); + return res.status(400).json({ success: false, message: 'μˆ˜μ •ν•  데이터가 μ—†μŠ΅λ‹ˆλ‹€' }); } - vacationBalanceModel.update(id, updateData, (err, result) => { - if (err) { - console.error('νœ΄κ°€ μž”μ•‘ μˆ˜μ • 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'νœ΄κ°€ μž”μ•‘μ„ μˆ˜μ •ν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } + const result = await vacationBalanceModel.update(id, updateData); + if (result.affectedRows === 0) { + return res.status(404).json({ success: false, message: 'νœ΄κ°€ μž”μ•‘μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€' }); + } - if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'νœ΄κ°€ μž”μ•‘μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€' - }); - } - - res.json({ - success: true, - message: 'νœ΄κ°€ μž”μ•‘μ΄ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€' - }); - }); + res.json({ success: true, message: 'νœ΄κ°€ μž”μ•‘μ΄ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€' }); } catch (error) { - console.error('updateBalance 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('νœ΄κ°€ μž”μ•‘ μˆ˜μ • 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -204,34 +123,16 @@ const vacationBalanceController = { async deleteBalance(req, res) { try { const { id } = req.params; + const result = await vacationBalanceModel.delete(id); - vacationBalanceModel.delete(id, (err, result) => { - if (err) { - console.error('νœ΄κ°€ μž”μ•‘ μ‚­μ œ 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'νœ΄κ°€ μž”μ•‘μ„ μ‚­μ œν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } + if (result.affectedRows === 0) { + return res.status(404).json({ success: false, message: 'νœ΄κ°€ μž”μ•‘μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€' }); + } - if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'νœ΄κ°€ μž”μ•‘μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€' - }); - } - - res.json({ - success: true, - message: 'νœ΄κ°€ μž”μ•‘μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€' - }); - }); + res.json({ success: true, message: 'νœ΄κ°€ μž”μ•‘μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€' }); } catch (error) { - console.error('deleteBalance 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('νœ΄κ°€ μž”μ•‘ μ‚­μ œ 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -251,74 +152,44 @@ const vacationBalanceController = { }); } - // μ—°μ°¨ 일수 계산 const annualDays = vacationBalanceModel.calculateAnnualLeaveDays(hire_date, year); // ANNUAL νœ΄κ°€ μœ ν˜• ID 쑰회 - vacationTypeModel.getByCode('ANNUAL', (err, types) => { - if (err || !types || types.length === 0) { - console.error('ANNUAL νœ΄κ°€ μœ ν˜• 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'ANNUAL νœ΄κ°€ μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€' - }); - } + const types = await vacationTypeModel.getByCode('ANNUAL'); + if (!types || types.length === 0) { + return res.status(500).json({ success: false, message: 'ANNUAL νœ΄κ°€ μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€' }); + } - const annualTypeId = types[0].id; + const annualTypeId = types[0].id; - // 쀑볡 체크 - vacationBalanceModel.getByWorkerTypeYear(worker_id, annualTypeId, year, (err, existing) => { - if (err) { - console.error('쀑볡 체크 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: '쀑볡 체크 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - if (existing && existing.length > 0) { - return res.status(400).json({ - success: false, - message: '이미 ν•΄λ‹Ή μž‘μ—…μžμ˜ ν•΄λ‹Ή 연도 μ—°μ°¨κ°€ μ‘΄μž¬ν•©λ‹ˆλ‹€' - }); - } - - const balanceData = { - worker_id, - vacation_type_id: annualTypeId, - year, - total_days: annualDays, - used_days: 0, - notes: `κ·Όμ†λ…„μˆ˜ 기반 μžλ™ 계산 (μž…μ‚¬μΌ: ${hire_date})`, - created_by - }; - - vacationBalanceModel.create(balanceData, (err, result) => { - if (err) { - console.error('νœ΄κ°€ μž”μ•‘ 생성 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'νœ΄κ°€ μž”μ•‘μ„ μƒμ„±ν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - res.status(201).json({ - success: true, - message: `${annualDays}일의 μ—°μ°¨κ°€ μžλ™μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€`, - data: { - id: result.insertId, - calculated_days: annualDays - } - }); - }); + // 쀑볡 체크 + const existing = await vacationBalanceModel.getByWorkerTypeYear(worker_id, annualTypeId, year); + if (existing && existing.length > 0) { + return res.status(400).json({ + success: false, + message: '이미 ν•΄λ‹Ή μž‘μ—…μžμ˜ ν•΄λ‹Ή 연도 μ—°μ°¨κ°€ μ‘΄μž¬ν•©λ‹ˆλ‹€' }); + } + + const balanceData = { + worker_id, + vacation_type_id: annualTypeId, + year, + total_days: annualDays, + used_days: 0, + notes: `κ·Όμ†λ…„μˆ˜ 기반 μžλ™ 계산 (μž…μ‚¬μΌ: ${hire_date})`, + created_by + }; + + const result = await vacationBalanceModel.create(balanceData); + res.status(201).json({ + success: true, + message: `${annualDays}일의 μ—°μ°¨κ°€ μžλ™μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€`, + data: { id: result.insertId, calculated_days: annualDays } }); } catch (error) { - console.error('autoCalculateAndCreate 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('μ—°μ°¨ μžλ™ 계산 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -332,10 +203,7 @@ const vacationBalanceController = { const created_by = req.user.user_id; if (!balances || !Array.isArray(balances) || balances.length === 0) { - return res.status(400).json({ - success: false, - message: 'μ €μž₯ν•  데이터가 μ—†μŠ΅λ‹ˆλ‹€' - }); + return res.status(400).json({ success: false, message: 'μ €μž₯ν•  데이터가 μ—†μŠ΅λ‹ˆλ‹€' }); } const { getDb } = require('../dbPool'); @@ -353,7 +221,6 @@ const vacationBalanceController = { } try { - // Upsert 쿼리 const query = ` INSERT INTO vacation_balance_details (worker_id, vacation_type_id, year, total_days, used_days, notes, created_by) @@ -367,7 +234,7 @@ const vacationBalanceController = { await db.query(query, [worker_id, vacation_type_id, year, total_days, notes || null, created_by]); successCount++; } catch (err) { - console.error('νœ΄κ°€ μž”μ•‘ μ €μž₯ 였λ₯˜:', err); + logger.error('νœ΄κ°€ μž”μ•‘ μ €μž₯ 였λ₯˜:', err); errorCount++; } } @@ -378,11 +245,8 @@ const vacationBalanceController = { data: { successCount, errorCount } }); } catch (error) { - console.error('bulkUpsert 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('bulkUpsert 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -393,27 +257,11 @@ const vacationBalanceController = { async getAvailableDays(req, res) { try { const { workerId, year } = req.params; - - vacationBalanceModel.getAvailableVacationDays(workerId, year, (err, results) => { - if (err) { - console.error('μ‚¬μš© κ°€λŠ₯ νœ΄κ°€ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μ‚¬μš© κ°€λŠ₯ νœ΄κ°€λ₯Ό μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - res.json({ - success: true, - data: results - }); - }); + const results = await vacationBalanceModel.getAvailableVacationDays(workerId, year); + res.json({ success: true, data: results }); } catch (error) { - console.error('getAvailableDays 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('μ‚¬μš© κ°€λŠ₯ νœ΄κ°€ 쑰회 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'μ‚¬μš© κ°€λŠ₯ νœ΄κ°€λ₯Ό μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } } }; diff --git a/system1-factory/api/controllers/vacationTypeController.js b/system1-factory/api/controllers/vacationTypeController.js index 9eddaeb..da56ee6 100644 --- a/system1-factory/api/controllers/vacationTypeController.js +++ b/system1-factory/api/controllers/vacationTypeController.js @@ -4,6 +4,7 @@ */ const vacationTypeModel = require('../models/vacationTypeModel'); +const logger = require('../utils/logger'); const vacationTypeController = { /** @@ -12,26 +13,11 @@ const vacationTypeController = { */ async getAllTypes(req, res) { try { - vacationTypeModel.getAll((err, results) => { - if (err) { - console.error('νœ΄κ°€ μœ ν˜• 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'νœ΄κ°€ μœ ν˜•μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - res.json({ - success: true, - data: results - }); - }); + const results = await vacationTypeModel.getAll(); + res.json({ success: true, data: results }); } catch (error) { - console.error('getAllTypes 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('νœ΄κ°€ μœ ν˜• 쑰회 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'νœ΄κ°€ μœ ν˜•μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -41,26 +27,11 @@ const vacationTypeController = { */ async getSystemTypes(req, res) { try { - vacationTypeModel.getSystemTypes((err, results) => { - if (err) { - console.error('μ‹œμŠ€ν…œ νœ΄κ°€ μœ ν˜• 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μ‹œμŠ€ν…œ νœ΄κ°€ μœ ν˜•μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - res.json({ - success: true, - data: results - }); - }); + const results = await vacationTypeModel.getSystemTypes(); + res.json({ success: true, data: results }); } catch (error) { - console.error('getSystemTypes 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('μ‹œμŠ€ν…œ νœ΄κ°€ μœ ν˜• 쑰회 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'μ‹œμŠ€ν…œ νœ΄κ°€ μœ ν˜•μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -70,26 +41,11 @@ const vacationTypeController = { */ async getSpecialTypes(req, res) { try { - vacationTypeModel.getSpecialTypes((err, results) => { - if (err) { - console.error('νŠΉλ³„ νœ΄κ°€ μœ ν˜• 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'νŠΉλ³„ νœ΄κ°€ μœ ν˜•μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - res.json({ - success: true, - data: results - }); - }); + const results = await vacationTypeModel.getSpecialTypes(); + res.json({ success: true, data: results }); } catch (error) { - console.error('getSpecialTypes 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('νŠΉλ³„ νœ΄κ°€ μœ ν˜• 쑰회 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'νŠΉλ³„ νœ΄κ°€ μœ ν˜•μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -99,15 +55,8 @@ const vacationTypeController = { */ async createType(req, res) { try { - const { - type_code, - type_name, - deduct_days, - priority, - description - } = req.body; + const { type_code, type_name, deduct_days, priority, description } = req.body; - // ν•„μˆ˜ ν•„λ“œ 검증 if (!type_code || !type_name || !deduct_days) { return res.status(400).json({ success: false, @@ -116,56 +65,31 @@ const vacationTypeController = { } // type_code 쀑볡 체크 - vacationTypeModel.getByCode(type_code, (err, existingTypes) => { - if (err) { - console.error('type_code 쀑볡 체크 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'type_code 쀑볡 체크 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } + const existingTypes = await vacationTypeModel.getByCode(type_code); + if (existingTypes && existingTypes.length > 0) { + return res.status(400).json({ success: false, message: '이미 μ‘΄μž¬ν•˜λŠ” type_codeμž…λ‹ˆλ‹€' }); + } - if (existingTypes && existingTypes.length > 0) { - return res.status(400).json({ - success: false, - message: '이미 μ‘΄μž¬ν•˜λŠ” type_codeμž…λ‹ˆλ‹€' - }); - } + const typeData = { + type_code, + type_name, + deduct_days, + priority: priority || 50, + description: description || null, + is_special: true, + is_system: false, + is_active: true + }; - // νŠΉλ³„ νœ΄κ°€ μœ ν˜•μœΌλ‘œ 생성 - const typeData = { - type_code, - type_name, - deduct_days, - priority: priority || 50, - description: description || null, - is_special: true, - is_system: false, - is_active: true - }; - - vacationTypeModel.create(typeData, (err, result) => { - if (err) { - console.error('νœ΄κ°€ μœ ν˜• 생성 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'νœ΄κ°€ μœ ν˜•μ„ μƒμ„±ν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - res.status(201).json({ - success: true, - message: 'νŠΉλ³„ νœ΄κ°€ μœ ν˜•μ΄ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€', - data: { id: result.insertId } - }); - }); + const result = await vacationTypeModel.create(typeData); + res.status(201).json({ + success: true, + message: 'νŠΉλ³„ νœ΄κ°€ μœ ν˜•μ΄ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€', + data: { id: result.insertId } }); } catch (error) { - console.error('createType 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('νœ΄κ°€ μœ ν˜• 생성 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -176,78 +100,37 @@ const vacationTypeController = { async updateType(req, res) { try { const { id } = req.params; - const { - type_name, - deduct_days, - priority, - description, - is_active - } = req.body; + const { type_name, deduct_days, priority, description, is_active } = req.body; - // λ¨Όμ € ν•΄λ‹Ή μœ ν˜• 쑰회 - vacationTypeModel.getById(id, (err, types) => { - if (err) { - console.error('νœ΄κ°€ μœ ν˜• 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'νœ΄κ°€ μœ ν˜•μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } + const types = await vacationTypeModel.getById(id); + if (!types || types.length === 0) { + return res.status(404).json({ success: false, message: 'νœ΄κ°€ μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€' }); + } - if (!types || types.length === 0) { - return res.status(404).json({ - success: false, - message: 'νœ΄κ°€ μœ ν˜•μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€' - }); - } + const type = types[0]; + const updateData = {}; - const type = types[0]; + if (type.is_system) { + if (priority !== undefined) updateData.priority = priority; + if (description !== undefined) updateData.description = description; + } else { + if (type_name) updateData.type_name = type_name; + if (deduct_days !== undefined) updateData.deduct_days = deduct_days; + if (priority !== undefined) updateData.priority = priority; + if (description !== undefined) updateData.description = description; + if (is_active !== undefined) updateData.is_active = is_active; + } - // μ‹œμŠ€ν…œ κΈ°λ³Έ νœ΄κ°€μ˜ 경우 μ œν•œμ μœΌλ‘œλ§Œ μˆ˜μ • κ°€λŠ₯ - const updateData = {}; - if (type.is_system) { - // μ‹œμŠ€ν…œ νœ΄κ°€λŠ” priority와 description만 μˆ˜μ • κ°€λŠ₯ - if (priority !== undefined) updateData.priority = priority; - if (description !== undefined) updateData.description = description; - } else { - // νŠΉλ³„ νœ΄κ°€λŠ” λͺ¨λ“  ν•„λ“œ μˆ˜μ • κ°€λŠ₯ - if (type_name) updateData.type_name = type_name; - if (deduct_days !== undefined) updateData.deduct_days = deduct_days; - if (priority !== undefined) updateData.priority = priority; - if (description !== undefined) updateData.description = description; - if (is_active !== undefined) updateData.is_active = is_active; - } + if (Object.keys(updateData).length === 0) { + return res.status(400).json({ success: false, message: 'μˆ˜μ •ν•  데이터가 μ—†μŠ΅λ‹ˆλ‹€' }); + } - if (Object.keys(updateData).length === 0) { - return res.status(400).json({ - success: false, - message: 'μˆ˜μ •ν•  데이터가 μ—†μŠ΅λ‹ˆλ‹€' - }); - } - - updateData.updated_at = new Date(); - - vacationTypeModel.update(id, updateData, (err, result) => { - if (err) { - console.error('νœ΄κ°€ μœ ν˜• μˆ˜μ • 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'νœ΄κ°€ μœ ν˜•μ„ μˆ˜μ •ν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - res.json({ - success: true, - message: 'νœ΄κ°€ μœ ν˜•μ΄ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€' - }); - }); - }); + updateData.updated_at = new Date(); + await vacationTypeModel.update(id, updateData); + res.json({ success: true, message: 'νœ΄κ°€ μœ ν˜•μ΄ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€' }); } catch (error) { - console.error('updateType 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('νœ΄κ°€ μœ ν˜• μˆ˜μ • 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -258,34 +141,19 @@ const vacationTypeController = { async deleteType(req, res) { try { const { id } = req.params; + const result = await vacationTypeModel.delete(id); - vacationTypeModel.delete(id, (err, result) => { - if (err) { - console.error('νœ΄κ°€ μœ ν˜• μ‚­μ œ 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'νœ΄κ°€ μœ ν˜•μ„ μ‚­μ œν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } - - if (result.affectedRows === 0) { - return res.status(400).json({ - success: false, - message: 'μ‚­μ œν•  수 μ—†μŠ΅λ‹ˆλ‹€. μ‹œμŠ€ν…œ κΈ°λ³Έ νœ΄κ°€μ΄κ±°λ‚˜ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” νœ΄κ°€ μœ ν˜•μž…λ‹ˆλ‹€' - }); - } - - res.json({ - success: true, - message: 'νœ΄κ°€ μœ ν˜•μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€' + if (result.affectedRows === 0) { + return res.status(400).json({ + success: false, + message: 'μ‚­μ œν•  수 μ—†μŠ΅λ‹ˆλ‹€. μ‹œμŠ€ν…œ κΈ°λ³Έ νœ΄κ°€μ΄κ±°λ‚˜ μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” νœ΄κ°€ μœ ν˜•μž…λ‹ˆλ‹€' }); - }); + } + + res.json({ success: true, message: 'νœ΄κ°€ μœ ν˜•μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€' }); } catch (error) { - console.error('deleteType 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('νœ΄κ°€ μœ ν˜• μ‚­μ œ 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } }, @@ -297,35 +165,22 @@ const vacationTypeController = { try { const { priorities } = req.body; - // priorities = [{ id: 1, priority: 10 }, { id: 2, priority: 20 }, ...] if (!priorities || !Array.isArray(priorities)) { - return res.status(400).json({ - success: false, - message: 'priorities 배열이 ν•„μš”ν•©λ‹ˆλ‹€' - }); + return res.status(400).json({ success: false, message: 'priorities 배열이 ν•„μš”ν•©λ‹ˆλ‹€' }); } - vacationTypeModel.updatePriorities(priorities, (err, result) => { - if (err) { - console.error('μš°μ„ μˆœμœ„ μ—…λ°μ΄νŠΈ 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μš°μ„ μˆœμœ„λ₯Ό μ—…λ°μ΄νŠΈν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); - } + for (const { id, priority } of priorities) { + await vacationTypeModel.updatePriority(id, priority); + } - res.json({ - success: true, - message: 'μš°μ„ μˆœμœ„κ°€ μ—…λ°μ΄νŠΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€', - data: { updated: result.affectedRows } - }); + res.json({ + success: true, + message: 'μš°μ„ μˆœμœ„κ°€ μ—…λ°μ΄νŠΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€', + data: { updated: priorities.length } }); } catch (error) { - console.error('updatePriorities 였λ₯˜:', error); - res.status(500).json({ - success: false, - message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' - }); + logger.error('μš°μ„ μˆœμœ„ μ—…λ°μ΄νŠΈ 였λ₯˜:', error); + res.status(500).json({ success: false, message: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€' }); } } }; diff --git a/system1-factory/api/controllers/visitRequestController.js b/system1-factory/api/controllers/visitRequestController.js index 8a7e5b6..948be3b 100644 --- a/system1-factory/api/controllers/visitRequestController.js +++ b/system1-factory/api/controllers/visitRequestController.js @@ -1,555 +1,284 @@ const visitRequestModel = require('../models/visitRequestModel'); +const logger = require('../utils/logger'); // ==================== μΆœμž… μ‹ μ²­ 관리 ==================== -/** - * μΆœμž… μ‹ μ²­ 생성 - */ -exports.createVisitRequest = (req, res) => { - const requester_id = req.user.user_id; - const requestData = { - requester_id, - ...req.body - }; +exports.createVisitRequest = async (req, res) => { + try { + const requester_id = req.user.user_id; + const requestData = { requester_id, ...req.body }; - // ν•„μˆ˜ ν•„λ“œ 검증 - const requiredFields = ['visitor_company', 'category_id', 'workplace_id', 'visit_date', 'visit_time', 'purpose_id']; - for (const field of requiredFields) { - if (!requestData[field]) { - return res.status(400).json({ - success: false, - message: `${field}λŠ” ν•„μˆ˜ μž…λ ₯ ν•­λͺ©μž…λ‹ˆλ‹€.` - }); - } - } - - visitRequestModel.createVisitRequest(requestData, (err, requestId) => { - if (err) { - console.error('μΆœμž… μ‹ μ²­ 생성 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μΆœμž… μ‹ μ²­ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); + const requiredFields = ['visitor_company', 'category_id', 'workplace_id', 'visit_date', 'visit_time', 'purpose_id']; + for (const field of requiredFields) { + if (!requestData[field]) { + return res.status(400).json({ success: false, message: `${field}λŠ” ν•„μˆ˜ μž…λ ₯ ν•­λͺ©μž…λ‹ˆλ‹€.` }); + } } + const requestId = await visitRequestModel.createVisitRequest(requestData); res.status(201).json({ success: true, message: 'μΆœμž… 신청이 μ„±κ³΅μ μœΌλ‘œ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', data: { request_id: requestId } }); - }); + } catch (err) { + logger.error('μΆœμž… μ‹ μ²­ 생성 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μΆœμž… μ‹ μ²­ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; -/** - * μΆœμž… μ‹ μ²­ λͺ©λ‘ 쑰회 - */ -exports.getAllVisitRequests = (req, res) => { - const filters = { - status: req.query.status, - visit_date: req.query.visit_date, - start_date: req.query.start_date, - end_date: req.query.end_date, - requester_id: req.query.requester_id, - category_id: req.query.category_id - }; +exports.getAllVisitRequests = async (req, res) => { + try { + const filters = { + status: req.query.status, + visit_date: req.query.visit_date, + start_date: req.query.start_date, + end_date: req.query.end_date, + requester_id: req.query.requester_id, + category_id: req.query.category_id + }; - visitRequestModel.getAllVisitRequests(filters, (err, requests) => { - if (err) { - console.error('μΆœμž… μ‹ μ²­ λͺ©λ‘ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μΆœμž… μ‹ μ²­ λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - - res.json({ - success: true, - data: requests - }); - }); + const requests = await visitRequestModel.getAllVisitRequests(filters); + res.json({ success: true, data: requests }); + } catch (err) { + logger.error('μΆœμž… μ‹ μ²­ λͺ©λ‘ 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μΆœμž… μ‹ μ²­ λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; -/** - * μΆœμž… μ‹ μ²­ 상세 쑰회 - */ -exports.getVisitRequestById = (req, res) => { - const requestId = req.params.id; - - visitRequestModel.getVisitRequestById(requestId, (err, request) => { - if (err) { - console.error('μΆœμž… μ‹ μ²­ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μΆœμž… μ‹ μ²­ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - +exports.getVisitRequestById = async (req, res) => { + try { + const request = await visitRequestModel.getVisitRequestById(req.params.id); if (!request) { - return res.status(404).json({ - success: false, - message: 'μΆœμž… 신청을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'μΆœμž… 신청을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - - res.json({ - success: true, - data: request - }); - }); + res.json({ success: true, data: request }); + } catch (err) { + logger.error('μΆœμž… μ‹ μ²­ 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μΆœμž… μ‹ μ²­ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; -/** - * μΆœμž… μ‹ μ²­ μˆ˜μ • - */ -exports.updateVisitRequest = (req, res) => { - const requestId = req.params.id; - const requestData = req.body; - - visitRequestModel.updateVisitRequest(requestId, requestData, (err, result) => { - if (err) { - console.error('μΆœμž… μ‹ μ²­ μˆ˜μ • 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μΆœμž… μ‹ μ²­ μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - +exports.updateVisitRequest = async (req, res) => { + try { + const result = await visitRequestModel.updateVisitRequest(req.params.id, req.body); if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'μΆœμž… 신청을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'μΆœμž… 신청을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - - res.json({ - success: true, - message: 'μΆœμž… 신청이 μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); + res.json({ success: true, message: 'μΆœμž… 신청이 μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μΆœμž… μ‹ μ²­ μˆ˜μ • 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μΆœμž… μ‹ μ²­ μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; -/** - * μΆœμž… μ‹ μ²­ μ‚­μ œ - */ -exports.deleteVisitRequest = (req, res) => { - const requestId = req.params.id; - - visitRequestModel.deleteVisitRequest(requestId, (err, result) => { - if (err) { - console.error('μΆœμž… μ‹ μ²­ μ‚­μ œ 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μΆœμž… μ‹ μ²­ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - +exports.deleteVisitRequest = async (req, res) => { + try { + const result = await visitRequestModel.deleteVisitRequest(req.params.id); if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'μΆœμž… 신청을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'μΆœμž… 신청을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - - res.json({ - success: true, - message: 'μΆœμž… 신청이 μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); + res.json({ success: true, message: 'μΆœμž… 신청이 μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μΆœμž… μ‹ μ²­ μ‚­μ œ 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μΆœμž… μ‹ μ²­ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; -/** - * μΆœμž… μ‹ μ²­ 승인 - */ -exports.approveVisitRequest = (req, res) => { - const requestId = req.params.id; - const approvedBy = req.user.user_id; - - visitRequestModel.approveVisitRequest(requestId, approvedBy, (err, result) => { - if (err) { - console.error('μΆœμž… μ‹ μ²­ 승인 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μΆœμž… μ‹ μ²­ 승인 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - +exports.approveVisitRequest = async (req, res) => { + try { + const result = await visitRequestModel.approveVisitRequest(req.params.id, req.user.user_id); if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'μΆœμž… 신청을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'μΆœμž… 신청을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - - res.json({ - success: true, - message: 'μΆœμž… 신청이 μŠΉμΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); + res.json({ success: true, message: 'μΆœμž… 신청이 μŠΉμΈλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μΆœμž… μ‹ μ²­ 승인 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μΆœμž… μ‹ μ²­ 승인 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; -/** - * μΆœμž… μ‹ μ²­ 반렀 - */ -exports.rejectVisitRequest = (req, res) => { - const requestId = req.params.id; - const approvedBy = req.user.user_id; - const rejectionReason = req.body.rejection_reason || 'μ‚¬μœ  μ—†μŒ'; - - const rejectionData = { - approved_by: approvedBy, - rejection_reason: rejectionReason - }; - - visitRequestModel.rejectVisitRequest(requestId, rejectionData, (err, result) => { - if (err) { - console.error('μΆœμž… μ‹ μ²­ 반렀 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μΆœμž… μ‹ μ²­ 반렀 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - +exports.rejectVisitRequest = async (req, res) => { + try { + const rejectionData = { + approved_by: req.user.user_id, + rejection_reason: req.body.rejection_reason || 'μ‚¬μœ  μ—†μŒ' + }; + const result = await visitRequestModel.rejectVisitRequest(req.params.id, rejectionData); if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'μΆœμž… 신청을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'μΆœμž… 신청을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - - res.json({ - success: true, - message: 'μΆœμž… 신청이 λ°˜λ €λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); + res.json({ success: true, message: 'μΆœμž… 신청이 λ°˜λ €λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μΆœμž… μ‹ μ²­ 반렀 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μΆœμž… μ‹ μ²­ 반렀 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; // ==================== λ°©λ¬Έ λͺ©μ  관리 ==================== -/** - * λͺ¨λ“  λ°©λ¬Έ λͺ©μ  쑰회 - */ -exports.getAllVisitPurposes = (req, res) => { - visitRequestModel.getAllVisitPurposes((err, purposes) => { - if (err) { - console.error('λ°©λ¬Έ λͺ©μ  쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'λ°©λ¬Έ λͺ©μ  쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - - res.json({ - success: true, - data: purposes - }); - }); -}; - -/** - * ν™œμ„± λ°©λ¬Έ λͺ©μ λ§Œ 쑰회 - */ -exports.getActiveVisitPurposes = (req, res) => { - visitRequestModel.getActiveVisitPurposes((err, purposes) => { - if (err) { - console.error('ν™œμ„± λ°©λ¬Έ λͺ©μ  쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'ν™œμ„± λ°©λ¬Έ λͺ©μ  쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - - res.json({ - success: true, - data: purposes - }); - }); -}; - -/** - * λ°©λ¬Έ λͺ©μ  μΆ”κ°€ - */ -exports.createVisitPurpose = (req, res) => { - const purposeData = req.body; - - if (!purposeData.purpose_name) { - return res.status(400).json({ - success: false, - message: 'purpose_name은 ν•„μˆ˜ μž…λ ₯ ν•­λͺ©μž…λ‹ˆλ‹€.' - }); +exports.getAllVisitPurposes = async (req, res) => { + try { + const purposes = await visitRequestModel.getAllVisitPurposes(); + res.json({ success: true, data: purposes }); + } catch (err) { + logger.error('λ°©λ¬Έ λͺ©μ  쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'λ°©λ¬Έ λͺ©μ  쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } +}; - visitRequestModel.createVisitPurpose(purposeData, (err, purposeId) => { - if (err) { - console.error('λ°©λ¬Έ λͺ©μ  μΆ”κ°€ 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'λ°©λ¬Έ λͺ©μ  μΆ”κ°€ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); +exports.getActiveVisitPurposes = async (req, res) => { + try { + const purposes = await visitRequestModel.getActiveVisitPurposes(); + res.json({ success: true, data: purposes }); + } catch (err) { + logger.error('ν™œμ„± λ°©λ¬Έ λͺ©μ  쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'ν™œμ„± λ°©λ¬Έ λͺ©μ  쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } +}; + +exports.createVisitPurpose = async (req, res) => { + try { + if (!req.body.purpose_name) { + return res.status(400).json({ success: false, message: 'purpose_name은 ν•„μˆ˜ μž…λ ₯ ν•­λͺ©μž…λ‹ˆλ‹€.' }); } - + const purposeId = await visitRequestModel.createVisitPurpose(req.body); res.status(201).json({ success: true, message: 'λ°©λ¬Έ λͺ©μ μ΄ μΆ”κ°€λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', data: { purpose_id: purposeId } }); - }); + } catch (err) { + logger.error('λ°©λ¬Έ λͺ©μ  μΆ”κ°€ 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'λ°©λ¬Έ λͺ©μ  μΆ”κ°€ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; -/** - * λ°©λ¬Έ λͺ©μ  μˆ˜μ • - */ -exports.updateVisitPurpose = (req, res) => { - const purposeId = req.params.id; - const purposeData = req.body; - - visitRequestModel.updateVisitPurpose(purposeId, purposeData, (err, result) => { - if (err) { - console.error('λ°©λ¬Έ λͺ©μ  μˆ˜μ • 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'λ°©λ¬Έ λͺ©μ  μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - +exports.updateVisitPurpose = async (req, res) => { + try { + const result = await visitRequestModel.updateVisitPurpose(req.params.id, req.body); if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'λ°©λ¬Έ λͺ©μ μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'λ°©λ¬Έ λͺ©μ μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - - res.json({ - success: true, - message: 'λ°©λ¬Έ λͺ©μ μ΄ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); + res.json({ success: true, message: 'λ°©λ¬Έ λͺ©μ μ΄ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('λ°©λ¬Έ λͺ©μ  μˆ˜μ • 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'λ°©λ¬Έ λͺ©μ  μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; -/** - * λ°©λ¬Έ λͺ©μ  μ‚­μ œ - */ -exports.deleteVisitPurpose = (req, res) => { - const purposeId = req.params.id; - - visitRequestModel.deleteVisitPurpose(purposeId, (err, result) => { - if (err) { - console.error('λ°©λ¬Έ λͺ©μ  μ‚­μ œ 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'λ°©λ¬Έ λͺ©μ  μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - +exports.deleteVisitPurpose = async (req, res) => { + try { + const result = await visitRequestModel.deleteVisitPurpose(req.params.id); if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'λ°©λ¬Έ λͺ©μ μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'λ°©λ¬Έ λͺ©μ μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - - res.json({ - success: true, - message: 'λ°©λ¬Έ λͺ©μ μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); + res.json({ success: true, message: 'λ°©λ¬Έ λͺ©μ μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('λ°©λ¬Έ λͺ©μ  μ‚­μ œ 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'λ°©λ¬Έ λͺ©μ  μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; // ==================== μ•ˆμ „κ΅μœ‘ 기둝 관리 ==================== -/** - * μ•ˆμ „κ΅μœ‘ 기둝 생성 - */ -exports.createTrainingRecord = (req, res) => { - const trainerId = req.user.user_id; - const trainingData = { - trainer_id: trainerId, - ...req.body - }; +exports.createTrainingRecord = async (req, res) => { + try { + const trainingData = { trainer_id: req.user.user_id, ...req.body }; - // ν•„μˆ˜ ν•„λ“œ 검증 - const requiredFields = ['request_id', 'training_date', 'training_start_time']; - for (const field of requiredFields) { - if (!trainingData[field]) { - return res.status(400).json({ - success: false, - message: `${field}λŠ” ν•„μˆ˜ μž…λ ₯ ν•­λͺ©μž…λ‹ˆλ‹€.` - }); - } - } - - visitRequestModel.createTrainingRecord(trainingData, (err, trainingId) => { - if (err) { - console.error('μ•ˆμ „κ΅μœ‘ 기둝 생성 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μ•ˆμ „κ΅μœ‘ 기둝 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } - - // μ•ˆμ „κ΅μœ‘ 기둝이 μƒμ„±λ˜λ©΄ μΆœμž… μ‹ μ²­ μƒνƒœλ₯Ό training_completed둜 λ³€κ²½ - console.log(`[ꡐ윑 μ™„λ£Œ] request_id=${trainingData.request_id} μƒνƒœλ₯Ό training_completed둜 λ³€κ²½ 쀑...`); - visitRequestModel.updateVisitRequestStatus(trainingData.request_id, 'training_completed', (statusErr) => { - if (statusErr) { - console.error('μΆœμž… μ‹ μ²­ μƒνƒœ μ—…λ°μ΄νŠΈ 였λ₯˜:', statusErr); - // μ—λŸ¬κ°€ λ°œμƒν•΄λ„ ꡐ윑 기둝은 μƒμ„±λ˜μ—ˆμœΌλ―€λ‘œ 성곡 응닡 - } else { - console.log(`[ꡐ윑 μ™„λ£Œ] request_id=${trainingData.request_id} μƒνƒœ λ³€κ²½ 성곡`); + const requiredFields = ['request_id', 'training_date', 'training_start_time']; + for (const field of requiredFields) { + if (!trainingData[field]) { + return res.status(400).json({ success: false, message: `${field}λŠ” ν•„μˆ˜ μž…λ ₯ ν•­λͺ©μž…λ‹ˆλ‹€.` }); } - - res.status(201).json({ - success: true, - message: 'μ•ˆμ „κ΅μœ‘ 기둝이 μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: { training_id: trainingId } - }); - }); - }); -}; - -/** - * νŠΉμ • μΆœμž… μ‹ μ²­μ˜ μ•ˆμ „κ΅μœ‘ 기둝 쑰회 - */ -exports.getTrainingRecordByRequestId = (req, res) => { - const requestId = req.params.requestId; - - visitRequestModel.getTrainingRecordByRequestId(requestId, (err, record) => { - if (err) { - console.error('μ•ˆμ „κ΅μœ‘ 기둝 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μ•ˆμ „κ΅μœ‘ 기둝 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); } - res.json({ + const trainingId = await visitRequestModel.createTrainingRecord(trainingData); + + // μ•ˆμ „κ΅μœ‘ 기둝 생성 ν›„ μΆœμž… μ‹ μ²­ μƒνƒœλ₯Ό training_completed둜 λ³€κ²½ + try { + await visitRequestModel.updateVisitRequestStatus(trainingData.request_id, 'training_completed'); + } catch (statusErr) { + logger.error('μΆœμž… μ‹ μ²­ μƒνƒœ μ—…λ°μ΄νŠΈ 였λ₯˜:', statusErr); + } + + res.status(201).json({ success: true, - data: record || null - }); - }); -}; - -/** - * μ•ˆμ „κ΅μœ‘ 기둝 μˆ˜μ • - */ -exports.updateTrainingRecord = (req, res) => { - const trainingId = req.params.id; - const trainingData = req.body; - - visitRequestModel.updateTrainingRecord(trainingId, trainingData, (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: 'μ•ˆμ „κ΅μœ‘ 기둝이 μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); -}; - -/** - * μ•ˆμ „κ΅μœ‘ μ™„λ£Œ (μ„œλͺ… 포함) - */ -exports.completeTraining = (req, res) => { - const trainingId = req.params.id; - const signatureData = req.body.signature_data; - - if (!signatureData) { - return res.status(400).json({ - success: false, - message: 'μ„œλͺ… 데이터가 ν•„μš”ν•©λ‹ˆλ‹€.' + message: 'μ•ˆμ „κ΅μœ‘ 기둝이 μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: { training_id: trainingId } }); + } catch (err) { + logger.error('μ•ˆμ „κ΅μœ‘ 기둝 생성 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „κ΅μœ‘ 기둝 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } +}; - visitRequestModel.completeTraining(trainingId, signatureData, (err, result) => { - if (err) { - console.error('μ•ˆμ „κ΅μœ‘ μ™„λ£Œ 처리 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μ•ˆμ „κ΅μœ‘ μ™„λ£Œ 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); - } +exports.getTrainingRecordByRequestId = async (req, res) => { + try { + const record = await visitRequestModel.getTrainingRecordByRequestId(req.params.requestId); + res.json({ success: true, data: record || null }); + } catch (err) { + logger.error('μ•ˆμ „κ΅μœ‘ 기둝 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „κ΅μœ‘ 기둝 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } +}; +exports.updateTrainingRecord = async (req, res) => { + try { + const result = await visitRequestModel.updateTrainingRecord(req.params.id, req.body); if (result.affectedRows === 0) { - return res.status(404).json({ - success: false, - message: 'μ•ˆμ „κ΅μœ‘ 기둝을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' - }); + return res.status(404).json({ success: false, message: 'μ•ˆμ „κ΅μœ‘ 기둝을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); + } + res.json({ success: true, message: 'μ•ˆμ „κ΅μœ‘ 기둝이 μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μ•ˆμ „κ΅μœ‘ 기둝 μˆ˜μ • 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „κ΅μœ‘ 기둝 μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } +}; + +exports.completeTraining = async (req, res) => { + try { + const trainingId = req.params.id; + const signatureData = req.body.signature_data; + + if (!signatureData) { + return res.status(400).json({ success: false, message: 'μ„œλͺ… 데이터가 ν•„μš”ν•©λ‹ˆλ‹€.' }); } - // ꡐ윑 μ™„λ£Œ ν›„ μΆœμž… μ‹ μ²­ μƒνƒœλ₯Ό 'training_completed'둜 λ³€κ²½ - visitRequestModel.getTrainingRecordByRequestId(trainingId, (err, record) => { - if (err || !record) { - return res.json({ - success: true, - message: 'μ•ˆμ „κ΅μœ‘μ΄ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); + const result = await visitRequestModel.completeTraining(trainingId, signatureData); + if (result.affectedRows === 0) { + return res.status(404).json({ success: false, message: 'μ•ˆμ „κ΅μœ‘ 기둝을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); + } + + // ꡐ윑 μ™„λ£Œ ν›„ μΆœμž… μ‹ μ²­ μƒνƒœ λ³€κ²½ + try { + const record = await visitRequestModel.getTrainingRecordByRequestId(trainingId); + if (record) { + await visitRequestModel.updateVisitRequestStatus(record.request_id, 'training_completed'); } - - visitRequestModel.updateVisitRequestStatus(record.request_id, 'training_completed', (err) => { - if (err) { - console.error('μΆœμž… μ‹ μ²­ μƒνƒœ μ—…λ°μ΄νŠΈ 였λ₯˜:', err); - } - - res.json({ - success: true, - message: 'μ•ˆμ „κ΅μœ‘μ΄ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' - }); - }); - }); - }); -}; - -/** - * μ•ˆμ „κ΅μœ‘ 기둝 λͺ©λ‘ 쑰회 - */ -exports.getTrainingRecords = (req, res) => { - const filters = { - training_date: req.query.training_date, - start_date: req.query.start_date, - end_date: req.query.end_date, - trainer_id: req.query.trainer_id - }; - - visitRequestModel.getTrainingRecords(filters, (err, records) => { - if (err) { - console.error('μ•ˆμ „κ΅μœ‘ 기둝 λͺ©λ‘ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - success: false, - message: 'μ•ˆμ „κ΅μœ‘ 기둝 λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - error: err.message - }); + } catch (statusErr) { + logger.error('μΆœμž… μ‹ μ²­ μƒνƒœ μ—…λ°μ΄νŠΈ 였λ₯˜:', statusErr); } - res.json({ - success: true, - data: records - }); - }); + res.json({ success: true, message: 'μ•ˆμ „κ΅μœ‘μ΄ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μ•ˆμ „κ΅μœ‘ μ™„λ£Œ 처리 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „κ΅μœ‘ μ™„λ£Œ 처리 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } +}; + +exports.getTrainingRecords = async (req, res) => { + try { + const filters = { + training_date: req.query.training_date, + start_date: req.query.start_date, + end_date: req.query.end_date, + trainer_id: req.query.trainer_id + }; + const records = await visitRequestModel.getTrainingRecords(filters); + res.json({ success: true, data: records }); + } catch (err) { + logger.error('μ•ˆμ „κ΅μœ‘ 기둝 λͺ©λ‘ 쑰회 였λ₯˜:', err); + res.status(500).json({ success: false, message: 'μ•ˆμ „κ΅μœ‘ 기둝 λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; diff --git a/system1-factory/api/controllers/workIssueController.js b/system1-factory/api/controllers/workIssueController.js index 54b472b..8abab9f 100644 --- a/system1-factory/api/controllers/workIssueController.js +++ b/system1-factory/api/controllers/workIssueController.js @@ -4,198 +4,149 @@ const workIssueModel = require('../models/workIssueModel'); const imageUploadService = require('../services/imageUploadService'); +const logger = require('../utils/logger'); // ==================== μ‹ κ³  μΉ΄ν…Œκ³ λ¦¬ 관리 ==================== -/** - * λͺ¨λ“  μΉ΄ν…Œκ³ λ¦¬ 쑰회 - */ -exports.getAllCategories = (req, res) => { - workIssueModel.getAllCategories((err, categories) => { - if (err) { - console.error('μΉ΄ν…Œκ³ λ¦¬ 쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ 쑰회 μ‹€νŒ¨' }); - } +exports.getAllCategories = async (req, res) => { + try { + const categories = await workIssueModel.getAllCategories(); res.json({ success: true, data: categories }); - }); + } catch (err) { + logger.error('μΉ΄ν…Œκ³ λ¦¬ 쑰회 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ 쑰회 μ‹€νŒ¨' }); + } }; -/** - * νƒ€μž…λ³„ μΉ΄ν…Œκ³ λ¦¬ 쑰회 - */ -exports.getCategoriesByType = (req, res) => { - const { type } = req.params; +exports.getCategoriesByType = async (req, res) => { + try { + const { type } = req.params; - if (!['nonconformity', 'safety', 'facility'].includes(type)) { - return res.status(400).json({ success: false, error: 'μœ νš¨ν•˜μ§€ μ•Šμ€ μΉ΄ν…Œκ³ λ¦¬ νƒ€μž…μž…λ‹ˆλ‹€.' }); - } - - workIssueModel.getCategoriesByType(type, (err, categories) => { - if (err) { - console.error('μΉ΄ν…Œκ³ λ¦¬ 쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ 쑰회 μ‹€νŒ¨' }); + if (!['nonconformity', 'safety', 'facility'].includes(type)) { + return res.status(400).json({ success: false, error: 'μœ νš¨ν•˜μ§€ μ•Šμ€ μΉ΄ν…Œκ³ λ¦¬ νƒ€μž…μž…λ‹ˆλ‹€.' }); } + + const categories = await workIssueModel.getCategoriesByType(type); res.json({ success: true, data: categories }); - }); -}; - -/** - * μΉ΄ν…Œκ³ λ¦¬ 생성 - */ -exports.createCategory = (req, res) => { - const { category_type, category_name, description, display_order } = req.body; - - if (!category_type || !category_name) { - return res.status(400).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ νƒ€μž…κ³Ό 이름은 ν•„μˆ˜μž…λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μΉ΄ν…Œκ³ λ¦¬ 쑰회 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ 쑰회 μ‹€νŒ¨' }); } - - workIssueModel.createCategory( - { category_type, category_name, description, display_order }, - (err, categoryId) => { - if (err) { - console.error('μΉ΄ν…Œκ³ λ¦¬ 생성 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ 생성 μ‹€νŒ¨' }); - } - res.status(201).json({ - success: true, - message: 'μΉ΄ν…Œκ³ λ¦¬κ°€ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: { category_id: categoryId } - }); - } - ); }; -/** - * μΉ΄ν…Œκ³ λ¦¬ μˆ˜μ • - */ -exports.updateCategory = (req, res) => { - const { id } = req.params; - const { category_name, description, display_order, is_active } = req.body; +exports.createCategory = async (req, res) => { + try { + const { category_type, category_name, description, display_order } = req.body; - workIssueModel.updateCategory( - id, - { category_name, description, display_order, is_active }, - (err, result) => { - if (err) { - console.error('μΉ΄ν…Œκ³ λ¦¬ μˆ˜μ • μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ μˆ˜μ • μ‹€νŒ¨' }); - } - res.json({ success: true, message: 'μΉ΄ν…Œκ³ λ¦¬κ°€ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + if (!category_type || !category_name) { + return res.status(400).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ νƒ€μž…κ³Ό 이름은 ν•„μˆ˜μž…λ‹ˆλ‹€.' }); } - ); + + const categoryId = await workIssueModel.createCategory({ category_type, category_name, description, display_order }); + res.status(201).json({ + success: true, + message: 'μΉ΄ν…Œκ³ λ¦¬κ°€ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: { category_id: categoryId } + }); + } catch (err) { + logger.error('μΉ΄ν…Œκ³ λ¦¬ 생성 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ 생성 μ‹€νŒ¨' }); + } }; -/** - * μΉ΄ν…Œκ³ λ¦¬ μ‚­μ œ - */ -exports.deleteCategory = (req, res) => { - const { id } = req.params; +exports.updateCategory = async (req, res) => { + try { + const { id } = req.params; + const { category_name, description, display_order, is_active } = req.body; - workIssueModel.deleteCategory(id, (err, result) => { - if (err) { - console.error('μΉ΄ν…Œκ³ λ¦¬ μ‚­μ œ μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ μ‚­μ œ μ‹€νŒ¨' }); - } + await workIssueModel.updateCategory(id, { category_name, description, display_order, is_active }); + res.json({ success: true, message: 'μΉ΄ν…Œκ³ λ¦¬κ°€ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μΉ΄ν…Œκ³ λ¦¬ μˆ˜μ • μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ μˆ˜μ • μ‹€νŒ¨' }); + } +}; + +exports.deleteCategory = async (req, res) => { + try { + const { id } = req.params; + await workIssueModel.deleteCategory(id); res.json({ success: true, message: 'μΉ΄ν…Œκ³ λ¦¬κ°€ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); - }); + } catch (err) { + logger.error('μΉ΄ν…Œκ³ λ¦¬ μ‚­μ œ μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ μ‚­μ œ μ‹€νŒ¨' }); + } }; // ==================== 사전 μ •μ˜ ν•­λͺ© 관리 ==================== -/** - * μΉ΄ν…Œκ³ λ¦¬λ³„ ν•­λͺ© 쑰회 - */ -exports.getItemsByCategory = (req, res) => { - const { categoryId } = req.params; - - workIssueModel.getItemsByCategory(categoryId, (err, items) => { - if (err) { - console.error('ν•­λͺ© 쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'ν•­λͺ© 쑰회 μ‹€νŒ¨' }); - } +exports.getItemsByCategory = async (req, res) => { + try { + const { categoryId } = req.params; + const items = await workIssueModel.getItemsByCategory(categoryId); res.json({ success: true, data: items }); - }); -}; - -/** - * λͺ¨λ“  ν•­λͺ© 쑰회 - */ -exports.getAllItems = (req, res) => { - workIssueModel.getAllItems((err, items) => { - if (err) { - console.error('ν•­λͺ© 쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'ν•­λͺ© 쑰회 μ‹€νŒ¨' }); - } - res.json({ success: true, data: items }); - }); -}; - -/** - * ν•­λͺ© 생성 - */ -exports.createItem = (req, res) => { - const { category_id, item_name, description, severity, display_order } = req.body; - - if (!category_id || !item_name) { - return res.status(400).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ ID와 ν•­λͺ©λͺ…은 ν•„μˆ˜μž…λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('ν•­λͺ© 쑰회 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'ν•­λͺ© 쑰회 μ‹€νŒ¨' }); } - - workIssueModel.createItem( - { category_id, item_name, description, severity, display_order }, - (err, itemId) => { - if (err) { - console.error('ν•­λͺ© 생성 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'ν•­λͺ© 생성 μ‹€νŒ¨' }); - } - res.status(201).json({ - success: true, - message: 'ν•­λͺ©μ΄ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: { item_id: itemId } - }); - } - ); }; -/** - * ν•­λͺ© μˆ˜μ • - */ -exports.updateItem = (req, res) => { - const { id } = req.params; - const { item_name, description, severity, display_order, is_active } = req.body; - - workIssueModel.updateItem( - id, - { item_name, description, severity, display_order, is_active }, - (err, result) => { - if (err) { - console.error('ν•­λͺ© μˆ˜μ • μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'ν•­λͺ© μˆ˜μ • μ‹€νŒ¨' }); - } - res.json({ success: true, message: 'ν•­λͺ©μ΄ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); - } - ); +exports.getAllItems = async (req, res) => { + try { + const items = await workIssueModel.getAllItems(); + res.json({ success: true, data: items }); + } catch (err) { + logger.error('ν•­λͺ© 쑰회 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'ν•­λͺ© 쑰회 μ‹€νŒ¨' }); + } }; -/** - * ν•­λͺ© μ‚­μ œ - */ -exports.deleteItem = (req, res) => { - const { id } = req.params; +exports.createItem = async (req, res) => { + try { + const { category_id, item_name, description, severity, display_order } = req.body; - workIssueModel.deleteItem(id, (err, result) => { - if (err) { - console.error('ν•­λͺ© μ‚­μ œ μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'ν•­λͺ© μ‚­μ œ μ‹€νŒ¨' }); + if (!category_id || !item_name) { + return res.status(400).json({ success: false, error: 'μΉ΄ν…Œκ³ λ¦¬ ID와 ν•­λͺ©λͺ…은 ν•„μˆ˜μž…λ‹ˆλ‹€.' }); } + + const itemId = await workIssueModel.createItem({ category_id, item_name, description, severity, display_order }); + res.status(201).json({ + success: true, + message: 'ν•­λͺ©μ΄ μƒμ„±λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: { item_id: itemId } + }); + } catch (err) { + logger.error('ν•­λͺ© 생성 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'ν•­λͺ© 생성 μ‹€νŒ¨' }); + } +}; + +exports.updateItem = async (req, res) => { + try { + const { id } = req.params; + const { item_name, description, severity, display_order, is_active } = req.body; + + await workIssueModel.updateItem(id, { item_name, description, severity, display_order, is_active }); + res.json({ success: true, message: 'ν•­λͺ©μ΄ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('ν•­λͺ© μˆ˜μ • μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'ν•­λͺ© μˆ˜μ • μ‹€νŒ¨' }); + } +}; + +exports.deleteItem = async (req, res) => { + try { + const { id } = req.params; + await workIssueModel.deleteItem(id); res.json({ success: true, message: 'ν•­λͺ©μ΄ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); - }); + } catch (err) { + logger.error('ν•­λͺ© μ‚­μ œ μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'ν•­λͺ© μ‚­μ œ μ‹€νŒ¨' }); + } }; // ==================== 문제 μ‹ κ³  관리 ==================== -/** - * μ‹ κ³  생성 - */ exports.createReport = async (req, res) => { try { const { @@ -206,7 +157,7 @@ exports.createReport = async (req, res) => { visit_request_id, issue_category_id, issue_item_id, - custom_item_name, // 직접 μž…λ ₯ν•œ ν•­λͺ©λͺ… + custom_item_name, additional_description, photos = [] } = req.body; @@ -217,42 +168,25 @@ exports.createReport = async (req, res) => { return res.status(400).json({ success: false, error: 'μ‹ κ³  μΉ΄ν…Œκ³ λ¦¬λŠ” ν•„μˆ˜μž…λ‹ˆλ‹€.' }); } - // μœ„μΉ˜ 정보 검증 (지도 선택 λ˜λŠ” 기타 μœ„μΉ˜) if (!factory_category_id && !custom_location) { return res.status(400).json({ success: false, error: 'μœ„μΉ˜ μ •λ³΄λŠ” ν•„μˆ˜μž…λ‹ˆλ‹€.' }); } - // ν•­λͺ© 검증 (κΈ°μ‘΄ ν•­λͺ© λ˜λŠ” 직접 μž…λ ₯) if (!issue_item_id && !custom_item_name) { return res.status(400).json({ success: false, error: 'μ‹ κ³  ν•­λͺ©μ€ ν•„μˆ˜μž…λ‹ˆλ‹€.' }); } - // 직접 μž…λ ₯ν•œ ν•­λͺ©μ΄ 있으면 DB에 μ €μž₯ let finalItemId = issue_item_id; if (custom_item_name && !issue_item_id) { - try { - finalItemId = await new Promise((resolve, reject) => { - workIssueModel.createItem( - { - category_id: issue_category_id, - item_name: custom_item_name, - description: 'μ‚¬μš©μž 직접 μž…λ ₯', - severity: 'medium', - display_order: 999 // λ§ˆμ§€λ§‰μ— ν‘œμ‹œ - }, - (err, itemId) => { - if (err) reject(err); - else resolve(itemId); - } - ); - }); - } catch (itemErr) { - console.error('μ»€μŠ€ν…€ ν•­λͺ© 생성 μ‹€νŒ¨:', itemErr); - return res.status(500).json({ success: false, error: 'ν•­λͺ© μ €μž₯ μ‹€νŒ¨' }); - } + finalItemId = await workIssueModel.createItem({ + category_id: issue_category_id, + item_name: custom_item_name, + description: 'μ‚¬μš©μž 직접 μž…λ ₯', + severity: 'medium', + display_order: 999 + }); } - // 사진 μ €μž₯ (μ΅œλŒ€ 5μž₯) const photoPaths = { photo_path1: null, photo_path2: null, @@ -283,73 +217,56 @@ exports.createReport = async (req, res) => { ...photoPaths }; - workIssueModel.createReport(reportData, (err, reportId) => { - if (err) { - console.error('μ‹ κ³  생성 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'μ‹ κ³  생성 μ‹€νŒ¨' }); - } - res.status(201).json({ - success: true, - message: '문제 μ‹ κ³ κ°€ λ“±λ‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - data: { report_id: reportId } - }); + const reportId = await workIssueModel.createReport(reportData); + res.status(201).json({ + success: true, + message: '문제 μ‹ κ³ κ°€ λ“±λ‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + data: { report_id: reportId } }); - } catch (error) { - console.error('μ‹ κ³  생성 μ—λŸ¬:', error); + } catch (err) { + logger.error('μ‹ κ³  생성 μ‹€νŒ¨:', err); res.status(500).json({ success: false, error: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); } }; -/** - * μ‹ κ³  λͺ©λ‘ 쑰회 - */ -exports.getAllReports = (req, res) => { - const filters = { - status: req.query.status, - category_type: req.query.category_type, - issue_category_id: req.query.issue_category_id, - factory_category_id: req.query.factory_category_id, - workplace_id: req.query.workplace_id, - assigned_user_id: req.query.assigned_user_id, - start_date: req.query.start_date, - end_date: req.query.end_date, - search: req.query.search, - limit: req.query.limit, - offset: req.query.offset - }; +exports.getAllReports = async (req, res) => { + try { + const filters = { + status: req.query.status, + category_type: req.query.category_type, + issue_category_id: req.query.issue_category_id, + factory_category_id: req.query.factory_category_id, + workplace_id: req.query.workplace_id, + assigned_user_id: req.query.assigned_user_id, + start_date: req.query.start_date, + end_date: req.query.end_date, + search: req.query.search, + limit: req.query.limit, + offset: req.query.offset + }; - // 일반 μ‚¬μš©μžλŠ” μžμ‹ μ˜ μ‹ κ³ λ§Œ 쑰회 (κ΄€λ¦¬μž μ œμ™Έ) - const userLevel = req.user.access_level; - if (!['admin', 'system', 'support_team'].includes(userLevel)) { - filters.reporter_id = req.user.user_id; - } - - workIssueModel.getAllReports(filters, (err, reports) => { - if (err) { - console.error('μ‹ κ³  λͺ©λ‘ 쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'μ‹ κ³  λͺ©λ‘ 쑰회 μ‹€νŒ¨' }); + const userLevel = req.user.access_level; + if (!['admin', 'system', 'support_team'].includes(userLevel)) { + filters.reporter_id = req.user.user_id; } + + const reports = await workIssueModel.getAllReports(filters); res.json({ success: true, data: reports }); - }); + } catch (err) { + logger.error('μ‹ κ³  λͺ©λ‘ 쑰회 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'μ‹ κ³  λͺ©λ‘ 쑰회 μ‹€νŒ¨' }); + } }; -/** - * μ‹ κ³  상세 쑰회 - */ -exports.getReportById = (req, res) => { - const { id } = req.params; - - workIssueModel.getReportById(id, (err, report) => { - if (err) { - console.error('μ‹ κ³  상세 쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'μ‹ κ³  상세 쑰회 μ‹€νŒ¨' }); - } +exports.getReportById = async (req, res) => { + try { + const { id } = req.params; + const report = await workIssueModel.getReportById(id); if (!report) { return res.status(404).json({ success: false, error: 'μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - // κΆŒν•œ 확인: 본인, λ‹΄λ‹Ήμž, λ˜λŠ” κ΄€λ¦¬μž const userLevel = req.user.access_level; const isOwner = report.reporter_id === req.user.user_id; const isAssignee = report.assigned_user_id === req.user.user_id; @@ -360,109 +277,84 @@ exports.getReportById = (req, res) => { } res.json({ success: true, data: report }); - }); + } catch (err) { + logger.error('μ‹ κ³  상세 쑰회 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'μ‹ κ³  상세 쑰회 μ‹€νŒ¨' }); + } }; -/** - * μ‹ κ³  μˆ˜μ • - */ exports.updateReport = async (req, res) => { try { const { id } = req.params; - // κΈ°μ‘΄ μ‹ κ³  확인 - workIssueModel.getReportById(id, async (err, report) => { - if (err) { - console.error('μ‹ κ³  쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'μ‹ κ³  쑰회 μ‹€νŒ¨' }); - } - - if (!report) { - return res.status(404).json({ success: false, error: 'μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); - } - - // κΆŒν•œ 확인 - const userLevel = req.user.access_level; - const isOwner = report.reporter_id === req.user.user_id; - const isManager = ['admin', 'system'].includes(userLevel); - - if (!isOwner && !isManager) { - return res.status(403).json({ success: false, error: 'μˆ˜μ • κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.' }); - } - - // μƒνƒœ 확인: reported μƒνƒœμ—μ„œλ§Œ μˆ˜μ • κ°€λŠ₯ (κ΄€λ¦¬μž μ œμ™Έ) - if (!isManager && report.status !== 'reported') { - return res.status(400).json({ success: false, error: '이미 μ ‘μˆ˜λœ μ‹ κ³ λŠ” μˆ˜μ •ν•  수 μ—†μŠ΅λ‹ˆλ‹€.' }); - } - - const { - factory_category_id, - workplace_id, - custom_location, - issue_category_id, - issue_item_id, - additional_description, - photos = [] - } = req.body; - - // 사진 μ—…λ°μ΄νŠΈ 처리 - const photoPaths = {}; - for (let i = 0; i < Math.min(photos.length, 5); i++) { - if (photos[i]) { - // κΈ°μ‘΄ 사진 μ‚­μ œ - const oldPath = report[`photo_path${i + 1}`]; - if (oldPath) { - await imageUploadService.deleteFile(oldPath); - } - // μƒˆ 사진 μ €μž₯ - const savedPath = await imageUploadService.saveBase64Image(photos[i], 'issue'); - if (savedPath) { - photoPaths[`photo_path${i + 1}`] = savedPath; - } - } - } - - const updateData = { - factory_category_id, - workplace_id, - custom_location, - issue_category_id, - issue_item_id, - additional_description, - ...photoPaths - }; - - workIssueModel.updateReport(id, updateData, req.user.user_id, (updateErr, result) => { - if (updateErr) { - console.error('μ‹ κ³  μˆ˜μ • μ‹€νŒ¨:', updateErr); - return res.status(500).json({ success: false, error: 'μ‹ κ³  μˆ˜μ • μ‹€νŒ¨' }); - } - res.json({ success: true, message: 'μ‹ κ³ κ°€ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); - }); - }); - } catch (error) { - console.error('μ‹ κ³  μˆ˜μ • μ—λŸ¬:', error); - res.status(500).json({ success: false, error: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); - } -}; - -/** - * μ‹ κ³  μ‚­μ œ - */ -exports.deleteReport = async (req, res) => { - const { id } = req.params; - - workIssueModel.getReportById(id, async (err, report) => { - if (err) { - console.error('μ‹ κ³  쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'μ‹ κ³  쑰회 μ‹€νŒ¨' }); - } - + const report = await workIssueModel.getReportById(id); + if (!report) { + return res.status(404).json({ success: false, error: 'μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); + } + + const userLevel = req.user.access_level; + const isOwner = report.reporter_id === req.user.user_id; + const isManager = ['admin', 'system'].includes(userLevel); + + if (!isOwner && !isManager) { + return res.status(403).json({ success: false, error: 'μˆ˜μ • κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.' }); + } + + if (!isManager && report.status !== 'reported') { + return res.status(400).json({ success: false, error: '이미 μ ‘μˆ˜λœ μ‹ κ³ λŠ” μˆ˜μ •ν•  수 μ—†μŠ΅λ‹ˆλ‹€.' }); + } + + const { + factory_category_id, + workplace_id, + custom_location, + issue_category_id, + issue_item_id, + additional_description, + photos = [] + } = req.body; + + const photoPaths = {}; + for (let i = 0; i < Math.min(photos.length, 5); i++) { + if (photos[i]) { + const oldPath = report[`photo_path${i + 1}`]; + if (oldPath) { + await imageUploadService.deleteFile(oldPath); + } + const savedPath = await imageUploadService.saveBase64Image(photos[i], 'issue'); + if (savedPath) { + photoPaths[`photo_path${i + 1}`] = savedPath; + } + } + } + + const updateData = { + factory_category_id, + workplace_id, + custom_location, + issue_category_id, + issue_item_id, + additional_description, + ...photoPaths + }; + + await workIssueModel.updateReport(id, updateData, req.user.user_id); + res.json({ success: true, message: 'μ‹ κ³ κ°€ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μ‹ κ³  μˆ˜μ • μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } +}; + +exports.deleteReport = async (req, res) => { + try { + const { id } = req.params; + + const report = await workIssueModel.getReportById(id); if (!report) { return res.status(404).json({ success: false, error: 'μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); } - // κΆŒν•œ 확인 const userLevel = req.user.access_level; const isOwner = report.reporter_id === req.user.user_id; const isManager = ['admin', 'system'].includes(userLevel); @@ -471,92 +363,74 @@ exports.deleteReport = async (req, res) => { return res.status(403).json({ success: false, error: 'μ‚­μ œ κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.' }); } - workIssueModel.deleteReport(id, async (deleteErr, { result, photos }) => { - if (deleteErr) { - console.error('μ‹ κ³  μ‚­μ œ μ‹€νŒ¨:', deleteErr); - return res.status(500).json({ success: false, error: 'μ‹ κ³  μ‚­μ œ μ‹€νŒ¨' }); - } + const { photos } = await workIssueModel.deleteReport(id); - // 사진 파일 μ‚­μ œ - if (photos) { - const allPhotos = [ - photos.photo_path1, photos.photo_path2, photos.photo_path3, - photos.photo_path4, photos.photo_path5, - photos.resolution_photo_path1, photos.resolution_photo_path2 - ].filter(Boolean); - await imageUploadService.deleteMultipleFiles(allPhotos); - } + if (photos) { + const allPhotos = [ + photos.photo_path1, photos.photo_path2, photos.photo_path3, + photos.photo_path4, photos.photo_path5, + photos.resolution_photo_path1, photos.resolution_photo_path2 + ].filter(Boolean); + await imageUploadService.deleteMultipleFiles(allPhotos); + } - res.json({ success: true, message: 'μ‹ κ³ κ°€ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); - }); - }); + res.json({ success: true, message: 'μ‹ κ³ κ°€ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μ‹ κ³  μ‚­μ œ μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + } }; // ==================== μƒνƒœ 관리 ==================== -/** - * μ‹ κ³  μ ‘μˆ˜ - */ -exports.receiveReport = (req, res) => { - const { id } = req.params; - - workIssueModel.receiveReport(id, req.user.user_id, (err, result) => { - if (err) { - console.error('μ‹ κ³  μ ‘μˆ˜ μ‹€νŒ¨:', err); - return res.status(400).json({ success: false, error: err.message || 'μ‹ κ³  μ ‘μˆ˜ μ‹€νŒ¨' }); - } +exports.receiveReport = async (req, res) => { + try { + const { id } = req.params; + await workIssueModel.receiveReport(id, req.user.user_id); res.json({ success: true, message: 'μ‹ κ³ κ°€ μ ‘μˆ˜λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); - }); -}; - -/** - * λ‹΄λ‹Ήμž λ°°μ • - */ -exports.assignReport = (req, res) => { - const { id } = req.params; - const { assigned_department, assigned_user_id } = req.body; - - if (!assigned_user_id) { - return res.status(400).json({ success: false, error: 'λ‹΄λ‹ΉμžλŠ” ν•„μˆ˜μž…λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('μ‹ κ³  μ ‘μˆ˜ μ‹€νŒ¨:', err); + res.status(400).json({ success: false, error: err.message || 'μ‹ κ³  μ ‘μˆ˜ μ‹€νŒ¨' }); } +}; - workIssueModel.assignReport(id, { - assigned_department, - assigned_user_id, - assigned_by: req.user.user_id - }, (err, result) => { - if (err) { - console.error('λ‹΄λ‹Ήμž λ°°μ • μ‹€νŒ¨:', err); - return res.status(400).json({ success: false, error: err.message || 'λ‹΄λ‹Ήμž λ°°μ • μ‹€νŒ¨' }); +exports.assignReport = async (req, res) => { + try { + const { id } = req.params; + const { assigned_department, assigned_user_id } = req.body; + + if (!assigned_user_id) { + return res.status(400).json({ success: false, error: 'λ‹΄λ‹ΉμžλŠ” ν•„μˆ˜μž…λ‹ˆλ‹€.' }); } + + await workIssueModel.assignReport(id, { + assigned_department, + assigned_user_id, + assigned_by: req.user.user_id + }); res.json({ success: true, message: 'λ‹΄λ‹Ήμžκ°€ λ°°μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); - }); + } catch (err) { + logger.error('λ‹΄λ‹Ήμž λ°°μ • μ‹€νŒ¨:', err); + res.status(400).json({ success: false, error: err.message || 'λ‹΄λ‹Ήμž λ°°μ • μ‹€νŒ¨' }); + } }; -/** - * 처리 μ‹œμž‘ - */ -exports.startProcessing = (req, res) => { - const { id } = req.params; - - workIssueModel.startProcessing(id, req.user.user_id, (err, result) => { - if (err) { - console.error('처리 μ‹œμž‘ μ‹€νŒ¨:', err); - return res.status(400).json({ success: false, error: err.message || '처리 μ‹œμž‘ μ‹€νŒ¨' }); - } +exports.startProcessing = async (req, res) => { + try { + const { id } = req.params; + await workIssueModel.startProcessing(id, req.user.user_id); res.json({ success: true, message: 'μ²˜λ¦¬κ°€ μ‹œμž‘λ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); - }); + } catch (err) { + logger.error('처리 μ‹œμž‘ μ‹€νŒ¨:', err); + res.status(400).json({ success: false, error: err.message || '처리 μ‹œμž‘ μ‹€νŒ¨' }); + } }; -/** - * 처리 μ™„λ£Œ - */ exports.completeReport = async (req, res) => { try { const { id } = req.params; const { resolution_notes, resolution_photos = [] } = req.body; - // μ™„λ£Œ 사진 μ €μž₯ let resolution_photo_path1 = null; let resolution_photo_path2 = null; @@ -567,108 +441,83 @@ exports.completeReport = async (req, res) => { resolution_photo_path2 = await imageUploadService.saveBase64Image(resolution_photos[1], 'resolution'); } - workIssueModel.completeReport(id, { + await workIssueModel.completeReport(id, { resolution_notes, resolution_photo_path1, resolution_photo_path2, resolved_by: req.user.user_id - }, (err, result) => { - if (err) { - console.error('처리 μ™„λ£Œ μ‹€νŒ¨:', err); - return res.status(400).json({ success: false, error: err.message || '처리 μ™„λ£Œ μ‹€νŒ¨' }); - } - res.json({ success: true, message: 'μ²˜λ¦¬κ°€ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); }); - } catch (error) { - console.error('처리 μ™„λ£Œ μ—λŸ¬:', error); - res.status(500).json({ success: false, error: 'μ„œλ²„ 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.' }); + res.json({ success: true, message: 'μ²˜λ¦¬κ°€ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); + } catch (err) { + logger.error('처리 μ™„λ£Œ μ‹€νŒ¨:', err); + res.status(400).json({ success: false, error: err.message || '처리 μ™„λ£Œ μ‹€νŒ¨' }); } }; -/** - * μ‹ κ³  μ’…λ£Œ - */ -exports.closeReport = (req, res) => { - const { id } = req.params; - - workIssueModel.closeReport(id, req.user.user_id, (err, result) => { - if (err) { - console.error('μ‹ κ³  μ’…λ£Œ μ‹€νŒ¨:', err); - return res.status(400).json({ success: false, error: err.message || 'μ‹ κ³  μ’…λ£Œ μ‹€νŒ¨' }); - } +exports.closeReport = async (req, res) => { + try { + const { id } = req.params; + await workIssueModel.closeReport(id, req.user.user_id); res.json({ success: true, message: 'μ‹ κ³ κ°€ μ’…λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.' }); - }); + } catch (err) { + logger.error('μ‹ κ³  μ’…λ£Œ μ‹€νŒ¨:', err); + res.status(400).json({ success: false, error: err.message || 'μ‹ κ³  μ’…λ£Œ μ‹€νŒ¨' }); + } }; -/** - * μƒνƒœ λ³€κ²½ 이λ ₯ 쑰회 - */ -exports.getStatusLogs = (req, res) => { - const { id } = req.params; - - workIssueModel.getStatusLogs(id, (err, logs) => { - if (err) { - console.error('μƒνƒœ 이λ ₯ 쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: 'μƒνƒœ 이λ ₯ 쑰회 μ‹€νŒ¨' }); - } +exports.getStatusLogs = async (req, res) => { + try { + const { id } = req.params; + const logs = await workIssueModel.getStatusLogs(id); res.json({ success: true, data: logs }); - }); + } catch (err) { + logger.error('μƒνƒœ 이λ ₯ 쑰회 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: 'μƒνƒœ 이λ ₯ 쑰회 μ‹€νŒ¨' }); + } }; // ==================== 톡계 ==================== -/** - * 톡계 μš”μ•½ - */ -exports.getStatsSummary = (req, res) => { - const filters = { - start_date: req.query.start_date, - end_date: req.query.end_date, - factory_category_id: req.query.factory_category_id - }; - - workIssueModel.getStatsSummary(filters, (err, stats) => { - if (err) { - console.error('톡계 쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: '톡계 쑰회 μ‹€νŒ¨' }); - } +exports.getStatsSummary = async (req, res) => { + try { + const filters = { + start_date: req.query.start_date, + end_date: req.query.end_date, + factory_category_id: req.query.factory_category_id + }; + const stats = await workIssueModel.getStatsSummary(filters); res.json({ success: true, data: stats }); - }); + } catch (err) { + logger.error('톡계 쑰회 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: '톡계 쑰회 μ‹€νŒ¨' }); + } }; -/** - * μΉ΄ν…Œκ³ λ¦¬λ³„ 톡계 - */ -exports.getStatsByCategory = (req, res) => { - const filters = { - start_date: req.query.start_date, - end_date: req.query.end_date - }; - - workIssueModel.getStatsByCategory(filters, (err, stats) => { - if (err) { - console.error('μΉ΄ν…Œκ³ λ¦¬λ³„ 톡계 쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: '톡계 쑰회 μ‹€νŒ¨' }); - } +exports.getStatsByCategory = async (req, res) => { + try { + const filters = { + start_date: req.query.start_date, + end_date: req.query.end_date + }; + const stats = await workIssueModel.getStatsByCategory(filters); res.json({ success: true, data: stats }); - }); + } catch (err) { + logger.error('μΉ΄ν…Œκ³ λ¦¬λ³„ 톡계 쑰회 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: '톡계 쑰회 μ‹€νŒ¨' }); + } }; -/** - * μž‘μ—…μž₯별 톡계 - */ -exports.getStatsByWorkplace = (req, res) => { - const filters = { - start_date: req.query.start_date, - end_date: req.query.end_date, - factory_category_id: req.query.factory_category_id - }; - - workIssueModel.getStatsByWorkplace(filters, (err, stats) => { - if (err) { - console.error('μž‘μ—…μž₯별 톡계 쑰회 μ‹€νŒ¨:', err); - return res.status(500).json({ success: false, error: '톡계 쑰회 μ‹€νŒ¨' }); - } +exports.getStatsByWorkplace = async (req, res) => { + try { + const filters = { + start_date: req.query.start_date, + end_date: req.query.end_date, + factory_category_id: req.query.factory_category_id + }; + const stats = await workIssueModel.getStatsByWorkplace(filters); res.json({ success: true, data: stats }); - }); + } catch (err) { + logger.error('μž‘μ—…μž₯별 톡계 쑰회 μ‹€νŒ¨:', err); + res.status(500).json({ success: false, error: '톡계 쑰회 μ‹€νŒ¨' }); + } }; diff --git a/system1-factory/api/controllers/workplaceController.js b/system1-factory/api/controllers/workplaceController.js index b384529..6f3a683 100644 --- a/system1-factory/api/controllers/workplaceController.js +++ b/system1-factory/api/controllers/workplaceController.js @@ -8,15 +8,12 @@ */ const workplaceModel = require('../models/workplaceModel'); -const { ValidationError, NotFoundError, DatabaseError } = require('../utils/errors'); +const { ValidationError, NotFoundError } = require('../utils/errors'); const { asyncHandler } = require('../middlewares/errorHandler'); const logger = require('../utils/logger'); // ==================== μΉ΄ν…Œκ³ λ¦¬(곡μž₯) κ΄€λ ¨ ==================== -/** - * μΉ΄ν…Œκ³ λ¦¬ 생성 - */ exports.createCategory = asyncHandler(async (req, res) => { const categoryData = req.body; @@ -26,12 +23,7 @@ exports.createCategory = asyncHandler(async (req, res) => { logger.info('μΉ΄ν…Œκ³ λ¦¬ 생성 μš”μ²­', { name: categoryData.category_name }); - const id = await new Promise((resolve, reject) => { - workplaceModel.createCategory(categoryData, (err, lastID) => { - if (err) reject(new DatabaseError('μΉ΄ν…Œκ³ λ¦¬ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(lastID); - }); - }); + const id = await workplaceModel.createCategory(categoryData); logger.info('μΉ΄ν…Œκ³ λ¦¬ 생성 성곡', { category_id: id }); @@ -42,16 +34,8 @@ exports.createCategory = asyncHandler(async (req, res) => { }); }); -/** - * 전체 μΉ΄ν…Œκ³ λ¦¬ 쑰회 - */ exports.getAllCategories = asyncHandler(async (req, res) => { - const rows = await new Promise((resolve, reject) => { - workplaceModel.getAllCategories((err, data) => { - if (err) reject(new DatabaseError('μΉ΄ν…Œκ³ λ¦¬ λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); + const rows = await workplaceModel.getAllCategories(); res.json({ success: true, @@ -60,16 +44,8 @@ exports.getAllCategories = asyncHandler(async (req, res) => { }); }); -/** - * ν™œμ„± μΉ΄ν…Œκ³ λ¦¬λ§Œ 쑰회 - */ exports.getActiveCategories = asyncHandler(async (req, res) => { - const rows = await new Promise((resolve, reject) => { - workplaceModel.getActiveCategories((err, data) => { - if (err) reject(new DatabaseError('ν™œμ„± μΉ΄ν…Œκ³ λ¦¬ λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); + const rows = await workplaceModel.getActiveCategories(); res.json({ success: true, @@ -78,18 +54,9 @@ exports.getActiveCategories = asyncHandler(async (req, res) => { }); }); -/** - * 단일 μΉ΄ν…Œκ³ λ¦¬ 쑰회 - */ exports.getCategoryById = asyncHandler(async (req, res) => { const categoryId = req.params.id; - - const category = await new Promise((resolve, reject) => { - workplaceModel.getCategoryById(categoryId, (err, data) => { - if (err) reject(new DatabaseError('μΉ΄ν…Œκ³ λ¦¬ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); + const category = await workplaceModel.getCategoryById(categoryId); if (!category) { throw new NotFoundError('μΉ΄ν…Œκ³ λ¦¬λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€'); @@ -102,9 +69,6 @@ exports.getCategoryById = asyncHandler(async (req, res) => { }); }); -/** - * μΉ΄ν…Œκ³ λ¦¬ μˆ˜μ • - */ exports.updateCategory = asyncHandler(async (req, res) => { const categoryId = req.params.id; const categoryData = req.body; @@ -115,19 +79,11 @@ exports.updateCategory = asyncHandler(async (req, res) => { logger.info('μΉ΄ν…Œκ³ λ¦¬ μˆ˜μ • μš”μ²­', { category_id: categoryId }); - // κΈ°μ‘΄ μΉ΄ν…Œκ³ λ¦¬ 정보 κ°€μ Έμ˜€κΈ° - const existingCategory = await new Promise((resolve, reject) => { - workplaceModel.getCategoryById(categoryId, (err, data) => { - if (err) reject(new DatabaseError('μΉ΄ν…Œκ³ λ¦¬ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); - + const existingCategory = await workplaceModel.getCategoryById(categoryId); if (!existingCategory) { throw new NotFoundError('μΉ΄ν…Œκ³ λ¦¬λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€'); } - // layout_imageκ°€ μš”μ²­μ— μ—†κ±°λ‚˜ null이면 κΈ°μ‘΄ κ°’ 보쑴 const updateData = { ...categoryData, layout_image: (categoryData.layout_image !== undefined && categoryData.layout_image !== null) @@ -135,12 +91,7 @@ exports.updateCategory = asyncHandler(async (req, res) => { : existingCategory.layout_image }; - await new Promise((resolve, reject) => { - workplaceModel.updateCategory(categoryId, updateData, (err, result) => { - if (err) reject(new DatabaseError('μΉ΄ν…Œκ³ λ¦¬ μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(result); - }); - }); + await workplaceModel.updateCategory(categoryId, updateData); logger.info('μΉ΄ν…Œκ³ λ¦¬ μˆ˜μ • 성곡', { category_id: categoryId }); @@ -150,20 +101,12 @@ exports.updateCategory = asyncHandler(async (req, res) => { }); }); -/** - * μΉ΄ν…Œκ³ λ¦¬ μ‚­μ œ - */ exports.deleteCategory = asyncHandler(async (req, res) => { const categoryId = req.params.id; logger.info('μΉ΄ν…Œκ³ λ¦¬ μ‚­μ œ μš”μ²­', { category_id: categoryId }); - await new Promise((resolve, reject) => { - workplaceModel.deleteCategory(categoryId, (err, result) => { - if (err) reject(new DatabaseError('μΉ΄ν…Œκ³ λ¦¬ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(result); - }); - }); + await workplaceModel.deleteCategory(categoryId); logger.info('μΉ΄ν…Œκ³ λ¦¬ μ‚­μ œ 성곡', { category_id: categoryId }); @@ -175,9 +118,6 @@ exports.deleteCategory = asyncHandler(async (req, res) => { // ==================== μž‘μ—…μž₯ κ΄€λ ¨ ==================== -/** - * μž‘μ—…μž₯ 생성 - */ exports.createWorkplace = asyncHandler(async (req, res) => { const workplaceData = req.body; @@ -187,12 +127,7 @@ exports.createWorkplace = asyncHandler(async (req, res) => { logger.info('μž‘μ—…μž₯ 생성 μš”μ²­', { name: workplaceData.workplace_name }); - const id = await new Promise((resolve, reject) => { - workplaceModel.createWorkplace(workplaceData, (err, lastID) => { - if (err) reject(new DatabaseError('μž‘μ—…μž₯ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(lastID); - }); - }); + const id = await workplaceModel.createWorkplace(workplaceData); logger.info('μž‘μ—…μž₯ 생성 성곡', { workplace_id: id }); @@ -203,35 +138,12 @@ exports.createWorkplace = asyncHandler(async (req, res) => { }); }); -/** - * 전체 μž‘μ—…μž₯ 쑰회 - */ exports.getAllWorkplaces = asyncHandler(async (req, res) => { const categoryId = req.query.category_id; - // μΉ΄ν…Œκ³ λ¦¬λ³„ 필터링 - if (categoryId) { - const rows = await new Promise((resolve, reject) => { - workplaceModel.getWorkplacesByCategory(categoryId, (err, data) => { - if (err) reject(new DatabaseError('μž‘μ—…μž₯ λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); - - return res.json({ - success: true, - data: rows, - message: 'μž‘μ—…μž₯ λͺ©λ‘ 쑰회 성곡' - }); - } - - // 전체 쑰회 - const rows = await new Promise((resolve, reject) => { - workplaceModel.getAllWorkplaces((err, data) => { - if (err) reject(new DatabaseError('μž‘μ—…μž₯ λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); + const rows = categoryId + ? await workplaceModel.getWorkplacesByCategory(categoryId) + : await workplaceModel.getAllWorkplaces(); res.json({ success: true, @@ -240,16 +152,8 @@ exports.getAllWorkplaces = asyncHandler(async (req, res) => { }); }); -/** - * ν™œμ„± μž‘μ—…μž₯만 쑰회 - */ exports.getActiveWorkplaces = asyncHandler(async (req, res) => { - const rows = await new Promise((resolve, reject) => { - workplaceModel.getActiveWorkplaces((err, data) => { - if (err) reject(new DatabaseError('ν™œμ„± μž‘μ—…μž₯ λͺ©λ‘ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); + const rows = await workplaceModel.getActiveWorkplaces(); res.json({ success: true, @@ -258,18 +162,9 @@ exports.getActiveWorkplaces = asyncHandler(async (req, res) => { }); }); -/** - * 단일 μž‘μ—…μž₯ 쑰회 - */ exports.getWorkplaceById = asyncHandler(async (req, res) => { const workplaceId = req.params.id; - - const workplace = await new Promise((resolve, reject) => { - workplaceModel.getWorkplaceById(workplaceId, (err, data) => { - if (err) reject(new DatabaseError('μž‘μ—…μž₯ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); + const workplace = await workplaceModel.getWorkplaceById(workplaceId); if (!workplace) { throw new NotFoundError('μž‘μ—…μž₯을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€'); @@ -282,9 +177,6 @@ exports.getWorkplaceById = asyncHandler(async (req, res) => { }); }); -/** - * μž‘μ—…μž₯ μˆ˜μ • - */ exports.updateWorkplace = asyncHandler(async (req, res) => { const workplaceId = req.params.id; const workplaceData = req.body; @@ -295,19 +187,11 @@ exports.updateWorkplace = asyncHandler(async (req, res) => { logger.info('μž‘μ—…μž₯ μˆ˜μ • μš”μ²­', { workplace_id: workplaceId }); - // κΈ°μ‘΄ μž‘μ—…μž₯ 정보 κ°€μ Έμ˜€κΈ° - const existingWorkplace = await new Promise((resolve, reject) => { - workplaceModel.getWorkplaceById(workplaceId, (err, data) => { - if (err) reject(new DatabaseError('μž‘μ—…μž₯ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); - + const existingWorkplace = await workplaceModel.getWorkplaceById(workplaceId); if (!existingWorkplace) { throw new NotFoundError('μž‘μ—…μž₯을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€'); } - // layout_imageκ°€ μš”μ²­μ— μ—†κ±°λ‚˜ null이면 κΈ°μ‘΄ κ°’ 보쑴 const updateData = { ...workplaceData, layout_image: (workplaceData.layout_image !== undefined && workplaceData.layout_image !== null) @@ -315,12 +199,7 @@ exports.updateWorkplace = asyncHandler(async (req, res) => { : existingWorkplace.layout_image }; - await new Promise((resolve, reject) => { - workplaceModel.updateWorkplace(workplaceId, updateData, (err, result) => { - if (err) reject(new DatabaseError('μž‘μ—…μž₯ μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(result); - }); - }); + await workplaceModel.updateWorkplace(workplaceId, updateData); logger.info('μž‘μ—…μž₯ μˆ˜μ • 성곡', { workplace_id: workplaceId }); @@ -330,20 +209,12 @@ exports.updateWorkplace = asyncHandler(async (req, res) => { }); }); -/** - * μž‘μ—…μž₯ μ‚­μ œ - */ exports.deleteWorkplace = asyncHandler(async (req, res) => { const workplaceId = req.params.id; logger.info('μž‘μ—…μž₯ μ‚­μ œ μš”μ²­', { workplace_id: workplaceId }); - await new Promise((resolve, reject) => { - workplaceModel.deleteWorkplace(workplaceId, (err, result) => { - if (err) reject(new DatabaseError('μž‘μ—…μž₯ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(result); - }); - }); + await workplaceModel.deleteWorkplace(workplaceId); logger.info('μž‘μ—…μž₯ μ‚­μ œ 성곡', { workplace_id: workplaceId }); @@ -355,9 +226,6 @@ exports.deleteWorkplace = asyncHandler(async (req, res) => { // ==================== μž‘μ—…μž₯ 지도 μ˜μ—­ κ΄€λ ¨ ==================== -/** - * μΉ΄ν…Œκ³ λ¦¬ λ ˆμ΄μ•„μ›ƒ 이미지 μ—…λ‘œλ“œ - */ exports.uploadCategoryLayoutImage = asyncHandler(async (req, res) => { const categoryId = req.params.id; @@ -369,19 +237,11 @@ exports.uploadCategoryLayoutImage = asyncHandler(async (req, res) => { logger.info('μΉ΄ν…Œκ³ λ¦¬ λ ˆμ΄μ•„μ›ƒ 이미지 μ—…λ‘œλ“œ μš”μ²­', { category_id: categoryId, path: imagePath }); - // ν˜„μž¬ μΉ΄ν…Œκ³ λ¦¬ 정보 κ°€μ Έμ˜€κΈ° - const category = await new Promise((resolve, reject) => { - workplaceModel.getCategoryById(categoryId, (err, data) => { - if (err) reject(new DatabaseError('μΉ΄ν…Œκ³ λ¦¬ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); - + const category = await workplaceModel.getCategoryById(categoryId); if (!category) { throw new NotFoundError('μΉ΄ν…Œκ³ λ¦¬λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€'); } - // μΉ΄ν…Œκ³ λ¦¬ 정보 μ—…λ°μ΄νŠΈ (이미지 경둜만 λ³€κ²½) const updatedData = { category_name: category.category_name, description: category.description, @@ -390,12 +250,7 @@ exports.uploadCategoryLayoutImage = asyncHandler(async (req, res) => { layout_image: imagePath }; - await new Promise((resolve, reject) => { - workplaceModel.updateCategory(categoryId, updatedData, (err, result) => { - if (err) reject(new DatabaseError('이미지 경둜 μ €μž₯ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(result); - }); - }); + await workplaceModel.updateCategory(categoryId, updatedData); logger.info('λ ˆμ΄μ•„μ›ƒ 이미지 μ—…λ‘œλ“œ 성곡', { category_id: categoryId }); @@ -406,9 +261,6 @@ exports.uploadCategoryLayoutImage = asyncHandler(async (req, res) => { }); }); -/** - * μž‘μ—…μž₯ λ ˆμ΄μ•„μ›ƒ 이미지 μ—…λ‘œλ“œ - */ exports.uploadWorkplaceLayoutImage = asyncHandler(async (req, res) => { const workplaceId = req.params.id; @@ -420,19 +272,11 @@ exports.uploadWorkplaceLayoutImage = asyncHandler(async (req, res) => { logger.info('μž‘μ—…μž₯ λ ˆμ΄μ•„μ›ƒ 이미지 μ—…λ‘œλ“œ μš”μ²­', { workplace_id: workplaceId, path: imagePath }); - // ν˜„μž¬ μž‘μ—…μž₯ 정보 κ°€μ Έμ˜€κΈ° - const workplace = await new Promise((resolve, reject) => { - workplaceModel.getWorkplaceById(workplaceId, (err, data) => { - if (err) reject(new DatabaseError('μž‘μ—…μž₯ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); - + const workplace = await workplaceModel.getWorkplaceById(workplaceId); if (!workplace) { throw new NotFoundError('μž‘μ—…μž₯을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€'); } - // μž‘μ—…μž₯ 정보 μ—…λ°μ΄νŠΈ (이미지 경둜만 λ³€κ²½) const updatedData = { workplace_name: workplace.workplace_name, category_id: workplace.category_id, @@ -443,12 +287,7 @@ exports.uploadWorkplaceLayoutImage = asyncHandler(async (req, res) => { layout_image: imagePath }; - await new Promise((resolve, reject) => { - workplaceModel.updateWorkplace(workplaceId, updatedData, (err, result) => { - if (err) reject(new DatabaseError('이미지 경둜 μ €μž₯ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(result); - }); - }); + await workplaceModel.updateWorkplace(workplaceId, updatedData); logger.info('μž‘μ—…μž₯ λ ˆμ΄μ•„μ›ƒ 이미지 μ—…λ‘œλ“œ 성곡', { workplace_id: workplaceId }); @@ -459,9 +298,6 @@ exports.uploadWorkplaceLayoutImage = asyncHandler(async (req, res) => { }); }); -/** - * 지도 μ˜μ—­ 생성 - */ exports.createMapRegion = asyncHandler(async (req, res) => { const regionData = req.body; @@ -471,12 +307,7 @@ exports.createMapRegion = asyncHandler(async (req, res) => { logger.info('지도 μ˜μ—­ 생성 μš”μ²­', { workplace_id: regionData.workplace_id }); - const id = await new Promise((resolve, reject) => { - workplaceModel.createMapRegion(regionData, (err, lastID) => { - if (err) reject(new DatabaseError('지도 μ˜μ—­ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(lastID); - }); - }); + const id = await workplaceModel.createMapRegion(regionData); logger.info('지도 μ˜μ—­ 생성 성곡', { region_id: id }); @@ -487,18 +318,9 @@ exports.createMapRegion = asyncHandler(async (req, res) => { }); }); -/** - * μΉ΄ν…Œκ³ λ¦¬λ³„ 지도 μ˜μ—­ 쑰회 (μž‘μ—…μž₯ 정보 포함) - */ exports.getMapRegionsByCategory = asyncHandler(async (req, res) => { const categoryId = req.params.categoryId; - - const rows = await new Promise((resolve, reject) => { - workplaceModel.getMapRegionsByCategory(categoryId, (err, data) => { - if (err) reject(new DatabaseError('지도 μ˜μ—­ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); + const rows = await workplaceModel.getMapRegionsByCategory(categoryId); res.json({ success: true, @@ -507,18 +329,9 @@ exports.getMapRegionsByCategory = asyncHandler(async (req, res) => { }); }); -/** - * μž‘μ—…μž₯별 지도 μ˜μ—­ 쑰회 - */ exports.getMapRegionByWorkplace = asyncHandler(async (req, res) => { const workplaceId = req.params.workplaceId; - - const region = await new Promise((resolve, reject) => { - workplaceModel.getMapRegionByWorkplace(workplaceId, (err, data) => { - if (err) reject(new DatabaseError('지도 μ˜μ—­ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(data); - }); - }); + const region = await workplaceModel.getMapRegionByWorkplace(workplaceId); res.json({ success: true, @@ -527,21 +340,13 @@ exports.getMapRegionByWorkplace = asyncHandler(async (req, res) => { }); }); -/** - * 지도 μ˜μ—­ μˆ˜μ • - */ exports.updateMapRegion = asyncHandler(async (req, res) => { const regionId = req.params.id; const regionData = req.body; logger.info('지도 μ˜μ—­ μˆ˜μ • μš”μ²­', { region_id: regionId }); - await new Promise((resolve, reject) => { - workplaceModel.updateMapRegion(regionId, regionData, (err, result) => { - if (err) reject(new DatabaseError('지도 μ˜μ—­ μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(result); - }); - }); + await workplaceModel.updateMapRegion(regionId, regionData); logger.info('지도 μ˜μ—­ μˆ˜μ • 성곡', { region_id: regionId }); @@ -551,20 +356,12 @@ exports.updateMapRegion = asyncHandler(async (req, res) => { }); }); -/** - * 지도 μ˜μ—­ μ‚­μ œ - */ exports.deleteMapRegion = asyncHandler(async (req, res) => { const regionId = req.params.id; logger.info('지도 μ˜μ—­ μ‚­μ œ μš”μ²­', { region_id: regionId }); - await new Promise((resolve, reject) => { - workplaceModel.deleteMapRegion(regionId, (err, result) => { - if (err) reject(new DatabaseError('지도 μ˜μ—­ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€')); - else resolve(result); - }); - }); + await workplaceModel.deleteMapRegion(regionId); logger.info('지도 μ˜μ—­ μ‚­μ œ 성곡', { region_id: regionId }); diff --git a/system1-factory/api/models/dailyIssueReportModel.js b/system1-factory/api/models/dailyIssueReportModel.js index 8d7f119..103ad14 100644 --- a/system1-factory/api/models/dailyIssueReportModel.js +++ b/system1-factory/api/models/dailyIssueReportModel.js @@ -2,8 +2,6 @@ const { getDb } = require('../dbPool'); /** * 1. μ—¬λŸ¬ 개의 이슈 λ³΄κ³ μ„œλ₯Ό νŠΈλžœμž­μ…˜μœΌλ‘œ μƒμ„±ν•©λ‹ˆλ‹€. - * @param {Array} reports - 생성할 λ³΄κ³ μ„œ 데이터 λ°°μ—΄ - * @returns {Promise>} - μ‚½μž…λœ ID λ°°μ—΄ */ const createMany = async (reports) => { const db = await getDb(); @@ -13,7 +11,7 @@ const createMany = async (reports) => { const insertedIds = []; const sql = ` - INSERT INTO DailyIssueReports + INSERT INTO DailyIssueReports (date, worker_id, project_id, start_time, end_time, issue_type_id) VALUES (?, ?, ?, ?, ?, ?) `; @@ -36,119 +34,71 @@ const createMany = async (reports) => { }; /** - * 2. νŠΉμ • λ‚ μ§œμ˜ 전체 이슈 λͺ©λ‘ 쑰회 (Promise 기반) + * 2. νŠΉμ • λ‚ μ§œμ˜ 전체 이슈 λͺ©λ‘ 쑰회 */ const getAllByDate = async (date) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT - d.id, d.date, w.worker_name, p.project_name, d.start_time, d.end_time, - t.category, t.subcategory, d.description - FROM DailyIssueReports d - LEFT JOIN workers w ON d.worker_id = w.worker_id - LEFT JOIN projects p ON d.project_id = p.project_id - LEFT JOIN IssueTypes t ON d.issue_type_id = t.issue_type_id - WHERE d.date = ? - ORDER BY d.start_time ASC`, - [date] - ); - return rows; - } catch (err) { - console.error(`[Model] ${date} 이슈 λͺ©λ‘ 쑰회 였λ₯˜:`, err); - throw new Error('λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 이슈 λͺ©λ‘μ„ μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); - } + const db = await getDb(); + const [rows] = await db.query( + `SELECT + d.id, d.date, w.worker_name, p.project_name, d.start_time, d.end_time, + t.category, t.subcategory, d.description + FROM DailyIssueReports d + LEFT JOIN workers w ON d.worker_id = w.worker_id + LEFT JOIN projects p ON d.project_id = p.project_id + LEFT JOIN IssueTypes t ON d.issue_type_id = t.issue_type_id + WHERE d.date = ? + ORDER BY d.start_time ASC`, + [date] + ); + return rows; }; /** - * 3. 단일 쑰회 (선택사항: μ»¨νŠΈλ‘€λŸ¬μ—μ„œ μ‚¬μš© 쀑) + * 3. 단일 쑰회 */ -const getById = async (id, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query(`SELECT id, date, worker_id, project_id, issue_type_id, description, created_at, start_time, end_time FROM DailyIssueReports WHERE id = ?`, [id]); - callback(null, rows[0]); - } catch (err) { - callback(err); - } +const getById = async (id) => { + const db = await getDb(); + const [rows] = await db.query(`SELECT id, date, worker_id, project_id, issue_type_id, description, created_at, start_time, end_time FROM DailyIssueReports WHERE id = ?`, [id]); + return rows[0]; }; /** * 4. μˆ˜μ • */ -const update = async (id, data, callback) => { - try { - const db = await getDb(); +const update = async (id, data) => { + const db = await getDb(); - const fields = []; - const values = []; + const fields = []; + const values = []; - for (const key in data) { - fields.push(`${key} = ?`); - values.push(data[key]); - } - - values.push(id); // λ§ˆμ§€λ§‰μ— id - - const [result] = await db.query( - `UPDATE DailyIssueReports SET ${fields.join(', ')} WHERE id = ?`, - values - ); - - callback(null, result.affectedRows); - } catch (err) { - callback(err); + for (const key in data) { + fields.push(`${key} = ?`); + values.push(data[key]); } + + values.push(id); + + const [result] = await db.query( + `UPDATE DailyIssueReports SET ${fields.join(', ')} WHERE id = ?`, + values + ); + + return result.affectedRows; }; /** - * 5. μ‚­μ œ (Promise 기반) + * 5. μ‚­μ œ */ const remove = async (id) => { - try { - const db = await getDb(); - const [result] = await db.query(`DELETE FROM DailyIssueReports WHERE id = ?`, [id]); - return result.affectedRows; - } catch (err) { - console.error(`[Model] 이슈(id: ${id}) μ‚­μ œ 였λ₯˜:`, err); - throw new Error('λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 이슈λ₯Ό μ‚­μ œν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); - } + const db = await getDb(); + const [result] = await db.query(`DELETE FROM DailyIssueReports WHERE id = ?`, [id]); + return result.affectedRows; }; -// V1 ν•¨μˆ˜λ“€μ€ μ μ§„μ μœΌλ‘œ 제거 μ˜ˆμ • -const create = async (report, callback) => { - try { - const db = await getDb(); - const { - date, - worker_id, - project_id, - start_time, - end_time, - issue_type_id, - description = null // 선택값 처리 - } = report; - - const [result] = await db.query( - `INSERT INTO DailyIssueReports - (date, worker_id, project_id, start_time, end_time, issue_type_id, description) - VALUES (?, ?, ?, ?, ?, ?, ?)`, - [date, worker_id, project_id, start_time, end_time, issue_type_id, description] - ); - - callback(null, result.insertId); - } catch (err) { - callback(err); - } -}; - - module.exports = { - createMany, // μ‹ κ·œ + createMany, getAllByDate, - remove, - // λ ˆκ±°μ‹œ ν˜Έν™˜μ„±μ„ μœ„ν•΄ V1 ν•¨μˆ˜λ“€ μž„μ‹œ μœ μ§€ - create: (report, callback) => createMany([report]).then(ids => callback(null, ids[0])).catch(err => callback(err)), getById, update, -}; \ No newline at end of file + remove +}; diff --git a/system1-factory/api/models/dailyWorkReportModel.js b/system1-factory/api/models/dailyWorkReportModel.js index 8cf266a..04fb63d 100644 --- a/system1-factory/api/models/dailyWorkReportModel.js +++ b/system1-factory/api/models/dailyWorkReportModel.js @@ -2,59 +2,43 @@ const { getDb } = require('../dbPool'); /** - * πŸ“‹ λ§ˆμŠ€ν„° 데이터 쑰회 ν•¨μˆ˜λ“€ + * λ§ˆμŠ€ν„° 데이터 쑰회 ν•¨μˆ˜λ“€ */ -const getAllWorkTypes = async (callback) => { - try { - const db = await getDb(); - const [rows] = await db.query('SELECT id, name, description, category, created_at, updated_at FROM work_types ORDER BY name ASC'); - callback(null, rows); - } catch (err) { - console.error('μž‘μ—… μœ ν˜• 쑰회 였λ₯˜:', err); - callback(err); - } +const getAllWorkTypes = async () => { + const db = await getDb(); + const [rows] = await db.query('SELECT id, name, description, category, created_at, updated_at FROM work_types ORDER BY name ASC'); + return rows; }; -const getAllWorkStatusTypes = async (callback) => { - try { - const db = await getDb(); - const [rows] = await db.query('SELECT id, name, description, is_error, created_at FROM work_status_types ORDER BY id ASC'); - callback(null, rows); - } catch (err) { - console.error('업무 μƒνƒœ μœ ν˜• 쑰회 였λ₯˜:', err); - callback(err); - } +const getAllWorkStatusTypes = async () => { + const db = await getDb(); + const [rows] = await db.query('SELECT id, name, description, is_error, created_at FROM work_status_types ORDER BY id ASC'); + return rows; }; -const getAllErrorTypes = async (callback) => { - try { - const db = await getDb(); - // issue_report_itemsμ—μ„œ 뢀적합(nonconformity) νƒ€μž…μ˜ ν•­λͺ©λ§Œ 쑰회 - const [rows] = await db.query(` - SELECT - iri.item_id as id, - iri.item_name as name, - iri.description, - iri.severity, - irc.category_name as category, - iri.display_order, - iri.created_at - FROM issue_report_items iri - INNER JOIN issue_report_categories irc ON iri.category_id = irc.category_id - WHERE irc.category_type = 'nonconformity' AND iri.is_active = TRUE - ORDER BY irc.display_order, iri.display_order, iri.item_name ASC - `); - callback(null, rows); - } catch (err) { - console.error('μ—λŸ¬ μœ ν˜• 쑰회 였λ₯˜:', err); - callback(err); - } +const getAllErrorTypes = async () => { + const db = await getDb(); + const [rows] = await db.query(` + SELECT + iri.item_id as id, + iri.item_name as name, + iri.description, + iri.severity, + irc.category_name as category, + iri.display_order, + iri.created_at + FROM issue_report_items iri + INNER JOIN issue_report_categories irc ON iri.category_id = irc.category_id + WHERE irc.category_type = 'nonconformity' AND iri.is_active = TRUE + ORDER BY irc.display_order, iri.display_order, iri.item_name ASC + `); + return rows; }; /** - * πŸ”„ λˆ„μ  μΆ”κ°€ μ „μš© ν•¨μˆ˜ (createDailyReport λŒ€μ²΄) - μ ˆλŒ€ μ‚­μ œ μ•ˆν•¨! + * λˆ„μ  μΆ”κ°€ μ „μš© ν•¨μˆ˜ (createDailyReport λŒ€μ²΄) - μ ˆλŒ€ μ‚­μ œ μ•ˆν•¨! */ -const createDailyReport = async (reportData, callback) => { +const createDailyReport = async (reportData) => { const { report_date, worker_id, work_entries, created_by, created_by_name, total_hours } = reportData; const db = await getDb(); const conn = await db.getConnection(); @@ -62,9 +46,8 @@ const createDailyReport = async (reportData, callback) => { try { await conn.beginTransaction(); - console.log(`πŸ“ ${created_by_name}이 ${report_date} ${worker_id}번 μž‘μ—…μžμ—κ²Œ 데이터 μΆ”κ°€ 쀑...`); + console.log(`${created_by_name}이 ${report_date} ${worker_id}번 μž‘μ—…μžμ—κ²Œ 데이터 μΆ”κ°€ 쀑...`); - // βœ… μˆ˜μ •λœ 쿼리 (ν…Œμ΄λΈ” alias μΆ”κ°€): const [existingReports] = await conn.query( `SELECT dwr.created_by, u.name as created_by_name, COUNT(*) as count, SUM(dwr.work_hours) as total_hours FROM daily_work_reports dwr @@ -74,17 +57,16 @@ const createDailyReport = async (reportData, callback) => { [report_date, worker_id] ); - console.log('κΈ°μ‘΄ 데이터 (μ‚­μ œν•˜μ§€ μ•ŠμŒ):', existingReports); - // 2. βœ… μ‚­μ œ 없이 μƒˆλ‘œμš΄ λ°μ΄ν„°λ§Œ μΆ”κ°€! + // μ‚­μ œ 없이 μƒˆλ‘œμš΄ λ°μ΄ν„°λ§Œ μΆ”κ°€! const insertedIds = []; for (const entry of work_entries) { const { project_id, work_type_id, work_status_id, error_type_id, work_hours } = entry; const [insertResult] = await conn.query( - `INSERT INTO daily_work_reports - (report_date, worker_id, project_id, work_type_id, work_status_id, error_type_id, work_hours, created_by, created_at) + `INSERT INTO daily_work_reports + (report_date, worker_id, project_id, work_type_id, work_status_id, error_type_id, work_hours, created_by, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, NOW())`, [report_date, worker_id, project_id, work_type_id, work_status_id || 1, error_type_id || null, work_hours, created_by] ); @@ -92,7 +74,6 @@ const createDailyReport = async (reportData, callback) => { insertedIds.push(insertResult.insertId); } - // βœ… μˆ˜μ •λœ 쿼리: const [finalReports] = await conn.query( `SELECT dwr.created_by, u.name as created_by_name, COUNT(*) as count, SUM(dwr.work_hours) as total_hours FROM daily_work_reports dwr @@ -109,13 +90,13 @@ const createDailyReport = async (reportData, callback) => { finalReports.forEach(report => { console.log(` - ${report.created_by_name}: ${report.total_hours}μ‹œκ°„ (${report.count}개 ν•­λͺ©)`); }); - console.log(` πŸ“Š 총합: ${grandTotal}μ‹œκ°„`); + console.log(` 총합: ${grandTotal}μ‹œκ°„`); - // 4. 감사 둜그 μΆ”κ°€ + // 감사 둜그 μΆ”κ°€ try { await conn.query( - `INSERT INTO work_report_audit_log - (action, report_id, new_values, changed_by, change_reason, created_at) + `INSERT INTO work_report_audit_log + (action, report_id, new_values, changed_by, change_reason, created_at) VALUES (?, ?, ?, ?, ?, NOW())`, [ 'ADD_ACCUMULATE', @@ -139,16 +120,15 @@ const createDailyReport = async (reportData, callback) => { await conn.commit(); - // 5. κ·Όνƒœ 기둝 동기화 (μΆ”κ°€) + // κ·Όνƒœ 기둝 동기화 try { const AttendanceModel = require('./attendanceModel'); await AttendanceModel.syncWithWorkReports(worker_id, report_date); } catch (syncErr) { console.error('κ·Όνƒœ 기둝 동기화 μ‹€νŒ¨:', syncErr); - // 메인 νŠΈλžœμž­μ…˜μ€ μ„±κ³΅ν–ˆμœΌλ―€λ‘œ 동기화 μ‹€νŒ¨λ‘œ λ‘€λ°±ν•˜μ§€ μ•ŠμŒ (비동기 처리 λ˜λŠ” λ¬΄μ‹œ) } - callback(null, { + return { success: true, inserted_count: insertedIds.length, deleted_count: 0, // 항상 0 (μ‚­μ œ μ•ˆν•¨) @@ -160,104 +140,88 @@ const createDailyReport = async (reportData, callback) => { total_contributors: finalReports.length, contributors: finalReports } - }); + }; } catch (err) { - await conn.rollback(); - console.error('μž‘μ—…λ³΄κ³ μ„œ λˆ„μ  μΆ”κ°€ 였λ₯˜:', err); - callback(err); + try { await conn.rollback(); } catch (e) {} + throw err; } finally { conn.release(); } }; /** - * πŸ“Š νŠΉμ • λ‚ μ§œ + μž‘μ—…μž + μž‘μ„±μžμ˜ λˆ„μ  ν˜„ν™© 쑰회 + * νŠΉμ • λ‚ μ§œ + μž‘μ—…μž + μž‘μ„±μžμ˜ λˆ„μ  ν˜„ν™© 쑰회 */ -const getMyAccumulatedHours = async (date, worker_id, created_by, callback) => { - try { - const db = await getDb(); +const getMyAccumulatedHours = async (date, worker_id, created_by) => { + const db = await getDb(); - const sql = ` - SELECT - SUM(work_hours) as my_total_hours, - COUNT(*) as my_entry_count, - GROUP_CONCAT( - CONCAT(p.project_name, ':', work_hours, 'h') - ORDER BY created_at - ) as my_entries - FROM daily_work_reports dwr - LEFT JOIN projects p ON dwr.project_id = p.project_id - WHERE dwr.report_date = ? AND dwr.worker_id = ? AND dwr.created_by = ? - `; + const sql = ` + SELECT + SUM(work_hours) as my_total_hours, + COUNT(*) as my_entry_count, + GROUP_CONCAT( + CONCAT(p.project_name, ':', work_hours, 'h') + ORDER BY created_at + ) as my_entries + FROM daily_work_reports dwr + LEFT JOIN projects p ON dwr.project_id = p.project_id + WHERE dwr.report_date = ? AND dwr.worker_id = ? AND dwr.created_by = ? + `; - const [rows] = await db.query(sql, [date, worker_id, created_by]); - callback(null, rows[0] || { my_total_hours: 0, my_entry_count: 0, my_entries: null }); - } catch (err) { - console.error('개인 λˆ„μ  ν˜„ν™© 쑰회 였λ₯˜:', err); - callback(err); - } + const [rows] = await db.query(sql, [date, worker_id, created_by]); + return rows[0] || { my_total_hours: 0, my_entry_count: 0, my_entries: null }; }; /** - * πŸ“Š λˆ„μ  ν˜„ν™© 쑰회 - λ‚ μ§œ+μž‘μ—…μžλ³„ (λͺ¨λ“  κΈ°μ—¬μž) + * λˆ„μ  ν˜„ν™© 쑰회 - λ‚ μ§œ+μž‘μ—…μžλ³„ (λͺ¨λ“  κΈ°μ—¬μž) */ -const getAccumulatedReportsByDate = async (date, worker_id, callback) => { - try { - const db = await getDb(); +const getAccumulatedReportsByDate = async (date, worker_id) => { + const db = await getDb(); - const sql = getSelectQuery() + ` - WHERE dwr.report_date = ? AND dwr.worker_id = ? - ORDER BY dwr.created_by, dwr.created_at ASC - `; + const sql = getSelectQuery() + ` + WHERE dwr.report_date = ? AND dwr.worker_id = ? + ORDER BY dwr.created_by, dwr.created_at ASC + `; - const [rows] = await db.query(sql, [date, worker_id]); - callback(null, rows); - } catch (err) { - console.error('λˆ„μ  ν˜„ν™© 쑰회 였λ₯˜:', err); - callback(err); - } + const [rows] = await db.query(sql, [date, worker_id]); + return rows; }; /** - * πŸ“Š κΈ°μ—¬μžλ³„ μš”μ•½ 쑰회 + * κΈ°μ—¬μžλ³„ μš”μ•½ 쑰회 */ -const getContributorsByDate = async (date, worker_id, callback) => { - try { - const db = await getDb(); +const getContributorsByDate = async (date, worker_id) => { + const db = await getDb(); - const sql = ` - SELECT - dwr.created_by, - u.name as created_by_name, - COUNT(*) as entry_count, - SUM(dwr.work_hours) as total_hours, - MIN(dwr.created_at) as first_entry, - MAX(dwr.created_at) as last_entry, - GROUP_CONCAT( - CONCAT(p.project_name, ':', dwr.work_hours, 'h') - ORDER BY dwr.created_at SEPARATOR ', ' - ) as entry_details - FROM daily_work_reports dwr - LEFT JOIN users u ON dwr.created_by = u.user_id - LEFT JOIN projects p ON dwr.project_id = p.project_id - WHERE dwr.report_date = ? AND dwr.worker_id = ? - GROUP BY dwr.created_by - ORDER BY total_hours DESC, first_entry ASC - `; + const sql = ` + SELECT + dwr.created_by, + u.name as created_by_name, + COUNT(*) as entry_count, + SUM(dwr.work_hours) as total_hours, + MIN(dwr.created_at) as first_entry, + MAX(dwr.created_at) as last_entry, + GROUP_CONCAT( + CONCAT(p.project_name, ':', dwr.work_hours, 'h') + ORDER BY dwr.created_at SEPARATOR ', ' + ) as entry_details + FROM daily_work_reports dwr + LEFT JOIN users u ON dwr.created_by = u.user_id + LEFT JOIN projects p ON dwr.project_id = p.project_id + WHERE dwr.report_date = ? AND dwr.worker_id = ? + GROUP BY dwr.created_by + ORDER BY total_hours DESC, first_entry ASC + `; - const [rows] = await db.query(sql, [date, worker_id]); - callback(null, rows); - } catch (err) { - console.error('κΈ°μ—¬μžλ³„ μš”μ•½ 쑰회 였λ₯˜:', err); - callback(err); - } + const [rows] = await db.query(sql, [date, worker_id]); + return rows; }; /** - * πŸ—‘οΈ νŠΉμ • μž‘μ—… ν•­λͺ©λ§Œ μ‚­μ œ (κ°œλ³„ μ‚­μ œ) + * νŠΉμ • μž‘μ—… ν•­λͺ©λ§Œ μ‚­μ œ (κ°œλ³„ μ‚­μ œ) */ -const removeSpecificEntry = async (entry_id, deleted_by, callback) => { +const removeSpecificEntry = async (entry_id, deleted_by) => { const db = await getDb(); const conn = await db.getConnection(); @@ -276,16 +240,14 @@ const removeSpecificEntry = async (entry_id, deleted_by, callback) => { ); if (entryInfo.length === 0) { - await conn.rollback(); - return callback(new Error('μ‚­μ œν•  ν•­λͺ©μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.')); + throw new Error('μ‚­μ œν•  ν•­λͺ©μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.'); } const entry = entryInfo[0]; // κΆŒν•œ 확인: 본인이 μž‘μ„±ν•œ κ²ƒλ§Œ μ‚­μ œ κ°€λŠ₯ if (entry.created_by !== deleted_by) { - await conn.rollback(); - return callback(new Error('본인이 μž‘μ„±ν•œ ν•­λͺ©λ§Œ μ‚­μ œν•  수 μžˆμŠ΅λ‹ˆλ‹€.')); + throw new Error('본인이 μž‘μ„±ν•œ ν•­λͺ©λ§Œ μ‚­μ œν•  수 μžˆμŠ΅λ‹ˆλ‹€.'); } // κ°œλ³„ ν•­λͺ© μ‚­μ œ @@ -295,19 +257,18 @@ const removeSpecificEntry = async (entry_id, deleted_by, callback) => { console.log(`[μ‚­μ œ 둜그] μž‘μ—…μž: ${entry.worker_name}, ν”„λ‘œμ νŠΈ: ${entry.project_name}, μž‘μ—…μ‹œκ°„: ${entry.work_hours}μ‹œκ°„, μ‚­μ œμž: ${deleted_by}`); await conn.commit(); - callback(null, { + return { success: true, deleted_entry: { worker_name: entry.worker_name, project_name: entry.project_name, work_hours: entry.work_hours } - }); + }; } catch (err) { - await conn.rollback(); - console.error('κ°œλ³„ ν•­λͺ© μ‚­μ œ 였λ₯˜:', err); - callback(err); + try { await conn.rollback(); } catch (e) {} + throw err; } finally { conn.release(); } @@ -350,336 +311,279 @@ const getSelectQuery = () => ` /** * 7. ID둜 μž‘μ—…λ³΄κ³ μ„œ 쑰회 */ -const getById = async (id, callback) => { - try { - const db = await getDb(); - const sql = getSelectQuery() + 'WHERE dwr.id = ?'; - const [rows] = await db.query(sql, [id]); - callback(null, rows[0] || null); - } catch (err) { - console.error('ID둜 μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); - callback(err); - } +const getById = async (id) => { + const db = await getDb(); + const sql = getSelectQuery() + 'WHERE dwr.id = ?'; + const [rows] = await db.query(sql, [id]); + return rows[0] || null; }; /** * 8. 일일 μž‘μ—…λ³΄κ³ μ„œ 쑰회 (λ‚ μ§œλ³„) */ -const getByDate = async (date, callback) => { - try { - const db = await getDb(); - const sql = getSelectQuery() + ` - WHERE dwr.report_date = ? - ORDER BY w.worker_name ASC, p.project_name ASC, dwr.id ASC - `; - const [rows] = await db.query(sql, [date]); - callback(null, rows); - } catch (err) { - console.error('λ‚ μ§œλ³„ μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); - callback(err); - } +const getByDate = async (date) => { + const db = await getDb(); + const sql = getSelectQuery() + ` + WHERE dwr.report_date = ? + ORDER BY w.worker_name ASC, p.project_name ASC, dwr.id ASC + `; + const [rows] = await db.query(sql, [date]); + return rows; }; /** * 9. 일일 μž‘μ—…λ³΄κ³ μ„œ 쑰회 (λ‚ μ§œ + μž‘μ„±μžλ³„) */ -const getByDateAndCreator = async (date, created_by, callback) => { - try { - const db = await getDb(); - const sql = getSelectQuery() + ` - WHERE dwr.report_date = ? AND dwr.created_by = ? - ORDER BY w.worker_name ASC, p.project_name ASC, dwr.id ASC - `; - const [rows] = await db.query(sql, [date, created_by]); - callback(null, rows); - } catch (err) { - console.error('λ‚ μ§œ+μž‘μ„±μžλ³„ μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); - callback(err); - } +const getByDateAndCreator = async (date, created_by) => { + const db = await getDb(); + const sql = getSelectQuery() + ` + WHERE dwr.report_date = ? AND dwr.created_by = ? + ORDER BY w.worker_name ASC, p.project_name ASC, dwr.id ASC + `; + const [rows] = await db.query(sql, [date, created_by]); + return rows; }; /** * 10. 일일 μž‘μ—…λ³΄κ³ μ„œ 쑰회 (μž‘μ—…μžλ³„) */ -const getByWorker = async (worker_id, callback) => { - try { - const db = await getDb(); - const sql = getSelectQuery() + ` - WHERE dwr.worker_id = ? - ORDER BY dwr.report_date DESC, dwr.id ASC - `; - const [rows] = await db.query(sql, [worker_id]); - callback(null, rows); - } catch (err) { - console.error('μž‘μ—…μžλ³„ μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); - callback(err); - } +const getByWorker = async (worker_id) => { + const db = await getDb(); + const sql = getSelectQuery() + ` + WHERE dwr.worker_id = ? + ORDER BY dwr.report_date DESC, dwr.id ASC + `; + const [rows] = await db.query(sql, [worker_id]); + return rows; }; /** * 11. 일일 μž‘μ—…λ³΄κ³ μ„œ 쑰회 (λ‚ μ§œ + μž‘μ—…μž) */ -const getByDateAndWorker = async (date, worker_id, callback) => { - try { - const db = await getDb(); - const sql = getSelectQuery() + ` - WHERE dwr.report_date = ? AND dwr.worker_id = ? - ORDER BY dwr.id ASC - `; - const [rows] = await db.query(sql, [date, worker_id]); - callback(null, rows); - } catch (err) { - console.error('λ‚ μ§œ+μž‘μ—…μžλ³„ μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); - callback(err); - } +const getByDateAndWorker = async (date, worker_id) => { + const db = await getDb(); + const sql = getSelectQuery() + ` + WHERE dwr.report_date = ? AND dwr.worker_id = ? + ORDER BY dwr.id ASC + `; + const [rows] = await db.query(sql, [date, worker_id]); + return rows; }; /** * 12. 기간별 쑰회 */ -const getByRange = async (start_date, end_date, callback) => { - try { - const db = await getDb(); - const sql = getSelectQuery() + ` - WHERE dwr.report_date BETWEEN ? AND ? - ORDER BY dwr.report_date DESC, w.worker_name ASC, dwr.id ASC - `; - const [rows] = await db.query(sql, [start_date, end_date]); - callback(null, rows); - } catch (err) { - console.error('기간별 μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); - callback(err); - } +const getByRange = async (start_date, end_date) => { + const db = await getDb(); + const sql = getSelectQuery() + ` + WHERE dwr.report_date BETWEEN ? AND ? + ORDER BY dwr.report_date DESC, w.worker_name ASC, dwr.id ASC + `; + const [rows] = await db.query(sql, [start_date, end_date]); + return rows; }; /** * 13. 상세 검색 (νŽ˜μ΄μ§€λ„€μ΄μ…˜ 포함) */ -const searchWithDetails = async (params, callback) => { +const searchWithDetails = async (params) => { const { start_date, end_date, worker_id, project_id, work_status_id, created_by, page, limit } = params; - try { - const db = await getDb(); + const db = await getDb(); - // 쑰건 ꡬ성 - let whereConditions = ['dwr.report_date BETWEEN ? AND ?']; - let queryParams = [start_date, end_date]; + // 쑰건 ꡬ성 + let whereConditions = ['dwr.report_date BETWEEN ? AND ?']; + let queryParams = [start_date, end_date]; - if (worker_id) { - whereConditions.push('dwr.worker_id = ?'); - queryParams.push(worker_id); - } - - if (project_id) { - whereConditions.push('dwr.project_id = ?'); - queryParams.push(project_id); - } - - if (work_status_id) { - whereConditions.push('dwr.work_status_id = ?'); - queryParams.push(work_status_id); - } - - if (created_by) { - whereConditions.push('dwr.created_by = ?'); - queryParams.push(created_by); - } - - const whereClause = whereConditions.join(' AND '); - - // 총 개수 쑰회 - const countQuery = ` - SELECT COUNT(*) as total - FROM daily_work_reports dwr - WHERE ${whereClause} - `; - const [countResult] = await db.query(countQuery, queryParams); - const total = countResult[0].total; - - // 데이터 쑰회 (JOIN 포함) - const offset = (page - 1) * limit; - const dataQuery = getSelectQuery() + ` - WHERE ${whereClause} - ORDER BY dwr.report_date DESC, w.worker_name ASC, dwr.created_at DESC - LIMIT ? OFFSET ? - `; - - const dataParams = [...queryParams, limit, offset]; - const [rows] = await db.query(dataQuery, dataParams); - - callback(null, { reports: rows, total }); - } catch (err) { - console.error('상세 검색 였λ₯˜:', err); - callback(err); + if (worker_id) { + whereConditions.push('dwr.worker_id = ?'); + queryParams.push(worker_id); } + + if (project_id) { + whereConditions.push('dwr.project_id = ?'); + queryParams.push(project_id); + } + + if (work_status_id) { + whereConditions.push('dwr.work_status_id = ?'); + queryParams.push(work_status_id); + } + + if (created_by) { + whereConditions.push('dwr.created_by = ?'); + queryParams.push(created_by); + } + + const whereClause = whereConditions.join(' AND '); + + // 총 개수 쑰회 + const countQuery = ` + SELECT COUNT(*) as total + FROM daily_work_reports dwr + WHERE ${whereClause} + `; + const [countResult] = await db.query(countQuery, queryParams); + const total = countResult[0].total; + + // 데이터 쑰회 (JOIN 포함) + const offset = (page - 1) * limit; + const dataQuery = getSelectQuery() + ` + WHERE ${whereClause} + ORDER BY dwr.report_date DESC, w.worker_name ASC, dwr.created_at DESC + LIMIT ? OFFSET ? + `; + + const dataParams = [...queryParams, limit, offset]; + const [rows] = await db.query(dataQuery, dataParams); + + return { reports: rows, total }; }; /** * 14. 일일 근무 μš”μ•½ 쑰회 (λ‚ μ§œλ³„) */ -const getSummaryByDate = async (date, callback) => { - try { - const db = await getDb(); +const getSummaryByDate = async (date) => { + const db = await getDb(); - const sql = ` - SELECT - dwr.worker_id, - w.worker_name, - dwr.report_date, - SUM(dwr.work_hours) as total_hours, - COUNT(*) as work_entries_count, - SUM(CASE WHEN dwr.work_status_id = 2 THEN 1 ELSE 0 END) as error_count - FROM daily_work_reports dwr - LEFT JOIN workers w ON dwr.worker_id = w.worker_id - WHERE dwr.report_date = ? - GROUP BY dwr.worker_id, dwr.report_date - ORDER BY w.worker_name ASC - `; - const [rows] = await db.query(sql, [date]); - callback(null, rows); - } catch (err) { - console.error('일일 근무 μš”μ•½ 쑰회 였λ₯˜:', err); - callback(err); - } + const sql = ` + SELECT + dwr.worker_id, + w.worker_name, + dwr.report_date, + SUM(dwr.work_hours) as total_hours, + COUNT(*) as work_entries_count, + SUM(CASE WHEN dwr.work_status_id = 2 THEN 1 ELSE 0 END) as error_count + FROM daily_work_reports dwr + LEFT JOIN workers w ON dwr.worker_id = w.worker_id + WHERE dwr.report_date = ? + GROUP BY dwr.worker_id, dwr.report_date + ORDER BY w.worker_name ASC + `; + const [rows] = await db.query(sql, [date]); + return rows; }; /** * 15. 일일 근무 μš”μ•½ 쑰회 (μž‘μ—…μžλ³„) */ -const getSummaryByWorker = async (worker_id, callback) => { - try { - const db = await getDb(); +const getSummaryByWorker = async (worker_id) => { + const db = await getDb(); - const sql = ` - SELECT - dwr.report_date, - dwr.worker_id, - w.worker_name, - SUM(dwr.work_hours) as total_hours, - COUNT(*) as work_entries_count, - SUM(CASE WHEN dwr.work_status_id = 2 THEN 1 ELSE 0 END) as error_count - FROM daily_work_reports dwr - LEFT JOIN workers w ON dwr.worker_id = w.worker_id - WHERE dwr.worker_id = ? - GROUP BY dwr.report_date, dwr.worker_id - ORDER BY dwr.report_date DESC - `; - const [rows] = await db.query(sql, [worker_id]); - callback(null, rows); - } catch (err) { - console.error('μž‘μ—…μžλ³„ 근무 μš”μ•½ 쑰회 였λ₯˜:', err); - callback(err); - } + const sql = ` + SELECT + dwr.report_date, + dwr.worker_id, + w.worker_name, + SUM(dwr.work_hours) as total_hours, + COUNT(*) as work_entries_count, + SUM(CASE WHEN dwr.work_status_id = 2 THEN 1 ELSE 0 END) as error_count + FROM daily_work_reports dwr + LEFT JOIN workers w ON dwr.worker_id = w.worker_id + WHERE dwr.worker_id = ? + GROUP BY dwr.report_date, dwr.worker_id + ORDER BY dwr.report_date DESC + `; + const [rows] = await db.query(sql, [worker_id]); + return rows; }; /** * 16. μ›”κ°„ μš”μ•½ */ -const getMonthlySummary = async (year, month, callback) => { - try { - const db = await getDb(); - const start = `${year.padStart(4, '0')}-${month.padStart(2, '0')}-01`; - const end = `${year.padStart(4, '0')}-${month.padStart(2, '0')}-31`; +const getMonthlySummary = async (year, month) => { + const db = await getDb(); + const start = `${year.padStart(4, '0')}-${month.padStart(2, '0')}-01`; + const end = `${year.padStart(4, '0')}-${month.padStart(2, '0')}-31`; - const sql = ` - SELECT - dwr.report_date, - dwr.worker_id, - w.worker_name, - SUM(dwr.work_hours) as total_work_hours, - COUNT(DISTINCT dwr.project_id) as project_count, - COUNT(*) as work_entries_count, - SUM(CASE WHEN dwr.work_status_id = 2 THEN 1 ELSE 0 END) as error_count, - GROUP_CONCAT(DISTINCT p.project_name ORDER BY p.project_name) as projects, - GROUP_CONCAT(DISTINCT wt.name ORDER BY wt.name) as work_types - FROM daily_work_reports dwr - LEFT JOIN workers w ON dwr.worker_id = w.worker_id - LEFT JOIN projects p ON dwr.project_id = p.project_id - LEFT JOIN work_types wt ON dwr.work_type_id = wt.id - WHERE dwr.report_date BETWEEN ? AND ? - GROUP BY dwr.report_date, dwr.worker_id - ORDER BY dwr.report_date DESC, w.worker_name ASC - `; - const [rows] = await db.query(sql, [start, end]); - callback(null, rows); - } catch (err) { - console.error('μ›”κ°„ μš”μ•½ 쑰회 였λ₯˜:', err); - callback(err); - } + const sql = ` + SELECT + dwr.report_date, + dwr.worker_id, + w.worker_name, + SUM(dwr.work_hours) as total_work_hours, + COUNT(DISTINCT dwr.project_id) as project_count, + COUNT(*) as work_entries_count, + SUM(CASE WHEN dwr.work_status_id = 2 THEN 1 ELSE 0 END) as error_count, + GROUP_CONCAT(DISTINCT p.project_name ORDER BY p.project_name) as projects, + GROUP_CONCAT(DISTINCT wt.name ORDER BY wt.name) as work_types + FROM daily_work_reports dwr + LEFT JOIN workers w ON dwr.worker_id = w.worker_id + LEFT JOIN projects p ON dwr.project_id = p.project_id + LEFT JOIN work_types wt ON dwr.work_type_id = wt.id + WHERE dwr.report_date BETWEEN ? AND ? + GROUP BY dwr.report_date, dwr.worker_id + ORDER BY dwr.report_date DESC, w.worker_name ASC + `; + const [rows] = await db.query(sql, [start, end]); + return rows; }; /** * 17. μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • */ -const updateById = async (id, updateData, callback) => { - try { - const db = await getDb(); +const updateById = async (id, updateData) => { + const db = await getDb(); - const setFields = []; - const values = []; + const setFields = []; + const values = []; - if (updateData.work_hours !== undefined) { - setFields.push('work_hours = ?'); - values.push(updateData.work_hours); - } - - if (updateData.work_status_id !== undefined) { - setFields.push('work_status_id = ?'); - values.push(updateData.work_status_id); - } - - if (updateData.error_type_id !== undefined) { - setFields.push('error_type_id = ?'); - values.push(updateData.error_type_id); - } - - if (updateData.project_id !== undefined) { - setFields.push('project_id = ?'); - values.push(updateData.project_id); - } - - if (updateData.work_type_id !== undefined) { - setFields.push('work_type_id = ?'); - values.push(updateData.work_type_id); - } - - setFields.push('updated_at = NOW()'); - - if (updateData.updated_by) { - setFields.push('updated_by = ?'); - values.push(updateData.updated_by); - } - - values.push(id); - - const sql = `UPDATE daily_work_reports SET ${setFields.join(', ')} WHERE id = ?`; - const [result] = await db.query(sql, values); - - - - // [Sync] κ·Όνƒœ 기둝 동기화 - try { - const [targetReport] = await db.query('SELECT worker_id, report_date FROM daily_work_reports WHERE id = ?', [id]); - if (targetReport.length > 0) { - const { worker_id, report_date } = targetReport[0]; - const AttendanceModel = require('./attendanceModel'); - await AttendanceModel.syncWithWorkReports(worker_id, report_date); - } - } catch (syncErr) { - console.error('κ·Όνƒœ 기둝 동기화 μ‹€νŒ¨ (Update):', syncErr); - } - - callback(null, result.affectedRows); - } catch (err) { - console.error('μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • 였λ₯˜:', err); - callback(err); + if (updateData.work_hours !== undefined) { + setFields.push('work_hours = ?'); + values.push(updateData.work_hours); } + + if (updateData.work_status_id !== undefined) { + setFields.push('work_status_id = ?'); + values.push(updateData.work_status_id); + } + + if (updateData.error_type_id !== undefined) { + setFields.push('error_type_id = ?'); + values.push(updateData.error_type_id); + } + + if (updateData.project_id !== undefined) { + setFields.push('project_id = ?'); + values.push(updateData.project_id); + } + + if (updateData.work_type_id !== undefined) { + setFields.push('work_type_id = ?'); + values.push(updateData.work_type_id); + } + + setFields.push('updated_at = NOW()'); + + if (updateData.updated_by) { + setFields.push('updated_by = ?'); + values.push(updateData.updated_by); + } + + values.push(id); + + const sql = `UPDATE daily_work_reports SET ${setFields.join(', ')} WHERE id = ?`; + const [result] = await db.query(sql, values); + + // [Sync] κ·Όνƒœ 기둝 동기화 + try { + const [targetReport] = await db.query('SELECT worker_id, report_date FROM daily_work_reports WHERE id = ?', [id]); + if (targetReport.length > 0) { + const { worker_id, report_date } = targetReport[0]; + const AttendanceModel = require('./attendanceModel'); + await AttendanceModel.syncWithWorkReports(worker_id, report_date); + } + } catch (syncErr) { + console.error('κ·Όνƒœ 기둝 동기화 μ‹€νŒ¨ (Update):', syncErr); + } + + return result.affectedRows; }; /** * 18. νŠΉμ • μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ */ -const removeById = async (id, deletedBy, callback) => { +const removeById = async (id, deletedBy) => { const db = await getDb(); const conn = await db.getConnection(); @@ -696,8 +600,8 @@ const removeById = async (id, deletedBy, callback) => { if (reportInfo.length > 0 && deletedBy) { try { await conn.query( - `INSERT INTO work_report_audit_log - (action, report_id, old_values, changed_by, change_reason, created_at) + `INSERT INTO work_report_audit_log + (action, report_id, old_values, changed_by, change_reason, created_at) VALUES ('DELETE', ?, ?, ?, 'Manual deletion', NOW())`, [id, JSON.stringify(reportInfo[0]), deletedBy] ); @@ -708,7 +612,6 @@ const removeById = async (id, deletedBy, callback) => { await conn.commit(); - // [Sync] κ·Όνƒœ 기둝 동기화 if (reportInfo.length > 0) { try { @@ -720,11 +623,10 @@ const removeById = async (id, deletedBy, callback) => { } } - callback(null, result.affectedRows); + return result.affectedRows; } catch (err) { - await conn.rollback(); - console.error('μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ 였λ₯˜:', err); - callback(err); + try { await conn.rollback(); } catch (e) {} + throw err; } finally { conn.release(); } @@ -733,7 +635,7 @@ const removeById = async (id, deletedBy, callback) => { /** * 19. μž‘μ—…μžμ˜ νŠΉμ • λ‚ μ§œ 전체 μ‚­μ œ */ -const removeByDateAndWorker = async (date, worker_id, deletedBy, callback) => { +const removeByDateAndWorker = async (date, worker_id, deletedBy) => { const db = await getDb(); const conn = await db.getConnection(); @@ -756,8 +658,8 @@ const removeByDateAndWorker = async (date, worker_id, deletedBy, callback) => { if (reportInfos.length > 0 && deletedBy) { try { await conn.query( - `INSERT INTO work_report_audit_log - (action, old_values, changed_by, change_reason, created_at) + `INSERT INTO work_report_audit_log + (action, old_values, changed_by, change_reason, created_at) VALUES ('DELETE_BATCH', ?, ?, 'Batch deletion by date and worker', NOW())`, [JSON.stringify({ deleted_reports: reportInfos, count: reportInfos.length }), deletedBy] ); @@ -768,7 +670,6 @@ const removeByDateAndWorker = async (date, worker_id, deletedBy, callback) => { await conn.commit(); - // [Sync] κ·Όνƒœ 기둝 동기화 try { const AttendanceModel = require('./attendanceModel'); @@ -777,58 +678,52 @@ const removeByDateAndWorker = async (date, worker_id, deletedBy, callback) => { console.error('κ·Όνƒœ 기둝 동기화 μ‹€νŒ¨ (Batch Delete):', syncErr); } - callback(null, result.affectedRows); + return result.affectedRows; } catch (err) { - await conn.rollback(); - console.error('μž‘μ—…λ³΄κ³ μ„œ 전체 μ‚­μ œ 였λ₯˜:', err); - callback(err); + try { await conn.rollback(); } catch (e) {} + throw err; } finally { conn.release(); } }; /** - * 20. 톡계 쑰회 (Promise 기반) + * 20. 톡계 쑰회 */ const getStatistics = async (start_date, end_date) => { - try { - const db = await getDb(); + const db = await getDb(); - const overallSql = ` - SELECT - COUNT(*) as total_reports, - SUM(work_hours) as total_hours, - COUNT(DISTINCT worker_id) as unique_workers, - COUNT(DISTINCT project_id) as unique_projects - FROM daily_work_reports - WHERE report_date BETWEEN ? AND ? - `; - const [overallRows] = await db.query(overallSql, [start_date, end_date]); + const overallSql = ` + SELECT + COUNT(*) as total_reports, + SUM(work_hours) as total_hours, + COUNT(DISTINCT worker_id) as unique_workers, + COUNT(DISTINCT project_id) as unique_projects + FROM daily_work_reports + WHERE report_date BETWEEN ? AND ? + `; + const [overallRows] = await db.query(overallSql, [start_date, end_date]); - const dailyStatsSql = ` - SELECT - report_date, - SUM(work_hours) as daily_hours, - COUNT(DISTINCT worker_id) as daily_workers - FROM daily_work_reports - WHERE report_date BETWEEN ? AND ? - GROUP BY report_date - ORDER BY report_date DESC - `; - const [dailyStats] = await db.query(dailyStatsSql, [start_date, end_date]); + const dailyStatsSql = ` + SELECT + report_date, + SUM(work_hours) as daily_hours, + COUNT(DISTINCT worker_id) as daily_workers + FROM daily_work_reports + WHERE report_date BETWEEN ? AND ? + GROUP BY report_date + ORDER BY report_date DESC + `; + const [dailyStats] = await db.query(dailyStatsSql, [start_date, end_date]); - return { - overall: overallRows[0], - daily_breakdown: dailyStats - }; - } catch (err) { - console.error('톡계 쑰회 였λ₯˜:', err); - throw new Error('λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 톡계 정보λ₯Ό μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); - } + return { + overall: overallRows[0], + daily_breakdown: dailyStats + }; }; /** - * [V2] μ—¬λŸ¬ μž‘μ—… λ³΄κ³ μ„œ ν•­λͺ©μ„ νŠΈλžœμž­μ…˜μœΌλ‘œ μƒμ„±ν•©λ‹ˆλ‹€. (Promise 기반) + * [V2] μ—¬λŸ¬ μž‘μ—… λ³΄κ³ μ„œ ν•­λͺ©μ„ νŠΈλžœμž­μ…˜μœΌλ‘œ μƒμ„±ν•©λ‹ˆλ‹€. * @param {object} modelData - μ„œλΉ„μŠ€ λ ˆμ΄μ–΄μ—μ„œ μ „λ‹¬λœ 데이터 * @returns {Promise} μ‚½μž…λœ ν•­λͺ©μ˜ ID λ°°μ—΄κ³Ό 개수 */ @@ -840,8 +735,8 @@ const createReportEntries = async ({ report_date, worker_id, entries }) => { const insertedIds = []; const sql = ` - INSERT INTO daily_work_reports - (report_date, worker_id, project_id, work_type_id, work_hours, work_status_id, error_type_id, created_by) + INSERT INTO daily_work_reports + (report_date, worker_id, project_id, work_type_id, work_hours, work_status_id, error_type_id, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `; @@ -862,8 +757,6 @@ const createReportEntries = async ({ report_date, worker_id, entries }) => { await conn.commit(); - - // [Sync] κ·Όνƒœ 기둝 동기화 try { const AttendanceModel = require('./attendanceModel'); @@ -879,9 +772,7 @@ const createReportEntries = async ({ report_date, worker_id, entries }) => { }; } catch (err) { - await conn.rollback(); - console.error('[Model] μž‘μ—… λ³΄κ³ μ„œ 생성 쀑 였λ₯˜ λ°œμƒ:', err); - // μ—¬κΈ°μ„œ λ°œμƒν•œ μ—λŸ¬λŠ” μ„œλΉ„μŠ€ λ ˆμ΄μ–΄λ‘œ μ „νŒŒλ©λ‹ˆλ‹€. + try { await conn.rollback(); } catch (e) {} throw new Error('λ°μ΄ν„°λ² μ΄μŠ€μ— μž‘μ—… λ³΄κ³ μ„œλ₯Ό μƒμ„±ν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); } finally { conn.release(); @@ -925,7 +816,7 @@ const getSelectQueryV2 = () => ` `; /** - * [V2] μ˜΅μ…˜ 기반으둜 μž‘μ—… λ³΄κ³ μ„œλ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€. (Promise 기반) + * [V2] μ˜΅μ…˜ 기반으둜 μž‘μ—… λ³΄κ³ μ„œλ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€. * @param {object} options - 쑰회 쑰건 (date, worker_id, created_by_user_id λ“±) * @returns {Promise} 쑰회된 μž‘μ—… λ³΄κ³ μ„œ λ°°μ—΄ */ @@ -951,7 +842,6 @@ const getReportsWithOptions = async (options) => { whereConditions.push('dwr.created_by = ?'); queryParams.push(options.created_by_user_id); } - // ν•„μš”μ— 따라 λ‹€λ₯Έ 쑰건 μΆ”κ°€ κ°€λŠ₯ (project_id λ“±) if (whereConditions.length === 0) { throw new Error('쑰회 쑰건이 ν•˜λ‚˜ 이상 ν•„μš”ν•©λ‹ˆλ‹€.'); @@ -960,17 +850,12 @@ const getReportsWithOptions = async (options) => { const whereClause = whereConditions.join(' AND '); const sql = `${getSelectQueryV2()} WHERE ${whereClause} ORDER BY w.worker_name ASC, p.project_name ASC`; - try { - const [rows] = await db.query(sql, queryParams); - return rows; - } catch (err) { - console.error('[Model] μž‘μ—… λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); - throw new Error('λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μž‘μ—… λ³΄κ³ μ„œλ₯Ό μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); - } + const [rows] = await db.query(sql, queryParams); + return rows; }; /** - * [V2] IDλ₯Ό κΈ°μ€€μœΌλ‘œ νŠΉμ • μž‘μ—… λ³΄κ³ μ„œ ν•­λͺ©μ„ μˆ˜μ •ν•©λ‹ˆλ‹€. (Promise 기반) + * [V2] IDλ₯Ό κΈ°μ€€μœΌλ‘œ νŠΉμ • μž‘μ—… λ³΄κ³ μ„œ ν•­λͺ©μ„ μˆ˜μ •ν•©λ‹ˆλ‹€. * @param {string} reportId - μˆ˜μ •ν•  λ³΄κ³ μ„œμ˜ ID * @param {object} updateData - μˆ˜μ •ν•  ν•„λ“œμ™€ κ°’ * @returns {Promise} 영ν–₯을 받은 ν–‰μ˜ 수 @@ -1011,28 +896,23 @@ const updateReportById = async (reportId, updateData) => { const sql = `UPDATE daily_work_reports SET ${setClauses.join(', ')} WHERE id = ?`; - try { - const [result] = await db.query(sql, queryParams); + const [result] = await db.query(sql, queryParams); - // [Sync] κ·Όνƒœ 기둝 동기화 - if (targetInfo) { - try { - const AttendanceModel = require('./attendanceModel'); - await AttendanceModel.syncWithWorkReports(targetInfo.worker_id, targetInfo.report_date); - } catch (syncErr) { - console.error('κ·Όνƒœ 기둝 동기화 μ‹€νŒ¨ (V2 Update):', syncErr); - } + // [Sync] κ·Όνƒœ 기둝 동기화 + if (targetInfo) { + try { + const AttendanceModel = require('./attendanceModel'); + await AttendanceModel.syncWithWorkReports(targetInfo.worker_id, targetInfo.report_date); + } catch (syncErr) { + console.error('κ·Όνƒœ 기둝 동기화 μ‹€νŒ¨ (V2 Update):', syncErr); } - - return result.affectedRows; - } catch (err) { - console.error(`[Model] μž‘μ—… λ³΄κ³ μ„œ μˆ˜μ • 였λ₯˜ (id: ${reportId}):`, err); - throw new Error('λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μž‘μ—… λ³΄κ³ μ„œλ₯Ό μˆ˜μ •ν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); } + + return result.affectedRows; }; /** - * [V2] IDλ₯Ό κΈ°μ€€μœΌλ‘œ νŠΉμ • μž‘μ—… λ³΄κ³ μ„œλ₯Ό μ‚­μ œν•©λ‹ˆλ‹€. (Promise 기반) + * [V2] IDλ₯Ό κΈ°μ€€μœΌλ‘œ νŠΉμ • μž‘μ—… λ³΄κ³ μ„œλ₯Ό μ‚­μ œν•©λ‹ˆλ‹€. * @param {string} reportId - μ‚­μ œν•  λ³΄κ³ μ„œ ID * @param {number} deletedByUserId - μ‚­μ œλ₯Ό μˆ˜ν–‰ν•˜λŠ” μ‚¬μš©μž ID * @returns {Promise} 영ν–₯을 받은 ν–‰μ˜ 수 @@ -1071,9 +951,8 @@ const removeReportById = async (reportId, deletedByUserId) => { return result.affectedRows; } catch (err) { - await conn.rollback(); - console.error(`[Model] μž‘μ—… λ³΄κ³ μ„œ μ‚­μ œ 였λ₯˜ (id: ${reportId}):`, err); - throw new Error('λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μž‘μ—… λ³΄κ³ μ„œλ₯Ό μ‚­μ œν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); + try { await conn.rollback(); } catch (e) {} + throw err; } finally { conn.release(); } @@ -1082,153 +961,108 @@ const removeReportById = async (reportId, deletedByUserId) => { // ========== λ§ˆμŠ€ν„° 데이터 CRUD λ©”μ„œλ“œλ“€ ========== /** - * πŸ“ μž‘μ—… μœ ν˜• 생성 + * μž‘μ—… μœ ν˜• 생성 */ -const createWorkType = async (data, callback) => { - try { - const db = await getDb(); - const { name, description, category } = data; - const [result] = await db.query( - 'INSERT INTO work_types (name, description, category) VALUES (?, ?, ?)', - [name, description, category] - ); - callback(null, { id: result.insertId, ...data }); - } catch (err) { - console.error('μž‘μ—… μœ ν˜• 생성 였λ₯˜:', err); - callback(err); - } +const createWorkType = async (data) => { + const db = await getDb(); + const { name, description, category } = data; + const [result] = await db.query( + 'INSERT INTO work_types (name, description, category) VALUES (?, ?, ?)', + [name, description, category] + ); + return { id: result.insertId, ...data }; }; /** - * ✏️ μž‘μ—… μœ ν˜• μˆ˜μ • + * μž‘μ—… μœ ν˜• μˆ˜μ • */ -const updateWorkType = async (id, data, callback) => { - try { - const db = await getDb(); - const { name, description, category } = data; - const [result] = await db.query( - 'UPDATE work_types SET name = ?, description = ?, category = ? WHERE id = ?', - [name, description, category, id] - ); - callback(null, result); - } catch (err) { - console.error('μž‘μ—… μœ ν˜• μˆ˜μ • 였λ₯˜:', err); - callback(err); - } +const updateWorkType = async (id, data) => { + const db = await getDb(); + const { name, description, category } = data; + const [result] = await db.query( + 'UPDATE work_types SET name = ?, description = ?, category = ? WHERE id = ?', + [name, description, category, id] + ); + return result; }; /** - * πŸ—‘οΈ μž‘μ—… μœ ν˜• μ‚­μ œ + * μž‘μ—… μœ ν˜• μ‚­μ œ */ -const deleteWorkType = async (id, callback) => { - try { - const db = await getDb(); - const [result] = await db.query('DELETE FROM work_types WHERE id = ?', [id]); - callback(null, result); - } catch (err) { - console.error('μž‘μ—… μœ ν˜• μ‚­μ œ 였λ₯˜:', err); - callback(err); - } +const deleteWorkType = async (id) => { + const db = await getDb(); + const [result] = await db.query('DELETE FROM work_types WHERE id = ?', [id]); + return result; }; /** - * πŸ“ μž‘μ—… μƒνƒœ 생성 + * μž‘μ—… μƒνƒœ 생성 */ -const createWorkStatus = async (data, callback) => { - try { - const db = await getDb(); - const { name, description, is_error } = data; - const [result] = await db.query( - 'INSERT INTO work_status_types (name, description, is_error) VALUES (?, ?, ?)', - [name, description, is_error || 0] - ); - callback(null, { id: result.insertId, ...data }); - } catch (err) { - console.error('μž‘μ—… μƒνƒœ 생성 였λ₯˜:', err); - callback(err); - } +const createWorkStatus = async (data) => { + const db = await getDb(); + const { name, description, is_error } = data; + const [result] = await db.query( + 'INSERT INTO work_status_types (name, description, is_error) VALUES (?, ?, ?)', + [name, description, is_error || 0] + ); + return { id: result.insertId, ...data }; }; /** - * ✏️ μž‘μ—… μƒνƒœ μˆ˜μ • + * μž‘μ—… μƒνƒœ μˆ˜μ • */ -const updateWorkStatus = async (id, data, callback) => { - try { - const db = await getDb(); - const { name, description, is_error } = data; - const [result] = await db.query( - 'UPDATE work_status_types SET name = ?, description = ?, is_error = ? WHERE id = ?', - [name, description, is_error || 0, id] - ); - callback(null, result); - } catch (err) { - console.error('μž‘μ—… μƒνƒœ μˆ˜μ • 였λ₯˜:', err); - callback(err); - } +const updateWorkStatus = async (id, data) => { + const db = await getDb(); + const { name, description, is_error } = data; + const [result] = await db.query( + 'UPDATE work_status_types SET name = ?, description = ?, is_error = ? WHERE id = ?', + [name, description, is_error || 0, id] + ); + return result; }; /** - * πŸ—‘οΈ μž‘μ—… μƒνƒœ μ‚­μ œ + * μž‘μ—… μƒνƒœ μ‚­μ œ */ -const deleteWorkStatus = async (id, callback) => { - try { - const db = await getDb(); - const [result] = await db.query('DELETE FROM work_status_types WHERE id = ?', [id]); - callback(null, result); - } catch (err) { - console.error('μž‘μ—… μƒνƒœ μ‚­μ œ 였λ₯˜:', err); - callback(err); - } +const deleteWorkStatus = async (id) => { + const db = await getDb(); + const [result] = await db.query('DELETE FROM work_status_types WHERE id = ?', [id]); + return result; }; /** - * πŸ“ 였λ₯˜ μœ ν˜• 생성 + * 였λ₯˜ μœ ν˜• 생성 */ -const createErrorType = async (data, callback) => { - try { - const db = await getDb(); - const { name, description, severity } = data; - const [result] = await db.query( - 'INSERT INTO error_types (name, description, severity) VALUES (?, ?, ?)', - [name, description, severity || 'medium'] - ); - callback(null, { id: result.insertId, ...data }); - } catch (err) { - console.error('였λ₯˜ μœ ν˜• 생성 였λ₯˜:', err); - callback(err); - } +const createErrorType = async (data) => { + const db = await getDb(); + const { name, description, severity } = data; + const [result] = await db.query( + 'INSERT INTO error_types (name, description, severity) VALUES (?, ?, ?)', + [name, description, severity || 'medium'] + ); + return { id: result.insertId, ...data }; }; /** - * ✏️ 였λ₯˜ μœ ν˜• μˆ˜μ • + * 였λ₯˜ μœ ν˜• μˆ˜μ • */ -const updateErrorType = async (id, data, callback) => { - try { - const db = await getDb(); - const { name, description, severity } = data; - const [result] = await db.query( - 'UPDATE error_types SET name = ?, description = ?, severity = ? WHERE id = ?', - [name, description, severity || 'medium', id] - ); - callback(null, result); - } catch (err) { - console.error('였λ₯˜ μœ ν˜• μˆ˜μ • 였λ₯˜:', err); - callback(err); - } +const updateErrorType = async (id, data) => { + const db = await getDb(); + const { name, description, severity } = data; + const [result] = await db.query( + 'UPDATE error_types SET name = ?, description = ?, severity = ? WHERE id = ?', + [name, description, severity || 'medium', id] + ); + return result; }; /** - * πŸ—‘οΈ 였λ₯˜ μœ ν˜• μ‚­μ œ + * 였λ₯˜ μœ ν˜• μ‚­μ œ */ -const deleteErrorType = async (id, callback) => { - try { - const db = await getDb(); - const [result] = await db.query('DELETE FROM error_types WHERE id = ?', [id]); - callback(null, result); - } catch (err) { - console.error('였λ₯˜ μœ ν˜• μ‚­μ œ 였λ₯˜:', err); - callback(err); - } +const deleteErrorType = async (id) => { + const db = await getDb(); + const [result] = await db.query('DELETE FROM error_types WHERE id = ?', [id]); + return result; }; @@ -1331,30 +1165,28 @@ const createFromTbmAssignment = async (reportData) => { }; } catch (err) { - await conn.rollback(); - console.error('[Model] TBM μž‘μ—…λ³΄κ³ μ„œ 생성 쀑 였λ₯˜ λ°œμƒ:', err); - throw new Error('TBM μž‘μ—…λ³΄κ³ μ„œ 생성 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); + try { await conn.rollback(); } catch (e) {} + throw err; } finally { conn.release(); } }; -// λͺ¨λ“  ν•¨μˆ˜ 내보내기 (Promise 기반 ν•¨μˆ˜ μœ„μ£Όλ‘œ μž¬κ΅¬μ„±) +// λͺ¨λ“  ν•¨μˆ˜ 내보내기 module.exports = { - // μƒˆλ‘œ μΆ”κ°€λœ V2 ν•¨μˆ˜ (Promise 기반) + // V2 ν•¨μˆ˜ createReportEntries, getReportsWithOptions, updateReportById, removeReportById, createFromTbmAssignment, - // Promise 기반으둜 λ¦¬νŒ©ν† λ§λœ ν•¨μˆ˜ + // 톡계/μš”μ•½ getStatistics, getSummaryByDate, getSummaryByWorker, - // 아직 λ¦¬νŒ©ν† λ§λ˜μ§€ μ•Šμ•˜μ§€λ§Œ ν•„μš”ν•œ κΈ°μ‘΄ ν•¨μˆ˜λ“€... - // (μ μ§„μ μœΌλ‘œ μ•„λž˜ ν•¨μˆ˜λ“€λ„ Promise 기반으둜 μ „ν™˜ν•΄μ•Ό 함) + // λ§ˆμŠ€ν„° 데이터 쑰회 getAllWorkTypes, getAllWorkStatusTypes, getAllErrorTypes, @@ -1369,6 +1201,8 @@ module.exports = { createErrorType, updateErrorType, deleteErrorType, + + // λ ˆκ±°μ‹œ ν•¨μˆ˜ (콜백 제거 μ™„λ£Œ) createDailyReport, getMyAccumulatedHours, getAccumulatedReportsByDate, @@ -1385,4 +1219,4 @@ module.exports = { updateById, removeById, removeByDateAndWorker, -}; \ No newline at end of file +}; diff --git a/system1-factory/api/models/equipmentModel.js b/system1-factory/api/models/equipmentModel.js index ec0bd15..0cd3a2d 100644 --- a/system1-factory/api/models/equipmentModel.js +++ b/system1-factory/api/models/equipmentModel.js @@ -4,459 +4,374 @@ const notificationModel = require('./notificationModel'); const EquipmentModel = { // CREATE - μ„€λΉ„ 생성 - create: async (equipmentData, callback) => { - try { - const db = await getDb(); - const query = ` - INSERT INTO equipments ( - equipment_code, equipment_name, equipment_type, model_name, - manufacturer, supplier, purchase_price, installation_date, serial_number, specifications, - status, notes, workplace_id, map_x_percent, map_y_percent, - map_width_percent, map_height_percent - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) - `; + create: async (equipmentData) => { + const db = await getDb(); + const query = ` + INSERT INTO equipments ( + equipment_code, equipment_name, equipment_type, model_name, + manufacturer, supplier, purchase_price, installation_date, serial_number, specifications, + status, notes, workplace_id, map_x_percent, map_y_percent, + map_width_percent, map_height_percent + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + `; - const values = [ - equipmentData.equipment_code, - equipmentData.equipment_name, - equipmentData.equipment_type || null, - equipmentData.model_name || null, - equipmentData.manufacturer || null, - equipmentData.supplier || null, - equipmentData.purchase_price || null, - equipmentData.installation_date || null, - equipmentData.serial_number || null, - equipmentData.specifications || null, - equipmentData.status || 'active', - equipmentData.notes || null, - equipmentData.workplace_id || null, - equipmentData.map_x_percent || null, - equipmentData.map_y_percent || null, - equipmentData.map_width_percent || null, - equipmentData.map_height_percent || null - ]; + const values = [ + equipmentData.equipment_code, + equipmentData.equipment_name, + equipmentData.equipment_type || null, + equipmentData.model_name || null, + equipmentData.manufacturer || null, + equipmentData.supplier || null, + equipmentData.purchase_price || null, + equipmentData.installation_date || null, + equipmentData.serial_number || null, + equipmentData.specifications || null, + equipmentData.status || 'active', + equipmentData.notes || null, + equipmentData.workplace_id || null, + equipmentData.map_x_percent || null, + equipmentData.map_y_percent || null, + equipmentData.map_width_percent || null, + equipmentData.map_height_percent || null + ]; - const [result] = await db.query(query, values); - callback(null, { - equipment_id: result.insertId, - ...equipmentData - }); - } catch (error) { - callback(error); - } + const [result] = await db.query(query, values); + return { + equipment_id: result.insertId, + ...equipmentData + }; }, // READ ALL - λͺ¨λ“  μ„€λΉ„ 쑰회 (필터링 μ˜΅μ…˜ 포함) - getAll: async (filters, callback) => { - try { - const db = await getDb(); - let query = ` - SELECT - e.*, - w.workplace_name, - wc.category_name - FROM equipments e - LEFT JOIN workplaces w ON e.workplace_id = w.workplace_id - LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id - WHERE 1=1 - `; + getAll: async (filters) => { + const db = await getDb(); + let query = ` + SELECT + e.*, + w.workplace_name, + wc.category_name + FROM equipments e + LEFT JOIN workplaces w ON e.workplace_id = w.workplace_id + LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id + WHERE 1=1 + `; - const values = []; + const values = []; - // 필터링: μž‘μ—…μž₯ ID - if (filters.workplace_id) { - query += ' AND e.workplace_id = ?'; - values.push(filters.workplace_id); - } - - // 필터링: μ„€λΉ„ μœ ν˜• - if (filters.equipment_type) { - query += ' AND e.equipment_type = ?'; - values.push(filters.equipment_type); - } - - // 필터링: μƒνƒœ - if (filters.status) { - query += ' AND e.status = ?'; - values.push(filters.status); - } - - // 필터링: 검색어 (μ„€λΉ„λͺ…, μ„€λΉ„μ½”λ“œ) - if (filters.search) { - query += ' AND (e.equipment_name LIKE ? OR e.equipment_code LIKE ?)'; - const searchTerm = `%${filters.search}%`; - values.push(searchTerm, searchTerm); - } - - query += ' ORDER BY e.equipment_code ASC'; - - const [rows] = await db.query(query, values); - callback(null, rows); - } catch (error) { - callback(error); + if (filters.workplace_id) { + query += ' AND e.workplace_id = ?'; + values.push(filters.workplace_id); } + + if (filters.equipment_type) { + query += ' AND e.equipment_type = ?'; + values.push(filters.equipment_type); + } + + if (filters.status) { + query += ' AND e.status = ?'; + values.push(filters.status); + } + + if (filters.search) { + query += ' AND (e.equipment_name LIKE ? OR e.equipment_code LIKE ?)'; + const searchTerm = `%${filters.search}%`; + values.push(searchTerm, searchTerm); + } + + query += ' ORDER BY e.equipment_code ASC'; + + const [rows] = await db.query(query, values); + return rows; }, // READ ONE - νŠΉμ • μ„€λΉ„ 쑰회 - getById: async (equipmentId, callback) => { - try { - const db = await getDb(); - const query = ` - SELECT - e.*, - w.workplace_name, - wc.category_name - FROM equipments e - LEFT JOIN workplaces w ON e.workplace_id = w.workplace_id - LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id - WHERE e.equipment_id = ? - `; - - const [rows] = await db.query(query, [equipmentId]); - callback(null, rows[0]); - } catch (error) { - callback(error); - } + getById: async (equipmentId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + e.*, + w.workplace_name, + wc.category_name + FROM equipments e + LEFT JOIN workplaces w ON e.workplace_id = w.workplace_id + LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id + WHERE e.equipment_id = ?`, + [equipmentId] + ); + return rows[0]; }, // READ BY WORKPLACE - νŠΉμ • μž‘μ—…μž₯의 μ„€λΉ„ 쑰회 - getByWorkplace: async (workplaceId, callback) => { - try { - const db = await getDb(); - const query = ` - SELECT e.* - FROM equipments e - WHERE e.workplace_id = ? - ORDER BY e.equipment_code ASC - `; - - const [rows] = await db.query(query, [workplaceId]); - callback(null, rows); - } catch (error) { - callback(error); - } + getByWorkplace: async (workplaceId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT e.* + FROM equipments e + WHERE e.workplace_id = ? + ORDER BY e.equipment_code ASC`, + [workplaceId] + ); + return rows; }, // READ ACTIVE - ν™œμ„± μ„€λΉ„λ§Œ 쑰회 - getActive: async (callback) => { - try { - const db = await getDb(); - const query = ` - SELECT - e.*, - w.workplace_name, - wc.category_name - FROM equipments e - LEFT JOIN workplaces w ON e.workplace_id = w.workplace_id - LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id - WHERE e.status = 'active' - ORDER BY e.equipment_code ASC - `; - - const [rows] = await db.query(query); - callback(null, rows); - } catch (error) { - callback(error); - } + getActive: async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + e.*, + w.workplace_name, + wc.category_name + FROM equipments e + LEFT JOIN workplaces w ON e.workplace_id = w.workplace_id + LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id + WHERE e.status = 'active' + ORDER BY e.equipment_code ASC` + ); + return rows; }, // UPDATE - μ„€λΉ„ μˆ˜μ • - update: async (equipmentId, equipmentData, callback) => { - try { - const db = await getDb(); - const query = ` - UPDATE equipments SET - equipment_code = ?, - equipment_name = ?, - equipment_type = ?, - model_name = ?, - manufacturer = ?, - supplier = ?, - purchase_price = ?, - installation_date = ?, - serial_number = ?, - specifications = ?, - status = ?, - notes = ?, - workplace_id = ?, - map_x_percent = ?, - map_y_percent = ?, - map_width_percent = ?, - map_height_percent = ?, - updated_at = NOW() - WHERE equipment_id = ? - `; + update: async (equipmentId, equipmentData) => { + const db = await getDb(); + const values = [ + equipmentData.equipment_code, + equipmentData.equipment_name, + equipmentData.equipment_type || null, + equipmentData.model_name || null, + equipmentData.manufacturer || null, + equipmentData.supplier || null, + equipmentData.purchase_price || null, + equipmentData.installation_date || null, + equipmentData.serial_number || null, + equipmentData.specifications || null, + equipmentData.status || 'active', + equipmentData.notes || null, + equipmentData.workplace_id || null, + equipmentData.map_x_percent || null, + equipmentData.map_y_percent || null, + equipmentData.map_width_percent || null, + equipmentData.map_height_percent || null, + equipmentId + ]; - const values = [ - equipmentData.equipment_code, - equipmentData.equipment_name, - equipmentData.equipment_type || null, - equipmentData.model_name || null, - equipmentData.manufacturer || null, - equipmentData.supplier || null, - equipmentData.purchase_price || null, - equipmentData.installation_date || null, - equipmentData.serial_number || null, - equipmentData.specifications || null, - equipmentData.status || 'active', - equipmentData.notes || null, - equipmentData.workplace_id || null, - equipmentData.map_x_percent || null, - equipmentData.map_y_percent || null, - equipmentData.map_width_percent || null, - equipmentData.map_height_percent || null, - equipmentId - ]; + const [result] = await db.query( + `UPDATE equipments SET + equipment_code = ?, + equipment_name = ?, + equipment_type = ?, + model_name = ?, + manufacturer = ?, + supplier = ?, + purchase_price = ?, + installation_date = ?, + serial_number = ?, + specifications = ?, + status = ?, + notes = ?, + workplace_id = ?, + map_x_percent = ?, + map_y_percent = ?, + map_width_percent = ?, + map_height_percent = ?, + updated_at = NOW() + WHERE equipment_id = ?`, + values + ); - const [result] = await db.query(query, values); - if (result.affectedRows === 0) { - return callback(new Error('Equipment not found')); - } - callback(null, { equipment_id: equipmentId, ...equipmentData }); - } catch (error) { - callback(error); + if (result.affectedRows === 0) { + throw new Error('Equipment not found'); } + return { equipment_id: equipmentId, ...equipmentData }; }, - // UPDATE MAP POSITION - 지도상 μœ„μΉ˜ μ—…λ°μ΄νŠΈ (μ„ νƒμ μœΌλ‘œ workplace_id도 μ—…λ°μ΄νŠΈ) - updateMapPosition: async (equipmentId, positionData, callback) => { - try { - const db = await getDb(); + // UPDATE MAP POSITION - 지도상 μœ„μΉ˜ μ—…λ°μ΄νŠΈ + updateMapPosition: async (equipmentId, positionData) => { + const db = await getDb(); - // workplace_idκ°€ ν¬ν•¨λœ 경우 ν•¨κ»˜ μ—…λ°μ΄νŠΈ - const hasWorkplaceId = positionData.workplace_id !== undefined; + const hasWorkplaceId = positionData.workplace_id !== undefined; - const query = hasWorkplaceId ? ` - UPDATE equipments SET - workplace_id = ?, - map_x_percent = ?, - map_y_percent = ?, - map_width_percent = ?, - map_height_percent = ?, - updated_at = NOW() - WHERE equipment_id = ? - ` : ` - UPDATE equipments SET - map_x_percent = ?, - map_y_percent = ?, - map_width_percent = ?, - map_height_percent = ?, - updated_at = NOW() - WHERE equipment_id = ? - `; + const query = hasWorkplaceId ? ` + UPDATE equipments SET + workplace_id = ?, + map_x_percent = ?, + map_y_percent = ?, + map_width_percent = ?, + map_height_percent = ?, + updated_at = NOW() + WHERE equipment_id = ? + ` : ` + UPDATE equipments SET + map_x_percent = ?, + map_y_percent = ?, + map_width_percent = ?, + map_height_percent = ?, + updated_at = NOW() + WHERE equipment_id = ? + `; - const values = hasWorkplaceId ? [ - positionData.workplace_id, - positionData.map_x_percent, - positionData.map_y_percent, - positionData.map_width_percent, - positionData.map_height_percent, - equipmentId - ] : [ - positionData.map_x_percent, - positionData.map_y_percent, - positionData.map_width_percent, - positionData.map_height_percent, - equipmentId - ]; + const values = hasWorkplaceId ? [ + positionData.workplace_id, + positionData.map_x_percent, + positionData.map_y_percent, + positionData.map_width_percent, + positionData.map_height_percent, + equipmentId + ] : [ + positionData.map_x_percent, + positionData.map_y_percent, + positionData.map_width_percent, + positionData.map_height_percent, + equipmentId + ]; - const [result] = await db.query(query, values); - if (result.affectedRows === 0) { - return callback(new Error('Equipment not found')); - } - callback(null, { equipment_id: equipmentId, ...positionData }); - } catch (error) { - callback(error); + const [result] = await db.query(query, values); + if (result.affectedRows === 0) { + throw new Error('Equipment not found'); } + return { equipment_id: equipmentId, ...positionData }; }, // DELETE - μ„€λΉ„ μ‚­μ œ - delete: async (equipmentId, callback) => { - try { - const db = await getDb(); - const query = 'DELETE FROM equipments WHERE equipment_id = ?'; - - const [result] = await db.query(query, [equipmentId]); - if (result.affectedRows === 0) { - return callback(new Error('Equipment not found')); - } - callback(null, { equipment_id: equipmentId }); - } catch (error) { - callback(error); + delete: async (equipmentId) => { + const db = await getDb(); + const [result] = await db.query('DELETE FROM equipments WHERE equipment_id = ?', [equipmentId]); + if (result.affectedRows === 0) { + throw new Error('Equipment not found'); } + return { equipment_id: equipmentId }; }, // CHECK DUPLICATE CODE - μ„€λΉ„ μ½”λ“œ 쀑볡 확인 - checkDuplicateCode: async (equipmentCode, excludeEquipmentId, callback) => { - try { - const db = await getDb(); - let query = 'SELECT equipment_id FROM equipments WHERE equipment_code = ?'; - const values = [equipmentCode]; + checkDuplicateCode: async (equipmentCode, excludeEquipmentId) => { + const db = await getDb(); + let query = 'SELECT equipment_id FROM equipments WHERE equipment_code = ?'; + const values = [equipmentCode]; - if (excludeEquipmentId) { - query += ' AND equipment_id != ?'; - values.push(excludeEquipmentId); - } - - const [rows] = await db.query(query, values); - callback(null, rows.length > 0); - } catch (error) { - callback(error); + if (excludeEquipmentId) { + query += ' AND equipment_id != ?'; + values.push(excludeEquipmentId); } + + const [rows] = await db.query(query, values); + return rows.length > 0; }, // GET EQUIPMENT TYPES - μ‚¬μš© 쀑인 μ„€λΉ„ μœ ν˜• λͺ©λ‘ 쑰회 - getEquipmentTypes: async (callback) => { - try { - const db = await getDb(); - const query = ` - SELECT DISTINCT equipment_type - FROM equipments - WHERE equipment_type IS NOT NULL - ORDER BY equipment_type ASC - `; - - const [rows] = await db.query(query); - callback(null, rows.map(row => row.equipment_type)); - } catch (error) { - callback(error); - } + getEquipmentTypes: async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT DISTINCT equipment_type + FROM equipments + WHERE equipment_type IS NOT NULL + ORDER BY equipment_type ASC` + ); + return rows.map(row => row.equipment_type); }, - // GET NEXT EQUIPMENT CODE - λ‹€μŒ κ΄€λ¦¬λ²ˆν˜Έ μžλ™ 생성 (TKP-001 ν˜•μ‹) - getNextEquipmentCode: async (prefix = 'TKP', callback) => { - try { - const db = await getDb(); - // ν•΄λ‹Ή μ ‘λ‘μ‚¬λ‘œ μ‹œμž‘ν•˜λŠ” κ°€μž₯ 큰 번호 μ°ΎκΈ° - const query = ` - SELECT equipment_code - FROM equipments - WHERE equipment_code LIKE ? - ORDER BY equipment_code DESC - LIMIT 1 - `; + // GET NEXT EQUIPMENT CODE - λ‹€μŒ κ΄€λ¦¬λ²ˆν˜Έ μžλ™ 생성 + getNextEquipmentCode: async (prefix = 'TKP') => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT equipment_code + FROM equipments + WHERE equipment_code LIKE ? + ORDER BY equipment_code DESC + LIMIT 1`, + [`${prefix}-%`] + ); - const [rows] = await db.query(query, [`${prefix}-%`]); - - let nextNumber = 1; - if (rows.length > 0) { - // TKP-001 ν˜•μ‹μ—μ„œ 숫자 λΆ€λΆ„ μΆ”μΆœ - const lastCode = rows[0].equipment_code; - const match = lastCode.match(new RegExp(`^${prefix}-(\\d+)$`)); - if (match) { - nextNumber = parseInt(match[1], 10) + 1; - } + let nextNumber = 1; + if (rows.length > 0) { + const lastCode = rows[0].equipment_code; + const match = lastCode.match(new RegExp(`^${prefix}-(\\d+)$`)); + if (match) { + nextNumber = parseInt(match[1], 10) + 1; } - - // 3자리둜 νŒ¨λ”© (001, 002, ...) - const nextCode = `${prefix}-${String(nextNumber).padStart(3, '0')}`; - callback(null, nextCode); - } catch (error) { - callback(error); } + + return `${prefix}-${String(nextNumber).padStart(3, '0')}`; }, // ========================================== // μ„€λΉ„ 사진 관리 // ========================================== - // ADD PHOTO - μ„€λΉ„ 사진 μΆ”κ°€ - addPhoto: async (equipmentId, photoData, callback) => { - try { - const db = await getDb(); - const query = ` - INSERT INTO equipment_photos ( - equipment_id, photo_path, description, display_order, uploaded_by - ) VALUES (?, ?, ?, ?, ?) - `; + addPhoto: async (equipmentId, photoData) => { + const db = await getDb(); + const values = [ + equipmentId, + photoData.photo_path, + photoData.description || null, + photoData.display_order || 0, + photoData.uploaded_by || null + ]; - const values = [ - equipmentId, - photoData.photo_path, - photoData.description || null, - photoData.display_order || 0, - photoData.uploaded_by || null - ]; + const [result] = await db.query( + `INSERT INTO equipment_photos ( + equipment_id, photo_path, description, display_order, uploaded_by + ) VALUES (?, ?, ?, ?, ?)`, + values + ); - const [result] = await db.query(query, values); - callback(null, { - photo_id: result.insertId, - equipment_id: equipmentId, - ...photoData - }); - } catch (error) { - callback(error); - } + return { + photo_id: result.insertId, + equipment_id: equipmentId, + ...photoData + }; }, - // GET PHOTOS - μ„€λΉ„ 사진 쑰회 - getPhotos: async (equipmentId, callback) => { - try { - const db = await getDb(); - const query = ` - SELECT ep.*, u.name AS uploaded_by_name - FROM equipment_photos ep - LEFT JOIN users u ON ep.uploaded_by = u.user_id - WHERE ep.equipment_id = ? - ORDER BY ep.display_order ASC, ep.created_at ASC - `; - - const [rows] = await db.query(query, [equipmentId]); - callback(null, rows); - } catch (error) { - callback(error); - } + getPhotos: async (equipmentId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT ep.*, u.name AS uploaded_by_name + FROM equipment_photos ep + LEFT JOIN users u ON ep.uploaded_by = u.user_id + WHERE ep.equipment_id = ? + ORDER BY ep.display_order ASC, ep.created_at ASC`, + [equipmentId] + ); + return rows; }, - // DELETE PHOTO - μ„€λΉ„ 사진 μ‚­μ œ - deletePhoto: async (photoId, callback) => { - try { - const db = await getDb(); + deletePhoto: async (photoId) => { + const db = await getDb(); - // λ¨Όμ € 사진 정보 쑰회 (파일 μ‚­μ œμš©) - const [photo] = await db.query( - 'SELECT photo_path FROM equipment_photos WHERE photo_id = ?', - [photoId] - ); + const [photo] = await db.query( + 'SELECT photo_path FROM equipment_photos WHERE photo_id = ?', + [photoId] + ); - const query = 'DELETE FROM equipment_photos WHERE photo_id = ?'; - const [result] = await db.query(query, [photoId]); + const [result] = await db.query('DELETE FROM equipment_photos WHERE photo_id = ?', [photoId]); - if (result.affectedRows === 0) { - return callback(new Error('Photo not found')); - } - - callback(null, { photo_id: photoId, photo_path: photo[0]?.photo_path }); - } catch (error) { - callback(error); + if (result.affectedRows === 0) { + throw new Error('Photo not found'); } + + return { photo_id: photoId, photo_path: photo[0]?.photo_path }; }, // ========================================== // μ„€λΉ„ μž„μ‹œ 이동 // ========================================== - // MOVE TEMPORARILY - μ„€λΉ„ μž„μ‹œ 이동 - moveTemporarily: async (equipmentId, moveData, callback) => { - try { - const db = await getDb(); + moveTemporarily: async (equipmentId, moveData) => { + const db = await getDb(); - // 1. μ„€λΉ„ ν˜„μž¬ μœ„μΉ˜ μ—…λ°μ΄νŠΈ - const updateQuery = ` - UPDATE equipments SET - current_workplace_id = ?, - current_map_x_percent = ?, - current_map_y_percent = ?, - current_map_width_percent = ?, - current_map_height_percent = ?, - is_temporarily_moved = TRUE, - moved_at = NOW(), - moved_by = ?, - updated_at = NOW() - WHERE equipment_id = ? - `; - - const updateValues = [ + const [result] = await db.query( + `UPDATE equipments SET + current_workplace_id = ?, + current_map_x_percent = ?, + current_map_y_percent = ?, + current_map_width_percent = ?, + current_map_height_percent = ?, + is_temporarily_moved = TRUE, + moved_at = NOW(), + moved_by = ?, + updated_at = NOW() + WHERE equipment_id = ?`, + [ moveData.target_workplace_id, moveData.target_x_percent, moveData.target_y_percent, @@ -464,26 +379,22 @@ const EquipmentModel = { moveData.target_height_percent || null, moveData.moved_by || null, equipmentId - ]; + ] + ); - const [result] = await db.query(updateQuery, updateValues); + if (result.affectedRows === 0) { + throw new Error('Equipment not found'); + } - if (result.affectedRows === 0) { - return callback(new Error('Equipment not found')); - } - - // 2. 이동 이λ ₯ 기둝 - const logQuery = ` - INSERT INTO equipment_move_logs ( - equipment_id, move_type, - from_workplace_id, to_workplace_id, - from_x_percent, from_y_percent, - to_x_percent, to_y_percent, - reason, moved_by - ) VALUES (?, 'temporary', ?, ?, ?, ?, ?, ?, ?, ?) - `; - - await db.query(logQuery, [ + await db.query( + `INSERT INTO equipment_move_logs ( + equipment_id, move_type, + from_workplace_id, to_workplace_id, + from_x_percent, from_y_percent, + to_x_percent, to_y_percent, + reason, moved_by + ) VALUES (?, 'temporary', ?, ?, ?, ?, ?, ?, ?, ?)`, + [ equipmentId, moveData.from_workplace_id || null, moveData.target_workplace_id, @@ -493,137 +404,107 @@ const EquipmentModel = { moveData.target_y_percent, moveData.reason || null, moveData.moved_by || null - ]); + ] + ); - callback(null, { equipment_id: equipmentId, moved: true }); - } catch (error) { - callback(error); - } + return { equipment_id: equipmentId, moved: true }; }, - // RETURN TO ORIGINAL - μ„€λΉ„ μ›μœ„μΉ˜ 볡귀 - returnToOriginal: async (equipmentId, userId, callback) => { - try { - const db = await getDb(); + returnToOriginal: async (equipmentId, userId) => { + const db = await getDb(); - // 1. ν˜„μž¬ μž„μ‹œ μœ„μΉ˜ 정보 쑰회 - const [equipment] = await db.query( - 'SELECT current_workplace_id, current_map_x_percent, current_map_y_percent FROM equipments WHERE equipment_id = ?', - [equipmentId] - ); + const [equipment] = await db.query( + 'SELECT current_workplace_id, current_map_x_percent, current_map_y_percent FROM equipments WHERE equipment_id = ?', + [equipmentId] + ); - if (!equipment[0]) { - return callback(new Error('Equipment not found')); - } + if (!equipment[0]) { + throw new Error('Equipment not found'); + } - // 2. μž„μ‹œ μœ„μΉ˜ ν•„λ“œ μ΄ˆκΈ°ν™” - const updateQuery = ` - UPDATE equipments SET - current_workplace_id = NULL, - current_map_x_percent = NULL, - current_map_y_percent = NULL, - current_map_width_percent = NULL, - current_map_height_percent = NULL, - is_temporarily_moved = FALSE, - moved_at = NULL, - moved_by = NULL, - updated_at = NOW() - WHERE equipment_id = ? - `; + await db.query( + `UPDATE equipments SET + current_workplace_id = NULL, + current_map_x_percent = NULL, + current_map_y_percent = NULL, + current_map_width_percent = NULL, + current_map_height_percent = NULL, + is_temporarily_moved = FALSE, + moved_at = NULL, + moved_by = NULL, + updated_at = NOW() + WHERE equipment_id = ?`, + [equipmentId] + ); - await db.query(updateQuery, [equipmentId]); - - // 3. 볡귀 이λ ₯ 기둝 - const logQuery = ` - INSERT INTO equipment_move_logs ( - equipment_id, move_type, - from_workplace_id, from_x_percent, from_y_percent, - reason, moved_by - ) VALUES (?, 'return', ?, ?, ?, 'μ›μœ„μΉ˜ 볡귀', ?) - `; - - await db.query(logQuery, [ + await db.query( + `INSERT INTO equipment_move_logs ( + equipment_id, move_type, + from_workplace_id, from_x_percent, from_y_percent, + reason, moved_by + ) VALUES (?, 'return', ?, ?, ?, 'μ›μœ„μΉ˜ 볡귀', ?)`, + [ equipmentId, equipment[0].current_workplace_id, equipment[0].current_map_x_percent, equipment[0].current_map_y_percent, userId || null - ]); + ] + ); - callback(null, { equipment_id: equipmentId, returned: true }); - } catch (error) { - callback(error); - } + return { equipment_id: equipmentId, returned: true }; }, - // GET TEMPORARILY MOVED - μž„μ‹œ μ΄λ™λœ μ„€λΉ„ λͺ©λ‘ - getTemporarilyMoved: async (callback) => { - try { - const db = await getDb(); - const query = ` - SELECT - e.*, - w_orig.workplace_name AS original_workplace_name, - w_curr.workplace_name AS current_workplace_name, - u.name AS moved_by_name - FROM equipments e - LEFT JOIN workplaces w_orig ON e.workplace_id = w_orig.workplace_id - LEFT JOIN workplaces w_curr ON e.current_workplace_id = w_curr.workplace_id - LEFT JOIN users u ON e.moved_by = u.user_id - WHERE e.is_temporarily_moved = TRUE - ORDER BY e.moved_at DESC - `; - - const [rows] = await db.query(query); - callback(null, rows); - } catch (error) { - callback(error); - } + getTemporarilyMoved: async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + e.*, + w_orig.workplace_name AS original_workplace_name, + w_curr.workplace_name AS current_workplace_name, + u.name AS moved_by_name + FROM equipments e + LEFT JOIN workplaces w_orig ON e.workplace_id = w_orig.workplace_id + LEFT JOIN workplaces w_curr ON e.current_workplace_id = w_curr.workplace_id + LEFT JOIN users u ON e.moved_by = u.user_id + WHERE e.is_temporarily_moved = TRUE + ORDER BY e.moved_at DESC` + ); + return rows; }, - // GET MOVE LOGS - μ„€λΉ„ 이동 이λ ₯ 쑰회 - getMoveLogs: async (equipmentId, callback) => { - try { - const db = await getDb(); - const query = ` - SELECT - eml.*, - w_from.workplace_name AS from_workplace_name, - w_to.workplace_name AS to_workplace_name, - u.name AS moved_by_name - FROM equipment_move_logs eml - LEFT JOIN workplaces w_from ON eml.from_workplace_id = w_from.workplace_id - LEFT JOIN workplaces w_to ON eml.to_workplace_id = w_to.workplace_id - LEFT JOIN users u ON eml.moved_by = u.user_id - WHERE eml.equipment_id = ? - ORDER BY eml.moved_at DESC - `; - - const [rows] = await db.query(query, [equipmentId]); - callback(null, rows); - } catch (error) { - callback(error); - } + getMoveLogs: async (equipmentId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + eml.*, + w_from.workplace_name AS from_workplace_name, + w_to.workplace_name AS to_workplace_name, + u.name AS moved_by_name + FROM equipment_move_logs eml + LEFT JOIN workplaces w_from ON eml.from_workplace_id = w_from.workplace_id + LEFT JOIN workplaces w_to ON eml.to_workplace_id = w_to.workplace_id + LEFT JOIN users u ON eml.moved_by = u.user_id + WHERE eml.equipment_id = ? + ORDER BY eml.moved_at DESC`, + [equipmentId] + ); + return rows; }, // ========================================== // μ„€λΉ„ μ™ΈλΆ€ 반좜/λ°˜μž… // ========================================== - // EXPORT EQUIPMENT - μ„€λΉ„ μ™ΈλΆ€ 반좜 - exportEquipment: async (exportData, callback) => { - try { - const db = await getDb(); + exportEquipment: async (exportData) => { + const db = await getDb(); - // 1. 반좜 둜그 생성 - const logQuery = ` - INSERT INTO equipment_external_logs ( - equipment_id, log_type, export_date, expected_return_date, - destination, reason, notes, exported_by - ) VALUES (?, 'export', ?, ?, ?, ?, ?, ?) - `; - - const logValues = [ + const [logResult] = await db.query( + `INSERT INTO equipment_external_logs ( + equipment_id, log_type, export_date, expected_return_date, + destination, reason, notes, exported_by + ) VALUES (?, 'export', ?, ?, ?, ?, ?, ?)`, + [ exportData.equipment_id, exportData.export_date || new Date().toISOString().slice(0, 10), exportData.expected_return_date || null, @@ -631,178 +512,144 @@ const EquipmentModel = { exportData.reason || null, exportData.notes || null, exportData.exported_by || null - ]; + ] + ); - const [logResult] = await db.query(logQuery, logValues); + const status = exportData.is_repair ? 'repair_external' : 'external'; + await db.query( + 'UPDATE equipments SET status = ?, updated_at = NOW() WHERE equipment_id = ?', + [status, exportData.equipment_id] + ); - // 2. μ„€λΉ„ μƒνƒœ μ—…λ°μ΄νŠΈ - const status = exportData.is_repair ? 'repair_external' : 'external'; - await db.query( - 'UPDATE equipments SET status = ?, updated_at = NOW() WHERE equipment_id = ?', - [status, exportData.equipment_id] - ); - - callback(null, { - log_id: logResult.insertId, - equipment_id: exportData.equipment_id, - exported: true - }); - } catch (error) { - callback(error); - } + return { + log_id: logResult.insertId, + equipment_id: exportData.equipment_id, + exported: true + }; }, - // RETURN EQUIPMENT - μ„€λΉ„ λ°˜μž… (μ™ΈλΆ€μ—μ„œ 볡귀) - returnEquipment: async (logId, returnData, callback) => { - try { - const db = await getDb(); + returnEquipment: async (logId, returnData) => { + const db = await getDb(); - // 1. 반좜 둜그 쑰회 - const [logs] = await db.query( - 'SELECT equipment_id FROM equipment_external_logs WHERE log_id = ?', - [logId] - ); + const [logs] = await db.query( + 'SELECT equipment_id FROM equipment_external_logs WHERE log_id = ?', + [logId] + ); - if (!logs[0]) { - return callback(new Error('Export log not found')); - } - - const equipmentId = logs[0].equipment_id; - - // 2. 반좜 둜그 μ—…λ°μ΄νŠΈ - await db.query( - `UPDATE equipment_external_logs SET - actual_return_date = ?, - returned_by = ?, - notes = CONCAT(IFNULL(notes, ''), '\nλ°˜μž…: ', IFNULL(?, '')), - updated_at = NOW() - WHERE log_id = ?`, - [ - returnData.return_date || new Date().toISOString().slice(0, 10), - returnData.returned_by || null, - returnData.notes || '', - logId - ] - ); - - // 3. μ„€λΉ„ μƒνƒœ 볡원 - await db.query( - 'UPDATE equipments SET status = ?, updated_at = NOW() WHERE equipment_id = ?', - [returnData.new_status || 'active', equipmentId] - ); - - callback(null, { - log_id: logId, - equipment_id: equipmentId, - returned: true - }); - } catch (error) { - callback(error); + if (!logs[0]) { + throw new Error('Export log not found'); } + + const equipmentId = logs[0].equipment_id; + + await db.query( + `UPDATE equipment_external_logs SET + actual_return_date = ?, + returned_by = ?, + notes = CONCAT(IFNULL(notes, ''), '\nλ°˜μž…: ', IFNULL(?, '')), + updated_at = NOW() + WHERE log_id = ?`, + [ + returnData.return_date || new Date().toISOString().slice(0, 10), + returnData.returned_by || null, + returnData.notes || '', + logId + ] + ); + + await db.query( + 'UPDATE equipments SET status = ?, updated_at = NOW() WHERE equipment_id = ?', + [returnData.new_status || 'active', equipmentId] + ); + + return { + log_id: logId, + equipment_id: equipmentId, + returned: true + }; }, - // GET EXTERNAL LOGS - μ„€λΉ„ μ™ΈλΆ€ 반좜 이λ ₯ 쑰회 - getExternalLogs: async (equipmentId, callback) => { - try { - const db = await getDb(); - const query = ` - SELECT - eel.*, - u_exp.name AS exported_by_name, - u_ret.name AS returned_by_name - FROM equipment_external_logs eel - LEFT JOIN users u_exp ON eel.exported_by = u_exp.user_id - LEFT JOIN users u_ret ON eel.returned_by = u_ret.user_id - WHERE eel.equipment_id = ? - ORDER BY eel.created_at DESC - `; - - const [rows] = await db.query(query, [equipmentId]); - callback(null, rows); - } catch (error) { - callback(error); - } + getExternalLogs: async (equipmentId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + eel.*, + u_exp.name AS exported_by_name, + u_ret.name AS returned_by_name + FROM equipment_external_logs eel + LEFT JOIN users u_exp ON eel.exported_by = u_exp.user_id + LEFT JOIN users u_ret ON eel.returned_by = u_ret.user_id + WHERE eel.equipment_id = ? + ORDER BY eel.created_at DESC`, + [equipmentId] + ); + return rows; }, - // GET EXPORTED EQUIPMENTS - ν˜„μž¬ μ™ΈλΆ€ 반좜 쀑인 μ„€λΉ„ λͺ©λ‘ - getExportedEquipments: async (callback) => { - try { - const db = await getDb(); - const query = ` - SELECT - e.*, - w.workplace_name, - eel.export_date, - eel.expected_return_date, - eel.destination, - eel.reason, - u.name AS exported_by_name - FROM equipments e - INNER JOIN ( - SELECT equipment_id, MAX(log_id) AS latest_log_id - FROM equipment_external_logs - WHERE actual_return_date IS NULL - GROUP BY equipment_id - ) latest ON e.equipment_id = latest.equipment_id - INNER JOIN equipment_external_logs eel ON eel.log_id = latest.latest_log_id - LEFT JOIN workplaces w ON e.workplace_id = w.workplace_id - LEFT JOIN users u ON eel.exported_by = u.user_id - WHERE e.status IN ('external', 'repair_external') - ORDER BY eel.export_date DESC - `; - - const [rows] = await db.query(query); - callback(null, rows); - } catch (error) { - callback(error); - } + getExportedEquipments: async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + e.*, + w.workplace_name, + eel.export_date, + eel.expected_return_date, + eel.destination, + eel.reason, + u.name AS exported_by_name + FROM equipments e + INNER JOIN ( + SELECT equipment_id, MAX(log_id) AS latest_log_id + FROM equipment_external_logs + WHERE actual_return_date IS NULL + GROUP BY equipment_id + ) latest ON e.equipment_id = latest.equipment_id + INNER JOIN equipment_external_logs eel ON eel.log_id = latest.latest_log_id + LEFT JOIN workplaces w ON e.workplace_id = w.workplace_id + LEFT JOIN users u ON eel.exported_by = u.user_id + WHERE e.status IN ('external', 'repair_external') + ORDER BY eel.export_date DESC` + ); + return rows; }, // ========================================== - // μ„€λΉ„ 수리 μ‹ μ²­ (work_issue_reports 연동) + // μ„€λΉ„ 수리 μ‹ μ²­ // ========================================== - // CREATE REPAIR REQUEST - 수리 μ‹ μ²­ (μ‹ κ³  μ‹œμŠ€ν…œ ν™œμš©) - createRepairRequest: async (requestData, callback) => { - try { - const db = await getDb(); + createRepairRequest: async (requestData) => { + const db = await getDb(); - // μ„€λΉ„ 수리 μΉ΄ν…Œκ³ λ¦¬ ID 쑰회 - const [categories] = await db.query( - "SELECT category_id FROM issue_report_categories WHERE category_name = 'μ„€λΉ„ 수리' LIMIT 1" + const [categories] = await db.query( + "SELECT category_id FROM issue_report_categories WHERE category_name = 'μ„€λΉ„ 수리' LIMIT 1" + ); + + if (!categories[0]) { + throw new Error('μ„€λΉ„ 수리 μΉ΄ν…Œκ³ λ¦¬κ°€ μ—†μŠ΅λ‹ˆλ‹€'); + } + + const categoryId = categories[0].category_id; + + let itemId = requestData.item_id; + if (!itemId) { + const [items] = await db.query( + 'SELECT item_id FROM issue_report_items WHERE category_id = ? LIMIT 1', + [categoryId] ); + itemId = items[0]?.item_id; + } - if (!categories[0]) { - return callback(new Error('μ„€λΉ„ 수리 μΉ΄ν…Œκ³ λ¦¬κ°€ μ—†μŠ΅λ‹ˆλ‹€')); - } + const photos = requestData.photo_paths || []; - const categoryId = categories[0].category_id; - - // ν•­λͺ© ID 쑰회 (μ§€μ •λœ ν•­λͺ©μ΄ μ—†μœΌλ©΄ 첫번째 ν•­λͺ© μ‚¬μš©) - let itemId = requestData.item_id; - if (!itemId) { - const [items] = await db.query( - 'SELECT item_id FROM issue_report_items WHERE category_id = ? LIMIT 1', - [categoryId] - ); - itemId = items[0]?.item_id; - } - - // 사진 경둜 뢄리 (μ΅œλŒ€ 5μž₯) - const photos = requestData.photo_paths || []; - - // work_issue_reports에 μ‚½μž… - const query = ` - INSERT INTO work_issue_reports ( - reporter_id, issue_category_id, issue_item_id, - workplace_id, equipment_id, - additional_description, - photo_path1, photo_path2, photo_path3, photo_path4, photo_path5, - status - ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'reported') - `; - - const values = [ + const [result] = await db.query( + `INSERT INTO work_issue_reports ( + reporter_id, issue_category_id, issue_item_id, + workplace_id, equipment_id, + additional_description, + photo_path1, photo_path2, photo_path3, photo_path4, photo_path5, + status + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'reported')`, + [ requestData.reported_by || null, categoryId, itemId, @@ -814,135 +661,103 @@ const EquipmentModel = { photos[2] || null, photos[3] || null, photos[4] || null - ]; + ] + ); - const [result] = await db.query(query, values); + await db.query( + 'UPDATE equipments SET status = ?, updated_at = NOW() WHERE equipment_id = ?', + ['repair_needed', requestData.equipment_id] + ); - // μ„€λΉ„ μƒνƒœλ₯Ό repair_needed둜 μ—…λ°μ΄νŠΈ - await db.query( - 'UPDATE equipments SET status = ?, updated_at = NOW() WHERE equipment_id = ?', - ['repair_needed', requestData.equipment_id] - ); - - // μ•Œλ¦Ό 생성 - try { - await notificationModel.createRepairNotification({ - equipment_id: requestData.equipment_id, - equipment_name: requestData.equipment_name || 'μ„€λΉ„', - repair_type: requestData.repair_type || '일반 수리', - request_id: result.insertId, - created_by: requestData.reported_by - }); - } catch (notifError) { - console.error('μ•Œλ¦Ό 생성 μ‹€νŒ¨:', notifError); - // μ•Œλ¦Ό 생성 μ‹€νŒ¨ν•΄λ„ 수리 신청은 μ„±κ³΅μœΌλ‘œ 처리 - } - - callback(null, { - report_id: result.insertId, + try { + await notificationModel.createRepairNotification({ equipment_id: requestData.equipment_id, - created: true + equipment_name: requestData.equipment_name || 'μ„€λΉ„', + repair_type: requestData.repair_type || '일반 수리', + request_id: result.insertId, + created_by: requestData.reported_by }); - } catch (error) { - callback(error); + } catch (notifError) { + // μ•Œλ¦Ό 생성 μ‹€νŒ¨ν•΄λ„ 수리 신청은 μ„±κ³΅μœΌλ‘œ 처리 } + + return { + report_id: result.insertId, + equipment_id: requestData.equipment_id, + created: true + }; }, - // GET REPAIR HISTORY - μ„€λΉ„ 수리 이λ ₯ 쑰회 - getRepairHistory: async (equipmentId, callback) => { - try { - const db = await getDb(); - const query = ` - SELECT - wir.*, - irc.category_name, - iri.item_name, - u_rep.name AS reported_by_name, - u_res.name AS resolved_by_name, - w.workplace_name - FROM work_issue_reports wir - LEFT JOIN issue_report_categories irc ON wir.issue_category_id = irc.category_id - LEFT JOIN issue_report_items iri ON wir.issue_item_id = iri.item_id - LEFT JOIN users u_rep ON wir.reporter_id = u_rep.user_id - LEFT JOIN users u_res ON wir.resolved_by = u_res.user_id - LEFT JOIN workplaces w ON wir.workplace_id = w.workplace_id - WHERE wir.equipment_id = ? - ORDER BY wir.created_at DESC - `; - - const [rows] = await db.query(query, [equipmentId]); - callback(null, rows); - } catch (error) { - callback(error); - } + getRepairHistory: async (equipmentId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + wir.*, + irc.category_name, + iri.item_name, + u_rep.name AS reported_by_name, + u_res.name AS resolved_by_name, + w.workplace_name + FROM work_issue_reports wir + LEFT JOIN issue_report_categories irc ON wir.issue_category_id = irc.category_id + LEFT JOIN issue_report_items iri ON wir.issue_item_id = iri.item_id + LEFT JOIN users u_rep ON wir.reporter_id = u_rep.user_id + LEFT JOIN users u_res ON wir.resolved_by = u_res.user_id + LEFT JOIN workplaces w ON wir.workplace_id = w.workplace_id + WHERE wir.equipment_id = ? + ORDER BY wir.created_at DESC`, + [equipmentId] + ); + return rows; }, - // GET REPAIR CATEGORIES - μ„€λΉ„ 수리 ν•­λͺ© λͺ©λ‘ 쑰회 - getRepairCategories: async (callback) => { - try { - const db = await getDb(); - - // μ„€λΉ„ 수리 μΉ΄ν…Œκ³ λ¦¬μ˜ ν•­λͺ©λ“€ 쑰회 - const query = ` - SELECT iri.item_id, iri.item_name, iri.description, iri.severity - FROM issue_report_items iri - INNER JOIN issue_report_categories irc ON iri.category_id = irc.category_id - WHERE irc.category_name = 'μ„€λΉ„ 수리' AND iri.is_active = 1 - ORDER BY iri.display_order ASC - `; - - const [rows] = await db.query(query); - callback(null, rows); - } catch (error) { - callback(error); - } + getRepairCategories: async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT iri.item_id, iri.item_name, iri.description, iri.severity + FROM issue_report_items iri + INNER JOIN issue_report_categories irc ON iri.category_id = irc.category_id + WHERE irc.category_name = 'μ„€λΉ„ 수리' AND iri.is_active = 1 + ORDER BY iri.display_order ASC` + ); + return rows; }, - // ADD REPAIR CATEGORY - μƒˆ 수리 ν•­λͺ© μΆ”κ°€ - addRepairCategory: async (itemName, callback) => { - try { - const db = await getDb(); + addRepairCategory: async (itemName) => { + const db = await getDb(); - // μ„€λΉ„ 수리 μΉ΄ν…Œκ³ λ¦¬ ID 쑰회 - const [categories] = await db.query( - "SELECT category_id FROM issue_report_categories WHERE category_name = 'μ„€λΉ„ 수리'" - ); + const [categories] = await db.query( + "SELECT category_id FROM issue_report_categories WHERE category_name = 'μ„€λΉ„ 수리'" + ); - if (categories.length === 0) { - return callback(new Error('μ„€λΉ„ 수리 μΉ΄ν…Œκ³ λ¦¬κ°€ μ—†μŠ΅λ‹ˆλ‹€.')); - } - - const categoryId = categories[0].category_id; - - // 쀑볡 확인 - const [existing] = await db.query( - 'SELECT item_id FROM issue_report_items WHERE category_id = ? AND item_name = ?', - [categoryId, itemName] - ); - - if (existing.length > 0) { - // 이미 μ‘΄μž¬ν•˜λ©΄ ν•΄λ‹Ή ID λ°˜ν™˜ - return callback(null, { item_id: existing[0].item_id, item_name: itemName, isNew: false }); - } - - // λ‹€μŒ display_order κ΅¬ν•˜κΈ° - const [maxOrder] = await db.query( - 'SELECT MAX(display_order) as max_order FROM issue_report_items WHERE category_id = ?', - [categoryId] - ); - const nextOrder = (maxOrder[0].max_order || 0) + 1; - - // μƒˆ ν•­λͺ© μΆ”κ°€ - const [result] = await db.query( - `INSERT INTO issue_report_items (category_id, item_name, display_order, is_active) - VALUES (?, ?, ?, 1)`, - [categoryId, itemName, nextOrder] - ); - - callback(null, { item_id: result.insertId, item_name: itemName, isNew: true }); - } catch (error) { - callback(error); + if (categories.length === 0) { + throw new Error('μ„€λΉ„ 수리 μΉ΄ν…Œκ³ λ¦¬κ°€ μ—†μŠ΅λ‹ˆλ‹€.'); } + + const categoryId = categories[0].category_id; + + const [existing] = await db.query( + 'SELECT item_id FROM issue_report_items WHERE category_id = ? AND item_name = ?', + [categoryId, itemName] + ); + + if (existing.length > 0) { + return { item_id: existing[0].item_id, item_name: itemName, isNew: false }; + } + + const [maxOrder] = await db.query( + 'SELECT MAX(display_order) as max_order FROM issue_report_items WHERE category_id = ?', + [categoryId] + ); + const nextOrder = (maxOrder[0].max_order || 0) + 1; + + const [result] = await db.query( + `INSERT INTO issue_report_items (category_id, item_name, display_order, is_active) + VALUES (?, ?, ?, 1)`, + [categoryId, itemName, nextOrder] + ); + + return { item_id: result.insertId, item_name: itemName, isNew: true }; } }; diff --git a/system1-factory/api/models/pageAccessModel.js b/system1-factory/api/models/pageAccessModel.js deleted file mode 100644 index f6801f6..0000000 --- a/system1-factory/api/models/pageAccessModel.js +++ /dev/null @@ -1,160 +0,0 @@ -// models/pageAccessModel.js -const db = require('../db/connection'); - -const PageAccessModel = { - // μ‚¬μš©μžμ˜ νŽ˜μ΄μ§€ κΆŒν•œ 쑰회 - getUserPageAccess: (userId, callback) => { - const sql = ` - SELECT - p.id, - p.page_key, - p.page_name, - p.page_path, - p.category, - p.is_admin_only, - COALESCE(upa.can_access, 0) as can_access, - upa.granted_at, - upa.granted_by, - granter.username as granted_by_username - FROM pages p - LEFT JOIN user_page_access upa ON p.id = upa.page_id AND upa.user_id = ? - LEFT JOIN users granter ON upa.granted_by = granter.user_id - WHERE p.is_admin_only = 0 - ORDER BY p.category, p.display_order - `; - - db.query(sql, [userId], callback); - }, - - // λͺ¨λ“  νŽ˜μ΄μ§€ λͺ©λ‘ 쑰회 - getAllPages: (callback) => { - const sql = ` - SELECT - id, - page_key, - page_name, - page_path, - category, - description, - is_admin_only, - display_order - FROM pages - WHERE is_admin_only = 0 - ORDER BY category, display_order - `; - - db.query(sql, callback); - }, - - // νŽ˜μ΄μ§€ κΆŒν•œ λΆ€μ—¬ - grantPageAccess: (userId, pageId, grantedBy, callback) => { - const sql = ` - INSERT INTO user_page_access (user_id, page_id, can_access, granted_by, granted_at) - VALUES (?, ?, 1, ?, NOW()) - ON DUPLICATE KEY UPDATE - can_access = 1, - granted_by = ?, - granted_at = NOW() - `; - - db.query(sql, [userId, pageId, grantedBy, grantedBy], callback); - }, - - // νŽ˜μ΄μ§€ κΆŒν•œ 회수 - revokePageAccess: (userId, pageId, callback) => { - const sql = ` - DELETE FROM user_page_access - WHERE user_id = ? AND page_id = ? - `; - - db.query(sql, [userId, pageId], callback); - }, - - // μ—¬λŸ¬ νŽ˜μ΄μ§€ κΆŒν•œ 일괄 μ„€μ • - setUserPageAccess: (userId, pageIds, grantedBy, callback) => { - db.beginTransaction((err) => { - if (err) return callback(err); - - // κΈ°μ‘΄ κΆŒν•œ λͺ¨λ‘ μ‚­μ œ - const deleteSql = 'DELETE FROM user_page_access WHERE user_id = ?'; - - db.query(deleteSql, [userId], (err) => { - if (err) { - return db.rollback(() => callback(err)); - } - - // μƒˆ κΆŒν•œμ΄ μ—†μœΌλ©΄ μ»€λ°‹ν•˜κ³  μ’…λ£Œ - if (!pageIds || pageIds.length === 0) { - return db.commit((err) => { - if (err) return db.rollback(() => callback(err)); - callback(null, { affectedRows: 0 }); - }); - } - - // μƒˆ κΆŒν•œ μΆ”κ°€ - const values = pageIds.map(pageId => [userId, pageId, 1, grantedBy]); - const insertSql = ` - INSERT INTO user_page_access (user_id, page_id, can_access, granted_by, granted_at) - VALUES ? - `; - - db.query(insertSql, [values], (err, result) => { - if (err) { - return db.rollback(() => callback(err)); - } - - db.commit((err) => { - if (err) return db.rollback(() => callback(err)); - callback(null, result); - }); - }); - }); - }); - }, - - // νŠΉμ • νŽ˜μ΄μ§€ μ ‘κ·Ό κΆŒν•œ 확인 - checkPageAccess: (userId, pageKey, callback) => { - const sql = ` - SELECT - COALESCE(upa.can_access, 0) as can_access, - p.is_admin_only - FROM pages p - LEFT JOIN user_page_access upa ON p.id = upa.page_id AND upa.user_id = ? - WHERE p.page_key = ? - `; - - db.query(sql, [userId, pageKey], (err, results) => { - if (err) return callback(err); - if (results.length === 0) return callback(null, { can_access: false }); - callback(null, results[0]); - }); - }, - - // 계정이 μžˆλŠ” μž‘μ—…μž λͺ©λ‘ 쑰회 (κΆŒν•œ κ΄€λ¦¬μš©) - getUsersWithAccounts: (callback) => { - const sql = ` - SELECT - u.user_id, - u.username, - u.name, - u.role_id, - r.name as role_name, - u.worker_id, - w.worker_name, - w.job_type, - COUNT(upa.page_id) as granted_pages_count - FROM users u - LEFT JOIN roles r ON u.role_id = r.id - LEFT JOIN workers w ON u.worker_id = w.worker_id - LEFT JOIN user_page_access upa ON u.user_id = upa.user_id AND upa.can_access = 1 - WHERE u.is_active = 1 - AND u.role_id IN (4, 5) - GROUP BY u.user_id - ORDER BY w.worker_name, u.username - `; - - db.query(sql, callback); - } -}; - -module.exports = PageAccessModel; diff --git a/system1-factory/api/models/tbmModel.js b/system1-factory/api/models/tbmModel.js index b459dfe..6ee7e04 100644 --- a/system1-factory/api/models/tbmModel.js +++ b/system1-factory/api/models/tbmModel.js @@ -4,19 +4,13 @@ const { getDb } = require('../dbPool'); const TbmModel = { // ==================== TBM μ„Έμ…˜ κ΄€λ ¨ ==================== - /** - * TBM μ„Έμ…˜ 생성 - */ - createSession: async (sessionData, callback) => { - try { - const db = await getDb(); - const sql = ` - INSERT INTO tbm_sessions - (session_date, leader_id, project_id, work_type_id, task_id, work_location, created_by) - VALUES (?, ?, ?, ?, ?, ?, ?) - `; - - const values = [ + createSession: async (sessionData) => { + const db = await getDb(); + const [result] = await db.query( + `INSERT INTO tbm_sessions + (session_date, leader_id, project_id, work_type_id, task_id, work_location, created_by) + VALUES (?, ?, ?, ?, ?, ?, ?)`, + [ sessionData.session_date, sessionData.leader_id, sessionData.project_id || null, @@ -24,202 +18,139 @@ const TbmModel = { sessionData.task_id || null, sessionData.work_location || null, sessionData.created_by - ]; - - const [result] = await db.query(sql, values); - callback(null, result); - } catch (err) { - callback(err); - } + ] + ); + return result; }, - /** - * νŠΉμ • λ‚ μ§œμ˜ TBM μ„Έμ…˜ 쑰회 - */ - getSessionsByDate: async (date, callback) => { - try { - const db = await getDb(); - const sql = ` - SELECT - s.*, - w.worker_name as leader_name, - w.job_type as leader_job_type, - u.username as created_by_username, - u.name as created_by_name, - COUNT(DISTINCT ta.worker_id) as team_member_count, - GROUP_CONCAT(DISTINCT w2.worker_name ORDER BY ta.assignment_id SEPARATOR ', ') as team_member_names, - -- 이동 수 (이 μ„Έμ…˜μ΄ source λ˜λŠ” dest인 이동 건수) - (SELECT COUNT(*) FROM tbm_transfers tf - WHERE (tf.source_session_id = s.session_id OR tf.dest_session_id = s.session_id) - AND tf.transfer_date = s.session_date) as transfer_count, - -- 첫 번째 νŒ€μ›μ˜ μž‘μ—… 정보 κ°€μ Έμ˜€κΈ° - first_ta.project_id, - first_ta.work_type_id, - first_ta.task_id, - first_ta.workplace_id, - first_p.project_name, - first_wt.name as work_type_name, - first_t.task_name, - first_wp.workplace_name as work_location - FROM tbm_sessions s - LEFT JOIN workers w ON s.leader_id = w.worker_id - LEFT JOIN sso_users u ON s.created_by = u.user_id - LEFT JOIN tbm_team_assignments ta ON s.session_id = ta.session_id - LEFT JOIN workers w2 ON ta.worker_id = w2.worker_id - -- 첫 번째 νŒ€μ› 정보 (κ°€μž₯ λ¨Όμ € λ“±λ‘λœ μž‘μ—…) - LEFT JOIN ( - SELECT * FROM tbm_team_assignments - WHERE (session_id, assignment_id) IN ( - SELECT session_id, MIN(assignment_id) - FROM tbm_team_assignments - GROUP BY session_id - ) - ) first_ta ON s.session_id = first_ta.session_id - LEFT JOIN projects first_p ON first_ta.project_id = first_p.project_id - LEFT JOIN work_types first_wt ON first_ta.work_type_id = first_wt.id - LEFT JOIN tasks first_t ON first_ta.task_id = first_t.task_id - LEFT JOIN workplaces first_wp ON first_ta.workplace_id = first_wp.workplace_id - WHERE s.session_date = ? - GROUP BY s.session_id - ORDER BY s.session_id DESC - `; - - const [rows] = await db.query(sql, [date]); - callback(null, rows); - } catch (err) { - callback(err); - } + getSessionsByDate: async (date) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + s.*, + w.worker_name as leader_name, + w.job_type as leader_job_type, + u.username as created_by_username, + u.name as created_by_name, + COUNT(DISTINCT ta.worker_id) as team_member_count, + GROUP_CONCAT(DISTINCT w2.worker_name ORDER BY ta.assignment_id SEPARATOR ', ') as team_member_names, + (SELECT COUNT(*) FROM tbm_transfers tf + WHERE (tf.source_session_id = s.session_id OR tf.dest_session_id = s.session_id) + AND tf.transfer_date = s.session_date) as transfer_count, + first_ta.project_id, + first_ta.work_type_id, + first_ta.task_id, + first_ta.workplace_id, + first_p.project_name, + first_wt.name as work_type_name, + first_t.task_name, + first_wp.workplace_name as work_location + FROM tbm_sessions s + LEFT JOIN workers w ON s.leader_id = w.worker_id + LEFT JOIN sso_users u ON s.created_by = u.user_id + LEFT JOIN tbm_team_assignments ta ON s.session_id = ta.session_id + LEFT JOIN workers w2 ON ta.worker_id = w2.worker_id + LEFT JOIN ( + SELECT * FROM tbm_team_assignments + WHERE (session_id, assignment_id) IN ( + SELECT session_id, MIN(assignment_id) + FROM tbm_team_assignments + GROUP BY session_id + ) + ) first_ta ON s.session_id = first_ta.session_id + LEFT JOIN projects first_p ON first_ta.project_id = first_p.project_id + LEFT JOIN work_types first_wt ON first_ta.work_type_id = first_wt.id + LEFT JOIN tasks first_t ON first_ta.task_id = first_t.task_id + LEFT JOIN workplaces first_wp ON first_ta.workplace_id = first_wp.workplace_id + WHERE s.session_date = ? + GROUP BY s.session_id + ORDER BY s.session_id DESC`, + [date] + ); + return rows; }, - /** - * TBM μ„Έμ…˜ 상세 쑰회 - */ - getSessionById: async (sessionId, callback) => { - try { - const db = await getDb(); - const sql = ` - SELECT - s.*, - w.worker_name as leader_name, - w.job_type as leader_job_type, - w.phone_number as leader_phone, - u.username as created_by_username, - u.name as created_by_name, - COUNT(DISTINCT ta.worker_id) as team_member_count, - first_p.project_name, - first_p.job_no, - first_wt.name as work_type_name, - first_wt.category as work_type_category, - first_t.task_name, - first_t.description as task_description, - first_wp.workplace_name as work_location, - first_wc.category_name as workplace_category_name - FROM tbm_sessions s - LEFT JOIN workers w ON s.leader_id = w.worker_id - LEFT JOIN sso_users u ON s.created_by = u.user_id - LEFT JOIN tbm_team_assignments ta ON s.session_id = ta.session_id - LEFT JOIN ( - SELECT * FROM tbm_team_assignments - WHERE (session_id, assignment_id) IN ( - SELECT session_id, MIN(assignment_id) - FROM tbm_team_assignments - GROUP BY session_id - ) - ) first_ta ON s.session_id = first_ta.session_id - LEFT JOIN projects first_p ON first_ta.project_id = first_p.project_id - LEFT JOIN work_types first_wt ON first_ta.work_type_id = first_wt.id - LEFT JOIN tasks first_t ON first_ta.task_id = first_t.task_id - LEFT JOIN workplaces first_wp ON first_ta.workplace_id = first_wp.workplace_id - LEFT JOIN workplace_categories first_wc ON first_ta.workplace_category_id = first_wc.category_id - WHERE s.session_id = ? - GROUP BY s.session_id - `; - - const [rows] = await db.query(sql, [sessionId]); - callback(null, rows); - } catch (err) { - callback(err); - } + getSessionById: async (sessionId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + s.*, + w.worker_name as leader_name, + w.job_type as leader_job_type, + w.phone_number as leader_phone, + u.username as created_by_username, + u.name as created_by_name, + COUNT(DISTINCT ta.worker_id) as team_member_count, + first_p.project_name, + first_p.job_no, + first_wt.name as work_type_name, + first_wt.category as work_type_category, + first_t.task_name, + first_t.description as task_description, + first_wp.workplace_name as work_location, + first_wc.category_name as workplace_category_name + FROM tbm_sessions s + LEFT JOIN workers w ON s.leader_id = w.worker_id + LEFT JOIN sso_users u ON s.created_by = u.user_id + LEFT JOIN tbm_team_assignments ta ON s.session_id = ta.session_id + LEFT JOIN ( + SELECT * FROM tbm_team_assignments + WHERE (session_id, assignment_id) IN ( + SELECT session_id, MIN(assignment_id) + FROM tbm_team_assignments + GROUP BY session_id + ) + ) first_ta ON s.session_id = first_ta.session_id + LEFT JOIN projects first_p ON first_ta.project_id = first_p.project_id + LEFT JOIN work_types first_wt ON first_ta.work_type_id = first_wt.id + LEFT JOIN tasks first_t ON first_ta.task_id = first_t.task_id + LEFT JOIN workplaces first_wp ON first_ta.workplace_id = first_wp.workplace_id + LEFT JOIN workplace_categories first_wc ON first_ta.workplace_category_id = first_wc.category_id + WHERE s.session_id = ? + GROUP BY s.session_id`, + [sessionId] + ); + return rows; }, - /** - * TBM μ„Έμ…˜ μˆ˜μ • - */ - updateSession: async (sessionId, sessionData, callback) => { - try { - const db = await getDb(); - const sql = ` - UPDATE tbm_sessions - SET - project_id = ?, - work_location = ?, - status = ?, - updated_at = NOW() - WHERE session_id = ? - `; - - const values = [ - sessionData.project_id, - sessionData.work_location, - sessionData.status, - sessionId - ]; - - const [result] = await db.query(sql, values); - callback(null, result); - } catch (err) { - callback(err); - } + updateSession: async (sessionId, sessionData) => { + const db = await getDb(); + const [result] = await db.query( + `UPDATE tbm_sessions + SET project_id = ?, work_location = ?, status = ?, updated_at = NOW() + WHERE session_id = ?`, + [sessionData.project_id, sessionData.work_location, sessionData.status, sessionId] + ); + return result; }, - /** - * TBM μ„Έμ…˜ μ™„λ£Œ 처리 - */ - completeSession: async (sessionId, endTime, callback) => { - try { - const db = await getDb(); - const sql = ` - UPDATE tbm_sessions - SET - status = 'completed', - end_time = ?, - updated_at = NOW() - WHERE session_id = ? - `; - - const [result] = await db.query(sql, [endTime, sessionId]); - callback(null, result); - } catch (err) { - callback(err); - } + completeSession: async (sessionId, endTime) => { + const db = await getDb(); + const [result] = await db.query( + `UPDATE tbm_sessions + SET status = 'completed', end_time = ?, updated_at = NOW() + WHERE session_id = ?`, + [endTime, sessionId] + ); + return result; }, - /** - * TBM μ„Έμ…˜ μ™„λ£Œ 처리 (κ·Όνƒœ μœ ν˜• 포함) - * @param {number} sessionId - TBM μ„Έμ…˜ ID - * @param {string} endTime - μ’…λ£Œ μ‹œκ°„ - * @param {Array} attendanceData - [{worker_id, attendance_type, attendance_hours}] - * @param {number} createdBy - 처리자 user_id - */ - completeSessionWithAttendance: async (sessionId, endTime, attendanceData, createdBy, callback) => { - let conn; + completeSessionWithAttendance: async (sessionId, endTime, attendanceData, createdBy) => { + const db = await getDb(); + const conn = await db.getConnection(); try { - const db = await getDb(); - conn = await db.getConnection(); await conn.beginTransaction(); - // 1. μ„Έμ…˜ 정보 쑰회 (λ‚ μ§œ ν™•μΈμš©) + // 1. μ„Έμ…˜ 정보 쑰회 const [sessionRows] = await conn.query( 'SELECT session_date FROM tbm_sessions WHERE session_id = ?', [sessionId] ); if (sessionRows.length === 0) { - await conn.rollback(); - conn.release(); - return callback(null, { affectedRows: 0 }); + await conn.commit(); + return { affectedRows: 0 }; } const sessionDate = sessionRows[0].session_date; - // sessionDateλ₯Ό YYYY-MM-DD ν˜•μ‹μœΌλ‘œ λ³€ν™˜ let reportDate; if (sessionDate instanceof Date) { reportDate = sessionDate.toISOString().split('T')[0]; @@ -239,16 +170,14 @@ const TbmModel = { ); } - // 3. μ—°μ°¨(annual) μž‘μ—…μž β†’ μž‘μ—…λ³΄κ³ μ„œ μžλ™ 생성 (project_id=13, 8h) + // 3. μ—°μ°¨ μž‘μ—…μž β†’ μž‘μ—…λ³΄κ³ μ„œ μžλ™ 생성 const annualWorkers = attendanceData.filter(a => a.attendance_type === 'annual'); for (const aw of annualWorkers) { - // ν•΄λ‹Ή μž‘μ—…μžμ˜ assignment_id 쑰회 const [assignRows] = await conn.query( 'SELECT assignment_id FROM tbm_team_assignments WHERE session_id = ? AND worker_id = ?', [sessionId, aw.worker_id] ); if (assignRows.length > 0) { - // 이미 λ³΄κ³ μ„œκ°€ μžˆλŠ”μ§€ 확인 const [existingReport] = await conn.query( 'SELECT id FROM daily_work_reports WHERE tbm_assignment_id = ?', [assignRows[0].assignment_id] @@ -273,7 +202,6 @@ const TbmModel = { ); await conn.commit(); - conn.release(); // 5. μ—°μ°¨ μž‘μ—…μž κ·Όνƒœ 동기화 for (const aw of annualWorkers) { @@ -281,64 +209,49 @@ const TbmModel = { const AttendanceModel = require('./attendanceModel'); await AttendanceModel.syncWithWorkReports(aw.worker_id, reportDate); } catch (syncErr) { - console.error('κ·Όνƒœ 동기화 였λ₯˜ (λ¬΄μ‹œλ¨):', syncErr); + // κ·Όνƒœ 동기화 였λ₯˜ (λ¬΄μ‹œλ¨) } } - callback(null, { affectedRows: 1 }); + return { affectedRows: 1 }; } catch (err) { - if (conn) { - try { await conn.rollback(); } catch (e) {} - conn.release(); - } - callback(err); + try { await conn.rollback(); } catch (e) {} + throw err; + } finally { + conn.release(); } }, - /** - * TBM μ„Έμ…˜ μ‚­μ œ (draft μƒνƒœλ§Œ κ°€λŠ₯) - */ - deleteSession: async (sessionId, callback) => { - try { - const db = await getDb(); - // draft μƒνƒœμΈ μ„Έμ…˜λ§Œ μ‚­μ œ ν—ˆμš© - const [result] = await db.query( - `DELETE FROM tbm_sessions WHERE session_id = ? AND status = 'draft'`, - [sessionId] - ); - callback(null, result); - } catch (err) { - callback(err); - } + deleteSession: async (sessionId) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM tbm_sessions WHERE session_id = ? AND status = 'draft'`, + [sessionId] + ); + return result; }, // ==================== νŒ€ ꡬ성 κ΄€λ ¨ ==================== - /** - * νŒ€μ› μΆ”κ°€ (μž‘μ—…μžλ³„ 상세 정보 포함) - */ - addTeamMember: async (assignmentData, callback) => { - try { - const db = await getDb(); - const sql = ` - INSERT INTO tbm_team_assignments - (session_id, worker_id, split_seq, assigned_role, work_detail, is_present, absence_reason, - project_id, work_type_id, task_id, workplace_category_id, workplace_id, work_hours) - 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), - project_id = VALUES(project_id), - work_type_id = VALUES(work_type_id), - task_id = VALUES(task_id), - workplace_category_id = VALUES(workplace_category_id), - workplace_id = VALUES(workplace_id), - work_hours = COALESCE(VALUES(work_hours), work_hours) - `; - - const values = [ + addTeamMember: async (assignmentData) => { + const db = await getDb(); + const [result] = await db.query( + `INSERT INTO tbm_team_assignments + (session_id, worker_id, split_seq, assigned_role, work_detail, is_present, absence_reason, + project_id, work_type_id, task_id, workplace_category_id, workplace_id, work_hours) + 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), + project_id = VALUES(project_id), + work_type_id = VALUES(work_type_id), + task_id = VALUES(task_id), + workplace_category_id = VALUES(workplace_category_id), + workplace_id = VALUES(workplace_id), + work_hours = COALESCE(VALUES(work_hours), work_hours)`, + [ assignmentData.session_id, assignmentData.worker_id, assignmentData.split_seq || 0, @@ -352,35 +265,25 @@ const TbmModel = { assignmentData.workplace_category_id || null, assignmentData.workplace_id || null, assignmentData.work_hours !== undefined ? assignmentData.work_hours : null - ]; - - const [result] = await db.query(sql, values); - callback(null, result); - } catch (err) { - callback(err); - } + ] + ); + return result; }, - /** - * λΆ„ν•  ν•­λͺ© μΆ”κ°€ (같은 μ„Έμ…˜+μž‘μ—…μžμ— split_seq μžλ™ 증가) - */ - addSplitAssignment: async (assignmentData, callback) => { - try { - const db = await getDb(); - // ν˜„μž¬ μ΅œλŒ€ split_seq 쑰회 - const [maxRows] = await db.query( - 'SELECT COALESCE(MAX(split_seq), -1) as max_seq FROM tbm_team_assignments WHERE session_id = ? AND worker_id = ?', - [assignmentData.session_id, assignmentData.worker_id] - ); - const nextSeq = (maxRows[0].max_seq || 0) + 1; + addSplitAssignment: async (assignmentData) => { + const db = await getDb(); + const [maxRows] = await db.query( + 'SELECT COALESCE(MAX(split_seq), -1) as max_seq FROM tbm_team_assignments WHERE session_id = ? AND worker_id = ?', + [assignmentData.session_id, assignmentData.worker_id] + ); + const nextSeq = (maxRows[0].max_seq || 0) + 1; - const sql = ` - INSERT INTO tbm_team_assignments - (session_id, worker_id, split_seq, work_hours, project_id, work_type_id, - task_id, workplace_category_id, workplace_id, is_present) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1) - `; - const [result] = await db.query(sql, [ + const [result] = await db.query( + `INSERT INTO tbm_team_assignments + (session_id, worker_id, split_seq, work_hours, project_id, work_type_id, + task_id, workplace_category_id, workplace_id, is_present) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 1)`, + [ assignmentData.session_id, assignmentData.worker_id, nextSeq, @@ -390,278 +293,181 @@ const TbmModel = { assignmentData.task_id || null, assignmentData.workplace_category_id || null, assignmentData.workplace_id || null - ]); - callback(null, { assignment_id: result.insertId, split_seq: nextSeq }); - } catch (err) { - callback(err); - } + ] + ); + return { assignment_id: result.insertId, split_seq: nextSeq }; }, - /** - * νŒ€ ꡬ성 일괄 μΆ”κ°€ (μž‘μ—…μžλ³„ 상세 정보 포함) - */ - addTeamMembers: async (sessionId, members, callback) => { - try { - if (!members || members.length === 0) { - return callback(null, { affectedRows: 0 }); - } - - const db = await getDb(); - 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, - m.project_id || null, - m.work_type_id || null, - m.task_id || null, - m.workplace_category_id || null, - m.workplace_id || null - ]); - - const sql = ` - INSERT INTO tbm_team_assignments - (session_id, worker_id, assigned_role, work_detail, is_present, absence_reason, - project_id, work_type_id, task_id, workplace_category_id, workplace_id) - VALUES ? - `; - - const [result] = await db.query(sql, [values]); - callback(null, result); - } catch (err) { - callback(err); + addTeamMembers: async (sessionId, members) => { + if (!members || members.length === 0) { + return { affectedRows: 0 }; } + + const db = await getDb(); + 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, + m.project_id || null, + m.work_type_id || null, + m.task_id || null, + m.workplace_category_id || null, + m.workplace_id || null + ]); + + const [result] = await db.query( + `INSERT INTO tbm_team_assignments + (session_id, worker_id, assigned_role, work_detail, is_present, absence_reason, + project_id, work_type_id, task_id, workplace_category_id, workplace_id) + VALUES ?`, + [values] + ); + return result; }, - /** - * TBM μ„Έμ…˜μ˜ νŒ€ ꡬ성 쑰회 (μž‘μ—…μžλ³„ 상세 정보 포함) - */ - getTeamMembers: async (sessionId, callback) => { - try { - const db = await getDb(); - const sql = ` - SELECT - ta.*, - w.worker_name, - w.job_type, - w.phone_number, - w.department, - p.project_name, - wt.name as work_type_name, - t.task_name, - wc.category_name AS workplace_category_name, - wp.workplace_name - FROM tbm_team_assignments ta - INNER JOIN workers w ON ta.worker_id = w.worker_id - LEFT JOIN projects p ON ta.project_id = p.project_id - LEFT JOIN work_types wt ON ta.work_type_id = wt.id - LEFT JOIN tasks t ON ta.task_id = t.task_id - LEFT JOIN workplace_categories wc ON ta.workplace_category_id = wc.category_id - LEFT JOIN workplaces wp ON ta.workplace_id = wp.workplace_id - WHERE ta.session_id = ? - ORDER BY ta.assigned_at DESC - `; - - const [rows] = await db.query(sql, [sessionId]); - callback(null, rows); - } catch (err) { - callback(err); - } + getTeamMembers: async (sessionId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + ta.*, + w.worker_name, + w.job_type, + w.phone_number, + w.department, + p.project_name, + wt.name as work_type_name, + t.task_name, + wc.category_name AS workplace_category_name, + wp.workplace_name + FROM tbm_team_assignments ta + INNER JOIN workers w ON ta.worker_id = w.worker_id + LEFT JOIN projects p ON ta.project_id = p.project_id + LEFT JOIN work_types wt ON ta.work_type_id = wt.id + LEFT JOIN tasks t ON ta.task_id = t.task_id + LEFT JOIN workplace_categories wc ON ta.workplace_category_id = wc.category_id + LEFT JOIN workplaces wp ON ta.workplace_id = wp.workplace_id + WHERE ta.session_id = ? + ORDER BY ta.assigned_at DESC`, + [sessionId] + ); + return rows; }, - /** - * νŒ€μ› 제거 - */ - removeTeamMember: async (sessionId, workerId, callback) => { - try { - const db = await getDb(); - const sql = ` - DELETE FROM tbm_team_assignments - WHERE session_id = ? AND worker_id = ? - `; - - const [result] = await db.query(sql, [sessionId, workerId]); - callback(null, result); - } catch (err) { - callback(err); - } + removeTeamMember: async (sessionId, workerId) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM tbm_team_assignments WHERE session_id = ? AND worker_id = ?`, + [sessionId, workerId] + ); + return result; }, - /** - * μ„Έμ…˜μ˜ λͺ¨λ“  νŒ€μ› μ‚­μ œ - */ - clearAllTeamMembers: async (sessionId, callback) => { - try { - const db = await getDb(); - const sql = ` - DELETE FROM tbm_team_assignments - WHERE session_id = ? - `; - - const [result] = await db.query(sql, [sessionId]); - callback(null, result); - } catch (err) { - callback(err); - } + clearAllTeamMembers: async (sessionId) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM tbm_team_assignments WHERE session_id = ?`, + [sessionId] + ); + return result; }, // ==================== μ•ˆμ „ 체크리슀트 κ΄€λ ¨ ==================== - /** - * λͺ¨λ“  μ•ˆμ „ 체크 ν•­λͺ© 쑰회 - */ - getAllSafetyChecks: async (callback) => { - try { - const db = await getDb(); - const sql = ` - SELECT * - FROM tbm_safety_checks - WHERE is_active = 1 - ORDER BY check_category, display_order - `; - - const [rows] = await db.query(sql); - callback(null, rows); - } catch (err) { - callback(err); - } + getAllSafetyChecks: async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT * FROM tbm_safety_checks WHERE is_active = 1 ORDER BY check_category, display_order` + ); + return rows; }, - /** - * μΉ΄ν…Œκ³ λ¦¬λ³„ μ•ˆμ „ 체크 ν•­λͺ© 쑰회 - */ - getSafetyChecksByCategory: async (category, callback) => { - try { - const db = await getDb(); - const sql = ` - SELECT * - FROM tbm_safety_checks - WHERE check_category = ? AND is_active = 1 - ORDER BY display_order - `; - - const [rows] = await db.query(sql, [category]); - callback(null, rows); - } catch (err) { - callback(err); - } + getSafetyChecksByCategory: async (category) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT * FROM tbm_safety_checks WHERE check_category = ? AND is_active = 1 ORDER BY display_order`, + [category] + ); + return rows; }, - /** - * TBM μ„Έμ…˜μ˜ μ•ˆμ „ 체크 기둝 쑰회 - */ - getSafetyRecords: async (sessionId, callback) => { - try { - const db = await getDb(); - 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 sso_users u ON sr.checked_by = u.user_id - WHERE sr.session_id = ? - ORDER BY sc.check_category, sc.display_order - `; - - const [rows] = await db.query(sql, [sessionId]); - callback(null, rows); - } catch (err) { - callback(err); - } + getSafetyRecords: async (sessionId) => { + const db = await getDb(); + const [rows] = await db.query( + `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 sso_users u ON sr.checked_by = u.user_id + WHERE sr.session_id = ? + ORDER BY sc.check_category, sc.display_order`, + [sessionId] + ); + return rows; }, - /** - * μ•ˆμ „ 체크 기둝 μ €μž₯/μ—…λ°μ΄νŠΈ - */ - saveSafetyRecord: async (recordData, callback) => { - try { - const db = await getDb(); - 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 - ]; - - const [result] = await db.query(sql, values); - callback(null, result); - } catch (err) { - callback(err); - } + saveSafetyRecord: async (recordData) => { + const db = await getDb(); + const [result] = await db.query( + `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()`, + [recordData.session_id, recordData.check_id, recordData.is_checked, recordData.notes, recordData.checked_by] + ); + return result; }, - /** - * μ•ˆμ „ 체크 일괄 μ €μž₯ - */ - saveSafetyRecords: async (sessionId, records, checkedBy, callback) => { - try { - if (!records || records.length === 0) { - return callback(null, { affectedRows: 0 }); - } - - const db = await getDb(); - 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() - `; - - const [result] = await db.query(sql, [values]); - callback(null, result); - } catch (err) { - callback(err); + saveSafetyRecords: async (sessionId, records, checkedBy) => { + if (!records || records.length === 0) { + return { affectedRows: 0 }; } + + const db = await getDb(); + const values = records.map(r => [ + sessionId, + r.check_id, + r.is_checked, + r.notes || null, + checkedBy + ]); + + const [result] = await db.query( + `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()`, + [values] + ); + return result; }, // ==================== μž‘μ—… 인계 κ΄€λ ¨ ==================== - /** - * μž‘μ—… 인계 생성 - */ - createHandover: async (handoverData, callback) => { - try { - const db = await getDb(); - 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 = [ + createHandover: async (handoverData) => { + const db = await getDb(); + const [result] = await db.query( + `INSERT INTO team_handovers + (session_id, from_leader_id, to_leader_id, handover_date, handover_time, + reason, handover_notes, worker_ids) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + [ handoverData.session_id, handoverData.from_leader_id, handoverData.to_leader_id, @@ -670,439 +476,332 @@ const TbmModel = { handoverData.reason, handoverData.handover_notes, JSON.stringify(handoverData.worker_ids || []) - ]; - - const [result] = await db.query(sql, values); - callback(null, result); - } catch (err) { - callback(err); - } + ] + ); + return result; }, - /** - * μž‘μ—… 인계 확인 - */ - confirmHandover: async (handoverId, confirmedBy, callback) => { - try { - const db = await getDb(); - const sql = ` - UPDATE team_handovers - SET - is_confirmed = 1, - confirmed_at = NOW(), - confirmed_by = ? - WHERE handover_id = ? - `; - - const [result] = await db.query(sql, [confirmedBy, handoverId]); - callback(null, result); - } catch (err) { - callback(err); - } + confirmHandover: async (handoverId, confirmedBy) => { + const db = await getDb(); + const [result] = await db.query( + `UPDATE team_handovers + SET is_confirmed = 1, confirmed_at = NOW(), confirmed_by = ? + WHERE handover_id = ?`, + [confirmedBy, handoverId] + ); + return result; }, - /** - * νŠΉμ • λ‚ μ§œμ˜ μž‘μ—… 인계 λͺ©λ‘ 쑰회 - */ - getHandoversByDate: async (date, callback) => { - try { - const db = await getDb(); - 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 sso_users u ON h.confirmed_by = u.user_id - WHERE h.handover_date = ? - ORDER BY h.handover_time DESC - `; - - const [rows] = await db.query(sql, [date]); - callback(null, rows); - } catch (err) { - callback(err); - } + getHandoversByDate: async (date) => { + const db = await getDb(); + const [rows] = await db.query( + `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 sso_users u ON h.confirmed_by = u.user_id + WHERE h.handover_date = ? + ORDER BY h.handover_time DESC`, + [date] + ); + return rows; }, - /** - * μΈμˆ˜μžκ°€ 받은 미확인 인계 건 쑰회 - */ - getPendingHandovers: async (toLeaderId, callback) => { - try { - const db = await getDb(); - const sql = ` - SELECT - h.*, - w1.worker_name as from_leader_name, - w1.phone_number as from_leader_phone, - s.work_location - 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 - `; - - const [rows] = await db.query(sql, [toLeaderId]); - callback(null, rows); - } catch (err) { - callback(err); - } + getPendingHandovers: async (toLeaderId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + h.*, + w1.worker_name as from_leader_name, + w1.phone_number as from_leader_phone, + s.work_location + 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`, + [toLeaderId] + ); + return rows; }, // ==================== 톡계 및 리포트 ==================== - /** - * νŠΉμ • κΈ°κ°„μ˜ TBM 톡계 - */ - getTbmStatistics: async (startDate, endDate, callback) => { - try { - const db = await getDb(); - 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 - `; - - const [rows] = await db.query(sql, [startDate, endDate]); - callback(null, rows); - } catch (err) { - callback(err); - } + getTbmStatistics: async (startDate, endDate) => { + const db = await getDb(); + const [rows] = await db.query( + `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`, + [startDate, endDate] + ); + return rows; }, - /** - * 리더별 TBM μ§„ν–‰ ν˜„ν™© - */ - getLeaderStatistics: async (startDate, endDate, callback) => { - try { - const db = await getDb(); - 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 - `; - - const [rows] = await db.query(sql, [startDate, endDate]); - callback(null, rows); - } catch (err) { - callback(err); - } + getLeaderStatistics: async (startDate, endDate) => { + const db = await getDb(); + const [rows] = await db.query( + `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`, + [startDate, endDate] + ); + return rows; }, - /** - * μž‘μ—…λ³΄κ³ μ„œκ°€ μž‘μ„±λ˜μ§€ μ•Šμ€ TBM μ„Έμ…˜μ˜ νŒ€ λ°°μ • 쑰회 - * @param {number|null} userId - μ‘°νšŒν•  μ‚¬μš©μž ID (null이면 λͺ¨λ“  TBM 쑰회 - κ΄€λ¦¬μžμš©) - */ - getIncompleteWorkReports: async (userId, callback) => { - try { - const db = await getDb(); + getIncompleteWorkReports: async (userId) => { + const db = await getDb(); - // WHERE 쑰건 동적 생성 - // TBM μ™„λ£Œ(κ·Όνƒœ μž…λ ₯) ν›„μ—λ§Œ μž‘μ—…λ³΄κ³ μ„œ μž‘μ„± κ°€λŠ₯ - let whereClause = ` - WHERE dwr.id IS NULL + let whereClause = ` + WHERE dwr.id IS NULL + AND s.status = 'completed' + AND (ta.attendance_type IS NULL OR ta.attendance_type != 'annual') + AND ta.task_id IS NOT NULL + AND ta.workplace_id IS NOT NULL + `; + const params = []; + + if (userId !== null && userId !== undefined) { + whereClause = ` + WHERE s.created_by = ? + AND dwr.id IS NULL AND s.status = 'completed' AND (ta.attendance_type IS NULL OR ta.attendance_type != 'annual') AND ta.task_id IS NOT NULL AND ta.workplace_id IS NOT NULL `; - const params = []; - - // userIdκ°€ 있으면 created_by 쑰건 μΆ”κ°€ (일반 μ‚¬μš©μž) - if (userId !== null && userId !== undefined) { - whereClause = ` - WHERE s.created_by = ? - AND dwr.id IS NULL - AND s.status = 'completed' - AND (ta.attendance_type IS NULL OR ta.attendance_type != 'annual') - AND ta.task_id IS NOT NULL - AND ta.workplace_id IS NOT NULL - `; - params.push(userId); - } - - const sql = ` - SELECT - ta.assignment_id, - ta.session_id, - ta.worker_id, - ta.project_id, - ta.work_type_id, - ta.task_id, - ta.workplace_category_id, - ta.workplace_id, - ta.attendance_type, - ta.attendance_hours, - ta.work_hours, - s.session_date, - s.status as session_status, - s.created_by, - w.worker_name, - w.job_type, - p.project_name, - wt.name as work_type_name, - t.task_name, - wp.workplace_name, - wc.category_name, - creator.name as created_by_name - FROM tbm_team_assignments ta - INNER JOIN tbm_sessions s ON ta.session_id = s.session_id - INNER JOIN workers w ON ta.worker_id = w.worker_id - LEFT JOIN users creator ON s.created_by = creator.user_id - LEFT JOIN projects p ON ta.project_id = p.project_id - LEFT JOIN work_types wt ON ta.work_type_id = wt.id - LEFT JOIN tasks t ON ta.task_id = t.task_id - LEFT JOIN workplaces wp ON ta.workplace_id = wp.workplace_id - LEFT JOIN workplace_categories wc ON ta.workplace_category_id = wc.category_id - LEFT JOIN daily_work_reports dwr ON ta.assignment_id = dwr.tbm_assignment_id - ${whereClause} - ORDER BY s.session_date DESC, ta.assignment_id ASC - `; - - const [rows] = await db.query(sql, params); - callback(null, rows); - } catch (err) { - callback(err); + params.push(userId); } + + const [rows] = await db.query( + `SELECT + ta.assignment_id, + ta.session_id, + ta.worker_id, + ta.project_id, + ta.work_type_id, + ta.task_id, + ta.workplace_category_id, + ta.workplace_id, + ta.attendance_type, + ta.attendance_hours, + ta.work_hours, + s.session_date, + s.status as session_status, + s.created_by, + w.worker_name, + w.job_type, + p.project_name, + wt.name as work_type_name, + t.task_name, + wp.workplace_name, + wc.category_name, + creator.name as created_by_name + FROM tbm_team_assignments ta + INNER JOIN tbm_sessions s ON ta.session_id = s.session_id + INNER JOIN workers w ON ta.worker_id = w.worker_id + LEFT JOIN users creator ON s.created_by = creator.user_id + LEFT JOIN projects p ON ta.project_id = p.project_id + LEFT JOIN work_types wt ON ta.work_type_id = wt.id + LEFT JOIN tasks t ON ta.task_id = t.task_id + LEFT JOIN workplaces wp ON ta.workplace_id = wp.workplace_id + LEFT JOIN workplace_categories wc ON ta.workplace_category_id = wc.category_id + LEFT JOIN daily_work_reports dwr ON ta.assignment_id = dwr.tbm_assignment_id + ${whereClause} + ORDER BY s.session_date DESC, ta.assignment_id ASC`, + params + ); + return rows; }, // ========== μ•ˆμ „ 체크리슀트 ν™•μž₯ λ©”μ„œλ“œ ========== - /** - * μœ ν˜•λ³„ μ•ˆμ „ 체크 ν•­λͺ© 쑰회 - * @param {string} checkType - 체크 μœ ν˜• (basic, weather, task) - * @param {Object} options - μΆ”κ°€ μ˜΅μ…˜ (weatherCondition, taskId) - */ - getSafetyChecksByType: async (checkType, options = {}, callback) => { - try { - const db = await getDb(); - let sql = ` - SELECT sc.*, - wc.condition_name as weather_condition_name, - wc.icon as weather_icon, - t.task_name - FROM tbm_safety_checks sc - LEFT JOIN weather_conditions wc ON sc.weather_condition = wc.condition_code - LEFT JOIN tasks t ON sc.task_id = t.task_id - WHERE sc.is_active = 1 AND sc.check_type = ? - `; - const params = [checkType]; + getSafetyChecksByType: async (checkType, options = {}) => { + const db = await getDb(); + let sql = ` + SELECT sc.*, + wc.condition_name as weather_condition_name, + wc.icon as weather_icon, + t.task_name + FROM tbm_safety_checks sc + LEFT JOIN weather_conditions wc ON sc.weather_condition = wc.condition_code + LEFT JOIN tasks t ON sc.task_id = t.task_id + WHERE sc.is_active = 1 AND sc.check_type = ? + `; + const params = [checkType]; - if (checkType === 'weather' && options.weatherCondition) { - sql += ' AND sc.weather_condition = ?'; - params.push(options.weatherCondition); - } - - if (checkType === 'task' && options.taskId) { - sql += ' AND sc.task_id = ?'; - params.push(options.taskId); - } - - sql += ' ORDER BY sc.check_category, sc.display_order'; - - const [rows] = await db.query(sql, params); - callback(null, rows); - } catch (err) { - callback(err); + if (checkType === 'weather' && options.weatherCondition) { + sql += ' AND sc.weather_condition = ?'; + params.push(options.weatherCondition); } + + if (checkType === 'task' && options.taskId) { + sql += ' AND sc.task_id = ?'; + params.push(options.taskId); + } + + sql += ' ORDER BY sc.check_category, sc.display_order'; + + const [rows] = await db.query(sql, params); + return rows; }, - /** - * 날씨 쑰건별 μ•ˆμ „ 체크 ν•­λͺ© 쑰회 (볡수 쑰건) - * @param {string[]} conditions - 날씨 쑰건 λ°°μ—΄ ['rain', 'wind'] - */ - getSafetyChecksByWeather: async (conditions, callback) => { - try { - const db = await getDb(); + getSafetyChecksByWeather: async (conditions) => { + if (!conditions || conditions.length === 0) { + return []; + } - if (!conditions || conditions.length === 0) { - return callback(null, []); - } + const db = await getDb(); + const placeholders = conditions.map(() => '?').join(','); + const [rows] = await db.query( + `SELECT sc.*, + wc.condition_name as weather_condition_name, + wc.icon as weather_icon + FROM tbm_safety_checks sc + LEFT JOIN weather_conditions wc ON sc.weather_condition = wc.condition_code + WHERE sc.is_active = 1 + AND sc.check_type = 'weather' + AND sc.weather_condition IN (${placeholders}) + ORDER BY sc.weather_condition, sc.display_order`, + conditions + ); + return rows; + }, - const placeholders = conditions.map(() => '?').join(','); - const sql = ` - SELECT sc.*, - wc.condition_name as weather_condition_name, - wc.icon as weather_icon + getSafetyChecksByTasks: async (taskIds) => { + if (!taskIds || taskIds.length === 0) { + return []; + } + + const db = await getDb(); + const placeholders = taskIds.map(() => '?').join(','); + const [rows] = await db.query( + `SELECT sc.*, + t.task_name, + wt.name as work_type_name + FROM tbm_safety_checks sc + LEFT JOIN tasks t ON sc.task_id = t.task_id + LEFT JOIN work_types wt ON t.work_type_id = wt.id + WHERE sc.is_active = 1 + AND sc.check_type = 'task' + AND sc.task_id IN (${placeholders}) + ORDER BY sc.task_id, sc.display_order`, + taskIds + ); + return rows; + }, + + getFilteredSafetyChecks: async (sessionId, weatherConditions = []) => { + const db = await getDb(); + + // 1. μ„Έμ…˜ μž‘μ—… ID λͺ©λ‘ + const [assignments] = await db.query( + `SELECT DISTINCT task_id FROM tbm_team_assignments WHERE session_id = ? AND task_id IS NOT NULL`, + [sessionId] + ); + const taskIds = assignments.map(a => a.task_id); + + // 2. κΈ°λ³Έ 체크항λͺ© + const [basicChecks] = await db.query( + `SELECT sc.*, 'basic' as section_type + FROM tbm_safety_checks sc + WHERE sc.is_active = 1 AND sc.check_type = 'basic' + ORDER BY sc.check_category, sc.display_order` + ); + + // 3. 날씨별 체크항λͺ© + let weatherChecks = []; + if (weatherConditions && weatherConditions.length > 0) { + const wcPlaceholders = weatherConditions.map(() => '?').join(','); + const [rows] = await db.query( + `SELECT sc.*, wc.condition_name as weather_condition_name, wc.icon as weather_icon, + 'weather' as section_type FROM tbm_safety_checks sc LEFT JOIN weather_conditions wc ON sc.weather_condition = wc.condition_code WHERE sc.is_active = 1 AND sc.check_type = 'weather' - AND sc.weather_condition IN (${placeholders}) - ORDER BY sc.weather_condition, sc.display_order - `; - - const [rows] = await db.query(sql, conditions); - callback(null, rows); - } catch (err) { - callback(err); + AND sc.weather_condition IN (${wcPlaceholders}) + ORDER BY sc.weather_condition, sc.display_order`, + weatherConditions + ); + weatherChecks = rows; } - }, - /** - * μž‘μ—…λ³„ μ•ˆμ „ 체크 ν•­λͺ© 쑰회 (볡수 μž‘μ—…) - * @param {number[]} taskIds - μž‘μ—… ID λ°°μ—΄ - */ - getSafetyChecksByTasks: async (taskIds, callback) => { - try { - const db = await getDb(); - - if (!taskIds || taskIds.length === 0) { - return callback(null, []); - } - - const placeholders = taskIds.map(() => '?').join(','); - const sql = ` - SELECT sc.*, - t.task_name, - wt.name as work_type_name + // 4. μž‘μ—…λ³„ 체크항λͺ© + let taskChecks = []; + if (taskIds.length > 0) { + const taskPlaceholders = taskIds.map(() => '?').join(','); + const [rows] = await db.query( + `SELECT sc.*, t.task_name, wt.name as work_type_name, + 'task' as section_type FROM tbm_safety_checks sc LEFT JOIN tasks t ON sc.task_id = t.task_id LEFT JOIN work_types wt ON t.work_type_id = wt.id WHERE sc.is_active = 1 AND sc.check_type = 'task' - AND sc.task_id IN (${placeholders}) - ORDER BY sc.task_id, sc.display_order - `; - - const [rows] = await db.query(sql, taskIds); - callback(null, rows); - } catch (err) { - callback(err); + AND sc.task_id IN (${taskPlaceholders}) + ORDER BY sc.task_id, sc.display_order`, + taskIds + ); + taskChecks = rows; } + + // 5. κΈ°μ‘΄ 체크 기둝 + const [existingRecords] = await db.query( + `SELECT check_id, is_checked, notes FROM tbm_safety_records WHERE session_id = ?`, + [sessionId] + ); + + const recordMap = {}; + existingRecords.forEach(r => { + recordMap[r.check_id] = { is_checked: r.is_checked, notes: r.notes }; + }); + + // 6. 기둝 병합 + const mergeWithRecords = (checks) => { + return checks.map(check => ({ + ...check, + is_checked: recordMap[check.check_id]?.is_checked || false, + notes: recordMap[check.check_id]?.notes || null + })); + }; + + return { + basic: mergeWithRecords(basicChecks), + weather: mergeWithRecords(weatherChecks), + task: mergeWithRecords(taskChecks), + totalCount: basicChecks.length + weatherChecks.length + taskChecks.length, + weatherConditions: weatherConditions + }; }, - /** - * TBM μ„Έμ…˜μ— λ§žλŠ” ν•„ν„°λ§λœ μ•ˆμ „ 체크 ν•­λͺ© 쑰회 - * κΈ°λ³Έ + 날씨 + μž‘μ—…λ³„ 체크항λͺ© 톡합 쑰회 - * @param {number} sessionId - TBM μ„Έμ…˜ ID - * @param {string[]} weatherConditions - 날씨 쑰건 λ°°μ—΄ (optional) - */ - getFilteredSafetyChecks: async (sessionId, weatherConditions = [], callback) => { - try { - const db = await getDb(); - - // 1. μ„Έμ…˜ μ •λ³΄μ—μ„œ μž‘μ—… ID λͺ©λ‘ 쑰회 - const [assignments] = await db.query(` - SELECT DISTINCT task_id - FROM tbm_team_assignments - WHERE session_id = ? AND task_id IS NOT NULL - `, [sessionId]); - - const taskIds = assignments.map(a => a.task_id); - - // 2. κΈ°λ³Έ 체크항λͺ© 쑰회 - const [basicChecks] = await db.query(` - SELECT sc.*, 'basic' as section_type - FROM tbm_safety_checks sc - WHERE sc.is_active = 1 AND sc.check_type = 'basic' - ORDER BY sc.check_category, sc.display_order - `); - - // 3. 날씨별 체크항λͺ© 쑰회 - let weatherChecks = []; - if (weatherConditions && weatherConditions.length > 0) { - const wcPlaceholders = weatherConditions.map(() => '?').join(','); - const [rows] = await db.query(` - SELECT sc.*, wc.condition_name as weather_condition_name, wc.icon as weather_icon, - 'weather' as section_type - FROM tbm_safety_checks sc - LEFT JOIN weather_conditions wc ON sc.weather_condition = wc.condition_code - WHERE sc.is_active = 1 - AND sc.check_type = 'weather' - AND sc.weather_condition IN (${wcPlaceholders}) - ORDER BY sc.weather_condition, sc.display_order - `, weatherConditions); - weatherChecks = rows; - } - - // 4. μž‘μ—…λ³„ 체크항λͺ© 쑰회 - let taskChecks = []; - if (taskIds.length > 0) { - const taskPlaceholders = taskIds.map(() => '?').join(','); - const [rows] = await db.query(` - SELECT sc.*, t.task_name, wt.name as work_type_name, - 'task' as section_type - FROM tbm_safety_checks sc - LEFT JOIN tasks t ON sc.task_id = t.task_id - LEFT JOIN work_types wt ON t.work_type_id = wt.id - WHERE sc.is_active = 1 - AND sc.check_type = 'task' - AND sc.task_id IN (${taskPlaceholders}) - ORDER BY sc.task_id, sc.display_order - `, taskIds); - taskChecks = rows; - } - - // 5. κΈ°μ‘΄ 체크 기둝 쑰회 - const [existingRecords] = await db.query(` - SELECT check_id, is_checked, notes - FROM tbm_safety_records - WHERE session_id = ? - `, [sessionId]); - - const recordMap = {}; - existingRecords.forEach(r => { - recordMap[r.check_id] = { is_checked: r.is_checked, notes: r.notes }; - }); - - // 6. 기둝과 병합 - const mergeWithRecords = (checks) => { - return checks.map(check => ({ - ...check, - is_checked: recordMap[check.check_id]?.is_checked || false, - notes: recordMap[check.check_id]?.notes || null - })); - }; - - const result = { - basic: mergeWithRecords(basicChecks), - weather: mergeWithRecords(weatherChecks), - task: mergeWithRecords(taskChecks), - totalCount: basicChecks.length + weatherChecks.length + taskChecks.length, - weatherConditions: weatherConditions - }; - - callback(null, result); - } catch (err) { - callback(err); - } - }, - - /** - * μ•ˆμ „ 체크 ν•­λͺ© 생성 (κ΄€λ¦¬μžμš©) - */ - createSafetyCheck: async (checkData, callback) => { - try { - const db = await getDb(); - const sql = ` - INSERT INTO tbm_safety_checks - (check_category, check_type, weather_condition, task_id, check_item, description, is_required, display_order) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) - `; - - const values = [ + createSafetyCheck: async (checkData) => { + const db = await getDb(); + const [result] = await db.query( + `INSERT INTO tbm_safety_checks + (check_category, check_type, weather_condition, task_id, check_item, description, is_required, display_order) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + [ checkData.check_category, checkData.check_type || 'basic', checkData.weather_condition || null, @@ -1111,37 +810,20 @@ const TbmModel = { checkData.description || null, checkData.is_required !== false, checkData.display_order || 0 - ]; - - const [result] = await db.query(sql, values); - callback(null, { insertId: result.insertId }); - } catch (err) { - callback(err); - } + ] + ); + return { insertId: result.insertId }; }, - /** - * μ•ˆμ „ 체크 ν•­λͺ© μˆ˜μ • (κ΄€λ¦¬μžμš©) - */ - updateSafetyCheck: async (checkId, checkData, callback) => { - try { - const db = await getDb(); - const sql = ` - UPDATE tbm_safety_checks - SET check_category = ?, - check_type = ?, - weather_condition = ?, - task_id = ?, - check_item = ?, - description = ?, - is_required = ?, - display_order = ?, - is_active = ?, - updated_at = NOW() - WHERE check_id = ? - `; - - const values = [ + updateSafetyCheck: async (checkId, checkData) => { + const db = await getDb(); + const [result] = await db.query( + `UPDATE tbm_safety_checks + SET check_category = ?, check_type = ?, weather_condition = ?, task_id = ?, + check_item = ?, description = ?, is_required = ?, display_order = ?, + is_active = ?, updated_at = NOW() + WHERE check_id = ?`, + [ checkData.check_category, checkData.check_type || 'basic', checkData.weather_condition || null, @@ -1152,29 +834,18 @@ const TbmModel = { checkData.display_order || 0, checkData.is_active !== false, checkId - ]; - - const [result] = await db.query(sql, values); - callback(null, { affectedRows: result.affectedRows }); - } catch (err) { - callback(err); - } + ] + ); + return { affectedRows: result.affectedRows }; }, - /** - * μ•ˆμ „ 체크 ν•­λͺ© μ‚­μ œ (λΉ„ν™œμ„±ν™”) - */ - deleteSafetyCheck: async (checkId, callback) => { - try { - const db = await getDb(); - // μ‹€μ œ μ‚­μ œ λŒ€μ‹  λΉ„ν™œμ„±ν™” - const sql = `UPDATE tbm_safety_checks SET is_active = 0 WHERE check_id = ?`; - - const [result] = await db.query(sql, [checkId]); - callback(null, { affectedRows: result.affectedRows }); - } catch (err) { - callback(err); - } + deleteSafetyCheck: async (checkId) => { + const db = await getDb(); + const [result] = await db.query( + `UPDATE tbm_safety_checks SET is_active = 0 WHERE check_id = ?`, + [checkId] + ); + return { affectedRows: result.affectedRows }; } }; diff --git a/system1-factory/api/models/tbmTransferModel.js b/system1-factory/api/models/tbmTransferModel.js index 5935443..7f60bf1 100644 --- a/system1-factory/api/models/tbmTransferModel.js +++ b/system1-factory/api/models/tbmTransferModel.js @@ -6,11 +6,10 @@ const TbmTransferModel = { * μž‘μ—…μž 이동 μ‹€ν–‰ (보내기/빼였기) * νŠΈλžœμž­μ…˜: source work_hours μ—…λ°μ΄νŠΈ + dest INSERT + 둜그 INSERT */ - createTransfer: async (transferData, callback) => { - let conn; + async createTransfer(transferData) { + const db = await getDb(); + const conn = await db.getConnection(); try { - const db = await getDb(); - conn = await db.getConnection(); await conn.beginTransaction(); const { @@ -27,8 +26,7 @@ const TbmTransferModel = { if (sourceRows.length === 0) { await conn.rollback(); - conn.release(); - return callback(null, { success: false, message: '원본 μ„Έμ…˜μ—μ„œ ν•΄λ‹Ή μž‘μ—…μžλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); + return { success: false, message: '원본 μ„Έμ…˜μ—μ„œ ν•΄λ‹Ή μž‘μ—…μžλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }; } const currentHours = sourceRows[0].work_hours === null ? 8 : parseFloat(sourceRows[0].work_hours); @@ -36,8 +34,7 @@ const TbmTransferModel = { if (newSourceHours < 0) { await conn.rollback(); - conn.release(); - return callback(null, { success: false, message: '이동 μ‹œκ°„μ΄ ν˜„μž¬ λ°°μ • μ‹œκ°„λ³΄λ‹€ ν½λ‹ˆλ‹€.' }); + return { success: false, message: '이동 μ‹œκ°„μ΄ ν˜„μž¬ λ°°μ • μ‹œκ°„λ³΄λ‹€ ν½λ‹ˆλ‹€.' }; } await conn.query( @@ -52,14 +49,12 @@ const TbmTransferModel = { ); if (destRows.length > 0) { - // 이미 있으면 μ‹œκ°„λ§Œ μΆ”κ°€ const existingHours = destRows[0].work_hours === null ? 8 : parseFloat(destRows[0].work_hours); await conn.query( 'UPDATE tbm_team_assignments SET work_hours = ? WHERE session_id = ? AND worker_id = ?', [existingHours + parseFloat(hours), dest_session_id, worker_id] ); } else { - // μƒˆλ‘œ INSERT await conn.query( `INSERT INTO tbm_team_assignments (session_id, worker_id, work_hours, project_id, work_type_id, task_id, workplace_category_id, workplace_id, is_present) @@ -89,7 +84,6 @@ const TbmTransferModel = { const totalHours = totalRows[0] ? parseFloat(totalRows[0].total_hours) : 0; await conn.commit(); - conn.release(); const result = { success: true, @@ -101,24 +95,22 @@ const TbmTransferModel = { result.warning = `ν•΄λ‹Ή μž‘μ—…μžμ˜ 당일 합계가 ${totalHours}hμž…λ‹ˆλ‹€ (8h 초과).`; } - callback(null, result); + return result; } catch (err) { - if (conn) { - try { await conn.rollback(); } catch (e) {} - conn.release(); - } - callback(err); + try { await conn.rollback(); } catch (e) {} + throw err; + } finally { + conn.release(); } }, /** * 이동 μ·¨μ†Œ (원볡) */ - cancelTransfer: async (transferId, callback) => { - let conn; + async cancelTransfer(transferId) { + const db = await getDb(); + const conn = await db.getConnection(); try { - const db = await getDb(); - conn = await db.getConnection(); await conn.beginTransaction(); // 1. 이동 둜그 쑰회 @@ -129,8 +121,7 @@ const TbmTransferModel = { if (transfers.length === 0) { await conn.rollback(); - conn.release(); - return callback(null, { success: false, message: '이동 기둝을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }); + return { success: false, message: '이동 기둝을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.' }; } const t = transfers[0]; @@ -146,7 +137,6 @@ const TbmTransferModel = { const newDestHours = destHours - parseFloat(t.hours); if (newDestHours <= 0) { - // μ‚­μ œ await conn.query( 'DELETE FROM tbm_team_assignments WHERE session_id = ? AND worker_id = ?', [t.dest_session_id, t.worker_id] @@ -168,7 +158,6 @@ const TbmTransferModel = { if (sourceRows.length > 0) { const sourceHours = sourceRows[0].work_hours === null ? 8 : parseFloat(sourceRows[0].work_hours); const restoredHours = sourceHours + parseFloat(t.hours); - // 8이면 NULL둜 볡원 (쒅일) await conn.query( 'UPDATE tbm_team_assignments SET work_hours = ? WHERE session_id = ? AND worker_id = ?', [restoredHours >= 8 ? null : restoredHours, t.source_session_id, t.worker_id] @@ -179,115 +168,103 @@ const TbmTransferModel = { await conn.query('DELETE FROM tbm_transfers WHERE transfer_id = ?', [transferId]); await conn.commit(); - conn.release(); - - callback(null, { success: true }); + return { success: true }; } catch (err) { - if (conn) { - try { await conn.rollback(); } catch (e) {} - conn.release(); - } - callback(err); + try { await conn.rollback(); } catch (e) {} + throw err; + } finally { + conn.release(); } }, /** * 당일 이동 λ‚΄μ—­ 쑰회 */ - getTransfersByDate: async (date, callback) => { - try { - const db = await getDb(); - const sql = ` - SELECT - t.*, - w.worker_name, - w.job_type, - sl.worker_name as source_leader_name, - dl.worker_name as dest_leader_name, - u.name as initiated_by_name - FROM tbm_transfers t - INNER JOIN workers w ON t.worker_id = w.worker_id - LEFT JOIN tbm_sessions ss ON t.source_session_id = ss.session_id - LEFT JOIN workers sl ON ss.leader_id = sl.worker_id - LEFT JOIN tbm_sessions ds ON t.dest_session_id = ds.session_id - LEFT JOIN workers dl ON ds.leader_id = dl.worker_id - LEFT JOIN sso_users u ON t.initiated_by = u.user_id - WHERE t.transfer_date = ? - ORDER BY t.created_at DESC - `; - const [rows] = await db.query(sql, [date]); - callback(null, rows); - } catch (err) { - callback(err); - } + async getTransfersByDate(date) { + const db = await getDb(); + const sql = ` + SELECT + t.*, + w.worker_name, + w.job_type, + sl.worker_name as source_leader_name, + dl.worker_name as dest_leader_name, + u.name as initiated_by_name + FROM tbm_transfers t + INNER JOIN workers w ON t.worker_id = w.worker_id + LEFT JOIN tbm_sessions ss ON t.source_session_id = ss.session_id + LEFT JOIN workers sl ON ss.leader_id = sl.worker_id + LEFT JOIN tbm_sessions ds ON t.dest_session_id = ds.session_id + LEFT JOIN workers dl ON ds.leader_id = dl.worker_id + LEFT JOIN sso_users u ON t.initiated_by = u.user_id + WHERE t.transfer_date = ? + ORDER BY t.created_at DESC + `; + const [rows] = await db.query(sql, [date]); + return rows; }, /** * 당일 μ „ μž‘μ—…μž λ°°μ • ν˜„ν™© 쑰회 */ - getWorkerAssignmentsByDate: async (date, callback) => { - try { - const db = await getDb(); + async getWorkerAssignmentsByDate(date) { + const db = await getDb(); - // 1. ν•΄λ‹Ή λ‚ μ§œμ˜ λͺ¨λ“  λ°°μ • κ°€μ Έμ˜€κΈ° - const [assignments] = await db.query(` - SELECT - ta.worker_id, - ta.session_id, - ta.work_hours, - w.worker_name, - w.job_type, - s.leader_id, - lw.worker_name as leader_name, - s.status as session_status - FROM tbm_team_assignments ta - INNER JOIN tbm_sessions s ON ta.session_id = s.session_id - INNER JOIN workers w ON ta.worker_id = w.worker_id - LEFT JOIN workers lw ON s.leader_id = lw.worker_id - WHERE s.session_date = ? - ORDER BY w.worker_name - `, [date]); + // 1. ν•΄λ‹Ή λ‚ μ§œμ˜ λͺ¨λ“  λ°°μ • κ°€μ Έμ˜€κΈ° + const [assignments] = await db.query(` + SELECT + ta.worker_id, + ta.session_id, + ta.work_hours, + w.worker_name, + w.job_type, + s.leader_id, + lw.worker_name as leader_name, + s.status as session_status + FROM tbm_team_assignments ta + INNER JOIN tbm_sessions s ON ta.session_id = s.session_id + INNER JOIN workers w ON ta.worker_id = w.worker_id + LEFT JOIN workers lw ON s.leader_id = lw.worker_id + WHERE s.session_date = ? + ORDER BY w.worker_name + `, [date]); - // 2. λͺ¨λ“  μž‘μ—…μž κ°€μ Έμ˜€κΈ° (λ°°μ • μ•ˆ 된 μ‚¬λžŒλ„ 포함) - const [allWorkers] = await db.query( - "SELECT worker_id, worker_name, job_type FROM workers WHERE status = 'active' AND department = '생산' ORDER BY worker_name" - ); + // 2. λͺ¨λ“  μž‘μ—…μž κ°€μ Έμ˜€κΈ° (λ°°μ • μ•ˆ 된 μ‚¬λžŒλ„ 포함) + const [allWorkers] = await db.query( + "SELECT worker_id, worker_name, job_type FROM workers WHERE status = 'active' AND department = '생산' ORDER BY worker_name" + ); - // 3. μž‘μ—…μžλ³„ λ°°μ • ν˜„ν™© ꡬ성 - const workerMap = {}; - allWorkers.forEach(w => { - workerMap[w.worker_id] = { - worker_id: w.worker_id, - worker_name: w.worker_name, - job_type: w.job_type, - sessions: [], - total_hours: 0, - available: true - }; - }); + // 3. μž‘μ—…μžλ³„ λ°°μ • ν˜„ν™© ꡬ성 + const workerMap = {}; + allWorkers.forEach(w => { + workerMap[w.worker_id] = { + worker_id: w.worker_id, + worker_name: w.worker_name, + job_type: w.job_type, + sessions: [], + total_hours: 0, + available: true + }; + }); - assignments.forEach(a => { - const hours = a.work_hours === null ? 8 : parseFloat(a.work_hours); - if (workerMap[a.worker_id]) { - workerMap[a.worker_id].sessions.push({ - session_id: a.session_id, - leader_name: a.leader_name, - work_hours: hours, - session_status: a.session_status - }); - workerMap[a.worker_id].total_hours += hours; - } - }); + assignments.forEach(a => { + const hours = a.work_hours === null ? 8 : parseFloat(a.work_hours); + if (workerMap[a.worker_id]) { + workerMap[a.worker_id].sessions.push({ + session_id: a.session_id, + leader_name: a.leader_name, + work_hours: hours, + session_status: a.session_status + }); + workerMap[a.worker_id].total_hours += hours; + } + }); - // available νŒλ‹¨ - Object.values(workerMap).forEach(w => { - w.available = w.total_hours < 8; - }); + Object.values(workerMap).forEach(w => { + w.available = w.total_hours < 8; + }); - callback(null, Object.values(workerMap)); - } catch (err) { - callback(err); - } + return Object.values(workerMap); } }; diff --git a/system1-factory/api/models/vacationBalanceModel.js b/system1-factory/api/models/vacationBalanceModel.js index d342417..62096d4 100644 --- a/system1-factory/api/models/vacationBalanceModel.js +++ b/system1-factory/api/models/vacationBalanceModel.js @@ -9,251 +9,190 @@ const vacationBalanceModel = { /** * νŠΉμ • μž‘μ—…μžμ˜ λͺ¨λ“  νœ΄κ°€ μž”μ•‘ 쑰회 (νŠΉμ • 연도) */ - async getByWorkerAndYear(workerId, year, callback) { - try { - const db = await getDb(); - const query = ` - SELECT - vbd.*, - vt.type_name, - vt.type_code, - vt.priority, - vt.is_special - FROM vacation_balance_details vbd - INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id - WHERE vbd.worker_id = ? AND vbd.year = ? - ORDER BY vt.priority ASC, vt.type_name ASC - `; - const [rows] = await db.query(query, [workerId, year]); - callback(null, rows); - } catch (error) { - callback(error); - } + async getByWorkerAndYear(workerId, year) { + const db = await getDb(); + const [rows] = await db.query(` + SELECT + vbd.*, + vt.type_name, + vt.type_code, + vt.priority, + vt.is_special + FROM vacation_balance_details vbd + INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id + WHERE vbd.worker_id = ? AND vbd.year = ? + ORDER BY vt.priority ASC, vt.type_name ASC + `, [workerId, year]); + return rows; }, /** * νŠΉμ • μž‘μ—…μžμ˜ νŠΉμ • νœ΄κ°€ μœ ν˜• μž”μ•‘ 쑰회 */ - async getByWorkerTypeYear(workerId, vacationTypeId, year, callback) { - try { - const db = await getDb(); - const query = ` - SELECT - vbd.*, - vt.type_name, - vt.type_code - FROM vacation_balance_details vbd - INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id - WHERE vbd.worker_id = ? - AND vbd.vacation_type_id = ? - AND vbd.year = ? - `; - const [rows] = await db.query(query, [workerId, vacationTypeId, year]); - callback(null, rows); - } catch (error) { - callback(error); - } + async getByWorkerTypeYear(workerId, vacationTypeId, year) { + const db = await getDb(); + const [rows] = await db.query(` + SELECT + vbd.*, + vt.type_name, + vt.type_code + FROM vacation_balance_details vbd + INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id + WHERE vbd.worker_id = ? + AND vbd.vacation_type_id = ? + AND vbd.year = ? + `, [workerId, vacationTypeId, year]); + return rows; }, /** * λͺ¨λ“  μž‘μ—…μžμ˜ νœ΄κ°€ μž”μ•‘ 쑰회 (νŠΉμ • 연도) - * - μ—°κ°„ μ—°μ°¨ ν˜„ν™© 차트용 */ - async getAllByYear(year, callback) { - try { - const db = await getDb(); - const query = ` - SELECT - vbd.*, - w.worker_name, - w.employment_status, - vt.type_name, - vt.type_code, - vt.priority - FROM vacation_balance_details vbd - INNER JOIN workers w ON vbd.worker_id = w.worker_id - INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id - WHERE vbd.year = ? - AND w.employment_status = 'employed' - ORDER BY w.worker_name ASC, vt.priority ASC - `; - const [rows] = await db.query(query, [year]); - callback(null, rows); - } catch (error) { - callback(error); - } + async getAllByYear(year) { + const db = await getDb(); + const [rows] = await db.query(` + SELECT + vbd.*, + w.worker_name, + w.employment_status, + vt.type_name, + vt.type_code, + vt.priority + FROM vacation_balance_details vbd + INNER JOIN workers w ON vbd.worker_id = w.worker_id + INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id + WHERE vbd.year = ? + AND w.employment_status = 'employed' + ORDER BY w.worker_name ASC, vt.priority ASC + `, [year]); + return rows; }, /** * νœ΄κ°€ μž”μ•‘ 생성 */ - async create(balanceData, callback) { - try { - const db = await getDb(); - const query = `INSERT INTO vacation_balance_details SET ?`; - const [rows] = await db.query(query, balanceData); - callback(null, rows); - } catch (error) { - callback(error); - } + async create(balanceData) { + const db = await getDb(); + const [result] = await db.query(`INSERT INTO vacation_balance_details SET ?`, balanceData); + return result; }, /** * νœ΄κ°€ μž”μ•‘ μˆ˜μ • */ - async update(id, updateData, callback) { - try { - const db = await getDb(); - const query = `UPDATE vacation_balance_details SET ? WHERE id = ?`; - const [rows] = await db.query(query, [updateData, id]); - callback(null, rows); - } catch (error) { - callback(error); - } + async update(id, updateData) { + const db = await getDb(); + const [result] = await db.query(`UPDATE vacation_balance_details SET ? WHERE id = ?`, [updateData, id]); + return result; }, /** * νœ΄κ°€ μž”μ•‘ μ‚­μ œ */ - async delete(id, callback) { - try { - const db = await getDb(); - const query = `DELETE FROM vacation_balance_details WHERE id = ?`; - const [rows] = await db.query(query, [id]); - callback(null, rows); - } catch (error) { - callback(error); - } + async delete(id) { + const db = await getDb(); + const [result] = await db.query(`DELETE FROM vacation_balance_details WHERE id = ?`, [id]); + return result; }, /** * μž‘μ—…μžμ˜ νœ΄κ°€ μ‚¬μš© 일수 μ—…λ°μ΄νŠΈ (차감) - * - νœ΄κ°€ μ‹ μ²­ 승인 μ‹œ 호좜 */ - async deductDays(workerId, vacationTypeId, year, daysToDeduct, callback) { - try { - const db = await getDb(); - const query = ` - UPDATE vacation_balance_details - SET used_days = used_days + ?, - updated_at = NOW() - WHERE worker_id = ? - AND vacation_type_id = ? - AND year = ? - `; - const [rows] = await db.query(query, [daysToDeduct, workerId, vacationTypeId, year]); - callback(null, rows); - } catch (error) { - callback(error); - } + async deductDays(workerId, vacationTypeId, year, daysToDeduct) { + const db = await getDb(); + const [result] = await db.query(` + UPDATE vacation_balance_details + SET used_days = used_days + ?, + updated_at = NOW() + WHERE worker_id = ? + AND vacation_type_id = ? + AND year = ? + `, [daysToDeduct, workerId, vacationTypeId, year]); + return result; }, /** * μž‘μ—…μžμ˜ νœ΄κ°€ μ‚¬μš© 일수 볡ꡬ (μ·¨μ†Œ) - * - νœ΄κ°€ μ‹ μ²­ μ·¨μ†Œ/κ±°λΆ€ μ‹œ 호좜 */ - async restoreDays(workerId, vacationTypeId, year, daysToRestore, callback) { - try { - const db = await getDb(); - const query = ` - UPDATE vacation_balance_details - SET used_days = GREATEST(0, used_days - ?), - updated_at = NOW() - WHERE worker_id = ? - AND vacation_type_id = ? - AND year = ? - `; - const [rows] = await db.query(query, [daysToRestore, workerId, vacationTypeId, year]); - callback(null, rows); - } catch (error) { - callback(error); - } + async restoreDays(workerId, vacationTypeId, year, daysToRestore) { + const db = await getDb(); + const [result] = await db.query(` + UPDATE vacation_balance_details + SET used_days = GREATEST(0, used_days - ?), + updated_at = NOW() + WHERE worker_id = ? + AND vacation_type_id = ? + AND year = ? + `, [daysToRestore, workerId, vacationTypeId, year]); + return result; }, /** * νŠΉμ • μž‘μ—…μžμ˜ μ‚¬μš© κ°€λŠ₯ν•œ νœ΄κ°€ 일수 확인 - * - μš°μ„ μˆœμœ„κ°€ 높은 μˆœμ„œλŒ€λ‘œ 차감 κ°€λŠ₯ μ—¬λΆ€ 확인 */ - async getAvailableVacationDays(workerId, year, callback) { - try { - const db = await getDb(); - const query = ` - SELECT - vbd.id, - vbd.vacation_type_id, - vt.type_name, - vt.type_code, - vt.priority, - vbd.total_days, - vbd.used_days, - vbd.remaining_days - FROM vacation_balance_details vbd - INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id - WHERE vbd.worker_id = ? - AND vbd.year = ? - AND vbd.remaining_days > 0 - ORDER BY vt.priority ASC - `; - const [rows] = await db.query(query, [workerId, year]); - callback(null, rows); - } catch (error) { - callback(error); - } + async getAvailableVacationDays(workerId, year) { + const db = await getDb(); + const [rows] = await db.query(` + SELECT + vbd.id, + vbd.vacation_type_id, + vt.type_name, + vt.type_code, + vt.priority, + vbd.total_days, + vbd.used_days, + vbd.remaining_days + FROM vacation_balance_details vbd + INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id + WHERE vbd.worker_id = ? + AND vbd.year = ? + AND vbd.remaining_days > 0 + ORDER BY vt.priority ASC + `, [workerId, year]); + return rows; }, /** * μž‘μ—…μžλ³„ νœ΄κ°€ μž”μ•‘ 일괄 생성 (연도별) - * - λ§€λ…„ 초 λ˜λŠ” μž…μ‚¬ μ‹œ μ‚¬μš© */ - async bulkCreate(balances, callback) { - try { - const db = await getDb(); - - if (!balances || balances.length === 0) { - return callback(new Error('생성할 νœ΄κ°€ μž”μ•‘ 데이터가 μ—†μŠ΅λ‹ˆλ‹€')); - } - - const query = `INSERT INTO vacation_balance_details - (worker_id, vacation_type_id, year, total_days, used_days, notes, created_by) - VALUES ?`; - - const values = balances.map(b => [ - b.worker_id, - b.vacation_type_id, - b.year, - b.total_days || 0, - b.used_days || 0, - b.notes || null, - b.created_by - ]); - - const [rows] = await db.query(query, [values]); - callback(null, rows); - } catch (error) { - callback(error); + async bulkCreate(balances) { + if (!balances || balances.length === 0) { + throw new Error('생성할 νœ΄κ°€ μž”μ•‘ 데이터가 μ—†μŠ΅λ‹ˆλ‹€'); } + + const db = await getDb(); + const query = `INSERT INTO vacation_balance_details + (worker_id, vacation_type_id, year, total_days, used_days, notes, created_by) + VALUES ?`; + + const values = balances.map(b => [ + b.worker_id, + b.vacation_type_id, + b.year, + b.total_days || 0, + b.used_days || 0, + b.notes || null, + b.created_by + ]); + + const [result] = await db.query(query, [values]); + return result; }, /** * κ·Όμ†λ…„μˆ˜ 기반 μ—°μ°¨ 일수 계산 (ν•œκ΅­ κ·Όλ‘œκΈ°μ€€λ²•) - * @param {Date} hireDate - μž…μ‚¬μΌ - * @param {number} targetYear - λŒ€μƒ 연도 - * @returns {number} - 뢀여받을 μ—°μ°¨ 일수 */ calculateAnnualLeaveDays(hireDate, targetYear) { const hire = new Date(hireDate); const targetDate = new Date(targetYear, 0, 1); - // 근속 μ›”μˆ˜ 계산 const monthsDiff = (targetDate.getFullYear() - hire.getFullYear()) * 12 + (targetDate.getMonth() - hire.getMonth()); - // 1λ…„ 미만: μ›” 1일 if (monthsDiff < 12) { return Math.floor(monthsDiff); } - // 1λ…„ 이상: 15일 κΈ°λ³Έ + 2λ…„λ§ˆλ‹€ 1일 μΆ”κ°€ (μ΅œλŒ€ 25일) const yearsWorked = Math.floor(monthsDiff / 12); const additionalDays = Math.floor((yearsWorked - 1) / 2); @@ -261,17 +200,11 @@ const vacationBalanceModel = { }, /** - * νœ΄κ°€ μ‚¬μš© μ‹œ μš°μ„ μˆœμœ„μ— 따라 μž”μ•‘μ—μ„œ 차감 (Promise 버전) - * - 일일 κ·Όνƒœ 기둝 μ €μž₯ μ‹œ 호좜 - * @param {number} workerId - μž‘μ—…μž ID - * @param {number} year - 연도 - * @param {number} daysToDeduct - 차감할 일수 (1, 0.5, 0.25) - * @returns {Promise} - 차감 κ²°κ³Ό + * νœ΄κ°€ μ‚¬μš© μ‹œ μš°μ„ μˆœμœ„μ— 따라 μž”μ•‘μ—μ„œ 차감 */ async deductByPriority(workerId, year, daysToDeduct) { const db = await getDb(); - // μš°μ„ μˆœμœ„μˆœμœΌλ‘œ μž”μ—¬ μΌμˆ˜κ°€ μžˆλŠ” μž”μ•‘ 쑰회 const [balances] = await db.query(` SELECT vbd.id, vbd.vacation_type_id, vbd.total_days, vbd.used_days, (vbd.total_days - vbd.used_days) as remaining_days, @@ -284,7 +217,6 @@ const vacationBalanceModel = { `, [workerId, year]); if (balances.length === 0) { - // μž”μ•‘μ΄ 없어도 일단 기둝은 μ €μž₯ (경고만) console.warn(`[VacationBalance] μž‘μ—…μž ${workerId}의 ${year}λ…„ νœ΄κ°€ μž”μ•‘μ΄ μ—†μŠ΅λ‹ˆλ‹€`); return { success: false, message: 'νœ΄κ°€ μž”μ•‘μ΄ μ—†μŠ΅λ‹ˆλ‹€', deducted: 0 }; } @@ -321,16 +253,11 @@ const vacationBalanceModel = { }, /** - * νœ΄κ°€ μ·¨μ†Œ μ‹œ μš°μ„ μˆœμœ„ μ—­μˆœμœΌλ‘œ 볡ꡬ (Promise 버전) - * @param {number} workerId - μž‘μ—…μž ID - * @param {number} year - 연도 - * @param {number} daysToRestore - 볡ꡬ할 일수 - * @returns {Promise} - 볡ꡬ κ²°κ³Ό + * νœ΄κ°€ μ·¨μ†Œ μ‹œ μš°μ„ μˆœμœ„ μ—­μˆœμœΌλ‘œ 볡ꡬ */ async restoreByPriority(workerId, year, daysToRestore) { const db = await getDb(); - // μš°μ„ μˆœμœ„ μ—­μˆœμœΌλ‘œ μ‚¬μš© μΌμˆ˜κ°€ μžˆλŠ” μž”μ•‘ 쑰회 (λ‚˜μ€‘μ— 차감된 것뢀터 볡ꡬ) const [balances] = await db.query(` SELECT vbd.id, vbd.vacation_type_id, vbd.used_days, vt.type_code, vt.type_name, vt.priority @@ -375,25 +302,20 @@ const vacationBalanceModel = { /** * νŠΉμ • ID둜 νœ΄κ°€ μž”μ•‘ 쑰회 */ - async getById(id, callback) { - try { - const db = await getDb(); - const query = ` - SELECT - vbd.*, - w.worker_name, - vt.type_name, - vt.type_code - FROM vacation_balance_details vbd - INNER JOIN workers w ON vbd.worker_id = w.worker_id - INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id - WHERE vbd.id = ? - `; - const [rows] = await db.query(query, [id]); - callback(null, rows); - } catch (error) { - callback(error); - } + async getById(id) { + const db = await getDb(); + const [rows] = await db.query(` + SELECT + vbd.*, + w.worker_name, + vt.type_name, + vt.type_code + FROM vacation_balance_details vbd + INNER JOIN workers w ON vbd.worker_id = w.worker_id + INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id + WHERE vbd.id = ? + `, [id]); + return rows; } }; diff --git a/system1-factory/api/models/vacationTypeModel.js b/system1-factory/api/models/vacationTypeModel.js index 9ecce63..fc3565c 100644 --- a/system1-factory/api/models/vacationTypeModel.js +++ b/system1-factory/api/models/vacationTypeModel.js @@ -9,123 +9,97 @@ const vacationTypeModel = { /** * λͺ¨λ“  ν™œμ„± νœ΄κ°€ μœ ν˜• 쑰회 (μš°μ„ μˆœμœ„ μˆœμ„œλŒ€λ‘œ) */ - async getAll(callback) { - try { - const db = await getDb(); - const query = ` - SELECT * - FROM vacation_types - WHERE is_active = 1 - ORDER BY priority ASC, id ASC - `; - const [rows] = await db.query(query); - callback(null, rows); - } catch (error) { - callback(error); - } + async getAll() { + const db = await getDb(); + const [rows] = await db.query(` + SELECT * + FROM vacation_types + WHERE is_active = 1 + ORDER BY priority ASC, id ASC + `); + return rows; }, /** * μ‹œμŠ€ν…œ κΈ°λ³Έ νœ΄κ°€ μœ ν˜•λ§Œ 쑰회 */ - async getSystemTypes(callback) { - try { - const db = await getDb(); - const query = ` - SELECT * - FROM vacation_types - WHERE is_system = 1 AND is_active = 1 - ORDER BY priority ASC - `; - const [rows] = await db.query(query); - callback(null, rows); - } catch (error) { - callback(error); - } + async getSystemTypes() { + const db = await getDb(); + const [rows] = await db.query(` + SELECT * + FROM vacation_types + WHERE is_system = 1 AND is_active = 1 + ORDER BY priority ASC + `); + return rows; + }, + + /** + * νŠΉλ³„ νœ΄κ°€ μœ ν˜•λ§Œ 쑰회 + */ + async getSpecialTypes() { + const db = await getDb(); + const [rows] = await db.query(` + SELECT * + FROM vacation_types + WHERE is_special = 1 AND is_active = 1 + ORDER BY priority ASC + `); + return rows; }, /** * νŠΉμ • ID둜 νœ΄κ°€ μœ ν˜• 쑰회 */ - async getById(id, callback) { - try { - const db = await getDb(); - const query = `SELECT * FROM vacation_types WHERE id = ?`; - const [rows] = await db.query(query, [id]); - callback(null, rows); - } catch (error) { - callback(error); - } + async getById(id) { + const db = await getDb(); + const [rows] = await db.query(`SELECT * FROM vacation_types WHERE id = ?`, [id]); + return rows; }, /** * νœ΄κ°€ μœ ν˜• μ½”λ“œλ‘œ 쑰회 */ - async getByCode(code, callback) { - try { - const db = await getDb(); - const query = `SELECT * FROM vacation_types WHERE type_code = ?`; - const [rows] = await db.query(query, [code]); - callback(null, rows); - } catch (error) { - callback(error); - } + async getByCode(code) { + const db = await getDb(); + const [rows] = await db.query(`SELECT * FROM vacation_types WHERE type_code = ?`, [code]); + return rows; }, /** * νœ΄κ°€ μœ ν˜• 생성 */ - async create(typeData, callback) { - try { - const db = await getDb(); - const query = `INSERT INTO vacation_types SET ?`; - const [result] = await db.query(query, typeData); - callback(null, result); - } catch (error) { - callback(error); - } + async create(typeData) { + const db = await getDb(); + const [result] = await db.query(`INSERT INTO vacation_types SET ?`, typeData); + return result; }, /** * νœ΄κ°€ μœ ν˜• μˆ˜μ • */ - async update(id, updateData, callback) { - try { - const db = await getDb(); - const query = `UPDATE vacation_types SET ? WHERE id = ?`; - const [result] = await db.query(query, [updateData, id]); - callback(null, result); - } catch (error) { - callback(error); - } + async update(id, updateData) { + const db = await getDb(); + const [result] = await db.query(`UPDATE vacation_types SET ? WHERE id = ?`, [updateData, id]); + return result; }, /** * νœ΄κ°€ μœ ν˜• μ‚­μ œ (논리적 μ‚­μ œ - is_active = 0) */ - async delete(id, callback) { - try { - const db = await getDb(); - const query = `UPDATE vacation_types SET is_active = 0, updated_at = NOW() WHERE id = ?`; - const [result] = await db.query(query, [id]); - callback(null, result); - } catch (error) { - callback(error); - } + async delete(id) { + const db = await getDb(); + const [result] = await db.query(`UPDATE vacation_types SET is_active = 0, updated_at = NOW() WHERE id = ?`, [id]); + return result; }, /** * μš°μ„ μˆœμœ„ μ—…λ°μ΄νŠΈ */ - async updatePriority(id, priority, callback) { - try { - const db = await getDb(); - const query = `UPDATE vacation_types SET priority = ?, updated_at = NOW() WHERE id = ?`; - const [result] = await db.query(query, [priority, id]); - callback(null, result); - } catch (error) { - callback(error); - } + async updatePriority(id, priority) { + const db = await getDb(); + const [result] = await db.query(`UPDATE vacation_types SET priority = ?, updated_at = NOW() WHERE id = ?`, [priority, id]); + return result; } }; diff --git a/system1-factory/api/models/visitRequestModel.js b/system1-factory/api/models/visitRequestModel.js index 87bb6c9..ba71e85 100644 --- a/system1-factory/api/models/visitRequestModel.js +++ b/system1-factory/api/models/visitRequestModel.js @@ -2,484 +2,324 @@ const { getDb } = require('../dbPool'); // ==================== μΆœμž… μ‹ μ²­ 관리 ==================== -/** - * μΆœμž… μ‹ μ²­ 생성 - */ -const createVisitRequest = async (requestData, callback) => { - try { - const db = await getDb(); - const { - requester_id, - visitor_company, - visitor_count = 1, - category_id, - workplace_id, - visit_date, - visit_time, - purpose_id, - notes = null - } = requestData; +const createVisitRequest = async (requestData) => { + const db = await getDb(); + const { + requester_id, + visitor_company, + visitor_count = 1, + category_id, + workplace_id, + visit_date, + visit_time, + purpose_id, + notes = null + } = requestData; - const [result] = await db.query( - `INSERT INTO workplace_visit_requests - (requester_id, visitor_company, visitor_count, category_id, workplace_id, - visit_date, visit_time, purpose_id, notes) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, - [requester_id, visitor_company, visitor_count, category_id, workplace_id, - visit_date, visit_time, purpose_id, notes] - ); + const [result] = await db.query( + `INSERT INTO workplace_visit_requests + (requester_id, visitor_company, visitor_count, category_id, workplace_id, + visit_date, visit_time, purpose_id, notes) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [requester_id, visitor_company, visitor_count, category_id, workplace_id, + visit_date, visit_time, purpose_id, notes] + ); - callback(null, result.insertId); - } catch (err) { - callback(err); - } + return result.insertId; }; -/** - * μΆœμž… μ‹ μ²­ λͺ©λ‘ 쑰회 (ν•„ν„° μ˜΅μ…˜ 포함) - */ -const getAllVisitRequests = async (filters = {}, callback) => { - try { - const db = await getDb(); - let query = ` - SELECT - vr.request_id, vr.requester_id, vr.visitor_company, vr.visitor_count, - vr.category_id, vr.workplace_id, vr.visit_date, vr.visit_time, - vr.purpose_id, vr.notes, vr.status, - vr.approved_by, vr.approved_at, vr.rejection_reason, - vr.created_at, vr.updated_at, - u.username as requester_name, u.name as requester_full_name, - wc.category_name, w.workplace_name, - vpt.purpose_name, - approver.username as approver_name - FROM workplace_visit_requests vr - INNER JOIN users u ON vr.requester_id = u.user_id - INNER JOIN workplace_categories wc ON vr.category_id = wc.category_id - INNER JOIN workplaces w ON vr.workplace_id = w.workplace_id - INNER JOIN visit_purpose_types vpt ON vr.purpose_id = vpt.purpose_id - LEFT JOIN users approver ON vr.approved_by = approver.user_id - WHERE 1=1 - `; +const getAllVisitRequests = async (filters = {}) => { + const db = await getDb(); + let query = ` + SELECT + vr.request_id, vr.requester_id, vr.visitor_company, vr.visitor_count, + vr.category_id, vr.workplace_id, vr.visit_date, vr.visit_time, + vr.purpose_id, vr.notes, vr.status, + vr.approved_by, vr.approved_at, vr.rejection_reason, + vr.created_at, vr.updated_at, + u.username as requester_name, u.name as requester_full_name, + wc.category_name, w.workplace_name, + vpt.purpose_name, + approver.username as approver_name + FROM workplace_visit_requests vr + INNER JOIN users u ON vr.requester_id = u.user_id + INNER JOIN workplace_categories wc ON vr.category_id = wc.category_id + INNER JOIN workplaces w ON vr.workplace_id = w.workplace_id + INNER JOIN visit_purpose_types vpt ON vr.purpose_id = vpt.purpose_id + LEFT JOIN users approver ON vr.approved_by = approver.user_id + WHERE 1=1 + `; - const params = []; + const params = []; - // ν•„ν„° 적용 - if (filters.status) { - query += ` AND vr.status = ?`; - params.push(filters.status); - } - - if (filters.visit_date) { - query += ` AND vr.visit_date = ?`; - params.push(filters.visit_date); - } - - if (filters.start_date && filters.end_date) { - query += ` AND vr.visit_date BETWEEN ? AND ?`; - params.push(filters.start_date, filters.end_date); - } - - if (filters.requester_id) { - query += ` AND vr.requester_id = ?`; - params.push(filters.requester_id); - } - - if (filters.category_id) { - query += ` AND vr.category_id = ?`; - params.push(filters.category_id); - } - - query += ` ORDER BY vr.visit_date DESC, vr.visit_time DESC, vr.created_at DESC`; - - const [rows] = await db.query(query, params); - callback(null, rows); - } catch (err) { - callback(err); + if (filters.status) { + query += ` AND vr.status = ?`; + params.push(filters.status); } + if (filters.visit_date) { + query += ` AND vr.visit_date = ?`; + params.push(filters.visit_date); + } + if (filters.start_date && filters.end_date) { + query += ` AND vr.visit_date BETWEEN ? AND ?`; + params.push(filters.start_date, filters.end_date); + } + if (filters.requester_id) { + query += ` AND vr.requester_id = ?`; + params.push(filters.requester_id); + } + if (filters.category_id) { + query += ` AND vr.category_id = ?`; + params.push(filters.category_id); + } + + query += ` ORDER BY vr.visit_date DESC, vr.visit_time DESC, vr.created_at DESC`; + + const [rows] = await db.query(query, params); + return rows; }; -/** - * μΆœμž… μ‹ μ²­ 상세 쑰회 - */ -const getVisitRequestById = async (requestId, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT - vr.request_id, vr.requester_id, vr.visitor_company, vr.visitor_count, - vr.category_id, vr.workplace_id, vr.visit_date, vr.visit_time, - vr.purpose_id, vr.notes, vr.status, - vr.approved_by, vr.approved_at, vr.rejection_reason, - vr.created_at, vr.updated_at, - u.username as requester_name, u.name as requester_full_name, - wc.category_name, w.workplace_name, - vpt.purpose_name, - approver.username as approver_name - FROM workplace_visit_requests vr - INNER JOIN users u ON vr.requester_id = u.user_id - INNER JOIN workplace_categories wc ON vr.category_id = wc.category_id - INNER JOIN workplaces w ON vr.workplace_id = w.workplace_id - INNER JOIN visit_purpose_types vpt ON vr.purpose_id = vpt.purpose_id - LEFT JOIN users approver ON vr.approved_by = approver.user_id - WHERE vr.request_id = ?`, - [requestId] - ); - - callback(null, rows[0]); - } catch (err) { - callback(err); - } +const getVisitRequestById = async (requestId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + vr.request_id, vr.requester_id, vr.visitor_company, vr.visitor_count, + vr.category_id, vr.workplace_id, vr.visit_date, vr.visit_time, + vr.purpose_id, vr.notes, vr.status, + vr.approved_by, vr.approved_at, vr.rejection_reason, + vr.created_at, vr.updated_at, + u.username as requester_name, u.name as requester_full_name, + wc.category_name, w.workplace_name, + vpt.purpose_name, + approver.username as approver_name + FROM workplace_visit_requests vr + INNER JOIN users u ON vr.requester_id = u.user_id + INNER JOIN workplace_categories wc ON vr.category_id = wc.category_id + INNER JOIN workplaces w ON vr.workplace_id = w.workplace_id + INNER JOIN visit_purpose_types vpt ON vr.purpose_id = vpt.purpose_id + LEFT JOIN users approver ON vr.approved_by = approver.user_id + WHERE vr.request_id = ?`, + [requestId] + ); + return rows[0]; }; -/** - * μΆœμž… μ‹ μ²­ μˆ˜μ • - */ -const updateVisitRequest = async (requestId, requestData, callback) => { - try { - const db = await getDb(); - const { - visitor_company, - visitor_count, - category_id, - workplace_id, - visit_date, - visit_time, - purpose_id, - notes - } = requestData; +const updateVisitRequest = async (requestId, requestData) => { + const db = await getDb(); + const { + visitor_company, visitor_count, category_id, workplace_id, + visit_date, visit_time, purpose_id, notes + } = requestData; - const [result] = await db.query( - `UPDATE workplace_visit_requests - SET visitor_company = ?, visitor_count = ?, category_id = ?, workplace_id = ?, - visit_date = ?, visit_time = ?, purpose_id = ?, notes = ?, updated_at = NOW() - WHERE request_id = ?`, - [visitor_company, visitor_count, category_id, workplace_id, - visit_date, visit_time, purpose_id, notes, requestId] - ); - - callback(null, result); - } catch (err) { - callback(err); - } + const [result] = await db.query( + `UPDATE workplace_visit_requests + SET visitor_company = ?, visitor_count = ?, category_id = ?, workplace_id = ?, + visit_date = ?, visit_time = ?, purpose_id = ?, notes = ?, updated_at = NOW() + WHERE request_id = ?`, + [visitor_company, visitor_count, category_id, workplace_id, + visit_date, visit_time, purpose_id, notes, requestId] + ); + return result; }; -/** - * μΆœμž… μ‹ μ²­ μ‚­μ œ - */ -const deleteVisitRequest = async (requestId, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `DELETE FROM workplace_visit_requests WHERE request_id = ?`, - [requestId] - ); - callback(null, result); - } catch (err) { - callback(err); - } +const deleteVisitRequest = async (requestId) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM workplace_visit_requests WHERE request_id = ?`, + [requestId] + ); + return result; }; -/** - * μΆœμž… μ‹ μ²­ 승인 - */ -const approveVisitRequest = async (requestId, approvedBy, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `UPDATE workplace_visit_requests - SET status = 'approved', approved_by = ?, approved_at = NOW(), updated_at = NOW() - WHERE request_id = ?`, - [approvedBy, requestId] - ); - - callback(null, result); - } catch (err) { - callback(err); - } +const approveVisitRequest = async (requestId, approvedBy) => { + const db = await getDb(); + const [result] = await db.query( + `UPDATE workplace_visit_requests + SET status = 'approved', approved_by = ?, approved_at = NOW(), updated_at = NOW() + WHERE request_id = ?`, + [approvedBy, requestId] + ); + return result; }; -/** - * μΆœμž… μ‹ μ²­ 반렀 - */ -const rejectVisitRequest = async (requestId, rejectionData, callback) => { - try { - const db = await getDb(); - const { approved_by, rejection_reason } = rejectionData; +const rejectVisitRequest = async (requestId, rejectionData) => { + const db = await getDb(); + const { approved_by, rejection_reason } = rejectionData; - const [result] = await db.query( - `UPDATE workplace_visit_requests - SET status = 'rejected', approved_by = ?, approved_at = NOW(), - rejection_reason = ?, updated_at = NOW() - WHERE request_id = ?`, - [approved_by, rejection_reason, requestId] - ); - - callback(null, result); - } catch (err) { - callback(err); - } + const [result] = await db.query( + `UPDATE workplace_visit_requests + SET status = 'rejected', approved_by = ?, approved_at = NOW(), + rejection_reason = ?, updated_at = NOW() + WHERE request_id = ?`, + [approved_by, rejection_reason, requestId] + ); + return result; }; -/** - * μΆœμž… μ‹ μ²­ μƒνƒœ λ³€κ²½ - */ -const updateVisitRequestStatus = async (requestId, status, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `UPDATE workplace_visit_requests - SET status = ?, updated_at = NOW() - WHERE request_id = ?`, - [status, requestId] - ); - - callback(null, result); - } catch (err) { - callback(err); - } +const updateVisitRequestStatus = async (requestId, status) => { + const db = await getDb(); + const [result] = await db.query( + `UPDATE workplace_visit_requests + SET status = ?, updated_at = NOW() + WHERE request_id = ?`, + [status, requestId] + ); + return result; }; // ==================== λ°©λ¬Έ λͺ©μ  관리 ==================== -/** - * λͺ¨λ“  λ°©λ¬Έ λͺ©μ  쑰회 - */ -const getAllVisitPurposes = async (callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT purpose_id, purpose_name, display_order, is_active, created_at - FROM visit_purpose_types - ORDER BY display_order ASC, purpose_id ASC` - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getAllVisitPurposes = async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT purpose_id, purpose_name, display_order, is_active, created_at + FROM visit_purpose_types + ORDER BY display_order ASC, purpose_id ASC` + ); + return rows; }; -/** - * ν™œμ„± λ°©λ¬Έ λͺ©μ λ§Œ 쑰회 - */ -const getActiveVisitPurposes = async (callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT purpose_id, purpose_name, display_order, is_active, created_at - FROM visit_purpose_types - WHERE is_active = TRUE - ORDER BY display_order ASC, purpose_id ASC` - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getActiveVisitPurposes = async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT purpose_id, purpose_name, display_order, is_active, created_at + FROM visit_purpose_types + WHERE is_active = TRUE + ORDER BY display_order ASC, purpose_id ASC` + ); + return rows; }; -/** - * λ°©λ¬Έ λͺ©μ  μΆ”κ°€ - */ -const createVisitPurpose = async (purposeData, callback) => { - try { - const db = await getDb(); - const { purpose_name, display_order = 0, is_active = true } = purposeData; +const createVisitPurpose = async (purposeData) => { + const db = await getDb(); + const { purpose_name, display_order = 0, is_active = true } = purposeData; - const [result] = await db.query( - `INSERT INTO visit_purpose_types (purpose_name, display_order, is_active) - VALUES (?, ?, ?)`, - [purpose_name, display_order, is_active] - ); - - callback(null, result.insertId); - } catch (err) { - callback(err); - } + const [result] = await db.query( + `INSERT INTO visit_purpose_types (purpose_name, display_order, is_active) + VALUES (?, ?, ?)`, + [purpose_name, display_order, is_active] + ); + return result.insertId; }; -/** - * λ°©λ¬Έ λͺ©μ  μˆ˜μ • - */ -const updateVisitPurpose = async (purposeId, purposeData, callback) => { - try { - const db = await getDb(); - const { purpose_name, display_order, is_active } = purposeData; +const updateVisitPurpose = async (purposeId, purposeData) => { + const db = await getDb(); + const { purpose_name, display_order, is_active } = purposeData; - const [result] = await db.query( - `UPDATE visit_purpose_types - SET purpose_name = ?, display_order = ?, is_active = ? - WHERE purpose_id = ?`, - [purpose_name, display_order, is_active, purposeId] - ); - - callback(null, result); - } catch (err) { - callback(err); - } + const [result] = await db.query( + `UPDATE visit_purpose_types + SET purpose_name = ?, display_order = ?, is_active = ? + WHERE purpose_id = ?`, + [purpose_name, display_order, is_active, purposeId] + ); + return result; }; -/** - * λ°©λ¬Έ λͺ©μ  μ‚­μ œ - */ -const deleteVisitPurpose = async (purposeId, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `DELETE FROM visit_purpose_types WHERE purpose_id = ?`, - [purposeId] - ); - callback(null, result); - } catch (err) { - callback(err); - } +const deleteVisitPurpose = async (purposeId) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM visit_purpose_types WHERE purpose_id = ?`, + [purposeId] + ); + return result; }; // ==================== μ•ˆμ „κ΅μœ‘ 기둝 관리 ==================== -/** - * μ•ˆμ „κ΅μœ‘ 기둝 생성 - */ -const createTrainingRecord = async (trainingData, callback) => { - try { - const db = await getDb(); - const { - request_id, - trainer_id, - training_date, - training_start_time, - training_end_time = null, - training_topics = null - } = trainingData; +const createTrainingRecord = async (trainingData) => { + const db = await getDb(); + const { + request_id, trainer_id, training_date, + training_start_time, training_end_time = null, training_topics = null + } = trainingData; - const [result] = await db.query( - `INSERT INTO safety_training_records - (request_id, trainer_id, training_date, training_start_time, training_end_time, training_topics) - VALUES (?, ?, ?, ?, ?, ?)`, - [request_id, trainer_id, training_date, training_start_time, training_end_time, training_topics] - ); - - callback(null, result.insertId); - } catch (err) { - callback(err); - } + const [result] = await db.query( + `INSERT INTO safety_training_records + (request_id, trainer_id, training_date, training_start_time, training_end_time, training_topics) + VALUES (?, ?, ?, ?, ?, ?)`, + [request_id, trainer_id, training_date, training_start_time, training_end_time, training_topics] + ); + return result.insertId; }; -/** - * νŠΉμ • μΆœμž… μ‹ μ²­μ˜ μ•ˆμ „κ΅μœ‘ 기둝 쑰회 - */ -const getTrainingRecordByRequestId = async (requestId, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT - str.training_id, str.request_id, str.trainer_id, str.training_date, - str.training_start_time, str.training_end_time, str.training_topics, - str.signature_data, str.completed_at, str.created_at, str.updated_at, - u.username as trainer_name, u.name as trainer_full_name - FROM safety_training_records str - INNER JOIN users u ON str.trainer_id = u.user_id - WHERE str.request_id = ?`, - [requestId] - ); - - callback(null, rows[0]); - } catch (err) { - callback(err); - } +const getTrainingRecordByRequestId = async (requestId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + str.training_id, str.request_id, str.trainer_id, str.training_date, + str.training_start_time, str.training_end_time, str.training_topics, + str.signature_data, str.completed_at, str.created_at, str.updated_at, + u.username as trainer_name, u.name as trainer_full_name + FROM safety_training_records str + INNER JOIN users u ON str.trainer_id = u.user_id + WHERE str.request_id = ?`, + [requestId] + ); + return rows[0]; }; -/** - * μ•ˆμ „κ΅μœ‘ 기둝 μˆ˜μ • - */ -const updateTrainingRecord = async (trainingId, trainingData, callback) => { - try { - const db = await getDb(); - const { - training_date, - training_start_time, - training_end_time, - training_topics - } = trainingData; +const updateTrainingRecord = async (trainingId, trainingData) => { + const db = await getDb(); + const { training_date, training_start_time, training_end_time, training_topics } = trainingData; - const [result] = await db.query( - `UPDATE safety_training_records - SET training_date = ?, training_start_time = ?, training_end_time = ?, - training_topics = ?, updated_at = NOW() - WHERE training_id = ?`, - [training_date, training_start_time, training_end_time, training_topics, trainingId] - ); - - callback(null, result); - } catch (err) { - callback(err); - } + const [result] = await db.query( + `UPDATE safety_training_records + SET training_date = ?, training_start_time = ?, training_end_time = ?, + training_topics = ?, updated_at = NOW() + WHERE training_id = ?`, + [training_date, training_start_time, training_end_time, training_topics, trainingId] + ); + return result; }; -/** - * μ•ˆμ „κ΅μœ‘ μ™„λ£Œ (μ„œλͺ… 포함) - */ -const completeTraining = async (trainingId, signatureData, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `UPDATE safety_training_records - SET signature_data = ?, completed_at = NOW(), updated_at = NOW() - WHERE training_id = ?`, - [signatureData, trainingId] - ); - - callback(null, result); - } catch (err) { - callback(err); - } +const completeTraining = async (trainingId, signatureData) => { + const db = await getDb(); + const [result] = await db.query( + `UPDATE safety_training_records + SET signature_data = ?, completed_at = NOW(), updated_at = NOW() + WHERE training_id = ?`, + [signatureData, trainingId] + ); + return result; }; -/** - * μ•ˆμ „κ΅μœ‘ λͺ©λ‘ 쑰회 (λ‚ μ§œλ³„ ν•„ν„°) - */ -const getTrainingRecords = async (filters = {}, callback) => { - try { - const db = await getDb(); - let query = ` - SELECT - str.training_id, str.request_id, str.trainer_id, str.training_date, - str.training_start_time, str.training_end_time, str.training_topics, - str.completed_at, str.created_at, str.updated_at, - u.username as trainer_name, u.name as trainer_full_name, - vr.visitor_company, vr.visitor_count, vr.visit_date - FROM safety_training_records str - INNER JOIN users u ON str.trainer_id = u.user_id - INNER JOIN workplace_visit_requests vr ON str.request_id = vr.request_id - WHERE 1=1 - `; +const getTrainingRecords = async (filters = {}) => { + const db = await getDb(); + let query = ` + SELECT + str.training_id, str.request_id, str.trainer_id, str.training_date, + str.training_start_time, str.training_end_time, str.training_topics, + str.completed_at, str.created_at, str.updated_at, + u.username as trainer_name, u.name as trainer_full_name, + vr.visitor_company, vr.visitor_count, vr.visit_date + FROM safety_training_records str + INNER JOIN users u ON str.trainer_id = u.user_id + INNER JOIN workplace_visit_requests vr ON str.request_id = vr.request_id + WHERE 1=1 + `; - const params = []; + const params = []; - if (filters.training_date) { - query += ` AND str.training_date = ?`; - params.push(filters.training_date); - } - - if (filters.start_date && filters.end_date) { - query += ` AND str.training_date BETWEEN ? AND ?`; - params.push(filters.start_date, filters.end_date); - } - - if (filters.trainer_id) { - query += ` AND str.trainer_id = ?`; - params.push(filters.trainer_id); - } - - query += ` ORDER BY str.training_date DESC, str.training_start_time DESC`; - - const [rows] = await db.query(query, params); - callback(null, rows); - } catch (err) { - callback(err); + if (filters.training_date) { + query += ` AND str.training_date = ?`; + params.push(filters.training_date); } + if (filters.start_date && filters.end_date) { + query += ` AND str.training_date BETWEEN ? AND ?`; + params.push(filters.start_date, filters.end_date); + } + if (filters.trainer_id) { + query += ` AND str.trainer_id = ?`; + params.push(filters.trainer_id); + } + + query += ` ORDER BY str.training_date DESC, str.training_start_time DESC`; + + const [rows] = await db.query(query, params); + return rows; }; module.exports = { - // μΆœμž… μ‹ μ²­ createVisitRequest, getAllVisitRequests, getVisitRequestById, @@ -488,15 +328,11 @@ module.exports = { approveVisitRequest, rejectVisitRequest, updateVisitRequestStatus, - - // λ°©λ¬Έ λͺ©μ  getAllVisitPurposes, getActiveVisitPurposes, createVisitPurpose, updateVisitPurpose, deleteVisitPurpose, - - // μ•ˆμ „κ΅μœ‘ createTrainingRecord, getTrainingRecordByRequestId, updateTrainingRecord, diff --git a/system1-factory/api/models/workIssueModel.js b/system1-factory/api/models/workIssueModel.js index 34ec5ef..c0fb018 100644 --- a/system1-factory/api/models/workIssueModel.js +++ b/system1-factory/api/models/workIssueModel.js @@ -7,195 +7,125 @@ const { getDb } = require('../dbPool'); // ==================== μ‹ κ³  μΉ΄ν…Œκ³ λ¦¬ 관리 ==================== -/** - * λͺ¨λ“  μ‹ κ³  μΉ΄ν…Œκ³ λ¦¬ 쑰회 - */ -const getAllCategories = async (callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT category_id, category_type, category_name, description, display_order, is_active, created_at - FROM issue_report_categories - ORDER BY category_type, display_order, category_id` - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getAllCategories = async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT category_id, category_type, category_name, description, display_order, is_active, created_at + FROM issue_report_categories + ORDER BY category_type, display_order, category_id` + ); + return rows; }; -/** - * νƒ€μž…λ³„ ν™œμ„± μΉ΄ν…Œκ³ λ¦¬ 쑰회 (nonconformity/safety) - */ -const getCategoriesByType = async (categoryType, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT category_id, category_type, category_name, description, display_order - FROM issue_report_categories - WHERE category_type = ? AND is_active = TRUE - ORDER BY display_order, category_id`, - [categoryType] - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getCategoriesByType = async (categoryType) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT category_id, category_type, category_name, description, display_order + FROM issue_report_categories + WHERE category_type = ? AND is_active = TRUE + ORDER BY display_order, category_id`, + [categoryType] + ); + return rows; }; -/** - * μΉ΄ν…Œκ³ λ¦¬ 생성 - */ -const createCategory = async (categoryData, callback) => { - try { - const db = await getDb(); - const { category_type, category_name, description = null, display_order = 0 } = categoryData; +const createCategory = async (categoryData) => { + const db = await getDb(); + const { category_type, category_name, description = null, display_order = 0 } = categoryData; - const [result] = await db.query( - `INSERT INTO issue_report_categories (category_type, category_name, description, display_order) - VALUES (?, ?, ?, ?)`, - [category_type, category_name, description, display_order] - ); + const [result] = await db.query( + `INSERT INTO issue_report_categories (category_type, category_name, description, display_order) + VALUES (?, ?, ?, ?)`, + [category_type, category_name, description, display_order] + ); - callback(null, result.insertId); - } catch (err) { - callback(err); - } + return result.insertId; }; -/** - * μΉ΄ν…Œκ³ λ¦¬ μˆ˜μ • - */ -const updateCategory = async (categoryId, categoryData, callback) => { - try { - const db = await getDb(); - const { category_name, description, display_order, is_active } = categoryData; +const updateCategory = async (categoryId, categoryData) => { + const db = await getDb(); + const { category_name, description, display_order, is_active } = categoryData; - const [result] = await db.query( - `UPDATE issue_report_categories - SET category_name = ?, description = ?, display_order = ?, is_active = ? - WHERE category_id = ?`, - [category_name, description, display_order, is_active, categoryId] - ); + const [result] = await db.query( + `UPDATE issue_report_categories + SET category_name = ?, description = ?, display_order = ?, is_active = ? + WHERE category_id = ?`, + [category_name, description, display_order, is_active, categoryId] + ); - callback(null, result); - } catch (err) { - callback(err); - } + return result; }; -/** - * μΉ΄ν…Œκ³ λ¦¬ μ‚­μ œ - */ -const deleteCategory = async (categoryId, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `DELETE FROM issue_report_categories WHERE category_id = ?`, - [categoryId] - ); - callback(null, result); - } catch (err) { - callback(err); - } +const deleteCategory = async (categoryId) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM issue_report_categories WHERE category_id = ?`, + [categoryId] + ); + return result; }; // ==================== 사전 μ •μ˜ μ‹ κ³  ν•­λͺ© 관리 ==================== -/** - * μΉ΄ν…Œκ³ λ¦¬λ³„ ν™œμ„± ν•­λͺ© 쑰회 - */ -const getItemsByCategory = async (categoryId, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT item_id, category_id, item_name, description, severity, display_order - FROM issue_report_items - WHERE category_id = ? AND is_active = TRUE - ORDER BY display_order, item_id`, - [categoryId] - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getItemsByCategory = async (categoryId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT item_id, category_id, item_name, description, severity, display_order + FROM issue_report_items + WHERE category_id = ? AND is_active = TRUE + ORDER BY display_order, item_id`, + [categoryId] + ); + return rows; }; -/** - * λͺ¨λ“  ν•­λͺ© 쑰회 (κ΄€λ¦¬μš©) - */ -const getAllItems = async (callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT iri.item_id, iri.category_id, iri.item_name, iri.description, - iri.severity, iri.display_order, iri.is_active, iri.created_at, - irc.category_name, irc.category_type - FROM issue_report_items iri - INNER JOIN issue_report_categories irc ON iri.category_id = irc.category_id - ORDER BY irc.category_type, irc.display_order, iri.display_order, iri.item_id` - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getAllItems = async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT iri.item_id, iri.category_id, iri.item_name, iri.description, + iri.severity, iri.display_order, iri.is_active, iri.created_at, + irc.category_name, irc.category_type + FROM issue_report_items iri + INNER JOIN issue_report_categories irc ON iri.category_id = irc.category_id + ORDER BY irc.category_type, irc.display_order, iri.display_order, iri.item_id` + ); + return rows; }; -/** - * ν•­λͺ© 생성 - */ -const createItem = async (itemData, callback) => { - try { - const db = await getDb(); - const { category_id, item_name, description = null, severity = 'medium', display_order = 0 } = itemData; +const createItem = async (itemData) => { + const db = await getDb(); + const { category_id, item_name, description = null, severity = 'medium', display_order = 0 } = itemData; - const [result] = await db.query( - `INSERT INTO issue_report_items (category_id, item_name, description, severity, display_order) - VALUES (?, ?, ?, ?, ?)`, - [category_id, item_name, description, severity, display_order] - ); + const [result] = await db.query( + `INSERT INTO issue_report_items (category_id, item_name, description, severity, display_order) + VALUES (?, ?, ?, ?, ?)`, + [category_id, item_name, description, severity, display_order] + ); - callback(null, result.insertId); - } catch (err) { - callback(err); - } + return result.insertId; }; -/** - * ν•­λͺ© μˆ˜μ • - */ -const updateItem = async (itemId, itemData, callback) => { - try { - const db = await getDb(); - const { item_name, description, severity, display_order, is_active } = itemData; +const updateItem = async (itemId, itemData) => { + const db = await getDb(); + const { item_name, description, severity, display_order, is_active } = itemData; - const [result] = await db.query( - `UPDATE issue_report_items - SET item_name = ?, description = ?, severity = ?, display_order = ?, is_active = ? - WHERE item_id = ?`, - [item_name, description, severity, display_order, is_active, itemId] - ); + const [result] = await db.query( + `UPDATE issue_report_items + SET item_name = ?, description = ?, severity = ?, display_order = ?, is_active = ? + WHERE item_id = ?`, + [item_name, description, severity, display_order, is_active, itemId] + ); - callback(null, result); - } catch (err) { - callback(err); - } + return result; }; -/** - * ν•­λͺ© μ‚­μ œ - */ -const deleteItem = async (itemId, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `DELETE FROM issue_report_items WHERE item_id = ?`, - [itemId] - ); - callback(null, result); - } catch (err) { - callback(err); - } +const deleteItem = async (itemId) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM issue_report_items WHERE item_id = ?`, + [itemId] + ); + return result; }; // ==================== 문제 μ‹ κ³  관리 ==================== @@ -203,651 +133,534 @@ const deleteItem = async (itemId, callback) => { // ν•œκ΅­ μ‹œκ°„ μœ ν‹Έλ¦¬ν‹° import const { getKoreaDatetime } = require('../utils/dateUtils'); -/** - * μ‹ κ³  생성 - */ -const createReport = async (reportData, callback) => { - try { - const db = await getDb(); - const { - reporter_id, - factory_category_id = null, - workplace_id = null, - custom_location = null, - tbm_session_id = null, - visit_request_id = null, - issue_category_id, - issue_item_id = null, - additional_description = null, - photo_path1 = null, - photo_path2 = null, - photo_path3 = null, - photo_path4 = null, - photo_path5 = null - } = reportData; +const createReport = async (reportData) => { + const db = await getDb(); + const { + reporter_id, + factory_category_id = null, + workplace_id = null, + custom_location = null, + tbm_session_id = null, + visit_request_id = null, + issue_category_id, + issue_item_id = null, + additional_description = null, + photo_path1 = null, + photo_path2 = null, + photo_path3 = null, + photo_path4 = null, + photo_path5 = null + } = reportData; - // ν•œκ΅­ μ‹œκ°„ κΈ°μ€€μœΌλ‘œ μ‹ κ³  μΌμ‹œ μ„€μ • - const reportDate = getKoreaDatetime(); + const reportDate = getKoreaDatetime(); - const [result] = await db.query( - `INSERT INTO work_issue_reports - (reporter_id, report_date, factory_category_id, workplace_id, custom_location, - tbm_session_id, visit_request_id, issue_category_id, issue_item_id, - additional_description, photo_path1, photo_path2, photo_path3, photo_path4, photo_path5) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, - [reporter_id, reportDate, factory_category_id, workplace_id, custom_location, - tbm_session_id, visit_request_id, issue_category_id, issue_item_id, - additional_description, photo_path1, photo_path2, photo_path3, photo_path4, photo_path5] - ); + const [result] = await db.query( + `INSERT INTO work_issue_reports + (reporter_id, report_date, factory_category_id, workplace_id, custom_location, + tbm_session_id, visit_request_id, issue_category_id, issue_item_id, + additional_description, photo_path1, photo_path2, photo_path3, photo_path4, photo_path5) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [reporter_id, reportDate, factory_category_id, workplace_id, custom_location, + tbm_session_id, visit_request_id, issue_category_id, issue_item_id, + additional_description, photo_path1, photo_path2, photo_path3, photo_path4, photo_path5] + ); - // μƒνƒœ λ³€κ²½ 둜그 기둝 - await db.query( - `INSERT INTO work_issue_status_logs (report_id, previous_status, new_status, changed_by) - VALUES (?, NULL, 'reported', ?)`, - [result.insertId, reporter_id] - ); + await db.query( + `INSERT INTO work_issue_status_logs (report_id, previous_status, new_status, changed_by) + VALUES (?, NULL, 'reported', ?)`, + [result.insertId, reporter_id] + ); - callback(null, result.insertId); - } catch (err) { - callback(err); - } + return result.insertId; }; -/** - * μ‹ κ³  λͺ©λ‘ 쑰회 (ν•„ν„° μ˜΅μ…˜ 포함) - */ -const getAllReports = async (filters = {}, callback) => { - try { - const db = await getDb(); - let query = ` - SELECT - wir.report_id, wir.reporter_id, wir.report_date, - wir.factory_category_id, wir.workplace_id, wir.custom_location, - wir.tbm_session_id, wir.visit_request_id, - wir.issue_category_id, wir.issue_item_id, wir.additional_description, - wir.photo_path1, wir.photo_path2, wir.photo_path3, wir.photo_path4, wir.photo_path5, - wir.status, wir.assigned_department, wir.assigned_user_id, wir.assigned_at, - wir.resolution_notes, wir.resolved_at, - wir.created_at, wir.updated_at, - u.username as reporter_name, u.name as reporter_full_name, - wc.category_name as factory_name, - w.workplace_name, - irc.category_type, irc.category_name as issue_category_name, - iri.item_name as issue_item_name, iri.severity, - assignee.username as assigned_user_name, assignee.name as assigned_full_name - FROM work_issue_reports wir - INNER JOIN users u ON wir.reporter_id = u.user_id - LEFT JOIN workplace_categories wc ON wir.factory_category_id = wc.category_id - LEFT JOIN workplaces w ON wir.workplace_id = w.workplace_id - INNER JOIN issue_report_categories irc ON wir.issue_category_id = irc.category_id - LEFT JOIN issue_report_items iri ON wir.issue_item_id = iri.item_id - LEFT JOIN users assignee ON wir.assigned_user_id = assignee.user_id - WHERE 1=1 - `; +const getAllReports = async (filters = {}) => { + const db = await getDb(); + let query = ` + SELECT + wir.report_id, wir.reporter_id, wir.report_date, + wir.factory_category_id, wir.workplace_id, wir.custom_location, + wir.tbm_session_id, wir.visit_request_id, + wir.issue_category_id, wir.issue_item_id, wir.additional_description, + wir.photo_path1, wir.photo_path2, wir.photo_path3, wir.photo_path4, wir.photo_path5, + wir.status, wir.assigned_department, wir.assigned_user_id, wir.assigned_at, + wir.resolution_notes, wir.resolved_at, + wir.created_at, wir.updated_at, + u.username as reporter_name, u.name as reporter_full_name, + wc.category_name as factory_name, + w.workplace_name, + irc.category_type, irc.category_name as issue_category_name, + iri.item_name as issue_item_name, iri.severity, + assignee.username as assigned_user_name, assignee.name as assigned_full_name + FROM work_issue_reports wir + INNER JOIN users u ON wir.reporter_id = u.user_id + LEFT JOIN workplace_categories wc ON wir.factory_category_id = wc.category_id + LEFT JOIN workplaces w ON wir.workplace_id = w.workplace_id + INNER JOIN issue_report_categories irc ON wir.issue_category_id = irc.category_id + LEFT JOIN issue_report_items iri ON wir.issue_item_id = iri.item_id + LEFT JOIN users assignee ON wir.assigned_user_id = assignee.user_id + WHERE 1=1 + `; - const params = []; + const params = []; - // ν•„ν„° 적용 - if (filters.status) { - query += ` AND wir.status = ?`; - params.push(filters.status); - } - - if (filters.category_type) { - query += ` AND irc.category_type = ?`; - params.push(filters.category_type); - } - - if (filters.issue_category_id) { - query += ` AND wir.issue_category_id = ?`; - params.push(filters.issue_category_id); - } - - if (filters.factory_category_id) { - query += ` AND wir.factory_category_id = ?`; - params.push(filters.factory_category_id); - } - - if (filters.workplace_id) { - query += ` AND wir.workplace_id = ?`; - params.push(filters.workplace_id); - } - - if (filters.reporter_id) { - query += ` AND wir.reporter_id = ?`; - params.push(filters.reporter_id); - } - - if (filters.assigned_user_id) { - query += ` AND wir.assigned_user_id = ?`; - params.push(filters.assigned_user_id); - } - - if (filters.start_date && filters.end_date) { - query += ` AND DATE(wir.report_date) BETWEEN ? AND ?`; - params.push(filters.start_date, filters.end_date); - } - - if (filters.search) { - query += ` AND (wir.additional_description LIKE ? OR iri.item_name LIKE ? OR wir.custom_location LIKE ?)`; - const searchTerm = `%${filters.search}%`; - params.push(searchTerm, searchTerm, searchTerm); - } - - query += ` ORDER BY wir.report_date DESC, wir.report_id DESC`; - - // νŽ˜μ΄μ§€λ„€μ΄μ…˜ - if (filters.limit) { - query += ` LIMIT ?`; - params.push(parseInt(filters.limit)); - - if (filters.offset) { - query += ` OFFSET ?`; - params.push(parseInt(filters.offset)); - } - } - - const [rows] = await db.query(query, params); - callback(null, rows); - } catch (err) { - callback(err); + if (filters.status) { + query += ` AND wir.status = ?`; + params.push(filters.status); } + + if (filters.category_type) { + query += ` AND irc.category_type = ?`; + params.push(filters.category_type); + } + + if (filters.issue_category_id) { + query += ` AND wir.issue_category_id = ?`; + params.push(filters.issue_category_id); + } + + if (filters.factory_category_id) { + query += ` AND wir.factory_category_id = ?`; + params.push(filters.factory_category_id); + } + + if (filters.workplace_id) { + query += ` AND wir.workplace_id = ?`; + params.push(filters.workplace_id); + } + + if (filters.reporter_id) { + query += ` AND wir.reporter_id = ?`; + params.push(filters.reporter_id); + } + + if (filters.assigned_user_id) { + query += ` AND wir.assigned_user_id = ?`; + params.push(filters.assigned_user_id); + } + + if (filters.start_date && filters.end_date) { + query += ` AND DATE(wir.report_date) BETWEEN ? AND ?`; + params.push(filters.start_date, filters.end_date); + } + + if (filters.search) { + query += ` AND (wir.additional_description LIKE ? OR iri.item_name LIKE ? OR wir.custom_location LIKE ?)`; + const searchTerm = `%${filters.search}%`; + params.push(searchTerm, searchTerm, searchTerm); + } + + query += ` ORDER BY wir.report_date DESC, wir.report_id DESC`; + + if (filters.limit) { + query += ` LIMIT ?`; + params.push(parseInt(filters.limit)); + + if (filters.offset) { + query += ` OFFSET ?`; + params.push(parseInt(filters.offset)); + } + } + + const [rows] = await db.query(query, params); + return rows; }; -/** - * μ‹ κ³  상세 쑰회 - */ -const getReportById = async (reportId, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT - wir.report_id, wir.reporter_id, wir.report_date, - wir.factory_category_id, wir.workplace_id, wir.custom_location, - wir.tbm_session_id, wir.visit_request_id, - wir.issue_category_id, wir.issue_item_id, wir.additional_description, - wir.photo_path1, wir.photo_path2, wir.photo_path3, wir.photo_path4, wir.photo_path5, - wir.status, wir.assigned_department, wir.assigned_user_id, wir.assigned_at, wir.assigned_by, - wir.resolution_notes, wir.resolution_photo_path1, wir.resolution_photo_path2, - wir.resolved_at, wir.resolved_by, - wir.modification_history, - wir.created_at, wir.updated_at, - u.username as reporter_name, u.name as reporter_full_name, - wc.category_name as factory_name, - w.workplace_name, - irc.category_type, irc.category_name as issue_category_name, - iri.item_name as issue_item_name, iri.severity, - assignee.username as assigned_user_name, assignee.name as assigned_full_name, - assigner.username as assigned_by_name, - resolver.username as resolved_by_name - FROM work_issue_reports wir - INNER JOIN users u ON wir.reporter_id = u.user_id - LEFT JOIN workplace_categories wc ON wir.factory_category_id = wc.category_id - LEFT JOIN workplaces w ON wir.workplace_id = w.workplace_id - INNER JOIN issue_report_categories irc ON wir.issue_category_id = irc.category_id - LEFT JOIN issue_report_items iri ON wir.issue_item_id = iri.item_id - LEFT JOIN users assignee ON wir.assigned_user_id = assignee.user_id - LEFT JOIN users assigner ON wir.assigned_by = assigner.user_id - LEFT JOIN users resolver ON wir.resolved_by = resolver.user_id - WHERE wir.report_id = ?`, - [reportId] - ); +const getReportById = async (reportId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT + wir.report_id, wir.reporter_id, wir.report_date, + wir.factory_category_id, wir.workplace_id, wir.custom_location, + wir.tbm_session_id, wir.visit_request_id, + wir.issue_category_id, wir.issue_item_id, wir.additional_description, + wir.photo_path1, wir.photo_path2, wir.photo_path3, wir.photo_path4, wir.photo_path5, + wir.status, wir.assigned_department, wir.assigned_user_id, wir.assigned_at, wir.assigned_by, + wir.resolution_notes, wir.resolution_photo_path1, wir.resolution_photo_path2, + wir.resolved_at, wir.resolved_by, + wir.modification_history, + wir.created_at, wir.updated_at, + u.username as reporter_name, u.name as reporter_full_name, + wc.category_name as factory_name, + w.workplace_name, + irc.category_type, irc.category_name as issue_category_name, + iri.item_name as issue_item_name, iri.severity, + assignee.username as assigned_user_name, assignee.name as assigned_full_name, + assigner.username as assigned_by_name, + resolver.username as resolved_by_name + FROM work_issue_reports wir + INNER JOIN users u ON wir.reporter_id = u.user_id + LEFT JOIN workplace_categories wc ON wir.factory_category_id = wc.category_id + LEFT JOIN workplaces w ON wir.workplace_id = w.workplace_id + INNER JOIN issue_report_categories irc ON wir.issue_category_id = irc.category_id + LEFT JOIN issue_report_items iri ON wir.issue_item_id = iri.item_id + LEFT JOIN users assignee ON wir.assigned_user_id = assignee.user_id + LEFT JOIN users assigner ON wir.assigned_by = assigner.user_id + LEFT JOIN users resolver ON wir.resolved_by = resolver.user_id + WHERE wir.report_id = ?`, + [reportId] + ); - callback(null, rows[0]); - } catch (err) { - callback(err); - } + return rows[0]; }; -/** - * μ‹ κ³  μˆ˜μ • - */ -const updateReport = async (reportId, reportData, userId, callback) => { - try { - const db = await getDb(); +const updateReport = async (reportId, reportData, userId) => { + const db = await getDb(); - // κΈ°μ‘΄ 데이터 쑰회 - const [existing] = await db.query( - `SELECT * FROM work_issue_reports WHERE report_id = ?`, - [reportId] - ); + const [existing] = await db.query( + `SELECT * FROM work_issue_reports WHERE report_id = ?`, + [reportId] + ); - if (existing.length === 0) { - return callback(new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.')); - } - - const current = existing[0]; - - // μˆ˜μ • 이λ ₯ 생성 - const modifications = []; - const now = new Date().toISOString(); - - for (const key of Object.keys(reportData)) { - if (current[key] !== reportData[key] && reportData[key] !== undefined) { - modifications.push({ - field: key, - old_value: current[key], - new_value: reportData[key], - modified_at: now, - modified_by: userId - }); - } - } - - // κΈ°μ‘΄ 이λ ₯κ³Ό 병합 - const existingHistory = current.modification_history ? JSON.parse(current.modification_history) : []; - const newHistory = [...existingHistory, ...modifications]; - - const { - factory_category_id, - workplace_id, - custom_location, - issue_category_id, - issue_item_id, - additional_description, - photo_path1, - photo_path2, - photo_path3, - photo_path4, - photo_path5 - } = reportData; - - const [result] = await db.query( - `UPDATE work_issue_reports - SET factory_category_id = COALESCE(?, factory_category_id), - workplace_id = COALESCE(?, workplace_id), - custom_location = COALESCE(?, custom_location), - issue_category_id = COALESCE(?, issue_category_id), - issue_item_id = COALESCE(?, issue_item_id), - additional_description = COALESCE(?, additional_description), - photo_path1 = COALESCE(?, photo_path1), - photo_path2 = COALESCE(?, photo_path2), - photo_path3 = COALESCE(?, photo_path3), - photo_path4 = COALESCE(?, photo_path4), - photo_path5 = COALESCE(?, photo_path5), - modification_history = ?, - updated_at = NOW() - WHERE report_id = ?`, - [factory_category_id, workplace_id, custom_location, - issue_category_id, issue_item_id, additional_description, - photo_path1, photo_path2, photo_path3, photo_path4, photo_path5, - JSON.stringify(newHistory), reportId] - ); - - callback(null, result); - } catch (err) { - callback(err); + if (existing.length === 0) { + throw new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.'); } + + const current = existing[0]; + + const modifications = []; + const now = new Date().toISOString(); + + for (const key of Object.keys(reportData)) { + if (current[key] !== reportData[key] && reportData[key] !== undefined) { + modifications.push({ + field: key, + old_value: current[key], + new_value: reportData[key], + modified_at: now, + modified_by: userId + }); + } + } + + const existingHistory = current.modification_history ? JSON.parse(current.modification_history) : []; + const newHistory = [...existingHistory, ...modifications]; + + const { + factory_category_id, + workplace_id, + custom_location, + issue_category_id, + issue_item_id, + additional_description, + photo_path1, + photo_path2, + photo_path3, + photo_path4, + photo_path5 + } = reportData; + + const [result] = await db.query( + `UPDATE work_issue_reports + SET factory_category_id = COALESCE(?, factory_category_id), + workplace_id = COALESCE(?, workplace_id), + custom_location = COALESCE(?, custom_location), + issue_category_id = COALESCE(?, issue_category_id), + issue_item_id = COALESCE(?, issue_item_id), + additional_description = COALESCE(?, additional_description), + photo_path1 = COALESCE(?, photo_path1), + photo_path2 = COALESCE(?, photo_path2), + photo_path3 = COALESCE(?, photo_path3), + photo_path4 = COALESCE(?, photo_path4), + photo_path5 = COALESCE(?, photo_path5), + modification_history = ?, + updated_at = NOW() + WHERE report_id = ?`, + [factory_category_id, workplace_id, custom_location, + issue_category_id, issue_item_id, additional_description, + photo_path1, photo_path2, photo_path3, photo_path4, photo_path5, + JSON.stringify(newHistory), reportId] + ); + + return result; }; -/** - * μ‹ κ³  μ‚­μ œ - */ -const deleteReport = async (reportId, callback) => { - try { - const db = await getDb(); +const deleteReport = async (reportId) => { + const db = await getDb(); - // λ¨Όμ € 사진 경둜 쑰회 (μ‚­μ œμš©) - const [photos] = await db.query( - `SELECT photo_path1, photo_path2, photo_path3, photo_path4, photo_path5, - resolution_photo_path1, resolution_photo_path2 - FROM work_issue_reports WHERE report_id = ?`, - [reportId] - ); + const [photos] = await db.query( + `SELECT photo_path1, photo_path2, photo_path3, photo_path4, photo_path5, + resolution_photo_path1, resolution_photo_path2 + FROM work_issue_reports WHERE report_id = ?`, + [reportId] + ); - const [result] = await db.query( - `DELETE FROM work_issue_reports WHERE report_id = ?`, - [reportId] - ); + const [result] = await db.query( + `DELETE FROM work_issue_reports WHERE report_id = ?`, + [reportId] + ); - // μ‚­μ œν•  사진 경둜 λ°˜ν™˜ - callback(null, { result, photos: photos[0] }); - } catch (err) { - callback(err); - } + return { result, photos: photos[0] }; }; // ==================== μƒνƒœ 관리 ==================== -/** - * μ‹ κ³  μ ‘μˆ˜ (reported β†’ received) - */ -const receiveReport = async (reportId, userId, callback) => { - try { - const db = await getDb(); +const receiveReport = async (reportId, userId) => { + const db = await getDb(); - // ν˜„μž¬ μƒνƒœ 확인 - const [current] = await db.query( - `SELECT status FROM work_issue_reports WHERE report_id = ?`, - [reportId] - ); + const [current] = await db.query( + `SELECT status FROM work_issue_reports WHERE report_id = ?`, + [reportId] + ); - if (current.length === 0) { - return callback(new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.')); - } - - if (current[0].status !== 'reported') { - return callback(new Error('μ ‘μˆ˜ λŒ€κΈ° μƒνƒœκ°€ μ•„λ‹™λ‹ˆλ‹€.')); - } - - const [result] = await db.query( - `UPDATE work_issue_reports - SET status = 'received', updated_at = NOW() - WHERE report_id = ?`, - [reportId] - ); - - // μƒνƒœ λ³€κ²½ 둜그 - await db.query( - `INSERT INTO work_issue_status_logs (report_id, previous_status, new_status, changed_by) - VALUES (?, 'reported', 'received', ?)`, - [reportId, userId] - ); - - callback(null, result); - } catch (err) { - callback(err); + if (current.length === 0) { + throw new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.'); } + + if (current[0].status !== 'reported') { + throw new Error('μ ‘μˆ˜ λŒ€κΈ° μƒνƒœκ°€ μ•„λ‹™λ‹ˆλ‹€.'); + } + + const [result] = await db.query( + `UPDATE work_issue_reports + SET status = 'received', updated_at = NOW() + WHERE report_id = ?`, + [reportId] + ); + + await db.query( + `INSERT INTO work_issue_status_logs (report_id, previous_status, new_status, changed_by) + VALUES (?, 'reported', 'received', ?)`, + [reportId, userId] + ); + + return result; }; -/** - * λ‹΄λ‹Ήμž λ°°μ • - */ -const assignReport = async (reportId, assignData, callback) => { - try { - const db = await getDb(); - const { assigned_department, assigned_user_id, assigned_by } = assignData; +const assignReport = async (reportId, assignData) => { + const db = await getDb(); + const { assigned_department, assigned_user_id, assigned_by } = assignData; - // ν˜„μž¬ μƒνƒœ 확인 - const [current] = await db.query( - `SELECT status FROM work_issue_reports WHERE report_id = ?`, - [reportId] - ); + const [current] = await db.query( + `SELECT status FROM work_issue_reports WHERE report_id = ?`, + [reportId] + ); - if (current.length === 0) { - return callback(new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.')); - } - - // μ ‘μˆ˜ μƒνƒœ 이상이어야 λ°°μ • κ°€λŠ₯ - const validStatuses = ['received', 'in_progress']; - if (!validStatuses.includes(current[0].status)) { - return callback(new Error('μ ‘μˆ˜λœ μƒνƒœμ—μ„œλ§Œ λ‹΄λ‹Ήμž 배정이 κ°€λŠ₯ν•©λ‹ˆλ‹€.')); - } - - const [result] = await db.query( - `UPDATE work_issue_reports - SET assigned_department = ?, assigned_user_id = ?, - assigned_at = NOW(), assigned_by = ?, updated_at = NOW() - WHERE report_id = ?`, - [assigned_department, assigned_user_id, assigned_by, reportId] - ); - - callback(null, result); - } catch (err) { - callback(err); + if (current.length === 0) { + throw new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.'); } + + const validStatuses = ['received', 'in_progress']; + if (!validStatuses.includes(current[0].status)) { + throw new Error('μ ‘μˆ˜λœ μƒνƒœμ—μ„œλ§Œ λ‹΄λ‹Ήμž 배정이 κ°€λŠ₯ν•©λ‹ˆλ‹€.'); + } + + const [result] = await db.query( + `UPDATE work_issue_reports + SET assigned_department = ?, assigned_user_id = ?, + assigned_at = NOW(), assigned_by = ?, updated_at = NOW() + WHERE report_id = ?`, + [assigned_department, assigned_user_id, assigned_by, reportId] + ); + + return result; }; -/** - * 처리 μ‹œμž‘ (received β†’ in_progress) - */ -const startProcessing = async (reportId, userId, callback) => { - try { - const db = await getDb(); +const startProcessing = async (reportId, userId) => { + const db = await getDb(); - // ν˜„μž¬ μƒνƒœ 확인 - const [current] = await db.query( - `SELECT status FROM work_issue_reports WHERE report_id = ?`, - [reportId] - ); + const [current] = await db.query( + `SELECT status FROM work_issue_reports WHERE report_id = ?`, + [reportId] + ); - if (current.length === 0) { - return callback(new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.')); - } - - if (current[0].status !== 'received') { - return callback(new Error('μ ‘μˆ˜λœ μƒνƒœμ—μ„œλ§Œ 처리λ₯Ό μ‹œμž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.')); - } - - const [result] = await db.query( - `UPDATE work_issue_reports - SET status = 'in_progress', updated_at = NOW() - WHERE report_id = ?`, - [reportId] - ); - - // μƒνƒœ λ³€κ²½ 둜그 - await db.query( - `INSERT INTO work_issue_status_logs (report_id, previous_status, new_status, changed_by) - VALUES (?, 'received', 'in_progress', ?)`, - [reportId, userId] - ); - - callback(null, result); - } catch (err) { - callback(err); + if (current.length === 0) { + throw new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.'); } + + if (current[0].status !== 'received') { + throw new Error('μ ‘μˆ˜λœ μƒνƒœμ—μ„œλ§Œ 처리λ₯Ό μ‹œμž‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.'); + } + + const [result] = await db.query( + `UPDATE work_issue_reports + SET status = 'in_progress', updated_at = NOW() + WHERE report_id = ?`, + [reportId] + ); + + await db.query( + `INSERT INTO work_issue_status_logs (report_id, previous_status, new_status, changed_by) + VALUES (?, 'received', 'in_progress', ?)`, + [reportId, userId] + ); + + return result; }; -/** - * 처리 μ™„λ£Œ (in_progress β†’ completed) - */ -const completeReport = async (reportId, completionData, callback) => { - try { - const db = await getDb(); - const { resolution_notes, resolution_photo_path1, resolution_photo_path2, resolved_by } = completionData; +const completeReport = async (reportId, completionData) => { + const db = await getDb(); + const { resolution_notes, resolution_photo_path1, resolution_photo_path2, resolved_by } = completionData; - // ν˜„μž¬ μƒνƒœ 확인 - const [current] = await db.query( - `SELECT status FROM work_issue_reports WHERE report_id = ?`, - [reportId] - ); + const [current] = await db.query( + `SELECT status FROM work_issue_reports WHERE report_id = ?`, + [reportId] + ); - if (current.length === 0) { - return callback(new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.')); - } - - if (current[0].status !== 'in_progress') { - return callback(new Error('처리 쀑 μƒνƒœμ—μ„œλ§Œ μ™„λ£Œν•  수 μžˆμŠ΅λ‹ˆλ‹€.')); - } - - const [result] = await db.query( - `UPDATE work_issue_reports - SET status = 'completed', resolution_notes = ?, - resolution_photo_path1 = ?, resolution_photo_path2 = ?, - resolved_at = NOW(), resolved_by = ?, updated_at = NOW() - WHERE report_id = ?`, - [resolution_notes, resolution_photo_path1, resolution_photo_path2, resolved_by, reportId] - ); - - // μƒνƒœ λ³€κ²½ 둜그 - await db.query( - `INSERT INTO work_issue_status_logs (report_id, previous_status, new_status, changed_by, change_reason) - VALUES (?, 'in_progress', 'completed', ?, ?)`, - [reportId, resolved_by, resolution_notes] - ); - - callback(null, result); - } catch (err) { - callback(err); + if (current.length === 0) { + throw new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.'); } + + if (current[0].status !== 'in_progress') { + throw new Error('처리 쀑 μƒνƒœμ—μ„œλ§Œ μ™„λ£Œν•  수 μžˆμŠ΅λ‹ˆλ‹€.'); + } + + const [result] = await db.query( + `UPDATE work_issue_reports + SET status = 'completed', resolution_notes = ?, + resolution_photo_path1 = ?, resolution_photo_path2 = ?, + resolved_at = NOW(), resolved_by = ?, updated_at = NOW() + WHERE report_id = ?`, + [resolution_notes, resolution_photo_path1, resolution_photo_path2, resolved_by, reportId] + ); + + await db.query( + `INSERT INTO work_issue_status_logs (report_id, previous_status, new_status, changed_by, change_reason) + VALUES (?, 'in_progress', 'completed', ?, ?)`, + [reportId, resolved_by, resolution_notes] + ); + + return result; }; -/** - * μ‹ κ³  μ’…λ£Œ (completed β†’ closed) - */ -const closeReport = async (reportId, userId, callback) => { - try { - const db = await getDb(); +const closeReport = async (reportId, userId) => { + const db = await getDb(); - // ν˜„μž¬ μƒνƒœ 확인 - const [current] = await db.query( - `SELECT status FROM work_issue_reports WHERE report_id = ?`, - [reportId] - ); + const [current] = await db.query( + `SELECT status FROM work_issue_reports WHERE report_id = ?`, + [reportId] + ); - if (current.length === 0) { - return callback(new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.')); - } - - if (current[0].status !== 'completed') { - return callback(new Error('μ™„λ£Œλœ μƒνƒœμ—μ„œλ§Œ μ’…λ£Œν•  수 μžˆμŠ΅λ‹ˆλ‹€.')); - } - - const [result] = await db.query( - `UPDATE work_issue_reports - SET status = 'closed', updated_at = NOW() - WHERE report_id = ?`, - [reportId] - ); - - // μƒνƒœ λ³€κ²½ 둜그 - await db.query( - `INSERT INTO work_issue_status_logs (report_id, previous_status, new_status, changed_by) - VALUES (?, 'completed', 'closed', ?)`, - [reportId, userId] - ); - - callback(null, result); - } catch (err) { - callback(err); + if (current.length === 0) { + throw new Error('μ‹ κ³ λ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.'); } + + if (current[0].status !== 'completed') { + throw new Error('μ™„λ£Œλœ μƒνƒœμ—μ„œλ§Œ μ’…λ£Œν•  수 μžˆμŠ΅λ‹ˆλ‹€.'); + } + + const [result] = await db.query( + `UPDATE work_issue_reports + SET status = 'closed', updated_at = NOW() + WHERE report_id = ?`, + [reportId] + ); + + await db.query( + `INSERT INTO work_issue_status_logs (report_id, previous_status, new_status, changed_by) + VALUES (?, 'completed', 'closed', ?)`, + [reportId, userId] + ); + + return result; }; -/** - * μƒνƒœ λ³€κ²½ 이λ ₯ 쑰회 - */ -const getStatusLogs = async (reportId, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT wisl.log_id, wisl.report_id, wisl.previous_status, wisl.new_status, - wisl.changed_by, wisl.change_reason, wisl.changed_at, - u.username as changed_by_name, u.name as changed_by_full_name - FROM work_issue_status_logs wisl - INNER JOIN users u ON wisl.changed_by = u.user_id - WHERE wisl.report_id = ? - ORDER BY wisl.changed_at ASC`, - [reportId] - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getStatusLogs = async (reportId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT wisl.log_id, wisl.report_id, wisl.previous_status, wisl.new_status, + wisl.changed_by, wisl.change_reason, wisl.changed_at, + u.username as changed_by_name, u.name as changed_by_full_name + FROM work_issue_status_logs wisl + INNER JOIN users u ON wisl.changed_by = u.user_id + WHERE wisl.report_id = ? + ORDER BY wisl.changed_at ASC`, + [reportId] + ); + return rows; }; // ==================== 톡계 ==================== -/** - * μ‹ κ³  톡계 μš”μ•½ - */ -const getStatsSummary = async (filters = {}, callback) => { - try { - const db = await getDb(); +const getStatsSummary = async (filters = {}) => { + const db = await getDb(); - let whereClause = '1=1'; - const params = []; + let whereClause = '1=1'; + const params = []; - if (filters.start_date && filters.end_date) { - whereClause += ` AND DATE(report_date) BETWEEN ? AND ?`; - params.push(filters.start_date, filters.end_date); - } - - if (filters.factory_category_id) { - whereClause += ` AND factory_category_id = ?`; - params.push(filters.factory_category_id); - } - - const [rows] = await db.query( - `SELECT - COUNT(*) as total, - SUM(CASE WHEN status = 'reported' THEN 1 ELSE 0 END) as reported, - SUM(CASE WHEN status = 'received' THEN 1 ELSE 0 END) as received, - SUM(CASE WHEN status = 'in_progress' THEN 1 ELSE 0 END) as in_progress, - SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed, - SUM(CASE WHEN status = 'closed' THEN 1 ELSE 0 END) as closed - FROM work_issue_reports - WHERE ${whereClause}`, - params - ); - - callback(null, rows[0]); - } catch (err) { - callback(err); + if (filters.start_date && filters.end_date) { + whereClause += ` AND DATE(report_date) BETWEEN ? AND ?`; + params.push(filters.start_date, filters.end_date); } + + if (filters.factory_category_id) { + whereClause += ` AND factory_category_id = ?`; + params.push(filters.factory_category_id); + } + + const [rows] = await db.query( + `SELECT + COUNT(*) as total, + SUM(CASE WHEN status = 'reported' THEN 1 ELSE 0 END) as reported, + SUM(CASE WHEN status = 'received' THEN 1 ELSE 0 END) as received, + SUM(CASE WHEN status = 'in_progress' THEN 1 ELSE 0 END) as in_progress, + SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed, + SUM(CASE WHEN status = 'closed' THEN 1 ELSE 0 END) as closed + FROM work_issue_reports + WHERE ${whereClause}`, + params + ); + + return rows[0]; }; -/** - * μΉ΄ν…Œκ³ λ¦¬λ³„ 톡계 - */ -const getStatsByCategory = async (filters = {}, callback) => { - try { - const db = await getDb(); +const getStatsByCategory = async (filters = {}) => { + const db = await getDb(); - let whereClause = '1=1'; - const params = []; + let whereClause = '1=1'; + const params = []; - if (filters.start_date && filters.end_date) { - whereClause += ` AND DATE(wir.report_date) BETWEEN ? AND ?`; - params.push(filters.start_date, filters.end_date); - } - - const [rows] = await db.query( - `SELECT - irc.category_type, irc.category_name, - COUNT(*) as count - FROM work_issue_reports wir - INNER JOIN issue_report_categories irc ON wir.issue_category_id = irc.category_id - WHERE ${whereClause} - GROUP BY irc.category_id - ORDER BY irc.category_type, count DESC`, - params - ); - - callback(null, rows); - } catch (err) { - callback(err); + if (filters.start_date && filters.end_date) { + whereClause += ` AND DATE(wir.report_date) BETWEEN ? AND ?`; + params.push(filters.start_date, filters.end_date); } + + const [rows] = await db.query( + `SELECT + irc.category_type, irc.category_name, + COUNT(*) as count + FROM work_issue_reports wir + INNER JOIN issue_report_categories irc ON wir.issue_category_id = irc.category_id + WHERE ${whereClause} + GROUP BY irc.category_id + ORDER BY irc.category_type, count DESC`, + params + ); + + return rows; }; -/** - * μž‘μ—…μž₯별 톡계 - */ -const getStatsByWorkplace = async (filters = {}, callback) => { - try { - const db = await getDb(); +const getStatsByWorkplace = async (filters = {}) => { + const db = await getDb(); - let whereClause = 'wir.workplace_id IS NOT NULL'; - const params = []; + let whereClause = 'wir.workplace_id IS NOT NULL'; + const params = []; - if (filters.start_date && filters.end_date) { - whereClause += ` AND DATE(wir.report_date) BETWEEN ? AND ?`; - params.push(filters.start_date, filters.end_date); - } - - if (filters.factory_category_id) { - whereClause += ` AND wir.factory_category_id = ?`; - params.push(filters.factory_category_id); - } - - const [rows] = await db.query( - `SELECT - wir.factory_category_id, wc.category_name as factory_name, - wir.workplace_id, w.workplace_name, - COUNT(*) as count - FROM work_issue_reports wir - INNER JOIN workplace_categories wc ON wir.factory_category_id = wc.category_id - INNER JOIN workplaces w ON wir.workplace_id = w.workplace_id - WHERE ${whereClause} - GROUP BY wir.factory_category_id, wir.workplace_id - ORDER BY count DESC`, - params - ); - - callback(null, rows); - } catch (err) { - callback(err); + if (filters.start_date && filters.end_date) { + whereClause += ` AND DATE(wir.report_date) BETWEEN ? AND ?`; + params.push(filters.start_date, filters.end_date); } + + if (filters.factory_category_id) { + whereClause += ` AND wir.factory_category_id = ?`; + params.push(filters.factory_category_id); + } + + const [rows] = await db.query( + `SELECT + wir.factory_category_id, wc.category_name as factory_name, + wir.workplace_id, w.workplace_name, + COUNT(*) as count + FROM work_issue_reports wir + INNER JOIN workplace_categories wc ON wir.factory_category_id = wc.category_id + INNER JOIN workplaces w ON wir.workplace_id = w.workplace_id + WHERE ${whereClause} + GROUP BY wir.factory_category_id, wir.workplace_id + ORDER BY count DESC`, + params + ); + + return rows; }; module.exports = { diff --git a/system1-factory/api/models/workReportModel.js b/system1-factory/api/models/workReportModel.js index db0cf48..3766369 100644 --- a/system1-factory/api/models/workReportModel.js +++ b/system1-factory/api/models/workReportModel.js @@ -3,7 +3,7 @@ const { getDb } = require('../dbPool'); /** * 1. μ—¬λŸ¬ 건 등둝 (νŠΈλžœμž­μ…˜ μ‚¬μš©) */ -const createBatch = async (reports, callback) => { +const createBatch = async (reports) => { const db = await getDb(); const conn = await db.getConnection(); @@ -30,10 +30,9 @@ const createBatch = async (reports, callback) => { } await conn.commit(); - callback(null); } catch (err) { await conn.rollback(); - callback(err); + throw err; } finally { conn.release(); } @@ -42,175 +41,146 @@ const createBatch = async (reports, callback) => { /** * 2. 단일 등둝 */ -const create = async (report, callback) => { - try { - const db = await getDb(); - const { - date, worker_id, project_id, - task_id, overtime_hours, - work_details, memo - } = report; +const create = async (report) => { + const db = await getDb(); + const { + date, worker_id, project_id, + task_id, overtime_hours, + work_details, memo + } = report; - const [result] = await db.query( - `INSERT INTO WorkReports - (\`date\`, worker_id, project_id, task_id, overtime_hours, work_details, memo) - VALUES (?, ?, ?, ?, ?, ?, ?)`, - [ - date, - worker_id, - project_id, - task_id || null, - overtime_hours || null, - work_details || null, - memo || null - ] - ); + const [result] = await db.query( + `INSERT INTO WorkReports + (\`date\`, worker_id, project_id, task_id, overtime_hours, work_details, memo) + VALUES (?, ?, ?, ?, ?, ?, ?)`, + [ + date, + worker_id, + project_id, + task_id || null, + overtime_hours || null, + work_details || null, + memo || null + ] + ); - callback(null, result.insertId); - } catch (err) { - callback(err); - } + return result.insertId; }; - /** - * 3. λ‚ μ§œλ³„ 쑰회 - */ - const getAllByDate = async (date, callback) => { - try { - const db = await getDb(); - const sql = ` - SELECT - wr.worker_id, -- 이 쀄을 μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€ - wr.id, - wr.\`date\`, - w.worker_name, - p.project_name, - CONCAT(t.category, ':', t.subcategory) AS task_name, - wr.overtime_hours, - wr.work_details, - wr.memo - FROM WorkReports wr - LEFT JOIN workers w ON wr.worker_id = w.worker_id - LEFT JOIN projects p ON wr.project_id = p.project_id - LEFT JOIN Tasks t ON wr.task_id = t.task_id - WHERE wr.\`date\` = ? - ORDER BY w.worker_name ASC - `; - const [rows] = await db.query(sql, [date]); - callback(null, rows); - } catch (err) { - callback(err); - } - }; +/** + * 3. λ‚ μ§œλ³„ 쑰회 + */ +const getAllByDate = async (date) => { + const db = await getDb(); + const sql = ` + SELECT + wr.worker_id, + wr.id, + wr.\`date\`, + w.worker_name, + p.project_name, + CONCAT(t.category, ':', t.subcategory) AS task_name, + wr.overtime_hours, + wr.work_details, + wr.memo + FROM WorkReports wr + LEFT JOIN workers w ON wr.worker_id = w.worker_id + LEFT JOIN projects p ON wr.project_id = p.project_id + LEFT JOIN Tasks t ON wr.task_id = t.task_id + WHERE wr.\`date\` = ? + ORDER BY w.worker_name ASC + `; + const [rows] = await db.query(sql, [date]); + return rows; +}; /** * 4. κΈ°κ°„ 쑰회 */ -const getByRange = async (start, end, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT id, \`date\`, worker_id, project_id, morning_task_id, afternoon_task_id, overtime_hours, overtime_task_id, work_details, note, memo, created_at, updated_at, morning_project_id, afternoon_project_id, overtime_project_id, task_id FROM WorkReports - WHERE \`date\` BETWEEN ? AND ? - ORDER BY \`date\` ASC`, - [start, end] - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getByRange = async (start, end) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT id, \`date\`, worker_id, project_id, morning_task_id, afternoon_task_id, overtime_hours, overtime_task_id, work_details, note, memo, created_at, updated_at, morning_project_id, afternoon_project_id, overtime_project_id, task_id FROM WorkReports + WHERE \`date\` BETWEEN ? AND ? + ORDER BY \`date\` ASC`, + [start, end] + ); + return rows; }; /** * 5. ID둜 쑰회 */ -const getById = async (id, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT id, \`date\`, worker_id, project_id, morning_task_id, afternoon_task_id, overtime_hours, overtime_task_id, work_details, note, memo, created_at, updated_at, morning_project_id, afternoon_project_id, overtime_project_id, task_id FROM WorkReports WHERE id = ?`, - [id] - ); - callback(null, rows[0]); - } catch (err) { - callback(err); - } +const getById = async (id) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT id, \`date\`, worker_id, project_id, morning_task_id, afternoon_task_id, overtime_hours, overtime_task_id, work_details, note, memo, created_at, updated_at, morning_project_id, afternoon_project_id, overtime_project_id, task_id FROM WorkReports WHERE id = ?`, + [id] + ); + return rows[0]; }; /** * 6. μˆ˜μ • */ -const update = async (id, report, callback) => { - try { - const db = await getDb(); - const { - date, worker_id, project_id, - task_id, overtime_hours, - work_details, memo - } = report; +const update = async (id, report) => { + const db = await getDb(); + const { + date, worker_id, project_id, + task_id, overtime_hours, + work_details, memo + } = report; - const [result] = await db.query( - `UPDATE WorkReports - SET \`date\` = ?, - worker_id = ?, - project_id = ?, - task_id = ?, - overtime_hours = ?, - work_details = ?, - memo = ?, - updated_at = CURRENT_TIMESTAMP - WHERE id = ?`, - [ - date, - worker_id, - project_id, - task_id || null, - overtime_hours || null, - work_details || null, - memo || null, - id - ] - ); + const [result] = await db.query( + `UPDATE WorkReports + SET \`date\` = ?, + worker_id = ?, + project_id = ?, + task_id = ?, + overtime_hours = ?, + work_details = ?, + memo = ?, + updated_at = CURRENT_TIMESTAMP + WHERE id = ?`, + [ + date, + worker_id, + project_id, + task_id || null, + overtime_hours || null, + work_details || null, + memo || null, + id + ] + ); - callback(null, result.affectedRows); - } catch (err) { - callback(err); - } + return result.affectedRows; }; /** * 7. μ‚­μ œ */ -const remove = async (id, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `DELETE FROM WorkReports WHERE id = ?`, - [id] - ); - callback(null, result.affectedRows); - } catch (err) { - callback(new Error(err.message || String(err))); - } +const remove = async (id) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM WorkReports WHERE id = ?`, + [id] + ); + return result.affectedRows; }; /** * 8. 쀑볡 확인 */ -const existsByDateAndWorker = async (date, worker_id, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT 1 FROM WorkReports WHERE \`date\` = ? AND worker_id = ? LIMIT 1`, - [date, worker_id] - ); - callback(null, rows.length > 0); - } catch (err) { - callback(err); - } +const existsByDateAndWorker = async (date, worker_id) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT 1 FROM WorkReports WHERE \`date\` = ? AND worker_id = ? LIMIT 1`, + [date, worker_id] + ); + return rows.length > 0; }; -// βœ… 내보내기 module.exports = { create, createBatch, @@ -220,4 +190,4 @@ module.exports = { update, remove, existsByDateAndWorker -}; \ No newline at end of file +}; diff --git a/system1-factory/api/models/workplaceModel.js b/system1-factory/api/models/workplaceModel.js index 4a7e754..e3239a1 100644 --- a/system1-factory/api/models/workplaceModel.js +++ b/system1-factory/api/models/workplaceModel.js @@ -2,426 +2,290 @@ const { getDb } = require('../dbPool'); // ==================== μΉ΄ν…Œκ³ λ¦¬(곡μž₯) κ΄€λ ¨ ==================== -/** - * μΉ΄ν…Œκ³ λ¦¬ 생성 - */ -const createCategory = async (category, callback) => { - try { - const db = await getDb(); - const { - category_name, - description = null, - display_order = 0, - is_active = true - } = category; +const createCategory = async (category) => { + const db = await getDb(); + const { + category_name, + description = null, + display_order = 0, + is_active = true + } = category; - const [result] = await db.query( - `INSERT INTO workplace_categories - (category_name, description, display_order, is_active) - VALUES (?, ?, ?, ?)`, - [category_name, description, display_order, is_active] - ); + const [result] = await db.query( + `INSERT INTO workplace_categories + (category_name, description, display_order, is_active) + VALUES (?, ?, ?, ?)`, + [category_name, description, display_order, is_active] + ); - callback(null, result.insertId); - } catch (err) { - callback(err); - } + return result.insertId; }; -/** - * λͺ¨λ“  μΉ΄ν…Œκ³ λ¦¬ 쑰회 - */ -const getAllCategories = async (callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at - FROM workplace_categories - ORDER BY display_order ASC, category_id ASC` - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getAllCategories = async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at + FROM workplace_categories + ORDER BY display_order ASC, category_id ASC` + ); + return rows; }; -/** - * ν™œμ„± μΉ΄ν…Œκ³ λ¦¬λ§Œ 쑰회 - */ -const getActiveCategories = async (callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at - FROM workplace_categories - WHERE is_active = TRUE - ORDER BY display_order ASC, category_id ASC` - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getActiveCategories = async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at + FROM workplace_categories + WHERE is_active = TRUE + ORDER BY display_order ASC, category_id ASC` + ); + return rows; }; -/** - * ID둜 μΉ΄ν…Œκ³ λ¦¬ 쑰회 - */ -const getCategoryById = async (categoryId, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at - FROM workplace_categories - WHERE category_id = ?`, - [categoryId] - ); - callback(null, rows[0]); - } catch (err) { - callback(err); - } +const getCategoryById = async (categoryId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at + FROM workplace_categories + WHERE category_id = ?`, + [categoryId] + ); + return rows[0]; }; -/** - * μΉ΄ν…Œκ³ λ¦¬ μˆ˜μ • - */ -const updateCategory = async (categoryId, category, callback) => { - try { - const db = await getDb(); - const { - category_name, - description, - display_order, - is_active, - layout_image - } = category; +const updateCategory = async (categoryId, category) => { + const db = await getDb(); + const { + category_name, + description, + display_order, + is_active, + layout_image + } = category; - const [result] = await db.query( - `UPDATE workplace_categories - SET category_name = ?, description = ?, display_order = ?, is_active = ?, layout_image = ?, updated_at = NOW() - WHERE category_id = ?`, - [category_name, description, display_order, is_active, layout_image, categoryId] - ); + const [result] = await db.query( + `UPDATE workplace_categories + SET category_name = ?, description = ?, display_order = ?, is_active = ?, layout_image = ?, updated_at = NOW() + WHERE category_id = ?`, + [category_name, description, display_order, is_active, layout_image, categoryId] + ); - callback(null, result); - } catch (err) { - callback(err); - } + return result; }; -/** - * μΉ΄ν…Œκ³ λ¦¬ μ‚­μ œ - */ -const deleteCategory = async (categoryId, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `DELETE FROM workplace_categories WHERE category_id = ?`, - [categoryId] - ); - callback(null, result); - } catch (err) { - callback(err); - } +const deleteCategory = async (categoryId) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM workplace_categories WHERE category_id = ?`, + [categoryId] + ); + return result; }; // ==================== μž‘μ—…μž₯ κ΄€λ ¨ ==================== -/** - * μž‘μ—…μž₯ 생성 - */ -const createWorkplace = async (workplace, callback) => { - try { - const db = await getDb(); - const { - category_id = null, - workplace_name, - description = null, - is_active = true, - workplace_purpose = null, - display_priority = 0 - } = workplace; +const createWorkplace = async (workplace) => { + const db = await getDb(); + const { + category_id = null, + workplace_name, + description = null, + is_active = true, + workplace_purpose = null, + display_priority = 0 + } = workplace; - const [result] = await db.query( - `INSERT INTO workplaces - (category_id, workplace_name, description, is_active, workplace_purpose, display_priority) - VALUES (?, ?, ?, ?, ?, ?)`, - [category_id, workplace_name, description, is_active, workplace_purpose, display_priority] - ); + const [result] = await db.query( + `INSERT INTO workplaces + (category_id, workplace_name, description, is_active, workplace_purpose, display_priority) + VALUES (?, ?, ?, ?, ?, ?)`, + [category_id, workplace_name, description, is_active, workplace_purpose, display_priority] + ); - callback(null, result.insertId); - } catch (err) { - callback(err); - } + return result.insertId; }; -/** - * λͺ¨λ“  μž‘μ—…μž₯ 쑰회 (μΉ΄ν…Œκ³ λ¦¬ 정보 포함) - */ -const getAllWorkplaces = async (callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority, - w.layout_image, w.created_at, w.updated_at, - wc.category_name - FROM workplaces w - LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id - ORDER BY wc.display_order ASC, w.display_priority ASC, w.workplace_id DESC` - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getAllWorkplaces = async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority, + w.layout_image, w.created_at, w.updated_at, + wc.category_name + FROM workplaces w + LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id + ORDER BY wc.display_order ASC, w.display_priority ASC, w.workplace_id DESC` + ); + return rows; }; -/** - * ν™œμ„± μž‘μ—…μž₯만 쑰회 - */ -const getActiveWorkplaces = async (callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority, - w.layout_image, w.created_at, w.updated_at, - wc.category_name - FROM workplaces w - LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id - WHERE w.is_active = TRUE - ORDER BY wc.display_order ASC, w.workplace_id DESC` - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getActiveWorkplaces = async () => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority, + w.layout_image, w.created_at, w.updated_at, + wc.category_name + FROM workplaces w + LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id + WHERE w.is_active = TRUE + ORDER BY wc.display_order ASC, w.workplace_id DESC` + ); + return rows; }; -/** - * μΉ΄ν…Œκ³ λ¦¬λ³„ μž‘μ—…μž₯ 쑰회 - */ -const getWorkplacesByCategory = async (categoryId, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority, - w.layout_image, w.created_at, w.updated_at, - wc.category_name - FROM workplaces w - LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id - WHERE w.category_id = ? - ORDER BY w.workplace_id DESC`, - [categoryId] - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getWorkplacesByCategory = async (categoryId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority, + w.layout_image, w.created_at, w.updated_at, + wc.category_name + FROM workplaces w + LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id + WHERE w.category_id = ? + ORDER BY w.workplace_id DESC`, + [categoryId] + ); + return rows; }; -/** - * ID둜 μž‘μ—…μž₯ 쑰회 - */ -const getWorkplaceById = async (workplaceId, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority, - w.layout_image, w.created_at, w.updated_at, - wc.category_name - FROM workplaces w - LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id - WHERE w.workplace_id = ?`, - [workplaceId] - ); - callback(null, rows[0]); - } catch (err) { - callback(err); - } +const getWorkplaceById = async (workplaceId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority, + w.layout_image, w.created_at, w.updated_at, + wc.category_name + FROM workplaces w + LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id + WHERE w.workplace_id = ?`, + [workplaceId] + ); + return rows[0]; }; -/** - * μž‘μ—…μž₯ μˆ˜μ • - */ -const updateWorkplace = async (workplaceId, workplace, callback) => { - try { - const db = await getDb(); - const { - category_id, - workplace_name, - description, - is_active, - workplace_purpose, - display_priority, - layout_image - } = workplace; +const updateWorkplace = async (workplaceId, workplace) => { + const db = await getDb(); + const { + category_id, + workplace_name, + description, + is_active, + workplace_purpose, + display_priority, + layout_image + } = workplace; - const [result] = await db.query( - `UPDATE workplaces - SET category_id = ?, workplace_name = ?, description = ?, is_active = ?, - workplace_purpose = ?, display_priority = ?, layout_image = ?, updated_at = NOW() - WHERE workplace_id = ?`, - [category_id, workplace_name, description, is_active, workplace_purpose, display_priority, layout_image, workplaceId] - ); + const [result] = await db.query( + `UPDATE workplaces + SET category_id = ?, workplace_name = ?, description = ?, is_active = ?, + workplace_purpose = ?, display_priority = ?, layout_image = ?, updated_at = NOW() + WHERE workplace_id = ?`, + [category_id, workplace_name, description, is_active, workplace_purpose, display_priority, layout_image, workplaceId] + ); - callback(null, result); - } catch (err) { - callback(err); - } + return result; }; -/** - * μž‘μ—…μž₯ μ‚­μ œ - */ -const deleteWorkplace = async (workplaceId, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `DELETE FROM workplaces WHERE workplace_id = ?`, - [workplaceId] - ); - callback(null, result); - } catch (err) { - callback(err); - } +const deleteWorkplace = async (workplaceId) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM workplaces WHERE workplace_id = ?`, + [workplaceId] + ); + return result; }; // ==================== μž‘μ—…μž₯ 지도 μ˜μ—­ κ΄€λ ¨ ==================== -/** - * μž‘μ—…μž₯ 지도 μ˜μ—­ 생성 - */ -const createMapRegion = async (region, callback) => { - try { - const db = await getDb(); - const { - workplace_id, - category_id, - x_start, - y_start, - x_end, - y_end, - shape = 'rect', - polygon_points = null - } = region; +const createMapRegion = async (region) => { + const db = await getDb(); + const { + workplace_id, + category_id, + x_start, + y_start, + x_end, + y_end, + shape = 'rect', + polygon_points = null + } = region; - const [result] = await db.query( - `INSERT INTO workplace_map_regions - (workplace_id, category_id, x_start, y_start, x_end, y_end, shape, polygon_points) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, - [workplace_id, category_id, x_start, y_start, x_end, y_end, shape, polygon_points] - ); + const [result] = await db.query( + `INSERT INTO workplace_map_regions + (workplace_id, category_id, x_start, y_start, x_end, y_end, shape, polygon_points) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + [workplace_id, category_id, x_start, y_start, x_end, y_end, shape, polygon_points] + ); - callback(null, result.insertId); - } catch (err) { - callback(err); - } + return result.insertId; }; -/** - * μΉ΄ν…Œκ³ λ¦¬(곡μž₯)별 지도 μ˜μ—­ 쑰회 - */ -const getMapRegionsByCategory = async (categoryId, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT mr.*, w.workplace_name, w.description - FROM workplace_map_regions mr - INNER JOIN workplaces w ON mr.workplace_id = w.workplace_id - WHERE mr.category_id = ? AND w.is_active = TRUE - ORDER BY mr.region_id ASC`, - [categoryId] - ); - callback(null, rows); - } catch (err) { - callback(err); - } +const getMapRegionsByCategory = async (categoryId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT mr.*, w.workplace_name, w.description + FROM workplace_map_regions mr + INNER JOIN workplaces w ON mr.workplace_id = w.workplace_id + WHERE mr.category_id = ? AND w.is_active = TRUE + ORDER BY mr.region_id ASC`, + [categoryId] + ); + return rows; }; -/** - * μž‘μ—…μž₯별 지도 μ˜μ—­ 쑰회 - */ -const getMapRegionByWorkplace = async (workplaceId, callback) => { - try { - const db = await getDb(); - const [rows] = await db.query( - `SELECT * FROM workplace_map_regions WHERE workplace_id = ?`, - [workplaceId] - ); - callback(null, rows[0]); - } catch (err) { - callback(err); - } +const getMapRegionByWorkplace = async (workplaceId) => { + const db = await getDb(); + const [rows] = await db.query( + `SELECT * FROM workplace_map_regions WHERE workplace_id = ?`, + [workplaceId] + ); + return rows[0]; }; -/** - * 지도 μ˜μ—­ μˆ˜μ • - */ -const updateMapRegion = async (regionId, region, callback) => { - try { - const db = await getDb(); - const { - x_start, - y_start, - x_end, - y_end, - shape, - polygon_points - } = region; +const updateMapRegion = async (regionId, region) => { + const db = await getDb(); + const { + x_start, + y_start, + x_end, + y_end, + shape, + polygon_points + } = region; - const [result] = await db.query( - `UPDATE workplace_map_regions - SET x_start = ?, y_start = ?, x_end = ?, y_end = ?, shape = ?, polygon_points = ?, updated_at = NOW() - WHERE region_id = ?`, - [x_start, y_start, x_end, y_end, shape, polygon_points, regionId] - ); + const [result] = await db.query( + `UPDATE workplace_map_regions + SET x_start = ?, y_start = ?, x_end = ?, y_end = ?, shape = ?, polygon_points = ?, updated_at = NOW() + WHERE region_id = ?`, + [x_start, y_start, x_end, y_end, shape, polygon_points, regionId] + ); - callback(null, result); - } catch (err) { - callback(err); - } + return result; }; -/** - * 지도 μ˜μ—­ μ‚­μ œ - */ -const deleteMapRegion = async (regionId, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `DELETE FROM workplace_map_regions WHERE region_id = ?`, - [regionId] - ); - callback(null, result); - } catch (err) { - callback(err); - } +const deleteMapRegion = async (regionId) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM workplace_map_regions WHERE region_id = ?`, + [regionId] + ); + return result; }; -/** - * μž‘μ—…μž₯ μ˜μ—­ 일괄 μ‚­μ œ (μΉ΄ν…Œκ³ λ¦¬λ³„) - */ -const deleteMapRegionsByCategory = async (categoryId, callback) => { - try { - const db = await getDb(); - const [result] = await db.query( - `DELETE FROM workplace_map_regions WHERE category_id = ?`, - [categoryId] - ); - callback(null, result); - } catch (err) { - callback(err); - } +const deleteMapRegionsByCategory = async (categoryId) => { + const db = await getDb(); + const [result] = await db.query( + `DELETE FROM workplace_map_regions WHERE category_id = ?`, + [categoryId] + ); + return result; }; module.exports = { - // μΉ΄ν…Œκ³ λ¦¬ createCategory, getAllCategories, getActiveCategories, getCategoryById, updateCategory, deleteCategory, - - // μž‘μ—…μž₯ createWorkplace, getAllWorkplaces, getActiveWorkplaces, @@ -429,8 +293,6 @@ module.exports = { getWorkplaceById, updateWorkplace, deleteWorkplace, - - // 지도 μ˜μ—­ createMapRegion, getMapRegionsByCategory, getMapRegionByWorkplace, diff --git a/system1-factory/api/services/workReportService.js b/system1-factory/api/services/workReportService.js index f1b0daf..6e88727 100644 --- a/system1-factory/api/services/workReportService.js +++ b/system1-factory/api/services/workReportService.js @@ -24,16 +24,10 @@ const createWorkReportService = async (reportData) => { logger.info('μž‘μ—… λ³΄κ³ μ„œ 생성 μš”μ²­', { count: reports.length }); - const workReport_ids = []; - try { + const workReport_ids = []; for (const report of reports) { - const id = await new Promise((resolve, reject) => { - workReportModel.create(report, (err, insertId) => { - if (err) reject(err); - else resolve(insertId); - }); - }); + const id = await workReportModel.create(report); workReport_ids.push(id); } @@ -66,15 +60,8 @@ const getWorkReportsByDateService = async (date) => { logger.info('μž‘μ—… λ³΄κ³ μ„œ λ‚ μ§œλ³„ 쑰회 μš”μ²­', { date }); try { - const rows = await new Promise((resolve, reject) => { - workReportModel.getAllByDate(date, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - + const rows = await workReportModel.getAllByDate(date); logger.info('μž‘μ—… λ³΄κ³ μ„œ 쑰회 성곡', { date, count: rows.length }); - return rows; } catch (error) { logger.error('μž‘μ—… λ³΄κ³ μ„œ 쑰회 μ‹€νŒ¨', { date, error: error.message }); @@ -96,15 +83,8 @@ const getWorkReportsInRangeService = async (start, end) => { logger.info('μž‘μ—… λ³΄κ³ μ„œ 기간별 쑰회 μš”μ²­', { start, end }); try { - const rows = await new Promise((resolve, reject) => { - workReportModel.getByRange(start, end, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); - + const rows = await workReportModel.getByRange(start, end); logger.info('μž‘μ—… λ³΄κ³ μ„œ 쑰회 성곡', { start, end, count: rows.length }); - return rows; } catch (error) { logger.error('μž‘μ—… λ³΄κ³ μ„œ 쑰회 μ‹€νŒ¨', { start, end, error: error.message }); @@ -123,12 +103,7 @@ const getWorkReportByIdService = async (id) => { logger.info('μž‘μ—… λ³΄κ³ μ„œ 쑰회 μš”μ²­', { report_id: id }); try { - const row = await new Promise((resolve, reject) => { - workReportModel.getById(id, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); + const row = await workReportModel.getById(id); if (!row) { logger.warn('μž‘μ—… λ³΄κ³ μ„œλ₯Ό 찾을 수 μ—†μŒ', { report_id: id }); @@ -136,13 +111,9 @@ const getWorkReportByIdService = async (id) => { } logger.info('μž‘μ—… λ³΄κ³ μ„œ 쑰회 성곡', { report_id: id }); - return row; } catch (error) { - if (error instanceof NotFoundError) { - throw error; - } - + if (error instanceof NotFoundError) throw error; logger.error('μž‘μ—… λ³΄κ³ μ„œ 쑰회 μ‹€νŒ¨', { report_id: id, error: error.message }); throw new DatabaseError('μž‘μ—… λ³΄κ³ μ„œ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€'); } @@ -159,12 +130,7 @@ const updateWorkReportService = async (id, updateData) => { logger.info('μž‘μ—… λ³΄κ³ μ„œ μˆ˜μ • μš”μ²­', { report_id: id }); try { - const changes = await new Promise((resolve, reject) => { - workReportModel.update(id, updateData, (err, affectedRows) => { - if (err) reject(err); - else resolve(affectedRows); - }); - }); + const changes = await workReportModel.update(id, updateData); if (changes === 0) { logger.warn('μž‘μ—… λ³΄κ³ μ„œλ₯Ό 찾을 수 μ—†κ±°λ‚˜ 변경사항 μ—†μŒ', { report_id: id }); @@ -172,13 +138,9 @@ const updateWorkReportService = async (id, updateData) => { } logger.info('μž‘μ—… λ³΄κ³ μ„œ μˆ˜μ • 성곡', { report_id: id, changes }); - return { changes }; } catch (error) { - if (error instanceof NotFoundError) { - throw error; - } - + if (error instanceof NotFoundError) throw error; logger.error('μž‘μ—… λ³΄κ³ μ„œ μˆ˜μ • μ‹€νŒ¨', { report_id: id, error: error.message }); throw new DatabaseError('μž‘μ—… λ³΄κ³ μ„œ μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€'); } @@ -195,12 +157,7 @@ const removeWorkReportService = async (id) => { logger.info('μž‘μ—… λ³΄κ³ μ„œ μ‚­μ œ μš”μ²­', { report_id: id }); try { - const changes = await new Promise((resolve, reject) => { - workReportModel.remove(id, (err, affectedRows) => { - if (err) reject(err); - else resolve(affectedRows); - }); - }); + const changes = await workReportModel.remove(id); if (changes === 0) { logger.warn('μž‘μ—… λ³΄κ³ μ„œλ₯Ό 찾을 수 μ—†μŒ', { report_id: id }); @@ -208,13 +165,9 @@ const removeWorkReportService = async (id) => { } logger.info('μž‘μ—… λ³΄κ³ μ„œ μ‚­μ œ 성곡', { report_id: id, changes }); - return { changes }; } catch (error) { - if (error instanceof NotFoundError) { - throw error; - } - + if (error instanceof NotFoundError) throw error; logger.error('μž‘μ—… λ³΄κ³ μ„œ μ‚­μ œ μ‹€νŒ¨', { report_id: id, error: error.message }); throw new DatabaseError('μž‘μ—… λ³΄κ³ μ„œ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€'); } @@ -237,35 +190,18 @@ const getSummaryService = async (year, month) => { logger.info('μž‘μ—… λ³΄κ³ μ„œ μ›”κ°„ μš”μ•½ 쑰회 μš”μ²­', { year, month, start, end }); try { - const rows = await new Promise((resolve, reject) => { - workReportModel.getByRange(start, end, (err, data) => { - if (err) reject(err); - else resolve(data); - }); - }); + const rows = await workReportModel.getByRange(start, end); if (!rows || rows.length === 0) { logger.warn('μ›”κ°„ μš”μ•½ 데이터 μ—†μŒ', { year, month }); throw new NotFoundError('ν•΄λ‹Ή κΈ°κ°„μ˜ μž‘μ—… λ³΄κ³ μ„œκ°€ μ—†μŠ΅λ‹ˆλ‹€'); } - logger.info('μž‘μ—… λ³΄κ³ μ„œ μ›”κ°„ μš”μ•½ 쑰회 성곡', { - year, - month, - count: rows.length - }); - + logger.info('μž‘μ—… λ³΄κ³ μ„œ μ›”κ°„ μš”μ•½ 쑰회 성곡', { year, month, count: rows.length }); return rows; } catch (error) { - if (error instanceof NotFoundError) { - throw error; - } - - logger.error('μž‘μ—… λ³΄κ³ μ„œ μ›”κ°„ μš”μ•½ 쑰회 μ‹€νŒ¨', { - year, - month, - error: error.message - }); + if (error instanceof NotFoundError) throw error; + logger.error('μž‘μ—… λ³΄κ³ μ„œ μ›”κ°„ μš”μ•½ 쑰회 μ‹€νŒ¨', { year, month, error: error.message }); throw new DatabaseError('μ›”κ°„ μš”μ•½ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€'); } }; @@ -274,7 +210,6 @@ const getSummaryService = async (year, month) => { /** * μž‘μ—… λ³΄κ³ μ„œμ˜ 뢀적합 원인 λͺ©λ‘ 쑰회 - * - error_type_id λ˜λŠ” issue_report_id 쀑 ν•˜λ‚˜λ‘œ μ—°κ²° */ const getReportDefectsService = async (reportId) => { const db = await getDb(); @@ -313,22 +248,16 @@ const getReportDefectsService = async (reportId) => { /** * 뢀적합 원인 μ €μž₯ (전체 ꡐ체) - * - error_type_id λ˜λŠ” issue_report_id 쀑 ν•˜λ‚˜ μ‚¬μš© κ°€λŠ₯ - * - issue_report_id: μ‹ κ³ λœ μ΄μŠˆμ™€ μ—°κ²° - * - error_type_id: κΈ°μ‘΄ 였λ₯˜ μœ ν˜• (λ ˆκ±°μ‹œ) */ const saveReportDefectsService = async (reportId, defects) => { const db = await getDb(); try { await db.query('START TRANSACTION'); - // κΈ°μ‘΄ 뢀적합 원인 μ‚­μ œ await db.execute('DELETE FROM work_report_defects WHERE report_id = ?', [reportId]); - // μƒˆ 뢀적합 원인 μΆ”κ°€ if (defects && defects.length > 0) { for (const defect of defects) { - // issue_report_id > category_id/item_id > error_type_id 순으둜 μš°μ„  await db.execute(` INSERT INTO work_report_defects (report_id, error_type_id, issue_report_id, category_id, item_id, defect_hours, note) VALUES (?, ?, ?, ?, ?, ?, ?) @@ -344,12 +273,10 @@ const saveReportDefectsService = async (reportId, defects) => { } } - // 총 뢀적합 μ‹œκ°„ 계산 및 daily_work_reports μ—…λ°μ΄νŠΈ const totalErrorHours = defects ? defects.reduce((sum, d) => sum + (parseFloat(d.defect_hours) || 0), 0) : 0; - // 첫 번째 defect의 error_type_idλ₯Ό λŒ€ν‘œκ°’μœΌλ‘œ (λ ˆκ±°μ‹œ ν˜Έν™˜) const firstErrorTypeId = defects && defects.length > 0 ? (defects.find(d => d.error_type_id)?.error_type_id || null) : null; @@ -380,7 +307,6 @@ const saveReportDefectsService = async (reportId, defects) => { /** * 뢀적합 원인 μΆ”κ°€ (단일) - * - issue_report_id λ˜λŠ” error_type_id 쀑 ν•˜λ‚˜ μ‚¬μš© */ const addReportDefectService = async (reportId, defectData) => { const db = await getDb(); @@ -396,7 +322,6 @@ const addReportDefectService = async (reportId, defectData) => { defectData.note || null ]); - // 총 뢀적합 μ‹œκ°„ μ—…λ°μ΄νŠΈ await updateTotalErrorHours(reportId); logger.info('뢀적합 원인 μΆ”κ°€ 성곡', { reportId, defectId: result.insertId }); @@ -413,7 +338,6 @@ const addReportDefectService = async (reportId, defectData) => { const removeReportDefectService = async (defectId) => { const db = await getDb(); try { - // report_id λ¨Όμ € 쑰회 const [defect] = await db.execute('SELECT report_id FROM work_report_defects WHERE defect_id = ?', [defectId]); if (defect.length === 0) { throw new NotFoundError('뢀적합 원인을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€'); @@ -421,10 +345,7 @@ const removeReportDefectService = async (defectId) => { const reportId = defect[0].report_id; - // μ‚­μ œ await db.execute('DELETE FROM work_report_defects WHERE defect_id = ?', [defectId]); - - // 총 뢀적합 μ‹œκ°„ μ—…λ°μ΄νŠΈ await updateTotalErrorHours(reportId); logger.info('뢀적합 원인 μ‚­μ œ 성곡', { defectId, reportId }); @@ -438,7 +359,6 @@ const removeReportDefectService = async (defectId) => { /** * 총 뢀적합 μ‹œκ°„ μ—…λ°μ΄νŠΈ 헬퍼 - * - issue_report_idκ°€ μžˆλŠ” κ²½μš°λ„ κ³ λ € */ const updateTotalErrorHours = async (reportId) => { const db = await getDb(); @@ -450,8 +370,6 @@ const updateTotalErrorHours = async (reportId) => { const totalErrorHours = result[0].total || 0; - // 첫 번째 뢀적합 μ›μΈμ˜ error_type_idλ₯Ό λŒ€ν‘œκ°’μœΌλ‘œ μ‚¬μš© (λ ˆκ±°μ‹œ ν˜Έν™˜) - // issue_report_id만 μžˆλŠ” 경우 error_type_idλŠ” null const [firstDefect] = await db.execute(` SELECT error_type_id FROM work_report_defects WHERE report_id = ? AND error_type_id IS NOT NULL @@ -480,7 +398,6 @@ module.exports = { updateWorkReportService, removeWorkReportService, getSummaryService, - // 뢀적합 원인 관리 getReportDefectsService, saveReportDefectsService, addReportDefectService, diff --git a/system1-factory/api/tests/unit/services/workReportService.test.js b/system1-factory/api/tests/unit/services/workReportService.test.js index 7567d31..aab243a 100644 --- a/system1-factory/api/tests/unit/services/workReportService.test.js +++ b/system1-factory/api/tests/unit/services/workReportService.test.js @@ -19,7 +19,6 @@ describe('WorkReportService', () => { describe('createWorkReportService', () => { it('단일 λ³΄κ³ μ„œλ₯Ό μ„±κ³΅μ μœΌλ‘œ 생성해야 함', async () => { - // Arrange const reportData = { report_date: '2025-12-11', worker_id: 1, @@ -29,46 +28,32 @@ describe('WorkReportService', () => { work_content: 'κΈ°λŠ₯ 개발' }; - // workReportModel.createκ°€ 콜백 ν˜•νƒœμ΄λ―€λ‘œ λͺ¨ν‚Ή μ„€μ • - workReportModel.create = jest.fn((data, callback) => { - callback(null, 123); // insertId = 123 - }); + workReportModel.create.mockResolvedValue(123); - // Act const result = await workReportService.createWorkReportService(reportData); - // Assert expect(result).toEqual({ workReport_ids: [123] }); expect(workReportModel.create).toHaveBeenCalledTimes(1); - expect(workReportModel.create).toHaveBeenCalledWith( - reportData, - expect.any(Function) - ); + expect(workReportModel.create).toHaveBeenCalledWith(reportData); }); it('닀쀑 λ³΄κ³ μ„œλ₯Ό μ„±κ³΅μ μœΌλ‘œ 생성해야 함', async () => { - // Arrange const reportsData = [ { report_date: '2025-12-11', worker_id: 1, work_hours: 8 }, { report_date: '2025-12-11', worker_id: 2, work_hours: 7 } ]; - let callCount = 0; - workReportModel.create = jest.fn((data, callback) => { - callCount++; - callback(null, 100 + callCount); - }); + workReportModel.create + .mockResolvedValueOnce(101) + .mockResolvedValueOnce(102); - // Act const result = await workReportService.createWorkReportService(reportsData); - // Assert expect(result).toEqual({ workReport_ids: [101, 102] }); expect(workReportModel.create).toHaveBeenCalledTimes(2); }); it('빈 배열이면 ValidationErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Act & Assert await expect(workReportService.createWorkReportService([])) .rejects.toThrow(ValidationError); @@ -77,14 +62,10 @@ describe('WorkReportService', () => { }); it('DB 였λ₯˜ μ‹œ DatabaseErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Arrange const reportData = { report_date: '2025-12-11', worker_id: 1 }; - workReportModel.create = jest.fn((data, callback) => { - callback(new Error('DB connection failed'), null); - }); + workReportModel.create.mockRejectedValue(new Error('DB connection failed')); - // Act & Assert await expect(workReportService.createWorkReportService(reportData)) .rejects.toThrow(DatabaseError); }); @@ -92,24 +73,18 @@ describe('WorkReportService', () => { describe('getWorkReportsByDateService', () => { it('λ‚ μ§œλ‘œ λ³΄κ³ μ„œλ₯Ό μ‘°νšŒν•΄μ•Ό 함', async () => { - // Arrange const date = '2025-12-11'; const mockReports = mockWorkReports.filter(r => r.report_date === date); - workReportModel.getAllByDate = jest.fn((date, callback) => { - callback(null, mockReports); - }); + workReportModel.getAllByDate.mockResolvedValue(mockReports); - // Act const result = await workReportService.getWorkReportsByDateService(date); - // Assert expect(result).toEqual(mockReports); - expect(workReportModel.getAllByDate).toHaveBeenCalledWith(date, expect.any(Function)); + expect(workReportModel.getAllByDate).toHaveBeenCalledWith(date); }); it('λ‚ μ§œκ°€ μ—†μœΌλ©΄ ValidationErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Act & Assert await expect(workReportService.getWorkReportsByDateService(null)) .rejects.toThrow(ValidationError); @@ -118,12 +93,8 @@ describe('WorkReportService', () => { }); it('DB 였λ₯˜ μ‹œ DatabaseErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Arrange - workReportModel.getAllByDate = jest.fn((date, callback) => { - callback(new Error('DB error'), null); - }); + workReportModel.getAllByDate.mockRejectedValue(new Error('DB error')); - // Act & Assert await expect(workReportService.getWorkReportsByDateService('2025-12-11')) .rejects.toThrow(DatabaseError); }); @@ -131,28 +102,19 @@ describe('WorkReportService', () => { describe('getWorkReportByIdService', () => { it('ID둜 λ³΄κ³ μ„œλ₯Ό μ‘°νšŒν•΄μ•Ό 함', async () => { - // Arrange const mockReport = mockWorkReports[0]; - workReportModel.getById = jest.fn((id, callback) => { - callback(null, mockReport); - }); + workReportModel.getById.mockResolvedValue(mockReport); - // Act const result = await workReportService.getWorkReportByIdService(1); - // Assert expect(result).toEqual(mockReport); - expect(workReportModel.getById).toHaveBeenCalledWith(1, expect.any(Function)); + expect(workReportModel.getById).toHaveBeenCalledWith(1); }); it('λ³΄κ³ μ„œκ°€ μ—†μœΌλ©΄ NotFoundErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Arrange - workReportModel.getById = jest.fn((id, callback) => { - callback(null, null); - }); + workReportModel.getById.mockResolvedValue(null); - // Act & Assert await expect(workReportService.getWorkReportByIdService(999)) .rejects.toThrow(NotFoundError); @@ -161,7 +123,6 @@ describe('WorkReportService', () => { }); it('IDκ°€ μ—†μœΌλ©΄ ValidationErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Act & Assert await expect(workReportService.getWorkReportByIdService(null)) .rejects.toThrow(ValidationError); }); @@ -169,38 +130,24 @@ describe('WorkReportService', () => { describe('updateWorkReportService', () => { it('λ³΄κ³ μ„œλ₯Ό μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •ν•΄μ•Ό 함', async () => { - // Arrange const updateData = { work_hours: 9 }; - workReportModel.update = jest.fn((id, data, callback) => { - callback(null, 1); // affectedRows = 1 - }); + workReportModel.update.mockResolvedValue(1); - // Act const result = await workReportService.updateWorkReportService(123, updateData); - // Assert expect(result).toEqual({ changes: 1 }); - expect(workReportModel.update).toHaveBeenCalledWith( - 123, - updateData, - expect.any(Function) - ); + expect(workReportModel.update).toHaveBeenCalledWith(123, updateData); }); it('μˆ˜μ •ν•  λ³΄κ³ μ„œκ°€ μ—†μœΌλ©΄ NotFoundErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Arrange - workReportModel.update = jest.fn((id, data, callback) => { - callback(null, 0); // affectedRows = 0 - }); + workReportModel.update.mockResolvedValue(0); - // Act & Assert await expect(workReportService.updateWorkReportService(999, {})) .rejects.toThrow(NotFoundError); }); it('IDκ°€ μ—†μœΌλ©΄ ValidationErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Act & Assert await expect(workReportService.updateWorkReportService(null, {})) .rejects.toThrow(ValidationError); }); @@ -208,32 +155,22 @@ describe('WorkReportService', () => { describe('removeWorkReportService', () => { it('λ³΄κ³ μ„œλ₯Ό μ„±κ³΅μ μœΌλ‘œ μ‚­μ œν•΄μ•Ό 함', async () => { - // Arrange - workReportModel.remove = jest.fn((id, callback) => { - callback(null, 1); - }); + workReportModel.remove.mockResolvedValue(1); - // Act const result = await workReportService.removeWorkReportService(123); - // Assert expect(result).toEqual({ changes: 1 }); - expect(workReportModel.remove).toHaveBeenCalledWith(123, expect.any(Function)); + expect(workReportModel.remove).toHaveBeenCalledWith(123); }); it('μ‚­μ œν•  λ³΄κ³ μ„œκ°€ μ—†μœΌλ©΄ NotFoundErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Arrange - workReportModel.remove = jest.fn((id, callback) => { - callback(null, 0); - }); + workReportModel.remove.mockResolvedValue(0); - // Act & Assert await expect(workReportService.removeWorkReportService(999)) .rejects.toThrow(NotFoundError); }); it('IDκ°€ μ—†μœΌλ©΄ ValidationErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Act & Assert await expect(workReportService.removeWorkReportService(null)) .rejects.toThrow(ValidationError); }); @@ -241,34 +178,23 @@ describe('WorkReportService', () => { describe('getWorkReportsInRangeService', () => { it('κΈ°κ°„μœΌλ‘œ λ³΄κ³ μ„œλ₯Ό μ‘°νšŒν•΄μ•Ό 함', async () => { - // Arrange const start = '2025-12-01'; const end = '2025-12-31'; - workReportModel.getByRange = jest.fn((start, end, callback) => { - callback(null, mockWorkReports); - }); + workReportModel.getByRange.mockResolvedValue(mockWorkReports); - // Act const result = await workReportService.getWorkReportsInRangeService(start, end); - // Assert expect(result).toEqual(mockWorkReports); - expect(workReportModel.getByRange).toHaveBeenCalledWith( - start, - end, - expect.any(Function) - ); + expect(workReportModel.getByRange).toHaveBeenCalledWith(start, end); }); it('μ‹œμž‘μΌμ΄ μ—†μœΌλ©΄ ValidationErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Act & Assert await expect(workReportService.getWorkReportsInRangeService(null, '2025-12-31')) .rejects.toThrow(ValidationError); }); it('μ’…λ£ŒμΌμ΄ μ—†μœΌλ©΄ ValidationErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Act & Assert await expect(workReportService.getWorkReportsInRangeService('2025-12-01', null)) .rejects.toThrow(ValidationError); }); @@ -276,41 +202,30 @@ describe('WorkReportService', () => { describe('getSummaryService', () => { it('μ›”κ°„ μš”μ•½μ„ μ‘°νšŒν•΄μ•Ό 함', async () => { - // Arrange const year = '2025'; const month = '12'; - workReportModel.getByRange = jest.fn((start, end, callback) => { - callback(null, mockWorkReports); - }); + workReportModel.getByRange.mockResolvedValue(mockWorkReports); - // Act const result = await workReportService.getSummaryService(year, month); - // Assert expect(result).toEqual(mockWorkReports); expect(workReportModel.getByRange).toHaveBeenCalled(); }); it('연도가 μ—†μœΌλ©΄ ValidationErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Act & Assert await expect(workReportService.getSummaryService(null, '12')) .rejects.toThrow(ValidationError); }); it('월이 μ—†μœΌλ©΄ ValidationErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Act & Assert await expect(workReportService.getSummaryService('2025', null)) .rejects.toThrow(ValidationError); }); it('데이터가 μ—†μœΌλ©΄ NotFoundErrorλ₯Ό λ˜μ Έμ•Ό 함', async () => { - // Arrange - workReportModel.getByRange = jest.fn((start, end, callback) => { - callback(null, []); - }); + workReportModel.getByRange.mockResolvedValue([]); - // Act & Assert await expect(workReportService.getSummaryService('2025', '12')) .rejects.toThrow(NotFoundError);