132 lines
3.8 KiB
TypeScript
132 lines
3.8 KiB
TypeScript
import { defineEventHandler, getQuery, createError } from 'h3'
|
|
import { query } from '../../../utils/db'
|
|
|
|
const ADMIN_EMAIL = 'admin@turbosoft.co.kr'
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const userEmail = event.node.req.headers['x-user-email'] as string
|
|
|
|
if (!userEmail) {
|
|
throw createError({ statusCode: 401, message: '로그인이 필요합니다.' })
|
|
}
|
|
|
|
// 관리자만 취합 가능
|
|
if (userEmail !== ADMIN_EMAIL) {
|
|
throw createError({ statusCode: 403, message: '관리자만 취합할 수 있습니다.' })
|
|
}
|
|
|
|
const { year, week, projectIds } = getQuery(event)
|
|
|
|
if (!year || !week) {
|
|
throw createError({ statusCode: 400, message: '연도와 주차를 지정해주세요.' })
|
|
}
|
|
|
|
// 프로젝트 ID 파싱
|
|
let projectIdList: number[] = []
|
|
if (projectIds) {
|
|
projectIdList = String(projectIds).split(',').map(Number).filter(n => !isNaN(n))
|
|
}
|
|
|
|
// 해당 주차의 모든 프로젝트별 Task 조회
|
|
let projectFilter = ''
|
|
const params: any[] = [Number(year), Number(week)]
|
|
|
|
if (projectIdList.length > 0) {
|
|
projectFilter = `AND t.project_id = ANY($3)`
|
|
params.push(projectIdList)
|
|
}
|
|
|
|
const tasks = await query(`
|
|
SELECT
|
|
t.task_id,
|
|
t.project_id,
|
|
p.project_code,
|
|
p.project_name,
|
|
t.task_type,
|
|
t.task_description,
|
|
t.task_hours,
|
|
t.is_completed,
|
|
r.report_id,
|
|
r.author_id,
|
|
e.employee_name as author_name
|
|
FROM wr_weekly_report r
|
|
JOIN wr_weekly_report_task t ON r.report_id = t.report_id
|
|
JOIN wr_project_info p ON t.project_id = p.project_id
|
|
JOIN wr_employee_info e ON r.author_id = e.employee_id
|
|
WHERE r.report_year = $1 AND r.report_week = $2
|
|
${projectFilter}
|
|
ORDER BY p.project_name, t.task_type, e.employee_name
|
|
`, params)
|
|
|
|
// 프로젝트별로 그룹핑
|
|
const projectMap = new Map<number, {
|
|
projectId: number
|
|
projectCode: string
|
|
projectName: string
|
|
workTasks: any[]
|
|
planTasks: any[]
|
|
totalWorkHours: number
|
|
totalPlanHours: number
|
|
}>()
|
|
|
|
for (const task of tasks) {
|
|
if (!projectMap.has(task.project_id)) {
|
|
projectMap.set(task.project_id, {
|
|
projectId: task.project_id,
|
|
projectCode: task.project_code,
|
|
projectName: task.project_name,
|
|
workTasks: [],
|
|
planTasks: [],
|
|
totalWorkHours: 0,
|
|
totalPlanHours: 0
|
|
})
|
|
}
|
|
|
|
const proj = projectMap.get(task.project_id)!
|
|
const taskItem = {
|
|
taskId: task.task_id,
|
|
description: task.task_description,
|
|
hours: parseFloat(task.task_hours) || 0,
|
|
isCompleted: task.is_completed,
|
|
authorName: task.author_name
|
|
}
|
|
|
|
if (task.task_type === 'WORK') {
|
|
proj.workTasks.push(taskItem)
|
|
proj.totalWorkHours += taskItem.hours
|
|
} else {
|
|
proj.planTasks.push(taskItem)
|
|
proj.totalPlanHours += taskItem.hours
|
|
}
|
|
}
|
|
|
|
// 해당 주차에 보고서가 있는 모든 프로젝트 목록
|
|
const allProjects = await query(`
|
|
SELECT DISTINCT p.project_id, p.project_code, p.project_name
|
|
FROM wr_weekly_report r
|
|
JOIN wr_weekly_report_task t ON r.report_id = t.report_id
|
|
JOIN wr_project_info p ON t.project_id = p.project_id
|
|
WHERE r.report_year = $1 AND r.report_week = $2
|
|
ORDER BY p.project_name
|
|
`, [Number(year), Number(week)])
|
|
|
|
// 해당 주차 보고서 수
|
|
const reportCount = await query(`
|
|
SELECT COUNT(DISTINCT report_id) as cnt
|
|
FROM wr_weekly_report
|
|
WHERE report_year = $1 AND report_week = $2
|
|
`, [Number(year), Number(week)])
|
|
|
|
return {
|
|
year: Number(year),
|
|
week: Number(week),
|
|
reportCount: parseInt(reportCount[0]?.cnt || '0'),
|
|
availableProjects: allProjects.map((p: any) => ({
|
|
projectId: p.project_id,
|
|
projectCode: p.project_code,
|
|
projectName: p.project_name
|
|
})),
|
|
projects: Array.from(projectMap.values())
|
|
}
|
|
})
|