From f58f1f73c553d99500acddf2d044c6e7b5e85fbf Mon Sep 17 00:00:00 2001 From: iven Date: Mon, 27 Apr 2026 22:27:36 +0800 Subject: [PATCH] =?UTF-8?q?test(health):=20=E5=81=A5=E5=BA=B7=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E9=9B=86=E6=88=90=E6=B5=8B=E8=AF=95=20=E2=80=94=208?= =?UTF-8?q?=20=E4=B8=AA=E6=B5=8B=E8=AF=95=E8=A6=86=E7=9B=96=E4=BD=93?= =?UTF-8?q?=E5=BE=81CRUD/=E5=8C=96=E9=AA=8C=E6=8A=A5=E5=91=8ACRUD+?= =?UTF-8?q?=E5=AE=A1=E9=98=85/=E7=A7=9F=E6=88=B7=E9=9A=94=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(auth): WechatSessionResp mock 缺少 unionid 字段 --- crates/erp-auth/src/service/wechat_service.rs | 18 +- crates/erp-server/tests/integration.rs | 2 + .../tests/integration/health_data_tests.rs | 254 ++++++++++++++++++ 3 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 crates/erp-server/tests/integration/health_data_tests.rs diff --git a/crates/erp-auth/src/service/wechat_service.rs b/crates/erp-auth/src/service/wechat_service.rs index 47893d2..b7ad8a9 100644 --- a/crates/erp-auth/src/service/wechat_service.rs +++ b/crates/erp-auth/src/service/wechat_service.rs @@ -59,7 +59,9 @@ impl WechatService { code = %code, "fetch_session 开始" ); - let session = fetch_session(&state.wechat_appid, &state.wechat_secret, code).await?; + let session = + fetch_session(&state.wechat_appid, &state.wechat_secret, code, state.wechat_dev_mode) + .await?; let openid = session .openid @@ -389,7 +391,21 @@ async fn fetch_session( appid: &str, secret: &str, code: &str, + dev_mode: bool, ) -> AuthResult { + // 开发模式降级:跳过 jscode2session,为 DevTools 模拟器生成确定性 mock openid + if dev_mode { + let mock_openid = format!("dev_mock_{}", &code[..8.min(code.len())]); + tracing::warn!(%mock_openid, "开发模式:使用 mock openid(跳过 jscode2session)"); + return Ok(WechatSessionResp { + openid: Some(mock_openid), + session_key: Some("dev_mock_session_key".to_string()), + unionid: None, + errcode: None, + errmsg: None, + }); + } + let client = reqwest::Client::new(); let resp = client .get("https://api.weixin.qq.com/sns/jscode2session") diff --git a/crates/erp-server/tests/integration.rs b/crates/erp-server/tests/integration.rs index 0220122..d26c85b 100644 --- a/crates/erp-server/tests/integration.rs +++ b/crates/erp-server/tests/integration.rs @@ -26,3 +26,5 @@ mod health_device_reading_tests; mod health_follow_up_tests; #[path = "integration/health_consultation_tests.rs"] mod health_consultation_tests; +#[path = "integration/health_data_tests.rs"] +mod health_data_tests; diff --git a/crates/erp-server/tests/integration/health_data_tests.rs b/crates/erp-server/tests/integration/health_data_tests.rs new file mode 100644 index 0000000..cc76605 --- /dev/null +++ b/crates/erp-server/tests/integration/health_data_tests.rs @@ -0,0 +1,254 @@ +//! erp-health 健康数据(体征 + 化验报告)集成测试 +//! +//! 验证体征 CRUD、化验报告 CRUD + 审阅、租户隔离、乐观锁。 + +use erp_health::dto::health_data_dto::*; +use erp_health::dto::dialysis_dto::ReviewLabReportReq; +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(), "无效患者应返回错误"); +}