import logging from datetime import datetime, timezone from fastapi import APIRouter, Depends, HTTPException from app.middleware.auth import get_current_user from app.models.analysis import ( Analysis, CategoryInterpretation, IndicatorInterpretation, ActionItem, ) from app.models.report import Report from app.models.user import User from app.services.ai_service import analyze_report from app.routers.calendar import link_report_to_checkup from app.utils.helpers import parse_object_id logger = logging.getLogger(__name__) router = APIRouter() @router.post("/{report_id}/generate") async def generate_analysis( report_id: str, user: User = Depends(get_current_user) ): """生成 AI 解读""" oid = parse_object_id(report_id) report = await Report.get(oid) if not report or report.user_id != user.id: raise HTTPException(status_code=404, detail="报告不存在") # Check if analysis already exists existing = await Analysis.find_one(Analysis.report_id == report.id) if existing: return _analysis_to_dict(existing) # Call Claude API try: result = await analyze_report(report, user) except Exception: logger.exception("AI 解读失败 report_id=%s", report_id) raise HTTPException(status_code=502, detail="AI 解读服务暂时不可用,请稍后重试") # Save analysis categories = [] for cat in result.get("categories", []): indicators = [ IndicatorInterpretation(**ind) for ind in cat.get("indicators", []) ] categories.append( CategoryInterpretation( category=cat.get("category", ""), status=cat.get("status", "normal"), indicators=indicators, ) ) action_items = [ActionItem(**a) for a in result.get("action_items", [])] analysis = Analysis( report_id=report.id, user_id=user.id, overall_status=result.get("overall_status", "normal"), risk_level=result.get("risk_level", "low"), overall_summary=result.get("overall_summary", ""), emotional_summary=result.get("emotional_summary", ""), categories=categories, action_items=action_items, tips=result.get("tips", []), ) await analysis.insert() # Update report status report.status = "analyzed" report.overall_status = analysis.overall_status report.updated_at = datetime.now(timezone.utc) await report.save() # Auto-link report to the nearest calendar checkup if report.pregnancy_week: try: await link_report_to_checkup(user.id, str(report.id), report.pregnancy_week) except Exception: logger.warning("自动关联日历失败 report_id=%s", report_id) return _analysis_to_dict(analysis) @router.get("/{report_id}") async def get_analysis(report_id: str, user: User = Depends(get_current_user)): """获取报告的 AI 解读结果""" oid = parse_object_id(report_id) report = await Report.get(oid) if not report or report.user_id != user.id: raise HTTPException(status_code=404, detail="报告不存在") analysis = await Analysis.find_one(Analysis.report_id == report.id) if not analysis: raise HTTPException(status_code=404, detail="暂无解读结果,请先生成") return _analysis_to_dict(analysis) def _analysis_to_dict(analysis: Analysis) -> dict: return { "id": str(analysis.id), "report_id": str(analysis.report_id), "overall_status": analysis.overall_status, "risk_level": analysis.risk_level, "overall_summary": analysis.overall_summary, "emotional_summary": analysis.emotional_summary, "categories": [ { "category": cat.category, "status": cat.status, "indicators": [ { "name": ind.name, "nickname": ind.nickname, "value": ind.value, "unit": "", "reference_range": "", "status": ind.status, "what_is_it": ind.what_is_it, "baby_impact": ind.impact, "suggestion": ind.suggestion, "follow_up": ind.recheck_reminder or "", } for ind in cat.indicators ], } for cat in analysis.categories ], "action_items": [ { "priority": a.priority, "title": a.content, "description": a.category, "timeframe": "", } for a in analysis.action_items ], "tips": analysis.tips, "created_at": analysis.created_at.isoformat(), }