针对测试反馈的四个问题进行代码级分析,包含根因定位、涉及文件和修复建议。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 KiB
测试问题分析报告
日期: 2026-03-22 分析人: Claude AI 项目: 民生汇 - 慢性肾病营养管理小程序
一、食谱计算器 —— 用油量计算逻辑错误
问题描述
食谱计算器计算出的用油量偏多,不符合实际膳食指导标准。
问题定位
文件: msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCalculatorServiceImpl.java
位置: generateFoodPortions() 方法,第 516 行
// 当前代码(有误)
list.add(createFoodPortion(7, "油脂类10g", round(5.7 * energyRatio)));
根因分析
油脂类的份数计算使用了系数 5.7,与第 510 行谷薯类的系数完全相同:
list.add(createFoodPortion(1, "谷薯50g", round(5.7 * energyRatio))); // 谷薯类
list.add(createFoodPortion(7, "油脂类10g", round(5.7 * energyRatio))); // 油脂类(错误地使用了同一系数)
其中 energyRatio = energy / 2000.0。以标准体重 60kg 患者为例:
- 每日能量目标 = 60 × 35 = 2100 kcal
- energyRatio = 2100 / 2000 = 1.05
- 油脂份数 = 5.7 × 1.05 ≈ 6.0 份
- 每份 10g,即每天需要 60g 油
根据《中国居民膳食指南》和《慢性肾脏病患者膳食指导(2017)》,CKD 患者每日食用油推荐量为 25-30g,当前计算结果严重偏高。
修复建议
将油脂类的计算系数从 5.7 调整为 2.5:
// 修复后
list.add(createFoodPortion(7, "油脂类10g", round(2.5 * energyRatio)));
修复后以同一患者为例:2.5 × 1.05 ≈ 2.6 份,即每日约 26g 油,符合膳食指南要求。
优先级:高
二、AI 营养师 —— 反应慢、内容凌乱、体验差
问题描述
AI 营养师功能反应比较慢,提供的内容比较凌乱,逻辑性差,整体体验感不好。
问题定位
后端文件: msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolAiNutritionistServiceImpl.java
前端文件: msh_single_uniapp/pages/tool/ai-nutritionist.vue
根因分析
1. AI 服务未真正接入(核心原因)
后端 sendMessage() 方法(第 89-97 行)使用的是 Mock 模拟回复,并未真正接入 AI 服务:
// Mock AI response
// In real system, this would call AI service
// Here we just set a mock response for now
message.setAiResponse("这是一个模拟的AI回复。");
message.setAiResponseStatus("success");
虽然项目中存在 ToolCozeServiceImpl(Coze AI 服务)的实现,但 sendMessage() 方法中并未调用它。
2. 前端轮询机制导致延迟感
前端通过定时轮询 getResponse() 接口等待 AI 回复:
- 用户发送消息 → 后端同步写入 mock 数据 → 前端轮询获取结果
- 轮询间隔本身带来延迟,加上网络开销,用户体感"反应慢"
3. 缺乏流式响应
当前架构没有 SSE(Server-Sent Events)或 WebSocket 支持,无法实现"逐字输出"的流式效果,用户需等待完整回复后才能看到内容。
修复建议
- 正式接入 Coze AI 服务:在
sendMessage()中调用ToolCozeServiceImpl,真正发起 AI 对话请求 - 优化 Prompt 模板:设计结构化的系统提示词(System Prompt),要求 AI 回复遵循固定格式(如:摘要 → 营养分析 → 建议 → 注意事项),提升内容逻辑性
- 引入 SSE 流式返回:后端通过 SSE 推送逐步响应,前端实时渲染,提升交互体验
- 增加加载状态动效:在等待回复期间展示更好的"正在思考"动画
优先级:高
三、食物百科 —— 成分表不全、图片错误、缺少重量标注
问题描述
食物百科中食物成分表内容不全,图片显示有错误,而且没有标注具体是多少重量的食物所提供的营养成分。
问题定位
后端文件: msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolFoodServiceImpl.java
前端文件: msh_single_uniapp/pages/tool/food-detail.vue
数据模型: msh_crmeb_22/crmeb-common/src/main/java/com/zbkj/common/model/tool/V2Food.java
根因分析
3.1 食物成分表内容不全
后端 getDetail() 方法返回的营养字段不完整:
// 当前返回的字段
map.put("energy", food.getEnergy());
map.put("protein", food.getProtein());
map.put("fat", food.getFat());
map.put("carbohydrate", food.getCarbohydrate());
map.put("potassium", food.getPotassium());
map.put("phosphorus", food.getPhosphorus());
map.put("sodium", food.getSodium());
V2Food 模型中定义了但未返回的字段包括:
calcium(钙)iron(铁)vitaminC(维生素 C)nutrientsJson(扩展营养素 JSON)recommendedAmount(推荐摄入量)
前端 food-detail.vue 的 parseNutritionTable() 方法尝试解析 calcium、purine 等字段,但后端根本没有返回这些数据。
3.2 图片显示错误
两个层面的图片问题:
a) 前端硬编码了 Figma 临时 URL
food-detail.vue 和 nutrient-detail.vue 中的默认图片使用了 Figma API 的临时资源 URL:
iconWhyImportant: 'https://www.figma.com/api/mcp/asset/51e00c9b-5719-4391-9dff-68b70d24aece'
这些 URL 是开发阶段从 Figma 设计稿导出的临时链接,过期后图片无法加载。
b) 后端 AI 生图服务不稳定
DishImageService 通过 AI 生成食物图片并上传 OSS。当 AI API 调用失败时,部分食物的 image 字段为空或仍为旧的无效 URL,导致前端图片显示异常。
3.3 缺少重量标注
后端接口返回数据中 没有 servingSize(份量基准)字段。前端虽然显示了"每100g"标签,但这是硬编码的静态文本,并非根据实际数据动态展示。如果数据库中部分食物的营养数据不是基于 100g 标准录入的,就会产生误导。
修复建议
- 补充后端返回字段:在
getDetail()中增加calcium、iron、vitaminC、nutrientsJson、recommendedAmount等字段的返回 - 替换 Figma 临时 URL:将所有 Figma API 链接替换为上传至 OSS 的稳定图片资源
- 增加图片容错处理:前端为食物图片增加
@error事件处理,在图片加载失败时显示占位图 - 增加重量标注字段:数据库添加
serving_size字段(如"每100g"、"每份(50g)"),后端返回后前端动态显示
优先级:中高
四、健康知识营养素板块 —— 所有选项显示同一内容
问题描述
健康知识中营养素板块,所有的营养素选项(蛋白质、钾、磷、钠、钙、水分)点击后显示的都是同一个内容。
问题定位
列表页: msh_single_uniapp/pages/tool/nutrition-knowledge.vue 第 39 行
详情页: msh_single_uniapp/pages/tool/nutrient-detail.vue 第 114 行、第 137-277 行
根因分析
这是一个前端事件传参 + 数据默认值联合导致的 Bug。
Bug 链条
第一环:列表页事件传参问题
nutrition-knowledge.vue 中营养素卡片的点击事件:
@click="goToNutrientDetail" :data-nutrient-index="index"
goToNutrientDetail 方法通过 event.currentTarget.dataset.nutrientIndex 获取索引值。但在微信小程序环境中,dataset 的属性名会被自动转换为全小写(nutrientindex 而非 nutrientIndex),导致取值为 undefined。
当 index 为 undefined 时,this.nutrientList[undefined] 返回 undefined,方法执行 if (!item) return 直接退出,不发起页面跳转。
即使跳转成功,中文参数 name 经过 encodeURIComponent 编码后,在某些小程序版本中解码可能不正确,导致 nutrientMap[name] 匹配失败。
第二环:详情页默认数据兜底
nutrient-detail.vue 的 data() 中硬编码了**"钠(Sodium)"**作为默认数据:
data() {
return {
nutrientData: {
name: '钠',
english: 'Sodium (Na)',
icon: '🧂',
// ... 其他钠的数据
}
}
}
当 loadNutrientData(name) 中 name 为空或不匹配 nutrientMap 的任何 key 时,nutrientData 不会被更新,页面始终显示默认的"钠"内容。
结果:无论点击哪个营养素,要么不跳转,要么跳转后参数丢失,最终所有页面都显示默认的"钠"内容。
修复建议
修复 1:改用直接传参方式(推荐)
<!-- nutrition-knowledge.vue -->
<!-- 修改前 -->
@click="goToNutrientDetail" :data-nutrient-index="index"
<!-- 修改后 -->
@click="goToNutrientDetail(index)"
// 方法改为接收 index 参数
goToNutrientDetail(index) {
const item = this.nutrientList[index];
if (!item) return;
uni.navigateTo({
url: `/pages/tool/nutrient-detail?name=${encodeURIComponent(item.name)}`
});
}
修复 2:详情页增加参数解码和容错
// nutrient-detail.vue
onLoad(options) {
if (options.name) {
const name = decodeURIComponent(options.name);
this.loadNutrientData(name);
}
}
修复 3:默认数据改为空状态
将 data() 中的默认 nutrientData 改为空对象,并在页面增加空状态提示,避免误导用户。
优先级:高
问题优先级汇总
| 序号 | 问题 | 类型 | 优先级 | 影响范围 |
|---|---|---|---|---|
| 一 | 食谱计算器用油量偏多 | 算法缺陷 | 高 | 所有使用计算器的用户 |
| 二 | AI 营养师体验差 | 功能缺失 | 高 | 所有使用 AI 营养师的用户 |
| 三 | 食物百科成分表/图片/重量 | 数据不完整 | 中高 | 所有查看食物详情的用户 |
| 四 | 营养素板块显示重复 | 前端 Bug | 高 | 所有查看营养素详情的用户 |
涉及文件清单
后端(Java)
| 文件 | 问题 |
|---|---|
ToolCalculatorServiceImpl.java |
问题一:油脂系数错误 |
ToolAiNutritionistServiceImpl.java |
问题二:AI 服务未接入 |
ToolFoodServiceImpl.java |
问题三:返回字段不完整 |
V2Food.java |
问题三:缺少 serving_size 字段 |
前端(Vue)
| 文件 | 问题 |
|---|---|
pages/tool/food-detail.vue |
问题三:图片 URL、重量标注 |
pages/tool/nutrient-detail.vue |
问题三/四:Figma URL、默认数据 |
pages/tool/nutrition-knowledge.vue |
问题四:事件传参兼容性 |