hl-api-changelog/changelogs-v2/2026-06/05_任务看板打开弹获取看板X失败toast-前端竞态重复请求被取消-管理后台.md

101 行
5.7 KiB
Markdown

此文件含有模棱两可的 Unicode 字符

此文件含有可能会与其他字符混淆的 Unicode 字符。 如果您是想特意这样的,可以安全地忽略该警告。 使用 Escape 按钮显示他们。

# 【前端 BUG·管理后台】任务看板打开弹「获取看板详情/任务/成员失败」红 toast = 前端竞态重复发请求 + 取消被误报错
> **关联**: 无工单(前端 bug 走 changelog 通知 mmg
> **服务**: 前端 hl-ui**后端零改动**| **提报时间**: 2026-06-05
>
> **存放目录**: `changelogs-v2/2026-06/`
> **影响范围**: 管理后台「任务看板」`/task/boards`(看板列表 / 看板详情视图)
> **类型**: 前端竞态 bugmmg 修),**后端实测全 200 健康无需改动**
---
## ⚠️ 关键说明
打开任务看板时弹出红色错误 toast「**获取看板详情失败 / 获取看板任务失败 / 获取看板成员失败**」,**但看板数据其实正常加载出来了**(公司看板、各状态列、任务卡片都在)。
**根因 = 前端竞态,不是后端故障。** 看板组件挂载时对**同一个数据接口重复发请求**`tasks` 发 3 次、`members` 发 2 次、`detail` 视时序也会重发),多余的在飞请求被前端 `AbortController` **主动取消**(浏览器 `net::ERR_ABORTED`)。前端 `.catch()` **没有把「请求被取消」和「请求真失败」区分开**,于是把取消也当成失败弹了红 toast。
后端侧:凡是被允许跑完的请求**全部返回 200**(看板列表/详情/任务/成员/状态 + token refresh,curl 80+ 次 + 真浏览器多轮复现,零服务端失败)。
---
## 1. 实证(真浏览器 DevTools Network,刚登录 token 全新,硬刷新复现)
打开 `/task/boards`(公司看板 `2022226454376366081`)后的请求序列:
| # | 请求 | 结果 |
|---|---|---|
| 1 | `GET /admin/task/boards`(看板列表) | **200** ✅ |
| 2 | `GET /admin/task/board/{id}`(看板详情,含 statuses | **200** ✅ |
| 3 | `GET /admin/task/board/{id}/tasks` | **FAILED `net::ERR_ABORTED`** ❌ |
| 4 | `GET /admin/task/board/{id}/tasks` | **200** ✅(同一接口重发,成功)|
| 5 | `GET /admin/task/board/{id}/members` | **200** ✅ |
| 6 | `GET /admin/task/board/{id}/tasks` | **FAILED `net::ERR_ABORTED`** ❌ |
| 7 | `GET /admin/task/board/{id}/members` | **200** ✅(同一接口重发)|
要点:
- `tasks` 接口被触发 **3 次**2 次被取消、1 次成功);`members` 触发 **2 次**`detail` 偶发重发被取消时就弹「获取看板详情失败」。
- `net::ERR_ABORTED` 是**客户端主动取消**(不是 4xx/5xx,服务端从没收到失败结果,典型来自 axios cancel / `AbortController.abort()` / 组件重渲染或重复触发同一 fetch。
- 浏览器 console **0 个 error**(说明取消已被 catch 捕获),但 catch 里仍调用了错误 toast。
后端实测对照(均 200,有数据
```
GET /admin/task/boards -> 200, 看板列表
GET /admin/task/board/2022226454376366081 -> 200, 详情含 4 个 statuses
GET /admin/task/board/2022226454376366081/tasks -> 200, 任务分组
GET /admin/task/board/2022226454376366081/members -> 200, []
GET /admin/task/board/2022226454376366081/statuses-> 200, 4 条
POST /admin/auth/refresh (真 refreshToken) -> 200连打 30 次全 200
```
---
## 2. 前端修法mmg
### 2.1 必修catch 里放过「被取消」的请求,别弹错误 toast
错误处理里先判断是否为取消,是则**静默 return**,不弹 toast
```js
import axios from 'axios'
// ...
.catch((e) => {
// 请求被取消(组件重渲染/重复触发抢占)是预期行为,不是错误
if (axios.isCancel(e) || e?.code === 'ERR_CANCELED' || e?.name === 'CanceledError') return
ElMessage.error('获取看板任务失败') // 只有真失败才提示
})
```
三处都要加:获取看板详情 / 获取看板任务 / 获取看板成员的 catch。
### 2.2 根治:去掉对同一接口的重复触发
`tasks` 发 3 次、`members` 发 2 次,说明同一 fetch 被多个来源同时触发(常见:`onMounted` 触发一次 + `watch(boardId)` 触发一次 + 默认「优先级/状态」筛选 radio 又触发一次)。建议:
- 数据加载**只保留单一触发源**(推荐由 `watch(boardId, { immediate: true })` 统一触发,去掉 `onMounted` 里的重复拉取);
- 或对每类请求维护一个 `AbortController`,重发前先 `abort()` 旧的——**但这种「主动取消」必须配合 §2.1,把取消视为正常、不报错**。
### 2.3 注意(与本 bug 无关、但同页常见)
看板有实时通道(`/ws/task/info` + `/admin/task/board/ws-doc`)。若 WS 推送后又触发整页重新拉取,也会放大上面的「重复请求 + 取消」现象,一并按 §2.1/§2.2 处理。
---
## 3. 后端状态(无需改动)
- 任务看板全部读接口list / detail / tasks / members / statuses实测 200,数据正确。
- `POST /admin/auth/refresh`:网关已在 `JwtAuthFilter.SKIP_URLS` 白名单放行,带过期 access token + 真 refreshToken 实测 200,20 并发全 200。
### 3.1 附注:你截图里 `refresh` 的 502 是「另一桩、瞬时」
你之前截图 Network 里 `refresh` 显示 **502**,那是**独立的瞬时事件**:当时会话 access token 已过期 → 前端自动调 `/admin/auth/refresh` 续签 → 恰好撞上测试服 user-service双实例 8081/8181部署/重启抖动,网关 LB 轮到正在重启的实例返 502。**当前 30+ 次 refresh 全 200,复现不出,不是看板代码缺陷**,无需前端改动;若再遇到,刷新页面即恢复。
---
## 4. 关联
- 后端负责人:@wx(已实测确认后端健康,无改动)
- 前端对接(本 bug 修复):**mmg**
- 复现路径:登录管理后台 → 任务看板(`/task/boards`)→ 打开「公司看板」即弹失败 toast,但数据正常。