Files
TK-FB-Project/api.hyungi.net/models/vacationRequestModel.js
Hyungi Ahn b6485e3140 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>
2026-01-29 15:46:47 +09:00

272 lines
7.1 KiB
JavaScript

/**
* 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;