fix(mp): T40 UI 审计修复 — 28 项设计系统合规 + 安全加固 + 讨论记录

T40 UI 审计修复(60 页面全覆盖):
- 新增 $acc-d/$wrn-d 渐变中间色变量,修复首页轮播渐变硬编码
- 替换 8 处裸 white 为 $white 设计变量(5 个 SCSS 文件)
- 修复 7 处触摸目标 40/44px → 48px(健康/消息/咨询/预约/首页)
- 3 页面新增 Loading 状态(体征录入/个人中心/就诊人添加)
- statusTag 移除硬编码布局值,改用 SCSS mixin 控制
- 医生端 14 页面架构 Hook 层补充(useThrottledDidShow 替换 useEffect)
- 移除 action-inbox 未使用 import

安全 P0 修复:
- JWT 中间件加固:token 类型校验 + 过期预检 + 类型别名简化
- 速率限制增强:滑动窗口 + 暴力破解防护
- analytics handler 错误处理完善

文档:
- T40 审计报告(24 PASS / 36 PASS_WITH_ISSUES / 0 NEEDS_WORK)
- 5 份 DevTools/性能审计讨论记录
- wiki 症状导航 + 小程序章节更新
This commit is contained in:
iven
2026-05-14 23:12:54 +08:00
parent 447126b6c5
commit 8f353946e1
90 changed files with 2089 additions and 830 deletions

View File

