fix: stabilize rpg creation entry and opening cg

This commit is contained in:
kdletters
2026-05-21 17:21:38 +08:00
parent 0eed942ce5
commit 41075e41a2
26 changed files with 866 additions and 47 deletions

View File

@@ -599,6 +599,37 @@ pub fn canonicalize_custom_world_profile_before_save(profile: &mut Value) -> boo
true
}
pub fn resolve_custom_world_publish_setting_text(
payload: &Map<String, Value>,
draft_profile: &Map<String, Value>,
seed_text: &str,
) -> String {
// 中文注释:发布按钮的前端契约只保证提交动作名;正式 settingText 必须从草稿真相补齐,
// 避免旧会话 seed_text 为空时通过 publish gate却在最终 compile/publish 阶段失败。
read_nested_text_field(payload, &["settingText"])
.or_else(|| {
read_nested_text_field(
draft_profile,
&[
"settingText",
"creatorIntent.rawSettingText",
"creatorIntent.worldHook",
"worldHook",
"anchorContent.worldPromise",
"anchorContent.worldPromise.hook",
"summary",
"name",
"title",
],
)
})
.or_else(|| {
let seed = seed_text.trim();
(!seed.is_empty()).then(|| seed.to_string())
})
.unwrap_or_default()
}
pub fn empty_agent_anchor_content_json() -> String {
r#"{"worldPromise":null,"playerFantasy":null,"themeBoundary":null,"playerEntryPoint":null,"coreConflict":null,"keyRelationships":null,"hiddenLines":null,"iconicElements":null}"#.to_string()
}
@@ -804,6 +835,32 @@ fn read_text(object: &Map<String, Value>, key: &str) -> Option<String> {
.map(ToOwned::to_owned)
}
fn read_nested_text_field(object: &Map<String, Value>, keys: &[&str]) -> Option<String> {
for key in keys {
let mut current = Value::Object(object.clone());
let mut found = true;
for segment in key.split('.') {
if let Some(next) = current.get(segment) {
current = next.clone();
} else {
found = false;
break;
}
}
if found {
if let Some(value) = current
.as_str()
.map(str::trim)
.filter(|value| !value.is_empty())
{
return Some(value.to_string());
}
}
}
None
}
fn read_string_list(object: &Map<String, Value>, key: &str) -> Vec<String> {
object
.get(key)
@@ -955,3 +1012,56 @@ fn build_compiled_profile_payload_json(
serde_json::to_string(&Value::Object(payload))
.map_err(|_| CustomWorldFieldError::InvalidDraftProfileJson)
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn publish_setting_text_falls_back_to_draft_profile_when_seed_is_empty() {
let payload = Map::new();
let draft_profile = json!({
"settingText": "海雾会吞掉记错航线的人。",
"worldHook": "在失真的海图上追查一场被篡改的沉船事故。",
"summary": "守灯人与群岛议会围绕沉船旧案对峙。"
})
.as_object()
.cloned()
.expect("draft profile should be object");
let setting_text =
resolve_custom_world_publish_setting_text(&payload, &draft_profile, "");
assert_eq!(setting_text, "海雾会吞掉记错航线的人。");
}
#[test]
fn publish_setting_text_prefers_payload_then_draft_then_seed() {
let mut payload = Map::new();
payload.insert(
"settingText".to_string(),
Value::String("发布载荷设定".to_string()),
);
let draft_profile = json!({
"worldHook": "草稿世界一句话",
"summary": "草稿摘要"
})
.as_object()
.cloned()
.expect("draft profile should be object");
assert_eq!(
resolve_custom_world_publish_setting_text(&payload, &draft_profile, "用户原始设定"),
"发布载荷设定"
);
assert_eq!(
resolve_custom_world_publish_setting_text(&Map::new(), &draft_profile, "用户原始设定"),
"草稿世界一句话"
);
assert_eq!(
resolve_custom_world_publish_setting_text(&Map::new(), &Map::new(), "用户原始设定"),
"用户原始设定"
);
}
}