fix(health): 走查止血 — 患者名显示修复 + 枚举补全 + 医护统计 + 设备选择器
后端: - alert_service: list_alerts 批量查询 patient_name 填充 AlertResponse - consultation_service: list_sessions 批量查询 patient_name/doctor_name - erp-ai handler: list_analysis 通过 raw SQL 查询 patient_name 前端: - AlertList/AlertDashboard: 使用后端返回的 patient_name 替代 ID 截断 - ConsultationDetail: 使用 patient_name/doctor_name 替代 ID 截断 - AiAnalysisList: 使用 patient_name 替代 ID 截断 - constants/health: SEVERITY 补 high/medium, STATUS 补 active - AdminDashboard: 医护人数改为 API 查询(useStatsData 新增 doctorCount) - DeviceManage: 患者 ID 输入改为 PatientSelect 搜索选择器
This commit is contained in:
@@ -291,8 +291,46 @@ where
|
||||
.analysis
|
||||
.list_analysis(ctx.tenant_id, params.patient_id, params.analysis_type, &pagination)
|
||||
.await?;
|
||||
|
||||
// 批量查询 patient_name(通过 raw SQL 避免跨 crate 依赖 erp-health)
|
||||
let patient_ids: std::collections::HashSet<uuid::Uuid> = items
|
||||
.iter()
|
||||
.filter(|a| a.patient_id != uuid::Uuid::nil())
|
||||
.map(|a| a.patient_id)
|
||||
.collect();
|
||||
|
||||
let patient_names: std::collections::HashMap<uuid::Uuid, String> = if !patient_ids.is_empty() {
|
||||
#[derive(sea_orm::FromQueryResult)]
|
||||
struct PatientName { id: uuid::Uuid, name: String }
|
||||
let ids: Vec<uuid::Uuid> = patient_ids.into_iter().collect();
|
||||
use sea_orm::FromQueryResult;
|
||||
PatientName::find_by_statement(sea_orm::Statement::from_sql_and_values(
|
||||
sea_orm::DatabaseBackend::Postgres,
|
||||
"SELECT id, name FROM patient WHERE id = ANY($1) AND tenant_id = $2 AND deleted_at IS NULL",
|
||||
[ids.into(), ctx.tenant_id.into()],
|
||||
))
|
||||
.all(&state.db)
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|p| (p.id, p.name))
|
||||
.collect()
|
||||
} else {
|
||||
std::collections::HashMap::new()
|
||||
};
|
||||
|
||||
let data: Vec<serde_json::Value> = items.into_iter().map(|a| {
|
||||
let mut val = serde_json::to_value(&a).unwrap_or_default();
|
||||
if let Some(obj) = val.as_object_mut() {
|
||||
obj.insert("patient_name".to_string(), serde_json::json!(
|
||||
patient_names.get(&a.patient_id).cloned()
|
||||
));
|
||||
}
|
||||
val
|
||||
}).collect();
|
||||
|
||||
Ok(Json(ApiResponse::ok(serde_json::json!({
|
||||
"data": items,
|
||||
"data": data,
|
||||
"total": total,
|
||||
"page": pagination.page.unwrap_or(1),
|
||||
"page_size": pagination.limit(),
|
||||
|
||||
@@ -74,6 +74,7 @@ pub struct AcknowledgeAlertRequest {
|
||||
pub struct AlertResponse {
|
||||
pub id: Uuid,
|
||||
pub patient_id: Uuid,
|
||||
pub patient_name: Option<String>,
|
||||
pub rule_id: Uuid,
|
||||
pub severity: String,
|
||||
pub title: String,
|
||||
|
||||
@@ -6,8 +6,9 @@ use uuid::Uuid;
|
||||
|
||||
use erp_core::error::check_version;
|
||||
|
||||
use crate::dto::alert_dto::AlertResponse;
|
||||
use crate::entity::alerts;
|
||||
use crate::entity::patient_doctor_relation;
|
||||
use crate::entity::{patient, patient_doctor_relation};
|
||||
use crate::error::{HealthError, HealthResult};
|
||||
use crate::service::validation;
|
||||
use crate::state::HealthState;
|
||||
@@ -20,7 +21,7 @@ pub async fn list_alerts(
|
||||
status: Option<&str>,
|
||||
page: u64,
|
||||
page_size: u64,
|
||||
) -> HealthResult<(Vec<alerts::Model>, u64)> {
|
||||
) -> HealthResult<(Vec<AlertResponse>, u64)> {
|
||||
let limit = page_size.min(100);
|
||||
let offset = page.saturating_sub(1) * limit;
|
||||
|
||||
@@ -32,7 +33,6 @@ pub async fn list_alerts(
|
||||
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));
|
||||
@@ -47,13 +47,44 @@ pub async fn list_alerts(
|
||||
}
|
||||
|
||||
let total = query.clone().count(&state.db).await?;
|
||||
let items = query
|
||||
let models = query
|
||||
.order_by_desc(alerts::Column::CreatedAt)
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
.all(&state.db)
|
||||
.await?;
|
||||
|
||||
// 批量查询 patient_name
|
||||
let patient_ids: std::collections::HashSet<Uuid> = models.iter().map(|m| m.patient_id).collect();
|
||||
let patient_names: std::collections::HashMap<Uuid, String> = if !patient_ids.is_empty() {
|
||||
patient::Entity::find()
|
||||
.filter(patient::Column::Id.is_in(patient_ids))
|
||||
.filter(patient::Column::TenantId.eq(tenant_id))
|
||||
.all(&state.db)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|p| (p.id, p.name))
|
||||
.collect()
|
||||
} else {
|
||||
std::collections::HashMap::new()
|
||||
};
|
||||
|
||||
let items = models.into_iter().map(|m| AlertResponse {
|
||||
id: m.id,
|
||||
patient_id: m.patient_id,
|
||||
patient_name: patient_names.get(&m.patient_id).cloned(),
|
||||
rule_id: m.rule_id,
|
||||
severity: m.severity,
|
||||
title: m.title,
|
||||
detail: m.detail,
|
||||
status: m.status,
|
||||
acknowledged_by: m.acknowledged_by,
|
||||
acknowledged_at: m.acknowledged_at,
|
||||
resolved_at: m.resolved_at,
|
||||
created_at: m.created_at,
|
||||
version: m.version,
|
||||
}).collect();
|
||||
|
||||
Ok((items, total))
|
||||
}
|
||||
|
||||
|
||||
@@ -142,8 +142,43 @@ pub async fn list_sessions(
|
||||
.all(&state.db)
|
||||
.await?;
|
||||
|
||||
// 批量查询 patient_name 和 doctor_name
|
||||
let patient_ids: std::collections::HashSet<Uuid> = models.iter().map(|m| m.patient_id).collect();
|
||||
let doctor_ids: std::collections::HashSet<Uuid> = models.iter().filter_map(|m| m.doctor_id).collect();
|
||||
|
||||
let patient_names: std::collections::HashMap<Uuid, String> = if !patient_ids.is_empty() {
|
||||
patient::Entity::find()
|
||||
.filter(patient::Column::Id.is_in(patient_ids))
|
||||
.filter(patient::Column::TenantId.eq(tenant_id))
|
||||
.all(&state.db)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|p| (p.id, p.name))
|
||||
.collect()
|
||||
} else {
|
||||
std::collections::HashMap::new()
|
||||
};
|
||||
|
||||
let doctor_names: std::collections::HashMap<Uuid, String> = if !doctor_ids.is_empty() {
|
||||
doctor_profile::Entity::find()
|
||||
.filter(doctor_profile::Column::Id.is_in(doctor_ids))
|
||||
.filter(doctor_profile::Column::TenantId.eq(tenant_id))
|
||||
.all(&state.db)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|d| (d.id, d.name))
|
||||
.collect()
|
||||
} else {
|
||||
std::collections::HashMap::new()
|
||||
};
|
||||
|
||||
let total_pages = total.div_ceil(limit.max(1));
|
||||
let data = models.into_iter().map(model_to_session_resp).collect();
|
||||
let data = models.into_iter().map(|m| {
|
||||
let mut resp = model_to_session_resp(m.clone());
|
||||
resp.patient_name = patient_names.get(&m.patient_id).cloned();
|
||||
resp.doctor_name = m.doctor_id.and_then(|did| doctor_names.get(&did).cloned());
|
||||
resp
|
||||
}).collect();
|
||||
|
||||
Ok(PaginatedResponse { data, total, page, page_size: limit, total_pages })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user