207 lines
5.4 KiB
TypeScript
207 lines
5.4 KiB
TypeScript
/**
|
||
* 统一的错误处理工具
|
||
* 用于解析和显示各种类型的错误
|
||
*/
|
||
|
||
import { DownloadErrorCode, parseDownloadError, DownloadErrorMessages } from './download-errors';
|
||
|
||
/**
|
||
* API错误响应接口
|
||
*/
|
||
export interface ApiErrorResponse {
|
||
error?: string;
|
||
code?: string;
|
||
details?: string;
|
||
message?: string;
|
||
}
|
||
|
||
/**
|
||
* 通用错误类型
|
||
*/
|
||
export enum ErrorType {
|
||
// 下载错误
|
||
DOWNLOAD = 'DOWNLOAD',
|
||
// 上传错误
|
||
UPLOAD = 'UPLOAD',
|
||
// 网络错误
|
||
NETWORK = 'NETWORK',
|
||
// 权限错误
|
||
PERMISSION = 'PERMISSION',
|
||
// 验证错误
|
||
VALIDATION = 'VALIDATION',
|
||
// 服务器错误
|
||
SERVER = 'SERVER',
|
||
// 未知错误
|
||
UNKNOWN = 'UNKNOWN',
|
||
}
|
||
|
||
/**
|
||
* 解析API错误响应
|
||
*/
|
||
export function parseApiError(error: any): {
|
||
type: ErrorType;
|
||
message: string;
|
||
code?: string;
|
||
details?: string;
|
||
} {
|
||
// 如果已经是解析后的格式
|
||
if (error?.type && error?.message) {
|
||
return error;
|
||
}
|
||
|
||
// 如果是下载错误(有DownloadErrorCode)
|
||
if (error?.code && Object.values(DownloadErrorCode).includes(error.code as DownloadErrorCode)) {
|
||
const parsed = parseDownloadError(error);
|
||
return {
|
||
type: ErrorType.DOWNLOAD,
|
||
message: parsed.message,
|
||
code: parsed.code,
|
||
details: parsed.details,
|
||
};
|
||
}
|
||
|
||
// 如果是API错误响应格式
|
||
if (error?.error || error?.message) {
|
||
const errorMessage = error.error || error.message || String(error);
|
||
const errorCode = error.code;
|
||
|
||
// 根据错误代码或消息推断错误类型
|
||
if (errorCode === 'UNAUTHORIZED' || errorMessage.includes('未登录') || errorMessage.includes('请先登录')) {
|
||
return {
|
||
type: ErrorType.PERMISSION,
|
||
message: '请先登录',
|
||
code: errorCode,
|
||
details: error.details,
|
||
};
|
||
}
|
||
|
||
if (errorCode === 'FORBIDDEN' || errorCode === 'NO_PERMISSION' || errorMessage.includes('权限') || errorMessage.includes('无权限')) {
|
||
return {
|
||
type: ErrorType.PERMISSION,
|
||
message: errorMessage.includes('下载') ? '无权限下载该素材' : '无权限执行此操作',
|
||
code: errorCode,
|
||
details: error.details,
|
||
};
|
||
}
|
||
|
||
if (errorMessage.includes('上传') || errorMessage.includes('upload')) {
|
||
return {
|
||
type: ErrorType.UPLOAD,
|
||
message: errorMessage,
|
||
code: errorCode,
|
||
details: error.details,
|
||
};
|
||
}
|
||
|
||
if (errorMessage.includes('网络') || errorMessage.includes('network') || errorMessage.includes('连接')) {
|
||
return {
|
||
type: ErrorType.NETWORK,
|
||
message: '网络连接失败,请检查网络后重试',
|
||
code: errorCode,
|
||
details: error.details,
|
||
};
|
||
}
|
||
|
||
if (errorMessage.includes('验证') || errorMessage.includes('validation') || errorMessage.includes('格式')) {
|
||
return {
|
||
type: ErrorType.VALIDATION,
|
||
message: errorMessage,
|
||
code: errorCode,
|
||
details: error.details,
|
||
};
|
||
}
|
||
|
||
// 默认返回原始错误消息
|
||
return {
|
||
type: ErrorType.UNKNOWN,
|
||
message: errorMessage,
|
||
code: errorCode,
|
||
details: error.details,
|
||
};
|
||
}
|
||
|
||
// 如果是Error对象
|
||
if (error instanceof Error) {
|
||
const message = error.message || '操作失败,请稍后重试';
|
||
|
||
// 根据错误消息推断类型
|
||
if (message.includes('权限') || message.includes('无权限')) {
|
||
return { type: ErrorType.PERMISSION, message };
|
||
}
|
||
if (message.includes('网络') || message.includes('network') || message.includes('连接')) {
|
||
return { type: ErrorType.NETWORK, message: '网络连接失败,请检查网络后重试' };
|
||
}
|
||
if (message.includes('上传') || message.includes('upload')) {
|
||
return { type: ErrorType.UPLOAD, message };
|
||
}
|
||
|
||
return { type: ErrorType.UNKNOWN, message };
|
||
}
|
||
|
||
// 如果是字符串
|
||
if (typeof error === 'string') {
|
||
return {
|
||
type: ErrorType.UNKNOWN,
|
||
message: error || '操作失败,请稍后重试',
|
||
};
|
||
}
|
||
|
||
// 默认未知错误
|
||
return {
|
||
type: ErrorType.UNKNOWN,
|
||
message: '操作失败,请稍后重试',
|
||
};
|
||
}
|
||
|
||
/**
|
||
* 从fetch响应中解析错误
|
||
*/
|
||
export async function parseFetchError(response: Response): Promise<{
|
||
type: ErrorType;
|
||
message: string;
|
||
code?: string;
|
||
details?: string;
|
||
}> {
|
||
try {
|
||
const errorData = await response.json();
|
||
return parseApiError(errorData);
|
||
} catch {
|
||
// 如果无法解析JSON,根据状态码返回错误
|
||
if (response.status === 401) {
|
||
return { type: ErrorType.PERMISSION, message: '请先登录' };
|
||
}
|
||
if (response.status === 403) {
|
||
return { type: ErrorType.PERMISSION, message: '无权限执行此操作' };
|
||
}
|
||
if (response.status === 404) {
|
||
return { type: ErrorType.UNKNOWN, message: '资源不存在' };
|
||
}
|
||
if (response.status >= 500) {
|
||
return { type: ErrorType.SERVER, message: '服务器错误,请稍后重试' };
|
||
}
|
||
return { type: ErrorType.UNKNOWN, message: `请求失败 (${response.status})` };
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 处理错误并显示提示(需要传入showToast函数)
|
||
*/
|
||
export function handleError(
|
||
error: any,
|
||
showToast?: (message: string, options?: { variant?: 'success' | 'error' }) => void
|
||
): {
|
||
type: ErrorType;
|
||
message: string;
|
||
code?: string;
|
||
details?: string;
|
||
} {
|
||
const parsed = parseApiError(error);
|
||
|
||
// 如果提供了showToast,自动显示错误
|
||
if (showToast) {
|
||
showToast(parsed.message, { variant: 'error' });
|
||
}
|
||
|
||
return parsed;
|
||
}
|