From f82eba4c94fa0ac45aad5c2f30e6e807929057a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Thu, 24 Sep 2026 17:55:12 +0800 Subject: [PATCH] =?UTF-8?q?3D=20=E6=9F=A5=E4=BB=B7=E7=9A=84=E5=8A=A0?= =?UTF-8?q?=E4=BB=B7=E7=B4=AF=E5=8A=A0=E6=94=B9=E6=88=90=E6=BA=A2=E5=87=BA?= =?UTF-8?q?=E5=8D=B3=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - server-rs/crates/api-server/src/tripo3d/pricing.rs:endpoint 查价用 checked_add 替换 saturating_add,溢出走新的 Model3dPricingError::PriceOverflow,与加载校验的 fail-closed 口径一致,不再静默夹到 u32 上限按错价收费 - server-rs/crates/api-server/src/tripo3d/pricing.rs:补用例锁住「底价 + 加价项溢出必须报错」 --- .../crates/api-server/src/tripo3d/pricing.rs | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/server-rs/crates/api-server/src/tripo3d/pricing.rs b/server-rs/crates/api-server/src/tripo3d/pricing.rs index c8326e751..b5a497a75 100644 --- a/server-rs/crates/api-server/src/tripo3d/pricing.rs +++ b/server-rs/crates/api-server/src/tripo3d/pricing.rs @@ -208,6 +208,8 @@ pub(crate) enum Model3dPricingError { MissingAddOnPrice(Model3dAddOn), /// `texture=false` 却带上贴图类加价项:组合自相矛盾,直接报错而不是照价收费。 InconsistentTextureAddOn(Model3dAddOn), + /// 底价 + 加价项累加超出 `u32`:配置阶段已挡过一次,这里是计费点的第二道闸。 + PriceOverflow, } impl std::fmt::Display for Model3dPricingError { @@ -230,6 +232,7 @@ impl std::fmt::Display for Model3dPricingError { f, "Tripo 3D 定价组合不合法:texture=false 与贴图类 add-on {add_on:?} 冲突" ), + Self::PriceOverflow => f.write_str("Tripo 3D 定价加总超出 u32 上限"), } } } @@ -327,10 +330,14 @@ impl Model3dEndpointPricing { .add_ons .iter() .try_fold(base.for_texture(query.texture), |total, add_on| { - self.add_on_prices + let price = self + .add_on_prices .get(&add_on) - .map(|price| total.saturating_add(*price)) - .ok_or(Model3dPricingError::MissingAddOnPrice(add_on)) + .ok_or(Model3dPricingError::MissingAddOnPrice(add_on))?; + // 与加载校验口径一致:溢出就报错退款,不静默夹到 u32 上限后按错价收费。 + total + .checked_add(*price) + .ok_or(Model3dPricingError::PriceOverflow) }) } } @@ -439,6 +446,29 @@ mod tests { ); } + #[test] + fn price_overflow_fails_closed_instead_of_clamping() { + let mut pricing = sample_endpoint_pricing(10, 20); + pricing + .add_on_prices + .insert(Model3dAddOn::HdGeometry, u32::MAX); + pricing.add_on_prices.insert(Model3dAddOn::QuadMesh, 1); + + let query = text_query( + false, + Model3dAddOnSet { + hd_geometry: true, + quad_mesh: true, + ..Model3dAddOnSet::default() + }, + ); + + assert_eq!( + pricing.price(&query), + Err(Model3dPricingError::PriceOverflow) + ); + } + #[test] fn texture_add_ons_without_texture_are_rejected() { let config = sample_config();