生成错误信息记录

This commit is contained in:
jonathang4 2025-10-10 19:09:57 +08:00
parent ade0e0250c
commit 490b6eca29
7 changed files with 180 additions and 104 deletions

View File

@ -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<string, number|string>;
private taskCache: Map<string, TaskResult>;
private tosProcessedTasks: Set<string>; // 记录已处理TOS上传的任务
private cleanupInterval: NodeJS.Timeout | null = null;
private constructor() {
this.taskCache = new Map<string, number|string>();
this.tosProcessedTasks = new Set<string>();
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<void> {
public async finishTask(taskId: string, status: -1 | -2 | -3, url: string = '', errorMessage?: string): Promise<void> {
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;
}
// 存储最终URLTOS地址或原始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;
const now = Date.now();
const expiredTasks: string[] = [];
for (const [taskId, status] of this.taskCache.entries()) {
if (typeof status === 'number' && status > 0) {
// 这是一个时间戳,检查是否过期
if (now - status > expiredTime) {
for (const [taskId, result] of this.taskCache.entries()) {
// 检查任务是否超过24小时
if (now - result.timestamp > 24 * 60 * 60 * 1000) {
expiredTasks.push(taskId);
}
}
for (const taskId of expiredTasks) {
this.taskCache.delete(taskId);
this.tosProcessedTasks.delete(taskId);
clearCount++;
}
}
cacheLog(`Expired task cleared: ${taskId}`);
}
if (clearCount > 0) {
cacheLog(`Cleared ${clearCount} expired tasks`);
if (expiredTasks.length > 0) {
cacheLog(`Cleared ${expiredTasks.length} expired tasks`);
}
}
@ -211,10 +232,10 @@ export class ImagesTaskCache {
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++;
}
}
@ -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;
}

View File

@ -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<string, number|string>;
private taskCache: Map<string, TaskResult>;
private tosProcessedTasks: Set<string>; // 记录已处理TOS上传的任务
private cleanupInterval: NodeJS.Timeout | null = null;
private constructor() {
this.taskCache = new Map<string, number|string>();
this.tosProcessedTasks = new Set<string>();
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<void> {
public async finishTask(taskId: string, status: -1 | -2 | -3, url: string = '', errorMessage?: string): Promise<void> {
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;
}
// 存储最终URLTOS地址或原始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;
const now = Date.now();
const expiredTasks: string[] = [];
for (const [taskId, status] of this.taskCache.entries()) {
if (typeof status === 'number' && status > 0) {
// 这是一个时间戳,检查是否过期
if (now - status > expiredTime) {
for (const [taskId, result] of this.taskCache.entries()) {
// 检查任务是否超过24小时
if (now - result.timestamp > 24 * 60 * 60 * 1000) {
expiredTasks.push(taskId);
}
}
for (const taskId of expiredTasks) {
this.taskCache.delete(taskId);
this.tosProcessedTasks.delete(taskId);
clearCount++;
}
}
cacheLog(`Expired task cleared: ${taskId}`);
}
if (clearCount > 0) {
cacheLog(`Cleared ${clearCount} expired tasks`);
if (expiredTasks.length > 0) {
cacheLog(`Cleared ${expiredTasks.length} expired tasks`);
}
}
@ -211,10 +232,10 @@ export class VideoTaskCache {
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++;
}
}
@ -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;
}

View File

@ -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, '即梦积分不足'],

View File

@ -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,14 +245,16 @@ 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)
return item?.common_attr?.cover_url || null;
@ -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
}
}

View File

@ -224,14 +224,16 @@ 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.
// A more robust check might be needed depending on actual API behavior for success.
@ -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
}
}

View File

@ -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
},
};
}
}

View File

@ -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
},
};
}
}