Add user played work stats for puzzle and big fish
Some checks failed
CI / verify (pull_request) Has been cancelled
Some checks failed
CI / verify (pull_request) Has been cancelled
This commit is contained in:
@@ -83,6 +83,17 @@ pub struct ProfilePlayedWorld {
|
||||
pub(crate) last_observed_play_time_ms: u64,
|
||||
}
|
||||
|
||||
pub(crate) struct ProfilePlayedWorkUpsertInput {
|
||||
pub(crate) user_id: String,
|
||||
pub(crate) world_key: String,
|
||||
pub(crate) owner_user_id: Option<String>,
|
||||
pub(crate) profile_id: Option<String>,
|
||||
pub(crate) world_type: Option<String>,
|
||||
pub(crate) world_title: String,
|
||||
pub(crate) world_subtitle: String,
|
||||
pub(crate) played_at_micros: i64,
|
||||
}
|
||||
|
||||
#[spacetimedb::table(accessor = profile_membership)]
|
||||
pub struct ProfileMembership {
|
||||
#[primary_key]
|
||||
@@ -498,6 +509,172 @@ pub(crate) fn sync_profile_projections_from_snapshot(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn upsert_profile_played_work(
|
||||
ctx: &ReducerContext,
|
||||
input: ProfilePlayedWorkUpsertInput,
|
||||
) -> Result<(), String> {
|
||||
let user_id = input.user_id.trim();
|
||||
let world_key = input.world_key.trim();
|
||||
if user_id.is_empty() {
|
||||
return Err("profile_played_world.user_id 不能为空".to_string());
|
||||
}
|
||||
if world_key.is_empty() {
|
||||
return Err("profile_played_world.world_key 不能为空".to_string());
|
||||
}
|
||||
|
||||
let played_at = Timestamp::from_micros_since_unix_epoch(input.played_at_micros);
|
||||
let played_world_id = format!("{user_id}:{world_key}");
|
||||
let existing = ctx
|
||||
.db
|
||||
.profile_played_world()
|
||||
.played_world_id()
|
||||
.find(&played_world_id);
|
||||
|
||||
if let Some(existing) = existing {
|
||||
ctx.db
|
||||
.profile_played_world()
|
||||
.played_world_id()
|
||||
.delete(&existing.played_world_id);
|
||||
ctx.db.profile_played_world().insert(ProfilePlayedWorld {
|
||||
played_world_id,
|
||||
user_id: user_id.to_string(),
|
||||
world_key: world_key.to_string(),
|
||||
owner_user_id: input.owner_user_id,
|
||||
profile_id: input.profile_id,
|
||||
world_type: input.world_type,
|
||||
world_title: input.world_title,
|
||||
world_subtitle: input.world_subtitle,
|
||||
first_played_at: existing.first_played_at,
|
||||
last_played_at: played_at,
|
||||
last_observed_play_time_ms: existing.last_observed_play_time_ms,
|
||||
});
|
||||
} else {
|
||||
ctx.db.profile_played_world().insert(ProfilePlayedWorld {
|
||||
played_world_id,
|
||||
user_id: user_id.to_string(),
|
||||
world_key: world_key.to_string(),
|
||||
owner_user_id: input.owner_user_id,
|
||||
profile_id: input.profile_id,
|
||||
world_type: input.world_type,
|
||||
world_title: input.world_title,
|
||||
world_subtitle: input.world_subtitle,
|
||||
first_played_at: played_at,
|
||||
last_played_at: played_at,
|
||||
last_observed_play_time_ms: 0,
|
||||
});
|
||||
}
|
||||
|
||||
ensure_profile_dashboard_state(ctx, user_id, played_at);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn add_profile_observed_play_time(
|
||||
ctx: &ReducerContext,
|
||||
user_id: &str,
|
||||
world_key: &str,
|
||||
elapsed_ms: u64,
|
||||
observed_at_micros: i64,
|
||||
) -> Result<(), String> {
|
||||
let user_id = user_id.trim();
|
||||
let world_key = world_key.trim();
|
||||
if user_id.is_empty() || world_key.is_empty() || elapsed_ms == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let observed_at = Timestamp::from_micros_since_unix_epoch(observed_at_micros);
|
||||
let played_world_id = format!("{user_id}:{world_key}");
|
||||
if let Some(existing) = ctx
|
||||
.db
|
||||
.profile_played_world()
|
||||
.played_world_id()
|
||||
.find(&played_world_id)
|
||||
{
|
||||
ctx.db
|
||||
.profile_played_world()
|
||||
.played_world_id()
|
||||
.delete(&existing.played_world_id);
|
||||
ctx.db.profile_played_world().insert(ProfilePlayedWorld {
|
||||
played_world_id,
|
||||
user_id: existing.user_id,
|
||||
world_key: existing.world_key,
|
||||
owner_user_id: existing.owner_user_id,
|
||||
profile_id: existing.profile_id,
|
||||
world_type: existing.world_type,
|
||||
world_title: existing.world_title,
|
||||
world_subtitle: existing.world_subtitle,
|
||||
first_played_at: existing.first_played_at,
|
||||
last_played_at: observed_at,
|
||||
last_observed_play_time_ms: existing
|
||||
.last_observed_play_time_ms
|
||||
.saturating_add(elapsed_ms),
|
||||
});
|
||||
}
|
||||
|
||||
add_profile_dashboard_play_time(ctx, user_id, elapsed_ms, observed_at);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn ensure_profile_dashboard_state(ctx: &ReducerContext, user_id: &str, updated_at: Timestamp) {
|
||||
if ctx
|
||||
.db
|
||||
.profile_dashboard_state()
|
||||
.user_id()
|
||||
.find(&user_id.to_string())
|
||||
.is_some()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.db
|
||||
.profile_dashboard_state()
|
||||
.insert(ProfileDashboardState {
|
||||
user_id: user_id.to_string(),
|
||||
wallet_balance: 0,
|
||||
total_play_time_ms: 0,
|
||||
created_at: updated_at,
|
||||
updated_at,
|
||||
});
|
||||
}
|
||||
|
||||
fn add_profile_dashboard_play_time(
|
||||
ctx: &ReducerContext,
|
||||
user_id: &str,
|
||||
elapsed_ms: u64,
|
||||
updated_at: Timestamp,
|
||||
) {
|
||||
let current = ctx
|
||||
.db
|
||||
.profile_dashboard_state()
|
||||
.user_id()
|
||||
.find(&user_id.to_string());
|
||||
|
||||
if let Some(existing) = current {
|
||||
ctx.db
|
||||
.profile_dashboard_state()
|
||||
.user_id()
|
||||
.delete(&existing.user_id);
|
||||
ctx.db
|
||||
.profile_dashboard_state()
|
||||
.insert(ProfileDashboardState {
|
||||
user_id: user_id.to_string(),
|
||||
wallet_balance: existing.wallet_balance,
|
||||
total_play_time_ms: existing.total_play_time_ms.saturating_add(elapsed_ms),
|
||||
created_at: existing.created_at,
|
||||
updated_at,
|
||||
});
|
||||
} else {
|
||||
ctx.db
|
||||
.profile_dashboard_state()
|
||||
.insert(ProfileDashboardState {
|
||||
user_id: user_id.to_string(),
|
||||
wallet_balance: 0,
|
||||
total_play_time_ms: elapsed_ms,
|
||||
created_at: updated_at,
|
||||
updated_at,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn sync_profile_dashboard_from_snapshot(
|
||||
ctx: &ReducerContext,
|
||||
snapshot: &RuntimeSnapshot,
|
||||
|
||||
Reference in New Issue
Block a user