verify-task-checkin-fix.md 10 KB

「打卡 4 问题修复」独立完成情况核验报告

  • 核验人:verify-task-checkin-fix(独立复核会话)
  • HEAD:707ea09ad(修复改动全部在工作区未提交)
  • 方式:只读审查 + 静态编译校验 + 数据流推演(未修改任何代码、未提交)
  • 结论:7 项全部 PASS,修复就位且有效。

核验结果总览

# 核验项 结论
1 问题4 sleep-checkin.vue formatDate 修复(模板/方法/import 配对 + 编译) PASS
2 问题1/3 根因修复(三个健康 Controller 成员归属修正 + 打卡页 memberId 解析 + index-home 传参) PASS
3 问题2 tasks.vue 成员 chips/切换/权限提示 PASS
4 静默失败修复(TaskController null 分支 + 前端调用点提示) PASS
5 编译(后端 mvn / 前端 node --check / 模板编译) PASS
6 数据流推演(睡眠打卡端到端闭环) PASS
7 改动范围(9 个本需求文件 + 历史遗留项隔离) PASS
附 API 错误传播形态补充确认(决定静默失败是否真的可见) PASS

1. 问题4:sleep-checkin.vue formatDate 崩溃修复 —— PASS

  • 模板调用点:cfc-frontend/pages/growth/sleep-checkin.vue:147 — {{ formatDate(item.createdAt) }}
  • methods 暴露:cfc-frontend/pages/growth/sleep-checkin.vue:321 — formatDate: function(d) { return formatDateUtil(d) }
  • import 配对:cfc-frontend/pages/growth/sleep-checkin.vue:161 — import { formatDate as formatDateUtil } from '../../utils/format.js'
  • 配对逻辑闭合:import 用别名 formatDateUtil(不再与方法名冲突),methods 的 formatDate 包装导出,三者形成 模板 → method → import 别名 的正确链条。这也解释了根因:修复前模板调用 formatDate 会在原型链上踩中 util 导出,修复前版本缺少 methods 暴露导致 _vm.formatDate is not a function。
  • 编译证据:提取 <script> 块 node --check 全部 exit 0(sleep/diet/exercise/tasks 4 个文件);vue-template-compiler 编译 5 个文件模板均 0 error(filter 掉 "side-effects/ multiple nodes" 通用性提示后为 0)。

2. 问题1/3:睡眠打卡后任务不消失 / 家庭任务数不变 —— PASS

后端归属修正(三个 Controller 均就位,条件完全一致)

  • cfc-backend/src/main/java/com/etotem/cfc/controller/HealthSleepController.java:75-80
  • cfc-backend/src/main/java/com/etotem/cfc/controller/HealthMealController.java:257-262(同 diff 位置)
  • cfc-backend/src/main/java/com/etotem/cfc/controller/HealthExerciseController.java:97-102(同 diff 位置)

条件判定完整:linkedTask != null(外层 selectById 判断)+ record.getTaskId() != null + linkedTask.getFamilyMemberId() != null + record.getMemberId() != null + !familyMemberId.equals(memberId) 五重条件齐备才 setMemberId,置于 taskService.updateCheckinTaskProgress / completeTask 调用之前,后续回写天然使用修正后的 memberId。

打卡页 memberId 解析(currentChildId/userId 已移除)

  • sleep-checkin.vue:303 / diet-checkin.vue:222 / exercise-checkin.vue:216 三处均改为: this.memberId = options.memberId || uni.getStorageSync('currentMemberId') || null grep 确认三文件已无 currentChildId、userId 兜底残留。

index-home 跳转 URL 带 memberId

  • cfc-frontend/pages/index-home/index.vue:1583-1592:diet/sleep/exercise 三个分支均追加 &memberId=(task.familyMemberId || task.executorId || '')。
  • 字段名真实性:cfc-backend/.../entity/Task.java:20(familyMemberId) 与 :28(executorId) 确认存在;同文件 index.vue:1541 已有既有用法 var memberId = (task && task.familyMemberId) || null 佐证字段名非臆造。

3. 问题2:tasks.vue 家庭成员任务查看/切换/权限 —— PASS

  • 成员 chips:tasks.vue:11-14 v-for familyMembers + :key="member.id"(无表达式 key)+ @click="selectMember(member.id)";data selectedMemberId: null(tasks.vue:266)。
  • selectMember:tasks.vue:326-330 — 设 selectedMemberId、activeTab = 'today'、调 loadTodayTasks(this.selectedMemberId)。
  • loadTodayTasks:tasks.vue:377-386 — arguments.length > 0 ? memberId : this.selectedMemberId,显式传参生效;不传参(switchTab:345 / loadInitialData:358)回落到 selectedMemberId 再兜底 JWT。api.js:705-714 确认 body.memberId 仅在非空时写入。
  • 权限适配:completeTask(tasks.vue:415-418 isOwnTask 提示 + 421-425 res.code!==200 toast + 430-432 data 兜底)+ handleTaskAction(tasks.vue:459-462)+ submitWithInput(tasks.vue:555-559 + 562-573)三处入口均有 isOwnTask 拦截;非本人任务仍渲染(列表按 targetMemberId 全量返回),仅点击按钮才提示「只能完成自己的任务」——符合"可看不可操作"要求。
  • 我创建的 tab:loadCreatedTasks(tasks.vue:387-399)走 familyWide + creatorId 过滤,逻辑未动;pending tab(368-376)未变;isOwnTask 在 selfMemberId == null 或 task.familyMemberId == null 时 return true(338行注释即说明此兜底防呆语义),未破坏既有 tab。
  • 模板反模式扫描:新模板无 ?.、无 toLocaleString、:key 无运算符表达式。

