2026-08-31-self-check-reminder-design.md 11 KB

五维家庭自检 · 15 天复检周期 + 首页入口显示分数 + 题目轮换设计

日期:2026-08-31 状态:已获用户方案确认(方案 A) 关联:FiveDimensionSelfCheckService / FiveDimensionSelfCheckController / five_dimension_self_checks 表


2.1 规格自检(2026-08-31 内联修复)

检查项 结果 说明
占位符扫描 ✅ 无 TODO/待定 无 "待定"、"TODO"、"XXX" 字段
内部一致性 ✅ 一致 后端接口与数据层、前端路径、迁移编号全部对齐
范围检查 ✅ 单一功能闭环 覆盖:周期判断 / 忽略记录 / 题目轮换 / 前端入口 / 中间页,范围可覆盖
模糊性 ✅ 已消除 以下歧义已在正文明确:body 维度固定 3 题无法轮换(降级说明);未到期直答保守不阻断(明确决策)

1. 背景与目标

现有五维家庭自检(P0-1)提供 15 题问卷,用户可无限次重做,首页入口固定展示同一引导文案。本次需求:

  1. 15 天复检周期:已检过后,15 天才提醒再次自检(按用户个人独立计算,跨设备)。
  2. 首页入口显示上次自检分数:首页自检卡片显示最近一次自检总分(totalScore /45)。
  3. 进入时显示上次结果 + 二选一:进入自检流程时展示上次结果摘要,用户可选择「再次自检」或「忽略本次」。
  4. 忽略后重置计时:忽略本次后,再过 15 天才再次提醒。
  5. 题目轮换:再次自检时题目不能与上一次完全一致。

已确认的决策:

  • 周期维度:按用户个人(非家庭共享)
  • 忽略持久化:后端记录(跨设备可靠)
  • 题目轮换:合并现有题库(15 主题 + 19 子维度)后随机抽

2. 现状盘点

后端

  • 控制器 FiveDimensionSelfCheckController(/api/family/self-check):
    • POST /questions — 返回固定 QUESTION_BANK(15 题)
    • POST /submit — 提交答案、计分、落库
    • POST /latest — 最近一次结果
    • POST /history — 历史记录
  • 服务 FiveDimensionSelfCheckService:
    • QUESTION_BANK:ids 1-15,每维度 3 题,A=3/B=2/C=1/D=0,维度满分 9,总分满分 45
    • SUB_DIMENSION_QUESTION_BANK:ids 101-119,A=100/B=66/C=33/D=0(百分制),当前无任何前端/Controller 调用(死代码,合并无破坏)
    • 计分逻辑在 submitSelfCheck 中写死遍历 QUESTION_BANK 分组
  • 表 five_dimension_self_checks:id / user_id / family_member_id / answers_json / scores_json / total_score / advice_json / created_at

前端

页面 现状
pages/index-home/index.vue selfTestSub 显示沙盘综合得分(非自检总分);点击跳答题页
pages/home-pages/parent-index.vue 五维家庭自检入口卡片 → 直接跳答题页
pages/family/self-check.vue 答题页(15 题分组渲染)
pages/family/self-check-result.vue 结果页(雷达图 + 维度分 + 寻源建议 + 「重新自检」按钮)
utils/api.js 已有 getSelfCheckQuestions / submitSelfCheck / getSelfCheckLatest / getSelfCheckHistory

3. 数据层设计

3.1 five_dimension_self_checks 新增列

ALTER TABLE five_dimension_self_checks
  ADD COLUMN question_ids_json VARCHAR(255) COMMENT '本次自检使用的题号JSON: [1,5,9,101,...]';

用途:记录本次抽到的题目集合,供下次轮换排除,并供 /status 返回 lastQuestionIds。

3.2 新增表 self_check_ignores

