feat: add work-level play tracking
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-05-09 19:56:59 +08:00
parent 32a1530ab1
commit 3ad1075227
24 changed files with 1452 additions and 105 deletions

View File

@@ -4,6 +4,7 @@ use axum::{
extract::{DefaultBodyLimit, Extension},
http::Request,
middleware,
response::Response,
routing::{delete, get, post},
};
use tower_http::{
@@ -26,8 +27,8 @@ use crate::{
create_sts_upload_credentials, get_asset_history, get_asset_read_url,
},
auth::{
attach_refresh_session_token, inspect_auth_claims, inspect_refresh_session_cookie,
require_bearer_auth,
AuthenticatedAccessToken, attach_refresh_session_token, inspect_auth_claims,
inspect_refresh_session_cookie, require_bearer_auth,
},
auth_me::auth_me,
auth_public_user::{get_public_user_by_code, get_public_user_by_id},
@@ -105,7 +106,7 @@ use crate::{
update_puzzle_run_pause, use_puzzle_runtime_prop,
},
refresh_session::refresh_session,
request_context::{attach_request_context, resolve_request_id},
request_context::{RequestContext, attach_request_context, resolve_request_id},
response_headers::propagate_request_id_header,
runtime_browse_history::{
delete_runtime_browse_history, get_runtime_browse_history, post_runtime_browse_history,
@@ -149,6 +150,7 @@ use crate::{
begin_story_runtime_session, begin_story_session, continue_story,
get_story_runtime_projection, get_story_session_state, resolve_story_runtime_action,
},
tracking::record_route_tracking_event_after_success,
vector_engine_audio_generation::{
create_visual_novel_background_music_task, create_visual_novel_sound_effect_task,
publish_visual_novel_background_music_asset, publish_visual_novel_sound_effect_asset,
@@ -499,16 +501,31 @@ pub fn build_router(state: AppState) -> Router {
)
.route(
"/api/assets/direct-upload-tickets",
post(create_direct_upload_ticket),
post(create_direct_upload_ticket).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/assets/sts-upload-credentials",
post(create_sts_upload_credentials),
post(create_sts_upload_credentials).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/assets/objects/confirm",
post(confirm_asset_object).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route("/api/assets/objects/confirm", post(confirm_asset_object))
.route(
"/api/assets/objects/bind",
post(bind_asset_object_to_entity),
post(bind_asset_object_to_entity).route_layer(middleware::from_fn_with_state(
state.clone(),
require_bearer_auth,
)),
)
.route(
"/api/assets/character-visual/generate",
@@ -1479,6 +1496,11 @@ pub fn build_router(state: AppState) -> Router {
.layer(middleware::from_fn(normalize_error_response))
// 响应头回写放在错误归一化外侧,确保最终写回的是归一化后的最终响应。
.layer(middleware::from_fn(propagate_request_id_header))
// 用户行为埋点放在错误归一化外侧,只观察最终成功响应,不阻断主链路。
.layer(middleware::from_fn_with_state(
state.clone(),
record_api_tracking_after_success,
))
// 当前阶段先统一挂接 HTTP tracing后续 request_id、响应头与错误中间件继续在这里扩展。
.layer(
TraceLayer::new_for_http()
@@ -1541,6 +1563,31 @@ pub fn build_router(state: AppState) -> Router {
.with_state(state)
}
async fn record_api_tracking_after_success(
axum::extract::State(state): axum::extract::State<AppState>,
Extension(request_context): Extension<RequestContext>,
request: Request<Body>,
next: middleware::Next,
) -> Response {
let method = request.method().clone();
let path = request.uri().path().to_string();
let response = next.run(request).await;
let authenticated = response
.extensions()
.get::<AuthenticatedAccessToken>()
.cloned();
record_route_tracking_event_after_success(
&state,
&request_context,
&method,
&path,
response.status(),
authenticated.as_ref(),
)
.await;
response
}
fn creative_agent_router(state: AppState) -> Router<AppState> {
Router::new()
.route(