104 lines
4.5 KiB
TypeScript
104 lines
4.5 KiB
TypeScript
/**
|
||
* AI 生成失败的普通用户展示层。
|
||
*
|
||
* 页面只消费这里的稳定 code,不解析供应商名称、HTTP 状态、URL 或英文异常。
|
||
* 本文件不发请求、不依赖 React,后续脚本/图片/视频页面可按需接入。
|
||
*/
|
||
|
||
export type GenerationOperation =
|
||
| "script_generate"
|
||
| "image_generate"
|
||
| "base_asset_generate"
|
||
| "triview_generate"
|
||
| "storyboard_generate"
|
||
| "video_generate"
|
||
| "voiceover_generate"
|
||
| "entity_extract";
|
||
|
||
export type GenerationErrorCode =
|
||
| "provider_quota_exhausted"
|
||
| "provider_rate_limited"
|
||
| "provider_unavailable"
|
||
| "provider_config_error"
|
||
| "model_unavailable"
|
||
| "content_rejected"
|
||
| "invalid_input"
|
||
| "asset_unavailable"
|
||
| "user_credit_insufficient"
|
||
| "task_timeout"
|
||
| "processing_failed"
|
||
| "unknown";
|
||
|
||
export type GenerationErrorAction = "retry" | "retry_later" | "revise_input" | "recharge" | "contact_support" | "none";
|
||
|
||
export type PublicGenerationError = {
|
||
domain: "generation";
|
||
code: GenerationErrorCode;
|
||
operation: GenerationOperation;
|
||
action: GenerationErrorAction;
|
||
retryable: boolean;
|
||
reference_id?: string;
|
||
};
|
||
|
||
export type GenerationErrorPresentation = {
|
||
title: string;
|
||
description: string;
|
||
action: GenerationErrorAction;
|
||
retryable: boolean;
|
||
referenceId?: string;
|
||
};
|
||
|
||
const OPERATION_LABEL: Record<GenerationOperation, string> = {
|
||
script_generate: "脚本",
|
||
image_generate: "图片",
|
||
base_asset_generate: "基础素材",
|
||
triview_generate: "三视图",
|
||
storyboard_generate: "故事板",
|
||
video_generate: "视频",
|
||
voiceover_generate: "配音",
|
||
entity_extract: "脚本信息"
|
||
};
|
||
|
||
type Copy = (operation: string) => Pick<GenerationErrorPresentation, "title" | "description">;
|
||
|
||
const ERROR_COPY: Record<GenerationErrorCode, Copy> = {
|
||
provider_quota_exhausted: (operation) => ({ title: `${operation}暂时不可用`, description: "生成服务正在处理中,请稍后再试。" }),
|
||
provider_rate_limited: (operation) => ({ title: `${operation}请求较多`, description: "当前生成请求较多,请稍后再试。" }),
|
||
provider_unavailable: (operation) => ({ title: `${operation}暂时不可用`, description: "生成服务暂时不可用,请稍后重试。" }),
|
||
provider_config_error: (operation) => ({ title: `${operation}暂时不可用`, description: "平台正在处理该问题,请稍后再试。" }),
|
||
model_unavailable: (operation) => ({ title: `${operation}暂时不可用`, description: "当前生成能力暂不可用,请稍后再试。" }),
|
||
content_rejected: (operation) => ({ title: `${operation}内容需要调整`, description: "内容未通过生成审核,请调整描述或素材后重试。" }),
|
||
invalid_input: (operation) => ({ title: `${operation}内容需检查`, description: "请检查描述、参数或素材格式后重试。" }),
|
||
asset_unavailable: () => ({ title: "引用素材当前不可用", description: "请重新上传或更换素材后重试。" }),
|
||
user_credit_insufficient: () => ({ title: "可用积分不足", description: "充值后可继续生成。" }),
|
||
task_timeout: (operation) => ({ title: `${operation}生成时间过长`, description: "服务响应较慢,未完成生成,请重试。" }),
|
||
processing_failed: (operation) => ({ title: `${operation}结果处理失败`, description: "本次未生成可用结果,请重试。" }),
|
||
unknown: (operation) => ({ title: `${operation}遇到问题`, description: "请稍后重试;若多次出现,请提供任务编号以便排查。" })
|
||
};
|
||
|
||
export function isPublicGenerationError(value: unknown): value is PublicGenerationError {
|
||
if (!value || typeof value !== "object") return false;
|
||
const candidate = value as Partial<PublicGenerationError>;
|
||
return candidate.domain === "generation"
|
||
&& typeof candidate.code === "string" && Object.hasOwn(ERROR_COPY, candidate.code)
|
||
&& typeof candidate.operation === "string" && Object.hasOwn(OPERATION_LABEL, candidate.operation)
|
||
&& typeof candidate.action === "string"
|
||
&& typeof candidate.retryable === "boolean";
|
||
}
|
||
|
||
export function presentGenerationError(error: PublicGenerationError): GenerationErrorPresentation {
|
||
const copy = ERROR_COPY[error.code] ?? ERROR_COPY.unknown;
|
||
return {
|
||
...copy(OPERATION_LABEL[error.operation] ?? "生成"),
|
||
action: error.action,
|
||
retryable: error.retryable,
|
||
referenceId: error.reference_id
|
||
};
|
||
}
|
||
|
||
export function generationErrorText(error: unknown, fallback: string): string {
|
||
if (!isPublicGenerationError(error)) return fallback;
|
||
const presentation = presentGenerationError(error);
|
||
return `${presentation.title}:${presentation.description}`;
|
||
}
|