fix(mp): 患者端卡死深度审查修复 — CRITICAL 回归 + 并发保护 + 页栈溢出防护
CRITICAL: - 咨询详情页 loadData 引用已删除的 pollingRef → 移除残余引用 HIGH: - 401 重试递归改循环结构,避免并发限制器双 slot 占用 - 医生端 4 个列表页添加 loadingRef 防重入(consultation/alerts/dialysis/prescription) - 新增 safeNavigateTo 页栈溢出保护(栈≥9 自动 redirectTo) 前期修复一并提交: - 全局并发限制 MAX_CONCURRENT=8 - doRefresh 失败时完整清理 Storage + 重置缓存状态 - 401 跳转登录页修正 - 长轮询 generation counter 模式 - 首页/健康页 loadingRef + refreshToday 去重
This commit is contained in:
@@ -474,7 +474,7 @@ secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>"
|
||||
| `ai-report/list` 缺少 `useThrottledDidShow` | `ai-report/list` | 只在挂载时加载,从详情页返回不刷新 |
|
||||
| `events/list` 缺少分页 | `events/index` | 固定加载 50 条,无滚动加载更多 |
|
||||
| `device-sync` tryAutoSync 无并发保护 | `device-sync` | 快速进出页面可能重复上传 |
|
||||
| `health/index` loadTrend 无并发保护 | `health/index` | 快速切 Tab 时可能并行请求+闪烁 |
|
||||
| `health/index` loadTrend 无并发保护 | `health/index` | **已修复:** 添加 `loadingRef` 防重入 |
|
||||
| `doctor/prescription` handleSearch loading 竞态 | `doctor/prescription` | handleSearch 和 useEffect 的 loadData 可能闪烁 |
|
||||
|
||||
#### 架构建议
|
||||
@@ -485,6 +485,83 @@ secret = "<通过环境变量 ERP__WECHAT__SECRET 设置>"
|
||||
4. **BLE 管理器生命周期**:BLE 等硬件相关管理器应通过 Context 或 hook 管理,避免模块级单例
|
||||
5. **getStorageSync 出渲染路径**:组件顶层不应有同步 I/O,统一通过 Zustand store 获取
|
||||
|
||||
### 2026-05-15 患者端登录后卡死深度审查(3 专家组 × 请求链路 + 并发分析 + 端点可达性)
|
||||
|
||||
> 游客端卡死修复后,患者登录后又频繁出现开发者工具卡死。组织 3 个并行专家组深度审查。
|
||||
> **根因:全局并发请求超过微信 10 个限制**,快速 Tab 切换时首页 5 个 + 健康页 4 个 + analytics 1 个 + 个人中心 2 个 = 12 个并发请求,超限排队阻塞 UI。
|
||||
|
||||
#### 审查发现
|
||||
|
||||
| 级别 | 问题 | 说明 |
|
||||
|------|------|------|
|
||||
| **根因** | **请求并发超微信 10 限制** | 快速 Tab 切换产生 12+ 并发请求,超出排队;慢请求占位导致后续全部阻塞 |
|
||||
| HIGH | doRefresh 失败未设 isLoggingOut | Storage 清了但标志未设,401 路径可能重入 |
|
||||
| HIGH | 401 失败后 reLaunch 首页而非登录页 | 首页 restoreAuth 又发请求,可能循环 |
|
||||
| MEDIUM | 长轮询 useDidShow 重启时新旧循环重叠 | pollingRef 设 true 时旧 setTimeout 回调也会继续 |
|
||||
| MEDIUM | 首页 loadReminders 无并发保护 | useThrottledDidShow + pullDownRefresh 可同时触发 |
|
||||
| MEDIUM | health store refreshToday 无防重入 | 两个页面同时调用会发两次请求 |
|
||||
| MEDIUM | 健康页 loadTrend/loadAiSuggestions 无防重入 | 下拉刷新 + useThrottledDidShow 双倍请求 |
|
||||
|
||||
#### 端点可达性验证
|
||||
|
||||
**所有 33 个小程序 service 端点后端均已实现**,无 ghost endpoint(特别确认 `/analytics/batch`、`/messages/unread-count`、`/ai/suggestions` 均存在)。
|
||||
|
||||
#### Tab 切换请求链路分析
|
||||
|
||||
每次 Tab 切换触发两层回调:
|
||||
|
||||
| 层级 | 操作 | 开销 |
|
||||
|------|------|------|
|
||||
| App 级 `useDidShow` | `restoreAuth()` + `restoreUI()` | 4-6 次同步 Storage IPC,约 10-50ms |
|
||||
| 页面级 `useThrottledDidShow` | 页面数据加载 | 1-5 个异步 API 请求,timeout 15s |
|
||||
|
||||
**最坏情况(所有缓存过期):**
|
||||
|
||||
| 页面 | 并发请求数 |
|
||||
|------|-----------|
|
||||
| 首页 HomeDashboard | 5(refreshToday + 3 个 loadReminders + getUnreadCount) |
|
||||
| 健康页 | 4(refreshToday + getTrend + listPendingSuggestions + getHealthThresholds) |
|
||||
| 消息页 | 1(listConsultations 或 listNotifications) |
|
||||
| 个人中心 | 2(getAccount + getCheckinStatus) |
|
||||
| analytics flushEvents | 1(POST /analytics/batch,每 30s) |
|
||||
| **合计** | **13 个** — 超限 3 个 |
|
||||
|
||||
#### 修复清单(6 项,全部已修复)
|
||||
|
||||
| # | 级别 | 文件 | 修复 |
|
||||
|---|------|------|------|
|
||||
| 1 | **HIGH** | `services/request.ts` | `doRefresh` 失败后设 `isLoggingOut=true` + 清理所有 Storage(含 `current_patient`)+ 清请求缓存 + 重置 headers 缓存 |
|
||||
| 2 | **HIGH** | `services/request.ts` | 401 失败后跳 `/pages/login/index`(而非首页);重试成功后重置 `isLoggingOut` |
|
||||
| 3 | **HIGH** | `services/request.ts` | **新增全局并发限制 `MAX_CONCURRENT=8`**:acquireSlot/releaseSlot 队列机制,防止超过微信 10 个请求限制;用 try/finally 确保所有路径释放槽位 |
|
||||
| 4 | MEDIUM | `pages/consultation/detail` + `doctor/consultation/detail` | 长轮询改为 **generation counter**,`startLongPolling` 递增 generation,旧循环检测到 generation 变化自动退出,杜绝新旧循环重叠 |
|
||||
| 5 | MEDIUM | `pages/index/index.tsx` | 首页 `loadReminders` 添加 `remindersLoadingRef` 防重入 |
|
||||
| 6 | MEDIUM | `pages/health/index.tsx` + `stores/health.ts` | 健康页整体 `loadingRef` 防重入;health store `refreshToday` 添加全局 `refreshingToday` 去重 |
|
||||
|
||||
#### 并发限制器原理
|
||||
|
||||
```typescript
|
||||
// request.ts — acquireSlot/releaseSlot 队列
|
||||
const MAX_CONCURRENT = 8; // 留 2 个空位给系统请求
|
||||
let activeRequests = 0;
|
||||
const pendingQueue: Array<() => void> = [];
|
||||
|
||||
function acquireSlot(): Promise<void> {
|
||||
if (activeRequests < MAX_CONCURRENT) {
|
||||
activeRequests++;
|
||||
return Promise.resolve();
|
||||
}
|
||||
return new Promise<void>((resolve) => { pendingQueue.push(resolve); });
|
||||
}
|
||||
|
||||
function releaseSlot(): void {
|
||||
activeRequests--;
|
||||
const next = pendingQueue.shift();
|
||||
if (next) { activeRequests++; next(); }
|
||||
}
|
||||
```
|
||||
|
||||
所有页面请求共享同一队列,保证同时最多 8 个网络请求在飞。超出自动排队,先到先得。
|
||||
|
||||
## 6. MCP 联调(微信开发者工具自动化)
|
||||
|
||||
> 通过 MCP (Model Context Protocol) 工具直接操控微信开发者工具中的小程序模拟器,实现页面导航、元素交互、数据读取等操作。
|
||||
@@ -813,6 +890,8 @@ node scripts/audit-pages.mjs --role doctor --batch-size 8
|
||||
|
||||
| 日期 | 变更 |
|
||||
|------|------|
|
||||
| 2026-05-15 | **患者端登录后卡死深度审查(3 专家组)**:根因 — 全局并发请求超微信 10 限制排队阻塞;端点可达性验证 33/33 全部存在;Tab 切换请求链路分析(最坏 13 并发);修复 HIGH×3(doRefresh 状态清理 + 401 跳转登录页 + 全局并发限制 MAX_CONCURRENT=8)+ MEDIUM×3(长轮询 generation counter + 首页/健康页 loadingRef 防重入 + refreshToday 去重) |
|
||||
| 2026-05-15 | **全量审计修复(第二轮)**:修复 CRITICAL×1(pollingRef 未定义回归,咨询详情页 loadData 引用已删除的 pollingRef → 闭会话时 ReferenceError 崩溃);HIGH×3 — 401 重试递归占用双 slot 改为循环结构释放后重入、4 个医生端列表页(consultation/alerts/dialysis/prescription)添加 loadingRef 防重入、safeNavigateTo 页栈溢出保护(栈≥9 自动 redirectTo);新增 `safeNavigateTo` 工具函数(`utils/navigate.ts`) |
|
||||
| 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` Token;44 处 `#fff` 统一为 `$white`;14 处圆角硬编码统一为变量;3 处 TSX inline 颜色提取为 SCSS 类;ErrorBoundary 重构为 SCSS;2 处静默 catch 修复;2 处离调色板颜色修正 |
|
||||
| 2026-05-10 | **访客首页改造**:轮播图接入 `/public/banners` API + `wx.downloadFile` 下载图片到本地临时路径;文章列表接入 `/public/articles` API;文章详情页根据登录状态选择认证/公开 API(`getPublicArticleDetail`);`.env` 新增 `TARO_APP_DEFAULT_TENANT_ID`;集成契约新增 4 个公开端点 |
|
||||
|
||||
Reference in New Issue
Block a user