All checks were successful
continuous-integration/drone/push Build is passing
- 扩展 ToolConfig 配置类型,新增 external_api 类型
- 实现接口注册表,包含 90+ 睿美云开放接口定义
- 实现 TPOS SHA256WithRSA 签名鉴权
- 实现睿美云 API 客户端,支持多租户配置
- 新增代理路由 /api/ruimeiyun/call/{api_name}
- 支持接口权限控制和健康检查
124 lines
3.5 KiB
JavaScript
124 lines
3.5 KiB
JavaScript
import axios from 'axios'
|
||
import { ElMessage } from 'element-plus'
|
||
import router from '@/router'
|
||
|
||
const api = axios.create({
|
||
baseURL: '',
|
||
timeout: 30000
|
||
})
|
||
|
||
/**
|
||
* 解析 API 错误响应
|
||
*/
|
||
function parseApiError(error) {
|
||
const result = {
|
||
code: 'UNKNOWN_ERROR',
|
||
message: '发生了未知错误',
|
||
traceId: '',
|
||
status: 500
|
||
}
|
||
|
||
// 网络错误(后端未启动、网络断开等)
|
||
if (!error.response) {
|
||
if (error.code === 'ECONNABORTED') {
|
||
result.code = 'TIMEOUT_ERROR'
|
||
result.message = '请求超时,请稍后重试'
|
||
result.status = 0
|
||
} else if (error.message?.includes('Network Error')) {
|
||
result.code = 'SERVICE_UNAVAILABLE'
|
||
result.message = '服务暂时不可用,请稍后重试'
|
||
result.status = 503
|
||
} else {
|
||
result.code = 'NETWORK_ERROR'
|
||
result.message = '网络连接失败,请检查网络后重试'
|
||
result.status = 0
|
||
}
|
||
return result
|
||
}
|
||
|
||
const { status, data, headers } = error.response
|
||
result.status = status
|
||
result.traceId = headers['x-trace-id'] || headers['X-Trace-ID'] || ''
|
||
|
||
if (data && data.error) {
|
||
result.code = data.error.code || result.code
|
||
result.message = data.error.message || result.message
|
||
result.traceId = data.error.trace_id || result.traceId
|
||
} else if (data && data.detail) {
|
||
result.message = typeof data.detail === 'string' ? data.detail : JSON.stringify(data.detail)
|
||
}
|
||
|
||
return result
|
||
}
|
||
|
||
/**
|
||
* 跳转到错误页面(使用 sessionStorage + replace,不影响浏览器历史)
|
||
*/
|
||
function navigateToErrorPage(errorInfo) {
|
||
// 记录当前页面路径(用于返回)
|
||
sessionStorage.setItem('errorFromPath', router.currentRoute.value.fullPath)
|
||
|
||
// 保存错误信息到 sessionStorage(不会显示在 URL 中)
|
||
sessionStorage.setItem('errorInfo', JSON.stringify({
|
||
code: errorInfo.code,
|
||
message: errorInfo.message,
|
||
traceId: errorInfo.traceId,
|
||
status: errorInfo.status,
|
||
timestamp: Date.now()
|
||
}))
|
||
|
||
// 使用 replace 而不是 push,这样浏览器返回时不会停留在错误页
|
||
router.replace({ name: 'Error' })
|
||
}
|
||
|
||
// 请求拦截器
|
||
api.interceptors.request.use(
|
||
config => {
|
||
const token = localStorage.getItem('token')
|
||
if (token) {
|
||
config.headers.Authorization = `Bearer ${token}`
|
||
}
|
||
return config
|
||
},
|
||
error => Promise.reject(error)
|
||
)
|
||
|
||
// 响应拦截器(集成 TraceID 追踪)
|
||
api.interceptors.response.use(
|
||
response => response,
|
||
error => {
|
||
const errorInfo = parseApiError(error)
|
||
const traceLog = errorInfo.traceId ? ` (trace: ${errorInfo.traceId})` : ''
|
||
|
||
console.error(`[API Error] ${errorInfo.code}: ${errorInfo.message}${traceLog}`)
|
||
|
||
// 严重错误列表(跳转错误页)
|
||
const criticalErrors = [
|
||
'INTERNAL_ERROR',
|
||
'SERVICE_UNAVAILABLE',
|
||
'GATEWAY_ERROR',
|
||
'NETWORK_ERROR',
|
||
'TIMEOUT_ERROR'
|
||
]
|
||
|
||
if (error.response?.status === 401) {
|
||
localStorage.removeItem('token')
|
||
localStorage.removeItem('user')
|
||
router.push('/login')
|
||
ElMessage.error('登录已过期,请重新登录')
|
||
} else if (error.response?.status === 403) {
|
||
ElMessage.error('没有权限执行此操作')
|
||
} else if (criticalErrors.includes(errorInfo.code)) {
|
||
// 严重错误(包括网络错误、服务不可用)跳转到错误页面
|
||
navigateToErrorPage(errorInfo)
|
||
} else {
|
||
// 普通错误显示消息
|
||
ElMessage.error(errorInfo.message)
|
||
}
|
||
|
||
return Promise.reject(error)
|
||
}
|
||
)
|
||
|
||
export default api
|