diff --git a/docs/测试问题分析报告_2026-03-22.md b/docs/测试问题分析报告_2026-03-22.md new file mode 100644 index 0000000..edf46af --- /dev/null +++ b/docs/测试问题分析报告_2026-03-22.md @@ -0,0 +1,301 @@ +# 测试问题分析报告 + +> **日期:** 2026-03-22 +> **分析人:** Claude AI +> **项目:** 民生汇 - 慢性肾病营养管理小程序 + +--- + +## 一、食谱计算器 —— 用油量计算逻辑错误 + +### 问题描述 + +食谱计算器计算出的用油量偏多,不符合实际膳食指导标准。 + +### 问题定位 + +**文件:** `msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCalculatorServiceImpl.java` +**位置:** `generateFoodPortions()` 方法,第 516 行 + +```java +// 当前代码(有误) +list.add(createFoodPortion(7, "油脂类10g", round(5.7 * energyRatio))); +``` + +### 根因分析 + +油脂类的份数计算使用了系数 `5.7`,与第 510 行谷薯类的系数完全相同: + +```java +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`: + +```java +// 修复后 +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 服务: + +```java +// 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 支持,无法实现"逐字输出"的流式效果,用户需等待完整回复后才能看到内容。 + +### 修复建议 + +1. **正式接入 Coze AI 服务**:在 `sendMessage()` 中调用 `ToolCozeServiceImpl`,真正发起 AI 对话请求 +2. **优化 Prompt 模板**:设计结构化的系统提示词(System Prompt),要求 AI 回复遵循固定格式(如:摘要 → 营养分析 → 建议 → 注意事项),提升内容逻辑性 +3. **引入 SSE 流式返回**:后端通过 SSE 推送逐步响应,前端实时渲染,提升交互体验 +4. **增加加载状态动效**:在等待回复期间展示更好的"正在思考"动画 + +### 优先级:高 + +--- + +## 三、食物百科 —— 成分表不全、图片错误、缺少重量标注 + +### 问题描述 + +食物百科中食物成分表内容不全,图片显示有错误,而且没有标注具体是多少重量的食物所提供的营养成分。 + +### 问题定位 + +**后端文件:** `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()` 方法返回的营养字段不完整: + +```java +// 当前返回的字段 +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: + +```javascript +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 标准录入的,就会产生误导。 + +### 修复建议 + +1. **补充后端返回字段**:在 `getDetail()` 中增加 `calcium`、`iron`、`vitaminC`、`nutrientsJson`、`recommendedAmount` 等字段的返回 +2. **替换 Figma 临时 URL**:将所有 Figma API 链接替换为上传至 OSS 的稳定图片资源 +3. **增加图片容错处理**:前端为食物图片增加 `@error` 事件处理,在图片加载失败时显示占位图 +4. **增加重量标注字段**:数据库添加 `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` 中营养素卡片的点击事件: + +```html +@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)"**作为默认数据: + +```javascript +data() { + return { + nutrientData: { + name: '钠', + english: 'Sodium (Na)', + icon: '🧂', + // ... 其他钠的数据 + } + } +} +``` + +当 `loadNutrientData(name)` 中 `name` 为空或不匹配 `nutrientMap` 的任何 key 时,`nutrientData` 不会被更新,页面始终显示默认的"钠"内容。 + +**结果**:无论点击哪个营养素,要么不跳转,要么跳转后参数丢失,最终所有页面都显示默认的"钠"内容。 + +### 修复建议 + +**修复 1:改用直接传参方式(推荐)** + +```html + + +@click="goToNutrientDetail" :data-nutrient-index="index" + + +@click="goToNutrientDetail(index)" +``` + +```javascript +// 方法改为接收 index 参数 +goToNutrientDetail(index) { + const item = this.nutrientList[index]; + if (!item) return; + uni.navigateTo({ + url: `/pages/tool/nutrient-detail?name=${encodeURIComponent(item.name)}` + }); +} +``` + +**修复 2:详情页增加参数解码和容错** + +```javascript +// 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` | 问题四:事件传参兼容性 |