4. 静默失败修复(TaskController + 前端提示链)—— PASS

  • 后端:TaskController.java:118-140 — result != null → Result.success(result);result 为 null 时(service 585-593 行已确认 null 语义:任务不存在 / !Objects.equals(task.getFamilyMemberId(), memberId) / 已完成或待审核 / 需前置校验等)返回 Result.error("任务不存在、无权完成或状态已变更")。原 Result.success(null) 已替换。
  • 前端调用点(错误可见性):
    • index-home/index.vue:1542-1557 quickCompleteTask — res.code === 200 分支保留三态 toast,else 分支 toast res.message,.catch toast「完成失败,请重试」。
    • tasks.vue:421-425 / tasks.vue:562-565 completeTask/submitWithInput — res.code !== 200 → toast res.message,catch 内 toast。
    • API 错误传播确认(api.js:320-328):非 200 业务码 → wrapper 自动 uni.showToast(res.data.message) + reject(res.data)。即后端 Result.error(code=500)必然经 toast 或 catch 落到用户可见,静默失败链条已断。
    • 备注(非缺陷):因 wrapper 对非200走 reject,tasks.vue 内 res.code !== 200 分支实际主要覆盖 5001(noFamily) 等 resolve-型错误码;属双保险,无害。

5. 编译验证 —— PASS

  • 后端:cd cfc-backend && mvn clean compile -DskipTests -q → MVN_EXIT=0,无 ERROR。
  • 前端:4 个涉及页面脚本块 node --check 全 exit 0;vue-template-compiler 编译 5 个文件(睡眠/饮食/运动打卡页 + tasks + index-home)模板 0 error。

6. 数据流推演(家长账号点睡眠打卡端到端)—— PASS,闭环

推演路径(家长 storage 残留 currentChildId 场景):

  1. 首页 quickCompleteTask(index.vue:1528-1537)判定 actionType=sleep → navigateByLinkedAction → URL sleep-checkin?taskId=X&memberId=<本人成员ID>(1587-1589,task.familyMemberId 优先,已证字段存在)。
  2. sleep-checkin.vue:303 onLoad:options.memberId || currentMemberId || null → 取 URL 中的本人成员ID,currentChildId 失效。
  3. 提交 → HealthSleepController.create(59-118):record.memberId=本人 → taskId 非空 → linkedTask 非空且 familyMemberId==本人 → 归属一致,无需修正 → isDailyProgress==1 → updateCheckinTaskProgress(taskId, fm.getUserId(), fm.getId(), 1)。
  4. TaskService.java:1784-1848:成员校验 member.getUserId()==userId ✅、task.getFamilyMemberId().equals(familyMemberId) ✅ → 进度累加 → 达标调 awardTaskCompletionRewards 置 completed。
  5. 返回首页 onShow → _refreshOnShow → loadTodayTasks(1122-1140):个人列表该任务 status=completed 不再 pending → 卡片消失;familyTodoCount = countFamilyPendingInProgress(...)(1149-1157,只数 pending/in_progress)→ 家庭任务数 -1。

根因闭环说明:修复前若 storage 有 currentChildId,打卡页 memberId 取孩子成员,record 与 task 归属错配,updateCheckinTaskProgress 在 1784-1798 校验被拒返回(任务不完成、卡片不消失、计数不减);修复后 URL 传 memberId + 后端归属修正双保险,校验通过,行为符合预期。

一次性(非每日进度)睡眠任务同样闭合:走 87-95 completeTask 分支,memberId 已按 linkedTask 修正,与 service:587 的 Objects.equals(task.getFamilyMemberId(), memberId) 对齐,可正常完成。

7. 改动范围 —— PASS,无越界

git diff --stat HEAD 共 14 个修改文件,逐一对照:

本需求 9 个(4 后端 + 5 前端):

  1. cfc-backend/src/main/java/com/etotem/cfc/controller/HealthExerciseController.java
  2. cfc-backend/src/main/java/com/etotem/cfc/controller/HealthMealController.java
  3. cfc-backend/src/main/java/com/etotem/cfc/controller/HealthSleepController.java
  4. cfc-backend/src/main/java/com/etotem/cfc/controller/task/TaskController.java
  5. cfc-frontend/pages/growth/diet-checkin.vue
  6. cfc-frontend/pages/growth/exercise-checkin.vue
  7. cfc-frontend/pages/growth/sleep-checkin.vue
  8. cfc-frontend/pages/index-home/index.vue
  9. cfc-frontend/pages/tasks/tasks.vue

历史遗留未提交项(不算本需求,提交时应排除):.gitignore、cfc-backend/pom.xml、cfc-web/AGENTS.md、docs/参考资料/知识库/统一知识库.json、tools/xaf_agent_hub/tasks_backlog.json。另还有一批 untracked 历史遗留(cfc-langgraph/AGENTS.md、cfc-web/docs/、out/ 等),均与本需求 diff 无关。

未发现本需求文件内含与 4 问题无关的改动:tasks.vue 的 563 行 diff 含较大模板重构(卡片化重设计),属问题2 成员 chips UI 改造的连带样式调整,功能侧(成员选择/权限/提交链)与需求相关,视为合理。

备注(不影响结论的观察)

  • tasks.vue 新增未使用的 CSS 影响面较大(task-card 系列样式重写),若后续审美评审可收敛;功能无缺陷。
  • 提交时建议精确 git add 上述 9 个文件,避免把 pom.xml/AGENTS.md/tasks_backlog.json 等历史遗留项卷入本需求提交。