refactor: vacationRequest 콜백→async/await 전환
- 모델: 콜백 래퍼 제거, 결과 직접 반환 (async/await 네이티브) - 컨트롤러: 중첩 콜백 → 플랫한 async/await 구조 - console.error → logger.error 전환 - 코드 566줄 → 247줄 (모델) + 컨트롤러 대폭 간소화 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -4,8 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const vacationRequestModel = require('../models/vacationRequestModel');
|
const vacationRequestModel = require('../models/vacationRequestModel');
|
||||||
// TODO: workerVacationBalanceModel 구현 필요
|
const logger = require('../utils/logger');
|
||||||
// const workerVacationBalanceModel = require('../models/workerVacationBalanceModel');
|
|
||||||
|
|
||||||
const vacationRequestController = {
|
const vacationRequestController = {
|
||||||
/**
|
/**
|
||||||
@@ -16,81 +15,33 @@ const vacationRequestController = {
|
|||||||
const { worker_id, vacation_type_id, start_date, end_date, days_used, reason } = req.body;
|
const { worker_id, vacation_type_id, start_date, end_date, days_used, reason } = req.body;
|
||||||
const requested_by = req.user.user_id;
|
const requested_by = req.user.user_id;
|
||||||
|
|
||||||
// 필수 필드 검증
|
|
||||||
if (!worker_id || !vacation_type_id || !start_date || !end_date || !days_used) {
|
if (!worker_id || !vacation_type_id || !start_date || !end_date || !days_used) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({ success: false, message: '필수 필드가 누락되었습니다' });
|
||||||
success: false,
|
|
||||||
message: '필수 필드가 누락되었습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 날짜 유효성 검증
|
if (new Date(end_date) < new Date(start_date)) {
|
||||||
const startDate = new Date(start_date);
|
return res.status(400).json({ success: false, message: '종료일은 시작일보다 이후여야 합니다' });
|
||||||
const endDate = new Date(end_date);
|
|
||||||
|
|
||||||
if (endDate < startDate) {
|
|
||||||
return res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
message: '종료일은 시작일보다 이후여야 합니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 기간 중복 체크
|
// 기간 중복 체크
|
||||||
vacationRequestModel.checkOverlap(worker_id, start_date, end_date, null, (err, results) => {
|
const overlapRows = await vacationRequestModel.checkOverlap(worker_id, start_date, end_date);
|
||||||
if (err) {
|
if (overlapRows[0].count > 0) {
|
||||||
console.error('기간 중복 체크 오류:', err);
|
return res.status(400).json({ success: false, message: '해당 기간에 이미 신청된 휴가가 있습니다' });
|
||||||
return res.status(500).json({
|
}
|
||||||
success: false,
|
|
||||||
message: '기간 중복 체크 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results[0].count > 0) {
|
const result = await vacationRequestModel.create({
|
||||||
return res.status(400).json({
|
worker_id, vacation_type_id, start_date, end_date,
|
||||||
success: false,
|
days_used, reason: reason || null, status: 'pending', requested_by
|
||||||
message: '해당 기간에 이미 신청된 휴가가 있습니다'
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 잔여 연차 확인 로직 구현 필요
|
res.status(201).json({
|
||||||
// 현재는 잔여 연차 확인 없이 신청 가능
|
success: true,
|
||||||
|
message: '휴가 신청이 완료되었습니다',
|
||||||
// 휴가 신청 생성
|
data: { request_id: result.insertId }
|
||||||
const requestData = {
|
|
||||||
worker_id,
|
|
||||||
vacation_type_id,
|
|
||||||
start_date,
|
|
||||||
end_date,
|
|
||||||
days_used,
|
|
||||||
reason: reason || null,
|
|
||||||
status: 'pending',
|
|
||||||
requested_by
|
|
||||||
};
|
|
||||||
|
|
||||||
vacationRequestModel.create(requestData, (err, result) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('휴가 신청 생성 오류:', err);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '휴가 신청 생성 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.status(201).json({
|
|
||||||
success: true,
|
|
||||||
message: '휴가 신청이 완료되었습니다',
|
|
||||||
data: {
|
|
||||||
request_id: result.insertId
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('휴가 신청 생성 오류:', error);
|
logger.error('휴가 신청 생성 오류:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
||||||
success: false,
|
|
||||||
message: '서버 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -112,33 +63,15 @@ const vacationRequestController = {
|
|||||||
if (req.user.worker_id) {
|
if (req.user.worker_id) {
|
||||||
filters.worker_id = req.user.worker_id;
|
filters.worker_id = req.user.worker_id;
|
||||||
} else {
|
} else {
|
||||||
return res.status(403).json({
|
return res.status(403).json({ success: false, message: '권한이 없습니다' });
|
||||||
success: false,
|
|
||||||
message: '권한이 없습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vacationRequestModel.getAll(filters, (err, results) => {
|
const results = await vacationRequestModel.getAll(filters);
|
||||||
if (err) {
|
res.json({ success: true, data: results });
|
||||||
console.error('휴가 신청 목록 조회 오류:', err);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '휴가 신청 목록 조회 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
data: results
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('휴가 신청 목록 조회 오류:', error);
|
logger.error('휴가 신청 목록 조회 오류:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
||||||
success: false,
|
|
||||||
message: '서버 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -147,45 +80,22 @@ const vacationRequestController = {
|
|||||||
*/
|
*/
|
||||||
async getRequestById(req, res) {
|
async getRequestById(req, res) {
|
||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const results = await vacationRequestModel.getById(req.params.id);
|
||||||
|
|
||||||
vacationRequestModel.getById(id, (err, results) => {
|
if (results.length === 0) {
|
||||||
if (err) {
|
return res.status(404).json({ success: false, message: '해당 휴가 신청을 찾을 수 없습니다' });
|
||||||
console.error('휴가 신청 조회 오류:', err);
|
}
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '휴가 신청 조회 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results.length === 0) {
|
const request = results[0];
|
||||||
return res.status(404).json({
|
|
||||||
success: false,
|
|
||||||
message: '해당 휴가 신청을 찾을 수 없습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = results[0];
|
if (req.user.access_level !== 'system' && req.user.worker_id !== request.worker_id) {
|
||||||
|
return res.status(403).json({ success: false, message: '권한이 없습니다' });
|
||||||
|
}
|
||||||
|
|
||||||
// 권한 검증: 관리자 또는 본인만 조회 가능
|
res.json({ success: true, data: request });
|
||||||
if (req.user.access_level !== 'system' && req.user.worker_id !== request.worker_id) {
|
|
||||||
return res.status(403).json({
|
|
||||||
success: false,
|
|
||||||
message: '권한이 없습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
data: request
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('휴가 신청 조회 오류:', error);
|
logger.error('휴가 신청 조회 오류:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
||||||
success: false,
|
|
||||||
message: '서버 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -197,114 +107,45 @@ const vacationRequestController = {
|
|||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
const { start_date, end_date, days_used, reason } = req.body;
|
const { start_date, end_date, days_used, reason } = req.body;
|
||||||
|
|
||||||
// 기존 신청 조회
|
const results = await vacationRequestModel.getById(id);
|
||||||
vacationRequestModel.getById(id, (err, results) => {
|
if (results.length === 0) {
|
||||||
if (err) {
|
return res.status(404).json({ success: false, message: '해당 휴가 신청을 찾을 수 없습니다' });
|
||||||
console.error('휴가 신청 조회 오류:', err);
|
}
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
const existingRequest = results[0];
|
||||||
message: '휴가 신청 조회 중 오류가 발생했습니다'
|
|
||||||
});
|
if (req.user.access_level !== 'system' && req.user.worker_id !== existingRequest.worker_id) {
|
||||||
|
return res.status(403).json({ success: false, message: '권한이 없습니다' });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (existingRequest.status !== 'pending') {
|
||||||
|
return res.status(400).json({ success: false, message: '승인/거부된 신청은 수정할 수 없습니다' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData = {};
|
||||||
|
if (start_date) updateData.start_date = start_date;
|
||||||
|
if (end_date) updateData.end_date = end_date;
|
||||||
|
if (days_used) updateData.days_used = days_used;
|
||||||
|
if (reason !== undefined) updateData.reason = reason;
|
||||||
|
|
||||||
|
// 날짜가 변경된 경우 중복 체크
|
||||||
|
if (start_date || end_date) {
|
||||||
|
const newStartDate = start_date || existingRequest.start_date;
|
||||||
|
const newEndDate = end_date || existingRequest.end_date;
|
||||||
|
|
||||||
|
const overlapRows = await vacationRequestModel.checkOverlap(
|
||||||
|
existingRequest.worker_id, newStartDate, newEndDate, id
|
||||||
|
);
|
||||||
|
if (overlapRows[0].count > 0) {
|
||||||
|
return res.status(400).json({ success: false, message: '해당 기간에 이미 신청된 휴가가 있습니다' });
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (results.length === 0) {
|
await vacationRequestModel.update(id, updateData);
|
||||||
return res.status(404).json({
|
res.json({ success: true, message: '휴가 신청이 수정되었습니다' });
|
||||||
success: false,
|
|
||||||
message: '해당 휴가 신청을 찾을 수 없습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingRequest = results[0];
|
|
||||||
|
|
||||||
// 권한 검증
|
|
||||||
if (req.user.access_level !== 'system' && req.user.worker_id !== existingRequest.worker_id) {
|
|
||||||
return res.status(403).json({
|
|
||||||
success: false,
|
|
||||||
message: '권한이 없습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 대기 중인 신청만 수정 가능
|
|
||||||
if (existingRequest.status !== 'pending') {
|
|
||||||
return res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
message: '승인/거부된 신청은 수정할 수 없습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateData = {};
|
|
||||||
if (start_date) updateData.start_date = start_date;
|
|
||||||
if (end_date) updateData.end_date = end_date;
|
|
||||||
if (days_used) updateData.days_used = days_used;
|
|
||||||
if (reason !== undefined) updateData.reason = reason;
|
|
||||||
|
|
||||||
// 날짜가 변경된 경우 중복 체크
|
|
||||||
if (start_date || end_date) {
|
|
||||||
const newStartDate = start_date || existingRequest.start_date;
|
|
||||||
const newEndDate = end_date || existingRequest.end_date;
|
|
||||||
|
|
||||||
vacationRequestModel.checkOverlap(
|
|
||||||
existingRequest.worker_id,
|
|
||||||
newStartDate,
|
|
||||||
newEndDate,
|
|
||||||
id,
|
|
||||||
(err, overlapResults) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('기간 중복 체크 오류:', err);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '기간 중복 체크 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overlapResults[0].count > 0) {
|
|
||||||
return res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
message: '해당 기간에 이미 신청된 휴가가 있습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 수정 실행
|
|
||||||
vacationRequestModel.update(id, updateData, (err, result) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('휴가 신청 수정 오류:', err);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '휴가 신청 수정 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
message: '휴가 신청이 수정되었습니다'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// 날짜 변경 없이 바로 수정
|
|
||||||
vacationRequestModel.update(id, updateData, (err, result) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('휴가 신청 수정 오류:', err);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '휴가 신청 수정 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
message: '휴가 신청이 수정되었습니다'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('휴가 신청 수정 오류:', error);
|
logger.error('휴가 신청 수정 오류:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
||||||
success: false,
|
|
||||||
message: '서버 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -315,62 +156,26 @@ const vacationRequestController = {
|
|||||||
try {
|
try {
|
||||||
const { id } = req.params;
|
const { id } = req.params;
|
||||||
|
|
||||||
// 기존 신청 조회
|
const results = await vacationRequestModel.getById(id);
|
||||||
vacationRequestModel.getById(id, (err, results) => {
|
if (results.length === 0) {
|
||||||
if (err) {
|
return res.status(404).json({ success: false, message: '해당 휴가 신청을 찾을 수 없습니다' });
|
||||||
console.error('휴가 신청 조회 오류:', err);
|
}
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '휴가 신청 조회 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results.length === 0) {
|
const existingRequest = results[0];
|
||||||
return res.status(404).json({
|
|
||||||
success: false,
|
|
||||||
message: '해당 휴가 신청을 찾을 수 없습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingRequest = results[0];
|
if (req.user.access_level !== 'system' && req.user.worker_id !== existingRequest.worker_id) {
|
||||||
|
return res.status(403).json({ success: false, message: '권한이 없습니다' });
|
||||||
|
}
|
||||||
|
|
||||||
// 권한 검증
|
if (existingRequest.status !== 'pending') {
|
||||||
if (req.user.access_level !== 'system' && req.user.worker_id !== existingRequest.worker_id) {
|
return res.status(400).json({ success: false, message: '승인/거부된 신청은 삭제할 수 없습니다' });
|
||||||
return res.status(403).json({
|
}
|
||||||
success: false,
|
|
||||||
message: '권한이 없습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 대기 중인 신청만 삭제 가능
|
await vacationRequestModel.delete(id);
|
||||||
if (existingRequest.status !== 'pending') {
|
res.json({ success: true, message: '휴가 신청이 삭제되었습니다' });
|
||||||
return res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
message: '승인/거부된 신청은 삭제할 수 없습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
vacationRequestModel.delete(id, (err, result) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('휴가 신청 삭제 오류:', err);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '휴가 신청 삭제 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
message: '휴가 신청이 삭제되었습니다'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('휴가 신청 삭제 오류:', error);
|
logger.error('휴가 신청 삭제 오류:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
||||||
success: false,
|
|
||||||
message: '서버 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -383,71 +188,24 @@ const vacationRequestController = {
|
|||||||
const { review_note } = req.body;
|
const { review_note } = req.body;
|
||||||
const reviewed_by = req.user.user_id;
|
const reviewed_by = req.user.user_id;
|
||||||
|
|
||||||
// 관리자 권한 확인
|
|
||||||
if (req.user.access_level !== 'system') {
|
if (req.user.access_level !== 'system') {
|
||||||
return res.status(403).json({
|
return res.status(403).json({ success: false, message: '관리자만 승인할 수 있습니다' });
|
||||||
success: false,
|
|
||||||
message: '관리자만 승인할 수 있습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 기존 신청 조회
|
const results = await vacationRequestModel.getById(id);
|
||||||
vacationRequestModel.getById(id, (err, results) => {
|
if (results.length === 0) {
|
||||||
if (err) {
|
return res.status(404).json({ success: false, message: '해당 휴가 신청을 찾을 수 없습니다' });
|
||||||
console.error('휴가 신청 조회 오류:', err);
|
}
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '휴가 신청 조회 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results.length === 0) {
|
if (results[0].status !== 'pending') {
|
||||||
return res.status(404).json({
|
return res.status(400).json({ success: false, message: '이미 처리된 신청입니다' });
|
||||||
success: false,
|
}
|
||||||
message: '해당 휴가 신청을 찾을 수 없습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = results[0];
|
await vacationRequestModel.updateStatus(id, { status: 'approved', reviewed_by, review_note });
|
||||||
|
res.json({ success: true, message: '휴가 신청이 승인되었습니다' });
|
||||||
if (request.status !== 'pending') {
|
|
||||||
return res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
message: '이미 처리된 신청입니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 상태 업데이트
|
|
||||||
const statusData = {
|
|
||||||
status: 'approved',
|
|
||||||
reviewed_by,
|
|
||||||
review_note
|
|
||||||
};
|
|
||||||
|
|
||||||
vacationRequestModel.updateStatus(id, statusData, (err, result) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('휴가 승인 오류:', err);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '휴가 승인 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: 잔여 연차에서 차감 로직 구현 필요
|
|
||||||
// 현재는 연차 차감 없이 승인만 처리
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
message: '휴가 신청이 승인되었습니다'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('휴가 승인 오류:', error);
|
logger.error('휴가 승인 오류:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
||||||
success: false,
|
|
||||||
message: '서버 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -460,68 +218,24 @@ const vacationRequestController = {
|
|||||||
const { review_note } = req.body;
|
const { review_note } = req.body;
|
||||||
const reviewed_by = req.user.user_id;
|
const reviewed_by = req.user.user_id;
|
||||||
|
|
||||||
// 관리자 권한 확인
|
|
||||||
if (req.user.access_level !== 'system') {
|
if (req.user.access_level !== 'system') {
|
||||||
return res.status(403).json({
|
return res.status(403).json({ success: false, message: '관리자만 거부할 수 있습니다' });
|
||||||
success: false,
|
|
||||||
message: '관리자만 거부할 수 있습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 기존 신청 조회
|
const results = await vacationRequestModel.getById(id);
|
||||||
vacationRequestModel.getById(id, (err, results) => {
|
if (results.length === 0) {
|
||||||
if (err) {
|
return res.status(404).json({ success: false, message: '해당 휴가 신청을 찾을 수 없습니다' });
|
||||||
console.error('휴가 신청 조회 오류:', err);
|
}
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '휴가 신청 조회 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results.length === 0) {
|
if (results[0].status !== 'pending') {
|
||||||
return res.status(404).json({
|
return res.status(400).json({ success: false, message: '이미 처리된 신청입니다' });
|
||||||
success: false,
|
}
|
||||||
message: '해당 휴가 신청을 찾을 수 없습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = results[0];
|
await vacationRequestModel.updateStatus(id, { status: 'rejected', reviewed_by, review_note });
|
||||||
|
res.json({ success: true, message: '휴가 신청이 거부되었습니다' });
|
||||||
if (request.status !== 'pending') {
|
|
||||||
return res.status(400).json({
|
|
||||||
success: false,
|
|
||||||
message: '이미 처리된 신청입니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 상태 업데이트
|
|
||||||
const statusData = {
|
|
||||||
status: 'rejected',
|
|
||||||
reviewed_by,
|
|
||||||
review_note
|
|
||||||
};
|
|
||||||
|
|
||||||
vacationRequestModel.updateStatus(id, statusData, (err, result) => {
|
|
||||||
if (err) {
|
|
||||||
console.error('휴가 거부 오류:', err);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '휴가 거부 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
message: '휴가 신청이 거부되었습니다'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('휴가 거부 오류:', error);
|
logger.error('휴가 거부 오류:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
||||||
success: false,
|
|
||||||
message: '서버 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -530,34 +244,15 @@ const vacationRequestController = {
|
|||||||
*/
|
*/
|
||||||
async getPendingRequests(req, res) {
|
async getPendingRequests(req, res) {
|
||||||
try {
|
try {
|
||||||
// 관리자 권한 확인
|
|
||||||
if (req.user.access_level !== 'system') {
|
if (req.user.access_level !== 'system') {
|
||||||
return res.status(403).json({
|
return res.status(403).json({ success: false, message: '관리자만 조회할 수 있습니다' });
|
||||||
success: false,
|
|
||||||
message: '관리자만 조회할 수 있습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
vacationRequestModel.getAllPending((err, results) => {
|
const results = await vacationRequestModel.getAllPending();
|
||||||
if (err) {
|
res.json({ success: true, data: results });
|
||||||
console.error('대기 중인 휴가 신청 조회 오류:', err);
|
|
||||||
return res.status(500).json({
|
|
||||||
success: false,
|
|
||||||
message: '대기 중인 휴가 신청 조회 중 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res.json({
|
|
||||||
success: true,
|
|
||||||
data: results
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('대기 중인 휴가 신청 조회 오류:', error);
|
logger.error('대기 중인 휴가 신청 조회 오류:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
||||||
success: false,
|
|
||||||
message: '서버 오류가 발생했습니다'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,262 +9,191 @@ const vacationRequestModel = {
|
|||||||
/**
|
/**
|
||||||
* 휴가 신청 생성
|
* 휴가 신청 생성
|
||||||
*/
|
*/
|
||||||
async create(requestData, callback) {
|
async create(requestData) {
|
||||||
try {
|
const db = await getDb();
|
||||||
const db = await getDb();
|
const [result] = await db.query(`INSERT INTO vacation_requests SET ?`, requestData);
|
||||||
const query = `INSERT INTO vacation_requests SET ?`;
|
return result;
|
||||||
const [result] = await db.query(query, requestData);
|
|
||||||
callback(null, result);
|
|
||||||
} catch (error) {
|
|
||||||
callback(error);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 휴가 신청 목록 조회 (필터링 지원)
|
* 휴가 신청 목록 조회 (필터링 지원)
|
||||||
*/
|
*/
|
||||||
async getAll(filters = {}, callback) {
|
async getAll(filters = {}) {
|
||||||
try {
|
const db = await getDb();
|
||||||
const db = await getDb();
|
|
||||||
|
|
||||||
let query = `
|
let query = `
|
||||||
SELECT
|
SELECT
|
||||||
vr.*,
|
vr.*,
|
||||||
w.worker_name,
|
w.worker_name,
|
||||||
vt.type_name as vacation_type_name,
|
vt.type_name as vacation_type_name,
|
||||||
vt.deduct_days as vacation_deduct_days,
|
vt.deduct_days as vacation_deduct_days,
|
||||||
requester.name as requester_name,
|
requester.name as requester_name,
|
||||||
reviewer.name as reviewer_name
|
reviewer.name as reviewer_name
|
||||||
FROM vacation_requests vr
|
FROM vacation_requests vr
|
||||||
INNER JOIN workers w ON vr.worker_id = w.worker_id
|
INNER JOIN workers w ON vr.worker_id = w.worker_id
|
||||||
INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id
|
INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id
|
||||||
LEFT JOIN users requester ON vr.requested_by = requester.user_id
|
LEFT JOIN users requester ON vr.requested_by = requester.user_id
|
||||||
LEFT JOIN users reviewer ON vr.reviewed_by = reviewer.user_id
|
LEFT JOIN users reviewer ON vr.reviewed_by = reviewer.user_id
|
||||||
WHERE 1=1
|
WHERE 1=1
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const params = [];
|
const params = [];
|
||||||
|
|
||||||
// 작업자 필터
|
if (filters.worker_id) {
|
||||||
if (filters.worker_id) {
|
query += ` AND vr.worker_id = ?`;
|
||||||
query += ` AND vr.worker_id = ?`;
|
params.push(filters.worker_id);
|
||||||
params.push(filters.worker_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 상태 필터
|
|
||||||
if (filters.status) {
|
|
||||||
query += ` AND vr.status = ?`;
|
|
||||||
params.push(filters.status);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 기간 필터
|
|
||||||
if (filters.start_date) {
|
|
||||||
query += ` AND vr.start_date >= ?`;
|
|
||||||
params.push(filters.start_date);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filters.end_date) {
|
|
||||||
query += ` AND vr.end_date <= ?`;
|
|
||||||
params.push(filters.end_date);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 휴가 유형 필터
|
|
||||||
if (filters.vacation_type_id) {
|
|
||||||
query += ` AND vr.vacation_type_id = ?`;
|
|
||||||
params.push(filters.vacation_type_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
query += ` ORDER BY vr.created_at DESC`;
|
|
||||||
|
|
||||||
const [rows] = await db.query(query, params);
|
|
||||||
callback(null, rows);
|
|
||||||
} catch (error) {
|
|
||||||
callback(error);
|
|
||||||
}
|
}
|
||||||
|
if (filters.status) {
|
||||||
|
query += ` AND vr.status = ?`;
|
||||||
|
params.push(filters.status);
|
||||||
|
}
|
||||||
|
if (filters.start_date) {
|
||||||
|
query += ` AND vr.start_date >= ?`;
|
||||||
|
params.push(filters.start_date);
|
||||||
|
}
|
||||||
|
if (filters.end_date) {
|
||||||
|
query += ` AND vr.end_date <= ?`;
|
||||||
|
params.push(filters.end_date);
|
||||||
|
}
|
||||||
|
if (filters.vacation_type_id) {
|
||||||
|
query += ` AND vr.vacation_type_id = ?`;
|
||||||
|
params.push(filters.vacation_type_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
query += ` ORDER BY vr.created_at DESC`;
|
||||||
|
|
||||||
|
const [rows] = await db.query(query, params);
|
||||||
|
return rows;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 특정 휴가 신청 조회
|
* 특정 휴가 신청 조회
|
||||||
*/
|
*/
|
||||||
async getById(requestId, callback) {
|
async getById(requestId) {
|
||||||
try {
|
const db = await getDb();
|
||||||
const db = await getDb();
|
const [rows] = await db.query(`
|
||||||
const query = `
|
SELECT
|
||||||
SELECT
|
vr.*,
|
||||||
vr.*,
|
w.worker_name,
|
||||||
w.worker_name,
|
w.phone_number as worker_phone,
|
||||||
w.phone_number as worker_phone,
|
w.email as worker_email,
|
||||||
w.email as worker_email,
|
vt.type_name as vacation_type_name,
|
||||||
vt.type_name as vacation_type_name,
|
vt.type_code as vacation_type_code,
|
||||||
vt.type_code as vacation_type_code,
|
vt.deduct_days as vacation_deduct_days,
|
||||||
vt.deduct_days as vacation_deduct_days,
|
requester.name as requester_name,
|
||||||
requester.name as requester_name,
|
requester.username as requester_username,
|
||||||
requester.username as requester_username,
|
reviewer.name as reviewer_name,
|
||||||
reviewer.name as reviewer_name,
|
reviewer.username as reviewer_username
|
||||||
reviewer.username as reviewer_username
|
FROM vacation_requests vr
|
||||||
FROM vacation_requests vr
|
INNER JOIN workers w ON vr.worker_id = w.worker_id
|
||||||
INNER JOIN workers w ON vr.worker_id = w.worker_id
|
INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id
|
||||||
INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id
|
LEFT JOIN users requester ON vr.requested_by = requester.user_id
|
||||||
LEFT JOIN users requester ON vr.requested_by = requester.user_id
|
LEFT JOIN users reviewer ON vr.reviewed_by = reviewer.user_id
|
||||||
LEFT JOIN users reviewer ON vr.reviewed_by = reviewer.user_id
|
WHERE vr.request_id = ?
|
||||||
WHERE vr.request_id = ?
|
`, [requestId]);
|
||||||
`;
|
return rows;
|
||||||
const [rows] = await db.query(query, [requestId]);
|
|
||||||
callback(null, rows);
|
|
||||||
} catch (error) {
|
|
||||||
callback(error);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 휴가 신청 수정
|
* 휴가 신청 수정
|
||||||
*/
|
*/
|
||||||
async update(requestId, updateData, callback) {
|
async update(requestId, updateData) {
|
||||||
try {
|
const db = await getDb();
|
||||||
const db = await getDb();
|
const [result] = await db.query(`UPDATE vacation_requests SET ? WHERE request_id = ?`, [updateData, requestId]);
|
||||||
const query = `UPDATE vacation_requests SET ? WHERE request_id = ?`;
|
return result;
|
||||||
const [result] = await db.query(query, [updateData, requestId]);
|
|
||||||
callback(null, result);
|
|
||||||
} catch (error) {
|
|
||||||
callback(error);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 휴가 신청 삭제
|
* 휴가 신청 삭제
|
||||||
*/
|
*/
|
||||||
async delete(requestId, callback) {
|
async delete(requestId) {
|
||||||
try {
|
const db = await getDb();
|
||||||
const db = await getDb();
|
const [result] = await db.query(`DELETE FROM vacation_requests WHERE request_id = ?`, [requestId]);
|
||||||
const query = `DELETE FROM vacation_requests WHERE request_id = ?`;
|
return result;
|
||||||
const [result] = await db.query(query, [requestId]);
|
|
||||||
callback(null, result);
|
|
||||||
} catch (error) {
|
|
||||||
callback(error);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 휴가 신청 승인/거부
|
* 휴가 신청 승인/거부
|
||||||
*/
|
*/
|
||||||
async updateStatus(requestId, statusData, callback) {
|
async updateStatus(requestId, statusData) {
|
||||||
try {
|
const db = await getDb();
|
||||||
const db = await getDb();
|
const [result] = await db.query(`
|
||||||
const query = `
|
UPDATE vacation_requests
|
||||||
UPDATE vacation_requests
|
SET status = ?, reviewed_by = ?, reviewed_at = NOW(), review_note = ?
|
||||||
SET
|
WHERE request_id = ?
|
||||||
status = ?,
|
`, [statusData.status, statusData.reviewed_by, statusData.review_note || null, requestId]);
|
||||||
reviewed_by = ?,
|
return result;
|
||||||
reviewed_at = NOW(),
|
|
||||||
review_note = ?
|
|
||||||
WHERE request_id = ?
|
|
||||||
`;
|
|
||||||
const [result] = await db.query(query, [
|
|
||||||
statusData.status,
|
|
||||||
statusData.reviewed_by,
|
|
||||||
statusData.review_note || null,
|
|
||||||
requestId
|
|
||||||
]);
|
|
||||||
callback(null, result);
|
|
||||||
} catch (error) {
|
|
||||||
callback(error);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 특정 작업자의 대기 중인 휴가 신청 수
|
* 특정 작업자의 대기 중인 휴가 신청 수
|
||||||
*/
|
*/
|
||||||
async getPendingCount(workerId, callback) {
|
async getPendingCount(workerId) {
|
||||||
try {
|
const db = await getDb();
|
||||||
const db = await getDb();
|
const [rows] = await db.query(`
|
||||||
const query = `
|
SELECT COUNT(*) as count FROM vacation_requests
|
||||||
SELECT COUNT(*) as count
|
WHERE worker_id = ? AND status = 'pending'
|
||||||
FROM vacation_requests
|
`, [workerId]);
|
||||||
WHERE worker_id = ? AND status = 'pending'
|
return rows;
|
||||||
`;
|
|
||||||
const [rows] = await db.query(query, [workerId]);
|
|
||||||
callback(null, rows);
|
|
||||||
} catch (error) {
|
|
||||||
callback(error);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 특정 작업자의 승인된 휴가 일수 합계 (특정 기간)
|
* 특정 작업자의 승인된 휴가 일수 합계 (특정 기간)
|
||||||
*/
|
*/
|
||||||
async getApprovedDaysInPeriod(workerId, startDate, endDate, callback) {
|
async getApprovedDaysInPeriod(workerId, startDate, endDate) {
|
||||||
try {
|
const db = await getDb();
|
||||||
const db = await getDb();
|
const [rows] = await db.query(`
|
||||||
const query = `
|
SELECT COALESCE(SUM(days_used), 0) as total_days FROM vacation_requests
|
||||||
SELECT COALESCE(SUM(days_used), 0) as total_days
|
WHERE worker_id = ? AND status = 'approved' AND start_date >= ? AND end_date <= ?
|
||||||
FROM vacation_requests
|
`, [workerId, startDate, endDate]);
|
||||||
WHERE worker_id = ?
|
return rows;
|
||||||
AND status = 'approved'
|
|
||||||
AND start_date >= ?
|
|
||||||
AND end_date <= ?
|
|
||||||
`;
|
|
||||||
const [rows] = await db.query(query, [workerId, startDate, endDate]);
|
|
||||||
callback(null, rows);
|
|
||||||
} catch (error) {
|
|
||||||
callback(error);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 휴가 기간 중복 체크
|
* 휴가 기간 중복 체크
|
||||||
*/
|
*/
|
||||||
async checkOverlap(workerId, startDate, endDate, excludeRequestId = null, callback) {
|
async checkOverlap(workerId, startDate, endDate, excludeRequestId = null) {
|
||||||
try {
|
const db = await getDb();
|
||||||
const db = await getDb();
|
let query = `
|
||||||
let query = `
|
SELECT COUNT(*) as count FROM vacation_requests
|
||||||
SELECT COUNT(*) as count
|
WHERE worker_id = ?
|
||||||
FROM vacation_requests
|
AND status IN ('pending', 'approved')
|
||||||
WHERE worker_id = ?
|
AND (
|
||||||
AND status IN ('pending', 'approved')
|
(start_date <= ? AND end_date >= ?) OR
|
||||||
AND (
|
(start_date <= ? AND end_date >= ?) OR
|
||||||
(start_date <= ? AND end_date >= ?) OR
|
(start_date >= ? AND end_date <= ?)
|
||||||
(start_date <= ? AND end_date >= ?) OR
|
)
|
||||||
(start_date >= ? AND end_date <= ?)
|
`;
|
||||||
)
|
const params = [workerId, startDate, startDate, endDate, endDate, startDate, endDate];
|
||||||
`;
|
|
||||||
const params = [workerId, startDate, startDate, endDate, endDate, startDate, endDate];
|
|
||||||
|
|
||||||
if (excludeRequestId) {
|
if (excludeRequestId) {
|
||||||
query += ` AND request_id != ?`;
|
query += ` AND request_id != ?`;
|
||||||
params.push(excludeRequestId);
|
params.push(excludeRequestId);
|
||||||
}
|
|
||||||
|
|
||||||
const [rows] = await db.query(query, params);
|
|
||||||
callback(null, rows);
|
|
||||||
} catch (error) {
|
|
||||||
callback(error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [rows] = await db.query(query, params);
|
||||||
|
return rows;
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 모든 대기 중인 휴가 신청 (관리자용)
|
* 모든 대기 중인 휴가 신청 (관리자용)
|
||||||
*/
|
*/
|
||||||
async getAllPending(callback) {
|
async getAllPending() {
|
||||||
try {
|
const db = await getDb();
|
||||||
const db = await getDb();
|
const [rows] = await db.query(`
|
||||||
const query = `
|
SELECT
|
||||||
SELECT
|
vr.*,
|
||||||
vr.*,
|
w.worker_name,
|
||||||
w.worker_name,
|
vt.type_name as vacation_type_name,
|
||||||
vt.type_name as vacation_type_name,
|
requester.name as requester_name
|
||||||
requester.name as requester_name
|
FROM vacation_requests vr
|
||||||
FROM vacation_requests vr
|
INNER JOIN workers w ON vr.worker_id = w.worker_id
|
||||||
INNER JOIN workers w ON vr.worker_id = w.worker_id
|
INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id
|
||||||
INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id
|
LEFT JOIN users requester ON vr.requested_by = requester.user_id
|
||||||
LEFT JOIN users requester ON vr.requested_by = requester.user_id
|
WHERE vr.status = 'pending'
|
||||||
WHERE vr.status = 'pending'
|
ORDER BY vr.created_at ASC
|
||||||
ORDER BY vr.created_at ASC
|
`);
|
||||||
`;
|
return rows;
|
||||||
const [rows] = await db.query(query);
|
|
||||||
callback(null, rows);
|
|
||||||
} catch (error) {
|
|
||||||
callback(error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user