fix(health): 修复 5 角色深度测试发现的 8 个问题
P0 修复: - 告警状态机新增 active 合法状态 + 转换规则 (active→acknowledged/dismissed) - 前端路由守卫改为默认拒绝,未注册路由返回 403 P1 修复: - 侧边栏菜单根据用户权限码过滤,非 admin 隐藏无权限菜单项 - Critical-alerts handler 增加详细错误日志 + div_ceil 安全防护 - 仪表盘统计 API 调用使用 silent 模式避免 500 触发全局 toast P2 修复: - 随访类型映射新增 visit → 上门 (前后端同步) - 随访 fallback 选项新增 visit 类型 排除的假 BUG (代码已正确): - 患者性别/血型: MCP fill() 不兼容 Select 组件,正常交互正确 - 随访筛选/对话框关闭: 代码逻辑验证正确 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -34,14 +34,20 @@ where
|
||||
let (items, total) = critical_alert_service::list_pending_alerts(
|
||||
&state, ctx.tenant_id, page, page_size,
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.map_err(|e| {
|
||||
tracing::error!(error = %e, tenant_id = %ctx.tenant_id, "查询危急值告警列表失败");
|
||||
e
|
||||
})?;
|
||||
|
||||
let total_pages = if page_size > 0 { total.div_ceil(page_size) } else { 0 };
|
||||
|
||||
Ok(axum::Json(ApiResponse::ok(PaginatedResponse {
|
||||
data: items,
|
||||
total,
|
||||
page,
|
||||
page_size,
|
||||
total_pages: total.div_ceil(page_size.max(1)),
|
||||
total_pages,
|
||||
})))
|
||||
}
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ pub fn validate_schedule_status(value: &str) -> HealthResult<()> {
|
||||
/// follow_up_task.follow_up_type
|
||||
pub fn validate_follow_up_type(value: &str) -> HealthResult<()> {
|
||||
validate_enum!(value, "follow_up_type", [
|
||||
"phone", "outpatient", "home_visit", "online", "wechat",
|
||||
"phone", "outpatient", "home_visit", "visit", "online", "wechat",
|
||||
]);
|
||||
Ok(())
|
||||
}
|
||||
@@ -231,18 +231,18 @@ pub fn validate_alert_severity(value: &str) -> HealthResult<()> {
|
||||
/// alert.status
|
||||
pub fn validate_alert_status(value: &str) -> HealthResult<()> {
|
||||
validate_enum!(value, "alert_status", [
|
||||
"pending", "acknowledged", "resolved", "dismissed",
|
||||
"pending", "active", "acknowledged", "resolved", "dismissed",
|
||||
]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 告警状态转换校验: pending→acknowledged/dismissed, acknowledged→resolved/dismissed
|
||||
/// 告警状态转换校验: pending/active→acknowledged/dismissed, acknowledged→resolved/dismissed
|
||||
pub fn validate_alert_status_transition(current: &str, next: &str) -> HealthResult<()> {
|
||||
if current == next {
|
||||
return Ok(());
|
||||
}
|
||||
let allowed = match current {
|
||||
"pending" => matches!(next, "acknowledged" | "dismissed"),
|
||||
"pending" | "active" => matches!(next, "acknowledged" | "dismissed"),
|
||||
"acknowledged" => matches!(next, "resolved" | "dismissed"),
|
||||
_ => false,
|
||||
};
|
||||
@@ -477,6 +477,8 @@ mod tests {
|
||||
#[test]
|
||||
fn alert_status_pending() { assert!(validate_alert_status("pending").is_ok()); }
|
||||
#[test]
|
||||
fn alert_status_active() { assert!(validate_alert_status("active").is_ok()); }
|
||||
#[test]
|
||||
fn alert_status_resolved() { assert!(validate_alert_status("resolved").is_ok()); }
|
||||
#[test]
|
||||
fn alert_status_invalid() { assert!(validate_alert_status("open").is_err()); }
|
||||
@@ -487,6 +489,10 @@ mod tests {
|
||||
#[test]
|
||||
fn alert_pending_to_dismissed() { assert!(validate_alert_status_transition("pending", "dismissed").is_ok()); }
|
||||
#[test]
|
||||
fn alert_active_to_acknowledged() { assert!(validate_alert_status_transition("active", "acknowledged").is_ok()); }
|
||||
#[test]
|
||||
fn alert_active_to_dismissed() { assert!(validate_alert_status_transition("active", "dismissed").is_ok()); }
|
||||
#[test]
|
||||
fn alert_pending_to_resolved_fails() { assert!(validate_alert_status_transition("pending", "resolved").is_err()); }
|
||||
#[test]
|
||||
fn alert_acknowledged_to_resolved() { assert!(validate_alert_status_transition("acknowledged", "resolved").is_ok()); }
|
||||
|
||||
Reference in New Issue
Block a user