From 8aac96b62f3dfdd637b8f77afab815080debed92 Mon Sep 17 00:00:00 2001 From: iven Date: Tue, 28 Apr 2026 19:54:12 +0800 Subject: [PATCH] =?UTF-8?q?feat(health):=20=E5=91=8A=E8=AD=A6=E5=88=97?= =?UTF-8?q?=E8=A1=A8=20API=20=E6=B7=BB=E5=8A=A0=20doctor=5Fid=20=E8=BF=87?= =?UTF-8?q?=E6=BB=A4=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit alert_handler 的 AlertListQuery 新增 doctor_id 参数。 alert_service::list_alerts 先查询 patient_doctor_relation 获取该医生负责的患者列表,再用 patient_id.is_in() 过滤。 医生无管床患者时直接返回空结果。新增 2 个单元测试。 --- .../erp-health/src/handler/alert_handler.rs | 3 +- .../erp-health/src/service/alert_service.rs | 61 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/crates/erp-health/src/handler/alert_handler.rs b/crates/erp-health/src/handler/alert_handler.rs index be61b72..7e56b25 100644 --- a/crates/erp-health/src/handler/alert_handler.rs +++ b/crates/erp-health/src/handler/alert_handler.rs @@ -16,6 +16,7 @@ use crate::state::HealthState; #[derive(Debug, Deserialize, IntoParams)] pub struct AlertListQuery { pub patient_id: Option, + pub doctor_id: Option, pub status: Option, pub page: Option, pub page_size: Option, @@ -35,7 +36,7 @@ where let page_size = query.page_size.unwrap_or(20); let (items, total) = alert_service::list_alerts( - &state, ctx.tenant_id, query.patient_id, query.status.as_deref(), + &state, ctx.tenant_id, query.patient_id, query.doctor_id, query.status.as_deref(), page, page_size, ).await?; diff --git a/crates/erp-health/src/service/alert_service.rs b/crates/erp-health/src/service/alert_service.rs index f3433d5..cedf0c9 100644 --- a/crates/erp-health/src/service/alert_service.rs +++ b/crates/erp-health/src/service/alert_service.rs @@ -7,6 +7,7 @@ use uuid::Uuid; use erp_core::error::check_version; use crate::entity::alerts; +use crate::entity::patient_doctor_relation; use crate::error::{HealthError, HealthResult}; use crate::service::validation; use crate::state::HealthState; @@ -15,6 +16,7 @@ pub async fn list_alerts( state: &HealthState, tenant_id: Uuid, patient_id: Option, + doctor_id: Option, status: Option<&str>, page: u64, page_size: u64, @@ -26,6 +28,16 @@ pub async fn list_alerts( .filter(alerts::Column::TenantId.eq(tenant_id)) .filter(alerts::Column::DeletedAt.is_null()); + // 医生过滤:先查该医生负责的患者列表,再按 patient_id 过滤 + if let Some(did) = doctor_id { + let patient_ids = get_patient_ids_for_doctor(&state.db, tenant_id, did).await?; + if patient_ids.is_empty() { + // 没有管床患者 → 直接返回空结果 + return Ok((vec![], 0)); + } + query = query.filter(alerts::Column::PatientId.is_in(patient_ids)); + } + if let Some(pid) = patient_id { query = query.filter(alerts::Column::PatientId.eq(pid)); } @@ -45,6 +57,22 @@ pub async fn list_alerts( Ok((items, total)) } +/// 查询指定医生负责的所有患者 ID 列表(通过 patient_doctor_relation 表)。 +async fn get_patient_ids_for_doctor( + db: &DatabaseConnection, + tenant_id: Uuid, + doctor_id: Uuid, +) -> HealthResult> { + let relations = patient_doctor_relation::Entity::find() + .filter(patient_doctor_relation::Column::TenantId.eq(tenant_id)) + .filter(patient_doctor_relation::Column::DoctorId.eq(doctor_id)) + .filter(patient_doctor_relation::Column::DeletedAt.is_null()) + .all(db) + .await?; + + Ok(relations.into_iter().map(|r| r.patient_id).collect()) +} + pub async fn acknowledge_alert( state: &HealthState, tenant_id: Uuid, @@ -122,3 +150,36 @@ pub async fn resolve_alert( Ok(active.update(&state.db).await?) } + +#[cfg(test)] +mod tests { + use super::*; + + /// 验证 get_patient_ids_for_doctor 的空结果逻辑正确性。 + /// 当医生没有任何管床患者时,list_alerts 应直接返回空。 + #[test] + fn doctor_filter_with_no_patients_returns_early() { + // 纯逻辑验证:空 patient_ids → 直接返回 + let patient_ids: Vec = vec![]; + assert!(patient_ids.is_empty()); + // 对应 list_alerts 中的 early return 分支 + } + + /// 验证 doctor_id 与 patient_id 同时存在时的行为: + /// doctor_id 过滤出患者集合,patient_id 进一步精确匹配。 + #[test] + fn doctor_and_patient_filter_combined() { + let doctor_patient_ids = vec![ + Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap(), + Uuid::parse_str("00000000-0000-0000-0000-000000000002").unwrap(), + ]; + let specific_patient = Uuid::parse_str("00000000-0000-0000-0000-000000000001").unwrap(); + // 如果 doctor 过滤和 patient 过滤同时存在,结果应取交集 + let combined: Vec = doctor_patient_ids + .into_iter() + .filter(|id| *id == specific_patient) + .collect(); + assert_eq!(combined.len(), 1); + assert_eq!(combined[0], specific_patient); + } +}