收紧编辑器内部处理元数据边界
Project CI / Native shell tests (pull_request) Successful in 12m45s
Project CI / Frontend tests (pull_request) Successful in 2m58s
Project CI / Repository checks (pull_request) Failing after 9s
Project CI / Backend tests (pull_request) Failing after 9s

普通用户与公开响应统一移除生成 provider、内部处理模型和抠图审计字段。
普通生成请求移除 segModel,由后端固定抠图策略并清理客户端伪造的保留字段。
手动去背景继承可信源模型,角色动作与去背景失败统一使用稳定用户文案。
后台原始审计继续保留真实 provider 与抠图模型,并补齐 Owner、Public、Admin 和紧凑结果测试。
同步更新前端响应契约、外部 OpenAPI、后端架构文档和项目决策记录。
This commit is contained in:
2026-07-31 04:58:07 +00:00
parent 915eb4b739
commit 56fe1cb85d
23 changed files with 993 additions and 207 deletions
@@ -3529,7 +3529,6 @@ describe('ImageCanvasEditorView generation integration', () => {
sourceImageSrc:
'generated-character-drafts/editor/generation-references/marked-ui-design.png',
screenColor: 'auto',
segModel: 'birefnet',
aspectRatio: '1:1',
imageSize: '1K',
projectId: 'editor-project-ui-extract',
@@ -341,7 +341,6 @@ describe('ImageCanvasGenerationLayerModel', () => {
prompt: '图标 prompt',
actualPrompt: '图标 actual prompt',
model: 'gpt-image-2',
provider: 'VectorEngine',
taskId: 'task-icons',
priceMudPoints: 20,
},
@@ -432,7 +431,6 @@ describe('ImageCanvasGenerationLayerModel', () => {
prompt: '图标 prompt',
actualPrompt: '图标 actual prompt',
model: 'gpt-image-2',
provider: 'VectorEngine',
taskId: 'task-icons',
priceMudPoints: 20,
},
@@ -464,7 +462,6 @@ describe('ImageCanvasGenerationLayerModel', () => {
prompt: '金币掉落叮当声',
actualPrompt: '金币掉落叮当声',
model: 'audio1.0',
provider: 'VectorEngine',
taskId: 'sound-task-1',
priceMudPoints: 10,
audioKind: 'sound-effect',
@@ -525,7 +522,6 @@ describe('ImageCanvasGenerationLayerModel', () => {
sourceType: 'generated',
prompt: '金币掉落叮当声',
model: 'audio1.0',
provider: 'VectorEngine',
taskId: 'sound-task-1',
objectKey: 'generated-character-drafts/editor-audios/sfx.mp3',
assetObjectId: 'assetobj-audio-1',
@@ -535,6 +531,7 @@ describe('ImageCanvasGenerationLayerModel', () => {
assetId: 'asset-audio-bff',
}),
});
expect(layer.provider).toBeUndefined();
});
it('creates video result layers with OSS asset identity', () => {
@@ -549,7 +546,6 @@ describe('ImageCanvasGenerationLayerModel', () => {
prompt: '开场动画',
actualPrompt: '开场动画',
model: 'seedance2.0-fast',
provider: 'VectorEngine',
taskId: 'video-task-1',
priceMudPoints: 40,
durationSeconds: 5,
@@ -586,6 +582,7 @@ describe('ImageCanvasGenerationLayerModel', () => {
durationSeconds: 5,
});
expect(layer.generatedAssetSnapshot?.assetId).toBe('asset-video-bff');
expect(layer.provider).toBeUndefined();
});
it('creates character animation result layers as playable image sequences', () => {
@@ -653,11 +650,11 @@ describe('ImageCanvasGenerationLayerModel', () => {
prompt: '待机动作',
actualPrompt: '待机动作',
model: 'seedance2.0-fast',
provider: 'ark',
taskId: 'character-animation-task-1',
assetKind: 'character-animation',
durationSeconds: 4,
});
expect(layer?.generationInputs?.fields[0]?.value).toBe('待机动作');
expect(layer?.provider).toBeUndefined();
});
});
@@ -114,7 +114,6 @@ function applyGeneratedMetadata(
prompt: generated.prompt,
actualPrompt: generated.actualPrompt ?? generated.prompt,
model: generated.model,
provider: generated.provider,
taskId: generated.taskId,
objectKey: resource?.objectKey ?? generated.objectKey,
assetObjectId: resource?.assetObjectId ?? generated.assetObjectId,
@@ -333,7 +332,6 @@ export function createIconSpritesheetResultLayers({
prompt: generated.prompt,
actualPrompt: generated.actualPrompt ?? generated.prompt,
model: generated.model,
provider: generated.provider,
taskId: generated.taskId,
assetKind: 'icon-spritesheet',
sourceAssetId: spritesheetAsset?.assetId,
@@ -382,7 +380,6 @@ export function createIconSpritesheetResultLayers({
prompt: generated.prompt,
actualPrompt: generated.actualPrompt ?? generated.prompt,
model: generated.model,
provider: generated.provider,
taskId: generated.taskId,
assetKind: 'icon',
sourceAssetId: asset?.assetId,
@@ -451,7 +448,6 @@ export function createVideoResultLayer({
prompt: generated.prompt,
actualPrompt: generated.actualPrompt ?? generated.prompt,
model: generated.model,
provider: generated.provider,
taskId: generated.taskId,
objectKey: resource?.objectKey ?? generated.objectKey,
assetObjectId: resource?.assetObjectId ?? generated.assetObjectId,
@@ -513,7 +509,6 @@ export function createAudioResultLayer({
prompt: generated.prompt,
actualPrompt: generated.actualPrompt ?? generated.prompt,
model: generated.model,
provider: generated.provider,
taskId: generated.taskId,
objectKey: resource?.objectKey ?? generated.objectKey,
assetObjectId: resource?.assetObjectId ?? generated.assetObjectId,
@@ -575,7 +570,6 @@ export function createCharacterAnimationResultLayer({
prompt: generated.prompt,
actualPrompt: generated.prompt,
model: generated.model,
provider: 'ark',
taskId: generated.taskId,
assetKind: 'character-animation',
durationSeconds: generated.durationSeconds,
@@ -100,7 +100,6 @@ export const DEFAULT_ICON_DESCRIPTIONS = [
'冻结按钮',
];
export const DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR = 'auto';
export const DEFAULT_EDITOR_BGFILTER_SEG_MODEL = 'birefnet';
export const UI_DESIGN_ASSET_EXTRACTION_PROMPT =
'仅提取被红色框框选的素材并整理成spritesheet,图集背景必须使用所选单一纯色抠图背景。纯色背景必须平整无纹理、无渐变、无阴影、无地面、无环境、无道具,方便后续扣除背景;素材自身不要出现与背景色相同或相近的描边、底板、投影或反光。';
export const QUICK_EDIT_SIZE_PRESETS = [
@@ -343,7 +343,6 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
kind: 'character',
model: 'gpt-image-2',
screenColor: 'auto',
segModel: 'birefnet',
style: 'pixelArt',
aspectRatio: '2:3',
imageSize: '2K',
@@ -717,7 +716,6 @@ describe('ImageCanvasGenerationSubmissionModel', () => {
iconDescriptions: ['返回按钮\n\n设置按钮'],
model: 'gpt-image-2',
screenColor: 'auto',
segModel: 'birefnet',
style: 'pixelArt',
aspectRatio: '3:2',
imageSize: '2K',
@@ -29,7 +29,6 @@ import {
buildUiDesignGenerationInputs,
buildVideoGenerationInputs,
CHARACTER_ANIMATION_MODEL,
DEFAULT_EDITOR_BGFILTER_SEG_MODEL,
DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR,
DEFAULT_SOUND_EFFECT_DURATION_SECONDS,
DEFAULT_SOUND_EFFECT_MODEL,
@@ -343,7 +342,6 @@ export function buildImageGenerationSubmissionPlan({
].filter((src): src is string => Boolean(src));
const imageModel = normalizeEditorImageModel(dialog.imageModel);
const screenColor = DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR;
const segModel = DEFAULT_EDITOR_BGFILTER_SEG_MODEL;
return {
kind: 'image',
normalizedPrompt,
@@ -352,7 +350,6 @@ export function buildImageGenerationSubmissionPlan({
kind: 'character',
model: imageModel,
screenColor,
segModel,
style: dialog.style === 'pixelArt' ? 'pixelArt' : 'none',
aspectRatio: dialog.aspectRatio ?? '1:1',
imageSize: dialog.imageSize ?? '1K',
@@ -619,7 +616,6 @@ export function buildIconSpritesheetGenerationSubmissionPlan(
const rememberImageModel = normalizeEditorImageModel(dialog.imageModel);
const screenColor = DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR;
const segModel = DEFAULT_EDITOR_BGFILTER_SEG_MODEL;
return {
ok: true,
iconDescriptions,
@@ -637,7 +633,6 @@ export function buildIconSpritesheetGenerationSubmissionPlan(
iconDescriptions,
model: rememberImageModel,
screenColor,
segModel,
style: dialog.style === 'pixelArt' ? 'pixelArt' : 'none',
aspectRatio: dialog.aspectRatio ?? '1:1',
imageSize: dialog.imageSize ?? '1K',
@@ -2573,7 +2573,6 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
sourceImageSrc: 'generated/ui-design.png',
model: 'gemini-3.1-flash-image-preview',
screenColor: 'auto',
segModel: 'birefnet',
}),
);
});
@@ -2710,7 +2709,6 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
'generated-character-drafts/editor/generation-references/reference.png',
model: 'gemini-3.1-flash-image-preview',
screenColor: 'auto',
segModel: 'birefnet',
aspectRatio: '1:1',
imageSize: '2K',
canvasCompletion: expect.objectContaining({
@@ -2772,7 +2770,6 @@ describe('useImageCanvasGenerationSubmissionWorkflow', () => {
expect(extractEditorUiDesignAssetsMock).toHaveBeenCalledWith(
expect.objectContaining({
screenColor: 'auto',
segModel: 'birefnet',
referenceImageSrcs: ['generated/ref-style.png'],
generationInputs: expect.objectContaining({
references: expect.arrayContaining([
@@ -58,7 +58,6 @@ import {
buildQuickEditGenerationInputs,
buildUiDesignAssetExtractionGenerationInputs,
buildVideoGenerationInputs,
DEFAULT_EDITOR_BGFILTER_SEG_MODEL,
DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR,
DEFAULT_IMAGE_MODEL,
DEFAULT_VIDEO_MODEL,
@@ -1086,7 +1085,6 @@ export function useImageCanvasGenerationSubmissionWorkflow({
);
}
const screenColor = DEFAULT_EDITOR_GENERATION_BACKGROUND_COLOR;
const segModel = DEFAULT_EDITOR_BGFILTER_SEG_MODEL;
const generationInputs = buildUiDesignAssetExtractionGenerationInputs(
sourceLayer,
options.references,
@@ -1123,7 +1121,6 @@ export function useImageCanvasGenerationSubmissionWorkflow({
options.model ?? DEFAULT_IMAGE_MODEL,
),
screenColor,
segModel,
...(referenceImageSrcs.length ? { referenceImageSrcs } : {}),
projectId,
generationInputs,
@@ -837,7 +837,6 @@ describe('editorProjectClient', () => {
kind: 'character',
model: 'gpt-image-2',
screenColor: '#FFD6C2',
segModel: 'anime-seg',
style: 'pixelArt',
aspectRatio: '2:3',
imageSize: '1K',
@@ -862,7 +861,6 @@ describe('editorProjectClient', () => {
kind: 'character',
model: 'gpt-image-2',
screenColor: '#FFD6C2',
segModel: 'anime-seg',
style: 'pixelArt',
aspectRatio: '2:3',
imageSize: '1K',
@@ -1016,7 +1014,6 @@ describe('editorProjectClient', () => {
iconDescriptions: ['返回按钮'],
model: 'gpt-image-2',
screenColor: '#E6D8FF',
segModel: 'anime-seg',
style: 'none',
aspectRatio: '1:1',
imageSize: '2K',
@@ -1038,7 +1035,6 @@ describe('editorProjectClient', () => {
iconDescriptions: ['返回按钮'],
model: 'gpt-image-2',
screenColor: '#E6D8FF',
segModel: 'anime-seg',
style: 'none',
aspectRatio: '1:1',
imageSize: '2K',
@@ -1157,7 +1153,6 @@ describe('editorProjectClient', () => {
sourceImageSrc: '/generated-images/editor/ui-design.png',
model: 'gpt-image-2',
screenColor: '#7FB3FF',
segModel: 'anime-seg',
referenceImageSrcs: ['/generated-images/editor/ui-ref.png'],
projectId: 'editor-project-1',
assetFolderId: 'project',
@@ -1180,7 +1175,6 @@ describe('editorProjectClient', () => {
sourceImageSrc: '/generated-images/editor/ui-design.png',
model: 'gpt-image-2',
screenColor: '#7FB3FF',
segModel: 'anime-seg',
referenceImageSrcs: ['/generated-images/editor/ui-ref.png'],
projectId: 'editor-project-1',
generationInputs: {
@@ -197,7 +197,6 @@ export type EditorImageGenerationInput = {
| 'publication-material';
model?: string;
screenColor?: string;
segModel?: string;
style?: EditorImageGenerationStyle;
aspectRatio?: string;
imageSize?: string;
@@ -230,7 +229,6 @@ export type EditorIconSpritesheetGenerationInput = {
iconDescriptions: string[];
model?: string;
screenColor?: string;
segModel?: string;
style?: EditorImageGenerationStyle;
aspectRatio?: string;
imageSize?: string;
@@ -252,7 +250,6 @@ export type EditorUiDesignAssetExtractionInput = {
sourceImageSrc: string;
model?: string;
screenColor?: string;
segModel?: string;
referenceImageSrcs?: string[];
aspectRatio?: string;
imageSize?: string;
@@ -304,7 +301,6 @@ export type EditorImageGenerationResult = {
prompt: string;
actualPrompt?: string | null;
model: string;
provider: string;
taskId: string;
resource?: EditorProjectResourceSnapshot | null;
asset?: EditorAssetSnapshot | null;
@@ -342,7 +338,6 @@ export type EditorIconSpritesheetGenerationResult = {
prompt: string;
actualPrompt?: string | null;
model: string;
provider: string;
taskId: string;
priceMudPoints: number;
spritesheetResource?: EditorProjectResourceSnapshot | null;
@@ -465,7 +460,6 @@ export type EditorVideoGenerationResult = {
prompt: string;
actualPrompt?: string | null;
model: string;
provider: string;
taskId: string;
durationSeconds: number;
priceMudPoints?: number;
@@ -506,7 +500,6 @@ export type EditorAudioGenerationResult = {
prompt: string;
actualPrompt?: string | null;
model: string;
provider: string;
taskId: string;
priceMudPoints: number;
audioKind: 'sound-effect' | 'background-music';
@@ -923,7 +916,6 @@ export async function generateEditorImage(input: EditorImageGenerationInput) {
...(input.kind ? { kind: input.kind } : {}),
...(input.model ? { model: input.model } : {}),
...(input.screenColor ? { screenColor: input.screenColor } : {}),
...(input.segModel ? { segModel: input.segModel } : {}),
...(input.style ? { style: input.style } : {}),
...(input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}),
...(input.imageSize ? { imageSize: input.imageSize } : {}),
@@ -970,7 +962,6 @@ export async function generateEditorIconSpritesheet(
iconDescriptions: input.iconDescriptions,
model: input.model?.trim() || EDITOR_IMAGE_MODEL_NANOBANANA2,
...(input.screenColor ? { screenColor: input.screenColor } : {}),
...(input.segModel ? { segModel: input.segModel } : {}),
...(input.style ? { style: input.style } : {}),
...(input.aspectRatio ? { aspectRatio: input.aspectRatio } : {}),
...(input.imageSize ? { imageSize: input.imageSize } : {}),
@@ -1018,7 +1009,6 @@ export async function extractEditorUiDesignAssets(
sourceImageSrc: input.sourceImageSrc,
...(input.model ? { model: input.model } : {}),
...(input.screenColor ? { screenColor: input.screenColor } : {}),
...(input.segModel ? { segModel: input.segModel } : {}),
...(input.referenceImageSrcs?.length
? { referenceImageSrcs: input.referenceImageSrcs }
: {}),