- index.md: 更新关键数字(716 提交)、新增 Design Token 指标行 - miniprogram.md: 新增 §1.1 Design Token 系统完整文档(10 级字号表、 4 结构 token、使用规则、关怀模式说明)、更新变更记录
706 lines
33 KiB
Markdown
706 lines
33 KiB
Markdown
---
|
||
title: 微信小程序(患者端)
|
||
updated: 2026-05-08
|
||
status: active
|
||
tags: [miniprogram, taro, wechat, patient]
|
||
---
|
||
|
||
# 微信小程序(患者端)
|
||
|
||
> 从 [[index]] 导航。关联: [[infrastructure]] [[erp-server]] [[erp-health]]
|
||
|
||
## 1. 设计决策
|
||
|
||
- **Taro 4.2 + React 18** — 跨平台小程序框架,React 生态
|
||
- **Zustand 状态管理** — 与 Web 前端一致的 store 模式
|
||
- **微信登录流程** — `Taro.login()` → code → 后端 `jscode2session` → openid → JWT
|
||
- **build 时注入环境变量** — `process.env` 在小程序运行时不存在,必须通过 `defineConstants` 编译时替换
|
||
- **不使用浏览器 Web API** — `btoa`/`atob` 等在小程序中不可用,用 Taro 原生 API 替代
|
||
- **Zod 输入验证** — 健康数据录入使用 Zod schema 验证
|
||
|
||
### 版本
|
||
|
||
Taro 4.2 / React 18 / TypeScript / Zustand 5 / Sass / Zod / ECharts 6(按需引入)
|
||
|
||
> **依赖统一(2026-04-27):** 移除 `echarts-taro3-react`(内嵌 Taro 3.0.8 + React 16,导致 webpack 模块 ID 不一致 → `Cannot read property 'call' of undefined`)。改为自定义 `EcCanvas` 组件 + `echarts/core` 按需引入。`taro.js` chunk 从 152KB 降至 133KB。
|
||
|
||
### 1.1 设计系统:温润东方风
|
||
|
||
> 自 2026-04-27 起采用。所有新增/修改页面必须遵循此设计系统,保持视觉一致性。
|
||
>
|
||
> 设计系统源文件:`apps/miniprogram/src/styles/variables.scss` + `mixins.scss` + `tokens.scss`
|
||
|
||
**设计哲学**:Kenya Hara 式东方极简。温润米底 + 单一赤土橙贯穿全场,留白呼吸感优先。
|
||
|
||
#### 色彩
|
||
|
||
| 角色 | 变量名 | 色值 | 用途 |
|
||
|------|--------|------|------|
|
||
| 强调色 | `$pri` | `#C4623A` | 按钮、链接、活跃 tab、品牌色 |
|
||
| 强调浅 | `$pri-l` | `#F0DDD4` | 图标底色、高亮背景 |
|
||
| 强调深 | `$pri-d` | `#8B3E1F` | 渐变终点、暗色变体 |
|
||
| 背景 | `$bg` | `#F5F0EB` | 页面底色(warm cream) |
|
||
| 卡片 | `$card` | `#FFFFFF` | 白色卡片 |
|
||
| 辅助底 | `$surface-alt` | `#EDE8E2` | 次级表面、分隔 |
|
||
| 主文字 | `$tx` | `#2D2A26` | warm black,标题和正文 |
|
||
| 次文字 | `$tx2` | `#7A756E` | 辅助说明、标签 |
|
||
| 淡文字 | `$tx3` | `#A8A29E` | 占位、时间戳、箭头 |
|
||
| 边框 | `$bd` / `$bd-l` | `#E8E2DC` / `#F0EBE5` | 分隔线、卡片边框 |
|
||
| 成功 | `$acc` / `$acc-l` | `#5B7A5E` / `#E8F0E8` | sage green 正常状态 |
|
||
| 警告 | `$wrn` / `$wrn-l` | `#C4873A` / `#FFF3E0` | warm amber 偏高/待确认 |
|
||
| 危险 | `$dan` / `$dan-l` | `#B54A4A` / `#FDEAEA` | muted red 异常/删除 |
|
||
|
||
#### 字体
|
||
|
||
| 用途 | 字体栈 | 说明 |
|
||
|------|--------|------|
|
||
| 标题 / 数据数字 | `Georgia, Times New Roman, serif` | 衬线体,传递专业与温度。用 `@include serif-number` |
|
||
| 正文 / 辅助 | `-apple-system, PingFang SC, sans-serif` | 系统无衬线,清晰可读 |
|
||
| 章节标题 | `@include section-title` | 30rpx / bold / serif,统一样式 |
|
||
|
||
#### 圆角 / 阴影 / 间距
|
||
|
||
| 元素 | 圆角 | 阴影 |
|
||
|------|------|------|
|
||
| 卡片 | `$r` 12px | `$shadow-md` (0 2px 12px rgba(45,42,38,0.08)) |
|
||
| 小元素(标签、输入框) | `$r-sm` 8px | `$shadow-sm` (0 1px 4px rgba(45,42,38,0.04)) |
|
||
| 浮层、弹窗 | `$r-lg` 16px | `$shadow-lg` (0 8px 32px rgba(45,42,38,0.12)) |
|
||
| 水平间距 | 页面两侧 24rpx,卡片内 24-28rpx | |
|
||
| 垂直间距 | 区块间 24rpx,卡片内标题与内容 20rpx | |
|
||
|
||
#### 组件规范
|
||
|
||
**按钮**:主按钮 `$pri` 实色 + 白字 + 圆角 12px + 阴影。次按钮 `$pri` 边框透明底。禁用 `$surface-alt` 底 + `$tx3` 字。
|
||
|
||
**状态标签**:用 `@include tag(背景色, 文字色)` 生成。不用粗边框、不用 emoji 状态图标。颜色映射:正常 → `$acc`/`$acc-l`,偏高 → `$wrn`/`$wrn-l`,异常 → `$dan`/`$dan-l`,默认 → `$tx2`/`$bd-l`。
|
||
|
||
**健康数据卡片**:2×2 网格,衬线大数字居中,小标签 + 状态标签在底部。数据用 `@include serif-number` 等宽数字。
|
||
|
||
**列表项**:白色卡片容器,每行 14-24rpx padding,`$bd-l` 底部分隔。右侧箭头用 `›` 字符。无左侧彩色 border accent。
|
||
|
||
**图标**:禁止用 emoji 作图标。用线性 SVG 或首字衬线体作为图标占位。图标底色用 `$pri-l`。
|
||
|
||
**禁止事项**:紫色渐变 / emoji 作图标 / 圆角卡片+左彩色 border / 无意义渐变背景 / 装饰性 icon 遍地配。
|
||
|
||
#### Design Token 系统
|
||
|
||
> 自 2026-05-09 起全面接入。68 个 SCSS 文件(59 页面 + 9 组件)全部使用 `var(--tk-*)` 引用字号和结构值。
|
||
>
|
||
> 源文件:`apps/miniprogram/src/styles/tokens.scss`
|
||
|
||
**架构原理**:通过 CSS 自定义属性(Custom Properties)实现单一真相源。`page {}` 定义正常值,`.elder-mode {}` 覆写关怀值,所有页面通过 `var(--tk-*)` 引用,关怀模式自动级联生效,无需逐页维护覆写。
|
||
|
||
**Taro 兼容性**:Taro 编译器正确处理 CSS 自定义属性中的 px→rpx 转换(如 `--tk-font-h1: 26px` → `--tk-font-h1: 52rpx`)。
|
||
|
||
##### 字号 Token(10 级)
|
||
|
||
| Token | 正常值 | 关怀值 | 倍率 | 语义 | 覆盖场景 |
|
||
|-------|--------|--------|------|------|----------|
|
||
| `--tk-font-hero` | 48px | 56px | ×1.17 | 装饰图标、空状态字符 | 大型装饰元素 |
|
||
| `--tk-font-h1` | 26px | 30px | ×1.15 | 页面/区块标题 | 99 处 |
|
||
| `--tk-font-h2` | 24px | 28px | ×1.17 | 副标题、日期 | 86 处 |
|
||
| `--tk-font-body-lg` | 28px | 34px | ×1.21 | 大正文、按钮 | 110 处 |
|
||
| `--tk-font-body` | 22px | 30px | ×1.36 | 正文、标签 | 92 处 |
|
||
| `--tk-font-body-sm` | 16px | 22px | ×1.38 | 中等正文、列表项 | 26 处 |
|
||
| `--tk-font-num` | 30px | 34px | ×1.13 | 数值显示 | 56 处 |
|
||
| `--tk-font-num-lg` | 34px | 40px | ×1.18 | 大数值、统计 | 21 处 |
|
||
| `--tk-font-cap` | 13px | 18px | ×1.38 | 说明文字、时间戳 | 54 处 |
|
||
| `--tk-font-micro` | 11px | 17px | ×1.55 | 角标、标签 | 22 处 |
|
||
|
||
##### 结构 Token
|
||
|
||
| Token | 正常值 | 关怀值 | 语义 |
|
||
|-------|--------|--------|------|
|
||
| `--tk-line-height` | 1.5 | 1.7 | 行高 |
|
||
| `--tk-touch-min` | 48px | 56px | 最小触控区域 |
|
||
| `--tk-btn-primary-h` | 56px | 64px | 主按钮高度 |
|
||
| `--tk-text-secondary` | #78716C | #5A554F | 辅助文字颜色(关怀模式提升对比度) |
|
||
|
||
##### 使用规则
|
||
|
||
**新增页面必须使用 token**,禁止硬编码 `font-size: Npx`:
|
||
|
||
```scss
|
||
// 正确
|
||
.title { font-size: var(--tk-font-h1); }
|
||
.desc { font-size: var(--tk-font-cap); color: var(--tk-text-secondary); }
|
||
.number { font-size: var(--tk-font-num); @include serif-number; }
|
||
|
||
// 错误
|
||
.title { font-size: 26px; } // 禁止
|
||
```
|
||
|
||
**特殊值例外**(仅允许以下场景硬编码):
|
||
- `72px` — 积分余额大数字(装饰性,不走 token)
|
||
- `80px` — 错误状态图标(组件内部装饰)
|
||
- `21px` — 长辈模式预览页(特殊 UI)
|
||
|
||
##### 关怀模式(Elder Mode)
|
||
|
||
通过 Zustand store (`stores/ui.ts`) 的 `DisplayMode` 控制。`useElderClass()` hook 在根 View 添加 `.elder-mode` class,触发所有 token 的关怀值级联。
|
||
|
||
`elder-mode.scss`(~120 行)仅保留结构布局覆写(grid 列数、padding、gap),所有字号/颜色覆写已通过 token 级联自动处理。
|
||
|
||
## 2. 关键文件 + 数据流
|
||
|
||
### 核心文件
|
||
|
||
| 文件 | 职责 |
|
||
|------|------|
|
||
| `apps/miniprogram/config/index.ts` | Taro 构建配置(defineConstants 注入环境变量) |
|
||
| `apps/miniprogram/src/services/request.ts` | HTTP 请求封装(401 自动刷新、错误处理) |
|
||
| `apps/miniprogram/src/services/auth.ts` | 微信登录/绑定手机号 API |
|
||
| `apps/miniprogram/src/stores/auth.ts` | 认证状态(login/bindPhone/restore) |
|
||
| `apps/miniprogram/src/utils/secure-storage.ts` | token 安全存储(XOR + Base64 混淆) |
|
||
| `apps/miniprogram/project.config.json` | 微信开发者工具配置(AppID、urlCheck) |
|
||
|
||
### 微信登录流程
|
||
|
||
```
|
||
用户点击"微信一键登录"
|
||
↓
|
||
Taro.login() → 临时 code
|
||
↓
|
||
POST /auth/wechat/login { code }
|
||
↓
|
||
后端调用微信 jscode2session → 获取 openid + session_key
|
||
↓
|
||
查询 wechat_users 表
|
||
├── 已绑定 → 返回 { bound: true, token: JWT } → 跳转首页
|
||
└── 未绑定 → 返回 { bound: false, openid } → 显示"授权手机号"按钮
|
||
↓ 用户授权手机号
|
||
POST /auth/wechat/bind-phone { openid, encrypted_data, iv }
|
||
↓
|
||
后端解密手机号 → 创建/关联用户 → 返回 JWT → 跳转首页
|
||
```
|
||
|
||
### 页面结构(59 个页面,6 个主包 + 10 个子包)
|
||
|
||
#### TabBar 页面(3 个)
|
||
|
||
| 页面路径 | 说明 |
|
||
|----------|------|
|
||
| `pages/index/index` | 首页(今日健康、快捷服务) |
|
||
| `pages/health/index` | 健康上报(Tab 页) |
|
||
| `pages/messages/index` | 消息中心(Tab 页) |
|
||
|
||
#### 患者端主页面
|
||
|
||
| 页面路径 | 说明 |
|
||
|----------|------|
|
||
| `pages/login/index` | 登录页(微信登录 + 协议勾选) |
|
||
| `pages/consultation/index` | 咨询列表 |
|
||
| `pages/consultation/detail/index` | 咨询详情 |
|
||
| `pages/mall/index` | 积分商城 |
|
||
| `pages/profile/index` | 个人中心 |
|
||
| `pages/appointment/index` | 预约列表 |
|
||
| `pages/appointment/create/index` | 预约挂号 |
|
||
| `pages/appointment/detail/index` | 预约详情 |
|
||
| `pages/legal/user-agreement` | 用户服务协议 |
|
||
| `pages/legal/privacy-policy` | 隐私政策 |
|
||
|
||
#### 健康子包(pkg-health,4 个)
|
||
|
||
| 页面路径 | 说明 |
|
||
|----------|------|
|
||
| `pages/pkg-health/trend/index` | 健康趋势(体征数据折线图) |
|
||
| `pages/pkg-health/input/index` | 健康数据录入(Zod 验证) |
|
||
| `pages/pkg-health/daily-monitoring/index` | 日常监测数据 |
|
||
| `pages/pkg-health/alerts/index` | 告警列表 |
|
||
|
||
#### 医生端子包(doctor,16 个)
|
||
|
||
| 页面路径 | 说明 |
|
||
|----------|------|
|
||
| `pages/doctor/index` | 医护首页/工作台 |
|
||
| `pages/doctor/patients/index` | 患者列表 |
|
||
| `pages/doctor/patients/detail/index` | 患者详情 |
|
||
| `pages/doctor/consultation/index` | 咨询管理 |
|
||
| `pages/doctor/consultation/detail/index` | 咨询详情 |
|
||
| `pages/doctor/followup/index` | 随访管理 |
|
||
| `pages/doctor/followup/detail/index` | 随访详情 |
|
||
| `pages/doctor/report/index` | 报告管理 |
|
||
| `pages/doctor/report/detail/index` | 报告详情 |
|
||
| `pages/doctor/alerts/index` | 告警管理 |
|
||
| `pages/doctor/alerts/detail/index` | 告警详情 |
|
||
| `pages/doctor/action-inbox/index` | 待办事项 |
|
||
| `pages/doctor/dialysis/index` | 透析管理 |
|
||
| `pages/doctor/dialysis/detail/index` | 透析详情 |
|
||
| `pages/doctor/dialysis/create/index` | 创建透析记录 |
|
||
| `pages/doctor/prescription/index` | 处方管理 |
|
||
| `pages/doctor/prescription/detail/index` | 处方详情 |
|
||
| `pages/doctor/prescription/create/index` | 开具处方 |
|
||
|
||
#### 商城子包(pkg-mall,3 个)
|
||
|
||
| 页面路径 | 说明 |
|
||
|----------|------|
|
||
| `pages/pkg-mall/exchange/index` | 积分兑换 |
|
||
| `pages/pkg-mall/orders/index` | 积分订单 |
|
||
| `pages/pkg-mall/detail/index` | 商品详情 |
|
||
|
||
#### 个人中心子包(pkg-profile,12 个)
|
||
|
||
| 页面路径 | 说明 |
|
||
|----------|------|
|
||
| `pages/pkg-profile/family/index` | 家庭成员管理 |
|
||
| `pages/pkg-profile/family-add/index` | 添加家庭成员 |
|
||
| `pages/pkg-profile/reports/index` | 我的报告 |
|
||
| `pages/pkg-profile/followups/index` | 我的随访 |
|
||
| `pages/pkg-profile/medication/index` | 用药记录 |
|
||
| `pages/pkg-profile/settings/index` | 设置 |
|
||
| `pages/pkg-profile/dialysis-records/index` | 透析记录 |
|
||
| `pages/pkg-profile/dialysis-records/detail/index` | 透析记录详情 |
|
||
| `pages/pkg-profile/dialysis-prescriptions/index` | 透析处方 |
|
||
| `pages/pkg-profile/dialysis-prescriptions/detail/index` | 透析处方详情 |
|
||
| `pages/pkg-profile/consents/index` | 知情同意 |
|
||
| `pages/pkg-profile/health-records/index` | 健康档案 |
|
||
| `pages/pkg-profile/diagnoses/index` | 诊断记录 |
|
||
|
||
#### 其他子包(9 个)
|
||
|
||
| 页面路径 | 说明 |
|
||
|----------|------|
|
||
| `pages/ai-report/list/index` | AI 分析报告列表 |
|
||
| `pages/ai-report/detail/index` | AI 分析报告详情 |
|
||
| `pages/article/index` | 健康资讯列表 |
|
||
| `pages/article/detail/index` | 文章详情 |
|
||
| `pages/report/detail/index` | 健康报告详情 |
|
||
| `pages/followup/detail/index` | 随访详情 |
|
||
| `pages/events/index` | 线下活动 |
|
||
| `pages/device-sync/index` | 设备数据同步 |
|
||
|
||
### 服务层(10+ 个文件)
|
||
|
||
| 文件 | 覆盖 |
|
||
|------|------|
|
||
| `request.ts` | HTTP 封装(401 刷新、错误处理) |
|
||
| `auth.ts` | 微信登录/绑定手机号 |
|
||
| `health.ts` | 体征数据/健康趋势 |
|
||
| `patient.ts` | 患者信息/家庭成员 |
|
||
| `appointment.ts` | 预约挂号/详情/取消 |
|
||
| `followup.ts` | 随访任务/详情 |
|
||
| `article.ts` | 健康文章 |
|
||
| `report.ts` | 健康报告 |
|
||
| `analytics.ts` | 数据分析 |
|
||
| `wechat-templates.ts` | 微信模板消息 ID |
|
||
|
||
### 组件(10 个)
|
||
|
||
| 组件 | 用途 |
|
||
|------|------|
|
||
| `EcCanvas` | ECharts Canvas 包装(Taro 4 兼容,按需引入 echarts/core) |
|
||
| `EmptyState` | 空状态占位 |
|
||
| `ErrorBoundary` | 错误边界捕获 |
|
||
| `ErrorState` | 错误状态展示 |
|
||
| `FamilyPicker` | 家庭成员选择器 |
|
||
| `HealthCard` | 健康数据卡片 |
|
||
| `Loading` | 加载状态 |
|
||
| `StepIndicator` | 步骤指示器 |
|
||
| `TrendChart` | 趋势图表(基于 EcCanvas) |
|
||
| `WeekCalendar` | 周日历组件 |
|
||
|
||
### 集成契约
|
||
|
||
| 方向 | 模块 | 接口 | 触发时机 |
|
||
|------|------|------|---------|
|
||
| 调用 → | [[erp-server]] | `POST /auth/wechat/login` | 微信登录 |
|
||
| 调用 → | [[erp-server]] | `POST /auth/wechat/bind-phone` | 手机号绑定 |
|
||
| 调用 → | [[erp-health]] | `/api/v1/health/*` | 健康数据查询 |
|
||
| 调用 → | [[erp-server]] | `/api/v1/auth/refresh` | Token 刷新 |
|
||
|
||
## 3. 代码逻辑
|
||
|
||
### 环境变量注入(关键)
|
||
|
||
小程序运行时没有 `process.env`,必须在 `config/index.ts` 中通过 `defineConstants` 编译时替换:
|
||
|
||
```typescript
|
||
defineConstants: {
|
||
'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development'),
|
||
'process.env.TARO_APP_API_URL': JSON.stringify(process.env.TARO_APP_API_URL || 'http://localhost:3000/api/v1'),
|
||
'process.env.TARO_APP_ENCRYPTION_KEY': JSON.stringify(process.env.TARO_APP_ENCRYPTION_KEY || ''),
|
||
},
|
||
```
|
||
|
||
### Token 安全存储
|
||
|
||
`secure-storage.ts` 使用 XOR 混淆 + Base64 编码存储 token:
|
||
- **不使用 `btoa`/`atob`** — 小程序环境不支持
|
||
- 使用 `Taro.arrayBufferToBase64` / `Taro.base64ToArrayBuffer` 替代
|
||
- 加密密钥通过 `TARO_APP_ENCRYPTION_KEY` 环境变量注入
|
||
|
||
### 请求封装(request.ts)
|
||
|
||
- 401 自动尝试 refresh token,失败后跳转登录页
|
||
- 错误响应格式: `{ error: string, message: string }`(无 `success` 字段)
|
||
- 成功响应格式: `{ success: true, data: T }`
|
||
- 当前 URL 拼接方式构建查询参数(待重构为 params 对象)
|
||
|
||
### 健康数据录入验证
|
||
|
||
使用 Zod schema 验证用户输入的体征数据(血压、心率、体重、血糖),确保数据类型和范围合法。
|
||
|
||
## 4. 构建与调试
|
||
|
||
### 构建
|
||
|
||
```bash
|
||
cd apps/miniprogram && pnpm build:weapp # 生产构建
|
||
cd apps/miniprogram && npx taro build --type weapp # 等效
|
||
```
|
||
|
||
### 微信开发者工具配置
|
||
|
||
| 配置项 | 值 | 说明 |
|
||
|--------|-----|------|
|
||
| AppID | `wx20f4ef9cc2ec66c5` | 真实微信 AppID |
|
||
| urlCheck | `false` | 不校验合法域名(开发模式) |
|
||
| miniprogramRoot | `dist/` | 编译输出目录 |
|
||
|
||
### 后端微信配置
|
||
|
||
`crates/erp-server/config/default.toml`:
|
||
|
||
```toml
|
||
[wechat]
|
||
appid = "wx20f4ef9cc2ec66c5"
|
||
secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>"
|
||
```
|
||
|
||
## 5. 活跃问题 + 陷阱
|
||
|
||
### 历史教训
|
||
|
||
| 问题 | 根因 | 修复 |
|
||
|------|------|------|
|
||
| 页面空白 `ReferenceError: process is not defined` | `process.env` 在运行时不存在 | `defineConstants` 编译时替换 |
|
||
| 登录成功但前端报失败 `btoa is not defined` | `secure-storage.ts` 使用 Web API | 改用 `Taro.arrayBufferToBase64` |
|
||
| 微信登录 500 `wechat_users.created_by 不存在` | 迁移创建的表缺少标准字段 | `ALTER TABLE` 补列 |
|
||
| 401 循环重定向 | 首页未登录时 `request.ts` 反复 redirectTo | 检查当前路径避免重复跳转 |
|
||
| `Cannot read property 'call' of undefined` | `echarts-taro3-react` 内嵌 Taro 3.0.8 + React 16,与 Taro 4.2 + React 18 产生双实例,webpack 模块 ID 不匹配 | 移除 `echarts-taro3-react`,改为自定义 `EcCanvas` + `echarts/core` 按需引入 |
|
||
|
||
### 待优化
|
||
|
||
| 问题 | 级别 | 说明 |
|
||
|------|------|------|
|
||
| ~~Auth token 日志输出~~ | ~~P0~~ 已解决 | terser `drop_console` 移除生产日志 |
|
||
| ~~Token 刷新竞态~~ | ~~P0~~ 已解决 | `refreshPromise` 单例 + GET 请求去重 |
|
||
| ~~ECharts 全量引入~~ | ~~P2~~ 已解决 | 分包后 echarts 514KB 仅在趋势页按需加载 |
|
||
| 积分商城降级 UI | P0 | 未关联患者档案时 Tab 页空白,需引导用户建档 |
|
||
| daily-monitoring 无 Zod 验证 | P1 | 对齐 health/input 的验证标准 |
|
||
| 文章列表返回草稿 | P1 | 患者端应只展示 `published` 状态文章 |
|
||
| URL 拼接构建查询参数 | P2 | `request.ts` 已支持 `buildQuery(params)` 但内部使用 |
|
||
| 生产配置 | P2 | `urlCheck`/`minified` 需区分环境 |
|
||
|
||
### 2026-04-30 审计发现
|
||
|
||
**三端对齐差距**(小程序 API 覆盖 76 个,Web 235 个,后端 328 个):
|
||
|
||
| 发现 | 严重性 | 说明 |
|
||
|------|--------|------|
|
||
| ~~晚间血压数据永久丢失~~ | ~~CRITICAL~~ **已修复** | 新增 `blood_pressure_evening` indicator_type,小程序录入页 + 日常监测页 + 后端 service + 集成测试均已覆盖 |
|
||
| ~~透析管理完全无入口~~ | ~~HIGH~~ **已修复** | 医生端新增 dialysis 3 页面 + prescription 3 页面,患者端新增 dialysis-records 2 页面 + dialysis-prescriptions 2 页面 |
|
||
| ~~知情同意完全无入口~~ | ~~HIGH~~ **已修复** | 患者端新增 `pkg-profile/consents` 页面 |
|
||
| 体温/血氧未映射 | MEDIUM | `body_temperature`/`spo2` 无 indicator_type |
|
||
| ~~健康记录小程序无入口~~ | ~~MEDIUM~~ **已修复** | 患者端新增 `pkg-profile/health-records` 页面 |
|
||
| ~~诊断记录小程序无入口~~ | ~~MEDIUM~~ **已修复** | 患者端新增 `pkg-profile/diagnoses` 页面 |
|
||
| 小程序无自动化测试 | HIGH | 已建立 MCP 自动化审计流程,4 角色 236 次探测通过率 96.2% |
|
||
|
||
**功能域完成度**(小程序端):
|
||
|
||
| 功能域 | 完成度 | 关键差距 |
|
||
|--------|--------|---------|
|
||
| 咨询管理 | 95% | 无导出(预期) |
|
||
| 预约管理 | 90% | 基本对齐 |
|
||
| 随访管理 | 70% | 仅列表+创建 |
|
||
| 患者管理 | 85% | 无删除(预期) |
|
||
| 健康数据 | 80% | ~~丢失晚间血压~~ 已修复 |
|
||
| 告警系统 | 80% | 查看+处理+医护端管理 |
|
||
| 透析管理 | 70% | 医护端管理+患者端查看记录/处方 |
|
||
| 知情同意 | 60% | 患者端查看,无签署流程 |
|
||
| 健康档案 | 60% | 患者端可查看健康档案和诊断 |
|
||
| 统计仪表盘 | 30% | 仅医护端 3 个统计 |
|
||
| AI 分析 | 30% | 仅历史查看 |
|
||
|
||
### 注意事项
|
||
|
||
- `Taro.login()` 的 code 一次性使用,每次调用会返回新 code
|
||
- `session_key` 缓存 5 分钟(TTL),过期需重新登录
|
||
- 微信开发者工具中 `getPhoneNumber` 需要真机调试或使用测试号
|
||
- Redis 不可达时限流降级为 fail-open,不影响登录
|
||
|
||
## 6. MCP 联调(微信开发者工具自动化)
|
||
|
||
> 通过 MCP (Model Context Protocol) 工具直接操控微信开发者工具中的小程序模拟器,实现页面导航、元素交互、数据读取等操作。
|
||
>
|
||
> **当前使用自建 MCP 服务器** `@hms/weapp-mcp`(`tools/weapp-mcp/`),基于 `@weapp-vite/miniprogram-automator@1.1.0`(社区活跃维护 fork)。
|
||
> 原有的 `@yfme/weapp-dev-mcp` 在 DevTools 2.01.2510290 版本后不兼容,已弃用。
|
||
|
||
### 6.1 前置条件
|
||
|
||
1. **后端运行** — `cargo run` 启动 `http://localhost:3000`
|
||
2. **小程序以 dev 模式构建** — 必须使用 `NODE_ENV=development` 构建(详见 §6.4)
|
||
```bash
|
||
cd apps/miniprogram
|
||
echo 'TARO_APP_API_URL=http://localhost:3000/api/v1
|
||
TARO_APP_ENCRYPTION_KEY=' > .env
|
||
NODE_ENV=development npx taro build --type weapp
|
||
```
|
||
3. **微信开发者工具已登录** — 确保已扫码登录(CLI 命令需要登录态)
|
||
4. **微信开发者工具打开项目** — 手动打开,加载 `apps/miniprogram/dist/`
|
||
5. **自动化端口已开启** — `project.config.json` 中 `"automationAudits": true`
|
||
|
||
> ⚠️ **为什么必须 dev 模式?** 生产构建(`pnpm build:weapp`)设置 `NODE_ENV=production`,`secure-storage.ts` 的 `decrypt()` 在密钥为空时会抛异常。dev 模式允许空密钥走明文存储。
|
||
|
||
### 6.2 启动调试环境
|
||
|
||
> **铁律:只允许一个 DevTools 实例运行。** 多实例会导致 App 级别自动化命令全部超时(无法恢复,必须全部关闭重开)。
|
||
|
||
#### 方式 A:Launcher.launch() 自动启动(推荐)
|
||
|
||
通过 `@weapp-vite/miniprogram-automator` 的 `Launcher` 自动启动 DevTools 并开启自动化端口,无需手动操作。
|
||
|
||
```bash
|
||
# 步骤 1:确认没有残留的 DevTools 进程
|
||
tasklist | findstr wechatdevtools
|
||
# 如有残留:taskkill /F /IM wechatdevtools.exe
|
||
|
||
# 步骤 2:MCP 的 inject_auth 或 connect 工具会自动调用 Launcher.launch()
|
||
# 或者手动测试:
|
||
cd tools/weapp-mcp
|
||
node --input-type=module -e "
|
||
import { Launcher } from '@weapp-vite/miniprogram-automator';
|
||
const mp = await new Launcher().launch({
|
||
cliPath: 'D:/微信web开发者工具/cli.bat',
|
||
projectPath: 'G:/hms/apps/miniprogram/dist',
|
||
});
|
||
console.log('OK:', (await mp.systemInfo()).model);
|
||
// 不要 disconnect,保持连接供 MCP 使用
|
||
"
|
||
# 自动在端口 9420 开启自动化
|
||
```
|
||
|
||
#### 方式 B:手动启动 + CLI 开端口
|
||
|
||
```bash
|
||
# 步骤 1:确认没有残留的 DevTools 进程
|
||
taskkill /F /IM wechatdevtools.exe
|
||
|
||
# 步骤 2:手动打开微信开发者工具,加载 apps/miniprogram/dist/
|
||
# 等待模拟器完全加载,确认无报错
|
||
|
||
# 步骤 3:通过 CLI 开启自动化端口(端口 9420)
|
||
"D:/微信web开发者工具/cli.bat" auto --project G:/hms/apps/miniprogram/dist --auto-port 9420
|
||
# 看到 "✔ auto" 即成功
|
||
|
||
# 步骤 4:验证端口已开启
|
||
netstat -ano | findstr 9420
|
||
# 应看到 LISTENING 状态
|
||
```
|
||
|
||
成功后可通过 `mcp__weapp-local__connect` 连接。
|
||
|
||
### 6.3 常用 MCP 操作(weapp-local 工具集)
|
||
|
||
| 操作 | MCP 工具 | 说明 |
|
||
|------|----------|------|
|
||
| 连接 | `connect` | 连接自动化端口,返回系统信息 |
|
||
| 断开 | `disconnect` | 断开连接 |
|
||
| 查看当前页面 | `current_page` | 返回路径、尺寸,可选包含 data |
|
||
| 导航到页面 | `navigate` | `navigateTo` / `switchTab` / `reLaunch` / `redirectTo` / `navigateBack` |
|
||
| 查找元素 | `get_element` | CSS 选择器,支持 `[index=N]` 语法 |
|
||
| 查找多个元素 | `get_elements` | CSS 选择器数组 |
|
||
| 点击元素 | `tap` | CSS 选择器定位后点击 |
|
||
| 输入文本 | `input` | CSS 选择器 + 值 |
|
||
| 滚动 | `scroll` | scroll-view 的 CSS 选择器 + x/y 坐标 |
|
||
| 读取页面数据 | `page_data` | 获取当前页面 data 对象(可选路径) |
|
||
| 设置页面数据 | `page_data` | JSON data 参数 → setData |
|
||
| 读取组件数据 | `element_data` | 组件 CSS 选择器 + 可选路径 |
|
||
| 设置组件数据 | `element_data` | CSS 选择器 + JSON data → setData |
|
||
| 调用页面方法 | `page_call_method` | 调用当前页面上暴露的方法 |
|
||
| 执行 JS | `evaluate` | 在 AppService 上下文执行 JavaScript |
|
||
| 调用 wx API | `call_wx` | 如 `getStorageSync`、`setStorageSync` |
|
||
| Mock wx API | `mock_wx` | Mock 一个 wx API 返回指定结果 |
|
||
| 恢复 wx API | `restore_wx` | 恢复被 mock 的 wx API |
|
||
| 获取 WXML | `get_wxml` | 获取元素的 WXML 结构 |
|
||
| 截图 | `screenshot` | ⚠️ 当前版本超时,见 §6.6 |
|
||
| 日志 | `get_logs` | 获取 console 日志和异常 |
|
||
| 等待元素 | `wait_for` | 轮询等待选择器匹配 |
|
||
| 注入认证 | `inject_auth` | 一键获取后端 token + 注入 storage + 跳转首页 |
|
||
| 审计页面 | `audit_page` | 导航 → 检查加载 → 收集错误 → 健康报告 |
|
||
| 批量审计 | `audit_all` | 批量审计所有小程序页面 |
|
||
|
||
### 6.4 绕过微信登录
|
||
|
||
MCP 无法模拟微信 OAuth(`Taro.login()` 返回的 code 走真实微信 `jscode2session` 接口,DevTools 模拟器会返回 mock code 导致后端 500)。
|
||
|
||
#### 方案一:inject_auth 一键注入(推荐)
|
||
|
||
**原理:** 以 dev 模式重编译(空加密密钥),通过 MCP 的 `inject_auth` 工具自动完成:获取 admin token → evaluate 注入 storage → reLaunch 首页。
|
||
|
||
```
|
||
1. 准备:确保已按 §6.1 以 dev 模式构建,且已连接 MCP(connect)
|
||
|
||
2. 调用 inject_auth 工具(默认参数即可):
|
||
- 自动 POST /auth/login 获取 admin token
|
||
- 通过 evaluate() 注入 access_token / refresh_token / user_data / tenant_id / patient_id
|
||
- reLaunch 到 /pages/index/index
|
||
- 等待 2 秒后返回当前页面路径
|
||
|
||
3. 成功后即可正常操作各页面
|
||
```
|
||
|
||
**关键 ID(默认值):**
|
||
- `tenant_id`: `019d80da-7a2c-7820-b0a3-3d5266a3a324`
|
||
- `patient_id`: `019dcd34-bc4d-72c1-8c19-77ce1f4839d6`
|
||
|
||
#### 方案二:手动分步注入
|
||
|
||
```bash
|
||
# 1. 获取 admin token
|
||
curl -X POST http://localhost:3000/api/v1/auth/login \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"username":"admin","password":"Admin@2026"}'
|
||
|
||
# 2. 通过 MCP call_wx 逐个写入 storage
|
||
# 3. 通过 MCP navigate reLaunch 到首页
|
||
```
|
||
|
||
> ⚠️ 长字符串 token 通过 `call_wx` 传输可能截断。建议用 `inject_auth` 工具(内部用 evaluate 直接执行,无传输问题)。
|
||
|
||
### 6.5 TabBar 页面列表
|
||
|
||
以下页面是 TabBar 页面,必须用 `switchTab` 导航(不能用 `navigateTo`):
|
||
|
||
- `pages/index/index` — 首页
|
||
- `pages/health/index` — 健康数据
|
||
- `pages/messages/index` — 消息中心
|
||
|
||
> 注意:咨询、商城、个人中心虽然显示为 Tab 样式,但实际不是 TabBar 页面(使用 `reLaunch` 导航)。
|
||
|
||
### 6.6 已知限制
|
||
|
||
| 问题 | 原因 | 替代方案 |
|
||
|------|------|----------|
|
||
| **screenshot 超时** | `App.captureScreenshot` 在 DevTools 2.01.2510290 不响应 | 用 `get_element` + `page_data` 获取 UI 状态 |
|
||
| **多实例导致全挂** | 两个 DevTools 实例抢端口,App 域命令全部超时且不可恢复 | `taskkill /F /IM wechatdevtools.exe` 后重开 |
|
||
| **navigateTo 超出 10 页栈** | 小程序 webview 限制最多 10 层页面栈 | 批量测试用 `reLaunch` 逐页导航 |
|
||
| **CLI 需要登录** | `cli.bat auto` 要求 DevTools 已扫码登录 | 在 DevTools 中先扫码登录 |
|
||
| **SummerCompiler 报错** | `project.config.json` 缺少必要字段 | 已配置 `packNpmManually`/`packNpmRelationList`/`ignoreUploadUnusedFiles`/`condition` |
|
||
| **require() 在 evaluate 不可用** | webpack 用数字 ID 注册模块 | 直接用 `wx.setStorageSync` / `wx.getStorageSync` |
|
||
| **auth 重定向** | request interceptor 检测 401 后跳转 login 并清空 storage | 确保 token 有效,reLaunch 后等待 2-3 秒 |
|
||
| **生产构建 decrypt 抛异常** | 空密钥 + `NODE_ENV=production` 时 `decrypt()` 直接 throw | 使用 `NODE_ENV=development` 构建 |
|
||
|
||
### 6.7 MCP 服务器架构
|
||
|
||
```
|
||
tools/weapp-mcp/
|
||
├── src/
|
||
│ ├── index.ts # MCP 服务器入口,25 个工具注册
|
||
│ └── automator.ts # 连接管理器(@weapp-vite/miniprogram-automator)
|
||
├── package.json # @hms/weapp-mcp, 依赖 @weapp-vite/miniprogram-automator@1.1.0
|
||
└── tsconfig.json
|
||
```
|
||
|
||
`.mcp.json` 配置:
|
||
```json
|
||
{
|
||
"weapp-local": {
|
||
"command": "node",
|
||
"args": ["tools/weapp-mcp/dist/index.js"],
|
||
"env": {
|
||
"WEAPP_WS_ENDPOINT": "ws://localhost:9420",
|
||
"WEAPP_CLI_PATH": "D:/微信web开发者工具/cli.bat",
|
||
"WEAPP_PROJECT_PATH": "G:/hms/apps/miniprogram/dist",
|
||
"HMS_API_URL": "http://localhost:3000/api/v1"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
修改后需 `cd tools/weapp-mcp && npm run build` 重新编译。
|
||
|
||
### 6.8 自动化审计脚本
|
||
|
||
| 脚本 | 用途 | 运行 |
|
||
|------|------|------|
|
||
| `tools/weapp-mcp/scripts/audit-pages.mjs` | **多角色分批审计**(推荐) | `node scripts/audit-pages.mjs --role admin` |
|
||
| `apps/miniprogram/inject-auth.cjs` | 注入明文 token(dev 模式) | `node inject-auth.cjs` |
|
||
| `apps/miniprogram/e2e-final.cjs` | 完整 E2E 链路验证(11 UI + 10 API) | `node e2e-final.cjs` |
|
||
|
||
#### 分批审计脚本用法
|
||
|
||
```bash
|
||
# 在 tools/weapp-mcp/ 目录下运行
|
||
cd tools/weapp-mcp
|
||
|
||
# 审计指定角色(admin/doctor/nurse/operator)
|
||
node scripts/audit-pages.mjs --role admin
|
||
|
||
# 自定义批次大小(默认 10 页/批,每批重启 DevTools 防止多实例超时)
|
||
node scripts/audit-pages.mjs --role doctor --batch-size 8
|
||
|
||
# 输出:
|
||
# - 控制台实时显示每页状态(OK / LOGIN_REDIRECT / ERROR)
|
||
# - JSON 报告保存到 docs/qa/role-test-results/MP-{role}-audit.json
|
||
```
|
||
|
||
**设计要点:**
|
||
- 每批 N 页后自动 `taskkill` 所有 DevTools 进程并重新 `Launcher.launch()`,避免多实例导致 App 级命令超时
|
||
- 通过 `mp.evaluate()` 注入 storage(`wx.setStorageSync`),写入 `access_token`/`refresh_token`/`user_data`/`user_roles`/`tenant_id`/`current_patient_id`
|
||
- 测试用户密码均为 `Admin@2026`(不是 `Test@2026`)
|
||
|
||
> ⚠️ 旧脚本 `audit-pages.cjs` / `audit-detail-pages.cjs` 基于 `miniprogram-automator`(已弃用),请使用新的 `audit-pages.mjs`。
|
||
|
||
### 6.9 审计结果
|
||
|
||
#### 2026-05-08 多角色自动化审计(59 页面 × 4 角色)
|
||
|
||
使用分批审计脚本对全部 59 个页面进行 4 角色全面审计(236 次页面探测):
|
||
|
||
| 角色 | OK | LOGIN_REDIRECT | ERROR | 通过率 |
|
||
|------|-----|----------------|-------|--------|
|
||
| admin(患者) | 57 | 0 | 2 | **96.6%** |
|
||
| doctor(医生) | 58 | 1 | 0 | **98.3%** |
|
||
| nurse(护士) | 57 | 0 | 2 | **96.6%** |
|
||
| operator(运营) | 55 | 0 | 4 | **93.2%** |
|
||
| **合计** | **227** | **1** | **8** | **96.2%** |
|
||
|
||
**关键结论:**
|
||
- 8 个 ERROR 全部是 DevTools 模拟器随机 timeout,非产品缺陷
|
||
- 1 个 LOGIN_REDIRECT 是 doctor 首页首次加载时序问题
|
||
- 所有 59 个页面在所有角色下均可正常渲染(排除测试工具因素)
|
||
- 测试用户密码均为 `Admin@2026`(之前误用 `Test@2026` 导致大量 LOGIN_REDIRECT)
|
||
|
||
报告文件:`docs/qa/role-test-results/MP-{role}-audit.json`
|
||
|
||
#### 2026-04-27 初次审计(40 页面,单角色)
|
||
|
||
通过 MCP 自动化工具对全部 40 个页面进行渲染审计:
|
||
|
||
| 类别 | 数量 | 结果 |
|
||
|------|------|------|
|
||
| TabBar 页面 | 5 | 5/5 OK |
|
||
| 患者端子页面 | 24 | 24/24 OK |
|
||
| 详情页(假 ID) | 11 | 11/11 优雅降级 |
|
||
| **合计** | **40** | **40/40 全部通过** |
|
||
|
||
详细审计报告见 `docs/discussions/2026-04-27-miniprogram-audit-report.md`。
|
||
|
||
## 7. 变更记录
|
||
|
||
| 日期 | 变更 |
|
||
|------|------|
|
||
| 2026-05-09 | **Design Token 全面接入**:68 SCSS 文件(59 页面 + 9 组件)全面迁移 `font-size: Npx` → `var(--tk-*)`;重写 `tokens.scss` 校准 10 级字号 + 4 结构 token 匹配实际设计值;更新 `mixins.scss` 4 个 mixin 引用 token;清理 12 个页面的本地 mixin 重复定义;`elder-mode.scss` 从 530 行缩减至 ~120 行(删除所有字号/颜色覆写,仅保留结构布局);634 token 引用 / 3 个特殊硬编码;新增 §1.1 Design Token 系统文档 |
|
||
| 2026-05-08 | **多角色自动化审计**:4 角色(admin/doctor/nurse/operator)× 59 页面 = 236 次探测,综合通过率 96.2%;更新 §2 页面结构为 59 页面完整列表(含医生端 dialysis/prescription/action-inbox + 患者端 dialysis-records/prescriptions/consents/health-records/diagnoses);更新 §5 审计发现(透析/知情同意/诊断/健康记录标记为已修复);更新 §6.5 TabBar 为 3 个;新增 §6.8 分批审计脚本;更新 §6.9 多角色审计结果 |
|
||
| 2026-05-08 | **MCP 联调全面重写**:自建 MCP 服务器 `@hms/weapp-mcp` 替代 `@yfme/weapp-dev-mcp`;基于 `@weapp-vite/miniprogram-automator@1.1.0`;新增 §6.2 启动步骤(登录+单实例铁律);更新工具列表为 weapp-local 25 个工具;新增 inject_auth 一键注入;新增 §6.7 MCP 服务器架构说明;多实例冲突、CLI 登录、SummerCompiler 等已知限制 |
|
||
| 2026-05-01 | 审计发现更新:CRITICAL 晚间血压丢失 / HIGH 透析+知情同意完全空白 / 功能域完成度矩阵 |
|
||
| 2026-04-28 | **全面性能优化**:分包加载(6 分包,主包 517KB→275KB,vendors 192KB→36KB);GET 请求去重+60s TTL 缓存;points store 集中积分/签到状态;todaySummary 60s TTL;7 组件 React.memo;TrendChart 双重渲染修复;restoreAuth 提升 App 级别;prod terser drop_console;crypto-js 按需引入 |
|
||
| 2026-04-27 | **移除 echarts-taro3-react**:内嵌 Taro 3 + React 16 导致 webpack 模块加载失败,改为自定义 `EcCanvas` 组件 + `echarts/core` 按需引入;更新版本说明 + 历史教训 + 组件列表 |
|
||
| 2026-04-27 | **MCP 联调全面更新**:§6.1 增加 dev 构建前置条件,§6.4 重写为明文 token 注入法(评估两种方案),§6.6 补充 7 条已知限制,新增 §6.7 审计脚本说明 + §6.8 实测审计结果(40/40 页面通过);§5 补充 4 条审计发现 |
|
||
| 2026-04-27 | 新增 §6 MCP 联调章节:连接、操作列表、绕过登录、已知限制、e2e 脚本 |
|
||
| 2026-04-26 | 全面更新:40 页面(含 9 个医护端页面)、15 目录、5 个 Tab 页、积分商城、线下活动 |
|
||
| 2026-04-25 | 全面更新:20 页面、10 服务、9 组件、Zod 验证、加密密钥外部化说明 |
|
||
| 2026-04-24 | 创建小程序 wiki 页面,记录登录流程、环境配置、历史陷阱 |
|