Files
msh-system/docs/测试问题分析报告_2026-03-22.md
Developer ba08abd374 fix: 修复6项测试问题并补全配套资源
- 修复油脂类食物推荐量系数 (5.7→2.5) [ToolCalculatorServiceImpl]
- AI营养师接入真实Coze API,替换Mock回复 [ToolAiNutritionistServiceImpl]
- 食物百科详情新增钙/铁/维C/嘌呤/重量基准字段返回 [ToolFoodServiceImpl]
- V2Food模型新增purine、servingSize字段 [V2Food.java]
- 食物百科详情页动态重量标注+新增4项营养展示+替换Figma URL [food-detail.vue]
- 修复营养素列表dataset传参Bug(WeChat camelCase) [nutrition-knowledge.vue]
- 营养素详情页接入后端API+兜底本地数据+替换Figma URL [nutrient-detail.vue]
- 新增数据库迁移脚本及参考初始化数据 [docs/sql/]
- 新增前端占位图标5个 [static/images/]
- 新增开发任务完成报告 [docs/]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-25 14:18:00 +08:00

505 lines
22 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 测试问题分析报告
> **日期:** 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 油**
根据《中国居民膳食指南》和《慢性肾脏病患者膳食指导2017CKD 患者每日食用油推荐量为 **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`
**Coze服务** `msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCozeServiceImpl.java`
**Coze控制器** `msh_crmeb_22/crmeb-front/src/main/java/com/zbkj/front/controller/CozeController.java`
### 根因分析
#### 1. 后端 AI 服务使用 Mock未调用 Coze API核心原因
后端 `ToolAiNutritionistServiceImpl.sendMessage()` 方法(第 89-97 行)使用的是 **Mock 模拟回复**
```java
// Mock AI response
message.setAiResponse("这是一个模拟的AI回复。");
message.setAiResponseStatus("success");
```
项目中已存在完整可用的 Coze API 对接服务 `ToolCozeServiceImpl`,提供了以下现成能力:
- `chat()` — 非流式对话(适用于简单问答)
- `chatStream()` — SSE 流式对话(适用于长回复的实时输出)
- `retrieveChat()` / `listMessages()` — 查询对话状态和消息列表
- `uploadFile()` — 上传图片/文件到 Coze
后端配置已就绪Bot ID `7591133240535449654`AI 营养师机器人API 地址 `https://api.coze.cn`,支持 PAT 和 JWT 两种认证方式。**但 `sendMessage()` 中并未调用这些服务。**
#### 2. 前端已对接 Coze但走了两条冗余路径
前端 `ai-nutritionist.vue` 实际上已编写了 Coze 调用逻辑,但存在两条并行路径导致混乱:
- **路径 A文本消息**:调用 KieAI Gemini (`api.kieaiGeminiChat()`) 进行多模态对话
- **路径 B图片消息**:调用 Coze API (`api.cozeChat()`) 并通过轮询 `api.cozeRetrieveChat()` 获取结果(每 1 秒轮询,最多 60 次)
两条路径的响应格式、错误处理、消息展示逻辑各不相同,导致用户体验不一致。文本走 KieAI 时响应速度取决于 Gemini 接口,且非流式返回;图片走 Coze 时需要轮询等待,延迟更大。
#### 3. 缺乏统一的流式响应体验
后端 `CozeController` 已提供 SSE 流式端点 `POST /api/front/coze/chat/stream``ToolCozeServiceImpl.chatStream()` 通过 RxJava Observable 实现了 60 秒超时的 SSE 推送。但前端并未使用此端点,而是采用非流式调用 + 轮询的方式,无法实现"逐字输出"效果。
### 修复方案
**核心策略:统一使用 Coze API 作为 AI 营养师的唯一对话服务,利用已有的 SSE 流式通道提升响应速度。**
#### 步骤 1后端 — 将 `sendMessage()` 对接 Coze 服务
```java
// ToolAiNutritionistServiceImpl.java — 修改 sendMessage() 方法
@Autowired
private ToolCozeServiceImpl cozeService;
// 替换 Mock 逻辑,调用 Coze chat
CozeChatRequest chatRequest = new CozeChatRequest();
chatRequest.setBotId("7591133240535449654");
chatRequest.setUserId(String.valueOf(userId));
chatRequest.setContent(message.getContent());
chatRequest.setConversationId(conversationId.toString());
CreateChatResp resp = cozeService.chat(chatRequest);
message.setAiResponse(resp.getContent());
message.setAiResponseStatus("success");
```
#### 步骤 2前端 — 统一调用 Coze 流式端点
`ai-nutritionist.vue` 中的文本消息路径从 KieAI Gemini 切换为 Coze SSE 流式调用:
```javascript
// 统一使用 Coze SSE 流式对话
const eventSource = new EventSourcePolyfill('/api/front/coze/chat/stream', {
method: 'POST',
body: JSON.stringify({ botId: this.botId, content: message, conversationId: this.conversationId }),
headers: { 'Content-Type': 'application/json', 'Authori-zation': token }
});
eventSource.onmessage = (event) => {
// 实时追加文字到消息气泡
this.currentAiMessage.content += event.data;
};
```
#### 步骤 3Coze Bot 侧 — 优化 Prompt 提升内容质量
在 Coze 平台编辑 Bot `7591133240535449654` 的系统提示词,增加结构化输出要求:
```
你是一位专业的肾病营养师。请按以下格式回复:
【摘要】一句话概括建议
【营养分析】针对用户问题的营养学分析
【饮食建议】具体可操作的建议(分点列出)
【注意事项】需要特别关注的风险提示
```
#### 预期效果
| 指标 | 修复前 | 修复后 |
|------|--------|--------|
| 首字响应时间 | 3-5 秒(轮询等待) | <1 秒SSE 流式) |
| 内容逻辑性 | 无结构 | 四段式结构化输出 |
| 对话路径 | KieAI + Coze 双路径混乱 | Coze 单一路径统一 |
| 多模态支持 | 图片走 Coze文本走 KieAI | 统一走 Coze支持文本+图片) |
### 优先级:高
---
## 三、食物百科 —— 成分表不全、图片错误、缺少重量标注
### 问题描述
食物百科中食物成分表内容不全,图片显示有错误,而且没有标注具体是多少重量的食物所提供的营养成分。
### 问题定位
**后端文件:** `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`
**图片服务:** `msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/DishImageServiceImpl.java`
**参考站点:** https://www.ishen365.com/article/cereal (爱肾网食物营养成分表)
### 根因分析
#### 3.1 食物成分表内容不全 + 缺少重量标注
**参考标准:** 爱肾网ishen365.com的食物成分表采用的展示规范是每种食物标注 **"每份重量"(如"谷类-大米 每100g"**,并列出完整的营养成分包括:能量(kcal)、蛋白质(g)、脂肪(g)、碳水化合物(g)、钾(mg)、磷(mg)、钠(mg)、钙(mg)、嘌呤(mg) 等。这是 CKD 患者膳食管理的行业标准展示方式。
**当前问题:** 后端 `getDetail()` 方法返回的营养字段不完整:
```java
// 当前返回的字段(仅 7 项)
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());
```
**对比 ishen365 标准缺失的字段:**
| 营养素 | ishen365 有 | 当前系统 | 状态 |
|--------|------------|----------|------|
| 能量(kcal) | 有 | 有 | 正常 |
| 蛋白质(g) | 有 | 有 | 正常 |
| 脂肪(g) | 有 | 有 | 正常 |
| 碳水化合物(g) | 有 | 有 | 正常 |
| 钾(mg) | 有 | 有 | 正常 |
| 磷(mg) | 有 | 有 | 正常 |
| 钠(mg) | 有 | 有 | 正常 |
| **钙(mg)** | **有** | **模型有字段,接口未返回** | **缺失** |
| **铁(mg)** | 有 | 模型有字段,接口未返回 | **缺失** |
| **维生素C(mg)** | 有 | 模型有字段,接口未返回 | **缺失** |
| **嘌呤(mg)** | **有** | **模型无此字段** | **缺失** |
| **每份重量标注** | **有(如"每100g"** | **前端硬编码,数据库无字段** | **缺失** |
`V2Food` 模型中定义了 `calcium``iron``vitaminC``nutrientsJson` 字段但后端未返回;同时缺少 `purine`(嘌呤)字段和 `servingSize`(每份重量基准)字段。
前端 `food-detail.vue``parseNutritionTable()` 尝试解析 `calcium``purine` 等,但后端没有返回。前端"每100g"标签是硬编码的静态文本,无法反映实际数据的重量基准。
#### 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) 部分食物图片为空或无效**
数据库中部分食物的 `image` 字段为空或为旧的无效 URL。项目中已有 `DishImageServiceImpl` 可自动生成并落库,但未对存量数据做全量刷新。
### 修复方案
#### 步骤 1补全数据库字段和成分数据参考 ishen365 标准)
**a) 数据库表 `v2_food` 增加字段:**
```sql
ALTER TABLE v2_food ADD COLUMN purine DECIMAL(10,2) DEFAULT NULL COMMENT '嘌呤含量(mg)';
ALTER TABLE v2_food ADD COLUMN serving_size VARCHAR(50) DEFAULT '每100g' COMMENT '营养成分对应的食物重量基准';
```
**b) 参考 ishen365.com 补全营养数据:**
以谷薯类为例,按 ishen365 的数据格式补充 `calcium``iron``vitaminC``purine``serving_size` 等字段值。数据可通过爬虫脚本从 ishen365 获取后批量导入,或手动录入。
**c) 后端 `getDetail()` 补充返回字段:**
```java
// ToolFoodServiceImpl.java — getDetail() 补充以下字段
map.put("calcium", food.getCalcium());
map.put("iron", food.getIron());
map.put("vitaminC", food.getVitaminC());
map.put("purine", food.getPurine());
map.put("servingSize", food.getServingSize()); // "每100g" / "每份(50g)"
map.put("nutrientsJson", food.getNutrientsJson());
map.put("recommendedAmount", food.getRecommendedAmount());
```
**d) 前端动态显示重量标注:**
```javascript
// food-detail.vue — 替换硬编码的 "每100g"
<text class="serving-label">{{ foodData.servingSize || '每100g' }}</text>
```
#### 步骤 2利用已有 DishImageService 批量修复食物图片
项目中 `DishImageServiceImpl` 已实现完整的 **AI 生图 → 压缩 → 上传 OSS → 缓存到 `v2_dish_image_cache` 表** 的流程,`ToolFoodServiceImpl.refreshFoodImages()``ToolController` 中已暴露批量刷新接口。
**直接调用已有接口修复:**
```bash
# 调用已有的批量刷新接口,每次处理 20 条缺失/无效图片的食物记录
POST /api/front/tool/food/refresh-images?limit=20
```
**内部执行流程(已实现,无需额外开发):**
1. 查询 `v2_food` 表中 `image` 为空或不含 `aliyuncs.com`(非 OSS的记录
2. 对每条记录调用 `DishImageService.ensureFoodImageAndUpdateDb(foodId)`
3. KieAI 生成食物照片Prompt`"新鲜食物/食材照片:{食物名},白色背景,自然光照"`
4. 下载 → 压缩至 ≤100KB → 上传阿里云 OSS路径 `foods/`
5. 更新 `v2_food.image` 字段 + 写入 `v2_dish_image_cache` 缓存表
**需多次调用直至所有食物图片修复完成。** 建议写一个定时任务或脚本循环调用。
#### 步骤 3替换前端 Figma 临时 URL
`food-detail.vue``nutrient-detail.vue` 中的 Figma API 图标链接替换为 OSS 稳定资源,或使用本地 icon 图片。
### 优先级:高
---
## 四、健康知识营养素板块 —— 所有选项显示同一内容
### 问题描述
健康知识中营养素板块,所有的营养素选项(蛋白质、钾、磷、钠、钙、水分)点击后显示的都是同一个内容。
### 问题定位
**列表页:** `msh_single_uniapp/pages/tool/nutrition-knowledge.vue` 第 39 行
**详情页:** `msh_single_uniapp/pages/tool/nutrient-detail.vue` 第 114 行、第 137-277 行
**后端服务:** `msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolKnowledgeServiceImpl.java`
**后端接口:** `GET /api/front/tool/knowledge/nutrient/{name}` — 按营养素名查询详情
### 根因分析
这是一个**前端事件传参 Bug + 数据来源设计缺陷**联合导致的问题。
#### Bug 1列表页事件传参问题前端 Bug
`nutrition-knowledge.vue` 中营养素卡片的点击事件:
```html
@click="goToNutrientDetail" :data-nutrient-index="index"
```
`goToNutrientDetail` 方法通过 `event.currentTarget.dataset.nutrientIndex` 获取索引值。但在**微信小程序**环境中,`dataset` 的属性名会被自动转换为全小写(`nutrientindex` 而非 `nutrientIndex`),导致取值为 `undefined`
当 index 为 `undefined` 时,方法直接 `return`,不跳转;即使跳转成功,中文参数编解码也可能导致 `nutrientMap[name]` 匹配失败。
#### Bug 2详情页数据来源设计缺陷
`nutrient-detail.vue` 的数据完全来自**前端硬编码的 `nutrientMap`**6 种营养素的静态内容),且 `data()` 默认值是"钠"的内容。当参数传递失败时,所有页面都显示"钠"。
**更深层问题:** 后端 `ToolKnowledgeServiceImpl` 已提供 `getNutrientDetail(name)` 方法,可从 `v2_knowledge` 表中查询 `type='nutrients'` 的记录,但前端根本没有调用这个接口,而是使用了硬编码数据。这导致内容无法动态更新,且 6 种营养素的详细内容质量参差不齐。
### 修复方案
**核心策略:通过 AI 生成 6 种营养素的专业内容后写入 `v2_knowledge` 表,前端改为从后端接口获取数据。**
#### 步骤 1AI 生成营养素内容并落库
利用已有的 Coze API 服务,批量生成 6 种营养素(蛋白质、钾、磷、钠、钙、水分)的专业科普内容,写入 `v2_knowledge` 表。
**a) 数据表结构(已存在 `v2_knowledge` 表):**
```sql
-- 每种营养素插入一条记录type='nutrients'
INSERT INTO v2_knowledge (type, nutrient_name, title, content, summary, cover_image, status, sort_order, created_at) VALUES
('nutrients', '蛋白质', '蛋白质 — CKD患者的关键营养素', '{AI生成的详细内容JSON}', '了解蛋白质摄入与肾功能的关系', '', 'published', 1, NOW()),
('nutrients', '', '钾 — 维持生命的双刃剑', '{AI生成的详细内容JSON}', '高钾血症的预防与饮食管理', '', 'published', 2, NOW()),
('nutrients', '', '磷 — 骨骼健康的隐形杀手', '{AI生成的详细内容JSON}', '控磷饮食与磷结合剂的正确使用', '', 'published', 3, NOW()),
('nutrients', '', '钠 — 水盐平衡的调节器', '{AI生成的详细内容JSON}', '低盐饮食的科学方法', '', 'published', 4, NOW()),
('nutrients', '', '钙 — 骨骼和心脏的守护者', '{AI生成的详细内容JSON}', '钙磷平衡与维生素D的补充', '', 'published', 5, NOW()),
('nutrients', '水分', '水分 — 透析患者的生命线', '{AI生成的详细内容JSON}', '量出为入的饮水管理原则', '', 'published', 6, NOW());
```
**b) AI 生成内容的 JSON 格式(与前端 `nutrientData` 结构对齐):**
```json
{
"name": "蛋白质",
"english": "Protein",
"icon": "🥩",
"description": "构成人体组织的重要营养素",
"status": "需控制",
"statusDesc": "根据CKD分期调整摄入量",
"importance": "蛋白质是人体细胞的基本组成成分...",
"recommendation": "CKD 1-2期0.8-1.0g/kg/天\nCKD 3-5期0.6-0.8g/kg/天\n透析患者1.0-1.2g/kg/天",
"foodSources": ["鸡蛋", "鱼类", "瘦肉", "牛奶", "豆腐", "鸡胸肉"],
"riskWarning": "过多蛋白质摄入会产生大量含氮废物,加重肾脏负担...",
"suggestions": ["优先选择优质蛋白", "控制植物蛋白摄入", "每餐均匀分配", "..."],
"disclaimer": "以上建议仅供参考,具体方案请咨询您的主治医生或营养师"
}
```
**c) 通过 Coze API 批量生成内容的实现思路:**
可编写一个后端管理接口或脚本,循环调用 `ToolCozeServiceImpl.chat()` 为每种营养素生成内容:
```java
// 管理端一次性脚本
String[] nutrients = {"蛋白质", "", "", "", "", "水分"};
for (String nutrient : nutrients) {
CozeChatRequest req = new CozeChatRequest();
req.setBotId("7591133240535449654");
req.setContent("请为慢性肾病患者生成关于'" + nutrient + "'的科普内容,"
+ "包含重要性说明、推荐摄入量、主要食物来源、风险提示、实用建议6条"
+ "请用JSON格式返回。");
CreateChatResp resp = cozeService.chat(req);
// 解析响应 → 存入 v2_knowledge 表
}
```
**d) 利用已有的 `fillMissingCoverImages()` 自动补充封面图:**
`ToolKnowledgeServiceImpl.fillMissingCoverImages()` 会自动为缺少封面图的知识记录生成图片(通过 `DishImageService.generateImageAndUploadToOssForKnowledge()`),生成后自动更新到数据库。调用:
```bash
POST /api/front/tool/knowledge/fill-cover-images?limit=6
```
#### 步骤 2修复前端传参 Bug
**a) 列表页改用直接传参:**
```html
<!-- nutrition-knowledge.vue — 修改前 -->
@click="goToNutrientDetail" :data-nutrient-index="index"
<!-- 修改后 -->
@click="goToNutrientDetail(index)"
```
```javascript
goToNutrientDetail(index) {
const item = this.nutrientList[index];
if (!item) return;
uni.navigateTo({
url: `/pages/tool/nutrient-detail?name=${encodeURIComponent(item.name)}`
});
}
```
#### 步骤 3详情页改为从后端接口获取数据
```javascript
// nutrient-detail.vue — 将硬编码的 nutrientMap 替换为 API 调用
async loadNutrientData(name) {
try {
const { getNutrientDetail } = await import('@/api/tool.js');
const result = await getNutrientDetail(name);
if (result && result.data) {
// 后端返回 v2_knowledge 记录content 字段为 JSON
const detail = result.data;
this.nutrientData = typeof detail.content === 'string'
? JSON.parse(detail.content)
: detail.content;
}
} catch (error) {
console.error('加载营养素详情失败:', error);
// 降级使用本地 nutrientMap保留作为兜底
if (this.nutrientMap[name]) {
this.nutrientData = this.nutrientMap[name];
}
}
}
```
#### 预期效果
| 方面 | 修复前 | 修复后 |
|------|--------|--------|
| 数据来源 | 前端硬编码 6 份静态数据 | 后端 `v2_knowledge`AI 生成专业内容 |
| 内容质量 | 手写内容,质量不均 | Coze AI 生成,专业、结构统一 |
| 点击跳转 | dataset 传参失败,不跳转 | 直接传参,兼容微信小程序 |
| 封面图片 | 无 | AI 生成后上传 OSS自动落库 |
| 内容维护 | 需改代码重新发版 | 后台修改数据库即可,无需发版 |
### 优先级:高
---
## 问题优先级汇总
| 序号 | 问题 | 类型 | 优先级 | 修复策略 |
|------|------|------|--------|----------|
| 一 | 食谱计算器用油量偏多 | 算法缺陷 | 高 | 修改油脂系数 5.7→2.5 |
| 二 | AI 营养师体验差 | 架构优化 | 高 | 统一走 Coze API + SSE 流式 |
| 三 | 食物百科成分表/图片/重量 | 数据补全 | 高 | 参考 ishen365 补全数据 + 调用已有 DishImageService 批量生图落库 |
| 四 | 营养素板块显示重复 | Bug + 重构 | 高 | 修复传参 Bug + AI 生成内容落库到 v2_knowledge |
---
## 涉及文件清单
### 后端Java
| 文件 | 问题 | 修改内容 |
|------|------|----------|
| `ToolCalculatorServiceImpl.java` | 问题一 | 修改油脂系数 |
| `ToolAiNutritionistServiceImpl.java` | 问题二 | `sendMessage()` 对接 `ToolCozeServiceImpl.chat()` |
| `ToolCozeServiceImpl.java` | 问题二 | 已有完整 Coze API 实现,直接调用 |
| `CozeController.java` | 问题二 | SSE 流式端点已就绪 |
| `ToolFoodServiceImpl.java` | 问题三 | `getDetail()` 补充 calcium/iron/vitaminC/purine/servingSize 返回 |
| `V2Food.java` | 问题三 | 新增 purine、serving_size 字段 |
| `DishImageServiceImpl.java` | 问题三 | 已有 AI 生图+OSS 上传+落库功能,调用 `refreshFoodImages()` 即可 |
| `ToolKnowledgeServiceImpl.java` | 问题四 | 已有 `getNutrientDetail(name)` + `fillMissingCoverImages()`,配合 AI 内容生成后落库 |
### 前端Vue
| 文件 | 问题 | 修改内容 |
|------|------|----------|
| `pages/tool/ai-nutritionist.vue` | 问题二 | 统一 Coze SSE 调用,移除 KieAI 双路径 |
| `pages/tool/food-detail.vue` | 问题三 | 替换 Figma URL动态显示 servingSize |
| `pages/tool/nutrient-detail.vue` | 问题四 | 改为调用后端 `getNutrientDetail()` API保留硬编码作兜底 |
| `pages/tool/nutrition-knowledge.vue` | 问题四 | 修复 `goToNutrientDetail` 传参方式 |
### 数据库SQL
| 操作 | 问题 | 说明 |
|------|------|------|
| `v2_food` 新增字段 | 问题三 | 添加 `purine``serving_size` 列 |
| `v2_food` 数据补全 | 问题三 | 参考 ishen365 补充 calcium/iron/vitaminC/purine/serving_size 数据 |
| `v2_knowledge` 插入数据 | 问题四 | AI 生成 6 种营养素内容 + 封面图type='nutrients' |