版本: v1.0 日期: 2026-10-09 状态: 已确认(头脑风暴 + Oracle 架构评审通过) 作者: Sisyphus
food_recommend_idx_log 变化日志和 getTrend() 接口,但没有按变化幅度排序的能力health_indicators(文本名),没有统一的指标身份,无法跨报告类型对比、无法做指标间的关联health_reports.report_date 目前存的是上传时间而非报告真实日期,曲线横轴必然错位good_direction 自动推断(一期管理端手配)| 现有资产 | 内容 | 对本次的可用性 |
|---|---|---|
health_reports |
报告主表(report_type/report_date/subject_id/payload_json) | 曲线原数据;report_date 需修正为真实日期 |
health_indicators |
解析后的指标明细(report_id/category/indicator_name/indicator_value/status) | 报告指标原数据;保持为单一写者目标,不删除 |
report_type_registry |
报告类型登记 + expected_indicators | 「同一类报告」分组依据;定义/映射种子来源 |
food_recommend_idx / _log |
食材推荐指数 + 变更日志(previous/new/report_id/changed_at) | 食材曲线几乎现成;缺变化幅度排名;保留语义 |
indicator_definitions / indicator_values |
通用指标字典 + 泛用指标值(sourceType/sourceId/definitionId/numericValue/recordedAt) | 升级为全平台统一指标层(当前仅舌诊在用) |
five_dimension_scores / health_dimension_scores |
五维 / 七维评分(含 assessedAt) | 维度评分趋势现成;不写入统一时序,API 层单独合入 |
| 指标关联表 | 不存在 | 新建 indicator_correlations |
关键缺口:统一指标身份、归一化映射层、指标关联表、真实报告日期、变化幅度排名接口。
| 决策项 | 选择 | 理由 |
|---|---|---|
| 指标统一身份 | 方案 A:复用 indicator_definitions + indicator_values 升级为统一指标层 |
项目本就有意的通用结构;跨类型统一;关联可跨类型;速查一处查全 |
| 指标范围 | 报告解析指标(含菌属、营养、维生素等)+ 食材推荐指数 + 五维/七维评分 | 用户确认「全报告类型统一」+「上述 + 五维/七维评分」 |
| 五维/七维存储 | 不进 indicator_values,保留原表,API 层单独合入「维度评分」小节 |
维度评分是聚合值,与原始测量混在一个时序中层级混乱 |
| 关联关系 | 人工定义(候选对+方向+人工强度)+ 真实数据校准(computed_strength + sample_size),混合模式 | 用户确认 |
| 报告日期 | 从 PDF 提取真实报告/采样日期;历史回填 | 曲线横轴准确性前提,用户确认纳入 |
| 归一化映射 | 独立表 indicator_mapping_rules(source_type+raw_name→definition_id),exact→fuzzy→AI兜底→惰性注册 |
Oracle 评审:比在 definitions 上加 aliases 列更可运营、可支撑 fuzzy/AI 兜底 |
| 食材推荐 | 保留 food_recommend_idx+log(变化日志语义),同时增写 indicator_values(source_type=food_recommend) |
不破坏现有变化日志接口 |
| 双写一致性 | 单一写者模式:解析照旧写 health_indicators,NormalizeIndicatorPipeline 同流程写 indicator_values,幂等覆盖 |
Oracle 评审:避免两表分叉 |
| 校准算法 | Spearman 秩相关;单对象 ≥3 配对、聚合有效样本 ≥30 才用 computed 覆盖 manual;非数值型对跳过 | 菌群丰度偏态分布,Spearman 稳健;样本不足不误导 |
| 图表实现 | 沿用小程序手写 canvas 模式,新增 TrendChart.vue,不引入图表库 |
小程序包体积 + HBuilderX 打包约束 |
| 变化着色 | good_direction(越高越好/越低越好/区间内越好)驱动箭头与颜色 |
Oracle 评审:没有它无法判断变化是改善还是恶化 |
| 关联对排序 | definition_id_a < definition_id_b(按 code 字典序),应用层强制 |
唯一键对称,避免 A-B / B-A 重复 |
PdfParseService / LangGraph report_parse_agent.py)新增提取「报告日期 / 采样日期」→ payload.reportDatehealth_reports.report_date 优先使用真实日期,缺失回退上传时间report_date 缺失或为上传时间的旧报告,从其 payload_json / 存储 PDF 中重新提取真实日期并回填;幂等可重跑;无法提取的置 NULL(不伪装成上传时间)HealthReportService 已有 reportDate 修正逻辑(约 1749 行,读 payload.reportDate → yyyy-MM-dd 解析 → setReportDate),新提取只负责把真实日期写入 payload.reportDate,入库逻辑复用health_indicators.collection_date 已存在于 schema.sql(1868 行),无需补列;实施时仅需确认生产库同样存在(用 ensureColumn 兜底)indicator_definitions 扩展新增列:
| 列 | 类型 | 说明 |
|---|---|---|
source_kind |
VARCHAR(32) | report_indicator / bacteria / food_index / dimension_score |
report_type_scope |
VARCHAR(32) | 适用报告类型(空=通用),支撑「同一类报告」分组 |
value_type |
VARCHAR(16) | numeric / categorical / level(语义类型,与 dataType 区分) |
good_direction |
VARCHAR(16) | higher_is_better / lower_is_better / in_range_is_better / neutral |
dimension_code |
VARCHAR(16) | 关联五维(body/mind/wisdom/action/wealth,可空) |
保留现有:domain / category / code / name / dataType / options / unit / refRange / sortOrder / status。
目录来源:
report_type_registry.expected_indicators → 预生成每类报告的指标定义bacteria.<name> / food.<foodId> / indicator.<category>.<name> 风格dim.body 等,仅作展示标签,不写 indicator_values)indicator_values 扩展(统一观测时序)新增列:
| 列 | 类型 | 说明 |
|---|---|---|
subject_id |
BIGINT | 观测对象(对齐 health_reports.subject_id / family_members.id) |
family_id |
BIGINT | 所属家庭 |
report_id |
BIGINT | 来源报告(可空:食物指数 / 维度评分类) |
report_date |
DATE | 冗余报告日期,供时序查询 |
unit |
VARCHAR(32) | 观测单位(若与定义不一致,写入时告警) |
status |
VARCHAR(32) | 观测时状态快照(正常/偏高/偏低…) |
保留现有:source_type / source_id / definition_id / value / numeric_value / remark / recorded_at。
索引与约束:
ALTER TABLE indicator_values
ADD COLUMN subject_id BIGINT NULL,
ADD COLUMN family_id BIGINT NULL,
ADD COLUMN report_id BIGINT NULL,
ADD COLUMN report_date DATE NULL,
ADD COLUMN unit VARCHAR(32) NULL,
ADD COLUMN status VARCHAR(32) NULL,
ADD UNIQUE KEY uk_source_definition (source_type, source_id, definition_id),
ADD INDEX idx_subject_def_date (subject_id, definition_id, report_date),
ADD INDEX idx_def (definition_id);
source_type 取值:报告类型(gut_flora 等)/ food_recommend(五维/七维评分不入本表,见 4.2)indicator_mapping_rules(新建)CREATE TABLE IF NOT EXISTS indicator_mapping_rules (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
source_type VARCHAR(32) NOT NULL COMMENT 'gut_flora|physical_exam|dan_a2|food_recommend|five_dimension|health_dimension',
raw_name VARCHAR(200) NOT NULL COMMENT '报告中的原始名称(指标名/菌名/食材名)',
alias_text TEXT COMMENT '别名 JSON 数组,用于模糊匹配',
definition_id BIGINT NOT NULL COMMENT 'FK → indicator_definitions.id',
match_type VARCHAR(20) DEFAULT 'exact' COMMENT 'exact|fuzzy|ai_fallback|auto_created',
priority INT DEFAULT 0 COMMENT '多规则冲突时取最高',
status TINYINT DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_source_raw (source_type, raw_name),
INDEX idx_def (definition_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='指标归一化映射规则';
indicator_correlations(新建)CREATE TABLE IF NOT EXISTS indicator_correlations (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
definition_id_a BIGINT NOT NULL COMMENT 'code 字典序较小的指标',
definition_id_b BIGINT NOT NULL COMMENT 'code 字典序较大的指标',
relation_type VARCHAR(20) DEFAULT 'correlation' COMMENT 'correlation(相关)|influence(影响)|same_source(同源)',
direction VARCHAR(10) NOT NULL COMMENT 'positive|negative',
manual_strength DECIMAL(3,2) NOT NULL COMMENT '人工标定强度 0~1,样本不足时使用',
computed_strength DECIMAL(4,3) NULL COMMENT '统计计算相关系数(Spearman rho)',
method VARCHAR(20) DEFAULT 'spearman' COMMENT 'pearson|spearman',
sample_size INT DEFAULT 0 COMMENT '参与计算的聚合有效样本数',
confidence VARCHAR(10) COMMENT 'high(>=30)|medium(10~29)|low(<10)',
effective_strength DECIMAL(4,3) NOT NULL COMMENT '展示值:有效样本>=30 时取 computed,否则取 manual',
manual_locked TINYINT DEFAULT 0 COMMENT '1=人工锁定,数据校准不覆盖 direction/manual_strength/effective_strength',
cohort_scope VARCHAR(20) DEFAULT 'subject' COMMENT 'subject|family|population',
status VARCHAR(20) DEFAULT 'active' COMMENT 'active|inactive|pending',
computed_at DATETIME NULL COMMENT '最后校准时间',
created_by BIGINT NULL COMMENT '创建者(人工定义时)',
description VARCHAR(500) NULL COMMENT '给用户看的解释',
source_note VARCHAR(255) NULL COMMENT '依据/出处',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_pair (definition_id_a, definition_id_b),
INDEX idx_def_a (definition_id_a),
INDEX idx_def_b (definition_id_b)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='指标关联关系(人工定义 + 数据校准)';
对序规则:写入前按 definition.code 字典序比较,小的存 a、大的存 b,应用层强制,保证唯一键对称。
@Scheduled,每日凌晨)+ 管理端手动触发(/api/admin/indicator/correlation/calibrate)status='active' 且 manual_locked=0 的关联对value_type IN ('numeric','level'))subject_id 分组,对每个对象取该指标对的配对测量序列(按 report_date 对齐,≥3 个配对点才参与)confidence:≥30 → high,10~29 → medium,<10 → lowcomputed_strength / sample_size / method / confidence / computed_ateffective_strength = 有效样本>=30 ? computed_strength : manual_strength(方向若与人工矛盾且锁定时,保持人工)报告解析/采集确认(或重解析)
├─ 原有逻辑照旧写 health_indicators(不动现有消费者:HealthAnalysisService 等)
└─ NormalizeIndicatorPipeline(同流程/同事务内)
1. 收集该报告全部:health_indicators 明细 + 菌属(payload/gut_flora)+ 食物(payload/食材)
2. 每条 (source_type=报告类型, raw_name) 解析 definition_id:
exact 命中 indicator_mapping_rules → definition_id
fuzzy 命中(CJK 二元组 / 拼音归一化)→ definition_id
AI 兜底(AiGateway → LangGraph 归类)→ 建议 definition_id(登记 pending 规则,管理端确认)
新发现 → 惰性注册新 definition + 自动登记 exact 规则(match_type=auto_created)
3. 幂等写入 indicator_values
INSERT ... ON DUPLICATE KEY UPDATE (source_type, source_id, definition_id)
4. 记录 subject_id / family_id / report_id / report_date(真实报告日期)
食物推荐指数钩子:FoodRecommendService.recalculateForUser() 完成后,把该用户最新 food_recommend_idx 全量快照写入 indicator_values(source_type=food_recommend,source_id=food_recommend_idx.id,definition_id 经 food.<foodId> 映射)。
批量种子:管理端 /api/admin/indicator/mapping/import 一次性导入菌属 200+ / 食材 224 / expected_indicators 的映射种子。
统一
@PostMapping、Result<T>、JWT;subject_id服务端校验归属(当前用户家庭内成员)。
POST /api/indicator/trend
body: { subjectId, definitionIds: [1,2,3] }
→ data: {
"items": [{
"definitionId": 1, "name": "双歧杆菌属",
"points": [ { "reportDate":"2026-08-15", "value":"12.8", "unit":"%", "status":"偏高" }, ... ]
}]
}
横轴 reportDate = 真实报告日期,升序。
POST /api/indicator/change-ranking
body: { subjectId, reportType?, category?, scope: "adjacent"|"first-vs-latest", orderBy: "up"|"down"|"magnitude", limit: 50 }
→ data: {
"items": [{
"definitionId": 5, "name": "维生素B1", "category": "维生素",
"sourceKind": "report_indicator", "unit": "mg",
"before": { "reportDate":"2026-08-15", "value":"2" },
"after": { "reportDate":"2026-10-01", "value":"4" },
"delta": 2, "deltaPct": 100,
"movement": "up"|"down"|"flat",
"trendLabel": "改善"|"恶化"|"中性" // 按 good_direction 判定
}],
"overall": { "improvedCount": 12, "worsenedCount": 3, "flatCount": 5 }
}
scope=adjacent:相邻两次报告之差(默认);first-vs-latest:首尾对比magnitude 按 Δ 绝对值降序(默认);up/down 按 Δ 正/负降序sourceKind=food_index)或混合(按 category 过滤)POST /api/indicator/quick-lookup
body: { subjectId, category?, page: 1, pageSize: 30 }
→ data: {
"categories": [ { "name":"核心菌属", "total": 32 }, ... ],
"items": [{
"definitionId": 1, "name": "双歧杆菌属", "category": "核心菌属",
"value": "12.8", "unit": "%", "refRange": "0.19-14.59", "status": "偏高",
"trend": "up"|"down"|"flat", "trendLabel": "改善",
"latestReportDate": "2026-10-01", "pointsCount": 3
}],
"dimensionScores": [ // 五维/七维评分小节(不写 indicator_values,API 层 join 原表)
{ "dimensionCode":"body", "name":"身", "score": 82, "assessedAt":"2026-09-30", "trend": "up" }
]
}
分类分页(每类 ≤30 项),避免一次拉取 200+ 菌属。
POST /api/indicator/correlation/list
body: { subjectId, definitionId }
→ data: {
"relations": [{
"relation": { "pairId": 1, "indicatorA": {"definitionId":1,"name":"记忆力"}, "indicatorB": {...},
"direction":"positive", "effectiveStrength": 0.72,
"strengthSource":"computed"|"manual", "sampleSize": 45,
"confidence":"high", "manualLocked": false,
"description":"记忆力与睡眠相关菌群含量正相关……" },
"subjectEvidence": { // 该成员自己的配对数据支撑(如有)
"points": 4, "personalRho": 0.68, "hasEnough": true
}
}]
}
| 接口 | 用途 |
|---|---|
POST /api/admin/indicator/correlation/save |
人工定义/编辑关联对(A/B/方向/人工强度/说明/locked/来源) |
POST /api/admin/indicator/correlation/list |
关联对分页列表(含校准状态) |
POST /api/admin/indicator/correlation/calibrate |
手动触发全校准或单对校准 |
POST /api/admin/indicator/mapping/import |
批量导入映射种子(JSON/CSV) |
POST /api/admin/indicator/mapping/list |
映射规则分页 + 未匹配 raw 的 review 列表 |
POST /api/food/recommendation/trend(食材单指标趋势)继续用于菌群食物详情页POST /api/health/knowledge/query 知识库弹出框继续用于速查/曲线页指标名称点击指标变化曲线页 pages/health/indicator-curve.vue(新增)
pages/health/report-list 顶部、身维度页TrendChart 折线(横轴=真实报告日期,多报告点,数据点标注数值)good_direction 着色components/TrendChart.vue(多序列折线 + 数据点 + 箭头),沿用手写 canvas 模式(参考 RadarChart.vue 的 type=2d + DPR 适配 + 重试初始化),不引入图表库指标速查页 pages/health/indicator-quicklook.vue(新增)
health-knowledge-popup)| 最新值 | 参考区间 | ↑↓→ 趋势 | 最近报告日期菌群报告详情页 pages/health/gut-flora-detail.vue(改)
pages.json:注册 2 个新页面路径;utils/api.js:新增 trend / change-ranking / quick-lookup / correlation/list 封装(遵守 API 去重规范,同 URL 单一规范入口)
views/admin/IndicatorCorrelations.vue(新增)
manual_locked 开关views/admin/IndicatorMappings.vue(新增)
router:注册对应管理端路由(admin 权限)
由 good_direction × 变化方向判定,全局统一:
| good_direction | 数值↑ | 数值↓ | 备注 |
|---|---|---|---|
higher_is_better |
🟢 绿 ↑(改善) | 🔴 红 ↓(恶化) | 如双歧杆菌属 |
lower_is_better |
🔴 红 ↑(恶化) | 🟢 绿 ↓(改善) | 如炎症水平 |
in_range_is_better |
向参考区间内移动 → 🟢 绿 ↑/↓;远离 → 🔴 红 | 结合 refRange 判定 | 如维生素 |
neutral |
⚪ 灰 ↑ | ⚪ 灰 ↓ | 无优劣方向 |
| 无变化 | ⚪ 灰 → |
实现遵循小程序限制::class="'mb-' + item._cssClass" 内联表达式(禁方法调用、禁中文类名),_cssClass 由后端或前端 computed 映射为英文(impr-up/worst-down/flat 等)。
| # | 验收项 | 验证方式 |
|---|---|---|
| 1 | 上传第二份同类型报告后,曲线页显示完整前后对比;变化排名按幅度排序正确 | 手工 + e2e |
| 2 | 变化箭头颜色符合 good_direction(如炎症下降=绿、双歧杆菌上升=绿) | 手工 |
| 3 | 速查按分类分页正常,含五维/七维小节;点击单项跳转曲线 | 手工 |
| 4 | 管理端配置关联对 → 小程序立即可见;有真实配对数据显示校准强度+样本量;样本<30 显示人工值 | 手工 |
| 5 | 新报告 report_date = PDF 真实日期;历史回填任务幂等可重跑(连跑两次结果一致) | 脚本 + 手工 |
| 6 | 归一化幂等:同一报告重解析不产生重复 observation(唯一键生效) | 脚本断言 |
| 7 | report_type_registry.expected_indicators 与默认映射种子导入后,解析出的常见指标 100% 命中映射(无 AI 兜底) |
脚本统计 |
| 8 | mvn clean compile 通过;前端 script 语法校验(node --check)通过;API 去重审计通过 |
CI 命令 |
| 层 | 文件 | 操作 | 内容 |
|---|---|---|---|
| 后端 | config/DatabaseInitializer.java |
改 | 迁移:definitions/values 加列 + 索引 + 唯一键;新建 mapping_rules/correlations 两表;report_date 真实日期回填;collection_date 已存在(schema.sql 1868 行)用 ensureColumn 兜底生产库;种子映射导入(当前最新迁移编号 355,从 356 起递增) |
| 后端 | docs/superpowers/api/API_REFERENCE.md |
改 | 新增接口前查重、新增后同步记录(trend/change-ranking/quick-lookup/correlation + 管理端 5 接口) |
| 后端 | resources/schema.sql |
改 | 同步 4 处 DDL |
| 后端 | entity/IndicatorDefinition.java / IndicatorValue.java |
改 | 新字段 |
| 后端 | entity/IndicatorMappingRule.java / entity/IndicatorCorrelation.java |
新建 | 新实体 |
| 后端 | mapper/IndicatorMappingRuleMapper.java / mapper/IndicatorCorrelationMapper.java |
新建 | BaseMapper |
| 后端 | service/IndicatorMappingService.java |
新建 | resolve(raw)→definitionId;register;import 种子 |
| 后端 | service/NormalizeIndicatorPipeline.java |
新建 | 解析后钩子,幂等写 indicator_values |
| 后端 | service/IndicatorTrendService.java |
新建 | trend / change-ranking / quick-lookup |
| 后端 | service/IndicatorCorrelationService.java |
新建 | 关联 CRUD + effective_strength + Spearman 校准 |
| 后端 | service/ReportDateExtractor.java |
新建 | PDF/payload 真实日期提取(写入 payload.reportDate 后复用 HealthReportService 既有入库逻辑)+ 历史回填 |
| 后端 | controller/IndicatorController.java |
新建 | /api/indicator/*(4 个视图接口) |
| 后端 | controller/admin/IndicatorCorrelationAdminController.java / IndicatorMappingAdminController.java |
新建 | 管理端接口 |
| 后端 | service/FoodRecommendService.java |
改 | recalculateForUser 后增写 indicator_values |
| 后端 | service/AiGateway.java(及 LangGraph) |
改 | 新增指标归类兜底方法/endpoint |
| 后端 | 新增 job/IndicatorCalibrationJob.java |
新建 | @Scheduled 每日校准 |
| 小程序 | components/TrendChart.vue |
新建 | 通用折线组件(手写 canvas) |
| 小程序 | pages/health/indicator-curve.vue / indicator-quicklook.vue |
新建 | 曲线页 / 速查页 |
| 小程序 | pages/health/gut-flora-detail.vue |
改 | 增加「变化」区块 |
| 小程序 | pages.json |
改 | 注册 2 页 |
| 小程序 | utils/api.js |
改 | 新增 4 个接口封装 |
| Web 端 | views/admin/IndicatorCorrelations.vue / IndicatorMappings.vue |
新建 | 关联配置 / 映射管理 |
| Web 端 | router/index.js |
改 | 注册管理端路由 |
| 测试 | tests/e2e/indicator-curve-flow.spec.js 等 |
新建 | 曲线/速查/关联冒烟 |
| 风险 | 缓解 |
|---|---|
| 归一化映射质量(AI 兜底可能错配) | 映射规则管理端可 review;AI 建议先登记 pending,人工确认后生效 |
| 双写一致性(health_indicators vs indicator_values) | 单一写者;同事务;唯一键幂等覆盖 |
| 速查性能(200+ 菌属 × 多次报告) | subject_id + 复合索引;分类分页;每类 30 项 |
| 历史报告回填工作量大 | 分批、幂等、可中断续跑;取不出日期的置 NULL |
| 方向/颜色误判 | good_direction 管理端可维护;一期手配,二期自动推断 |
| 关联校准样本不足 | effective_strength 逻辑:样本 ≥30 才用计算值,否则人工值;confidence 分级展示 |
| 指标名变化(同一菌不同报告写法不同) | fuzzy 匹配 + alias_text + AI 兜底多级回落 |
mvn clean compileIndicatorMappingService + NormalizeIndicatorPipeline + 食物钩子 + 种子导入 → 幂等验证mvn clean compileTrendChart → 曲线页 → 速查页 → 菌群详情「变化」区块 → pages.json / api.js → HBuilderX 打包人工验证IndicatorCorrelations.vue + IndicatorMappings.vue + admin API + 路由IndicatorCalibrationJob + 管理端手动触发 + 验收清单逐项过good_direction 从参考区间/历史数据自动推断