修正手动去背景入队响应契约
统一手动去背景客户端类型为 queueState 响应 移除前端同步完成分支并更新队列测试 修正文档中的接口响应说明
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1868,21 +1868,19 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
expect(screen.getByTestId('sidebar').textContent).toBe('layers');
|
||||
});
|
||||
|
||||
it('resolves private character image before removing background', async () => {
|
||||
it('queues background removal for private character images', async () => {
|
||||
resolveEditorImageReferenceDataUrlMock.mockResolvedValueOnce(
|
||||
'data:image/png;base64,resolved-character',
|
||||
);
|
||||
removeImageBackgroundMock.mockResolvedValueOnce({
|
||||
imageSrc:
|
||||
'/generated-character-drafts/editor/background-removal/result.png',
|
||||
objectKey:
|
||||
'generated-character-drafts/editor/background-removal/result.png',
|
||||
assetObjectId: 'asset-object-background-removal',
|
||||
width: 512,
|
||||
height: 768,
|
||||
taskId: 'background-removal-task',
|
||||
elapsedMs: 1234,
|
||||
provider: 'BgFilter',
|
||||
queueState: {
|
||||
operationId: 'background-removal-task',
|
||||
status: 'completed',
|
||||
phaseLabel: '已完成',
|
||||
phaseDetail: '已完成',
|
||||
progress: 100,
|
||||
updatedAtMicros: 1,
|
||||
},
|
||||
});
|
||||
render(
|
||||
<GenerationWorkflowHarness
|
||||
@@ -1912,19 +1910,20 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
sourceResourceId: 'resource-source',
|
||||
});
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.getByTestId('task-refresh-key').textContent).toBe('1');
|
||||
});
|
||||
expect(screen.getByTestId('layers').textContent).toContain(
|
||||
'layer-source:源图:resource-source:character:/generated-character-drafts/editor/background-removal/result.png:background-removal-task',
|
||||
'/api/assets/read-url?objectKey=generated-character-drafts/editor/private.png',
|
||||
);
|
||||
expect(screen.getByTestId('layers').textContent).not.toContain(
|
||||
'background-removal-task',
|
||||
);
|
||||
expect(screen.getByTestId('task-refresh-key').textContent).toBe('0');
|
||||
});
|
||||
|
||||
it('opens a generating canvas placeholder when removing background in a project', async () => {
|
||||
let resolveBackgroundRemoval: ((value: unknown) => void) | null = null;
|
||||
removeImageBackgroundMock.mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
resolveBackgroundRemoval = resolve;
|
||||
}),
|
||||
() => new Promise(() => {}),
|
||||
);
|
||||
|
||||
render(
|
||||
@@ -1960,136 +1959,6 @@ describe('useImageCanvasGenerationWorkflow', () => {
|
||||
}),
|
||||
);
|
||||
expect(resolveEditorImageReferenceDataUrlMock).not.toHaveBeenCalled();
|
||||
await act(async () => {
|
||||
resolveBackgroundRemoval?.({
|
||||
imageSrc:
|
||||
'/generated-character-drafts/editor/background-removal/project-result.png',
|
||||
objectKey:
|
||||
'generated-character-drafts/editor/background-removal/project-result.png',
|
||||
assetObjectId: 'asset-object-background-removal',
|
||||
width: 512,
|
||||
height: 768,
|
||||
taskId: 'background-removal-project-task',
|
||||
elapsedMs: 1234,
|
||||
provider: 'BgFilter',
|
||||
project: null,
|
||||
});
|
||||
});
|
||||
expect(screen.getByTestId('layers').textContent).not.toContain(
|
||||
'project-result.png',
|
||||
);
|
||||
expect(screen.getByTestId('layers').textContent).not.toContain(
|
||||
'background-removal-project-task',
|
||||
);
|
||||
});
|
||||
|
||||
it('preserves a just-created crop layer when applying a background removal project snapshot', async () => {
|
||||
const applyProjectSnapshot = vi.fn();
|
||||
let resolveBackgroundRemoval: ((value: unknown) => void) | null = null;
|
||||
removeImageBackgroundMock.mockImplementationOnce(
|
||||
() =>
|
||||
new Promise((resolve) => {
|
||||
resolveBackgroundRemoval = resolve;
|
||||
}),
|
||||
);
|
||||
render(
|
||||
<GenerationWorkflowHarness
|
||||
projectId="project-1"
|
||||
applyProjectSnapshot={applyProjectSnapshot}
|
||||
initialLayers={[
|
||||
createLayer({
|
||||
id: 'layer-crop-expand-1',
|
||||
resourceId: 'resource-crop-expand',
|
||||
title: '源图 裁扩',
|
||||
src: '/generated-character-drafts/editor/crop-expand/project-1/result.png',
|
||||
objectKey:
|
||||
'generated-character-drafts/editor/crop-expand/project-1/result.png',
|
||||
assetObjectId: 'asset-object-crop-expand',
|
||||
sourceType: 'generated',
|
||||
sourceResourceId: 'resource-source',
|
||||
}),
|
||||
]}
|
||||
/>,
|
||||
);
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: '去除背景' }));
|
||||
|
||||
await waitFor(() => expect(removeImageBackgroundMock).toHaveBeenCalled());
|
||||
await act(async () => {
|
||||
resolveBackgroundRemoval?.({
|
||||
imageSrc:
|
||||
'/generated-character-drafts/editor/background-removal/project-result.png',
|
||||
objectKey:
|
||||
'generated-character-drafts/editor/background-removal/project-result.png',
|
||||
assetObjectId: 'asset-object-background-removal',
|
||||
width: 512,
|
||||
height: 768,
|
||||
taskId: 'background-removal-project-task',
|
||||
elapsedMs: 1234,
|
||||
provider: 'BgFilter',
|
||||
project: {
|
||||
projectId: 'project-1',
|
||||
title: '队列项目',
|
||||
viewport: { x: 0, y: 0, scale: 1 },
|
||||
layers: [
|
||||
{
|
||||
layerId: 'layer-background-removal-result',
|
||||
resourceId: 'resource-background-removal',
|
||||
title: '源图 裁扩 去背景',
|
||||
x: 480,
|
||||
y: 140,
|
||||
width: 512,
|
||||
height: 768,
|
||||
originalWidth: 512,
|
||||
originalHeight: 768,
|
||||
zIndex: 9,
|
||||
sourceType: 'generated',
|
||||
},
|
||||
],
|
||||
resources: [
|
||||
{
|
||||
resourceId: 'resource-background-removal',
|
||||
projectId: 'project-1',
|
||||
imageSrc:
|
||||
'/generated-character-drafts/editor/background-removal/project-result.png',
|
||||
objectKey:
|
||||
'generated-character-drafts/editor/background-removal/project-result.png',
|
||||
assetObjectId: 'asset-object-background-removal',
|
||||
width: 512,
|
||||
height: 768,
|
||||
sourceType: 'generated',
|
||||
sourceResourceId: 'resource-crop-expand',
|
||||
},
|
||||
],
|
||||
updatedAt: '2026-06-23T08:00:01.000Z',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const appliedProject = applyProjectSnapshot.mock.calls[0]?.[0];
|
||||
expect(appliedProject?.layers).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
layerId: 'layer-crop-expand-1',
|
||||
resourceId: 'resource-crop-expand',
|
||||
title: '源图 裁扩',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
layerId: 'layer-background-removal-result',
|
||||
resourceId: 'resource-background-removal',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
expect(appliedProject?.resources).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
resourceId: 'resource-crop-expand',
|
||||
objectKey:
|
||||
'generated-character-drafts/editor/crop-expand/project-1/result.png',
|
||||
assetObjectId: 'asset-object-crop-expand',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
it('opens UI design extraction as a mark selection state before submitting', () => {
|
||||
|
||||
@@ -1613,58 +1613,19 @@ export function useImageCanvasGenerationWorkflow({
|
||||
}
|
||||
: {}),
|
||||
});
|
||||
if (
|
||||
await applyQueuedEditorGenerationProject(
|
||||
result,
|
||||
projectId,
|
||||
applyProjectSnapshot,
|
||||
refreshTaskListForQueuedGeneration,
|
||||
onWalletBalanceMayHaveChanged,
|
||||
setGenerationWarning,
|
||||
backgroundRemovalDialogId,
|
||||
applyBackgroundRemovalProjectSnapshot
|
||||
? (project) =>
|
||||
preserveSourceLayerInProjectSnapshot(project, sourceLayer)
|
||||
: undefined,
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
if (result.project && applyBackgroundRemovalProjectSnapshot) {
|
||||
applyBackgroundRemovalProjectSnapshot(result.project);
|
||||
return;
|
||||
}
|
||||
if (backgroundRemovalPlacement?.placeholder && applyProjectSnapshot) {
|
||||
return;
|
||||
}
|
||||
updateSourceLayer(sourceLayer.id, (layer) => ({
|
||||
...layer,
|
||||
resourceId: result.resource?.resourceId ?? layer.resourceId,
|
||||
src: result.imageSrc,
|
||||
width: result.width,
|
||||
height: result.height,
|
||||
originalWidth: result.width,
|
||||
originalHeight: result.height,
|
||||
sourceType: result.sourceType ?? 'generated',
|
||||
provider: result.provider ?? 'BgFilter',
|
||||
taskId: result.taskId ?? null,
|
||||
objectKey: result.resource?.objectKey ?? result.objectKey ?? null,
|
||||
assetObjectId:
|
||||
result.resource?.assetObjectId ?? result.assetObjectId ?? null,
|
||||
sourceResourceId:
|
||||
result.resource?.sourceResourceId ?? sourceLayer.resourceId,
|
||||
sourceAssetId: null,
|
||||
prompt: result.resource?.prompt ?? layer.prompt,
|
||||
actualPrompt: result.resource?.actualPrompt ?? layer.actualPrompt,
|
||||
model: result.resource?.model ?? layer.model,
|
||||
assetKind:
|
||||
(result.resource?.assetKind as CanvasLayer['assetKind']) ??
|
||||
layer.assetKind,
|
||||
generationInputs:
|
||||
result.resource?.generationInputs ?? layer.generationInputs,
|
||||
generatedAssetSnapshot: result.asset ?? layer.generatedAssetSnapshot,
|
||||
}));
|
||||
setActiveSidebarPanel('layers');
|
||||
await applyQueuedEditorGenerationProject(
|
||||
result,
|
||||
projectId,
|
||||
applyProjectSnapshot,
|
||||
refreshTaskListForQueuedGeneration,
|
||||
onWalletBalanceMayHaveChanged,
|
||||
setGenerationWarning,
|
||||
backgroundRemovalDialogId,
|
||||
applyBackgroundRemovalProjectSnapshot
|
||||
? (project) =>
|
||||
preserveSourceLayerInProjectSnapshot(project, sourceLayer)
|
||||
: undefined,
|
||||
);
|
||||
} catch (error) {
|
||||
if (backgroundRemovalDialogId) {
|
||||
updateCanvasGenerationDialogById(
|
||||
@@ -1689,11 +1650,9 @@ export function useImageCanvasGenerationWorkflow({
|
||||
openPlacedCanvasGenerationDialog,
|
||||
projectId,
|
||||
refreshTaskListForQueuedGeneration,
|
||||
setActiveSidebarPanel,
|
||||
setImageContextMenu,
|
||||
setMetadataLayer,
|
||||
updateCanvasGenerationDialogById,
|
||||
updateSourceLayer,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -1618,18 +1618,17 @@ describe('editorProjectClient', () => {
|
||||
|
||||
it('passes canvas completion context to background removal', async () => {
|
||||
requestJsonMock.mockResolvedValueOnce({
|
||||
imageSrc: 'data:image/png;base64,cutout',
|
||||
width: 512,
|
||||
height: 512,
|
||||
objectKey: 'generated-character-drafts/editor-cutouts/cutout.png',
|
||||
assetObjectId: 'asset-object-cutout',
|
||||
taskId: 'background-removal-1',
|
||||
elapsedMs: 1200,
|
||||
provider: 'BgFilter',
|
||||
project: null,
|
||||
queueState: {
|
||||
operationId: 'background-removal-1',
|
||||
status: 'queued',
|
||||
phaseLabel: '排队中',
|
||||
phaseDetail: '排队中',
|
||||
progress: 0,
|
||||
updatedAtMicros: 1,
|
||||
},
|
||||
});
|
||||
|
||||
await removeEditorImageBackground({
|
||||
const response = await removeEditorImageBackground({
|
||||
sourceImageSrc: 'data:image/png;base64,source',
|
||||
projectId: 'editor-project-1',
|
||||
targetLayerId: 'layer-source',
|
||||
@@ -1647,6 +1646,8 @@ describe('editorProjectClient', () => {
|
||||
},
|
||||
});
|
||||
|
||||
expect(response.queueState.operationId).toBe('background-removal-1');
|
||||
|
||||
expect(requestJsonMock).toHaveBeenCalledWith(
|
||||
'/api/editor/images/background-removals',
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -277,19 +277,7 @@ export type EditorImageGenerationResult = {
|
||||
};
|
||||
|
||||
export type EditorBackgroundRemovalResult = {
|
||||
imageSrc: string;
|
||||
objectKey?: string | null;
|
||||
assetObjectId?: string | null;
|
||||
width: number;
|
||||
height: number;
|
||||
sourceType?: 'generated';
|
||||
taskId?: string | null;
|
||||
elapsedMs?: number;
|
||||
provider?: string;
|
||||
resource?: EditorProjectResourceSnapshot | null;
|
||||
asset?: EditorAssetSnapshot | null;
|
||||
project?: EditorProjectSnapshot | null;
|
||||
queueState?: ExternalGenerationJobStatusRecord | null;
|
||||
queueState: ExternalGenerationJobStatusRecord;
|
||||
};
|
||||
|
||||
export type EditorIconSpritesheetIconResult = {
|
||||
|
||||
Reference in New Issue
Block a user