//! 统一的用户事件审计写入:手拼 ActiveModel 的调用点都收口到这里。 use sea_orm::ActiveModelTrait; use serde_json::Value; use crate::db::DbConn; use crate::models::users::user_events::{self, UserEventType}; use crate::errors::AuthResult; pub fn current_request_id() -> String { envelope::context::request_id().unwrap_or_else(envelope::new_request_id) } /// 写一条用户事件;事件总线之外的持久审计兜底。 #[allow(clippy::too_many_arguments)] #[tracing::instrument(skip_all, fields(user_id = user_id, request_id = tracing::field::Empty))] pub async fn entry( db: &DbConn, user_id: i64, actor_user_id: Option, event_type: UserEventType, details: Option, request_id: Option<&str>, client_ip: Option, user_agent: Option, ) -> AuthResult<()> { let request_id = request_id .filter(|value| !value.is_empty()) .map(str::to_owned) .or_else(envelope::context::request_id); tracing::Span::current().record("request_id", request_id.as_deref().unwrap_or_default()); user_events::ActiveModel { user_id: sea_orm::Set(user_id), actor_user_id: sea_orm::Set(actor_user_id), event_type: sea_orm::Set(event_type), occurred_at: sea_orm::Set(chrono::Utc::now()), ip_address: sea_orm::Set(client_ip), user_agent: sea_orm::Set(user_agent), details: sea_orm::Set(details), trace_id: sea_orm::Set(request_id), ..Default::default() } .insert(db.write()) .await?; Ok(()) }