CREATE TABLE IF NOT EXISTS self_check_ignores (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id BIGINT NOT NULL COMMENT '用户ID',
  check_id BIGINT COMMENT '忽略的哪次自检记录ID(可空)',
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '忽略时间',
  INDEX idx_user_id (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='五维自检忽略提醒记录';

用途:记录「忽略本次提醒」动作,作为 15 天周期重置依据。

3.3 15 天周期算法

lastDecisiveTime = max(最近一次自检.created_at, 最近一次忽略.created_at)
canCheck = (now - lastDecisiveTime) >= 15 天
daysLeft = 15 - (now - lastDecisiveTime) 天数(未到期时 >0)
  • 从未自检:hasCheck=false,直接进入答题。
  • 已自检且距最近决定性动作 ≥15 天:可再次自检,或忽略(忽略后重置计时)。
  • 已自检且 <15 天:只读展示上次结果 + 「X 天后可再次自检」,不提供再次自检/忽略按钮。

4. 后端接口设计

4.1 新增 POST /api/family/self-check/status

请求:空 body(从 JWT 取 userId)。

响应 Result<Map>:

{
  "hasCheck": true,
  "lastResult": { "id": 12, "totalScore": 32, "dimensions": [...], "createdAt": "..." },
  "canCheck": true,
  "daysLeft": 0,
  "lastQuestionIds": [1, 2, 3, 101, 5, ...]
}
  • lastResult:最近一次 SelfCheckResultVO(无记录时为 null)
  • lastQuestionIds:最近一次 question_ids_json 解析(无记录时为 [])

4.2 修改 POST /questions — 随机抽题

逻辑:服务层新增 getQuestionsForRetake(userId):

  1. 查询最近一次自检的 question_ids_json → lastQuestionIds(无记录视为空集)。
  2. 构建合并池:QUESTION_BANK + SUB_DIMENSION_QUESTION_BANK(34 题),按 dimension 分组。
  3. 每维度:从该维度池中随机抽取 3 题,优先排除 lastQuestionIds;若排除后不足 3 题(body 仅 3 题),允许复用上次题号补足。
  4. 返回抽出的 15 题(排序按 body/wisdom/wealth/action/mind 固定维度序,组内保持池内原序)。

各维度可用题数:body=3(固定)、wisdom=4、wealth=5、action=15、mind=7。

保证:整体 15 题与上次不完全一致(body 可能重复,其余维度必然有变化;若用户上次已做过全部可用题,按随机抽取仍尽量不同)。

4.3 修改 POST /submit — 按提交题号计分 + 落库题号

  1. 计分:不再写死遍历 QUESTION_BANK,改为从合并池按提交的 questionId 定位题目 → 取 dimension → 计分(A=3/B=2/C=1/D=0,E=跳过)。维度分组逻辑保持不变(每维度至少答 1 题,按实际答题数折算到 9 分制)。
  2. 落库:保存 question_ids_json(= 本次提交答案中的 questionId 去重升序列表)。
  3. 其余逻辑(寻源建议、onboardingService.completeTask、unlockGateService.refresh*)保持不变。

4.4 新增 POST /api/family/self-check/ignore

请求:{ "checkId": 12 }(可空)。

逻辑:

  1. 幂等:同一用户最近 15 天内已有忽略记录则不重复插入(返回当前 nextRemindAt)。
  2. 写入 self_check_ignores(user_id, check_id, created_at=now)。
  3. 返回 { "nextRemindAt": "2026-09-15T00:00:00" }(= now + 15 天)。

5. 前端设计

5.1 首页入口显示自检分数

pages/index-home/index.vue + pages/home-pages/parent-index.vue:

  • 加载时调用 getSelfCheckStatus()。
  • 有自检记录:入口副文案改为 最近得分 {totalScore}/45 分 · 点击查看(替代现有沙盘分或补充分数)。
  • 点击入口 → 跳转中间页 self-check-entry(不再直跳答题页)。

5.2 新增中间页 pages/family/self-check-entry.vue

onLoad 调 /status,按状态分流:

状态 展示 操作
hasCheck=false 「开始自检」 直接跳 self-check 答题页
hasCheck=true && canCheck=true 上次结果摘要(总分 + 五维分数)+ 提示「已满 15 天」 按钮「再次自检」→ self-check;按钮「忽略本次」→ 调 /ignore → toast「已忽略,15 天后再次提醒」→ 返回上一页
hasCheck=true && canCheck=false 上次结果摘要 + 「X 天后可再次自检」 仅返回按钮
  • 结果摘要复用 self-check-result.vue 的展示结构(简化版:总分横幅 + 五维分数条),避免整页复制。
  • 新页面需在 pages.json 注册(pages/family/ 分包下,navigationBarTitleText: "自检")。

5.3 pages/family/self-check.vue — 保持答题页

  • onLoad 调 /questions(后端已随机),渲染逻辑不变。
  • 从中间页跳入时正常答题;直接 URL 进入也能拿到随机题(后端不阻断,见第 6 节)。

5.4 pages/family/self-check-result.vue

  • 「重新自检」按钮:改为 uni.navigateTo('/pages/family/self-check-entry')(先走状态判断)。

5.5 utils/api.js 新增

export function getSelfCheckStatus() {
  return request('/api/family/self-check/status', 'POST', {})
}
export function ignoreSelfCheck(data) {
  return request('/api/family/self-check/ignore', 'POST', data)
}

6. 边界行为

场景 行为
忽略幂等 15 天内重复忽略不重复插入,返回最新 nextRemindAt
题目不足 body 维度仅 3 题,排除后不足则复用,不报错
未到期直答 用户绕过中间页直接进 self-check 页,/questions 仍返回随机题,/submit 不阻断(保守策略,前端入口已挡)
历史老记录 老记录 question_ids_json 为 NULL → 视为空集,首次轮换全量随机
子维度题库 合并仅用于主题自检抽取与计分;原 getSubDimensionQuestions / submitSubDimensionCheck 为死代码,保持不变

7. 迁移

当前最大迁移编号:268(coupon.status)。本次新增 269/270,递增连续。

  • 迁移269: five_dimension_self_checks 表添加 question_ids_json 列(ensureColumn)
  • 迁移270: 创建 self_check_ignores 表(CREATE TABLE IF NOT EXISTS)

并同步更新 schema.sql:

  • five_dimension_self_checks 建表语句补 question_ids_json 列
  • 追加 self_check_ignores 建表语句

8. 测试与验证

  • 后端:mvn clean compile 通过;逻辑测试覆盖(如 FiveDimensionSelfCheckService 抽题/计分变更点)。
  • 前端:node --check 语法校验;HBuilderX 重新打包(Agent 不自行 build)。

9. 涉及文件清单

后端

  • entity/FiveDimensionSelfCheck.java(新增 questionIdsJson 字段)
  • mapper/FiveDimensionSelfCheckMapper.java(不变,必要时加 ignore 查询)
  • service/FiveDimensionSelfCheckService.java(抽题、计分改造、status/ignore)
  • 新增 entity/SelfCheckIgnore.java + mapper/SelfCheckIgnoreMapper.java
  • controller/family/FiveDimensionSelfCheckController.java(新增 status/ignore)
  • config/DatabaseInitializer.java(迁移 269/270)
  • resources/schema.sql(同步)

前端

  • pages/family/self-check-entry.vue(新增)
  • pages/index-home/index.vue、pages/home-pages/parent-index.vue(入口显示分数 + 跳中间页)
  • pages/family/self-check-result.vue(重新自检改跳中间页)
  • pages.json(注册中间页)
  • utils/api.js(新增两个方法)