use axum::Extension; use axum::extract::{FromRef, Json, Path, Query, State}; use serde::Deserialize; use utoipa::IntoParams; use uuid::Uuid; use erp_core::error::AppError; use erp_core::rbac::require_permission; use erp_core::types::{ApiResponse, PaginatedResponse, TenantContext}; use crate::dto::health_data_dto::*; use crate::dto::DeleteWithVersion; use crate::service::health_data_service; use crate::service::trend_service; use crate::state::HealthState; // --------------------------------------------------------------------------- // 查询参数 // --------------------------------------------------------------------------- #[derive(Debug, Deserialize, IntoParams)] pub struct PaginationParams { pub page: Option, pub page_size: Option, } #[derive(Debug, Deserialize, IntoParams)] pub struct IndicatorTimeseriesParams { pub start_date: Option, pub end_date: Option, } #[derive(Debug, serde::Deserialize, utoipa::ToSchema)] pub struct GenerateTrendReq { pub period_start: chrono::NaiveDate, pub period_end: chrono::NaiveDate, } #[derive(Debug, serde::Deserialize, utoipa::ToSchema)] pub struct UpdateWithVersion { pub data: T, pub version: i32, } // --------------------------------------------------------------------------- // 生命体征 // --------------------------------------------------------------------------- pub async fn list_vital_signs( State(state): State, Extension(ctx): Extension, Path(patient_id): Path, Query(params): Query, ) -> Result>>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.list")?; let page = params.page.unwrap_or(1); let page_size = params.page_size.unwrap_or(20); let result = health_data_service::list_vital_signs( &state, ctx.tenant_id, patient_id, page, page_size, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn create_vital_signs( State(state): State, Extension(ctx): Extension, Path(patient_id): Path, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.manage")?; let mut req = req; req.sanitize(); let result = health_data_service::create_vital_signs( &state, ctx.tenant_id, patient_id, Some(ctx.user_id), req, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn update_vital_signs( State(state): State, Extension(ctx): Extension, Path((patient_id, vid)): Path<(Uuid, Uuid)>, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.manage")?; let mut data = req.data; data.sanitize(); let result = health_data_service::update_vital_signs( &state, ctx.tenant_id, patient_id, vid, Some(ctx.user_id), data, req.version, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn delete_vital_signs( State(state): State, Extension(ctx): Extension, Path((_patient_id, vid)): Path<(Uuid, Uuid)>, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.manage")?; health_data_service::delete_vital_signs(&state, ctx.tenant_id, vid, Some(ctx.user_id), req.version).await?; Ok(Json(ApiResponse::ok(()))) } // --------------------------------------------------------------------------- // 化验报告 // --------------------------------------------------------------------------- pub async fn list_lab_reports( State(state): State, Extension(ctx): Extension, Path(patient_id): Path, Query(params): Query, ) -> Result>>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.list")?; let page = params.page.unwrap_or(1); let page_size = params.page_size.unwrap_or(20); let result = health_data_service::list_lab_reports( &state, ctx.tenant_id, patient_id, page, page_size, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn create_lab_report( State(state): State, Extension(ctx): Extension, Path(patient_id): Path, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.manage")?; let mut req = req; req.sanitize(); let result = health_data_service::create_lab_report( &state, ctx.tenant_id, patient_id, Some(ctx.user_id), req, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn update_lab_report( State(state): State, Extension(ctx): Extension, Path((_patient_id, rid)): Path<(Uuid, Uuid)>, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.manage")?; let mut data = req.data; data.sanitize(); let result = health_data_service::update_lab_report( &state, ctx.tenant_id, _patient_id, rid, Some(ctx.user_id), data, req.version, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn delete_lab_report( State(state): State, Extension(ctx): Extension, Path((_patient_id, rid)): Path<(Uuid, Uuid)>, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.manage")?; health_data_service::delete_lab_report(&state, ctx.tenant_id, rid, Some(ctx.user_id), req.version).await?; Ok(Json(ApiResponse::ok(()))) } pub async fn review_lab_report( State(state): State, Extension(ctx): Extension, Path((_patient_id, rid)): Path<(Uuid, Uuid)>, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.manage")?; let mut data = req.data; data.sanitize(); let result = health_data_service::review_lab_report( &state, ctx.tenant_id, _patient_id, rid, ctx.user_id, data, req.version, ) .await?; Ok(Json(ApiResponse::ok(result))) } // --------------------------------------------------------------------------- // 健康档案 // --------------------------------------------------------------------------- pub async fn list_health_records( State(state): State, Extension(ctx): Extension, Path(patient_id): Path, Query(params): Query, ) -> Result>>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.list")?; let page = params.page.unwrap_or(1); let page_size = params.page_size.unwrap_or(20); let result = health_data_service::list_health_records( &state, ctx.tenant_id, patient_id, page, page_size, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn create_health_record( State(state): State, Extension(ctx): Extension, Path(patient_id): Path, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.manage")?; let mut req = req; req.sanitize(); let result = health_data_service::create_health_record( &state, ctx.tenant_id, patient_id, Some(ctx.user_id), req, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn update_health_record( State(state): State, Extension(ctx): Extension, Path((patient_id, rid)): Path<(Uuid, Uuid)>, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.manage")?; let mut data = req.data; data.sanitize(); let result = health_data_service::update_health_record( &state, ctx.tenant_id, patient_id, rid, Some(ctx.user_id), data, req.version, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn delete_health_record( State(state): State, Extension(ctx): Extension, Path((_patient_id, rid)): Path<(Uuid, Uuid)>, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.manage")?; health_data_service::delete_health_record(&state, ctx.tenant_id, rid, Some(ctx.user_id), req.version).await?; Ok(Json(ApiResponse::ok(()))) } // --------------------------------------------------------------------------- // 趋势分析 // --------------------------------------------------------------------------- pub async fn list_trends( State(state): State, Extension(ctx): Extension, Path(patient_id): Path, Query(params): Query, ) -> Result>>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.list")?; let page = params.page.unwrap_or(1); let page_size = params.page_size.unwrap_or(20); let result = trend_service::list_trends( &state, ctx.tenant_id, patient_id, page, page_size, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn generate_trend( State(state): State, Extension(ctx): Extension, Path(patient_id): Path, Json(req): Json, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.manage")?; let result = trend_service::generate_trend( &state, ctx.tenant_id, patient_id, Some(ctx.user_id), req.period_start, req.period_end, ) .await?; Ok(Json(ApiResponse::ok(result))) } pub async fn get_indicator_timeseries( State(state): State, Extension(ctx): Extension, Path((patient_id, indicator)): Path<(Uuid, String)>, Query(params): Query, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.list")?; let result = trend_service::get_indicator_timeseries( &state, ctx.tenant_id, patient_id, indicator, params.start_date, params.end_date, ) .await?; Ok(Json(ApiResponse::ok(result))) } // --------------------------------------------------------------------------- // 小程序趋势查询(通过当前用户关联 patient,无需传 patient_id) // --------------------------------------------------------------------------- pub async fn get_mini_trend( State(state): State, Extension(ctx): Extension, Query(params): Query, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.list")?; let result = trend_service::get_mini_trend( &state, ctx.tenant_id, ctx.user_id, params.indicator, params.range, ) .await?; Ok(Json(ApiResponse::ok(result))) } // --------------------------------------------------------------------------- // 小程序今日体征摘要(通过当前用户关联 patient,无需传 patient_id) // --------------------------------------------------------------------------- pub async fn get_mini_today( State(state): State, Extension(ctx): Extension, Query(params): Query, ) -> Result>, AppError> where HealthState: FromRef, S: Clone + Send + Sync + 'static, { require_permission(&ctx, "health.health-data.list")?; let result = trend_service::get_mini_today( &state, ctx.tenant_id, ctx.user_id, params.patient_id, ) .await?; Ok(Json(ApiResponse::ok(result))) } /// 小程序今日体征请求参数 #[derive(Debug, serde::Deserialize, utoipa::IntoParams)] pub struct MiniTodayParams { /// 可选:直接指定患者 ID(小程序传入当前选中患者) pub patient_id: Option, } // --------------------------------------------------------------------------- // 带版本号的更新请求包装 // --------------------------------------------------------------------------- #[derive(Debug, serde::Deserialize, utoipa::ToSchema)] pub struct UpdateVitalSignsWithVersion { #[serde(flatten)] pub data: UpdateVitalSignsReq, pub version: i32, } #[derive(Debug, serde::Deserialize, utoipa::ToSchema)] pub struct UpdateLabReportWithVersion { #[serde(flatten)] pub data: UpdateLabReportReq, pub version: i32, } #[derive(Debug, serde::Deserialize, utoipa::ToSchema)] pub struct UpdateHealthRecordWithVersion { #[serde(flatten)] pub data: UpdateHealthRecordReq, pub version: i32, } #[derive(Debug, serde::Deserialize, utoipa::ToSchema)] pub struct ReviewLabReportWithVersion { #[serde(flatten)] pub data: ReviewLabReportReq, pub version: i32, }