작업계획서대로 진행
This commit is contained in:
108
backend/api/todo/report/similar.post.ts
Normal file
108
backend/api/todo/report/similar.post.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
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<SearchBody>(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
|
||||
}
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user