Merge remote-tracking branch 'origin/master' into feat/recommend-runtime-guest

# Conflicts:
#	docs/【玩法创作】平台入口与玩法链路-2026-05-15.md
This commit is contained in:
kdletters
2026-05-25 14:12:39 +08:00
470 changed files with 8570 additions and 3058 deletions

View File

@@ -450,6 +450,10 @@ mod tests {
cover_image_src: None,
ui_background_image_src: None,
ui_background_image_object_key: None,
level_background_image_src: None,
level_background_image_object_key: None,
ui_spritesheet_image_src: None,
ui_spritesheet_image_object_key: None,
background_music: None,
board: PuzzleBoardSnapshot {
rows: 3,

View File

@@ -11,6 +11,9 @@ impl From<module_runtime::CreationEntryTypeAdminUpsertInput> for CreationEntryTy
visible: input.visible,
open: input.open,
sort_order: input.sort_order,
category_id: input.category_id,
category_label: input.category_label,
category_sort_order: input.category_sort_order,
}
}
}
@@ -151,6 +154,29 @@ pub(crate) fn build_creation_entry_config_record_from_rows(
title: header.modal_title,
description: header.modal_description,
},
event_banner: module_runtime::CreationEntryEventBannerSnapshot {
title: creation_entry_text_or_default(
header.event_title,
module_runtime::DEFAULT_CREATION_ENTRY_EVENT_TITLE,
),
description: creation_entry_text_or_default(
header.event_description,
module_runtime::DEFAULT_CREATION_ENTRY_EVENT_DESCRIPTION,
),
cover_image_src: creation_entry_text_or_default(
header.event_cover_image_src,
module_runtime::DEFAULT_CREATION_ENTRY_EVENT_COVER_IMAGE_SRC,
),
prize_pool_mud_points: header.event_prize_pool_mud_points,
starts_at_text: creation_entry_text_or_default(
header.event_starts_at_text,
module_runtime::DEFAULT_CREATION_ENTRY_EVENT_STARTS_AT_TEXT,
),
ends_at_text: creation_entry_text_or_default(
header.event_ends_at_text,
module_runtime::DEFAULT_CREATION_ENTRY_EVENT_ENDS_AT_TEXT,
),
},
creation_types: creation_types
.into_iter()
.map(|item| module_runtime::CreationEntryTypeSnapshot {
@@ -162,6 +188,15 @@ pub(crate) fn build_creation_entry_config_record_from_rows(
visible: item.visible,
open: item.open,
sort_order: item.sort_order,
category_id: creation_entry_text_or_default(
item.category_id,
module_runtime::DEFAULT_CREATION_ENTRY_CATEGORY_ID,
),
category_label: creation_entry_text_or_default(
item.category_label,
module_runtime::DEFAULT_CREATION_ENTRY_CATEGORY_LABEL,
),
category_sort_order: item.category_sort_order,
updated_at_micros: item.updated_at.to_micros_since_unix_epoch(),
})
.collect(),
@@ -185,6 +220,14 @@ fn map_creation_entry_config_snapshot(
title: snapshot.type_modal.title,
description: snapshot.type_modal.description,
},
event_banner: module_runtime::CreationEntryEventBannerSnapshot {
title: snapshot.event_banner.title,
description: snapshot.event_banner.description,
cover_image_src: snapshot.event_banner.cover_image_src,
prize_pool_mud_points: snapshot.event_banner.prize_pool_mud_points,
starts_at_text: snapshot.event_banner.starts_at_text,
ends_at_text: snapshot.event_banner.ends_at_text,
},
creation_types: snapshot
.creation_types
.into_iter()
@@ -197,6 +240,9 @@ fn map_creation_entry_config_snapshot(
visible: item.visible,
open: item.open,
sort_order: item.sort_order,
category_id: item.category_id,
category_label: item.category_label,
category_sort_order: item.category_sort_order,
updated_at_micros: item.updated_at_micros,
})
.collect(),
@@ -204,6 +250,13 @@ fn map_creation_entry_config_snapshot(
}
}
fn creation_entry_text_or_default(value: Option<String>, default_value: &str) -> String {
value
.map(|value| value.trim().to_string())
.filter(|value| !value.is_empty())
.unwrap_or_else(|| default_value.to_string())
}
pub(crate) fn map_runtime_setting_procedure_result(
result: RuntimeSettingProcedureResult,
) -> Result<RuntimeSettingsRecord, SpacetimeClientError> {

View File

@@ -113,6 +113,7 @@ fn map_wooden_fish_work_snapshot(
floating_words: snapshot.floating_words.clone(),
hit_object_asset: snapshot.hit_object_asset.clone().map(map_image_asset),
background_asset: snapshot.background_asset.clone().map(map_image_asset),
back_button_asset: snapshot.back_button_asset.clone().map(map_image_asset),
hit_sound_asset: snapshot.hit_sound_asset.clone().map(map_audio_asset),
cover_image_src: empty_string_to_none(snapshot.cover_image_src.clone()),
generation_status: parse_generation_status(&snapshot.generation_status),
@@ -147,6 +148,7 @@ fn map_wooden_fish_work_snapshot(
draft,
hit_object_asset,
background_asset: snapshot.background_asset.map(map_image_asset),
back_button_asset: snapshot.back_button_asset.map(map_image_asset),
hit_sound_asset,
floating_words: snapshot.floating_words,
})
@@ -166,6 +168,7 @@ fn map_wooden_fish_draft_snapshot(snapshot: WoodenFishDraftSnapshot) -> WoodenFi
floating_words: snapshot.floating_words,
hit_object_asset: snapshot.hit_object_asset.map(map_image_asset),
background_asset: snapshot.background_asset.map(map_image_asset),
back_button_asset: snapshot.back_button_asset.map(map_image_asset),
hit_sound_asset: snapshot.hit_sound_asset.map(map_audio_asset),
cover_image_src: snapshot.cover_image_src,
generation_status: parse_generation_status(&snapshot.generation_status),

View File

@@ -234,6 +234,7 @@ pub mod creation_entry_config_procedure_result_type;
pub mod creation_entry_config_snapshot_type;
pub mod creation_entry_config_table;
pub mod creation_entry_config_type;
pub mod creation_entry_event_banner_snapshot_type;
pub mod creation_entry_start_card_snapshot_type;
pub mod creation_entry_type_admin_upsert_input_type;
pub mod creation_entry_type_config_table;
@@ -1262,6 +1263,7 @@ pub use creation_entry_config_procedure_result_type::CreationEntryConfigProcedur
pub use creation_entry_config_snapshot_type::CreationEntryConfigSnapshot;
pub use creation_entry_config_table::*;
pub use creation_entry_config_type::CreationEntryConfig;
pub use creation_entry_event_banner_snapshot_type::CreationEntryEventBannerSnapshot;
pub use creation_entry_start_card_snapshot_type::CreationEntryStartCardSnapshot;
pub use creation_entry_type_admin_upsert_input_type::CreationEntryTypeAdminUpsertInput;
pub use creation_entry_type_config_table::*;
@@ -3285,10 +3287,6 @@ impl __sdk::DbUpdate for DbUpdate {
&self.visual_novel_work_profile,
)
.with_updates_by_pk(|row| &row.profile_id);
diff.bark_battle_gallery_view = cache.apply_diff_to_table::<BarkBattleGalleryViewRow>(
"bark_battle_gallery_view",
&self.bark_battle_gallery_view,
);
diff.wooden_fish_agent_session = cache
.apply_diff_to_table::<WoodenFishAgentSessionRow>(
"wooden_fish_agent_session",
@@ -3310,6 +3308,10 @@ impl __sdk::DbUpdate for DbUpdate {
&self.wooden_fish_work_profile,
)
.with_updates_by_pk(|row| &row.profile_id);
diff.bark_battle_gallery_view = cache.apply_diff_to_table::<BarkBattleGalleryViewRow>(
"bark_battle_gallery_view",
&self.bark_battle_gallery_view,
);
diff.big_fish_gallery_view = cache.apply_diff_to_table::<BigFishWorkSummarySnapshot>(
"big_fish_gallery_view",
&self.big_fish_gallery_view,

View File

@@ -4,6 +4,7 @@
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
use super::creation_entry_event_banner_snapshot_type::CreationEntryEventBannerSnapshot;
use super::creation_entry_start_card_snapshot_type::CreationEntryStartCardSnapshot;
use super::creation_entry_type_modal_snapshot_type::CreationEntryTypeModalSnapshot;
use super::creation_entry_type_snapshot_type::CreationEntryTypeSnapshot;
@@ -14,6 +15,7 @@ pub struct CreationEntryConfigSnapshot {
pub config_id: String,
pub start_card: CreationEntryStartCardSnapshot,
pub type_modal: CreationEntryTypeModalSnapshot,
pub event_banner: CreationEntryEventBannerSnapshot,
pub creation_types: Vec<CreationEntryTypeSnapshot>,
pub updated_at_micros: i64,
}

View File

@@ -15,6 +15,12 @@ pub struct CreationEntryConfig {
pub modal_title: String,
pub modal_description: String,
pub updated_at: __sdk::Timestamp,
pub event_title: Option<String>,
pub event_description: Option<String>,
pub event_cover_image_src: Option<String>,
pub event_prize_pool_mud_points: u64,
pub event_starts_at_text: Option<String>,
pub event_ends_at_text: Option<String>,
}
impl __sdk::InModule for CreationEntryConfig {
@@ -33,6 +39,12 @@ pub struct CreationEntryConfigCols {
pub modal_title: __sdk::__query_builder::Col<CreationEntryConfig, String>,
pub modal_description: __sdk::__query_builder::Col<CreationEntryConfig, String>,
pub updated_at: __sdk::__query_builder::Col<CreationEntryConfig, __sdk::Timestamp>,
pub event_title: __sdk::__query_builder::Col<CreationEntryConfig, Option<String>>,
pub event_description: __sdk::__query_builder::Col<CreationEntryConfig, Option<String>>,
pub event_cover_image_src: __sdk::__query_builder::Col<CreationEntryConfig, Option<String>>,
pub event_prize_pool_mud_points: __sdk::__query_builder::Col<CreationEntryConfig, u64>,
pub event_starts_at_text: __sdk::__query_builder::Col<CreationEntryConfig, Option<String>>,
pub event_ends_at_text: __sdk::__query_builder::Col<CreationEntryConfig, Option<String>>,
}
impl __sdk::__query_builder::HasCols for CreationEntryConfig {
@@ -47,6 +59,21 @@ impl __sdk::__query_builder::HasCols for CreationEntryConfig {
modal_title: __sdk::__query_builder::Col::new(table_name, "modal_title"),
modal_description: __sdk::__query_builder::Col::new(table_name, "modal_description"),
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
event_title: __sdk::__query_builder::Col::new(table_name, "event_title"),
event_description: __sdk::__query_builder::Col::new(table_name, "event_description"),
event_cover_image_src: __sdk::__query_builder::Col::new(
table_name,
"event_cover_image_src",
),
event_prize_pool_mud_points: __sdk::__query_builder::Col::new(
table_name,
"event_prize_pool_mud_points",
),
event_starts_at_text: __sdk::__query_builder::Col::new(
table_name,
"event_starts_at_text",
),
event_ends_at_text: __sdk::__query_builder::Col::new(table_name, "event_ends_at_text"),
}
}
}

View File

@@ -0,0 +1,20 @@
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
#![allow(unused, clippy::all)]
use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws};
#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)]
#[sats(crate = __lib)]
pub struct CreationEntryEventBannerSnapshot {
pub title: String,
pub description: String,
pub cover_image_src: String,
pub prize_pool_mud_points: u64,
pub starts_at_text: String,
pub ends_at_text: String,
}
impl __sdk::InModule for CreationEntryEventBannerSnapshot {
type Module = super::RemoteModule;
}

View File

@@ -15,6 +15,9 @@ pub struct CreationEntryTypeAdminUpsertInput {
pub visible: bool,
pub open: bool,
pub sort_order: i32,
pub category_id: String,
pub category_label: String,
pub category_sort_order: i32,
}
impl __sdk::InModule for CreationEntryTypeAdminUpsertInput {

View File

@@ -16,6 +16,9 @@ pub struct CreationEntryTypeConfig {
pub open: bool,
pub sort_order: i32,
pub updated_at: __sdk::Timestamp,
pub category_id: Option<String>,
pub category_label: Option<String>,
pub category_sort_order: i32,
}
impl __sdk::InModule for CreationEntryTypeConfig {
@@ -35,6 +38,9 @@ pub struct CreationEntryTypeConfigCols {
pub open: __sdk::__query_builder::Col<CreationEntryTypeConfig, bool>,
pub sort_order: __sdk::__query_builder::Col<CreationEntryTypeConfig, i32>,
pub updated_at: __sdk::__query_builder::Col<CreationEntryTypeConfig, __sdk::Timestamp>,
pub category_id: __sdk::__query_builder::Col<CreationEntryTypeConfig, Option<String>>,
pub category_label: __sdk::__query_builder::Col<CreationEntryTypeConfig, Option<String>>,
pub category_sort_order: __sdk::__query_builder::Col<CreationEntryTypeConfig, i32>,
}
impl __sdk::__query_builder::HasCols for CreationEntryTypeConfig {
@@ -50,6 +56,12 @@ impl __sdk::__query_builder::HasCols for CreationEntryTypeConfig {
open: __sdk::__query_builder::Col::new(table_name, "open"),
sort_order: __sdk::__query_builder::Col::new(table_name, "sort_order"),
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
category_id: __sdk::__query_builder::Col::new(table_name, "category_id"),
category_label: __sdk::__query_builder::Col::new(table_name, "category_label"),
category_sort_order: __sdk::__query_builder::Col::new(
table_name,
"category_sort_order",
),
}
}
}

View File

@@ -15,6 +15,9 @@ pub struct CreationEntryTypeSnapshot {
pub visible: bool,
pub open: bool,
pub sort_order: i32,
pub category_id: String,
pub category_label: String,
pub category_sort_order: i32,
pub updated_at_micros: i64,
}

View File

@@ -20,6 +20,7 @@ pub struct WoodenFishDraftCompileInput {
pub hit_object_asset_json: Option<String>,
pub background_asset_json: Option<String>,
pub hit_sound_asset_json: Option<String>,
pub back_button_asset_json: Option<String>,
pub floating_words_json: Option<String>,
pub cover_image_src: Option<String>,
pub generation_status: Option<String>,

View File

@@ -22,6 +22,7 @@ pub struct WoodenFishDraftSnapshot {
pub floating_words: Vec<String>,
pub hit_object_asset: Option<WoodenFishImageAssetSnapshot>,
pub background_asset: Option<WoodenFishImageAssetSnapshot>,
pub back_button_asset: Option<WoodenFishImageAssetSnapshot>,
pub hit_sound_asset: Option<WoodenFishAudioAssetSnapshot>,
pub cover_image_src: Option<String>,
pub generation_status: String,

View File

@@ -24,6 +24,7 @@ pub struct WoodenFishGalleryViewRow {
pub hit_sound_prompt: Option<String>,
pub hit_object_asset: Option<WoodenFishImageAssetSnapshot>,
pub background_asset: Option<WoodenFishImageAssetSnapshot>,
pub back_button_asset: Option<WoodenFishImageAssetSnapshot>,
pub hit_sound_asset: Option<WoodenFishAudioAssetSnapshot>,
pub floating_words: Vec<String>,
pub cover_image_src: String,
@@ -60,6 +61,8 @@ pub struct WoodenFishGalleryViewRowCols {
__sdk::__query_builder::Col<WoodenFishGalleryViewRow, Option<WoodenFishImageAssetSnapshot>>,
pub background_asset:
__sdk::__query_builder::Col<WoodenFishGalleryViewRow, Option<WoodenFishImageAssetSnapshot>>,
pub back_button_asset:
__sdk::__query_builder::Col<WoodenFishGalleryViewRow, Option<WoodenFishImageAssetSnapshot>>,
pub hit_sound_asset:
__sdk::__query_builder::Col<WoodenFishGalleryViewRow, Option<WoodenFishAudioAssetSnapshot>>,
pub floating_words: __sdk::__query_builder::Col<WoodenFishGalleryViewRow, Vec<String>>,
@@ -96,6 +99,7 @@ impl __sdk::__query_builder::HasCols for WoodenFishGalleryViewRow {
hit_sound_prompt: __sdk::__query_builder::Col::new(table_name, "hit_sound_prompt"),
hit_object_asset: __sdk::__query_builder::Col::new(table_name, "hit_object_asset"),
background_asset: __sdk::__query_builder::Col::new(table_name, "background_asset"),
back_button_asset: __sdk::__query_builder::Col::new(table_name, "back_button_asset"),
hit_sound_asset: __sdk::__query_builder::Col::new(table_name, "hit_sound_asset"),
floating_words: __sdk::__query_builder::Col::new(table_name, "floating_words"),
cover_image_src: __sdk::__query_builder::Col::new(table_name, "cover_image_src"),

View File

@@ -28,6 +28,7 @@ pub struct WoodenFishWorkProfileRow {
pub updated_at: __sdk::Timestamp,
pub published_at: Option<__sdk::Timestamp>,
pub background_asset_json: Option<String>,
pub back_button_asset_json: Option<String>,
}
impl __sdk::InModule for WoodenFishWorkProfileRow {
@@ -62,6 +63,8 @@ pub struct WoodenFishWorkProfileRowCols {
__sdk::__query_builder::Col<WoodenFishWorkProfileRow, Option<__sdk::Timestamp>>,
pub background_asset_json:
__sdk::__query_builder::Col<WoodenFishWorkProfileRow, Option<String>>,
pub back_button_asset_json:
__sdk::__query_builder::Col<WoodenFishWorkProfileRow, Option<String>>,
}
impl __sdk::__query_builder::HasCols for WoodenFishWorkProfileRow {
@@ -107,6 +110,10 @@ impl __sdk::__query_builder::HasCols for WoodenFishWorkProfileRow {
table_name,
"background_asset_json",
),
back_button_asset_json: __sdk::__query_builder::Col::new(
table_name,
"back_button_asset_json",
),
}
}
}

View File

@@ -23,6 +23,7 @@ pub struct WoodenFishWorkSnapshot {
pub hit_sound_prompt: Option<String>,
pub hit_object_asset: Option<WoodenFishImageAssetSnapshot>,
pub background_asset: Option<WoodenFishImageAssetSnapshot>,
pub back_button_asset: Option<WoodenFishImageAssetSnapshot>,
pub hit_sound_asset: Option<WoodenFishAudioAssetSnapshot>,
pub floating_words: Vec<String>,
pub cover_image_src: String,

View File

@@ -18,6 +18,7 @@ pub struct WoodenFishWorkUpdateInput {
pub hit_object_asset_json: Option<String>,
pub background_asset_json: Option<String>,
pub hit_sound_asset_json: Option<String>,
pub back_button_asset_json: Option<String>,
pub floating_words_json: Option<String>,
pub cover_image_src: Option<String>,
pub generation_status: Option<String>,

View File

@@ -15,7 +15,6 @@ use shared_kernel::build_prefixed_uuid_id;
const WOODEN_FISH_TEMPLATE_ID: &str = "wooden-fish";
const WOODEN_FISH_TEMPLATE_NAME: &str = "敲木鱼";
const DEFAULT_HIT_OBJECT_PROMPT: &str = "默认敲击物图案,圆润木质质感,透明背景";
const DEFAULT_HIT_SOUND_PROMPT: &str = "清脆短促的木鱼敲击声";
impl SpacetimeClient {
pub async fn create_wooden_fish_session(
@@ -532,21 +531,11 @@ fn merge_action_into_draft(
if let Some(asset) = payload.background_asset.clone() {
draft.background_asset = Some(asset);
}
}
if matches!(
scope,
WoodenFishDraftMergeScope::CompileDraft
| WoodenFishDraftMergeScope::GenerateHitSound
| WoodenFishDraftMergeScope::ReplaceHitSound
) {
if let Some(value) = payload
.hit_sound_prompt
.as_ref()
.filter(|value| !value.trim().is_empty())
{
draft.hit_sound_prompt = Some(value.trim().to_string());
if let Some(asset) = payload.back_button_asset.clone() {
draft.back_button_asset = Some(asset);
}
}
draft.hit_sound_prompt = None;
if matches!(scope, WoodenFishDraftMergeScope::GenerateHitSound) {
draft.hit_sound_asset = payload.hit_sound_asset.clone();
} else if matches!(
@@ -577,6 +566,7 @@ fn merge_action_into_draft(
{
draft.hit_object_asset = None;
draft.background_asset = None;
draft.back_button_asset = None;
}
if draft.floating_words.is_empty() {
draft.floating_words = default_floating_words();
@@ -613,6 +603,9 @@ fn build_compile_input(
let background_asset = draft.background_asset.clone().ok_or_else(|| {
SpacetimeClientError::validation_failed("wooden fish background asset 缺少真实生成资产")
})?;
let back_button_asset = draft.back_button_asset.clone().ok_or_else(|| {
SpacetimeClientError::validation_failed("wooden fish back button asset 缺少真实生成资产")
})?;
Ok(WoodenFishDraftCompileInput {
session_id: current.session_id.clone(),
@@ -628,6 +621,7 @@ fn build_compile_input(
hit_object_asset_json: Some(json_string(&hit_object_asset)?),
background_asset_json: Some(json_string(&background_asset)?),
hit_sound_asset_json: Some(json_string(&hit_sound_asset)?),
back_button_asset_json: Some(json_string(&back_button_asset)?),
floating_words_json: Some(json_string(&draft.floating_words)?),
cover_image_src: draft.cover_image_src.clone(),
generation_status: Some("ready".to_string()),
@@ -662,6 +656,7 @@ fn build_update_input(
} else {
None
},
back_button_asset_json: None,
floating_words_json: Some(json_string(&draft.floating_words)?),
cover_image_src: draft.cover_image_src.clone(),
generation_status: None,
@@ -716,10 +711,11 @@ fn default_draft() -> WoodenFishDraftResponse {
theme_tags: vec!["休闲".to_string()],
hit_object_prompt: DEFAULT_HIT_OBJECT_PROMPT.to_string(),
hit_object_reference_image_src: None,
hit_sound_prompt: Some(DEFAULT_HIT_SOUND_PROMPT.to_string()),
hit_sound_prompt: None,
floating_words: default_floating_words(),
hit_object_asset: None,
background_asset: None,
back_button_asset: None,
hit_sound_asset: None,
cover_image_src: None,
generation_status: WoodenFishGenerationStatus::Draft,
@@ -807,6 +803,7 @@ mod tests {
let mut payload = action(WoodenFishActionType::CompileDraft);
payload.hit_object_asset = Some(generated_hit_object_asset("generated-compile-object"));
payload.background_asset = Some(generated_background_asset("generated-compile-background"));
payload.back_button_asset = Some(generated_back_button_asset("generated-compile-back"));
payload.hit_sound_asset = Some(generated_hit_sound_asset("generated-compile-sound"));
let (plan, draft) =
@@ -840,6 +837,13 @@ mod tests {
.unwrap_or("")
.contains("generated-compile-background")
);
assert!(
input
.back_button_asset_json
.as_deref()
.unwrap_or("")
.contains("generated-compile-back")
);
assert_eq!(draft.generation_status, WoodenFishGenerationStatus::Ready);
}
@@ -849,6 +853,7 @@ mod tests {
let mut payload = action(WoodenFishActionType::CompileDraft);
payload.hit_object_asset = Some(generated_hit_object_asset("generated-compile-object"));
payload.background_asset = Some(generated_background_asset("generated-compile-background"));
payload.back_button_asset = Some(generated_back_button_asset("generated-compile-back"));
let error =
match build_wooden_fish_action_plan(&session, OWNER_USER_ID, &payload, NOW_MICROS) {
@@ -869,6 +874,7 @@ mod tests {
let mut payload = action(WoodenFishActionType::CompileDraft);
payload.hit_object_asset = Some(generated_hit_object_asset("generated-compile-object"));
payload.hit_sound_asset = Some(generated_hit_sound_asset("generated-compile-sound"));
payload.back_button_asset = Some(generated_back_button_asset("generated-compile-back"));
let error =
match build_wooden_fish_action_plan(&session, OWNER_USER_ID, &payload, NOW_MICROS) {
@@ -883,6 +889,27 @@ mod tests {
);
}
#[test]
fn wooden_fish_compile_requires_real_back_button_asset_from_api_server() {
let session = session_with_draft(draft_without_assets());
let mut payload = action(WoodenFishActionType::CompileDraft);
payload.hit_object_asset = Some(generated_hit_object_asset("generated-compile-object"));
payload.background_asset = Some(generated_background_asset("generated-compile-background"));
payload.hit_sound_asset = Some(generated_hit_sound_asset("generated-compile-sound"));
let error =
match build_wooden_fish_action_plan(&session, OWNER_USER_ID, &payload, NOW_MICROS) {
Ok(_) => panic!("compile-draft should not publish without back button asset"),
Err(error) => error,
};
assert!(
error
.to_string()
.contains("back button asset 缺少真实生成资产")
);
}
#[test]
fn wooden_fish_action_regenerate_hit_object_replaces_only_object_asset() {
let session = session_with_draft(draft_with_assets());
@@ -890,6 +917,7 @@ mod tests {
payload.hit_object_prompt = Some("新的敲击物".to_string());
payload.hit_object_asset = Some(generated_hit_object_asset("generated-object"));
payload.background_asset = Some(generated_background_asset("generated-background"));
payload.back_button_asset = Some(generated_back_button_asset("generated-back"));
let (plan, _draft) =
build_wooden_fish_action_plan(&session, OWNER_USER_ID, &payload, NOW_MICROS)
@@ -933,6 +961,13 @@ mod tests {
.unwrap_or("")
.contains("generated-background")
);
assert!(
input
.back_button_asset_json
.as_deref()
.unwrap_or("")
.contains("generated-back")
);
}
#[test]
@@ -967,6 +1002,13 @@ mod tests {
);
}
#[test]
fn wooden_fish_default_draft_has_no_hit_sound_prompt() {
let draft = default_draft();
assert!(draft.hit_sound_prompt.is_none());
}
fn action(action_type: WoodenFishActionType) -> WoodenFishActionRequest {
WoodenFishActionRequest {
action_type,
@@ -978,6 +1020,7 @@ mod tests {
hit_object_reference_image_src: None,
hit_object_asset: None,
background_asset: None,
back_button_asset: None,
hit_sound_prompt: None,
hit_sound_asset: None,
floating_words: None,
@@ -1032,6 +1075,21 @@ mod tests {
}
}
fn generated_back_button_asset(asset_id: &str) -> WoodenFishImageAsset {
WoodenFishImageAsset {
asset_id: asset_id.to_string(),
image_src: "/generated-wooden-fish-assets/real-profile/back-button/image.png"
.to_string(),
image_object_key: "generated-wooden-fish-assets/real-profile/back-button/image.png"
.to_string(),
asset_object_id: format!("{asset_id}-asset"),
generation_provider: "image2".to_string(),
prompt: "新的返回按钮".to_string(),
width: 1024,
height: 1024,
}
}
fn generated_hit_sound_asset(asset_id: &str) -> WoodenFishAudioAsset {
WoodenFishAudioAsset {
asset_id: asset_id.to_string(),
@@ -1068,6 +1126,16 @@ mod tests {
width: 1024,
height: 1536,
}),
back_button_asset: Some(WoodenFishImageAsset {
asset_id: "old-back".to_string(),
image_src: "/generated-wooden-fish-assets/old-back.png".to_string(),
image_object_key: "generated-wooden-fish-assets/old-back.png".to_string(),
asset_object_id: "old-back-asset".to_string(),
generation_provider: "image2".to_string(),
prompt: "旧返回按钮".to_string(),
width: 1024,
height: 1024,
}),
hit_sound_asset: Some(WoodenFishAudioAsset {
asset_id: "old-sound".to_string(),
audio_src: "/generated-wooden-fish-assets/old-sound.mp3".to_string(),
@@ -1093,10 +1161,11 @@ mod tests {
theme_tags: vec!["旧标签".to_string()],
hit_object_prompt: "旧敲击物".to_string(),
hit_object_reference_image_src: None,
hit_sound_prompt: Some("旧音效".to_string()),
hit_sound_prompt: None,
floating_words: default_floating_words(),
hit_object_asset: None,
background_asset: None,
back_button_asset: None,
hit_sound_asset: None,
cover_image_src: None,
generation_status: WoodenFishGenerationStatus::Draft,