Ver código fonte

chore: auto bump version and changelog [skip ci]

iwt 1 mês atrás
pai
commit
5255952aab

+ 1 - 1
cfc-web/.last_build_commit

@@ -1 +1 @@
-f9b2b75aa0b916bf0f7575b840aa4725846110f6
+6554de1438432c95608148f10564f0c4117e4430

+ 143 - 21
docs/superpowers/specs/2026-08-13-health-status-survey-design.md

@@ -1,8 +1,15 @@
-# 「当前状态调研」健康现状档案 设计
+# 「当前状态调研」健康现状档案 设计(v2)
 
-> 日期:2026-08-13
+> 日期:2026-08-13(v2 更新:2026-08-18)
 > 分支:cfclub
-> 状态:待评审
+> 状态:v2 待评审(疾病史/用药输入重设计)
+
+## 0. 变更记录(v2)
+
+| 版本 | 日期 | 变更 |
+|------|------|------|
+| v1 | 2026-08-13 | 初版设计(疾病史:10 预置标签 + 自由输入;用药:每药 药名+服药时间) |
+| v2 | 2026-08-18 | **疾病史重设计**:预置清单扩为「菌群报告病症风险 + 互联网统计常见病」两源合并去重(约 50 项,分类折叠多选),每条疾病新增**确诊时间(年月)**;**用药重设计**:每药一行保留,新增**开始服药日期 → 自动计算已服时长**,记不清时可用时长文本兜底 |
 
 ## 1. 背景与目标
 
@@ -13,10 +20,14 @@
 1. 在用户要出方案时进行"当前状态调研",以更准确地出方案。
 2. 用户**可以不填**,但要提醒"不保证准确"。
 3. 调研字段:身高、体重、血压、血糖、血脂、过敏源、食物喜好、在服药物/治疗、疾病史等。
+4. **(v2)疾病史不靠用户自由输入**:根据菌群报告能体现的病症风险 + 互联网统计常见病,预置完整清单供多选;未列出的才由用户补充(补充为兜底,非主输入路径)。
+5. **(v2)每条疾病记录确诊时间**(年月粒度)。
+6. **(v2)近期服药每个药一条,并记录每种药已服药的时长**(开始日期自动计算,记不清可用时长文本)。
 
 **设计目标**:
 
 - 出方案前收集结构化健康现状,随 AI 请求注入上下文,提升方案精准度。
+- 疾病/用药输入**以选择为主、输入为辅**,降低用户负担、提高数据规范性。
 - 双入口:出方案流程内提示填写 + 独立档案页随时维护。
 - 不填可跳过,但需明确提醒精度风险。
 
@@ -28,9 +39,10 @@
 | 2 | 数据归属对象 | **按账号(user_id)存一份** |
 | 3 | 过敏/食物喜好与现有表关系 | **复用现有 `diet_preferences`**(按 family_member_id 存),新表不重复存;出方案时一并读取传入 |
 | 4 | 指标值形态 | **结构化数值 + 可选备注**(血压 120/80、血糖 5.6 + 备注补充诊断信息) |
-| 5 | 疾病史/用药形态 | **JSON 数组** `[{name, note}]`,前端标签式多选+自定义输入 |
-| 6 | 不填的提醒方式 | **弹窗确认**:「未填写健康现状,方案可能不精准」→「去填写」/「直接生成」 |
-| 7 | AI 接入方式 | **前端随请求传参**:前端把健康现状+饮食偏好随 `aiSendMessage` 请求传给 AI(Dify inputs) |
+| 5 | **疾病史形态(v2)** | **预置清单多选**:菌群报告病症风险(20 项)+ 互联网统计常见病(约 40 项)**合并去重后按类别分组折叠多选**(约 50 项);未列出的保留自定义输入兜底;每条疾病含 `diagnosedAt`(确诊时间,年月) |
+| 6 | **用药形态(v2)** | **每药一条**:`{name, startDate, durationText, time}`;`startDate`(开始服药年月)自动计算已服时长,记不清时填 `durationText`(约 X 个月/年) |
+| 7 | 不填的提醒方式 | **弹窗确认**:「未填写健康现状,方案可能不精准」→「去填写」/「直接生成」 |
+| 8 | AI 接入方式 | **前端随请求传参**:前端把健康现状+饮食偏好随 `aiSendMessage` 请求传给 AI(Dify inputs) |
 
 ## 3. 目标架构
 
