Files
hms/docs/superpowers/specs/2026-04-24-hms-miniprogram-iteration-design.md
iven 19be2a08c7
Some checks failed
CI / security-audit (push) Has been cancelled
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
docs(miniprogram): 新增小程序迭代设计规格 + 25 Task 实施计划
设计规格:4 Sprint 混合策略(Sprint 0 修基础 → Sprint 1-3 模块打磨),
覆盖 18 个问题,含健康数据、预约挂号、报告详情、安全加固、增长基础。

实施计划:25 个 Task,4 个 Chunk,经 4 轮审查修复关键问题:
- Task 10 依赖后端 today 端点 status/reference_range 字段
- Task 14/15 补全 StepIndicator 连接线 + WeekCalendar 完整实现
- Task 21 request.ts Token 加密绕过修复
- Task 22 手机号解密前后端 API 契约明确(推荐 code 模式)
- Task 24 埋点补充核心页面手动调用
- Task 25 hooks 无条件调用修复
2026-04-24 12:08:13 +08:00

19 KiB
Raw Blame History

HMS 患者小程序迭代设计规格

版本: v1.0 日期: 2026-04-24 状态: 草案 关联: 小程序初版设计 2026-04-23-hms-miniprogram-design.md


1. 概述

1.1 背景

小程序初版已完成 21 个页面、7 个 API service 的基础实现,覆盖登录、健康数据、预约挂号、检验报告、随访管理、用药提醒、健康资讯、个人中心。当前处于开发阶段,工程质量和用户体验存在明显短板,距测试阶段尚有差距。

1.2 问题全景

