fix(health): 穷尽审计修复 — 权限同步/编译错误/前端bug/审计日志
审计发现并修复的问题: HIGH: - H1: ConsultationDetail 使用 getSession(id) 替代错误的列表搜索 - H2: SessionResp 添加 version/updated_at 字段 - H3: 移除 FollowUpRecordList 调用不存在的导出端点 - H4: 新增 articles.ts 前端 API 模块 MEDIUM: - M1: article delete 添加乐观锁 (expected_version) - M2: 取消预约排班释放传播错误 (log::warn -> ?) - M3: FollowUpTaskList 日期格式 Dayjs -> string - M4: 补充 15 个缺失审计日志 LOW: - L1: 替换 follow_up_service 中的 .unwrap() - L2: PatientListItem 添加 version 字段 CRITICAL (新发现): - 权限未同步: 健康模块 14 个权限从未写入数据库,添加启动时自动同步 - migration 表名错误: patients -> patient - 编译错误: health_trend entity 未导入, ToPrimitive trait 未导入 - HealthError 缺少 From<AppError> 实现
This commit is contained in:
@@ -17,19 +17,26 @@ impl HealthModule {
|
||||
Self
|
||||
}
|
||||
|
||||
/// 启动定时逾期随访检查(每 6 小时运行一次)
|
||||
pub fn start_overdue_checker(db: sea_orm::DatabaseConnection) {
|
||||
/// 启动定时逾期随访检查(每 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 {
|
||||
interval.tick().await;
|
||||
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::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;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
pub fn public_routes<S>() -> Router<S>
|
||||
@@ -190,6 +197,14 @@ impl HealthModule {
|
||||
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),
|
||||
@@ -202,10 +217,6 @@ impl HealthModule {
|
||||
"/health/consultation-messages",
|
||||
axum::routing::post(consultation_handler::create_message),
|
||||
)
|
||||
.route(
|
||||
"/health/consultation-sessions/export",
|
||||
axum::routing::get(consultation_handler::export_sessions),
|
||||
)
|
||||
// 医护管理
|
||||
.route(
|
||||
"/health/doctors",
|
||||
@@ -258,14 +269,23 @@ impl ErpModule for HealthModule {
|
||||
}
|
||||
|
||||
async fn on_startup(&self, ctx: &erp_core::module::ModuleContext) -> erp_core::error::AppResult<()> {
|
||||
let crypto = crate::crypto::HealthCrypto::from_keys(
|
||||
let crypto = match crate::crypto::HealthCrypto::from_keys(
|
||||
&std::env::var("HEALTH_AES_KEY").unwrap_or_default(),
|
||||
&std::env::var("HEALTH_HMAC_KEY").unwrap_or_default(),
|
||||
)
|
||||
.unwrap_or_else(|_| {
|
||||
tracing::warn!("HEALTH_AES_KEY / HEALTH_HMAC_KEY 未设置或无效,使用开发默认密钥");
|
||||
crate::crypto::HealthCrypto::dev_default()
|
||||
});
|
||||
) {
|
||||
Ok(c) => c,
|
||||
Err(_) => {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
tracing::warn!("HEALTH_AES_KEY / HEALTH_HMAC_KEY 未设置或无效,使用开发默认密钥");
|
||||
crate::crypto::HealthCrypto::dev_default()
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
panic!("HEALTH_AES_KEY 和 HEALTH_HMAC_KEY 必须设置为有效的 64 字符 hex 字符串(生产环境不允许回退到开发密钥)");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let state = crate::state::HealthState {
|
||||
db: ctx.db.clone(),
|
||||
|
||||
Reference in New Issue
Block a user