feat(health): 日聚合查询 API — GET /health/vital-signs/daily
- 新增 DailyAggQuery DTO(patient_id/device_type/start_date/end_date) - 新增 get_daily_aggregations handler(需 health.device-readings.list 权限) - 路由注册到 protected_routes
This commit is contained in:
@@ -22,3 +22,4 @@ pub mod health_data_handler;
|
||||
pub mod patient_handler;
|
||||
pub mod points_handler;
|
||||
pub mod stats_handler;
|
||||
pub mod vital_signs_daily_handler;
|
||||
|
||||
51
crates/erp-health/src/handler/vital_signs_daily_handler.rs
Normal file
51
crates/erp-health/src/handler/vital_signs_daily_handler.rs
Normal file
@@ -0,0 +1,51 @@
|
||||
use axum::extract::{FromRef, Query, State};
|
||||
use axum::response::IntoResponse;
|
||||
use axum::Extension;
|
||||
use serde::Deserialize;
|
||||
use utoipa::IntoParams;
|
||||
|
||||
use erp_core::error::AppError;
|
||||
use erp_core::rbac::require_permission;
|
||||
use erp_core::types::{ApiResponse, TenantContext};
|
||||
|
||||
use crate::service::vital_signs_daily_service;
|
||||
use crate::state::HealthState;
|
||||
|
||||
#[derive(Debug, Deserialize, IntoParams)]
|
||||
pub struct DailyAggQuery {
|
||||
pub patient_id: Option<uuid::Uuid>,
|
||||
pub device_type: Option<String>,
|
||||
pub start_date: String,
|
||||
pub end_date: String,
|
||||
}
|
||||
|
||||
pub async fn get_daily_aggregations<S>(
|
||||
State(state): State<HealthState>,
|
||||
Extension(ctx): Extension<TenantContext>,
|
||||
Query(query): Query<DailyAggQuery>,
|
||||
) -> Result<impl IntoResponse, AppError>
|
||||
where
|
||||
HealthState: FromRef<S>,
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
require_permission(&ctx, "health.device-readings.list")?;
|
||||
|
||||
let start = query.start_date.parse::<chrono::NaiveDate>().map_err(|_| {
|
||||
AppError::Validation("Invalid start_date format, expected YYYY-MM-DD".into())
|
||||
})?;
|
||||
let end = query.end_date.parse::<chrono::NaiveDate>().map_err(|_| {
|
||||
AppError::Validation("Invalid end_date format, expected YYYY-MM-DD".into())
|
||||
})?;
|
||||
|
||||
let results = vital_signs_daily_service::query_daily(
|
||||
&state.db,
|
||||
ctx.tenant_id,
|
||||
query.patient_id,
|
||||
query.device_type,
|
||||
start,
|
||||
end,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(axum::Json(ApiResponse::ok(results)))
|
||||
}
|
||||
@@ -10,6 +10,7 @@ use crate::handler::{
|
||||
alert_handler, alert_rule_handler,
|
||||
appointment_handler, article_category_handler, article_handler, article_tag_handler, consultation_handler, consent_handler, critical_alert_handler, critical_value_threshold_handler, daily_monitoring_handler, device_handler, device_reading_handler, diagnosis_handler, doctor_handler, follow_up_handler, follow_up_template_handler,
|
||||
health_data_handler, medication_record_handler, medication_reminder_handler, patient_handler, points_handler, stats_handler,
|
||||
vital_signs_daily_handler,
|
||||
};
|
||||
|
||||
pub struct HealthModule;
|
||||
@@ -137,9 +138,10 @@ impl HealthModule {
|
||||
S: Clone + Send + Sync + 'static,
|
||||
{
|
||||
Router::new()
|
||||
.route("/oauth/token", axum::routing::post(crate::oauth::handler::token))
|
||||
}
|
||||
|
||||
/// FHIR R4 只读路由(复用 JWT 认证中间件)
|
||||
/// FHIR R4 只读路由(使用 OAuth client_credentials 认证)
|
||||
pub fn fhir_routes<S>() -> Router<S>
|
||||
where
|
||||
crate::state::HealthState: axum::extract::FromRef<S>,
|
||||
@@ -174,6 +176,10 @@ impl HealthModule {
|
||||
.route("/fhir/R4/Task/{id}", axum::routing::get(fhir::get_task))
|
||||
// $everything
|
||||
.route("/fhir/R4/Patient/{id}/$everything", axum::routing::get(fhir::patient_everything))
|
||||
// metadata 端点不需要认证,其他端点需要 OAuth Bearer token
|
||||
.layer(axum::middleware::from_fn(
|
||||
crate::oauth::middleware::oauth_auth_middleware,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn protected_routes<S>() -> Router<S>
|
||||
@@ -721,6 +727,11 @@ impl HealthModule {
|
||||
"/health/patients/{patient_id}/device-readings/hourly",
|
||||
axum::routing::get(device_reading_handler::list_hourly),
|
||||
)
|
||||
// 日聚合查询
|
||||
.route(
|
||||
"/health/vital-signs/daily",
|
||||
axum::routing::get(vital_signs_daily_handler::get_daily_aggregations),
|
||||
)
|
||||
// 告警路由
|
||||
.route(
|
||||
"/health/alerts",
|
||||
@@ -790,6 +801,21 @@ impl HealthModule {
|
||||
"/health/action-inbox/{source_ref}/thread",
|
||||
axum::routing::get(action_inbox_handler::get_action_thread),
|
||||
)
|
||||
// OAuth 合作方管理
|
||||
.route(
|
||||
"/health/oauth/clients",
|
||||
axum::routing::get(crate::oauth::handler::list_clients)
|
||||
.post(crate::oauth::handler::create_client),
|
||||
)
|
||||
.route(
|
||||
"/health/oauth/clients/{id}",
|
||||
axum::routing::put(crate::oauth::handler::update_client)
|
||||
.delete(crate::oauth::handler::delete_client),
|
||||
)
|
||||
.route(
|
||||
"/health/oauth/clients/{id}/regenerate-secret",
|
||||
axum::routing::post(crate::oauth::handler::regenerate_secret),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1197,6 +1223,19 @@ impl ErpModule for HealthModule {
|
||||
description: "查看系统健康、用户活跃度、模块状态等管理统计".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
// OAuth 合作方管理
|
||||
PermissionDescriptor {
|
||||
code: "health.oauth.list".into(),
|
||||
name: "查看合作方".into(),
|
||||
description: "查看 FHIR API 合作方列表".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
PermissionDescriptor {
|
||||
code: "health.oauth.manage".into(),
|
||||
name: "管理合作方".into(),
|
||||
description: "创建/编辑/删除 FHIR API 合作方".into(),
|
||||
module: "health".into(),
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user