Files
hms/crates/erp-workflow/src/dto.rs
iven b05b7c27a0
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
feat: 审计修复 Phase 6-7 — SSE 推送/工作流补全/消息群发/前端收尾
Phase 6 功能补全:
- P1-3: 消息 SSE 实时推送端点 + 前端 EventSource 连接
- P1-6: ServiceTask HTTP 调用能力 (reqwest GET/POST)
- P1-7: user.deleted 事件处理 — 终止相关流程实例
- P1-8: 任务认领 (claim) 端点 + handler
- P1-9: 超时检查器发布 task.timeout 事件
- P1-15: 组织/部门名称唯一性校验 (create + update)
- P1-18: 消息群发 fan-out (role/department/all 批量投递)

Phase 7 P3-P4 收尾:
- PluginAdmin purge 按钮状态修复
- ChangePassword 最小 8 字符 + 新旧密码不同验证
- AuditLogViewer 用户名缓存 + 扩展资源类型
- InstanceMonitor 通过 definition 缓存解析 node_name
- NotificationPreferences DND 时间范围校验
2026-04-26 19:44:04 +08:00

234 lines
6.8 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, ToSchema)]
pub struct ServiceTaskConfig {
/// 请求 URL
pub url: String,
/// HTTP 方法GET / POST默认 GET
#[serde(default = "default_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()
}
/// 流程图连线定义
#[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,
}