106 lines
3.2 KiB
TypeScript
106 lines
3.2 KiB
TypeScript
import { queryOne, execute } from '../../../utils/db'
|
|
import { requireAuth } from '../../../utils/session'
|
|
import { callOpenAI } from '../../../utils/openai'
|
|
|
|
/**
|
|
* 회의록 AI 분석
|
|
* POST /api/meeting/[id]/analyze
|
|
*/
|
|
export default defineEventHandler(async (event) => {
|
|
await requireAuth(event)
|
|
const meetingId = parseInt(event.context.params?.id || '0')
|
|
|
|
if (!meetingId) {
|
|
throw createError({ statusCode: 400, message: '회의록 ID가 필요합니다.' })
|
|
}
|
|
|
|
// 회의록 조회
|
|
const meeting = await queryOne<any>(`
|
|
SELECT m.*, p.project_name
|
|
FROM wr_meeting m
|
|
LEFT JOIN wr_project_info p ON m.project_id = p.project_id
|
|
WHERE m.meeting_id = $1
|
|
`, [meetingId])
|
|
|
|
if (!meeting) {
|
|
throw createError({ statusCode: 404, message: '회의록을 찾을 수 없습니다.' })
|
|
}
|
|
|
|
if (!meeting.raw_content) {
|
|
throw createError({ statusCode: 400, message: '분석할 회의 내용이 없습니다.' })
|
|
}
|
|
|
|
// AI 프롬프트
|
|
const systemPrompt = `당신은 회의록 정리 전문가입니다.
|
|
아래 회의 내용을 분석하여 JSON 형식으로 정리해주세요.
|
|
|
|
## 출력 형식 (JSON만 출력, 다른 텍스트 없이)
|
|
{
|
|
"agendas": [
|
|
{
|
|
"no": 1,
|
|
"title": "안건 제목",
|
|
"content": "상세 내용 요약",
|
|
"status": "DECIDED | PENDING | IN_PROGRESS",
|
|
"decision": "결정 내용 (결정된 경우만)",
|
|
"todos": [
|
|
{
|
|
"title": "TODO 제목",
|
|
"assignee": "담당자명 또는 null",
|
|
"reason": "TODO로 추출한 이유"
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"summary": "전체 회의 요약 (2-3문장)"
|
|
}
|
|
|
|
## 규칙
|
|
1. 안건은 주제별로 분리하여 넘버링
|
|
2. 결정된 사항은 DECIDED, 추후 논의는 PENDING, 진행중은 IN_PROGRESS
|
|
3. 미결정/진행중 사항 중 액션이 필요한 것은 todos로 추출
|
|
4. 담당자가 언급되면 assignee에 기록 (없으면 null)
|
|
5. JSON 외 다른 텍스트 출력 금지`
|
|
|
|
const userPrompt = `## 회의 정보
|
|
- 제목: ${meeting.meeting_title}
|
|
- 프로젝트: ${meeting.project_name || '없음 (내부업무)'}
|
|
- 일자: ${meeting.meeting_date}
|
|
|
|
## 회의 내용
|
|
${meeting.raw_content}`
|
|
|
|
try {
|
|
const result = await callOpenAI([
|
|
{ role: 'system', content: systemPrompt },
|
|
{ role: 'user', content: userPrompt }
|
|
], true, 'gpt-4o-mini')
|
|
|
|
// JSON 파싱
|
|
let aiResult: any
|
|
try {
|
|
// JSON 블록 추출 (```json ... ``` 형태 처리)
|
|
let jsonStr = result.trim()
|
|
if (jsonStr.startsWith('```')) {
|
|
jsonStr = jsonStr.replace(/^```json?\n?/, '').replace(/\n?```$/, '')
|
|
}
|
|
aiResult = JSON.parse(jsonStr)
|
|
} catch (e) {
|
|
console.error('AI result parse error:', result)
|
|
throw createError({ statusCode: 500, message: 'AI 응답 파싱 실패' })
|
|
}
|
|
|
|
// DB 저장
|
|
await execute(`
|
|
UPDATE wr_meeting
|
|
SET ai_summary = $1, ai_status = 'PENDING', ai_processed_at = NOW()
|
|
WHERE meeting_id = $2
|
|
`, [JSON.stringify(aiResult), meetingId])
|
|
|
|
return { success: true, result: aiResult }
|
|
} catch (e: any) {
|
|
console.error('AI analyze error:', e)
|
|
throw createError({ statusCode: 500, message: e.message || 'AI 분석 실패' })
|
|
}
|
|
})
|