Files
hms/crates/erp-workflow/src/dto.rs
iven f3bf8b3b1d 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>
2026-05-20 06:58:54 +08:00

253 lines
7.7 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;
use uuid::Uuid;
use validator::Validate;
// --- 流程图节点/边定义 ---
/// BPMN 节点类型
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, PartialEq)]
pub enum NodeType {
StartEvent,
EndEvent,
UserTask,
ServiceTask,
ExclusiveGateway,
ParallelGateway,
}
/// 流程图节点定义
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct NodeDef {
pub id: String,
#[serde(rename = "type")]
pub node_type: NodeType,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub assignee_id: Option<Uuid>,
#[serde(skip_serializing_if = "Option::is_none")]
pub candidate_groups: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub service_type: Option<String>,
/// 服务任务 HTTP 调用配置
#[serde(skip_serializing_if = "Option::is_none")]
pub service_config: Option<ServiceTaskConfig>,
/// 前端渲染位置
#[serde(skip_serializing_if = "Option::is_none")]
pub position: Option<NodePosition>,
}
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct NodePosition {
pub x: f64,
pub y: f64,
}
/// ServiceTask HTTP 调用配置
#[derive(Debug, Clone, Serialize, Deserialize, Validate, ToSchema)]
pub struct ServiceTaskConfig {
/// 请求 URL仅允许 http/https 协议,禁止内网地址)
#[validate(length(min = 1, max = 2048), custom(function = "validate_service_url"))]
pub url: String,
/// HTTP 方法GET / POST默认 GET
#[serde(default = "default_method")]
#[validate(custom(function = "validate_http_method"))]
pub method: String,
/// POST body 模板(支持从流程变量替换 ${var_name}
#[serde(skip_serializing_if = "Option::is_none")]
pub body: Option<serde_json::Value>,
}
fn default_method() -> String {
"GET".to_string()
}
fn validate_service_url(value: &str) -> Result<(), validator::ValidationError> {
if !value.starts_with("https://") && !value.starts_with("http://") {
return Err(validator::ValidationError::new("invalid_url_scheme"));
}
if value.contains("127.0.0.1") || value.contains("localhost") || value.contains("0.0.0.0") {
return Err(validator::ValidationError::new("ssrf_blocked"));
}
Ok(())
}
fn validate_http_method(value: &str) -> Result<(), validator::ValidationError> {
match value {
"GET" | "POST" => Ok(()),
_ => Err(validator::ValidationError::new("invalid_http_method")),
}
}
/// 流程图连线定义
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct EdgeDef {
pub id: String,
pub source: String,
pub target: String,
/// 条件表达式(排他网关分支)
#[serde(skip_serializing_if = "Option::is_none")]
pub condition: Option<String>,
/// 前端渲染标签
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
}
/// 完整流程图
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct FlowDiagram {
pub nodes: Vec<NodeDef>,
pub edges: Vec<EdgeDef>,
}
// --- 流程定义 DTOs ---
#[derive(Debug, Serialize, ToSchema)]
pub struct ProcessDefinitionResp {
pub id: Uuid,
pub name: String,
pub key: String,
pub version: i32,
#[serde(skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub nodes: serde_json::Value,
pub edges: serde_json::Value,
pub status: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
pub lock_version: i32,
}
#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct CreateProcessDefinitionReq {
#[validate(length(min = 1, max = 200, message = "流程名称不能为空"))]
pub name: String,
#[validate(length(min = 1, max = 100, message = "流程编码不能为空"))]
pub key: String,
pub category: Option<String>,
pub description: Option<String>,
pub nodes: Vec<NodeDef>,
pub edges: Vec<EdgeDef>,
}
#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct UpdateProcessDefinitionReq {
#[validate(length(max = 200, message = "流程名称过长"))]
pub name: Option<String>,
pub category: Option<String>,
pub description: Option<String>,
pub nodes: Option<Vec<NodeDef>>,
pub edges: Option<Vec<EdgeDef>>,
pub version: i32,
}
// --- 流程实例 DTOs ---
#[derive(Debug, Serialize, ToSchema)]
pub struct ProcessInstanceResp {
pub id: Uuid,
pub definition_id: Uuid,
pub definition_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub business_key: Option<String>,
pub status: String,
pub started_by: Uuid,
pub started_at: DateTime<Utc>,
#[serde(skip_serializing_if = "Option::is_none")]
pub completed_at: Option<DateTime<Utc>>,
pub created_at: DateTime<Utc>,
/// 当前活跃的 token 位置
pub active_tokens: Vec<TokenResp>,
pub version: i32,
}
#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct StartInstanceReq {
pub definition_id: Uuid,
pub business_key: Option<String>,
/// 初始流程变量
pub variables: Option<Vec<SetVariableReq>>,
}
// --- Token DTOs ---
#[derive(Debug, Serialize, ToSchema)]
pub struct TokenResp {
pub id: Uuid,
pub node_id: String,
pub status: String,
pub created_at: DateTime<Utc>,
}
// --- 任务 DTOs ---
#[derive(Debug, Serialize, ToSchema)]
pub struct TaskResp {
pub id: Uuid,
pub instance_id: Uuid,
pub token_id: Uuid,
pub node_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub node_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub assignee_id: Option<Uuid>,
#[serde(skip_serializing_if = "Option::is_none")]
pub candidate_groups: Option<serde_json::Value>,
pub status: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub outcome: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub form_data: Option<serde_json::Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub due_date: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub completed_at: Option<DateTime<Utc>>,
pub created_at: DateTime<Utc>,
/// 流程定义名称(用于列表展示)
#[serde(skip_serializing_if = "Option::is_none")]
pub definition_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub business_key: Option<String>,
pub version: i32,
}
#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct CompleteTaskReq {
#[validate(length(min = 1, max = 50, message = "审批结果不能为空"))]
pub outcome: String,
pub form_data: Option<serde_json::Value>,
}
#[derive(Debug, Deserialize, Validate, ToSchema)]
pub struct DelegateTaskReq {
pub delegate_to: Uuid,
}
// --- 流程变量 DTOs ---
#[derive(Debug, Serialize, ToSchema)]
pub struct ProcessVariableResp {
pub id: Uuid,
pub name: String,
pub var_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub value_string: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value_number: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value_boolean: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value_date: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, Deserialize, Validate, ToSchema)]
pub struct SetVariableReq {
#[validate(length(min = 1, max = 100, message = "变量名不能为空"))]
pub name: String,
pub var_type: Option<String>,
pub value: serde_json::Value,
}