fix: DTO 输入校验补全 + 编译修复 + AuthButton 类型修复

- erp-auth/config/workflow/message/plugin/health: 44 处 DTO 校验缺失修复
- erp-plugin/data_dto: utoipa derive 宏 import 修复
- erp-server/main: tracing 宏类型推断修复
- web AuthButton: AiAnalysisCard/VitalSignsTab Button 包裹在 children 内

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
iven
2026-05-20 06:58:54 +08:00
parent d74c7a61de
commit f3bf8b3b1d
17 changed files with 149 additions and 66 deletions

View File

@@ -1,8 +1,9 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use utoipa::{IntoParams, ToSchema};
/// 插件数据记录响应
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct PluginDataResp {
pub id: String,
pub data: serde_json::Value,
@@ -12,27 +13,27 @@ pub struct PluginDataResp {
}
/// 创建插件数据请求
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct CreatePluginDataReq {
pub data: serde_json::Value,
}
/// 更新插件数据请求(全量替换)
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct UpdatePluginDataReq {
pub data: serde_json::Value,
pub version: i32,
}
/// 部分更新请求PATCH — 只合并提供的字段)
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct PatchPluginDataReq {
pub data: serde_json::Value,
pub version: i32,
}
/// 插件数据列表查询参数
#[derive(Debug, Serialize, Deserialize, utoipa::IntoParams)]
#[derive(Debug, Serialize, Deserialize, IntoParams)]
pub struct PluginDataListParams {
pub page: Option<u64>,
pub page_size: Option<u64>,
@@ -47,7 +48,7 @@ pub struct PluginDataListParams {
}
/// 聚合查询响应项
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct AggregateItem {
/// 分组键(字段值)
pub key: String,
@@ -56,7 +57,7 @@ pub struct AggregateItem {
}
/// 多聚合查询响应项
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct AggregateMultiRow {
/// 分组键
pub key: String,
@@ -68,7 +69,7 @@ pub struct AggregateMultiRow {
}
/// 聚合查询参数
#[derive(Debug, Serialize, Deserialize, utoipa::IntoParams)]
#[derive(Debug, Serialize, Deserialize, IntoParams)]
pub struct AggregateQueryParams {
/// 分组字段名
pub group_by: String,
@@ -77,7 +78,7 @@ pub struct AggregateQueryParams {
}
/// 多聚合查询请求体
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct AggregateMultiReq {
/// 分组字段名
pub group_by: String,
@@ -88,7 +89,7 @@ pub struct AggregateMultiReq {
}
/// 单个聚合定义
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct AggregateDefDto {
/// 聚合函数: count, sum, avg, min, max
pub func: String,
@@ -97,7 +98,7 @@ pub struct AggregateDefDto {
}
/// 统计查询参数
#[derive(Debug, Serialize, Deserialize, utoipa::IntoParams)]
#[derive(Debug, Serialize, Deserialize, IntoParams)]
pub struct CountQueryParams {
/// 搜索关键词
pub search: Option<String>,
@@ -106,7 +107,7 @@ pub struct CountQueryParams {
}
/// 批量操作请求
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct BatchActionReq {
/// 操作类型: "batch_delete" 或 "batch_update"
pub action: String,
@@ -117,7 +118,7 @@ pub struct BatchActionReq {
}
/// 时间序列查询参数
#[derive(Debug, Serialize, Deserialize, utoipa::IntoParams)]
#[derive(Debug, Serialize, Deserialize, IntoParams)]
pub struct TimeseriesParams {
/// 时间字段名
pub time_field: String,
@@ -130,7 +131,7 @@ pub struct TimeseriesParams {
}
/// 时间序列数据项
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct TimeseriesItem {
/// 时间周期
pub period: String,
@@ -141,14 +142,14 @@ pub struct TimeseriesItem {
// ─── 跨插件引用 DTO ──────────────────────────────────────────────────
/// 批量标签解析请求
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct ResolveLabelsReq {
/// 字段名 → UUID 列表
pub fields: std::collections::HashMap<String, Vec<String>>,
}
/// 批量标签解析响应
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct ResolveLabelsResp {
/// 字段名 → { uuid: label } 映射
pub labels: serde_json::Value,
@@ -157,7 +158,7 @@ pub struct ResolveLabelsResp {
}
/// 公开实体信息(实体注册表查询响应)
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct PublicEntityResp {
pub manifest_id: String,
pub plugin_id: String,
@@ -168,7 +169,7 @@ pub struct PublicEntityResp {
// ─── 导入导出 DTO ──────────────────────────────────────────────────
/// 数据导出参数
#[derive(Debug, Serialize, Deserialize, utoipa::IntoParams)]
#[derive(Debug, Serialize, Deserialize, IntoParams)]
pub struct ExportParams {
/// JSON 格式过滤: {"field":"value"}
pub filter: Option<String>,
@@ -190,14 +191,14 @@ pub enum ExportPayload {
}
/// 数据导入请求
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct ImportReq {
/// 导入数据行列表,每行是一个 JSON 对象
pub rows: Vec<serde_json::Value>,
}
/// 数据导入结果
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct ImportResult {
/// 成功导入行数
pub success_count: usize,
@@ -209,7 +210,7 @@ pub struct ImportResult {
}
/// 单行导入错误
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct ImportRowError {
/// 行号0-based
pub row: usize,
@@ -220,7 +221,7 @@ pub struct ImportRowError {
// ─── 市场目录 DTO ──────────────────────────────────────────────────
/// 市场条目列表查询参数
#[derive(Debug, Serialize, Deserialize, utoipa::IntoParams)]
#[derive(Debug, Serialize, Deserialize, IntoParams)]
pub struct MarketListParams {
pub page: Option<u64>,
pub page_size: Option<u64>,
@@ -229,7 +230,7 @@ pub struct MarketListParams {
}
/// 市场条目响应(不含二进制数据)
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct MarketEntryResp {
pub id: String,
pub plugin_id: String,
@@ -252,7 +253,7 @@ pub struct MarketEntryResp {
}
/// 市场条目详情响应(含完整信息)
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct MarketEntryDetailResp {
#[serde(flatten)]
pub entry: MarketEntryResp,
@@ -261,7 +262,7 @@ pub struct MarketEntryDetailResp {
}
/// 提交评分/评论请求
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct SubmitReviewReq {
/// 评分 1-5
pub rating: i32,
@@ -270,7 +271,7 @@ pub struct SubmitReviewReq {
}
/// 评论响应
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct MarketReviewResp {
pub id: String,
pub user_id: String,
@@ -283,7 +284,7 @@ pub struct MarketReviewResp {
// ─── 对账扫描 DTO ──────────────────────────────────────────────────
/// 对账报告
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct ReconciliationReport {
/// 有效引用数
pub valid_count: i64,
@@ -294,7 +295,7 @@ pub struct ReconciliationReport {
}
/// 悬空引用详情
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct DanglingRef {
/// 实体名
pub entity: String,
@@ -309,7 +310,7 @@ pub struct DanglingRef {
// ─── 自定义视图 DTO ──────────────────────────────────────────────────
/// 用户视图配置请求
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct UserViewReq {
pub view_name: String,
pub view_config: serde_json::Value,
@@ -317,7 +318,7 @@ pub struct UserViewReq {
}
/// 用户视图响应
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, ToSchema)]
pub struct UserViewResp {
pub id: String,
pub plugin_id: String,

View File

@@ -1,6 +1,7 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use validator::Validate;
/// 插件信息响应
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
@@ -49,17 +50,19 @@ pub struct PluginHealthResp {
}
/// 更新插件配置请求
#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema)]
#[derive(Debug, Serialize, Deserialize, Validate, utoipa::ToSchema)]
pub struct UpdatePluginConfigReq {
pub config: serde_json::Value,
pub version: i32,
}
/// 插件列表查询参数
#[derive(Debug, Serialize, Deserialize, utoipa::IntoParams)]
#[derive(Debug, Serialize, Deserialize, Validate, utoipa::IntoParams)]
pub struct PluginListParams {
pub page: Option<u64>,
pub page_size: Option<u64>,
#[validate(length(max = 20, message = "状态值无效"))]
pub status: Option<String>,
#[validate(length(max = 100, message = "搜索关键词过长"))]
pub search: Option<String>,
}

View File

@@ -2,6 +2,7 @@ use axum::Extension;
use axum::extract::{FromRef, Multipart, Path, Query, State};
use axum::response::Json;
use uuid::Uuid;
use validator::Validate;
use erp_core::error::AppError;
use erp_core::rbac::require_permission;
@@ -391,6 +392,8 @@ where
S: Clone + Send + Sync + 'static,
{
require_permission(&ctx, "plugin.admin")?;
req.validate()
.map_err(|e| AppError::Validation(e.to_string()))?;
let result = PluginService::update_config(
id,
ctx.tenant_id,