diff --git a/.gitignore b/.gitignore index 1c1e001..5c9c9cf 100644 --- a/.gitignore +++ b/.gitignore @@ -35,3 +35,32 @@ docs/debug-*.png # Development env .env.development docker/docker-compose.override.yml +.agents/skills/ +.claude/skills/ +.kiro/skills/ +.trae/skills/ +.windsurf/skills/ +skills/ + +# Logs +.logs/ +*.log + +# Playwright reports +**/playwright-report/ + +# Plans +plans/ + +# MCP config +.mcp.json + +# Superpowers temp +.superpowers/brainstorm/ + +# Test temp files +.test_token* +chi_sim.traineddata + +# Local settings +.claude/settings.local.json \ No newline at end of file diff --git a/dev.ps1 b/dev.ps1 index 73b6ed9..1824797 100644 --- a/dev.ps1 +++ b/dev.ps1 @@ -26,6 +26,7 @@ $env:ERP__AUTH__SUPER_ADMIN_PASSWORD = "Admin@2026" $env:ERP__REDIS__URL = "redis://:redis_KBCYJk@129.204.154.246:6379" $env:ERP__WECHAT__APPID = "wx20f4ef9cc2ec66c5" $env:ERP__WECHAT__SECRET = "placeholder_wechat_secret" +$env:ERP__WECHAT__DEV_MODE = "true" $env:ERP__HEALTH__AES_KEY = "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4e5f6a1b2" $env:ERP__HEALTH__HMAC_KEY = "f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5d4c3b2a1f6e5" diff --git a/wiki/frontend.md b/wiki/frontend.md index eef9d1b..dbadf8b 100644 --- a/wiki/frontend.md +++ b/wiki/frontend.md @@ -1,6 +1,6 @@ --- title: Web 前端 -updated: 2026-04-26 +updated: 2026-04-28 status: stable tags: [frontend, react, antd, vite, spa] --- @@ -51,10 +51,53 @@ React 19.2.4 / Ant Design 6.3.5 / React Router 7.14.0 / Zustand 5.0.12 / Vite 8. - 使用 Ant Design 组件库,不自造轮子 - 中文优先,所有文案通过 i18n key 引用 -- 支持暗色/亮色主题切换 +- 支持 4 套主题切换:信任蓝 / 温润东方 / 深邃夜色 / 翡翠清雅 - 侧边栏按模块分组:基础模块 / 行业模块 - 表单验证使用 Ant Design Form 的 validateRules +### 2.1 多主题系统(4 套内置主题) + +> 自 2026-04-28 起采用多主题架构。用户可在顶栏主题切换器中选择偏好主题,选择持久化到 localStorage。 + +#### 4 套主题视觉人格 + +| 主题 | 主色 | 背景 | 圆角 | 性格 | +|------|------|------|------|------| +| **信任蓝** (blue) | `#2563EB` | `#F8FAFC` 冷灰 | 10/12/6px | 专业·企业 | +| **温润东方** (warm) | `#C4623A` | `#F5F0EB` 暖米 | 12/16/8px | 温润·人文 | +| **深邃夜色** (dark) | `#60A5FA` | `#0F172A` 深蓝黑 | 10/12/6px | 护眼·专注 | +| **翡翠清雅** (emerald) | `#5B7A5E` | `#F4F7F4` 浅绿灰 | 10/14/8px | 清新·健康 | + +#### 技术架构 + +- **CSS 变量层** — `:root` 默认为 blue,`[data-theme='xxx']` 覆盖全部视觉 token(`apps/web/src/index.css`) +- **Ant Design 动态主题** — `ConfigProvider` 的 `theme` prop 按 ThemeName 选择不同配置(`apps/web/src/App.tsx`) +- **Zustand 持久化** — `useAppStore().theme` + `localStorage('hms-theme')`(`apps/web/src/stores/app.ts`) +- **暗色检测** — `useThemeMode()` hook 从 store 读取,不再比对色值(`apps/web/src/hooks/useThemeMode.ts`) + +#### 温润东方风详细 Token(与小程序端共享) + +> 小程序端源文件:`apps/miniprogram/src/styles/variables.scss` + `mixins.scss` + +| 角色 | CSS 变量 | 色值 | +|------|----------|------| +| 主色 | `--erp-primary` | `#C4623A` | +| 背景 | `--erp-bg-page` | `#F5F0EB` | +| 容器 | `--erp-bg-container` | `#FFFFFF` | +| 主文字 | `--erp-text-primary` | `#2D2A26` | +| 次文字 | `--erp-text-secondary` | `#7A756E` | +| 边框 | `--erp-border` | `#E8E2DC` | +| 成功 | `--erp-success` | `#5B7A5E` | +| 警告 | `--erp-warning` | `#C4873A` | +| 错误 | `--erp-error` | `#B54A4A` | + +#### 禁止事项 + +- 禁止紫色渐变、禁止 emoji 作图标 +- 禁止左侧彩色边框卡片标示状态(改用 tag 标签) +- 禁止无意义的渐变背景 +- 禁止装饰性 icon 遍地配 + ## 3. 关键文件 + 数据流 ### 核心文件 @@ -62,9 +105,11 @@ React 19.2.4 / Ant Design 6.3.5 / React Router 7.14.0 / Zustand 5.0.12 / Vite 8. | 文件 | 职责 | |------|------| | `apps/web/src/main.tsx` | React 入口 | -| `apps/web/src/App.tsx` | 路由定义(公开 + 受保护) | -| `apps/web/src/layouts/MainLayout.tsx` | SaaS 后台管理布局 | +| `apps/web/src/App.tsx` | 路由定义 + Ant Design 动态主题(4 套) | +| `apps/web/src/layouts/MainLayout.tsx` | SaaS 后台管理布局 + ThemeSwitcher 集成 | | `apps/web/src/stores/` | 4 个 Zustand store | +| `apps/web/src/components/ThemeSwitcher.tsx` | 主题选择下拉面板 | +| `apps/web/src/hooks/useThemeMode.ts` | 暗色模式检测(从 store 读取) | | `apps/web/src/api/` | 28 个 API 服务文件(含 7 个健康模块 API) | | `apps/web/vite.config.ts` | Vite 配置 + API 代理 | @@ -145,7 +190,7 @@ React 19.2.4 / Ant Design 6.3.5 / React Router 7.14.0 / Zustand 5.0.12 / Vite 8. | Store | 状态 | |-------|------| -| `app.ts` | theme(light/dark), sidebarCollapsed | +| `app.ts` | theme(blue/warm/dark/emerald), sidebarCollapsed, localStorage 持久化 | | `auth.ts` | user, isAuthenticated, localStorage 持久化 | | `message.ts` | unreadCount, recentMessages, 请求去重 | | `plugin.ts` | plugins 列表, 动态菜单, schema 缓存, 请求去重 | @@ -208,6 +253,7 @@ ws://localhost:5174/ws/* → ws://localhost:3000/* (WebSocket) | 日期 | 变更 | |------|------| +| 2026-04-28 | 多主题系统:4 套主题(blue/warm/dark/emerald)+ CSS 变量 + Ant Design 动态主题 + ThemeSwitcher + useThemeMode 修复 | | 2026-04-26 | 全面更新:22 条健康路由(+12 内容/积分/统计/活动/AI)、11 个共享组件、77 个 TSX 文件 | | 2026-04-26 | 从 CLAUDE.md 迁移:UI 布局规范(§8) | | 2026-04-26 | VitalSignsChart 重设计:概览卡片条 + 点击展开详情图,5 指标独立 Y 轴 | diff --git a/wiki/index.md b/wiki/index.md index 40f0f3b..f761cfd 100644 --- a/wiki/index.md +++ b/wiki/index.md @@ -13,7 +13,7 @@ | erp-health 实体 | 34 个 Entity(17k 行 Rust) | | erp-ai 实体 | 3 个 Entity(1.7k 行 Rust) | | Web 前端 | 77 个 TSX + 56 个 TS = 133 个源文件(48 个页面 + 22 健康页面 + 11 健康组件) | -| 微信小程序 | Taro 4.2 + React 18,12 个页面 | +| 微信小程序 | Taro 4.2 + React 18,40 个页面(31 患者端 + 9 医护端),5 个 TabBar | | 前端单元测试 | 3 个(vitest)+ 4 E2E spec(playwright) | | 后端测试 | 36 个(workspace)+ 83 validation 纯函数测试 | | 总代码量 | Rust ~63k 行 + 前端 TSX/TS ~133 文件 + 小程序 ~7.5k 行 | @@ -40,7 +40,10 @@ | 迁移文件缺失报错 | [[database]] 迁移注册 | migration/src/lib.rs | 已应用的迁移文件被删除,需创建 stub | | MCP 连接失败 `split` error | [[miniprogram]] MCP 联调 | project.config.json | 未开启 `automationAudits` | | MCP mp_screenshot 超时 | [[miniprogram]] MCP 联调 | automator 已知 bug | 用 `page_getElement` 替代截图 | -| MCP 导航后跳回登录页 | [[miniprogram]] MCP 联调 §6.4 | storage 被清空 | 加密 token 写入后立即 reLaunch | +| MCP 导航后跳回登录页 | [[miniprogram]] MCP 联调 §6.4 | storage 被清空 | 明文 token 写入后立即 reLaunch | +| MCP token 注入后仍 401 | [[miniprogram]] MCP 联调 §6.1 | 用了生产构建 | dev 构建(`NODE_ENV=development`)+ 空密钥 | +| 积分商城 Tab 页空白 | [[miniprogram]] 待优化 | 未关联患者档案 | 需增加降级 UI 引导建档 | +| MCP 批量审计页面栈溢出 | [[miniprogram]] MCP 联调 §6.6 | `navigateTo` 超 10 层 | 改用 `reLaunch` 逐页测试 | ## 模块导航 diff --git a/wiki/infrastructure.md b/wiki/infrastructure.md index c631697..267c2f3 100644 --- a/wiki/infrastructure.md +++ b/wiki/infrastructure.md @@ -55,6 +55,7 @@ psql: `D:\postgreSQL\bin\psql.exe -U postgres -h localhost -d erp` | `ERP__REDIS__URL` | `redis://:redis_KBCYJk@129.204.154.246:6379` | | `ERP__WECHAT__APPID` | `wx20f4ef9cc2ec66c5` | | `ERP__WECHAT__SECRET` | 微信小程序 Secret | +| `ERP__WECHAT__DEV_MODE` | `true`(开发时跳过 jscode2session,允许 DevTools 模拟器登录) | | `ERP__HEALTH__AES_KEY` | 64 字符 hex 编码(32 字节) | | `ERP__HEALTH__HMAC_KEY` | 64 字符 hex 编码(32 字节) | diff --git a/wiki/miniprogram.md b/wiki/miniprogram.md index eb61b63..741959b 100644 --- a/wiki/miniprogram.md +++ b/wiki/miniprogram.md @@ -1,6 +1,6 @@ --- title: 微信小程序(患者端) -updated: 2026-04-26 +updated: 2026-04-27 status: active tags: [miniprogram, taro, wechat, patient] --- @@ -20,7 +20,67 @@ tags: [miniprogram, taro, wechat, patient] ### 版本 -Taro 4.2 / React 18 / TypeScript / Zustand 5 / Sass / Zod +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` + +**设计哲学**: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 遍地配。 ## 2. 关键文件 + 数据流 @@ -122,10 +182,11 @@ POST /auth/wechat/login { code } | `analytics.ts` | 数据分析 | | `wechat-templates.ts` | 微信模板消息 ID | -### 组件(9 个) +### 组件(10 个) | 组件 | 用途 | |------|------| +| `EcCanvas` | ECharts Canvas 包装(Taro 4 兼容,按需引入 echarts/core) | | `EmptyState` | 空状态占位 | | `ErrorBoundary` | 错误边界捕获 | | `ErrorState` | 错误状态展示 | @@ -133,7 +194,7 @@ POST /auth/wechat/login { code } | `HealthCard` | 健康数据卡片 | | `Loading` | 加载状态 | | `StepIndicator` | 步骤指示器 | -| `TrendChart` | 趋势图表(ECharts) | +| `TrendChart` | 趋势图表(基于 EcCanvas) | | `WeekCalendar` | 周日历组件 | ### 集成契约 @@ -214,15 +275,21 @@ secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>" | 登录成功但前端报失败 `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` 按需引入 | ### 待优化 | 问题 | 级别 | 说明 | |------|------|------| | URL 拼接构建查询参数 | P2 | `request.ts` 应支持 params 对象 | -| 加密密钥硬编码 | P0 | 需外部化到 `TARO_APP_ENCRYPTION_KEY` 环境变量 | +| 加密密钥硬编码 | ~~P0~~ 已解决 | 已外部化到 `TARO_APP_ENCRYPTION_KEY` 环境变量 | | Auth token 日志输出 | P0 | 生产环境需移除 console.log | | 生产配置 | P2 | `urlCheck`/`minified` 需区分环境 | +| 积分商城降级 UI | P0 | 未关联患者档案时 Tab 页空白,需引导用户建档 | +| daily-monitoring 无 Zod 验证 | P1 | 对齐 health/input 的验证标准 | +| 文章列表返回草稿 | P1 | 患者端应只展示 `published` 状态文章 | +| Token 刷新竞态 | P0 | 多个 API 同时 401 时各自独立刷新,可能锁死用户 | +| ECharts 全量引入 | P2 | 趋势页 455KiB,按需引入可减少 ~80% | ### 注意事项 @@ -238,10 +305,20 @@ secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>" ### 6.1 前置条件 1. **后端运行** — `cargo run` 启动 `http://localhost:3000` -2. **小程序已构建** — `cd apps/miniprogram && pnpm build:weapp` +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 +> ⚠️ **为什么必须 dev 模式?** 生产构建(`pnpm build:weapp`)设置 `NODE_ENV=production`,`secure-storage.ts` 的 `decrypt()` 在密钥为空时会抛异常 "TARO_APP_ENCRYPTION_KEY 未设置,生产环境禁止明文读取"。dev 模式允许空密钥走明文存储。 + ### 6.2 建立连接 ``` @@ -273,29 +350,70 @@ secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>" ### 6.4 绕过微信登录 -MCP 无法模拟微信 OAuth,需要通过 `mp_callWx` 直接写入加密 auth 数据: +MCP 无法模拟微信 OAuth(`Taro.login()` 返回的 code 走真实微信 `jscode2session` 接口,DevTools 模拟器会返回 mock code 导致后端 500)。 + +#### 方案一:明文 token 注入(推荐,用于自动化测试) + +**原理:** 以 dev 模式重编译(空加密密钥),`secure-storage.ts` 走明文路径,直接用 `wx.setStorageSync` 写入。 + +**步骤:** ``` -1. 调用后端 API 获取 admin token: - POST /api/v1/auth/login { username: "admin", password: "Admin@2026" } +1. 准备:确保已按 §6.1 以 dev 模式构建 -2. 用 CryptoJS.AES.encrypt(token, ENC_KEY) 加密 +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, ... } } } -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) +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 到首页 +4. reLaunch 到首页: + mp.reLaunch('/pages/index/index'); + await sleep(3000); ``` -**ENC_KEY**: `0a17b71d46064b06f993c9c202b342425e311a79f5be026d830562e7ad51f522` +**关键 tenant/patient ID:** +- `tenant_id`: `019d80da-7a2c-7820-b0a3-3d5266a3a324` +- `current_patient_id`: `019dcd34-bc4d-72c1-8c19-77ce1f4839d6` -> ⚠️ 注意:写完 storage 后必须立即 reLaunch,否则 app 的 API 请求会因 token 无效触发 401 → logout 清空 storage。 +**为什么不用 mp_callWx?** +- 加密 token(~2100 字符)通过 `mp_callWx setStorageSync` 传输时会被截断/损坏 +- `automator.evaluate()` 直接在小程序 JS 上下文执行,无传输问题 +- `require('./utils/secure-storage')` 在 evaluate 中不可用(webpack 用数字 ID 注册模块),只能用 `wx.setStorageSync` + +#### 方案二:加密 token 注入(用于真实环境测试) + +如果需要测试加密存储路径(密钥非空),可以: + +``` +1. 恢复 .env 中的加密密钥: + TARO_APP_ENCRYPTION_KEY=0a17b71d46064b06f993c9c202b342425e311a79f5be026d830562e7ad51f522 + +2. 重新构建: pnpm build:weapp + +3. 使用 inject-auth.cjs 脚本(内含 CryptoJS AES 加密逻辑) +``` + +> ⚠️ 实测发现加密 token 通过 MCP 传输存在截断问题,方案二仅在需要测试加密存储时使用。 + +#### 注意事项 + +- 写完 storage 后**必须立即 reLaunch**,否则 app 的 API 请求会因 token 无效触发 401 → logout 清空 storage +- `reLaunch` 后等待 2-3 秒再进行后续操作,页面需要时间加载和初始化 +- 恢复 `.env` 加密密钥后需重新构建,否则 `secure-storage.ts` 行为不一致 ### 6.5 TabBar 页面列表 @@ -312,25 +430,93 @@ MCP 无法模拟微信 OAuth,需要通过 `mp_callWx` 直接写入加密 auth | 问题 | 原因 | 替代方案 | |------|------|----------| | **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 是否过期,必要时重新注入 | +| **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 秒 | +| **生产构建 decrypt 抛异常** | 空密钥 + `NODE_ENV=production` 时 `decrypt()` 直接 throw | 使用 `NODE_ENV=development` 构建 | -### 6.7 端到端验证脚本 +### 6.7 自动化审计脚本 -`apps/miniprogram/e2e-final.cjs` 使用 `miniprogram-automator` Node.js 库执行完整链路验证: +| 脚本 | 用途 | 运行 | +|------|------|------| +| `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` | -- 11 条 UI 链路(页面导航 + 元素交互) -- 10 个 API 数据闭环验证 -- 加密 storage 注入绕过微信登录 -- 超时保护避免卡死 +#### 批量审计脚本模板 -运行:`cd apps/miniprogram && node e2e-final.cjs` +```javascript +const automator = require('miniprogram-automator'); + +async function main() { + const mp = await automator.connect({ wsEndpoint: 'ws://localhost:9420' }); + const results = { ok: [], crash: [], login: [] }; + + 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); + } + } + + console.log(`OK: ${results.ok.length}, Login: ${results.login.length}, Crash: ${results.crash.length}`); + await mp.disconnect(); +} +main(); +``` + +### 6.8 审计结果(2026-04-27 实测) + +通过 MCP 自动化工具对全部 40 个页面进行渲染审计: + +| 类别 | 数量 | 结果 | +|------|------|------| +| TabBar 页面 | 5 | 5/5 OK | +| 患者端子页面 | 24 | 24/24 OK | +| 详情页(假 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-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 验证、加密密钥外部化说明 |