"""简历诊断接口""" import time from fastapi import APIRouter, Depends from pydantic import BaseModel, Field from app.ai.resume_diagnoser.diagnoser import diagnose_all, generate_summary, polish_content from app.core.auth import func_permission from app.core.context import RequestContext from app.core.database import get_db from app.core.logger import log from app.services.resume_diagnose_service import ResumeDiagnoseService, aggregate_results router = APIRouter(prefix="/resume/diagnose", tags=["简历诊断"]) class DiagnoseParam(BaseModel): resume_id: int = Field(..., alias="resumeId") @router.post("", summary="触发简历诊断") async def diagnose_resume(param: DiagnoseParam, _: None = Depends(func_permission("resume_diag"))): """触发简历AI诊断,返回报告ID""" user_id = RequestContext.user_id.get() log.info(f"简历诊断开始, resumeId={param.resume_id}, userId={user_id}") # 1. 短事务:加载简历数据 async for session in get_db(): service = ResumeDiagnoseService(session) resume, tasks = await service.load_resume_data(param.resume_id, user_id) if not tasks: log.warning(f"简历无可诊断内容, resumeId={param.resume_id}") raise ValueError("简历没有可诊断的描述内容") log.info(f"加载简历数据完成, 待诊断任务数={len(tasks)}") # 2. 并行 AI 诊断(不持有数据库连接) ai_tasks = [{k: v for k, v in t.items() if not k.startswith("_")} for t in tasks] t0 = time.time() ai_results = await diagnose_all(ai_tasks) log.info(f"AI诊断全部完成, 耗时{time.time() - t0:.2f}s") # 3. 统计 + 评级(纯计算) stats = aggregate_results(tasks, ai_results) log.info(f"统计完成, grade={stats['grade']}, urgent={stats['urgent_total']}, important={stats['important_total']}, expression={stats['expression_total']}") # 4. AI 生成整体评价(不持有数据库连接) t1 = time.time() summary = await generate_summary( grade=stats["grade"], urgent_total=stats["urgent_total"], important_total=stats["important_total"], expression_total=stats["expression_total"], target_position=resume.target_position or "", all_findings=stats["all_findings"], ) log.info(f"AI生成整体评价完成, 耗时{time.time() - t1:.2f}s") # 5. 短事务:纯写入 async for session in get_db(): service = ResumeDiagnoseService(session) report_id = await service.save_report( param.resume_id, user_id, stats["grade"], summary, stats["urgent_total"], stats["important_total"], stats["expression_total"], tasks, ai_results, ) log.info(f"简历诊断完成, reportId={report_id}") return {"reportId": report_id} @router.get("/{resume_id}", summary="查询最近一次诊断报告") async def get_diagnosis_report(resume_id: int): """查询指定简历的最近一次诊断报告 + 所有诊断问题""" user_id = RequestContext.user_id.get() async for session in get_db(): service = ResumeDiagnoseService(session) result = await service.get_latest_report(resume_id, user_id) return result @router.put("/issue/{issue_id}/resolve", summary="标记问题已处理") async def resolve_issue(issue_id: int): """标记诊断问题已处理""" user_id = RequestContext.user_id.get() async for session in get_db(): service = ResumeDiagnoseService(session) await service.resolve_issue(issue_id, user_id) class FeedbackParam(BaseModel): user_feedback: int = Field(..., alias="userFeedback") @router.put("/issue/{issue_id}/feedback", summary="用户评价诊断问题") async def feedback_issue(issue_id: int, param: FeedbackParam): """单独更新用户评价(1=符合 2=不符合)""" user_id = RequestContext.user_id.get() async for session in get_db(): service = ResumeDiagnoseService(session) await service.update_feedback(issue_id, user_id, param.user_feedback) class PolishParam(BaseModel): content: list[str] = Field(..., description="用户编辑后的文本段落数组") @router.post("/issue/{issue_id}/polish", summary="AI润色用户编辑的文本") async def polish_issue_content(issue_id: int, param: PolishParam): """基于诊断问题上下文,AI润色用户编辑后的文本""" user_id = RequestContext.user_id.get() async for session in get_db(): service = ResumeDiagnoseService(session) ctx = await service.get_issue_for_polish(issue_id, user_id) result = await polish_content( module_type=ctx["module_label"], reference_content=ctx["optimized_content"], user_content=param.content, is_summary=ctx["is_summary"], ) return {"content": result}