3D 提交失败提示不再直接显示传输层英文文案
- resolveModel3dGenerationErrorMessage 对超时单独给出中文可重试提示 - fetch 失败与中断收敛到中文兜底,服务端文案仍原样透出 - 补服务端文案透出、传输层兜底与超时提示的用例
This commit is contained in:
@@ -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)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user