use axum::Json; use serde::Deserialize; use tracing; use erp_core::types::ApiResponse; #[derive(Debug, Deserialize)] #[allow(dead_code)] // 客户端上报结构体,字段后续接入分析表时使用 pub struct AnalyticsEvent { pub event: String, pub properties: Option, #[serde(deserialize_with = "deserialize_flexible_timestamp")] pub timestamp: Option, pub page: Option, pub user_id: Option, pub patient_id: Option, } fn deserialize_flexible_timestamp<'de, D>(de: D) -> Result, D::Error> where D: serde::Deserializer<'de>, { use serde::de; let val = Option::::deserialize(de)?; match val { None => Ok(None), Some(serde_json::Value::String(s)) => Ok(Some(s)), Some(serde_json::Value::Number(n)) => Ok(Some(n.to_string())), _ => Err(de::Error::custom("timestamp must be string or number")), } } #[derive(Debug, Deserialize)] pub struct BatchRequest { pub events: Vec, } /// 接收小程序批量埋点事件。 /// 当前为日志记录模式 — 后续可接入 ClickHouse/PostgreSQL 分析表。 pub async fn batch(Json(req): Json) -> Json> { for evt in &req.events { tracing::info!( event = %evt.event, page = ?evt.page, properties = ?evt.properties, "Analytics event received" ); } Json(ApiResponse::ok(())) }