feat: 集成 KieAI 服务,移除 models-integration 子项目

- 添加 Gemini 2.5 Flash 对话接口(流式+非流式)
- 添加 NanoBanana 图像生成/编辑接口
- 添加 Sora2 视频生成接口(文生视频、图生视频、去水印)
- 移除 models-integration 子项目(功能已迁移至主后端)
- 新增测试文档和 Playwright E2E 配置
- 更新前端页面和 API 接口
- 更新后端配置和日志处理
This commit is contained in:
2026-03-03 15:33:50 +08:00
parent 1ddb051977
commit 4be53dcd1b
586 changed files with 21142 additions and 25130 deletions

251
scripts/fix-bugs-scheduled.sh Executable file
View File

@@ -0,0 +1,251 @@
#!/usr/bin/env bash
# =============================================================================
# MSH Bug 自动修复 + 回归测试 定时任务主脚本
#
# 对应 bug 清单(来自手工测试报告 + Playwright 回归结果):
# BUG-001 打卡积分显示提前跳变 & 累加逻辑未生效
# BUG-002 食谱计算器结果页 Tab 选中样式不明显
# BUG-003 食物百科列表缺配图与营养简介
# BUG-004 食物百科详情页提示「数据加载失败」
# BUG-005 AI 营养师始终返回固定默认答复
# BUG-006 「健康知识」与「营养知识」名称不统一
# BUG-007 饮食指南 / 科普文章详情页无任何内容
# BUG-008 帖子详情页营养统计数据未显示
# BUG-009 社区帖子类型命名未统一为中文
#
# 总时间估算(含 Playwright 验证):≤ 5 h
# 执行顺序:先修快速项(风险低),后修复杂项,最后全量回归
# =============================================================================
set -euo pipefail
# ─── 路径配置 ──────────────────────────────────────────────────────────────
WORKSPACE="/Users/apple/scott2026/msh-system"
SCRIPTS_DIR="$WORKSPACE/scripts"
LOG_DIR="$WORKSPACE/scripts/logs"
CURSOR="/Applications/Cursor.app/Contents/Resources/app/bin/cursor"
PW="npx playwright test tests/e2e/bug-regression.spec.ts --project=mobile-chrome --reporter=list"
mkdir -p "$LOG_DIR"
MAIN_LOG="$LOG_DIR/fix-bugs-$(date '+%Y%m%d_%H%M').log"
LOCK_FILE="$LOG_DIR/fix-bugs.lock"
# ─── 锁:防止多实例并发 ────────────────────────────────────────────────────
if [ -e "$LOCK_FILE" ]; then
echo "[SKIP] 已有一个修复任务在运行 (lock: $LOCK_FILE),退出。" >&2
exit 0
fi
trap 'rm -f "$LOCK_FILE"' EXIT
touch "$LOCK_FILE"
# ─── 日志工具 ─────────────────────────────────────────────────────────────
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$MAIN_LOG"; }
phase() {
echo "" | tee -a "$MAIN_LOG"
echo "═══════════════════════════════════════════════" | tee -a "$MAIN_LOG"
log "$*"
echo "═══════════════════════════════════════════════" | tee -a "$MAIN_LOG"
}
# ─── Cursor Agent 调用工具 ────────────────────────────────────────────────
# 参数: $1=bug_id $2=prompt文本
run_agent() {
local bug_id="$1"
local prompt="$2"
local agent_log="$LOG_DIR/agent-${bug_id}-$(date '+%H%M%S').log"
log "[${bug_id}] 启动 Cursor Agent 修复..."
"$CURSOR" agent --print --trust --model auto \
--workspace "$WORKSPACE" \
"$prompt" 2>&1 | tee -a "$agent_log" >> "$MAIN_LOG" || true
log "[${bug_id}] Agent 执行完成 → $agent_log"
}
# ─── Playwright 局部验证 ──────────────────────────────────────────────────
# 参数: $1=grep_pattern (匹配用例名)
run_partial_test() {
local pattern="$1"
log "[TEST] 验证: $pattern"
cd "$WORKSPACE"
$PW --grep "$pattern" 2>&1 | tee -a "$MAIN_LOG" || true
}
# ─── 全量回归 ────────────────────────────────────────────────────────────
run_full_regression() {
log "[TEST] 全量回归测试..."
cd "$WORKSPACE"
$PW 2>&1 | tee -a "$MAIN_LOG" || true
log "[TEST] 全量回归完成,报告: tests/e2e/reports/index.html"
}
# =============================================================================
# 开始
# =============================================================================
START_TS=$(date +%s)
phase "阶段 0 — 基线确认(运行回归测试,记录当前失败情况)"
# 软失败:仅记录,不中断脚本
cd "$WORKSPACE"
$PW 2>&1 | tee -a "$MAIN_LOG" || true
log "基线记录完成"
# =============================================================================
# 阶段 1快速修复~60 min
# BUG-002 Tab 样式 | BUG-006 名称统一 | BUG-009 中文命名
# =============================================================================
phase "阶段 1 — 快速修复BUG-002, BUG-006, BUG-009"
run_agent "BUG-002" "修复 BUG-002
文件msh_single_uniapp/pages/tool/calculator-result.vue
问题:食谱计算器结果页的「健康概览」和「营养配餐」两个 Tab 切换后,选中与未选中的视觉差异不明显,用户难以辨识当前激活项。
要求:
1. 增强 .tab-item.active 的视觉样式例如加粗字体font-weight: 700、橙色底部下划线border-bottom: 3px solid #f97316、字色变为主色color: #f97316
2. 未激活 Tab 字色应明显变灰color: #9ca3af不加下划线。
3. 不得修改 Tab 的 JS 逻辑,只改 CSS 样式。
4. 修改前先阅读该文件的 .tab-container / .tab-item / .tab-item.active 样式。
请执行修改。"
run_agent "BUG-006" "修复 BUG-006
文件msh_single_uniapp/pages/tool_main/index.vue 以及 msh_single_uniapp/pages/tool/nutrition-knowledge.vue
问题:主页「健康知识」区块标题与营养知识页面导航栏标题命名不一致,一处用「健康知识」,另一处用「营养知识」。
要求:
1. 先阅读两个文件,确认各自现有文案。
2. 统一将两处名称改为「健康知识」(若现在 nutrition-knowledge.vue 导航栏写的是「营养知识」,改成「健康知识」)。
3. 仅修改文案字符串,不改动任何逻辑或样式。
请执行修改。"
run_agent "BUG-009" "修复 BUG-009
文件msh_single_uniapp/pages/tool_main/community.vue
问题:社区页面的 Tab 标签(推荐/最新/关注/热门)和帖子类型标签(.type-tag / .meal-tag中存在英文命名或非中文命名需统一为中文。
要求:
1. 阅读 community.vue 的 template 部分,找出所有 Tab 文案和帖子类型标签文案。
2. 将所有英文或拼音命名改为对应中文(如 recommend→推荐, latest→最新 等)。
3. 检查数据中的类型字段(如 type: 'breakfast'),如显示时有 label 映射,确保 label 均为中文。
4. 不改动路由、接口调用等逻辑。
请执行修改。"
log "[阶段1] 阶段1修复完成进行局部验证..."
run_partial_test "TC-B02|TC-B06|TC-B09"
# =============================================================================
# 阶段 2中等难度修复~60 min
# BUG-001 打卡积分 | BUG-007 文章详情内容
# =============================================================================
phase "阶段 2 — 中等修复BUG-001, BUG-007"
run_agent "BUG-001" "修复 BUG-001共两个子问题
文件msh_single_uniapp/pages/tool/checkin.vue
子问题 A — 打卡前积分提前跳变:
点击「立即打卡」后,在页面跳转前,前端已将积分 +30 显示出来,造成视觉误导。
修复handleCheckin 方法中,不得在 API 返回成功前修改 currentPoints。
仅在 API 调用成功、且获取到服务端最新积分后才更新 currentPoints优先用服务端返回的值不做前端本地累加
子问题 B — 打卡成功后积分未实际增加:
后端积分累加逻辑可能未被正确触发。
修复:
1. 阅读 handleCheckin 中的 API 调用,确认调用的是 /api/front/user/checkin 或类似接口。
2. 若调用成功,再发请求 GET /api/front/user/info 刷新用户积分,并将返回值赋给 currentPoints。
3. 不可使用硬编码 +30积分值必须来自服务端响应。
请先阅读文件,再执行修改。
"
run_agent "BUG-007" "修复 BUG-007
文件msh_single_uniapp/pages/tool/nutrition-knowledge.vue
问题切换到「饮食指南」或「科普文章」Tab 后列表为空guideList / articleList 均为 []),点击条目进入详情页后也无任何内容,页面一片空白。
要求:
1. 阅读 loadKnowledgeList 方法和 getKnowledgeList API 调用。
2. 如果 API 正常但数据为空请检查请求参数type 字段的值是否正确guide / article
3. 如果 API 失败,添加 catch 后的错误提示uni.showToast并确保 guideList / articleList 不被设为 undefined。
4. 在 onLoad 中,若没有 id 参数,应默认加载当前 tab 对应的列表(当前为 nutrients应在 switchTab 后加载 guide/articles 列表)。
5. 修复详情页跳转goToDetail 方法中,确保 knowledgeId 或 id 字段存在才跳转,否则提示「暂无详情」。
请先阅读文件,再执行修改。"
log "[阶段2] 阶段2修复完成进行局部验证..."
run_partial_test "TC-B01|TC-B07"
# =============================================================================
# 阶段 3数据展示修复~60 min
# BUG-003 食物列表 | BUG-008 营养统计
# =============================================================================
phase "阶段 3 — 数据展示修复BUG-003, BUG-008"
run_agent "BUG-003" "修复 BUG-003
文件msh_single_uniapp/pages/tool/food-encyclopedia.vue
问题:食物百科全部列表页中,所有食物条目均未展示配图(.food-image和营养简介.nutrition-item
要求:
1. 阅读 food-encyclopedia.vue 的 template 中 .food-item 部分,找到 .food-image 的 :src 绑定和 .nutrition-item 的 v-for 数据来源。
2. 检查数据加载方法(如 loadFoodList 或 getFoodList API确认响应数据结构找出 image/imageUrl/img 字段和 nutrition/nutrients 字段。
3. 修复字段映射:确保 .food-image 的 :src 绑定到正确字段(如 item.imageUrl || item.image且 .nutrition-item 遍历正确的数组(如 item.nutrients || item.nutritions
4. 若图片字段为空,显示一个默认占位图(可使用已有的本地资源或灰色背景)。
请先阅读文件和相关 API 文件api/tool.js再执行修改。"
run_agent "BUG-008" "修复 BUG-008
文件msh_single_uniapp/pages/tool/post-detail.vue
问题:帖子详情页中,营养统计数据(.nutrition-stats-card不显示.stat-item 数组为空。
要求:
1. 阅读 post-detail.vue 的 data 中 postData.nutritionStats 的初始化和赋值逻辑。
2. 找到加载帖子详情的方法(如 loadPostDetail确认后端响应中是否有 nutritionStats 字段。
3. 若后端无该字段,根据帖子关联的饮食打卡数据(如 dietaryData / mealData计算蛋白质、热量、钾、磷等关键营养素填充 nutritionStats 数组格式:[{label:'蛋白质', value:'56g'}, ...]。
4. 若后端有该字段但字段名不一致,修复映射。
5. 营养统计卡片的显示条件v-if应改为nutritionStats.length > 0而不是依赖后端字段存在性。
请先阅读文件,再执行修改。"
log "[阶段3] 阶段3修复完成进行局部验证..."
run_partial_test "TC-B03|TC-B08"
# =============================================================================
# 阶段 4复杂修复~90 min
# BUG-004 食物详情 | BUG-005 AI 回复
# =============================================================================
phase "阶段 4 — 复杂修复BUG-004, BUG-005"
run_agent "BUG-004" "修复 BUG-004
文件msh_single_uniapp/pages/tool/food-detail.vue
问题:点击任意食物条目进入详情页,提示「数据加载失败」,页面无法正常展示食物名称、营养成分等信息。
要求:
1. 阅读 food-detail.vue 的 onLoad 和数据加载方法(如 loadFoodDetail
2. 打印或记录 API 请求参数id、name 等),确认传参是否正确。
3. 如果 API 调用失败catch 块中不要只 showToast应同时
a. 将 loadError 置为具体错误信息(用于调试),
b. 使用 defaultFoodData 填充页面,保证用户能看到基础界面而不是空白,
c. 在页面显示「当前数据来自缓存,可能不是最新」提示。
4. 确认 .food-name-overlay / .nutrient-card / .nutrition-row 等元素在 defaultFoodData 状态下也能正常渲染(数据不为空数组/空字符串)。
5. 如果问题是 API 路径或参数有误(如 id 传了 name 字段),同时修复调用参数。
请先阅读文件和 api/tool.js再执行修改。"
run_agent "BUG-005" "修复 BUG-005对话接口已改为 KieAI Gemini chat若仍有固定回复再修
文件msh_single_uniapp/pages/tool/ai-nutritionist.vue、api/models-api.js
后端msh_crmeb_22 的 /api/front/kieai/gemini/chatToolKieAIServiceImpl.geminiChat
要求:
1. 文本对话必须走 KieAI Gemini 大模型:调用 POST /api/front/kieai/gemini/chat请求体为 { messages: [{ role: 'user', content: 用户输入 }], stream: false }。
2. 前端从响应 data.choices[0].message.content 取回复并展示,不得使用 getAIResponse 等固定话术作为接口成功时的回复。
3. 若后端 ToolKieAIServiceImpl.geminiChat 或 buildGeminiRequestBody 中存在硬编码 prompt改为使用 request.getMessages() 透传用户内容。
4. 不修改 UI 布局,仅保证数据流:用户输入 → kieai/gemini/chat → 展示模型返回内容。"
log "[阶段4] 阶段4修复完成进行局部验证..."
run_partial_test "TC-B04|TC-B05"
# =============================================================================
# 阶段 5全量回归 + 报告
# =============================================================================
phase "阶段 5 — 全量回归测试 + 报告生成"
run_full_regression
END_TS=$(date +%s)
ELAPSED=$(( END_TS - START_TS ))
ELAPSED_MIN=$(( ELAPSED / 60 ))
log "────────────────────────────────────────────────"
log "全部阶段完成!总耗时:${ELAPSED_MIN} 分钟(${ELAPSED} 秒)"
log "回归报告:$WORKSPACE/tests/e2e/reports/index.html"
log "详细日志:$MAIN_LOG"
log "────────────────────────────────────────────────"

View File

@@ -0,0 +1,32 @@
已按 BUG-001 的两个子问题改好,修改总结如下。
---
**子问题 A — 打卡前积分不提前跳变**
- **逻辑**:在 `handleCheckin` 里,**只有**在 `setSignIntegral()` 成功返回之后才更新状态和积分。
- **具体**
- 先 `await setSignIntegral()`,不成功会进 `catch`,不会改 `currentPoints`。
- 成功后再 `this.todaySigned = true`,然后再用服务端数据更新 `currentPoints`。
- 全程没有在接口成功前修改 `currentPoints`,也没有任何前端本地 `+30`。
---
**子问题 B — 打卡成功后用服务端积分刷新**
- **接口确认**:打卡接口仍是 `setSignIntegral()``user/sign/integral`),对应后端 `/api/front/user/sign/integral`,即签到/打卡接口。
- **刷新积分**:打卡成功后用「用户信息」接口刷新积分:
- 先调 `getUserInfo()`(即 GET `/api/front/user`,即 user info 接口)。
- 从返回里取 `integral` 或 `points`,赋给 `currentPoints`(优先 `integral`)。
- 若 user info 里没有积分字段,再请求 `getUserPoints()``tool/points/info`),用其返回的 `totalPoints` / `points` / `availablePoints` 更新 `currentPoints`。
- **禁止硬编码**:积分只来自上述两个接口的返回值,没有写死 `+30`。
---
**代码改动要点**
1. 引入并调用 `getUserInfo`。
2. 在 `await setSignIntegral()` 成功后,先 `getUserInfo()`,用 `userRes.data.integral ?? userRes.data.points` 更新 `currentPoints`。
3. 仅当 user info 无积分时,再 `getUserPoints()` 并以其结果更新 `currentPoints`。
4. 注释中写明:不在 API 成功前改 `currentPoints`,且用 GET user info 刷新积分、禁止前端本地 +30。

View File

@@ -0,0 +1,35 @@
已按 BUG-001 的两个子问题改好,修改总结如下。
---
### 子问题 A — 打卡前积分不再提前跳变
- **原逻辑**:点击「立即打卡」后先执行 `uni.navigateTo`,再执行 `this.currentPoints += 30`,在接口返回前就改动了积分。
- **现逻辑**
- 在 `handleCheckin` 中**不再**在接口成功前修改 `currentPoints`。
- 仅当签到接口调用成功,并且用 `getUserPoints()` 拿到服务端最新积分后,才给 `currentPoints` 赋值(见下)。
---
### 子问题 B — 打卡成功后用服务端积分更新
- **原逻辑**:没有调用任何打卡/签到接口,只在前端写死 `currentPoints += 30`,后端没有参与积分变更。
- **现逻辑**
1. **调用签到接口**:使用现有接口 `GET /api/front/user/sign/integral``@/api/user.js` 中的 `setSignIntegral()`),与需求中的「/api/front/user/checkin 或类似接口」一致,用于触发后端签到与积分累加。
2. **成功后刷新积分**:签到成功后请求 `getUserPoints()`(对应后端 `GET .../tool/points/info`),用返回的积分更新页面。
3. **积分来源**
`currentPoints` 只从接口赋值:
`pointsRes.data.totalPoints ?? pointsRes.data.points ?? pointsRes.data.availablePoints ?? 0`
不再使用硬编码的 +30。
4. **错误与跳转**:若签到或拉取积分失败,会 `uni.showToast` 提示并 `return`,不跳转;只有成功后才 `uni.navigateTo('/pages/tool/checkin-publish')`。
---
### 其它修改
- **loadCheckinData**:积分取值改为优先使用服务端字段,与上面一致:
`currentPoints = pointsRes.data.totalPoints ?? pointsRes.data.points ?? 0`
这样列表和打卡页的积分都来自服务端。
需求里提到的「GET /api/front/user/info」在当前项目里没有对应接口用户积分接口是 `GET /api/front/tool/points/info``getUserPoints()`),因此用该接口的返回值刷新并赋值给 `currentPoints`。若后续后端提供 `/api/front/user/info` 并返回积分,只需在成功后再调该接口,用其返回的积分更新 `currentPoints` 即可。

View File

@@ -0,0 +1,16 @@
Connection lost, reconnecting...
Retry attempt 1...
已按 BUG-002 要求只改了 CSS未动任何 JS修改如下
**1. 未激活 Tab`.tab-item`**
- 在根级增加 `color: #9ca3af`,保证整项为灰色。
- 下划线保持 `border-bottom: 3px solid transparent`,无可见下划线。
**2. 激活 Tab`.tab-item.active`**
- `border-bottom: 3px solid #f97316`,橙色底线下划线(由原 6rpx 改为 3px
- `color: #f97316`,主色字色。
- `font-weight: 700`,加粗。
- 保留内部 `.tab-text`、`.tab-icon` 的 `#f97316` 与 `.tab-text` 的 `font-weight: 700`。
这样「健康概览」和「营养配餐」切换时,激活项为橙色加粗+橙色下划线,未激活为灰色且无下划线,对比更清晰。

View File

@@ -0,0 +1,15 @@
已按 BUG-002 要求只改了 CSS未动任何 JS。修改总结如下
### 1. 未激活 Tab`.tab-item`
- **字色**`.tab-text` 与 `.tab-icon` 均为 `#9ca3af`
- **下划线**:无实线,用 `border-bottom: 6rpx solid transparent` 占位,避免切换时高度抖动
- 其它:`height: 100%`、`min-height: 75rpx`,让底部边框落在 tab 栏最下方;`border-radius: 0`,改为下划线式 tab
### 2. 激活 Tab`.tab-item.active`
- **字重**`.tab-text` 设为 `font-weight: 700`
- **下划线**`border-bottom: 6rpx solid #f97316`(与要求 3px 等效,用 6rpx 保持与页面其它 rpx 一致)
- **字色**`.tab-text`、`.tab-icon` 均为 `color: #f97316`
- **背景**`background: transparent`,去掉原来的橙色块,只保留下划线样式
效果:当前选中的「健康概览」或「营养配餐」会以**加粗 + 橙色字 + 橙色底线下划线**显示,未选中的 Tab 为**灰色字、无下划线**,视觉区分更明显。

View File

@@ -0,0 +1,44 @@
BUG-003 已按你的要求修好,修改总结如下。
## 1. Template 与数据来源(确认)
- **`.food-image` 的 `:src`**:绑定为 `getFoodImage(item)`,由该方法统一取图并处理占位。
- **`.nutrition-item` 的 v-for**:遍历 `(item.nutrition || [])`,数据在 `normalizeFoodItem` 里统一整理成 `nutrition` 数组。
## 2. 后端数据结构api/tool.js + ToolFoodServiceImpl
- **列表/搜索** 返回每条为:`id`, `name`, `image`, `category`, `energy`, `protein`, `potassium`, `phosphorus`, `suitabilityLevel`。
没有 `nutrition`/`nutrients` 数组,营养来自扁平字段。
## 3. 具体修改
**1图片字段与占位**
- 在 **`getFoodImage(item)`** 中:
- 统一取 `item.imageUrl || item.image || item.img`。
- 若为相对路径(以 `/` 开头且非 `//`),用 `HTTP_REQUEST_URL` 拼成完整 URL。
- 无有效 URL 时返回已有的灰色 SVG 占位图(`defaultPlaceholder`)。
**2营养字段与列表**
- 在 **`normalizeFoodItem(item)`** 中:
- **图片**:同样做 image/imageUrl/img 兼容和相对路径补全,并写入 `image` / `imageUrl`,保证列表项有统一图源。
- **营养**
- 若有 `item.nutrition` 数组,按 `{ label, value, colorClass }` 规范化(兼容 `name`/`labelName`)。
- 否则若有 `item.nutrients`,同样规范化后当作 `nutrition`。
- 否则用后端扁平字段组装:能量、蛋白质、钾、磷、钠、钙(有值才加),保证列表有 `nutrition` 数组供 `.nutrition-item` 使用。
**3列表数据来源**
- `loadFoodList` 和搜索回调中,列表来源改为:
`result.data.list || (Array.isArray(result.data) ? result.data : [])`,再对每一项执行 `normalizeFoodItem`,避免因接口形态不同导致列表为空。
**4配置**
- 顶部增加:`import { HTTP_REQUEST_URL } from '@/config/app.js'`,用于相对路径补全。
效果上:
- `.food-image` 会正确显示后端返回的图(含相对路径补全),无图时显示灰色占位图。
- `.nutrition-item` 会正确展示营养简介(来自后端扁平字段或已有的 nutrition/nutrients 数组)。

View File

@@ -0,0 +1,39 @@
BUG-003 已按你的要求修好,修改总结如下。
---
## 1. 问题原因
- **后端列表接口**`getFoodList` / `searchFood`)返回的是:`id, name, image, energy, protein, suitabilityLevel`,没有 `nutrition` 数组,也没有 `category`、`potassium`、`phosphorus`。
- 模板里用的是 `item.image`、`item.nutrition`。当接口替换掉本地 mock 后,`item.nutrition` 为 `undefined`,营养简介不渲染;图片若为空也没有占位。
## 2. 修改内容
### 前端 `msh_single_uniapp/pages/tool/food-encyclopedia.vue`
**模板:**
- **`.food-image` 的 `:src`**:改为 `getFoodImage(item)`,内部使用 `item.imageUrl || item.image || item.img`,都没有时用占位图。
- **`.nutrition-item` 的 `v-for`**:改为 `v-for="(nut, idx) in (item.nutrition || [])"`,并给 `nut.colorClass` 加了默认 `'green'`,避免无数据或缺字段时报错。
- **分类与安全标签**`item.category` 为空时不渲染分类徽章(`v-if="item.category"`);安全标签增加 `item.safetyClass || 'safe'`、`item.safety || '—'` 兜底。
- **列表 `:key`**:改为 `item.id != null ? item.id : index`,便于接口数据稳定渲染。
**逻辑:**
- **`defaultPlaceholder`**:在 `data()` 里增加灰色 SVG 的 data URL作为无图时的占位图与 `.food-image-wrapper` 的灰色风格一致)。
- **`getFoodImage(item)`**:返回 `item.imageUrl || item.image || item.img`,若都为空则返回 `defaultPlaceholder`。
- **`normalizeFoodItem(item)`**:把接口数据统一成列表项结构:
- 图片:沿用 `image` / `imageUrl` / `img`(展示由 `getFoodImage` 处理)。
- 安全:若已有 `safety`/`safetyClass` 则保留,否则按 `suitabilityLevel` 映射suitable→放心吃/safemoderate→限量吃/limitedrestricted/forbidden→谨慎吃/careful
- 营养:若已有 `item.nutrition` 数组则直接用;否则用接口字段组装:能量、蛋白质、钾、磷(有值才加),格式为 `{ label, value, colorClass }`。
- **列表赋值**`loadFoodList` 和搜索分支里,对 `result.data.list` 做 `(result.data.list || []).map(item => this.normalizeFoodItem(item))` 再赋给 `foodList`,保证每条都有 `nutrition` 和可用的图片、安全、分类展示。
### 后端 `msh_crmeb_22/.../ToolFoodServiceImpl.java`
- 列表/搜索的 map 中增加:`category`、`potassium`、`phosphorus`,这样前端 `normalizeFoodItem` 能拼出更完整的营养简介,分类徽章也能显示。
效果:
- 配图:有图用接口的 `image`(或 `imageUrl`/`img`),无图用灰色占位图。
- 营养简介:接口有 `nutrition` 用接口的,没有则用能量/蛋白质/钾/磷自动组一条,`.nutrition-item` 始终有稳定数据源可遍历。

View File

@@ -0,0 +1,48 @@
BUG-004 已按你的要求修好,修改总结如下。
---
## 根因
- 后端详情接口 **只接受 Long 类型 id**`ToolController#getFoodDetail(Long id)`)。
- 列表里部分条目没有 `id``food-encyclopedia` 用 `item.name` 当 id 传,例如 `id=羊肉(熟)`,后端报 `NumberFormatException`,请求失败,页面只弹出「数据加载失败」且内容空白。
---
## 修改说明
### 1. `msh_single_uniapp/pages/tool/food-encyclopedia.vue`
- **只传数字 id**:仅在 `item.id` 为有效数字时拼进 `id` 参数;否则不传 `id`,避免把名称当 id 传给后端。
- **始终传 name**:跳转时带上 `name`(有则 `id=xxx&name=xxx`,无 id 则只传 `name=xxx`),详情页在失败或无 id 时用 name 展示。
### 2. `msh_single_uniapp/pages/tool/food-detail.vue`
- **入参与日志**
- 使用 `pageParams: { id, name }` 保存入参。
- `onLoad` 里打印:`console.log('[food-detail] onLoad params:', this.pageParams)`。
- `loadFoodData` 里打印:`console.log('[food-detail] getFoodDetail request param:', { id, type: typeof id })`。
- **只对数字 id 调接口**
- 仅当 `options.id` 能解析为数字时才调用 `loadFoodData(Number(options.id))`。
- 仅有 `options.name` 时:不请求接口,直接 `loadError = '暂无该食物详情数据,展示参考数据'`,用 `applyDefaultFoodData(false)` 填充,并把 `foodData.name` 设为解码后的 `options.name`。
- **API 失败时catch**
- **a.** `loadError = errMsg`(具体错误信息,便于调试)。
- **b.** `applyDefaultFoodData(false)`,用 `defaultFoodData` 填满页面,保证有名称、分类、关键营养、营养成分表等。
- **c.** 若有 `pageParams.name`,用其覆盖 `foodData.name`,避免显示默认「五谷香」。
- 再 `showToast('数据加载失败')`。
- `loadError` 有值时,页面顶部已有「当前数据来自缓存,可能不是最新」的提示(原有 `v-if="loadError"` 的 `.cache-notice`)。
- **保证列表能渲染**
- `applyDefaultFoodData` 中保证 `name/category/safetyTag/image` 有默认值,`keyNutrients`、`nutritionTable` 用 `defaultFoodData` 的非空数组拷贝,确保 `.food-name-overlay`、`.nutrient-card`、`.nutrition-row` 在默认数据下也能正常渲染。
- 成功拿到 API 数据时,用 `ensureNonEmptyArray(parseKeyNutrients(data), defaultFoodData.keyNutrients)` 等,避免解析出空数组导致列表为空。
---
效果简述:
- 列表点有**数字 id** 的条目:正常请求详情并展示;失败时用默认数据 + 入参 name若有+ 缓存提示 + Toast。
- 列表点**无 id、只有 name** 的条目:不请求接口,直接展示默认数据 + 该 name + 缓存提示,不再报错或空白。
- 控制台可看到 `onLoad params` 和 `getFoodDetail request param`,便于确认传参是否正确。

View File

@@ -0,0 +1,67 @@
BUG-004 已按你的要求修好,修改总结如下。
---
## 1. 根因API 参数错误)
- **后端**`ToolController.getFoodDetail(@PathVariable Long id)` 只接受 **Long 类型 id**。
- **前端**:食物百科列表跳转详情时用了 **`item.name`**(如「羊肉(熟)」「儿童营养饼干」),导致后端报 `NumberFormatException`,接口失败,页面只提示「数据加载失败」。
日志中可见:`Failed to convert value of type 'java.lang.String' to required type 'java.lang.Long'`.
---
## 2. 修改内容
### 2.1 `msh_single_uniapp/pages/tool/food-encyclopedia.vue`
- **跳转参数**:改为传 **`item.id`**(后端列表返回的 `id`),不再传 `item.name`。
- 若某条没有 `id`,则回退为 `item.name`,并对 name 做 `encodeURIComponent`,避免中文等字符在 URL 里出错。
```javascript
// 后端详情接口需要 Long 类型 id传 id 而非 name
const id = item.id != null ? item.id : item.name
url: `/pages/tool/food-detail?id=${encodeURIComponent(id)}`
```
这样从列表点进详情会带正确的 id接口可正常返回数据。
---
### 2.2 `msh_single_uniapp/pages/tool/food-detail.vue`
1. **请求参数打印**
- `onLoad``console.log('[food-detail] onLoad params:', { id: options.id, name: options.name })`
- `loadFoodData``console.log('[food-detail] getFoodDetail request param:', { id, type: typeof id })`
便于确认传参是否正确。
2. **`loadError` 与调试**
- 增加 `loadError: ''`。
- 在 `loadFoodData` 的 **catch** 里把 `loadError` 设为具体错误信息(`error.message || error.msg || error`),便于调试。
3. **失败时用默认数据 + 缓存提示**
- 抽取 **`applyDefaultFoodData(clearError)`**:用 `defaultFoodData` 填充 `foodData`,并对 `keyNutrients`、`nutritionTable` 做数组拷贝,保证不为空数组、能正常渲染 `.food-name-overlay` / `.nutrient-card` / `.nutrition-row`。
- 在 catch 里:先设置 `loadError`,再调用 `applyDefaultFoodData(false)`(不再清空 `loadError`),并保留原有 `showToast('数据加载失败')`。
- 无入参时仍走默认数据,此时调用 `applyDefaultFoodData()` 会清空 `loadError`。
4. **「当前数据来自缓存」提示**
- 当 `loadError` 有值时,在页面顶部展示提示条:「当前数据来自缓存,可能不是最新」,并增加 `.cache-notice` 样式。
5. **默认数据下的展示**
- `defaultFoodData` 已包含完整的 `name`、`category`、`safetyTag`、`image`、`keyNutrients`、`nutritionTable`(均为非空数组),应用 `applyDefaultFoodData` 后,`.food-name-overlay`、`.nutrient-card`、`.nutrition-row` 在默认数据下也能正常渲染。
6. **样式**
- 为 `.nutrition-dot` 增加 `&.high`,避免后端返回 `level: 'high'` 时缺样式。
---
## 3. 行为小结
| 场景 | 行为 |
|----------------|------|
| 列表点击条目 | 传 `item.id`,详情接口用 id 请求,正常展示该食物。 |
| 接口失败/异常 | 设置 `loadError`、用 `defaultFoodData` 填页面、Toast「数据加载失败」、顶部显示「当前数据来自缓存可能不是最新」名称/营养成分等仍可正常显示。 |
| 无 id/name 入参 | 直接使用默认数据,不显示缓存提示。 |
按当前逻辑,从食物百科点进详情应能正常看到名称和营养成分;若接口仍失败,会显示默认示例数据并提示「当前数据来自缓存,可能不是最新」,同时控制台和 `loadError` 可用来排查具体错误。

View File

@@ -0,0 +1,46 @@
BUG-005 已按你的要求修好,修改总结如下。
---
## 1. 前端 `msh_single_uniapp/pages/tool/ai-nutritionist.vue`
**已做修改:**
- **文本/多模态统一走 KieAI Gemini**
逻辑本来就是:`sendToAI` 里对 `text` 和 `multimodal` 都调用 `api.kieaiGeminiChat({ messages, stream: false })`,请求体为 `{ messages: [{ role: 'user', content: 用户输入或图+文 }], stream: false }`,未改。
- **成功时只展示接口返回内容**
- 回复**仅**从 `response.data.choices[0].message.content` 取值并展示。
- 已改为显式使用 `msgObj.content`(不再用 `choice.message.text` 等),并在注释中写明“仅使用接口返回的 content禁止固定话术”。
- **删除固定话术 `getAIResponse`**
- 已移除整个 `getAIResponse(question)` 方法(原关键词匹配的固定回复)。
- 成功时不再使用任何固定话术,只展示接口返回内容。
- **未动 UI**
仅改数据流与回复来源,布局和样式未改。
---
## 2. 前端 `msh_single_uniapp/api/models-api.js`
- **未改**
`kieaiGeminiChat` 已正确:
- POST `/api/front/kieai/gemini/chat`
- 请求体为 `{ messages, stream }`(你要求的 `{ messages: [{ role: 'user', content: 用户输入 }], stream: false }` 已满足)。
---
## 3. 后端 `msh_crmeb_22`ToolKieAIServiceImpl / buildGeminiRequestBody
- **未改**
已确认:
- `buildGeminiRequestBody` 完全用 `request.getMessages()` 透传,没有硬编码 prompt。
- `geminiChat` 只是用该 body 调 KieAI无额外固定内容。
因此后端无需修改。
---
当前数据流为:**用户输入 → POST /api/front/kieai/gemini/chatbody: messages + stream: false→ 前端从 `data.choices[0].message.content` 取回复并展示**,且成功时不再使用任何固定话术。

View File

@@ -0,0 +1,45 @@
BUG-005 相关修改已按你的要求做完,结论与改动如下。
---
## 1. 问题原因
- **前端**:创建对话后从 `response.data.chat` 取 `conversation_id`、`id`,但后端返回的是 `CozeBaseResponse.success(resp)``data` 就是 Coze 的 CreateChatResp没有嵌套的 `chat`,且多为 camelCase如 `conversationID`)。因此取不到会话/聊天 ID轮询失败请求在 catch 里走本地兜底 `getAIResponse(content)`,看起来就像“固定一段话”。
- **后端**Coze 实现里没有发现硬编码占位词,用户内容来自 `additionalMessages[0].content`。已做的修改是“无用户消息时不发请求”的防护。
---
## 2. 前端修改(`msh_single_uniapp/pages/tool/ai-nutritionist.vue`
- **创建对话响应解析**
- 兼容 `response.data.chat` 与 `response.data`。
- 兼容 `conversation_id` / `conversationID` / `conversationId` 和 `id`。
- 只有拿到 `conversationId` 和 `chatId` 后才调用 `pollChatStatus`,否则抛出明确错误。
- **轮询状态**
- 使用 `const chatObj = res.data.chat || res.data` 再取 `status`,避免因结构不同取不到状态。
- **消息列表**
- 支持 `res.data.messages` 或 `res.data` 为数组两种结构,用 `rawMessages` 统一处理,保证 AI 回复用 `role: 'ai', content: msg.content` 正确 push 到 `messageList`。
未改 UI只修了数据流`sendMessage` / `sendToAI` 里用的始终是 `inputText.trim()` → `content`,请求体已正确带用户输入。
---
## 3. 后端修改(`ToolCozeServiceImpl.java`
- **无用户消息时不调用 Coze**
- `chat()` 与 `chatStream()` 中:若 `buildMessages(request)` 为空,则不再调用 Coze直接返回错误如“请提供对话内容”避免“空请求”导致平台返回欢迎语等固定回复。
未改动 `ToolKieAIServiceImpl.java`:该文件是 Gemini 对话实现,营养师页走的是 Coze`/api/front/coze/chat`且其中没有硬编码占位词prompt 来自 `KieAIGeminiChatRequest.getMessages()`,因此按要求未修改。
---
## 4. 修改汇总
| 文件 | 修改内容 |
|------|----------|
| `ai-nutritionist.vue` | 修正 create-chat 响应解析data / data.chat、camel/snake轮询与消息列表兼容不同返回结构保证 AI 回复写入 `messageList`。 |
| `ToolCozeServiceImpl.java` | 在 `chat` / `chatStream` 中增加“无用户消息则不请求 Coze”的校验。 |
| `ToolKieAIServiceImpl.java` | 未修改(非本接口且无硬编码)。 |
按当前逻辑,用户输入会正确传到 Coze且能正确解析创建对话与轮询结果AI 的真实回复会显示在列表中,而不再被固定兜底文案覆盖。

