import { query } from '../../../utils/db' import { callOpenAI } from '../../../utils/openai' interface SearchBody { taskDescription: string projectId?: number } /** * 주간보고 실적과 유사한 TODO 검색 * POST /api/todo/report/similar */ export default defineEventHandler(async (event) => { const body = await readBody(event) if (!body.taskDescription?.trim()) { return { todos: [] } } // 해당 프로젝트의 미완료 TODO 조회 const conditions = ["t.status IN ('PENDING', 'IN_PROGRESS')"] const values: any[] = [] let paramIndex = 1 if (body.projectId) { conditions.push(`t.project_id = $${paramIndex++}`) values.push(body.projectId) } const todos = await query(` SELECT t.todo_id, t.todo_title, t.todo_content, t.project_id, p.project_name, t.status, t.due_date FROM wr_todo t LEFT JOIN wr_project_info p ON t.project_id = p.project_id WHERE ${conditions.join(' AND ')} ORDER BY t.created_at DESC LIMIT 20 `, values) if (todos.length === 0) { return { todos: [] } } // OpenAI로 유사도 분석 const todoList = todos.map((t: any, i: number) => `${i + 1}. [ID:${t.todo_id}] ${t.todo_title}${t.todo_content ? ' - ' + t.todo_content.substring(0, 50) : ''}` ).join('\n') const prompt = `다음 주간보고 실적과 가장 유사한 TODO를 찾아주세요. 실적 내용: "${body.taskDescription}" TODO 목록: ${todoList} 유사한 TODO의 ID를 배열로 반환해주세요 (유사도 70% 이상만, 최대 3개). JSON 형식: { "similarIds": [1, 2] }` try { const response = await callOpenAI([ { role: 'system', content: '주간보고 실적과 TODO의 유사도를 분석하는 전문가입니다.' }, { role: 'user', content: prompt } ], true) const parsed = JSON.parse(response) const similarIds = parsed.similarIds || [] const similarTodos = todos.filter((t: any) => similarIds.includes(t.todo_id)) return { todos: similarTodos.map((t: any) => ({ todoId: t.todo_id, todoTitle: t.todo_title, todoContent: t.todo_content, projectId: t.project_id, projectName: t.project_name, status: t.status, dueDate: t.due_date })) } } catch (e) { console.error('OpenAI error:', e) // 실패 시 키워드 기반 간단 매칭 const keywords = body.taskDescription.split(/\s+/).filter(k => k.length >= 2) const matched = todos.filter((t: any) => { const title = t.todo_title?.toLowerCase() || '' return keywords.some(k => title.includes(k.toLowerCase())) }).slice(0, 3) return { todos: matched.map((t: any) => ({ todoId: t.todo_id, todoTitle: t.todo_title, projectId: t.project_id, projectName: t.project_name, status: t.status, dueDate: t.due_date })), fallback: true } } })