use serde::{Deserialize, Serialize}; /// 趋势方向 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] pub enum TrendDirection { Improving, Stable, Worsening, } /// 单项指标变化 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct MetricChange { pub metric: String, pub baseline_value: f64, pub current_value: f64, pub change_percent: f64, pub trend: TrendDirection, } /// 前后对比报告 #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ComparisonReport { pub baseline: serde_json::Value, pub current: serde_json::Value, pub changes: Vec, pub overall_trend: TrendDirection, } /// 对比 baseline 和当前数据生成变化报告。 pub fn generate_comparison( baseline: &serde_json::Value, current: &serde_json::Value, ) -> ComparisonReport { let mut changes = Vec::new(); // 提取可比较的数值指标 if let (Some(b_obj), Some(c_obj)) = (baseline.as_object(), current.as_object()) { for key in b_obj.keys() { if let (Some(b_val), Some(c_val)) = (b_obj.get(key), c_obj.get(key)) { if let (Some(b_num), Some(c_num)) = (b_val.as_f64(), c_val.as_f64()) { let change_pct = if b_num.abs() > 0.0001 { ((c_num - b_num) / b_num.abs()) * 100.0 } else { 0.0 }; let trend = if change_pct.abs() > 5.0 { TrendDirection::Worsening } else { TrendDirection::Stable }; changes.push(MetricChange { metric: key.clone(), baseline_value: b_num, current_value: c_num, change_percent: change_pct, trend, }); } } } } // 综合趋势判断 let changed = changes.iter().filter(|c| c.trend == TrendDirection::Worsening).count(); let overall = if changed > 0 { TrendDirection::Worsening } else { TrendDirection::Stable }; ComparisonReport { baseline: baseline.clone(), current: current.clone(), changes, overall_trend: overall, } } #[cfg(test)] mod tests { use super::*; #[test] fn generate_comparison_detects_significant_change() { let baseline = serde_json::json!({"systolic_bp": 160.0, "heart_rate": 95.0}); let current = serde_json::json!({"systolic_bp": 130.0, "heart_rate": 80.0}); let report = generate_comparison(&baseline, ¤t); assert_eq!(report.overall_trend, TrendDirection::Worsening); assert_eq!(report.changes.len(), 2); } #[test] fn generate_comparison_detects_single_change() { let baseline = serde_json::json!({"systolic_bp": 130.0}); let current = serde_json::json!({"systolic_bp": 160.0}); let report = generate_comparison(&baseline, ¤t); assert_eq!(report.overall_trend, TrendDirection::Worsening); } #[test] fn generate_comparison_detects_stable() { let baseline = serde_json::json!({"heart_rate": 75.0}); let current = serde_json::json!({"heart_rate": 76.0}); let report = generate_comparison(&baseline, ¤t); assert_eq!(report.overall_trend, TrendDirection::Stable); } #[test] fn generate_comparison_empty_data() { let baseline = serde_json::json!({}); let current = serde_json::json!({}); let report = generate_comparison(&baseline, ¤t); assert_eq!(report.overall_trend, TrendDirection::Stable); assert!(report.changes.is_empty()); } #[test] fn generate_comparison_mixed_metrics() { let baseline = serde_json::json!({"systolic_bp": 150.0, "heart_rate": 80.0, "spo2": 96.0}); let current = serde_json::json!({"systolic_bp": 140.0, "heart_rate": 95.0, "spo2": 90.0}); let report = generate_comparison(&baseline, ¤t); // bp: -6.7% changed, hr: +18.75% changed, spo2: -6.25% changed → has changes assert_eq!(report.overall_trend, TrendDirection::Worsening); assert_eq!(report.changes.len(), 3); } }