diff --git a/api.hyungi.net/controllers/dailyWorkReportController.js b/api.hyungi.net/controllers/dailyWorkReportController.js index 2227fd8..3dff628 100644 --- a/api.hyungi.net/controllers/dailyWorkReportController.js +++ b/api.hyungi.net/controllers/dailyWorkReportController.js @@ -182,110 +182,29 @@ const removeMyEntry = (req, res) => { }; /** - * πŸ“Š μž‘μ—…λ³΄κ³ μ„œ 쑰회 (κΆŒν•œλ³„ 전체 쑰회 지원 - 핡심 μˆ˜μ •!) + * πŸ“Š μž‘μ—…λ³΄κ³ μ„œ 쑰회 (V2 - Service Layer μ‚¬μš©) */ -const getDailyWorkReports = (req, res) => { - const { date, worker_id, created_by: requested_created_by, view_all, admin, all, no_filter, ignore_created_by } = req.query; - const current_user_id = req.user?.user_id || req.user?.id; - const user_access_level = req.user?.access_level; +const getDailyWorkReports = async (req, res) => { + try { + const userInfo = { + user_id: req.user?.user_id || req.user?.id, + role: req.user?.role || 'user' // 기본값을 'user'둜 μ„€μ •ν•˜μ—¬ μ•ˆμ „ν•˜κ²Œ 처리 + }; - if (!current_user_id) { - return res.status(401).json({ - error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' - }); - } + if (!userInfo.user_id) { + return res.status(401).json({ error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' }); + } - // 🎯 κΆŒν•œλ³„ 필터링 둜직 κ°œμ„  - const isAdmin = user_access_level === 'system' || user_access_level === 'admin'; - const hasViewAllFlag = view_all === 'true' || admin === 'true' || all === 'true' || - no_filter === 'true' || ignore_created_by === 'true' || - requested_created_by === 'all' || requested_created_by === ''; - - const canViewAll = isAdmin || hasViewAllFlag; - - // κ΄€λ¦¬μžκ°€ μ•„λ‹ˆκ³  전체 쑰회 ν”Œλž˜κ·Έλ„ μ—†μœΌλ©΄ 본인 μž‘μ„±λΆ„μœΌλ‘œ μ œν•œ - let final_created_by = null; - if (!canViewAll) { - final_created_by = requested_created_by || current_user_id; - } else if (requested_created_by && requested_created_by !== 'all' && requested_created_by !== '') { - final_created_by = requested_created_by; - } + const reports = await dailyWorkReportService.getDailyWorkReportsService(req.query, userInfo); + + res.json(reports); - console.log('πŸ“Š μž‘μ—…λ³΄κ³ μ„œ 쑰회 μš”μ²­:', { - date, - worker_id, - requested_created_by, - current_user_id, - user_access_level, - isAdmin, - hasViewAllFlag, - canViewAll, - final_created_by - }); - - if (date && final_created_by) { - // λ‚ μ§œ + μž‘μ„±μžλ³„ 쑰회 - dailyWorkReportModel.getByDateAndCreator(date, final_created_by, (err, data) => { - if (err) { - console.error('μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - error: 'μž‘μ—…λ³΄κ³ μ„œ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - - console.log(`πŸ“Š λ‚ μ§œ+μž‘μ„±μžλ³„ 쑰회 κ²°κ³Ό: ${data.length}개`); - res.json(data); - }); - } else if (date && worker_id) { - // λ‚ μ§œ + μž‘μ—…μžλ³„ 쑰회 - dailyWorkReportModel.getByDateAndWorker(date, worker_id, (err, data) => { - if (err) { - console.error('μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - error: 'μž‘μ—…λ³΄κ³ μ„œ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - - // 🎯 κΆŒν•œλ³„ 필터링 - let finalData = data; - if (!canViewAll) { - finalData = data.filter(report => report.created_by === current_user_id); - console.log(`πŸ“Š κΆŒν•œ 필터링: 전체 ${data.length}개 β†’ ${finalData.length}개`); - } else { - console.log(`πŸ“Š κ΄€λ¦¬μž/전체 쑰회 κΆŒν•œ: ${data.length}개 전체 λ°˜ν™˜`); - } - - res.json(finalData); - }); - } else if (date) { - // λ‚ μ§œλ³„ 쑰회 - dailyWorkReportModel.getByDate(date, (err, data) => { - if (err) { - console.error('μž‘μ—…λ³΄κ³ μ„œ 쑰회 였λ₯˜:', err); - return res.status(500).json({ - error: 'μž‘μ—…λ³΄κ³ μ„œ 쑰회 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - - // 🎯 κΆŒν•œλ³„ 필터링 - let finalData = data; - if (!canViewAll) { - finalData = data.filter(report => report.created_by === current_user_id); - console.log(`πŸ“Š κΆŒν•œ 필터링: 전체 ${data.length}개 β†’ ${finalData.length}개`); - } else { - console.log(`πŸ“Š κ΄€λ¦¬μž/전체 쑰회 κΆŒν•œ: ${data.length}개 전체 λ°˜ν™˜`); - } - - res.json(finalData); - }); - } else { + } catch (error) { + console.error('πŸ’₯ μž‘μ—…λ³΄κ³ μ„œ 쑰회 컨트둀러 였λ₯˜:', error.message); res.status(400).json({ - error: 'λ‚ μ§œ(date) νŒŒλΌλ―Έν„°κ°€ ν•„μš”ν•©λ‹ˆλ‹€.', - example: 'date=2024-06-16', - optional: ['worker_id', 'created_by', 'view_all', 'admin', 'all'] + success: false, + error: 'μž‘μ—…λ³΄κ³ μ„œ μ‘°νšŒμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.', + details: error.message }); } }; @@ -498,94 +417,75 @@ const getMonthlySummary = (req, res) => { }; /** - * ✏️ μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • + * ✏️ μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • (V2 - Service Layer μ‚¬μš©) */ -const updateWorkReport = (req, res) => { - const { id } = req.params; - const updateData = req.body; - const updated_by = req.user?.user_id || req.user?.id; +const updateWorkReport = async (req, res) => { + try { + const { id: reportId } = req.params; + const updateData = req.body; + const userInfo = { + user_id: req.user?.user_id || req.user?.id, + role: req.user?.role || 'user' + }; - if (!updated_by) { - return res.status(401).json({ - error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' + if (!userInfo.user_id) { + return res.status(401).json({ error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' }); + } + + const result = await dailyWorkReportService.updateWorkReportService(reportId, updateData, userInfo); + + res.json({ + success: true, + timestamp: new Date().toISOString(), + ...result + }); + + } catch (error) { + console.error(`πŸ’₯ μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • 컨트둀러 였λ₯˜ (id: ${req.params.id}):`, error.message); + const statusCode = error.statusCode || 400; + res.status(statusCode).json({ + success: false, + error: 'μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ •μ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.', + details: error.message }); } - - updateData.updated_by = updated_by; - - console.log(`✏️ μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • μš”μ²­: id=${id}, μˆ˜μ •μž=${updated_by}`); - - dailyWorkReportModel.updateById(id, updateData, (err, affectedRows) => { - if (err) { - console.error('μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • 였λ₯˜:', err); - return res.status(500).json({ - error: 'μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - - if (affectedRows === 0) { - return res.status(404).json({ - error: 'μˆ˜μ •ν•  μž‘μ—…λ³΄κ³ μ„œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.', - id: id - }); - } - - console.log(`βœ… μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • μ™„λ£Œ: id=${id}`); - res.json({ - message: 'μž‘μ—…λ³΄κ³ μ„œκ°€ μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - id: id, - affected_rows: affectedRows, - updated_by, - timestamp: new Date().toISOString() - }); - }); }; /** - * πŸ—‘οΈ νŠΉμ • μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ + * πŸ—‘οΈ νŠΉμ • μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ (V2 - Service Layer μ‚¬μš©) */ -const removeDailyWorkReport = (req, res) => { - const { id } = req.params; - const deleted_by = req.user?.user_id || req.user?.id; +const removeDailyWorkReport = async (req, res) => { + try { + const { id: reportId } = req.params; + const userInfo = { + user_id: req.user?.user_id || req.user?.id, + }; - if (!deleted_by) { - return res.status(401).json({ - error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' + if (!userInfo.user_id) { + return res.status(401).json({ error: 'μ‚¬μš©μž 인증 정보가 μ—†μŠ΅λ‹ˆλ‹€.' }); + } + + const result = await dailyWorkReportService.removeDailyWorkReportService(reportId, userInfo); + + res.json({ + success: true, + timestamp: new Date().toISOString(), + ...result + }); + + } catch (error) { + console.error(`πŸ’₯ μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ 컨트둀러 였λ₯˜ (id: ${req.params.id}):`, error.message); + const statusCode = error.statusCode || 400; + res.status(statusCode).json({ + success: false, + error: 'μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œμ— μ‹€νŒ¨ν–ˆμŠ΅λ‹ˆλ‹€.', + details: error.message }); } - - console.log(`πŸ—‘οΈ μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ μš”μ²­: id=${id}, μ‚­μ œμž=${deleted_by}`); - - dailyWorkReportModel.removeById(id, deleted_by, (err, affectedRows) => { - if (err) { - console.error('μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ 였λ₯˜:', err); - return res.status(500).json({ - error: 'μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.', - details: err.message - }); - } - - if (affectedRows === 0) { - return res.status(404).json({ - error: 'μ‚­μ œν•  μž‘μ—…λ³΄κ³ μ„œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.', - id: id - }); - } - - console.log(`βœ… μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ μ™„λ£Œ: id=${id}`); - res.json({ - message: 'μž‘μ—…λ³΄κ³ μ„œκ°€ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', - id: id, - affected_rows: affectedRows, - deleted_by, - timestamp: new Date().toISOString() - }); - }); }; /** - * πŸ—‘οΈ μž‘μ—…μžμ˜ νŠΉμ • λ‚ μ§œ 전체 μ‚­μ œ + * ��️ μž‘μ—…μžμ˜ νŠΉμ • λ‚ μ§œ 전체 μ‚­μ œ */ const removeDailyWorkReportByDateAndWorker = (req, res) => { const { date, worker_id } = req.params; diff --git a/api.hyungi.net/models/dailyWorkReportModel.js b/api.hyungi.net/models/dailyWorkReportModel.js index 67e5cd8..f3fe502 100644 --- a/api.hyungi.net/models/dailyWorkReportModel.js +++ b/api.hyungi.net/models/dailyWorkReportModel.js @@ -845,6 +845,163 @@ const createReportEntries = async ({ report_date, worker_id, entries }) => { } }; +/** + * [V2] 곡톡 SELECT 쿼리 (μƒˆλ‘œμš΄ μŠ€ν‚€λ§ˆ κΈ°μ€€) + */ +const getSelectQueryV2 = () => ` + SELECT + dwr.report_id, + dwr.report_date, + dwr.worker_id, + dwr.project_id, + dwr.task_id, + dwr.work_hours, + dwr.is_error, + dwr.error_type_code_id, + dwr.created_by_user_id, + w.worker_name, + p.project_name, + t.task_name, + c.code_name as error_type_name, + u.name as created_by_name, + dwr.created_at + 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 tasks t ON dwr.task_id = t.task_id + LEFT JOIN users u ON dwr.created_by_user_id = u.user_id + LEFT JOIN codes c ON dwr.error_type_code_id = c.code_id AND c.code_type_id = 'ERROR_TYPE' +`; + +/** + * [V2] μ˜΅μ…˜ 기반으둜 μž‘μ—… λ³΄κ³ μ„œλ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€. (Promise 기반) + * @param {object} options - 쑰회 쑰건 (date, worker_id, created_by_user_id λ“±) + * @returns {Promise} 쑰회된 μž‘μ—… λ³΄κ³ μ„œ λ°°μ—΄ + */ +const getReportsWithOptions = async (options) => { + const db = await getDb(); + let whereConditions = []; + let queryParams = []; + + if (options.date) { + whereConditions.push('dwr.report_date = ?'); + queryParams.push(options.date); + } + if (options.worker_id) { + whereConditions.push('dwr.worker_id = ?'); + queryParams.push(options.worker_id); + } + if (options.created_by_user_id) { + whereConditions.push('dwr.created_by_user_id = ?'); + queryParams.push(options.created_by_user_id); + } + // ν•„μš”μ— 따라 λ‹€λ₯Έ 쑰건 μΆ”κ°€ κ°€λŠ₯ (project_id λ“±) + + if (whereConditions.length === 0) { + throw new Error('쑰회 쑰건이 ν•˜λ‚˜ 이상 ν•„μš”ν•©λ‹ˆλ‹€.'); + } + + 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('λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μž‘μ—… λ³΄κ³ μ„œλ₯Ό μ‘°νšŒν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); + } +}; + +/** + * [V2] IDλ₯Ό κΈ°μ€€μœΌλ‘œ νŠΉμ • μž‘μ—… λ³΄κ³ μ„œ ν•­λͺ©μ„ μˆ˜μ •ν•©λ‹ˆλ‹€. (Promise 기반) + * @param {string} reportId - μˆ˜μ •ν•  λ³΄κ³ μ„œμ˜ ID + * @param {object} updateData - μˆ˜μ •ν•  ν•„λ“œμ™€ κ°’ + * @returns {Promise} 영ν–₯을 받은 ν–‰μ˜ 수 + */ +const updateReportById = async (reportId, updateData) => { + const db = await getDb(); + + // ν—ˆμš©λœ ν•„λ“œ λͺ©λ‘ (λ³΄μ•ˆ 및 μ•ˆμ •μ„±) + const allowedFields = ['project_id', 'task_id', 'work_hours', 'is_error', 'error_type_code_id']; + const setClauses = []; + const queryParams = []; + + for (const field of allowedFields) { + if (updateData[field] !== undefined) { + setClauses.push(`${field} = ?`); + queryParams.push(updateData[field]); + } + } + + // updated_by_user_idλŠ” 항상 μ—…λ°μ΄νŠΈ + if (updateData.updated_by_user_id) { + setClauses.push('updated_by_user_id = ?'); + queryParams.push(updateData.updated_by_user_id); + } + + if (setClauses.length === 0) { + throw new Error('μˆ˜μ •ν•  데이터가 μ—†μŠ΅λ‹ˆλ‹€.'); + } + + queryParams.push(reportId); + + const sql = `UPDATE daily_work_reports SET ${setClauses.join(', ')} WHERE report_id = ?`; + + try { + const [result] = await db.query(sql, queryParams); + return result.affectedRows; + } catch (err) { + console.error(`[Model] μž‘μ—… λ³΄κ³ μ„œ μˆ˜μ • 였λ₯˜ (id: ${reportId}):`, err); + throw new Error('λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μž‘μ—… λ³΄κ³ μ„œλ₯Ό μˆ˜μ •ν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); + } +}; + +/** + * [V2] IDλ₯Ό κΈ°μ€€μœΌλ‘œ νŠΉμ • μž‘μ—… λ³΄κ³ μ„œλ₯Ό μ‚­μ œν•©λ‹ˆλ‹€. (Promise 기반) + * @param {string} reportId - μ‚­μ œν•  λ³΄κ³ μ„œ ID + * @param {number} deletedByUserId - μ‚­μ œλ₯Ό μˆ˜ν–‰ν•˜λŠ” μ‚¬μš©μž ID + * @returns {Promise} 영ν–₯을 받은 ν–‰μ˜ 수 + */ +const removeReportById = async (reportId, deletedByUserId) => { + const db = await getDb(); + const conn = await db.getConnection(); + + try { + await conn.beginTransaction(); + + // 감사 둜그λ₯Ό μœ„ν•΄ μ‚­μ œ μ „ 정보 쑰회 + const [reportInfo] = await conn.query('SELECT * FROM daily_work_reports WHERE report_id = ?', [reportId]); + + // μ‹€μ œ μ‚­μ œ μž‘μ—… + const [result] = await conn.query('DELETE FROM daily_work_reports WHERE report_id = ?', [reportId]); + + // 감사 둜그 μΆ”κ°€ (μ‚­μ œλœ ν•­λͺ©μ΄ 있고, μ‚­μ œμžκ°€ λͺ…μ‹œλœ 경우) + if (reportInfo.length > 0 && deletedByUserId) { + try { + await conn.query( + `INSERT INTO work_report_audit_log (action, report_id, old_values, changed_by, change_reason) VALUES (?, ?, ?, ?, ?)`, + ['DELETE', reportId, JSON.stringify(reportInfo[0]), deletedByUserId, 'Manual deletion by user'] + ); + } catch (auditErr) { + console.warn('감사 둜그 μΆ”κ°€ μ‹€νŒ¨:', auditErr.message); + // 감사 둜그 μ‹€νŒ¨κ°€ 전체 νŠΈλžœμž­μ…˜μ„ λ‘€λ°±μ‹œν‚€μ§€λŠ” μ•ŠμŒ + } + } + + await conn.commit(); + return result.affectedRows; + + } catch (err) { + await conn.rollback(); + console.error(`[Model] μž‘μ—… λ³΄κ³ μ„œ μ‚­μ œ 였λ₯˜ (id: ${reportId}):`, err); + throw new Error('λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ μž‘μ—… λ³΄κ³ μ„œλ₯Ό μ‚­μ œν•˜λŠ” 쀑 였λ₯˜κ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.'); + } finally { + conn.release(); + } +}; + + // λͺ¨λ“  ν•¨μˆ˜ 내보내기 (κΈ°μ‘΄ κΈ°λŠ₯ + λˆ„μ  κΈ°λŠ₯) module.exports = { // πŸ“‹ λ§ˆμŠ€ν„° 데이터 @@ -880,5 +1037,8 @@ module.exports = { getStatistics, // μƒˆλ‘œ μΆ”κ°€λœ V2 ν•¨μˆ˜ - createReportEntries + createReportEntries, + getReportsWithOptions, + updateReportById, + removeReportById }; \ No newline at end of file diff --git a/api.hyungi.net/services/dailyWorkReportService.js b/api.hyungi.net/services/dailyWorkReportService.js index f08ec6f..9986ba4 100644 --- a/api.hyungi.net/services/dailyWorkReportService.js +++ b/api.hyungi.net/services/dailyWorkReportService.js @@ -79,6 +79,135 @@ const createDailyWorkReportService = async (reportData) => { } }; +/** + * μ‚¬μš©μž κΆŒν•œκ³Ό μš”μ²­ νŒŒλΌλ―Έν„°μ— 따라 일일 μž‘μ—… λ³΄κ³ μ„œλ₯Ό μ‘°νšŒν•˜λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€. + * @param {object} queryParams - μ»¨νŠΈλ‘€λŸ¬μ—μ„œ μ „λ‹¬λœ 쿼리 νŒŒλΌλ―Έν„° + * @param {object} userInfo - μš”μ²­μ„ 보낸 μ‚¬μš©μžμ˜ 정보 (id, role λ“±) + * @returns {Promise} 쑰회된 μž‘μ—… λ³΄κ³ μ„œ λ°°μ—΄ + */ +const getDailyWorkReportsService = async (queryParams, userInfo) => { + const { date, worker_id, created_by: requested_created_by, view_all } = queryParams; + const { user_id: current_user_id, role } = userInfo; + + if (!date) { + throw new Error('쑰회λ₯Ό μœ„ν•΄ λ‚ μ§œ(date)λŠ” ν•„μˆ˜μž…λ‹ˆλ‹€.'); + } + + // κ΄€λ¦¬μž μ—¬λΆ€ 확인 + const isAdmin = role === 'system' || role === 'admin'; + const canViewAll = isAdmin || view_all === 'true'; + + // λͺ¨λΈμ— 전달할 쑰회 μ˜΅μ…˜ 객체 생성 + const options = { date }; + + if (worker_id) { + options.worker_id = parseInt(worker_id); + } + + // μ΅œμ’…μ μœΌλ‘œ 필터링할 μž‘μ„±μž ID κ²°μ • + if (!canViewAll) { + // κ΄€λ¦¬μžκ°€ μ•„λ‹ˆλ©΄ μžμ‹ μ˜ λ°μ΄ν„°λ§Œ λ³΄κ±°λ‚˜, λͺ…μ‹œμ μœΌλ‘œ μš”μ²­λœ μžμ‹ μ˜ ID만 ν—ˆμš© + options.created_by_user_id = requested_created_by ? Math.min(requested_created_by, current_user_id) : current_user_id; + } else if (requested_created_by) { + // κ΄€λ¦¬μžλŠ” λ‹€λ₯Έ μ‚¬λžŒμ˜ 데이터도 쑰회 κ°€λŠ₯ + options.created_by_user_id = parseInt(requested_created_by); + } + // created_by_user_idκ°€ λͺ…μ‹œλ˜μ§€ μ•ŠμœΌλ©΄ λͺ¨λ“  μž‘μ„±μžμ˜ 데이터λ₯Ό 쑰회 + + console.log('πŸ“Š [Service] μž‘μ—…λ³΄κ³ μ„œ 쑰회 μš”μ²­:', { ...options, requester: current_user_id, isAdmin }); + + try { + // λͺ¨λΈ ν•¨μˆ˜ 호좜 + const reports = await dailyWorkReportModel.getReportsWithOptions(options); + console.log(`βœ… [Service] μž‘μ—…λ³΄κ³ μ„œ ${reports.length}개 쑰회 성곡`); + return reports; + } catch (error) { + console.error('[Service] μž‘μ—…λ³΄κ³ μ„œ 쑰회 쀑 였λ₯˜ λ°œμƒ:', error); + throw error; + } +}; + +/** + * νŠΉμ • μž‘μ—… λ³΄κ³ μ„œ ν•­λͺ©μ„ μˆ˜μ •ν•˜λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€. + * @param {string} reportId - μˆ˜μ •ν•  λ³΄κ³ μ„œμ˜ ID + * @param {object} updateData - μˆ˜μ •ν•  데이터 + * @param {object} userInfo - μš”μ²­μ„ 보낸 μ‚¬μš©μžμ˜ 정보 + * @returns {Promise} μˆ˜μ • κ²°κ³Ό + */ +const updateWorkReportService = async (reportId, updateData, userInfo) => { + const { user_id: updated_by } = userInfo; + + if (!reportId || !updateData || Object.keys(updateData).length === 0) { + throw new Error('μˆ˜μ •μ„ μœ„ν•΄ λ³΄κ³ μ„œ ID와 μˆ˜μ •ν•  데이터가 ν•„μš”ν•©λ‹ˆλ‹€.'); + } + + const modelUpdateData = { ...updateData, updated_by_user_id: updated_by }; + + console.log(`✏️ [Service] μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • μš”μ²­: id=${reportId}`); + + try { + const affectedRows = await dailyWorkReportModel.updateReportById(reportId, modelUpdateData); + + if (affectedRows === 0) { + // μ—λŸ¬λ₯Ό λ°œμƒμ‹œμΌœ μ»¨νŠΈλ‘€λŸ¬μ—μ„œ 404 처리λ₯Ό ν•  수 μžˆλ„λ‘ 함 + const notFoundError = new Error('μˆ˜μ •ν•  μž‘μ—…λ³΄κ³ μ„œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.'); + notFoundError.statusCode = 404; + throw notFoundError; + } + + console.log(`βœ… [Service] μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • 성곡: id=${reportId}`); + return { + message: 'μž‘μ—…λ³΄κ³ μ„œκ°€ μ„±κ³΅μ μœΌλ‘œ μˆ˜μ •λ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + report_id: reportId, + affected_rows: affectedRows + }; + } catch (error) { + console.error(`[Service] μž‘μ—…λ³΄κ³ μ„œ μˆ˜μ • 쀑 였λ₯˜ λ°œμƒ (id: ${reportId}):`, error); + throw error; + } +}; + +/** + * νŠΉμ • μž‘μ—… λ³΄κ³ μ„œ ν•­λͺ©μ„ μ‚­μ œν•˜λŠ” λΉ„μ¦ˆλ‹ˆμŠ€ λ‘œμ§μ„ μ²˜λ¦¬ν•©λ‹ˆλ‹€. + * @param {string} reportId - μ‚­μ œν•  λ³΄κ³ μ„œμ˜ ID + * @param {object} userInfo - μš”μ²­μ„ 보낸 μ‚¬μš©μžμ˜ 정보 + * @returns {Promise} μ‚­μ œ κ²°κ³Ό + */ +const removeDailyWorkReportService = async (reportId, userInfo) => { + const { user_id: deleted_by } = userInfo; + + if (!reportId) { + throw new Error('μ‚­μ œλ₯Ό μœ„ν•΄ λ³΄κ³ μ„œ IDκ°€ ν•„μš”ν•©λ‹ˆλ‹€.'); + } + + console.log(`πŸ—‘οΈ [Service] μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ μš”μ²­: id=${reportId}`); + + try { + // λͺ¨λΈ ν•¨μˆ˜λŠ” μ‚­μ œ μ „ κΆŒν•œ 검사λ₯Ό μœ„ν•΄ deleted_by 정보λ₯Ό 받을 수 μžˆμŠ΅λ‹ˆλ‹€ (ν˜„μž¬ λͺ¨λΈμ—μ„œλŠ” λ―Έμ‚¬μš©). + const affectedRows = await dailyWorkReportModel.removeReportById(reportId, deleted_by); + + if (affectedRows === 0) { + const notFoundError = new Error('μ‚­μ œν•  μž‘μ—…λ³΄κ³ μ„œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€.'); + notFoundError.statusCode = 404; + throw notFoundError; + } + + console.log(`βœ… [Service] μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ 성곡: id=${reportId}`); + return { + message: 'μž‘μ—…λ³΄κ³ μ„œκ°€ μ„±κ³΅μ μœΌλ‘œ μ‚­μ œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.', + report_id: reportId, + affected_rows: affectedRows + }; + } catch (error) { + console.error(`[Service] μž‘μ—…λ³΄κ³ μ„œ μ‚­μ œ 쀑 였λ₯˜ λ°œμƒ (id: ${reportId}):`, error); + throw error; + } +}; + + module.exports = { createDailyWorkReportService, + getDailyWorkReportsService, + updateWorkReportService, + removeDailyWorkReportService, }; \ No newline at end of file