diff --git a/api.hyungi.net/controllers/dailyIssueReportController.js b/api.hyungi.net/controllers/dailyIssueReportController.js index cf0907e..6dca260 100644 --- a/api.hyungi.net/controllers/dailyIssueReportController.js +++ b/api.hyungi.net/controllers/dailyIssueReportController.js @@ -1,58 +1,64 @@ -// /controllers/dailyIssueReportController.js +/** + * 일일 이슈 보고서 관리 컨트롤러 + * + * 일일 이슈 보고서 CRUD API 엔드포인트 핸들러 + * + * @author TK-FB-Project + * @since 2025-12-11 + */ + const dailyIssueReportService = require('../services/dailyIssueReportService'); +const { asyncHandler } = require('../middlewares/errorHandler'); /** - * 1. CREATE: 일일 이슈 보고서 생성 (Service Layer 사용) + * 일일 이슈 보고서 생성 */ -const createDailyIssueReport = async (req, res) => { - try { - // 프론트엔드에서 worker_ids로 보내주기로 약속함 - const issueData = { ...req.body, worker_ids: req.body.worker_ids || req.body.worker_id }; - - const result = await dailyIssueReportService.createDailyIssueReportService(issueData); - - res.status(201).json({ success: true, ...result }); - } catch (err) { - console.error('💥 이슈 보고서 생성 컨트롤러 오류:', err); - res.status(400).json({ success: false, error: err.message }); - } -}; +const createDailyIssueReport = asyncHandler(async (req, res) => { + // 프론트엔드에서 worker_ids 또는 worker_id로 보낼 수 있음 + const issueData = { + ...req.body, + worker_ids: req.body.worker_ids || req.body.worker_id + }; + + const result = await dailyIssueReportService.createDailyIssueReportService(issueData); + + res.status(201).json({ + success: true, + data: result, + message: result.message + }); +}); /** - * 2. READ BY DATE: 날짜별 이슈 조회 (Service Layer 사용) + * 날짜별 이슈 조회 */ -const getDailyIssuesByDate = async (req, res) => { - try { - const { date } = req.query; - const issues = await dailyIssueReportService.getDailyIssuesByDateService(date); - res.json(issues); - } catch (err) { - console.error('💥 이슈 보고서 조회 컨트롤러 오류:', err); - res.status(500).json({ success: false, error: err.message }); - } -}; +const getDailyIssuesByDate = asyncHandler(async (req, res) => { + const { date } = req.query; + const issues = await dailyIssueReportService.getDailyIssuesByDateService(date); + + res.json({ + success: true, + data: issues, + message: '이슈 보고서 조회 성공' + }); +}); /** - * 3. DELETE: 이슈 보고서 삭제 (Service Layer 사용) + * 이슈 보고서 삭제 */ -const removeDailyIssue = async (req, res) => { - try { - const { id } = req.params; - const result = await dailyIssueReportService.removeDailyIssueService(id); - res.json({ success: true, ...result }); - } catch (err) { - console.error('💥 이슈 보고서 삭제 컨트롤러 오류:', err); - const statusCode = err.statusCode || 500; - res.status(statusCode).json({ success: false, error: err.message }); - } -}; +const removeDailyIssue = asyncHandler(async (req, res) => { + const { id } = req.params; + const result = await dailyIssueReportService.removeDailyIssueService(id); -// 레거시 함수들은 더 이상 라우팅되지 않으므로 제거하거나 주석 처리 가능 -// exports.getDailyIssueById = ... -// exports.updateDailyIssue = ... + res.json({ + success: true, + data: result, + message: result.message + }); +}); module.exports = { createDailyIssueReport, getDailyIssuesByDate, - removeDailyIssue, -}; \ No newline at end of file + removeDailyIssue +}; diff --git a/api.hyungi.net/controllers/uploadController.js b/api.hyungi.net/controllers/uploadController.js index 0a28af6..8d2b75c 100644 --- a/api.hyungi.net/controllers/uploadController.js +++ b/api.hyungi.net/controllers/uploadController.js @@ -1,26 +1,38 @@ -const uploadModel = require('../models/uploadModel'); +/** + * 문서 업로드 관리 컨트롤러 + * + * 파일 업로드 및 문서 메타데이터 CRUD API 엔드포인트 핸들러 + * + * @author TK-FB-Project + * @since 2025-12-11 + */ -// 1. 문서 업로드 -exports.createUpload = async (req, res) => { - try { - const doc = req.body; - const id = await new Promise((resolve, reject) => { - uploadModel.create(doc, (err, insertId) => (err ? reject(err) : resolve(insertId))); - }); - res.status(201).json({ success: true, id }); - } catch (err) { - res.status(500).json({ error: err.message || String(err) }); - } -}; +const uploadService = require('../services/uploadService'); +const { asyncHandler } = require('../middlewares/errorHandler'); -// 2. 전체 업로드 문서 조회 -exports.getUploads = async (req, res) => { - try { - const rows = await new Promise((resolve, reject) => { - uploadModel.getAll((err, data) => (err ? reject(err) : resolve(data))); - }); - res.json(rows); - } catch (err) { - res.status(500).json({ error: err.message || String(err) }); - } -}; \ No newline at end of file +/** + * 문서 업로드 + */ +exports.createUpload = asyncHandler(async (req, res) => { + const doc = req.body; + const result = await uploadService.createUploadService(doc); + + res.status(201).json({ + success: true, + data: result, + message: '문서가 성공적으로 업로드되었습니다' + }); +}); + +/** + * 전체 업로드 문서 조회 + */ +exports.getUploads = asyncHandler(async (req, res) => { + const rows = await uploadService.getAllUploadsService(); + + res.json({ + success: true, + data: rows, + message: '업로드 문서 목록 조회 성공' + }); +}); diff --git a/api.hyungi.net/services/dailyIssueReportService.js b/api.hyungi.net/services/dailyIssueReportService.js index abeb7d8..f8e8f92 100644 --- a/api.hyungi.net/services/dailyIssueReportService.js +++ b/api.hyungi.net/services/dailyIssueReportService.js @@ -1,24 +1,55 @@ -// /services/dailyIssueReportService.js +/** + * 일일 이슈 보고서 관리 서비스 + * + * 일일 이슈 보고서 생성, 조회, 삭제 관련 비즈니스 로직 처리 + * + * @author TK-FB-Project + * @since 2025-12-11 + */ + const dailyIssueReportModel = require('../models/dailyIssueReportModel'); +const { ValidationError, NotFoundError, DatabaseError } = require('../utils/errors'); +const logger = require('../utils/logger'); /** - * 일일 이슈 보고서를 생성하는 비즈니스 로직을 처리합니다. + * 일일 이슈 보고서 생성 + * * 한 번에 여러 작업자에 대해 동일한 이슈를 등록할 수 있습니다. + * * @param {object} issueData - 컨트롤러에서 전달된 이슈 데이터 + * @param {string} issueData.date - 이슈 발생 날짜 (YYYY-MM-DD) + * @param {number} issueData.project_id - 프로젝트 ID + * @param {string} issueData.start_time - 이슈 시작 시간 + * @param {string} issueData.end_time - 이슈 종료 시간 + * @param {number} issueData.issue_type_id - 이슈 유형 ID + * @param {number[]} issueData.worker_ids - 작업자 ID 배열 * @returns {Promise} 생성 결과 */ const createDailyIssueReportService = async (issueData) => { const { date, project_id, start_time, end_time, issue_type_id, worker_ids } = issueData; - // 1. 유효성 검사 + // 필수 필드 검증 if (!date || !project_id || !start_time || !end_time || !issue_type_id || !worker_ids) { - throw new Error('필수 필드가 누락되었습니다.'); - } - if (!Array.isArray(worker_ids) || worker_ids.length === 0) { - throw new Error('worker_ids는 최소 한 명 이상의 작업자를 포함하는 배열이어야 합니다.'); + throw new ValidationError('필수 필드가 누락되었습니다', { + required: ['date', 'project_id', 'start_time', 'end_time', 'issue_type_id', 'worker_ids'], + received: { date, project_id, start_time, end_time, issue_type_id, worker_ids: !!worker_ids } + }); } - // 2. 모델에 전달할 데이터 준비 + if (!Array.isArray(worker_ids) || worker_ids.length === 0) { + throw new ValidationError('worker_ids는 최소 한 명 이상의 작업자를 포함하는 배열이어야 합니다', { + received: { worker_ids, isArray: Array.isArray(worker_ids), length: worker_ids?.length } + }); + } + + logger.info('이슈 보고서 생성 요청', { + date, + project_id, + issue_type_id, + worker_count: worker_ids.length + }); + + // 모델에 전달할 데이터 준비 const reportsToCreate = worker_ids.map(worker_id => ({ date, project_id, @@ -28,66 +59,112 @@ const createDailyIssueReportService = async (issueData) => { worker_id })); - // 3. 모델 함수 호출 (모델에 createMany와 같은 함수가 필요) try { const insertedIds = await dailyIssueReportModel.createMany(reportsToCreate); + + logger.info('이슈 보고서 생성 성공', { + count: insertedIds.length, + issue_report_ids: insertedIds + }); + return { - message: `${insertedIds.length}개의 이슈 보고서가 성공적으로 생성되었습니다.`, + message: `${insertedIds.length}개의 이슈 보고서가 성공적으로 생성되었습니다`, issue_report_ids: insertedIds }; } catch (error) { - console.error('[Service] 이슈 보고서 생성 중 오류 발생:', error); - throw error; + logger.error('이슈 보고서 생성 실패', { + date, + project_id, + worker_ids, + error: error.message + }); + throw new DatabaseError('이슈 보고서 생성 중 오류가 발생했습니다'); } }; /** - * 특정 날짜의 모든 이슈 보고서를 조회합니다. + * 특정 날짜의 모든 이슈 보고서 조회 + * * @param {string} date - 조회할 날짜 (YYYY-MM-DD) * @returns {Promise} 조회된 이슈 보고서 배열 */ const getDailyIssuesByDateService = async (date) => { if (!date) { - throw new Error('조회를 위해 날짜(date)는 필수입니다.'); + throw new ValidationError('날짜가 필요합니다', { + required: ['date'], + received: { date } + }); } + + logger.info('이슈 보고서 날짜별 조회 요청', { date }); + try { const issues = await dailyIssueReportModel.getAllByDate(date); + + logger.info('이슈 보고서 조회 성공', { + date, + count: issues.length + }); + return issues; } catch (error) { - console.error(`[Service] ${date}의 이슈 보고서 조회 중 오류 발생:`, error); - throw error; + logger.error('이슈 보고서 조회 실패', { + date, + error: error.message + }); + throw new DatabaseError('이슈 보고서 조회 중 오류가 발생했습니다'); } }; /** - * 특정 ID의 이슈 보고서를 삭제합니다. - * @param {string} issueId - 삭제할 이슈 보고서의 ID + * 특정 ID의 이슈 보고서 삭제 + * + * @param {string|number} issueId - 삭제할 이슈 보고서의 ID * @returns {Promise} 삭제 결과 */ const removeDailyIssueService = async (issueId) => { if (!issueId) { - throw new Error('삭제를 위해 이슈 보고서 ID가 필요합니다.'); + throw new ValidationError('이슈 보고서 ID가 필요합니다', { + required: ['issue_id'], + received: { issueId } + }); } + + logger.info('이슈 보고서 삭제 요청', { issue_id: issueId }); + try { const affectedRows = await dailyIssueReportModel.remove(issueId); + if (affectedRows === 0) { - const notFoundError = new Error('삭제할 이슈 보고서를 찾을 수 없습니다.'); - notFoundError.statusCode = 404; - throw notFoundError; + logger.warn('삭제할 이슈 보고서를 찾을 수 없음', { issue_id: issueId }); + throw new NotFoundError('삭제할 이슈 보고서를 찾을 수 없습니다'); } - return { - message: '이슈 보고서가 성공적으로 삭제되었습니다.', - deleted_id: issueId, - affected_rows: affectedRows + + logger.info('이슈 보고서 삭제 성공', { + issue_id: issueId, + affected_rows: affectedRows + }); + + return { + message: '이슈 보고서가 성공적으로 삭제되었습니다', + deleted_id: issueId, + affected_rows: affectedRows }; } catch (error) { - console.error(`[Service] 이슈 보고서(id: ${issueId}) 삭제 중 오류 발생:`, error); - throw error; + if (error instanceof NotFoundError) { + throw error; + } + + logger.error('이슈 보고서 삭제 실패', { + issue_id: issueId, + error: error.message + }); + throw new DatabaseError('이슈 보고서 삭제 중 오류가 발생했습니다'); } }; module.exports = { createDailyIssueReportService, getDailyIssuesByDateService, - removeDailyIssueService, -}; \ No newline at end of file + removeDailyIssueService +}; diff --git a/api.hyungi.net/services/uploadService.js b/api.hyungi.net/services/uploadService.js new file mode 100644 index 0000000..89951cc --- /dev/null +++ b/api.hyungi.net/services/uploadService.js @@ -0,0 +1,96 @@ +/** + * 문서 업로드 관리 서비스 + * + * 파일 업로드 및 문서 메타데이터 관리 관련 비즈니스 로직 처리 + * + * @author TK-FB-Project + * @since 2025-12-11 + */ + +const uploadModel = require('../models/uploadModel'); +const { ValidationError, DatabaseError } = require('../utils/errors'); +const logger = require('../utils/logger'); + +/** + * 문서 업로드 생성 + */ +const createUploadService = async (uploadData) => { + const { + title, + tags, + description, + original_name, + stored_name, + file_path, + file_type, + file_size, + submitted_by + } = uploadData; + + // 필수 필드 검증 + if (!original_name || !stored_name || !file_path) { + throw new ValidationError('필수 필드가 누락되었습니다', { + required: ['original_name', 'stored_name', 'file_path'], + received: { original_name, stored_name, file_path } + }); + } + + logger.info('문서 업로드 생성 요청', { + title, + original_name, + file_type, + file_size, + submitted_by + }); + + try { + const insertId = await new Promise((resolve, reject) => { + uploadModel.create(uploadData, (err, id) => { + if (err) reject(err); + else resolve(id); + }); + }); + + logger.info('문서 업로드 생성 성공', { + upload_id: insertId, + original_name, + file_size + }); + + return { upload_id: insertId }; + } catch (error) { + logger.error('문서 업로드 생성 실패', { + original_name, + error: error.message + }); + throw new DatabaseError('문서 업로드 생성 중 오류가 발생했습니다'); + } +}; + +/** + * 전체 업로드 문서 조회 + */ +const getAllUploadsService = async () => { + logger.info('업로드 문서 목록 조회 요청'); + + try { + const rows = await new Promise((resolve, reject) => { + uploadModel.getAll((err, data) => { + if (err) reject(err); + else resolve(data); + }); + }); + + logger.info('업로드 문서 목록 조회 성공', { count: rows.length }); + + return rows; + } catch (error) { + logger.error('업로드 문서 목록 조회 실패', { error: error.message }); + throw new DatabaseError('업로드 문서 목록 조회 중 오류가 발생했습니다'); + } +}; + +module.exports = { + createUploadService, + getAllUploadsService +};