@@ -94,19 +94,19 @@
|
||||
},
|
||||
{
|
||||
"templateId": "big_fish",
|
||||
"displayName": "大鱼吃小鱼共创",
|
||||
"displayName": "大鱼吃小鱼",
|
||||
"creationGoal": "收束成可直接编译为竖屏大鱼吃小鱼玩法草稿的成长、生态、节奏方案。",
|
||||
"anchorQuestions": [
|
||||
{
|
||||
"key": "gameplayPromise",
|
||||
"label": "玩法承诺",
|
||||
"question": "这版大鱼吃小鱼最核心的吞噬成长爽点是什么?",
|
||||
"label": "玩法爽点",
|
||||
"question": "核心的吞噬成长爽点是什么?",
|
||||
"requiredEffect": "明确玩家为什么要持续吞噬、升级和冒险。"
|
||||
},
|
||||
{
|
||||
"key": "ecologyVisualTheme",
|
||||
"label": "生态视觉主题",
|
||||
"question": "鱼群、场景和敌我生态的视觉主题是什么?",
|
||||
"label": "视觉主题",
|
||||
"question": "场景和形象的视觉主题是什么?",
|
||||
"requiredEffect": "提供后续角色图、动作图和背景图的一致视觉方向。"
|
||||
},
|
||||
{
|
||||
|
||||
@@ -80,12 +80,22 @@ impl SpacetimeClient {
|
||||
procedure_input: BigFishWorksListInput,
|
||||
) -> Result<Vec<BigFishWorkSummaryRecord>, SpacetimeClientError> {
|
||||
self.call_after_connect(move |connection, sender| {
|
||||
let fallback_owner_user_id = if procedure_input.published_only {
|
||||
None
|
||||
} else {
|
||||
Some(procedure_input.owner_user_id.clone())
|
||||
};
|
||||
connection
|
||||
.procedures()
|
||||
.list_big_fish_works_then(procedure_input, move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
|
||||
.and_then(map_big_fish_works_procedure_result);
|
||||
.and_then(|result| {
|
||||
map_big_fish_works_procedure_result(
|
||||
result,
|
||||
fallback_owner_user_id.as_deref(),
|
||||
)
|
||||
});
|
||||
send_once(&sender, mapped);
|
||||
});
|
||||
})
|
||||
@@ -103,12 +113,18 @@ impl SpacetimeClient {
|
||||
};
|
||||
|
||||
self.call_after_connect(move |connection, sender| {
|
||||
let fallback_owner_user_id = Some(procedure_input.owner_user_id.clone());
|
||||
connection
|
||||
.procedures()
|
||||
.delete_big_fish_work_then(procedure_input, move |_, result| {
|
||||
let mapped = result
|
||||
.map_err(|error| SpacetimeClientError::Procedure(error.to_string()))
|
||||
.and_then(map_big_fish_works_procedure_result);
|
||||
.and_then(|result| {
|
||||
map_big_fish_works_procedure_result(
|
||||
result,
|
||||
fallback_owner_user_id.as_deref(),
|
||||
)
|
||||
});
|
||||
send_once(&sender, mapped);
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1278,6 +1278,7 @@ pub(crate) fn map_big_fish_session_procedure_result(
|
||||
|
||||
pub(crate) fn map_big_fish_works_procedure_result(
|
||||
result: BigFishWorksProcedureResult,
|
||||
fallback_owner_user_id: Option<&str>,
|
||||
) -> Result<Vec<BigFishWorkSummaryRecord>, SpacetimeClientError> {
|
||||
if !result.ok {
|
||||
return Err(SpacetimeClientError::Procedure(
|
||||
@@ -1292,9 +1293,15 @@ pub(crate) fn map_big_fish_works_procedure_result(
|
||||
"SpacetimeDB procedure 未返回 big fish works 快照".to_string(),
|
||||
)
|
||||
})?;
|
||||
serde_json::from_str::<Vec<BigFishWorkSummaryRecord>>(&items_json).map_err(|error| {
|
||||
SpacetimeClientError::Runtime(format!("big fish works items_json 非法: {error}"))
|
||||
})
|
||||
let items = serde_json::from_str::<Vec<CompatibleBigFishWorkSummaryRecord>>(&items_json)
|
||||
.map_err(|error| {
|
||||
SpacetimeClientError::Runtime(format!("big fish works items_json 非法: {error}"))
|
||||
})?;
|
||||
|
||||
Ok(items
|
||||
.into_iter()
|
||||
.map(|item| item.into_record(fallback_owner_user_id))
|
||||
.collect())
|
||||
}
|
||||
|
||||
pub(crate) fn map_story_session_procedure_result(
|
||||
@@ -4601,6 +4608,120 @@ pub struct BigFishWorkSummaryRecord {
|
||||
pub background_ready: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize)]
|
||||
struct CompatibleBigFishWorkSummaryRecord {
|
||||
work_id: String,
|
||||
source_session_id: String,
|
||||
#[serde(default)]
|
||||
owner_user_id: Option<String>,
|
||||
title: String,
|
||||
subtitle: String,
|
||||
summary: String,
|
||||
cover_image_src: Option<String>,
|
||||
status: String,
|
||||
updated_at_micros: i64,
|
||||
publish_ready: bool,
|
||||
level_count: u32,
|
||||
level_main_image_ready_count: u32,
|
||||
level_motion_ready_count: u32,
|
||||
background_ready: bool,
|
||||
}
|
||||
|
||||
impl CompatibleBigFishWorkSummaryRecord {
|
||||
fn into_record(self, fallback_owner_user_id: Option<&str>) -> BigFishWorkSummaryRecord {
|
||||
BigFishWorkSummaryRecord {
|
||||
work_id: self.work_id,
|
||||
source_session_id: self.source_session_id,
|
||||
// 中文注释:兼容旧 works JSON 没有 owner_user_id 的历史数据,避免一次字段升级把整个作品列表打崩。
|
||||
owner_user_id: self.owner_user_id.unwrap_or_else(|| {
|
||||
fallback_owner_user_id
|
||||
.map(str::to_string)
|
||||
.unwrap_or_default()
|
||||
}),
|
||||
title: self.title,
|
||||
subtitle: self.subtitle,
|
||||
summary: self.summary,
|
||||
cover_image_src: self.cover_image_src,
|
||||
status: self.status,
|
||||
updated_at_micros: self.updated_at_micros,
|
||||
publish_ready: self.publish_ready,
|
||||
level_count: self.level_count,
|
||||
level_main_image_ready_count: self.level_main_image_ready_count,
|
||||
level_motion_ready_count: self.level_motion_ready_count,
|
||||
background_ready: self.background_ready,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn big_fish_works_mapper_backfills_missing_owner_user_id_for_private_lists() {
|
||||
let result = BigFishWorksProcedureResult {
|
||||
ok: true,
|
||||
items_json: Some(
|
||||
r#"[{
|
||||
"work_id":"big-fish-work-session-1",
|
||||
"source_session_id":"session-1",
|
||||
"title":"深海草稿",
|
||||
"subtitle":"副标题",
|
||||
"summary":"摘要",
|
||||
"cover_image_src":null,
|
||||
"status":"draft",
|
||||
"updated_at_micros":123,
|
||||
"publish_ready":false,
|
||||
"level_count":8,
|
||||
"level_main_image_ready_count":0,
|
||||
"level_motion_ready_count":0,
|
||||
"background_ready":false
|
||||
}]"#
|
||||
.to_string(),
|
||||
),
|
||||
error_message: None,
|
||||
};
|
||||
|
||||
let items = map_big_fish_works_procedure_result(result, Some("user-1"))
|
||||
.expect("旧 works JSON 应能被兼容解析");
|
||||
|
||||
assert_eq!(items.len(), 1);
|
||||
assert_eq!(items[0].owner_user_id, "user-1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn big_fish_works_mapper_keeps_empty_owner_when_gallery_legacy_json_lacks_field() {
|
||||
let result = BigFishWorksProcedureResult {
|
||||
ok: true,
|
||||
items_json: Some(
|
||||
r#"[{
|
||||
"work_id":"big-fish-work-session-2",
|
||||
"source_session_id":"session-2",
|
||||
"title":"公开作品",
|
||||
"subtitle":"副标题",
|
||||
"summary":"摘要",
|
||||
"cover_image_src":null,
|
||||
"status":"published",
|
||||
"updated_at_micros":456,
|
||||
"publish_ready":true,
|
||||
"level_count":8,
|
||||
"level_main_image_ready_count":8,
|
||||
"level_motion_ready_count":16,
|
||||
"background_ready":true
|
||||
}]"#
|
||||
.to_string(),
|
||||
),
|
||||
error_message: None,
|
||||
};
|
||||
|
||||
let items = map_big_fish_works_procedure_result(result, None)
|
||||
.expect("公开 works 旧 JSON 也不应因缺字段报错");
|
||||
|
||||
assert_eq!(items.len(), 1);
|
||||
assert!(items[0].owner_user_id.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ResolveNpcBattleInteractionInput {
|
||||
pub npc_interaction: DomainResolveNpcInteractionInput,
|
||||
|
||||
Reference in New Issue
Block a user