3D 失败提示只透出服务端给的文案

- src/components/image-editor/model3d-generation/Model3dGenerationSubmission.ts:resolveModel3dGenerationErrorMessage 只对 ApiClientError 原样透出 message;原生 SyntaxError(2xx 但 body 不是 JSON)与其它运行时 TypeError 的英文技术文案不再直接显示给用户
- src/components/image-editor/model3d-generation/Model3dGenerationSubmission.test.ts、useModel3dGenerationTask.test.tsx:用例改用 ApiClientError 构造服务端文案,并补非服务端错误退回兜底文案的断言
This commit is contained in:
2026-09-24 17:30:01 +08:00
parent 96b52ae7ee
commit 2350c56f2f
3 changed files with 34 additions and 4 deletions
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest';
import { ApiClientError } from '../../../services/apiClient';
import type { Model3dPricingConfig } from '../../../services/image-editor/editorProjectClient';
import type {
CharacterReferenceImage,
@@ -478,7 +479,11 @@ describe('resolveModel3dGenerationErrorMessage', () => {
it('服务端给的文案原样透出', () => {
expect(
resolveModel3dGenerationErrorMessage(
new Error('3D 生成服务当前限流,请稍后重试。'),
new ApiClientError({
message: '3D 生成服务当前限流,请稍后重试。',
status: 429,
code: 'tripo-api-rate-limited',
}),
),
).toBe('3D 生成服务当前限流,请稍后重试。');
expect(resolveModel3dGenerationErrorMessage('落点不可用')).toBe(
@@ -486,6 +491,20 @@ describe('resolveModel3dGenerationErrorMessage', () => {
);
});
it('非服务端来源的错误不把技术文案透给用户', () => {
// 2xx 但 body 不是 JSON 时 requestJson 抛的是原生 SyntaxError。
expect(
resolveModel3dGenerationErrorMessage(
new SyntaxError('Unexpected token < in JSON at position 0'),
),
).toBe(MODEL3D_GENERATION_FAILURE_MESSAGE);
expect(
resolveModel3dGenerationErrorMessage(
new TypeError("Cannot read properties of undefined (reading 'ok')"),
),
).toBe(MODEL3D_GENERATION_FAILURE_MESSAGE);
});
it('传输层英文技术文案换成中文兜底', () => {
expect(
resolveModel3dGenerationErrorMessage(new TypeError('Failed to fetch')),
@@ -4,7 +4,11 @@ import type {
Model3dImageToModelRequest,
Model3dTextToModelRequest,
} from '../../../../packages/shared/src/contracts/model3d';
import { isAbortError, isTimeoutError } from '../../../services/apiClient';
import {
ApiClientError,
isAbortError,
isTimeoutError,
} from '../../../services/apiClient';
import type {
EditorCanvasGenerationCompletionInput,
Model3dGenerationSubmissionResponse,
@@ -307,7 +311,9 @@ export function resolveModel3dGenerationErrorMessage(error: unknown): string {
if (isAbortError(error) || isFetchFailureError(error)) {
return MODEL3D_GENERATION_FAILURE_MESSAGE;
}
if (error instanceof Error && error.message.trim()) {
// 只有 ApiClientError 的 message 是按服务端响应拼出来的;`JSON.parse` 失败的 SyntaxError
// 或其它运行时 TypeError 的 message 是英文技术文案,同样不能当用户提示。
if (error instanceof ApiClientError && error.message.trim()) {
return error.message.trim();
}
if (typeof error === 'string' && error.trim()) {
@@ -5,6 +5,7 @@ import { useState } from 'react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { ExternalGenerationJobStatusRecord } from '../../../../packages/shared/src/contracts/externalGeneration';
import { ApiClientError } from '../../../services/apiClient';
import type {
EditorCanvasGenerationCompletionInput,
Model3dGenerationSubmissionResponse,
@@ -256,7 +257,11 @@ describe('useModel3dGenerationTask', () => {
it('请求失败时留在面板上展示失败原因', async () => {
submitModel3dTextToModelRequestMock.mockRejectedValue(
new Error('3D 模型生成失败,请稍后重试。'),
new ApiClientError({
message: '3D 模型生成失败,请稍后重试。',
status: 502,
code: 'tripo-upstream-error',
}),
);
const { result } = renderTask({ dialog: createDialog() });