jimeng-api / src /lib /error-handler.ts
skatef's picture
Add Jimeng API Space deployment.
461222a
import APIException from "@/lib/exceptions/APIException.ts";
import EX from "@/api/consts/exceptions.ts";
import logger from "@/lib/logger.ts";
/**
* 即梦API错误响应接口
*/
export interface JimengErrorResponse {
ret: string;
errmsg: string;
data?: any;
historyId?: string;
}
/**
* 错误处理选项
*/
export interface ErrorHandlerOptions {
context?: string;
historyId?: string;
retryCount?: number;
maxRetries?: number;
operation?: string;
}
/**
* 统一的即梦API错误处理器
*/
export class JimengErrorHandler {
/**
* 处理即梦API响应错误
*/
static handleApiResponse(
response: JimengErrorResponse,
options: ErrorHandlerOptions = {}
): never {
const { ret, errmsg, historyId } = response;
const { context = '即梦API请求', operation = '操作' } = options;
logger.error(`${context}失败: ret=${ret}, errmsg=${errmsg}${historyId ? `, historyId=${historyId}` : ''}`);
// 根据错误码分类处理
switch (ret) {
case '1015':
throw new APIException(EX.API_TOKEN_EXPIRES, `[登录失效]: ${errmsg}。请重新获取refresh_token并更新配置`);
case '5000':
throw new APIException(EX.API_IMAGE_GENERATION_INSUFFICIENT_POINTS,
`[积分不足]: ${errmsg}。建议:1)尝试使用1024x1024分辨率,2)检查是否需要购买积分,3)确认账户状态正常`);
case '4001':
throw new APIException(EX.API_CONTENT_FILTERED, `[内容违规]: ${errmsg}`);
case '4002':
throw new APIException(EX.API_REQUEST_PARAMS_INVALID, `[参数错误]: ${errmsg}`);
case '5001':
throw new APIException(EX.API_IMAGE_GENERATION_FAILED, `[生成失败]: ${errmsg}`);
case '5002':
throw new APIException(EX.API_VIDEO_GENERATION_FAILED, `[视频生成失败]: ${errmsg}`);
default:
throw new APIException(EX.API_REQUEST_FAILED, `[${operation}失败]: ${errmsg} (错误码: ${ret})`);
}
}
/**
* 处理网络请求错误
*/
static handleNetworkError(
error: any,
options: ErrorHandlerOptions = {}
): never {
const { context = '网络请求', retryCount = 0, maxRetries = 3 } = options;
logger.error(`${context}网络错误 (尝试 ${retryCount + 1}/${maxRetries + 1}): ${error.message}`);
if (error.code === 'ECONNABORTED') {
throw new APIException(EX.API_REQUEST_FAILED, `[请求超时]: ${context}超时,请稍后重试`);
}
if (error.code === 'ENOTFOUND') {
throw new APIException(EX.API_REQUEST_FAILED, `[网络错误]: 无法连接到即梦服务器,请检查网络连接`);
}
if (error.response?.status >= 500) {
throw new APIException(EX.API_REQUEST_FAILED, `[服务器错误]: 即梦服务器暂时不可用 (${error.response.status})`);
}
if (error.response?.status === 429) {
throw new APIException(EX.API_REQUEST_FAILED, `[请求频率限制]: 请求过于频繁,请稍后重试`);
}
throw new APIException(EX.API_REQUEST_FAILED, `[${context}失败]: ${error.message}`);
}
/**
* 处理轮询超时错误
* @returns 如果有部分结果,返回 void 而不抛出异常
*/
static handlePollingTimeout(
pollCount: number,
maxPollCount: number,
elapsedTime: number,
status: number,
itemCount: number,
historyId?: string
): void {
const message = `轮询超时: 已轮询 ${pollCount} 次,耗时 ${elapsedTime} 秒,最终状态: ${status},图片数量: ${itemCount}`;
logger.warn(message + (historyId ? `,历史ID: ${historyId}` : ''));
if (itemCount === 0) {
throw new APIException(EX.API_IMAGE_GENERATION_FAILED,
`生成超时且无结果,状态码: ${status}${historyId ? `,历史ID: ${historyId}` : ''}`);
}
// 如果有部分结果,不抛出异常,让调用者处理
logger.info(`轮询超时但已获得 ${itemCount} 张图片,将返回现有结果`);
}
/**
* 处理生成失败错误
* @param itemCount 已生成的结果数量,如果 > 0 则不抛出异常
* @returns 如果有部分结果,返回 false 表示不应抛出异常
*/
static handleGenerationFailure(
status: number,
failCode: string | undefined,
historyId?: string,
type: 'image' | 'video' = 'image',
itemCount: number = 0
): boolean {
const typeText = type === 'image' ? '图像' : '视频';
const message = `${typeText}生成最终失败: status=${status}, failCode=${failCode}${historyId ? `, historyId=${historyId}` : ''}, 已生成数量=${itemCount}`;
// 如果有部分结果,只记录警告,不抛出异常
if (itemCount > 0) {
logger.warn(message);
logger.info(`${typeText}生成部分失败,但已获得 ${itemCount} 个结果,将返回现有结果`);
return false; // 不抛出异常
}
// 没有任何结果时,记录错误并抛出异常
logger.error(message);
const exception = type === 'image' ? EX.API_IMAGE_GENERATION_FAILED : EX.API_VIDEO_GENERATION_FAILED;
throw new APIException(exception, `${typeText}生成失败,状态码: ${status}${failCode ? `,错误码: ${failCode}` : ''}`);
}
/**
* 包装重试逻辑的错误处理
*/
static async withRetry<T>(
operation: () => Promise<T>,
options: ErrorHandlerOptions & { maxRetries?: number; retryDelay?: number } = {}
): Promise<T> {
const {
maxRetries = 3,
retryDelay = 5000,
context = '操作',
operation: operationName = '请求'
} = options;
let lastError: any;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await operation();
} catch (error) {
lastError = error;
// 如果是APIException,直接抛出,不重试
if (error instanceof APIException) {
throw error;
}
if (attempt < maxRetries) {
logger.warn(`${context}失败 (尝试 ${attempt + 1}/${maxRetries + 1}): ${error.message}`);
logger.info(`${retryDelay / 1000}秒后重试...`);
await new Promise(resolve => setTimeout(resolve, retryDelay));
}
}
}
// 所有重试都失败了
this.handleNetworkError(lastError, {
context,
retryCount: maxRetries,
maxRetries,
operation: operationName
});
}
}
/**
* 便捷的错误处理函数
*/
export const handleJimengError = JimengErrorHandler.handleApiResponse;
export const handleNetworkError = JimengErrorHandler.handleNetworkError;
export const handlePollingTimeout = JimengErrorHandler.handlePollingTimeout;
export const handleGenerationFailure = JimengErrorHandler.handleGenerationFailure;
export const withRetry = JimengErrorHandler.withRetry;