收口创作入口契约后台表单
将统一创作契约泥点消耗改为数字字段并由前端格式化展示 将后台契约编辑从 JSON 文本改为结构化卡片与弹窗表单 隐藏玩法阶段等内部标识并按玩法默认映射自动带出 更新创作入口文档、团队记忆和回归测试
This commit is contained in:
@@ -83,6 +83,12 @@ pub struct CreationEntryTypeResponse {
|
||||
pub struct UnifiedCreationSpecResponse {
|
||||
pub play_id: String,
|
||||
pub title: String,
|
||||
#[serde(
|
||||
default = "default_unified_creation_mud_point_cost",
|
||||
alias = "mudPointCostText",
|
||||
deserialize_with = "deserialize_unified_creation_mud_point_cost"
|
||||
)]
|
||||
pub mud_point_cost: u32,
|
||||
pub workspace_stage: String,
|
||||
pub generation_stage: String,
|
||||
pub result_stage: String,
|
||||
@@ -99,6 +105,11 @@ pub struct UnifiedCreationFieldResponse {
|
||||
}
|
||||
|
||||
pub const UNIFIED_CREATION_FIELD_KINDS: [&str; 4] = ["text", "select", "image", "audio"];
|
||||
pub const DEFAULT_UNIFIED_CREATION_MUD_POINT_COST: u32 = 10;
|
||||
|
||||
pub fn default_unified_creation_mud_point_cost() -> u32 {
|
||||
DEFAULT_UNIFIED_CREATION_MUD_POINT_COST
|
||||
}
|
||||
|
||||
pub fn build_phase1_unified_creation_spec(play_id: &str) -> Option<UnifiedCreationSpecResponse> {
|
||||
let (workspace_stage, generation_stage, result_stage, fields) = match play_id {
|
||||
@@ -202,6 +213,7 @@ pub fn build_phase1_unified_creation_spec(play_id: &str) -> Option<UnifiedCreati
|
||||
Some(UnifiedCreationSpecResponse {
|
||||
play_id: play_id.to_string(),
|
||||
title: default_unified_creation_title(play_id)?.to_string(),
|
||||
mud_point_cost: default_unified_creation_mud_point_cost(),
|
||||
workspace_stage: workspace_stage.to_string(),
|
||||
generation_stage: generation_stage.to_string(),
|
||||
result_stage: result_stage.to_string(),
|
||||
@@ -235,6 +247,9 @@ pub fn validate_unified_creation_spec_response(
|
||||
if spec.title.trim().is_empty() {
|
||||
return Err("统一创作契约标题不能为空".to_string());
|
||||
}
|
||||
if spec.mud_point_cost == 0 {
|
||||
return Err("统一创作契约泥点消耗数量必须大于 0".to_string());
|
||||
}
|
||||
|
||||
let workspace_stage = spec.workspace_stage.trim();
|
||||
let generation_stage = spec.generation_stage.trim();
|
||||
@@ -305,6 +320,31 @@ pub fn decode_unified_creation_spec_response(
|
||||
Ok(spec)
|
||||
}
|
||||
|
||||
fn deserialize_unified_creation_mud_point_cost<'de, D>(deserializer: D) -> Result<u32, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let value = serde_json::Value::deserialize(deserializer)?;
|
||||
match value {
|
||||
serde_json::Value::Number(number) => number
|
||||
.as_u64()
|
||||
.and_then(|raw| u32::try_from(raw).ok())
|
||||
.ok_or_else(|| serde::de::Error::custom("泥点消耗数量必须是非负整数")),
|
||||
serde_json::Value::String(text) => parse_unified_creation_mud_point_cost_text(&text)
|
||||
.ok_or_else(|| serde::de::Error::custom("泥点消耗数量必须是数字")),
|
||||
_ => Err(serde::de::Error::custom("泥点消耗数量必须是数字")),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_unified_creation_mud_point_cost_text(value: &str) -> Option<u32> {
|
||||
let digits: String = value
|
||||
.chars()
|
||||
.skip_while(|character| !character.is_ascii_digit())
|
||||
.take_while(|character| character.is_ascii_digit())
|
||||
.collect();
|
||||
digits.parse::<u32>().ok()
|
||||
}
|
||||
|
||||
pub fn resolve_unified_creation_spec_response(
|
||||
play_id: &str,
|
||||
value: Option<&str>,
|
||||
@@ -337,6 +377,7 @@ mod tests {
|
||||
fn phase1_unified_creation_specs_cover_existing_templates() {
|
||||
let puzzle = build_phase1_unified_creation_spec("puzzle").expect("puzzle spec");
|
||||
assert_eq!(puzzle.title, "拼图");
|
||||
assert_eq!(puzzle.mud_point_cost, 10);
|
||||
assert_eq!(puzzle.fields[0].id, "pictureDescription");
|
||||
assert_eq!(puzzle.fields[1].kind, "image");
|
||||
|
||||
@@ -385,6 +426,7 @@ mod tests {
|
||||
let raw = r#"{
|
||||
"playId": "puzzle",
|
||||
"title": "想做个什么玩法?",
|
||||
"mudPointCost": 12,
|
||||
"workspaceStage": "puzzle-agent-workspace",
|
||||
"generationStage": "puzzle-generating",
|
||||
"resultStage": "puzzle-result",
|
||||
@@ -402,6 +444,56 @@ mod tests {
|
||||
resolve_unified_creation_spec_response("puzzle", Some(raw)).expect("puzzle spec");
|
||||
|
||||
assert_eq!(spec.title, "想做个什么玩法?");
|
||||
assert_eq!(spec.mud_point_cost, 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unified_creation_spec_reads_legacy_mud_point_cost_text() {
|
||||
let raw = r#"{
|
||||
"playId": "puzzle",
|
||||
"title": "想做个什么玩法?",
|
||||
"mudPointCostText": "12泥点数",
|
||||
"workspaceStage": "puzzle-agent-workspace",
|
||||
"generationStage": "puzzle-generating",
|
||||
"resultStage": "puzzle-result",
|
||||
"fields": [
|
||||
{
|
||||
"id": "pictureDescription",
|
||||
"kind": "text",
|
||||
"label": "画面描述",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
}"#;
|
||||
|
||||
let spec =
|
||||
resolve_unified_creation_spec_response("puzzle", Some(raw)).expect("puzzle spec");
|
||||
|
||||
assert_eq!(spec.mud_point_cost, 12);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unified_creation_spec_uses_default_mud_point_cost_for_legacy_json() {
|
||||
let raw = r#"{
|
||||
"playId": "puzzle",
|
||||
"title": "拼图",
|
||||
"workspaceStage": "puzzle-agent-workspace",
|
||||
"generationStage": "puzzle-generating",
|
||||
"resultStage": "puzzle-result",
|
||||
"fields": [
|
||||
{
|
||||
"id": "pictureDescription",
|
||||
"kind": "text",
|
||||
"label": "画面描述",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
}"#;
|
||||
|
||||
let spec =
|
||||
resolve_unified_creation_spec_response("puzzle", Some(raw)).expect("puzzle spec");
|
||||
|
||||
assert_eq!(spec.mud_point_cost, 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user