Files
hms/wiki/miniprogram.md
iven 7c0f0ce906
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
docs(miniprogram): 新增 MCP 联调章节 — 操作指南 + 已知限制 + 故障排查
- wiki/miniprogram.md §6: 完整 MCP 联调文档
  - 前置条件与建立连接
  - 16 个 MCP 工具使用说明
  - 绕过微信登录的 storage 注入流程
  - TabBar 页面列表与导航注意事项
  - 已知限制:screenshot 超时、navigateTo 超时、auth 重定向
  - e2e 验证脚本说明
- wiki/index.md: 新增 3 条 MCP 相关症状导航
2026-04-27 12:13:52 +08:00

338 lines
13 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.
---
title: 微信小程序(患者端)
updated: 2026-04-26
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
## 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 → 跳转首页
```
### 页面结构40 个页面15 个目录)
#### 患者端页面
| 页面路径 | 说明 |
|----------|------|
| `pages/login/index` | 登录页(微信登录 + 协议勾选) |
| `pages/index/index` | 首页(今日健康、快捷服务) |
| `pages/health/index` | 健康上报Tab 页) |
| `pages/health/input/index` | 健康数据录入Zod 验证) |
| `pages/health/trend/index` | 健康趋势(体征数据折线图) |
| `pages/health/daily-monitoring/index` | 日常监测数据 |
| `pages/appointment/index` | 预约列表 |
| `pages/appointment/create/index` | 预约挂号 |
| `pages/appointment/detail/index` | 预约详情 |
| `pages/article/index` | 健康资讯列表 |
| `pages/article/detail/index` | 文章详情 |
| `pages/report/detail/index` | 健康报告详情 |
| `pages/ai-report/list/index` | AI 分析报告列表 |
| `pages/ai-report/detail/index` | AI 分析报告详情 |
| `pages/followup/detail/index` | 随访详情 |
| `pages/consultation/index` | 咨询列表Tab 页) |
| `pages/consultation/detail/index` | 咨询详情 |
| `pages/mall/index` | 积分商城Tab 页) |
| `pages/mall/detail/index` | 商品详情 |
| `pages/mall/exchange/index` | 积分兑换 |
| `pages/mall/orders/index` | 积分订单 |
| `pages/events/index` | 线下活动 |
| `pages/profile/index` | 个人中心Tab 页) |
| `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` | 设置 |
| `pages/legal/user-agreement` | 用户服务协议 |
| `pages/legal/privacy-policy` | 隐私政策 |
#### 医护端页面8 个)
| 页面路径 | 说明 |
|----------|------|
| `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` | 报告详情 |
### 服务层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 |
### 组件9 个)
| 组件 | 用途 |
|------|------|
| `EmptyState` | 空状态占位 |
| `ErrorBoundary` | 错误边界捕获 |
| `ErrorState` | 错误状态展示 |
| `FamilyPicker` | 家庭成员选择器 |
| `HealthCard` | 健康数据卡片 |
| `Loading` | 加载状态 |
| `StepIndicator` | 步骤指示器 |
| `TrendChart` | 趋势图表ECharts |
| `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 | 检查当前路径避免重复跳转 |
### 待优化
| 问题 | 级别 | 说明 |
|------|------|------|
| URL 拼接构建查询参数 | P2 | `request.ts` 应支持 params 对象 |
| 加密密钥硬编码 | P0 | 需外部化到 `TARO_APP_ENCRYPTION_KEY` 环境变量 |
| Auth token 日志输出 | P0 | 生产环境需移除 console.log |
| 生产配置 | P2 | `urlCheck`/`minified` 需区分环境 |
### 注意事项
- `Taro.login()` 的 code 一次性使用,每次调用会返回新 code
- `session_key` 缓存 5 分钟TTL过期需重新登录
- 微信开发者工具中 `getPhoneNumber` 需要真机调试或使用测试号
- Redis 不可达时限流降级为 fail-open不影响登录
## 6. MCP 联调(微信开发者工具自动化)
> 通过 MCP (Model Context Protocol) 工具直接操控微信开发者工具中的小程序模拟器,实现页面导航、元素交互、数据读取等操作。
### 6.1 前置条件
1. **后端运行**`cargo run` 启动 `http://localhost:3000`
2. **小程序已构建**`cd apps/miniprogram && pnpm build:weapp`
3. **微信开发者工具已打开项目** — 加载 `apps/miniprogram/dist/`
4. **自动化端口已开启**`project.config.json``"automationAudits": true`,端口 9420
### 6.2 建立连接
```
调用 mp_ensureConnection → 自动连接 ws://localhost:9420
```
成功后返回系统信息SDK 版本、设备型号、屏幕尺寸等)。
### 6.3 常用 MCP 操作
| 操作 | MCP 工具 | 说明 |
|------|----------|------|
| 查看当前页面 | `mp_currentPage` | 返回路径、尺寸、数据 |
| 导航到页面 | `mp_navigate` | `navigateTo` / `switchTab` / `reLaunch` |
| 返回上一页 | `mp_navigate` | `transition: "navigateBack"` |
| 截图 | `mp_screenshot` | ⚠️ 当前版本超时,见下方限制 |
| 读取 storage | `mp_callWx` | method: `getStorageSync` |
| 写入 storage | `mp_callWx` | method: `setStorageSync` |
| 查找元素 | `page_getElement` | CSS 选择器,返回标签/文本/尺寸 |
| 查找多个元素 | `page_getElements` | CSS 选择器数组 |
| 点击元素 | `element_tap` | CSS 选择器定位后点击 |
| 输入文本 | `element_input` | CSS 选择器 + 值 |
| 读取组件数据 | `element_getData` | 获取自定义组件的 data |
| 设置组件数据 | `element_setData` | 修改自定义组件的 data |
| 读取页面数据 | `page_getData` | 获取当前页面 data 对象 |
| 设置页面数据 | `page_setData` | 修改当前页面 data |
| 调用页面方法 | `page_callMethod` | 调用当前页面上暴露的方法 |
| 等待元素出现 | `page_waitElement` | 轮询等待选择器匹配 |
### 6.4 绕过微信登录
MCP 无法模拟微信 OAuth需要通过 `mp_callWx` 直接写入加密 auth 数据:
```
1. 调用后端 API 获取 admin token:
POST /api/v1/auth/login { username: "admin", password: "Admin@2026" }
2. 用 CryptoJS.AES.encrypt(token, ENC_KEY) 加密
3. 通过 mp_callWx 写入 storage:
setStorageSync("access_token", encrypted_token)
setStorageSync("refresh_token", encrypted_refresh)
setStorageSync("user_data", encrypted_user_json)
setStorageSync("user_roles", encrypted_roles_json)
setStorageSync("tenant_id", encrypted_tenant_id)
setStorageSync("current_patient", patient_object)
setStorageSync("current_patient_id", patient_id)
4. reLaunch 到首页
```
**ENC_KEY**: `0a17b71d46064b06f993c9c202b342425e311a79f5be026d830562e7ad51f522`
> ⚠️ 注意:写完 storage 后必须立即 reLaunch否则 app 的 API 请求会因 token 无效触发 401 → logout 清空 storage。
### 6.5 TabBar 页面列表
以下页面是 TabBar 页面,必须用 `switchTab` 导航(不能用 `navigateTo`
- `pages/index/index` — 首页
- `pages/health/index` — 健康数据
- `pages/consultation/index` — 咨询
- `pages/mall/index` — 积分商城
- `pages/profile/index` — 个人中心
### 6.6 已知限制
| 问题 | 原因 | 替代方案 |
|------|------|----------|
| **mp_screenshot 超时** | `miniprogram-automator``screenshot()` 方法在当前 DevTools 版本卡死 | 用 `page_getElement` + `page_getData` 获取 UI 状态 |
| **navigateTo 超时** | 某些页面的 API 调用阻塞了页面渲染回调 | 页面实际已加载,用 `mp_currentPage` 确认路径 |
| **auth 重定向** | app 的 request interceptor 检测 401 后自动跳转 login 并清空 storage | 确保 token 有效reLaunch 后等待足够时间 |
| **getStorageSync 返回空** | reLaunch 触发 app 初始化401 → logout 清空了 storage | 检查 token 是否过期,必要时重新注入 |
### 6.7 端到端验证脚本
`apps/miniprogram/e2e-final.cjs` 使用 `miniprogram-automator` Node.js 库执行完整链路验证:
- 11 条 UI 链路(页面导航 + 元素交互)
- 10 个 API 数据闭环验证
- 加密 storage 注入绕过微信登录
- 超时保护避免卡死
运行:`cd apps/miniprogram && node e2e-final.cjs`
## 7. 变更记录
| 日期 | 变更 |
|------|------|
| 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 页面,记录登录流程、环境配置、历史陷阱 |