装闭 RenoPit 源码解析(12):从AI分析结果到React避坑报告

设计分析、合同审查、增项预测和跨文档核查分别产生不同结构的数据,报告页却需要一次性展示它们。装闭 RenoPit 在开源仓库 fthux/RenoPit 中通过 /result 接口先统一后端数据,再由 React 按结果内容组合页面。

一、报告页只请求一个聚合接口
AnalysisPage.tsx 从路由参数取得项目 ID,调用:
const res = await fetch(`${API}/projects/${projectId}/result`)
if (res.ok) setResult(await res.json())
页面不直接查询 Analysis、DocumentAnalysis 或交叉核查接口。数据拼装集中在后端,前端只消费 AnalysisResult。
get_analysis_result() 先确认项目存在,再按 Analysis.created_at 倒序获取最近一条分析。失败分析也会返回固定结构:评分和各级问题数量为零,summary_text 与 error_message 保存失败原因,使前端仍能进入统一的状态判断。
二、原始 AI JSON 如何变成 pitfalls
成功记录交给 _build_result_data()。它从 raw_result_json.problems 读取模型问题,再逐条转换成前端的 PitfallItem:
pitfalls.append({
"id": str(i + 1),
"category": _infer_category(problem),
"description": problem.get("title", ""),
"severity": _infer_severity(problem),
"location": problem.get("location"),
"suggestion": problem.get("alternative", ""),
"critique": problem.get("critique"),
"trap_explanation": problem.get("trap_explanation"),
"bbox": problem.get("bbox"),
})
严重程度通过标题和批判文本中的关键词推断。例如安全、坠落、火灾、漏电优先归为 critical;高额、陷阱、浪费归为 high;未命中时默认 medium。
类别同样采用关键词映射,依次识别卫生死角、空间压迫、安全健康、隐性成本和实用性伪需求,无法识别时返回“其他”。
三、评分和统计如何生成
如果模型返回的 summary 中带有 score,后端优先使用该值;否则根据问题数计算:
score = 95 if problem_count == 0 else max(
0, 100 - problem_count * 8
)
_count_severities() 再统计严重、高、中、低四类数量,组装 summary。因此前端无需遍历问题列表计算顶部统计卡。
页面将评分分为三个显示区间:80 分以上“优秀”,60~79 分“待改进”,低于 60 分“需重视”。这些标签只负责展示,原始分数仍来自后端结果。
四、文档结果如何合并
接口查询当前项目全部已完成的 DocumentAnalysis,按完成时间倒序交给 _serialize_document_analyses()。
序列化结果以 project_file_id 为键。同一文件如果被分析多次,只保留排序后的第一条,也就是最新结果。每条数据包含风险列表、分类置信度、总结、预估风险金额和可选的 extra_item_prediction。
最后,接口检查综合分析 JSON 中是否存在 cross_document_checks,存在就直接注入返回值。至此,一次响应已经包含报告页的全部章节。
五、React 如何决定显示哪些章节
AnalysisPage 根据数据动态创建导航项:
- 总体评价、陷阱数、综合评分始终显示;
pitfalls非空时显示问题详情;- 存在文档分析时显示合同与报价单;
- 任一文档带增项预测时显示增项章节;
- 存在差异项时显示跨文档核查。
页面使用 IntersectionObserver 观察这些章节,选择可见比例最高的部分更新 activeSection。桌面端显示可折叠的固定侧栏,移动端使用单独导航,点击后按照顶部偏移量平滑滚动。
六、问题详情如何渲染

每个 PitfallCard 根据 severityConfig 选择图标、颜色、背景和中文标签。卡片展示分类、位置、问题分析、套路说明与替代建议;后端没有提供的可选字段不会渲染空区域。
合同结果由文档风险区块展示,增项预测交给 ExtraPredictionPanel,跨文档结果交给 CrossCheckPanel。这些组件不再请求后端,只处理已经聚合到 result 中的数据。
加载、接口失败和分析失败也分别有独立页面状态。这样报告页不会在数据未准备好时尝试解构 summary 或 pitfalls。
七、报告数据链小结
RenoPit 的报告展示分成两层:后端把模型原始 JSON 转换为稳定的 AnalysisResult,React 再根据字段是否存在组合页面。评分、严重程度、最新文档结果和交叉核查都在 /result 这一条边界上汇合。
结果聚合函数、TypeScript 类型和报告组件可以在 fthux/RenoPit 中查看。下一篇将继续复用同一份结果数据,分析 RenoPit 如何使用 ReportLab 生成可下载的中文 PDF 报告。
多种 AI 分析结果怎样汇聚成一张可交互的装修避坑报告?从 `/result` 数据聚合、风险等级推断到 React 条件渲染和章节导航,打通 RenoPit 报告展示全链路。
浙公网安备 33010602011771号