diff --git a/src/api/ImagesTaskCache.ts b/src/api/ImagesTaskCache.ts index b4fb03a..1dbb333 100644 --- a/src/api/ImagesTaskCache.ts +++ b/src/api/ImagesTaskCache.ts @@ -24,19 +24,25 @@ function cacheLog(value: string, color?: string) { } } +// 定义任务结果接口 +interface TaskResult { + status: -1 | -2 | -3 | number; + url?: string; + errorMessage?: string; + timestamp: number; +} + export class ImagesTaskCache { private static instance: ImagesTaskCache; - private taskCache: Map; + private taskCache: Map; private tosProcessedTasks: Set; // 记录已处理TOS上传的任务 private cleanupInterval: NodeJS.Timeout | null = null; private constructor() { - this.taskCache = new Map(); - this.tosProcessedTasks = new Set(); - cacheLog("ImagesTaskCache initialized"); - - // 启动定时清理任务(每30分钟) + this.taskCache = new Map(); + this.tosProcessedTasks = new Set(); this.startPeriodicCleanup(); + cacheLog("ImagesTaskCache initialized"); } public static getInstance(): ImagesTaskCache { @@ -71,7 +77,10 @@ export class ImagesTaskCache { public startTask(taskId: string): void { const startTime = Math.floor(Date.now() / 1000); // Current time in seconds - this.taskCache.set(taskId, startTime); + this.taskCache.set(taskId, { + status: startTime, + timestamp: Date.now() + }); cacheLog(`Task started: ${taskId} at ${startTime}`); } @@ -101,7 +110,7 @@ export class ImagesTaskCache { return tosUrls; } - public async finishTask(taskId: string, status: -1 | -2 | -3, url: string = ''): Promise { + public async finishTask(taskId: string, status: -1 | -2 | -3, url: string = '', errorMessage?: string): Promise { if (!this.taskCache.has(taskId)) { cacheLog(`Attempted to finish non-existent task: ${taskId}`); return; @@ -139,12 +148,17 @@ export class ImagesTaskCache { case -3: statusMessage = 'timed out'; break; } - // 存储最终URL(TOS地址或原始URL) - this.taskCache.set(taskId, finalUrl || status); + // 存储任务结果,包括错误信息 + this.taskCache.set(taskId, { + status, + url: finalUrl, + errorMessage: status !== -1 ? errorMessage : undefined, + timestamp: Date.now() + }); cacheLog(`Task ${taskId} finished ${statusMessage} (status: ${status})`); } - public getTaskStatus(taskId: string): number | string | undefined { + public getTaskStatus(taskId: string): TaskResult | undefined { return this.taskCache.get(taskId); } @@ -168,14 +182,20 @@ export class ImagesTaskCache { * @param taskId 任务ID * @returns 任务结果,如果不存在返回undefined */ - public getTaskResultAndClear(taskId: string): number | string | undefined { + public getTaskResultAndClear(taskId: string): TaskResult | undefined { const result = this.taskCache.get(taskId); - if (result && typeof result === 'string') { - // 只有当任务完成时(返回字符串URL)才清除缓存 - this.taskCache.delete(taskId); - this.tosProcessedTasks.delete(taskId); - cacheLog(`Task ${taskId} result retrieved and cache cleared`); + if (!result) { + return undefined; } + + // 如果任务已完成(状态为字符串URL或负数状态码),则清理缓存 + if (typeof result.status === 'number' && result.status < 0) { + this.taskCache.delete(taskId); + cacheLog(`Task ${taskId} result retrieved and cache cleared`); + return result; + } + + // 任务仍在进行中,不清理缓存 return result; } @@ -184,23 +204,24 @@ export class ImagesTaskCache { * 防止在低配置服务器上内存泄漏 */ public clearExpiredTasks(): void { - const now = Math.floor(Date.now() / 1000); - const expiredTime = 3600; // 1尊时 - let clearCount = 0; - - for (const [taskId, status] of this.taskCache.entries()) { - if (typeof status === 'number' && status > 0) { - // 这是一个时间戳,检查是否过期 - if (now - status > expiredTime) { - this.taskCache.delete(taskId); - this.tosProcessedTasks.delete(taskId); - clearCount++; - } + const now = Date.now(); + const expiredTasks: string[] = []; + + for (const [taskId, result] of this.taskCache.entries()) { + // 检查任务是否超过24小时 + if (now - result.timestamp > 24 * 60 * 60 * 1000) { + expiredTasks.push(taskId); } } - - if (clearCount > 0) { - cacheLog(`Cleared ${clearCount} expired tasks`); + + for (const taskId of expiredTasks) { + this.taskCache.delete(taskId); + this.tosProcessedTasks.delete(taskId); + cacheLog(`Expired task cleared: ${taskId}`); + } + + if (expiredTasks.length > 0) { + cacheLog(`Cleared ${expiredTasks.length} expired tasks`); } } @@ -210,15 +231,15 @@ export class ImagesTaskCache { public getCacheStats(): { totalTasks: number, completedTasks: number, pendingTasks: number } { let completedTasks = 0; let pendingTasks = 0; - - for (const [, status] of this.taskCache.entries()) { - if (typeof status === 'string') { + + for (const result of this.taskCache.values()) { + if (typeof result.status === 'number' && result.status < 0) { completedTasks++; - } else if (typeof status === 'number' && status > 0) { + } else { pendingTasks++; } } - + return { totalTasks: this.taskCache.size, completedTasks, @@ -228,11 +249,13 @@ export class ImagesTaskCache { public getPendingTasks(): string[] { const pendingTasks: string[] = []; - for (const [taskId, status] of this.taskCache.entries()) { - if (typeof status == 'number' && status > 0) { + + for (const [taskId, result] of this.taskCache.entries()) { + if (typeof result.status === 'number' && result.status >= 0) { pendingTasks.push(taskId); } } + return pendingTasks; } diff --git a/src/api/VideoTaskCache.ts b/src/api/VideoTaskCache.ts index cf1aedd..bdc7ce5 100644 --- a/src/api/VideoTaskCache.ts +++ b/src/api/VideoTaskCache.ts @@ -24,19 +24,25 @@ function cacheLog(value: string, color?: string) { } } +// 定义任务结果接口 +interface TaskResult { + status: -1 | -2 | -3 | number; + url?: string; + errorMessage?: string; + timestamp: number; +} + export class VideoTaskCache { private static instance: VideoTaskCache; - private taskCache: Map; + private taskCache: Map; private tosProcessedTasks: Set; // 记录已处理TOS上传的任务 private cleanupInterval: NodeJS.Timeout | null = null; private constructor() { - this.taskCache = new Map(); - this.tosProcessedTasks = new Set(); - cacheLog("VideoTaskCache initialized"); - - // 启动定时清理任务(每30分钟) + this.taskCache = new Map(); + this.tosProcessedTasks = new Set(); this.startPeriodicCleanup(); + cacheLog("VideoTaskCache initialized"); } public static getInstance(): VideoTaskCache { @@ -71,7 +77,10 @@ export class VideoTaskCache { public startTask(taskId: string): void { const startTime = Math.floor(Date.now() / 1000); // Current time in seconds - this.taskCache.set(taskId, startTime); + this.taskCache.set(taskId, { + status: startTime, + timestamp: Date.now() + }); cacheLog(`Task started: ${taskId} at ${startTime}`); } @@ -101,7 +110,7 @@ export class VideoTaskCache { return tosUrls; } - public async finishTask(taskId: string, status: -1 | -2 | -3, url: string = ''): Promise { + public async finishTask(taskId: string, status: -1 | -2 | -3, url: string = '', errorMessage?: string): Promise { if (!this.taskCache.has(taskId)) { cacheLog(`Attempted to finish non-existent task: ${taskId}`); return; @@ -139,12 +148,17 @@ export class VideoTaskCache { case -3: statusMessage = 'timed out'; break; } - // 存储最终URL(TOS地址或原始URL) - this.taskCache.set(taskId, finalUrl || status); + // 存储任务结果,包括错误信息 + this.taskCache.set(taskId, { + status, + url: finalUrl, + errorMessage: status !== -1 ? errorMessage : undefined, + timestamp: Date.now() + }); cacheLog(`Task ${taskId} finished ${statusMessage} (status: ${status})`); } - public getTaskStatus(taskId: string): number | string | undefined { + public getTaskStatus(taskId: string): TaskResult | undefined { return this.taskCache.get(taskId); } @@ -168,14 +182,20 @@ export class VideoTaskCache { * @param taskId 任务ID * @returns 任务结果,如果不存在返回undefined */ - public getTaskResultAndClear(taskId: string): number | string | undefined { + public getTaskResultAndClear(taskId: string): TaskResult | undefined { const result = this.taskCache.get(taskId); - if (result && typeof result === 'string') { - // 只有当任务完成时(返回字符串URL)才清除缓存 - this.taskCache.delete(taskId); - this.tosProcessedTasks.delete(taskId); - cacheLog(`Task ${taskId} result retrieved and cache cleared`); + if (!result) { + return undefined; } + + // 如果任务已完成(状态为字符串URL或负数状态码),则清理缓存 + if (typeof result.status === 'number' && result.status < 0) { + this.taskCache.delete(taskId); + cacheLog(`Task ${taskId} result retrieved and cache cleared`); + return result; + } + + // 任务仍在进行中,不清理缓存 return result; } @@ -184,23 +204,24 @@ export class VideoTaskCache { * 防止在低配置服务器上内存泄漏 */ public clearExpiredTasks(): void { - const now = Math.floor(Date.now() / 1000); - const expiredTime = 3600; // 1小时 - let clearCount = 0; - - for (const [taskId, status] of this.taskCache.entries()) { - if (typeof status === 'number' && status > 0) { - // 这是一个时间戳,检查是否过期 - if (now - status > expiredTime) { - this.taskCache.delete(taskId); - this.tosProcessedTasks.delete(taskId); - clearCount++; - } + const now = Date.now(); + const expiredTasks: string[] = []; + + for (const [taskId, result] of this.taskCache.entries()) { + // 检查任务是否超过24小时 + if (now - result.timestamp > 24 * 60 * 60 * 1000) { + expiredTasks.push(taskId); } } - - if (clearCount > 0) { - cacheLog(`Cleared ${clearCount} expired tasks`); + + for (const taskId of expiredTasks) { + this.taskCache.delete(taskId); + this.tosProcessedTasks.delete(taskId); + cacheLog(`Expired task cleared: ${taskId}`); + } + + if (expiredTasks.length > 0) { + cacheLog(`Cleared ${expiredTasks.length} expired tasks`); } } @@ -210,15 +231,15 @@ export class VideoTaskCache { public getCacheStats(): { totalTasks: number, completedTasks: number, pendingTasks: number } { let completedTasks = 0; let pendingTasks = 0; - - for (const [, status] of this.taskCache.entries()) { - if (typeof status === 'string') { + + for (const result of this.taskCache.values()) { + if (typeof result.status === 'number' && result.status < 0) { completedTasks++; - } else if (typeof status === 'number' && status > 0) { + } else { pendingTasks++; } } - + return { totalTasks: this.taskCache.size, completedTasks, @@ -228,11 +249,13 @@ export class VideoTaskCache { public getPendingTasks(): string[] { const pendingTasks: string[] = []; - for (const [taskId, status] of this.taskCache.entries()) { - if (typeof status == 'number' && status > 0) { + + for (const [taskId, result] of this.taskCache.entries()) { + if (typeof result.status === 'number' && result.status >= 0) { pendingTasks.push(taskId); } } + return pendingTasks; } diff --git a/src/api/consts/exceptions.ts b/src/api/consts/exceptions.ts index 84b42cb..320a99e 100644 --- a/src/api/consts/exceptions.ts +++ b/src/api/consts/exceptions.ts @@ -6,7 +6,7 @@ export default { API_FILE_URL_INVALID: [-2003, '远程文件URL非法'], API_FILE_EXECEEDS_SIZE: [-2004, '远程文件超出大小'], API_CHAT_STREAM_PUSHING: [-2005, '已有对话流正在输出'], - API_CONTENT_FILTERED: [-2006, '内容由于合规问题已被阻止生成'], + API_CONTENT_FILTERED: [-2006, 'sensitive 存在敏感信息'], API_IMAGE_GENERATION_FAILED: [-2007, '图像生成失败'], API_VIDEO_GENERATION_FAILED: [-2008, '视频生成失败'], API_IMAGE_GENERATION_INSUFFICIENT_POINTS: [-2009, '即梦积分不足'], diff --git a/src/api/controllers/images.ts b/src/api/controllers/images.ts index c807b47..0fcfeda 100644 --- a/src/api/controllers/images.ts +++ b/src/api/controllers/images.ts @@ -137,7 +137,7 @@ export async function generateImages( const historyId = aigc_data.history_record_id; if (!historyId) throw new APIException(EX.API_IMAGE_GENERATION_FAILED, "记录ID不存在"); - let status = 20, failCode, item_list = []; + let status = 20, failCode = '', fail_msg = '', item_list = []; while (status === 20) { await new Promise((resolve) => setTimeout(resolve, 5000)); const result = await request("post", "/mweb/v1/get_history_by_ids", refreshToken, { @@ -245,13 +245,15 @@ export async function generateImages( throw new APIException(EX.API_IMAGE_GENERATION_FAILED, "记录不存在"); status = result[historyId].status; failCode = result[historyId].fail_code; + fail_msg = result[historyId].fail_msg; item_list = result[historyId].item_list; } if (status === 30) { - if (failCode === '2038') + if (failCode == '2038' || failCode == '2041') { throw new APIException(EX.API_CONTENT_FILTERED); - else + } else { throw new APIException(EX.API_IMAGE_GENERATION_FAILED); + } } const imageUrls = item_list.map((item) => { if(!item?.image?.large_images?.[0]?.image_url) @@ -262,16 +264,18 @@ export async function generateImages( if (validImageUrls.length > 0) { await imagesTaskCache.finishTask(task_id, -1, validImageUrls.join(",")); // Success } else { - logger.error(`图片生成任务 ${task_id} 异常: 图片生成未返回有效链接`); + const errorMessage = "图片生成未返回有效链接"; + logger.error(`图片生成任务 ${task_id} 异常: ${errorMessage}`); // If no valid URLs but no explicit error thrown earlier, consider it a failure. // This could happen if item_list is empty or items don't have video_url. - await imagesTaskCache.finishTask(task_id, -2); // Failure - throw new APIException(EX.API_IMAGE_GENERATION_FAILED, "图片生成未返回有效链接"); + await imagesTaskCache.finishTask(task_id, -2, "", errorMessage); // Failure + throw new APIException(EX.API_IMAGE_GENERATION_FAILED, errorMessage); } return validImageUrls; }catch (error) { - logger.error(`图片生成任务 ${task_id} 异常: ${error.message}`); - await imagesTaskCache.finishTask(task_id, -2); // Failure due to exception + const errorMessage = error.message || "图片生成过程中发生未知错误"; + logger.error(`图片生成任务 ${task_id} 异常: ${errorMessage}`); + await imagesTaskCache.finishTask(task_id, -2, "", errorMessage); // Failure due to exception throw error; // Re-throw the error to be handled by the caller } } diff --git a/src/api/controllers/video.ts b/src/api/controllers/video.ts index 821657e..b60bfcd 100644 --- a/src/api/controllers/video.ts +++ b/src/api/controllers/video.ts @@ -224,13 +224,15 @@ export async function generateVideo( } status = result[historyId].status; failCode = result[historyId].fail_code; + const fail_msg = result[historyId].fail_msg; item_list = result[historyId].item_list; } if (status === 30) { - if (failCode === '2038') + if (failCode === '2038' || failCode == '2041') { throw new APIException(EX.API_CONTENT_FILTERED); - else + } else { throw new APIException(EX.API_VIDEO_GENERATION_FAILED); + } } // Assuming success if status is not 30 (failed) and not 20 (pending) // and item_list is populated. @@ -246,14 +248,18 @@ export async function generateVideo( if (validVideoUrls.length > 0) { await videoTaskCache.finishTask(task_id, -1, validVideoUrls.join(",")); // Success } else { + const errorMessage = "视频生成未返回有效链接"; + logger.error(`视频生成任务 ${task_id} 异常: ${errorMessage}`); // If no valid URLs but no explicit error thrown earlier, consider it a failure. // This could happen if item_list is empty or items don't have video_url. - await videoTaskCache.finishTask(task_id, -2); // Failure - throw new APIException(EX.API_VIDEO_GENERATION_FAILED, "视频生成未返回有效链接"); + await videoTaskCache.finishTask(task_id, -2, "", errorMessage); // Failure + throw new APIException(EX.API_VIDEO_GENERATION_FAILED, errorMessage); } return validVideoUrls; } catch (error) { - await videoTaskCache.finishTask(task_id, -2); // Failure due to exception + const errorMessage = error.message || "视频生成过程中发生未知错误"; + logger.error(`视频生成任务 ${task_id} 异常: ${errorMessage}`); + await videoTaskCache.finishTask(task_id, -2, "", errorMessage); // Failure due to exception throw error; // Re-throw the error to be handled by the caller } } diff --git a/src/api/routes/images.ts b/src/api/routes/images.ts index a66f617..463d343 100644 --- a/src/api/routes/images.ts +++ b/src/api/routes/images.ts @@ -28,20 +28,30 @@ export default { const imagesTaskCache = ImagesTaskCache.getInstance(); // 使用新的方法获取任务结果并清理缓存 - let res = imagesTaskCache.getTaskResultAndClear(task_id); + let result = imagesTaskCache.getTaskResultAndClear(task_id); - if (typeof res === 'string') { - // 任务已完成,返回TOS地址(已经在finishTask中处理过) + if (result && typeof result.status === 'number' && result.status < 0) { + // 任务已完成 return { created: util.unixTimestamp(), - data: { task_id, url: res, status: -1 }, + data: { + task_id, + url: result.url || "", + status: result.status, + error_message: result.errorMessage || null + }, }; } else { // 任务进行中或失败,不清理缓存 - res = imagesTaskCache.getTaskStatus(task_id); + result = imagesTaskCache.getTaskStatus(task_id); return { created: util.unixTimestamp(), - data: { task_id, url: "", status: res || 0 }, + data: { + task_id, + url: "", + status: result?.status || 0, + error_message: result?.errorMessage || null + }, }; } } diff --git a/src/api/routes/video.ts b/src/api/routes/video.ts index 0a2adad..2aa45fe 100644 --- a/src/api/routes/video.ts +++ b/src/api/routes/video.ts @@ -28,20 +28,30 @@ export default { const videoTaskCache = VideoTaskCache.getInstance(); // 使用新的方法获取任务结果并清理缓存 - let res = videoTaskCache.getTaskResultAndClear(task_id); + let result = videoTaskCache.getTaskResultAndClear(task_id); - if (typeof res === 'string') { - // 任务已完成,返回TOS地址(已经在finishTask中处理过) + if (result && typeof result.status === 'number' && result.status < 0) { + // 任务已完成 return { created: util.unixTimestamp(), - data: { task_id, url: res, status: -1 }, + data: { + task_id, + url: result.url || "", + status: result.status, + error_message: result.errorMessage || null + }, }; } else { // 任务进行中或失败,不清理缓存 - res = videoTaskCache.getTaskStatus(task_id); + result = videoTaskCache.getTaskStatus(task_id); return { created: util.unixTimestamp(), - data: { task_id, url: "", status: res || 0 }, + data: { + task_id, + url: "", + status: result?.status || 0, + error_message: result?.errorMessage || null + }, }; } }