import { query, execute, queryOne } from '../../utils/db' const ADMIN_EMAIL = 'coziny@gmail.com' interface TaskInput { description: string hours: number isCompleted?: boolean } interface ProjectInput { projectId: number | null projectName: string workTasks: TaskInput[] planTasks: TaskInput[] } interface ReportInput { employeeId: number | null employeeName: string employeeEmail: string projects: ProjectInput[] issueDescription?: string vacationDescription?: string remarkDescription?: string } /** * 주간보고 일괄 등록 * POST /api/admin/bulk-register */ export default defineEventHandler(async (event) => { // 관리자 권한 체크 const userId = getCookie(event, 'user_id') if (!userId) { throw createError({ statusCode: 401, message: '로그인이 필요합니다.' }) } const clientIp = getHeader(event, 'x-forwarded-for') || 'unknown' const currentUser = await query(` SELECT employee_email FROM wr_employee_info WHERE employee_id = $1 `, [userId]) if (!currentUser[0] || currentUser[0].employee_email !== ADMIN_EMAIL) { throw createError({ statusCode: 403, message: '관리자만 사용할 수 있습니다.' }) } const adminEmail = currentUser[0].employee_email const body = await readBody<{ reportYear: number reportWeek: number weekStartDate: string weekEndDate: string reports: ReportInput[] }>(event) const results: any[] = [] for (const report of body.reports) { try { let employeeId = report.employeeId let isNewEmployee = false const newProjects: string[] = [] // 신규 직원 생성 if (!employeeId && report.employeeName && report.employeeEmail) { const newEmp = await queryOne(` INSERT INTO wr_employee_info (employee_name, employee_email, is_active, created_ip, created_email, updated_ip, updated_email) VALUES ($1, $2, true, $3, $4, $3, $4) RETURNING employee_id `, [report.employeeName, report.employeeEmail, clientIp, adminEmail]) employeeId = newEmp.employee_id isNewEmployee = true } if (!employeeId) { results.push({ success: false, employeeName: report.employeeName, employeeEmail: report.employeeEmail, error: '직원 정보가 없습니다.' }) continue } // 기존 보고서 확인 및 삭제 (덮어쓰기) const existing = await queryOne(` SELECT report_id FROM wr_weekly_report WHERE author_id = $1 AND report_year = $2 AND report_week = $3 `, [employeeId, body.reportYear, body.reportWeek]) let isUpdate = false if (existing) { await execute(`DELETE FROM wr_weekly_report_task WHERE report_id = $1`, [existing.report_id]) await execute(`DELETE FROM wr_weekly_report WHERE report_id = $1`, [existing.report_id]) isUpdate = true } // 주간보고 마스터 등록 const newReport = await queryOne(` INSERT INTO wr_weekly_report ( author_id, report_year, report_week, week_start_date, week_end_date, issue_description, vacation_description, remark_description, report_status, submitted_at, created_ip, created_email, updated_ip, updated_email ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'SUBMITTED', NOW(), $9, $10, $9, $10) RETURNING report_id `, [ employeeId, body.reportYear, body.reportWeek, body.weekStartDate, body.weekEndDate, report.issueDescription || null, report.vacationDescription || null, report.remarkDescription || null, clientIp, adminEmail ]) const reportId = newReport.report_id // 프로젝트별 Task 등록 for (const proj of report.projects) { let projectId = proj.projectId // 신규 프로젝트 생성 if (!projectId && proj.projectName) { const year = new Date().getFullYear() const codeResult = await queryOne(` SELECT COALESCE(MAX(CAST(SUBSTRING(project_code FROM 6) AS INTEGER)), 0) + 1 as next_num FROM wr_project_info WHERE project_code LIKE $1 `, [`${year}-%`]) const projectCode = `${year}-${String(codeResult.next_num).padStart(3, '0')}` const newProj = await queryOne(` INSERT INTO wr_project_info (project_code, project_name, project_status, created_ip, created_email, updated_ip, updated_email) VALUES ($1, $2, 'IN_PROGRESS', $3, $4, $3, $4) RETURNING project_id `, [projectCode, proj.projectName, clientIp, adminEmail]) projectId = newProj.project_id newProjects.push(proj.projectName) } if (!projectId) continue // 금주실적 Task 등록 for (const task of proj.workTasks || []) { await execute(` INSERT INTO wr_weekly_report_task ( report_id, project_id, task_type, task_description, task_hours, is_completed, created_ip, created_email, updated_ip, updated_email ) VALUES ($1, $2, 'WORK', $3, $4, $5, $6, $7, $6, $7) `, [reportId, projectId, task.description, task.hours || 0, task.isCompleted !== false, clientIp, adminEmail]) } // 차주계획 Task 등록 for (const task of proj.planTasks || []) { await execute(` INSERT INTO wr_weekly_report_task ( report_id, project_id, task_type, task_description, task_hours, created_ip, created_email, updated_ip, updated_email ) VALUES ($1, $2, 'PLAN', $3, $4, $5, $6, $5, $6) `, [reportId, projectId, task.description, task.hours || 0, clientIp, adminEmail]) } } results.push({ success: true, employeeId, employeeName: report.employeeName, employeeEmail: report.employeeEmail, reportId, isUpdate, isNewEmployee, newProjects }) } catch (e: any) { results.push({ success: false, employeeName: report.employeeName, employeeEmail: report.employeeEmail, error: e.message }) } } return { totalCount: results.length, successCount: results.filter(r => r.success).length, results } })