feat: 대시보드 작업장 현황 지도 구현
- 실시간 작업장 현황을 지도로 시각화 - 작업장 관리 페이지에서 정의한 구역 정보 활용 - TBM 작업자 및 방문자 현황 표시 주요 변경사항: - dashboard.html: 작업장 현황 섹션 추가 (기존 작업 현황 테이블 제거) - workplace-status.js: 지도 렌더링 및 데이터 통합 로직 구현 - modern-dashboard.js: 삭제된 DOM 요소 조건부 체크 추가 시각화 방식: - 인원 없음: 회색 테두리 + 작업장 이름 - 내부 작업자: 파란색 영역 + 인원 수 - 외부 방문자: 보라색 영역 + 인원 수 - 둘 다: 초록색 영역 + 총 인원 수 기술 구현: - Canvas API 기반 사각형 영역 렌더링 - map-regions API를 통한 데이터 일관성 보장 - 클릭 이벤트로 상세 정보 모달 표시 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -297,7 +297,7 @@ class AttendanceModel {
|
||||
|
||||
// 휴가 유형 정보 조회
|
||||
const [vacationTypes] = await db.execute(
|
||||
'SELECT id, type_code, type_name, hours_deduction, description, is_active, created_at, updated_at FROM vacation_types WHERE type_code = ?',
|
||||
'SELECT id, type_code, type_name, deduct_days, is_active, created_at, updated_at FROM vacation_types WHERE type_code = ?',
|
||||
[vacationType]
|
||||
);
|
||||
|
||||
@@ -391,7 +391,7 @@ class AttendanceModel {
|
||||
static async getVacationTypes() {
|
||||
const db = await getDb();
|
||||
const [rows] = await db.execute(
|
||||
'SELECT id, type_code, type_name, hours_deduction, description, is_active, created_at, updated_at FROM vacation_types WHERE is_active = TRUE ORDER BY hours_deduction DESC'
|
||||
'SELECT id, type_code, type_name, deduct_days, is_active, created_at, updated_at FROM vacation_types WHERE is_active = TRUE ORDER BY deduct_days DESC'
|
||||
);
|
||||
return rows;
|
||||
}
|
||||
@@ -458,6 +458,68 @@ class AttendanceModel {
|
||||
const [rows] = await db.execute(query, params);
|
||||
return rows;
|
||||
}
|
||||
|
||||
// 출근 체크 기록 생성 또는 업데이트
|
||||
static async upsertCheckin(checkinData) {
|
||||
const db = await getDb();
|
||||
const { worker_id, record_date, is_present } = checkinData;
|
||||
|
||||
// 해당 날짜에 기록이 있는지 확인
|
||||
const [existing] = await db.execute(
|
||||
'SELECT id FROM daily_attendance_records WHERE worker_id = ? AND record_date = ?',
|
||||
[worker_id, record_date]
|
||||
);
|
||||
|
||||
if (existing.length > 0) {
|
||||
// 업데이트
|
||||
await db.execute(
|
||||
'UPDATE daily_attendance_records SET is_present = ? WHERE id = ?',
|
||||
[is_present, existing[0].id]
|
||||
);
|
||||
return existing[0].id;
|
||||
} else {
|
||||
// 새로 생성 (기본값으로)
|
||||
const [result] = await db.execute(
|
||||
`INSERT INTO daily_attendance_records
|
||||
(worker_id, record_date, is_present, attendance_type_id, created_by)
|
||||
VALUES (?, ?, ?, 1, 1)`,
|
||||
[worker_id, record_date, is_present]
|
||||
);
|
||||
return result.insertId;
|
||||
}
|
||||
}
|
||||
|
||||
// 특정 날짜의 출근 체크 목록 조회 (휴가 정보 포함)
|
||||
static async getCheckinList(date) {
|
||||
const db = await getDb();
|
||||
const query = `
|
||||
SELECT
|
||||
w.worker_id,
|
||||
w.worker_name,
|
||||
w.job_type,
|
||||
w.employment_status,
|
||||
COALESCE(dar.is_present, TRUE) as is_present,
|
||||
dar.id as record_id,
|
||||
vr.request_id as vacation_request_id,
|
||||
vr.status as vacation_status,
|
||||
vt.type_name as vacation_type_name,
|
||||
vt.type_code as vacation_type_code,
|
||||
vr.days_used as vacation_days
|
||||
FROM workers w
|
||||
LEFT JOIN daily_attendance_records dar
|
||||
ON w.worker_id = dar.worker_id AND dar.record_date = ?
|
||||
LEFT JOIN vacation_requests vr
|
||||
ON w.worker_id = vr.worker_id
|
||||
AND ? BETWEEN vr.start_date AND vr.end_date
|
||||
AND vr.status = 'approved'
|
||||
LEFT JOIN vacation_types vt ON vr.vacation_type_id = vt.id
|
||||
WHERE w.employment_status = 'employed'
|
||||
ORDER BY w.worker_name
|
||||
`;
|
||||
|
||||
const [rows] = await db.execute(query, [date, date]);
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = AttendanceModel;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
288
api.hyungi.net/models/vacationBalanceModel.js
Normal file
288
api.hyungi.net/models/vacationBalanceModel.js
Normal file
@@ -0,0 +1,288 @@
|
||||
/**
|
||||
* vacationBalanceModel.js
|
||||
* 휴가 잔액 관련 데이터베이스 쿼리 모델
|
||||
*/
|
||||
|
||||
const { getDb } = require('../dbPool');
|
||||
|
||||
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 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 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 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 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 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 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 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 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 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);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 근속년수 기반 연차 일수 계산 (한국 근로기준법)
|
||||
* @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);
|
||||
|
||||
return Math.min(15 + additionalDays, 25);
|
||||
},
|
||||
|
||||
/**
|
||||
* 특정 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = vacationBalanceModel;
|
||||
271
api.hyungi.net/models/vacationRequestModel.js
Normal file
271
api.hyungi.net/models/vacationRequestModel.js
Normal file
@@ -0,0 +1,271 @@
|
||||
/**
|
||||
* vacationRequestModel.js
|
||||
* 휴가 신청 관련 데이터베이스 쿼리 모델
|
||||
*/
|
||||
|
||||
const { getDb } = require('../dbPool');
|
||||
|
||||
const vacationRequestModel = {
|
||||
/**
|
||||
* 휴가 신청 생성
|
||||
*/
|
||||
async create(requestData, callback) {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const query = `INSERT INTO vacation_requests SET ?`;
|
||||
const [result] = await db.query(query, requestData);
|
||||
callback(null, result);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 휴가 신청 목록 조회 (필터링 지원)
|
||||
*/
|
||||
async getAll(filters = {}, callback) {
|
||||
try {
|
||||
const db = await getDb();
|
||||
|
||||
let query = `
|
||||
SELECT
|
||||
vr.*,
|
||||
w.worker_name,
|
||||
vt.type_name as vacation_type_name,
|
||||
vt.deduct_days as vacation_deduct_days,
|
||||
requester.name as requester_name,
|
||||
reviewer.name as reviewer_name
|
||||
FROM vacation_requests vr
|
||||
INNER JOIN workers w ON vr.worker_id = w.worker_id
|
||||
INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id
|
||||
LEFT JOIN users requester ON vr.requested_by = requester.user_id
|
||||
LEFT JOIN users reviewer ON vr.reviewed_by = reviewer.user_id
|
||||
WHERE 1=1
|
||||
`;
|
||||
|
||||
const params = [];
|
||||
|
||||
// 작업자 필터
|
||||
if (filters.worker_id) {
|
||||
query += ` AND vr.worker_id = ?`;
|
||||
params.push(filters.worker_id);
|
||||
}
|
||||
|
||||
// 상태 필터
|
||||
if (filters.status) {
|
||||
query += ` AND vr.status = ?`;
|
||||
params.push(filters.status);
|
||||
}
|
||||
|
||||
// 기간 필터
|
||||
if (filters.start_date) {
|
||||
query += ` AND vr.start_date >= ?`;
|
||||
params.push(filters.start_date);
|
||||
}
|
||||
|
||||
if (filters.end_date) {
|
||||
query += ` AND vr.end_date <= ?`;
|
||||
params.push(filters.end_date);
|
||||
}
|
||||
|
||||
// 휴가 유형 필터
|
||||
if (filters.vacation_type_id) {
|
||||
query += ` AND vr.vacation_type_id = ?`;
|
||||
params.push(filters.vacation_type_id);
|
||||
}
|
||||
|
||||
query += ` ORDER BY vr.created_at DESC`;
|
||||
|
||||
const [rows] = await db.query(query, params);
|
||||
callback(null, rows);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 특정 휴가 신청 조회
|
||||
*/
|
||||
async getById(requestId, callback) {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const query = `
|
||||
SELECT
|
||||
vr.*,
|
||||
w.worker_name,
|
||||
w.phone_number as worker_phone,
|
||||
w.email as worker_email,
|
||||
vt.type_name as vacation_type_name,
|
||||
vt.type_code as vacation_type_code,
|
||||
vt.deduct_days as vacation_deduct_days,
|
||||
requester.name as requester_name,
|
||||
requester.username as requester_username,
|
||||
reviewer.name as reviewer_name,
|
||||
reviewer.username as reviewer_username
|
||||
FROM vacation_requests vr
|
||||
INNER JOIN workers w ON vr.worker_id = w.worker_id
|
||||
INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id
|
||||
LEFT JOIN users requester ON vr.requested_by = requester.user_id
|
||||
LEFT JOIN users reviewer ON vr.reviewed_by = reviewer.user_id
|
||||
WHERE vr.request_id = ?
|
||||
`;
|
||||
const [rows] = await db.query(query, [requestId]);
|
||||
callback(null, rows);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 휴가 신청 수정
|
||||
*/
|
||||
async update(requestId, updateData, callback) {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const query = `UPDATE vacation_requests SET ? WHERE request_id = ?`;
|
||||
const [result] = await db.query(query, [updateData, requestId]);
|
||||
callback(null, result);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 휴가 신청 삭제
|
||||
*/
|
||||
async delete(requestId, callback) {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const query = `DELETE FROM vacation_requests WHERE request_id = ?`;
|
||||
const [result] = await db.query(query, [requestId]);
|
||||
callback(null, result);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 휴가 신청 승인/거부
|
||||
*/
|
||||
async updateStatus(requestId, statusData, callback) {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const query = `
|
||||
UPDATE vacation_requests
|
||||
SET
|
||||
status = ?,
|
||||
reviewed_by = ?,
|
||||
reviewed_at = NOW(),
|
||||
review_note = ?
|
||||
WHERE request_id = ?
|
||||
`;
|
||||
const [result] = await db.query(query, [
|
||||
statusData.status,
|
||||
statusData.reviewed_by,
|
||||
statusData.review_note || null,
|
||||
requestId
|
||||
]);
|
||||
callback(null, result);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 특정 작업자의 대기 중인 휴가 신청 수
|
||||
*/
|
||||
async getPendingCount(workerId, callback) {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const query = `
|
||||
SELECT COUNT(*) as count
|
||||
FROM vacation_requests
|
||||
WHERE worker_id = ? AND status = 'pending'
|
||||
`;
|
||||
const [rows] = await db.query(query, [workerId]);
|
||||
callback(null, rows);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 특정 작업자의 승인된 휴가 일수 합계 (특정 기간)
|
||||
*/
|
||||
async getApprovedDaysInPeriod(workerId, startDate, endDate, callback) {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const query = `
|
||||
SELECT COALESCE(SUM(days_used), 0) as total_days
|
||||
FROM vacation_requests
|
||||
WHERE worker_id = ?
|
||||
AND status = 'approved'
|
||||
AND start_date >= ?
|
||||
AND end_date <= ?
|
||||
`;
|
||||
const [rows] = await db.query(query, [workerId, startDate, endDate]);
|
||||
callback(null, rows);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 휴가 기간 중복 체크
|
||||
*/
|
||||
async checkOverlap(workerId, startDate, endDate, excludeRequestId = null, callback) {
|
||||
try {
|
||||
const db = await getDb();
|
||||
let query = `
|
||||
SELECT COUNT(*) as count
|
||||
FROM vacation_requests
|
||||
WHERE worker_id = ?
|
||||
AND status IN ('pending', 'approved')
|
||||
AND (
|
||||
(start_date <= ? AND end_date >= ?) OR
|
||||
(start_date <= ? AND end_date >= ?) OR
|
||||
(start_date >= ? AND end_date <= ?)
|
||||
)
|
||||
`;
|
||||
const params = [workerId, startDate, startDate, endDate, endDate, startDate, endDate];
|
||||
|
||||
if (excludeRequestId) {
|
||||
query += ` AND request_id != ?`;
|
||||
params.push(excludeRequestId);
|
||||
}
|
||||
|
||||
const [rows] = await db.query(query, params);
|
||||
callback(null, rows);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 모든 대기 중인 휴가 신청 (관리자용)
|
||||
*/
|
||||
async getAllPending(callback) {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const query = `
|
||||
SELECT
|
||||
vr.*,
|
||||
w.worker_name,
|
||||
vt.type_name as vacation_type_name,
|
||||
requester.name as requester_name
|
||||
FROM vacation_requests vr
|
||||
INNER JOIN workers w ON vr.worker_id = w.worker_id
|
||||
INNER JOIN vacation_types vt ON vr.vacation_type_id = vt.id
|
||||
LEFT JOIN users requester ON vr.requested_by = requester.user_id
|
||||
WHERE vr.status = 'pending'
|
||||
ORDER BY vr.created_at ASC
|
||||
`;
|
||||
const [rows] = await db.query(query);
|
||||
callback(null, rows);
|
||||
} catch (error) {
|
||||
callback(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = vacationRequestModel;
|
||||
132
api.hyungi.net/models/vacationTypeModel.js
Normal file
132
api.hyungi.net/models/vacationTypeModel.js
Normal file
@@ -0,0 +1,132 @@
|
||||
/**
|
||||
* vacationTypeModel.js
|
||||
* 휴가 유형 관련 데이터베이스 쿼리 모델
|
||||
*/
|
||||
|
||||
const { getDb } = require('../dbPool');
|
||||
|
||||
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 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);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 특정 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 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 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 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);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 휴가 유형 삭제 (논리적 삭제 - 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 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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = vacationTypeModel;
|
||||
505
api.hyungi.net/models/visitRequestModel.js
Normal file
505
api.hyungi.net/models/visitRequestModel.js
Normal file
@@ -0,0 +1,505 @@
|
||||
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 [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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 출입 신청 목록 조회 (필터 옵션 포함)
|
||||
*/
|
||||
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 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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 출입 신청 상세 조회
|
||||
*/
|
||||
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 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 [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 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 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 rejectVisitRequest = async (requestId, rejectionData, callback) => {
|
||||
try {
|
||||
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 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 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 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 createVisitPurpose = async (purposeData, callback) => {
|
||||
try {
|
||||
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 updateVisitPurpose = async (purposeId, purposeData, callback) => {
|
||||
try {
|
||||
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 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 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 [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 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 updateTrainingRecord = async (trainingId, trainingData, callback) => {
|
||||
try {
|
||||
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 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 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 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);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
// 출입 신청
|
||||
createVisitRequest,
|
||||
getAllVisitRequests,
|
||||
getVisitRequestById,
|
||||
updateVisitRequest,
|
||||
deleteVisitRequest,
|
||||
approveVisitRequest,
|
||||
rejectVisitRequest,
|
||||
updateVisitRequestStatus,
|
||||
|
||||
// 방문 목적
|
||||
getAllVisitPurposes,
|
||||
getActiveVisitPurposes,
|
||||
createVisitPurpose,
|
||||
updateVisitPurpose,
|
||||
deleteVisitPurpose,
|
||||
|
||||
// 안전교육
|
||||
createTrainingRecord,
|
||||
getTrainingRecordByRequestId,
|
||||
updateTrainingRecord,
|
||||
completeTraining,
|
||||
getTrainingRecords
|
||||
};
|
||||
@@ -35,7 +35,7 @@ const getAllCategories = async (callback) => {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const [rows] = await db.query(
|
||||
`SELECT category_id, category_name, description, display_order, is_active, created_at, updated_at
|
||||
`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`
|
||||
);
|
||||
@@ -52,7 +52,7 @@ const getActiveCategories = async (callback) => {
|
||||
try {
|
||||
const db = await getDb();
|
||||
const [rows] = await db.query(
|
||||
`SELECT category_id, category_name, description, display_order, is_active, created_at, updated_at
|
||||
`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`
|
||||
@@ -70,7 +70,7 @@ 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, created_at, updated_at
|
||||
`SELECT category_id, category_name, description, display_order, is_active, layout_image, created_at, updated_at
|
||||
FROM workplace_categories
|
||||
WHERE category_id = ?`,
|
||||
[categoryId]
|
||||
@@ -91,14 +91,15 @@ const updateCategory = async (categoryId, category, callback) => {
|
||||
category_name,
|
||||
description,
|
||||
display_order,
|
||||
is_active
|
||||
is_active,
|
||||
layout_image
|
||||
} = category;
|
||||
|
||||
const [result] = await db.query(
|
||||
`UPDATE workplace_categories
|
||||
SET category_name = ?, description = ?, display_order = ?, is_active = ?, updated_at = NOW()
|
||||
SET category_name = ?, description = ?, display_order = ?, is_active = ?, layout_image = ?, updated_at = NOW()
|
||||
WHERE category_id = ?`,
|
||||
[category_name, description, display_order, is_active, categoryId]
|
||||
[category_name, description, display_order, is_active, layout_image, categoryId]
|
||||
);
|
||||
|
||||
callback(null, result);
|
||||
@@ -135,14 +136,16 @@ const createWorkplace = async (workplace, callback) => {
|
||||
category_id = null,
|
||||
workplace_name,
|
||||
description = null,
|
||||
is_active = true
|
||||
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)
|
||||
VALUES (?, ?, ?, ?)`,
|
||||
[category_id, workplace_name, description, is_active]
|
||||
(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);
|
||||
@@ -158,12 +161,12 @@ 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.created_at, w.updated_at,
|
||||
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
|
||||
w.workplace_purpose, w.display_priority, 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.workplace_id DESC`
|
||||
ORDER BY wc.display_order ASC, w.display_priority ASC, w.workplace_id DESC`
|
||||
);
|
||||
callback(null, rows);
|
||||
} catch (err) {
|
||||
@@ -178,7 +181,7 @@ 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,
|
||||
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
|
||||
w.created_at, w.updated_at,
|
||||
wc.category_name
|
||||
FROM workplaces w
|
||||
@@ -199,7 +202,7 @@ 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,
|
||||
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
|
||||
w.created_at, w.updated_at,
|
||||
wc.category_name
|
||||
FROM workplaces w
|
||||
@@ -221,7 +224,7 @@ 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,
|
||||
`SELECT w.workplace_id, w.category_id, w.workplace_name, w.description, w.is_active, w.workplace_purpose, w.display_priority,
|
||||
w.created_at, w.updated_at,
|
||||
wc.category_name
|
||||
FROM workplaces w
|
||||
@@ -245,14 +248,17 @@ const updateWorkplace = async (workplaceId, workplace, callback) => {
|
||||
category_id,
|
||||
workplace_name,
|
||||
description,
|
||||
is_active
|
||||
is_active,
|
||||
workplace_purpose,
|
||||
display_priority
|
||||
} = workplace;
|
||||
|
||||
const [result] = await db.query(
|
||||
`UPDATE workplaces
|
||||
SET category_id = ?, workplace_name = ?, description = ?, is_active = ?, updated_at = NOW()
|
||||
SET category_id = ?, workplace_name = ?, description = ?, is_active = ?,
|
||||
workplace_purpose = ?, display_priority = ?, updated_at = NOW()
|
||||
WHERE workplace_id = ?`,
|
||||
[category_id, workplace_name, description, is_active, workplaceId]
|
||||
[category_id, workplace_name, description, is_active, workplace_purpose, display_priority, workplaceId]
|
||||
);
|
||||
|
||||
callback(null, result);
|
||||
@@ -277,6 +283,134 @@ const deleteWorkplace = async (workplaceId, callback) => {
|
||||
}
|
||||
};
|
||||
|
||||
// ==================== 작업장 지도 영역 관련 ====================
|
||||
|
||||
/**
|
||||
* 작업장 지도 영역 생성
|
||||
*/
|
||||
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 [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);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 카테고리(공장)별 지도 영역 조회
|
||||
*/
|
||||
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 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 updateMapRegion = async (regionId, region, callback) => {
|
||||
try {
|
||||
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]
|
||||
);
|
||||
|
||||
callback(null, result);
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 지도 영역 삭제
|
||||
*/
|
||||
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 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);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
// 카테고리
|
||||
createCategory,
|
||||
@@ -293,5 +427,13 @@ module.exports = {
|
||||
getWorkplacesByCategory,
|
||||
getWorkplaceById,
|
||||
updateWorkplace,
|
||||
deleteWorkplace
|
||||
deleteWorkplace,
|
||||
|
||||
// 지도 영역
|
||||
createMapRegion,
|
||||
getMapRegionsByCategory,
|
||||
getMapRegionByWorkplace,
|
||||
updateMapRegion,
|
||||
deleteMapRegion,
|
||||
deleteMapRegionsByCategory
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user