From c1342910c5a0c0db501b330366c5fdb836a5cf76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Sat, 8 Aug 2026 18:16:31 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E9=BD=90=E5=9C=BA=E6=99=AF=E7=94=9F?= =?UTF-8?q?=E6=88=90=E7=9A=84=E6=8C=81=E4=B9=85=E8=AE=A1=E8=B4=B9=E8=BE=B9?= =?UTF-8?q?=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 场景 inline 路由建立稳定 operation 身份并延迟退款决策至持久化完成 更新图标图集与场景路由的生产调用计数 静态计数排除测试模块并校验场景调用顺序 --- .../crates/api-server/src/asset_billing.rs | 30 +++++++++++++++++-- .../crates/api-server/src/editor_project.rs | 13 +++++++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/server-rs/crates/api-server/src/asset_billing.rs b/server-rs/crates/api-server/src/asset_billing.rs index 975a45b91..11e7369eb 100644 --- a/server-rs/crates/api-server/src/asset_billing.rs +++ b/server-rs/crates/api-server/src/asset_billing.rs @@ -928,21 +928,45 @@ mod tests { #[test] fn every_provider_inline_route_defers_billing_until_durable_completion() { for (source, expected_calls) in [ - (include_str!("editor_project.rs"), 3), - (include_str!("editor_project_icon.rs"), 1), + (include_str!("editor_project.rs"), 4), + (include_str!("editor_project_icon.rs"), 2), (include_str!("character_animation_assets.rs"), 2), ( include_str!("vector_engine_audio_generation/generation.rs"), 2, ), ] { + let production_source = source + .rsplit_once("\n#[cfg(test)]\nmod tests") + .map(|(production_source, _)| production_source) + .unwrap_or(source); assert_eq!( - source + production_source .matches("with_editor_generation_durable_billing_boundary(") .count(), expected_calls, ); } + + let editor_source = include_str!("editor_project.rs"); + let scene_route = editor_source + .split_once("pub async fn generate_editor_scene(") + .and_then(|(_, tail)| { + tail.split_once("pub async fn generate_editor_image(") + .map(|(body, _)| body) + }) + .expect("scene route"); + let inline_operation = scene_route + .find(".with_inline_operation(") + .expect("scene inline operation"); + let billing_boundary = scene_route + .find("with_editor_generation_durable_billing_boundary(") + .expect("scene durable billing boundary"); + let provider_execution = scene_route + .rfind("generate_editor_image_for_owner(") + .expect("scene provider execution"); + assert!(inline_operation < billing_boundary); + assert!(billing_boundary < provider_execution); } #[test] diff --git a/server-rs/crates/api-server/src/editor_project.rs b/server-rs/crates/api-server/src/editor_project.rs index a2d1ce0b4..406c87e9f 100644 --- a/server-rs/crates/api-server/src/editor_project.rs +++ b/server-rs/crates/api-server/src/editor_project.rs @@ -2559,7 +2559,18 @@ pub async fn generate_editor_scene( }, )); } - generate_editor_image_for_owner(&state, &request_context, caller, image_payload).await + let caller = caller.with_inline_operation( + &request_context, + EDITOR_IMAGE_GENERATION_JOB_KIND, + &image_payload, + )?; + with_editor_generation_durable_billing_boundary(generate_editor_image_for_owner( + &state, + &request_context, + caller, + image_payload, + )) + .await } pub async fn generate_editor_image(