//! erp-health 随访管理集成测试 //! //! 验证随访任务 CRUD、状态流转、批量操作、记录创建、租户隔离、乐观锁。 use erp_health::dto::follow_up_dto::*; use erp_health::service::follow_up_service; use super::test_fixture::TestApp; fn default_create_task(patient_id: uuid::Uuid) -> CreateFollowUpTaskReq { CreateFollowUpTaskReq { patient_id, assigned_to: None, follow_up_type: "phone".to_string(), planned_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 15).unwrap(), content_template: Some("请询问血压情况".to_string()), related_appointment_id: None, } } // --------------------------------------------------------------------------- // 测试 1: 创建 + 查询随访任务 // --------------------------------------------------------------------------- #[tokio::test] async fn test_follow_up_task_create_and_get() { let app = TestApp::new().await; let patient_id = app.create_patient("随访患者").await; let task = follow_up_service::create_task( app.health_state(), app.tenant_id(), Some(app.operator_id()), default_create_task(patient_id), ) .await .expect("创建任务应成功"); assert_eq!(task.patient_id, patient_id); assert_eq!(task.follow_up_type, "phone"); assert_eq!(task.status, "pending"); assert_eq!(task.version, 1); let fetched = follow_up_service::get_task(app.health_state(), app.tenant_id(), task.id) .await .expect("查询应成功"); assert_eq!(fetched.id, task.id); } // --------------------------------------------------------------------------- // 测试 2: 列表按患者过滤 // --------------------------------------------------------------------------- #[tokio::test] async fn test_follow_up_task_list_by_patient() { let app = TestApp::new().await; let patient_a = app.create_patient("列表A").await; let patient_b = app.create_patient("列表B").await; follow_up_service::create_task( app.health_state(), app.tenant_id(), None, default_create_task(patient_a), ) .await .unwrap(); follow_up_service::create_task( app.health_state(), app.tenant_id(), None, default_create_task(patient_a), ) .await .unwrap(); follow_up_service::create_task( app.health_state(), app.tenant_id(), None, default_create_task(patient_b), ) .await .unwrap(); let list_a = follow_up_service::list_tasks( app.health_state(), app.tenant_id(), 1, 20, Some(patient_a), None, None, ) .await .unwrap(); assert_eq!(list_a.total, 2); let list_b = follow_up_service::list_tasks( app.health_state(), app.tenant_id(), 1, 20, Some(patient_b), None, None, ) .await .unwrap(); assert_eq!(list_b.total, 1); } // --------------------------------------------------------------------------- // 测试 3: 状态流转 pending → in_progress → completed // --------------------------------------------------------------------------- #[tokio::test] async fn test_follow_up_task_status_flow() { let app = TestApp::new().await; let patient_id = app.create_patient("流转患者").await; let task = follow_up_service::create_task( app.health_state(), app.tenant_id(), Some(app.operator_id()), default_create_task(patient_id), ) .await .unwrap(); assert_eq!(task.status, "pending"); // pending → in_progress let started = follow_up_service::update_task( app.health_state(), app.tenant_id(), task.id, Some(app.operator_id()), UpdateFollowUpTaskReq { status: Some("in_progress".to_string()), assigned_to: None, follow_up_type: None, planned_date: None, content_template: None, }, task.version, ) .await .expect("开始应成功"); assert_eq!(started.status, "in_progress"); // in_progress → completed let completed = follow_up_service::update_task( app.health_state(), app.tenant_id(), task.id, Some(app.operator_id()), UpdateFollowUpTaskReq { status: Some("completed".to_string()), assigned_to: None, follow_up_type: None, planned_date: None, content_template: None, }, started.version, ) .await .expect("完成应成功"); assert_eq!(completed.status, "completed"); } // --------------------------------------------------------------------------- // 测试 4: 乐观锁冲突 // --------------------------------------------------------------------------- #[tokio::test] async fn test_follow_up_task_version_conflict() { let app = TestApp::new().await; let patient_id = app.create_patient("乐观锁患者").await; let task = follow_up_service::create_task( app.health_state(), app.tenant_id(), Some(app.operator_id()), default_create_task(patient_id), ) .await .unwrap(); // 正确版本更新 follow_up_service::update_task( app.health_state(), app.tenant_id(), task.id, Some(app.operator_id()), UpdateFollowUpTaskReq { status: Some("in_progress".to_string()), assigned_to: None, follow_up_type: None, planned_date: None, content_template: None, }, task.version, ) .await .expect("更新应成功"); // 旧版本更新应失败 let result = follow_up_service::update_task( app.health_state(), app.tenant_id(), task.id, Some(app.operator_id()), UpdateFollowUpTaskReq { status: Some("cancelled".to_string()), assigned_to: None, follow_up_type: None, planned_date: None, content_template: None, }, task.version, ) .await; assert!(result.is_err(), "旧版本应失败"); } // --------------------------------------------------------------------------- // 测试 5: 软删除后不可见 // --------------------------------------------------------------------------- #[tokio::test] async fn test_follow_up_task_soft_delete() { let app = TestApp::new().await; let patient_id = app.create_patient("软删除患者").await; let task = follow_up_service::create_task( app.health_state(), app.tenant_id(), Some(app.operator_id()), default_create_task(patient_id), ) .await .unwrap(); follow_up_service::delete_task( app.health_state(), app.tenant_id(), task.id, Some(app.operator_id()), task.version, ) .await .expect("删除应成功"); let result = follow_up_service::get_task(app.health_state(), app.tenant_id(), task.id).await; assert!(result.is_err(), "软删除后应不可见"); } // --------------------------------------------------------------------------- // 测试 6: 租户隔离 // --------------------------------------------------------------------------- #[tokio::test] async fn test_follow_up_task_tenant_isolation() { let app = TestApp::new().await; let patient_id = app.create_patient("隔离患者").await; let task = follow_up_service::create_task( app.health_state(), app.tenant_id(), None, default_create_task(patient_id), ) .await .unwrap(); let other_tenant = uuid::Uuid::new_v4(); let result = follow_up_service::get_task(app.health_state(), other_tenant, task.id).await; assert!(result.is_err(), "不同租户不应看到此任务"); } // --------------------------------------------------------------------------- // 测试 7: 批量创建随访任务 // --------------------------------------------------------------------------- #[tokio::test] async fn test_follow_up_batch_create() { let app = TestApp::new().await; let patient_a = app.create_patient("批量A").await; let patient_b = app.create_patient("批量B").await; let result = follow_up_service::batch_create_tasks( app.health_state(), app.tenant_id(), Some(app.operator_id()), BatchCreateTasksReq { patient_ids: vec![patient_a, patient_b], assigned_to: None, follow_up_type: "phone".to_string(), planned_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 20).unwrap(), content_template: None, }, ) .await .expect("批量创建应成功"); assert_eq!(result.succeeded, 2); assert_eq!(result.failed, 0); } // --------------------------------------------------------------------------- // 测试 8: 创建随访记录 // --------------------------------------------------------------------------- #[tokio::test] async fn test_follow_up_record_create() { let app = TestApp::new().await; let patient_id = app.create_patient("记录患者").await; let task = follow_up_service::create_task( app.health_state(), app.tenant_id(), Some(app.operator_id()), default_create_task(patient_id), ) .await .unwrap(); let record = follow_up_service::create_record( app.health_state(), app.tenant_id(), Some(app.operator_id()), CreateFollowUpRecordReq { task_id: task.id, executed_by: Some(app.operator_id()), executed_date: chrono::NaiveDate::from_ymd_opt(2026, 5, 16).unwrap(), result: "已跟进".to_string(), patient_condition: Some("血压正常".to_string()), medical_advice: Some("继续服药".to_string()), next_follow_up_date: Some(chrono::NaiveDate::from_ymd_opt(2026, 6, 16).unwrap()), }, ) .await .expect("创建记录应成功"); assert_eq!(record.task_id, task.id); assert_eq!(record.result, "已跟进"); assert_eq!(record.medical_advice, Some("继续服药".to_string())); } // --------------------------------------------------------------------------- // 测试 9: 无效患者返回错误 // --------------------------------------------------------------------------- #[tokio::test] async fn test_follow_up_task_invalid_patient() { let app = TestApp::new().await; let fake_patient = uuid::Uuid::new_v4(); let result = follow_up_service::create_task( app.health_state(), app.tenant_id(), None, default_create_task(fake_patient), ) .await; assert!(result.is_err(), "无效患者应返回错误"); }