# 测试问题与优化建议 — 修改解决方案 **分析日期:** 2026年4月11日 **项目:** msh_single_uniapp(UniApp 小程序) **涉及页面:** calculator-result / ai-nutritionist / food-encyclopedia --- ## 一、总览:需处理项 vs 暂不处理项 | # | 问题 | 决策 | 涉及文件 | |---|------|------|----------| | 1 | 食物份数 → 克数 | **采纳** | `pages/tool/calculator-result.vue` | | 2 | AI对话交互按钮 | **部分采纳** | `pages/tool/ai-nutritionist.vue` | | 3 | AI回复速度慢 | **已优化(切换Coze流式API)** | `ai-nutritionist.vue` + `models-api.js` | | 4 | 食物百科图片缺失 | **不处理**(后台可改) | — | | 5 | 百科卡片误导点击 | **采纳**(跳转详情页) | `pages/tool/food-encyclopedia.vue` | | 6 | 角色权限与分销 | **暂不处理**(后台人工配置) | — | | 7 | 百科点击报错 Bug | **必须修复** | `pages/tool/food-encyclopedia.vue` | --- ## 二、需修改的问题及代码级解决方案 --- ### 问题1:食物份数建议改为克数 **页面:** `pages/tool/calculator-result.vue` **现状分析:** 当前"食物份数建议"卡片(约第94-113行)中,食物列表渲染逻辑为: ```html {{ item.portion }} 份 ``` 数据结构 `foodList` 中每个 item 包含 `{ number, name, portion }`,`portion` 为份数值。 **修改方案:** 1. **后端接口改造**(推荐):`getCalculatorResult` 接口返回数据中增加 `gram` 字段,或将 `portion` 的含义改为克数。 2. **前端模板修改**(`calculator-result.vue` 约第94-113行): ```html 食物份数建议 ... {{ item.portion }} 份 每日食物建议 ... {{ item.gram || item.portion }} 克 ``` 3. **数据层适配**(`applyResult` 方法,约第320-350行):解析接口返回时,确保 `foodList` 中的 item 携带 `gram` 字段。如后端暂未改造,可前端用换算公式临时过渡: ```javascript // 在 applyResult 方法中 this.foodList = (res.data.foodList || []).map(item => ({ ...item, gram: item.gram || Math.round(item.portion * item.gramPerServing) || item.portion })) ``` **工作量估计:** 前端 0.5天,后端(如需改接口)0.5天 --- ### 问题2:AI营养师对话页面新增交互按钮 **页面:** `pages/tool/ai-nutritionist.vue` **现状分析:** 当前每条 AI 消息仅有 **1个操作按钮**:TTS 语音朗读(约第84-90行)。消息数据结构为 `{ role, content, type, loading, streaming }`。AI 回复通过 `api.kieaiGeminiChat()` 调用(实际走 `/api/front/kieai/gemini/chat`,`stream: false` 非流式),整体响应一次性返回。 **部分采纳后的需求:** 新增"复制"、"重新生成"、"删除"按钮(语音朗读已有)。 **修改方案:** 在消息气泡下方的操作区域(约第84-90行 TTS 按钮位置),扩展为按钮组: ```html {{ ttsPlayingIndex === index ? '⏹' : '▶' }} 📋 🔄 {{ ttsPlayingIndex === index ? '⏹' : '▶' }} 🗑️ ``` **新增 methods:** ```javascript // 复制消息 copyMessage(index) { const msg = this.messageList[index] uni.setClipboardData({ data: msg.content, success: () => uni.showToast({ title: '已复制', icon: 'success' }) }) }, // 删除单条消息 deleteMessage(index) { uni.showModal({ title: '提示', content: '确定删除这条消息吗?', success: (res) => { if (res.confirm) { this.messageList.splice(index, 1) } } }) }, // 重新生成(重发上一条用户消息) regenerateMessage(index) { // 找到该AI消息对应的上一条用户消息 let userMsgIndex = index - 1 while (userMsgIndex >= 0 && this.messageList[userMsgIndex].role !== 'user') { userMsgIndex-- } if (userMsgIndex < 0) return // 移除当前AI回复 this.messageList.splice(index, 1) // 重新发送 this.sendToAI() }, // 判断是否为最后一条AI消息 isLastAiMessage(index) { for (let i = this.messageList.length - 1; i >= 0; i--) { if (this.messageList[i].role === 'ai') return i === index } return false } ``` **新增样式:** ```scss .msg-actions { display: flex; gap: 16rpx; margin-top: 8rpx; justify-content: flex-start; } .action-btn { width: 56rpx; height: 56rpx; display: flex; align-items: center; justify-content: center; border-radius: 50%; background: #f4f5f7; } .action-icon { font-size: 24rpx; } ``` **工作量估计:** 1天 --- ### 问题3:AI回复速度慢(已优化 — 切换至 Coze API) **优化前调用链路:** ``` 小程序 → POST /api/front/kieai/gemini/chat/stream → Spring KieAIController (SseEmitter) → OkHttp3 异步 POST https://api.kie.ai/gemini-2.5-flash/v1/chat/completions → KieAI 代理网关 → Gemini 2.5 Flash 模型 ``` **优化后调用链路:** ``` 小程序 → POST /api/front/coze/chat/stream → Spring CozeController (SseEmitter, 120s timeout, 15s heartbeat) → Coze 官方 SDK (client.chat().stream()) → https://api.coze.cn → Coze Bot (7591133240535449654) ``` **已完成的优化:** 1. **前端切换至 Coze 流式 API:** `sendToAI()` 改为调用 `api.cozeChatStream()`,直接使用 Coze 的流式事件(`conversation.message.delta`),逐字渲染 AI 回复。首字可见时间大幅缩短。 2. **Coze 事件解析:** 正确解析 Coze SSE 事件格式 `{ event, conversation_id, chat_id, content, role, type }`,仅累积 `event=conversation.message.delta` 且 `type=answer` 的文本增量。 3. **多轮对话支持:** 自动保存 `conversation_id`,后续消息在同一会话中进行,Coze Bot 可获取完整上下文。 4. **消息格式适配:** 新增 `buildCozeMessages()` / `buildCozeRequest()` 方法,将用户输入(文本/图片/多模态)转换为 Coze 格式的 `additionalMessages`。 5. **降级策略:** 流式失败时自动降级为非流式 `api.cozeChat()`,并通过 `pollCozeResult()` 轮询获取最终回复。 **Coze 相比 KieAI 代理的优势:** - 去掉一层 KieAI 代理网关,减少一跳网络延迟 - Coze 后端已配置 `X-Accel-Buffering: no` + `Cache-Control: no-cache`,SSE 不会被 nginx 缓冲 - Coze SDK 内置连接池管理,不再每次请求 new OkHttpClient - 内置 15s heartbeat 防止连接超时断开 - 支持通过 `conversation_id` 进行多轮上下文对话 --- ### 问题4:食物百科图片缺失 **决策:不处理。** 图片数据通过商城 PC 管理后台维护,属运营侧工作。 --- ### 问题5:百科卡片点击 → 跳转食物详情页 **页面:** `pages/tool/food-encyclopedia.vue` **现状分析:** 当前卡片已绑定了 `@click="goToFoodDetail(item)"`(约第103行),`goToFoodDetail` 方法(约第1012-1030行)会尝试提取 item 的 id 并跳转到 `/pages/tool/food-detail`。项目中已存在 `pages/tool/food-detail.vue` 页面。 **问题:** 跳转逻辑本身已实现,但存在 Bug(见问题7),导致点击报错而非正常跳转,用户看到的效果就是"点击无反应"或"点击后闪退"。 **修改方案:** 修复问题7的 Bug 后,卡片点击跳转详情页的功能即可正常工作。需确认: 1. `food-detail.vue` 详情页内容是否完整可用 2. `pages.json` 中是否已注册 `/pages/tool/food-detail` 路由 **工作量估计:** 与问题7合并处理,0.5天 --- ### 问题6:角色权限与分销系统 **决策:暂不处理。** 临时方案为运营人员在后台手动为指定用户账号配置角色标签(医生/护士/普通用户)。 --- ### 问题7(Bug):食物百科点击报错 TypeError **页面:** `pages/tool/food-encyclopedia.vue` **错误信息:** ``` TypeError: Cannot read property 'id' of undefined at pickNumericIdStr (food-encyclopedia.vue:1015) at VueComponent.goToFoodDetail (food-encyclopedia.vue:1022) ``` **根因分析:** `goToFoodDetail(item)` 方法(第1012行)中,内部闭包 `pickNumericIdStr` 直接访问 `item.id`(第1015行),但未做空值校验。当 `filteredFoodList` 中的某个 item 为 `undefined` 或 `null` 时(可能因 API 返回脏数据、`normalizeFoodItem` 边界情况),点击即触发 TypeError。 **修复方案:** 在 `goToFoodDetail` 方法开头增加防御性校验(约第1012行): ```javascript goToFoodDetail(item) { // 防御性校验:避免 item 为空时报错 if (!item || typeof item !== 'object') { console.warn('[food-encyclopedia] goToFoodDetail: item 为空,跳过跳转') return } const pickNumericIdStr = () => { const cands = [item.id, item.foodId, item.food_id, item.v2FoodId, item.v2_food_id, item.foodID] // ... 原有逻辑不变 } // ... 后续逻辑不变 } ``` 同时建议加固 `filteredFoodList` 计算属性(约第261行),确保过滤掉无效项: ```javascript filteredFoodList() { const list = (this.foodList || []).filter(item => item != null && typeof item === 'object' && item.name) // ... 后续过滤逻辑不变 } ``` **工作量估计:** 0.5天(含测试验证) --- ## 三、执行优先级排序 | 优先级 | 问题 | 预估工时 | 负责方 | |--------|------|----------|--------| | **P0** | #7 百科点击报错 Bug | 0.5天 | 前端 | | **P1** | #1 份数→克数 | 0.5-1天 | 前端+后端 | | **P1** | #5 百科卡片跳转详情页 | 与#7合并 | 前端 | | **P2** | #3 AI响应速度(已切换Coze流式API) | ✅ 已完成 | 前端 | | **P2** | #2 AI对话新增操作按钮 | 1天 | 前端 | | **—** | #4 百科图片 | 不处理 | 运营 | | **—** | #6 角色权限 | 暂不处理 | 运营(后台手动) | **总预估:** 前端约 3-4天,后端约 2-3天 --- *基于 msh_single_uniapp 项目代码分析生成,所有代码引用均已核对源文件。*