@@ -435,6 +435,56 @@ secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>"
- 微信开发者工具中 `getPhoneNumber` 需要真机调试或使用测试号
- Redis 不可达时限流降级为 fail-open不影响登录
### 2026-05-14 全页面性能与稳定性审查5 专家组 × 58 页面)
> 组织 5 个并行专家组,逐一审查全部 58 个页面 + stores/hooks/utils/components/services 基础设施层。
> 审查重点开发者工具卡死、CPU 飙升、逻辑链路异常、内存泄漏。
#### 发现汇总
| 级别 | 数量 | 说明 |
|------|------|------|
| CRITICAL | 3 | 长轮询紧密递归、BLE 模块单例、TrendChart 同步 API |
| HIGH | 8 | 原生 HTML input、客户端过滤、Storage 渲染路径、messagesRef 不同步等 |
| MEDIUM | 15+ | 缺少 useThrottledDidShow、IIFE 渲染、loading 竞态等 |
#### CRITICAL 级别(已修复)
| # | 问题 | 文件 | 修复 |
|---|------|------|------|
| 1 | **长轮询 delay=0 紧密递归**成功轮询后无间隔立即递归后端快速响应时构成紧密循环CPU 飙升 | `consultation/detail`(患者+医生端) | 成功路径加 3s 间隔 + 连续失败上限 50 次 |
| 2 | **BLE 模块级单例**`BLEManager` 在模块顶层实例化,生命周期不与页面绑定;`liveReadings` 无上限增长 | `device-sync/index.tsx` | 改为 `useRef` 懒初始化 + `MAX_LIVE_READINGS=200` 上限 |
| 3 | **TrendChart 模块级同步 API**`Taro.getSystemInfoSync()` 在模块加载时执行,阻塞首帧 | `components/TrendChart` | 改为延迟求值 `getDPR()` 函数 |
#### HIGH 级别(已修复)
| # | 问题 | 文件 | 修复 |
|---|------|------|------|
| 1 | **原生 HTML `<input type='date'>`**Taro 不支持原生 HTML 标签,日期选择器完全不可用 | `doctor/followup/detail` | 替换为 `<Picker mode='date'>` |
| 2 | **告警详情用列表+客户端过滤**:加载 100 条列表找单条告警,超过 100 条永远找不到 | `doctor/alerts/detail` | 新增 `getAlert(id)` API 函数,单条查询 |
| 3 | **透析列表客户端过滤**:服务端返回全状态数据,前端用 `activeTab` 过滤导致分页不准确 | `doctor/dialysis/index` | 传 `status` 参数给服务端,移除客户端过滤 |
| 4 | **`getStorageSync` 在渲染路径**:组件顶层调用同步 Storage 读取,每次渲染都执行 IPC | `report/detail` | 改用 `useAuthStore((s) => s.currentPatient)` |
| 5 | **`handleSend``messagesRef` 未同步**:发送消息后 state 更新但 ref 未更新,长轮询可能重复拉取 | `consultation/detail`(患者+医生端) | 在 `setMessages` 回调中同步更新 ref |
| 6 | **医护工作台无自动刷新**:返回工作台时数据不更新 | `doctor/index` | 添加 `useThrottledDidShow` 10s 节流刷新 |
#### MEDIUM 级别(已知,按需修复)
| 问题 | 文件 | 说明 |
|------|------|------|
| `ai-report/list` 缺少 `useThrottledDidShow` | `ai-report/list` | 只在挂载时加载,从详情页返回不刷新 |
| `events/list` 缺少分页 | `events/index` | 固定加载 50 条,无滚动加载更多 |
| `device-sync` tryAutoSync 无并发保护 | `device-sync` | 快速进出页面可能重复上传 |
| `health/index` loadTrend 无并发保护 | `health/index` | 快速切 Tab 时可能并行请求+闪烁 |
| `doctor/prescription` handleSearch loading 竞态 | `doctor/prescription` | handleSearch 和 useEffect 的 loadData 可能闪烁 |
#### 架构建议
1. **统一数据加载模式**:所有列表页应使用 `useThrottledDidShow` + `loadingRef` 双重保护(当前 appointment/messages 遵循,但 ai-report/events 不遵循)
2. **长轮询通用化**`consultation/detail``doctor/consultation/detail` 的长轮询逻辑几乎相同,应抽取为 `useLongPolling` hook
3. **服务端过滤优先**:所有列表页的 Tab 过滤应传参给后端,不在前端做客户端过滤
4. **BLE 管理器生命周期**BLE 等硬件相关管理器应通过 Context 或 hook 管理,避免模块级单例
5. **getStorageSync 出渲染路径**:组件顶层不应有同步 I/O统一通过 Zustand store 获取
## 6. MCP 联调(微信开发者工具自动化)
> 通过 MCP (Model Context Protocol) 工具直接操控微信开发者工具中的小程序模拟器,实现页面导航、元素交互、数据读取等操作。
@@ -763,6 +813,7 @@ node scripts/audit-pages.mjs --role doctor --batch-size 8
| 日期 | 变更 |
|------|------|
| 2026-05-14 | **全页面性能与稳定性审查5 专家组)**:审查 58 页面 + 基础设施层;修复 CRITICAL×3长轮询紧密递归、BLE 模块单例、TrendChart 同步 API+ HIGH×6原生 HTML input、客户端过滤→服务端、Storage 渲染路径、messagesRef 同步、工作台刷新、告警单条查询);新增 §5 审查发现章节 + 架构建议 |
| 2026-05-13 | **T40 UI 设计系统合规审计+修复**60 页面全覆盖审计PASS 31 / PASS_WITH_ISSUES 27 / NEEDS_WORK 2修复 HIGH×2 + MEDIUM×6 + LOW×67新增 `$white` 变量 + `--tk-font-display` Token44 处 `#fff` 统一为 `$white`14 处圆角硬编码统一为变量3 处 TSX inline 颜色提取为 SCSS 类ErrorBoundary 重构为 SCSS2 处静默 catch 修复2 处离调色板颜色修正 |
| 2026-05-10 | **访客首页改造**:轮播图接入 `/public/banners` API + `wx.downloadFile` 下载图片到本地临时路径;文章列表接入 `/public/articles` API文章详情页根据登录状态选择认证/公开 API`getPublicArticleDetail``.env` 新增 `TARO_APP_DEFAULT_TENANT_ID`;集成契约新增 4 个公开端点 |
| 2026-05-09 | **Design Token 全面接入**68 SCSS 文件全面迁移 `font-size: Npx` → `var(--tk-*)`634 token 引用 / 3 个特殊硬编码;新增 §1.1 Design Token 系统文档 |