feat(health): consultation/follow_up 列表 API 内联 patient_name/doctor_name
consultation session list 添加 patient_name/doctor_name, follow_up task list 添加 patient_name,批量查询消除 N+1。 DTO 新增 Option 字段,向后兼容。
This commit is contained in:
@@ -8,6 +8,8 @@ pub struct SessionResp {
|
||||
pub id: Uuid,
|
||||
pub patient_id: Uuid,
|
||||
pub doctor_id: Option<Uuid>,
|
||||
pub patient_name: Option<String>,
|
||||
pub doctor_name: Option<String>,
|
||||
pub consultation_type: String,
|
||||
pub status: String,
|
||||
pub last_message_at: Option<chrono::DateTime<chrono::Utc>>,
|
||||
|
||||
@@ -49,6 +49,7 @@ pub struct FollowUpTaskResp {
|
||||
pub id: Uuid,
|
||||
pub patient_id: Uuid,
|
||||
pub assigned_to: Option<Uuid>,
|
||||
pub patient_name: Option<String>,
|
||||
pub follow_up_type: String,
|
||||
pub planned_date: NaiveDate,
|
||||
pub status: String,
|
||||
|
||||
@@ -12,7 +12,7 @@ use erp_core::error::check_version;
|
||||
use erp_core::types::PaginatedResponse;
|
||||
|
||||
use crate::dto::consultation_dto::*;
|
||||
use crate::entity::{consultation_message, consultation_session, patient};
|
||||
use crate::entity::{consultation_message, consultation_session, doctor_profile, patient};
|
||||
use crate::error::{HealthError, HealthResult};
|
||||
use crate::service::validation::{validate_sender_role, validate_content_type, validate_consultation_type};
|
||||
use crate::state::HealthState;
|
||||
@@ -25,6 +25,7 @@ use erp_core::crypto as pii;
|
||||
fn model_to_session_resp(m: consultation_session::Model) -> SessionResp {
|
||||
SessionResp {
|
||||
id: m.id, patient_id: m.patient_id, doctor_id: m.doctor_id,
|
||||
patient_name: None, doctor_name: None,
|
||||
consultation_type: m.consultation_type, status: m.status,
|
||||
last_message_at: m.last_message_at,
|
||||
unread_count_patient: m.unread_count_patient,
|
||||
@@ -216,8 +217,43 @@ pub async fn export_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_num, page_size: limit, total_pages })
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use erp_core::types::PaginatedResponse;
|
||||
|
||||
use crate::dto::follow_up_dto::*;
|
||||
use crate::entity::{follow_up_record, follow_up_task, patient};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use crate::error::{HealthError, HealthResult};
|
||||
use crate::service::validation::validate_follow_up_type;
|
||||
use crate::state::HealthState;
|
||||
@@ -51,8 +52,25 @@ pub async fn list_tasks(
|
||||
.await?;
|
||||
|
||||
let total_pages = total.div_ceil(limit.max(1));
|
||||
|
||||
// 批量查询 patient_name
|
||||
let patient_ids: HashSet<Uuid> = models.iter().map(|m| m.patient_id).collect();
|
||||
let patient_names: 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 {
|
||||
HashMap::new()
|
||||
};
|
||||
|
||||
let data = models.into_iter().map(|m| FollowUpTaskResp {
|
||||
id: m.id, patient_id: m.patient_id, assigned_to: m.assigned_to,
|
||||
patient_name: patient_names.get(&m.patient_id).cloned(),
|
||||
follow_up_type: m.follow_up_type, planned_date: m.planned_date,
|
||||
status: m.status, content_template: m.content_template,
|
||||
related_appointment_id: m.related_appointment_id,
|
||||
@@ -77,6 +95,7 @@ pub async fn get_task(
|
||||
|
||||
Ok(FollowUpTaskResp {
|
||||
id: m.id, patient_id: m.patient_id, assigned_to: m.assigned_to,
|
||||
patient_name: None,
|
||||
follow_up_type: m.follow_up_type, planned_date: m.planned_date,
|
||||
status: m.status, content_template: m.content_template,
|
||||
related_appointment_id: m.related_appointment_id,
|
||||
@@ -137,6 +156,7 @@ pub async fn create_task(
|
||||
|
||||
Ok(FollowUpTaskResp {
|
||||
id: m.id, patient_id: m.patient_id, assigned_to: m.assigned_to,
|
||||
patient_name: None,
|
||||
follow_up_type: m.follow_up_type, planned_date: m.planned_date,
|
||||
status: m.status, content_template: m.content_template,
|
||||
related_appointment_id: m.related_appointment_id,
|
||||
@@ -207,6 +227,7 @@ pub async fn update_task(
|
||||
|
||||
Ok(FollowUpTaskResp {
|
||||
id: m.id, patient_id: m.patient_id, assigned_to: m.assigned_to,
|
||||
patient_name: None,
|
||||
follow_up_type: m.follow_up_type, planned_date: m.planned_date,
|
||||
status: m.status, content_template: m.content_template,
|
||||
related_appointment_id: m.related_appointment_id,
|
||||
|
||||
Reference in New Issue
Block a user