//! erp-health 健康数据(体征 + 化验报告)集成测试 //! //! 验证体征 CRUD、化验报告 CRUD + 审阅、租户隔离、乐观锁。 use erp_health::dto::health_data_dto::*; use erp_health::service::health_data_service; use super::test_fixture::TestApp; fn default_vital_signs_req() -> CreateVitalSignsReq { CreateVitalSignsReq { record_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 1).unwrap(), systolic_bp_morning: Some(125), diastolic_bp_morning: Some(82), systolic_bp_evening: Some(120), diastolic_bp_evening: Some(78), heart_rate: Some(72), weight: Some(68.5), blood_sugar: Some(5.2), body_temperature: None, spo2: Some(98), blood_sugar_type: Some("fasting".to_string()), water_intake_ml: Some(2000), urine_output_ml: Some(1500), notes: Some("状态良好".to_string()), source: Some("manual".to_string()), } } // --------------------------------------------------------------------------- // 测试 1: 创建体征记录 // --------------------------------------------------------------------------- #[tokio::test] async fn test_vital_signs_create() { let app = TestApp::new().await; let patient_id = app.create_patient("体征患者").await; let vs = health_data_service::create_vital_signs( app.health_state(), app.tenant_id(), patient_id, Some(app.operator_id()), default_vital_signs_req(), ) .await .expect("创建体征应成功"); assert_eq!(vs.patient_id, patient_id); assert_eq!(vs.systolic_bp_morning, Some(125)); assert_eq!(vs.heart_rate, Some(72)); assert_eq!(vs.weight, Some(68.5)); } // --------------------------------------------------------------------------- // 测试 2: 体征列表按患者过滤 // --------------------------------------------------------------------------- #[tokio::test] async fn test_vital_signs_list() { let app = TestApp::new().await; let patient_a = app.create_patient("列表A").await; let patient_b = app.create_patient("列表B").await; health_data_service::create_vital_signs( app.health_state(), app.tenant_id(), patient_a, None, default_vital_signs_req(), ) .await .unwrap(); health_data_service::create_vital_signs( app.health_state(), app.tenant_id(), patient_b, None, default_vital_signs_req(), ) .await .unwrap(); let list_a = health_data_service::list_vital_signs( app.health_state(), app.tenant_id(), patient_a, 1, 20, ) .await .unwrap(); assert_eq!(list_a.total, 1); let list_b = health_data_service::list_vital_signs( app.health_state(), app.tenant_id(), patient_b, 1, 20, ) .await .unwrap(); assert_eq!(list_b.total, 1); } // --------------------------------------------------------------------------- // 测试 3: 体征更新 // --------------------------------------------------------------------------- #[tokio::test] async fn test_vital_signs_update() { let app = TestApp::new().await; let patient_id = app.create_patient("更新患者").await; let vs = health_data_service::create_vital_signs( app.health_state(), app.tenant_id(), patient_id, Some(app.operator_id()), default_vital_signs_req(), ) .await .unwrap(); let updated = health_data_service::update_vital_signs( app.health_state(), app.tenant_id(), patient_id, vs.id, Some(app.operator_id()), UpdateVitalSignsReq { record_date: None, systolic_bp_morning: None, diastolic_bp_morning: None, systolic_bp_evening: None, diastolic_bp_evening: None, heart_rate: Some(65), weight: Some(67.0), blood_sugar: None, body_temperature: None, spo2: None, blood_sugar_type: None, water_intake_ml: None, urine_output_ml: None, notes: None, }, vs.version, ) .await .expect("更新应成功"); assert_eq!(updated.heart_rate, Some(65)); assert_eq!(updated.weight, Some(67.0)); } // --------------------------------------------------------------------------- // 测试 4: 体征租户隔离 // --------------------------------------------------------------------------- #[tokio::test] async fn test_vital_signs_tenant_isolation() { let app = TestApp::new().await; let patient_id = app.create_patient("隔离患者").await; health_data_service::create_vital_signs( app.health_state(), app.tenant_id(), patient_id, None, default_vital_signs_req(), ) .await .unwrap(); let other_tenant = uuid::Uuid::new_v4(); let list = health_data_service::list_vital_signs(app.health_state(), other_tenant, patient_id, 1, 20) .await .unwrap(); assert_eq!(list.total, 0, "不同租户不应看到体征记录"); } // --------------------------------------------------------------------------- // 测试 5: 创建化验报告 // --------------------------------------------------------------------------- #[tokio::test] async fn test_lab_report_create() { let app = TestApp::new().await; let patient_id = app.create_patient("化验患者").await; let report = health_data_service::create_lab_report( app.health_state(), app.tenant_id(), patient_id, Some(app.operator_id()), CreateLabReportReq { report_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 2).unwrap(), report_type: "blood_routine".to_string(), source: Some("manual_input".to_string()), items: Some(serde_json::json!([ {"name": "WBC", "value": "6.5", "unit": "10^9/L", "reference_low": "3.5", "reference_high": "9.5", "is_abnormal": false}, {"name": "HGB", "value": "110", "unit": "g/L", "reference_low": "120", "reference_high": "160", "is_abnormal": true}, ])), image_urls: None, doctor_notes: Some("轻度贫血".to_string()), }, ) .await .expect("创建化验报告应成功"); assert_eq!(report.patient_id, patient_id); assert_eq!(report.report_type, "blood_routine"); assert_eq!(report.status, "pending"); assert!(report.items.is_some()); } // --------------------------------------------------------------------------- // 测试 6: 化验报告审阅 // --------------------------------------------------------------------------- #[tokio::test] async fn test_lab_report_review() { let app = TestApp::new().await; let patient_id = app.create_patient("审阅患者").await; let report = health_data_service::create_lab_report( app.health_state(), app.tenant_id(), patient_id, Some(app.operator_id()), CreateLabReportReq { report_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 3).unwrap(), report_type: "blood_routine".to_string(), source: None, items: None, image_urls: None, doctor_notes: None, }, ) .await .unwrap(); assert_eq!(report.status, "pending"); let reviewed = health_data_service::review_lab_report( app.health_state(), app.tenant_id(), patient_id, report.id, app.operator_id(), ReviewLabReportReq { doctor_notes: Some("复查确认".to_string()), items: None, }, report.version, ) .await .expect("审阅应成功"); assert_eq!(reviewed.status, "reviewed"); assert!(reviewed.reviewed_by.is_some()); } // --------------------------------------------------------------------------- // 测试 7: 化验报告列表按患者过滤 // --------------------------------------------------------------------------- #[tokio::test] async fn test_lab_report_list() { let app = TestApp::new().await; let patient_a = app.create_patient("化验列表A").await; let patient_b = app.create_patient("化验列表B").await; for pid in &[patient_a, patient_b] { health_data_service::create_lab_report( app.health_state(), app.tenant_id(), *pid, None, CreateLabReportReq { report_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 4).unwrap(), report_type: "blood_routine".to_string(), source: None, items: None, image_urls: None, doctor_notes: None, }, ) .await .unwrap(); } let list_a = health_data_service::list_lab_reports( app.health_state(), app.tenant_id(), patient_a, 1, 20, ) .await .unwrap(); assert_eq!(list_a.total, 1); assert_eq!(list_a.data[0].patient_id, patient_a); } // --------------------------------------------------------------------------- // 测试 8: 无效患者创建体征返回错误 // --------------------------------------------------------------------------- #[tokio::test] async fn test_vital_signs_invalid_patient() { let app = TestApp::new().await; let fake_patient = uuid::Uuid::new_v4(); let result = health_data_service::create_vital_signs( app.health_state(), app.tenant_id(), fake_patient, None, default_vital_signs_req(), ) .await; assert!(result.is_err(), "无效患者应返回错误"); }