/** * 错误处理工具 * * 提供统一的错误处理和用户友好的错误提示 */ export interface ErrorDetail { code?: string; message: string; title?: string; solution?: string; actions?: Array<{ label: string; onClick: () => void; }>; } /** * DashScope API 错误映射 */ const DASHSCOPE_ERROR_MAP: Record = { 'App.AccessDenied': { title: 'API Key 权限不足', message: '您的 API Key 没有访问 Deep Research 的权限', solution: '请在阿里云百炼控制台开通 Deep Research 功能,或重新生成包含该权限的 API Key', }, 'InvalidParameter': { title: '请求参数错误', message: '请求参数不正确', solution: '请检查输入内容是否符合要求,或联系技术支持', }, 'QuotaExceeded': { title: 'API 调用配额已用尽', message: '您的 API 调用配额已达到上限', solution: '请充值或等待配额重置,也可以升级套餐获得更多配额', }, 'Throttling': { title: '请求过于频繁', message: '您的请求速度超过了限制', solution: '请稍后再试,或联系客服提升限流阈值', }, 'InternalError': { title: '服务内部错误', message: 'DashScope 服务遇到内部错误', solution: '请稍后重试,如果问题持续存在,请联系技术支持', }, 'ServiceUnavailable': { title: '服务暂时不可用', message: 'DashScope 服务暂时无法访问', solution: '请稍后重试,或查看服务状态页面', }, }; /** * 通用错误映射 */ const COMMON_ERROR_MAP: Record = { 'NetworkError': { title: '网络连接失败', message: '无法连接到服务器', solution: '请检查网络连接,或稍后重试', }, 'Timeout': { title: '请求超时', message: '服务器响应超时', solution: '请检查网络连接,或稍后重试', }, 'Unauthorized': { title: '未授权', message: '您的登录已过期', solution: '请重新登录', }, 'Forbidden': { title: '访问被拒绝', message: '您没有权限执行此操作', solution: '请联系管理员获取权限', }, 'NotFound': { title: '资源不存在', message: '请求的资源未找到', solution: '请检查请求是否正确', }, }; /** * 解析错误信息 */ export function parseError(error: any): ErrorDetail { // 如果是字符串,直接返回 if (typeof error === 'string') { return { message: error, }; } // 如果是 Error 对象 if (error instanceof Error) { // 尝试解析错误消息中的错误代码 const errorMessage = error.message; // axios 错误常包含 response 对象,优先解析 // 处理 axios 风格的错误对象 // @ts-ignore if ((error as any).response) { // @ts-ignore const resp = (error as any).response; const msg = resp.data?.message || resp.data?.detail; if (msg) { return { message: msg, }; } } // 检查是否包含 DashScope 错误代码 for (const [code, detail] of Object.entries(DASHSCOPE_ERROR_MAP)) { if (errorMessage.includes(code)) { return { code, ...detail, }; } } // 检查是否包含通用错误 for (const [code, detail] of Object.entries(COMMON_ERROR_MAP)) { if (errorMessage.includes(code) || errorMessage.toLowerCase().includes(code.toLowerCase())) { return { code, ...detail, }; } } // 返回原始错误消息 return { message: errorMessage, }; } // 如果是对象,尝试提取错误信息 if (typeof error === 'object' && error !== null) { // axios 风格的错误体可能包含 response // @ts-ignore if (error.response) { // @ts-ignore const resp = error.response; const msg = resp.data?.message || resp.data?.detail; if (msg) { return { message: msg, }; } } const code = error.code || error.error_code; const message = error.message || error.error_message || error.msg; // 检查是否是已知的错误代码 if (code && DASHSCOPE_ERROR_MAP[code]) { return { code, ...DASHSCOPE_ERROR_MAP[code], }; } if (code && COMMON_ERROR_MAP[code]) { return { code, ...COMMON_ERROR_MAP[code], }; } // 返回提取的信息 return { code, message: message || '未知错误', }; } // 默认错误 return { message: '发生未知错误', }; } /** * 格式化错误消息(用于 Toast 显示) */ export function formatErrorMessage(error: any): string { const detail = parseError(error); if (detail.title) { return `${detail.title}: ${detail.message}`; } return detail.message; } /** * 获取错误的完整信息(用于详细展示) */ export function getErrorDetail(error: any): ErrorDetail { return parseError(error); } /** * 判断是否是 API Key 权限错误 */ export function isApiKeyPermissionError(error: any): boolean { const detail = parseError(error); return detail.code === 'App.AccessDenied'; } /** * 判断是否是网络错误 */ export function isNetworkError(error: any): boolean { const detail = parseError(error); return detail.code === 'NetworkError' || detail.code === 'Timeout' || detail.message.includes('network') || detail.message.includes('timeout'); } /** * 获取错误的建议操作 */ export function getErrorActions(error: any, navigate?: (path: string) => void): Array<{ label: string; onClick: () => void; }> { const detail = parseError(error); const actions: Array<{ label: string; onClick: () => void }> = []; // API Key 权限错误 if (detail.code === 'App.AccessDenied') { actions.push({ label: '查看解决方案', onClick: () => { window.open('https://help.aliyun.com/zh/model-studio/', '_blank'); }, }); if (navigate) { actions.push({ label: '更新 API Key', onClick: () => navigate('/settings'), }); } } // 配额用尽 if (detail.code === 'QuotaExceeded') { actions.push({ label: '查看配额', onClick: () => { window.open('https://bailian.console.aliyun.com/', '_blank'); }, }); } return actions; } /** * 创建用户友好的错误对话框配置 */ export function createErrorDialog(error: any, navigate?: (path: string) => void) { const detail = getErrorDetail(error); const actions = getErrorActions(error, navigate); return { title: detail.title || '错误', message: detail.message, solution: detail.solution, actions, }; }