View File

@@ -0,0 +1,19 @@
检查结果如下:
**1. `msh_single_uniapp/pages/tool_main/index.vue`**
- 第 66 行功能入口标题:`<view class="function-title">健康知识</view>` → 已是「健康知识」
- 第 126 行区块标题:`<text class="section-title">健康知识</text>` → 已是「健康知识」
**2. `msh_single_uniapp/pages/tool/nutrition-knowledge.vue`**
- 页面内没有设置导航栏标题的文案(没有「营养知识」或「健康知识」字符串)
- 该页导航栏标题由 `pages.json` 配置
**3. `pages.json`nutrition-knowledge 页配置)**
- 第 1018 行:`"navigationBarTitleText": "健康知识"` → 已是「健康知识」
因此,当前代码里:
- 主页「健康知识」区块和功能入口均为「健康知识」
- 营养知识页的导航栏标题在 `pages.json` 中也是「健康知识」
两处命名已统一为「健康知识」,**无需再改任何文件**。若你本地或某次构建里仍看到「营养知识」,请说明是在哪个页面、哪一行或哪个配置(例如是否改过 `pages.json`),我可以按那个位置再帮你对一下。

View File

@@ -0,0 +1,18 @@
BUG-006 已按「统一为健康知识」处理,修改如下。
**1. 确认的现有文案**
- **tool_main/index.vue**
- 下方区块标题第125行`健康知识`
- 四大入口里对应卡片标题第65行`营养知识`
- **营养知识页导航栏**
- 在 **pages.json** 里配置:`nutrition-knowledge` 的 `navigationBarTitleText` 为 `营养知识`
- **nutrition-knowledge.vue** 内没有设置导航栏标题的文案。
**2. 已做修改(仅文案,未改逻辑与样式)**
- **msh_single_uniapp/pages/tool_main/index.vue**
- 四大入口卡片标题:`营养知识` → `健康知识`第67行 `function-title`)。
- **msh_single_uniapp/pages.json**
- `nutrition-knowledge` 页的 `navigationBarTitleText``营养知识` → `健康知识`。
现在主页入口卡片、主页「健康知识」区块和营养知识页导航栏都统一为「健康知识」。`nutrition-knowledge.vue` 未改,因为导航栏标题是在 `pages.json` 里配置的。

