sso_users.user_id를 단일 식별자로 통합. JWT에서 worker_id 제거, department_id/is_production 추가. 백엔드 15개 모델, 11개 컨트롤러, 4개 서비스, 7개 라우트, 프론트엔드 32+ JS/11+ HTML 변환. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
261 lines
9.2 KiB
JavaScript
261 lines
9.2 KiB
JavaScript
/**
|
|
* vacationRequestController.js
|
|
* 휴가 신청 관련 컨트롤러
|
|
*/
|
|
|
|
const vacationRequestModel = require('../models/vacationRequestModel');
|
|
const logger = require('../utils/logger');
|
|
|
|
const vacationRequestController = {
|
|
/**
|
|
* 휴가 신청 생성
|
|
*/
|
|
async createRequest(req, res) {
|
|
try {
|
|
const { user_id, vacation_type_id, start_date, end_date, days_used, reason } = req.body;
|
|
const requested_by = req.user.user_id;
|
|
|
|
if (!user_id || !vacation_type_id || !start_date || !end_date || !days_used) {
|
|
return res.status(400).json({ success: false, message: '필수 필드가 누락되었습니다' });
|
|
}
|
|
|
|
if (new Date(end_date) < new Date(start_date)) {
|
|
return res.status(400).json({ success: false, message: '종료일은 시작일보다 이후여야 합니다' });
|
|
}
|
|
|
|
// 기간 중복 체크
|
|
const overlapRows = await vacationRequestModel.checkOverlap(user_id, start_date, end_date);
|
|
if (overlapRows[0].count > 0) {
|
|
return res.status(400).json({ success: false, message: '해당 기간에 이미 신청된 휴가가 있습니다' });
|
|
}
|
|
|
|
const result = await vacationRequestModel.create({
|
|
user_id, vacation_type_id, start_date, end_date,
|
|
days_used, reason: reason || null, status: 'pending', requested_by
|
|
});
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: '휴가 신청이 완료되었습니다',
|
|
data: { request_id: result.insertId }
|
|
});
|
|
} catch (error) {
|
|
logger.error('휴가 신청 생성 오류:', error);
|
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 휴가 신청 목록 조회
|
|
*/
|
|
async getAllRequests(req, res) {
|
|
try {
|
|
const filters = {
|
|
user_id: req.query.user_id,
|
|
status: req.query.status,
|
|
start_date: req.query.start_date,
|
|
end_date: req.query.end_date,
|
|
vacation_type_id: req.query.vacation_type_id
|
|
};
|
|
|
|
// 일반 사용자는 자신의 신청만 조회 가능
|
|
if (req.user.access_level !== 'system') {
|
|
if (req.user.user_id) {
|
|
filters.user_id = req.user.user_id;
|
|
} else {
|
|
return res.status(403).json({ success: false, message: '권한이 없습니다' });
|
|
}
|
|
}
|
|
|
|
const results = await vacationRequestModel.getAll(filters);
|
|
res.json({ success: true, data: results });
|
|
} catch (error) {
|
|
logger.error('휴가 신청 목록 조회 오류:', error);
|
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 특정 휴가 신청 조회
|
|
*/
|
|
async getRequestById(req, res) {
|
|
try {
|
|
const results = await vacationRequestModel.getById(req.params.id);
|
|
|
|
if (results.length === 0) {
|
|
return res.status(404).json({ success: false, message: '해당 휴가 신청을 찾을 수 없습니다' });
|
|
}
|
|
|
|
const request = results[0];
|
|
|
|
if (req.user.access_level !== 'system' && req.user.user_id !== request.user_id) {
|
|
return res.status(403).json({ success: false, message: '권한이 없습니다' });
|
|
}
|
|
|
|
res.json({ success: true, data: request });
|
|
} catch (error) {
|
|
logger.error('휴가 신청 조회 오류:', error);
|
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 휴가 신청 수정 (대기 중인 신청만)
|
|
*/
|
|
async updateRequest(req, res) {
|
|
try {
|
|
const { id } = req.params;
|
|
const { start_date, end_date, days_used, reason } = req.body;
|
|
|
|
const results = await vacationRequestModel.getById(id);
|
|
if (results.length === 0) {
|
|
return res.status(404).json({ success: false, message: '해당 휴가 신청을 찾을 수 없습니다' });
|
|
}
|
|
|
|
const existingRequest = results[0];
|
|
|
|
if (req.user.access_level !== 'system' && req.user.user_id !== existingRequest.user_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.user_id, newStartDate, newEndDate, id
|
|
);
|
|
if (overlapRows[0].count > 0) {
|
|
return res.status(400).json({ success: false, message: '해당 기간에 이미 신청된 휴가가 있습니다' });
|
|
}
|
|
}
|
|
|
|
await vacationRequestModel.update(id, updateData);
|
|
res.json({ success: true, message: '휴가 신청이 수정되었습니다' });
|
|
} catch (error) {
|
|
logger.error('휴가 신청 수정 오류:', error);
|
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 휴가 신청 삭제 (대기 중인 신청만)
|
|
*/
|
|
async deleteRequest(req, res) {
|
|
try {
|
|
const { id } = req.params;
|
|
|
|
const results = await vacationRequestModel.getById(id);
|
|
if (results.length === 0) {
|
|
return res.status(404).json({ success: false, message: '해당 휴가 신청을 찾을 수 없습니다' });
|
|
}
|
|
|
|
const existingRequest = results[0];
|
|
|
|
if (req.user.access_level !== 'system' && req.user.user_id !== existingRequest.user_id) {
|
|
return res.status(403).json({ success: false, message: '권한이 없습니다' });
|
|
}
|
|
|
|
if (existingRequest.status !== 'pending') {
|
|
return res.status(400).json({ success: false, message: '승인/거부된 신청은 삭제할 수 없습니다' });
|
|
}
|
|
|
|
await vacationRequestModel.delete(id);
|
|
res.json({ success: true, message: '휴가 신청이 삭제되었습니다' });
|
|
} catch (error) {
|
|
logger.error('휴가 신청 삭제 오류:', error);
|
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 휴가 신청 승인 (관리자만)
|
|
*/
|
|
async approveRequest(req, res) {
|
|
try {
|
|
const { id } = req.params;
|
|
const { review_note } = req.body;
|
|
const reviewed_by = req.user.user_id;
|
|
|
|
if (req.user.access_level !== 'system') {
|
|
return res.status(403).json({ success: false, message: '관리자만 승인할 수 있습니다' });
|
|
}
|
|
|
|
const results = await vacationRequestModel.getById(id);
|
|
if (results.length === 0) {
|
|
return res.status(404).json({ success: false, message: '해당 휴가 신청을 찾을 수 없습니다' });
|
|
}
|
|
|
|
if (results[0].status !== 'pending') {
|
|
return res.status(400).json({ success: false, message: '이미 처리된 신청입니다' });
|
|
}
|
|
|
|
await vacationRequestModel.updateStatus(id, { status: 'approved', reviewed_by, review_note });
|
|
res.json({ success: true, message: '휴가 신청이 승인되었습니다' });
|
|
} catch (error) {
|
|
logger.error('휴가 승인 오류:', error);
|
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 휴가 신청 거부 (관리자만)
|
|
*/
|
|
async rejectRequest(req, res) {
|
|
try {
|
|
const { id } = req.params;
|
|
const { review_note } = req.body;
|
|
const reviewed_by = req.user.user_id;
|
|
|
|
if (req.user.access_level !== 'system') {
|
|
return res.status(403).json({ success: false, message: '관리자만 거부할 수 있습니다' });
|
|
}
|
|
|
|
const results = await vacationRequestModel.getById(id);
|
|
if (results.length === 0) {
|
|
return res.status(404).json({ success: false, message: '해당 휴가 신청을 찾을 수 없습니다' });
|
|
}
|
|
|
|
if (results[0].status !== 'pending') {
|
|
return res.status(400).json({ success: false, message: '이미 처리된 신청입니다' });
|
|
}
|
|
|
|
await vacationRequestModel.updateStatus(id, { status: 'rejected', reviewed_by, review_note });
|
|
res.json({ success: true, message: '휴가 신청이 거부되었습니다' });
|
|
} catch (error) {
|
|
logger.error('휴가 거부 오류:', error);
|
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 대기 중인 휴가 신청 목록 (관리자용)
|
|
*/
|
|
async getPendingRequests(req, res) {
|
|
try {
|
|
if (req.user.access_level !== 'system') {
|
|
return res.status(403).json({ success: false, message: '관리자만 조회할 수 있습니다' });
|
|
}
|
|
|
|
const results = await vacationRequestModel.getAllPending();
|
|
res.json({ success: true, data: results });
|
|
} catch (error) {
|
|
logger.error('대기 중인 휴가 신청 조회 오류:', error);
|
|
res.status(500).json({ success: false, message: '서버 오류가 발생했습니다' });
|
|
}
|
|
}
|
|
};
|
|
|
|
module.exports = vacationRequestController;
|