fix(health+server): 多专家组生产就绪度分析 — DTO 校验补全 + 审计日志用户名

五维度分析结果(DevOps 4.0/10, 医疗合规 9C/6P/1NC, 前端 Lighthouse 94/100/100):

1. Article/Category/Tag DTO 补全 #[derive(Validate)] + handler .validate() 调用(6 DTO + 8 handler)
2. 审计日志 API 新增 user_name 字段(批量关联 users 表),仪表盘显示用户名而非 UUID
3. 多专家组分析报告存档 docs/discussions/
This commit is contained in:
iven
2026-05-21 17:53:00 +08:00
parent 7ad5ddb898
commit b0f96258ee
6 changed files with 318 additions and 14 deletions

View File

@@ -10,6 +10,8 @@ use crate::dto::article_dto::{CategoryResp, CreateCategoryReq, UpdateCategoryReq
use crate::service::article_category_service;
use crate::state::HealthState;
use validator::Validate;
pub async fn list_categories<S>(
State(state): State<HealthState>,
Extension(ctx): Extension<TenantContext>,
@@ -33,6 +35,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.manage")?;
(*req)
.validate()
.map_err(|e| AppError::Validation(e.to_string()))?;
req.sanitize();
let result =
article_category_service::create_category(&state, ctx.tenant_id, Some(ctx.user_id), req.0)
@@ -51,6 +56,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.manage")?;
(*req)
.validate()
.map_err(|e| AppError::Validation(e.to_string()))?;
req.sanitize();
let result = article_category_service::update_category(
&state,

View File

@@ -12,6 +12,8 @@ use crate::dto::article_dto::{
use crate::service::article_service;
use crate::state::HealthState;
use validator::Validate;
pub async fn list_articles<S>(
State(state): State<HealthState>,
Extension(ctx): Extension<TenantContext>,
@@ -107,6 +109,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.manage")?;
(*req)
.validate()
.map_err(|e| AppError::Validation(e.to_string()))?;
req.sanitize();
if req.title.trim().is_empty() {
return Err(AppError::Validation("文章标题不能为空".into()));
@@ -127,6 +132,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.manage")?;
(*req)
.validate()
.map_err(|e| AppError::Validation(e.to_string()))?;
req.sanitize();
let result =
article_service::update_article(&state, ctx.tenant_id, id, Some(ctx.user_id), req.0)
@@ -194,6 +202,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.review")?;
(*req)
.validate()
.map_err(|e| AppError::Validation(e.to_string()))?;
req.sanitize();
let version = req.version.unwrap_or(0);
let result = article_service::approve_article(
@@ -220,6 +231,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.review")?;
(*req)
.validate()
.map_err(|e| AppError::Validation(e.to_string()))?;
req.sanitize();
let version = req.version.unwrap_or(0);
let result = article_service::reject_article(

View File

@@ -10,6 +10,8 @@ use crate::dto::article_dto::{CreateTagReq, TagResp, UpdateTagReq};
use crate::service::article_tag_service;
use crate::state::HealthState;
use validator::Validate;
pub async fn list_tags<S>(
State(state): State<HealthState>,
Extension(ctx): Extension<TenantContext>,
@@ -33,6 +35,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.manage")?;
(*req)
.validate()
.map_err(|e| AppError::Validation(e.to_string()))?;
req.sanitize();
if req.name.trim().is_empty() {
return Err(AppError::Validation("标签名称不能为空".into()));
@@ -53,6 +58,9 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "health.articles.manage")?;
(*req)
.validate()
.map_err(|e| AppError::Validation(e.to_string()))?;
req.sanitize();
let result =
article_tag_service::update_tag(&state, ctx.tenant_id, id, Some(ctx.user_id), req.0)