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

View File

@@ -2,8 +2,6 @@ const { getDb } = require('../dbPool');
/**
* 1. 여러 개의 이슈 보고서를 트랜잭션으로 생성합니다.
* @param {Array<object>} reports - 생성할 보고서 데이터 배열
* @returns {Promise<Array<number>>} - 삽입된 ID 배열
*/
const createMany = async (reports) => {
const db = await getDb();
@@ -13,7 +11,7 @@ const createMany = async (reports) => {
const insertedIds = [];
const sql = `
INSERT INTO DailyIssueReports
INSERT INTO DailyIssueReports
(date, worker_id, project_id, start_time, end_time, issue_type_id)
VALUES (?, ?, ?, ?, ?, ?)
`;
@@ -36,119 +34,71 @@ const createMany = async (reports) => {
};
/**
* 2. 특정 날짜의 전체 이슈 목록 조회 (Promise 기반)
* 2. 특정 날짜의 전체 이슈 목록 조회
*/
const getAllByDate = async (date) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT
d.id, d.date, w.worker_name, p.project_name, d.start_time, d.end_time,
t.category, t.subcategory, d.description
FROM DailyIssueReports d
LEFT JOIN workers w ON d.worker_id = w.worker_id
LEFT JOIN projects p ON d.project_id = p.project_id
LEFT JOIN IssueTypes t ON d.issue_type_id = t.issue_type_id
WHERE d.date = ?
ORDER BY d.start_time ASC`,
[date]
);
return rows;
} catch (err) {
console.error(`[Model] ${date} 이슈 목록 조회 오류:`, err);
throw new Error('데이터베이스에서 이슈 목록을 조회하는 중 오류가 발생했습니다.');
}
const db = await getDb();
const [rows] = await db.query(
`SELECT
d.id, d.date, w.worker_name, p.project_name, d.start_time, d.end_time,
t.category, t.subcategory, d.description
FROM DailyIssueReports d
LEFT JOIN workers w ON d.worker_id = w.worker_id
LEFT JOIN projects p ON d.project_id = p.project_id
LEFT JOIN IssueTypes t ON d.issue_type_id = t.issue_type_id
WHERE d.date = ?
ORDER BY d.start_time ASC`,
[date]
);
return rows;
};
/**
* 3. 단일 조회 (선택사항: 컨트롤러에서 사용 중)
* 3. 단일 조회
*/
const getById = async (id, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(`SELECT id, date, worker_id, project_id, issue_type_id, description, created_at, start_time, end_time FROM DailyIssueReports WHERE id = ?`, [id]);
callback(null, rows[0]);
} catch (err) {
callback(err);
}
const getById = async (id) => {
const db = await getDb();
const [rows] = await db.query(`SELECT id, date, worker_id, project_id, issue_type_id, description, created_at, start_time, end_time FROM DailyIssueReports WHERE id = ?`, [id]);
return rows[0];
};
/**
* 4. 수정
*/
const update = async (id, data, callback) => {
try {
const db = await getDb();
const update = async (id, data) => {
const db = await getDb();
const fields = [];
const values = [];
const fields = [];
const values = [];
for (const key in data) {
fields.push(`${key} = ?`);
values.push(data[key]);
}
values.push(id); // 마지막에 id
const [result] = await db.query(
`UPDATE DailyIssueReports SET ${fields.join(', ')} WHERE id = ?`,
values
);
callback(null, result.affectedRows);
} catch (err) {
callback(err);
for (const key in data) {
fields.push(`${key} = ?`);
values.push(data[key]);
}
values.push(id);
const [result] = await db.query(
`UPDATE DailyIssueReports SET ${fields.join(', ')} WHERE id = ?`,
values
);
return result.affectedRows;
};
/**
* 5. 삭제 (Promise 기반)
* 5. 삭제
*/
const remove = async (id) => {
try {
const db = await getDb();
const [result] = await db.query(`DELETE FROM DailyIssueReports WHERE id = ?`, [id]);
return result.affectedRows;
} catch (err) {
console.error(`[Model] 이슈(id: ${id}) 삭제 오류:`, err);
throw new Error('데이터베이스에서 이슈를 삭제하는 중 오류가 발생했습니다.');
}
const db = await getDb();
const [result] = await db.query(`DELETE FROM DailyIssueReports WHERE id = ?`, [id]);
return result.affectedRows;
};
// V1 함수들은 점진적으로 제거 예정
const create = async (report, callback) => {
try {
const db = await getDb();
const {
date,
worker_id,
project_id,
start_time,
end_time,
issue_type_id,
description = null // 선택값 처리
} = report;
const [result] = await db.query(
`INSERT INTO DailyIssueReports
(date, worker_id, project_id, start_time, end_time, issue_type_id, description)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
[date, worker_id, project_id, start_time, end_time, issue_type_id, description]
);
callback(null, result.insertId);
} catch (err) {
callback(err);
}
};
module.exports = {
createMany, // 신규
createMany,
getAllByDate,
remove,
// 레거시 호환성을 위해 V1 함수들 임시 유지
create: (report, callback) => createMany([report]).then(ids => callback(null, ids[0])).catch(err => callback(err)),
getById,
update,
};
remove
};

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,160 +0,0 @@
// models/pageAccessModel.js
const db = require('../db/connection');
const PageAccessModel = {
// 사용자의 페이지 권한 조회
getUserPageAccess: (userId, callback) => {
const sql = `
SELECT
p.id,
p.page_key,
p.page_name,
p.page_path,
p.category,
p.is_admin_only,
COALESCE(upa.can_access, 0) as can_access,
upa.granted_at,
upa.granted_by,
granter.username as granted_by_username
FROM pages p
LEFT JOIN user_page_access upa ON p.id = upa.page_id AND upa.user_id = ?
LEFT JOIN users granter ON upa.granted_by = granter.user_id
WHERE p.is_admin_only = 0
ORDER BY p.category, p.display_order
`;
db.query(sql, [userId], callback);
},
// 모든 페이지 목록 조회
getAllPages: (callback) => {
const sql = `
SELECT
id,
page_key,
page_name,
page_path,
category,
description,
is_admin_only,
display_order
FROM pages
WHERE is_admin_only = 0
ORDER BY category, display_order
`;
db.query(sql, callback);
},
// 페이지 권한 부여
grantPageAccess: (userId, pageId, grantedBy, callback) => {
const sql = `
INSERT INTO user_page_access (user_id, page_id, can_access, granted_by, granted_at)
VALUES (?, ?, 1, ?, NOW())
ON DUPLICATE KEY UPDATE
can_access = 1,
granted_by = ?,
granted_at = NOW()
`;
db.query(sql, [userId, pageId, grantedBy, grantedBy], callback);
},
// 페이지 권한 회수
revokePageAccess: (userId, pageId, callback) => {
const sql = `
DELETE FROM user_page_access
WHERE user_id = ? AND page_id = ?
`;
db.query(sql, [userId, pageId], callback);
},
// 여러 페이지 권한 일괄 설정
setUserPageAccess: (userId, pageIds, grantedBy, callback) => {
db.beginTransaction((err) => {
if (err) return callback(err);
// 기존 권한 모두 삭제
const deleteSql = 'DELETE FROM user_page_access WHERE user_id = ?';
db.query(deleteSql, [userId], (err) => {
if (err) {
return db.rollback(() => callback(err));
}
// 새 권한이 없으면 커밋하고 종료
if (!pageIds || pageIds.length === 0) {
return db.commit((err) => {
if (err) return db.rollback(() => callback(err));
callback(null, { affectedRows: 0 });
});
}
// 새 권한 추가
const values = pageIds.map(pageId => [userId, pageId, 1, grantedBy]);
const insertSql = `
INSERT INTO user_page_access (user_id, page_id, can_access, granted_by, granted_at)
VALUES ?
`;
db.query(insertSql, [values], (err, result) => {
if (err) {
return db.rollback(() => callback(err));
}
db.commit((err) => {
if (err) return db.rollback(() => callback(err));
callback(null, result);
});
});
});
});
},
// 특정 페이지 접근 권한 확인
checkPageAccess: (userId, pageKey, callback) => {
const sql = `
SELECT
COALESCE(upa.can_access, 0) as can_access,
p.is_admin_only
FROM pages p
LEFT JOIN user_page_access upa ON p.id = upa.page_id AND upa.user_id = ?
WHERE p.page_key = ?
`;
db.query(sql, [userId, pageKey], (err, results) => {
if (err) return callback(err);
if (results.length === 0) return callback(null, { can_access: false });
callback(null, results[0]);
});
},
// 계정이 있는 작업자 목록 조회 (권한 관리용)
getUsersWithAccounts: (callback) => {
const sql = `
SELECT
u.user_id,
u.username,
u.name,
u.role_id,
r.name as role_name,
u.worker_id,
w.worker_name,
w.job_type,
COUNT(upa.page_id) as granted_pages_count
FROM users u
LEFT JOIN roles r ON u.role_id = r.id
LEFT JOIN workers w ON u.worker_id = w.worker_id
LEFT JOIN user_page_access upa ON u.user_id = upa.user_id AND upa.can_access = 1
WHERE u.is_active = 1
AND u.role_id IN (4, 5)
GROUP BY u.user_id
ORDER BY w.worker_name, u.username
`;
db.query(sql, callback);
}
};
module.exports = PageAccessModel;

File diff suppressed because it is too large Load Diff

View File

