Files
weeklyreport/backend/api/business-report/generate.post.ts

149 lines
4.4 KiB
TypeScript

import { query, queryOne, insertReturning, execute } from '../../utils/db'
import { callOpenAI } from '../../utils/openai'
import { getCurrentUserId } from '../../utils/user'
interface GenerateBody {
businessId: number
reportYear: number
reportWeek: number
weekStartDate: string
weekEndDate: string
}
/**
* 사업 주간보고 취합 생성
* POST /api/business-report/generate
*/
export default defineEventHandler(async (event) => {
const body = await readBody<GenerateBody>(event)
const userId = await getCurrentUserId(event)
if (!body.businessId || !body.reportYear || !body.reportWeek) {
throw createError({ statusCode: 400, message: '필수 파라미터가 누락되었습니다.' })
}
// 기존 보고서 확인
const existing = await queryOne(`
SELECT * FROM wr_business_weekly_report
WHERE business_id = $1 AND report_year = $2 AND report_week = $3
`, [body.businessId, body.reportYear, body.reportWeek])
// 사업에 속한 프로젝트 목록
const projects = await query(`
SELECT project_id, project_name FROM wr_project_info WHERE business_id = $1
`, [body.businessId])
if (projects.length === 0) {
throw createError({ statusCode: 400, message: '해당 사업에 속한 프로젝트가 없습니다.' })
}
const projectIds = projects.map((p: any) => p.project_id)
// 해당 주차 주간보고 실적 조회
const tasks = await query(`
SELECT
t.task_description,
t.task_type,
t.task_hours,
p.project_name,
e.employee_name
FROM wr_weekly_report_task t
JOIN wr_weekly_report r ON t.report_id = r.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 t.project_id = ANY($1)
AND r.report_year = $2
AND r.report_week = $3
ORDER BY p.project_name, e.employee_name
`, [projectIds, body.reportYear, body.reportWeek])
if (tasks.length === 0) {
throw createError({ statusCode: 400, message: '해당 주차에 등록된 실적이 없습니다.' })
}
// 프로젝트별로 그룹화
const groupedTasks: Record<string, any[]> = {}
for (const task of tasks) {
const key = task.project_name
if (!groupedTasks[key]) groupedTasks[key] = []
groupedTasks[key].push(task)
}
// OpenAI 프롬프트 생성
let taskText = ''
for (const [projectName, projectTasks] of Object.entries(groupedTasks)) {
taskText += `\n[${projectName}]\n`
for (const t of projectTasks) {
taskText += `- ${t.employee_name}: ${t.task_description}\n`
}
}
const prompt = `다음은 사업의 주간 실적입니다. 이를 경영진에게 보고하기 위한 간결한 요약문을 작성해주세요.
${taskText}
요약 작성 가이드:
1. 프로젝트별로 구분하여 작성
2. 핵심 성과와 진행 상황 중심
3. 한국어로 작성
4. 불릿 포인트 형식
5. 200자 이내로 간결하게
JSON 형식으로 응답해주세요:
{
"summary": "요약 내용"
}`
let aiSummary = ''
try {
const response = await callOpenAI([
{ role: 'system', content: '당신은 프로젝트 관리 전문가입니다. 주간 실적을 간결하게 요약합니다.' },
{ role: 'user', content: prompt }
], true)
const parsed = JSON.parse(response)
aiSummary = parsed.summary || response
} catch (e) {
console.error('OpenAI error:', e)
aiSummary = '(AI 요약 생성 실패)'
}
let result
if (existing) {
// 업데이트
await execute(`
UPDATE wr_business_weekly_report SET
ai_summary = $1,
updated_at = NOW()
WHERE business_report_id = $2
`, [aiSummary, existing.business_report_id])
result = { ...existing, ai_summary: aiSummary }
} else {
// 신규 생성
result = await insertReturning(`
INSERT INTO wr_business_weekly_report (
business_id, report_year, report_week, week_start_date, week_end_date,
ai_summary, status, created_by
) VALUES ($1, $2, $3, $4, $5, $6, 'draft', $7)
RETURNING *
`, [
body.businessId,
body.reportYear,
body.reportWeek,
body.weekStartDate,
body.weekEndDate,
aiSummary,
userId
])
}
return {
success: true,
report: {
businessReportId: result.business_report_id,
aiSummary: result.ai_summary || aiSummary,
status: result.status || 'draft'
}
}
})