|
|
@@ -0,0 +1,73 @@
|
|
|
+# 首页每日任务卡未关闭 Bug 修复报告
|
|
|
+
|
|
|
+## 1. 根因确认
|
|
|
+
|
|
|
+| 根因项 | 确认结果 | 说明 |
|
|
|
+|---|---|---|
|
|
|
+| **A. 首页仅跳转,不标记完成** | 确认 | `pages/index-home/index.vue` `quickCompleteTask()` 对联动任务(actionType 含 diet/sleep/exercise)仅调用 `navigateByLinkedAction(task)` 通过 `?taskId=` 跳转打卡页,未在跳转前后调用 `completeTaskApi` 将每日任务标记完成。 |
|
|
|
+| **B. 打卡页成功分支未调 completeTaskApi** | 确认 | `sleep-checkin.vue` / `exercise-checkin.vue` / `diet-checkin.vue` 的打卡成功分支仅 `showToast` + `uni.navigateBack()`,未调用 `completeTaskApi(taskId, memberId, '')` 把对应每日任务标记完成。 |
|
|
|
+| **C. 后端任务回写对“每日进度型”任务不足以完成** | 独立核实 | `HealthSleepController.create` / `HealthExerciseController.create` / `HealthMealController.create` 的任务回写挂钩:若关联任务为 `isDailyProgress=1`(每日进度型,taskKey=SLEEP/DIET/EXERCISE)仅调用 `taskService.updateProgress(userId, memberId, taskKey, 1)` 累计进度,**若目标进度>1 不会在一次打卡后直接置 completed**;非进度型才会直接 `completeTask`。故前端必须显式调 `completeTaskApi` 保证每日任务卡片能被关闭。 |
|
|
|
+| **D. 后端完成接口可用** | 独立核实 | `TaskController.completeTask` (`POST /api/tasks/{id}/complete`),接收 `memberId`(缺省则取 JWT 的 `currentMemberId`),内部 `taskService.completeTask(id, memberId, photoUrl, content, contentType)` 将任务置为 completed。能按 taskId 正确完成任务,无需改后端。 |
|
|
|
+| **E. 首页返回刷新** | 部分已有/已补强 | `index-home` `onShow` → `_refreshOnShow()` → `loadFamilyData()` → `.then()` 内 `loadTodayTasks()` 可在返回时刷新任务列表;但 `loadFamilyData` 的 `.catch()` 路径**不**调用 `loadTodayTasks()`,若获取成员列表失败则任务不刷新、卡片残留。已补强:catch 路径亦调用 `loadTodayTasks()`。 |
|
|
|
+
|
|
|
+## 2. 改动文件清单与每处改动说明
|
|
|
+
|
|
|
+### 2.1 `cfc-frontend/pages/index-home/index.vue`
|
|
|
+- **位置**:`loadFamilyData()` 方法的 `.catch()` 分支(获取家庭成员列表失败路径)。
|
|
|
+- **改动**:在 catch 块末追加 `self.loadTodayTasks()`。
|
|
|
+- **说明**:确保即使获取成员列表失败,从打卡页通过 `navigateBack` 返回首页后仍能刷新今日任务,已完成的任务卡片能消失。正常 `.then()` 路径已通过 `loadFamilyData` → `loadTodayTasks()` 刷新,未重复请求(catch 与 then 互斥,不构成重复调用)。
|
|
|
+
|
|
|
+### 2.2 `cfc-frontend/pages/growth/sleep-checkin.vue`
|
|
|
+- **导入**:新增 `import { completeTask as completeTaskApi } from '../../utils/api.js'`(与 `index-home` 保持一致的别名)。
|
|
|
+- **改动**:在 `doCheckin()` 成功分支(`res.data.code === 200`)中,`uni.navigateBack()` **之前** 新增:若 `self.taskId` 存在,则调用 `completeTaskApi(self.taskId, self.memberId || null, '')` 并 `.catch()` 吞异常(不阻断返回)。
|
|
|
+- **说明**:睡眠打卡完成后显式将关联每日任务置 completed。
|
|
|
+
|
|
|
+### 2.3 `cfc-frontend/pages/growth/exercise-checkin.vue`
|
|
|
+- **导入**:`import { sttTranscribe } from '../../utils/api.js'` 改为 `import { sttTranscribe, completeTask as completeTaskApi } from '../../utils/api.js'`。
|
|
|
+- **改动**:在 `submitCheckin()` 成功分支(`res.data.code === 200`)中,`uni.navigateBack()` 之前 新增 `if (self.taskId) completeTaskApi(self.taskId, self.memberId || null, '').catch(...)`。
|
|
|
+- **说明**:运动打卡完成后显式将关联每日任务置 completed。
|
|
|
+
|
|
|
+### 2.4 `cfc-frontend/pages/growth/diet-checkin.vue`
|
|
|
+- **导入**:`import { sttTranscribe, recognizeDietFood, getAllQuotaStatus } from '../../utils/api.js'` 改为追加 `completeTask as completeTaskApi`。
|
|
|
+- **改动**:在 `submitCheckin()` 成功分支(`res.data.code === 200`)中,`uni.navigateBack()` 之前 新增 `if (self.taskId) completeTaskApi(self.taskId, self.memberId || null, '').catch(...)`。
|
|
|
+- **说明**:饮食打卡完成后显式将关联每日任务置 completed。(该文件已有的 BASIC 会员配额提示等改动为预先存在的未提交工作,本次未动。)
|
|
|
+
|
|
|
+> memberId 语义:取页面已有的 `this.memberId`(打卡所属成员,优先 currentChildId/currentMemberId/userId),若为 null 则传 `null`,由后端 `TaskController` 按登录用户 `currentMemberId` 处理,与 `index-home` 的 `completeTaskApi(task.id, task.familyMemberId || null, '')` 用法一致。
|
|
|
+
|
|
|
+## 3. completeTaskApi 来源与签名
|
|
|
+
|
|
|
+- **来源**:`cfc-frontend/utils/api.js`,导出函数 `completeTask`。
|
|
|
+- **签名**:
|
|
|
+ ```js
|
|
|
+ export const completeTask = (taskId, memberId, photoUrl, content, contentType) => {
|
|
|
+ return request(`/api/tasks/${taskId}/complete`, 'POST', { memberId, photoUrl, content, contentType })
|
|
|
+ }
|
|
|
+ ```
|
|
|
+- **index-home 已有用法**:`import { ... completeTask as completeTaskApi ... } from '../../utils/api.js'`,调用 `completeTaskApi(task.id, memberId, '')`。
|
|
|
+- **三个打卡页**:统一采用相同导入路径 `../../utils/api.js` 及别名 `completeTaskApi`,调用形态 `completeTaskApi(taskId, memberId || null, '')`。
|
|
|
+- **后端接口**:`TaskController.completeTask`,`POST /api/tasks/{id}/complete`,`memberId` 缺省取 JWT `currentMemberId`;内部 `taskService.completeTask(...)` 置任务为 completed。已核验能按 taskId 正确完成任务,**未改动后端**。
|
|
|
+
|
|
|
+## 4. 是否改动后端
|
|
|
+
|
|
|
+**未改动后端。** 修改仅限 `cfc-frontend` 下 4 个 `.vue` 文件。后端 `TaskController.completeTask` (`/api/tasks/{id}/complete`) 已具备按 `taskId` 将任务置为 completed 的能力,且健康打卡三 controller 的任务回写对“每日进度型”任务仅做进度累计不一定直接完成,故前端显式调 `completeTaskApi` 即可修复,无需后端改动。未触碰 BASIC 会员相关后端文件,未手改 `dist/unpackage`。
|
|
|
+
|
|
|
+## 5. 代码自审结论
|
|
|
+
|
|
|
+- **语法/编译**:`node scripts/verify-vue-compile.js` 对 4 个变更文件全部 PASS(模板/样式/脚本均通过)。
|
|
|
+- **重复 API 调用审计**:`node scripts/audit-duplicate-api-calls.js` 退出码 0,0 个 ERROR,仅项目预存在的 WARN。本次改动未引入新的 LIFECYCLE_DOUBLE(未在 onLoad/onShow 直接重复调用 `loadTodayTasks`,仅在 `loadFamilyData` 的互斥 catch 路径补强)亦未引入新的 DUPLICATE_WRAPPER(复用已有 `completeTask` 导出,未新增封装)。
|
|
|
+- **小程序反模式**:未使用可选链 `?.`、未在 `:key` 使用表达式、未使用 `new Date(string)`/toLocale*String、未使用 `uni.getSystemInfoSync()`,符合 `cfc-frontend/AGENTS.md` 与 `AGENTS.md` 规范。
|
|
|
+- **Vue 2 Options API**:所有新增逻辑均写在 `methods` 内,使用 `var`/function 表达式,与现有风格一致。
|
|
|
+- **导航逻辑**:`completeTaskApi` 调用置于打卡成功分支、`uni.navigateBack()` 之前,异常通过 `.catch()` 吞掉,不阻断页面返回;若页面无 `taskId`(非首页联动入口打卡),则不调用,行为与之前一致。
|
|
|
+
|
|
|
+## 6. 建议的回归验证步骤(HBuilderX 构建后手测)
|
|
|
+
|
|
|
+在 HBuilderX 中重新打包 `cfc-frontend`,用微信开发者工具运行,登录后按以下步骤验证:
|
|
|
+
|
|
|
+1. **首页「我的任务」**:确认快捷列表中出现睡眠/运动/饮食三类联动任务卡片(“去完成”按钮)。
|
|
|
+2. **睡眠打卡**:点睡眠卡片“去完成” → 进入 `pages/growth/sleep-checkin.vue` → 填写入睡/醒来/质量 → 点“完成打卡” → 返回首页 → **睡眠任务卡片应消失**。
|
|
|
+3. **运动打卡**:点运动卡片“去完成” → 进入 `pages/growth/exercise-checkin.vue` → 填写类型/时长等 → 点“✓ 完成打卡” → 返回首页 → **运动任务卡片应消失**。
|
|
|
+4. **饮食打卡**:点饮食卡片“去完成” → 进入 `pages/growth/diet-checkin.vue` → 选择餐次并填内容 → 点“✓ 完成打卡” → 返回首页 → **饮食任务卡片应消失**。
|
|
|
+5. **非联动打卡兜底**:直接在打卡页(不带 `?taskId=`)完成打卡并返回,应正常跳转、不报错、不影响其他页面。
|
|
|
+6. **异常路径**:断网或后端 `completeTask` 返回非 200 时,打卡页仍应正常 `showToast` 并 `navigateBack` 返回首页(`completeTaskApi` 失败不阻断返回)。
|
|
|
+
|
|
|
+预期结果:完成任意一类联动打卡并返回首页后,对应任务卡片立即消失;未完成的联动任务卡片保持显示。
|