fix(jump-hop): isolate draft runs from public leaderboard
This commit is contained in:
@@ -416,11 +416,13 @@ npm run check:server-rs-ddd
|
|||||||
|
|
||||||
- Rust 结构体:`JumpHopRuntimeRunRow`
|
- Rust 结构体:`JumpHopRuntimeRunRow`
|
||||||
- 源码:`server-rs/crates/spacetime-module/src/jump_hop/tables.rs`
|
- 源码:`server-rs/crates/spacetime-module/src/jump_hop/tables.rs`
|
||||||
|
- 说明:运行记录持久化 `runtime_mode`,取值为 `draft` / `published`;草稿试玩只允许作品所有者启动,不累计公开游玩次数,也不写入公开排行榜。
|
||||||
|
|
||||||
### `jump_hop_work_profile`
|
### `jump_hop_work_profile`
|
||||||
|
|
||||||
- Rust 结构体:`JumpHopWorkProfileRow`
|
- Rust 结构体:`JumpHopWorkProfileRow`
|
||||||
- 源码:`server-rs/crates/spacetime-module/src/jump_hop/tables.rs`
|
- 源码:`server-rs/crates/spacetime-module/src/jump_hop/tables.rs`
|
||||||
|
- 说明:作品投影持久化独立 `theme_text`,用于生成主题和公开卡片主题展示;历史行为空时按 `work_title` 兜底。
|
||||||
|
|
||||||
### SpacetimeDB view:`jump_hop_gallery_card_view`
|
### SpacetimeDB view:`jump_hop_gallery_card_view`
|
||||||
|
|
||||||
|
|||||||
@@ -238,11 +238,16 @@ impl SpacetimeClient {
|
|||||||
payload: JumpHopStartRunRequest,
|
payload: JumpHopStartRunRequest,
|
||||||
owner_user_id: String,
|
owner_user_id: String,
|
||||||
) -> Result<JumpHopRuntimeRunSnapshotResponse, SpacetimeClientError> {
|
) -> Result<JumpHopRuntimeRunSnapshotResponse, SpacetimeClientError> {
|
||||||
let profile_id = payload.profile_id;
|
|
||||||
let work = self
|
|
||||||
.get_jump_hop_work_profile(profile_id.clone(), String::new())
|
|
||||||
.await?;
|
|
||||||
let runtime_mode = normalize_jump_hop_runtime_mode(payload.runtime_mode.as_deref());
|
let runtime_mode = normalize_jump_hop_runtime_mode(payload.runtime_mode.as_deref());
|
||||||
|
let profile_id = payload.profile_id;
|
||||||
|
let work_owner_user_id = if runtime_mode == "draft" {
|
||||||
|
owner_user_id.clone()
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
let work = self
|
||||||
|
.get_jump_hop_work_profile(profile_id.clone(), work_owner_user_id)
|
||||||
|
.await?;
|
||||||
validate_jump_hop_runtime_ready(&work, runtime_mode)?;
|
validate_jump_hop_runtime_ready(&work, runtime_mode)?;
|
||||||
let run_id = build_prefixed_uuid_id("jump-hop-run-");
|
let run_id = build_prefixed_uuid_id("jump-hop-run-");
|
||||||
let procedure_input = JumpHopRunStartInput {
|
let procedure_input = JumpHopRunStartInput {
|
||||||
|
|||||||
@@ -84,13 +84,18 @@ pub(crate) fn map_jump_hop_leaderboard_procedure_result(
|
|||||||
pub(crate) fn map_jump_hop_gallery_card_view_row(
|
pub(crate) fn map_jump_hop_gallery_card_view_row(
|
||||||
row: JumpHopGalleryCardViewRow,
|
row: JumpHopGalleryCardViewRow,
|
||||||
) -> JumpHopGalleryCardResponse {
|
) -> JumpHopGalleryCardResponse {
|
||||||
|
let theme_text = if row.theme_text.trim().is_empty() {
|
||||||
|
row.work_title.clone()
|
||||||
|
} else {
|
||||||
|
row.theme_text.clone()
|
||||||
|
};
|
||||||
JumpHopGalleryCardResponse {
|
JumpHopGalleryCardResponse {
|
||||||
public_work_code: row.public_work_code,
|
public_work_code: row.public_work_code,
|
||||||
work_id: row.work_id,
|
work_id: row.work_id,
|
||||||
profile_id: row.profile_id,
|
profile_id: row.profile_id,
|
||||||
owner_user_id: row.owner_user_id,
|
owner_user_id: row.owner_user_id,
|
||||||
author_display_name: row.author_display_name,
|
author_display_name: row.author_display_name,
|
||||||
theme_text: row.work_title.clone(),
|
theme_text,
|
||||||
work_title: row.work_title,
|
work_title: row.work_title,
|
||||||
work_description: row.work_description,
|
work_description: row.work_description,
|
||||||
cover_image_src: empty_string_to_none(row.cover_image_src),
|
cover_image_src: empty_string_to_none(row.cover_image_src),
|
||||||
@@ -125,11 +130,16 @@ fn map_jump_hop_session_snapshot(
|
|||||||
fn map_jump_hop_work_snapshot(
|
fn map_jump_hop_work_snapshot(
|
||||||
snapshot: JumpHopWorkSnapshot,
|
snapshot: JumpHopWorkSnapshot,
|
||||||
) -> Result<JumpHopWorkProfileResponse, SpacetimeClientError> {
|
) -> Result<JumpHopWorkProfileResponse, SpacetimeClientError> {
|
||||||
|
let theme_text = if snapshot.theme_text.trim().is_empty() {
|
||||||
|
snapshot.work_title.clone()
|
||||||
|
} else {
|
||||||
|
snapshot.theme_text.clone()
|
||||||
|
};
|
||||||
let draft = JumpHopDraftResponse {
|
let draft = JumpHopDraftResponse {
|
||||||
template_id: "jump-hop".to_string(),
|
template_id: "jump-hop".to_string(),
|
||||||
template_name: "跳一跳".to_string(),
|
template_name: "跳一跳".to_string(),
|
||||||
profile_id: Some(snapshot.profile_id.clone()),
|
profile_id: Some(snapshot.profile_id.clone()),
|
||||||
theme_text: snapshot.work_title.clone(),
|
theme_text: theme_text.clone(),
|
||||||
work_title: snapshot.work_title.clone(),
|
work_title: snapshot.work_title.clone(),
|
||||||
work_description: snapshot.work_description.clone(),
|
work_description: snapshot.work_description.clone(),
|
||||||
theme_tags: snapshot.theme_tags.clone(),
|
theme_tags: snapshot.theme_tags.clone(),
|
||||||
@@ -166,7 +176,7 @@ fn map_jump_hop_work_snapshot(
|
|||||||
profile_id: snapshot.profile_id,
|
profile_id: snapshot.profile_id,
|
||||||
owner_user_id: snapshot.owner_user_id,
|
owner_user_id: snapshot.owner_user_id,
|
||||||
source_session_id: empty_string_to_none(snapshot.source_session_id),
|
source_session_id: empty_string_to_none(snapshot.source_session_id),
|
||||||
theme_text: snapshot.work_title.clone(),
|
theme_text,
|
||||||
work_title: snapshot.work_title,
|
work_title: snapshot.work_title,
|
||||||
work_description: snapshot.work_description,
|
work_description: snapshot.work_description,
|
||||||
theme_tags: snapshot.theme_tags,
|
theme_tags: snapshot.theme_tags,
|
||||||
@@ -195,7 +205,11 @@ fn map_jump_hop_work_snapshot(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn map_jump_hop_draft_snapshot(snapshot: JumpHopDraftSnapshot) -> JumpHopDraftResponse {
|
fn map_jump_hop_draft_snapshot(snapshot: JumpHopDraftSnapshot) -> JumpHopDraftResponse {
|
||||||
let theme_text = snapshot.work_title.clone();
|
let theme_text = if snapshot.theme_text.trim().is_empty() {
|
||||||
|
snapshot.work_title.clone()
|
||||||
|
} else {
|
||||||
|
snapshot.theme_text.clone()
|
||||||
|
};
|
||||||
JumpHopDraftResponse {
|
JumpHopDraftResponse {
|
||||||
template_id: snapshot.template_id,
|
template_id: snapshot.template_id,
|
||||||
template_name: snapshot.template_name,
|
template_name: snapshot.template_name,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ pub struct JumpHopDraftSnapshot {
|
|||||||
pub template_id: String,
|
pub template_id: String,
|
||||||
pub template_name: String,
|
pub template_name: String,
|
||||||
pub profile_id: Option<String>,
|
pub profile_id: Option<String>,
|
||||||
|
pub theme_text: String,
|
||||||
pub work_title: String,
|
pub work_title: String,
|
||||||
pub work_description: String,
|
pub work_description: String,
|
||||||
pub theme_tags: Vec<String>,
|
pub theme_tags: Vec<String>,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ pub struct JumpHopGalleryCardViewRow {
|
|||||||
pub profile_id: String,
|
pub profile_id: String,
|
||||||
pub owner_user_id: String,
|
pub owner_user_id: String,
|
||||||
pub author_display_name: String,
|
pub author_display_name: String,
|
||||||
|
pub theme_text: String,
|
||||||
pub work_title: String,
|
pub work_title: String,
|
||||||
pub work_description: String,
|
pub work_description: String,
|
||||||
pub theme_tags: Vec<String>,
|
pub theme_tags: Vec<String>,
|
||||||
@@ -38,6 +39,7 @@ pub struct JumpHopGalleryCardViewRowCols {
|
|||||||
pub profile_id: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, String>,
|
pub profile_id: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, String>,
|
||||||
pub owner_user_id: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, String>,
|
pub owner_user_id: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, String>,
|
||||||
pub author_display_name: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, String>,
|
pub author_display_name: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, String>,
|
||||||
|
pub theme_text: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, String>,
|
||||||
pub work_title: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, String>,
|
pub work_title: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, String>,
|
||||||
pub work_description: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, String>,
|
pub work_description: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, String>,
|
||||||
pub theme_tags: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, Vec<String>>,
|
pub theme_tags: __sdk::__query_builder::Col<JumpHopGalleryCardViewRow, Vec<String>>,
|
||||||
@@ -63,6 +65,7 @@ impl __sdk::__query_builder::HasCols for JumpHopGalleryCardViewRow {
|
|||||||
table_name,
|
table_name,
|
||||||
"author_display_name",
|
"author_display_name",
|
||||||
),
|
),
|
||||||
|
theme_text: __sdk::__query_builder::Col::new(table_name, "theme_text"),
|
||||||
work_title: __sdk::__query_builder::Col::new(table_name, "work_title"),
|
work_title: __sdk::__query_builder::Col::new(table_name, "work_title"),
|
||||||
work_description: __sdk::__query_builder::Col::new(table_name, "work_description"),
|
work_description: __sdk::__query_builder::Col::new(table_name, "work_description"),
|
||||||
theme_tags: __sdk::__query_builder::Col::new(table_name, "theme_tags"),
|
theme_tags: __sdk::__query_builder::Col::new(table_name, "theme_tags"),
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ pub struct JumpHopGalleryViewRow {
|
|||||||
pub owner_user_id: String,
|
pub owner_user_id: String,
|
||||||
pub source_session_id: String,
|
pub source_session_id: String,
|
||||||
pub author_display_name: String,
|
pub author_display_name: String,
|
||||||
|
pub theme_text: String,
|
||||||
pub work_title: String,
|
pub work_title: String,
|
||||||
pub work_description: String,
|
pub work_description: String,
|
||||||
pub theme_tags: Vec<String>,
|
pub theme_tags: Vec<String>,
|
||||||
@@ -51,6 +52,7 @@ pub struct JumpHopGalleryViewRowCols {
|
|||||||
pub owner_user_id: __sdk::__query_builder::Col<JumpHopGalleryViewRow, String>,
|
pub owner_user_id: __sdk::__query_builder::Col<JumpHopGalleryViewRow, String>,
|
||||||
pub source_session_id: __sdk::__query_builder::Col<JumpHopGalleryViewRow, String>,
|
pub source_session_id: __sdk::__query_builder::Col<JumpHopGalleryViewRow, String>,
|
||||||
pub author_display_name: __sdk::__query_builder::Col<JumpHopGalleryViewRow, String>,
|
pub author_display_name: __sdk::__query_builder::Col<JumpHopGalleryViewRow, String>,
|
||||||
|
pub theme_text: __sdk::__query_builder::Col<JumpHopGalleryViewRow, String>,
|
||||||
pub work_title: __sdk::__query_builder::Col<JumpHopGalleryViewRow, String>,
|
pub work_title: __sdk::__query_builder::Col<JumpHopGalleryViewRow, String>,
|
||||||
pub work_description: __sdk::__query_builder::Col<JumpHopGalleryViewRow, String>,
|
pub work_description: __sdk::__query_builder::Col<JumpHopGalleryViewRow, String>,
|
||||||
pub theme_tags: __sdk::__query_builder::Col<JumpHopGalleryViewRow, Vec<String>>,
|
pub theme_tags: __sdk::__query_builder::Col<JumpHopGalleryViewRow, Vec<String>>,
|
||||||
@@ -88,6 +90,7 @@ impl __sdk::__query_builder::HasCols for JumpHopGalleryViewRow {
|
|||||||
table_name,
|
table_name,
|
||||||
"author_display_name",
|
"author_display_name",
|
||||||
),
|
),
|
||||||
|
theme_text: __sdk::__query_builder::Col::new(table_name, "theme_text"),
|
||||||
work_title: __sdk::__query_builder::Col::new(table_name, "work_title"),
|
work_title: __sdk::__query_builder::Col::new(table_name, "work_title"),
|
||||||
work_description: __sdk::__query_builder::Col::new(table_name, "work_description"),
|
work_description: __sdk::__query_builder::Col::new(table_name, "work_description"),
|
||||||
theme_tags: __sdk::__query_builder::Col::new(table_name, "theme_tags"),
|
theme_tags: __sdk::__query_builder::Col::new(table_name, "theme_tags"),
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ pub struct JumpHopRuntimeRunRow {
|
|||||||
pub snapshot_json: String,
|
pub snapshot_json: String,
|
||||||
pub created_at: __sdk::Timestamp,
|
pub created_at: __sdk::Timestamp,
|
||||||
pub updated_at: __sdk::Timestamp,
|
pub updated_at: __sdk::Timestamp,
|
||||||
|
pub runtime_mode: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl __sdk::InModule for JumpHopRuntimeRunRow {
|
impl __sdk::InModule for JumpHopRuntimeRunRow {
|
||||||
@@ -41,6 +42,7 @@ pub struct JumpHopRuntimeRunRowCols {
|
|||||||
pub snapshot_json: __sdk::__query_builder::Col<JumpHopRuntimeRunRow, String>,
|
pub snapshot_json: __sdk::__query_builder::Col<JumpHopRuntimeRunRow, String>,
|
||||||
pub created_at: __sdk::__query_builder::Col<JumpHopRuntimeRunRow, __sdk::Timestamp>,
|
pub created_at: __sdk::__query_builder::Col<JumpHopRuntimeRunRow, __sdk::Timestamp>,
|
||||||
pub updated_at: __sdk::__query_builder::Col<JumpHopRuntimeRunRow, __sdk::Timestamp>,
|
pub updated_at: __sdk::__query_builder::Col<JumpHopRuntimeRunRow, __sdk::Timestamp>,
|
||||||
|
pub runtime_mode: __sdk::__query_builder::Col<JumpHopRuntimeRunRow, Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl __sdk::__query_builder::HasCols for JumpHopRuntimeRunRow {
|
impl __sdk::__query_builder::HasCols for JumpHopRuntimeRunRow {
|
||||||
@@ -62,6 +64,7 @@ impl __sdk::__query_builder::HasCols for JumpHopRuntimeRunRow {
|
|||||||
snapshot_json: __sdk::__query_builder::Col::new(table_name, "snapshot_json"),
|
snapshot_json: __sdk::__query_builder::Col::new(table_name, "snapshot_json"),
|
||||||
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
|
created_at: __sdk::__query_builder::Col::new(table_name, "created_at"),
|
||||||
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
|
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
|
||||||
|
runtime_mode: __sdk::__query_builder::Col::new(table_name, "runtime_mode"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ pub struct JumpHopWorkProfileRow {
|
|||||||
pub updated_at: __sdk::Timestamp,
|
pub updated_at: __sdk::Timestamp,
|
||||||
pub published_at: Option<__sdk::Timestamp>,
|
pub published_at: Option<__sdk::Timestamp>,
|
||||||
pub visible: bool,
|
pub visible: bool,
|
||||||
|
pub theme_text: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl __sdk::InModule for JumpHopWorkProfileRow {
|
impl __sdk::InModule for JumpHopWorkProfileRow {
|
||||||
@@ -67,6 +68,7 @@ pub struct JumpHopWorkProfileRowCols {
|
|||||||
pub updated_at: __sdk::__query_builder::Col<JumpHopWorkProfileRow, __sdk::Timestamp>,
|
pub updated_at: __sdk::__query_builder::Col<JumpHopWorkProfileRow, __sdk::Timestamp>,
|
||||||
pub published_at: __sdk::__query_builder::Col<JumpHopWorkProfileRow, Option<__sdk::Timestamp>>,
|
pub published_at: __sdk::__query_builder::Col<JumpHopWorkProfileRow, Option<__sdk::Timestamp>>,
|
||||||
pub visible: __sdk::__query_builder::Col<JumpHopWorkProfileRow, bool>,
|
pub visible: __sdk::__query_builder::Col<JumpHopWorkProfileRow, bool>,
|
||||||
|
pub theme_text: __sdk::__query_builder::Col<JumpHopWorkProfileRow, Option<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl __sdk::__query_builder::HasCols for JumpHopWorkProfileRow {
|
impl __sdk::__query_builder::HasCols for JumpHopWorkProfileRow {
|
||||||
@@ -107,6 +109,7 @@ impl __sdk::__query_builder::HasCols for JumpHopWorkProfileRow {
|
|||||||
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
|
updated_at: __sdk::__query_builder::Col::new(table_name, "updated_at"),
|
||||||
published_at: __sdk::__query_builder::Col::new(table_name, "published_at"),
|
published_at: __sdk::__query_builder::Col::new(table_name, "published_at"),
|
||||||
visible: __sdk::__query_builder::Col::new(table_name, "visible"),
|
visible: __sdk::__query_builder::Col::new(table_name, "visible"),
|
||||||
|
theme_text: __sdk::__query_builder::Col::new(table_name, "theme_text"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ pub struct JumpHopWorkSnapshot {
|
|||||||
pub owner_user_id: String,
|
pub owner_user_id: String,
|
||||||
pub source_session_id: String,
|
pub source_session_id: String,
|
||||||
pub author_display_name: String,
|
pub author_display_name: String,
|
||||||
|
pub theme_text: String,
|
||||||
pub work_title: String,
|
pub work_title: String,
|
||||||
pub work_description: String,
|
pub work_description: String,
|
||||||
pub theme_tags: Vec<String>,
|
pub theme_tags: Vec<String>,
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ pub fn jump_hop_gallery_card_view(ctx: &AnonymousViewContext) -> Vec<JumpHopGall
|
|||||||
profile_id: row.profile_id,
|
profile_id: row.profile_id,
|
||||||
owner_user_id: row.owner_user_id,
|
owner_user_id: row.owner_user_id,
|
||||||
author_display_name: row.author_display_name,
|
author_display_name: row.author_display_name,
|
||||||
|
theme_text: row.theme_text,
|
||||||
work_title: row.work_title,
|
work_title: row.work_title,
|
||||||
work_description: row.work_description,
|
work_description: row.work_description,
|
||||||
theme_tags: row.theme_tags,
|
theme_tags: row.theme_tags,
|
||||||
@@ -74,6 +75,7 @@ pub struct JumpHopGalleryViewRow {
|
|||||||
pub owner_user_id: String,
|
pub owner_user_id: String,
|
||||||
pub source_session_id: String,
|
pub source_session_id: String,
|
||||||
pub author_display_name: String,
|
pub author_display_name: String,
|
||||||
|
pub theme_text: String,
|
||||||
pub work_title: String,
|
pub work_title: String,
|
||||||
pub work_description: String,
|
pub work_description: String,
|
||||||
pub theme_tags: Vec<String>,
|
pub theme_tags: Vec<String>,
|
||||||
@@ -103,6 +105,7 @@ pub struct JumpHopGalleryCardViewRow {
|
|||||||
pub profile_id: String,
|
pub profile_id: String,
|
||||||
pub owner_user_id: String,
|
pub owner_user_id: String,
|
||||||
pub author_display_name: String,
|
pub author_display_name: String,
|
||||||
|
pub theme_text: String,
|
||||||
pub work_title: String,
|
pub work_title: String,
|
||||||
pub work_description: String,
|
pub work_description: String,
|
||||||
pub theme_tags: Vec<String>,
|
pub theme_tags: Vec<String>,
|
||||||
@@ -295,6 +298,7 @@ fn create_jump_hop_agent_session_tx(
|
|||||||
template_id: JUMP_HOP_TEMPLATE_ID.to_string(),
|
template_id: JUMP_HOP_TEMPLATE_ID.to_string(),
|
||||||
template_name: JUMP_HOP_TEMPLATE_NAME.to_string(),
|
template_name: JUMP_HOP_TEMPLATE_NAME.to_string(),
|
||||||
profile_id: None,
|
profile_id: None,
|
||||||
|
theme_text: config.theme_text.clone(),
|
||||||
work_title: input.work_title.clone(),
|
work_title: input.work_title.clone(),
|
||||||
work_description: input.work_description.clone(),
|
work_description: input.work_description.clone(),
|
||||||
theme_tags: parse_tags(input.theme_tags_json.as_deref().unwrap_or("[]"))?,
|
theme_tags: parse_tags(input.theme_tags_json.as_deref().unwrap_or("[]"))?,
|
||||||
@@ -360,6 +364,7 @@ fn compile_jump_hop_draft_tx(
|
|||||||
template_id: JUMP_HOP_TEMPLATE_ID.to_string(),
|
template_id: JUMP_HOP_TEMPLATE_ID.to_string(),
|
||||||
template_name: JUMP_HOP_TEMPLATE_NAME.to_string(),
|
template_name: JUMP_HOP_TEMPLATE_NAME.to_string(),
|
||||||
profile_id: Some(input.profile_id.clone()),
|
profile_id: Some(input.profile_id.clone()),
|
||||||
|
theme_text: clean_string(&config.theme_text, &input.work_title),
|
||||||
work_title: clean_string(&input.work_title, "跳一跳作品"),
|
work_title: clean_string(&input.work_title, "跳一跳作品"),
|
||||||
work_description: input.work_description.trim().to_string(),
|
work_description: input.work_description.trim().to_string(),
|
||||||
theme_tags: tags.clone(),
|
theme_tags: tags.clone(),
|
||||||
@@ -426,6 +431,7 @@ fn compile_jump_hop_draft_tx(
|
|||||||
updated_at: compiled_at,
|
updated_at: compiled_at,
|
||||||
published_at: None,
|
published_at: None,
|
||||||
visible: true,
|
visible: true,
|
||||||
|
theme_text: Some(draft.theme_text.clone()),
|
||||||
};
|
};
|
||||||
upsert_work(ctx, row);
|
upsert_work(ctx, row);
|
||||||
replace_session(
|
replace_session(
|
||||||
@@ -567,6 +573,9 @@ fn start_jump_hop_run_tx(
|
|||||||
require_non_empty(&input.run_id, "jump_hop run_id")?;
|
require_non_empty(&input.run_id, "jump_hop run_id")?;
|
||||||
let work = find_work(ctx, &input.profile_id)?;
|
let work = find_work(ctx, &input.profile_id)?;
|
||||||
let runtime_mode = normalize_runtime_mode(&input.runtime_mode);
|
let runtime_mode = normalize_runtime_mode(&input.runtime_mode);
|
||||||
|
if runtime_mode == JUMP_HOP_RUNTIME_MODE_DRAFT && work.owner_user_id != input.owner_user_id {
|
||||||
|
return Err("jump_hop draft runtime 只能由作品所有者启动".to_string());
|
||||||
|
}
|
||||||
if runtime_mode == JUMP_HOP_RUNTIME_MODE_PUBLISHED
|
if runtime_mode == JUMP_HOP_RUNTIME_MODE_PUBLISHED
|
||||||
&& work.publication_status != JUMP_HOP_PUBLICATION_PUBLISHED
|
&& work.publication_status != JUMP_HOP_PUBLICATION_PUBLISHED
|
||||||
{
|
{
|
||||||
@@ -582,7 +591,7 @@ fn start_jump_hop_run_tx(
|
|||||||
)
|
)
|
||||||
.map_err(|error| error.to_string())?;
|
.map_err(|error| error.to_string())?;
|
||||||
let snapshot = domain_run;
|
let snapshot = domain_run;
|
||||||
upsert_run(ctx, &snapshot, input.started_at_ms);
|
upsert_run(ctx, &snapshot, input.started_at_ms, runtime_mode);
|
||||||
if runtime_mode == JUMP_HOP_RUNTIME_MODE_PUBLISHED {
|
if runtime_mode == JUMP_HOP_RUNTIME_MODE_PUBLISHED {
|
||||||
increment_work_play_count(ctx, &work, input.started_at_ms);
|
increment_work_play_count(ctx, &work, input.started_at_ms);
|
||||||
}
|
}
|
||||||
@@ -623,7 +632,10 @@ fn jump_hop_jump_tx(
|
|||||||
.map_err(|error| error.to_string())?;
|
.map_err(|error| error.to_string())?;
|
||||||
let next = domain_next;
|
let next = domain_next;
|
||||||
replace_run(ctx, &row, &next, input.jumped_at_ms);
|
replace_run(ctx, &row, &next, input.jumped_at_ms);
|
||||||
if next.status == module_jump_hop::JumpHopRunStatus::Failed {
|
if next.status == module_jump_hop::JumpHopRunStatus::Failed
|
||||||
|
&& normalize_runtime_mode(row.runtime_mode.as_deref().unwrap_or_default())
|
||||||
|
== JUMP_HOP_RUNTIME_MODE_PUBLISHED
|
||||||
|
{
|
||||||
upsert_jump_hop_leaderboard_entry(ctx, &next, input.jumped_at_ms);
|
upsert_jump_hop_leaderboard_entry(ctx, &next, input.jumped_at_ms);
|
||||||
}
|
}
|
||||||
insert_event(
|
insert_event(
|
||||||
@@ -654,7 +666,10 @@ fn get_jump_hop_leaderboard_tx(
|
|||||||
String,
|
String,
|
||||||
> {
|
> {
|
||||||
require_non_empty(&input.profile_id, "jump_hop profile_id")?;
|
require_non_empty(&input.profile_id, "jump_hop profile_id")?;
|
||||||
let _ = find_work(ctx, &input.profile_id)?;
|
let work = find_work(ctx, &input.profile_id)?;
|
||||||
|
if work.publication_status != JUMP_HOP_PUBLICATION_PUBLISHED {
|
||||||
|
return Err("jump_hop leaderboard 只开放已发布作品".to_string());
|
||||||
|
}
|
||||||
let limit = input.limit.clamp(1, 50) as usize;
|
let limit = input.limit.clamp(1, 50) as usize;
|
||||||
let mut rows = ctx
|
let mut rows = ctx
|
||||||
.db
|
.db
|
||||||
@@ -696,7 +711,8 @@ fn restart_jump_hop_run_tx(
|
|||||||
)
|
)
|
||||||
.map_err(|error| error.to_string())?;
|
.map_err(|error| error.to_string())?;
|
||||||
let next = domain_next;
|
let next = domain_next;
|
||||||
upsert_run(ctx, &next, input.restarted_at_ms);
|
let runtime_mode = normalize_runtime_mode(source.runtime_mode.as_deref().unwrap_or_default());
|
||||||
|
upsert_run(ctx, &next, input.restarted_at_ms, runtime_mode);
|
||||||
insert_event(
|
insert_event(
|
||||||
ctx,
|
ctx,
|
||||||
input.client_action_id,
|
input.client_action_id,
|
||||||
@@ -718,6 +734,7 @@ fn build_gallery_view_row(row: &JumpHopWorkProfileRow) -> Result<JumpHopGalleryV
|
|||||||
owner_user_id: work.owner_user_id,
|
owner_user_id: work.owner_user_id,
|
||||||
source_session_id: work.source_session_id,
|
source_session_id: work.source_session_id,
|
||||||
author_display_name: work.author_display_name,
|
author_display_name: work.author_display_name,
|
||||||
|
theme_text: work.theme_text,
|
||||||
work_title: work.work_title,
|
work_title: work.work_title,
|
||||||
work_description: work.work_description,
|
work_description: work.work_description,
|
||||||
theme_tags: work.theme_tags,
|
theme_tags: work.theme_tags,
|
||||||
@@ -783,12 +800,18 @@ fn build_session_snapshot(
|
|||||||
|
|
||||||
fn build_work_snapshot(row: &JumpHopWorkProfileRow) -> Result<JumpHopWorkSnapshot, String> {
|
fn build_work_snapshot(row: &JumpHopWorkProfileRow) -> Result<JumpHopWorkSnapshot, String> {
|
||||||
let path = parse_json(&row.path_json)?;
|
let path = parse_json(&row.path_json)?;
|
||||||
|
let theme_text = row
|
||||||
|
.theme_text
|
||||||
|
.as_deref()
|
||||||
|
.and_then(clean_optional)
|
||||||
|
.unwrap_or_else(|| row.work_title.trim().to_string());
|
||||||
Ok(JumpHopWorkSnapshot {
|
Ok(JumpHopWorkSnapshot {
|
||||||
work_id: row.work_id.clone(),
|
work_id: row.work_id.clone(),
|
||||||
profile_id: row.profile_id.clone(),
|
profile_id: row.profile_id.clone(),
|
||||||
owner_user_id: row.owner_user_id.clone(),
|
owner_user_id: row.owner_user_id.clone(),
|
||||||
source_session_id: row.source_session_id.clone(),
|
source_session_id: row.source_session_id.clone(),
|
||||||
author_display_name: row.author_display_name.clone(),
|
author_display_name: row.author_display_name.clone(),
|
||||||
|
theme_text,
|
||||||
work_title: row.work_title.clone(),
|
work_title: row.work_title.clone(),
|
||||||
work_description: row.work_description.clone(),
|
work_description: row.work_description.clone(),
|
||||||
theme_tags: parse_tags(&row.theme_tags_json)?,
|
theme_tags: parse_tags(&row.theme_tags_json)?,
|
||||||
@@ -833,7 +856,11 @@ fn sync_session_from_work_update(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut config = parse_config(&session.config_json)?;
|
let mut config = parse_config(&session.config_json)?;
|
||||||
config.theme_text = work.work_title.clone();
|
config.theme_text = work
|
||||||
|
.theme_text
|
||||||
|
.as_deref()
|
||||||
|
.and_then(clean_optional)
|
||||||
|
.unwrap_or_else(|| work.work_title.trim().to_string());
|
||||||
config.difficulty = work.difficulty.clone();
|
config.difficulty = work.difficulty.clone();
|
||||||
config.style_preset = work.style_preset.clone();
|
config.style_preset = work.style_preset.clone();
|
||||||
config.character_prompt = work.character_prompt.clone();
|
config.character_prompt = work.character_prompt.clone();
|
||||||
@@ -844,6 +871,7 @@ fn sync_session_from_work_update(
|
|||||||
template_id: JUMP_HOP_TEMPLATE_ID.to_string(),
|
template_id: JUMP_HOP_TEMPLATE_ID.to_string(),
|
||||||
template_name: JUMP_HOP_TEMPLATE_NAME.to_string(),
|
template_name: JUMP_HOP_TEMPLATE_NAME.to_string(),
|
||||||
profile_id: Some(work.profile_id.clone()),
|
profile_id: Some(work.profile_id.clone()),
|
||||||
|
theme_text: config.theme_text.clone(),
|
||||||
work_title: work.work_title.clone(),
|
work_title: work.work_title.clone(),
|
||||||
work_description: work.work_description.clone(),
|
work_description: work.work_description.clone(),
|
||||||
theme_tags: parse_tags(&work.theme_tags_json)?,
|
theme_tags: parse_tags(&work.theme_tags_json)?,
|
||||||
@@ -957,7 +985,12 @@ fn replace_session(
|
|||||||
ctx.db.jump_hop_agent_session().insert(next);
|
ctx.db.jump_hop_agent_session().insert(next);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn upsert_run(ctx: &ReducerContext, snapshot: &JumpHopRunSnapshot, updated_at_ms: i64) {
|
fn upsert_run(
|
||||||
|
ctx: &ReducerContext,
|
||||||
|
snapshot: &JumpHopRunSnapshot,
|
||||||
|
updated_at_ms: i64,
|
||||||
|
runtime_mode: &str,
|
||||||
|
) {
|
||||||
if let Some(old) = ctx
|
if let Some(old) = ctx
|
||||||
.db
|
.db
|
||||||
.jump_hop_runtime_run()
|
.jump_hop_runtime_run()
|
||||||
@@ -967,9 +1000,12 @@ fn upsert_run(ctx: &ReducerContext, snapshot: &JumpHopRunSnapshot, updated_at_ms
|
|||||||
ctx.db.jump_hop_runtime_run().delete(old);
|
ctx.db.jump_hop_runtime_run().delete(old);
|
||||||
}
|
}
|
||||||
let created_at = Timestamp::from_micros_since_unix_epoch(updated_at_ms.saturating_mul(1000));
|
let created_at = Timestamp::from_micros_since_unix_epoch(updated_at_ms.saturating_mul(1000));
|
||||||
ctx.db
|
ctx.db.jump_hop_runtime_run().insert(run_row_from_snapshot(
|
||||||
.jump_hop_runtime_run()
|
snapshot,
|
||||||
.insert(run_row_from_snapshot(snapshot, created_at, created_at));
|
created_at,
|
||||||
|
created_at,
|
||||||
|
runtime_mode,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replace_run(
|
fn replace_run(
|
||||||
@@ -983,6 +1019,7 @@ fn replace_run(
|
|||||||
snapshot,
|
snapshot,
|
||||||
old.created_at,
|
old.created_at,
|
||||||
Timestamp::from_micros_since_unix_epoch(updated_at_ms.saturating_mul(1000)),
|
Timestamp::from_micros_since_unix_epoch(updated_at_ms.saturating_mul(1000)),
|
||||||
|
normalize_runtime_mode(old.runtime_mode.as_deref().unwrap_or_default()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -990,6 +1027,7 @@ fn run_row_from_snapshot(
|
|||||||
snapshot: &JumpHopRunSnapshot,
|
snapshot: &JumpHopRunSnapshot,
|
||||||
created_at: Timestamp,
|
created_at: Timestamp,
|
||||||
updated_at: Timestamp,
|
updated_at: Timestamp,
|
||||||
|
runtime_mode: &str,
|
||||||
) -> JumpHopRuntimeRunRow {
|
) -> JumpHopRuntimeRunRow {
|
||||||
JumpHopRuntimeRunRow {
|
JumpHopRuntimeRunRow {
|
||||||
run_id: snapshot.run_id.clone(),
|
run_id: snapshot.run_id.clone(),
|
||||||
@@ -1007,6 +1045,7 @@ fn run_row_from_snapshot(
|
|||||||
snapshot_json: to_json_string(snapshot),
|
snapshot_json: to_json_string(snapshot),
|
||||||
created_at,
|
created_at,
|
||||||
updated_at,
|
updated_at,
|
||||||
|
runtime_mode: Some(normalize_runtime_mode(runtime_mode).to_string()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1359,6 +1398,7 @@ fn clone_work(row: &JumpHopWorkProfileRow) -> JumpHopWorkProfileRow {
|
|||||||
updated_at: row.updated_at,
|
updated_at: row.updated_at,
|
||||||
published_at: row.published_at,
|
published_at: row.published_at,
|
||||||
visible: row.visible,
|
visible: row.visible,
|
||||||
|
theme_text: row.theme_text.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1376,6 +1416,7 @@ fn clone_run(row: &JumpHopRuntimeRunRow) -> JumpHopRuntimeRunRow {
|
|||||||
snapshot_json: row.snapshot_json.clone(),
|
snapshot_json: row.snapshot_json.clone(),
|
||||||
created_at: row.created_at,
|
created_at: row.created_at,
|
||||||
updated_at: row.updated_at,
|
updated_at: row.updated_at,
|
||||||
|
runtime_mode: row.runtime_mode.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,9 @@ pub struct JumpHopWorkProfileRow {
|
|||||||
// 后台可见性开关;默认显示,隐藏后不进入公开列表。
|
// 后台可见性开关;默认显示,隐藏后不进入公开列表。
|
||||||
#[default(WORK_VISIBLE_DEFAULT)]
|
#[default(WORK_VISIBLE_DEFAULT)]
|
||||||
pub(crate) visible: bool,
|
pub(crate) visible: bool,
|
||||||
|
// 跳一跳生成主题独立于作品标题;旧行按 work_title 兜底。
|
||||||
|
#[default(None::<String>)]
|
||||||
|
pub(crate) theme_text: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[spacetimedb::table(
|
#[spacetimedb::table(
|
||||||
@@ -77,6 +80,9 @@ pub struct JumpHopRuntimeRunRow {
|
|||||||
pub(crate) snapshot_json: String,
|
pub(crate) snapshot_json: String,
|
||||||
pub(crate) created_at: Timestamp,
|
pub(crate) created_at: Timestamp,
|
||||||
pub(crate) updated_at: Timestamp,
|
pub(crate) updated_at: Timestamp,
|
||||||
|
// draft / published,用于隔离试玩统计和公开排行榜;旧行按 published 兜底。
|
||||||
|
#[default(None::<String>)]
|
||||||
|
pub(crate) runtime_mode: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[spacetimedb::table(
|
#[spacetimedb::table(
|
||||||
|
|||||||
@@ -233,6 +233,8 @@ pub struct JumpHopDraftSnapshot {
|
|||||||
pub template_id: String,
|
pub template_id: String,
|
||||||
pub template_name: String,
|
pub template_name: String,
|
||||||
pub profile_id: Option<String>,
|
pub profile_id: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub theme_text: String,
|
||||||
pub work_title: String,
|
pub work_title: String,
|
||||||
pub work_description: String,
|
pub work_description: String,
|
||||||
pub theme_tags: Vec<String>,
|
pub theme_tags: Vec<String>,
|
||||||
@@ -274,6 +276,7 @@ pub struct JumpHopWorkSnapshot {
|
|||||||
pub owner_user_id: String,
|
pub owner_user_id: String,
|
||||||
pub source_session_id: String,
|
pub source_session_id: String,
|
||||||
pub author_display_name: String,
|
pub author_display_name: String,
|
||||||
|
pub theme_text: String,
|
||||||
pub work_title: String,
|
pub work_title: String,
|
||||||
pub work_description: String,
|
pub work_description: String,
|
||||||
pub theme_tags: Vec<String>,
|
pub theme_tags: Vec<String>,
|
||||||
|
|||||||
@@ -338,6 +338,7 @@ fn map_custom_world_detail_entry(row: CustomWorldProfileSnapshot) -> PublicWorkD
|
|||||||
fn map_jump_hop_gallery_entry(row: JumpHopGalleryCardViewRow) -> PublicWorkGalleryEntry {
|
fn map_jump_hop_gallery_entry(row: JumpHopGalleryCardViewRow) -> PublicWorkGalleryEntry {
|
||||||
let subtitle = jump_hop_difficulty_label(&row.difficulty).to_string();
|
let subtitle = jump_hop_difficulty_label(&row.difficulty).to_string();
|
||||||
let sort_time_micros = row.published_at_micros.unwrap_or(row.updated_at_micros);
|
let sort_time_micros = row.published_at_micros.unwrap_or(row.updated_at_micros);
|
||||||
|
let theme_text = row.theme_text.clone();
|
||||||
|
|
||||||
PublicWorkGalleryEntry {
|
PublicWorkGalleryEntry {
|
||||||
source_type: "jump-hop".to_string(),
|
source_type: "jump-hop".to_string(),
|
||||||
@@ -352,7 +353,7 @@ fn map_jump_hop_gallery_entry(row: JumpHopGalleryCardViewRow) -> PublicWorkGalle
|
|||||||
summary_text: row.work_description,
|
summary_text: row.work_description,
|
||||||
cover_image_src: empty_string_to_option(row.cover_image_src),
|
cover_image_src: empty_string_to_option(row.cover_image_src),
|
||||||
cover_asset_id: None,
|
cover_asset_id: None,
|
||||||
theme_tags: fallback_tags(row.theme_tags, &["跳一跳"]),
|
theme_tags: fallback_tags(row.theme_tags, &[theme_text.as_str(), "跳一跳"]),
|
||||||
play_count: row.play_count,
|
play_count: row.play_count,
|
||||||
remix_count: 0,
|
remix_count: 0,
|
||||||
like_count: 0,
|
like_count: 0,
|
||||||
@@ -363,6 +364,7 @@ fn map_jump_hop_gallery_entry(row: JumpHopGalleryCardViewRow) -> PublicWorkGalle
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn map_jump_hop_detail_entry(row: JumpHopGalleryViewRow) -> PublicWorkDetailEntry {
|
fn map_jump_hop_detail_entry(row: JumpHopGalleryViewRow) -> PublicWorkDetailEntry {
|
||||||
|
let theme_text = row.theme_text.clone();
|
||||||
let entry = PublicWorkGalleryEntry {
|
let entry = PublicWorkGalleryEntry {
|
||||||
source_type: "jump-hop".to_string(),
|
source_type: "jump-hop".to_string(),
|
||||||
work_id: row.work_id,
|
work_id: row.work_id,
|
||||||
@@ -376,7 +378,7 @@ fn map_jump_hop_detail_entry(row: JumpHopGalleryViewRow) -> PublicWorkDetailEntr
|
|||||||
summary_text: row.work_description,
|
summary_text: row.work_description,
|
||||||
cover_image_src: empty_string_to_option(row.cover_image_src),
|
cover_image_src: empty_string_to_option(row.cover_image_src),
|
||||||
cover_asset_id: None,
|
cover_asset_id: None,
|
||||||
theme_tags: fallback_tags(row.theme_tags, &["跳一跳"]),
|
theme_tags: fallback_tags(row.theme_tags, &[theme_text.as_str(), "跳一跳"]),
|
||||||
play_count: row.play_count,
|
play_count: row.play_count,
|
||||||
remix_count: 0,
|
remix_count: 0,
|
||||||
like_count: 0,
|
like_count: 0,
|
||||||
@@ -388,6 +390,7 @@ fn map_jump_hop_detail_entry(row: JumpHopGalleryViewRow) -> PublicWorkDetailEntr
|
|||||||
"sourceType": "jump-hop",
|
"sourceType": "jump-hop",
|
||||||
"difficulty": row.difficulty,
|
"difficulty": row.difficulty,
|
||||||
"stylePreset": row.style_preset,
|
"stylePreset": row.style_preset,
|
||||||
|
"themeText": theme_text,
|
||||||
"tileAssetCount": row.tile_assets.len(),
|
"tileAssetCount": row.tile_assets.len(),
|
||||||
"platformCount": row.path.platforms.len(),
|
"platformCount": row.path.platforms.len(),
|
||||||
"generationStatus": row.generation_status,
|
"generationStatus": row.generation_status,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
/* @vitest-environment jsdom */
|
/* @vitest-environment jsdom */
|
||||||
|
|
||||||
import { render, screen } from '@testing-library/react';
|
import { render, screen } from '@testing-library/react';
|
||||||
import { expect, test, vi } from 'vitest';
|
import { beforeEach, expect, test, vi } from 'vitest';
|
||||||
|
|
||||||
import type { JumpHopWorkProfileResponse } from '../../../packages/shared/src/contracts/jumpHop';
|
import type { JumpHopWorkProfileResponse } from '../../../packages/shared/src/contracts/jumpHop';
|
||||||
import { useJumpHopLeaderboard } from '../../services/jump-hop/useJumpHopLeaderboard';
|
import { useJumpHopLeaderboard } from '../../services/jump-hop/useJumpHopLeaderboard';
|
||||||
@@ -11,6 +11,16 @@ vi.mock('../../services/jump-hop/useJumpHopLeaderboard', () => ({
|
|||||||
useJumpHopLeaderboard: vi.fn(),
|
useJumpHopLeaderboard: vi.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
vi.mocked(useJumpHopLeaderboard).mockReturnValue({
|
||||||
|
leaderboard: null,
|
||||||
|
isLoading: false,
|
||||||
|
error: null,
|
||||||
|
refresh: vi.fn(),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('跳一跳结果页展示排行榜列表', () => {
|
test('跳一跳结果页展示排行榜列表', () => {
|
||||||
vi.mocked(useJumpHopLeaderboard).mockReturnValue({
|
vi.mocked(useJumpHopLeaderboard).mockReturnValue({
|
||||||
leaderboard: {
|
leaderboard: {
|
||||||
@@ -40,7 +50,7 @@ test('跳一跳结果页展示排行榜列表', () => {
|
|||||||
|
|
||||||
render(
|
render(
|
||||||
<JumpHopResultView
|
<JumpHopResultView
|
||||||
profile={buildProfile()}
|
profile={buildProfile({ publicationStatus: 'published' })}
|
||||||
onBack={() => {}}
|
onBack={() => {}}
|
||||||
onEdit={() => {}}
|
onEdit={() => {}}
|
||||||
onStartTestRun={() => {}}
|
onStartTestRun={() => {}}
|
||||||
@@ -57,13 +67,6 @@ test('跳一跳结果页展示排行榜列表', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('跳一跳结果页默认角色预览使用陶泥儿透明 logo', () => {
|
test('跳一跳结果页默认角色预览使用陶泥儿透明 logo', () => {
|
||||||
vi.mocked(useJumpHopLeaderboard).mockReturnValue({
|
|
||||||
leaderboard: null,
|
|
||||||
isLoading: false,
|
|
||||||
error: null,
|
|
||||||
refresh: vi.fn(),
|
|
||||||
});
|
|
||||||
|
|
||||||
render(
|
render(
|
||||||
<JumpHopResultView
|
<JumpHopResultView
|
||||||
profile={buildProfile()}
|
profile={buildProfile()}
|
||||||
@@ -80,7 +83,27 @@ test('跳一跳结果页默认角色预览使用陶泥儿透明 logo', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
function buildProfile(): JumpHopWorkProfileResponse {
|
test('跳一跳草稿结果页不请求公开排行榜', () => {
|
||||||
|
render(
|
||||||
|
<JumpHopResultView
|
||||||
|
profile={buildProfile({ publicationStatus: 'draft' })}
|
||||||
|
onBack={() => {}}
|
||||||
|
onEdit={() => {}}
|
||||||
|
onStartTestRun={() => {}}
|
||||||
|
onPublish={() => {}}
|
||||||
|
onRegenerateTiles={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(useJumpHopLeaderboard).not.toHaveBeenCalled();
|
||||||
|
expect(screen.queryByText('排行榜')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
function buildProfile(
|
||||||
|
options: {
|
||||||
|
publicationStatus?: JumpHopWorkProfileResponse['summary']['publicationStatus'];
|
||||||
|
} = {},
|
||||||
|
): JumpHopWorkProfileResponse {
|
||||||
return {
|
return {
|
||||||
summary: {
|
summary: {
|
||||||
runtimeKind: 'jump-hop',
|
runtimeKind: 'jump-hop',
|
||||||
@@ -95,7 +118,7 @@ function buildProfile(): JumpHopWorkProfileResponse {
|
|||||||
difficulty: 'standard',
|
difficulty: 'standard',
|
||||||
stylePreset: 'minimal-blocks',
|
stylePreset: 'minimal-blocks',
|
||||||
coverImageSrc: null,
|
coverImageSrc: null,
|
||||||
publicationStatus: 'draft',
|
publicationStatus: options.publicationStatus ?? 'draft',
|
||||||
playCount: 0,
|
playCount: 0,
|
||||||
updatedAt: '2026-05-27T00:00:00Z',
|
updatedAt: '2026-05-27T00:00:00Z',
|
||||||
publishedAt: null,
|
publishedAt: null,
|
||||||
|
|||||||
@@ -272,6 +272,8 @@ export function JumpHopResultView({
|
|||||||
const profileId = isWorkProfile
|
const profileId = isWorkProfile
|
||||||
? profile.summary.profileId
|
? profile.summary.profileId
|
||||||
: safeDraft.profileId;
|
: safeDraft.profileId;
|
||||||
|
const canShowLeaderboard =
|
||||||
|
isWorkProfile && profile.summary.publicationStatus === 'published';
|
||||||
const titleSource = isWorkProfile
|
const titleSource = isWorkProfile
|
||||||
? profile.summary.workTitle
|
? profile.summary.workTitle
|
||||||
: profile.workTitle;
|
: profile.workTitle;
|
||||||
@@ -365,7 +367,9 @@ export function JumpHopResultView({
|
|||||||
<div className="text-xs font-bold tracking-[0.18em] text-[var(--platform-text-soft)]">
|
<div className="text-xs font-bold tracking-[0.18em] text-[var(--platform-text-soft)]">
|
||||||
结果操作
|
结果操作
|
||||||
</div>
|
</div>
|
||||||
|
{canShowLeaderboard ? (
|
||||||
<JumpHopResultLeaderboard profileId={profileId} />
|
<JumpHopResultLeaderboard profileId={profileId} />
|
||||||
|
) : null}
|
||||||
{error ? (
|
{error ? (
|
||||||
<div className="platform-banner platform-banner--danger mt-3 rounded-2xl text-sm leading-6">
|
<div className="platform-banner platform-banner--danger mt-3 rounded-2xl text-sm leading-6">
|
||||||
{error}
|
{error}
|
||||||
|
|||||||
@@ -278,7 +278,7 @@ test('跳一跳运行态失败后在弹窗中展示排行榜', () => {
|
|||||||
|
|
||||||
render(
|
render(
|
||||||
<JumpHopRuntimeShell
|
<JumpHopRuntimeShell
|
||||||
profile={buildProfile()}
|
profile={buildProfile({ publicationStatus: 'published' })}
|
||||||
run={buildFailedRun()}
|
run={buildFailedRun()}
|
||||||
runtimeRequestOptions={runtimeRequestOptions}
|
runtimeRequestOptions={runtimeRequestOptions}
|
||||||
onJump={vi.fn().mockResolvedValue(undefined)}
|
onJump={vi.fn().mockResolvedValue(undefined)}
|
||||||
@@ -298,6 +298,21 @@ test('跳一跳运行态失败后在弹窗中展示排行榜', () => {
|
|||||||
expect(within(leaderboard).getByText('00:08')).toBeTruthy();
|
expect(within(leaderboard).getByText('00:08')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('跳一跳草稿运行失败后不请求公开排行榜', () => {
|
||||||
|
render(
|
||||||
|
<JumpHopRuntimeShell
|
||||||
|
profile={buildProfile({ publicationStatus: 'draft' })}
|
||||||
|
run={buildFailedRun()}
|
||||||
|
onJump={vi.fn().mockResolvedValue(undefined)}
|
||||||
|
onRestart={() => {}}
|
||||||
|
/>,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(useJumpHopLeaderboard).not.toHaveBeenCalled();
|
||||||
|
expect(screen.getByRole('dialog', { name: '失败' })).toBeTruthy();
|
||||||
|
expect(screen.queryByTestId('jump-hop-runtime-leaderboard')).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
test('跳一跳角色层永远压在地块层之上', () => {
|
test('跳一跳角色层永远压在地块层之上', () => {
|
||||||
render(
|
render(
|
||||||
<JumpHopRuntimeShell
|
<JumpHopRuntimeShell
|
||||||
@@ -943,6 +958,7 @@ function buildProfile(options: {
|
|||||||
tileAssets?: JumpHopWorkProfileResponse['tileAssets'];
|
tileAssets?: JumpHopWorkProfileResponse['tileAssets'];
|
||||||
coverComposite?: string | null;
|
coverComposite?: string | null;
|
||||||
coverImageSrc?: string | null;
|
coverImageSrc?: string | null;
|
||||||
|
publicationStatus?: JumpHopWorkProfileResponse['summary']['publicationStatus'];
|
||||||
} = {}): JumpHopWorkProfileResponse {
|
} = {}): JumpHopWorkProfileResponse {
|
||||||
const characterAsset = {
|
const characterAsset = {
|
||||||
assetId: 'builtin',
|
assetId: 'builtin',
|
||||||
@@ -968,7 +984,7 @@ function buildProfile(options: {
|
|||||||
difficulty: 'standard',
|
difficulty: 'standard',
|
||||||
stylePreset: 'minimal-blocks',
|
stylePreset: 'minimal-blocks',
|
||||||
coverImageSrc: options.coverImageSrc ?? null,
|
coverImageSrc: options.coverImageSrc ?? null,
|
||||||
publicationStatus: 'draft',
|
publicationStatus: options.publicationStatus ?? 'draft',
|
||||||
playCount: 0,
|
playCount: 0,
|
||||||
updatedAt: '2026-05-27T00:00:00Z',
|
updatedAt: '2026-05-27T00:00:00Z',
|
||||||
publishedAt: null,
|
publishedAt: null,
|
||||||
|
|||||||
@@ -843,7 +843,9 @@ export function JumpHopRuntimeShell({
|
|||||||
const jumpFeedbackForDisplay = getJumpHopJumpFeedbackLabel(stageRun);
|
const jumpFeedbackForDisplay = getJumpHopJumpFeedbackLabel(stageRun);
|
||||||
const isSettled =
|
const isSettled =
|
||||||
stageRun?.status === 'failed' || stageRun?.status === 'cleared';
|
stageRun?.status === 'failed' || stageRun?.status === 'cleared';
|
||||||
const shouldShowFailureLeaderboard = stageRun?.status === 'failed';
|
const shouldShowFailureLeaderboard =
|
||||||
|
stageRun?.status === 'failed' &&
|
||||||
|
profile?.summary.publicationStatus === 'published';
|
||||||
const successfulJumpCount = stageRun?.successfulJumpCount ?? 0;
|
const successfulJumpCount = stageRun?.successfulJumpCount ?? 0;
|
||||||
const durationLabel = formatJumpHopDurationLabel(
|
const durationLabel = formatJumpHopDurationLabel(
|
||||||
getJumpHopRunDurationMs(stageRun, nowMs),
|
getJumpHopRunDurationMs(stageRun, nowMs),
|
||||||
|
|||||||
Reference in New Issue
Block a user