From 48cde9624c2a7c57516647bc6005bbb0c0f92112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Tue, 22 Sep 2026 16:30:28 +0800 Subject: [PATCH] =?UTF-8?q?operation=5Fkind=20=E7=99=BD=E5=90=8D=E5=8D=95?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E4=B8=A4=20crate=20=E5=85=B1=E7=94=A8?= =?UTF-8?q?=E7=9A=84=E5=85=B1=E4=BA=AB=E5=B8=B8=E9=87=8F=20-=20=E7=99=BD?= =?UTF-8?q?=E5=90=8D=E5=8D=95=E5=B8=B8=E9=87=8F=E8=BF=81=E5=88=B0=20shared?= =?UTF-8?q?-contracts=20=E5=B9=B6=E4=BB=8E=20crate=20=E6=A0=B9=E5=AF=BC?= =?UTF-8?q?=E5=87=BA=20-=20spacetime-module=20=E7=9A=84=E5=8E=9F=E5=AD=90?= =?UTF-8?q?=E8=90=BD=E5=BA=93=E4=BA=8B=E5=8A=A1=E6=94=B9=E4=B8=BA=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E8=AF=A5=E5=B8=B8=E9=87=8F=EF=BC=8C=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E9=87=8D=E5=A4=8D=E5=AE=9A=E4=B9=89=20-=20ap?= =?UTF-8?q?i-server=20=E7=9A=84=E7=99=BB=E8=AE=B0=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=B8=8D=E5=86=8D=E8=A7=A3=E6=9E=90=E5=AF=B9=E6=96=B9=E6=BA=90?= =?UTF-8?q?=E7=A0=81=E6=96=87=E6=9C=AC=EF=BC=8C=E6=94=B9=E4=B8=BA=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E6=96=AD=E8=A8=80=E5=B8=B8=E9=87=8F=E5=86=85=E5=AE=B9?= =?UTF-8?q?=20-=20=E5=90=8E=E7=AB=AF=E6=9E=B6=E6=9E=84=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E8=A1=A5=E4=B8=8A=E7=99=BD=E5=90=8D=E5=8D=95=E5=B8=B8=E9=87=8F?= =?UTF-8?q?=E7=9A=84=E4=BD=8D=E7=BD=AE=E4=B8=8E=E5=85=B1=E7=94=A8=E5=8F=A3?= =?UTF-8?q?=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...】server-rs与SpacetimeDB数据契约-2026-05-15.md | 2 +- .../crates/api-server/src/tripo3d/job.rs | 11 ++-------- .../shared-contracts/src/editor_generation.rs | 21 +++++++++++++++++++ server-rs/crates/shared-contracts/src/lib.rs | 8 +++---- .../src/editor_project_storage.rs | 17 ++------------- 5 files changed, 30 insertions(+), 29 deletions(-) diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index 6f592da8f..79f964f8c 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -514,7 +514,7 @@ Responses 的终态载荷既是工具调用的恢复源,也是正文的恢复 - `assetKind` 是数据库、Rust DTO 和对外 JSON 的可选语义类别真相;普通静态图片必须为空。不要新增或返回并列的 `mediaType`;前端 renderer 可以保留内部派生媒体类型,但不得回写后端。 - 每次新增 `assetKind` 都必须分别完成“用户标签覆盖”和“快速编辑”两项资格评估;两者互不推导。可被用户选择不等于可快速编辑,属于图片媒体族也不等于自动进入快速编辑正向白名单。 - `assetKind` 只描述素材类别,不代表生成配方可执行或允许“改造”;新增类别必须单独完成上表的 capability 决策和验收。 -- 编辑器前端暂无入口的后端生成类别同样适用本清单:四项资格评估、原子落库 `operation_kind` 白名单和画布回填口径都必须在对应技术方案里显式登记,未登记即视为未接入,不能靠“前端还没入口”跳过。生成类结果落库前 `EDITOR_GENERATION_OPERATION_KINDS` 必须已包含该 job kind,否则整笔事务被拒;画布回填只允许走 placement-only 口径(`build_editor_canvas_resource_placement_item`)。`EDITOR_CANVAS_ASSET_KINDS` 必须与前端 `CANVAS_ASSET_KIND_TAG_OPTIONS` 同步:前端已有标签入口而白名单缺项、或白名单多出前端不认的类别,都算接入未完成。Tripo 3D 的 `model3d` 已按该口径进入两端(见 `docs/technical/【技术方案】Tripo 3D生成API集成-2026-09-21.md`),其素材库行与后台 renderer 仍未进入。 +- 编辑器前端暂无入口的后端生成类别同样适用本清单:四项资格评估、原子落库 `operation_kind` 白名单和画布回填口径都必须在对应技术方案里显式登记,未登记即视为未接入,不能靠“前端还没入口”跳过。生成类结果落库前 `EDITOR_GENERATION_OPERATION_KINDS` 必须已包含该 job kind,否则整笔事务被拒;该白名单常量位于 `shared-contracts`(`shared_contracts::EDITOR_GENERATION_OPERATION_KINDS`),入队方与 SpacetimeDB 原子落库事务共用同一份清单,新增生成类型只改这一处;画布回填只允许走 placement-only 口径(`build_editor_canvas_resource_placement_item`)。`EDITOR_CANVAS_ASSET_KINDS` 必须与前端 `CANVAS_ASSET_KIND_TAG_OPTIONS` 同步:前端已有标签入口而白名单缺项、或白名单多出前端不认的类别,都算接入未完成。Tripo 3D 的 `model3d` 已按该口径进入两端(见 `docs/technical/【技术方案】Tripo 3D生成API集成-2026-09-21.md`),其素材库行与后台 renderer 仍未进入。 - 只是新增分类或 renderer 且现有媒体字段足够时,不改 schema。只有必须跨刷新、复用、审核或公开保留的数据才新增类别专属字段。 - legacy 数据必须先通过有界、可审计、带 dry-run/hash/apply 门禁的数据库迁移收口;迁移后的 api-server、mapper、主站、后台和画布只读取正式字段,不保留运行时 fallback。 diff --git a/server-rs/crates/api-server/src/tripo3d/job.rs b/server-rs/crates/api-server/src/tripo3d/job.rs index 49182f748..9baf4892d 100644 --- a/server-rs/crates/api-server/src/tripo3d/job.rs +++ b/server-rs/crates/api-server/src/tripo3d/job.rs @@ -190,19 +190,12 @@ mod tests { } /// 原子落库在 SpacetimeDB 侧按 operation_kind 白名单拒绝未知生成结果; - /// 白名单是另一个 crate 里的字符串字面量,所以在这里按源码钉住,避免只在运行期才炸。 + /// 白名单与落库事务共用 shared-contracts 的常量,这里直接断言两个 job kind 都已登记。 #[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())), + shared_contracts::EDITOR_GENERATION_OPERATION_KINDS.contains(&kind.as_str()), "{} 必须在编辑器生成 operation_kind 白名单内", kind.as_str() ); diff --git a/server-rs/crates/shared-contracts/src/editor_generation.rs b/server-rs/crates/shared-contracts/src/editor_generation.rs index 5ffd1b23c..ca541cf2e 100644 --- a/server-rs/crates/shared-contracts/src/editor_generation.rs +++ b/server-rs/crates/shared-contracts/src/editor_generation.rs @@ -5,6 +5,27 @@ pub const EDITOR_GENERATION_ASSET_ID_PREFIX: &str = "editor-asset-"; pub const EDITOR_GENERATION_ASSET_OBJECT_ID_PREFIX: &str = "assetobj_"; pub const EDITOR_GENERATION_ASSET_BINDING_ID_PREFIX: &str = "assetbind_"; +/// 编辑器生成结果原子落库认可的 operation_kind 白名单。 +/// +/// 入队方(api-server)与原子落库事务(spacetime-module)共用这一份清单:事务会拒绝 +/// 清单外的 operation_kind,所以新增生成类型必须在这里登记;两侧测试都直接引用常量, +/// 不再解析对方的源码文本。 +pub const EDITOR_GENERATION_OPERATION_KINDS: [&str; 12] = [ + "editor_image_generation", + "editor_icon_spec_generation", + "editor_image_edit", + "editor_background_removal", + "editor_icon_spritesheet_generation", + "editor_ui_design_asset_extraction", + "editor_character_animation_generation", + "editor_video_generation", + "editor_sound_effect_generation", + "editor_background_music_generation", + // Tripo 3D 的产物没有图片画布入口,但共用同一原子落库事务;不在这里登记就会被整笔拒绝。 + "model3d_text_to_model", + "model3d_image_to_model", +]; + pub fn editor_generation_request_fingerprint( operation_kind: &str, request_payload_json: &str, diff --git a/server-rs/crates/shared-contracts/src/lib.rs b/server-rs/crates/shared-contracts/src/lib.rs index 2e66a32fd..ec0cb10db 100644 --- a/server-rs/crates/shared-contracts/src/lib.rs +++ b/server-rs/crates/shared-contracts/src/lib.rs @@ -25,10 +25,10 @@ pub mod editor_generation; pub mod error_reports; pub use editor_generation::{ EDITOR_GENERATION_ASSET_BINDING_ID_PREFIX, EDITOR_GENERATION_ASSET_ID_PREFIX, - EDITOR_GENERATION_ASSET_OBJECT_ID_PREFIX, EDITOR_GENERATION_RESOURCE_ID_PREFIX, - editor_generation_request_fingerprint, editor_generation_stable_asset_binding_id, - editor_generation_stable_asset_id, editor_generation_stable_asset_object_id, - editor_generation_stable_resource_id, + EDITOR_GENERATION_ASSET_OBJECT_ID_PREFIX, EDITOR_GENERATION_OPERATION_KINDS, + EDITOR_GENERATION_RESOURCE_ID_PREFIX, editor_generation_request_fingerprint, + editor_generation_stable_asset_binding_id, editor_generation_stable_asset_id, + editor_generation_stable_asset_object_id, editor_generation_stable_resource_id, }; pub mod external_generation; pub mod game_creation_app; 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 839583046..46e169380 100644 --- a/server-rs/crates/spacetime-module/src/editor_project_storage.rs +++ b/server-rs/crates/spacetime-module/src/editor_project_storage.rs @@ -26,21 +26,8 @@ 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; 12] = [ - "editor_image_generation", - "editor_icon_spec_generation", - "editor_image_edit", - "editor_background_removal", - "editor_icon_spritesheet_generation", - "editor_ui_design_asset_extraction", - "editor_character_animation_generation", - "editor_video_generation", - "editor_sound_effect_generation", - "editor_background_music_generation", - // Tripo 3D 的产物没有图片画布入口,但共用同一原子落库事务;不在这里登记就会被整笔拒绝。 - "model3d_text_to_model", - "model3d_image_to_model", -]; +// operation_kind 白名单由 shared-contracts 提供,入队方与这里共用同一份清单。 +use shared_contracts::EDITOR_GENERATION_OPERATION_KINDS; const EDITOR_CHARACTER_ANIMATION_ASSET_KIND: &str = "character-animation"; const EDITOR_CHARACTER_ANIMATION_OBJECT_KIND: &str = "editor_character_animation"; const EDITOR_CANVAS_LAYER_RESOURCE_METADATA_FIELDS: [&str; 12] = [