- dashboardModel: department_page_permissions의 s1. 접두사 제거하여 pages.page_key와 매칭 (두 테이블 명명규칙 차이 처리) - permissionModel: 신규 페이지 13개 추가 (공정표, 생산회의록, 대리입력, 입력현황, 근태관리 7종, 부서/알림 관리) - pages 테이블: 누락 16개 페이지 INSERT (DB 직접 실행) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
145 lines
4.7 KiB
JavaScript
145 lines
4.7 KiB
JavaScript
/**
|
|
* 대시보드 개인 요약 모델
|
|
* Sprint 003 — 연차/연장근로/접근 페이지 통합 조회
|
|
*/
|
|
const { getDb } = require('../config/database');
|
|
|
|
const OVERTIME_THRESHOLD = 8; // 연장근로 기준 시간
|
|
|
|
const DashboardModel = {
|
|
/**
|
|
* 사용자 정보 조회 (쿼리 1 — 먼저 실행)
|
|
*/
|
|
getUserInfo: async (userId) => {
|
|
const db = await getDb();
|
|
const [rows] = await db.execute(`
|
|
SELECT u.user_id, u.name, u.role,
|
|
w.worker_id, w.worker_name, w.job_type, w.department_id,
|
|
COALESCE(d.department_name, '미배정') AS department_name
|
|
FROM sso_users u
|
|
LEFT JOIN workers w ON u.user_id = w.user_id
|
|
LEFT JOIN departments d ON w.department_id = d.department_id
|
|
WHERE u.user_id = ?
|
|
`, [userId]);
|
|
return rows[0] || null;
|
|
},
|
|
|
|
/**
|
|
* 연차 현황 조회 (쿼리 2)
|
|
*/
|
|
getVacationBalance: async (workerId, year) => {
|
|
if (!workerId) return [];
|
|
const db = await getDb();
|
|
const [rows] = await db.execute(`
|
|
SELECT vbd.vacation_type_id, vbd.total_days, vbd.used_days, vbd.remaining_days,
|
|
vt.type_name, vt.type_code
|
|
FROM vacation_balance_details vbd
|
|
JOIN vacation_types vt ON vbd.vacation_type_id = vt.id
|
|
WHERE vbd.worker_id = ? AND vbd.year = ?
|
|
ORDER BY vt.priority
|
|
`, [workerId, year]);
|
|
return rows;
|
|
},
|
|
|
|
/**
|
|
* 월간 연장근로 조회 (쿼리 3)
|
|
*/
|
|
getMonthlyOvertime: async (userId, year, month) => {
|
|
const db = await getDb();
|
|
const [rows] = await db.execute(`
|
|
SELECT
|
|
COUNT(CASE WHEN dar.total_work_hours > ${OVERTIME_THRESHOLD} THEN 1 END) AS overtime_days,
|
|
COALESCE(SUM(CASE WHEN dar.total_work_hours > ${OVERTIME_THRESHOLD} THEN dar.total_work_hours - ${OVERTIME_THRESHOLD} ELSE 0 END), 0) AS total_overtime_hours,
|
|
COUNT(*) AS total_work_days,
|
|
COALESCE(SUM(dar.total_work_hours), 0) AS total_work_hours,
|
|
COALESCE(AVG(dar.total_work_hours), 0) AS avg_daily_hours
|
|
FROM daily_attendance_records dar
|
|
WHERE dar.user_id = ? AND YEAR(dar.record_date) = ? AND MONTH(dar.record_date) = ?
|
|
AND dar.total_work_hours > 0
|
|
`, [userId, year, month]);
|
|
return rows[0] || { overtime_days: 0, total_overtime_hours: 0, total_work_days: 0, total_work_hours: 0, avg_daily_hours: 0 };
|
|
},
|
|
|
|
/**
|
|
* 접근 가능 페이지 조회 (쿼리 4)
|
|
*/
|
|
getQuickAccess: async (userId, departmentId, role) => {
|
|
const db = await getDb();
|
|
const isAdmin = ['admin', 'system'].includes((role || '').toLowerCase());
|
|
|
|
// 모든 페이지 조회
|
|
const [allPages] = await db.execute(`
|
|
SELECT id, page_key, page_name, page_path, category, is_admin_only
|
|
FROM pages
|
|
ORDER BY display_order, page_name
|
|
`);
|
|
|
|
if (isAdmin) {
|
|
const adminPages = allPages.filter(p => p.is_admin_only);
|
|
const normalPages = allPages.filter(p => !p.is_admin_only);
|
|
return {
|
|
department_pages: normalPages.map(formatPage),
|
|
personal_pages: [],
|
|
admin_pages: adminPages.map(formatPage)
|
|
};
|
|
}
|
|
|
|
// 부서 권한 페이지
|
|
// department_page_permissions.page_name은 's1.work.tbm' 형식 (시스템 접두사 포함)
|
|
// pages.page_key는 'work.tbm' 형식 (접두사 없음)
|
|
// → 's1.' 접두사를 제거하여 매칭
|
|
let deptPageKeys = new Set();
|
|
if (departmentId) {
|
|
const [deptRows] = await db.execute(`
|
|
SELECT dpp.page_name
|
|
FROM department_page_permissions dpp
|
|
WHERE dpp.department_id = ? AND dpp.can_access = 1
|
|
`, [departmentId]);
|
|
deptRows.forEach(r => {
|
|
const key = r.page_name.startsWith('s1.') ? r.page_name.slice(3) : r.page_name;
|
|
deptPageKeys.add(key);
|
|
});
|
|
}
|
|
|
|
// 개인 권한 페이지 (page_id = pages.id)
|
|
const [personalRows] = await db.execute(`
|
|
SELECT upa.page_id
|
|
FROM user_page_access upa
|
|
WHERE upa.user_id = ? AND upa.can_access = 1
|
|
`, [userId]);
|
|
const personalPageIds = new Set(personalRows.map(r => r.page_id));
|
|
|
|
// 분류 (부서 우선, 중복 없음 — 권한 있는 페이지만)
|
|
const departmentPages = [];
|
|
const personalPages = [];
|
|
|
|
for (const page of allPages) {
|
|
if (page.is_admin_only) continue;
|
|
|
|
if (deptPageKeys.has(page.page_key)) {
|
|
departmentPages.push(formatPage(page));
|
|
} else if (personalPageIds.has(page.id)) {
|
|
personalPages.push(formatPage(page));
|
|
}
|
|
}
|
|
|
|
return {
|
|
department_pages: departmentPages,
|
|
personal_pages: personalPages,
|
|
admin_pages: []
|
|
};
|
|
}
|
|
};
|
|
|
|
function formatPage(page) {
|
|
return {
|
|
page_key: page.page_key,
|
|
page_name: page.page_name,
|
|
page_path: page.page_path,
|
|
icon: '',
|
|
category: page.category || ''
|
|
};
|
|
}
|
|
|
|
module.exports = DashboardModel;
|