diff --git a/server-rs/crates/module-runtime/src/domain.rs b/server-rs/crates/module-runtime/src/domain.rs index 882b659c..76b0f81d 100644 --- a/server-rs/crates/module-runtime/src/domain.rs +++ b/server-rs/crates/module-runtime/src/domain.rs @@ -544,6 +544,13 @@ pub struct RuntimeTrackingEventInput { pub occurred_at_micros: i64, } +#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct RuntimeTrackingEventProcedureResult { + pub ok: bool, + pub error_message: Option, +} + #[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct RuntimeProfileTaskConfigSnapshot { diff --git a/server-rs/crates/spacetime-client/src/lib.rs b/server-rs/crates/spacetime-client/src/lib.rs index 1829a452..09754238 100644 --- a/server-rs/crates/spacetime-client/src/lib.rs +++ b/server-rs/crates/spacetime-client/src/lib.rs @@ -155,10 +155,11 @@ use module_runtime::{ RuntimeProfileTaskStatus as DomainRuntimeProfileTaskStatus, RuntimeProfileWalletLedgerEntryRecord, RuntimeReferralInviteCenterRecord, RuntimeReferralRedeemRecord, RuntimeSettingsRecord, RuntimeSnapshotRecord, - RuntimeTrackingScopeKind as DomainRuntimeTrackingScopeKind, build_analytics_metric_query_input, - build_runtime_browse_history_clear_input, build_runtime_browse_history_list_input, - build_runtime_browse_history_record, build_runtime_browse_history_sync_input, - build_runtime_profile_dashboard_get_input, build_runtime_profile_dashboard_record, + RuntimeTrackingEventProcedureResult, RuntimeTrackingScopeKind as DomainRuntimeTrackingScopeKind, + build_analytics_metric_query_input, build_runtime_browse_history_clear_input, + build_runtime_browse_history_list_input, build_runtime_browse_history_record, + build_runtime_browse_history_sync_input, build_runtime_profile_dashboard_get_input, + build_runtime_profile_dashboard_record, build_runtime_profile_invite_code_admin_list_input, build_runtime_profile_invite_code_admin_upsert_input, build_runtime_profile_invite_code_record, build_runtime_profile_play_stats_get_input, build_runtime_profile_play_stats_record, diff --git a/server-rs/crates/spacetime-client/src/mapper.rs b/server-rs/crates/spacetime-client/src/mapper.rs index 2359bf4c..1ecc1f96 100644 --- a/server-rs/crates/spacetime-client/src/mapper.rs +++ b/server-rs/crates/spacetime-client/src/mapper.rs @@ -894,6 +894,16 @@ pub(crate) fn map_runtime_profile_reward_code_redeem_procedure_result( )) } +pub(crate) fn map_runtime_tracking_event_procedure_result( + result: RuntimeTrackingEventProcedureResult, +) -> Result<(), SpacetimeClientError> { + if !result.ok { + return Err(SpacetimeClientError::procedure_failed(result.error_message)); + } + + Ok(()) +} + pub(crate) fn map_runtime_profile_task_center_procedure_result( result: RuntimeProfileTaskCenterProcedureResult, ) -> Result { diff --git a/server-rs/crates/spacetime-client/src/runtime.rs b/server-rs/crates/spacetime-client/src/runtime.rs index ab41b7b0..935dc751 100644 --- a/server-rs/crates/spacetime-client/src/runtime.rs +++ b/server-rs/crates/spacetime-client/src/runtime.rs @@ -304,6 +304,30 @@ impl SpacetimeClient { .await } + pub async fn record_daily_login_tracking_event( + &self, + user_id: String, + ) -> Result<(), SpacetimeClientError> { + let procedure_input = build_runtime_profile_task_center_get_input(user_id) + .map_err(SpacetimeClientError::validation_failed)? + .into(); + + self.call_after_connect(move |connection, sender| { + connection + .procedures() + .record_daily_login_tracking_event_and_return_then( + procedure_input, + move |_, result| { + let mapped = result + .map_err(SpacetimeClientError::from_sdk_error) + .and_then(map_runtime_tracking_event_procedure_result); + send_once(&sender, mapped); + }, + ); + }) + .await + } + pub async fn get_profile_task_center( &self, user_id: String, diff --git a/server-rs/crates/spacetime-module/src/runtime/profile.rs b/server-rs/crates/spacetime-module/src/runtime/profile.rs index ef9f030b..7ca04a52 100644 --- a/server-rs/crates/spacetime-module/src/runtime/profile.rs +++ b/server-rs/crates/spacetime-module/src/runtime/profile.rs @@ -491,7 +491,30 @@ pub fn query_analytics_metric( } } -// 任务中心读取会顺手记录当日登录埋点,确保“每日登录”只依赖后端事实。 +// 登录成功埋点由认证链路主动调用;任务中心只负责读取和刷新任务进度。 +#[spacetimedb::procedure] +pub fn record_daily_login_tracking_event_and_return( + ctx: &mut ProcedureContext, + input: RuntimeProfileTaskCenterGetInput, +) -> RuntimeTrackingEventProcedureResult { + match ctx.try_with_tx(|tx| { + let validated_input = build_runtime_profile_task_center_get_input(input.user_id) + .map_err(|error| error.to_string())?; + ensure_default_profile_task_config(tx); + record_daily_login_tracking_event(tx, &validated_input.user_id) + }) { + Ok(()) => RuntimeTrackingEventProcedureResult { + ok: true, + error_message: None, + }, + Err(message) => RuntimeTrackingEventProcedureResult { + ok: false, + error_message: Some(message), + }, + } +} + +// 任务中心读取会刷新进度;每日登录埋点应由登录成功链路提前记录。 #[spacetimedb::procedure] pub fn get_profile_task_center( ctx: &mut ProcedureContext,