T40 UI 审计修复(60 页面全覆盖): - 新增 $acc-d/$wrn-d 渐变中间色变量,修复首页轮播渐变硬编码 - 替换 8 处裸 white 为 $white 设计变量(5 个 SCSS 文件) - 修复 7 处触摸目标 40/44px → 48px(健康/消息/咨询/预约/首页) - 3 页面新增 Loading 状态(体征录入/个人中心/就诊人添加) - statusTag 移除硬编码布局值,改用 SCSS mixin 控制 - 医生端 14 页面架构 Hook 层补充(useThrottledDidShow 替换 useEffect) - 移除 action-inbox 未使用 import 安全 P0 修复: - JWT 中间件加固:token 类型校验 + 过期预检 + 类型别名简化 - 速率限制增强:滑动窗口 + 暴力破解防护 - analytics handler 错误处理完善 文档: - T40 审计报告(24 PASS / 36 PASS_WITH_ISSUES / 0 NEEDS_WORK) - 5 份 DevTools/性能审计讨论记录 - wiki 症状导航 + 小程序章节更新
51 lines
1.5 KiB
Rust
51 lines
1.5 KiB
Rust
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_json::Value>,
|
|
#[serde(deserialize_with = "deserialize_flexible_timestamp")]
|
|
pub timestamp: Option<String>,
|
|
pub page: Option<String>,
|
|
pub user_id: Option<String>,
|
|
pub patient_id: Option<String>,
|
|
}
|
|
|
|
fn deserialize_flexible_timestamp<'de, D>(de: D) -> Result<Option<String>, D::Error>
|
|
where
|
|
D: serde::Deserializer<'de>,
|
|
{
|
|
use serde::de;
|
|
let val = Option::<serde_json::Value>::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<AnalyticsEvent>,
|
|
}
|
|
|
|
/// 接收小程序批量埋点事件。
|
|
/// 当前为日志记录模式 — 后续可接入 ClickHouse/PostgreSQL 分析表。
|
|
pub async fn batch(Json(req): Json<BatchRequest>) -> Json<ApiResponse<()>> {
|
|
for evt in &req.events {
|
|
tracing::info!(
|
|
event = %evt.event,
|
|
page = ?evt.page,
|
|
properties = ?evt.properties,
|
|
"Analytics event received"
|
|
);
|
|
}
|
|
Json(ApiResponse::ok(()))
|
|
}
|