5.7 KiB
【前端 BUG·管理后台】任务看板打开弹「获取看板详情/任务/成员失败」红 toast = 前端竞态重复发请求 + 取消被误报错
关联: 无工单(前端 bug 走 changelog 通知 mmg) 服务: 前端 hl-ui(后端零改动)| 提报时间: 2026-06-05
存放目录:
changelogs-v2/2026-06/影响范围: 管理后台「任务看板」/task/boards(看板列表 / 看板详情视图) 类型: 前端竞态 bug(mmg 修),后端实测全 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:
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,但数据正常。