From ca0b16cf367448848815ddd4c7823f88991ad02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 20 Aug 2026 10:47:49 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BB=84=E4=BB=B6=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E9=AA=8C=E8=AF=81=E9=80=BB=E8=BE=91=E4=BB=A5=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=B7=B2=E6=9C=89=E7=8B=AC=E7=AB=8B=E7=B4=A0=E6=9D=90?= =?UTF-8?q?=E9=AA=8C=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 调整验证逻辑以支持引用项目中已存在的素材,同时拒绝不存在的素材 - 补充相关测试,验证未知素材与跨批次素材引用行为 --- .../src/ui_editor/commands/binding.rs | 42 ++++++++++++++----- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/binding.rs b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/binding.rs index 971a02798..2752078d8 100644 --- a/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/binding.rs +++ b/apps/ai-game-creator-shell/src-tauri/src/ui_editor/commands/binding.rs @@ -107,7 +107,7 @@ fn collect_editable_nodes<'a>(node: &'a Node, output: &mut Vec, editable_ids: &HashSet, - batch_sprite_ids: &HashSet, + known_sprite_ids: &HashSet, ) -> Result { let mut changed_ids = HashSet::new(); let mut materialized = Vec::with_capacity(changes.len()); @@ -126,9 +126,9 @@ fn validate_and_materialize( if image .target_graphic .as_ref() - .is_some_and(|id| !batch_sprite_ids.contains(id)) + .is_some_and(|id| !known_sprite_ids.contains(id)) { - return Err("组件绑定引用了不属于当前批次的独立素材".to_string()); + return Err("组件绑定引用了不存在的独立素材".to_string()); } } } @@ -258,7 +258,8 @@ pub(crate) async fn bind_components_impl( .ok_or_else(|| "LLM 未返回 bind_ui_components 工具调用".to_string())?; let parsed = serde_json::from_str::(&call.arguments) .map_err(|error| format!("组件绑定工具参数无效:{error}"))?; - let result = validate_and_materialize(parsed.changes, &editable_ids, &batch_sprite_ids)?; + let known_sprite_ids = state.sprite_assets.keys().cloned().collect::>(); + let result = validate_and_materialize(parsed.changes, &editable_ids, &known_sprite_ids)?; eprintln!( "ui_binding.completed ui_images={} sprites={} editable_nodes={} changes={}", state.ui_design_images.len(), @@ -280,21 +281,27 @@ mod tests { } #[test] - fn materialization_rejects_unapproved_node_and_foreign_sprite() { + fn materialization_rejects_unapproved_node_and_unknown_sprite() { let editable = HashSet::from([id("editable")]); - let sprites = HashSet::from([SpriteAssetId::new("sprite").expect("valid sprite")]); + let known = HashSet::from([ + SpriteAssetId::new("sprite").expect("valid sprite"), + SpriteAssetId::new("other-batch-sprite").expect("valid sprite"), + ]); let unapproved = BindingChangeDraft { node_id: id("other"), components: Vec::new(), components_status: DraftStatus::NoProblem, }; - assert!(validate_and_materialize(vec![unapproved], &editable, &sprites).is_err()); + assert!(validate_and_materialize(vec![unapproved], &editable, &known).is_err()); - let foreign = BindingChangeDraft { + // References to sprites from another batch are allowed once they exist in the project. + let other_batch = BindingChangeDraft { node_id: id("editable"), components: vec![Component::Image( crate::ui_editor::component::image::ImageComponent { - target_graphic: Some(SpriteAssetId::new("foreign").expect("valid sprite")), + target_graphic: Some( + SpriteAssetId::new("other-batch-sprite").expect("valid sprite"), + ), image_type: crate::ui_editor::component::image::ImageType::Simple { preserve_aspect: false, }, @@ -302,7 +309,22 @@ mod tests { )], components_status: DraftStatus::NoProblem, }; - assert!(validate_and_materialize(vec![foreign], &editable, &sprites).is_err()); + assert!(validate_and_materialize(vec![other_batch], &editable, &known).is_ok()); + + // References to sprites that do not exist in the project at all are still rejected. + let unknown = BindingChangeDraft { + node_id: id("editable"), + components: vec![Component::Image( + crate::ui_editor::component::image::ImageComponent { + target_graphic: Some(SpriteAssetId::new("unknown").expect("valid sprite")), + image_type: crate::ui_editor::component::image::ImageType::Simple { + preserve_aspect: false, + }, + }, + )], + components_status: DraftStatus::NoProblem, + }; + assert!(validate_and_materialize(vec![unknown], &editable, &known).is_err()); } #[test]