按后台配置扣除创作泥点

前端创作表单泥点预校验改为读取入口契约配置

拼图和抓大鹅初始生成后端扣费改为解析后台配置

汪汪声浪初始三图生成按入口总成本拆分扣费

创作工作台按钮和确认弹窗展示后台配置泥点成本

补充泥点扣费回归测试并同步文档与共享记忆
This commit is contained in:
2026-06-08 15:47:48 +08:00
parent 3ca5a460f1
commit 5ea9f0a120
21 changed files with 425 additions and 45 deletions

View File

@@ -126,6 +126,44 @@ pub fn resolve_creation_entry_route_id(path: &str) -> Option<&'static str> {
None
}
pub(crate) fn resolve_creation_entry_mud_point_cost_from_config(
config: &CreationEntryConfigResponse,
creation_type_id: &str,
fallback_cost: u64,
) -> u64 {
config
.creation_types
.iter()
.find(|item| item.id == creation_type_id)
.and_then(|item| item.unified_creation_spec.as_ref())
.map(|spec| u64::from(spec.mud_point_cost))
.filter(|cost| *cost > 0)
.unwrap_or(fallback_cost)
}
pub(crate) async fn resolve_creation_entry_mud_point_cost(
state: &AppState,
creation_type_id: &str,
fallback_cost: u64,
) -> u64 {
match state.get_creation_entry_config().await {
Ok(config) => resolve_creation_entry_mud_point_cost_from_config(
&config,
creation_type_id,
fallback_cost,
),
Err(error) => {
tracing::warn!(
creation_type_id,
fallback_cost,
error = %error,
"读取创作入口泥点成本失败,回退到代码默认值"
);
fallback_cost
}
}
}
fn creation_entry_error_response(request_context: &RequestContext, error: AppError) -> Response {
error.into_response_with_context(Some(request_context))
}
@@ -170,6 +208,7 @@ pub(crate) fn test_creation_entry_config_response() -> CreationEntryConfigRespon
#[cfg(test)]
mod tests {
use super::*;
use shared_contracts::creation_entry_config::DEFAULT_UNIFIED_CREATION_MUD_POINT_COST;
#[test]
fn resolves_new_creation_paths_to_creation_type_ids() {
@@ -258,6 +297,50 @@ mod tests {
assert_eq!(resolve_creation_entry_route_id("/healthz"), None);
}
#[test]
fn resolves_mud_point_cost_from_unified_creation_spec() {
let mut config = test_creation_entry_config_response();
let puzzle = config
.creation_types
.iter_mut()
.find(|item| item.id == "puzzle")
.expect("puzzle config should exist");
let spec = puzzle
.unified_creation_spec
.as_mut()
.expect("puzzle unified spec should exist");
spec.mud_point_cost = 8;
assert_eq!(
resolve_creation_entry_mud_point_cost_from_config(&config, "puzzle", 2),
8,
);
}
#[test]
fn resolves_mud_point_cost_with_fallback_for_legacy_config() {
let mut config = test_creation_entry_config_response();
let puzzle = config
.creation_types
.iter_mut()
.find(|item| item.id == "puzzle")
.expect("puzzle config should exist");
puzzle.unified_creation_spec = None;
assert_eq!(
resolve_creation_entry_mud_point_cost_from_config(&config, "puzzle", 2),
2,
);
assert_eq!(
resolve_creation_entry_mud_point_cost_from_config(
&config,
"missing-play",
u64::from(DEFAULT_UNIFIED_CREATION_MUD_POINT_COST),
),
u64::from(DEFAULT_UNIFIED_CREATION_MUD_POINT_COST),
);
}
#[test]
fn test_creation_entry_config_response_opens_bark_battle() {
let config = test_creation_entry_config_response();