diff --git a/crates/erp-health/src/dto/health_data_dto.rs b/crates/erp-health/src/dto/health_data_dto.rs index a1f1323..964c99d 100644 --- a/crates/erp-health/src/dto/health_data_dto.rs +++ b/crates/erp-health/src/dto/health_data_dto.rs @@ -171,3 +171,31 @@ pub struct MiniTrendResp { /// 数据点列表(按日期升序) pub data_points: Vec, } + +// --------------------------------------------------------------------------- +// 小程序今日体征摘要 +// --------------------------------------------------------------------------- + +/// 单个指标摘要 +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +pub struct IndicatorSummary { + /// 指标数值 + pub value: f64, + /// 状态: "normal" | "high" | "low" + pub status: String, + /// 参考范围,如 "60-100" + pub reference_range: Option, + /// 收缩压(仅血压指标) + pub systolic: Option, + /// 舒张压(仅血压指标) + pub diastolic: Option, +} + +/// 小程序今日体征摘要响应 +#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] +pub struct MiniTodayResp { + pub blood_pressure: Option, + pub heart_rate: Option, + pub blood_sugar: Option, + pub weight: Option, +} diff --git a/crates/erp-health/src/handler/health_data_handler.rs b/crates/erp-health/src/handler/health_data_handler.rs index a10acdb..3aa0ae4 100644 --- a/crates/erp-health/src/handler/health_data_handler.rs +++ b/crates/erp-health/src/handler/health_data_handler.rs @@ -351,6 +351,26 @@ where Ok(Json(ApiResponse::ok(result))) } +// --------------------------------------------------------------------------- +// 小程序今日体征摘要(通过当前用户关联 patient,无需传 patient_id) +// --------------------------------------------------------------------------- + +pub async fn get_mini_today( + State(state): State, + Extension(ctx): Extension, +) -> Result>, AppError> +where + HealthState: FromRef, + S: Clone + Send + Sync + 'static, +{ + require_permission(&ctx, "health.health-data.list")?; + let result = health_data_service::get_mini_today( + &state, ctx.tenant_id, ctx.user_id, + ) + .await?; + Ok(Json(ApiResponse::ok(result))) +} + // --------------------------------------------------------------------------- // 带版本号的更新请求包装 // --------------------------------------------------------------------------- diff --git a/crates/erp-health/src/module.rs b/crates/erp-health/src/module.rs index d03330a..a7f5830 100644 --- a/crates/erp-health/src/module.rs +++ b/crates/erp-health/src/module.rs @@ -117,6 +117,11 @@ impl HealthModule { "/health/vital-signs/trend", axum::routing::get(health_data_handler::get_mini_trend), ) + // 小程序今日体征摘要 + .route( + "/health/vital-signs/today", + axum::routing::get(health_data_handler::get_mini_today), + ) // 预约排班 .route( "/health/appointments", diff --git a/crates/erp-health/src/service/health_data_service.rs b/crates/erp-health/src/service/health_data_service.rs index b4ea7e6..03b8897 100644 --- a/crates/erp-health/src/service/health_data_service.rs +++ b/crates/erp-health/src/service/health_data_service.rs @@ -722,3 +722,112 @@ pub async fn get_mini_trend( data_points, }) } + +// --------------------------------------------------------------------------- +// 小程序今日体征摘要 +// --------------------------------------------------------------------------- + +/// 根据参考范围计算指标状态 +fn compute_status(value: f64, low: f64, high: f64) -> &'static str { + if value < low { + "low" + } else if value > high { + "high" + } else { + "normal" + } +} + +/// 查询今日最新体征记录并生成摘要 +pub async fn get_mini_today( + state: &HealthState, + tenant_id: Uuid, + user_id: Uuid, +) -> HealthResult { + let patient_id = find_patient_by_user_id(state, tenant_id, user_id).await?; + + let Some(patient_id) = patient_id else { + return Ok(MiniTodayResp { + blood_pressure: None, + heart_rate: None, + blood_sugar: None, + weight: None, + }); + }; + + let today = chrono::Local::now().date_naive(); + + // 查询今日最新体征记录 + let vital = vital_signs::Entity::find() + .filter(vital_signs::Column::TenantId.eq(tenant_id)) + .filter(vital_signs::Column::PatientId.eq(patient_id)) + .filter(vital_signs::Column::DeletedAt.is_null()) + .filter(vital_signs::Column::RecordDate.eq(today)) + .order_by_desc(vital_signs::Column::CreatedAt) + .one(&state.db) + .await?; + + let Some(v) = vital else { + return Ok(MiniTodayResp { + blood_pressure: None, + heart_rate: None, + blood_sugar: None, + weight: None, + }); + }; + + // 构建各指标摘要,优先使用晨间数据 + let blood_pressure = v.systolic_bp_morning.and_then(|sys| { + v.diastolic_bp_morning.map(|dia| { + let status = compute_status(sys as f64, 90.0, 140.0); + IndicatorSummary { + value: sys as f64, + status: status.to_string(), + reference_range: Some("90-140/60-90".to_string()), + systolic: Some(sys as f64), + diastolic: Some(dia as f64), + } + }) + }); + + let heart_rate = v.heart_rate.map(|hr| { + let status = compute_status(hr as f64, 60.0, 100.0); + IndicatorSummary { + value: hr as f64, + status: status.to_string(), + reference_range: Some("60-100".to_string()), + systolic: None, + diastolic: None, + } + }); + + let blood_sugar = v.blood_sugar.map(|bs| { + let val = bs.to_f64().unwrap_or(0.0); + let status = compute_status(val, 3.9, 6.1); + IndicatorSummary { + value: val, + status: status.to_string(), + reference_range: Some("3.9-6.1".to_string()), + systolic: None, + diastolic: None, + } + }); + + let weight = v.weight.map(|w| { + let val = w.to_f64().unwrap_or(0.0); + IndicatorSummary { + value: val, + status: "normal".to_string(), // 体重无通用参考范围 + reference_range: None, + systolic: None, + diastolic: None, + } + }); + + Ok(MiniTodayResp { + blood_pressure, + heart_rate, + blood_sugar, + weight, + }) +}