fix(health+plugin): 空标签名校验 + 出生日期校验 + metrics 错误映射 + 测试报告修正

- C1 已修复: CreateTagReq 添加 validate(length(min=1)) + handler 调 .validate()
- C2 非BUG: 媒体库实际路径 /health/media-folders(非 /health/media/folders)
- H6 已修复: create/update patient 添加 birth_date <= today 校验
- H7 已修复: 插件 metrics 移除手动 map_err,用 From trait 自动映射
- H1-H5 非BUG: 测试使用了错误的 API 路径(积分/随访/告警/设备)
- M1-M2 非BUG: Pagination 已有 .min(100) 上限 + u64 不接受负数
- 测试报告更新: Go/No-Go 从 CONDITIONAL GO 升级为 GO

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
iven
2026-05-20 07:25:38 +08:00
parent 3c94f5d585
commit e83101dd23
4 changed files with 67 additions and 52 deletions

View File

@@ -3,6 +3,7 @@ use axum::extract::{FromRef, Json, Path, Query, State};
use serde::Deserialize;
use utoipa::IntoParams;
use uuid::Uuid;
use validator::Validate;
use erp_core::error::AppError;
use erp_core::rbac::require_permission;
@@ -73,6 +74,11 @@ where
if req.name.len() > 255 {
return Err(AppError::Validation("患者姓名长度不能超过255个字符".into()));
}
if let Some(ref bd) = req.birth_date
&& *bd > chrono::Utc::now().date_naive()
{
return Err(AppError::Validation("出生日期不能是未来日期".into()));
}
let result =
patient_service::create_patient(&state, ctx.tenant_id, Some(ctx.user_id), req).await?;
Ok(Json(ApiResponse::ok(result)))
@@ -120,6 +126,11 @@ where
verification_status: req.verification_status,
};
update.sanitize();
if let Some(ref bd) = update.birth_date
&& *bd > chrono::Utc::now().date_naive()
{
return Err(AppError::Validation("出生日期不能是未来日期".into()));
}
let result = patient_service::update_patient(
&state,
ctx.tenant_id,
@@ -353,8 +364,9 @@ where
Ok(Json(ApiResponse::ok(tags)))
}
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
#[derive(Debug, serde::Deserialize, utoipa::ToSchema, validator::Validate)]
pub struct CreateTagReq {
#[validate(length(min = 1, max = 255, message = "标签名称不能为空且不超过255个字符"))]
pub name: String,
pub color: Option<String>,
pub description: Option<String>,
@@ -370,6 +382,8 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.patient.manage")?;
req.validate()
.map_err(|e| AppError::Validation(e.to_string()))?;
let result = patient_service::create_tag(
&state,
ctx.tenant_id,
@@ -384,8 +398,9 @@ where
Ok(Json(ApiResponse::ok(result)))
}
#[derive(Debug, serde::Deserialize, utoipa::ToSchema)]
#[derive(Debug, serde::Deserialize, utoipa::ToSchema, validator::Validate)]
pub struct UpdateTagWithVersion {
#[validate(length(min = 1, max = 255, message = "标签名称不能为空且不超过255个字符"))]
pub name: Option<String>,
pub color: Option<String>,
pub description: Option<String>,

View File

@@ -348,11 +348,7 @@ where
// 通过 plugin_id 找到 manifest_id再查询 metrics
let manifest_id =
crate::data_service::resolve_manifest_id(id, ctx.tenant_id, &state.db).await?;
let metrics = state
.engine
.get_metrics(&manifest_id)
.await
.map_err(|e| AppError::Internal(e.to_string()))?;
let metrics = state.engine.get_metrics(&manifest_id).await?;
let avg_ms = if metrics.total_invocations > 0 {
metrics.total_response_ms / metrics.total_invocations as f64