This commit is contained in:
@@ -108,6 +108,7 @@ pub(crate) fn generate_big_fish_asset_tx(
|
||||
.map_err(|error| error.to_string())?,
|
||||
last_assistant_reply: Some(reply.clone()),
|
||||
publish_ready: coverage.publish_ready,
|
||||
play_count: session.play_count,
|
||||
created_at: session.created_at,
|
||||
updated_at,
|
||||
};
|
||||
@@ -164,6 +165,7 @@ pub(crate) fn publish_big_fish_game_tx(
|
||||
.map_err(|error| error.to_string())?,
|
||||
last_assistant_reply: Some("玩法已发布,可以进入测试运行态。".to_string()),
|
||||
publish_ready: true,
|
||||
play_count: session.play_count,
|
||||
created_at: session.created_at,
|
||||
updated_at: published_at,
|
||||
};
|
||||
|
||||
@@ -93,6 +93,32 @@ pub fn delete_big_fish_work(
|
||||
}
|
||||
}
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn record_big_fish_play(
|
||||
ctx: &mut ProcedureContext,
|
||||
input: BigFishPlayRecordInput,
|
||||
) -> BigFishWorksProcedureResult {
|
||||
match ctx.try_with_tx(|tx| record_big_fish_play_tx(tx, input.clone())) {
|
||||
Ok(items) => match serde_json::to_string(&items) {
|
||||
Ok(items_json) => BigFishWorksProcedureResult {
|
||||
ok: true,
|
||||
items_json: Some(items_json),
|
||||
error_message: None,
|
||||
},
|
||||
Err(error) => BigFishWorksProcedureResult {
|
||||
ok: false,
|
||||
items_json: None,
|
||||
error_message: Some(error.to_string()),
|
||||
},
|
||||
},
|
||||
Err(message) => BigFishWorksProcedureResult {
|
||||
ok: false,
|
||||
items_json: None,
|
||||
error_message: Some(message),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[spacetimedb::procedure]
|
||||
pub fn submit_big_fish_message(
|
||||
ctx: &mut ProcedureContext,
|
||||
@@ -194,6 +220,7 @@ pub(crate) fn create_big_fish_session_tx(
|
||||
.map_err(|error| error.to_string())?,
|
||||
last_assistant_reply: Some(input.welcome_message_text.clone()),
|
||||
publish_ready: false,
|
||||
play_count: 0,
|
||||
created_at,
|
||||
updated_at: created_at,
|
||||
});
|
||||
@@ -383,6 +410,7 @@ pub(crate) fn submit_big_fish_message_tx(
|
||||
asset_coverage_json: session.asset_coverage_json.clone(),
|
||||
last_assistant_reply: session.last_assistant_reply.clone(),
|
||||
publish_ready: session.publish_ready,
|
||||
play_count: session.play_count,
|
||||
created_at: session.created_at,
|
||||
updated_at: submitted_at,
|
||||
};
|
||||
@@ -429,6 +457,7 @@ pub(crate) fn finalize_big_fish_agent_message_turn_tx(
|
||||
asset_coverage_json: session.asset_coverage_json.clone(),
|
||||
last_assistant_reply: session.last_assistant_reply.clone(),
|
||||
publish_ready: session.publish_ready,
|
||||
play_count: session.play_count,
|
||||
created_at: session.created_at,
|
||||
updated_at,
|
||||
};
|
||||
@@ -483,6 +512,7 @@ pub(crate) fn finalize_big_fish_agent_message_turn_tx(
|
||||
asset_coverage_json: session.asset_coverage_json.clone(),
|
||||
last_assistant_reply: Some(assistant_reply_text),
|
||||
publish_ready: session.publish_ready,
|
||||
play_count: session.play_count,
|
||||
created_at: session.created_at,
|
||||
updated_at,
|
||||
};
|
||||
@@ -530,6 +560,7 @@ pub(crate) fn compile_big_fish_draft_tx(
|
||||
.map_err(|error| error.to_string())?,
|
||||
last_assistant_reply: Some(reply.clone()),
|
||||
publish_ready: coverage.publish_ready,
|
||||
play_count: session.play_count,
|
||||
created_at: session.created_at,
|
||||
updated_at: compiled_at,
|
||||
};
|
||||
@@ -657,9 +688,51 @@ pub(crate) fn build_big_fish_work_summary(
|
||||
level_main_image_ready_count: coverage.level_main_image_ready_count,
|
||||
level_motion_ready_count: coverage.level_motion_ready_count,
|
||||
background_ready: coverage.background_ready,
|
||||
play_count: row.play_count,
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn record_big_fish_play_tx(
|
||||
ctx: &ReducerContext,
|
||||
input: BigFishPlayRecordInput,
|
||||
) -> Result<Vec<BigFishWorkSummarySnapshot>, String> {
|
||||
validate_play_record_input(&input).map_err(|error| error.to_string())?;
|
||||
let session = ctx
|
||||
.db
|
||||
.big_fish_creation_session()
|
||||
.session_id()
|
||||
.find(&input.session_id)
|
||||
.filter(|row| row.stage == BigFishCreationStage::Published)
|
||||
.ok_or_else(|| "big_fish 已发布作品不存在".to_string())?;
|
||||
let played_at = Timestamp::from_micros_since_unix_epoch(input.played_at_micros);
|
||||
let next_session = BigFishCreationSession {
|
||||
session_id: session.session_id.clone(),
|
||||
owner_user_id: session.owner_user_id.clone(),
|
||||
seed_text: session.seed_text.clone(),
|
||||
current_turn: session.current_turn,
|
||||
progress_percent: session.progress_percent,
|
||||
stage: session.stage,
|
||||
anchor_pack_json: session.anchor_pack_json.clone(),
|
||||
draft_json: session.draft_json.clone(),
|
||||
asset_coverage_json: session.asset_coverage_json.clone(),
|
||||
last_assistant_reply: session.last_assistant_reply.clone(),
|
||||
publish_ready: session.publish_ready,
|
||||
// 中文注释:这里只记录正式发布作品的进入次数,创作结果页测试运行不走这个 procedure。
|
||||
play_count: session.play_count.saturating_add(1),
|
||||
created_at: session.created_at,
|
||||
updated_at: played_at,
|
||||
};
|
||||
replace_big_fish_session(ctx, &session, next_session);
|
||||
|
||||
list_big_fish_works_tx(
|
||||
ctx,
|
||||
BigFishWorksListInput {
|
||||
owner_user_id: String::new(),
|
||||
published_only: true,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn replace_big_fish_session(
|
||||
ctx: &ReducerContext,
|
||||
current: &BigFishCreationSession,
|
||||
@@ -693,6 +766,7 @@ mod tests {
|
||||
asset_coverage_json: "{}".to_string(),
|
||||
last_assistant_reply: Some("欢迎来到大鱼吃小鱼共创。".to_string()),
|
||||
publish_ready: false,
|
||||
play_count: 0,
|
||||
created_at: Timestamp::from_micros_since_unix_epoch(1),
|
||||
updated_at: Timestamp::from_micros_since_unix_epoch(1),
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ pub struct BigFishCreationSession {
|
||||
pub(crate) asset_coverage_json: String,
|
||||
pub(crate) last_assistant_reply: Option<String>,
|
||||
pub(crate) publish_ready: bool,
|
||||
pub(crate) play_count: u32,
|
||||
pub(crate) created_at: Timestamp,
|
||||
pub(crate) updated_at: Timestamp,
|
||||
}
|
||||
|
||||
@@ -659,6 +659,19 @@ where
|
||||
Ok(wrapped.0)
|
||||
}
|
||||
|
||||
fn normalize_migration_row(table_name: &str, value: &serde_json::Value) -> serde_json::Value {
|
||||
let mut next_value = value.clone();
|
||||
if table_name == "big_fish_creation_session" {
|
||||
if let Some(object) = next_value.as_object_mut() {
|
||||
// 中文注释:旧迁移包没有公开游玩次数字段,导入时按新建作品默认 0 兼容。
|
||||
object
|
||||
.entry("play_count".to_string())
|
||||
.or_insert_with(|| serde_json::Value::from(0));
|
||||
}
|
||||
}
|
||||
next_value
|
||||
}
|
||||
|
||||
fn insert_migration_table_rows(
|
||||
ctx: &ReducerContext,
|
||||
table: &MigrationTable,
|
||||
@@ -672,7 +685,8 @@ fn insert_migration_table_rows(
|
||||
let mut imported = 0u64;
|
||||
let mut skipped = 0u64;
|
||||
for value in &table.rows {
|
||||
let row = row_from_json(value)
|
||||
let normalized_value = normalize_migration_row(stringify!($table), value);
|
||||
let row = row_from_json(&normalized_value)
|
||||
.map_err(|error| format!("{}: {error}", stringify!($table)))?;
|
||||
let insert_result = ctx.db
|
||||
.$table()
|
||||
|
||||
@@ -3,10 +3,10 @@ use module_puzzle::{
|
||||
PuzzleAgentMessageRole, PuzzleAgentMessageSnapshot, PuzzleAgentSessionCreateInput,
|
||||
PuzzleAgentSessionGetInput, PuzzleAgentSessionProcedureResult, PuzzleAgentSessionSnapshot,
|
||||
PuzzleAgentStage, PuzzleAnchorPack, PuzzleDraftCompileInput, PuzzleGeneratedImageCandidate,
|
||||
PuzzleGeneratedImagesSaveInput, PuzzlePublicationStatus, PuzzlePublishInput, PuzzleResultDraft,
|
||||
PuzzleLeaderboardEntry, PuzzleLeaderboardSubmitInput, PuzzleRunDragInput, PuzzleRunGetInput,
|
||||
PuzzleRunNextLevelInput, PuzzleRunProcedureResult, PuzzleRunSnapshot, PuzzleRunStartInput,
|
||||
PuzzleRunSwapInput, PuzzleRuntimeLevelStatus, PuzzleSelectCoverImageInput,
|
||||
PuzzleGeneratedImagesSaveInput, PuzzleLeaderboardEntry, PuzzleLeaderboardSubmitInput,
|
||||
PuzzlePublicationStatus, PuzzlePublishInput, PuzzleResultDraft, PuzzleRunDragInput,
|
||||
PuzzleRunGetInput, PuzzleRunNextLevelInput, PuzzleRunProcedureResult, PuzzleRunSnapshot,
|
||||
PuzzleRunStartInput, PuzzleRunSwapInput, PuzzleRuntimeLevelStatus, PuzzleSelectCoverImageInput,
|
||||
PuzzleWorkDeleteInput, PuzzleWorkGetInput, PuzzleWorkProcedureResult, PuzzleWorkProfile,
|
||||
PuzzleWorkUpsertInput, PuzzleWorksListInput, PuzzleWorksProcedureResult,
|
||||
apply_publish_overrides_to_draft, apply_selected_candidate, build_result_preview,
|
||||
@@ -1689,12 +1689,7 @@ fn upsert_puzzle_leaderboard_entry(
|
||||
) {
|
||||
let entry_id = build_puzzle_leaderboard_entry_id(user_id, profile_id, grid_size);
|
||||
let updated_at = Timestamp::from_micros_since_unix_epoch(updated_at_micros);
|
||||
if let Some(existing) = ctx
|
||||
.db
|
||||
.puzzle_leaderboard_entry()
|
||||
.entry_id()
|
||||
.find(&entry_id)
|
||||
{
|
||||
if let Some(existing) = ctx.db.puzzle_leaderboard_entry().entry_id().find(&entry_id) {
|
||||
let should_replace = elapsed_ms < existing.best_elapsed_ms
|
||||
|| (elapsed_ms == existing.best_elapsed_ms
|
||||
&& updated_at.to_micros_since_unix_epoch()
|
||||
@@ -1725,16 +1720,18 @@ fn upsert_puzzle_leaderboard_entry(
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.db.puzzle_leaderboard_entry().insert(PuzzleLeaderboardEntryRow {
|
||||
entry_id,
|
||||
profile_id: profile_id.to_string(),
|
||||
grid_size,
|
||||
user_id: user_id.to_string(),
|
||||
nickname: nickname.to_string(),
|
||||
best_elapsed_ms: elapsed_ms,
|
||||
last_run_id: run_id.to_string(),
|
||||
updated_at,
|
||||
});
|
||||
ctx.db
|
||||
.puzzle_leaderboard_entry()
|
||||
.insert(PuzzleLeaderboardEntryRow {
|
||||
entry_id,
|
||||
profile_id: profile_id.to_string(),
|
||||
grid_size,
|
||||
user_id: user_id.to_string(),
|
||||
nickname: nickname.to_string(),
|
||||
best_elapsed_ms: elapsed_ms,
|
||||
last_run_id: run_id.to_string(),
|
||||
updated_at,
|
||||
});
|
||||
}
|
||||
|
||||
fn list_puzzle_leaderboard_entries(
|
||||
@@ -1799,8 +1796,8 @@ fn deserialize_run(value: &str) -> Result<PuzzleRunSnapshot, String> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use module_puzzle::{
|
||||
build_generated_candidates, empty_anchor_pack, recommendation_score, tag_similarity_score,
|
||||
PuzzleLeaderboardEntry,
|
||||
PuzzleLeaderboardEntry, build_generated_candidates, empty_anchor_pack,
|
||||
recommendation_score, tag_similarity_score,
|
||||
};
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user