112 lines
3.3 KiB
Rust
112 lines
3.3 KiB
Rust
use module_runtime::RuntimeTrackingScopeKind;
|
||
use serde_json::{Value, json};
|
||
|
||
use crate::{
|
||
auth::AuthenticatedAccessToken,
|
||
request_context::RequestContext,
|
||
state::AppState,
|
||
tracking::{TrackingEventDraft, record_tracking_event_after_success},
|
||
};
|
||
|
||
pub(crate) const WORK_PLAY_START_EVENT_KEY: &str = "work_play_start";
|
||
|
||
pub(crate) struct WorkPlayTrackingDraft {
|
||
pub play_type: &'static str,
|
||
pub work_id: String,
|
||
pub user_id: String,
|
||
pub owner_user_id: Option<String>,
|
||
pub profile_id: Option<String>,
|
||
pub run_id: Option<String>,
|
||
pub source_route: &'static str,
|
||
pub extra: Value,
|
||
}
|
||
|
||
impl WorkPlayTrackingDraft {
|
||
pub(crate) fn new(
|
||
play_type: &'static str,
|
||
work_id: impl Into<String>,
|
||
authenticated: &AuthenticatedAccessToken,
|
||
source_route: &'static str,
|
||
) -> Self {
|
||
let user_id = authenticated.claims().user_id().to_string();
|
||
Self {
|
||
play_type,
|
||
work_id: work_id.into(),
|
||
user_id,
|
||
owner_user_id: None,
|
||
profile_id: None,
|
||
run_id: None,
|
||
source_route,
|
||
extra: json!({}),
|
||
}
|
||
}
|
||
|
||
pub(crate) fn owner_user_id(mut self, owner_user_id: impl Into<String>) -> Self {
|
||
self.owner_user_id = Some(owner_user_id.into());
|
||
self
|
||
}
|
||
|
||
pub(crate) fn profile_id(mut self, profile_id: impl Into<String>) -> Self {
|
||
self.profile_id = Some(profile_id.into());
|
||
self
|
||
}
|
||
|
||
pub(crate) fn run_id(mut self, run_id: impl Into<String>) -> Self {
|
||
self.run_id = Some(run_id.into());
|
||
self
|
||
}
|
||
|
||
pub(crate) fn extra(mut self, extra: Value) -> Self {
|
||
self.extra = extra;
|
||
self
|
||
}
|
||
}
|
||
|
||
/// 作品级正式游玩埋点:scope 固定为 work,scope_id 固定为稳定作品 ID。
|
||
/// 中文注释:该埋点用于“某作品被多少用户玩过”等分析,写入失败不阻断 runtime 主流程。
|
||
pub(crate) async fn record_work_play_start_after_success(
|
||
state: &AppState,
|
||
request_context: &RequestContext,
|
||
draft: WorkPlayTrackingDraft,
|
||
) {
|
||
let mut metadata = json!({
|
||
"operation": WORK_PLAY_START_EVENT_KEY,
|
||
"playType": draft.play_type,
|
||
"workId": draft.work_id,
|
||
"sourceRoute": draft.source_route,
|
||
});
|
||
metadata["userId"] = json!(draft.user_id);
|
||
if let Some(owner_user_id) = draft.owner_user_id.as_deref() {
|
||
metadata["ownerUserId"] = json!(owner_user_id);
|
||
}
|
||
if let Some(profile_id) = draft.profile_id.as_deref() {
|
||
metadata["profileId"] = json!(profile_id);
|
||
}
|
||
if let Some(run_id) = draft.run_id.as_deref() {
|
||
metadata["runId"] = json!(run_id);
|
||
}
|
||
if !draft.extra.is_null() {
|
||
metadata["extra"] = draft.extra;
|
||
}
|
||
|
||
let mut tracking = TrackingEventDraft::new(WORK_PLAY_START_EVENT_KEY, draft.play_type);
|
||
tracking.scope_kind = RuntimeTrackingScopeKind::Work;
|
||
tracking.scope_id = draft.work_id;
|
||
tracking.user_id = Some(draft.user_id);
|
||
tracking.owner_user_id = draft.owner_user_id;
|
||
tracking.profile_id = draft.profile_id;
|
||
tracking.metadata = metadata;
|
||
|
||
record_tracking_event_after_success(state, request_context, tracking).await;
|
||
}
|
||
|
||
#[cfg(test)]
|
||
mod tests {
|
||
use super::*;
|
||
|
||
#[test]
|
||
fn work_play_event_key_is_stable() {
|
||
assert_eq!(WORK_PLAY_START_EVENT_KEY, "work_play_start");
|
||
}
|
||
}
|