P0 (CRITICAL): - C1: 统计 API 全部改为 safe_aggregate 容错,防止单个子查询崩溃导致 500 - C2: Token 刷新增加用户身份验证,防止并发场景下身份切换 - C3: 患者端线下活动接口添加患者档案验证,防止 Doctor/HM 越权访问 P1 (HIGH): - H1: 操作记录用 EntityName 组件解析用户名,不再显示截断 UUID - H4: 告警标题添加中英文映射 (translateAlertTitle) - H5: 告警面板补全 message import + 修复 hooks 顺序 - H8: 咨询消息发送按钮添加 AuthButton 权限控制 - H9: routeConfig 日常监测权限码改为 health.daily-monitoring.* P2 (MEDIUM): - M4: 咨询类型映射补全 online/phone/doctor/follow_up 中文标签 DTO: LabReportStatisticsResp, AppointmentStatisticsResp, VitalSignsReportRateResp 添加 Default derive
260 lines
8.0 KiB
Rust
260 lines
8.0 KiB
Rust
use axum::Extension;
|
|
use axum::extract::{FromRef, Json, State};
|
|
use erp_core::aggregate::safe_aggregate;
|
|
use erp_core::error::AppError;
|
|
use erp_core::rbac::require_permission;
|
|
use erp_core::types::{ApiResponse, TenantContext};
|
|
|
|
use crate::dto::stats_dto::*;
|
|
use crate::service::stats_service;
|
|
use crate::state::HealthState;
|
|
|
|
pub async fn get_patient_stats<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<PatientStatisticsResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.patient.list")?;
|
|
let result = stats_service::get_patient_statistics(&state, ctx.tenant_id).await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn get_consultation_stats<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<ConsultationStatisticsResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.consultation.list")?;
|
|
let result = safe_aggregate(
|
|
stats_service::get_consultation_statistics(&state, ctx.tenant_id),
|
|
"咨询统计",
|
|
)
|
|
.await;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn get_follow_up_stats<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<FollowUpStatisticsResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.follow-up.list")?;
|
|
let result = stats_service::get_follow_up_statistics(&state, ctx.tenant_id).await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn get_dashboard_stats<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<DashboardStatsResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.patient.list")?;
|
|
|
|
let patients = safe_aggregate(
|
|
stats_service::get_patient_statistics(&state, ctx.tenant_id),
|
|
"患者统计",
|
|
)
|
|
.await;
|
|
|
|
let consultations = safe_aggregate(
|
|
stats_service::get_consultation_statistics(&state, ctx.tenant_id),
|
|
"咨询统计",
|
|
)
|
|
.await;
|
|
|
|
let follow_ups = safe_aggregate(
|
|
stats_service::get_follow_up_statistics(&state, ctx.tenant_id),
|
|
"随访统计",
|
|
)
|
|
.await;
|
|
|
|
Ok(Json(ApiResponse::ok(DashboardStatsResp {
|
|
patients,
|
|
consultations,
|
|
follow_ups,
|
|
})))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 健康数据统计
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub async fn get_lab_report_stats<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<LabReportStatisticsResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.patient.list")?;
|
|
let result = safe_aggregate(
|
|
stats_service::get_lab_report_statistics(&state, ctx.tenant_id),
|
|
"化验报告统计",
|
|
)
|
|
.await;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn get_appointment_stats<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<AppointmentStatisticsResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.patient.list")?;
|
|
let result = safe_aggregate(
|
|
stats_service::get_appointment_statistics(&state, ctx.tenant_id),
|
|
"预约统计",
|
|
)
|
|
.await;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn get_vital_signs_report_rate<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<VitalSignsReportRateResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.patient.list")?;
|
|
let result = safe_aggregate(
|
|
stats_service::get_vital_signs_report_rate(&state, ctx.tenant_id),
|
|
"体征上报率统计",
|
|
)
|
|
.await;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn get_health_data_stats<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<HealthDataStatsResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.patient.list")?;
|
|
let lab_reports = safe_aggregate(
|
|
stats_service::get_lab_report_statistics(&state, ctx.tenant_id),
|
|
"化验报告统计",
|
|
)
|
|
.await;
|
|
let appointments = safe_aggregate(
|
|
stats_service::get_appointment_statistics(&state, ctx.tenant_id),
|
|
"预约统计",
|
|
)
|
|
.await;
|
|
let vital_signs_report_rate = safe_aggregate(
|
|
stats_service::get_vital_signs_report_rate(&state, ctx.tenant_id),
|
|
"体征上报率统计",
|
|
)
|
|
.await;
|
|
Ok(Json(ApiResponse::ok(HealthDataStatsResp {
|
|
lab_reports,
|
|
appointments,
|
|
vital_signs_report_rate,
|
|
})))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 个人维度统计
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub async fn get_personal_stats<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<PersonalStatsResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.patient.list")?;
|
|
let result = stats_service::get_personal_stats(&state, ctx.user_id, ctx.tenant_id).await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 工作台管理统计
|
|
// ---------------------------------------------------------------------------
|
|
|
|
pub async fn get_system_health<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(_ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<SystemHealthResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
let result = stats_service::get_system_health(&state).await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn get_user_activity<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<UserActivityResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.dashboard.manage")?;
|
|
let result = stats_service::get_user_activity(&state.db, ctx.tenant_id).await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn get_module_status<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<Vec<ModuleStatusResp>>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.dashboard.manage")?;
|
|
let result = stats_service::get_module_status(&state).await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn get_points_recent_activity<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<Vec<PointsActivityItem>>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.dashboard.manage")?;
|
|
let result = stats_service::get_points_recent_activity(&state.db, ctx.tenant_id, 10).await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|
|
|
|
pub async fn get_article_stats<S>(
|
|
State(state): State<HealthState>,
|
|
Extension(ctx): Extension<TenantContext>,
|
|
) -> Result<Json<ApiResponse<ArticleStatsResp>>, AppError>
|
|
where
|
|
HealthState: FromRef<S>,
|
|
S: Clone + Send + Sync + 'static,
|
|
{
|
|
require_permission(&ctx, "health.dashboard.manage")?;
|
|
let result = stats_service::get_article_stats(&state.db, ctx.tenant_id).await?;
|
|
Ok(Json(ApiResponse::ok(result)))
|
|
}
|