Files
hms/docs/superpowers/specs/2026-04-23-hms-miniprogram-design.md
iven 9ef65b9a9f
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
feat(health+miniprogram): 预约/报告/随访/资讯/家庭管理 — Chunk 4-6
后端:
- 添加 articles 表迁移 + Entity + Service + Handler
- 健康数据趋势 API (get_mini_trend) 注册路由
- article CRUD (list/get) + DTO

前端 (11个新页面 + 5个服务):
- 预约挂号: 列表/创建向导/详情页
- 报告管理: 列表/详情页
- 随访管理: 任务列表/记录详情页
- 资讯文章: 文章详情页
- 个人中心: 就诊人管理/新增/我的报告/我的随访/用药提醒/设置
- 更新 app.config.ts 注册全部路由
- 更新 profile/article 页面为真实功能
2026-04-24 00:58:40 +08:00

510 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HMS 患者小程序设计规格
> **版本**: v1.0
> **日期**: 2026-04-23
> **状态**: 草案
> **关联**: 健康模块设计规格 `2026-04-23-health-management-module-design.md`
---
## 1. 概述
### 1.1 产品定位
HMS 患者小程序是**综合健康管理入口**,面向体检中心/医疗机构的患者。覆盖体检预约、报告查询、健康数据长期监测、随访管理、家庭健康管理等场景。
医护端以 PC 管理后台(`apps/web/`)为主力,小程序聚焦患者体验。医护端小程序可在后续按需补一个轻量版(随访提醒、排班查看),不在本规格范围内。
### 1.2 核心决策
| 维度 | 决策 | 原因 |
|------|------|------|
| 技术选型 | Taro 4 + React 19 | 与 Web 端 React 技能复用,支持多端编译 |
| 架构方案 | 直连后端 | MVP 阶段最务实,复用 erp-server API |
| 登录方式 | 微信授权 + 手机号补充 | 降低门槛 + 确保身份可靠 |
| 代码位置 | `apps/miniprogram/`Monorepo | 方便接口同步,共享类型定义 |
| 目标平台 | 微信小程序优先 | 覆盖最广泛用户,后续可扩展 |
| 数据录入 | 手动 + 蓝牙预留接口 | MVP 快速交付,后续对接设备 |
| 视觉风格 | 医疗清新(青色主调) | 专业可靠,沿用现有 HTML 原型风格 |
### 1.3 MVP 功能范围
**MVP 包含7 个功能模块):**
1. 登录 + 个人中心
2. 健康数据录入 + 趋势图
3. 预约挂号
4. 报告查询
5. 随访管理
6. 家庭健康管理(就诊人切换)
7. 健康资讯 + 用药提醒
**后续版本:**
- 在线咨询即时通讯WebSocket 长连接)
---
## 2. 项目结构
```
apps/miniprogram/
├── config/ # Taro 编译配置
│ ├── index.ts # 通用配置
│ ├── dev.ts # 开发环境
│ └── prod.ts # 生产环境
├── project.config.json # 微信小程序项目配置
├── src/
│ ├── app.config.ts # Taro 全局配置TabBar、页面路由
│ ├── app.tsx # 入口组件
│ ├── app.scss # 全局样式(医疗清新主题变量)
│ ├── components/ # 通用组件
│ │ ├── HealthCard/ # 健康指标卡片(血压/血糖/体重)
│ │ ├── AppointmentCard/ # 预约卡片
│ │ ├── ReportItem/ # 报告列表项
│ │ ├── FamilyPicker/ # 就诊人切换器
│ │ ├── EmptyState/ # 空状态占位
│ │ └── TrendChart/ # 趋势图echarts-taro3-react
│ ├── pages/
│ │ ├── index/ # 首页(今日健康+快捷入口+待办)
│ │ ├── health/ # 健康数据(录入+趋势图)
│ │ ├── appointment/ # 预约(列表+新建预约)
│ │ ├── report/ # 报告(体检报告+化验单)
│ │ ├── followup/ # 随访(任务+问卷填写)
│ │ ├── article/ # 健康资讯(文章列表+详情)
│ │ ├── profile/ # 我的(个人信息+就诊人管理+设置)
│ │ └── login/ # 登录(微信授权+手机号)
│ ├── services/ # API 调用层
│ │ ├── request.ts # 封装 Taro.requestJWT 注入、错误处理)
│ │ ├── auth.ts # 登录/刷新 token
│ │ ├── health.ts # 健康数据 CRUD
│ │ ├── appointment.ts # 预约 CRUD
│ │ ├── report.ts # 报告查询
│ │ ├── followup.ts # 随访任务/记录
│ │ └── article.ts # 资讯/科普
│ ├── stores/ # Zustand 状态管理
│ │ ├── auth.ts # 登录态、用户信息、就诊人列表
│ │ └── health.ts # 健康数据缓存
│ ├── utils/
│ │ ├── bluetooth.ts # 蓝牙接口预留MVP 不实现)
│ │ ├── format.ts # 日期/数值格式化
│ │ └── constants.ts # 常量定义
│ └── styles/
│ ├── variables.scss # 主题变量(青色主调)
│ └── mixins.scss # 常用样式混入
├── package.json
└── tsconfig.json
```
**设计原则:**
- services 层与 Web 端 `apps/web/src/api/` 职责对齐,用 Taro.request 替代 fetch
- stores 复用 Zustand 模式,与 Web 端保持一致的状态管理风格
- 组件命名 PascalCase 目录,与 Web 端风格统一
- MVP 阶段不强抽取 `packages/shared/`,等两端跑起来后根据重复度决定
---
## 3. 认证流程
### 3.1 整体流程
```
用户打开小程序
检查本地 storage 有无有效 JWT
├── 有且未过期 → 直接进入首页
└── 无或已过期 ↓
Step 1: 微信静默登录
wx.login() → code
→ POST /api/v1/auth/wechat/login { code }
→ 后端用 code 换 openid查找绑定用户
├── 已绑定 → 签发 JWT { token, user }
└── 未绑定 → 返回 { need_bind: true, openid }
Step 2: 手机号绑定(仅新用户)
wx.getPhoneNumber 按钮组件 → encryptedData + iv
→ POST /api/v1/auth/wechat/bind-phone { openid, encryptedData, iv }
→ 后端解密手机号,创建/关联 user + patient 档案
→ 签发 JWT { token, user, patient }
Step 3: 补充档案(首次绑定后)
→ 引导填写姓名、性别、出生日期、身份证号(可选)
→ PUT /api/v1/health/patients/me { name, gender, birthday }
```
### 3.2 后端新增内容
**`erp-auth` 新增:**
| 新增 | 说明 |
|------|------|
| `wechat_users` 表 | `id, openid, union_id, user_id, phone, created_at, updated_at` |
| `POST /api/v1/auth/wechat/login` | code → openid 查询,返回绑定状态 |
| `POST /api/v1/auth/wechat/bind-phone` | 绑定手机号,创建 user + patient |
| `GET /api/v1/auth/wechat/qrcode` | 生成带参数小程序码PC 端扫码登录场景) |
`wechat_users` 表必须包含 `tenant_id`(多租户隔离)和标准审计字段(`created_at`, `updated_at`, `deleted_at`)。
### 3.3 Token 策略
| Token | 有效期 | 存储 |
|-------|--------|------|
| Access Token (JWT) | 15 分钟 | 内存 + Taro.setStorage |
| Refresh Token | 7 天 | Taro.setStorage |
自动刷新机制:`services/request.ts` 拦截 401 → 调用 `POST /auth/refresh` → 重试原请求。刷新失败则跳转登录页。
### 3.4 多就诊人
- 一个微信账号可管理多个 patient本人 + 家人)
- 切换就诊人时请求 header 带 `X-Patient-Id`
- 后端校验该 patient 属于当前 user
---
## 4. 页面结构与导航
### 4.1 Tab Bar
底部导航栏 5 个入口:
| Tab | 图标 | 页面路径 |
|-----|------|----------|
| 首页 | 🏠 | /pages/index/index |
| 健康 | 📊 | /pages/health/index |
| 预约 | 📅 | /pages/appointment/index |
| 资讯 | 📰 | /pages/article/index |
| 我的 | 👤 | /pages/profile/index |
### 4.2 页面层级
```
Tab: 首页 /pages/index
├── /pages/notifications/index # 通知列表
└── /pages/followup/detail/index # 随访任务详情
Tab: 健康 /pages/health
├── /pages/health/input/index # 录入数据
├── /pages/health/trend/index # 指标趋势
└── /pages/health/history/index # 历史记录
Tab: 预约 /pages/appointment
├── /pages/appointment/create/index # 新建预约
└── /pages/appointment/detail/index # 预约详情
Tab: 资讯 /pages/article
└── /pages/article/detail/index # 文章详情
Tab: 我的 /pages/profile
├── /pages/profile/family/index # 就诊人管理
├── /pages/profile/family-add/index # 添加就诊人
├── /pages/profile/reports/index # 我的报告
├── /pages/profile/followups/index # 我的随访
├── /pages/profile/medication/index # 用药提醒
└── /pages/profile/settings/index # 设置
独立页面(不在 Tab 内):
├── /pages/login/index # 登录
└── /pages/login/profile/index # 档案补全
```
### 4.3 首页布局
```
┌─────────────────────────────┐
│ 问候栏(渐变青色背景) │ 用户名 + 日期 + 通知铃铛
├─────────────────────────────┤
│ 今日健康卡片(上浮 -20px │ 血压/心率/血糖/体重 2×2 网格
├─────────────────────────────┤
│ 快捷服务4 宫格) │ 录数据/预约/报告/随访
├─────────────────────────────┤
│ 即将到来 │ 最近 1 条预约卡片
├─────────────────────────────┤
│ 待办随访 │ 最多 2 条待办 + 查看全部
├─────────────────────────────┤
│ [ 首页 ] [ 健康 ] [ 预约 ] [ 资讯 ] [ 我的 ] │
└─────────────────────────────┘
```
---
## 5. 核心功能数据流
### 5.1 健康数据录入
```
选择指标类型 → 输入数值 + 测量时间 → 添加备注(可选)→ POST /vital-signs
成功 → 更新首页卡片 + 趋势缓存
失败 → Toast + 本地暂存
```
**MVP 支持的指标类型:**
| 指标 | 单位 | 输入控件 |
|------|------|---------|
| 收缩压 / 舒张压 | mmHg | 两个数字输入框 |
| 心率 | bpm | 数字输入框 |
| 空腹血糖 | mmol/L | 数字输入框 |
| 餐后血糖 | mmol/L | 数字输入框 |
| 体重 | kg | 数字输入框1 位小数) |
| 体温 | ℃ | 数字输入框1 位小数) |
每次可同时填多项或只填一项。录入时间默认当前,可手动调整为当天任意时间。
### 5.2 预约挂号
```
选择科室 → 选择医生 → 选择日期(排班日历)→ 选择时段 → 确认预约
POST /appointments
成功 → 订阅消息通知 + 日历同步
满员 → 提示"该时段已满"
```
**关键交互:**
- 排班日历用周视图,有排班的日期标绿点
- 点击日期后展示该日可用时段
- 时段显示"剩余 X 位"
- 预约成功后支持微信订阅消息提醒
### 5.3 报告查询
```
报告列表(分页,时间倒序)
↓ 点击某份报告
报告详情 → 基本信息卡 + 指标列表 + PDF/图片附件预览
```
**指标状态标记:**
- 异常偏高:红色 + ↑ 箭头
- 异常偏低:红色 + ↓ 箭头
- 正常范围:灰色
### 5.4 随访管理
```
待办列表(按截止日期排序)
↓ 支持"待完成/已完成/已过期"筛选
点击任务 → 动态表单(后端定义字段)→ 提交 → 标记完成
```
问卷由 PC 端医护创建follow_up_task小程序负责展示和填写。提交后创建 follow_up_record。
### 5.5 家庭健康管理
```
就诊人列表(本人 + 已添加家属)
↓ 点击头像或下拉切换
切换就诊人 → 全局 X-Patient-Id 更新 → 所有页面数据刷新
↓ 添加家属
填写信息 → 姓名 + 关系 + 身份证号(可选)→ POST /patients
```
切换就诊人通过全局 store 更新,所有 service 请求自动携带新 `X-Patient-Id`
### 5.6 健康资讯 + 用药提醒
**资讯:**
- `GET /articles` → 分页列表(缩略图 + 标题 + 摘要 + 时间)
- 文章详情使用 Taro `RichText` 组件渲染富文本
**用药提醒MVP**
- 小程序本地 storage 存储提醒规则(药品名 + 频率 + 时间)
- 每日触发检查
- 通过微信订阅消息推送提醒
- 不依赖后端新表
---
## 6. API 集成与状态管理
### 6.1 请求层封装
`services/request.ts` 职责:
| 拦截点 | 行为 |
|--------|------|
| 请求拦截 | 自动注入 `Authorization: Bearer {token}` |
| 请求拦截 | 自动注入 `X-Patient-Id`(当前选中就诊人) |
| 请求拦截 | 自动注入 `X-Tenant-Id`(从登录信息获取) |
| 响应拦截 | 401 → 静默刷新 token → 重试原请求 |
| 响应拦截 | 刷新失败 → 跳转登录页 |
| 错误处理 | 网络错误 / 业务错误 / 超时统一处理 |
多租户处理:患者只属于一个租户。登录时后端返回 `tenant_id`,前端每次请求带上。不走 `tenant_id` 中间件自动注入。
### 6.2 Zustand Stores
**auth store**
```typescript
interface AuthState {
token: string | null
refreshToken: string | null
user: { id: string; name: string; phone: string; avatar: string } | null
currentPatient: Patient | null
patients: Patient[]
setCurrentPatient: (id: string) => void
login: (code: string) => Promise<void>
bindPhone: (data: BindPhoneData) => Promise<void>
logout: () => void
}
```
**health store**
```typescript
interface HealthState {
todaySummary: VitalSigns | null
trendData: Record<string, TrendPoint[]>
refreshToday: () => Promise<void>
getTrend: (type: string, range: '7d' | '30d' | '90d') => Promise<TrendPoint[]>
}
```
### 6.3 API 端点对应表
| 小程序 service | 后端端点 | 方法 |
|----------------|----------|------|
| `auth.login(code)` | `/api/v1/auth/wechat/login` | POST |
| `auth.bindPhone(data)` | `/api/v1/auth/wechat/bind-phone` | POST |
| `auth.refresh()` | `/api/v1/auth/refresh` | POST |
| `health.getToday()` | `/api/v1/health/vital-signs?date=today` | GET |
| `health.input(data)` | `/api/v1/health/vital-signs` | POST |
| `health.getTrend(type, range)` | `/api/v1/health/vital-signs/trend` | GET |
| `appointment.list()` | `/api/v1/health/appointments` | GET |
| `appointment.create(data)` | `/api/v1/health/appointments` | POST |
| `appointment.cancel(id)` | `/api/v1/health/appointments/:id/cancel` | PUT |
| `schedule.getByDoctor(id)` | `/api/v1/health/doctor-schedules` | GET |
| `report.list()` | `/api/v1/health/lab-reports` | GET |
| `report.detail(id)` | `/api/v1/health/lab-reports/:id` | GET |
| `followup.list()` | `/api/v1/health/follow-up-tasks` | GET |
| `followup.submit(id, data)` | `/api/v1/health/follow-up-records` | POST |
| `patient.list()` | `/api/v1/health/patients` | GET |
| `patient.create(data)` | `/api/v1/health/patients` | POST |
| `patient.update(id, data)` | `/api/v1/health/patients/:id` | PUT |
**后端需新增的端点(尚未实现):**
| 端点 | 说明 |
|------|------|
| `POST /auth/wechat/login` | 微信登录 |
| `POST /auth/wechat/bind-phone` | 手机号绑定 |
| `GET /vital-signs/trend` | 趋势聚合查询 |
| `GET /doctor-schedules` | 按科室/医生查询排班 |
| `GET /articles` | 健康资讯列表 |
| `GET /articles/:id` | 资讯详情 |
---
## 7. 视觉设计
### 7.1 主题色
沿用现有 HTML 原型的医疗清新风格:
| 用途 | 色值 | 说明 |
|------|------|------|
| 主色 | `#0891B2` | 青色,按钮、导航、强调 |
| 主色浅 | `#E0F7FA` | 背景、卡片高亮 |
| 主色深 | `#065A73` | 渐变、按压态 |
| 辅助色 | `#059669` | 绿色,成功、正常指标 |
| 危险色 | `#DC2626` | 红色,异常指标、删除 |
| 警告色 | `#D97706` | 琥珀,待办、提醒 |
| 背景色 | `#F0FDFA` | 页面底色 |
| 卡片色 | `#FFFFFF` | 卡片背景 |
| 主文字 | `#134E4A` | 标题、正文 |
| 副文字 | `#6B7280` | 说明、标签 |
| 轻文字 | `#94A3B8` | 时间戳、占位符 |
### 7.2 圆角规范
| 元素 | 圆角 |
|------|------|
| 卡片 | 12px |
| 按钮 | 8px |
| 输入框 | 8px |
| 头像 | 50% |
| 快捷图标 | 14px |
### 7.3 阴影规范
| 层级 | 值 |
|------|---|
| 轻阴影 | `0 1px 3px rgba(0,0,0,.04)` |
| 标准阴影 | `0 2px 8px rgba(0,0,0,.06)` |
| 中阴影 | `0 4px 16px rgba(0,0,0,.08)` |
| 重阴影 | `0 8px 32px rgba(0,0,0,.12)` |
---
## 8. 开发工作流
### 8.1 开发环境
```bash
# 安装依赖
cd apps/miniprogram && pnpm install
# 开发模式(需配合微信开发者工具)
pnpm dev:weapp # Taro 编译 + watch → dist/
# 用微信开发者工具打开 dist/ 目录预览
# 生产构建
pnpm build:weapp # 压缩 + tree-shaking
# 后端联调
# 需同时运行 erp-server (port 3000)
# 小程序开发设置中关闭域名校验(开发阶段)
```
### 8.2 与 Web 端的代码复用
| 复用内容 | 方式 | 说明 |
|---------|------|------|
| TypeScript 类型 | 按需引用 Web 端 DTO 类型 | API 请求/响应结构一致 |
| 主题变量值 | Web CSS 变量 → SCSS 变量 | 青色主调色值保持一致 |
| Zustand 模式 | 相同 store 设计模式 | 各自独立实现 |
| API 接口定义 | service 层函数签名对齐 | Web 用 fetch小程序用 Taro.request |
### 8.3 后端需同步开发的内容
| 优先级 | 内容 | 涉及 crate |
|--------|------|-----------|
| P0 | `wechat_users` 表 + 微信登录/绑定 API | erp-auth |
| P0 | `vital_signs` 趋势查询 API | erp-health |
| P0 | `doctor_schedules` 按科室/医生查询 API | erp-health |
| P1 | `lab_reports` 指标异常标注字段 | erp-health |
| P1 | `follow_up_tasks` 动态问卷字段扩展 | erp-health |
| P2 | `articles` 表 + CRUD | erp-health |
| P2 | 微信订阅消息模板注册 | erp-server |
---
## 9. 分期交付计划
| 阶段 | 内容 | 目标 |
|------|------|------|
| Phase 1 | 项目骨架 + 登录流程 + 首页(静态数据) | 基础搭建 |
| Phase 2 | 健康数据录入 + 趋势图 | 核心功能 |
| Phase 3 | 预约挂号 + 排班日历 | 核心功能 |
| Phase 4 | 报告查询 + 家庭管理 | 扩展功能 |
| Phase 5 | 随访 + 资讯 + 用药提醒 | 扩展功能 |
| Phase 6 | 打磨 + 真机测试 + 提审 | 上线准备 |
每个 Phase 内部遵循:先对接后端 API → 再实现 UI → 真机验证 → 提交。
---
## 10. 约束与风险
| 约束/风险 | 应对策略 |
|-----------|---------|
| 小程序包体积限制2MB 主包) | 按功能分包加载,图表库按需引入 |
| 微信审核周期3-7 天) | Phase 6 预留充足审核时间 |
| 后端 API 部分未实现 | 小程序开发与后端同步推进,优先实现 P0 端点 |
| 微信订阅消息需用户主动触发 | 在预约成功、随访提交等场景引导用户订阅 |
| 蓝牙设备适配复杂 | MVP 预留接口不实现,后续按设备型号逐一对接 |
| 多就诊人数据隔离 | 后端严格校验 user-patient 归属关系 |