1ㅊㅏ완료

This commit is contained in:
2026-01-05 02:00:13 +09:00
parent 1bbad6efa7
commit 185161db16
30 changed files with 4331 additions and 837 deletions

View File

@@ -0,0 +1,167 @@
import { defineEventHandler, readBody, createError } from 'h3'
import { query } from '../../utils/db'
import OpenAI from 'openai'
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
})
/**
* 주간보고 PMO AI 리뷰
* POST /api/report/review
*/
export default defineEventHandler(async (event) => {
const body = await readBody(event)
const { reportId } = body
if (!reportId) {
throw createError({ statusCode: 400, message: 'reportId가 필요합니다.' })
}
// 주간보고 조회
const reports = await query(`
SELECT
r.report_id,
r.report_year,
r.report_week,
e.employee_name as author_name
FROM wr_weekly_report r
JOIN wr_employee_info e ON r.author_id = e.employee_id
WHERE r.report_id = $1
`, [reportId])
if (reports.length === 0) {
throw createError({ statusCode: 404, message: '주간보고를 찾을 수 없습니다.' })
}
const report = reports[0]
// Task 조회
const tasks = await query(`
SELECT
t.task_type,
t.task_description,
t.task_hours,
t.is_completed,
p.project_name
FROM wr_weekly_report_task t
JOIN wr_project_info p ON t.project_id = p.project_id
WHERE t.report_id = $1
ORDER BY t.task_type, p.project_name
`, [reportId])
if (tasks.length === 0) {
throw createError({ statusCode: 400, message: '등록된 Task가 없습니다.' })
}
// Task를 실적/계획으로 분리
const workTasks = tasks.filter((t: any) => t.task_type === 'WORK')
const planTasks = tasks.filter((t: any) => t.task_type === 'PLAN')
// 프롬프트용 텍스트 생성
let taskText = `[작성자] ${report.author_name}\n[기간] ${report.report_year}${report.report_week}주차\n\n`
if (workTasks.length > 0) {
taskText += `[금주 실적]\n`
workTasks.forEach((t: any) => {
const status = t.is_completed ? '완료' : '진행중'
taskText += `- ${t.project_name} / ${t.task_description} / ${t.task_hours}h / ${status}\n`
})
taskText += '\n'
}
if (planTasks.length > 0) {
taskText += `[차주 계획]\n`
planTasks.forEach((t: any) => {
taskText += `- ${t.project_name} / ${t.task_description} / ${t.task_hours}h\n`
})
}
// OpenAI PMO 리뷰 요청
const systemPrompt = `당신은 SI 프로젝트의 PMO(Project Management Officer)이자 주간보고 작성 코치입니다.
개발자들이 더 나은 주간보고를 작성할 수 있도록 구체적인 피드백과 가이드를 제공해주세요.
[주간보고 작성의 목적]
- 프로젝트 진행 현황을 명확히 파악
- 일정 지연이나 리스크를 사전에 감지
- 팀원 간 업무 공유 및 협업 촉진
[검토 기준 - 엄격하게 적용]
1. **실적의 구체성** (가장 중요!)
- "DB 작업", "화면 개발", "API 개발" 같은 모호한 표현 지양
- 좋은 예시: "사용자 관리 테이블 3개(user, role, permission) 설계 및 생성"
- 좋은 예시: "로그인 API 개발 - JWT 토큰 발급, 리프레시 토큰 구현"
- 좋은 예시: "검색 화면 UI 구현 - 필터 조건 5개, 페이징, 엑셀 다운로드"
- 어떤 기능/모듈/화면인지, 무엇을 구체적으로 했는지 명시되어야 함
2. **일정의 명확성**
- "진행중"만 있고 완료 예정일이 없으면 부족
- 언제 완료될 예정인지, 진척률은 얼마인지 표기 권장
- 좋은 예시: "사용자 관리 화면 개발 (70% 완료, 1/10 완료 예정)"
3. **시간 산정의 적절성**
- 8시간(1일) 이상 작업은 세부 내역이 필요
- 16시간(2일) 이상인데 내용이 한 줄이면 분리 필요
- "회의", "검토" 등은 별도 기재 권장
4. **차주 계획의 실현 가능성**
- 계획이 너무 추상적이면 실행하기 어려움
- 구체적인 목표와 예상 산출물 명시 필요
- 좋은 예시: "결제 모듈 연동 - PG사 API 연동, 결제 테스트 완료 목표"
[피드백 작성 규칙]
- 각 Task별로 구체적인 개선 제안 제시
- 잘 작성된 부분은 "✅" 로 인정
- 보완이 필요한 부분은 "📝" 로 개선 방향 제시
- 일정 관련 질문은 "📅" 로 표시
- 리스크/우려사항은 "⚠️" 로 경고
- **반드시 어떻게 수정하면 좋을지 예시를 들어 설명**
- 친절하지만 명확하게, 구체적인 작성 예시를 포함
- 마지막에 전체적인 작성 팁 1-2개 추가
[피드백 톤]
- 비난하지 않고 코칭하는 느낌으로
- "~하면 더 좋겠습니다", "~로 수정해보시면 어떨까요?" 형태로
- 개선점뿐 아니라 잘한 점도 언급`
const userPrompt = `다음 주간보고를 PMO 관점에서 상세히 리뷰해주세요.
특히 실적과 계획이 구체적으로 작성되었는지, 일정이 명확한지 중점적으로 검토해주세요.
모호한 표현이 있다면 어떻게 수정하면 좋을지 예시와 함께 피드백해주세요.
${taskText}`
try {
const response = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userPrompt }
],
max_tokens: 1500,
temperature: 0.7
})
const review = response.choices[0]?.message?.content || '리뷰를 생성할 수 없습니다.'
const reviewedAt = new Date().toISOString()
// DB에 저장
await query(`
UPDATE wr_weekly_report
SET ai_review = $1, ai_review_at = $2
WHERE report_id = $3
`, [review, reviewedAt, reportId])
return {
success: true,
review,
reviewedAt
}
} catch (error: any) {
console.error('OpenAI API error:', error)
throw createError({
statusCode: 500,
message: 'AI 리뷰 생성 중 오류가 발생했습니다: ' + error.message
})
}
})