ai-chatbot/lib/agents/psd-template-timeout.ts
Your Name 19970915fd
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled
feat: optimize psd template asset backfill
2026-05-09 11:28:38 +08:00

133 lines
3.6 KiB
TypeScript

import "server-only";
import { upsertSystemSetting } from "@/lib/db/queries";
import {
AGENT_PSD_TEMPLATES_KEY,
type AgentPsdTemplate,
} from "@/lib/agents/psd-template-config";
import { readAgentPsdTemplates } from "@/lib/agents/psd-template-settings";
export const AGENT_PSD_TEMPLATE_QUICK_PARSE_TIMEOUT_MS = 8 * 60 * 1000;
export const AGENT_PSD_TEMPLATE_ASSET_BACKFILL_TIMEOUT_MS = 20 * 60 * 1000;
function getTemplateUpdatedAtTime(template: AgentPsdTemplate) {
const updatedAt = new Date(template.updatedAt).getTime();
if (Number.isFinite(updatedAt)) {
return updatedAt;
}
const createdAt = new Date(template.createdAt).getTime();
return Number.isFinite(createdAt) ? createdAt : null;
}
function hasPassedFastParsePhase(template: AgentPsdTemplate) {
return (
Number(template.parseProgress || 0) >= 48 ||
template.layers.length > 0 ||
(Number(template.width || 0) > 1 && Number(template.height || 0) > 1)
);
}
export function getAgentPsdTemplateParseTimeoutMs(template: AgentPsdTemplate) {
return hasPassedFastParsePhase(template)
? AGENT_PSD_TEMPLATE_ASSET_BACKFILL_TIMEOUT_MS
: AGENT_PSD_TEMPLATE_QUICK_PARSE_TIMEOUT_MS;
}
export function getAgentPsdTemplateParseAgeMs(
template: AgentPsdTemplate,
now = Date.now()
) {
const updatedAt = getTemplateUpdatedAtTime(template);
if (!updatedAt) {
return Number.POSITIVE_INFINITY;
}
return Math.max(0, now - updatedAt);
}
export function isAgentPsdTemplateParseTimedOut(
template: AgentPsdTemplate,
now = Date.now()
) {
if (template.parseStatus !== "processing") {
return false;
}
return (
getAgentPsdTemplateParseAgeMs(template, now) >
getAgentPsdTemplateParseTimeoutMs(template)
);
}
function buildTimeoutMessage(template: AgentPsdTemplate) {
const timeoutMinutes = Math.round(
getAgentPsdTemplateParseTimeoutMs(template) / 60_000
);
const phase = hasPassedFastParsePhase(template)
? "后台图层资产补全"
: "PSD 快速解析";
const stage = template.parseStage ? `,最后阶段:${template.parseStage}` : "";
return `${phase}超过 ${timeoutMinutes} 分钟未更新进度,系统已自动结束本次任务${stage}。可检查 PSD 文件后重新解析。`;
}
export function failTimedOutAgentPsdTemplatesInMemory(params: {
templates: AgentPsdTemplate[];
now?: Date;
}) {
const now = params.now || new Date();
let changed = false;
const timedOutTemplates: AgentPsdTemplate[] = [];
const templates = params.templates.map((template) => {
if (!isAgentPsdTemplateParseTimedOut(template, now.getTime())) {
return template;
}
changed = true;
const nextTemplate: AgentPsdTemplate = {
...template,
parseStatus: "failed",
parseProgress: Math.min(
95,
Math.max(0, Math.round(Number(template.parseProgress || 0)))
),
parseStage: hasPassedFastParsePhase(template)
? "后台图层资产补全超时"
: "PSD 快速解析超时",
parseError: buildTimeoutMessage(template),
updatedAt: now.toISOString(),
};
timedOutTemplates.push(nextTemplate);
return nextTemplate;
});
return {
changed,
templates,
timedOutTemplates,
};
}
export async function failTimedOutAgentPsdTemplates(params: {
updatedBy: string;
now?: Date;
}) {
const currentTemplates = await readAgentPsdTemplates();
const result = failTimedOutAgentPsdTemplatesInMemory({
templates: currentTemplates,
now: params.now,
});
if (result.changed) {
await upsertSystemSetting({
key: AGENT_PSD_TEMPLATES_KEY,
value: result.templates,
updatedBy: params.updatedBy,
});
}
return result;
}