Add user played work stats for puzzle and big fish
Some checks failed
CI / verify (pull_request) Has been cancelled

This commit is contained in:
2026-04-28 12:58:31 +08:00
parent bb4100fca4
commit 377d7d0412
21 changed files with 1028 additions and 82 deletions

View File

@@ -1,12 +1,15 @@
use crate::runtime::{
ProfilePlayedWorkUpsertInput, add_profile_observed_play_time, upsert_profile_played_work,
};
use module_puzzle::{
PUZZLE_MAX_TAG_COUNT, PuzzleAgentMessageFinalizeInput, PuzzleAgentMessageKind,
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,
@@ -1072,6 +1075,12 @@ fn start_puzzle_run_tx(
.map(|value| value.profile_id.clone());
increment_puzzle_profile_play_count(ctx, &entry_profile_row, input.started_at_micros);
upsert_puzzle_profile_played_work(
ctx,
&input.owner_user_id,
&entry_profile_row,
input.started_at_micros,
)?;
insert_puzzle_runtime_run(ctx, &run, &input.owner_user_id, input.started_at_micros)?;
Ok(run)
}
@@ -1179,6 +1188,12 @@ fn advance_puzzle_next_level_tx(
.find(&next_profile.profile_id)
{
increment_puzzle_profile_play_count(ctx, &next_profile_row, input.advanced_at_micros);
upsert_puzzle_profile_played_work(
ctx,
&input.owner_user_id,
&next_profile_row,
input.advanced_at_micros,
)?;
}
replace_puzzle_runtime_run(ctx, &row, &next_run, input.advanced_at_micros);
Ok(next_run)
@@ -1219,6 +1234,13 @@ fn submit_puzzle_leaderboard_entry_tx(
&input.run_id,
input.submitted_at_micros,
);
add_profile_observed_play_time(
ctx,
&input.owner_user_id,
&format!("puzzle:{}", input.profile_id),
input.elapsed_ms.max(1_000),
input.submitted_at_micros,
)?;
let leaderboard_entries = list_puzzle_leaderboard_entries(
ctx,
@@ -1607,6 +1629,28 @@ fn increment_puzzle_profile_play_count(
);
}
fn upsert_puzzle_profile_played_work(
ctx: &TxContext,
user_id: &str,
row: &PuzzleWorkProfileRow,
played_at_micros: i64,
) -> Result<(), String> {
// 拼图正式游玩以作品 profile_id 作为公开作品号,用户侧明细按 world_key 去重。
upsert_profile_played_work(
ctx,
ProfilePlayedWorkUpsertInput {
user_id: user_id.to_string(),
world_key: format!("puzzle:{}", row.profile_id),
owner_user_id: Some(row.owner_user_id.clone()),
profile_id: Some(row.profile_id.clone()),
world_type: Some("PUZZLE".to_string()),
world_title: row.level_name.clone(),
world_subtitle: row.summary.clone(),
played_at_micros,
},
)
}
fn replace_generated_candidate(
draft: &mut PuzzleResultDraft,
candidates: Vec<PuzzleGeneratedImageCandidate>,
@@ -1689,12 +1733,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 +1764,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 +1840,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]