View File

@@ -0,0 +1,37 @@
BUG-007 已按你的要求改完,修改总结如下:
### 1. **`loadKnowledgeList` 与 API 调用**
- **根因**:在 `rawList.map` 里用了 `formatKnowledgeTime(...)` 未加 `this.`,在 API 有数据时会报错(如 "formatKnowledgeTime is not a function"),进入 catch列表不会被赋值所以一直为空。
- **修改**:改为 `this.formatKnowledgeTime(...)`,确保在 map 里正确调用组件方法。
### 2. **请求参数 type**
- type 已与后端一致:`guide` / `article`(注释里也写明了)。
### 3. **API 失败时的处理**
- catch 里已有 `uni.showToast` 提示。
- 在 catch 中保证不把列表置为 undefined
`this.guideList = this.guideList ?? []`、`this.articleList = this.articleList ?? []`,出错时仍保持为数组。
### 4. **响应数据结构兼容**
- 增加对两种返回形式的兼容:
- `result.data.list` 为数组时用 `result.data.list`
- 若 `result.data` 本身是数组则用 `result.data`。
这样无论后端用分页对象还是直接数组,都能正确取到列表。
### 5. **onLoad 无 id 时的逻辑**
- 有 `id`:调用 `switchTab('articles')`,会触发 `loadKnowledgeList` 拉取科普文章列表。
- 无 `id`:显式保持 `currentTab = 'nutrients'`;用户切到「饮食指南」或「科普文章」时由 `switchTab` 触发 `loadKnowledgeList`,与需求一致。
### 6. **详情页跳转 `goToDetail`**
- 已有「无 id 不跳转」的逻辑,本次只做了小加固:
- 若 `!item` 也先提示「暂无详情」再 return
- 仅当 `knowledgeId` 或 `id` 存在且有效时才 `navigateTo`,否则一律提示「暂无详情」。
当前无新的 lint 报错。若后端库里没有 type 为 `guide` / `article` 的数据,列表仍会为空,但会正常显示「暂无饮食指南数据」/「暂无科普文章数据」;若详情页打开仍空白,需要再确认详情页是否应调 `tool/knowledge/detail/:id` 而不是文章接口。

