diff --git a/server-rs/crates/api-server/src/editor_project.rs b/server-rs/crates/api-server/src/editor_project.rs index e576c9518..e4cf6e788 100644 --- a/server-rs/crates/api-server/src/editor_project.rs +++ b/server-rs/crates/api-server/src/editor_project.rs @@ -1808,18 +1808,19 @@ pub(crate) async fn validate_editor_image_generation_parameters_for_owner( payload.image_size.as_deref(), ); let reference_limit = if matches!(normalized_kind, Some("quick-edit")) { - 9 + EDITOR_QUICK_EDIT_REFERENCE_LIMIT + .min(editor_provider_reference_limit(generation_options.model)) } else { - 5 + EDITOR_IMAGE_GENERATION_REFERENCE_LIMIT }; - for source in payload - .reference_image_srcs - .as_deref() - .unwrap_or_default() - .iter() - .map(|source| source.trim()) - .filter(|source| !source.is_empty()) - .take(reference_limit) + ensure_editor_reference_image_source_limit( + payload.reference_image_srcs.as_deref(), + reference_limit, + "editor-image-generation", + "referenceImageSrcs", + "生成参考图", + )?; + for source in normalize_editor_reference_image_sources(payload.reference_image_srcs.as_deref()) { parse_editor_reference_image(state, caller.owner_user_id.as_str(), source).await?; } @@ -14784,18 +14785,25 @@ mod tests { fn image_generation_preflight_checks_references_provider_and_pricing() { let source = include_str!("editor_project.rs"); - assert_function_contains( + assert_function_contains_in_order( source, "pub(crate) async fn validate_editor_image_generation_parameters_for_owner", "pub(crate) async fn generate_editor_image_for_owner", &[ "ensure_editor_reference_image_sources_are_stable(", + "ensure_editor_reference_image_source_limit(", "parse_editor_reference_image(", "require_openai_image_settings(", "build_openai_image_http_client(", ".editor_generation_pricing()", ], ); + assert_function_not_contains( + source, + "pub(crate) async fn validate_editor_image_generation_parameters_for_owner", + "pub(crate) async fn generate_editor_image_for_owner", + &[".take(reference_limit)"], + ); } #[test] diff --git a/src/services/image-editor/editorProjectClient.test.ts b/src/services/image-editor/editorProjectClient.test.ts index e0cbe35c6..388646173 100644 --- a/src/services/image-editor/editorProjectClient.test.ts +++ b/src/services/image-editor/editorProjectClient.test.ts @@ -1452,6 +1452,20 @@ describe('editorProjectClient', () => { expect(requestJsonMock).not.toHaveBeenCalled(); }); + it('rejects excess icon spec references before dispatch', async () => { + await expect( + generateEditorIconSpec({ + playSetting: '回合制占点', + artStyle: '低多边形', + referenceImageSrcs: Array.from( + { length: 6 }, + (_, index) => `resource-reference-${index + 1}`, + ), + }), + ).rejects.toThrow('图标规范参考图最多允许 5 张,当前选择 6 张'); + expect(requestJsonMock).not.toHaveBeenCalled(); + }); + it('submits icon spec business fields without a client prompt or image kind', async () => { requestJsonMock.mockResolvedValueOnce({ imageSrc: 'data:image/png;base64,spec', diff --git a/src/services/image-editor/editorProjectClient.ts b/src/services/image-editor/editorProjectClient.ts index cbdb2067e..e2969149a 100644 --- a/src/services/image-editor/editorProjectClient.ts +++ b/src/services/image-editor/editorProjectClient.ts @@ -1106,6 +1106,11 @@ export async function generateEditorIconSpec( '美术风格', ); assertStableEditorMediaReferences(input.referenceImageSrcs, '图标规范参考图'); + assertEditorReferenceLimit( + input.referenceImageSrcs, + EDITOR_IMAGE_REFERENCE_LIMIT, + '图标规范参考图', + ); return requestJson( EDITOR_ICON_SPEC_GENERATION_API, jsonRequest('POST', {