优先级 问题 影响
P0 大量重复代码profile/reports ≈ report/index, profile/followups ≈ followup/index 维护成本翻倍
P0 预约详情通过 Storage 缓存传递而非 API 获取 数据不一致
P0 EmptyState 导入方式不一致导致运行时报错 页面崩溃
P0 手机号绑定后端硬编码 "13800000000" 无法上线
P0 getTodaySummary() 调用的后端端点不存在 首页/健康页数据无法加载
P1 ErrorState 组件定义但未使用 错误处理不统一
P1 mixins.scss 定义但未使用 样式重复内联
P1 无全局错误边界 页面崩溃无兜底
P1 tryRefreshToken 静默吞异常 调试困难
P1 趋势图缓存永不过期 数据过时
P1 随访详情获取低效listTasks().find() 性能浪费
P1 首页/健康页缺少 loading 状态 体验空白
P1 用药提醒纯本地 Storage 换设备即丢失(后续版本解决)
P2 路径别名 @/* 未使用 代码可读性差
P2 无 schema 验证库 表单验证脆弱
P2 趋势图纯 CSS 柱状图 无交互能力
P2 用药提醒时间选择器未实现 功能不完整
P2 无日志/埋点/上报 无法追踪问题

1.3 迭代策略:混合策略

采用先基建再模块的混合策略,分 4 个 Sprint 交付:

Sprint 0 (2-3天)     Sprint 1 (3-4天)      Sprint 2 (3-4天)      Sprint 3 (4-5天)
┌──────────────┐    ┌──────────────┐    ┌──────────────┐    ┌──────────────┐
│  工程基础修复  │ →  │ 健康数据打磨  │ →  │ 预约+通知    │ →  │ 报告/随访/   │
│              │    │              │    │              │    │ 安全+增长    │
│ · 消除重复代码│    │ · ECharts图表│    │ · 步骤指示器  │    │ · 指标卡片   │
│ · 统一错误处理│    │ · 缓存TTL    │    │ · 周视图日历  │    │ · Token加密  │
│ · 修复数据传递│    │ · zod验证     │    │ · 订阅消息    │    │ · 手机号解密 │
│ · 统一Loading │    │ · 状态色卡片  │    │ · 时段可视化  │    │ · 埋点+分享  │
└──────────────┘    └──────────────┘    └──────────────┘    └──────────────┘

原则Sprint 0 铺路,后续每个 Sprint 都受益于基础设施改善。Sprint 0 只修不建,不引入新依赖。


2. Sprint 0工程基础修复

目标:消除最痛的工程问题,为后续所有 Sprint 铺路。约束 2-3 天完成。

2.1 修复阻断性 API 端点缺失

现状:前端 services/health.tsgetTodaySummary() 调用 GET /health/vital-signs?date=today,但后端路由中不存在此端点。后端仅有 GET /health/patients/{id}/vital-signs(需 patient_id 路径参数)。这意味着首页"今日健康"卡片和健康页的数据从一开始就无法加载

方案

  • 后端在 erp-health 新增小程序专用端点 GET /health/vital-signs/today,通过 JWT user_id 自动关联 patient类似已有的 GET /health/vital-signs/trend 模式)
  • 前端 services/health.tsgetTodaySummary() 调整为调用新端点
  • 此项为 Sprint 0 最高优先级,阻塞首页和健康页基本功能

涉及文件

  • 后端新增:erp-health handler + 路由注册
  • 修改:services/health.ts

2.2 消除重复页面

现状pages/report/indexpages/profile/reports/index 几乎完全重复,pages/followup/indexpages/profile/followups/index 同理。且 report/indexfollowup/index 没有明确的导航入口。

方案

  1. 删除 pages/report/indexpages/followup/index 及其 SCSS 文件
  2. app.config.ts 移除对应路由注册
  3. 首页快捷入口和 profile 菜单统一指向 profile/reportsprofile/followups
  4. 如果后续需要独立入口,则抽取共享组件 components/ReportListcomponents/FollowupList,两个页面只做薄壳路由

涉及文件

  • 删除:pages/report/index.tsxpages/report/index.scss
  • 删除:pages/followup/index.tsxpages/followup/index.scss
  • 修改:app.config.ts(移除路由)
  • 修改:pages/index/index.tsx(快捷入口路径)

2.2 统一错误处理

现状ErrorState 组件已定义但未被任何页面使用,各页面内联 showToast 错误提示。无全局错误边界。

方案

  1. 所有列表页、详情页统一使用 ErrorState 组件,替换内联错误提示
  2. app.tsx 添加 React Error Boundary 组件,兜底页面崩溃
  3. 新建 components/ErrorBoundary/index.tsx
  4. 修复 tryRefreshToken 的 catch 块,添加 console.error 日志

涉及文件

  • 新增:components/ErrorBoundary/index.tsx
  • 修改:app.tsx(包裹 ErrorBoundary
  • 修改:所有列表页和详情页(替换内联错误处理为 ErrorState
  • 修改:services/request.tstryRefreshToken 日志)

2.3 修复数据传递问题

预约详情

  • 移除 appointment_detail_cache Storage 传递
  • 改为进入页面时通过 GET /health/appointments/:id 获取数据
  • 后端需新增此端点(当前仅有列表 GET、创建 POST、状态更新 PUT,缺少单条查询 GET

随访详情

  • 后端需新增 GET /health/follow-up-tasks/:id 单条查询端点(当前 {id} 路由仅注册了 PUTDELETE,缺少 GET
  • 前端替换 listTasks().find() 为直接按 ID 查询

注意:以上后端新增端点为 Sprint 0 前置阻塞项。如果后端资源有限,前端先做"调用端点"的准备代码,后端并行实现。

涉及文件

  • 修改:pages/appointment/detail/index.tsx
  • 修改:services/appointment.ts(新增 getDetail 方法)
  • 修改:services/followup.ts(新增 getTaskDetail 方法)
  • 后端新增:erp-health 预约单条查询 + 随访单条查询端点

2.4 统一 Loading 状态

现状:首页和健康页的 loading 状态已在 store 中定义但未在 UI 层消费。详情页使用内联 <Text>加载中...</Text>

方案

  1. 首页和健康页在数据加载时展示 Loading 组件
  2. 所有详情页统一使用 Loading 组件替换内联文字
  3. 预约创建页三步骤切换时也展示 loading

涉及文件

  • 修改:pages/index/index.tsx(消费 loading 状态)
  • 修改:pages/health/index.tsx(消费 loading 状态)
  • 修改:所有详情页 tsx替换内联加载文字

2.5 杂项修复

项目 方案
EmptyState 导入 bug 首页 import { EmptyState } 改为 import EmptyState(默认导入)
路径别名启用 services/stores/ 层的 import 逐步改为 @/ 别名
mixins.scss 复用 新写的页面样式使用 @include card@include flex-center@include safe-bottom

3. Sprint 1健康数据模块打磨

目标:升级健康数据录入、展示和趋势分析体验,从"能用"到"好用"。

3.1 健康卡片状态色

现状:四张健康卡片(血压/心率/血糖/体重)样式统一灰色,无状态区分。

方案

每张卡片根据指标状态着色:

  • 正常:左侧绿色边条 + 绿色"正常 ─"标签
  • 偏高:左侧红色边条 + 红色"偏高 ▲{差值}"标签
  • 偏低:左侧红色边条 + 红色"偏低 ▼{差值}"标签
  • 无数据:灰色,保持现状

异常指标数值变红,卡片底部显示参考范围。

后端配合:后端需在新增的 GET /health/vital-signs/today 端点中返回 statusnormal/high/lowreference_range。前端 TodaySummary 类型同步新增 reference_range 字段(当前已有 status 字段但后端无对应返回)。

涉及文件

  • 修改:pages/health/index.tsx(卡片样式逻辑)
  • 修改:pages/index/index.tsx(首页健康卡片同步更新)
  • 修改:services/health.ts(类型定义增加 status 字段)

3.2 ECharts 趋势图

现状:纯 CSS div 柱状图,无交互、无缩放、无 tooltip。

方案

引入 echarts-taro3-react(设计规格中已规划)。前置条件Sprint 1 开始前需做技术预研spike验证 echarts-taro3-react 在 Taro 4.2.0 + webpack5 下的兼容性。如果不可用,备选方案为 echarts-for-weixin + 手动封装为 React 组件。

实现:

  • 折线图:数据点连线,异常点标红放大
  • 参考范围色带:正常值区间以半透明绿色背景显示
  • Tooltip:长按/点击显示具体数值和日期
  • 时间范围切换7天/30天/90天 三个 tab
  • 缓存 TTL:趋势数据缓存 5 分钟后自动过期,强制重新请求

涉及文件

  • 新增:components/TrendChart/index.tsxcomponents/TrendChart/index.scss
  • 重写:pages/health/trend/index.tsx
  • 修改:stores/health.ts(缓存 TTL 机制)
  • 新增依赖:echarts-taro3-react

3.3 表单验证升级

现状:所有表单验证为手动 if 判断,无 schema 约束。

方案

引入 zod~3KB gzip为每个表单定义验证 schema

// 示例:体征录入验证
const vitalSignSchema = z.object({
  indicator_type: z.enum(['blood_pressure', 'heart_rate', 'blood_sugar', 'weight']),
  value: z.number().positive(),
  extra: z.object({ systolic: z.number().min(60).max(250).optional(),
                     diastolic: z.number().min(40).max(150).optional() }).optional(),
  measured_at: z.string().datetime().optional(),
  note: z.string().max(200).optional()
});

异常值即时警告(如收缩压 > 180 显示红色提示"请及时就医")。

录入成功后自动刷新首页卡片 + 清除趋势缓存。

涉及文件

  • 修改:pages/health/input/index.tsxzod schema 验证)
  • 修改:stores/health.ts(录入成功后清除缓存)
  • 新增依赖:zod

4. Sprint 2预约挂号 + 通知触达

目标:优化预约三步流程体验,建立微信订阅消息通知机制。

4.1 三步流程升级

现状:三个步骤(选科室 → 选医生 → 选日期时段)无进度指示,排班信息为纯文字列表。

方案

步骤指示器

  • 新增 components/StepIndicator/index.tsx
  • 顶部固定 1→2→3 步骤条,当前步骤高亮,已完成步骤可点击回退
  • 步骤间切换带过渡动画

科室选择

  • 从文字列表改为宫格卡片(图标 + 科室名 + 医生数)
  • 每个科室卡片可点击,选中后高亮边框

排班日历

  • 新增 components/WeekCalendar/index.tsx 周视图日历
  • 有排班的日期标记绿点,无排班的日期灰色
  • 点击日期展示该日可用时段卡片
  • 时段卡片按剩余名额着色:>3 绿色、1-3 橙色、0 灰色不可选

涉及文件

  • 新增:components/StepIndicator/index.tsx
  • 新增:components/WeekCalendar/index.tsx
  • 重写:pages/appointment/create/index.tsx

4.2 微信订阅消息

现状:无任何推送通知机制。

方案

  1. 后端在微信公众平台注册订阅消息模板:

    • 预约就诊提醒(就诊前 1 天推送)
    • 随访任务提醒(截止前 1 天推送)
    • 报告出具通知(新报告发布时推送)
  2. 前端在关键场景引导用户订阅:

    • 预约成功后弹出订阅授权
    • 随访提交后引导订阅下次提醒
  3. 后端定时任务检查待推送消息并触发

  4. 降级设计:用户拒绝订阅时,消息仍写入 erp-message 消息中心。小程序"我的"页面顶部显示未读消息数量红点,作为消息触达的备选渠道。

涉及文件

  • 修改:pages/appointment/detail/index.tsx(预约成功后订阅引导)
  • 修改:pages/followup/detail/index.tsx(随访提交后订阅引导)
  • 后端新增:erp-server 订阅消息模板注册 + 定时推送任务

5. Sprint 3报告/随访/个人中心 + 安全 + 增长

目标:打磨剩余模块,完成安全加固和增长基础建设,达到可测试状态。

5.1 报告详情页升级

现状:所有指标卡片样式相同,无法一眼区分正常/异常。

方案

指标卡片按状态着色:

  • 正常:绿色背景 + 绿色"✓ 正常"标签 + 绿色数值
  • 偏高:红色背景 + 红色"↑ 偏高"标签 + 红色数值
  • 偏低:红色背景 + 红色"↓ 偏低"标签 + 红色数值

顶部汇总标签:2 项异常 · 1 项正常,一眼掌握整体状况。

涉及文件

  • 修改:pages/report/detail/index.tsx
  • 修改:pages/profile/reports/index.tsx(如果仍独立存在)

5.2 随访 UX 细节

  • 任务卡片增加截止日期倒计时("还剩 2 天",红色紧迫)
  • 过期任务灰色标记
  • 提交记录后增加"提交成功"确认动画checkmark 缩放)

涉及文件

  • 修改:pages/profile/followups/index.tsx
  • 修改:pages/followup/detail/index.tsx

5.3 个人中心改进

用药提醒

  • 实现时间选择器 Picker替换当前静态文本
  • 增加"提醒开关"enabled/disabled
  • 注:用药提醒数据仍为本地 Storage 存储,后端同步作为后续版本事项。MVP 阶段接受"换设备即丢失"的限制。

就诊人管理

  • 增加编辑功能(当前只能添加不能编辑)
  • 复用 family-add 页面,传入已有数据进入编辑模式

涉及文件

  • 修改:pages/profile/medication/index.tsx(时间 Picker
  • 修改:pages/profile/family/index.tsx(编辑入口)
  • 修改:pages/profile/family-add/index.tsx(编辑模式支持)

5.4 安全加固

5.4.1 Token 安全

现状Access Token 和 Refresh Token 明文存储在 Taro.setStorageSync

方案

MVP 阶段采用简化方案:微信小程序的 Storage 本身有沙箱隔离,明文存储的边际风险有限。做以下最低成本改进:

  • 使用 wx.getRandomValues() 生成随机密钥,单独 key 存储
  • Token 存储时用此密钥做简单混淆XOR 或 AES-ECB 单块加密)
  • 目的:防止 Storage 被直接明文读取,非追求密码学安全级别

后续版本:如果合规要求提高,再升级为完整的 AES-GCM 方案。

涉及文件

  • 新增:utils/crypto.ts(轻量混淆工具)
  • 修改:stores/auth.tsStorage 读写走混淆层)

5.4.2 手机号真实解密

现状wechat_service.rs 第 82 行硬编码 "13800000000"

方案

  • 后端接入微信 phonenumber.getPhoneNumber 接口
  • 使用 encryptedData + iv + session_key 解密真实手机号
  • 前端无需改动(已传递正确的 encryptedData 和 iv

涉及文件

  • 修改:crates/erp-auth/src/service/wechat_service.rs

5.4.3 用户协议与隐私政策

  • 新增 pages/agreement/index.tsx 页面
  • 登录页增加"阅读并同意《用户协议》和《隐私政策》"勾选
  • 权限使用说明文案(获取手机号用途声明)

涉及文件

  • 新增:pages/agreement/index.tsx
  • 修改:pages/login/index.tsx(协议勾选)
  • 修改:app.config.ts(新增路由)

5.5 增长基础

5.5.1 数据埋点

新增 services/analytics.ts,轻量事件记录:

// 核心事件类型
type AnalyticsEvent =
  | { type: 'page_view'; page: string; duration_ms?: number }
  | { type: 'feature_use'; feature: string; action: string }
  | { type: 'error'; message: string; stack?: string }
  • 页面进入/离开自动记录 page_view
  • 关键操作(录入数据、创建预约、提交随访)记录 feature_use
  • 捕获的错误记录 error
  • MVP 阶段:事件写入本地 console.info + Taro Storage 缓存(最近 100 条)
  • 后续版本:批量上报到后端 POST /api/v1/analytics/events

涉及文件

  • 新增:services/analytics.ts
  • 修改:app.tsx(全局页面进入/离开监听)

5.5.2 分享能力

  • 文章详情页支持分享到微信好友/朋友圈
  • 自定义分享卡片(标题 + 摘要 + 封面图)通过 onShareAppMessageonShareTimeline 实现

后续版本:健康报告 Canvas 分享图片生成、PC 扫码登录。

涉及文件

  • 修改:pages/article/detail/index.tsxonShareAppMessage + onShareTimeline

6. 文件变更总览

新增文件

文件 说明 Sprint
components/ErrorBoundary/index.tsx 全局错误边界 0
components/TrendChart/index.tsx ECharts 趋势图 1
components/StepIndicator/index.tsx 步骤指示器 2
components/WeekCalendar/index.tsx 周视图日历 2
pages/agreement/index.tsx 用户协议/隐私政策 3
utils/crypto.ts Token 加密工具 3
services/analytics.ts 数据埋点 3

删除文件

文件 原因 Sprint
pages/report/index.tsx 与 profile/reports 重复 0
pages/report/index.scss 同上 0
pages/followup/index.tsx 与 profile/followups 重复 0
pages/followup/index.scss 同上 0

新增依赖

依赖 用途 体积 Sprint
echarts-taro3-react 交互式图表 封装层 ~5KB + echarts 按需 ~100-200KB (gzip) 1
zod 表单 schema 验证(长期投资) ~3KB (gzip) 1

7. 约束与风险

风险 应对策略
ECharts 增大包体积 按需引入 echarts 模块,不引入全量包;监控主包大小不超过 2MB
微信订阅消息需用户主动触发 在预约成功、随访提交等高意愿场景引导订阅
Token 加密增加启动耗时 AES-GCM 加解密 < 1ms可忽略
zod 增加包体积 3KB gzip远小于自写验证代码量
Sprint 0 范围膨胀 严格只修不建,不引入新依赖,不重构架构
后端端点未实现阻塞前端 Sprint 0/1 的端点基本已实现Sprint 2 订阅消息、Sprint 3 analytics 需后端配合

8. 验收标准

每个 Sprint 完成时必须满足:

  • pnpm build:weapp 生产构建通过
  • 微信开发者工具无编译错误
  • 所有涉及页面真机预览功能正常
  • 无 console.error 或未捕获异常
  • 已修改的页面 loading/error/empty 三态完整
  • 所有代码已提交