View File

@@ -0,0 +1,26 @@
BUG-007 已按你的要求修好,修改总结如下:
### 1. **`loadKnowledgeList` 与 API**
- **请求参数**`type` 已按 tab 传 `guide` / `article`(与后端 `type` 一致),并显式用 `typeParam` 传参。
- **响应处理**:用 `Array.isArray(result.data.list)` 判断,只把合法数组赋给列表,否则用 `[]`,避免赋成 `undefined`。
- **列表赋值**:用 `currentTab === 'articles'` 分支给 `articleList` 赋值与「科普文章」tab 一致。
### 2. **错误处理**
- 在 `catch` 里增加 `uni.showToast`,文案为 `error.message` / `error.msg` 或「加载列表失败」。
- 在 `catch` 中**不**对 `guideList` / `articleList` 赋值,保持为初始的 `[]`,避免变成 `undefined`。
### 3. **onLoad 与无 id 时的列表**
- 无 `id` 时不再在 onLoad 里调用 `loadKnowledgeList()`(当前 tab 为 nutrients调用也会直接 return
- 列表只在「切换 tab」时加载`switchTab('guide')` / `switchTab('articles')` 内会调用 `loadKnowledgeList()`,从而加载对应 guide/article 列表。
### 4. **详情跳转 `goToDetail`**
- 先取 `id = item.knowledgeId ?? item.id`,再判断 `id === undefined || id === null || id === ''`。
- 无有效 id 时:`uni.showToast({ title: '暂无详情', icon: 'none' })` 并 return不跳转。
- 有 id 时才 `navigateTo` 到 `/pages/news/news_details/index?id=${id}`。
### 5. **列表展示与后端字段**
- 后端返回 `summary`、`viewCount`、`publishedAt` 等,模板用的是 `desc`、`time`、`views`、`icon`。
- 在赋值前对列表做了一层映射:`desc` ← summary、`time` ← 格式化 `publishedAt`/`createdAt`、`views` ← viewCount、`icon` 默认 `'📄'`,并增加了 `formatKnowledgeTime` 做日期格式化。
按当前逻辑:切换「饮食指南」或「科普文章」会请求 `type=guide` / `type=article` 并正确赋给 `guideList`/`articleList`;请求失败会 toast 且列表保持为 `[]`;只有存在 `knowledgeId` 或 `id` 时才跳转详情,否则提示「暂无详情」。

View File

@@ -0,0 +1,42 @@
BUG-008 已按你的要求修好,修改总结如下。
---
## 1. 逻辑与数据流(已确认)
- **`postData.nutritionStats`**:在 `data()` 里初始化为 `[]`;在 `formatPostData()` 里由 `buildNutritionStatsFromDetailData(data)` 得到并赋值。
- **加载入口**`loadPostData(id)` 调 `getCommunityDetail(id)`,再用返回的 `data` 做 `formatPostData(data)`。
- **后端社区详情**:返回 `nutritionDataJson`、`checkInRecordId`**没有** `nutritionStats` 数组。
`buildNutritionStatsFromDetailData` 已支持:
- `nutritionStats` / `nutrition_stats`
- `nutritionDataJson` / `nutrition_data_json`(解析出 calories、protein、potassium、phosphorus
- `dietaryData` / `mealData` / `dietary_data` / `meal_data`
- **打卡详情**:当详情里没有营养数据但有 `checkInRecordId` 时,会再调 `getCheckinDetail(checkInRecordId)`;返回里有 `actualEnergy`、`actualProtein`,已用 `buildNutritionStatsFromCheckinDetail` 转成 `[{label, value}, ...]` 并写回 `postData.nutritionStats`。
---
## 2. 实际修改内容
**1用打卡详情补全营养数据并等待完成**
- 原来:`fillNutritionStatsFromCheckin(data.checkInRecordId)` 未 `await`,页面先渲染,营养卡常为空。
- 现在:在「无营养数据 + 有 checkInRecordId」时改为 **`await this.fillNutritionStatsFromCheckin(data.checkInRecordId)`**,在加载结束前就拿到打卡详情并填好 `nutritionStats`,营养卡能稳定显示。
**2显示条件改为“只看数组长度”且做防御**
- 要求v-if 用 `nutritionStats.length > 0`,不依赖后端是否返回某字段。
- 实现:
- 增加计算属性 **`nutritionStatsLength`**
`(postData && postData.nutritionStats)` 且为数组时返回 `length`,否则为 `0`,避免 `nutritionStats` 未定义时报错。
- 营养统计卡片的 v-if 改为:**`v-if="nutritionStatsLength > 0"`**。
这样显示只依赖「当前页面的 nutritionStats 数组是否有项」,与后端字段名无关。
---
## 3. 未改动的部分(已符合需求)
- 后端无 `nutritionStats` 时,已通过「打卡详情」的 `actualEnergy`、`actualProtein` 等计算并填充 `nutritionStats`(格式 `[{label:'蛋白质', value:'56g'}, ...]`),无需再改。
- 后端有 `nutritionDataJson` 等不同命名时,`buildNutritionStatsFromDetailData` 里已有多种字段名和 `dietaryData`/`mealData` 的兼容,无需额外映射修改。
当前逻辑是:先尝试从详情/`nutritionDataJson`/饮食数据得到 `nutritionStats`;若仍为空且存在 `checkInRecordId`,则 **await** 拉取打卡详情并据此填充;展示上只用 **`nutritionStatsLength > 0`** 控制营养卡显示。

View File

@@ -0,0 +1,39 @@
BUG-008 已按你的要求修好,修改总结如下。
### 1. 阅读结果简述
- **postData.nutritionStats**:在 `data()` 里初始化为 `[]`,在 `formatPostData` 里只从 `data.nutritionDataJson` 解析并赋值;若后端没给或字段名不同,就会一直是空数组。
- **加载逻辑**`loadPostData` 调 `getCommunityDetail(id)`,用返回的 `data` 调用 `formatPostData(data)`。后端确实会返回 `nutritionDataJson`,但可能为空或字段名不一致(如 snake_case
### 2. 已做修改
**1统一从详情数据里构建营养统计**
- 新增 **`buildNutritionStatsFromDetailData(data)`**,按优先级从接口数据里生成 `nutritionStats`
- 若存在 **`nutritionStats` / `nutrition_stats`** 数组,直接规范成 `[{label, value}, ...]` 使用。
- 否则解析 **`nutritionDataJson` / `nutrition_data_json`**,按原有逻辑生成热量、蛋白质、钾、磷四项。
- 否则从 **`dietaryData` / `mealData` / `dietary_data` / `meal_data`** 等对象里取 calories/energy、protein、potassium、phosphorus及常见别名生成同样四项。
**2后端没有营养数据时用打卡数据补充**
- 新增 **`buildNutritionStatsFromCheckinDetail(detail)`**:根据打卡详情里的 `actualEnergy`、`actualProtein` 等生成 `[{label, value}, ...]`,钾/磷无则显示 `-`。
- 新增 **`fillNutritionStatsFromCheckin(checkInRecordId)`**:请求 `getCheckinDetail(checkInRecordId)`,用上面方法得到 `nutritionStats` 并赋给 `postData.nutritionStats`。
- 在 **`loadPostData`** 里,在 `formatPostData(data)` 之后:若当前 **`postData.nutritionStats.length === 0`** 且 **`data.checkInRecordId`** 存在,则调用 **`fillNutritionStatsFromCheckin(data.checkInRecordId)`**,用打卡数据补全营养统计(不阻塞评论、推荐等加载)。
**3展示条件只依赖数组长度**
- 营养统计卡片的 **v-if** 已改为只依赖是否有数据:
- 原:`v-if="postData.nutritionStats && postData.nutritionStats.length > 0"`
- 现:`v-if="postData.nutritionStats.length > 0"`
`postData.nutritionStats` 初始化为 `[]`,不会为 undefined。
**4接口引用**
- 在 `@/api/tool.js` 的 import 中增加了 **`getCheckinDetail`**,用于在无详情营养数据时拉取打卡详情并计算营养统计。
效果总结:
- 后端有 `nutritionDataJson` 或其它营养相关字段(含不同命名)时,会正确解析并显示。
- 后端没有营养数据但有关联打卡记录时,会用打卡详情中的热量、蛋白质等补全并显示。
- 卡片是否展示只由 **`postData.nutritionStats.length > 0`** 决定,不再依赖“后端某字段是否存在”。

View File

@@ -0,0 +1,24 @@
已按 BUG-009 要求处理,修改总结如下。
**1. Template 中的 Tab 与类型标签**
- **Tab 文案**:四个 Tab 的展示文字已是中文(推荐、最新、关注、热门),无需改。
- **Tab 内部值**`currentTab` 仍为 `recommend` / `latest` / `follow` / `hot`,用于 `switchTab` 和接口参数 `tab: this.currentTab`。按要求不改动接口逻辑,因此这些值保持英文,仅界面显示为中文。
- **帖子类型标签**`.type-tag` 和 `.meal-tag` 的文案都来自 `getMealTypeLabel(item.mealType)`,没有在模板里写死英文。
**2. 数据中的类型字段与 label 映射**
- `getMealTypeLabel` 中已有英文 → 中文映射breakfast→早餐、lunch→午餐、dinner→晚餐、snack→加餐、share→分享、checkin→打卡。
- 原先在未知类型时会 `return mealType`,可能把后端英文直接展示出来。
- **修改**:未知类型时的 fallback 从 `mealType` 改为 `'分享'`,保证所有展示的 label 都是中文。
**具体改动(仅一处)**
- 在 `getMealTypeLabel` 中,将
`return map[lower] != null ? map[lower] : mealType`
改为
`return map[lower] != null ? map[lower] : '分享'`
这样任意未在 map 中的 `mealType`(包括英文或其它值)在界面上都会显示为「分享」,不会出现英文。
路由与接口调用未做任何修改。

View File

@@ -0,0 +1,20 @@
BUG-009 已按你的要求改完,修改总结如下:
## 1. Template 检查结果
- **Tab 文案**:四个 Tab 的展示文字早就是中文(推荐、最新、关注、热门),未改。
- **Tab 内部值**`currentTab` 仍为 `recommend` / `latest` / `follow` / `hot`,用于 `switchTab()` 和接口参数 `tab`,按「不改动接口调用等逻辑」保留。
- **帖子类型标签**:两处原先直接显示 `{{ item.mealType }}`,若接口返回英文(如 `breakfast`)会显示英文,已改为通过映射显示中文。
## 2. 具体修改
1. **类型标签展示改为用“中文 label”**
- 有图:`<view class="meal-tag">{{ getMealTypeLabel(item.mealType) }}</view>`
- 无图:`<view class="type-tag">{{ getMealTypeLabel(item.mealType) }}</view>`
2. **新增 `getMealTypeLabel(mealType)` 方法**
- 英文 → 中文映射:`breakfast`→早餐、`lunch`→午餐、`dinner`→晚餐、`snack`→加餐、`share`→分享、`checkin`→打卡。
- 若 `mealType` 已是中文或未知,则原样返回;空则返回「分享」。
这样 Tab 展示仍是中文,帖子类型在界面上也统一为中文;`currentTab`、接口入参和请求逻辑均未改动。

View File

@@ -0,0 +1,8 @@
═══════════════════════════════════════════════
[2026-03-02 16:43:17] ▶ 阶段 0 — 基线确认(运行回归测试,记录当前失败情况)
═══════════════════════════════════════════════
Running 10 tests using 1 worker
✘ 1 [mobile-chrome] tests/e2e/bug-regression.spec.ts:114:5 TC-B01a 打卡前积分无提前跳变 (31.5s)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff