公开读模型 3D 段保持迁移前的旧形状

- 新增 Model3dPricingPublicView,把内部两段结构投影回 basePrices 端点判别 + addOnPrices 单表
- 公开路由改用投影视图,画布 3D 入口与前端类型不需要随本次迁移改动
- 投影取文生 3D 段的加价项,两段不一致时打告警提示前端升级前不要差异化
- 公开路由用例补断言钉住 3D 段旧形状
This commit is contained in:
2026-09-23 11:04:50 +08:00
parent 9f4a71d2e3
commit ebd904e193
4 changed files with 81 additions and 2 deletions
+14
View File
@@ -5930,6 +5930,20 @@ mod tests {
payload["models"]["seedance2.0"]["prices"]["720p"],
Value::Number(24.into())
);
// 公开读模型的 3D 段保持迁移前的旧形状:前端画布按端点读底价、按单表读加价项。
assert_eq!(
payload["model3d"]["basePrices"]["text-to-model"]["v3.1-20260211"]["noTexture"],
Value::Number(8.into())
);
assert_eq!(
payload["model3d"]["basePrices"]["image-to-model"]["v3.1-20260211"]["texture"],
Value::Number(24.into())
);
assert_eq!(
payload["model3d"]["addOnPrices"]["quadMesh"],
Value::Number(4.into())
);
assert!(payload["model3d"]["textToModelPricing"].is_null());
}
#[tokio::test]
@@ -8,7 +8,9 @@ use std::{
use serde::{Deserialize, Serialize};
use crate::asset_billing::current_external_generation_billing_price_mud_points;
use crate::tripo3d::pricing::{Model3dPricingConfig, Model3dPricingError, Model3dPricingQuery};
use crate::tripo3d::pricing::{
Model3dPricingConfig, Model3dPricingError, Model3dPricingPublicView, Model3dPricingQuery,
};
/// 图片画布编辑器生成类能力的泥点配置。
///
@@ -83,6 +85,16 @@ pub(crate) struct EditorGenerationPricingConfig {
pub model3d: Option<Model3dPricingConfig>,
}
/// 主站公开读模型的响应体:`models` 原样透出,3D 段投影回迁移前的旧形状
/// [`Model3dPricingPublicView`]),让画布 3D 入口不需要随本次迁移改动。
#[derive(Clone, Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct EditorGenerationPricingPublicResponse {
pub models: BTreeMap<String, EditorGenerationModelPricing>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub model3d: Option<Model3dPricingPublicView>,
}
#[derive(Clone, Debug)]
pub(crate) struct EditorGenerationPricingStore {
current: Arc<RwLock<EditorGenerationPricingConfig>>,
@@ -107,6 +119,15 @@ impl From<Model3dPricingError> for EditorGenerationPricingError {
}
impl EditorGenerationPricingConfig {
/// 公开读模型视图:字段名与嵌套形状保持迁移前一致,见
/// [`EditorGenerationPricingPublicResponse`]。
pub(crate) fn into_public_response(self) -> EditorGenerationPricingPublicResponse {
EditorGenerationPricingPublicResponse {
models: self.models,
model3d: self.model3d.map(Model3dPricingConfig::into_public_view),
}
}
/// Tripo 3D 提交查价:缺段或缺键都直接报错,不回退默认值。
///
/// 错误与同文件其它查询统一成 [`EditorGenerationPricingError`]3D 结构化原因收在
@@ -2025,7 +2025,11 @@ pub async fn get_editor_generation_pricing(
"message": error.to_string(),
}))
})?;
Ok(json_success_body(Some(&request_context), pricing))
// 公开读模型不改形状:内部两段结构在这里投影回旧的 `basePrices` / `addOnPrices`。
Ok(json_success_body(
Some(&request_context),
pricing.into_public_response(),
))
}
pub async fn list_editor_projects(
@@ -11,6 +11,7 @@ use shared_contracts::model3d::common::{
Model3dGeometryQuality, Model3dModelVersion, Model3dTextureQuality,
};
use shared_contracts::model3d::wire_str_lossy;
use tracing::warn;
/// 生成端点。批量价键与请求入口一一对应,不做 provider 侧的变形。
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
@@ -90,6 +91,19 @@ pub(crate) struct Model3dPricingConfig {
pub image_to_model_pricing: Model3dEndpointPricing,
}
/// 主站公开读模型里的 3D 段。
///
/// 形状保持迁移前的 `basePrices`(端点判别)× `addOnPrices` 单表:画布 3D 入口直接读它、
/// 没有内置兜底,形状一变入口就会判定「定价缺失」。两段内部结构到这份视图的投影是有损的
/// ——加价项只有一张表,取文生 3D 段,两段不一致时打告警。前端升级到按端点读取加价项之前,
/// 不要在两段之间差异化加价项。
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Model3dPricingPublicView {
pub base_prices: BTreeMap<Model3dEndpoint, BTreeMap<Model3dModelVersion, Model3dBasePrice>>,
pub add_on_prices: BTreeMap<Model3dAddOn, u32>,
}
impl Model3dPricingConfig {
pub(crate) fn endpoint_pricing(&self, endpoint: Model3dEndpoint) -> &Model3dEndpointPricing {
match endpoint {
@@ -97,6 +111,32 @@ impl Model3dPricingConfig {
Model3dEndpoint::ImageToModel => &self.image_to_model_pricing,
}
}
/// 投影成公开读模型的旧形状。
pub(crate) fn into_public_view(self) -> Model3dPricingPublicView {
let Model3dPricingConfig {
text_to_model_pricing,
image_to_model_pricing,
} = self;
if text_to_model_pricing.add_on_prices != image_to_model_pricing.add_on_prices {
warn!(
"文生 3D 与图生 3D 的加价项不一致,公开读模型只能给出文生 3D 的加价项:前端按端点读取前不要差异化加价项"
);
}
let mut base_prices = BTreeMap::new();
base_prices.insert(
Model3dEndpoint::TextToModel,
text_to_model_pricing.version_prices,
);
base_prices.insert(
Model3dEndpoint::ImageToModel,
image_to_model_pricing.version_prices,
);
Model3dPricingPublicView {
base_prices,
add_on_prices: text_to_model_pricing.add_on_prices,
}
}
}
/// 一次提交的定价输入:只携带判定价格需要的字段,全部来自已校验的请求。