|
|
@@ -1,355 +0,0 @@
|
|
|
-# 健康数据中心 — 实现计划
|
|
|
-
|
|
|
-**源文档**: docs/superpowers/specs/2026-08-08-health-data-center-design.md
|
|
|
-**日期**: 2026-08-09
|
|
|
-**分支**: cfclub
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 一、Codebase 现状核查(与 spec 差异)
|
|
|
-
|
|
|
-| 项 | spec 描述 | 实际现状 | 影响 |
|
|
|
-|---|---|---|---|
|
|
|
-| 身维度首页路径 | `pages/body/index.vue` | **不存在**;实际为 `pages/body-detail/index.vue`(pages.json:883-890) | 改造目标文件需修正 |
|
|
|
-| 迁移编号 | spec §8.2 写"迁移194" | 当前最高迁移为 **迁移203**(notices表) | 新迁移编号应为 迁移204 |
|
|
|
-| dimension-questionnaire 维度 | spec §6.1 要新增 cognitive/mental/relationship | 现有仅 7 个体质维度(growth/sleep/vision/immunity/nutrition/gut/exercise) | 需新增题目+后端 DIMENSIONS 列表扩展 |
|
|
|
-| LangGraph 端点 | spec §7.2 调用 `analysis/run` | Python 侧 `/api/v1/analysis/run` 存在(`cfc-langgraph/app/api/adapter.py:124`);Java 侧需走 AiGateway | 需补充 AiGateway 方法 |
|
|
|
-| health_plans 表 | spec §7.3 建表 | 不存在 | 需新建 |
|
|
|
-| RadarChart 复用 | spec 未说明 | `components/RadarChart.vue:34-43` 已支持 props `{dimensions, scores, avgScores}` | **无需扩展**,直接复用 |
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 二、任务分解
|
|
|
-
|
|
|
-### Batch A — 数据库 + 实体(串行,依赖无)
|
|
|
-
|
|
|
-**任务 1: 迁移204 — 创建 health_plans 表**
|
|
|
-
|
|
|
-文件: `cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java`(在迁移203后追加)
|
|
|
-
|
|
|
-DDL(与 spec §7.3 一致,补充 deleted 字段支持软删除):
|
|
|
-```sql
|
|
|
-CREATE TABLE IF NOT EXISTS health_plans (
|
|
|
- id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
|
- family_id BIGINT NOT NULL COMMENT '家庭ID',
|
|
|
- member_ids VARCHAR(200) NOT NULL COMMENT '目标成员ID列表(逗号分隔)',
|
|
|
- dimensions VARCHAR(100) NOT NULL COMMENT '维度列表(逗号分隔)',
|
|
|
- goal TEXT NOT NULL COMMENT '用户输入的目标',
|
|
|
- plan_content TEXT COMMENT 'LangGraph生成的方案内容(JSON/HTML)',
|
|
|
- member_name VARCHAR(50) COMMENT '成员名称摘要',
|
|
|
- deleted TINYINT DEFAULT 0 COMMENT '软删标记(0=正常 1=已删)',
|
|
|
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
|
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
|
- INDEX idx_family (family_id),
|
|
|
- INDEX idx_created (created_at)
|
|
|
-) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康方案汇总记录';
|
|
|
-```
|
|
|
-
|
|
|
-同步修改 `cfc-backend/src/main/resources/schema.sql`(文件末尾追加 CREATE TABLE)。
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- `mvn clean compile` 通过
|
|
|
-- 启动后查日志 "已创建health_plans表"
|
|
|
-- 连接数据库执行 `DESCRIBE health_plans` 确认字段存在
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-**任务 2: HealthPlan 实体 + Mapper**
|
|
|
-
|
|
|
-新建:
|
|
|
-- `cfc-backend/src/main/java/com/etotem/cfc/entity/HealthPlan.java`(字段与 DDL 一一对应)
|
|
|
-- `cfc-backend/src/main/java/com/etotem/cfc/mapper/HealthPlanMapper.java`(extends BaseMapper<HealthPlan>)
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- `mvn clean compile` 通过,无编译错误
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-### Batch B — 服务层 + API 契约(可并行)
|
|
|
-
|
|
|
-**任务 3: HealthPlanService + ServiceImpl**
|
|
|
-
|
|
|
-新建:
|
|
|
-- `cfc-backend/src/main/java/com/etotem/cfc/service/HealthPlanService.java`
|
|
|
-- `cfc-backend/src/main/java/com/etotem/cfc/service/impl/HealthPlanServiceImpl.java`
|
|
|
-
|
|
|
-接口方法:
|
|
|
-```java
|
|
|
-Long savePlan(Long familyId, Long adminId, String memberIds, String dimensions, String goal, String planContent, String memberName);
|
|
|
-List<HealthPlan> listPlans(Long familyId, Integer page, Integer size);
|
|
|
-HealthPlan getPlan(Long id, Long userId); // 校验 familyId 归属
|
|
|
-void deletePlan(Long id, Long userId); // 软删除:deleted=1,校验归属
|
|
|
-```
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- `mvn clean compile` 通过
|
|
|
-- 单元测试(如有)或手动调接口验证 CRUD
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-**任务 4: AiGateway — 新增 generateHealthPlan 方法**
|
|
|
-
|
|
|
-文件: `cfc-backend/src/main/java/com/etotem/cfc/service/AiGateway.java`
|
|
|
-
|
|
|
-新增方法(与现有 `generateQuestionnaire` 行185-216 模式一致):
|
|
|
-```java
|
|
|
-public String generateHealthPlan(Map<String, Object> inputs) {
|
|
|
- if (!enabled || baseUrl == null) return null;
|
|
|
- try {
|
|
|
- String url = baseUrl + "/api/v1/analysis/run";
|
|
|
- HttpEntity<String> entity = new HttpEntity<>(JSON.toJSONString(inputs), headers());
|
|
|
- ResponseEntity<String> resp = restTemplate.postForEntity(url, entity, String.class);
|
|
|
- // 解析返回的 content/result 字段
|
|
|
- return extractContent(resp.getBody());
|
|
|
- } catch (Exception e) {
|
|
|
- log.warn("AiGateway generateHealthPlan 失败: {}", e.getMessage());
|
|
|
- return null;
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- `mvn clean compile` 通过
|
|
|
-- 单元测试或手动调 AiGateway(Python 服务未运行时返回 null,不抛异常)
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-**任务 5: DimensionScoreService DIMENSIONS 列表扩展**
|
|
|
-
|
|
|
-文件: `cfc-backend/src/main/java/com/etotem/cfc/service/impl/DimensionScoreServiceImpl.java`
|
|
|
-
|
|
|
-在 `DIMENSIONS` 列表(行39-47)末尾追加:
|
|
|
-```java
|
|
|
-new String[]{"cognitive", "认知能力"},
|
|
|
-new String[]{"mental", "心理健康"},
|
|
|
-new String[]{"relationship", "关系质量"},
|
|
|
-```
|
|
|
-
|
|
|
-**注意**: `submitQuestionnaire` 方法(行208-237)无维度白名单校验,传入新维度码可直接写入 health_dimension_scores 表。但 `getDimensionOverview` 和 `getDimensionHistory` 依赖 DIMENSIONS 列表来展示,扩展后前端可正常获取数据。
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- `mvn clean compile` 通过
|
|
|
-- 调 `POST /api/dimension/overview?dimension=cognitive` 返回数据结构与现有维度一致
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-**任务 6: 前端 API 函数声明(无需等后端编译)**
|
|
|
-
|
|
|
-文件: `cfc-frontend/utils/api.js`
|
|
|
-
|
|
|
-新增:
|
|
|
-```javascript
|
|
|
-export const generateHealthPlan = (data) => {
|
|
|
- return request('/api/health/plan/generate', 'POST', data)
|
|
|
-}
|
|
|
-export const getHealthPlanList = (familyId) => {
|
|
|
- return request('/api/health/plan/list', 'POST', { familyId })
|
|
|
-}
|
|
|
-export const saveHealthPlan = (data) => {
|
|
|
- return request('/api/health/plan/save', 'POST', data)
|
|
|
-}
|
|
|
-export const deleteHealthPlan = (id) => {
|
|
|
- return request('/api/health/plan/delete', 'POST', { id })
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- 语法检查:`node -e "require('./utils/api.js')"` 无 Unexpected token
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-**任务 7: pages.json 注册 health-plan-summary 页面**
|
|
|
-
|
|
|
-文件: `cfc-frontend/pages.json`,在 `pages/health` 分包末尾追加:
|
|
|
-```json
|
|
|
-{
|
|
|
- "path": "health-plan-summary",
|
|
|
- "style": {
|
|
|
- "navigationBarTitleText": "方案汇总"
|
|
|
- }
|
|
|
-}
|
|
|
-```
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- HBuilderX 或命令行检查 pages.json JSON 语法:`node -e "JSON.parse(require('fs').readFileSync('pages.json','utf8'))"` 无异常
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-### Batch C — Controller 层(依赖 Batch A+B 的实体/服务)
|
|
|
-
|
|
|
-**任务 8: HealthPlanController(小程序前端接口)**
|
|
|
-
|
|
|
-新建: `cfc-backend/src/main/java/com/etotem/cfc/controller/health/HealthPlanController.java`
|
|
|
-
|
|
|
-端点:
|
|
|
-| 方法 | 路径 | 说明 |
|
|
|
-|---|---|---|
|
|
|
-| POST | `/api/health/plan/generate` | 调 LangGraph 生成方案并返回结果 |
|
|
|
-| POST | `/api/health/plan/save` | 保存方案到 health_plans 表 |
|
|
|
-| POST | `/api/health/plan/list` | 查询当前用户家庭的历史方案列表 |
|
|
|
-| POST | `/api/health/plan/detail` | 查询方案详情 |
|
|
|
-| POST | `/api/health/plan/delete` | 软删除方案 |
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- `mvn clean compile` 通过
|
|
|
-- 用 curl 手动调 `/api/health/plan/list` 返回 JSON(未登录返回 401)
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-**任务 9: AdminHealthPlanController(管理后台接口,可选 MVP)**
|
|
|
-
|
|
|
-新建: `cfc-backend/src/main/java/com/etotem/cfc/controller/admin/AdminHealthPlanController.java`
|
|
|
-
|
|
|
-端点:
|
|
|
-- `POST /api/admin/health-plans/list` — 分页查询全平台方案列表
|
|
|
-- `POST /api/admin/health-plans/delete` — 管理员强制删除
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- `mvn clean compile` 通过
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-### Batch D — 小程序前端页面(依赖 Batch C 的 API 端点)
|
|
|
-
|
|
|
-**任务 10: dimension-questionnaire.vue 扩展**
|
|
|
-
|
|
|
-文件: `cfc-frontend/pages/body-detail/dimension-questionnaire.vue`(546 行)
|
|
|
-
|
|
|
-改动:
|
|
|
-1. `dimensions` 数组(行95-103)新增 3 项(spec §6.1)
|
|
|
-2. `questions` 对象(行105-221)新增 cognitive/mental/relationship 三组题库(spec §6.2-6.4,每组 8 题)
|
|
|
-3. `onLoad` 增加 `dim` 参数支持(spec §6.5):
|
|
|
- ```javascript
|
|
|
- onLoad(options) {
|
|
|
- if (options.dim) {
|
|
|
- this.selectedDimension = options.dim
|
|
|
- this.step = 2 // 跳过选维度,直接进入答题
|
|
|
- }
|
|
|
- // 原有逻辑保留
|
|
|
- }
|
|
|
- ```
|
|
|
-4. 答题完成结果页新增"返回数据中心"按钮(跳回 body-detail/index.vue)
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- HBuilderX 打包无语法错误
|
|
|
-- 微信开发者工具打开 `/pages/body-detail/dimension-questionnaire?dim=cognitive&memberId=1`,直接进入答题页
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-**任务 11: body-detail/index.vue 改造为健康数据中心**
|
|
|
-
|
|
|
-文件: `cfc-frontend/pages/body-detail/index.vue`(676 行)
|
|
|
-
|
|
|
-改造要点:
|
|
|
-1. **新增 Tab 栏**(PageBanner 下方):身(橙)/智(靛)/心(粉)/行(绿),切换时仅刷新雷达图区域
|
|
|
-2. **雷达图随 Tab 切换**:
|
|
|
- - 身 → 现有七维雷达图(growth/sleep/vision/immunity/nutrition/gut/exercise)
|
|
|
- - 智 → 五维蛛网图(记忆力/注意力/逻辑推理/空间想象/语言表达),color=#6366F1
|
|
|
- - 心 → 五维图(情绪稳定性/抗压能力/自我认知/社交意愿/幸福感),color=#FF6B9D
|
|
|
- - 行 → 五维图(亲子关系/夫妻关系/亲友关系/同事关系/社区参与),color=#10B981
|
|
|
-3. **移除 funcList 功能网格**(运动/饮食/作息/冥想/舌诊/健康维度)
|
|
|
-4. **新增"看见·健康数据中心"区块**:
|
|
|
- - 数据采集卡片1:上传健康报告 → `/pages/health/report-upload`
|
|
|
- - 数据采集卡片2:在线测评(认知/心理/关系 三个入口,各带开始/已完成分数)
|
|
|
- - 数据查看卡片:最近报告/测评摘要(无数据时显示空状态提示)
|
|
|
- - 方案汇总卡片:上次方案摘要 + [生成新方案] 按钮 → `/pages/health/health-plan-summary`
|
|
|
-5. **弱化活动/商品/文章区**(收窄宽度至 80%,opacity 0.7)
|
|
|
-6. 保留:RadarChart、FamilyMemberStrip、FamilyEnergyBar、AIFloatingAvatar
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- HBuilderX 打包无语法错误
|
|
|
-- 微信开发者工具:切换身/智/心/行 Tab,雷达图标签和颜色随之变化;无数据时显示"上传报告后显示个人数据"
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-**任务 12: 新建 health-plan-summary.vue**
|
|
|
-
|
|
|
-新文件: `cfc-frontend/pages/health/health-plan-summary.vue`
|
|
|
-
|
|
|
-页面结构(spec §7.1):
|
|
|
-- 顶部导航栏(← 返回)
|
|
|
-- 生成新方案表单:
|
|
|
- - 维度多选(健康/认知/心理/社会性)
|
|
|
- - 成员多选(从家庭列表选取,调用 getVisibleFamilyMembers)
|
|
|
- - 目标输入框(textarea,placeholder:"例如:改善孩子睡眠质量")
|
|
|
- - [生成方案] 按钮 → 调 generateHealthPlan API
|
|
|
-- 方案结果展示区(HTML 富文本渲染):
|
|
|
- - [保存方案] → 调 saveHealthPlan API
|
|
|
- - [重新生成] → 再次调 generateHealthPlan
|
|
|
-- 历史方案列表(按时间倒序,调 getHealthPlanList)
|
|
|
-
|
|
|
-**QA 场景**:
|
|
|
-- HBuilderX 打包无语法错误
|
|
|
-- 微信开发者工具:填写表单 → 生成方案 → 保存 → 返回列表出现新条目
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 三、依赖关系图
|
|
|
-
|
|
|
-```
|
|
|
-任务1 (迁移204) ──→ 任务2 (实体+Mapper) ──→ 任务3 (Service)
|
|
|
- │
|
|
|
-任务6 (API函数) ←──────────────────────────────────────┘
|
|
|
-任务7 (pages.json) ←───────────────────────────────────┘
|
|
|
- │
|
|
|
- ┌────────────────────────┼────────────────────────┐
|
|
|
- ▼ ▼ ▼
|
|
|
- 任务4 (AiGateway) 任务5 (DIMENSIONS) 任务8 (Controller)
|
|
|
- │ │ │
|
|
|
- └────────────────────────┴────────────────────────┘
|
|
|
- │
|
|
|
- 任务9 (AdminController,可选)
|
|
|
- │
|
|
|
- ┌────────────────────────┴────────────────────────┐
|
|
|
- ▼ ▼
|
|
|
- 任务10 (问卷扩展) 任务11 (body-detail改造)
|
|
|
- │ │
|
|
|
- └────────────────────────┬────────────────────────┘
|
|
|
- ▼
|
|
|
- 任务12 (方案汇总页)
|
|
|
-```
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 四、执行顺序(3个批次)
|
|
|
-
|
|
|
-### 批次 1(串行,无并行依赖)
|
|
|
-- 任务 1 → 任务 2(实体依赖 DDL)
|
|
|
-
|
|
|
-### 批次 2(4项可并行)
|
|
|
-- 任务 3(HealthPlanService,依赖任务2)
|
|
|
-- 任务 4(AiGateway,无依赖)
|
|
|
-- 任务 5(DIMENSIONS扩展,无依赖)
|
|
|
-- 任务 6(API函数声明,无依赖)
|
|
|
-- 任务 7(pages.json注册,无依赖)
|
|
|
-
|
|
|
-### 批次 3(串行,依赖批次2)
|
|
|
-- 任务 8(Controller,依赖任务3+4)
|
|
|
-- 任务 10(问卷扩展,依赖任务5)
|
|
|
-- 任务 11(body-detail改造,依赖任务8 API端点)
|
|
|
-- 任务 12(方案汇总页,依赖任务8+10+11)
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 五、风险点
|
|
|
-
|
|
|
-| 风险 | 影响 | 缓解 |
|
|
|
-|---|---|---|
|
|
|
-| LangGraph Python 服务 `/analysis/run` 未部署或返回异常 | 方案生成功能不可用 | AiGateway 已有熔断器,返回 null;Controller 层做 mock fallback(返回预置方案文本) |
|
|
|
-| body-detail/index.vue 现有样式复杂(676行,含大量残留样式) | 改造时易引入样式冲突 | 保留 RadarChart/FamilyMemberStrip/FamilyEnergyBar 组件引用,仅替换 funcList 下方区块;新样式加 `.health-center-` 前缀避免冲突 |
|
|
|
-| dimension-questionnaire 跳转 URL 参数变化 | 现有链接失效 | `dim` 参数为可选,不传时走原有选维度流程 |
|
|
|
-| 五维雷达图标签数据从哪来 | 智/心/行 Tab 的 radarDimensions 如何填充 | 复用 `getDimensionOverview`,传 dimension=cognitive/mental/relationship;后端 DIMENSIONS 列表扩展后,返回对应五维分值 |
|
|
|
-| 迁移204 幂等性 | 重复部署不报错 | 使用 `CREATE TABLE IF NOT EXISTS`(与现有迁移203一致) |
|
|
|
-
|
|
|
----
|
|
|
-
|
|
|
-## 六、验收标准
|
|
|
-
|
|
|
-1. **任务1+2**: `mvn clean compile` 通过;数据库有 health_plans 表,含 deleted 字段
|
|
|
-2. **任务3+8**: 调 `POST /api/health/plan/save` 写入一条记录;调 `POST /api/health/plan/list` 返回该记录
|
|
|
-3. **任务4**: AiGateway.generateHealthPlan 在无 Python 服务时返回 null(不抛异常)
|
|
|
-4. **任务5**: 调 `POST /api/dimension/overview?dimension=cognitive` 返回正常数据结构(即使分值为0)
|
|
|
-5. **任务10**: 微信开发者工具打开 `?dim=cognitive&memberId=1` 直接进入答题页,8题完整可作答
|
|
|
-6. **任务11**: 切换身/智/心/行 Tab,雷达图标签和颜色正确变化;数据中心区块可见
|
|
|
-7. **任务12**: 生成方案 → 保存 → 返回列表出现新条目;历史方案可点击查看
|
|
|
-8. **编译**: 无 Java 编译错误(`mvn clean compile`);无 Vue/JS 语法错误(HBuilderX 打包无 Unexpected token)
|