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:
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user