fix: 取票失败静默处理,不再弹出连接相关报错
This commit is contained in:
parent
d580efebe5
commit
f6a4fc6cb6
|
|
@ -19,7 +19,8 @@ export const authApi = {
|
|||
})
|
||||
},
|
||||
// 换取 WebSocket 短期一次性票据:属于组织级请求,拦截器会自动带上当前公司头。
|
||||
// 标记 silent:取票失败属于后台辅助失败,不应弹与实时连接相关的全局错误提示。
|
||||
createWebSocketTicket() {
|
||||
return request.post('/auth/websocket-ticket')
|
||||
return request.post('/auth/websocket-ticket', undefined, { silent: true })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,13 @@ function isOrganizationExempt(url) {
|
|||
return ORGANIZATION_EXEMPT_PATHS.some(exempt => path === exempt || path.startsWith(`${exempt}/`))
|
||||
}
|
||||
|
||||
// 单次请求可通过 config.silent 关闭全局错误提示。取票等后台辅助请求失败时,
|
||||
// 不应让用户看到与自己操作无关的报错;默认不静默(未显式置 true 即照旧提示)。
|
||||
// 注意:silent 只抑制“业务/连接类”提示,不影响 401 的登录态全局处理。
|
||||
function isSilent(config) {
|
||||
return config?.silent === true
|
||||
}
|
||||
|
||||
// Request interceptor - add auth token
|
||||
request.interceptors.request.use(
|
||||
(config) => {
|
||||
|
|
@ -75,7 +82,9 @@ request.interceptors.response.use(
|
|||
if (res.code === 0) {
|
||||
return res
|
||||
}
|
||||
if (!isSilent(response.config)) {
|
||||
ElMessage.error(hasChinese(res.message) ? res.message : '请求失败')
|
||||
}
|
||||
return Promise.reject(new Error(res.message))
|
||||
},
|
||||
(error) => {
|
||||
|
|
@ -88,7 +97,10 @@ request.interceptors.response.use(
|
|||
if (context && !isCurrentOrganizationContext(context)) {
|
||||
return Promise.reject(new OrganizationContextSupersededError())
|
||||
}
|
||||
const silent = isSilent(error.config)
|
||||
if (error.response?.status === 401) {
|
||||
// 登录态失效的全局处理保持原样:清令牌并跳登录页。这与请求是否为 silent 无关,
|
||||
// silent 只抑制“业务/连接类”错误提示。
|
||||
localStorage.removeItem('wmms_token')
|
||||
localStorage.removeItem('wmms_user')
|
||||
// 登录页自己会把失败原因写在表单里,再弹一句"登录已过期"只会让人困惑
|
||||
|
|
@ -112,10 +124,14 @@ request.interceptors.response.use(
|
|||
msg = fields.map(f => labels[f] || f).join('、') + '不能为空'
|
||||
}
|
||||
}
|
||||
if (!silent) {
|
||||
ElMessage.error(hasChinese(msg) ? msg : '请检查必填字段')
|
||||
}
|
||||
} else {
|
||||
if (!silent) {
|
||||
ElMessage.error(responseErrorText(error))
|
||||
}
|
||||
}
|
||||
return Promise.reject(error)
|
||||
}
|
||||
)
|
||||
|
|
|
|||
80
frontend/src/api/index.test.js
Normal file
80
frontend/src/api/index.test.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import request from './index'
|
||||
import { commitOrganizationId, resetOrganizationContext } from '../utils/organizationContext'
|
||||
|
||||
// 断言 WebSocket 取票这条后台辅助请求失败时静默:不弹全局错误提示,
|
||||
// 同时不破坏拦截器的组织上下文逻辑(仍注入 X-Organization-ID)。
|
||||
describe('请求拦截器的 silent 静默开关', () => {
|
||||
const originalAdapter = request.defaults.adapter
|
||||
|
||||
beforeEach(() => {
|
||||
resetOrganizationContext()
|
||||
commitOrganizationId(2)
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
request.defaults.adapter = originalAdapter
|
||||
vi.restoreAllMocks()
|
||||
})
|
||||
|
||||
// 适配器直接 reject,模拟 HTTP 层失败,走响应拦截器的错误分支。
|
||||
function rejectingAdapter(status, data) {
|
||||
return (config) => {
|
||||
const error = new Error(`Request failed with status code ${status}`)
|
||||
error.config = config
|
||||
error.response = { status, statusText: '', headers: {}, data }
|
||||
error.isAxiosError = true
|
||||
return Promise.reject(error)
|
||||
}
|
||||
}
|
||||
|
||||
// 适配器解析出响应体,模拟业务层失败(res.code !== 0)。
|
||||
function resolvingAdapter(data, status = 200) {
|
||||
return async (config) => ({ data, status, statusText: 'OK', headers: {}, config })
|
||||
}
|
||||
|
||||
test('取票 403 失败静默:不弹全局错误提示', async () => {
|
||||
const errorSpy = vi.spyOn(ElMessage, 'error').mockImplementation(() => {})
|
||||
request.defaults.adapter = rejectingAdapter(403, { message: '当前公司不可访问' })
|
||||
|
||||
await expect(
|
||||
request.post('/auth/websocket-ticket', undefined, { silent: true })
|
||||
).rejects.toBeDefined()
|
||||
|
||||
expect(errorSpy).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('取票业务失败(res.code !== 0)同样静默', async () => {
|
||||
const errorSpy = vi.spyOn(ElMessage, 'error').mockImplementation(() => {})
|
||||
request.defaults.adapter = resolvingAdapter({ code: 403, message: '当前公司不可访问' })
|
||||
|
||||
await expect(
|
||||
request.post('/auth/websocket-ticket', undefined, { silent: true })
|
||||
).rejects.toThrow()
|
||||
|
||||
expect(errorSpy).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('未标记 silent 的同类失败仍会弹全局错误提示(未被全局静默)', async () => {
|
||||
const errorSpy = vi.spyOn(ElMessage, 'error').mockImplementation(() => {})
|
||||
request.defaults.adapter = rejectingAdapter(500, { message: '服务器错误' })
|
||||
|
||||
await expect(request.post('/materials')).rejects.toBeDefined()
|
||||
|
||||
expect(errorSpy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
test('取票请求仍按组织级请求注入 X-Organization-ID(白名单语义未变)', async () => {
|
||||
let captured
|
||||
request.defaults.adapter = async (config) => {
|
||||
captured = config
|
||||
return { data: { code: 0, data: { ticket: 't' } }, status: 200, statusText: 'OK', headers: {}, config }
|
||||
}
|
||||
|
||||
await request.post('/auth/websocket-ticket', undefined, { silent: true })
|
||||
|
||||
expect(captured.headers['X-Organization-ID']).toBe('2')
|
||||
expect(captured.silent).toBe(true)
|
||||
})
|
||||
})
|
||||
Loading…
Reference in New Issue
Block a user