@@ -6,11 +6,10 @@ const TbmTransferModel = {
* 작업자 이동 실행 (보내기/빼오기)
* 트랜잭션: source work_hours 업데이트 + dest INSERT + 로그 INSERT
*/
createTransfer: async (transferData, callback) => {
let conn;
async createTransfer(transferData) {
const db = await getDb();
const conn = await db.getConnection();
try {
const db = await getDb();
conn = await db.getConnection();
await conn.beginTransaction();
const {
@@ -27,8 +26,7 @@ const TbmTransferModel = {
if (sourceRows.length === 0) {
await conn.rollback();
conn.release();
return callback(null, { success: false, message: '원본 세션에서 해당 작업자를 찾을 수 없습니다.' });
return { success: false, message: '원본 세션에서 해당 작업자를 찾을 수 없습니다.' };
}
const currentHours = sourceRows[0].work_hours === null ? 8 : parseFloat(sourceRows[0].work_hours);
@@ -36,8 +34,7 @@ const TbmTransferModel = {
if (newSourceHours < 0) {
await conn.rollback();
conn.release();
return callback(null, { success: false, message: '이동 시간이 현재 배정 시간보다 큽니다.' });
return { success: false, message: '이동 시간이 현재 배정 시간보다 큽니다.' };
}
await conn.query(
@@ -52,14 +49,12 @@ const TbmTransferModel = {
);
if (destRows.length > 0) {
// 이미 있으면 시간만 추가
const existingHours = destRows[0].work_hours === null ? 8 : parseFloat(destRows[0].work_hours);
await conn.query(
'UPDATE tbm_team_assignments SET work_hours = ? WHERE session_id = ? AND worker_id = ?',
[existingHours + parseFloat(hours), dest_session_id, worker_id]
);
} else {
// 새로 INSERT
await conn.query(
`INSERT INTO tbm_team_assignments
(session_id, worker_id, work_hours, project_id, work_type_id, task_id, workplace_category_id, workplace_id, is_present)
@@ -89,7 +84,6 @@ const TbmTransferModel = {
const totalHours = totalRows[0] ? parseFloat(totalRows[0].total_hours) : 0;
await conn.commit();
conn.release();
const result = {
success: true,
@@ -101,24 +95,22 @@ const TbmTransferModel = {
result.warning = `해당 작업자의 당일 합계가 ${totalHours}h입니다 (8h 초과).`;
}
callback(null, result);
return result;
} catch (err) {
if (conn) {
try { await conn.rollback(); } catch (e) {}
conn.release();
}
callback(err);
try { await conn.rollback(); } catch (e) {}
throw err;
} finally {
conn.release();
}
},
/**
* 이동 취소 (원복)
*/
cancelTransfer: async (transferId, callback) => {
let conn;
async cancelTransfer(transferId) {
const db = await getDb();
const conn = await db.getConnection();
try {
const db = await getDb();
conn = await db.getConnection();
await conn.beginTransaction();
// 1. 이동 로그 조회
@@ -129,8 +121,7 @@ const TbmTransferModel = {
if (transfers.length === 0) {
await conn.rollback();
conn.release();
return callback(null, { success: false, message: '이동 기록을 찾을 수 없습니다.' });
return { success: false, message: '이동 기록을 찾을 수 없습니다.' };
}
const t = transfers[0];
@@ -146,7 +137,6 @@ const TbmTransferModel = {
const newDestHours = destHours - parseFloat(t.hours);
if (newDestHours <= 0) {
// 삭제
await conn.query(
'DELETE FROM tbm_team_assignments WHERE session_id = ? AND worker_id = ?',
[t.dest_session_id, t.worker_id]
@@ -168,7 +158,6 @@ const TbmTransferModel = {
if (sourceRows.length > 0) {
const sourceHours = sourceRows[0].work_hours === null ? 8 : parseFloat(sourceRows[0].work_hours);
const restoredHours = sourceHours + parseFloat(t.hours);
// 8이면 NULL로 복원 (종일)
await conn.query(
'UPDATE tbm_team_assignments SET work_hours = ? WHERE session_id = ? AND worker_id = ?',
[restoredHours >= 8 ? null : restoredHours, t.source_session_id, t.worker_id]
@@ -179,115 +168,103 @@ const TbmTransferModel = {
await conn.query('DELETE FROM tbm_transfers WHERE transfer_id = ?', [transferId]);
await conn.commit();
conn.release();
callback(null, { success: true });
return { success: true };
} catch (err) {
if (conn) {
try { await conn.rollback(); } catch (e) {}
conn.release();
}
callback(err);
try { await conn.rollback(); } catch (e) {}
throw err;
} finally {
conn.release();
}
},
/**
* 당일 이동 내역 조회
*/
getTransfersByDate: async (date, callback) => {
try {
const db = await getDb();
const sql = `
SELECT
t.*,
w.worker_name,
w.job_type,
sl.worker_name as source_leader_name,
dl.worker_name as dest_leader_name,
u.name as initiated_by_name
FROM tbm_transfers t
INNER JOIN workers w ON t.worker_id = w.worker_id
LEFT JOIN tbm_sessions ss ON t.source_session_id = ss.session_id
LEFT JOIN workers sl ON ss.leader_id = sl.worker_id
LEFT JOIN tbm_sessions ds ON t.dest_session_id = ds.session_id
LEFT JOIN workers dl ON ds.leader_id = dl.worker_id
LEFT JOIN sso_users u ON t.initiated_by = u.user_id
WHERE t.transfer_date = ?
ORDER BY t.created_at DESC
`;
const [rows] = await db.query(sql, [date]);
callback(null, rows);
} catch (err) {
callback(err);
}
async getTransfersByDate(date) {
const db = await getDb();
const sql = `
SELECT
t.*,
w.worker_name,
w.job_type,
sl.worker_name as source_leader_name,
dl.worker_name as dest_leader_name,
u.name as initiated_by_name
FROM tbm_transfers t
INNER JOIN workers w ON t.worker_id = w.worker_id
LEFT JOIN tbm_sessions ss ON t.source_session_id = ss.session_id
LEFT JOIN workers sl ON ss.leader_id = sl.worker_id
LEFT JOIN tbm_sessions ds ON t.dest_session_id = ds.session_id
LEFT JOIN workers dl ON ds.leader_id = dl.worker_id
LEFT JOIN sso_users u ON t.initiated_by = u.user_id
WHERE t.transfer_date = ?
ORDER BY t.created_at DESC
`;
const [rows] = await db.query(sql, [date]);
return rows;
},
/**
* 당일 전 작업자 배정 현황 조회
*/
getWorkerAssignmentsByDate: async (date, callback) => {
try {
const db = await getDb();
async getWorkerAssignmentsByDate(date) {
const db = await getDb();
// 1. 해당 날짜의 모든 배정 가져오기
const [assignments] = await db.query(`
SELECT
ta.worker_id,
ta.session_id,
ta.work_hours,
w.worker_name,
w.job_type,
s.leader_id,
lw.worker_name as leader_name,
s.status as session_status
FROM tbm_team_assignments ta
INNER JOIN tbm_sessions s ON ta.session_id = s.session_id
INNER JOIN workers w ON ta.worker_id = w.worker_id
LEFT JOIN workers lw ON s.leader_id = lw.worker_id
WHERE s.session_date = ?
ORDER BY w.worker_name
`, [date]);
// 1. 해당 날짜의 모든 배정 가져오기
const [assignments] = await db.query(`
SELECT
ta.worker_id,
ta.session_id,
ta.work_hours,
w.worker_name,
w.job_type,
s.leader_id,
lw.worker_name as leader_name,
s.status as session_status
FROM tbm_team_assignments ta
INNER JOIN tbm_sessions s ON ta.session_id = s.session_id
INNER JOIN workers w ON ta.worker_id = w.worker_id
LEFT JOIN workers lw ON s.leader_id = lw.worker_id
WHERE s.session_date = ?
ORDER BY w.worker_name
`, [date]);
// 2. 모든 작업자 가져오기 (배정 안 된 사람도 포함)
const [allWorkers] = await db.query(
"SELECT worker_id, worker_name, job_type FROM workers WHERE status = 'active' AND department = '생산' ORDER BY worker_name"
);
// 2. 모든 작업자 가져오기 (배정 안 된 사람도 포함)
const [allWorkers] = await db.query(
"SELECT worker_id, worker_name, job_type FROM workers WHERE status = 'active' AND department = '생산' ORDER BY worker_name"
);
// 3. 작업자별 배정 현황 구성
const workerMap = {};
allWorkers.forEach(w => {
workerMap[w.worker_id] = {
worker_id: w.worker_id,
worker_name: w.worker_name,
job_type: w.job_type,
sessions: [],
total_hours: 0,
available: true
};
});
// 3. 작업자별 배정 현황 구성
const workerMap = {};
allWorkers.forEach(w => {
workerMap[w.worker_id] = {
worker_id: w.worker_id,
worker_name: w.worker_name,
job_type: w.job_type,
sessions: [],
total_hours: 0,
available: true
};
});
assignments.forEach(a => {
const hours = a.work_hours === null ? 8 : parseFloat(a.work_hours);
if (workerMap[a.worker_id]) {
workerMap[a.worker_id].sessions.push({
session_id: a.session_id,
leader_name: a.leader_name,
work_hours: hours,
session_status: a.session_status
});
workerMap[a.worker_id].total_hours += hours;
}
});
assignments.forEach(a => {
const hours = a.work_hours === null ? 8 : parseFloat(a.work_hours);
if (workerMap[a.worker_id]) {
workerMap[a.worker_id].sessions.push({
session_id: a.session_id,
leader_name: a.leader_name,
work_hours: hours,
session_status: a.session_status
});
workerMap[a.worker_id].total_hours += hours;
}
});
// available 판단
Object.values(workerMap).forEach(w => {
w.available = w.total_hours < 8;
});
Object.values(workerMap).forEach(w => {
w.available = w.total_hours < 8;
});
callback(null, Object.values(workerMap));
} catch (err) {
callback(err);
}
return Object.values(workerMap);
}
};

View File

@@ -9,251 +9,190 @@ const vacationBalanceModel = {
/**
* 특정 작업자의 모든 휴가 잔액 조회 (특정 연도)
*/
async getByWorkerAndYear(workerId, year, callback) {
try {
const db = await getDb();
const query = `
SELECT
vbd.*,
vt.type_name,
vt.type_code,
vt.priority,
vt.is_special
FROM vacation_balance_details vbd
INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
WHERE vbd.worker_id = ? AND vbd.year = ?
ORDER BY vt.priority ASC, vt.type_name ASC
`;
const [rows] = await db.query(query, [workerId, year]);
callback(null, rows);
} catch (error) {
callback(error);
}
async getByWorkerAndYear(workerId, year) {
const db = await getDb();
const [rows] = await db.query(`
SELECT
vbd.*,
vt.type_name,
vt.type_code,
vt.priority,
vt.is_special
FROM vacation_balance_details vbd
INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
WHERE vbd.worker_id = ? AND vbd.year = ?
ORDER BY vt.priority ASC, vt.type_name ASC
`, [workerId, year]);
return rows;
},
/**
* 특정 작업자의 특정 휴가 유형 잔액 조회
*/
async getByWorkerTypeYear(workerId, vacationTypeId, year, callback) {
try {
const db = await getDb();
const query = `
SELECT
vbd.*,
vt.type_name,
vt.type_code
FROM vacation_balance_details vbd
INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
WHERE vbd.worker_id = ?
AND vbd.vacation_type_id = ?
AND vbd.year = ?
`;
const [rows] = await db.query(query, [workerId, vacationTypeId, year]);
callback(null, rows);
} catch (error) {
callback(error);
}
async getByWorkerTypeYear(workerId, vacationTypeId, year) {
const db = await getDb();
const [rows] = await db.query(`
SELECT
vbd.*,
vt.type_name,
vt.type_code
FROM vacation_balance_details vbd
INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
WHERE vbd.worker_id = ?
AND vbd.vacation_type_id = ?
AND vbd.year = ?
`, [workerId, vacationTypeId, year]);
return rows;
},
/**
* 모든 작업자의 휴가 잔액 조회 (특정 연도)
* - 연간 연차 현황 차트용
*/
async getAllByYear(year, callback) {
try {
const db = await getDb();
const query = `
SELECT
vbd.*,
w.worker_name,
w.employment_status,
vt.type_name,
vt.type_code,
vt.priority
FROM vacation_balance_details vbd
INNER JOIN workers w ON vbd.worker_id = w.worker_id
INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
WHERE vbd.year = ?
AND w.employment_status = 'employed'
ORDER BY w.worker_name ASC, vt.priority ASC
`;
const [rows] = await db.query(query, [year]);
callback(null, rows);
} catch (error) {
callback(error);
}
async getAllByYear(year) {
const db = await getDb();
const [rows] = await db.query(`
SELECT
vbd.*,
w.worker_name,
w.employment_status,
vt.type_name,
vt.type_code,
vt.priority
FROM vacation_balance_details vbd
INNER JOIN workers w ON vbd.worker_id = w.worker_id
INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
WHERE vbd.year = ?
AND w.employment_status = 'employed'
ORDER BY w.worker_name ASC, vt.priority ASC
`, [year]);
return rows;
},
/**
* 휴가 잔액 생성
*/
async create(balanceData, callback) {
try {
const db = await getDb();
const query = `INSERT INTO vacation_balance_details SET ?`;
const [rows] = await db.query(query, balanceData);
callback(null, rows);
} catch (error) {
callback(error);
}
async create(balanceData) {
const db = await getDb();
const [result] = await db.query(`INSERT INTO vacation_balance_details SET ?`, balanceData);
return result;
},
/**
* 휴가 잔액 수정
*/
async update(id, updateData, callback) {
try {
const db = await getDb();
const query = `UPDATE vacation_balance_details SET ? WHERE id = ?`;
const [rows] = await db.query(query, [updateData, id]);
callback(null, rows);
} catch (error) {
callback(error);
}
async update(id, updateData) {
const db = await getDb();
const [result] = await db.query(`UPDATE vacation_balance_details SET ? WHERE id = ?`, [updateData, id]);
return result;
},
/**
* 휴가 잔액 삭제
*/
async delete(id, callback) {
try {
const db = await getDb();
const query = `DELETE FROM vacation_balance_details WHERE id = ?`;
const [rows] = await db.query(query, [id]);
callback(null, rows);
} catch (error) {
callback(error);
}
async delete(id) {
const db = await getDb();
const [result] = await db.query(`DELETE FROM vacation_balance_details WHERE id = ?`, [id]);
return result;
},
/**
* 작업자의 휴가 사용 일수 업데이트 (차감)
* - 휴가 신청 승인 시 호출
*/
async deductDays(workerId, vacationTypeId, year, daysToDeduct, callback) {
try {
const db = await getDb();
const query = `
UPDATE vacation_balance_details
SET used_days = used_days + ?,
updated_at = NOW()
WHERE worker_id = ?
AND vacation_type_id = ?
AND year = ?
`;
const [rows] = await db.query(query, [daysToDeduct, workerId, vacationTypeId, year]);
callback(null, rows);
} catch (error) {
callback(error);
}
async deductDays(workerId, vacationTypeId, year, daysToDeduct) {
const db = await getDb();
const [result] = await db.query(`
UPDATE vacation_balance_details
SET used_days = used_days + ?,
updated_at = NOW()
WHERE worker_id = ?
AND vacation_type_id = ?
AND year = ?
`, [daysToDeduct, workerId, vacationTypeId, year]);
return result;
},
/**
* 작업자의 휴가 사용 일수 복구 (취소)
* - 휴가 신청 취소/거부 시 호출
*/
async restoreDays(workerId, vacationTypeId, year, daysToRestore, callback) {
try {
const db = await getDb();
const query = `
UPDATE vacation_balance_details
SET used_days = GREATEST(0, used_days - ?),
updated_at = NOW()
WHERE worker_id = ?
AND vacation_type_id = ?
AND year = ?
`;
const [rows] = await db.query(query, [daysToRestore, workerId, vacationTypeId, year]);
callback(null, rows);
} catch (error) {
callback(error);
}
async restoreDays(workerId, vacationTypeId, year, daysToRestore) {
const db = await getDb();
const [result] = await db.query(`
UPDATE vacation_balance_details
SET used_days = GREATEST(0, used_days - ?),
updated_at = NOW()
WHERE worker_id = ?
AND vacation_type_id = ?
AND year = ?
`, [daysToRestore, workerId, vacationTypeId, year]);
return result;
},
/**
* 특정 작업자의 사용 가능한 휴가 일수 확인
* - 우선순위가 높은 순서대로 차감 가능 여부 확인
*/
async getAvailableVacationDays(workerId, year, callback) {
try {
const db = await getDb();
const query = `
SELECT
vbd.id,
vbd.vacation_type_id,
vt.type_name,
vt.type_code,
vt.priority,
vbd.total_days,
vbd.used_days,
vbd.remaining_days
FROM vacation_balance_details vbd
INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
WHERE vbd.worker_id = ?
AND vbd.year = ?
AND vbd.remaining_days > 0
ORDER BY vt.priority ASC
`;
const [rows] = await db.query(query, [workerId, year]);
callback(null, rows);
} catch (error) {
callback(error);
}
async getAvailableVacationDays(workerId, year) {
const db = await getDb();
const [rows] = await db.query(`
SELECT
vbd.id,
vbd.vacation_type_id,
vt.type_name,
vt.type_code,
vt.priority,
vbd.total_days,
vbd.used_days,
vbd.remaining_days
FROM vacation_balance_details vbd
INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
WHERE vbd.worker_id = ?
AND vbd.year = ?
AND vbd.remaining_days > 0
ORDER BY vt.priority ASC
`, [workerId, year]);
return rows;
},
/**
* 작업자별 휴가 잔액 일괄 생성 (연도별)
* - 매년 초 또는 입사 시 사용
*/
async bulkCreate(balances, callback) {
try {
const db = await getDb();
if (!balances || balances.length === 0) {
return callback(new Error('생성할 휴가 잔액 데이터가 없습니다'));
}
const query = `INSERT INTO vacation_balance_details
(worker_id, vacation_type_id, year, total_days, used_days, notes, created_by)
VALUES ?`;
const values = balances.map(b => [
b.worker_id,
b.vacation_type_id,
b.year,
b.total_days || 0,
b.used_days || 0,
b.notes || null,
b.created_by
]);
const [rows] = await db.query(query, [values]);
callback(null, rows);
} catch (error) {
callback(error);
async bulkCreate(balances) {
if (!balances || balances.length === 0) {
throw new Error('생성할 휴가 잔액 데이터가 없습니다');
}
const db = await getDb();
const query = `INSERT INTO vacation_balance_details
(worker_id, vacation_type_id, year, total_days, used_days, notes, created_by)
VALUES ?`;
const values = balances.map(b => [
b.worker_id,
b.vacation_type_id,
b.year,
b.total_days || 0,
b.used_days || 0,
b.notes || null,
b.created_by
]);
const [result] = await db.query(query, [values]);
return result;
},
/**
* 근속년수 기반 연차 일수 계산 (한국 근로기준법)
* @param {Date} hireDate - 입사일
* @param {number} targetYear - 대상 연도
* @returns {number} - 부여받을 연차 일수
*/
calculateAnnualLeaveDays(hireDate, targetYear) {
const hire = new Date(hireDate);
const targetDate = new Date(targetYear, 0, 1);
// 근속 월수 계산
const monthsDiff = (targetDate.getFullYear() - hire.getFullYear()) * 12
+ (targetDate.getMonth() - hire.getMonth());
// 1년 미만: 월 1일
if (monthsDiff < 12) {
return Math.floor(monthsDiff);
}
// 1년 이상: 15일 기본 + 2년마다 1일 추가 (최대 25일)
const yearsWorked = Math.floor(monthsDiff / 12);
const additionalDays = Math.floor((yearsWorked - 1) / 2);
@@ -261,17 +200,11 @@ const vacationBalanceModel = {
},
/**
* 휴가 사용 시 우선순위에 따라 잔액에서 차감 (Promise 버전)
* - 일일 근태 기록 저장 시 호출
* @param {number} workerId - 작업자 ID
* @param {number} year - 연도
* @param {number} daysToDeduct - 차감할 일수 (1, 0.5, 0.25)
* @returns {Promise<Object>} - 차감 결과
* 휴가 사용 시 우선순위에 따라 잔액에서 차감
*/
async deductByPriority(workerId, year, daysToDeduct) {
const db = await getDb();
// 우선순위순으로 잔여 일수가 있는 잔액 조회
const [balances] = await db.query(`
SELECT vbd.id, vbd.vacation_type_id, vbd.total_days, vbd.used_days,
(vbd.total_days - vbd.used_days) as remaining_days,
@@ -284,7 +217,6 @@ const vacationBalanceModel = {
`, [workerId, year]);
if (balances.length === 0) {
// 잔액이 없어도 일단 기록은 저장 (경고만)
console.warn(`[VacationBalance] 작업자 ${workerId}${year}년 휴가 잔액이 없습니다`);
return { success: false, message: '휴가 잔액이 없습니다', deducted: 0 };
}
@@ -321,16 +253,11 @@ const vacationBalanceModel = {
},
/**
* 휴가 취소 시 우선순위 역순으로 복구 (Promise 버전)
* @param {number} workerId - 작업자 ID
* @param {number} year - 연도
* @param {number} daysToRestore - 복구할 일수
* @returns {Promise<Object>} - 복구 결과
* 휴가 취소 시 우선순위 역순으로 복구
*/
async restoreByPriority(workerId, year, daysToRestore) {
const db = await getDb();
// 우선순위 역순으로 사용 일수가 있는 잔액 조회 (나중에 차감된 것부터 복구)
const [balances] = await db.query(`
SELECT vbd.id, vbd.vacation_type_id, vbd.used_days,
vt.type_code, vt.type_name, vt.priority
@@ -375,25 +302,20 @@ const vacationBalanceModel = {
/**
* 특정 ID로 휴가 잔액 조회
*/
async getById(id, callback) {
try {
const db = await getDb();
const query = `
SELECT
vbd.*,
w.worker_name,
vt.type_name,
vt.type_code
FROM vacation_balance_details vbd
INNER JOIN workers w ON vbd.worker_id = w.worker_id
INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
WHERE vbd.id = ?
`;
const [rows] = await db.query(query, [id]);
callback(null, rows);
} catch (error) {
callback(error);
}
async getById(id) {
const db = await getDb();
const [rows] = await db.query(`
SELECT
vbd.*,
w.worker_name,
vt.type_name,
vt.type_code
FROM vacation_balance_details vbd
INNER JOIN workers w ON vbd.worker_id = w.worker_id
INNER JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
WHERE vbd.id = ?
`, [id]);
return rows;
}
};

View File

@@ -9,123 +9,97 @@ const vacationTypeModel = {
/**
* 모든 활성 휴가 유형 조회 (우선순위 순서대로)
*/
async getAll(callback) {
try {
const db = await getDb();
const query = `
SELECT *
FROM vacation_types
WHERE is_active = 1
ORDER BY priority ASC, id ASC
`;
const [rows] = await db.query(query);
callback(null, rows);
} catch (error) {
callback(error);
}
async getAll() {
const db = await getDb();
const [rows] = await db.query(`
SELECT *
FROM vacation_types
WHERE is_active = 1
ORDER BY priority ASC, id ASC
`);
return rows;
},
/**
* 시스템 기본 휴가 유형만 조회
*/
async getSystemTypes(callback) {
try {
const db = await getDb();
const query = `
SELECT *
FROM vacation_types
WHERE is_system = 1 AND is_active = 1
ORDER BY priority ASC
`;
const [rows] = await db.query(query);
callback(null, rows);
} catch (error) {
callback(error);
}
async getSystemTypes() {
const db = await getDb();
const [rows] = await db.query(`
SELECT *
FROM vacation_types
WHERE is_system = 1 AND is_active = 1
ORDER BY priority ASC
`);
return rows;
},
/**
* 특별 휴가 유형만 조회
*/
async getSpecialTypes() {
const db = await getDb();
const [rows] = await db.query(`
SELECT *
FROM vacation_types
WHERE is_special = 1 AND is_active = 1
ORDER BY priority ASC
`);
return rows;
},
/**
* 특정 ID로 휴가 유형 조회
*/
async getById(id, callback) {
try {
const db = await getDb();
const query = `SELECT * FROM vacation_types WHERE id = ?`;
const [rows] = await db.query(query, [id]);
callback(null, rows);
} catch (error) {
callback(error);
}
async getById(id) {
const db = await getDb();
const [rows] = await db.query(`SELECT * FROM vacation_types WHERE id = ?`, [id]);
return rows;
},
/**
* 휴가 유형 코드로 조회
*/
async getByCode(code, callback) {
try {
const db = await getDb();
const query = `SELECT * FROM vacation_types WHERE type_code = ?`;
const [rows] = await db.query(query, [code]);
callback(null, rows);
} catch (error) {
callback(error);
}
async getByCode(code) {
const db = await getDb();
const [rows] = await db.query(`SELECT * FROM vacation_types WHERE type_code = ?`, [code]);
return rows;
},
/**
* 휴가 유형 생성
*/
async create(typeData, callback) {
try {
const db = await getDb();
const query = `INSERT INTO vacation_types SET ?`;
const [result] = await db.query(query, typeData);
callback(null, result);
} catch (error) {
callback(error);
}
async create(typeData) {
const db = await getDb();
const [result] = await db.query(`INSERT INTO vacation_types SET ?`, typeData);
return result;
},
/**
* 휴가 유형 수정
*/
async update(id, updateData, callback) {
try {
const db = await getDb();
const query = `UPDATE vacation_types SET ? WHERE id = ?`;
const [result] = await db.query(query, [updateData, id]);
callback(null, result);
} catch (error) {
callback(error);
}
async update(id, updateData) {
const db = await getDb();
const [result] = await db.query(`UPDATE vacation_types SET ? WHERE id = ?`, [updateData, id]);
return result;
},
/**
* 휴가 유형 삭제 (논리적 삭제 - is_active = 0)
*/
async delete(id, callback) {
try {
const db = await getDb();
const query = `UPDATE vacation_types SET is_active = 0, updated_at = NOW() WHERE id = ?`;
const [result] = await db.query(query, [id]);
callback(null, result);
} catch (error) {
callback(error);
}
async delete(id) {
const db = await getDb();
const [result] = await db.query(`UPDATE vacation_types SET is_active = 0, updated_at = NOW() WHERE id = ?`, [id]);
return result;
},
/**
* 우선순위 업데이트
*/
async updatePriority(id, priority, callback) {
try {
const db = await getDb();
const query = `UPDATE vacation_types SET priority = ?, updated_at = NOW() WHERE id = ?`;
const [result] = await db.query(query, [priority, id]);
callback(null, result);
} catch (error) {
callback(error);
}
async updatePriority(id, priority) {
const db = await getDb();
const [result] = await db.query(`UPDATE vacation_types SET priority = ?, updated_at = NOW() WHERE id = ?`, [priority, id]);
return result;
}
};

View File

@@ -2,484 +2,324 @@ const { getDb } = require('../dbPool');
// ==================== 출입 신청 관리 ====================
/**
* 출입 신청 생성
*/
const createVisitRequest = async (requestData, callback) => {
try {
const db = await getDb();
const {
requester_id,
visitor_company,
visitor_count = 1,
category_id,
workplace_id,
visit_date,
visit_time,
purpose_id,
notes = null
} = requestData;
const createVisitRequest = async (requestData) => {
const db = await getDb();
const {
requester_id,
visitor_company,
visitor_count = 1,
category_id,
workplace_id,
visit_date,
visit_time,
purpose_id,
notes = null
} = requestData;
const [result] = await db.query(
`INSERT INTO workplace_visit_requests
(requester_id, visitor_company, visitor_count, category_id, workplace_id,
visit_date, visit_time, purpose_id, notes)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[requester_id, visitor_company, visitor_count, category_id, workplace_id,
visit_date, visit_time, purpose_id, notes]
);
const [result] = await db.query(
`INSERT INTO workplace_visit_requests
(requester_id, visitor_company, visitor_count, category_id, workplace_id,
visit_date, visit_time, purpose_id, notes)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
[requester_id, visitor_company, visitor_count, category_id, workplace_id,
visit_date, visit_time, purpose_id, notes]
);
callback(null, result.insertId);
} catch (err) {
callback(err);
}
return result.insertId;
};
/**
* 출입 신청 목록 조회 (필터 옵션 포함)
*/
const getAllVisitRequests = async (filters = {}, callback) => {
try {
const db = await getDb();
let query = `
SELECT
vr.request_id, vr.requester_id, vr.visitor_company, vr.visitor_count,
vr.category_id, vr.workplace_id, vr.visit_date, vr.visit_time,
vr.purpose_id, vr.notes, vr.status,
vr.approved_by, vr.approved_at, vr.rejection_reason,
vr.created_at, vr.updated_at,
u.username as requester_name, u.name as requester_full_name,
wc.category_name, w.workplace_name,
vpt.purpose_name,
approver.username as approver_name
FROM workplace_visit_requests vr
INNER JOIN users u ON vr.requester_id = u.user_id
INNER JOIN workplace_categories wc ON vr.category_id = wc.category_id
INNER JOIN workplaces w ON vr.workplace_id = w.workplace_id
INNER JOIN visit_purpose_types vpt ON vr.purpose_id = vpt.purpose_id
LEFT JOIN users approver ON vr.approved_by = approver.user_id
WHERE 1=1
`;
const getAllVisitRequests = async (filters = {}) => {
const db = await getDb();
let query = `
SELECT
vr.request_id, vr.requester_id, vr.visitor_company, vr.visitor_count,
vr.category_id, vr.workplace_id, vr.visit_date, vr.visit_time,
vr.purpose_id, vr.notes, vr.status,
vr.approved_by, vr.approved_at, vr.rejection_reason,
vr.created_at, vr.updated_at,
u.username as requester_name, u.name as requester_full_name,
wc.category_name, w.workplace_name,
vpt.purpose_name,
approver.username as approver_name
FROM workplace_visit_requests vr
INNER JOIN users u ON vr.requester_id = u.user_id
INNER JOIN workplace_categories wc ON vr.category_id = wc.category_id
INNER JOIN workplaces w ON vr.workplace_id = w.workplace_id
INNER JOIN visit_purpose_types vpt ON vr.purpose_id = vpt.purpose_id
LEFT JOIN users approver ON vr.approved_by = approver.user_id
WHERE 1=1
`;
const params = [];
const params = [];
// 필터 적용
if (filters.status) {
query += ` AND vr.status = ?`;
params.push(filters.status);
}
if (filters.visit_date) {
query += ` AND vr.visit_date = ?`;
params.push(filters.visit_date);
}
if (filters.start_date && filters.end_date) {
query += ` AND vr.visit_date BETWEEN ? AND ?`;
params.push(filters.start_date, filters.end_date);
}
if (filters.requester_id) {
query += ` AND vr.requester_id = ?`;
params.push(filters.requester_id);
}
if (filters.category_id) {
query += ` AND vr.category_id = ?`;
params.push(filters.category_id);
}
query += ` ORDER BY vr.visit_date DESC, vr.visit_time DESC, vr.created_at DESC`;
const [rows] = await db.query(query, params);
callback(null, rows);
} catch (err) {
callback(err);
if (filters.status) {
query += ` AND vr.status = ?`;
params.push(filters.status);
}
if (filters.visit_date) {
query += ` AND vr.visit_date = ?`;
params.push(filters.visit_date);
}
if (filters.start_date && filters.end_date) {
query += ` AND vr.visit_date BETWEEN ? AND ?`;
params.push(filters.start_date, filters.end_date);
}
if (filters.requester_id) {
query += ` AND vr.requester_id = ?`;
params.push(filters.requester_id);
}
if (filters.category_id) {
query += ` AND vr.category_id = ?`;
params.push(filters.category_id);
}
query += ` ORDER BY vr.visit_date DESC, vr.visit_time DESC, vr.created_at DESC`;
const [rows] = await db.query(query, params);
return rows;
};
/**
* 출입 신청 상세 조회
*/
const getVisitRequestById = async (requestId, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT
vr.request_id, vr.requester_id, vr.visitor_company, vr.visitor_count,
vr.category_id, vr.workplace_id, vr.visit_date, vr.visit_time,
vr.purpose_id, vr.notes, vr.status,
vr.approved_by, vr.approved_at, vr.rejection_reason,
vr.created_at, vr.updated_at,
u.username as requester_name, u.name as requester_full_name,
wc.category_name, w.workplace_name,
vpt.purpose_name,
approver.username as approver_name
FROM workplace_visit_requests vr
INNER JOIN users u ON vr.requester_id = u.user_id
INNER JOIN workplace_categories wc ON vr.category_id = wc.category_id
INNER JOIN workplaces w ON vr.workplace_id = w.workplace_id
INNER JOIN visit_purpose_types vpt ON vr.purpose_id = vpt.purpose_id
LEFT JOIN users approver ON vr.approved_by = approver.user_id
WHERE vr.request_id = ?`,
[requestId]
);
callback(null, rows[0]);
} catch (err) {
callback(err);
}
const getVisitRequestById = async (requestId) => {
const db = await getDb();
const [rows] = await db.query(
`SELECT
vr.request_id, vr.requester_id, vr.visitor_company, vr.visitor_count,
vr.category_id, vr.workplace_id, vr.visit_date, vr.visit_time,
vr.purpose_id, vr.notes, vr.status,
vr.approved_by, vr.approved_at, vr.rejection_reason,
vr.created_at, vr.updated_at,
u.username as requester_name, u.name as requester_full_name,
wc.category_name, w.workplace_name,
vpt.purpose_name,
approver.username as approver_name
FROM workplace_visit_requests vr
INNER JOIN users u ON vr.requester_id = u.user_id
INNER JOIN workplace_categories wc ON vr.category_id = wc.category_id
INNER JOIN workplaces w ON vr.workplace_id = w.workplace_id
INNER JOIN visit_purpose_types vpt ON vr.purpose_id = vpt.purpose_id
LEFT JOIN users approver ON vr.approved_by = approver.user_id
WHERE vr.request_id = ?`,
[requestId]
);
return rows[0];
};
/**
* 출입 신청 수정
*/
const updateVisitRequest = async (requestId, requestData, callback) => {
try {
const db = await getDb();
const {
visitor_company,
visitor_count,
category_id,
workplace_id,
visit_date,
visit_time,
purpose_id,
notes
} = requestData;
const updateVisitRequest = async (requestId, requestData) => {
const db = await getDb();
const {
visitor_company, visitor_count, category_id, workplace_id,
visit_date, visit_time, purpose_id, notes
} = requestData;
const [result] = await db.query(
`UPDATE workplace_visit_requests
SET visitor_company = ?, visitor_count = ?, category_id = ?, workplace_id = ?,
visit_date = ?, visit_time = ?, purpose_id = ?, notes = ?, updated_at = NOW()
WHERE request_id = ?`,
[visitor_company, visitor_count, category_id, workplace_id,
visit_date, visit_time, purpose_id, notes, requestId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const [result] = await db.query(
`UPDATE workplace_visit_requests
SET visitor_company = ?, visitor_count = ?, category_id = ?, workplace_id = ?,
visit_date = ?, visit_time = ?, purpose_id = ?, notes = ?, updated_at = NOW()
WHERE request_id = ?`,
[visitor_company, visitor_count, category_id, workplace_id,
visit_date, visit_time, purpose_id, notes, requestId]
);
return result;
};
/**
* 출입 신청 삭제
*/
const deleteVisitRequest = async (requestId, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM workplace_visit_requests WHERE request_id = ?`,
[requestId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const deleteVisitRequest = async (requestId) => {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM workplace_visit_requests WHERE request_id = ?`,
[requestId]
);
return result;
};
/**
* 출입 신청 승인
*/
const approveVisitRequest = async (requestId, approvedBy, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
`UPDATE workplace_visit_requests
SET status = 'approved', approved_by = ?, approved_at = NOW(), updated_at = NOW()
WHERE request_id = ?`,
[approvedBy, requestId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const approveVisitRequest = async (requestId, approvedBy) => {
const db = await getDb();
const [result] = await db.query(
`UPDATE workplace_visit_requests
SET status = 'approved', approved_by = ?, approved_at = NOW(), updated_at = NOW()
WHERE request_id = ?`,
[approvedBy, requestId]
);
return result;
};
/**
* 출입 신청 반려
*/
const rejectVisitRequest = async (requestId, rejectionData, callback) => {
try {
const db = await getDb();
const { approved_by, rejection_reason } = rejectionData;
const rejectVisitRequest = async (requestId, rejectionData) => {
const db = await getDb();
const { approved_by, rejection_reason } = rejectionData;
const [result] = await db.query(
`UPDATE workplace_visit_requests
SET status = 'rejected', approved_by = ?, approved_at = NOW(),
rejection_reason = ?, updated_at = NOW()
WHERE request_id = ?`,
[approved_by, rejection_reason, requestId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const [result] = await db.query(
`UPDATE workplace_visit_requests
SET status = 'rejected', approved_by = ?, approved_at = NOW(),
rejection_reason = ?, updated_at = NOW()
WHERE request_id = ?`,
[approved_by, rejection_reason, requestId]
);
return result;
};
/**
* 출입 신청 상태 변경
*/
const updateVisitRequestStatus = async (requestId, status, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
`UPDATE workplace_visit_requests
SET status = ?, updated_at = NOW()
WHERE request_id = ?`,
[status, requestId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const updateVisitRequestStatus = async (requestId, status) => {
const db = await getDb();
const [result] = await db.query(
`UPDATE workplace_visit_requests
SET status = ?, updated_at = NOW()
WHERE request_id = ?`,
[status, requestId]
);
return result;
};
// ==================== 방문 목적 관리 ====================
/**
* 모든 방문 목적 조회
*/
const getAllVisitPurposes = async (callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT purpose_id, purpose_name, display_order, is_active, created_at
FROM visit_purpose_types
ORDER BY display_order ASC, purpose_id ASC`
);
callback(null, rows);
} catch (err) {
callback(err);
}
const getAllVisitPurposes = async () => {
const db = await getDb();
const [rows] = await db.query(
`SELECT purpose_id, purpose_name, display_order, is_active, created_at
FROM visit_purpose_types
ORDER BY display_order ASC, purpose_id ASC`
);
return rows;
};
/**
* 활성 방문 목적만 조회
*/
const getActiveVisitPurposes = async (callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT purpose_id, purpose_name, display_order, is_active, created_at
FROM visit_purpose_types
WHERE is_active = TRUE
ORDER BY display_order ASC, purpose_id ASC`
);
callback(null, rows);
} catch (err) {
callback(err);
}
const getActiveVisitPurposes = async () => {
const db = await getDb();
const [rows] = await db.query(
`SELECT purpose_id, purpose_name, display_order, is_active, created_at
FROM visit_purpose_types
WHERE is_active = TRUE
ORDER BY display_order ASC, purpose_id ASC`
);
return rows;
};
/**
* 방문 목적 추가
*/
const createVisitPurpose = async (purposeData, callback) => {
try {
const db = await getDb();
const { purpose_name, display_order = 0, is_active = true } = purposeData;
const createVisitPurpose = async (purposeData) => {
const db = await getDb();
const { purpose_name, display_order = 0, is_active = true } = purposeData;
const [result] = await db.query(
`INSERT INTO visit_purpose_types (purpose_name, display_order, is_active)
VALUES (?, ?, ?)`,
[purpose_name, display_order, is_active]
);
callback(null, result.insertId);
} catch (err) {
callback(err);
}
const [result] = await db.query(
`INSERT INTO visit_purpose_types (purpose_name, display_order, is_active)
VALUES (?, ?, ?)`,
[purpose_name, display_order, is_active]
);
return result.insertId;
};
/**
* 방문 목적 수정
*/
const updateVisitPurpose = async (purposeId, purposeData, callback) => {
try {
const db = await getDb();
const { purpose_name, display_order, is_active } = purposeData;
const updateVisitPurpose = async (purposeId, purposeData) => {
const db = await getDb();
const { purpose_name, display_order, is_active } = purposeData;
const [result] = await db.query(
`UPDATE visit_purpose_types
SET purpose_name = ?, display_order = ?, is_active = ?
WHERE purpose_id = ?`,
[purpose_name, display_order, is_active, purposeId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const [result] = await db.query(
`UPDATE visit_purpose_types
SET purpose_name = ?, display_order = ?, is_active = ?
WHERE purpose_id = ?`,
[purpose_name, display_order, is_active, purposeId]
);
return result;
};
/**
* 방문 목적 삭제
*/
const deleteVisitPurpose = async (purposeId, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM visit_purpose_types WHERE purpose_id = ?`,
[purposeId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const deleteVisitPurpose = async (purposeId) => {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM visit_purpose_types WHERE purpose_id = ?`,
[purposeId]
);
return result;
};
// ==================== 안전교육 기록 관리 ====================
/**
* 안전교육 기록 생성
*/
const createTrainingRecord = async (trainingData, callback) => {
try {
const db = await getDb();
const {
request_id,
trainer_id,
training_date,
training_start_time,
training_end_time = null,
training_topics = null
} = trainingData;
const createTrainingRecord = async (trainingData) => {
const db = await getDb();
const {
request_id, trainer_id, training_date,
training_start_time, training_end_time = null, training_topics = null
} = trainingData;
const [result] = await db.query(
`INSERT INTO safety_training_records
(request_id, trainer_id, training_date, training_start_time, training_end_time, training_topics)
VALUES (?, ?, ?, ?, ?, ?)`,
[request_id, trainer_id, training_date, training_start_time, training_end_time, training_topics]
);
callback(null, result.insertId);
} catch (err) {
callback(err);
}
const [result] = await db.query(
`INSERT INTO safety_training_records
(request_id, trainer_id, training_date, training_start_time, training_end_time, training_topics)
VALUES (?, ?, ?, ?, ?, ?)`,
[request_id, trainer_id, training_date, training_start_time, training_end_time, training_topics]
);
return result.insertId;
};
/**
* 특정 출입 신청의 안전교육 기록 조회
*/
const getTrainingRecordByRequestId = async (requestId, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT
str.training_id, str.request_id, str.trainer_id, str.training_date,
str.training_start_time, str.training_end_time, str.training_topics,
str.signature_data, str.completed_at, str.created_at, str.updated_at,
u.username as trainer_name, u.name as trainer_full_name
FROM safety_training_records str
INNER JOIN users u ON str.trainer_id = u.user_id
WHERE str.request_id = ?`,
[requestId]
);
callback(null, rows[0]);
} catch (err) {
callback(err);
}
const getTrainingRecordByRequestId = async (requestId) => {
const db = await getDb();
const [rows] = await db.query(
`SELECT
str.training_id, str.request_id, str.trainer_id, str.training_date,
str.training_start_time, str.training_end_time, str.training_topics,
str.signature_data, str.completed_at, str.created_at, str.updated_at,
u.username as trainer_name, u.name as trainer_full_name
FROM safety_training_records str
INNER JOIN users u ON str.trainer_id = u.user_id
WHERE str.request_id = ?`,
[requestId]
);
return rows[0];
};
/**
* 안전교육 기록 수정
*/
const updateTrainingRecord = async (trainingId, trainingData, callback) => {
try {
const db = await getDb();
const {
training_date,
training_start_time,
training_end_time,
training_topics
} = trainingData;
const updateTrainingRecord = async (trainingId, trainingData) => {
const db = await getDb();
const { training_date, training_start_time, training_end_time, training_topics } = trainingData;
const [result] = await db.query(
`UPDATE safety_training_records
SET training_date = ?, training_start_time = ?, training_end_time = ?,
training_topics = ?, updated_at = NOW()
WHERE training_id = ?`,
[training_date, training_start_time, training_end_time, training_topics, trainingId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const [result] = await db.query(
`UPDATE safety_training_records
SET training_date = ?, training_start_time = ?, training_end_time = ?,
training_topics = ?, updated_at = NOW()
WHERE training_id = ?`,
[training_date, training_start_time, training_end_time, training_topics, trainingId]
);
return result;
};
/**
* 안전교육 완료 (서명 포함)
*/
const completeTraining = async (trainingId, signatureData, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
`UPDATE safety_training_records
SET signature_data = ?, completed_at = NOW(), updated_at = NOW()
WHERE training_id = ?`,
[signatureData, trainingId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const completeTraining = async (trainingId, signatureData) => {
const db = await getDb();
const [result] = await db.query(
`UPDATE safety_training_records
SET signature_data = ?, completed_at = NOW(), updated_at = NOW()
WHERE training_id = ?`,
[signatureData, trainingId]
);
return result;
};
/**
* 안전교육 목록 조회 (날짜별 필터)
*/
const getTrainingRecords = async (filters = {}, callback) => {
try {
const db = await getDb();
let query = `
SELECT
str.training_id, str.request_id, str.trainer_id, str.training_date,
str.training_start_time, str.training_end_time, str.training_topics,
str.completed_at, str.created_at, str.updated_at,
u.username as trainer_name, u.name as trainer_full_name,
vr.visitor_company, vr.visitor_count, vr.visit_date
FROM safety_training_records str
INNER JOIN users u ON str.trainer_id = u.user_id
INNER JOIN workplace_visit_requests vr ON str.request_id = vr.request_id
WHERE 1=1
`;
const getTrainingRecords = async (filters = {}) => {
const db = await getDb();
let query = `
SELECT
str.training_id, str.request_id, str.trainer_id, str.training_date,
str.training_start_time, str.training_end_time, str.training_topics,
str.completed_at, str.created_at, str.updated_at,
u.username as trainer_name, u.name as trainer_full_name,
vr.visitor_company, vr.visitor_count, vr.visit_date
FROM safety_training_records str
INNER JOIN users u ON str.trainer_id = u.user_id
INNER JOIN workplace_visit_requests vr ON str.request_id = vr.request_id
WHERE 1=1
`;
const params = [];
const params = [];
if (filters.training_date) {
query += ` AND str.training_date = ?`;
params.push(filters.training_date);
}
if (filters.start_date && filters.end_date) {
query += ` AND str.training_date BETWEEN ? AND ?`;
params.push(filters.start_date, filters.end_date);
}
if (filters.trainer_id) {
query += ` AND str.trainer_id = ?`;
params.push(filters.trainer_id);
}
query += ` ORDER BY str.training_date DESC, str.training_start_time DESC`;
const [rows] = await db.query(query, params);
callback(null, rows);
} catch (err) {
callback(err);
if (filters.training_date) {
query += ` AND str.training_date = ?`;
params.push(filters.training_date);
}
if (filters.start_date && filters.end_date) {
query += ` AND str.training_date BETWEEN ? AND ?`;
params.push(filters.start_date, filters.end_date);
}
if (filters.trainer_id) {
query += ` AND str.trainer_id = ?`;
params.push(filters.trainer_id);
}
query += ` ORDER BY str.training_date DESC, str.training_start_time DESC`;
const [rows] = await db.query(query, params);
return rows;
};
module.exports = {
// 출입 신청
createVisitRequest,
getAllVisitRequests,
getVisitRequestById,
@@ -488,15 +328,11 @@ module.exports = {
approveVisitRequest,
rejectVisitRequest,
updateVisitRequestStatus,
// 방문 목적
getAllVisitPurposes,
getActiveVisitPurposes,
createVisitPurpose,
updateVisitPurpose,
deleteVisitPurpose,
// 안전교육
createTrainingRecord,
getTrainingRecordByRequestId,
updateTrainingRecord,

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,7 @@ const { getDb } = require('../dbPool');
/**
* 1. 여러 건 등록 (트랜잭션 사용)
*/
const createBatch = async (reports, callback) => {
const createBatch = async (reports) => {
const db = await getDb();
const conn = await db.getConnection();
@@ -30,10 +30,9 @@ const createBatch = async (reports, callback) => {
}
await conn.commit();
callback(null);
} catch (err) {
await conn.rollback();
callback(err);
throw err;
} finally {
conn.release();
}
@@ -42,175 +41,146 @@ const createBatch = async (reports, callback) => {
/**
* 2. 단일 등록
*/
const create = async (report, callback) => {
try {
const db = await getDb();
const {
date, worker_id, project_id,
task_id, overtime_hours,
work_details, memo
} = report;
const create = async (report) => {
const db = await getDb();
const {
date, worker_id, project_id,
task_id, overtime_hours,
work_details, memo
} = report;
const [result] = await db.query(
`INSERT INTO WorkReports
(\`date\`, worker_id, project_id, task_id, overtime_hours, work_details, memo)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
[
date,
worker_id,
project_id,
task_id || null,
overtime_hours || null,
work_details || null,
memo || null
]
);
const [result] = await db.query(
`INSERT INTO WorkReports
(\`date\`, worker_id, project_id, task_id, overtime_hours, work_details, memo)
VALUES (?, ?, ?, ?, ?, ?, ?)`,
[
date,
worker_id,
project_id,
task_id || null,
overtime_hours || null,
work_details || null,
memo || null
]
);
callback(null, result.insertId);
} catch (err) {
callback(err);
}
return result.insertId;
};
/**
* 3. 날짜별 조회
*/
const getAllByDate = async (date, callback) => {
try {
const db = await getDb();
const sql = `
SELECT
wr.worker_id, -- 이 줄을 추가했습니다
wr.id,
wr.\`date\`,
w.worker_name,
p.project_name,
CONCAT(t.category, ':', t.subcategory) AS task_name,
wr.overtime_hours,
wr.work_details,
wr.memo
FROM WorkReports wr
LEFT JOIN workers w ON wr.worker_id = w.worker_id
LEFT JOIN projects p ON wr.project_id = p.project_id
LEFT JOIN Tasks t ON wr.task_id = t.task_id
WHERE wr.\`date\` = ?
ORDER BY w.worker_name ASC
`;
const [rows] = await db.query(sql, [date]);
callback(null, rows);
} catch (err) {
callback(err);
}
};
/**
* 3. 날짜별 조회
*/
const getAllByDate = async (date) => {
const db = await getDb();
const sql = `
SELECT
wr.worker_id,
wr.id,
wr.\`date\`,
w.worker_name,
p.project_name,
CONCAT(t.category, ':', t.subcategory) AS task_name,
wr.overtime_hours,
wr.work_details,
wr.memo
FROM WorkReports wr
LEFT JOIN workers w ON wr.worker_id = w.worker_id
LEFT JOIN projects p ON wr.project_id = p.project_id
LEFT JOIN Tasks t ON wr.task_id = t.task_id
WHERE wr.\`date\` = ?
ORDER BY w.worker_name ASC
`;
const [rows] = await db.query(sql, [date]);
return rows;
};
/**
* 4. 기간 조회
*/
const getByRange = async (start, end, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT id, \`date\`, worker_id, project_id, morning_task_id, afternoon_task_id, overtime_hours, overtime_task_id, work_details, note, memo, created_at, updated_at, morning_project_id, afternoon_project_id, overtime_project_id, task_id FROM WorkReports
WHERE \`date\` BETWEEN ? AND ?
ORDER BY \`date\` ASC`,
[start, end]
);
callback(null, rows);
} catch (err) {
callback(err);
}
const getByRange = async (start, end) => {
const db = await getDb();
const [rows] = await db.query(
`SELECT id, \`date\`, worker_id, project_id, morning_task_id, afternoon_task_id, overtime_hours, overtime_task_id, work_details, note, memo, created_at, updated_at, morning_project_id, afternoon_project_id, overtime_project_id, task_id FROM WorkReports
WHERE \`date\` BETWEEN ? AND ?
ORDER BY \`date\` ASC`,
[start, end]
);
return rows;
};
/**
* 5. ID로 조회
*/
const getById = async (id, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT id, \`date\`, worker_id, project_id, morning_task_id, afternoon_task_id, overtime_hours, overtime_task_id, work_details, note, memo, created_at, updated_at, morning_project_id, afternoon_project_id, overtime_project_id, task_id FROM WorkReports WHERE id = ?`,
[id]
);
callback(null, rows[0]);
} catch (err) {
callback(err);
}
const getById = async (id) => {
const db = await getDb();
const [rows] = await db.query(
`SELECT id, \`date\`, worker_id, project_id, morning_task_id, afternoon_task_id, overtime_hours, overtime_task_id, work_details, note, memo, created_at, updated_at, morning_project_id, afternoon_project_id, overtime_project_id, task_id FROM WorkReports WHERE id = ?`,
[id]
);
return rows[0];
};
/**
* 6. 수정
*/
const update = async (id, report, callback) => {
try {
const db = await getDb();
const {
date, worker_id, project_id,
task_id, overtime_hours,
work_details, memo
} = report;
const update = async (id, report) => {
const db = await getDb();
const {
date, worker_id, project_id,
task_id, overtime_hours,
work_details, memo
} = report;
const [result] = await db.query(
`UPDATE WorkReports
SET \`date\` = ?,
worker_id = ?,
project_id = ?,
task_id = ?,
overtime_hours = ?,
work_details = ?,
memo = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?`,
[
date,
worker_id,
project_id,
task_id || null,
overtime_hours || null,
work_details || null,
memo || null,
id
]
);
const [result] = await db.query(
`UPDATE WorkReports
SET \`date\` = ?,
worker_id = ?,
project_id = ?,
task_id = ?,
overtime_hours = ?,
work_details = ?,
memo = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = ?`,
[
date,
worker_id,
project_id,
task_id || null,
overtime_hours || null,
work_details || null,
memo || null,
id
]
);
callback(null, result.affectedRows);
} catch (err) {
callback(err);
}
return result.affectedRows;
};
/**
* 7. 삭제
*/
const remove = async (id, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM WorkReports WHERE id = ?`,
[id]
);
callback(null, result.affectedRows);
} catch (err) {
callback(new Error(err.message || String(err)));
}
const remove = async (id) => {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM WorkReports WHERE id = ?`,
[id]
);
return result.affectedRows;
};
/**
* 8. 중복 확인
*/
const existsByDateAndWorker = async (date, worker_id, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT 1 FROM WorkReports WHERE \`date\` = ? AND worker_id = ? LIMIT 1`,
[date, worker_id]
);
callback(null, rows.length > 0);
} catch (err) {
callback(err);
}
const existsByDateAndWorker = async (date, worker_id) => {
const db = await getDb();
const [rows] = await db.query(
`SELECT 1 FROM WorkReports WHERE \`date\` = ? AND worker_id = ? LIMIT 1`,
[date, worker_id]
);
return rows.length > 0;
};
// ✅ 내보내기
module.exports = {
create,
createBatch,
@@ -220,4 +190,4 @@ module.exports = {
update,
remove,
existsByDateAndWorker
};
};

View File

@@ -2,426 +2,290 @@ const { getDb } = require('../dbPool');
// ==================== 카테고리(공장) 관련 ====================
/**
* 카테고리 생성
*/
const createCategory = async (category, callback) => {
try {
const db = await getDb();
const {
category_name,
description = null,
display_order = 0,
is_active = true
} = category;
const createCategory = async (category) => {
const db = await getDb();
const {
category_name,
description = null,
display_order = 0,
is_active = true
} = category;
const [result] = await db.query(
`INSERT INTO workplace_categories
(category_name, description, display_order, is_active)
VALUES (?, ?, ?, ?)`,
[category_name, description, display_order, is_active]
);
const [result] = await db.query(
`INSERT INTO workplace_categories
(category_name, description, display_order, is_active)
VALUES (?, ?, ?, ?)`,
[category_name, description, display_order, is_active]
);
callback(null, result.insertId);
} catch (err) {
callback(err);
}
return result.insertId;
};
/**
* 모든 카테고리 조회
*/
const getAllCategories = async (callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at
FROM workplace_categories
ORDER BY display_order ASC, category_id ASC`
);
callback(null, rows);
} catch (err) {
callback(err);
}
const getAllCategories = async () => {
const db = await getDb();
const [rows] = await db.query(
`SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at
FROM workplace_categories
ORDER BY display_order ASC, category_id ASC`
);
return rows;
};
/**
* 활성 카테고리만 조회
*/
const getActiveCategories = async (callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at
FROM workplace_categories
WHERE is_active = TRUE
ORDER BY display_order ASC, category_id ASC`
);
callback(null, rows);
} catch (err) {
callback(err);
}
const getActiveCategories = async () => {
const db = await getDb();
const [rows] = await db.query(
`SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at
FROM workplace_categories
WHERE is_active = TRUE
ORDER BY display_order ASC, category_id ASC`
);
return rows;
};
/**
* ID로 카테고리 조회
*/
const getCategoryById = async (categoryId, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at
FROM workplace_categories
WHERE category_id = ?`,
[categoryId]
);
callback(null, rows[0]);
} catch (err) {
callback(err);
}
const getCategoryById = async (categoryId) => {
const db = await getDb();
const [rows] = await db.query(
`SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at
FROM workplace_categories
WHERE category_id = ?`,
[categoryId]
);
return rows[0];
};
/**
* 카테고리 수정
*/
const updateCategory = async (categoryId, category, callback) => {
try {
const db = await getDb();
const {
category_name,
description,
display_order,
is_active,
layout_image
} = category;
const updateCategory = async (categoryId, category) => {
const db = await getDb();
const {
category_name,
description,
display_order,
is_active,
layout_image
} = category;
const [result] = await db.query(
`UPDATE workplace_categories
SET category_name = ?, description = ?, display_order = ?, is_active = ?, layout_image = ?, updated_at = NOW()
WHERE category_id = ?`,
[category_name, description, display_order, is_active, layout_image, categoryId]
);
const [result] = await db.query(
`UPDATE workplace_categories
SET category_name = ?, description = ?, display_order = ?, is_active = ?, layout_image = ?, updated_at = NOW()
WHERE category_id = ?`,
[category_name, description, display_order, is_active, layout_image, categoryId]
);
callback(null, result);
} catch (err) {
callback(err);
}
return result;
};
/**
* 카테고리 삭제
*/
const deleteCategory = async (categoryId, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM workplace_categories WHERE category_id = ?`,
[categoryId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const deleteCategory = async (categoryId) => {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM workplace_categories WHERE category_id = ?`,
[categoryId]
);
return result;
};
// ==================== 작업장 관련 ====================
/**
* 작업장 생성
*/
const createWorkplace = async (workplace, callback) => {
try {
const db = await getDb();
const {
category_id = null,
workplace_name,
description = null,
is_active = true,
workplace_purpose = null,
display_priority = 0
} = workplace;
const createWorkplace = async (workplace) => {
const db = await getDb();
const {
category_id = null,
workplace_name,
description = null,
is_active = true,
workplace_purpose = null,
display_priority = 0
} = workplace;
const [result] = await db.query(
`INSERT INTO workplaces
(category_id, workplace_name, description, is_active, workplace_purpose, display_priority)
VALUES (?, ?, ?, ?, ?, ?)`,
[category_id, workplace_name, description, is_active, workplace_purpose, display_priority]
);
const [result] = await db.query(
`INSERT INTO workplaces
(category_id, workplace_name, description, is_active, workplace_purpose, display_priority)
VALUES (?, ?, ?, ?, ?, ?)`,
[category_id, workplace_name, description, is_active, workplace_purpose, display_priority]
);
callback(null, result.insertId);
} catch (err) {
callback(err);
}
return result.insertId;
};
/**
* 모든 작업장 조회 (카테고리 정보 포함)
*/
const getAllWorkplaces = async (callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
w.layout_image, w.created_at, w.updated_at,
wc.category_name
FROM workplaces w
LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id
ORDER BY wc.display_order ASC, w.display_priority ASC, w.workplace_id DESC`
);
callback(null, rows);
} catch (err) {
callback(err);
}
const getAllWorkplaces = async () => {
const db = await getDb();
const [rows] = await db.query(
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
w.layout_image, w.created_at, w.updated_at,
wc.category_name
FROM workplaces w
LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id
ORDER BY wc.display_order ASC, w.display_priority ASC, w.workplace_id DESC`
);
return rows;
};
/**
* 활성 작업장만 조회
*/
const getActiveWorkplaces = async (callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
w.layout_image, w.created_at, w.updated_at,
wc.category_name
FROM workplaces w
LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id
WHERE w.is_active = TRUE
ORDER BY wc.display_order ASC, w.workplace_id DESC`
);
callback(null, rows);
} catch (err) {
callback(err);
}
const getActiveWorkplaces = async () => {
const db = await getDb();
const [rows] = await db.query(
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
w.layout_image, w.created_at, w.updated_at,
wc.category_name
FROM workplaces w
LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id
WHERE w.is_active = TRUE
ORDER BY wc.display_order ASC, w.workplace_id DESC`
);
return rows;
};
/**
* 카테고리별 작업장 조회
*/
const getWorkplacesByCategory = async (categoryId, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
w.layout_image, w.created_at, w.updated_at,
wc.category_name
FROM workplaces w
LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id
WHERE w.category_id = ?
ORDER BY w.workplace_id DESC`,
[categoryId]
);
callback(null, rows);
} catch (err) {
callback(err);
}
const getWorkplacesByCategory = async (categoryId) => {
const db = await getDb();
const [rows] = await db.query(
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
w.layout_image, w.created_at, w.updated_at,
wc.category_name
FROM workplaces w
LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id
WHERE w.category_id = ?
ORDER BY w.workplace_id DESC`,
[categoryId]
);
return rows;
};
/**
* ID로 작업장 조회
*/
const getWorkplaceById = async (workplaceId, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
w.layout_image, w.created_at, w.updated_at,
wc.category_name
FROM workplaces w
LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id
WHERE w.workplace_id = ?`,
[workplaceId]
);
callback(null, rows[0]);
} catch (err) {
callback(err);
}
const getWorkplaceById = async (workplaceId) => {
const db = await getDb();
const [rows] = await db.query(
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
w.layout_image, w.created_at, w.updated_at,
wc.category_name
FROM workplaces w
LEFT JOIN workplace_categories wc ON w.category_id = wc.category_id
WHERE w.workplace_id = ?`,
[workplaceId]
);
return rows[0];
};
/**
* 작업장 수정
*/
const updateWorkplace = async (workplaceId, workplace, callback) => {
try {
const db = await getDb();
const {
category_id,
workplace_name,
description,
is_active,
workplace_purpose,
display_priority,
layout_image
} = workplace;
const updateWorkplace = async (workplaceId, workplace) => {
const db = await getDb();
const {
category_id,
workplace_name,
description,
is_active,
workplace_purpose,
display_priority,
layout_image
} = workplace;
const [result] = await db.query(
`UPDATE workplaces
SET category_id = ?, workplace_name = ?, description = ?, is_active = ?,
workplace_purpose = ?, display_priority = ?, layout_image = ?, updated_at = NOW()
WHERE workplace_id = ?`,
[category_id, workplace_name, description, is_active, workplace_purpose, display_priority, layout_image, workplaceId]
);
const [result] = await db.query(
`UPDATE workplaces
SET category_id = ?, workplace_name = ?, description = ?, is_active = ?,
workplace_purpose = ?, display_priority = ?, layout_image = ?, updated_at = NOW()
WHERE workplace_id = ?`,
[category_id, workplace_name, description, is_active, workplace_purpose, display_priority, layout_image, workplaceId]
);
callback(null, result);
} catch (err) {
callback(err);
}
return result;
};
/**
* 작업장 삭제
*/
const deleteWorkplace = async (workplaceId, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM workplaces WHERE workplace_id = ?`,
[workplaceId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const deleteWorkplace = async (workplaceId) => {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM workplaces WHERE workplace_id = ?`,
[workplaceId]
);
return result;
};
// ==================== 작업장 지도 영역 관련 ====================
/**
* 작업장 지도 영역 생성
*/
const createMapRegion = async (region, callback) => {
try {
const db = await getDb();
const {
workplace_id,
category_id,
x_start,
y_start,
x_end,
y_end,
shape = 'rect',
polygon_points = null
} = region;
const createMapRegion = async (region) => {
const db = await getDb();
const {
workplace_id,
category_id,
x_start,
y_start,
x_end,
y_end,
shape = 'rect',
polygon_points = null
} = region;
const [result] = await db.query(
`INSERT INTO workplace_map_regions
(workplace_id, category_id, x_start, y_start, x_end, y_end, shape, polygon_points)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[workplace_id, category_id, x_start, y_start, x_end, y_end, shape, polygon_points]
);
const [result] = await db.query(
`INSERT INTO workplace_map_regions
(workplace_id, category_id, x_start, y_start, x_end, y_end, shape, polygon_points)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
[workplace_id, category_id, x_start, y_start, x_end, y_end, shape, polygon_points]
);
callback(null, result.insertId);
} catch (err) {
callback(err);
}
return result.insertId;
};
/**
* 카테고리(공장)별 지도 영역 조회
*/
const getMapRegionsByCategory = async (categoryId, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT mr.*, w.workplace_name, w.description
FROM workplace_map_regions mr
INNER JOIN workplaces w ON mr.workplace_id = w.workplace_id
WHERE mr.category_id = ? AND w.is_active = TRUE
ORDER BY mr.region_id ASC`,
[categoryId]
);
callback(null, rows);
} catch (err) {
callback(err);
}
const getMapRegionsByCategory = async (categoryId) => {
const db = await getDb();
const [rows] = await db.query(
`SELECT mr.*, w.workplace_name, w.description
FROM workplace_map_regions mr
INNER JOIN workplaces w ON mr.workplace_id = w.workplace_id
WHERE mr.category_id = ? AND w.is_active = TRUE
ORDER BY mr.region_id ASC`,
[categoryId]
);
return rows;
};
/**
* 작업장별 지도 영역 조회
*/
const getMapRegionByWorkplace = async (workplaceId, callback) => {
try {
const db = await getDb();
const [rows] = await db.query(
`SELECT * FROM workplace_map_regions WHERE workplace_id = ?`,
[workplaceId]
);
callback(null, rows[0]);
} catch (err) {
callback(err);
}
const getMapRegionByWorkplace = async (workplaceId) => {
const db = await getDb();
const [rows] = await db.query(
`SELECT * FROM workplace_map_regions WHERE workplace_id = ?`,
[workplaceId]
);
return rows[0];
};
/**
* 지도 영역 수정
*/
const updateMapRegion = async (regionId, region, callback) => {
try {
const db = await getDb();
const {
x_start,
y_start,
x_end,
y_end,
shape,
polygon_points
} = region;
const updateMapRegion = async (regionId, region) => {
const db = await getDb();
const {
x_start,
y_start,
x_end,
y_end,
shape,
polygon_points
} = region;
const [result] = await db.query(
`UPDATE workplace_map_regions
SET x_start = ?, y_start = ?, x_end = ?, y_end = ?, shape = ?, polygon_points = ?, updated_at = NOW()
WHERE region_id = ?`,
[x_start, y_start, x_end, y_end, shape, polygon_points, regionId]
);
const [result] = await db.query(
`UPDATE workplace_map_regions
SET x_start = ?, y_start = ?, x_end = ?, y_end = ?, shape = ?, polygon_points = ?, updated_at = NOW()
WHERE region_id = ?`,
[x_start, y_start, x_end, y_end, shape, polygon_points, regionId]
);
callback(null, result);
} catch (err) {
callback(err);
}
return result;
};
/**
* 지도 영역 삭제
*/
const deleteMapRegion = async (regionId, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM workplace_map_regions WHERE region_id = ?`,
[regionId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const deleteMapRegion = async (regionId) => {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM workplace_map_regions WHERE region_id = ?`,
[regionId]
);
return result;
};
/**
* 작업장 영역 일괄 삭제 (카테고리별)
*/
const deleteMapRegionsByCategory = async (categoryId, callback) => {
try {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM workplace_map_regions WHERE category_id = ?`,
[categoryId]
);
callback(null, result);
} catch (err) {
callback(err);
}
const deleteMapRegionsByCategory = async (categoryId) => {
const db = await getDb();
const [result] = await db.query(
`DELETE FROM workplace_map_regions WHERE category_id = ?`,
[categoryId]
);
return result;
};
module.exports = {
// 카테고리
createCategory,
getAllCategories,
getActiveCategories,
getCategoryById,
updateCategory,
deleteCategory,
// 작업장
createWorkplace,
getAllWorkplaces,
getActiveWorkplaces,
@@ -429,8 +293,6 @@ module.exports = {
getWorkplaceById,
updateWorkplace,
deleteWorkplace,
// 지도 영역
createMapRegion,
getMapRegionsByCategory,
getMapRegionByWorkplace,