3D 提交失败提示不再直接显示传输层英文文案

- resolveModel3dGenerationErrorMessage 对超时单独给出中文可重试提示
- fetch 失败与中断收敛到中文兜底,服务端文案仍原样透出
- 补服务端文案透出、传输层兜底与超时提示的用例
This commit is contained in:
2026-09-22 16:57:55 +08:00
parent 852e65f7d8
commit 62f1ca4537
2 changed files with 60 additions and 1 deletions
@@ -11,9 +11,12 @@ import {
} from './Model3dGenerationFormModel';
import {
buildModel3dSubmissionPlan,
MODEL3D_GENERATION_FAILURE_MESSAGE,
MODEL3D_GENERATION_TIMEOUT_MESSAGE,
MODEL3D_IMAGE_SOURCE_REQUIRED_MESSAGE,
MODEL3D_PROJECT_REQUIRED_MESSAGE,
MODEL3D_TEXT_PROMPT_REQUIRED_MESSAGE,
resolveModel3dGenerationErrorMessage,
resolveModel3dImageSource,
resolveModel3dRequestKey,
resolveModel3dResultTitle,
@@ -336,3 +339,39 @@ describe('Idempotency-Key', () => {
expect(right).toBe(left);
});
});
describe('resolveModel3dGenerationErrorMessage', () => {
it('服务端给的文案原样透出', () => {
expect(
resolveModel3dGenerationErrorMessage(
new Error('3D 生成服务当前限流,请稍后重试。'),
),
).toBe('3D 生成服务当前限流,请稍后重试。');
expect(resolveModel3dGenerationErrorMessage('落点不可用')).toBe(
'落点不可用',
);
});
it('传输层英文技术文案换成中文兜底', () => {
expect(
resolveModel3dGenerationErrorMessage(new TypeError('Failed to fetch')),
).toBe(MODEL3D_GENERATION_FAILURE_MESSAGE);
expect(
resolveModel3dGenerationErrorMessage(
new DOMException('The operation was aborted.', 'AbortError'),
),
).toBe(MODEL3D_GENERATION_FAILURE_MESSAGE);
expect(resolveModel3dGenerationErrorMessage(null)).toBe(
MODEL3D_GENERATION_FAILURE_MESSAGE,
);
});
it('超时单独给出可重试的提示', () => {
const timeout = new Error('请求超时:60000ms');
timeout.name = 'TimeoutError';
expect(resolveModel3dGenerationErrorMessage(timeout)).toBe(
MODEL3D_GENERATION_TIMEOUT_MESSAGE,
);
});
});
@@ -1,5 +1,6 @@
import type { ExternalGenerationJobStatusRecord } from '../../../../packages/shared/src/contracts/externalGeneration';
import type { Model3dGenerationSource } from '../../../../packages/shared/src/contracts/model3d';
import { isAbortError, isTimeoutError } from '../../../services/apiClient';
import type {
EditorCanvasGenerationCompletionInput,
Model3dGenerationSubmissionResponse,
@@ -22,6 +23,10 @@ export const MODEL3D_IMAGE_SOURCE_REQUIRED_MESSAGE =
'请先选择一张画布图片或素材库图片';
export const MODEL3D_PROJECT_REQUIRED_MESSAGE =
'请先保存当前画布工程,再生成 3D 模型';
export const MODEL3D_GENERATION_FAILURE_MESSAGE =
'生成 3D 模型失败,请稍后重试。';
export const MODEL3D_GENERATION_TIMEOUT_MESSAGE =
'3D 生成请求超时,请稍后重试。';
export type Model3dSubmissionPlan = {
endpoint: Model3dPricingEndpoint;
@@ -213,11 +218,26 @@ export function model3dQueueStateFromResponse(
}
export function resolveModel3dGenerationErrorMessage(error: unknown): string {
if (isTimeoutError(error)) {
return MODEL3D_GENERATION_TIMEOUT_MESSAGE;
}
// 传输层失败的 message 是英文技术文案(`Failed to fetch` / `The operation was
// aborted.`),不能直接当用户提示;只有服务端给出的 message 才是面向用户的。
if (isAbortError(error) || isFetchFailureError(error)) {
return MODEL3D_GENERATION_FAILURE_MESSAGE;
}
if (error instanceof Error && error.message.trim()) {
return error.message.trim();
}
if (typeof error === 'string' && error.trim()) {
return error.trim();
}
return '生成 3D 模型失败,请稍后重试。';
return MODEL3D_GENERATION_FAILURE_MESSAGE;
}
/** fetch 的网络层失败统一是 `TypeError: Failed to fetch`。 */
function isFetchFailureError(error: unknown) {
return (
error instanceof TypeError && /failed to fetch|network/i.test(error.message)
);
}