- 迁移 000157: follow_up_task 新增 source_type/source_id 字段追踪任务来源 - 迁移 000157: points_rule 新增 health_data_report/lab_report_upload/streak_7_days 种子规则 - P0-2: follow_up 事件处理器新增 health_data.critical_alert 消费,告警触发时自动创建随访任务 - 自动查找管床医生分配,critical 级别 1 天内、warning 级别 3 天内 - 新增 alert_auto 随访类型,source_type 标记来源为 critical_alert - P1-1: points 事件处理器新增 daily_monitoring.created 消费,日常监测上报自动获取积分 - P1-1: points 事件处理器新增 lab_report.uploaded 消费,化验报告上传自动获取积分 - 更新 HMS 系统设计思路文档 v2.0(实体数/链路图/业务链路章节全面更新) - 新增业务链路打通讨论记录 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
360 lines
11 KiB
Rust
360 lines
11 KiB
Rust
//! 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,
|
|
source_type: None,
|
|
source_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(), "无效患者应返回错误");
|
|
}
|