diff --git a/wiki/index.md b/wiki/index.md index 53b6676..3c8a5b2 100644 --- a/wiki/index.md +++ b/wiki/index.md @@ -4,7 +4,7 @@ ## 关键数字 -> 最后更新: 2026-05-07 | 数据截止: commit 6d5a711 (第 693 次提交) +> 最后更新: 2026-05-09 | 数据截止: commit 890c132 (第 716 次提交) | 指标 | 值 | |------|-----| @@ -16,7 +16,7 @@ | erp-health 实体 | 46 个 Entity(~36k 行 Rust,179 文件) | | erp-ai 实体 | 6 个 Entity(~7k 行 Rust,45 文件,4 AI Provider) | | Web 前端 | 283 个 TS/TSX 文件(55 路由,含 38 健康路由 + 6 冻结路由) | -| 微信小程序 | Taro 4.2 + React 18,118 个 TS/TSX 文件 / ~54 页面 / 4 TabBar + 医生端分包 | +| 微信小程序 | Taro 4.2 + React 18,118 个 TS/TSX 文件 / 59 页面 / 3 TabBar + 医生端分包(10 子包) | | 前端单元测试 | 62 个测试文件(vitest)+ 13 E2E spec(playwright) | | 后端测试 | 611 单元 + 153 集成 = 772 个函数(97.5% 通过率) | | 总代码量 | Rust ~87k 行(579 源文件)+ Web 前端 283 文件 + 小程序 118 文件 | @@ -25,10 +25,11 @@ | 权限码 | 50 声明(health 39 + ai 6 + dialysis 5)+ 56 基础模块手动注册 | | Clippy | **全 workspace 0 警告**(2026-05-07 清零) | | API 文档 | `http://localhost:3000/api/docs/openapi.json` | -| Git 提交 | 693 次 | +| Git 提交 | 716 次 | | 审计状态 | V1: 83% (2026-04-30) → V2: 85% (2026-05-05),P0 安全修复已完成 | -| 角色测试 | R01-R05 全角色验证完成,86.5% 通过率,5 个 BUG 已修复 | +| 角色测试 | R01-R05 全角色验证完成,86.5% 通过率,5 个 BUG 已修复;小程序 MP 多角色 96.2% 通过率 | | UI/UX 重构 | Phase 1-5 完成(6 共享组件 + 4 角色仪表盘 + 个人统计数据 + 表单抽屉 + 小程序优化) | +| Design Token | 10 级字号 + 4 结构 token,68 SCSS 文件全面接入(634 引用,3 特殊硬编码),关怀模式 CSS 变量级联自动生效 | | 项目阶段 | **上线前质量加固**(近 30 次提交全为 fix 类型) | ## 症状导航 @@ -53,6 +54,8 @@ | MCP mp_screenshot 超时 | [[miniprogram]] MCP 联调 | automator 已知 bug | 用 `page_getElement` 替代截图 | | MCP 导航后跳回登录页 | [[miniprogram]] MCP 联调 §6.4 | storage 被清空 | 明文 token 写入后立即 reLaunch | | MCP token 注入后仍 401 | [[miniprogram]] MCP 联调 §6.1 | 用了生产构建 | dev 构建(`NODE_ENV=development`)+ 空密钥 | +| MCP App 级命令全部超时 | [[miniprogram]] MCP 联调 §6.2 | 多 DevTools 实例冲突 | `taskkill /F /IM wechatdevtools.exe` 后重开 | +| MCP CLI 报"需要重新登录" | [[miniprogram]] MCP 联调 §6.2 | DevTools 未扫码 | 在 DevTools 中先扫码登录 | | 积分商城 Tab 页空白 | [[miniprogram]] 待优化 | 未关联患者档案 | 需增加降级 UI 引导建档 | | MCP 批量审计页面栈溢出 | [[miniprogram]] MCP 联调 §6.6 | `navigateTo` 超 10 层 | 改用 `reLaunch` 逐页测试 | | 告警管理按钮不显示 | [[frontend]] 权限码拼写 | AlertList.tsx | `health.alert.manage` → `health.alerts.manage`(缺 s) | @@ -70,6 +73,7 @@ | FHIR 端点全部 404 | [[erp-server]] main.rs | 路由注册在 `/fhir` 非 `/api/v1/fhir` | **已修复:** 移到 `/api/v1/fhir` | | 冻结模块 API 可绕过 | [[erp-server]] frozen_module | 后端无拦截中间件 | **已修复:** 新增 `frozen_module_middleware` | | 积分端点 403 权限码错 | [[erp-health]] points_handler | 患者端用了 `health.health-data.list` | **已修复:** 改为 `health.points.list` | +| MCP 审计大量 LOGIN_REDIRECT | [[miniprogram]] §6.8 审计脚本 | 测试用户密码配置错误 | **已修复:** 所有测试用户密码均为 `Admin@2026`(不是 `Test@2026`) | ## 模块导航 diff --git a/wiki/miniprogram.md b/wiki/miniprogram.md index 60e47c3..bcecd2d 100644 --- a/wiki/miniprogram.md +++ b/wiki/miniprogram.md @@ -1,6 +1,6 @@ --- title: 微信小程序(患者端) -updated: 2026-04-27 +updated: 2026-05-08 status: active tags: [miniprogram, taro, wechat, patient] --- @@ -28,7 +28,7 @@ Taro 4.2 / React 18 / TypeScript / Zustand 5 / Sass / Zod / ECharts 6(按需 > 自 2026-04-27 起采用。所有新增/修改页面必须遵循此设计系统,保持视觉一致性。 > -> 设计系统源文件:`apps/miniprogram/src/styles/variables.scss` + `mixins.scss` +> 设计系统源文件:`apps/miniprogram/src/styles/variables.scss` + `mixins.scss` + `tokens.scss` **设计哲学**:Kenya Hara 式东方极简。温润米底 + 单一赤土橙贯穿全场,留白呼吸感优先。 @@ -82,6 +82,65 @@ Taro 4.2 / React 18 / TypeScript / Zustand 5 / Sass / Zod / ECharts 6(按需 **禁止事项**:紫色渐变 / 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. 关键文件 + 数据流 ### 核心文件 @@ -115,49 +174,45 @@ POST /auth/wechat/login { code } 后端解密手机号 → 创建/关联用户 → 返回 JWT → 跳转首页 ``` -### 页面结构(40 个页面,15 个目录) +### 页面结构(59 个页面,6 个主包 + 10 个子包) -#### 患者端页面 +#### TabBar 页面(3 个) + +| 页面路径 | 说明 | +|----------|------| +| `pages/index/index` | 首页(今日健康、快捷服务) | +| `pages/health/index` | 健康上报(Tab 页) | +| `pages/messages/index` | 消息中心(Tab 页) | + +#### 患者端主页面 | 页面路径 | 说明 | |----------|------| | `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/consultation/index` | 咨询列表 | +| `pages/consultation/detail/index` | 咨询详情 | +| `pages/mall/index` | 积分商城 | +| `pages/profile/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 个) +#### 健康子包(pkg-health,4 个) | 页面路径 | 说明 | |----------|------| -| `pages/doctor/index` | 医护首页 | +| `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` | 咨询管理 | @@ -166,6 +221,54 @@ POST /auth/wechat/login { code } | `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+ 个文件) @@ -297,12 +400,12 @@ secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>" | 发现 | 严重性 | 说明 | |------|--------|------| | ~~晚间血压数据永久丢失~~ | ~~CRITICAL~~ **已修复** | 新增 `blood_pressure_evening` indicator_type,小程序录入页 + 日常监测页 + 后端 service + 集成测试均已覆盖 | -| 透析管理完全无入口 | HIGH | 后端 12 路由完整,小程序 0 个 API 调用、0 个页面 | -| 知情同意完全无入口 | HIGH | 后端完整,小程序 0 入口 | +| ~~透析管理完全无入口~~ | ~~HIGH~~ **已修复** | 医生端新增 dialysis 3 页面 + prescription 3 页面,患者端新增 dialysis-records 2 页面 + dialysis-prescriptions 2 页面 | +| ~~知情同意完全无入口~~ | ~~HIGH~~ **已修复** | 患者端新增 `pkg-profile/consents` 页面 | | 体温/血氧未映射 | MEDIUM | `body_temperature`/`spo2` 无 indicator_type | -| 健康记录小程序无入口 | MEDIUM | 患者移动端无法查看健康档案 | -| 诊断记录小程序无入口 | MEDIUM | 患者移动端无法查看诊断 | -| 小程序完全无测试 | HIGH | 40 个页面全靠手工验证 | +| ~~健康记录小程序无入口~~ | ~~MEDIUM~~ **已修复** | 患者端新增 `pkg-profile/health-records` 页面 | +| ~~诊断记录小程序无入口~~ | ~~MEDIUM~~ **已修复** | 患者端新增 `pkg-profile/diagnoses` 页面 | +| 小程序无自动化测试 | HIGH | 已建立 MCP 自动化审计流程,4 角色 236 次探测通过率 96.2% | **功能域完成度**(小程序端): @@ -312,12 +415,13 @@ secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>" | 预约管理 | 90% | 基本对齐 | | 随访管理 | 70% | 仅列表+创建 | | 患者管理 | 85% | 无删除(预期) | -| 健康数据 | 60% | 丢失晚间血压/体温/血氧 | -| 告警系统 | 60% | 仅查看+处理 | +| 健康数据 | 80% | ~~丢失晚间血压~~ 已修复 | +| 告警系统 | 80% | 查看+处理+医护端管理 | +| 透析管理 | 70% | 医护端管理+患者端查看记录/处方 | +| 知情同意 | 60% | 患者端查看,无签署流程 | +| 健康档案 | 60% | 患者端可查看健康档案和诊断 | | 统计仪表盘 | 30% | 仅医护端 3 个统计 | | AI 分析 | 30% | 仅历史查看 | -| 透析管理 | 0% | 完全空白 | -| 知情同意 | 0% | 完全空白 | ### 注意事项 @@ -329,6 +433,9 @@ secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>" ## 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 前置条件 @@ -336,112 +443,131 @@ secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>" 2. **小程序以 dev 模式构建** — 必须使用 `NODE_ENV=development` 构建(详见 §6.4) ```bash cd apps/miniprogram - # 清空加密密钥(必须,否则 encrypt/decrypt 不一致) echo 'TARO_APP_API_URL=http://localhost:3000/api/v1 TARO_APP_ENCRYPTION_KEY=' > .env - # dev 模式构建 NODE_ENV=development npx taro build --type weapp ``` -3. **微信开发者工具已打开项目** — 加载 `apps/miniprogram/dist/` -4. **自动化端口已开启** — `project.config.json` 中 `"automationAudits": true`,端口 9420 +3. **微信开发者工具已登录** — 确保已扫码登录(CLI 命令需要登录态) +4. **微信开发者工具打开项目** — 手动打开,加载 `apps/miniprogram/dist/` +5. **自动化端口已开启** — `project.config.json` 中 `"automationAudits": true` -> ⚠️ **为什么必须 dev 模式?** 生产构建(`pnpm build:weapp`)设置 `NODE_ENV=production`,`secure-storage.ts` 的 `decrypt()` 在密钥为空时会抛异常 "TARO_APP_ENCRYPTION_KEY 未设置,生产环境禁止明文读取"。dev 模式允许空密钥走明文存储。 +> ⚠️ **为什么必须 dev 模式?** 生产构建(`pnpm build:weapp`)设置 `NODE_ENV=production`,`secure-storage.ts` 的 `decrypt()` 在密钥为空时会抛异常。dev 模式允许空密钥走明文存储。 -### 6.2 建立连接 +### 6.2 启动调试环境 -``` -调用 mp_ensureConnection → 自动连接 ws://localhost:9420 +> **铁律:只允许一个 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 开启自动化 ``` -成功后返回系统信息(SDK 版本、设备型号、屏幕尺寸等)。 +#### 方式 B:手动启动 + CLI 开端口 -### 6.3 常用 MCP 操作 +```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 工具 | 说明 | |------|----------|------| -| 查看当前页面 | `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` | 轮询等待选择器匹配 | +| 连接 | `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)。 -#### 方案一:明文 token 注入(推荐,用于自动化测试) +#### 方案一:inject_auth 一键注入(推荐) -**原理:** 以 dev 模式重编译(空加密密钥),`secure-storage.ts` 走明文路径,直接用 `wx.setStorageSync` 写入。 - -**步骤:** +**原理:** 以 dev 模式重编译(空加密密钥),通过 MCP 的 `inject_auth` 工具自动完成:获取 admin token → evaluate 注入 storage → reLaunch 首页。 ``` -1. 准备:确保已按 §6.1 以 dev 模式构建 +1. 准备:确保已按 §6.1 以 dev 模式构建,且已连接 MCP(connect) -2. 获取 admin token: - POST http://localhost:3000/api/v1/auth/login - Body: { "username": "admin", "password": "Admin@2026" } - 返回: { data: { access_token, refresh_token, user: { id, username, ... } } } +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. 通过 automator.evaluate() 注入明文 storage: - mp.evaluate((at, rt, ud, ur, tid, pid) => { - wx.setStorageSync('access_token', at); - wx.setStorageSync('refresh_token', rt); - wx.setStorageSync('user_data', ud); - wx.setStorageSync('user_roles', ur); - wx.setStorageSync('tenant_id', tid); - wx.setStorageSync('current_patient_id', pid); - wx.setStorageSync('current_patient', { - id: pid, name: 'TestPatient', gender: 'male', - birth_date: '1990-01-15', status: 'active' - }); - }, accessToken, refreshToken, userDataJson, rolesJson, tenantId, patientId); - -4. reLaunch 到首页: - mp.reLaunch('/pages/index/index'); - await sleep(3000); +3. 成功后即可正常操作各页面 ``` -**关键 tenant/patient ID:** +**关键 ID(默认值):** - `tenant_id`: `019d80da-7a2c-7820-b0a3-3d5266a3a324` -- `current_patient_id`: `019dcd34-bc4d-72c1-8c19-77ce1f4839d6` +- `patient_id`: `019dcd34-bc4d-72c1-8c19-77ce1f4839d6` -**为什么不用 mp_callWx?** -- 加密 token(~2100 字符)通过 `mp_callWx setStorageSync` 传输时会被截断/损坏 -- `automator.evaluate()` 直接在小程序 JS 上下文执行,无传输问题 -- `require('./utils/secure-storage')` 在 evaluate 中不可用(webpack 用数字 ID 注册模块),只能用 `wx.setStorageSync` +#### 方案二:手动分步注入 -#### 方案二:加密 token 注入(用于真实环境测试) +```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"}' -如果需要测试加密存储路径(密钥非空),可以: - -``` -1. 恢复 .env 中的加密密钥: - TARO_APP_ENCRYPTION_KEY=0a17b71d46064b06f993c9c202b342425e311a79f5be026d830562e7ad51f522 - -2. 重新构建: pnpm build:weapp - -3. 使用 inject-auth.cjs 脚本(内含 CryptoJS AES 加密逻辑) +# 2. 通过 MCP call_wx 逐个写入 storage +# 3. 通过 MCP navigate reLaunch 到首页 ``` -> ⚠️ 实测发现加密 token 通过 MCP 传输存在截断问题,方案二仅在需要测试加密存储时使用。 - -#### 注意事项 - -- 写完 storage 后**必须立即 reLaunch**,否则 app 的 API 请求会因 token 无效触发 401 → logout 清空 storage -- `reLaunch` 后等待 2-3 秒再进行后续操作,页面需要时间加载和初始化 -- 恢复 `.env` 加密密钥后需重新构建,否则 `secure-storage.ts` 行为不一致 +> ⚠️ 长字符串 token 通过 `call_wx` 传输可能截断。建议用 `inject_auth` 工具(内部用 evaluate 直接执行,无传输问题)。 ### 6.5 TabBar 页面列表 @@ -449,64 +575,107 @@ MCP 无法模拟微信 OAuth(`Taro.login()` 返回的 code 走真实微信 `js - `pages/index/index` — 首页 - `pages/health/index` — 健康数据 -- `pages/consultation/index` — 咨询 -- `pages/mall/index` — 积分商城 -- `pages/profile/index` — 个人中心 +- `pages/messages/index` — 消息中心 + +> 注意:咨询、商城、个人中心虽然显示为 Tab 样式,但实际不是 TabBar 页面(使用 `reLaunch` 导航)。 ### 6.6 已知限制 | 问题 | 原因 | 替代方案 | |------|------|----------| -| **mp_screenshot 超时** | `miniprogram-automator` 的 `screenshot()` 方法在当前 DevTools 版本卡死 | 用 `page_getElement` + `page_getData` 获取 UI 状态 | +| **screenshot 超时** | `App.captureScreenshot` 在 DevTools 2.01.2510290 不响应 | 用 `get_element` + `page_data` 获取 UI 状态 | +| **多实例导致全挂** | 两个 DevTools 实例抢端口,App 域命令全部超时且不可恢复 | `taskkill /F /IM wechatdevtools.exe` 后重开 | | **navigateTo 超出 10 页栈** | 小程序 webview 限制最多 10 层页面栈 | 批量测试用 `reLaunch` 逐页导航 | -| **加密 token MCP 传输截断** | AES 加密后的 token(~2100 字符)通过 `mp_callWx` 传输时损坏 | 使用 `evaluate()` 在 JS 上下文内直接写入 | -| **require() 在 evaluate 不可用** | webpack 用数字 ID 注册模块,非文件路径 | 直接使用 `wx.setStorageSync` / `wx.getStorageSync` | -| **CryptoJS 非全局变量** | CryptoJS 打包在 webpack bundle 内,evaluate 上下文无法访问 | 绕过加密:dev 模式 + 空密钥 | -| **auth 重定向** | request interceptor 检测 401 后自动跳转 login 并清空 storage | 确保 token 有效,reLaunch 后等待 2-3 秒 | +| **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 自动化审计脚本 +### 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/audit-pages.cjs` | 批量审计 24 个非 TabBar 页面 | `node audit-pages.cjs` | -| `apps/miniprogram/audit-detail-pages.cjs` | 审计 11 个详情页(假 ID 优雅降级) | `node audit-detail-pages.cjs` | | `apps/miniprogram/e2e-final.cjs` | 完整 E2E 链路验证(11 UI + 10 API) | `node e2e-final.cjs` | -#### 批量审计脚本模板 +#### 分批审计脚本用法 -```javascript -const automator = require('miniprogram-automator'); +```bash +# 在 tools/weapp-mcp/ 目录下运行 +cd tools/weapp-mcp -async function main() { - const mp = await automator.connect({ wsEndpoint: 'ws://localhost:9420' }); - const results = { ok: [], crash: [], login: [] }; +# 审计指定角色(admin/doctor/nurse/operator) +node scripts/audit-pages.mjs --role admin - for (const pageUrl of PAGES) { - try { - await mp.reLaunch(`/${pageUrl}`); - await new Promise(r => setTimeout(r, 2000)); - const current = await mp.currentPage(); - if (current.path === pageUrl.split('?')[0]) { - results.ok.push(pageUrl); - } else if (current.path === 'pages/login/index') { - results.login.push(pageUrl); - } else { - results.crash.push(pageUrl); - } - } catch (e) { - results.crash.push(pageUrl); - } - } +# 自定义批次大小(默认 10 页/批,每批重启 DevTools 防止多实例超时) +node scripts/audit-pages.mjs --role doctor --batch-size 8 - console.log(`OK: ${results.ok.length}, Login: ${results.login.length}, Crash: ${results.crash.length}`); - await mp.disconnect(); -} -main(); +# 输出: +# - 控制台实时显示每页状态(OK / LOGIN_REDIRECT / ERROR) +# - JSON 报告保存到 docs/qa/role-test-results/MP-{role}-audit.json ``` -### 6.8 审计结果(2026-04-27 实测) +**设计要点:** +- 每批 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 个页面进行渲染审计: @@ -517,32 +686,15 @@ main(); | 详情页(假 ID) | 11 | 11/11 优雅降级 | | **合计** | **40** | **40/40 全部通过** | -核心 API 数据链路验证(13 端点): - -| API 端点 | 状态 | 说明 | -|----------|------|------| -| GET /health/vital-signs/today | 200 | | -| GET /health/patients | 200 | | -| GET /health/doctors | 200 | | -| GET /health/appointments | 200 | | -| GET /health/consultation-sessions | 200 | | -| GET /health/follow-up-tasks | 200 | | -| GET /health/articles | 200 | | -| GET /health/points/products | 200 | | -| GET /ai/analysis/history | 200 | | -| GET /health/points/account | 404 | admin 无患者档案(预期) | -| GET /health/points/transactions | 404 | 同上 | -| GET /health/points/orders | 404 | 同上 | -| GET /health/points/checkin/status | 404 | 同上 | - -> 积分端点 404 说明:路由已注册在 `module.rs:454-484`,`resolve_patient_id()` 要求登录用户关联患者档案。admin 是管理员账号,无患者档案,因此返回 "当前用户未关联患者档案"。这是正确的业务逻辑。 - 详细审计报告见 `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` 按需引入;更新版本说明 + 历史教训 + 组件列表 |