Files
000-platform/frontend/src/api/index.js
Admin afcf30b519
All checks were successful
continuous-integration/drone/push Build is passing
feat: 新增睿美云对接模块
- 扩展 ToolConfig 配置类型,新增 external_api 类型
- 实现接口注册表,包含 90+ 睿美云开放接口定义
- 实现 TPOS SHA256WithRSA 签名鉴权
- 实现睿美云 API 客户端,支持多租户配置
- 新增代理路由 /api/ruimeiyun/call/{api_name}
- 支持接口权限控制和健康检查
2026-01-30 17:27:58 +08:00

124 lines
3.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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