@@ -46,7 +58,7 @@ health_status 表 (新, user_id 唯一)          diet_preferences (现有, famil
               AIChatController.sendMessage
-       inputs.put("health_status", ...) / inputs.put("diet_preferences", ...)
+        inputs.put("health_status", ...) / inputs.put("diet_preferences", ...)
                         Dify 生成方案
 ```
@@ -68,8 +80,8 @@ CREATE TABLE IF NOT EXISTS health_status (
   blood_pressure VARCHAR(20) DEFAULT NULL COMMENT '血压,如 120/80',
   blood_glucose DECIMAL(4,1) DEFAULT NULL COMMENT '空腹血糖(mmol/L)',
   blood_lipids JSON DEFAULT NULL COMMENT '血脂 [{name,value,unit,note}]',
-  disease_history JSON DEFAULT NULL COMMENT '疾病史 [{name,note}]',
-  medications JSON DEFAULT NULL COMMENT '在服药物/治疗 [{name,note}]',
+  disease_history JSON DEFAULT NULL COMMENT '疾病史 [{name,diagnosedAt,note}]',
+  medications JSON DEFAULT NULL COMMENT '在服药物/治疗 [{name,startDate,durationText,time}]',
   notes VARCHAR(500) DEFAULT NULL COMMENT '其他补充说明',
   filled_at DATETIME DEFAULT NULL COMMENT '首次填写时间',
   updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
@@ -80,6 +92,7 @@ CREATE TABLE IF NOT EXISTS health_status (
 - 所有指标字段**均可空**(用户可不填单项)。
 - 过敏源/食物喜好不在此表,复用 `diet_preferences`。
 - 迁移:`DatabaseInitializer.runMigrations()` 新增迁移(`CREATE TABLE IF NOT EXISTS`),同步 `schema.sql`。迁移编号按现有最大编号递增。
+- **v2 无 DDL 变更**:`disease_history`/`medications` 为 JSON 字符串列,字段结构变化发生在 JSON 内部,表结构、实体、Controller、Service 均不变。
 
 ### 4.2 JSON 结构约定
 
@@ -91,16 +104,77 @@ CREATE TABLE IF NOT EXISTS health_status (
 ]
 ```
 
-**disease_history / medications**:
+**disease_history(v2)**:
 ```json
-[{"name": "过敏性鼻炎", "note": "三年前确诊,春季易发"}]
+[
+  {"name": "高血压", "diagnosedAt": "2021-03"},
+  {"name": "胃病", "diagnosedAt": ""},
+  {"name": "自定义补充病", "diagnosedAt": "2019-08"}
+]
+```
+- `diagnosedAt`:确诊时间(年月,`yyyy-MM`),可空。
+- `note`:保留字段,仅用于兼容 v1 旧数据回显(`{name, note}` 无 diagnosedAt 时 `diagnosedAt` 置空);v2 新数据不产生 `note`。
+
+**medications(v2)**:
+```json
+[
+  {"name": "立普妥", "startDate": "2024-05", "durationText": "已服 15 个月", "time": "早饭后一次"},
+  {"name": "二甲双胍", "startDate": "", "durationText": "约 3 个月", "time": "随餐"}
+]
 ```
+- `startDate`:开始服药年月(`yyyy-MM`),可空;保存时前端据此自动计算 `durationText`。
+- `durationText`:已服时长文本,自动计算值("已服 X 个月/年")或手动兜底("约 X 个月/年")。
+- `time`:服药时间点(如"早饭后一次"),保留 v1 字段。
+
+### 4.3 互联网统计常见病数据来源(供科普文案/排序参考)
+
+> 预置清单的"互联网统计源"疾病排序与文案依据,数据来自国家卫健委、国家疾控中心、国家癌症中心、中华医学会及《柳叶刀》等权威来源;大型流行病学调查周期为 5-10 年,以下为最新权威口径。
+
+| 疾病 | 患病率 / 人数 | 数据出处 | 数据年份 |
+|------|--------------|---------|---------|
+| 高血压 | 现患约 2.45 亿;成人患病率 25.2%(另一口径 27.5%) | 《中国心血管健康与疾病报告2023》;《健康中国行动2019—2030年》;《中国居民营养与慢性病状况报告2020》 | 2023 / 2019 / 2020 |
+| 血脂异常 | 18 岁及以上成人患病率 40.4% | 《健康中国行动2019—2030年》 | 2019 |
+| 糖尿病 | 成人患病率约 11.9%;糖尿病前期 35.2%;患病人数或超 1.8 亿 | 国家卫健委《高血糖症营养和运动指导原则(2024年版)》;2024-11 全国糖尿病学术会议 | 2024 |
+| 冠心病 | 现患约 1139 万 | 《中国心血管健康与疾病报告2023》 | 2023 |
+| 脑卒中 | 现患约 1300 万 | 《中国心血管健康与疾病报告2023》 | 2023 |
+| 心力衰竭 | 现患约 890 万 | 《中国心血管健康与疾病报告2023》 | 2023 |
+| 心房颤动 | 现患约 487 万 | 《中国心血管健康与疾病报告2023》 | 2023 |
+| 慢阻肺 | 20 岁+ 8.6%、40 岁+ 13.7%;患者近 1 亿 | 王辰院士"中国成人肺部健康研究"(Lancet) | 2018 |
+| 哮喘 | 20 岁+ 4.2%,患者约 4570 万 | 王辰院士团队(Lancet) | 2019 |
+| 慢性肾病 | 8.2%(超 8000 万,知晓率仅 10%);另一口径 10.8% | 2018 年中国居民慢性病监测(JAMA Intern Med 2024);2012 年调查(NEJM) | 2024 / 2012 |
+| 幽门螺杆菌感染 | 自然人群感染率约 40%-50% | 多项流行病学调查及 2022 年 meta | 2022 |
+| 脂肪肝 | 患病率接近 30% | 流行病学综述 | 近年 |
+| 胃食管反流病 | 总体患病率约 8.7% | 2022 年 meta 分析 | 2022 |
+| 肠易激综合征 | 普通人群约 1.4%-11.5%(各调查差异大) | 中国流行病学调查汇总 | 多项 |
+| 慢性便秘 | 总患病率约 8.5%,60 岁+约 32.6% | 2022 年 meta 分析 | 2022 |
+| 胆结石 | 患病率约 7%-10% | 流行病学资料 | 2024 综述 |
+| 过敏性鼻炎 | 中国成人患者约 1.5 亿;患病率约 17.6% | 张罗等全国调查估算;2011 年调查 | 2017 / 2011 |
+| 超重/肥胖 | 成人超重肥胖率超 50%(BMI≥24 口径);6-17 岁约 19% | 《中国居民营养与慢性病状况报告2020》;2024 肥胖防控大会 | 2020 / 2024 |
+| 高尿酸血症 | 成人总体约 13.3%;18-29 岁青年男性 32.3% | 《中国高尿酸血症与痛风诊疗指南(2019)》;《痛风诊疗规范(2023)》 | 2019 / 2023 |
+| 骨质疏松 | 50 岁+ 19.2%(女性 32.1%,65 岁+女性 51.6%) | 首次中国骨质疏松症流行病学调查(国家卫健委) | 2018 |
+| 缺铁性贫血 | 全国居民贫血 9.7%(女性 12.6%、孕妇 17.2%) | 中国居民营养与健康状况监测 | 2010-2013 |
+| 肺癌 | 新发 106.06 万例,居第 1 位 | 国家癌症中心+IARC(JNCC) | 2022 数据 / 2024 发布 |
+| 结直肠癌 | 51.71 万例,第 2 位 | 同上 | 同上 |
+| 甲状腺癌 | 46.61 万例,第 3 位 | 同上 | 同上 |
+| 肝癌 | 36.77 万例,第 4 位 | 同上 | 同上 |
+| 胃癌 | 35.87 万例,第 5 位 | 同上 | 同上 |
+| 乳腺癌 | 女性第 2 位 | 同上 | 同上 |
+| 食管癌 | 男性第 5 位 | 同上 | 同上 |
+| 近视 | 儿童青少年总体 52.7%(小学 35.6%/初中 71.1%/高中 80.5%) | 国家疾控局监测 | 2023 |
+| 龋齿 | 12 岁儿童约 34.5%、5 岁约 70.9% | 第四次全国口腔健康流行病学调查 | 2015-2017 |
+| 注意缺陷多动障碍 | 患病率约 6%(meta 口径,待核实) | 流行病学 meta 分析 | 近年 |
+| 焦虑障碍 | 成人终生 7.6%,12 个月 4.98% | 中国精神卫生调查(CMHS,Lancet Psychiatry) | 2019 |
+| 抑郁障碍 | 成人 12 个月约 3.6% | 中国精神卫生调查(CMHS) | 2019 |
+| 偏头痛 | 年患病率 9.3%(女性 12.8%);约 2 亿患者 | 中国头痛流行病学调查 | 2021 |
+
+> **口径差异说明**:高血压(25.2% vs 27.5%)、CKD(8.2% vs 10.8%)存在不同调查口径,科普文案取最新发布口径并注明调查年份。
+> **待核实项**:湿疹/特应性皮炎、食物过敏、ADHD、失眠、IBD 具体患病率未获权威一手来源,预置清单保留条目但文案引用前需补充核实。
 
 ## 5. 后端设计
 
 ### 5.1 新 Entity / Mapper / Service
 
-- `entity/HealthStatus.java` — `@TableName("health_status")`,字段对应上表。
+- `entity/HealthStatus.java` — `@TableName("health_status")`,字段对应上表(`diseaseHistory`/`medications` 保持 `String`,JSON 字符串透传,**v2 无字段变更**)
 - `mapper/HealthStatusMapper.java` — MyBatis-Plus BaseMapper。
 - `service/HealthStatusService.java`(接口)+ `impl/HealthStatusServiceImpl.java`:
   - `HealthStatus getByUserId(Long userId)`
@@ -113,7 +187,7 @@ CREATE TABLE IF NOT EXISTS health_status (
 | 接口 | 说明 | 参数 | 返回 |
 |------|------|------|------|
 | `POST /api/health-status/get` | 查询当前账号健康现状 | `{}`(从 JWT 取 userId) | `HealthStatus` 或 `null` |
-| `POST /api/health-status/save` | 保存/更新(upsert) | HealthStatus 字段(JSON body) | 保存后的 `HealthStatus` |
+| `POST /api/health-status/save` | 保存/更新(upsert) | HealthStatus 字段(JSON body,diseaseHistory/medications 为 JSON 字符串) | 保存后的 `HealthStatus` |
 
 > 成员饮食偏好**复用现有接口**:`POST /api/diet/preferences/current-member`(已有 `DietPreferencesService.getCurrentMemberPreferences`,前端已有 `getDietPreferences`),不新增重复端点。
 
@@ -141,14 +215,52 @@ if (dietPrefsStr != null && !dietPrefsStr.trim().isEmpty()) {
 
 ## 6. 前端设计
 
-### 6.1 独立档案页 `pages/health/health-status-form.vue`(新增)
+### 6.1 独立档案页 `pages/health/health-status-form.vue`(修改,非新增)
 
-- 注册到 `pages.json`(health 分包)。
-- 表单字段:身高、体重、血压、血糖、血脂(多行,每行 name/value/unit/note)、疾病史(标签+自定义)、用药/治疗(标签+自定义)、备注。
+- 注册到 `pages.json`(health 分包,v1 已实现)。
+- 表单字段:身高、体重、血压、血糖、血脂(多行,每行 name/value/unit/note)、**疾病史(v2 重写)**、**用药/治疗(v2 增强)**、备注。
 - 进入时 `POST /api/health-status/get` 回显;保存 `POST /api/health-status/save`。
 - 校验:身高 30–250、体重 3–300、血压 `\d{2,3}/\d{2,3}`、血糖 1–30;前端校验 + 后端兜底。
 - 至少填写 1 项才可保存(避免空记录)。
-- 入口:健康主页(`health-main/index.vue`)「方案制定」旁加「健康档案」按钮。
+- 入口:健康主页(`health-main/index.vue`)「方案制定」旁加「健康档案」按钮(v1 已实现)。
+
+#### 6.1.1 疾病史区(v2 重写)
+
+**预置疾病清单(两源合并去重,约 50 项)**:
+
+| 分类 | 疾病(★ = 菌群报告病症风险源) |
+|------|------------------------------|
+| 🫀 心血管代谢 | 高血压、糖尿病、血脂异常、冠心病、脑卒中、心力衰竭、心房颤动、慢阻肺、心脑血管疾病★、II型糖尿病★、肥胖★、超重、高尿酸血症、骨质疏松 |
+| 🍽️ 消化系统 | 幽门螺杆菌感染、脂肪肝、慢性胃炎、胃食管反流、肠易激综合征★、炎症性肠炎★、慢性便秘★、感染性腹泻★、胆结石、炎症性肠病、肝病★、胃病★、胆病★、肾病★、腹胀★ |
+| 🤧 过敏免疫 | 过敏性鼻炎★、哮喘、湿疹、食物过敏、自体免疫病★ |
+| 🧠 精神神经 | 抑郁症★、失眠★、焦虑障碍、偏头痛、自闭症★ |
+| 🫁 呼吸系统 | 肺部感染或疾病★、肺部疾病★ |
+| 🎗️ 肿瘤 | 结直肠癌★、肺癌、甲状腺癌、肝癌、胃癌、乳腺癌、食管癌 |
+| 🧒 儿童青少年 | 近视、龋齿、注意缺陷多动障碍、甲状腺疾病★ |
+| 其他 | 缺铁性贫血、贫血 |
+
+> **菌群源说明**:★ 标注的疾病来自 `PdfParseService.KNOWN_DISEASE_RISKS` 白名单(20 项)+ 报告指标知识库,与肠道菌群报告"疾病风险评估"板块一一对应(如炎症性肠炎、肠易激综合征、II型糖尿病、心脑血管疾病等),勾选后可关联菌群报告风险值。互联网统计源数据(患病率/出处/年份)见 4.3 节,供科普文案使用。
+
+**分类折叠交互**:
+- 每组标题可展开/收起(`uni-collapse` 或自实现折叠),默认展开前 3 组;已选中的疾病自动展开所在组。
+- 组内标签多选(`tag-active` 高亮),点选=选中/取消。
+- ★ 疾病加「报告关联」小角标,点击弹 `showModal` 说明该病与菌群报告的关系(如"菌群报告可评估炎症性肠炎风险")。
+
+**已选疾病区**:
+- 表单顶部或底部展示已选疾病列表,每条含:疾病名 + 「设确诊时间」按钮 + 删除。
+- 「设确诊时间」→ 年月选择器(`picker mode="date"` 前端限制到年月,或自实现年月滚动选择器)→ 回填 `diagnosedAt`。
+- 确诊时间展示格式:`yyyy-MM`(如 `2021-03`)。
+
+**自定义补充(兜底)**:
+- 保留 v1 的「输入其他疾病 + 添加」输入框,作为预置清单未覆盖时的补充入口。
+- 补充的疾病同样可设确诊时间。
+- 补充条目以 `tag-active` 样式呈现于已选区,可删除。
+
+#### 6.1.2 用药/治疗区(v2 增强)
+
+- 每药一行(保持 v1):药名输入 + **开始服药年月选择器**(新增)+ **已服时长展示**(自动计算)+ 服药时间输入(v1 保留)+ 删除。
+- 记不清开始日期:点「记不清」→ 弹出时长文本输入框("约 X 个月/年")→ 写入 `durationText`。
+- 已选时长自动计算规则:`当前年月 - startDate` → "已服 X 个月"(<12 个月)/"已服 X 年"(≥12 个月,取整)。
 
 ### 6.2 出方案流程 `health-plan-summary.vue`(修改)
 
@@ -182,6 +294,9 @@ if (dietPrefsStr != null && !dietPrefsStr.trim().isEmpty()) {
 | 数值非法(身高 999、血压 abc) | 前端校验拦截 + 后端返回 `Result.error` |
 | 空提交 | 至少填 1 项,否则返回 `Result.error("请至少填写一项")` |
 | 超过 6 个月未更新 | 出方案弹窗提示「上次填写时间较早,建议更新」(可跳过) |
+| **v1 旧数据回显** | `{name, note}` 无 `diagnosedAt`/`startDate`/`durationText` → 前端置空默认值,不报错 |
+| **确诊时间晚于当前** | 年月选择器 `end` 限制为当前年月,防未来日期 |
+| **服药开始日期晚于当前** | 同上限制 |
 
 ## 8. 测试策略
 
@@ -190,11 +305,16 @@ if (dietPrefsStr != null && !dietPrefsStr.trim().isEmpty()) {
   - get 未填返回 null
 - **前端**:`node --check` 语法校验(不打包,HBuilderX 打包)。
 - **手工验证清单**:
-  1. 档案页首次填写 → 保存 → 重新进入回显
+  1. 档案页首次填写 → 保存 → 重新进入回显(含确诊时间/服药时长)
   2. 重复保存 → 仍只有一行记录
-  3. 未填状态点生成方案 → 弹窗出现 → 直接生成可继续
-  4. 填写后点生成方案 → 不再弹窗 → AI 返回方案
-  5. 校验非法值(身高 999、血压 abc)→ 拦截提示
+  3. 疾病预置分类折叠展开、多选、★ 角标说明
+  4. 选中的疾病设确诊时间(年月选择器)→ 保存回显
+  5. 自定义补充疾病 → 设确诊时间 → 保存回显
+  6. 用药行:开始日期自动计算时长;「记不清」手动填时长文本
+  7. **v1 旧数据(`{name,note}` / `{name,time}`)回显兼容,不报错**
+  8. 未填状态点生成方案 → 弹窗出现 → 直接生成可继续
+  9. 填写后点生成方案 → 不再弹窗 → AI 返回方案
+  10. 校验非法值(身高 999、血压 abc)→ 拦截提示
 
 ## 9. 非目标(本次不做)
 
@@ -202,3 +322,5 @@ if (dietPrefsStr != null && !dietPrefsStr.trim().isEmpty()) {
 - 不做过敏源/食物喜好的新表迁移——继续用 `diet_preferences`。
 - 不做 AiContextService 新 intent——采用前端随请求传参方案。
 - 不做 LangGraph 侧改造(langgraph 服务当前为空壳,实际走 Dify fallback;若后续启用 langgraph,传参透传已在前端 api.js 层预留)。
+- **不做疾病预置清单的管理端配置化**——清单为代码常量(v2 固化约 50 项);后续如需运营维护,另立项。
+- **不做菌群报告风险值与疾病勾选自动联动**(如勾选"炎症性肠炎"自动拉取该病风险值)——本期仅做标记与说明文案;联动留待后续。