覆盖文件缺 3D 段时按受控默认价补齐,不再把 3D 整段算成未配置

- 新增 backfill_legacy_model3d_pricing:旧覆盖文件整段没有 3D 时补上默认 JSON 的两段并告警
- 覆盖文件继续只承担种子与兜底,避免老种子文件把 3D 生成整段关掉
- 补用例覆盖覆盖文件缺 3D 段的读取
This commit is contained in:
2026-09-23 11:15:42 +08:00
parent 5e47f0c41e
commit 952bc1ccaa
@@ -6,6 +6,7 @@ use std::{
};
use serde::{Deserialize, Serialize};
use tracing::warn;
use crate::asset_billing::current_external_generation_billing_price_mud_points;
use crate::tripo3d::pricing::{
@@ -391,6 +392,7 @@ fn load_editor_generation_pricing_from_candidates(
serde_json::from_str::<EditorGenerationPricingConfig>(override_json.as_str())
.map_err(EditorGenerationPricingError::Json)?;
backfill_legacy_sfx_pricing(&mut override_config, &config, source.as_ref())?;
backfill_legacy_model3d_pricing(&mut override_config, &config, source.as_ref());
override_config.validate().map_err(|error| match error {
EditorGenerationPricingError::Invalid(message) => {
EditorGenerationPricingError::Invalid(format!("{source}: {message}"))
@@ -429,6 +431,25 @@ fn backfill_legacy_sfx_pricing(
Ok(())
}
/// 旧覆盖文件可能整段没有 3D 定价(3D 是后加的段):缺失时用当前受控默认价补齐。
///
/// 覆盖文件只承担种子与兜底,不代表「3D 未配置」;这里补齐后,表内缺 3D 段的老行也能
/// 按同一份默认价归一化,不会因为文件里没有这一段就把 3D 生成整段关掉。
fn backfill_legacy_model3d_pricing(
config: &mut EditorGenerationPricingConfig,
fallback: &EditorGenerationPricingConfig,
source: &str,
) {
if config.model3d.is_some() || fallback.model3d.is_none() {
return;
}
warn!(
source,
"覆盖文件缺少 3D 定价段,已按受控默认价补齐;确认 3D 价格后再从后台保存一次"
);
config.model3d = fallback.model3d.clone();
}
pub(crate) fn parse_editor_generation_pricing_json(
json: &str,
source: &str,
@@ -1136,6 +1157,36 @@ mod tests {
std::fs::remove_dir_all(&temp_dir).expect("temp dir should remove");
}
#[test]
fn editor_generation_pricing_legacy_override_backfills_missing_model3d_section() {
let temp_dir = unique_temp_dir("genarrative-pricing-legacy-model3d-test");
std::fs::create_dir_all(&temp_dir).expect("temp dir should create");
let override_path = temp_dir.join("editor-generation-pricing.override.json");
let mut legacy_config = default_runtime_pricing();
legacy_config.model3d = None;
legacy_config
.models
.get_mut(EDITOR_SOUND_EFFECT_MODEL_VIDU)
.expect("legacy sound effect pricing should exist")
.price = Some(17);
std::fs::write(
&override_path,
serde_json::to_string(&legacy_config).expect("legacy config should serialize"),
)
.expect("legacy override should write");
let loaded = load_editor_generation_pricing_from_paths(Some(&override_path))
.expect("legacy override should backfill the 3D section");
assert_eq!(
loaded.model3d,
default_runtime_pricing().model3d,
"覆盖文件缺 3D 段时应补上受控默认价"
);
assert_eq!(loaded.sound_effect_model_mud_points(Some("audio1.0")), 17);
std::fs::remove_dir_all(&temp_dir).expect("temp dir should remove");
}
#[test]
fn editor_generation_pricing_legacy_override_still_rejects_other_missing_models() {
let temp_dir = unique_temp_dir("genarrative-pricing-legacy-required-model-test");