2025-07-04 17:16:38 +08:00
|
|
|
import { AxiosError } from 'axios'
|
|
|
|
|
import { ElMessage } from 'element-plus'
|
|
|
|
|
import { ApiStatus } from './status'
|
|
|
|
|
import { $t } from '@/locales'
|
|
|
|
|
|
|
|
|
|
// 错误响应接口
|
|
|
|
|
export interface ErrorResponse {
|
|
|
|
|
code: number
|
2025-07-11 18:37:16 +08:00
|
|
|
message: string
|
2025-07-04 17:16:38 +08:00
|
|
|
data?: unknown
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 错误日志数据接口
|
|
|
|
|
export interface ErrorLogData {
|
|
|
|
|
code: number
|
|
|
|
|
message: string
|
|
|
|
|
data?: unknown
|
|
|
|
|
timestamp: string
|
|
|
|
|
url?: string
|
|
|
|
|
method?: string
|
|
|
|
|
stack?: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 自定义 HttpError 类
|
|
|
|
|
export class HttpError extends Error {
|
|
|
|
|
public readonly code: number
|
|
|
|
|
public readonly data?: unknown
|
|
|
|
|
public readonly timestamp: string
|
|
|
|
|
public readonly url?: string
|
|
|
|
|
public readonly method?: string
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
message: string,
|
|
|
|
|
code: number,
|
|
|
|
|
options?: {
|
|
|
|
|
data?: unknown
|
|
|
|
|
url?: string
|
|
|
|
|
method?: string
|
|
|
|
|
}
|
|
|
|
|
) {
|
|
|
|
|
super(message)
|
|
|
|
|
this.name = 'HttpError'
|
|
|
|
|
this.code = code
|
|
|
|
|
this.data = options?.data
|
|
|
|
|
this.timestamp = new Date().toISOString()
|
|
|
|
|
this.url = options?.url
|
|
|
|
|
this.method = options?.method
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public toLogData(): ErrorLogData {
|
|
|
|
|
return {
|
|
|
|
|
code: this.code,
|
|
|
|
|
message: this.message,
|
|
|
|
|
data: this.data,
|
|
|
|
|
timestamp: this.timestamp,
|
|
|
|
|
url: this.url,
|
|
|
|
|
method: this.method,
|
|
|
|
|
stack: this.stack
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取错误消息
|
|
|
|
|
* @param status 错误状态码
|
|
|
|
|
* @returns 错误消息
|
|
|
|
|
*/
|
|
|
|
|
const getErrorMessage = (status: number): string => {
|
|
|
|
|
const errorMap: Record<number, string> = {
|
|
|
|
|
[ApiStatus.unauthorized]: 'httpMsg.unauthorized',
|
|
|
|
|
[ApiStatus.forbidden]: 'httpMsg.forbidden',
|
|
|
|
|
[ApiStatus.notFound]: 'httpMsg.notFound',
|
|
|
|
|
[ApiStatus.methodNotAllowed]: 'httpMsg.methodNotAllowed',
|
|
|
|
|
[ApiStatus.requestTimeout]: 'httpMsg.requestTimeout',
|
|
|
|
|
[ApiStatus.internalServerError]: 'httpMsg.internalServerError',
|
|
|
|
|
[ApiStatus.badGateway]: 'httpMsg.badGateway',
|
|
|
|
|
[ApiStatus.serviceUnavailable]: 'httpMsg.serviceUnavailable',
|
|
|
|
|
[ApiStatus.gatewayTimeout]: 'httpMsg.gatewayTimeout'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $t(errorMap[status] || 'httpMsg.internalServerError')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 处理错误
|
|
|
|
|
* @param error 错误对象
|
|
|
|
|
* @returns 错误对象
|
|
|
|
|
*/
|
|
|
|
|
export function handleError(error: AxiosError<ErrorResponse>): never {
|
|
|
|
|
// 处理取消的请求
|
|
|
|
|
if (error.code === 'ERR_CANCELED') {
|
|
|
|
|
console.warn('Request cancelled:', error.message)
|
|
|
|
|
throw new HttpError($t('httpMsg.requestCancelled'), ApiStatus.error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const statusCode = error.response?.status
|
2025-07-11 18:37:16 +08:00
|
|
|
const errorMessage = error.response?.data?.message || error.message
|
2025-07-04 17:16:38 +08:00
|
|
|
const requestConfig = error.config
|
|
|
|
|
|
|
|
|
|
// 处理网络错误
|
|
|
|
|
if (!error.response) {
|
|
|
|
|
throw new HttpError($t('httpMsg.networkError'), ApiStatus.error, {
|
|
|
|
|
url: requestConfig?.url,
|
|
|
|
|
method: requestConfig?.method?.toUpperCase()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理 HTTP 状态码错误
|
2025-07-11 18:37:16 +08:00
|
|
|
// const message = statusCode
|
|
|
|
|
// ? getErrorMessage(statusCode)
|
|
|
|
|
// : errorMessage || $t('httpMsg.requestFailed')
|
|
|
|
|
const message = errorMessage
|
2025-07-04 17:16:38 +08:00
|
|
|
throw new HttpError(message, statusCode || ApiStatus.error, {
|
|
|
|
|
data: error.response.data,
|
|
|
|
|
url: requestConfig?.url,
|
|
|
|
|
method: requestConfig?.method?.toUpperCase()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 显示错误消息
|
|
|
|
|
* @param error 错误对象
|
|
|
|
|
* @param showMessage 是否显示错误消息
|
|
|
|
|
*/
|
|
|
|
|
export function showError(error: HttpError, showMessage: boolean = true): void {
|
|
|
|
|
if (showMessage) {
|
|
|
|
|
ElMessage.error(error.message)
|
|
|
|
|
}
|
|
|
|
|
// 记录错误日志
|
2025-07-11 18:37:16 +08:00
|
|
|
// console.error('[HTTP Error]', error.toLogData())
|
2025-07-04 17:16:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 判断是否为 HttpError 类型
|
|
|
|
|
* @param error 错误对象
|
|
|
|
|
* @returns 是否为 HttpError 类型
|
|
|
|
|
*/
|
|
|
|
|
export const isHttpError = (error: unknown): error is HttpError => {
|
|
|
|
|
return error instanceof HttpError
|
|
|
|
|
}
|