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 症状导航 + 小程序章节更新
95 lines
4.4 KiB
Markdown
95 lines
4.4 KiB
Markdown
# DevTools 卡死根因分析与修复
|
||
|
||
> 日期: 2026-05-14 | 参与者: AI 辅助分析
|
||
|
||
## 背景
|
||
|
||
微信小程序在 DevTools 中频繁卡死,表现为:
|
||
- 所有 API 请求失败(`ERR_SSL_PROTOCOL_ERROR`)
|
||
- Tab 切换后页面长时间无响应(30s+)
|
||
- 登录流程点击无反应
|
||
- DevTools 完全失去响应需强制关闭
|
||
|
||
## 根因分析
|
||
|
||
### 根因 1(CRITICAL):http→https 自动转换
|
||
|
||
**文件**: `apps/miniprogram/src/services/request.ts`
|
||
|
||
```typescript
|
||
// 旧代码:production 模式自动将 http 替换为 https
|
||
if (process.env.NODE_ENV === 'production' && url.startsWith('http://')) {
|
||
return url.replace('http://', 'https://');
|
||
}
|
||
```
|
||
|
||
**问题**: `taro build --type weapp` 默认是 production 构建。所有 API 请求从 `http://localhost:3000` 被强制改为 `https://localhost:3000`,而后端只支持 HTTP。每个请求都失败 → 错误处理循环 → React 重渲染 → 内存增长 → DevTools 卡死。
|
||
|
||
**修复**: 移除自动协议升级,让 `.env` 文件作为 URL 唯一来源。
|
||
|
||
### 根因 2(CRITICAL):getHeaders 中的同步 Token 刷新
|
||
|
||
**文件**: `apps/miniprogram/src/services/request.ts`
|
||
|
||
```typescript
|
||
// 旧代码:每个请求前 await tryRefreshToken()
|
||
async function getHeaders() {
|
||
if (expiresAt && Date.now() > expiresAt - 60_000) {
|
||
await tryRefreshToken(); // 15 秒超时
|
||
refreshHeadersCache();
|
||
}
|
||
}
|
||
```
|
||
|
||
**问题**: 健康页 `didShow` 触发 4 个并发 API 请求,每个都先 `await getHeaders()`。当 Token 接近过期时,所有请求同时卡在 `tryRefreshToken()` 上。如果 refresh 超时(15 秒),加上 401 重试再超时(15 秒),**总计 30 秒无响应**。
|
||
|
||
**修复**: 移除 `getHeaders()` 中的 Token 刷新预检查,仅依赖已有的 401 重试逻辑(`request` 函数中 status === 401 时触发 `tryRefreshToken()`)。
|
||
|
||
### 根因 3(HIGH):DevTools 中 getPhoneNumber 不工作
|
||
|
||
**文件**: `apps/miniprogram/src/pages/login/index.tsx`
|
||
|
||
**问题**: 微信登录流程需要两步:
|
||
1. `Taro.login()` → 获取 code → 后端返回 `bound: false`(mock openid 不在 DB)
|
||
2. `<Button openType='getPhoneNumber'>` → 微信手机号授权弹窗 → 绑定手机号
|
||
|
||
在 DevTools 中,`getPhoneNumber` 可能不弹出授权弹窗(或被限频 `invoke getPhoneNumber too frequently`),导致登录流程卡死在第二步。
|
||
|
||
**修复**: dev 模式新增"开发模式快速登录"按钮,直接调用 `bindPhone('dev_mock_encrypted', 'dev_mock_iv')` 绕过手机号授权。后端 dev_mode 下会自动生成 mock 手机号。
|
||
|
||
## 患者端页面性能审查
|
||
|
||
对全部患者端页面进行了六维度审查:
|
||
|
||
| 风险 | 严重程度 | 文件 | 描述 |
|
||
|------|----------|------|------|
|
||
| 咨询详情页长轮询 | CRITICAL | consultation/detail/ | 递归 setTimeout 轮询(已有保护:3s 间隔 + 50 次失败上限 + useDidHide 暂停) |
|
||
| 首页 5 个并发 API | MEDIUM | index/ | didShow 触发 refreshToday + loadReminders(3 子请求) + loadUnread |
|
||
| 健康页 4 个并发 API | MEDIUM | health/ | didShow 触发 refreshToday + loadTrend + loadAiSuggestions + getHealthThresholds |
|
||
| 兑换页 page_size=100 | MEDIUM | pkg-mall/exchange/ | 请求过大量数据 |
|
||
| 咨询消息渲染 200 条 | MEDIUM | consultation/detail/ | DOM 节点上限偏高 |
|
||
|
||
## 修复清单
|
||
|
||
| 修复 | 文件 | 状态 |
|
||
|------|------|------|
|
||
| 移除 http→https 自动转换 | `services/request.ts` | 已完成 |
|
||
| 移除 getHeaders 同步 Token 刷新 | `services/request.ts` | 已完成 |
|
||
| 新增 dev 快速登录按钮 | `pages/login/index.tsx` + `index.scss` | 已完成 |
|
||
| JWT 中间件 scope 缓存 60s | `erp-auth/middleware/jwt_auth.rs` | 已完成 |
|
||
| Redis 失败缓存 5s | `erp-server/middleware/rate_limit.rs` | 已完成 |
|
||
|
||
## 遗留问题
|
||
|
||
1. **banner-image 500**: `/public/banner-image/{id}` 返回 500(测试数据媒体文件不在磁盘上)
|
||
2. **首页首次加载慢**: 首次 Tab 切换可能需要几秒加载时间(5 个并发 API),但不卡死
|
||
3. **MCP 超时**: MCP 的 switchTab/reLaunch 操作可能导致 DevTools 暂时无响应(MCP 工具本身的限制)
|
||
|
||
## 结论
|
||
|
||
DevTools 卡死的根因是 `request.ts` 中的两个设计缺陷叠加:
|
||
1. 所有请求被强制改为 HTTPS(后端不支持)→ 全部失败
|
||
2. 并发请求全部阻塞在 Token 刷新预检查上 → 长时间无响应
|
||
|
||
两个修复都是"减法"——移除不必要的自动升级和预检查,让系统更简单、更可靠。
|