refactor: System 1 모델/컨트롤러 콜백→async/await 전환

11개 모델 파일의 171개 콜백 메서드를 직접 return/throw 패턴으로 변환.
8개 컨트롤러에서 new Promise 래퍼와 중첩 콜백 제거, console.error→logger.error 교체.
미사용 pageAccessModel.js 삭제. 전체 -3,600줄 감소.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-02-25 09:40:33 +09:00
parent c29a1506bb
commit 5183e9ff85
22 changed files with 4791 additions and 8406 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -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: '사용 가능 휴가를 조회하는 중 오류가 발생했습니다' });
}
}
};

View File

@@ -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: '서버 오류가 발생했습니다' });
}
}
};

View File

@@ -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: '안전교육 기록 목록 조회 중 오류가 발생했습니다.' });
}
};

View File

@@ -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: '통계 조회 실패' });
}
};

View File

@@ -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 });