diff --git a/server-rs/crates/api-server/src/tripo3d/job.rs b/server-rs/crates/api-server/src/tripo3d/job.rs index 962d399ee..39e48b6f4 100644 --- a/server-rs/crates/api-server/src/tripo3d/job.rs +++ b/server-rs/crates/api-server/src/tripo3d/job.rs @@ -180,4 +180,24 @@ mod tests { assert!(parse_model3d_job_request(MODEL3D_TEXT_TO_MODEL_JOB_KIND, &payload).is_err()); assert!(parse_model3d_job_request("unknown_job_kind", &payload).is_err()); } + + /// 原子落库在 SpacetimeDB 侧按 operation_kind 白名单拒绝未知生成结果; + /// 白名单是另一个 crate 里的字符串字面量,所以在这里按源码钉住,避免只在运行期才炸。 + #[test] + fn model3d_job_kinds_are_registered_in_editor_generation_whitelist() { + let whitelist = include_str!("../../../spacetime-module/src/editor_project_storage.rs"); + let declared = whitelist + .split_once("const EDITOR_GENERATION_OPERATION_KINDS") + .and_then(|(_, rest)| rest.split_once("];")) + .map(|(list, _)| list) + .expect("应能在 SpacetimeDB storage 源码里定位 operation_kind 白名单"); + + for kind in Model3dJobKind::ALL { + assert!( + declared.contains(&format!("\"{}\"", kind.as_str())), + "{} 必须在编辑器生成 operation_kind 白名单内", + kind.as_str() + ); + } + } } diff --git a/server-rs/crates/spacetime-module/src/editor_project_storage.rs b/server-rs/crates/spacetime-module/src/editor_project_storage.rs index 1f506eed4..96a736bf9 100644 --- a/server-rs/crates/spacetime-module/src/editor_project_storage.rs +++ b/server-rs/crates/spacetime-module/src/editor_project_storage.rs @@ -26,7 +26,7 @@ const EDITOR_IMAGE_ASSET_KIND_CLEANUP_CANVAS_MAX_BATCH_SIZE: u32 = 5; const EDITOR_LEGACY_IMAGE_ASSET_KIND: &str = "image"; // 透明图集最多产出 256 个切片,另有 provider 原图和透明整图两个正式 item。 const EDITOR_GENERATION_RESULT_MAX_ITEMS: usize = 258; -const EDITOR_GENERATION_OPERATION_KINDS: [&str; 10] = [ +const EDITOR_GENERATION_OPERATION_KINDS: [&str; 12] = [ "editor_image_generation", "editor_icon_spec_generation", "editor_image_edit", @@ -37,6 +37,9 @@ const EDITOR_GENERATION_OPERATION_KINDS: [&str; 10] = [ "editor_video_generation", "editor_sound_effect_generation", "editor_background_music_generation", + // Tripo 3D 的产物没有图片画布入口,但共用同一原子落库事务;不在这里登记就会被整笔拒绝。 + "model3d_text_to_model", + "model3d_image_to_model", ]; const EDITOR_CHARACTER_ANIMATION_ASSET_KIND: &str = "character-animation"; const EDITOR_CHARACTER_ANIMATION_OBJECT_KIND: &str = "editor_character_animation";