use axum::Router; use uuid::Uuid; use erp_core::error::AppResult; use erp_core::events::EventBus; use erp_core::module::{ErpModule, PermissionDescriptor}; use crate::handler::{ action_inbox_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, }; pub struct HealthModule; impl HealthModule { pub fn new() -> Self { Self } /// 启动定时逾期随访检查(每 6 小时运行一次),返回 JoinHandle 用于优雅关闭 pub fn start_overdue_checker(db: sea_orm::DatabaseConnection) -> tokio::task::JoinHandle<()> { tokio::spawn(async move { let mut interval = tokio::time::interval(std::time::Duration::from_secs(6 * 3600)); loop { tokio::select! { _ = interval.tick() => { match crate::service::follow_up_service::check_overdue_tasks(&db).await { Ok(count) if count > 0 => tracing::info!(count = count, "随访逾期检查完成"), Ok(_) => {} Err(e) => tracing::warn!(error = %e, "随访逾期检查失败"), } } _ = tokio::signal::ctrl_c() => { tracing::info!("随访逾期检查任务收到关闭信号,正在停止"); break; } } } }) } /// 启动积分过期清理(每 24 小时运行一次),返回 JoinHandle 用于优雅关闭 pub fn start_points_expiration_checker(db: sea_orm::DatabaseConnection, event_bus: erp_core::events::EventBus) -> tokio::task::JoinHandle<()> { tokio::spawn(async move { let mut interval = tokio::time::interval(std::time::Duration::from_secs(24 * 3600)); loop { tokio::select! { _ = interval.tick() => { match crate::service::points_service::expire_points(&db, &event_bus).await { Ok(count) if count > 0 => tracing::info!(count = count, "积分过期清理完成"), Ok(_) => {} Err(e) => tracing::warn!(error = %e, "积分过期清理失败"), } } _ = tokio::signal::ctrl_c() => { tracing::info!("积分过期清理任务收到关闭信号,正在停止"); break; } } } }) } /// 启动预约提醒调度(每 1 小时运行一次),扫描明天有预约的患者发送提醒 pub fn start_appointment_reminder(db: sea_orm::DatabaseConnection, event_bus: erp_core::events::EventBus) -> tokio::task::JoinHandle<()> { tokio::spawn(async move { let mut interval = tokio::time::interval(std::time::Duration::from_secs(3600)); loop { tokio::select! { _ = interval.tick() => { match crate::service::appointment_service::send_reminders(&db, &event_bus).await { Ok(count) if count > 0 => tracing::info!(count = count, "预约提醒发送完成"), Ok(_) => {} Err(e) => tracing::warn!(error = %e, "预约提醒发送失败"), } } _ = tokio::signal::ctrl_c() => { tracing::info!("预约提醒调度任务收到关闭信号,正在停止"); break; } } } }) } /// 启动设备原始数据清理(每 24 小时运行一次),删除超过 90 天的 device_readings pub fn start_device_readings_cleanup(db: sea_orm::DatabaseConnection) -> tokio::task::JoinHandle<()> { tokio::spawn(async move { let mut interval = tokio::time::interval(std::time::Duration::from_secs(24 * 3600)); loop { tokio::select! { _ = interval.tick() => { match crate::service::device_reading_service::cleanup_stale_readings(&db).await { Ok(count) if count > 0 => tracing::info!(count = count, "设备原始数据清理完成"), Ok(_) => {} Err(e) => tracing::warn!(error = %e, "设备原始数据清理失败"), } } _ = tokio::signal::ctrl_c() => { tracing::info!("设备原始数据清理任务收到关闭信号,正在停止"); break; } } } }) } pub fn public_routes() -> Router where crate::state::HealthState: axum::extract::FromRef, S: Clone + Send + Sync + 'static, { Router::new() } pub fn protected_routes() -> Router where crate::state::HealthState: axum::extract::FromRef, S: Clone + Send + Sync + 'static, { Router::new() // 患者管理 .route( "/health/patients", axum::routing::get(patient_handler::list_patients) .post(patient_handler::create_patient), ) .route( "/health/patients/{id}", axum::routing::get(patient_handler::get_patient) .put(patient_handler::update_patient) .delete(patient_handler::delete_patient), ) .route( "/health/patients/{id}/tags", axum::routing::post(patient_handler::manage_patient_tags), ) .route( "/health/patient-tags", axum::routing::get(patient_handler::list_tags) .post(patient_handler::create_tag), ) .route( "/health/patient-tags/{id}", axum::routing::put(patient_handler::update_tag) .delete(patient_handler::delete_tag), ) .route( "/health/patients/{id}/health-summary", axum::routing::get(patient_handler::get_health_summary), ) .route( "/health/patients/{id}/family-members", axum::routing::get(patient_handler::list_family_members) .post(patient_handler::create_family_member), ) .route( "/health/patients/{id}/family-members/{fid}", axum::routing::put(patient_handler::update_family_member) .delete(patient_handler::delete_family_member), ) .route( "/health/patients/{id}/doctors", axum::routing::post(patient_handler::assign_doctor), ) .route( "/health/patients/{id}/doctors/{did}", axum::routing::delete(patient_handler::remove_doctor), ) // 健康数据 .route( "/health/patients/{id}/vital-signs", axum::routing::get(health_data_handler::list_vital_signs) .post(health_data_handler::create_vital_signs), ) // 诊断记录 .route( "/health/patients/{id}/diagnoses", axum::routing::get(diagnosis_handler::list_diagnoses) .post(diagnosis_handler::create_diagnosis), ) .route( "/health/diagnoses/{id}", axum::routing::put(diagnosis_handler::update_diagnosis) .delete(diagnosis_handler::delete_diagnosis), ) // 用药记录 .route( "/health/patients/{id}/medications", axum::routing::get(medication_record_handler::list_medications), ) .route( "/health/medications", axum::routing::post(medication_record_handler::create_medication), ) .route( "/health/medications/{id}", axum::routing::get(medication_record_handler::get_medication) .put(medication_record_handler::update_medication) .delete(medication_record_handler::delete_medication), ) // 药物提醒 CRUD .route( "/health/patients/{id}/medication-reminders", axum::routing::get(medication_reminder_handler::list_reminders), ) .route( "/health/medication-reminders", axum::routing::post(medication_reminder_handler::create_reminder), ) .route( "/health/medication-reminders/{id}", axum::routing::put(medication_reminder_handler::update_reminder) .delete(medication_reminder_handler::delete_reminder), ) .route( "/health/patients/{id}/vital-signs/{vid}", axum::routing::put(health_data_handler::update_vital_signs) .delete(health_data_handler::delete_vital_signs), ) .route( "/health/patients/{id}/lab-reports", axum::routing::get(health_data_handler::list_lab_reports) .post(health_data_handler::create_lab_report), ) .route( "/health/patients/{id}/lab-reports/{rid}", axum::routing::put(health_data_handler::update_lab_report) .delete(health_data_handler::delete_lab_report), ) .route( "/health/patients/{id}/health-records", axum::routing::get(health_data_handler::list_health_records) .post(health_data_handler::create_health_record), ) .route( "/health/patients/{id}/health-records/{rid}", axum::routing::put(health_data_handler::update_health_record) .delete(health_data_handler::delete_health_record), ) .route( "/health/patients/{id}/trends", axum::routing::get(health_data_handler::list_trends), ) .route( "/health/patients/{id}/trends/generate", axum::routing::post(health_data_handler::generate_trend), ) .route( "/health/patients/{id}/trends/{indicator}", axum::routing::get(health_data_handler::get_indicator_timeseries), ) // 小程序趋势查询(通过 JWT user_id 关联 patient,无需传 patient_id) .route( "/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/follow-up-templates", axum::routing::get(follow_up_template_handler::list_templates) .post(follow_up_template_handler::create_template), ) .route( "/health/follow-up-templates/{id}", axum::routing::get(follow_up_template_handler::get_template) .put(follow_up_template_handler::update_template) .delete(follow_up_template_handler::delete_template), ) // 日常监测 .route( "/health/patients/{id}/daily-monitoring", axum::routing::get(daily_monitoring_handler::list_daily_monitoring), ) .route( "/health/daily-monitoring", axum::routing::post(daily_monitoring_handler::create_daily_monitoring), ) .route( "/health/daily-monitoring/{id}", axum::routing::get(daily_monitoring_handler::get_daily_monitoring) .put(daily_monitoring_handler::update_daily_monitoring) .delete(daily_monitoring_handler::delete_daily_monitoring), ) // 化验报告审阅 .route( "/health/patients/{id}/lab-reports/{rid}/review", axum::routing::put(health_data_handler::review_lab_report), ) // 预约排班 .route( "/health/appointments", axum::routing::get(appointment_handler::list_appointments) .post(appointment_handler::create_appointment), ) .route( "/health/appointments/{id}/status", axum::routing::put(appointment_handler::update_appointment_status), ) .route( "/health/appointments/{id}", axum::routing::get(appointment_handler::get_appointment), ) .route( "/health/doctor-schedules", axum::routing::get(appointment_handler::list_schedules) .post(appointment_handler::create_schedule), ) .route( "/health/doctor-schedules/{id}", axum::routing::put(appointment_handler::update_schedule), ) .route( "/health/doctor-schedules/calendar", axum::routing::get(appointment_handler::calendar_view), ) // 随访管理 .route( "/health/follow-up-tasks", axum::routing::get(follow_up_handler::list_tasks) .post(follow_up_handler::create_task), ) .route( "/health/follow-up-tasks/{id}", axum::routing::get(follow_up_handler::get_task) .put(follow_up_handler::update_task) .delete(follow_up_handler::delete_task), ) .route( "/health/follow-up-tasks/{id}/records", axum::routing::post(follow_up_handler::create_record), ) .route( "/health/follow-up-records", axum::routing::get(follow_up_handler::list_records), ) // 随访批量操作 .route( "/health/follow-up-tasks/batch-create", axum::routing::post(follow_up_handler::batch_create_tasks), ) .route( "/health/follow-up-tasks/batch-assign", axum::routing::post(follow_up_handler::batch_assign_tasks), ) .route( "/health/follow-up-tasks/batch-complete", axum::routing::post(follow_up_handler::batch_complete_tasks), ) // 咨询管理 .route( "/health/consultation-sessions", axum::routing::get(consultation_handler::list_sessions) .post(consultation_handler::create_session), ) .route( "/health/consultation-sessions/export", axum::routing::get(consultation_handler::export_sessions), ) .route( "/health/consultation-sessions/{id}", axum::routing::get(consultation_handler::get_session), ) .route( "/health/consultation-sessions/{id}/messages", axum::routing::get(consultation_handler::list_messages), ) .route( "/health/consultation-sessions/{id}/close", axum::routing::put(consultation_handler::close_session), ) .route( "/health/consultation-sessions/{id}/read", axum::routing::put(consultation_handler::mark_session_read), ) .route( "/health/consultation-messages", axum::routing::post(consultation_handler::create_message), ) // 医生仪表盘 .route( "/health/doctor/dashboard", axum::routing::get(consultation_handler::get_doctor_dashboard), ) // 医护管理 .route( "/health/doctors", axum::routing::get(doctor_handler::list_doctors) .post(doctor_handler::create_doctor), ) .route( "/health/doctors/{id}", axum::routing::get(doctor_handler::get_doctor) .put(doctor_handler::update_doctor) .delete(doctor_handler::delete_doctor), ) // 健康资讯 .route( "/health/articles", axum::routing::get(article_handler::list_articles) .post(article_handler::create_article), ) .route( "/health/articles/{id}", axum::routing::get(article_handler::get_article) .put(article_handler::update_article) .delete(article_handler::delete_article), ) // 资讯审核工作流 .route( "/health/articles/{id}/submit", axum::routing::post(article_handler::submit_article), ) .route( "/health/articles/{id}/approve", axum::routing::post(article_handler::approve_article), ) .route( "/health/articles/{id}/reject", axum::routing::post(article_handler::reject_article), ) .route( "/health/articles/{id}/unpublish", axum::routing::post(article_handler::unpublish_article), ) .route( "/health/articles/{id}/view", axum::routing::post(article_handler::view_article), ) .route( "/health/articles/{id}/revisions", axum::routing::get(article_handler::list_revisions), ) // 资讯分类 .route( "/health/article-categories", axum::routing::get(article_category_handler::list_categories) .post(article_category_handler::create_category), ) .route( "/health/article-categories/{id}", axum::routing::put(article_category_handler::update_category) .delete(article_category_handler::delete_category), ) // 资讯标签 .route( "/health/article-tags", axum::routing::get(article_tag_handler::list_tags) .post(article_tag_handler::create_tag), ) .route( "/health/article-tags/{id}", axum::routing::put(article_tag_handler::update_tag) .delete(article_tag_handler::delete_tag), ) // 积分商城 — 患者端 .route( "/health/points/account", axum::routing::get(points_handler::get_my_account), ) .route( "/health/points/checkin", axum::routing::post(points_handler::daily_checkin), ) .route( "/health/points/checkin/status", axum::routing::get(points_handler::get_checkin_status), ) .route( "/health/points/transactions", axum::routing::get(points_handler::list_my_transactions), ) .route( "/health/points/products", axum::routing::get(points_handler::list_products), ) .route( "/health/points/products/{id}", axum::routing::get(points_handler::get_product), ) .route( "/health/points/exchange", axum::routing::post(points_handler::exchange_product), ) .route( "/health/points/orders", axum::routing::get(points_handler::list_my_orders), ) // 线下活动 — 患者端 .route( "/health/offline-events", axum::routing::get(points_handler::list_offline_events), ) .route( "/health/offline-events/{id}/register", axum::routing::post(points_handler::register_event), ) // 积分商城 — 管理端 .route( "/health/points/verify", axum::routing::post(points_handler::verify_order), ) .route( "/health/admin/points/rules", axum::routing::get(points_handler::list_rules) .post(points_handler::create_rule), ) .route( "/health/admin/points/rules/{id}", axum::routing::put(points_handler::update_rule) .delete(points_handler::delete_rule), ) .route( "/health/admin/points/products", axum::routing::get(points_handler::admin_list_products) .post(points_handler::admin_create_product), ) .route( "/health/admin/points/products/{id}", axum::routing::put(points_handler::admin_update_product) .delete(points_handler::admin_delete_product), ) .route( "/health/admin/points/orders", axum::routing::get(points_handler::admin_list_orders), ) // 积分账户 — 管理端按患者查询 .route( "/health/admin/points/patients/{patient_id}/account", axum::routing::get(points_handler::admin_get_patient_account), ) .route( "/health/admin/points/patients/{patient_id}/transactions", axum::routing::get(points_handler::admin_list_patient_transactions), ) // 线下活动 — 管理端 .route( "/health/admin/offline-events", axum::routing::get(points_handler::admin_list_events) .post(points_handler::admin_create_event), ) .route( "/health/admin/offline-events/{id}", axum::routing::put(points_handler::admin_update_event) .delete(points_handler::admin_delete_event), ) .route( "/health/admin/offline-events/{id}/checkin", axum::routing::post(points_handler::admin_checkin_event), ) // 积分统计 — 管理端 .route( "/health/admin/points/statistics", axum::routing::get(points_handler::get_points_statistics), ) // 统计数据 — 管理端 .route( "/health/admin/statistics/patients", axum::routing::get(stats_handler::get_patient_stats), ) .route( "/health/admin/statistics/consultations", axum::routing::get(stats_handler::get_consultation_stats), ) .route( "/health/admin/statistics/follow-ups", axum::routing::get(stats_handler::get_follow_up_stats), ) .route( "/health/admin/statistics/dashboard", axum::routing::get(stats_handler::get_dashboard_stats), ) .route( "/health/admin/statistics/lab-reports", axum::routing::get(stats_handler::get_lab_report_stats), ) .route( "/health/admin/statistics/appointments", axum::routing::get(stats_handler::get_appointment_stats), ) .route( "/health/admin/statistics/vital-signs-report-rate", axum::routing::get(stats_handler::get_vital_signs_report_rate), ) .route( "/health/admin/statistics/health-data", axum::routing::get(stats_handler::get_health_data_stats), ) .route( "/health/admin/statistics/personal-stats", axum::routing::get(stats_handler::get_personal_stats), ) // 工作台管理统计 API .route( "/health/admin/system-health", axum::routing::get(stats_handler::get_system_health), ) .route( "/health/admin/user-activity", axum::routing::get(stats_handler::get_user_activity), ) .route( "/health/admin/modules", axum::routing::get(stats_handler::get_module_status), ) .route( "/health/points/recent-activity", axum::routing::get(stats_handler::get_points_recent_activity), ) .route( "/health/articles/stats", axum::routing::get(stats_handler::get_article_stats), ) // 危急值阈值配置 .route( "/health/critical-value-thresholds", axum::routing::get(critical_value_threshold_handler::list_thresholds) .post(critical_value_threshold_handler::create_threshold), ) .route( "/health/critical-value-thresholds/{id}", axum::routing::put(critical_value_threshold_handler::update_threshold) .delete(critical_value_threshold_handler::delete_threshold), ) // 患者端只读阈值(仅需认证) .route( "/health/critical-value-thresholds/public", axum::routing::get(critical_value_threshold_handler::list_public_thresholds), ) // 知情同意记录 .route( "/health/patients/{patient_id}/consents", axum::routing::get(consent_handler::list_consents), ) .route( "/health/consents", axum::routing::post(consent_handler::grant_consent), ) .route( "/health/consents/{consent_id}/revoke", axum::routing::put(consent_handler::revoke_consent), ) // 设备数据采集 .route( "/health/patients/{patient_id}/device-readings/batch", axum::routing::post(device_reading_handler::batch_create), ) .route( "/health/patients/{patient_id}/device-readings", axum::routing::get(device_reading_handler::list_readings), ) .route( "/health/patients/{patient_id}/device-readings/hourly", axum::routing::get(device_reading_handler::list_hourly), ) // 告警路由 .route( "/health/alerts", axum::routing::get(alert_handler::list_alerts), ) .route( "/health/alerts/{id}/acknowledge", axum::routing::put(alert_handler::acknowledge), ) .route( "/health/alerts/{id}/dismiss", axum::routing::put(alert_handler::dismiss), ) .route( "/health/alerts/{id}/resolve", axum::routing::put(alert_handler::resolve), ) // 危急值告警路由 .route( "/health/critical-alerts", axum::routing::get(critical_alert_handler::list_critical_alerts), ) .route( "/health/critical-alerts/{id}", axum::routing::get(critical_alert_handler::get_critical_alert), ) .route( "/health/critical-alerts/{id}/acknowledge", axum::routing::post(critical_alert_handler::acknowledge_critical_alert), ) .route( "/health/alert-rules", axum::routing::get(alert_rule_handler::list_rules) .post(alert_rule_handler::create), ) .route( "/health/alert-rules/{id}", axum::routing::put(alert_rule_handler::update), ) .route( "/health/alert-rules/{id}/deactivate", axum::routing::put(alert_rule_handler::deactivate), ) // 设备管理 .route( "/health/devices", axum::routing::get(device_handler::list_devices), ) .route( "/health/devices/{id}", axum::routing::delete(device_handler::unbind_device), ) // 行动收件箱 .route( "/health/action-inbox", axum::routing::get(action_inbox_handler::list_action_inbox), ) .route( "/health/action-inbox/stats", axum::routing::get(action_inbox_handler::get_workbench_stats), ) .route( "/health/action-inbox/team", axum::routing::get(action_inbox_handler::get_team_overview), ) .route( "/health/action-inbox/{source_ref}/thread", axum::routing::get(action_inbox_handler::get_action_thread), ) } } impl Default for HealthModule { fn default() -> Self { Self::new() } } #[async_trait::async_trait] impl ErpModule for HealthModule { fn name(&self) -> &str { "health" } fn version(&self) -> &str { env!("CARGO_PKG_VERSION") } fn dependencies(&self) -> Vec<&str> { vec!["auth"] } fn register_event_handlers(&self, _bus: &EventBus) { // 事件处理器已迁移到 on_startup,此处保留空实现以兼容 trait 签名 } async fn on_startup(&self, ctx: &erp_core::module::ModuleContext) -> erp_core::error::AppResult<()> { let crypto = match erp_core::crypto::PiiCrypto::from_kek_hex( &std::env::var("ERP__CRYPTO__KEK").unwrap_or_default(), ) { Ok(c) => c, Err(_) => { #[cfg(debug_assertions)] { tracing::warn!("ERP__CRYPTO__KEK 未设置或无效,使用开发默认密钥"); erp_core::crypto::PiiCrypto::dev_default() } #[cfg(not(debug_assertions))] { panic!("ERP__CRYPTO__KEK 必须设置为有效的 64 字符 hex 字符串(生产环境不允许回退到开发密钥)"); } } }; let state = crate::state::HealthState { db: ctx.db.clone(), event_bus: ctx.event_bus.clone(), crypto, }; crate::event::register_handlers_with_state(state.clone()); tracing::info!(module = "health", "Health module event handlers registered via on_startup"); // 启动逾期随访检查器(立即执行一次 + 每 6 小时重复) { let state_clone = state.clone(); tokio::spawn(async move { match crate::service::follow_up_service::check_overdue_and_notify(&state_clone).await { Ok(count) if count > 0 => tracing::info!(count = count, "启动时逾期随访检查完成"), Ok(_) => tracing::info!("启动时逾期随访检查完成(无逾期任务)"), Err(e) => tracing::warn!(error = %e, "启动时逾期随访检查失败"), } }); } let _overdue_handle = Self::start_overdue_checker(state.db.clone()); tracing::info!(module = "health", "Overdue follow-up checker started"); // 启动积分过期清理(启动时执行一次 + 每 24 小时重复) { let db = ctx.db.clone(); let event_bus = ctx.event_bus.clone(); tokio::spawn(async move { match crate::service::points_service::expire_points(&db, &event_bus).await { Ok(count) if count > 0 => tracing::info!(count = count, "启动时积分过期清理完成"), Ok(_) => tracing::info!("启动时积分过期清理完成(无过期积分)"), Err(e) => tracing::warn!(error = %e, "启动时积分过期清理失败"), } }); } let _expire_handle = Self::start_points_expiration_checker(ctx.db.clone(), ctx.event_bus.clone()); tracing::info!(module = "health", "Points expiration checker started"); // 启动预约提醒调度(启动时立即执行一次 + 每 1 小时重复) { let db = ctx.db.clone(); let event_bus = ctx.event_bus.clone(); tokio::spawn(async move { match crate::service::appointment_service::send_reminders(&db, &event_bus).await { Ok(count) if count > 0 => tracing::info!(count = count, "启动时预约提醒发送完成"), Ok(_) => tracing::info!("启动时预约提醒检查完成"), Err(e) => tracing::warn!(error = %e, "启动时预约提醒发送失败"), } }); } let _reminder_handle = Self::start_appointment_reminder(ctx.db.clone(), ctx.event_bus.clone()); tracing::info!(module = "health", "Appointment reminder scheduler started"); // 启动设备原始数据清理(每 24 小时删除超过 90 天的数据) let _cleanup_handle = Self::start_device_readings_cleanup(ctx.db.clone()); tracing::info!(module = "health", "Device readings cleanup task started"); Ok(()) } async fn on_tenant_created( &self, tenant_id: Uuid, db: &sea_orm::DatabaseConnection, _event_bus: &EventBus, ) -> AppResult<()> { crate::service::seed::seed_tenant_health(db, tenant_id) .await .map_err(|e| erp_core::error::AppError::Internal(e.to_string()))?; tracing::info!(tenant_id = %tenant_id, "Health module tenant initialized"); Ok(()) } async fn on_tenant_deleted( &self, tenant_id: Uuid, db: &sea_orm::DatabaseConnection, ) -> AppResult<()> { crate::service::seed::soft_delete_tenant_data(db, tenant_id) .await .map_err(|e| erp_core::error::AppError::Internal(e.to_string()))?; tracing::info!(tenant_id = %tenant_id, "Health module tenant data soft-deleted"); Ok(()) } fn permissions(&self) -> Vec { vec![ PermissionDescriptor { code: "health.patient.list".into(), name: "查看患者列表".into(), description: "查看和搜索患者列表、详情".into(), module: "health".into(), }, PermissionDescriptor { code: "health.patient.manage".into(), name: "管理患者".into(), description: "创建、编辑、删除患者".into(), module: "health".into(), }, PermissionDescriptor { code: "health.health-data.list".into(), name: "查看健康数据".into(), description: "查看体检记录、监测数据、化验报告".into(), module: "health".into(), }, PermissionDescriptor { code: "health.health-data.manage".into(), name: "管理健康数据".into(), description: "录入、编辑、删除健康数据".into(), module: "health".into(), }, PermissionDescriptor { code: "health.appointment.list".into(), name: "查看预约".into(), description: "查看预约列表和排班".into(), module: "health".into(), }, PermissionDescriptor { code: "health.appointment.manage".into(), name: "管理预约".into(), description: "创建、确认、取消预约".into(), module: "health".into(), }, PermissionDescriptor { code: "health.follow-up.list".into(), name: "查看随访".into(), description: "查看随访任务和记录".into(), module: "health".into(), }, PermissionDescriptor { code: "health.follow-up.manage".into(), name: "管理随访".into(), description: "创建、分配、完成随访任务".into(), module: "health".into(), }, PermissionDescriptor { code: "health.consultation.list".into(), name: "查看咨询".into(), description: "查看咨询会话和消息记录".into(), module: "health".into(), }, PermissionDescriptor { code: "health.consultation.manage".into(), name: "管理咨询".into(), description: "关闭会话、导出记录".into(), module: "health".into(), }, PermissionDescriptor { code: "health.doctor.list".into(), name: "查看医护".into(), description: "查看医护列表和详情".into(), module: "health".into(), }, PermissionDescriptor { code: "health.doctor.manage".into(), name: "管理医护".into(), description: "创建、编辑医护档案、排班".into(), module: "health".into(), }, PermissionDescriptor { code: "health.articles.list".into(), name: "查看资讯".into(), description: "查看健康资讯文章列表和详情".into(), module: "health".into(), }, PermissionDescriptor { code: "health.articles.manage".into(), name: "管理资讯".into(), description: "创建、编辑、删除健康资讯文章".into(), module: "health".into(), }, PermissionDescriptor { code: "health.articles.review".into(), name: "审核资讯".into(), description: "审核通过或拒绝资讯文章发布".into(), module: "health".into(), }, PermissionDescriptor { code: "health.points.list".into(), name: "查看积分".into(), description: "查看积分规则、订单列表".into(), module: "health".into(), }, PermissionDescriptor { code: "health.points.manage".into(), name: "管理积分".into(), description: "创建积分规则、管理商品、核销订单".into(), module: "health".into(), }, PermissionDescriptor { code: "health.device-readings.list".into(), name: "查看设备数据".into(), description: "查看患者的设备采集数据".into(), module: "health".into(), }, PermissionDescriptor { code: "health.device-readings.manage".into(), name: "管理设备数据".into(), description: "提交设备采集数据".into(), module: "health".into(), }, PermissionDescriptor { code: "health.devices.list".into(), name: "查看设备绑定".into(), description: "查看设备绑定记录列表".into(), module: "health".into(), }, PermissionDescriptor { code: "health.devices.manage".into(), name: "管理设备绑定".into(), description: "解绑设备".into(), module: "health".into(), }, PermissionDescriptor { code: "health.alerts.list".into(), name: "查看告警".into(), description: "查看告警记录".into(), module: "health".into(), }, PermissionDescriptor { code: "health.alerts.manage".into(), name: "管理告警".into(), description: "确认/处置告警".into(), module: "health".into(), }, PermissionDescriptor { code: "health.alert-rules.list".into(), name: "查看告警规则".into(), description: "查看告警规则配置".into(), module: "health".into(), }, PermissionDescriptor { code: "health.alert-rules.manage".into(), name: "管理告警规则".into(), description: "创建/编辑/启停告警规则".into(), module: "health".into(), }, PermissionDescriptor { code: "health.critical-alerts.list".into(), name: "查看危急值告警".into(), description: "查看危急值告警列表和详情".into(), module: "health".into(), }, PermissionDescriptor { code: "health.critical-alerts.manage".into(), name: "处理危急值告警".into(), description: "确认危急值告警".into(), module: "health".into(), }, PermissionDescriptor { code: "health.critical-value-thresholds.list".into(), name: "查看危急值阈值".into(), description: "查看危急值阈值配置".into(), module: "health".into(), }, PermissionDescriptor { code: "health.critical-value-thresholds.manage".into(), name: "管理危急值阈值".into(), description: "创建/编辑/删除危急值阈值配置".into(), module: "health".into(), }, PermissionDescriptor { code: "health.follow-up-templates.list".into(), name: "查看随访模板".into(), description: "查看随访模板列表和详情".into(), module: "health".into(), }, PermissionDescriptor { code: "health.follow-up-templates.manage".into(), name: "管理随访模板".into(), description: "创建/编辑/删除随访模板".into(), module: "health".into(), }, PermissionDescriptor { code: "health.daily-monitoring.list".into(), name: "查看日常监测".into(), description: "查看患者日常监测数据".into(), module: "health".into(), }, PermissionDescriptor { code: "health.daily-monitoring.manage".into(), name: "管理日常监测".into(), description: "录入/编辑/删除日常监测数据".into(), module: "health".into(), }, PermissionDescriptor { code: "health.consent.list".into(), name: "查看知情同意".into(), description: "查看患者知情同意记录".into(), module: "health".into(), }, PermissionDescriptor { code: "health.consent.manage".into(), name: "管理知情同意".into(), description: "签署/撤销知情同意".into(), module: "health".into(), }, PermissionDescriptor { code: "health.medication-records.list".into(), name: "查看用药记录".into(), description: "查看患者用药记录列表和详情".into(), module: "health".into(), }, PermissionDescriptor { code: "health.medication-records.manage".into(), name: "管理用药记录".into(), description: "创建/编辑/删除用药记录".into(), module: "health".into(), }, PermissionDescriptor { code: "health.medication-reminders.list".into(), name: "查看药物提醒".into(), description: "查看患者药物提醒列表".into(), module: "health".into(), }, PermissionDescriptor { code: "health.medication-reminders.manage".into(), name: "管理药物提醒".into(), description: "创建/编辑/删除药物提醒".into(), module: "health".into(), }, // 行动收件箱 PermissionDescriptor { code: "health.action-inbox.list".into(), name: "查看行动收件箱".into(), description: "查看统一行动收件箱中的待办事项".into(), module: "health".into(), }, PermissionDescriptor { code: "health.action-inbox.manage".into(), name: "管理行动项".into(), description: "审批/拒绝/标记行动收件箱中的事项".into(), module: "health".into(), }, PermissionDescriptor { code: "health.action-inbox.team".into(), name: "查看团队概览".into(), description: "查看科室团队工作负载和风险分布(主任专属)".into(), module: "health".into(), }, // 工作台管理 PermissionDescriptor { code: "health.dashboard.manage".into(), name: "工作台管理".into(), description: "查看系统健康、用户活跃度、模块状态等管理统计".into(), module: "health".into(), }, ] } fn as_any(&self) -> &dyn std::any::Any { self } }