# 五维家庭自检 · 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` 新增列 ```sql 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` ```sql 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`: ```json { "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` 新增 ```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`(新增两个方法)