From ad017ca4da1bc0d42c7ada5883b6d1726a2068b8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?=
<15040126243@163.com>
Date: Fri, 11 Sep 2026 16:19:24 +0800
Subject: [PATCH] =?UTF-8?q?add=20=E6=96=B0=E5=A2=9E=20warmflow=20=E6=BA=90?=
=?UTF-8?q?=E7=A0=81=E7=BA=A7=E5=AE=9A=E5=88=B6=E5=8C=96=E5=BC=80=E5=8F=91?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 14 -
.../common/core/utils/SpringUtils.java | 14 +
ruoyi-modules/ruoyi-workflow/pom.xml | 12 -
.../org/dromara/warm/flow/FlowEngine.java | 152 ++
.../warm/flow/config/WarmFlowInitializer.java | 53 +
.../warm/flow/config/WarmFlowProperties.java | 136 ++
.../warm/flow/constant/ExceptionCons.java | 152 ++
.../dromara/warm/flow/constant/FlowCons.java | 68 +
.../org/dromara/warm/flow/dto/ApiResult.java | 96 ++
.../org/dromara/warm/flow/dto/DefChart.java | 50 +
.../org/dromara/warm/flow/dto/DefJson.java | 292 ++++
.../dromara/warm/flow/dto/FlowCombine.java | 59 +
.../org/dromara/warm/flow/dto/FlowDto.java | 42 +
.../org/dromara/warm/flow/dto/FlowPage.java | 70 +
.../org/dromara/warm/flow/dto/FlowParams.java | 340 ++++
.../org/dromara/warm/flow/dto/NodeJson.java | 125 ++
.../dromara/warm/flow/dto/PathWayData.java | 69 +
.../dromara/warm/flow/dto/PromptContent.java | 80 +
.../org/dromara/warm/flow/dto/SkipJson.java | 85 +
.../java/org/dromara/warm/flow/dto/Tree.java | 62 +
.../warm/flow/entity/FlowDefinition.java | 154 ++
.../dromara/warm/flow/entity/FlowHisTask.java | 171 ++
.../warm/flow/entity/FlowInstance.java | 140 ++
.../dromara/warm/flow/entity/FlowNode.java | 157 ++
.../dromara/warm/flow/entity/FlowSkip.java | 131 ++
.../dromara/warm/flow/entity/FlowTask.java | 129 ++
.../dromara/warm/flow/entity/FlowUser.java | 78 +
.../dromara/warm/flow/entity/RootEntity.java | 61 +
.../warm/flow/enums/ActivityStatus.java | 57 +
.../dromara/warm/flow/enums/ChartStatus.java | 158 ++
.../dromara/warm/flow/enums/Condition.java | 180 +++
.../warm/flow/enums/CooperateType.java | 198 +++
.../dromara/warm/flow/enums/FlowStatus.java | 104 ++
.../warm/flow/enums/FormCustomEnum.java | 40 +
.../warm/flow/enums/FrameworkType.java | 35 +
.../org/dromara/warm/flow/enums/Handler.java | 62 +
.../dromara/warm/flow/enums/ModelEnum.java | 40 +
.../org/dromara/warm/flow/enums/NodeType.java | 162 ++
.../warm/flow/enums/PublishStatus.java | 70 +
.../org/dromara/warm/flow/enums/SkipType.java | 102 ++
.../org/dromara/warm/flow/enums/UserType.java | 70 +
.../org/dromara/warm/flow/enums/VoteSign.java | 62 +
.../warm/flow/exception/FlowException.java | 80 +
.../flow/expression/ListenerStrategySpel.java | 41 +
.../flow/expression/SafeMethodResolver.java | 53 +
.../warm/flow/expression/SafeTypeLocator.java | 113 ++
.../warm/flow/expression/SpelHelper.java | 120 ++
.../warm/flow/handler/DataFillHandler.java | 106 ++
.../warm/flow/handler/PermissionHandler.java | 55 +
.../org/dromara/warm/flow/json/JsonUtil.java | 121 ++
.../warm/flow/listener/GlobalListener.java | 80 +
.../dromara/warm/flow/listener/Listener.java | 58 +
.../warm/flow/listener/ListenerVariable.java | 212 +++
.../warm/flow/listener/ValueHolder.java | 39 +
.../flow/mapper/FlowDefinitionMapper.java | 55 +
.../warm/flow/mapper/FlowHisTaskMapper.java | 74 +
.../warm/flow/mapper/FlowInstanceMapper.java | 42 +
.../warm/flow/mapper/FlowNodeMapper.java | 50 +
.../warm/flow/mapper/FlowSkipMapper.java | 41 +
.../warm/flow/mapper/FlowTaskMapper.java | 47 +
.../warm/flow/mapper/FlowUserMapper.java | 86 +
.../dromara/warm/flow/mapper/WarmMapper.java | 69 +
.../warm/flow/service/ChartService.java | 173 ++
.../dromara/warm/flow/service/DefService.java | 361 +++++
.../warm/flow/service/HisTaskService.java | 253 +++
.../warm/flow/service/IWarmService.java | 115 ++
.../dromara/warm/flow/service/InsService.java | 252 +++
.../warm/flow/service/NodeService.java | 366 +++++
.../warm/flow/service/SkipService.java | 59 +
.../warm/flow/service/TaskService.java | 993 ++++++++++++
.../warm/flow/service/UserService.java | 162 ++
.../warm/flow/service/WarmServiceImpl.java | 119 ++
.../warm/flow/strategy/ConditionStrategy.java | 44 +
.../flow/strategy/ExpressionStrategy.java | 60 +
.../warm/flow/strategy/HandlerStrategy.java | 62 +
.../warm/flow/strategy/ListenerStrategy.java | 38 +
.../warm/flow/strategy/VoteSignStrategy.java | 44 +
.../warm/flow/ui/config/WarmFlowUiConfig.java | 43 +
.../ui/controller/WarmFlowController.java | 182 +++
.../ui/controller/WarmFlowUiController.java | 44 +
.../warm/flow/ui/dto/HandlerFeedBackDto.java | 39 +
.../warm/flow/ui/dto/HandlerFunDto.java | 73 +
.../warm/flow/ui/dto/HandlerQuery.java | 71 +
.../dromara/warm/flow/ui/dto/TreeFunDto.java | 59 +
.../warm/flow/ui/service/CategoryService.java | 36 +
.../warm/flow/ui/service/ChartExtService.java | 93 ++
.../flow/ui/service/HandlerDictService.java | 35 +
.../flow/ui/service/HandlerSelectService.java | 134 ++
.../flow/ui/service/ListenerListService.java | 35 +
.../warm/flow/ui/service/NodeExtService.java | 35 +
.../warm/flow/ui/service/WarmFlowService.java | 330 ++++
.../dromara/warm/flow/ui/utils/TreeUtil.java | 86 +
.../org/dromara/warm/flow/ui/vo/Dict.java | 53 +
.../dromara/warm/flow/ui/vo/HandlerAuth.java | 58 +
.../warm/flow/ui/vo/HandlerFeedBackVo.java | 45 +
.../warm/flow/ui/vo/HandlerSelectVo.java | 46 +
.../dromara/warm/flow/ui/vo/ListenerVo.java | 52 +
.../org/dromara/warm/flow/ui/vo/NodeExt.java | 80 +
.../dromara/warm/flow/ui/vo/WarmFlowVo.java | 44 +
.../dromara/warm/flow/utils/AssertUtil.java | 114 ++
.../warm/flow/utils/ExpressionUtil.java | 197 +++
.../warm/flow/utils/FlowConfigUtil.java | 187 +++
.../dromara/warm/flow/utils/ListenerUtil.java | 164 ++
.../org/dromara/warm/flow/utils/MapUtil.java | 104 ++
.../org/dromara/warm/flow/utils/MathUtil.java | 121 ++
.../controller/FlwDefinitionController.java | 7 +-
.../controller/FlwInstanceController.java | 2 +-
.../controller/FlwTaskController.java | 5 +-
.../domain/context/CompleteTaskContext.java | 8 +-
.../domain/context/InstanceDeleteContext.java | 2 +-
.../domain/context/StartProcessContext.java | 13 +-
.../domain/context/TaskOperationContext.java | 8 +-
.../workflow/domain/vo/FlowHisTaskVo.java | 2 +-
.../workflow/domain/vo/FlowTaskVo.java | 4 +-
.../dromara/workflow/domain/vo/NodeExtVo.java | 3 +-
.../workflow/event/WorkflowCopyEvent.java | 4 +-
.../handler/FlowExceptionHandler.java | 2 +-
.../handler/FlowProcessEventHandler.java | 8 +-
.../handler/WorkflowPermissionHandler.java | 4 +-
.../listener/WorkflowGlobalListener.java | 40 +-
.../complete/CompleteAutoPassComponent.java | 16 +-
.../complete/CompleteExecuteComponent.java | 6 +-
.../complete/CompletePrepareComponent.java | 10 +-
.../InstanceDeleteEventComponent.java | 14 +-
.../InstanceDeleteExecuteComponent.java | 12 +-
.../instance/InstanceDeleteLoadComponent.java | 4 +-
.../operation/TaskOpExecuteComponent.java | 2 +-
.../operation/TaskOpLoadComponent.java | 12 +-
.../operation/TaskOpPrepareComponent.java | 2 +-
.../liteflow/start/StartExecuteComponent.java | 17 +-
.../start/StartPrepareInstanceComponent.java | 6 +-
.../start/StartPrepareRequestComponent.java | 4 +-
.../liteflow/start/StartResumeComponent.java | 9 +-
.../workflow/mapper/FlwHisTaskMapper.java | 8 +-
.../workflow/mapper/FlwInstanceMapper.java | 2 +-
.../workflow/mapper/FlwTaskMapper.java | 4 +-
.../workflow/mapper/FlwUserMapper.java | 2 +-
.../service/IFlwDefinitionService.java | 2 +-
.../workflow/service/IFlwInstanceService.java | 2 +-
.../workflow/service/IFlwNodeExtService.java | 3 +-
.../workflow/service/IFlwTaskService.java | 12 +-
.../service/impl/FlwCategoryServiceImpl.java | 19 +-
.../service/impl/FlwChartExtServiceImpl.java | 14 +-
.../service/impl/FlwCommonServiceImpl.java | 8 +-
.../impl/FlwDefinitionServiceImpl.java | 20 +-
.../service/impl/FlwInstanceServiceImpl.java | 74 +-
.../service/impl/FlwNodeExtServiceImpl.java | 11 +-
.../impl/FlwTaskAssigneeServiceImpl.java | 4 -
.../service/impl/FlwTaskServiceImpl.java | 134 +-
.../service/impl/WorkflowServiceImpl.java | 2 +-
.../warm-flow-ui/css/index-DlKfNZiv.css | 1 +
.../warm-flow-ui/ico/favicon-FqvijpIH.ico | Bin 0 -> 5488 bytes
.../main/resources/warm-flow-ui/index.html | 228 +++
.../warm-flow-ui/js/index-BTmmNT3K.js | 1401 +++++++++++++++++
script/sql/oracle/oracle_ry_workflow.sql | 34 +-
script/sql/postgres/postgres_ry_workflow.sql | 14 -
script/sql/ry_workflow.sql | 7 -
.../sql/sqlserver/sqlserver_ry_workflow.sql | 56 -
158 files changed, 13409 insertions(+), 414 deletions(-)
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/FlowEngine.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/config/WarmFlowInitializer.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/config/WarmFlowProperties.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/constant/ExceptionCons.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/constant/FlowCons.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/ApiResult.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/DefChart.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/DefJson.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowCombine.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowDto.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowPage.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowParams.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/NodeJson.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/PathWayData.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/PromptContent.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/SkipJson.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/Tree.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowDefinition.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowHisTask.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowInstance.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowNode.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowSkip.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowTask.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowUser.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/RootEntity.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ActivityStatus.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ChartStatus.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/Condition.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/CooperateType.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FlowStatus.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FormCustomEnum.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FrameworkType.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/Handler.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ModelEnum.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/NodeType.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/PublishStatus.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/SkipType.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/UserType.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/VoteSign.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/exception/FlowException.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/ListenerStrategySpel.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SafeMethodResolver.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SafeTypeLocator.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SpelHelper.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/handler/DataFillHandler.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/handler/PermissionHandler.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/json/JsonUtil.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/GlobalListener.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/Listener.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/ListenerVariable.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/ValueHolder.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowDefinitionMapper.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowHisTaskMapper.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowInstanceMapper.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowNodeMapper.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowSkipMapper.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowTaskMapper.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowUserMapper.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/WarmMapper.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/ChartService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/DefService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/HisTaskService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/IWarmService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/InsService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/NodeService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/SkipService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/TaskService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/UserService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/WarmServiceImpl.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/strategy/ConditionStrategy.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/strategy/ExpressionStrategy.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/strategy/HandlerStrategy.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/strategy/ListenerStrategy.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/strategy/VoteSignStrategy.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/config/WarmFlowUiConfig.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/controller/WarmFlowController.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/controller/WarmFlowUiController.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/dto/HandlerFeedBackDto.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/dto/HandlerFunDto.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/dto/HandlerQuery.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/dto/TreeFunDto.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/service/CategoryService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/service/ChartExtService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/service/HandlerDictService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/service/HandlerSelectService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/service/ListenerListService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/service/NodeExtService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/service/WarmFlowService.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/utils/TreeUtil.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/vo/Dict.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/vo/HandlerAuth.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/vo/HandlerFeedBackVo.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/vo/HandlerSelectVo.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/vo/ListenerVo.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/vo/NodeExt.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/ui/vo/WarmFlowVo.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/utils/AssertUtil.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/utils/ExpressionUtil.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/utils/FlowConfigUtil.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/utils/ListenerUtil.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/utils/MapUtil.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/utils/MathUtil.java
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/warm-flow-ui/css/index-DlKfNZiv.css
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/warm-flow-ui/ico/favicon-FqvijpIH.ico
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/warm-flow-ui/index.html
create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/warm-flow-ui/js/index-BTmmNT3K.js
diff --git a/pom.xml b/pom.xml
index 8494ba12b..d596e7177 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,7 +62,6 @@
3.3.5
- 1.8.9
2.16.1.2
2.6.8
@@ -346,19 +345,6 @@
${mapstruct-plus.version}
-
-
- org.dromara.warm
- warm-flow-mybatis-plus-sb4-starter
- ${warm-flow.version}
-
-
-
- org.dromara.warm
- warm-flow-plugin-ui-sb-web
- ${warm-flow.version}
-
-
com.yomahub
diff --git a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java
index fdf49585f..d612cdd73 100644
--- a/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java
+++ b/ruoyi-common/ruoyi-common-core/src/main/java/org/dromara/common/core/utils/SpringUtils.java
@@ -69,4 +69,18 @@ public final class SpringUtils extends SpringUtil {
return Threading.VIRTUAL.isActive(getBean(Environment.class));
}
+ /**
+ * 获取Bean 容器中不存在时返回null而非抛出异常
+ *
+ * @param clazz Bean类型
+ * @return Bean实例 不存在时返回null
+ */
+ public static T getBeanOrNull(Class clazz) {
+ try {
+ return getBean(clazz);
+ } catch (NoSuchBeanDefinitionException e) {
+ return null;
+ }
+ }
+
}
diff --git a/ruoyi-modules/ruoyi-workflow/pom.xml b/ruoyi-modules/ruoyi-workflow/pom.xml
index 814de3e96..afe06c82d 100644
--- a/ruoyi-modules/ruoyi-workflow/pom.xml
+++ b/ruoyi-modules/ruoyi-workflow/pom.xml
@@ -84,18 +84,6 @@
ruoyi-api
-
-
- org.dromara.warm
- warm-flow-mybatis-plus-sb4-starter
-
-
-
-
- org.dromara.warm
- warm-flow-plugin-ui-sb-web
-
-
org.dromara
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/FlowEngine.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/FlowEngine.java
new file mode 100644
index 000000000..6fa2281a4
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/FlowEngine.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow;
+
+import cn.hutool.core.util.StrUtil;
+
+import org.dromara.warm.flow.config.WarmFlowProperties;
+import org.dromara.warm.flow.handler.DataFillHandler;
+import org.dromara.warm.flow.handler.PermissionHandler;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.warm.flow.listener.GlobalListener;
+import org.dromara.warm.flow.service.*;
+import cn.hutool.core.util.ClassUtil;
+
+
+
+import java.lang.reflect.Constructor;
+import java.util.function.Supplier;
+/**
+ * 流程引擎,通过静态方法驱动流程流转
+ */
+public class FlowEngine {
+
+ private static WarmFlowProperties flowConfig;
+ private static DataFillHandler dataFillHandler;
+ private static PermissionHandler permissionHandler;
+ private static GlobalListener globalListener;
+
+ public static DefService defService() {
+ return SpringUtils.getBeanOrNull(DefService.class);
+ }
+
+ public static NodeService nodeService() {
+ return SpringUtils.getBeanOrNull(NodeService.class);
+ }
+
+ public static SkipService skipService() {
+ return SpringUtils.getBeanOrNull(SkipService.class);
+ }
+
+ public static InsService insService() {
+ return SpringUtils.getBeanOrNull(InsService.class);
+ }
+
+ public static TaskService taskService() {
+ return SpringUtils.getBeanOrNull(TaskService.class);
+ }
+
+ public static HisTaskService hisTaskService() {
+ return SpringUtils.getBeanOrNull(HisTaskService.class);
+ }
+
+ public static UserService userService() {
+ return SpringUtils.getBeanOrNull(UserService.class);
+ }
+
+ public static ChartService chartService() {
+ return SpringUtils.getBeanOrNull(ChartService.class);
+ }
+
+ public static WarmFlowProperties getFlowConfig() {
+ return flowConfig;
+ }
+
+ public static void setFlowConfig(WarmFlowProperties flowConfig) {
+ FlowEngine.flowConfig = flowConfig;
+ }
+
+ public static void initDataFillHandler(String handlerPath) {
+ dataFillHandler = initBean(DataFillHandler.class, handlerPath, () -> new DataFillHandler() {});
+ }
+
+ public static void initPermissionHandler(String handlerPath) {
+ permissionHandler = initBean(PermissionHandler.class, handlerPath, null);
+ }
+
+ public static void initGlobalListener(String handlerPath) {
+ globalListener = initBean(GlobalListener.class, handlerPath, null);
+ }
+
+ /**
+ * 获取填充类
+ */
+ public static DataFillHandler dataFillHandler() {
+ return dataFillHandler;
+ }
+
+ /**
+ * 获取填充类
+ */
+ public static PermissionHandler permissionHandler() {
+ return permissionHandler;
+ }
+
+ /**
+ * 获取全局监听器
+ */
+ public static GlobalListener globalListener() {
+ return globalListener;
+ }
+
+ /**
+ * 获取数据库类型
+ */
+ public static String dataSourceType() {
+ return flowConfig.getDataSourceType();
+ }
+
+ /**
+ * 初始化bean,先从yml配置获取bean的全包名路径,否则从spring容器获取bean,如果都没有,则通过supplier获取bean
+ *
+ * @param tClazz bean的class类型
+ * @param beanPath bean全包名路径
+ * @param supplier 获取bean的lambda
+ * @param bean类型
+ * @return bean
+ */
+ private static T initBean(Class tClazz, String beanPath, Supplier supplier) {
+ T hander = null;
+ try {
+ if (!StrUtil.isEmpty(beanPath)) {
+ Class> clazz = ClassUtil.loadClass(beanPath);
+ if (clazz != null && tClazz.isAssignableFrom(clazz)) {
+ Constructor> constructor = clazz.getConstructor();
+ hander = tClazz.cast(constructor.newInstance());
+ }
+ }
+ } catch (Exception ignored) {
+ }
+ if (hander == null) {
+ hander = SpringUtils.getBeanOrNull(tClazz);
+ }
+ if (hander == null && supplier != null) {
+ hander = supplier.get();
+ }
+ return hander;
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/config/WarmFlowInitializer.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/config/WarmFlowInitializer.java
new file mode 100644
index 000000000..438caa1a3
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/config/WarmFlowInitializer.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.config;
+
+import jakarta.annotation.PostConstruct;
+import org.dromara.warm.flow.FlowEngine;
+import org.dromara.warm.flow.enums.FrameworkType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+/**
+ * 工作流引擎初始化,各服务由注解扫描注册(见 flow.service 包的 @Service)
+ *
+ * @author warm
+ * @since 2023/6/5 23:01
+ */
+@Component
+@EnableConfigurationProperties(WarmFlowProperties.class)
+@ConditionalOnProperty(value = "warm-flow.enabled", havingValue = "true", matchIfMissing = true)
+public class WarmFlowInitializer {
+
+ private static final Logger log = LoggerFactory.getLogger(WarmFlowInitializer.class);
+
+ private final WarmFlowProperties warmFlow;
+
+ public WarmFlowInitializer(WarmFlowProperties warmFlow) {
+ this.warmFlow = warmFlow;
+ }
+
+ @PostConstruct
+ public void init() {
+ warmFlow.init();
+ warmFlow.setFramework(FrameworkType.SPRING_BOOT);
+ FlowEngine.setFlowConfig(warmFlow);
+ log.info("【warm-flow】,加载完成");
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/config/WarmFlowProperties.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/config/WarmFlowProperties.java
new file mode 100644
index 000000000..616b52e53
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/config/WarmFlowProperties.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.config;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.dromara.warm.flow.FlowEngine;
+import org.dromara.warm.flow.enums.ChartStatus;
+import org.dromara.warm.flow.enums.FrameworkType;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.io.Serializable;
+import java.util.List;
+/**
+ * WarmFlow属性配置文件(warm-flow 前缀绑定)
+ *
+ * @author warm
+ */
+@Getter
+@Setter
+@ConfigurationProperties("warm-flow")
+public class WarmFlowProperties implements Serializable {
+
+ /**
+ * 开关
+ */
+ private boolean enabled = true;
+
+ /**
+ * 框架类型: springboot、solon
+ */
+ private FrameworkType framework;
+
+ /**
+ * 启动banner
+ */
+ private boolean banner = true;
+
+ /**
+ * 数据填充处理类路径
+ */
+ private String dataFillHandlerPath;
+
+ /**
+ * 办理人权限处理器类路径
+ */
+ private String permissionHandlerPath;
+
+ /**
+ * 全局监听器类路径
+ */
+ private String globalListenerPath;
+
+ /**
+ * 数据源类型, mybatis模块对orm进一步的封装, 由于各数据库分页语句存在差异,
+ * 当配置此参数时, 以此参数结果为基准, 未配置时, 取DataSource中数据源类型,
+ * 兜底为mysql数据库
+ */
+ private String dataSourceType;
+
+ /**
+ * ui开关
+ */
+ private boolean ui = true;
+
+ /**
+ * 如果需要工作流共享业务系统权限,默认Authorization,如果有多个token,用逗号分隔
+ */
+ private String tokenName = "Authorization";
+
+ /**
+ * 公共模型流程状态对应的三原色
+ */
+ private List chartStatusColor;
+
+ /**
+ * 经典模式流程状态对应的三原色
+ */
+ private List chartStatusColorClassics;
+
+ /**
+ * 仿钉钉模式流程状态对应的三原色
+ */
+ private List chartStatusColorMimic;
+
+ /**
+ * 是否显示流程图顶部文字
+ */
+ private boolean topTextShow = true;
+
+ public void init() {;
+
+ // 设置数据填充处理类
+ FlowEngine.initDataFillHandler(this.getDataFillHandlerPath());
+
+ // 设置办理人权限处理类
+ FlowEngine.initPermissionHandler(this.getPermissionHandlerPath());
+
+ // 设置全局监听器
+ FlowEngine.initGlobalListener(this.getGlobalListenerPath());
+
+ // 打印banner图
+ printBanner();
+
+ // 初始化流程状态对应的自定义三原色
+ ChartStatus.initCustomColor(this.getChartStatusColor(), this.getChartStatusColorClassics(), this.getChartStatusColorMimic());
+ }
+
+ private void printBanner() {
+ if (this.isBanner()) {
+ System.out.println("\n" +
+ " ▄ ▄ ▄▄▄▄▄▄ ▄ \n" +
+ " █ █ █ ▄▄▄ ▄ ▄▄ ▄▄▄▄▄ █ █ ▄▄▄ ▄ ▄ \n" +
+ " ▀ █▀█ █ ▀ █ █▀ ▀ █ █ █ ▄▄▄▄▄ █▄▄▄▄▄ █ █▀ ▀█ ▀▄ ▄ ▄▀ \n" +
+ " ██ ██▀ ▄▀▀▀█ █ █ █ █ █ █ █ █ █▄█▄█ \n" +
+ " █ █ ▀▄▄▀█ █ █ █ █ █ █▄▄ ▀█▄█▀ █ █ \n" +
+ "\n" +
+ "\033[32m :: Warm-Flow :: (v" + WarmFlowProperties.class.getPackage()
+ .getImplementationVersion() + ")\033[0m\n");
+ }
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/constant/ExceptionCons.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/constant/ExceptionCons.java
new file mode 100644
index 000000000..9ffbc03d0
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/constant/ExceptionCons.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.constant;
+
+/**
+ * 工作流中用到的一些常量
+ *
+ * @author warm
+ * @since 2023/3/30 14:05
+ */
+public class ExceptionCons {
+
+ public static final String SAME_CONDITION_VALUE = "中间节点,同一个节点不能有相同跳转类型,跳转同一个目标节点!";
+
+ public static final String SAME_CONDITION_NODE = "互斥网关,同一个节点不能有相同跳转条件,跳转同一个目标节点!";
+
+ public static final String SAME_DEST_NODE = "并行网关,同一个节点不能跳转同一个目标节点!";
+
+ public static final String MUL_START_NODE = "开始节点不能超过1个!";
+
+ public static final String MUL_SKIP_BETWEEN = "不可同时通过或者退回到多个中间节点,必须先流转到网关节点!";
+
+ public static final String MUL_START_SKIP = "节点流转条件不能超过1个!";
+
+ public static final String LOST_START_NODE = "流程缺少开始节点!";
+
+ public static final String LOST_NODE_CODE = "节点编码缺失";
+
+ public static final String MUST_SKIP = "开始或者中间节点必须画跳转线";
+
+ public static final String SAME_NODE_CODE = "同一流程中节点编码重复!";
+
+ public static final String NULL_DEST_NODE = "无法他跳转,未配置目标节点!";
+
+ public static final String NULL_SKIP_TYPE = "未找到跳转类型匹配的目标节点!";
+
+ public static final String NULL_CONDITION_VALUE_NODE = "未找到跳转条件,不支持跳转!";
+
+ public static final String NULL_CONDITION_VALUE = "跳转条件不能为空!";
+
+ public static final String FIRST_FORBID_BACK = "禁止退回到第一个节点";
+
+ public static final String NULL_ROLE_NODE = "无法跳转到该节点,请检查当前用户是否有权限!";
+
+ public static final String LOST_DEST_NODE = "目标节点为空!";
+
+ public static final String LOST_CUR_NODE = "当前流程节点丢失!";
+
+ public static final String NULL_NODE_CODE = "目标节点编码不存在!";
+
+ public static final String NULL_BUSINESS_ID = "业务id为空!";
+
+ public static final String NULL_FLOW_CODE = "流程编码缺失!";
+
+ public static final String NOT_FOUNT_DEF = "流程定义不存在!";
+
+ public static final String NOT_FOUNT_INSTANCE = "流程实例获取失败!";
+
+ public static final String NULL_INSTANCE_ID = "流程实例id不能为空!";
+
+ public static final String NULL_TASK_ID = "任务id不能为空!";
+
+ public static final String NOT_FOUNT_TASK = "未找到待办任务!";
+
+ public static final String TASK_NOT_ONE = "此接口不能同时跳转多个待办任务,请更换!";
+
+ public static final String NOT_DEFINITION_ID = "流程定义id不能为空!";
+
+ public static final String NOT_NODE_DATA = "流程节点数据缺失!";
+
+ public static final String EXIST_START_TASK = "流程定义已开启过审批任务,不可操作!";
+
+ public static final String FLOW_FINISH = "流程已完成!";
+
+ public static final String NOT_AUTHORITY = "请检查当前用户是否有权限!";
+
+ public static final String SIGN_NULL_HANDLER = "会签票签时,办理人标识不能为空";
+
+ public static final String REDUCTION_SIGN_ONE_ERROR = "办理人不足或者只有一人,不可减签";
+
+ public static final String IS_ALREADY_SIGN = "已经是待办人,不可加签";
+
+ public static final String IS_ALREADY_TRANSFER = "已经是转办人,不可转办";
+
+ public static final String IS_ALREADY_DEPUTE = "已经是受托人,不可委托";
+
+ public static final String NOT_ACTIVITY = "当前流程定义或者实例已经挂起,请先激活";
+
+ public static final String NOT_DEFINITION_ACTIVITY = "当前流程定义已挂起,不可开启新的流程";
+
+ public static final String DEFINITION_ALREADY_ACTIVITY = "当前流程定义已经激活";
+
+ public static final String DEFINITION_ALREADY_SUSPENDED = "当前流程定义已经挂起";
+
+ public static final String INSTANCE_ALREADY_ACTIVITY = "当前流程实例已经激活";
+
+ public static final String INSTANCE_ALREADY_SUSPENDED = "当前流程实例已经挂起";
+
+ public static final String FORM_ALREADY_PUBLISH = "当前表单状态已发布";
+
+ public static final String FORM_ALREADY_UN_PUBLISH = "当前表单状态未发布";
+
+ public static final String FORM_NOT_ONE = "表单数据错误, 请联系管理员排查!";
+
+ public static final String ID_EMPTY = "ID不能为空";
+
+ public static final String NOT_DEF_PROMOTER_NOT_CANCEL = "不是当前流程的发起人,无法撤销";
+
+ public static final String HANDLER_NOT_EMPTY = "办理人不能为空";
+
+ public static final String TAR_NOT_GATEWAY = "目标节点不能是网关节点!";
+
+ public static final String NOT_FOUND_FLOW_TASK = "未获取到流程任务";
+
+ public static final String FLOW_HAVE_USELESS_SKIP = "存在无用的跳转";
+
+ public static final String NULL_TRANSFER_HANDLER = "转办对象不能为空";
+
+ public static final String NULL_DEPUTE_HANDLER = "委托对象不能为空";
+
+ public static final String NULL_ADD_SIGNATURE_HANDLER = "加签对象不能为空";
+
+ public static final String NULL_REDUCTION_SIGNATURE_HANDLER = "减签对象不能为空";
+
+ public static final String READ_IS_ERROR = "读取is流失败";
+
+ public static final String EXIST_USE_FORM = "流程表单已使用,不可操作!";
+
+ public static final String NOT_FOUNT_LAST_TASK = "未找到前置任务!";
+
+ public static final String NOT_FOUNT_HANDLED_TASK = "未找到您已办理过的任务!";
+
+ public static final String NOT_FOUNT_HANDLED_TASK_HANDLER = "拿回的任务未办理!";
+
+ public static final String START_NODE_NOT_ALLOW_JUMP = "开始节点不允许跳转!";
+
+ public static final String NOT_DRAW_FLOW_ERROR = "未绘制流程图,不可发布";
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/constant/FlowCons.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/constant/FlowCons.java
new file mode 100644
index 000000000..246003249
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/constant/FlowCons.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.constant;
+
+import java.util.regex.Pattern;
+
+/**
+ * warm-flow常量
+ *
+ * @author warm
+ */
+public class FlowCons {
+
+ /**
+ * 分隔符
+ */
+ public static final String SPLIT_AT = "@@";
+ public static final String SPLIT_VERTICAL = "\\|";
+ public static final String DEFAULT = "default";
+ public static final String SPEL = "spel";
+ public static final String SNEL = "snel";
+
+ public static final Pattern LISTENER_PATTERN = Pattern.compile("^([^()]*)(.*)$");
+
+ /**
+ * 权限标识中的发起人标识符,办理过程中进行替换
+ */
+ public static final String WARMFLOWINITIATOR = "warmFlowInitiator";
+
+ /**
+ * 监听器参数
+ */
+ public static final String WARM_LISTENER_PARAM = "WarmListenerParam";
+
+ /**
+ * 表单自定义状态
+ * 内置表单
+ */
+ public static final String FORM_CUSTOM_Y = "Y";
+ /**
+ * 外挂表单路径
+ */
+ public static final String FORM_CUSTOM_N = "N";
+
+ /**
+ * 表单数据
+ */
+ public static final String FORM_DATA = "formData";
+
+
+ public static final String PREVIOUS = "previous";
+
+ public static final String SUFFIX = "suffix";
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/ApiResult.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/ApiResult.java
new file mode 100644
index 000000000..59bf22c7d
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/ApiResult.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+/**
+ * 响应信息主体
+ *
+ * @author ruoyi
+ */
+@Setter
+@Getter
+public class ApiResult implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 成功
+ */
+ public static final int SUCCESS = 200;
+
+ /**
+ * 失败
+ */
+ public static final int FAIL = 500;
+
+ private int code;
+
+ private String msg;
+
+ private T data;
+
+ public static ApiResult ok() {
+ return restResult(null, SUCCESS, "操作成功");
+ }
+
+ public static ApiResult ok(T data) {
+ return restResult(data, SUCCESS, "操作成功");
+ }
+
+ public static ApiResult ok(T data, String msg) {
+ return restResult(data, SUCCESS, msg);
+ }
+
+ public static ApiResult fail() {
+ return restResult(null, FAIL, "操作失败");
+ }
+
+ public static ApiResult fail(String msg) {
+ return restResult(null, FAIL, msg);
+ }
+
+ public static ApiResult fail(T data) {
+ return restResult(data, FAIL, "操作失败");
+ }
+
+ public static ApiResult fail(T data, String msg) {
+ return restResult(data, FAIL, msg);
+ }
+
+ public static ApiResult fail(int code, String msg) {
+ return restResult(null, code, msg);
+ }
+
+ private static ApiResult restResult(T data, int code, String msg) {
+ ApiResult apiResult = new ApiResult<>();
+ apiResult.setCode(code);
+ apiResult.setData(data);
+ apiResult.setMsg(msg);
+ return apiResult;
+ }
+
+ public static Boolean isError(ApiResult ret) {
+ return !isSuccess(ret);
+ }
+
+ public static Boolean isSuccess(ApiResult ret) {
+ return ApiResult.SUCCESS == ret.getCode();
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/DefChart.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/DefChart.java
new file mode 100644
index 000000000..925aceed5
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/DefChart.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * 流程图所需数据集合
+ *
+ * @author warm
+ * @since 2023/3/30 14:27
+ */
+@Getter
+@Setter
+public class DefChart {
+ /**
+ * 流程图所需的流程定义
+ */
+ private DefJson defJson = new DefJson();
+
+ /**
+ * 流程图所需的流程节点
+ */
+ private List nodeJsonList = new ArrayList<>();
+
+ /**
+ * 流程图所需的流程跳转
+ */
+ private List skipJsonList = new ArrayList<>();
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/DefJson.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/DefJson.java
new file mode 100644
index 000000000..f693274c8
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/DefJson.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+import org.dromara.warm.flow.entity.*;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import lombok.experimental.Accessors;
+import org.dromara.warm.flow.FlowEngine;
+import org.dromara.warm.flow.entity.FlowDefinition;
+import org.dromara.warm.flow.entity.FlowInstance;
+import org.dromara.warm.flow.entity.FlowNode;
+import org.dromara.warm.flow.entity.FlowSkip;
+
+
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 流程定义json对象
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+@Setter
+@Getter
+@Accessors(chain = true)
+@ToString
+public class DefJson {
+
+ /**
+ * 主键
+ */
+ private Long id;
+
+ /**
+ * 流程编码
+ */
+ private String flowCode;
+
+ /**
+ * 流程名称
+ */
+ private String flowName;
+
+ /**
+ * 设计器模型(CLASSICS经典模型 MIMIC仿钉钉模型)
+ */
+ private String modelValue;
+
+ /**
+ * 流程类别
+ */
+ private String category;
+
+ /**
+ * 流程版本
+ */
+ private String version;
+
+ /**
+ * 是否发布(0未开启 1开启)
+ */
+ private Integer isPublish;
+
+ /**
+ * 审批表单是否自定义(Y=是 N=否)
+ */
+ private String formCustom;
+
+ /**
+ * 审批表单是否自定义(Y=是 N=否)
+ */
+ private String formPath;
+
+ /**
+ * 监听器类型
+ */
+ private String listenerType;
+
+ /**
+ * 监听器路径
+ */
+ private String listenerPath;
+
+ /**
+ * 实例对象
+ */
+ private FlowInstance instance;
+
+ /**
+ * 扩展字段,预留给业务系统使用
+ */
+ private String ext;
+
+ /**
+ * 扩展map,保存业务自定义扩展属性
+ */
+ private Map extMap;
+
+ /**
+ * 所有节点结合
+ */
+ private List nodeList = new ArrayList<>();
+
+ /**
+ * 流程状态对应的三原色
+ */
+ private List chartStatusColor;
+
+ /**
+ * 顶部信息: 比如流程名称等
+ */
+ private String topText;
+
+ /**
+ * 顶部信息: 流程名称是否显示
+ */
+ private boolean topTextShow;
+
+ private String createBy;
+
+ private String updateBy;
+
+ /**
+ * 流程类别
+ */
+ private List categoryList;
+
+ /**
+ * 自定义表单的唯一标识:如formCode+version
+ */
+ private List formPathList;
+
+
+ public String getModelValue() {
+ if (StrUtil.isEmpty(modelValue)) {
+ modelValue = "CLASSICS";
+ }
+ return modelValue;
+ }
+
+ public static DefJson copyDef(FlowDefinition definition) {
+ DefJson defJson = new DefJson()
+ .setFlowCode(definition.getFlowCode())
+ .setFlowName(definition.getFlowName())
+ .setModelValue(definition.getModelValue())
+ .setVersion(definition.getVersion())
+ .setIsPublish(definition.getIsPublish())
+ .setCategory(definition.getCategory())
+ .setFormCustom(definition.getFormCustom())
+ .setFormPath(definition.getFormPath())
+ .setListenerType(definition.getListenerType())
+ .setListenerPath(definition.getListenerPath())
+ .setExt(definition.getExt())
+ .setCreateBy(definition.getCreateBy())
+ .setUpdateBy(definition.getUpdateBy());
+
+ List nodeList = new ArrayList<>();
+ defJson.setNodeList(nodeList);
+ for (FlowNode node : definition.getNodeList()) {
+ // 向节点中添加子节点
+ NodeJson nodeJson = new NodeJson()
+ .setNodeType(node.getNodeType())
+ .setNodeCode(node.getNodeCode())
+ .setNodeName(node.getNodeName())
+ .setPermissionFlag(node.getPermissionFlag())
+ .setNodeRatio(node.getNodeRatio())
+ .setCoordinate(node.getCoordinate())
+ .setAnyNodeSkip(node.getAnyNodeSkip())
+ .setListenerType(node.getListenerType())
+ .setListenerPath(node.getListenerPath())
+ .setFormCustom(node.getFormCustom())
+ .setFormPath(node.getFormPath())
+ .setExt(node.getExt())
+ .setCreateBy(node.getCreateBy())
+ .setUpdateBy(node.getUpdateBy());
+ nodeList.add(nodeJson);
+
+ List skipList = new ArrayList<>();
+ nodeJson.setSkipList(skipList);
+ if (CollUtil.isNotEmpty(node.getSkipList())) {
+ for (FlowSkip skip : node.getSkipList()) {
+ skipList.add(new SkipJson()
+ .setCoordinate(skip.getCoordinate())
+ .setSkipType(skip.getSkipType())
+ .setSkipName(skip.getSkipName())
+ .setSkipCondition(skip.getSkipCondition())
+ .setNowNodeCode(skip.getNowNodeCode())
+ .setNextNodeCode(skip.getNextNodeCode())
+ .setCreateBy(skip.getCreateBy())
+ .setUpdateBy(skip.getUpdateBy()));
+ }
+ }
+
+ }
+ return defJson;
+ }
+
+ public static FlowDefinition copyDef(DefJson defJson) {
+ FlowDefinition definition = new FlowDefinition()
+ .setId(defJson.getId())
+ .setFlowCode(defJson.getFlowCode())
+ .setFlowName(defJson.getFlowName())
+ .setModelValue(defJson.getModelValue())
+ .setVersion(defJson.getVersion())
+ .setCategory(defJson.getCategory())
+ .setFormCustom(defJson.getFormCustom())
+ .setFormPath(defJson.getFormPath())
+ .setListenerType(defJson.getListenerType())
+ .setListenerPath(defJson.getListenerPath())
+ .setExt(defJson.getExt())
+ .setCreateBy(defJson.getCreateBy())
+ .setUpdateBy(defJson.getUpdateBy());
+
+ List nodeList = new ArrayList<>();
+ definition.setNodeList(nodeList);
+ for (NodeJson nodeJson : defJson.getNodeList()) {
+ // 向节点中添加子节点
+ FlowNode node = new FlowNode()
+ .setNodeType(nodeJson.getNodeType())
+ .setNodeCode(nodeJson.getNodeCode())
+ .setNodeName(nodeJson.getNodeName())
+ .setPermissionFlag(nodeJson.getPermissionFlag())
+ .setNodeRatio(nodeJson.getNodeRatio() != null ? nodeJson.getNodeRatio() : "0")
+ .setCoordinate(nodeJson.getCoordinate())
+ .setAnyNodeSkip(nodeJson.getAnyNodeSkip())
+ .setListenerType(nodeJson.getListenerType())
+ .setListenerPath(nodeJson.getListenerPath())
+ .setFormCustom(nodeJson.getFormCustom())
+ .setFormPath(nodeJson.getFormPath())
+ .setExt(nodeJson.getExt())
+ .setCreateBy(nodeJson.getCreateBy())
+ .setUpdateBy(nodeJson.getUpdateBy());
+ nodeList.add(node);
+
+ List skipList = new ArrayList<>();
+ node.setSkipList(skipList);
+
+ if (CollUtil.isNotEmpty(nodeJson.getSkipList())) {
+ for (SkipJson skipJson : nodeJson.getSkipList()) {
+ skipList.add(new FlowSkip()
+ .setCoordinate(skipJson.getCoordinate())
+ .setSkipType(skipJson.getSkipType())
+ .setSkipName(skipJson.getSkipName())
+ .setSkipCondition(skipJson.getSkipCondition())
+ .setNowNodeCode(skipJson.getNowNodeCode())
+ .setNextNodeCode(skipJson.getNextNodeCode())
+ .setCreateBy(skipJson.getCreateBy())
+ .setUpdateBy(skipJson.getUpdateBy()));
+ }
+ }
+
+ }
+ return definition;
+ }
+
+ public static FlowCombine copyCombine(DefJson defJson) {
+ FlowDefinition definition = copyDef(defJson);
+ FlowCombine flowCombine = new FlowCombine();
+ flowCombine.setDefinition(definition);
+ flowCombine.setAllNodes(definition.getNodeList());
+ List skipList = definition.getNodeList().stream()
+ .map(FlowNode::getSkipList)
+ .filter(Objects::nonNull)
+ .flatMap(List::stream)
+ .collect(Collectors.toList());
+
+ flowCombine.setAllSkips(skipList);
+ return flowCombine;
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowCombine.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowCombine.java
new file mode 100644
index 000000000..a57f3fa01
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowCombine.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+import org.dromara.warm.flow.entity.*;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.dromara.warm.flow.FlowEngine;
+import org.dromara.warm.flow.entity.FlowDefinition;
+import org.dromara.warm.flow.entity.FlowNode;
+import org.dromara.warm.flow.entity.FlowSkip;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * 流程数据集合
+ *
+ * @author warm
+ * @since 2023/3/30 14:27
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+public class FlowCombine {
+ /**
+ * 所有的流程定义
+ */
+ private FlowDefinition definition = new FlowDefinition();
+
+ /**
+ * 所有的流程节点
+ */
+ private List allNodes = new ArrayList<>();
+
+ /**
+ * 所有的流程节点跳转关联
+ */
+ private List allSkips = new ArrayList<>();
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowDto.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowDto.java
new file mode 100644
index 000000000..5e26facaa
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowDto.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.io.Serializable;
+
+/**
+ * @author vanlin
+ * @since 2024-9-24 11:11
+ */
+@Getter
+@Setter
+public class FlowDto implements Serializable {
+
+ /**
+ * ID
+ */
+ private Long id;
+
+ /**
+ * 数据
+ */
+ private Object data;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowPage.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowPage.java
new file mode 100644
index 000000000..b486bc3bc
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowPage.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.List;
+
+/**
+ * 表格分页数据对象
+ *
+ * @author ruoyi
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@Accessors(chain = true)
+public class FlowPage implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 总记录数
+ */
+ private long total;
+
+ /**
+ * 列表数据
+ */
+ private List rows;
+
+ /**
+ * 消息状态码
+ */
+ private int code;
+
+ /**
+ * 消息内容
+ */
+ private String msg;
+
+ /**
+ * 分页
+ *
+ * @param list 列表数据
+ * @param total 总记录数
+ */
+ public FlowPage(List list, int total) {
+ this.rows = list;
+ this.total = total;
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowParams.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowParams.java
new file mode 100644
index 000000000..81a3e3929
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/FlowParams.java
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+
+import org.dromara.warm.flow.json.JsonUtil;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+
+import lombok.Getter;
+import org.dromara.warm.flow.FlowEngine;
+import org.dromara.warm.flow.handler.PermissionHandler;
+
+
+
+import java.io.Serializable;
+import java.util.*;
+
+/**
+ * 工作流内置参数
+ *
+ * @author warm
+ * @since 2023/3/31 17:18
+ */
+public class FlowParams implements Serializable {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 流程编码
+ */
+ @Getter
+ private String flowCode;
+
+ /**
+ * 当前办理人唯一标识:就是确定唯一用的,如用户id,通常用来入库,记录流程实例创建人,办理人
+ */
+ private String handler;
+
+ /**
+ * 节点编码(如果要指定跳转节点,传入)
+ */
+ @Getter
+ private String nodeCode;
+
+ /**
+ * 用户权限标识:和办理权限有关,是否有办理权限,通俗来说,就是设计器里面预设的办理人,和这个标识是否有交集,有交集就可以办理,审批的时候,就不会提示报错
+ */
+ private List permissionFlag;
+
+ /**
+ * 跳转类型(PASS审批通过 REJECT退回)
+ */
+ @Getter
+ private String skipType;
+
+ /**
+ * 审批意见
+ */
+ @Getter
+ private String message;
+
+ /**
+ * 流程变量
+ */
+ @Getter
+ private Map variable = new HashMap<>();
+
+ /**
+ * 流程实例状态
+ */
+ @Getter
+ private String flowStatus;
+
+ /**
+ * 历史任务表状态
+ */
+ @Getter
+ private String hisStatus;
+
+ /**
+ * 流程激活状态(0挂起 1激活)
+ */
+ @Getter
+ private Integer activityStatus;
+
+ /**
+ * 协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)
+ */
+ @Getter
+ private Integer cooperateType;
+
+ /**
+ * 扩展字段,预留给业务系统使用
+ */
+ @Getter
+ private String ext;
+
+ /**
+ * 扩展字段,预留给业务系统使用
+ */
+ @Getter
+ private String hisTaskExt;
+
+ /**
+ * 增加办理人:加签,转办,委托
+ */
+ @Getter
+ private List addHandlers;
+
+ /**
+ * 减少办理人:减签,委托
+ */
+ @Getter
+ private List reductionHandlers;
+
+ /**
+ * 忽略-办理权限校验(true:忽略,false:不忽略)
+ */
+ @Getter
+ private boolean ignore;
+
+ /**
+ * 忽略-委派处理(true:忽略,false:不忽略)
+ */
+ @Getter
+ private boolean ignoreDepute;
+
+ /**
+ * 忽略-会签票签处理(true:忽略,false:不忽略)
+ */
+ @Getter
+ private boolean ignoreCooperate;
+
+ /**
+ * 执行的下个任务的办理人
+ */
+ @Getter
+ private String[] nextHandler;
+
+ /**
+ * 下个任务处理人配置类型(true-追加,false-覆盖,默认false)
+ */
+ @Getter
+ private boolean nextHandlerAppend;
+
+ public FlowParams() {
+ }
+
+ public FlowParams(String skipType, String message, Map variable) {
+ this.skipType = skipType;
+ this.message = message;
+ this.variable = variable;
+ }
+
+ public FlowParams(String nodeCode, String skipType, String message, Map variable) {
+ this.nodeCode = nodeCode;
+ this.skipType = skipType;
+ this.message = message;
+ this.variable = variable;
+ }
+
+ public FlowParams(String skipType, String message, Map variable
+ , String flowStatus, String hisStatus) {
+ this.skipType = skipType;
+ this.message = message;
+ this.variable = variable;
+ this.flowStatus = flowStatus;
+ this.hisStatus = hisStatus;
+ }
+
+ public FlowParams(String nodeCode, String skipType, String message, Map variable
+ , String flowStatus, String hisStatus) {
+ this.nodeCode = nodeCode;
+ this.skipType = skipType;
+ this.message = message;
+ this.variable = variable;
+ this.flowStatus = flowStatus;
+ this.hisStatus = hisStatus;
+ }
+
+ public static FlowParams build() {
+ return new FlowParams();
+ }
+
+ public FlowParams flowCode(String flowCode) {
+ this.flowCode = flowCode;
+ return this;
+ }
+
+ public FlowParams handler(String handler) {
+ this.handler = handler;
+ return this;
+ }
+
+ public FlowParams nodeCode(String nodeCode) {
+ this.nodeCode = nodeCode;
+ return this;
+ }
+
+ public FlowParams permissionFlag(List permissionFlag) {
+ this.permissionFlag = permissionFlag;
+ return this;
+ }
+
+ public FlowParams message(String message) {
+ this.message = message;
+ return this;
+ }
+
+ public FlowParams variable(Map variable) {
+ this.variable = variable;
+ return this;
+ }
+
+ public FlowParams flowStatus(String flowStatus) {
+ this.flowStatus = flowStatus;
+ return this;
+ }
+
+ public FlowParams hisStatus(String hisStatus) {
+ this.hisStatus = hisStatus;
+ return this;
+ }
+
+ public FlowParams activityStatus(Integer activityStatus) {
+ this.activityStatus = activityStatus;
+ return this;
+ }
+
+ public FlowParams cooperateType(Integer cooperateType) {
+ this.cooperateType = cooperateType;
+ return this;
+ }
+
+ public FlowParams ext(String ext) {
+ this.ext = ext;
+ return this;
+ }
+
+ public FlowParams hisTaskExt(String hisTaskExt) {
+ this.hisTaskExt = hisTaskExt;
+ return this;
+ }
+
+ public FlowParams nextHandler(String... nextHandler) {
+ // 如果外部传递 null , 就忽略 , 维持节点本身审批,防止 null 数据变成 [null]
+ if (nextHandler == null) {
+ return this;
+ }
+ this.nextHandler = Arrays.stream(nextHandler).filter(Objects::nonNull).toArray(String[]::new);
+ return this;
+ }
+
+ public String getVariableStr() {
+ return JsonUtil.objToStr(variable);
+ }
+
+ public String getHandler() {
+ if (StrUtil.isEmpty(handler)) {
+ PermissionHandler permissionHandler = FlowEngine.permissionHandler();
+ if (permissionHandler != null) {
+ try {
+ handler = permissionHandler.getHandler();
+ } catch (Exception ignored) {
+ }
+ }
+ }
+ return handler;
+ }
+
+ public List getPermissionFlag() {
+ if (CollUtil.isEmpty(permissionFlag)) {
+ PermissionHandler permissionHandler = FlowEngine.permissionHandler();
+ if (permissionHandler != null) {
+ try {
+ permissionFlag = permissionHandler.permissions();
+ } catch (Exception ignored) {
+ }
+ }
+ }
+ return permissionFlag;
+ }
+
+ public FlowParams skipType(String skipType) {
+ this.skipType = skipType;
+ return this;
+ }
+
+ public FlowParams addHandlers(List addHandlers) {
+ this.addHandlers = addHandlers;
+ return this;
+ }
+
+ public FlowParams reductionHandlers(List reductionHandlers) {
+ this.reductionHandlers = reductionHandlers;
+ return this;
+ }
+
+ public FlowParams ignore(boolean ignore) {
+ this.ignore = ignore;
+ return this;
+ }
+
+ public FlowParams ignoreDepute(boolean ignoreDepute) {
+ this.ignoreDepute = ignoreDepute;
+ return this;
+ }
+
+ public FlowParams ignoreCooperate(boolean ignoreCooperate) {
+ this.ignoreCooperate = ignoreCooperate;
+ return this;
+ }
+
+ public FlowParams nextHandlerAppend(boolean nextHandlerAppend) {
+ this.nextHandlerAppend = nextHandlerAppend;
+ return this;
+ }
+
+ public FlowParams formData(Map formData) {
+ if (this.variable == null) {
+ this.variable = new HashMap<>();
+ }
+ this.variable.put("formData", formData);
+ return this;
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/NodeJson.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/NodeJson.java
new file mode 100644
index 000000000..292e3d5e3
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/NodeJson.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+import org.dromara.warm.flow.utils.MapUtil;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 流程节点对象Vo
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+@Setter
+@Getter
+@Accessors(chain = true)
+public class NodeJson {
+
+ /**
+ * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
+ */
+ private Integer nodeType;
+ /**
+ * 流程节点编码 每个流程的nodeCode是唯一的,即definitionId+nodeCode唯一,在数据库层面做了控制
+ */
+ private String nodeCode;
+ /**
+ * 流程节点名称
+ */
+ private String nodeName;
+ /**
+ * 流程节点版本
+ */
+ private String version;
+ /**
+ * 权限标识(权限类型:权限标识,可以多个,用@@隔开)
+ */
+ private String permissionFlag;
+ /**
+ * 流程签署比例值
+ */
+ private String nodeRatio;
+ /**
+ * 流程节点坐标
+ */
+ private String coordinate;
+ /**
+ * 任意结点跳转
+ */
+ private String anyNodeSkip;
+ /**
+ * 监听器类型
+ */
+ private String listenerType;
+ /**
+ * 监听器路径
+ */
+ private String listenerPath;
+ /**
+ * 审批表单是否自定义(Y=是 N=否)
+ */
+ private String formCustom;
+
+ /**
+ * 审批表单是否自定义(Y=是 N=否)
+ */
+ private String formPath;
+
+ /**
+ * 节点扩展属性
+ */
+ private String ext;
+
+ /**
+ * 办理状态: 0未办理 1待办理 2已办理
+ */
+ private Integer status;
+
+ /**
+ * 扩展map,保存业务自定义扩展属性
+ */
+ private Map extMap;
+
+ /**
+ * 流程图节点提示内容
+ */
+ private PromptContent promptContent;
+
+ /**
+ * 跳转条件
+ */
+ private List skipList = new ArrayList<>();
+
+ private String createBy;
+
+ private String updateBy;
+
+ public Map getExtMap() {
+ if (MapUtil.isEmpty(extMap)) {
+ extMap = new HashMap<>();
+ }
+ return extMap;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/PathWayData.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/PathWayData.java
new file mode 100644
index 000000000..d485d319a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/PathWayData.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+import org.dromara.warm.flow.entity.FlowNode;
+import org.dromara.warm.flow.entity.FlowSkip;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * 办理过程中途径数据,用于渲染流程图
+ *
+ * @author warm
+ * @since 2025/1/4
+ */
+@Getter
+@Setter
+@Accessors(chain = true)
+public class PathWayData {
+ /**
+ * 流程定义id
+ */
+ private Long defId;
+
+ /**
+ * 流程实例id
+ */
+ private Long insId;
+
+ /**
+ * 跳转类型(PASS审批通过 REJECT退回)
+ */
+ private String skipType;
+
+ /**
+ * 目标结点集合
+ */
+ private List targetNodes = new ArrayList<>();
+
+ /**
+ * 途径结点集合
+ */
+ private List pathWayNodes = new ArrayList<>();
+
+ /**
+ * 途径流程跳转线
+ */
+ private List pathWaySkips = new ArrayList<>();
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/PromptContent.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/PromptContent.java
new file mode 100644
index 000000000..56eb737ff
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/PromptContent.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 提示信息
+ *
+ * @author warm
+ * @since 2025/6/5
+ */
+@Getter
+@Setter
+@Accessors(chain = true)
+@AllArgsConstructor
+@NoArgsConstructor
+public class PromptContent {
+ /**
+ * 弹窗样式
+ */
+ private Map dialogStyle;
+ /**
+ * 提示信息
+ */
+ private List info;
+
+ /**
+ * 提示信息项
+ */
+ @Getter
+ @Setter
+ @Accessors(chain = true)
+ @AllArgsConstructor
+ @NoArgsConstructor
+ public static class InfoItem {
+ /**
+ * 前缀
+ */
+ private String prefix;
+ /**
+ * 前缀样式
+ */
+ private Map prefixStyle;
+ /**
+ * 内容
+ */
+ private String content;
+ /**
+ * 内容样式
+ */
+ private Map contentStyle;
+ /**
+ * 行样式
+ */
+ private Map rowStyle;
+
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/SkipJson.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/SkipJson.java
new file mode 100644
index 000000000..64fdefcf2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/SkipJson.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 节点跳转关联对象Vo
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+@Setter
+@Getter
+@Accessors(chain = true)
+public class SkipJson {
+
+ /**
+ * 当前流程节点的编码
+ */
+ private String nowNodeCode;
+
+ /**
+ * 下一个流程节点的编码
+ */
+ private String nextNodeCode;
+
+ /**
+ * 跳转名称
+ */
+ private String skipName;
+
+ /**
+ * 跳转类型(PASS审批通过 REJECT退回)
+ */
+ private String skipType;
+
+ /**
+ * 跳转条件
+ */
+ private String skipCondition;
+
+ /**
+ * 流程跳转坐标
+ */
+ private String coordinate;
+
+ /**
+ * 办理状态: 0未办理 1待办理 2已办理
+ */
+ private Integer status;
+
+ /**
+ * 扩展map,保存业务自定义扩展属性
+ */
+ private Map extMap;
+
+ /**
+ * 流程图节点提示内容
+ */
+ private List promptContent;
+
+ private String createBy;
+
+ private String updateBy;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/Tree.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/Tree.java
new file mode 100644
index 000000000..384758954
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/dto/Tree.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 页面树列表
+ *
+ * @author ruoyi
+ */
+@Getter
+@Setter
+@Accessors(chain = true)
+@AllArgsConstructor
+@NoArgsConstructor
+public class Tree implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * ID
+ */
+ private String id;
+
+ /**
+ * 名称
+ */
+ private String name;
+
+ /**
+ * 父ID
+ */
+ private String parentId;
+
+ /**
+ * 子
+ */
+ private List children = new ArrayList<>();
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowDefinition.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowDefinition.java
new file mode 100644
index 000000000..468d77c83
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowDefinition.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.entity;
+import org.dromara.warm.flow.entity.RootEntity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.dromara.warm.flow.entity.FlowDefinition;
+import org.dromara.warm.flow.entity.FlowNode;
+import org.dromara.warm.flow.entity.FlowUser;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+/**
+ * 流程定义
+ */
+@Data
+@Accessors(chain = true)
+public class FlowDefinition implements RootEntity {
+
+ /**
+ * 主键
+ */
+ private Long id;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 更新时间
+ */
+ private Date updateTime;
+
+ /**
+ * 创建人
+ */
+ private String createBy;
+
+ /**
+ * 更新人
+ */
+ private String updateBy;
+
+ /**
+ *
+ * 删除标记
+ */
+ @TableLogic(value = "0", delval = "1")
+ private String delFlag;
+
+ /**
+ * 流程编码
+ */
+ private String flowCode;
+
+ /**
+ * 流程名称
+ */
+ private String flowName;
+
+ /**
+ * 设计器模型(CLASSICS经典模型 MIMIC仿钉钉模型)
+ */
+ private String modelValue;
+
+ /**
+ * 流程类别
+ */
+ private String category;
+
+ /**
+ * 流程版本
+ */
+ private String version;
+
+ /**
+ * 是否发布(0未开启 1开启)
+ */
+ private Integer isPublish;
+
+ /**
+ * 审批表单是否自定义(Y是 N否)
+ */
+ private String formCustom;
+
+ /**
+ * 审批表单路径
+ */
+ private String formPath;
+
+ /**
+ * 流程激活状态(0挂起 1激活)
+ */
+ private Integer activityStatus;
+
+ /**
+ * 监听器类型
+ */
+ private String listenerType;
+
+ /**
+ * 监听器路径
+ */
+ private String listenerPath;
+
+ /**
+ * 扩展字段,预留给业务系统使用
+ */
+ private String ext;
+
+ @TableField(exist = false)
+ private List nodeList = new ArrayList<>();
+
+ @TableField(exist = false)
+ private List userList = new ArrayList<>();
+
+
+ /**
+ * 复制当前对象(不含主键、时间字段)
+ */
+ public FlowDefinition copy() {
+ return new FlowDefinition()
+ .setDelFlag(this.getDelFlag())
+ .setFlowCode(this.getFlowCode())
+ .setFlowName(this.getFlowName())
+ .setModelValue(this.getModelValue())
+ .setCategory(this.getCategory())
+ .setVersion(this.getVersion())
+ .setFormCustom(this.getFormCustom())
+ .setFormPath(this.getFormPath())
+ .setListenerType(this.getListenerType())
+ .setListenerPath(this.getListenerPath())
+ .setExt(this.getExt())
+ .setCreateBy(this.getCreateBy())
+ .setUpdateBy(this.getUpdateBy());
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowHisTask.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowHisTask.java
new file mode 100644
index 000000000..589b17845
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowHisTask.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.entity;
+import org.dromara.warm.flow.json.JsonUtil;
+
+import org.dromara.warm.flow.entity.RootEntity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.dromara.warm.flow.entity.FlowHisTask;
+
+import java.util.Date;
+import java.util.List;
+/**
+ * 历史任务记录
+ */
+@Data
+@Accessors(chain = true)
+public class FlowHisTask implements RootEntity {
+
+ /**
+ * 主键
+ */
+ private Long id;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 更新时间
+ */
+ private Date updateTime;
+
+ /**
+ * 删除标记
+ */
+ @TableLogic(value = "0", delval = "1")
+ private String delFlag;
+
+ /**
+ * 对应flow_definition表的id
+ */
+ private Long definitionId;
+
+ /**
+ * 流程名称
+ */
+ @TableField(exist = false)
+ private String flowName;
+
+ /**
+ * 流程实例表id
+ */
+ private Long instanceId;
+
+ /**
+ * 任务表id
+ */
+ private Long taskId;
+
+ /**
+ * 协作方式(1审批 2转办 3委派 4会签 5票签 6加签 7减签)
+ */
+ private Integer cooperateType;
+
+ /**
+ * 业务id
+ */
+ @TableField(exist = false)
+ private String businessId;
+
+ /**
+ * 开始节点编码
+ */
+ private String nodeCode;
+
+ /**
+ * 开始节点名称
+ */
+ private String nodeName;
+
+ /**
+ * 开始节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
+ */
+ private Integer nodeType;
+
+ /**
+ * 目标节点编码
+ */
+ private String targetNodeCode;
+
+ /**
+ * 结束节点名称
+ */
+ private String targetNodeName;
+
+ /**
+ * 审批者
+ */
+ private String approver;
+
+ /**
+ * 协作人(只有转办、会签、票签、委派)
+ */
+ private String collaborator;
+
+ /**
+ * 权限标识 permissionFlag的list形式
+ */
+ @TableField(exist = false)
+ private List permissionList;
+
+ /**
+ * 跳转类型(PASS通过 REJECT退回 NONE无动作)
+ */
+ private String skipType;
+
+ /**
+ * 流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)
+ */
+ private String flowStatus;
+
+ /**
+ * 审批意见
+ */
+ private String message;
+
+ /**
+ * 流程变量
+ */
+ private String variable;
+
+ /**
+ * 业务详情 存业务类的json
+ */
+ private String ext;
+
+ /**
+ * 审批表单是否自定义(Y=是 N=否)
+ */
+ private String formCustom;
+
+ /**
+ * 审批表单路径
+ */
+ private String formPath;
+
+
+ /**
+ * 流程变量转 map
+ */
+ public java.util.Map getVariableMap() {
+ return JsonUtil.strToMap(getVariable());
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowInstance.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowInstance.java
new file mode 100644
index 000000000..5930ba577
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowInstance.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.entity;
+import org.dromara.warm.flow.json.JsonUtil;
+
+import org.dromara.warm.flow.entity.RootEntity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.dromara.warm.flow.entity.FlowInstance;
+
+import java.util.Date;
+/**
+ * 流程实例
+ */
+@Data
+@Accessors(chain = true)
+public class FlowInstance implements RootEntity {
+
+ /**
+ * 主键
+ */
+ private Long id;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 更新时间
+ */
+ private Date updateTime;
+
+ /**
+ * 创建人
+ */
+ private String createBy;
+
+ /**
+ * 更新人
+ */
+ private String updateBy;
+
+ /**
+ * 删除标记
+ */
+ @TableLogic(value = "0", delval = "1")
+ private String delFlag;
+
+ /**
+ * 对应flow_definition表的id
+ */
+ private Long definitionId;
+
+ /**
+ * 流程名称
+ */
+ @TableField(exist = false)
+ private String flowName;
+
+ /**
+ * 业务id
+ */
+ private String businessId;
+
+ /**
+ * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
+ */
+ private Integer nodeType;
+
+ /**
+ * 流程节点编码 每个流程的nodeCode是唯一的,即definitionId+nodeCode唯一,在数据库层面做了控制
+ */
+ private String nodeCode;
+
+ /**
+ * 流程节点名称
+ */
+ private String nodeName;
+
+ /**
+ * 流程变量
+ */
+ private String variable;
+
+ /**
+ * 流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)
+ */
+ private String flowStatus;
+
+ /**
+ * 流程激活状态(0挂起 1激活)
+ */
+ private Integer activityStatus;
+
+ /**
+ * 审批表单是否自定义(Y=是 N=否)
+ */
+ @TableField(exist = false)
+ private String formCustom;
+
+ /**
+ * 审批表单是否自定义(Y=是 N=否)
+ */
+ @TableField(exist = false)
+ private String formPath;
+
+ /**
+ * 流程定义json
+ */
+ private String defJson;
+
+ /**
+ * 扩展字段,预留给业务系统使用
+ */
+ private String ext;
+
+
+ /**
+ * 流程变量转 map
+ */
+ public java.util.Map getVariableMap() {
+ return JsonUtil.strToMap(getVariable());
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowNode.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowNode.java
new file mode 100644
index 000000000..1922b9706
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowNode.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.entity;
+import org.dromara.warm.flow.entity.RootEntity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.dromara.warm.flow.entity.FlowNode;
+import org.dromara.warm.flow.entity.FlowSkip;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+/**
+ * 流程节点
+ */
+@Data
+@Accessors(chain = true)
+public class FlowNode implements RootEntity {
+
+ /**
+ * 节点跳转列表(非表字段)
+ */
+ @TableField(exist = false)
+ List skipList;
+
+ /**
+ * 主键
+ */
+ private Long id;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 更新时间
+ */
+ private Date updateTime;
+
+ /**
+ * 创建人
+ */
+ private String createBy;
+
+ /**
+ * 更新人
+ */
+ private String updateBy;
+
+ /**
+ * 删除标记
+ */
+ @TableLogic(value = "0", delval = "1")
+ private String delFlag;
+
+ /**
+ * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
+ */
+ private Integer nodeType;
+ /**
+ * 流程id
+ */
+ private Long definitionId;
+ /**
+ * 流程节点编码 每个流程的nodeCode是唯一的,即definitionId+nodeCode唯一,在数据库层面做了控制
+ */
+ private String nodeCode;
+ /**
+ * 流程节点名称
+ */
+ private String nodeName;
+ /**
+ * 权限标识(权限类型:权限标识,可以多个,用@@隔开)
+ */
+ private String permissionFlag;
+ /**
+ * 流程签署比例值
+ */
+ private String nodeRatio;
+ /**
+ * 流程节点坐标
+ */
+ private String coordinate;
+ /**
+ * 版本
+ *
+ * @deprecated 下个版本废弃
+ */
+ @Deprecated
+ private String version;
+ /**
+ * 任意结点跳转
+ */
+ private String anyNodeSkip;
+ /**
+ * 监听器类型
+ */
+ private String listenerType;
+ /**
+ * 监听器路径
+ */
+ private String listenerPath;
+ /**
+ * 审批表单是否自定义(Y=是 N=否)
+ */
+ private String formCustom;
+
+ /**
+ * 审批表单路径
+ */
+ private String formPath;
+
+ /**
+ * 节点扩展属性
+ */
+ private String ext;
+
+
+ /**
+ * 复制当前对象(不含主键、时间字段)
+ */
+ public FlowNode copy() {
+ return new FlowNode()
+ .setDelFlag(this.getDelFlag())
+ .setNodeType(this.getNodeType())
+ .setDefinitionId(this.getDefinitionId())
+ .setNodeCode(this.getNodeCode())
+ .setNodeName(this.getNodeName())
+ .setNodeRatio(this.getNodeRatio())
+ .setPermissionFlag(this.getPermissionFlag())
+ .setCoordinate(this.getCoordinate())
+ .setVersion(this.getVersion())
+ .setAnyNodeSkip(this.getAnyNodeSkip())
+ .setListenerType(this.getListenerType())
+ .setListenerPath(this.getListenerPath())
+ .setFormCustom(this.getFormCustom())
+ .setFormPath(this.getFormPath())
+ .setExt(this.getExt());
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowSkip.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowSkip.java
new file mode 100644
index 000000000..de7501af7
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowSkip.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.entity;
+import org.dromara.warm.flow.entity.RootEntity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.dromara.warm.flow.entity.FlowSkip;
+
+import java.util.Date;
+/**
+ * 节点跳转关联
+ */
+@Data
+@Accessors(chain = true)
+public class FlowSkip implements RootEntity {
+
+ /**
+ * 主键
+ */
+ private Long id;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 更新时间
+ */
+ private Date updateTime;
+
+ /**
+ * 创建人
+ */
+ private String createBy;
+
+ /**
+ * 更新人
+ */
+ private String updateBy;
+
+ /**
+ * 删除标记
+ */
+ @TableLogic(value = "0", delval = "1")
+ private String delFlag;
+
+ /**
+ * 流程id
+ */
+ private Long definitionId;
+
+ /**
+ * 节点id
+ */
+ @TableField(exist = false)
+ private Long nodeId;
+
+ /**
+ * 当前流程节点的编码
+ */
+ private String nowNodeCode;
+
+ /**
+ * 当前节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
+ */
+ private Integer nowNodeType;
+
+ /**
+ * 下一个流程节点的编码
+ */
+ private String nextNodeCode;
+
+ /**
+ * 下一个节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
+ */
+ private Integer nextNodeType;
+
+ /**
+ * 跳转名称
+ */
+ private String skipName;
+
+ /**
+ * 跳转类型(PASS审批通过 REJECT退回)
+ */
+ private String skipType;
+
+ /**
+ * 跳转条件
+ */
+ private String skipCondition;
+
+ /**
+ * 流程跳转坐标
+ */
+ private String coordinate;
+
+
+ /**
+ * 复制当前对象(不含主键、时间字段)
+ */
+ public FlowSkip copy() {
+ return new FlowSkip()
+ .setDelFlag(getDelFlag())
+ .setDefinitionId(getDefinitionId())
+ .setNowNodeCode(getNowNodeCode())
+ .setNowNodeType(getNowNodeType())
+ .setNextNodeCode(getNextNodeCode())
+ .setNextNodeType(getNextNodeType())
+ .setSkipName(getSkipName())
+ .setSkipType(getSkipType())
+ .setSkipCondition(getSkipCondition())
+ .setCoordinate(getCoordinate());
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowTask.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowTask.java
new file mode 100644
index 000000000..42e684b5e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowTask.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.entity;
+import org.dromara.warm.flow.entity.RootEntity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.dromara.warm.flow.entity.FlowTask;
+import org.dromara.warm.flow.entity.FlowUser;
+
+import java.util.Date;
+import java.util.List;
+/**
+ * 待办任务
+ */
+@Data
+@Accessors(chain = true)
+public class FlowTask implements RootEntity {
+
+ /**
+ * 主键
+ */
+ private Long id;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 更新时间
+ */
+ private Date updateTime;
+
+ /**
+ * 创建人
+ */
+ private String createBy;
+
+ /**
+ * 更新人
+ */
+ private String updateBy;
+
+ /**
+ * 删除标记
+ */
+ @TableLogic(value = "0", delval = "1")
+ private String delFlag;
+
+ /**
+ * 对应flow_definition表的id
+ */
+ private Long definitionId;
+
+ /**
+ * 流程实例表id
+ */
+ private Long instanceId;
+
+ /**
+ * 流程名称
+ */
+ @TableField(exist = false)
+ private String flowName;
+
+ /**
+ * 业务id
+ */
+ @TableField(exist = false)
+ private String businessId;
+
+ /**
+ * 节点编码
+ */
+ private String nodeCode;
+
+ /**
+ * 节点名称
+ */
+ private String nodeName;
+
+ /**
+ * 节点类型(0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关)
+ */
+ private Integer nodeType;
+
+ /**
+ * 流程状态(0待提交 1审批中 2审批通过 4终止 5作废 6撤销 8已完成 9已退回 10失效 11拿回)
+ */
+ private String flowStatus;
+
+ /**
+ * 权限标识 permissionFlag的list形式
+ */
+ @TableField(exist = false)
+ private List permissionList;
+
+ /**
+ * 流程用户列表
+ */
+ @TableField(exist = false)
+ private List userList;
+
+ /**
+ * 审批表单是否自定义(Y=是 N=否)
+ */
+ private String formCustom;
+
+ /**
+ * 审批表单
+ */
+ private String formPath;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowUser.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowUser.java
new file mode 100644
index 000000000..d3f3a5668
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/FlowUser.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.entity;
+import org.dromara.warm.flow.entity.RootEntity;
+
+import com.baomidou.mybatisplus.annotation.*;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import org.dromara.warm.flow.entity.FlowUser;
+
+import java.util.Date;
+/**
+ * 流程用户
+ */
+@Data
+@Accessors(chain = true)
+public class FlowUser implements RootEntity {
+
+ /**
+ * 主键
+ */
+ private Long id;
+
+ /**
+ * 创建时间
+ */
+ private Date createTime;
+
+ /**
+ * 更新时间
+ */
+ private Date updateTime;
+
+ /**
+ * 创建人
+ */
+ private String createBy;
+
+ /**
+ * 更新人
+ */
+ private String updateBy;
+
+ /**
+ * 删除标记
+ */
+ @TableLogic(value = "0", delval = "1")
+ private String delFlag;
+
+ /**
+ * 人员类型(1待办任务的审批人权限 2待办任务的转办人权限 3待办任务的委托人权限)
+ */
+ private String type;
+
+ /**
+ * 权限人
+ */
+ private String processedBy;
+
+ /**
+ * 任务表id
+ */
+ private Long associated;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/RootEntity.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/RootEntity.java
new file mode 100644
index 000000000..8e6b58a06
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/entity/RootEntity.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.entity;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * 流程基础entity
+ *
+ * @author warm
+ * @since 2023/5/17 17:23
+ */
+public interface RootEntity extends Serializable {
+
+ Long getId();
+
+ RootEntity setId(Long id);
+
+ Date getCreateTime();
+
+ RootEntity setCreateTime(Date createTime);
+
+ Date getUpdateTime();
+
+ RootEntity setUpdateTime(Date updateTime);
+
+ default String getCreateBy() {
+ return null;
+ }
+
+ default RootEntity setCreateBy(String createBy) {
+ return this;
+ }
+
+ default String getUpdateBy() {
+ return null;
+ }
+
+ default RootEntity setUpdateBy(String updateBy) {
+ return this;
+ }
+
+ String getDelFlag();
+
+ RootEntity setDelFlag(String delFlag);
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ActivityStatus.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ActivityStatus.java
new file mode 100644
index 000000000..eac0392ff
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ActivityStatus.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import cn.hutool.core.util.ObjectUtil;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+
+/**
+ * 激活状态
+ *
+ * @author warm
+ * @since 2025/6/25
+ */
+@Getter
+@AllArgsConstructor
+public enum ActivityStatus {
+
+ /**
+ * 激活状态
+ */
+ SUSPENDED(0, "挂起"),
+
+ ACTIVITY(1, "激活");
+
+ private final Integer key;
+ private final String value;
+
+ /**
+ * 判断流程是否激活
+ */
+ public static Boolean isActivity(Integer key) {
+ return ObjectUtil.isNotNull(key) && (ActivityStatus.ACTIVITY.getKey().equals(key));
+ }
+
+ /**
+ * 判断流程是否挂起
+ */
+ public static Boolean isSuspended(Integer key) {
+ return ObjectUtil.isNotNull(key) && (ActivityStatus.SUSPENDED.getKey().equals(key));
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ChartStatus.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ChartStatus.java
new file mode 100644
index 000000000..afe6c9c85
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ChartStatus.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.ObjectUtil;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+
+
+
+import java.awt.*;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 流程图状态
+ *
+ * @author warm
+ * @since 2023/3/31 12:16
+ */
+@Getter
+@AllArgsConstructor
+public enum ChartStatus {
+ /**
+ * 流程图状态
+ */
+ NOT_DONE(0, "未办理", new Color(107,114,128)),
+
+ TO_DO(1, "待办理", new Color(245,158,11)),
+
+ DONE(2, "已办理", new Color(56,161,105));
+
+ private final Integer key;
+ private final String value;
+ private final Color color;
+
+ private static final Map CUSTOM_COLOR = new HashMap<>();
+ private static final Map CUSTOM_COLOR_CLASSICS = new HashMap<>();
+ private static final Map CUSTOM_COLOR_MIMIC = new HashMap<>();
+
+ public static void initCustomColor(List chartStatusColor, List chartStatusColorClassics,
+ List chartStatusColorMimic) {
+ if (CollUtil.isNotEmpty(chartStatusColor) && chartStatusColor.size() == 3) {
+ for (int i = 0; i < chartStatusColor.size(); i++) {
+ String statusColor = chartStatusColor.get(i);
+ if (StrUtil.isNotEmpty(statusColor)) {
+ String[] colorArr = statusColor.split(",");
+ if (colorArr.length == 3) {
+ ChartStatus.CUSTOM_COLOR.put(i, new Color(Integer.parseInt(colorArr[0]), Integer.parseInt(colorArr[1]), Integer.parseInt(colorArr[2])));
+ }
+ }
+ }
+ }
+ if (CollUtil.isNotEmpty(chartStatusColorClassics) && chartStatusColorClassics.size() == 3) {
+ for (int i = 0; i < chartStatusColorClassics.size(); i++) {
+ String statusColor = chartStatusColorClassics.get(i);
+ if (StrUtil.isNotEmpty(statusColor)) {
+ String[] colorArr = statusColor.split(",");
+ if (colorArr.length == 3) {
+ ChartStatus.CUSTOM_COLOR_CLASSICS.put(i, new Color(Integer.parseInt(colorArr[0]), Integer.parseInt(colorArr[1]), Integer.parseInt(colorArr[2])));
+ }
+ }
+ }
+ }
+ if (CollUtil.isNotEmpty(chartStatusColorMimic) && chartStatusColorMimic.size() == 3) {
+ for (int i = 0; i < chartStatusColorMimic.size(); i++) {
+ String statusColor = chartStatusColorMimic.get(i);
+ if (StrUtil.isNotEmpty(statusColor)) {
+ String[] colorArr = statusColor.split(",");
+ if (colorArr.length == 3) {
+ ChartStatus.CUSTOM_COLOR_MIMIC.put(i, new Color(Integer.parseInt(colorArr[0]), Integer.parseInt(colorArr[1]), Integer.parseInt(colorArr[2])));
+ }
+ }
+ }
+ }
+ }
+
+ public static Color getNotDone(String modelValue) {
+ return getColorByKey(ChartStatus.NOT_DONE, modelValue);
+ }
+
+ public static Color getToDo(String modelValue) {
+ return getColorByKey(ChartStatus.TO_DO, modelValue);
+ }
+
+ public static Color getDone(String modelValue) {
+ return getColorByKey(ChartStatus.DONE, modelValue);
+ }
+
+ public static Color getColorByKey(ChartStatus chartStatus, String modelValue) {
+ Color color = null;
+ if (ModelEnum.CLASSICS.name().equals(modelValue)) {
+ color = ChartStatus.CUSTOM_COLOR_CLASSICS.get(chartStatus.getKey());
+ } else if (ModelEnum.MIMIC.name().equals(modelValue)) {
+ color = ChartStatus.CUSTOM_COLOR_MIMIC.get(chartStatus.getKey());
+ }
+ if (ObjectUtil.isNull(color)) {
+ color = ChartStatus.CUSTOM_COLOR.get(chartStatus.getKey());
+ }
+ return ObjectUtil.defaultIfNull(color, chartStatus.getColor());
+ }
+
+ public static Color getColorByKey(Integer key) {
+ for (ChartStatus item : ChartStatus.values()) {
+ if (item.getKey().equals(key)) {
+ Color color = ChartStatus.CUSTOM_COLOR.get(key);
+ return ObjectUtil.defaultIfNull(color, item.getColor());
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 判断是否未办理
+ *
+ * @param key 状态
+ */
+ public static Boolean isNotDone(Integer key) {
+ return ObjectUtil.isNotNull(key) && (ChartStatus.NOT_DONE.getKey().equals(key));
+ }
+
+ /**
+ * 判断是否待办理
+ *
+ * @param key 状态
+ */
+ public static Boolean isToDo(Integer key) {
+ return ObjectUtil.isNotNull(key) && (ChartStatus.TO_DO.getKey().equals(key));
+ }
+
+ /**
+ * 判断是否已办理
+ *
+ * @param key 状态
+ */
+ public static Boolean isDone(Integer key) {
+ return ObjectUtil.isNotNull(key) && (ChartStatus.DONE.getKey().equals(key));
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/Condition.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/Condition.java
new file mode 100644
index 000000000..04f2ae3e5
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/Condition.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import org.dromara.warm.flow.constant.ExceptionCons;
+import org.dromara.warm.flow.constant.FlowCons;
+import org.dromara.warm.flow.expression.SpelHelper;
+import org.dromara.warm.flow.strategy.ConditionStrategy;
+import org.dromara.warm.flow.utils.AssertUtil;
+import org.dromara.warm.flow.utils.MathUtil;
+
+import java.util.Map;
+
+/**
+ * 内置条件表达式策略,合并原 eq/ne/gt/ge/lt/le/like/notLike/spel/default 十个策略类及 ConditionType 枚举
+ * 表达式格式:eq@@flag|4(前缀 eq@@ 由 {@link ConditionStrategy} 截取,进入 eval 的为 flag|4)
+ *
+ * @author warm
+ */
+public enum Condition implements ConditionStrategy {
+
+ /**
+ * 等于
+ */
+ EQ("eq") {
+ @Override
+ public Boolean afterEval(String value, String variableValue) {
+ if (MathUtil.isNumeric(value)) {
+ return MathUtil.determineSize(variableValue, value) == 0;
+ }
+ return variableValue.equals(value);
+ }
+ },
+
+ /**
+ * 不等于
+ */
+ NE("ne") {
+ @Override
+ public Boolean afterEval(String value, String variableValue) {
+ if (MathUtil.isNumeric(value)) {
+ return MathUtil.determineSize(variableValue, value) != 0;
+ }
+ return !variableValue.equals(value);
+ }
+ },
+
+ /**
+ * 大于(仅数值比较)
+ */
+ GT("gt") {
+ @Override
+ public Boolean afterEval(String value, String variableValue) {
+ return MathUtil.isNumeric(value) && MathUtil.determineSize(variableValue, value) > 0;
+ }
+ },
+
+ /**
+ * 大于等于(仅数值比较)
+ */
+ GE("ge") {
+ @Override
+ public Boolean afterEval(String value, String variableValue) {
+ return MathUtil.isNumeric(value) && MathUtil.determineSize(variableValue, value) >= 0;
+ }
+ },
+
+ /**
+ * 小于(仅数值比较)
+ */
+ LT("lt") {
+ @Override
+ public Boolean afterEval(String value, String variableValue) {
+ return MathUtil.isNumeric(value) && MathUtil.determineSize(variableValue, value) < 0;
+ }
+ },
+
+ /**
+ * 小于等于(仅数值比较)
+ */
+ LE("le") {
+ @Override
+ public Boolean afterEval(String value, String variableValue) {
+ return MathUtil.isNumeric(value) && MathUtil.determineSize(variableValue, value) <= 0;
+ }
+ },
+
+ /**
+ * 包含
+ */
+ LIKE("like") {
+ @Override
+ public Boolean afterEval(String value, String variableValue) {
+ return variableValue.contains(value);
+ }
+ },
+
+ /**
+ * 不包含
+ */
+ NOT_LIKE("notLike") {
+ @Override
+ public Boolean afterEval(String value, String variableValue) {
+ return !variableValue.contains(value);
+ }
+ },
+
+ /**
+ * spel 条件表达式 spel@@#{@user.eval()}
+ */
+ SPEL(FlowCons.SPEL) {
+ @Override
+ public Boolean eval(String expression, Map variable) {
+ return SpelHelper.evalBool(expression, variable);
+ }
+
+ @Override
+ public Boolean afterEval(String value, String variableValue) {
+ throw new UnsupportedOperationException("spel 条件表达式不走值比较");
+ }
+ },
+
+ /**
+ * 默认条件表达式 default@@${flag == 5 && flag > 4},基于 spel 简化使用
+ */
+ DEFAULT(FlowCons.DEFAULT) {
+ @Override
+ public Boolean eval(String expression, Map variable) {
+ return SPEL.eval(SpelHelper.replace(expression, variable), variable);
+ }
+
+ @Override
+ public Boolean afterEval(String value, String variableValue) {
+ throw new UnsupportedOperationException("默认条件表达式不走值比较");
+ }
+ };
+
+ private final String key;
+
+ Condition(String key) {
+ this.key = key;
+ }
+
+ @Override
+ public String getType() {
+ return key;
+ }
+
+ @Override
+ public Boolean eval(String expression, Map variable) {
+ AssertUtil.isEmpty(variable, ExceptionCons.NULL_CONDITION_VALUE);
+ String[] split = expression.split(FlowCons.SPLIT_VERTICAL);
+ String name = split[0].trim();
+ Object o = variable.get(name);
+ AssertUtil.isNull(o, ExceptionCons.NULL_CONDITION_VALUE);
+ return afterEval(split[1].trim(), String.valueOf(o));
+ }
+
+ /**
+ * 比较表达式值与流程变量值
+ *
+ * @param value 表达式最后一个参数,比如:eq@@flag|5 的 [5]
+ * @param variableValue 流程变量值
+ * @return 比较结果
+ */
+ public abstract Boolean afterEval(String value, String variableValue);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/CooperateType.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/CooperateType.java
new file mode 100644
index 000000000..b806f9d95
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/CooperateType.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import cn.hutool.core.util.StrUtil;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.dromara.warm.flow.constant.FlowCons;
+import org.dromara.warm.flow.utils.MathUtil;
+
+
+/**
+ * 协作类型
+ * APPROVAL-无:无其他协作方式
+ * TRANSFER-转办:任务转给其他人办理
+ * DEPUTE-委派:求助其他人审批,然后参照他的意见决定是否审批通过
+ * COUNTERSIGN-会签:和其他人一起审批通过,才算通过
+ * VOTE-票签:和部分人一起审批,达到一定通过率,才算通过
+ * ADD_SIGNATURE-加签:办理中途,希望其他人一起参与办理
+ * REDUCTION_SIGNATURE-减签:办理中途,希望某些人不参与办理
+ *
+ * @author xiarg
+ * @since 2024/5/10 16:04
+ */
+@Getter
+@AllArgsConstructor
+public enum CooperateType {
+
+ /**
+ * 协作类型
+ */
+ APPROVAL(1, "无"),
+
+ TRANSFER(2, "转办"),
+
+ DEPUTE(3, "委派"),
+
+ COUNTERSIGN(4, "会签"),
+
+ VOTE(5, "票签"),
+
+ ADD_SIGNATURE(6, "加签"),
+
+ REDUCTION_SIGNATURE(7, "减签");
+
+ private final Integer key;
+ private final String value;
+
+
+ /**
+ * 票签中的固定通过人数策略前缀
+ */
+ public static final String PASS_COUNT = "passCount";
+ /**
+ * 票签中的固定驳回人数策略前缀
+ */
+ public static final String REJECT_COUNT = "rejectCount";
+
+ /**
+ * 顺签
+ */
+ public static final String SEQUENCE = "sequence";
+
+ public static Integer getKeyByValue(String value) {
+ for (CooperateType item : CooperateType.values()) {
+ if (item.getValue().equals(value)) {
+ return item.getKey();
+ }
+ }
+ return null;
+ }
+
+ public static String getValueByKey(Integer key) {
+ for (CooperateType item : CooperateType.values()) {
+ if (item.getKey().equals(key)) {
+ return item.getValue();
+ }
+ }
+ return null;
+ }
+
+ public static CooperateType getByKey(Integer key) {
+ for (CooperateType item : CooperateType.values()) {
+ if (item.getKey().equals(key)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ * 判断是否为或签
+ * @param ratio 比例
+ * @return true:是;false:不是
+ */
+ public static boolean isOrSign(String ratio) {
+ return MathUtil.isZero(ratio);
+ }
+
+ /**
+ * 判断是否是会签
+ *
+ * @param ratio 比例
+ * @return true:是;false:不是
+ */
+ public static boolean isCountersign(String ratio) {
+ return MathUtil.isHundred(ratio);
+ }
+
+ /**
+ * 判断是否是票签中通过率策略
+ *
+ * @param ratio 比例
+ * @return true:是;false:不是
+ */
+ public static boolean isVoteSignPassRatio(String ratio) {
+ return MathUtil.isBetweenZeroAndHundred(ratio);
+ }
+
+ /**
+ * 判断是否是票签中的固定通过人数策略
+ *
+ * @param passCount 固定通过人数
+ * @return true:是;false:不是
+ */
+ public static boolean isVoteSignPassCount(String passCount) {
+ return StrUtil.isNotEmpty(passCount) && passCount.startsWith(PASS_COUNT);
+ }
+
+ /**
+ * 判断是否是票签中的固定驳回人数策略
+ *
+ * @param rejectCount 固定驳回人数
+ * @return true:是;false:不是
+ */
+ public static boolean isVoteSignRejectCount(String rejectCount) {
+ return StrUtil.isNotEmpty(rejectCount) && rejectCount.startsWith(REJECT_COUNT);
+ }
+
+ /**
+ * 判断是否是票签中的默认表达式策略
+ *
+ * @param expression 默认表达式
+ * @return true:是;false:不是
+ */
+ public static boolean isVoteSignDefault(String expression) {
+ return StrUtil.isNotEmpty(expression) && expression.startsWith(FlowCons.DEFAULT);
+ }
+
+ /**
+ * 判断是否是票签中的spel表达式策略
+ *
+ * @param expression spel表达式
+ * @return true:是;false:不是
+ */
+ public static boolean isVoteSignRejectSpel(String expression) {
+ return StrUtil.isNotEmpty(expression) && expression.startsWith(FlowCons.SPEL);
+ }
+
+ /**
+ * 判断是否是顺签
+ *
+ * @param expression 表达式
+ * @return true:是;false:不是
+ */
+ public static boolean isSequenceSign(String expression) {
+ return StrUtil.isNotEmpty(expression) && expression.endsWith(FlowCons.SPLIT_AT + SEQUENCE);
+ }
+
+ /**
+ * 判断是否是顺签
+ *
+ * @param expression 表达式
+ * @return true:是;false:不是
+ */
+ public static String removeSequence(String expression) {
+ if (isSequenceSign(expression)) {
+ return expression.substring(0, expression.lastIndexOf(FlowCons.SPLIT_AT + SEQUENCE));
+ }
+ return expression;
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FlowStatus.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FlowStatus.java
new file mode 100644
index 000000000..e2be298cf
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FlowStatus.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import cn.hutool.core.util.ObjectUtil;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+
+/**
+ * 流程状态
+ *
+ * @author warm
+ * @since 2023/3/31 12:16
+ */
+@Getter
+@AllArgsConstructor
+public enum FlowStatus {
+ /**
+ * 流程状态
+ */
+ TOBESUBMIT("0", "待提交"),
+
+ APPROVAL("1", "审批中"),
+
+ PASS("2", "审批通过"),
+
+ AUTO_PASS("3", "自动完成"),
+
+ TERMINATE("4", "终止"),
+
+ NULLIFY("5", "作废"),
+
+ CANCEL("6", "撤销"),
+
+ RETRIEVE("7", "取回"),
+
+ FINISHED("8", "已完成"),
+
+ REJECT("9", "已退回"),
+
+ INVALID("10", "失效"),
+
+ TASK_BACK("11", "拿回"),
+
+ RE_START("12", "重启"),
+
+ PENDING("13", "暂存");
+
+ private final String key;
+ private final String value;
+
+ public static String getKeyByValue(String value) {
+ for (FlowStatus item : FlowStatus.values()) {
+ if (item.getValue().equals(value)) {
+ return item.getKey();
+ }
+ }
+ return null;
+ }
+
+ public static String getValueByKey(String key) {
+ for (FlowStatus item : FlowStatus.values()) {
+ if (item.getKey().equals(key)) {
+ return item.getValue();
+ }
+ }
+ return null;
+ }
+
+ public static FlowStatus getByKey(String key) {
+ for (FlowStatus item : FlowStatus.values()) {
+ if (item.getKey().equals(key)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 判断是否结束节点
+ *
+ * @param key 枚举key
+ * @return boolean
+ */
+ public static Boolean isFinished(String key) {
+ return ObjectUtil.isNotNull(key) && (FlowStatus.FINISHED.getKey().equals(key));
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FormCustomEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FormCustomEnum.java
new file mode 100644
index 000000000..ee6e98071
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FormCustomEnum.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 表单类型
+ *
+ * @author warm
+ * @since 2025/6/25
+ */
+@Getter
+@AllArgsConstructor
+public enum FormCustomEnum {
+
+ /**
+ * 表单路径
+ */
+ N,
+
+ /**
+ * 表单路径
+ */
+ Y,
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FrameworkType.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FrameworkType.java
new file mode 100644
index 000000000..dcb066778
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/FrameworkType.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 框架类型: springboot、solon
+ *
+ * @author warm
+ * @since 2026/3/24
+ */
+@Getter
+@AllArgsConstructor
+public enum FrameworkType {
+
+ SPRING_BOOT,
+
+ SOLON;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/Handler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/Handler.java
new file mode 100644
index 000000000..42cec8987
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/Handler.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import org.dromara.warm.flow.expression.SpelHelper;
+import org.dromara.warm.flow.strategy.HandlerStrategy;
+
+import java.util.Map;
+
+/**
+ * 内置办理人表达式策略,合并原 DefaultHandlerStrategy/HandlerStrategySpel 两个策略类
+ * 表达式格式:${flag} 或 #{@user.evalVar()}
+ *
+ * @author warm,battcn
+ */
+public enum Handler implements HandlerStrategy {
+
+ /**
+ * 默认办理人表达式 ${flag}
+ */
+ DEFAULT("$") {
+ @Override
+ public Object preEval(String expression, Map variable) {
+ String result = expression.replace("${", "").replace("}", "");
+ return variable.get(result);
+ }
+ },
+
+ /**
+ * spel 办理人表达式 #{@user.evalVar()}
+ */
+ SPEL("#") {
+ @Override
+ public Object preEval(String expression, Map variable) {
+ return SpelHelper.parseExpression(expression, variable);
+ }
+ };
+
+ private final String key;
+
+ Handler(String key) {
+ this.key = key;
+ }
+
+ @Override
+ public String getType() {
+ return key;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ModelEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ModelEnum.java
new file mode 100644
index 000000000..7172c8b41
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/ModelEnum.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 设计器模型(CLASSICS经典模型 MIMIC仿钉钉模型)
+ *
+ * @author warm
+ * @since 2025/6/25
+ */
+@Getter
+@AllArgsConstructor
+public enum ModelEnum {
+
+ /**
+ * 经典模型
+ */
+ CLASSICS,
+
+ /**
+ * 仿钉钉模型
+ */
+ MIMIC
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/NodeType.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/NodeType.java
new file mode 100644
index 000000000..5ec1fc108
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/NodeType.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import cn.hutool.core.util.ObjectUtil;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+
+/**
+ * 节点类型
+ *
+ * @author warm
+ * @since 2023/3/31 12:16
+ */
+@AllArgsConstructor
+@Getter
+public enum NodeType {
+ /**
+ * 开始节点
+ */
+ START(0, "start"),
+ /**
+ * 中间节点
+ */
+ BETWEEN(1, "between"),
+ /**
+ * 结束节点
+ */
+ END(2, "end"),
+
+ /**
+ * 互斥网关
+ */
+ SERIAL(3, "serial"),
+
+ /**
+ * 并行网关
+ */
+ PARALLEL(4, "parallel"),
+
+ /**
+ * 包容网关
+ */
+ INCLUSIVE(5, "inclusive");
+
+ private final Integer key;
+ private final String value;
+
+ public static Integer getKeyByValue(String value) {
+ for (NodeType item : NodeType.values()) {
+ if (item.getValue().equals(value)) {
+ return item.getKey();
+ }
+ }
+ return null;
+ }
+
+ public static String getValueByKey(Integer key) {
+ for (NodeType item : NodeType.values()) {
+ if (item.getKey().equals(key)) {
+ return item.getValue();
+ }
+ }
+ return null;
+ }
+
+ public static NodeType getByKey(Integer key) {
+ for (NodeType item : NodeType.values()) {
+ if (item.getKey().equals(key)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 判断是否开始节点
+ *
+ * @param key 枚举key
+ * @return boolean
+ */
+ public static Boolean isStart(Integer key) {
+ return ObjectUtil.isNotNull(key) && (NodeType.START.getKey().equals(key));
+ }
+
+ /**
+ * 判断是否中间节点
+ *
+ * @param key 枚举key
+ * @return boolean
+ */
+ public static Boolean isBetween(Integer key) {
+ return ObjectUtil.isNotNull(key) && (NodeType.BETWEEN.getKey().equals(key));
+ }
+
+ /**
+ * 判断是否结束节点
+ *
+ * @param key 枚举key
+ * @return boolean
+ */
+ public static Boolean isEnd(Integer key) {
+ return ObjectUtil.isNotNull(key) && (NodeType.END.getKey().equals(key));
+ }
+
+ /**
+ * 判断是否网关节点
+ *
+ * @param key 枚举key
+ * @return boolean
+ */
+ public static Boolean isGateWay(Integer key) {
+ return ObjectUtil.isNotNull(key) && (NodeType.SERIAL.getKey().equals(key)
+ || NodeType.PARALLEL.getKey().equals(key)|| NodeType.INCLUSIVE.getKey().equals(key));
+ }
+
+ /**
+ * 判断是否互斥网关节点
+ *
+ * @param key 枚举key
+ * @return boolean
+ */
+ public static Boolean isGateWaySerial(Integer key) {
+ return ObjectUtil.isNotNull(key) && NodeType.SERIAL.getKey().equals(key);
+ }
+
+ /**
+ * 判断是否并行网关节点
+ *
+ * @param key 枚举key
+ * @return boolean
+ */
+ public static Boolean isGateWayParallel(Integer key) {
+ return ObjectUtil.isNotNull(key) && NodeType.PARALLEL.getKey().equals(key);
+ }
+
+ /**
+ * 判断是否包容网关节点
+ *
+ * @param key 枚举key
+ * @return boolean
+ */
+ public static Boolean isGateWayInclusive(Integer key) {
+ return ObjectUtil.isNotNull(key) && NodeType.INCLUSIVE.getKey().equals(key);
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/PublishStatus.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/PublishStatus.java
new file mode 100644
index 000000000..01e3ee618
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/PublishStatus.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 节点类型
+ *
+ * @author warm
+ * @since 2023/3/31 12:16
+ */
+@Getter
+@AllArgsConstructor
+public enum PublishStatus {
+
+ /**
+ * 9=已失效;0=未发布;1=已发布
+ */
+ EXPIRED(9, "已失效"),
+
+ UNPUBLISHED(0, "未发布"),
+
+ PUBLISHED(1, "已发布");
+
+ private final Integer key;
+ private final String value;
+
+ public static Integer getKeyByValue(String value) {
+ for (PublishStatus item : PublishStatus.values()) {
+ if (item.getValue().equals(value)) {
+ return item.getKey();
+ }
+ }
+ return null;
+ }
+
+ public static String getValueByKey(Integer key) {
+ for (PublishStatus item : PublishStatus.values()) {
+ if (item.getKey().equals(key)) {
+ return item.getValue();
+ }
+ }
+ return null;
+ }
+
+ public static PublishStatus getByKey(Integer key) {
+ for (PublishStatus item : PublishStatus.values()) {
+ if (item.getKey().equals(key)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/SkipType.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/SkipType.java
new file mode 100644
index 000000000..44d382b3a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/SkipType.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import cn.hutool.core.util.StrUtil;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+
+/**
+ * 审批动作
+ *
+ * @author warm
+ * @since 2023/3/31 12:16
+ */
+@Getter
+@AllArgsConstructor
+public enum SkipType {
+ /**
+ * 审批动作
+ */
+ PASS("PASS", "审批通过"),
+
+ REJECT("REJECT", "退回"),
+
+ NONE("NONE", "无动作");
+
+ private final String key;
+ private final String value;
+
+ public static String getKeyByValue(String value) {
+ for (SkipType item : SkipType.values()) {
+ if (item.getValue().equals(value)) {
+ return item.getKey();
+ }
+ }
+ return null;
+ }
+
+ public static String getValueByKey(String key) {
+ for (SkipType item : SkipType.values()) {
+ if (item.getKey().equals(key)) {
+ return item.getValue();
+ }
+ }
+ return null;
+ }
+
+ public static SkipType getByKey(String key) {
+ for (SkipType item : SkipType.values()) {
+ if (item.getKey().equals(key)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 判断是否通过类型
+ *
+ * @param key 枚举key
+ * @return boolean
+ */
+ public static Boolean isPass(String key) {
+ return StrUtil.isNotEmpty(key) && (SkipType.PASS.getKey().equals(key));
+ }
+
+ /**
+ * 判断是否退回类型
+ *
+ * @param key 枚举key
+ * @return boolean
+ */
+ public static Boolean isReject(String key) {
+ return StrUtil.isNotEmpty(key) && (SkipType.REJECT.getKey().equals(key));
+ }
+
+ /**
+ * 判断是否无动作类型
+ *
+ * @param key 枚举key
+ * @return boolean
+ */
+ public static Boolean isNone(String key) {
+ return StrUtil.isNotEmpty(key) && (SkipType.NONE.getKey().equals(key));
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/UserType.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/UserType.java
new file mode 100644
index 000000000..9523cbd8f
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/UserType.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+/**
+ * 流程用户类型
+ *
+ * @author xiarg
+ * @since 2024/5/10 16:04
+ */
+@Getter
+@AllArgsConstructor
+public enum UserType {
+
+ /**
+ * 流程用户类型
+ */
+ APPROVAL("1", "待办任务的审批人权限"),
+
+ TRANSFER("2", "待办任务的转办人权限"),
+
+ DEPUTE("3", "待办任务的委托人权限");
+
+ private final String key;
+ private final String value;
+
+ public static String getKeyByValue(String value) {
+ for (UserType item : UserType.values()) {
+ if (item.getValue().equals(value)) {
+ return item.getKey();
+ }
+ }
+ return null;
+ }
+
+ public static String getValueByKey(String key) {
+ for (UserType item : UserType.values()) {
+ if (item.getKey().equals(key)) {
+ return item.getValue();
+ }
+ }
+ return null;
+ }
+
+ public static UserType getByKey(String key) {
+ for (UserType item : UserType.values()) {
+ if (item.getKey().equals(key)) {
+ return item;
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/VoteSign.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/VoteSign.java
new file mode 100644
index 000000000..22dd4fdd7
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/enums/VoteSign.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.enums;
+
+import org.dromara.warm.flow.constant.FlowCons;
+import org.dromara.warm.flow.expression.SpelHelper;
+import org.dromara.warm.flow.strategy.VoteSignStrategy;
+
+import java.util.Map;
+
+/**
+ * 内置会签票签表达式策略,合并原 VoteSignStrategySpel/VoteSignStrategyDefault 两个策略类
+ * 表达式格式:spel@@#{@user.eval()} 或 default@@${flag == 5 && flag > 4}
+ *
+ * @author warm
+ */
+public enum VoteSign implements VoteSignStrategy {
+
+ /**
+ * spel 会签表达式 spel@@#{@user.eval()}
+ */
+ SPEL(FlowCons.SPEL) {
+ @Override
+ public Boolean eval(String expression, Map variable) {
+ return SpelHelper.evalBool(expression, variable);
+ }
+ },
+
+ /**
+ * 默认会签表达式 default@@${flag == 5 && flag > 4},基于 spel 简化使用
+ */
+ DEFAULT(FlowCons.DEFAULT) {
+ @Override
+ public Boolean eval(String expression, Map variable) {
+ return SPEL.eval(SpelHelper.replace(expression, variable), variable);
+ }
+ };
+
+ private final String key;
+
+ VoteSign(String key) {
+ this.key = key;
+ }
+
+ @Override
+ public String getType() {
+ return key;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/exception/FlowException.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/exception/FlowException.java
new file mode 100644
index 000000000..b2ac3022e
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/exception/FlowException.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.exception;
+
+import lombok.Getter;
+
+/**
+ * 流程异常
+ *
+ * @author warm
+ */
+public final class FlowException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 错误码
+ */
+ @Getter
+ private Integer code;
+
+ /**
+ * 错误提示
+ */
+ private String message;
+
+ /**
+ * 错误明细,内部调试错误
+ */
+ @Getter
+ private String detailMessage;
+
+ /**
+ * 空构造方法,避免反序列化问题
+ */
+ public FlowException() {
+ }
+
+ public FlowException(String message) {
+ this.message = message;
+ }
+
+ public FlowException(String message, Throwable cause) {
+ super(message, cause);
+ this.message = message;
+ }
+
+ public FlowException(String message, Integer code) {
+ this.message = message;
+ this.code = code;
+ }
+
+ public FlowException setDetailMessage(String detailMessage) {
+ this.detailMessage = detailMessage;
+ return this;
+ }
+
+ @Override
+ public String getMessage() {
+ return message;
+ }
+
+ public FlowException setMessage(String message) {
+ this.message = message;
+ return this;
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/ListenerStrategySpel.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/ListenerStrategySpel.java
new file mode 100644
index 000000000..b5effc9cd
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/ListenerStrategySpel.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.expression;
+
+import org.dromara.warm.flow.strategy.ListenerStrategy;
+import org.dromara.warm.flow.expression.SpelHelper;
+
+import java.util.Map;
+
+/**
+ * spel监听器表达式 #{@user.eval()}
+ *
+ * @author warm
+ */
+public class ListenerStrategySpel implements ListenerStrategy {
+
+ @Override
+ public String getType() {
+ return "#";
+ }
+
+ @Override
+ public Boolean eval(String expression, Map variable) {
+ SpelHelper.parseExpression(expression, variable);
+ // 恒返回true,说明匹配上监听器表达式,扩展策略也一定要返回true
+ return true;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SafeMethodResolver.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SafeMethodResolver.java
new file mode 100644
index 000000000..3099a8f14
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SafeMethodResolver.java
@@ -0,0 +1,53 @@
+package org.dromara.warm.flow.expression;
+
+import org.springframework.core.convert.TypeDescriptor;
+import org.springframework.expression.AccessException;
+import org.springframework.expression.EvaluationContext;
+import org.springframework.expression.MethodExecutor;
+import org.springframework.expression.MethodResolver;
+import org.springframework.expression.spel.support.DataBindingMethodResolver;
+import org.springframework.lang.NonNull;
+import org.springframework.lang.Nullable;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * 安全的方法解析器(限制可调用的方法)
+ *
+ * @author warm
+ * @since 2026/3/31
+ */
+public class SafeMethodResolver implements MethodResolver {
+
+ private static final Set DANGEROUS_METHODS = new HashSet<>(Arrays.asList(
+ "getRuntime",
+ "exec",
+ "forName",
+ "loadClass",
+ "getClassLoader",
+ "setAccessible",
+ "newInstance",
+ "invoke",
+ "getField",
+ "getDeclaredField",
+ "getMethod",
+ "getDeclaredMethod"
+ ));
+
+
+ @Nullable
+ @Override
+ public MethodExecutor resolve(@NonNull EvaluationContext context, @NonNull Object targetObject
+ , @NonNull String name, @NonNull List argumentTypes) throws AccessException {
+ if (DANGEROUS_METHODS.contains(name)) {
+ throw new AccessException("不允许调用方法:" + name);
+ }
+
+ // 委托给默认的方法解析器
+ return DataBindingMethodResolver.forInstanceMethodInvocation()
+ .resolve(context, targetObject, name, argumentTypes);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SafeTypeLocator.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SafeTypeLocator.java
new file mode 100644
index 000000000..b75de6956
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SafeTypeLocator.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.expression;
+
+import cn.hutool.core.util.StrUtil;
+
+import org.dromara.warm.flow.exception.FlowException;
+
+import org.springframework.expression.TypeLocator;
+import org.springframework.expression.spel.support.StandardTypeLocator;
+import org.springframework.lang.NonNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * 自定义类型定位器,用于限制访问的类
+ *
+ * @author warm
+ * @since 2026/3/31
+ */
+// ... existing code ...
+
+/**
+ * 自定义类型定位器,用于限制访问的类
+ *
+ * @author warm
+ * @since 2026/3/31
+ */
+public class SafeTypeLocator implements TypeLocator {
+ private final StandardTypeLocator defaultTypeLocator;
+ private final Set allowedClasses;
+ private static final Set DANGEROUS_CLASSES;
+
+ static {
+ // 初始化危险类黑名单(使用全限定名便于前缀匹配)
+ DANGEROUS_CLASSES = new HashSet<>();
+ DANGEROUS_CLASSES.add("java.lang.Runtime");
+ DANGEROUS_CLASSES.add("java.lang.ProcessBuilder");
+ DANGEROUS_CLASSES.add("java.lang.System");
+ DANGEROUS_CLASSES.add("java.lang.Class");
+ DANGEROUS_CLASSES.add("java.lang.reflect");
+ DANGEROUS_CLASSES.add("sun.misc.Unsafe");
+ DANGEROUS_CLASSES.add("sun.reflect");
+ }
+
+ {
+ allowedClasses = new HashSet<>();
+ allowedClasses.add("java.lang.String");
+ allowedClasses.add("java.lang.Integer");
+ allowedClasses.add("java.lang.Long");
+ allowedClasses.add("java.lang.Double");
+ allowedClasses.add("java.lang.Float");
+ allowedClasses.add("java.lang.Boolean");
+ allowedClasses.add("java.lang.Byte");
+ allowedClasses.add("java.lang.Short");
+ allowedClasses.add("java.lang.Character");
+ allowedClasses.add("java.lang.Object");
+ allowedClasses.add("java.util.List");
+ allowedClasses.add("java.util.ArrayList");
+ allowedClasses.add("java.util.Map");
+ allowedClasses.add("java.util.HashMap");
+ allowedClasses.add("java.util.Set");
+ allowedClasses.add("java.util.HashSet");
+ allowedClasses.add("java.util.Date");
+ }
+
+ public SafeTypeLocator() {
+ this.defaultTypeLocator = new StandardTypeLocator();
+ }
+
+ @Override
+ public Class> findType(@NonNull String typeName) {
+ if (StrUtil.isEmpty(typeName)) {
+ throw new FlowException("Type name cannot be null or empty");
+ }
+
+ // 检查是否为危险类(黑名单)
+ if (isDangerousClass(typeName)) {
+ throw new FlowException("Forbidden dangerous class: " + typeName);
+ }
+
+ // 检查是否在白名单内
+ if (!allowedClasses.contains(typeName)) {
+ throw new FlowException("Forbidden class not in whitelist: " + typeName);
+ }
+
+ return defaultTypeLocator.findType(typeName);
+ }
+
+ private boolean isDangerousClass(String typeName) {
+ for (String dangerousClass : DANGEROUS_CLASSES) {
+ if (typeName.equals(dangerousClass) || typeName.startsWith(dangerousClass + ".")) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SpelHelper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SpelHelper.java
new file mode 100644
index 000000000..8bbcf3ebe
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/expression/SpelHelper.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.expression;
+
+import org.dromara.warm.flow.exception.FlowException;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.ListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.expression.BeanFactoryResolver;
+import org.springframework.expression.BeanResolver;
+import org.springframework.expression.ExpressionParser;
+import org.springframework.expression.ParserContext;
+import org.springframework.expression.common.TemplateParserContext;
+import org.springframework.expression.spel.standard.SpelExpressionParser;
+import org.springframework.expression.spel.support.StandardEvaluationContext;
+import org.springframework.lang.NonNull;
+
+import java.util.Map;
+
+/**
+ * 条件表达式 spel
+ *
+ * @author warm,battcn
+ */
+@Configuration
+public class SpelHelper implements ApplicationContextAware {
+
+ private static final ExpressionParser PARSER = new SpelExpressionParser();
+ private final static ParserContext PARSER_CONTEXT = new TemplateParserContext();
+
+ private static ApplicationContext applicationContext;
+
+ /**
+ * bean解析器 用于处理 spel 表达式中对 bean 的调用
+ */
+ private static BeanResolver beanResolver = null;
+
+ public static BeanResolver beanResolver() {
+ if (beanResolver == null) {
+ beanResolver = new BeanFactoryResolver(getBeanFactory());
+ }
+ return beanResolver;
+ }
+
+ /**
+ * @param expression expression
+ * @return Object
+ */
+ public static Object parseExpression(String expression, Map variable) {
+
+ // 创建带沙箱保护的评估上下文
+ StandardEvaluationContext context = new StandardEvaluationContext();
+
+ // 设置 Bean 解析器(支持 @beanName 引用)
+ context.setBeanResolver(beanResolver());
+
+ // 设置变量
+ context.setVariables(variable);
+
+ // 设置沙箱:限制类型访问(白名单机制)
+ context.setTypeLocator(new SafeTypeLocator());
+
+ // 设置沙箱:限制方法调用
+ context.addMethodResolver(new SafeMethodResolver());
+
+ return PARSER.parseExpression(expression, PARSER_CONTEXT).getValue(context, Object.class);
+ }
+
+ /**
+ * 执行 spel 表达式并按布尔结果返回
+ *
+ * @param expression 表达式
+ * @param variable 流程变量
+ * @return 布尔结果
+ */
+ public static boolean evalBool(String expression, Map variable) {
+ return Boolean.TRUE.equals(parseExpression(expression, variable));
+ }
+
+ @Override
+ public void setApplicationContext(@NonNull ApplicationContext applicationContext) throws BeansException {
+ if (SpelHelper.applicationContext == null) {
+ SpelHelper.applicationContext = applicationContext;
+ }
+ }
+
+ public static ListableBeanFactory getBeanFactory() {
+ if (null == applicationContext) {
+ throw new FlowException("No ConfigurableListableBeanFactory or ApplicationContext injected, maybe not in the Spring environment?");
+ } else {
+ return applicationContext;
+ }
+ }
+
+ public static String replace(String expression, Map variable) {
+ expression = expression.replace("$", "#");
+ for (Map.Entry entry : variable.entrySet()) {
+ if (expression.contains(entry.getKey())) {
+ expression = expression.replace(entry.getKey(), "#" + entry.getKey());
+ }
+ }
+ return expression;
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/handler/DataFillHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/handler/DataFillHandler.java
new file mode 100644
index 000000000..44634fce3
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/handler/DataFillHandler.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.handler;
+
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.ObjectUtil;
+
+import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import org.dromara.common.core.utils.SpringUtils;
+import org.dromara.warm.flow.FlowEngine;
+import org.dromara.warm.flow.entity.RootEntity;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Date;
+import java.util.Objects;
+
+/**
+ * 数据填充handler,以下三个接口按照实际情况实现
+ *
+ * @author warm
+ * @since 2023/4/1 15:37
+ */
+public interface DataFillHandler {
+
+ Logger logger = LoggerFactory.getLogger(DataFillHandler.class);
+
+ /**
+ * id填充。引擎在入库前就需要主键做行间互引(如历史任务的 instanceId/taskId),
+ * 必须此处生成而非留给 MyBatis-Plus 在 insert 时补;与全局 IdentifierGenerator 同源,
+ * insert 时对非空 id 不会重复生成
+ *
+ * @param object object
+ */
+ default void idFill(Object object) {
+ if (object instanceof RootEntity entity && ObjectUtil.isNull(entity.getId())) {
+ IdentifierGenerator generator = SpringUtils.getBeanOrNull(IdentifierGenerator.class);
+ entity.setId(generator != null ? generator.nextId(entity).longValue() : IdWorker.getId());
+ }
+ }
+
+ /**
+ * 新增填充
+ *
+ * @param object object
+ */
+ default void insertFill(Object object) {
+ RootEntity entity = (RootEntity) object;
+ if (ObjectUtil.isNull(entity)) {
+ logger.warn("Insert operation failed - Reason: Entity is null after casting");
+ return;
+ }
+ entity.setCreateTime(ObjectUtil.isNotNull(entity.getCreateTime()) ? entity.getCreateTime() : new Date());
+ entity.setUpdateTime(ObjectUtil.isNotNull(entity.getUpdateTime()) ? entity.getUpdateTime() : new Date());
+
+ PermissionHandler permissionHandler = FlowEngine.permissionHandler();
+ String handler = null;
+ if (permissionHandler != null) {
+ try {
+ handler = permissionHandler.getHandler();
+ } catch (Exception ignored) {
+ }
+ }
+ entity.setCreateBy(StrUtil.isNotEmpty(handler) ? handler : entity.getCreateBy());
+ entity.setUpdateBy(StrUtil.isNotEmpty(handler) ? handler : entity.getUpdateBy());
+ }
+
+ /**
+ * 设置更新常用参数
+ *
+ * @param object object
+ */
+ default void updateFill(Object object) {
+ RootEntity entity = (RootEntity) object;
+ if (ObjectUtil.isNull(entity)) {
+ logger.warn("Insert operation failed - Reason: Entity is null after casting");
+ return;
+ }
+ entity.setUpdateTime(ObjectUtil.isNotNull(entity.getUpdateTime()) ? entity.getUpdateTime() : new Date());
+ PermissionHandler permissionHandler = FlowEngine.permissionHandler();
+ String handler = null;
+ if (permissionHandler != null) {
+ try {
+ handler = permissionHandler.getHandler();
+ } catch (Exception ignored) {
+ }
+ }
+ entity.setUpdateBy(StrUtil.isNotEmpty(handler) ? handler : entity.getUpdateBy());
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/handler/PermissionHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/handler/PermissionHandler.java
new file mode 100644
index 000000000..85b70f496
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/handler/PermissionHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.handler;
+
+import org.dromara.warm.flow.dto.FlowParams;
+
+import java.util.List;
+
+/**
+ * 办理人权限处理器
+ * 用户获取工作流中用到的permissionFlag和handler
+ * permissionFlag: 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务
+ * handler: 当前办理人唯一标识,就是确定唯一用的,如用户id,通常用来入库,记录流程实例创建人,办理人
+ *
+ * @author shadow
+ */
+public interface PermissionHandler {
+
+ /**
+ * 办理人权限标识,比如用户,角色,部门等,用于校验是否有权限办理任务
+ * 后续在{@link FlowParams#getPermissionFlag} 中获取
+ * 返回当前用户权限集合
+ *
+ */
+ List permissions();
+
+ /**
+ * 获取当前办理人:就是确定唯一用的,如用户id,通常用来入库,记录流程实例创建人,办理人
+ * 后续在{@link FlowParams#getHandler()} 中获取
+ *
+ * @return 当前办理人
+ */
+ String getHandler();
+
+ /**
+ * 转换办理人,比如设计器中预设了能办理的人,如果其中包含角色或者部门id等,可以通过此接口进行转换成用户id
+ */
+ default List convertPermissions(List permissions) {
+ return permissions;
+ }
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/json/JsonUtil.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/json/JsonUtil.java
new file mode 100644
index 000000000..7845506be
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/json/JsonUtil.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.json;
+
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import org.dromara.warm.flow.exception.FlowException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import tools.jackson.core.type.TypeReference;
+import tools.jackson.databind.DeserializationFeature;
+import tools.jackson.databind.json.JsonMapper;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Jackson 3:map和json字符串转换工具类
+ *
+ * @author warm
+ */
+public final class JsonUtil {
+
+ private static final Logger log = LoggerFactory.getLogger(JsonUtil.class);
+
+ private static final JsonMapper JSON_MAPPER = JsonMapper.builder()
+ .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
+ .build();
+
+ private JsonUtil() {
+ }
+
+ /**
+ * 将字符串转为map
+ *
+ * @param jsonStr json字符串
+ * @return map
+ */
+ public static Map strToMap(String jsonStr) {
+ if (StrUtil.isNotEmpty(jsonStr)) {
+ try {
+ return JSON_MAPPER.readValue(jsonStr,
+ JSON_MAPPER.getTypeFactory().constructMapType(Map.class, String.class, Object.class));
+ } catch (Exception e) {
+ log.error("json转换异常", e);
+ throw new FlowException("json转换异常");
+ }
+ }
+ return new HashMap<>();
+ }
+
+ /**
+ * 将字符串转为bean
+ *
+ * @param jsonStr json字符串
+ * @param clazz Class
+ * @return T
+ */
+ public static T strToBean(String jsonStr, Class clazz) {
+ if (StrUtil.isNotEmpty(jsonStr)) {
+ try {
+ return JSON_MAPPER.readValue(jsonStr, clazz);
+ } catch (Exception e) {
+ log.error("json转换异常", e);
+ throw new FlowException("json转换异常");
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 将字符串转为集合
+ *
+ * @param jsonStr json字符串
+ * @return List
+ */
+ public static List strToList(String jsonStr) {
+ if (StrUtil.isNotEmpty(jsonStr)) {
+ try {
+ return JSON_MAPPER.readValue(jsonStr, new TypeReference>() {
+ });
+ } catch (Exception e) {
+ log.error("json转换异常", e);
+ throw new FlowException("json转换异常");
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 将对象转为字符串
+ *
+ * @param variable object
+ * @return json字符串
+ */
+ public static String objToStr(Object variable) {
+ if (ObjectUtil.isNotNull(variable)) {
+ try {
+ return JSON_MAPPER.writeValueAsString(variable);
+ } catch (Exception e) {
+ log.error("Map转换异常", e);
+ throw new FlowException("Map转换异常");
+ }
+ }
+ return null;
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/GlobalListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/GlobalListener.java
new file mode 100644
index 000000000..9eca9d59f
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/GlobalListener.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.listener;
+
+import java.io.Serializable;
+
+/**
+ * 全局监听器: 整个系统只有一个,任务开始、分派、完成和创建、时期执行
+ *
+ * @author warm
+ * @since 2024/11/17
+ */
+public interface GlobalListener extends Serializable {
+
+ /**
+ * 开始监听器,任务开始办理时执行
+ *
+ * @param listenerVariable 监听器变量
+ */
+ default void start(ListenerVariable listenerVariable) {
+
+ }
+
+ /**
+ * 分派监听器,动态修改代办任务信息
+ *
+ * @param listenerVariable 监听器变量
+ */
+ default void assignment(ListenerVariable listenerVariable) {
+
+ }
+
+ /**
+ * 完成监听器,当前任务完成后执行
+ *
+ * @param listenerVariable 监听器变量
+ */
+ default void finish(ListenerVariable listenerVariable) {
+
+ }
+
+ /**
+ * 创建监听器,任务创建时执行
+ *
+ * @param listenerVariable 监听器变量
+ */
+ default void create(ListenerVariable listenerVariable) {
+
+ }
+
+ default void notify(String type, ListenerVariable listenerVariable) {
+ switch (type) {
+ case Listener.LISTENER_START:
+ start(listenerVariable);
+ break;
+ case Listener.LISTENER_ASSIGNMENT:
+ assignment(listenerVariable);
+ break;
+ case Listener.LISTENER_FINISH:
+ finish(listenerVariable);
+ break;
+ case Listener.LISTENER_CREATE:
+ create(listenerVariable);
+ break;
+ }
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/Listener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/Listener.java
new file mode 100644
index 000000000..7589b3fbc
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/Listener.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.listener;
+
+import java.io.Serializable;
+
+/**
+ * 监听器
+ *
+ * @author warm
+ */
+public interface Listener extends Serializable {
+
+ /**
+ * 开始监听器,任务开始办理时执行
+ */
+ String LISTENER_START = "start";
+
+ /**
+ * 分派监听器,动态修改代办任务信息
+ */
+ String LISTENER_ASSIGNMENT = "assignment";
+
+ /**
+ * 完成监听器,当前任务完成后执行
+ */
+ String LISTENER_FINISH = "finish";
+
+ /**
+ * 创建监听器,任务创建时执行
+ */
+ String LISTENER_CREATE = "create";
+
+ /**
+ * 表单数据加载监听器,1.3.0 内置表单使用
+ */
+ String LISTENER_FORM_LOAD = "formLoad";
+
+ /**
+ * 通知
+ *
+ * @param variable variable
+ */
+ void notify(ListenerVariable variable);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/ListenerVariable.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/ListenerVariable.java
new file mode 100644
index 000000000..d169f78bc
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/ListenerVariable.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.listener;
+
+import org.dromara.warm.flow.dto.FlowParams;
+import org.dromara.warm.flow.entity.FlowDefinition;
+import org.dromara.warm.flow.entity.FlowInstance;
+import org.dromara.warm.flow.entity.FlowNode;
+import org.dromara.warm.flow.entity.FlowTask;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 监听器变量
+ *
+ * @author warm
+ */
+public class ListenerVariable {
+
+ /**
+ * 流程定义
+ */
+ private FlowDefinition definition;
+
+ /**
+ * 流程实例
+ */
+ private FlowInstance instance;
+
+ /**
+ * 监听器对应的节点
+ */
+ private FlowNode node;
+
+ /**
+ * 当前任务
+ */
+ private FlowTask task;
+
+ /**
+ * 下一次执行的节点集合
+ */
+ private List nextNodes;
+
+ /**
+ * 新创建任务集合
+ */
+ private List nextTasks;
+
+ /**
+ * 流程变量
+ */
+ private Map variable;
+
+ /**
+ * 工作流内置参数
+ */
+ private FlowParams flowParams;
+
+
+ public ListenerVariable() {
+ }
+
+ public ListenerVariable(FlowDefinition definition, FlowInstance instance, Map variable) {
+ this.definition = definition;
+ this.instance = instance;
+ this.variable = variable;
+ }
+
+ public ListenerVariable(FlowDefinition definition, FlowInstance instance, FlowNode node, Map variable) {
+ this.definition = definition;
+ this.instance = instance;
+ this.node = node;
+ this.variable = variable;
+ }
+
+ public ListenerVariable(FlowDefinition definition, FlowInstance instance, Map variable, FlowTask task) {
+ this.definition = definition;
+ this.instance = instance;
+ this.variable = variable;
+ this.task = task;
+ }
+
+ public ListenerVariable(FlowDefinition definition, FlowInstance instance, FlowNode node, Map variable, FlowTask task) {
+ this.definition = definition;
+ this.instance = instance;
+ this.node = node;
+ this.variable = variable;
+ this.task = task;
+ }
+
+ public ListenerVariable(FlowDefinition definition, FlowInstance instance, FlowNode node, Map variable, FlowTask task, List nextNodes) {
+ this.definition = definition;
+ this.instance = instance;
+ this.node = node;
+ this.variable = variable;
+ this.task = task;
+ this.nextNodes = nextNodes;
+ }
+
+ public ListenerVariable(FlowDefinition definition, FlowInstance instance, FlowNode node, Map variable, FlowTask task
+ , List nextNodes, List nextTasks) {
+ this.definition = definition;
+ this.instance = instance;
+ this.node = node;
+ this.variable = variable;
+ this.task = task;
+ this.nextNodes = nextNodes;
+ this.nextTasks = nextTasks;
+ }
+
+ public FlowDefinition getDefinition() {
+ return definition;
+ }
+
+ public ListenerVariable setDefinition(FlowDefinition definition) {
+ this.definition = definition;
+ return this;
+ }
+
+ public FlowInstance getInstance() {
+ return instance;
+ }
+
+ public ListenerVariable setInstance(FlowInstance instance) {
+ this.instance = instance;
+ return this;
+ }
+
+ public FlowNode getNode() {
+ return node;
+ }
+
+ public ListenerVariable setNode(FlowNode node) {
+ this.node = node;
+ return this;
+ }
+
+ public FlowTask getTask() {
+ return task;
+ }
+
+ public ListenerVariable setTask(FlowTask task) {
+ this.task = task;
+ return this;
+ }
+
+ public List getNextNodes() {
+ return nextNodes;
+ }
+
+ public ListenerVariable setNextNodes(List nextNodes) {
+ this.nextNodes = nextNodes;
+ return this;
+ }
+
+ public List getNextTasks() {
+ return nextTasks;
+ }
+
+ public ListenerVariable setNextTasks(List nextTasks) {
+ this.nextTasks = nextTasks;
+ return this;
+ }
+
+ public Map getVariable() {
+ return variable;
+ }
+
+ public ListenerVariable setVariable(Map variable) {
+ this.variable = variable;
+ return this;
+ }
+
+ public FlowParams getFlowParams() {
+ return flowParams;
+ }
+
+ public ListenerVariable setFlowParams(FlowParams flowParams) {
+ this.flowParams = flowParams;
+ return this;
+ }
+
+
+ @Override
+ public String toString() {
+ return "ListenerVariable{" +
+ "definition=" + definition +
+ ", instance=" + instance +
+ ", node=" + node +
+ ", task=" + task +
+ ", nextNodes=" + nextNodes +
+ ", nextTasks=" + nextTasks +
+ ", variable=" + variable +
+ ", flowParams=" + flowParams +
+ '}';
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/ValueHolder.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/ValueHolder.java
new file mode 100644
index 000000000..acfce8d20
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/listener/ValueHolder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.listener;
+
+import lombok.Data;
+
+/**
+ * @author warm
+ */
+@Data
+public class ValueHolder {
+
+ /**
+ * 路径
+ */
+ private String path;
+ /**
+ * 监听器
+ */
+ private Listener listener;
+ /**
+ * 参数
+ */
+ private String params;
+
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowDefinitionMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowDefinitionMapper.java
new file mode 100644
index 000000000..5973d2743
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowDefinitionMapper.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.dromara.warm.flow.entity.FlowDefinition;
+
+import java.util.List;
+
+/**
+ * 流程定义Mapper接口
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+public interface FlowDefinitionMapper extends WarmMapper {
+
+ /**
+ * 根据编码批量查询
+ *
+ * @param flowCodeList 流程编码集
+ * @return 查询结果
+ */
+ default List queryByCodeList(List flowCodeList) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.in(FlowDefinition::getFlowCode, flowCodeList);
+ return selectList(queryWrapper);
+ }
+
+ /**
+ * 根据ID批量修改发布状态
+ *
+ * @param ids ids
+ * @param publishStatus 发布状态(9=已失效;0=未发布;1=已发布)
+ * @see org.dromara.warm.flow.enums.PublishStatus
+ */
+ default void updatePublishStatus(List ids, Integer publishStatus) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.in(FlowDefinition::getId, ids);
+ update(new FlowDefinition().setIsPublish(publishStatus), queryWrapper);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowHisTaskMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowHisTaskMapper.java
new file mode 100644
index 000000000..3f6b178f2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowHisTaskMapper.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.mapper;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.dromara.warm.flow.entity.FlowHisTask;
+import org.dromara.warm.flow.enums.SkipType;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 历史任务记录Mapper接口
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+public interface FlowHisTaskMapper extends WarmMapper {
+
+ /**
+ * 根据instanceId获取未退回的历史记录
+ */
+ default List getNoReject(Long instanceId) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(FlowHisTask::getInstanceId, instanceId)
+ .eq(FlowHisTask::getSkipType, SkipType.PASS.getKey())
+ .orderByDesc(FlowHisTask::getCreateTime);
+ return selectList(queryWrapper);
+ }
+
+ /**
+ * 根据instanceId和流程编码获取未退回的历史记录
+ */
+ default List getByInsAndNodeCodes(Long instanceId, List nodeCodes) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(FlowHisTask::getInstanceId, instanceId)
+ .in(CollUtil.isNotEmpty(nodeCodes), FlowHisTask::getNodeCode, nodeCodes)
+ .orderByDesc(FlowHisTask::getCreateTime);
+ return selectList(queryWrapper);
+ }
+
+ /**
+ * 根据instanceIds删除
+ *
+ * @param instanceIds 主键
+ * @return 结果
+ */
+ default int deleteByInsIds(List instanceIds) {
+ return delete(new LambdaQueryWrapper().in(FlowHisTask::getInstanceId, instanceIds));
+ }
+
+ /**
+ * 根据任务id和协作类型查询
+ */
+ default List listByTaskIdAndCooperateTypes(Long taskId, Integer[] cooperateTypes) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(FlowHisTask::getTaskId, taskId).in(FlowHisTask::getCooperateType, Arrays.asList(cooperateTypes));
+ return selectList(queryWrapper);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowInstanceMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowInstanceMapper.java
new file mode 100644
index 000000000..36698d85a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowInstanceMapper.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.dromara.warm.flow.entity.FlowInstance;
+
+import java.util.List;
+
+/**
+ * 流程实例Mapper接口
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+public interface FlowInstanceMapper extends WarmMapper {
+
+ /**
+ * 根据流程定义ID,查询流程实例集合
+ *
+ * @param defIds 流程定义ID集合
+ * @return 流程实例集合
+ */
+ default List getByDefIds(List defIds) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.in(FlowInstance::getDefinitionId, defIds);
+ return selectList(queryWrapper);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowNodeMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowNodeMapper.java
new file mode 100644
index 000000000..b22032770
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowNodeMapper.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.mapper;
+
+import cn.hutool.core.collection.CollUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.dromara.warm.flow.entity.FlowNode;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 流程节点Mapper接口
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+public interface FlowNodeMapper extends WarmMapper {
+
+ default List getByNodeCodes(List nodeCodes, Long definitionId) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.in(CollUtil.isNotEmpty(nodeCodes), FlowNode::getNodeCode, nodeCodes)
+ .eq(FlowNode::getDefinitionId, definitionId);
+ return selectList(queryWrapper);
+ }
+
+ /**
+ * 批量删除流程节点
+ *
+ * @param defIds 需要删除的数据主键集合
+ * @return 结果
+ */
+ default int deleteNodeByDefIds(Collection extends Serializable> defIds) {
+ return delete(new LambdaQueryWrapper().in(FlowNode::getDefinitionId, defIds));
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowSkipMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowSkipMapper.java
new file mode 100644
index 000000000..11b1d3c3c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowSkipMapper.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.dromara.warm.flow.entity.FlowSkip;
+
+import java.io.Serializable;
+import java.util.Collection;
+
+/**
+ * 节点跳转关联Mapper接口
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+public interface FlowSkipMapper extends WarmMapper {
+
+ /**
+ * 批量删除节点跳转关联
+ *
+ * @param defIds 需要删除的数据主键集合
+ * @return 结果
+ */
+ default int deleteSkipByDefIds(Collection extends Serializable> defIds) {
+ return delete(new LambdaQueryWrapper().in(FlowSkip::getDefinitionId, defIds));
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowTaskMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowTaskMapper.java
new file mode 100644
index 000000000..9047ea251
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowTaskMapper.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.dromara.warm.flow.entity.FlowTask;
+
+import java.util.List;
+
+/**
+ * 待办任务Mapper接口
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+public interface FlowTaskMapper extends WarmMapper {
+
+ /**
+ * 根据instanceIds删除
+ *
+ * @param instanceIds 主键
+ * @return 结果
+ */
+ default int deleteByInsIds(List instanceIds) {
+ return delete(new LambdaQueryWrapper().in(FlowTask::getInstanceId, instanceIds));
+ }
+
+ default List getByInsIdAndNodeCodes(Long instanceId, List nodeCodes) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(FlowTask::getInstanceId, instanceId);
+ queryWrapper.in(FlowTask::getNodeCode, nodeCodes);
+ return selectList(queryWrapper);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowUserMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowUserMapper.java
new file mode 100644
index 000000000..9e4226747
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/FlowUserMapper.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.mapper;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ArrayUtil;
+import cn.hutool.core.util.ObjectUtil;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import org.dromara.warm.flow.entity.FlowUser;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * 流程用户Mapper接口
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+public interface FlowUserMapper extends WarmMapper {
+
+ /**
+ * 根据taskId删除
+ *
+ * @param taskIdList 待办任务主键集合
+ * @return 结果
+ */
+ default int deleteByTaskIds(List taskIdList) {
+ return delete(new LambdaQueryWrapper().in(FlowUser::getAssociated, taskIdList));
+ }
+
+ /**
+ * 根据(待办任务,实例,历史表,节点等)id查询权限人或者处理人
+ *
+ * @param associatedList (待办任务,实例,历史表,节点等)id集合
+ * @param types 用户表类型
+ * @return 查询结果
+ */
+ default List listByAssociatedAndTypes(List associatedList, String[] types) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ if (CollUtil.isNotEmpty(associatedList)) {
+ if (associatedList.size() == 1) {
+ queryWrapper.eq(FlowUser::getAssociated, associatedList.get(0));
+ } else {
+ queryWrapper.in(FlowUser::getAssociated, associatedList);
+ }
+ }
+ queryWrapper.in(ArrayUtil.isNotEmpty(types), FlowUser::getType, Arrays.asList(types));
+ return selectList(queryWrapper);
+ }
+
+ /**
+ * 根据办理人查询
+ *
+ * @param associated 待办任务id
+ * @param processedBys 办理人id集合
+ * @param types 用户表类型
+ * @return 查询结果
+ */
+ default List listByProcessedBys(Long associated, List processedBys, String[] types) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.eq(ObjectUtil.isNotNull(associated), FlowUser::getAssociated, associated);
+ if (CollUtil.isNotEmpty(processedBys)) {
+ if (processedBys.size() == 1) {
+ queryWrapper.eq(FlowUser::getProcessedBy, processedBys.get(0));
+ } else {
+ queryWrapper.in(FlowUser::getProcessedBy, processedBys);
+ }
+ }
+ queryWrapper.in(ArrayUtil.isNotEmpty(types), FlowUser::getType, types);
+ return selectList(queryWrapper);
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/WarmMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/WarmMapper.java
new file mode 100644
index 000000000..57eadf8c5
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/mapper/WarmMapper.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.mapper;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import org.dromara.warm.flow.entity.RootEntity;
+
+import java.util.List;
+
+/**
+ * BaseMapper接口(含原 Dao 层通用查询,default 方法由 MyBatis 直接调用)
+ *
+ * @author warm
+ * @since 2023-03-17
+ */
+public interface WarmMapper extends BaseMapper {
+
+ /**
+ * 实体条件查询
+ *
+ * @param entity 条件实体
+ */
+ default List selectList(T entity) {
+ return selectList(new QueryWrapper<>(entity));
+ }
+
+ /**
+ * 实体条件计数(接口类型 lambda 需显式设置实体类)
+ */
+ @SuppressWarnings("unchecked")
+ default long selectCount(T entity) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(entity);
+ queryWrapper.setEntityClass((Class) entity.getClass());
+ return selectCount(queryWrapper);
+ }
+
+ /**
+ * 按主键更新(接口类型 lambda 需显式设置实体类)
+ */
+ @SuppressWarnings("unchecked")
+ default int update(T entity) {
+ LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
+ queryWrapper.setEntityClass((Class) entity.getClass());
+ queryWrapper.eq(RootEntity::getId, entity.getId());
+ return update(entity, queryWrapper);
+ }
+
+ /**
+ * 实体条件删除
+ */
+ default int delete(T entity) {
+ return delete(new LambdaQueryWrapper<>(entity));
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/ChartService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/ChartService.java
new file mode 100644
index 000000000..f5df7af88
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/ChartService.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.service;
+
+import org.dromara.warm.flow.json.JsonUtil;
+
+import cn.hutool.core.util.ArrayUtil;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.ObjectUtil;
+
+import org.dromara.warm.flow.FlowEngine;
+import org.dromara.warm.flow.dto.DefJson;
+import org.dromara.warm.flow.dto.NodeJson;
+import org.dromara.warm.flow.dto.PathWayData;
+import org.dromara.warm.flow.dto.SkipJson;
+import org.dromara.warm.flow.entity.FlowInstance;
+import org.dromara.warm.flow.entity.FlowSkip;
+import org.dromara.warm.flow.enums.ChartStatus;
+import org.dromara.warm.flow.enums.NodeType;
+import org.dromara.warm.flow.enums.SkipType;
+
+
+import org.dromara.common.core.utils.StreamUtils;
+import org.springframework.stereotype.Service;
+
+
+import java.awt.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 流程图绘制Service业务层处理
+ *
+ * @author warm
+ * @since 2024-12-30
+ */
+@Service
+public class ChartService {
+
+ public String startMetadata(PathWayData pathWayData) {
+
+ DefJson defJson = FlowEngine.defService().queryDesign(pathWayData.getDefId());
+ List nodeList = defJson.getNodeList();
+
+ Map nodeMap = StreamUtils.toMap(nodeList, NodeJson::getNodeCode
+ , node -> node.setStatus(ChartStatus.NOT_DONE.getKey()));
+ Map skipMap = nodeList.stream().map(NodeJson::getSkipList).flatMap(List::stream)
+ .collect(Collectors.toMap(this::getSkipKey, skip -> skip.setStatus(ChartStatus.NOT_DONE.getKey())));
+
+ pathWayData.getPathWayNodes().forEach(node -> nodeMap.get(node.getNodeCode()).setStatus(ChartStatus.DONE.getKey()));
+ pathWayData.getPathWaySkips().forEach(skip -> skipMap.get(getSkipKey(skip)).setStatus(ChartStatus.DONE.getKey()));
+ pathWayData.getTargetNodes().forEach(node -> nodeMap.get(node.getNodeCode()).setStatus(
+ NodeType.isEnd(node.getNodeType()) ? ChartStatus.DONE.getKey() : ChartStatus.TO_DO.getKey()
+ ));
+
+ return JsonUtil.objToStr(defJson);
+ }
+
+ public String skipMetadata(PathWayData pathWayData) {
+ FlowInstance instance = FlowEngine.insService().getById(pathWayData.getInsId());
+ DefJson defJson = JsonUtil.strToBean(instance.getDefJson(), DefJson.class);
+
+ List nodeList = defJson.getNodeList();
+ List skipList = defJson.getNodeList().stream().map(NodeJson::getSkipList)
+ .filter(Objects::nonNull).flatMap(List::stream).collect(Collectors.toList());
+ Map nodeMap = StreamUtils.toMap(nodeList, NodeJson::getNodeCode, node -> node);
+ Map skipMap = StreamUtils.toMap(skipList, this::getSkipKey, skip -> skip);
+
+ pathWayData.getPathWayNodes().forEach(node -> {
+ NodeJson nodeJson = nodeMap.get(node.getNodeCode());
+ if (SkipType.isPass(pathWayData.getSkipType())) {
+ nodeJson.setStatus(ChartStatus.DONE.getKey());
+ } else if (SkipType.isReject(pathWayData.getSkipType())) {
+ nodeJson.setStatus(ChartStatus.NOT_DONE.getKey());
+ }
+ });
+ pathWayData.getPathWaySkips().forEach(skip -> {
+ SkipJson skipJson = skipMap.get(getSkipKey(skip));
+ if (SkipType.isPass(pathWayData.getSkipType())) {
+ skipJson.setStatus(ChartStatus.DONE.getKey());
+ } else if (SkipType.isReject(pathWayData.getSkipType())) {
+ skipJson.setStatus(ChartStatus.NOT_DONE.getKey());
+ }
+ });
+ pathWayData.getTargetNodes().forEach(node -> {
+ NodeJson nodeJson = nodeMap.get(node.getNodeCode());
+ if (NodeType.isEnd(node.getNodeType())) {
+ nodeJson.setStatus(ChartStatus.DONE.getKey());
+ } else {
+ nodeJson.setStatus(ChartStatus.TO_DO.getKey());
+ }
+ });
+
+ if (SkipType.isReject(pathWayData.getSkipType())) {
+ Map> skipNextMap = StreamUtils.groupByKey(
+ StreamUtils.filter(skipList, skip -> !SkipType.isReject(skip.getSkipType())), SkipJson::getNowNodeCode);
+ pathWayData.getTargetNodes().forEach(node -> rejectReset(node.getNodeCode(), skipNextMap, nodeMap));
+ }
+
+ pathWayData.getTargetNodes().forEach(node -> {
+ if (NodeType.isEnd(node.getNodeType())) {
+ nodeList.forEach(nodeJson -> {
+ if (ChartStatus.isToDo(nodeJson.getStatus())) {
+ nodeJson.setStatus(ChartStatus.NOT_DONE.getKey());
+ }
+ });
+ }
+ });
+
+
+ return JsonUtil.objToStr(defJson);
+ }
+
+ public List getChartRgb(String modelValue) {
+ List chartStatusColor = new ArrayList<>();
+ Color done = ChartStatus.getDone(modelValue);
+ chartStatusColor.add(done.getRed() + "," + done.getGreen() + "," + done.getBlue());
+ Color toDo = ChartStatus.getToDo(modelValue);
+ chartStatusColor.add(toDo.getRed() + "," + toDo.getGreen() + "," + toDo.getBlue());
+ Color notDone = ChartStatus.getNotDone(modelValue);
+ chartStatusColor.add(notDone.getRed() + "," + notDone.getGreen() + "," + notDone.getBlue());
+ return chartStatusColor;
+ }
+
+ private String getSkipKey(SkipJson skip) {
+ return ArrayUtil.join(new String[]{
+ skip.getNowNodeCode(),
+ skip.getSkipType(),
+ skip.getSkipCondition(),
+ skip.getNextNodeCode()}, ":");
+ }
+
+ private String getSkipKey(FlowSkip skip) {
+ return ArrayUtil.join(new String[]{
+ skip.getNowNodeCode(),
+ skip.getSkipType(),
+ skip.getSkipCondition(),
+ skip.getNextNodeCode()}, ":");
+ }
+
+ private void rejectReset(String nodeCode, Map> skipNextMap, Map nodeMap) {
+ List oneNextSkips = skipNextMap.get(nodeCode);
+ if (CollUtil.isNotEmpty(oneNextSkips)) {
+ oneNextSkips.forEach(oneNextSkip -> {
+ if (ObjectUtil.isNotNull(oneNextSkip) && !ChartStatus.isNotDone(oneNextSkip.getStatus())) {
+ oneNextSkip.setStatus(ChartStatus.NOT_DONE.getKey());
+ NodeJson nodeJson = nodeMap.get(oneNextSkip.getNextNodeCode());
+ if (ObjectUtil.isNotNull(nodeJson) && !ChartStatus.isNotDone(nodeJson.getStatus())) {
+ nodeJson.setStatus(ChartStatus.NOT_DONE.getKey());
+ rejectReset(nodeJson.getNodeCode(), skipNextMap, nodeMap);
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/DefService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/DefService.java
new file mode 100644
index 000000000..84648dad6
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/DefService.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.service;
+import org.dromara.warm.flow.json.JsonUtil;
+
+import org.dromara.warm.flow.entity.*;
+
+import org.dromara.common.core.utils.StreamUtils;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.ObjectUtil;
+
+import lombok.extern.slf4j.Slf4j;
+import org.dromara.warm.flow.FlowEngine;
+import org.dromara.warm.flow.constant.ExceptionCons;
+import org.dromara.warm.flow.dto.DefJson;
+import org.dromara.warm.flow.dto.FlowCombine;
+import org.dromara.warm.flow.entity.FlowDefinition;
+import org.dromara.warm.flow.entity.FlowInstance;
+import org.dromara.warm.flow.entity.FlowNode;
+import org.dromara.warm.flow.entity.FlowSkip;
+import org.dromara.warm.flow.enums.ActivityStatus;
+import org.dromara.warm.flow.enums.PublishStatus;
+import org.dromara.warm.flow.exception.FlowException;
+import org.dromara.warm.flow.service.WarmServiceImpl;
+import org.dromara.warm.flow.utils.*;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.*;
+import java.util.stream.Collectors;
+
+
+
+import org.dromara.common.core.utils.SpringUtils;
+import org.springframework.stereotype.Service;
+import org.dromara.warm.flow.mapper.FlowDefinitionMapper;
+
+import java.util.List;
+
+
+/**
+ * 流程定义Service业务层处理
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+@Slf4j
+@Service
+public class DefService extends WarmServiceImpl {
+ public FlowDefinition importIs(InputStream is) {
+ StringBuilder stringBuilder = new StringBuilder();
+ try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(is))) {
+ String line;
+ while ((line = bufferedReader.readLine()) != null) {
+ stringBuilder.append(line);
+ stringBuilder.append(System.lineSeparator());
+ }
+ } catch (IOException e) {
+ throw new FlowException(ExceptionCons.READ_IS_ERROR);
+ }
+ return importJson(stringBuilder.toString());
+ }
+
+ public FlowDefinition importJson(String defJson) {
+ return importDef(JsonUtil.strToBean(defJson, DefJson.class));
+ }
+
+ public FlowDefinition importDef(DefJson defJson) {
+ FlowDefinition definition = DefJson.copyDef(defJson);
+ FlowCombine flowCombine = FlowConfigUtil.structureFlow(definition);
+ return insertFlow(flowCombine.getDefinition(), flowCombine.getAllNodes(), flowCombine.getAllSkips());
+ }
+
+ public FlowDefinition insertFlow(FlowDefinition definition, List nodeList, List skipList) {
+ definition.setVersion(getNewVersion(definition));
+ for (FlowNode node : nodeList) {
+ node.setVersion(definition.getVersion());
+ }
+ FlowEngine.defService().save(definition);
+ FlowEngine.nodeService().saveBatch(nodeList);
+ FlowEngine.skipService().saveBatch(skipList);
+ return definition;
+ }
+
+ public boolean checkAndSave(FlowDefinition definition) {
+ return save(definition.setVersion(getNewVersion(definition)));
+ }
+
+ public void saveDef(DefJson defJson, boolean onlyNodeSkip) {
+ if (ObjectUtil.isNull(defJson)) {
+ return;
+ }
+ FlowCombine flowCombine = DefJson.copyCombine(defJson);
+ FlowDefinition definition = flowCombine.getDefinition();
+ Long id = definition.getId();
+ // 如果是新增的流程定义
+ if (ObjectUtil.isNull(id)) {
+ definition.setVersion(getNewVersion(definition));
+ FlowEngine.dataFillHandler().idFill(definition);
+ }
+
+ // 校验流程定义合法性
+ checkFlowLegal(flowCombine);
+
+ // 如果是新增的流程定义
+ if (ObjectUtil.isNull(id)) {
+ FlowEngine.defService().save(definition);
+ } else {
+ if (!onlyNodeSkip) {
+ FlowEngine.defService().updateById(definition);
+ }
+ // 删除所有节点和连线
+ FlowEngine.nodeService().remove(new FlowNode().setDefinitionId(id));
+ FlowEngine.skipService().remove(new FlowSkip().setDefinitionId(id));
+ }
+
+ // 保存流程节点和跳转
+ List allNodes = flowCombine.getAllNodes();
+ allNodes.forEach(node -> {
+ if(StrUtil.isEmpty(node.getNodeRatio())) {
+ node.setNodeRatio("0");
+ }
+ });
+ // 所有的流程连线
+ List allSkips = flowCombine.getAllSkips();
+
+ // 保存节点,流程连线,权利人
+ FlowEngine.nodeService().saveBatch(allNodes);
+ FlowEngine.skipService().saveBatch(allSkips);
+ }
+
+ public String exportJson(Long id) {
+ return JsonUtil.objToStr(queryDesign(id).setIsPublish(null));
+ }
+
+ public FlowDefinition getAllDataDefinition(Long id) {
+ FlowDefinition definition = getMapper().selectById(id);
+ List nodeList = FlowEngine.nodeService().getByDefId(id);
+ definition.setNodeList(nodeList);
+ List skips = FlowEngine.skipService().getByDefId(id);
+ Map> flowSkipMap = skips.stream()
+ .collect(Collectors.groupingBy(FlowSkip::getNowNodeCode));
+ nodeList.forEach(flowNode -> flowNode.setSkipList(flowSkipMap.get(flowNode.getNodeCode())));
+ return definition;
+ }
+
+ public FlowCombine getFlowCombine(Long id) {
+ return getFlowCombine(getMapper().selectById(id));
+ }
+
+ public FlowCombine getFlowCombineNoDef(Long id) {
+ FlowCombine flowCombine = new FlowCombine();
+ flowCombine.setAllNodes(FlowEngine.nodeService().getByDefId(id));
+ flowCombine.setAllSkips(FlowEngine.skipService().getByDefId(id));
+ return flowCombine;
+ }
+
+ public FlowCombine getFlowCombine(FlowDefinition definition) {
+ FlowCombine flowCombine = getFlowCombineNoDef(definition.getId());
+ flowCombine.setDefinition(definition);
+ return flowCombine;
+ }
+
+ public DefJson queryDesign(Long id) {
+ return DefJson.copyDef(getAllDataDefinition(id));
+ }
+
+ public List queryByCodeList(List flowCodeList) {
+ return getMapper().queryByCodeList(flowCodeList);
+ }
+
+ public void updatePublishStatus(List ids, Integer publishStatus) {
+ getMapper().updatePublishStatus(ids, publishStatus);
+ }
+
+ /**
+ * 删除流程定义
+ *
+ * @param ids 流程定义id
+ */
+ public boolean removeDef(List ids) {
+ ids.forEach(id -> {
+ List instances = FlowEngine.insService().getByDefId(id);
+ AssertUtil.isNotEmpty(instances, ExceptionCons.EXIST_START_TASK);
+ });
+ FlowEngine.nodeService().deleteNodeByDefIds(ids);
+ FlowEngine.skipService().deleteSkipByDefIds(ids);
+ return removeByIds(ids);
+ }
+
+ public boolean publish(Long id) {
+ List nodeList = FlowEngine.nodeService().getByDefId(id);
+ AssertUtil.isEmpty(nodeList, ExceptionCons.NOT_DRAW_FLOW_ERROR);
+ FlowDefinition definition = getById(id);
+ List definitions = getByFlowCode(definition.getFlowCode());
+ // 已发布流程定义,改为已失效或者未发布状态
+ List otherDefIds = definitions.stream()
+ .filter(item -> !Objects.equals(definition.getId(), item.getId())
+ && PublishStatus.PUBLISHED.getKey().equals(item.getIsPublish()))
+ .map(FlowDefinition::getId)
+ .collect(Collectors.toList());
+ if (CollUtil.isNotEmpty(otherDefIds)) {
+ List instanceList = FlowEngine.insService().listByDefIds(otherDefIds);
+ if (CollUtil.isNotEmpty(instanceList)) {
+ // 已发布已使用过的流程定义
+ Set useDefIds = StreamUtils.toSet(instanceList, FlowInstance::getDefinitionId);
+ if (CollUtil.isNotEmpty(useDefIds)) {
+ // 已发布已使用过的流程定义,改为已失效
+ getMapper().updatePublishStatus(new ArrayList<>(useDefIds), PublishStatus.EXPIRED.getKey());
+ // 过滤掉已发布已使用-->已发布未使用
+ otherDefIds.removeIf(useDefIds::contains);
+ }
+ }
+ if (CollUtil.isNotEmpty(otherDefIds)) {
+ // 已发布未使用过的流程定义,改为未发布
+ getMapper().updatePublishStatus(otherDefIds, PublishStatus.UNPUBLISHED.getKey());
+ }
+ }
+
+ FlowDefinition flowDefinition = new FlowDefinition();
+ flowDefinition.setId(id);
+ flowDefinition.setIsPublish(PublishStatus.PUBLISHED.getKey());
+ return updateById(flowDefinition);
+ }
+
+ public boolean unPublish(Long id) {
+ List instances = FlowEngine.insService().getByDefId(id);
+ AssertUtil.isNotEmpty(instances, ExceptionCons.EXIST_START_TASK);
+ FlowDefinition definition = new FlowDefinition().setId(id);
+ definition.setIsPublish(PublishStatus.UNPUBLISHED.getKey());
+ return updateById(definition);
+ }
+
+ public boolean copyDef(Long id) {
+ FlowDefinition sourceDef = getById(id);
+ AssertUtil.isNull(sourceDef, ExceptionCons.NOT_FOUNT_DEF);
+ FlowDefinition definition = sourceDef.copy();
+ definition.setVersion(getNewVersion(definition));
+
+ List nodeList = FlowEngine.nodeService().getByDefId(id).stream().map(FlowNode::copy).collect(Collectors.toList());
+ List skipList = FlowEngine.skipService().getByDefId(id).stream().map(FlowSkip::copy).collect(Collectors.toList());
+ FlowEngine.dataFillHandler().idFill(definition);
+
+ nodeList.forEach(node -> node.setDefinitionId(definition.getId()).setVersion(definition.getVersion()));
+ FlowEngine.nodeService().saveBatch(nodeList);
+
+ skipList.forEach(skip -> skip.setDefinitionId(definition.getId()));
+ FlowEngine.skipService().saveBatch(skipList);
+ return save(definition);
+ }
+
+ public boolean active(Long id) {
+ FlowDefinition definition = getById(id);
+ AssertUtil.isNull(definition, ExceptionCons.NOT_FOUNT_DEF);
+ AssertUtil.isTrue(ActivityStatus.isActivity(definition.getActivityStatus()), ExceptionCons.DEFINITION_ALREADY_ACTIVITY);
+ definition.setActivityStatus(ActivityStatus.ACTIVITY.getKey());
+ return updateById(definition);
+ }
+
+ public boolean unActive(Long id) {
+ FlowDefinition definition = getById(id);
+ AssertUtil.isNull(definition, ExceptionCons.NOT_FOUNT_DEF);
+ AssertUtil.isTrue(ActivityStatus.isSuspended(definition.getActivityStatus()), ExceptionCons.DEFINITION_ALREADY_SUSPENDED);
+ definition.setActivityStatus(ActivityStatus.SUSPENDED.getKey());
+ return updateById(definition);
+ }
+
+ public List getByFlowCode(String flowCode) {
+ return list(new FlowDefinition().setFlowCode(flowCode));
+ }
+
+ public FlowDefinition getPublishByFlowCode(String flowCode) {
+ return FlowEngine.defService().getOne(new FlowDefinition()
+ .setFlowCode(flowCode).setIsPublish(PublishStatus.PUBLISHED.getKey()));
+ }
+
+ private String getNewVersion(FlowDefinition definition) {
+ List flowCodeList = Collections.singletonList(definition.getFlowCode());
+ List definitions = getMapper().queryByCodeList(flowCodeList);
+ int highestVersion = 0;
+ String latestNonPositiveVersion = null;
+ long latestTimestamp = Long.MIN_VALUE;
+
+ for (FlowDefinition otherDef : definitions) {
+ if (definition.getFlowCode().equals(otherDef.getFlowCode())) {
+ try {
+ int version = Integer.parseInt(otherDef.getVersion());
+ if (version > highestVersion) {
+ highestVersion = version;
+ }
+ } catch (NumberFormatException e) {
+ long timestamp = otherDef.getCreateTime().getTime();
+ if (timestamp > latestTimestamp) {
+ latestTimestamp = timestamp;
+ latestNonPositiveVersion = otherDef.getVersion();
+ }
+ }
+ }
+ }
+
+ String version = "1";
+ if (highestVersion > 0) {
+ version = String.valueOf(highestVersion + 1);
+ } else if (latestNonPositiveVersion != null) {
+ version = latestNonPositiveVersion + "_1";
+ }
+
+ return version;
+ }
+
+ private void checkFlowLegal(FlowCombine flowCombine) {
+ FlowDefinition definition = flowCombine.getDefinition();
+ String flowName = definition.getFlowName();
+ AssertUtil.isEmpty(definition.getFlowCode(), "【" + flowName + "】流程flowCode为空!");
+ // 节点校验
+ List allNodes = flowCombine.getAllNodes();
+ List allSkips = flowCombine.getAllSkips();
+ Map> skipMap = StreamUtils.groupByKey(allSkips, FlowSkip::getNowNodeCode);
+ allNodes.forEach(node -> {
+ node.setSkipList(skipMap.get(node.getNodeCode()));
+ skipMap.remove(node.getNodeCode());
+ });
+ AssertUtil.isNotEmpty(skipMap, "[" + flowName + "]" + ExceptionCons.FLOW_HAVE_USELESS_SKIP);
+ // 每一个流程的开始节点个数
+ Set nodeCodeSet = new HashSet<>();
+ // 便利一个流程中的各个节点
+ int startNum = 0;
+ for (FlowNode node : allNodes) {
+ FlowConfigUtil.initNodeAndCondition(node, definition.getId(), definition.getVersion());
+ startNum = FlowConfigUtil.checkStartAndSame(node, startNum, flowName, nodeCodeSet);
+ }
+ AssertUtil.isTrue(startNum == 0, "[" + flowName + "]" + ExceptionCons.LOST_START_NODE);
+ // 校验跳转节点的合法性
+ FlowConfigUtil.checkSkipNode(allSkips);
+ // 校验所有目标节点是否都存在
+ FlowConfigUtil.validaIsExistDestNode(allSkips, nodeCodeSet);
+ }
+
+
+ @Override
+ public FlowDefinitionMapper getMapper() {
+ return SpringUtils.getBean(FlowDefinitionMapper.class);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/HisTaskService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/HisTaskService.java
new file mode 100644
index 000000000..e7906b51a
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/HisTaskService.java
@@ -0,0 +1,253 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.service;
+import org.dromara.warm.flow.entity.*;
+
+import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
+import org.dromara.common.core.utils.StreamUtils;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.ArrayUtil;
+
+import org.dromara.warm.flow.FlowEngine;
+import org.dromara.warm.flow.dto.FlowParams;
+import org.dromara.warm.flow.entity.FlowHisTask;
+import org.dromara.warm.flow.entity.FlowNode;
+import org.dromara.warm.flow.entity.FlowTask;
+import org.dromara.warm.flow.entity.FlowUser;
+import org.dromara.warm.flow.enums.CooperateType;
+import org.dromara.warm.flow.enums.FlowStatus;
+import org.dromara.warm.flow.enums.SkipType;
+import org.dromara.warm.flow.service.WarmServiceImpl;
+import org.dromara.warm.flow.utils.*;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+
+
+
+import org.dromara.common.core.utils.SpringUtils;
+import org.springframework.stereotype.Service;
+import org.dromara.warm.flow.mapper.FlowHisTaskMapper;
+
+
+
+
+/**
+ * 历史任务记录Service业务层处理
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+@Service
+public class HisTaskService extends WarmServiceImpl {
+ public List listByTaskId(Long taskId) {
+ return list(new FlowHisTask().setTaskId(taskId));
+ }
+
+ public List listByTaskIdAndCooperateTypes(Long taskId, Integer... cooperateTypes) {
+ if (ArrayUtil.isEmpty(cooperateTypes)) {
+ return listByTaskId(taskId);
+ }
+ if (cooperateTypes.length == 1) {
+ return list(new FlowHisTask().setTaskId(taskId).setCooperateType(cooperateTypes[0]));
+ }
+ return getMapper().listByTaskIdAndCooperateTypes(taskId, cooperateTypes);
+ }
+
+ public List getByInsAndNodeCodes(Long instanceId, List nodeCodes) {
+ return getMapper().getByInsAndNodeCodes(instanceId, nodeCodes);
+ }
+
+ public boolean deleteByInsIds(List instanceIds) {
+ return SqlHelper.retBool(getMapper().deleteByInsIds(instanceIds));
+ }
+
+ public FlowHisTask setSkipInsHis(FlowTask task, List nextNodes, FlowParams flowParams) {
+ String flowStatus = getFlowStatus(flowParams);
+ return setSkipHis(task, nextNodes, flowParams, flowStatus);
+ }
+
+ public List setSkipHisList(List taskList, List nextNodes, FlowParams flowParams) {
+ String flowStatus = getFlowStatus(flowParams);
+ List hisTasks = new ArrayList<>();
+ for (FlowTask task : taskList) {
+ FlowHisTask hisTask = setSkipHis(task, nextNodes, flowParams, flowStatus);
+ hisTasks.add(hisTask);
+ }
+ return hisTasks;
+ }
+
+ public FlowHisTask setSkipHisTask(FlowTask task, FlowNode nextNode, FlowParams flowParams) {
+ String flowStatus = getFlowStatus(flowParams);
+ return setSkipHis(task, CollUtil.toList(nextNode), flowParams, flowStatus);
+ }
+
+
+ public FlowHisTask setCooperateHis(FlowTask task, FlowParams flowParams
+ , List collaborators) {
+ String flowStatus = getFlowStatus(flowParams);
+ FlowHisTask hisTask = new FlowHisTask()
+ .setTaskId(task.getId())
+ .setInstanceId(task.getInstanceId())
+ .setCooperateType(ObjectUtil.defaultIfNull(flowParams.getCooperateType(), CooperateType.APPROVAL.getKey()))
+ .setCollaborator(StreamUtils.join(collaborators, c -> c))
+ .setNodeCode(task.getNodeCode())
+ .setNodeName(task.getNodeName())
+ .setNodeType(task.getNodeType())
+ .setDefinitionId(task.getDefinitionId())
+ .setTargetNodeCode(task.getNodeCode())
+ .setTargetNodeName(task.getNodeName())
+ .setApprover(flowParams.getHandler())
+ .setSkipType(flowParams.getSkipType())
+ .setFlowStatus(StrUtil.emptyToDefault(flowStatus, FlowStatus.APPROVAL.getKey()))
+ .setFormCustom(task.getFormCustom())
+ .setFormPath(task.getFormPath())
+ .setMessage(flowParams.getMessage())
+ .setVariable(flowParams.getVariableStr())
+ //业务详情添加至历史记录
+ .setExt(flowParams.getHisTaskExt())
+ .setCreateTime(task.getCreateTime());
+ FlowEngine.dataFillHandler().idFill(hisTask);
+ return hisTask;
+ }
+
+ public FlowHisTask notSkip(FlowTask task, FlowParams flowParams) {
+ String flowStatus = getFlowStatus(flowParams);
+ FlowHisTask hisTask = new FlowHisTask()
+ .setTaskId(task.getId())
+ .setInstanceId(task.getInstanceId())
+ .setCooperateType(ObjectUtil.defaultIfNull(flowParams.getCooperateType(), CooperateType.APPROVAL.getKey()))
+ .setNodeCode(task.getNodeCode())
+ .setNodeName(task.getNodeName())
+ .setNodeType(task.getNodeType())
+ .setDefinitionId(task.getDefinitionId())
+ .setTargetNodeCode(task.getNodeCode())
+ .setTargetNodeName(task.getNodeName())
+ .setApprover(flowParams.getHandler())
+ .setSkipType(SkipType.NONE.getKey())
+ .setFlowStatus(flowStatus)
+ .setFormCustom(task.getFormCustom())
+ .setFormPath(task.getFormPath())
+ .setMessage(flowParams.getMessage())
+ .setVariable(flowParams.getVariableStr())
+ //业务详情添加至历史记录
+ .setExt(flowParams.getHisTaskExt())
+ .setCreateTime(task.getCreateTime());
+ FlowEngine.dataFillHandler().idFill(hisTask);
+ return hisTask;
+ }
+
+ public FlowHisTask setDeputeHisTask(FlowTask task, FlowParams flowParams, FlowUser entrustedUser) {
+ String flowStatus = getFlowStatus(flowParams);
+ FlowHisTask hisTask = new FlowHisTask()
+ .setTaskId(task.getId())
+ .setInstanceId(task.getInstanceId())
+ .setCooperateType(CooperateType.DEPUTE.getKey())
+ .setNodeCode(task.getNodeCode())
+ .setNodeName(task.getNodeName())
+ .setNodeType(task.getNodeType())
+ .setDefinitionId(task.getDefinitionId())
+ .setTargetNodeCode(task.getNodeCode())
+ .setTargetNodeName(task.getNodeName())
+ .setApprover(flowParams.getHandler())
+ .setCollaborator(entrustedUser.getCreateBy())
+ .setSkipType(flowParams.getSkipType())
+ .setFlowStatus(StrUtil.isNotEmpty(flowStatus)
+ ? flowStatus : SkipType.isReject(flowParams.getSkipType())
+ ? FlowStatus.REJECT.getKey() : FlowStatus.PASS.getKey())
+ .setFormCustom(task.getFormCustom())
+ .setFormPath(task.getFormPath())
+ .setMessage(flowParams.getMessage())
+ .setVariable(flowParams.getVariableStr())
+ //业务详情添加至历史记录
+ .setExt(flowParams.getHisTaskExt())
+ .setCreateTime(task.getCreateTime());
+ FlowEngine.dataFillHandler().idFill(hisTask);
+ return hisTask;
+ }
+
+ public FlowHisTask setSignHisTask(FlowTask task, FlowParams flowParams, String nodeRatio, boolean isPass) {
+ String flowStatus = getFlowStatus(flowParams);
+ FlowHisTask hisTask = new FlowHisTask()
+ .setTaskId(task.getId())
+ .setInstanceId(task.getInstanceId())
+ .setCooperateType(CooperateType.isCountersign(nodeRatio)
+ ? CooperateType.COUNTERSIGN.getKey() : CooperateType.VOTE.getKey())
+ .setNodeCode(task.getNodeCode())
+ .setNodeName(task.getNodeName())
+ .setNodeType(task.getNodeType())
+ .setDefinitionId(task.getDefinitionId())
+ .setApprover(flowParams.getHandler())
+ .setSkipType(isPass ? SkipType.PASS.getKey() : SkipType.REJECT.getKey())
+ .setFlowStatus(StrUtil.isNotEmpty(flowStatus)
+ ? flowStatus : isPass
+ ? FlowStatus.PASS.getKey() : FlowStatus.REJECT.getKey())
+ .setFormCustom(task.getFormCustom())
+ .setFormPath(task.getFormPath())
+ .setMessage(flowParams.getMessage())
+ .setVariable(flowParams.getVariableStr())
+ //业务详情添加至历史记录
+ .setExt(flowParams.getHisTaskExt())
+ .setCreateTime(task.getCreateTime());
+ FlowEngine.dataFillHandler().idFill(hisTask);
+ return hisTask;
+ }
+
+ public List getByInsId(Long instanceId) {
+ return FlowEngine.hisTaskService().list(new FlowHisTask().setInstanceId(instanceId));
+ }
+
+ private FlowHisTask setSkipHis(FlowTask task, List nextNodes, FlowParams flowParams, String flowStatus) {
+ FlowHisTask hisTask = new FlowHisTask()
+ .setTaskId(task.getId())
+ .setInstanceId(task.getInstanceId())
+ .setCooperateType(ObjectUtil.defaultIfNull(flowParams.getCooperateType(), CooperateType.APPROVAL.getKey()))
+ .setNodeCode(task.getNodeCode())
+ .setNodeName(task.getNodeName())
+ .setNodeType(task.getNodeType())
+ .setDefinitionId(task.getDefinitionId())
+ .setTargetNodeCode(StreamUtils.join(nextNodes, FlowNode::getNodeCode))
+ .setTargetNodeName(StreamUtils.join(nextNodes, FlowNode::getNodeName))
+ .setApprover(flowParams.getHandler())
+ .setSkipType(flowParams.getSkipType())
+ .setFlowStatus(StrUtil.isNotEmpty(flowStatus)
+ ? flowStatus : SkipType.isReject(flowParams.getSkipType())
+ ? FlowStatus.REJECT.getKey() : FlowStatus.PASS.getKey())
+ .setFormCustom(task.getFormCustom())
+ .setFormPath(task.getFormPath())
+ .setMessage(flowParams.getMessage())
+ .setVariable(flowParams.getVariableStr())
+ //业务详情添加至历史记录
+ .setExt(flowParams.getHisTaskExt())
+ .setCreateTime(task.getCreateTime());
+ FlowEngine.dataFillHandler().idFill(hisTask);
+ return hisTask;
+ }
+
+ private String getFlowStatus(FlowParams flowParams) {
+ return StrUtil.emptyToDefault(flowParams.getHisStatus(), flowParams.getFlowStatus());
+ }
+
+ @Override
+ public FlowHisTaskMapper getMapper() {
+ return SpringUtils.getBean(FlowHisTaskMapper.class);
+ }
+}
\ No newline at end of file
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/IWarmService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/IWarmService.java
new file mode 100644
index 000000000..001aee51b
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/IWarmService.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.service;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Service接口
+ *
+ * @author warm
+ * @since 2023-03-17
+ */
+public interface IWarmService {
+ /**
+ * 根据id查询
+ *
+ * @param id 主键
+ * @return 实体
+ */
+ T getById(Serializable id);
+
+ /**
+ * 根据ids查询
+ *
+ * @param ids 主键
+ * @return 实体
+ */
+ List getByIds(Collection extends Serializable> ids);
+
+ /**
+ * 查询列表
+ *
+ * @param entity 查询实体
+ * @return 集合
+ */
+ List list(T entity);
+
+ /**
+ * 查询一条记录
+ *
+ * @param entity 查询实体
+ * @return 结果
+ */
+ T getOne(T entity);
+
+ /**
+ * 判断是否存在
+ *
+ * @param entity 查询实体
+ * @return 结果
+ */
+ Boolean exists(T entity);
+
+ /**
+ * 新增
+ *
+ * @param entity 实体
+ * @return 结果
+ */
+ boolean save(T entity);
+
+ /**
+ * 根据id修改
+ *
+ * @param entity 实体
+ * @return 结果
+ */
+ boolean updateById(T entity);
+
+ /**
+ * 根据id删除
+ *
+ * @param id 主键
+ * @return 结果
+ */
+ boolean removeById(Serializable id);
+
+ /**
+ * 根据entity删除
+ *
+ * @param entity 实体
+ * @return 结果
+ */
+ boolean remove(T entity);
+
+ /**
+ * 根据ids批量删除
+ *
+ * @param ids 需要删除的数据主键集合
+ * @return 结果
+ */
+ boolean removeByIds(Collection extends Serializable> ids);
+
+ /**
+ * 批量新增
+ *
+ * @param list 实体集合
+ */
+ void saveBatch(List list);
+}
diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/InsService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/InsService.java
new file mode 100644
index 000000000..d16f09dcb
--- /dev/null
+++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/warm/flow/service/InsService.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2024-2025, Warm-Flow (290631660@qq.com).
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.dromara.warm.flow.service;
+import org.dromara.warm.flow.json.JsonUtil;
+
+import org.dromara.warm.flow.entity.*;
+
+import org.dromara.common.core.utils.StreamUtils;
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+
+import org.dromara.warm.flow.FlowEngine;
+import org.dromara.warm.flow.constant.ExceptionCons;
+import org.dromara.warm.flow.dto.FlowCombine;
+import org.dromara.warm.flow.dto.FlowParams;
+import org.dromara.warm.flow.dto.PathWayData;
+import org.dromara.warm.flow.enums.ActivityStatus;
+import org.dromara.warm.flow.enums.FlowStatus;
+import org.dromara.warm.flow.enums.NodeType;
+import org.dromara.warm.flow.enums.SkipType;
+import org.dromara.warm.flow.listener.ListenerVariable;
+import org.dromara.warm.flow.service.WarmServiceImpl;
+import org.dromara.warm.flow.utils.*;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+
+import org.dromara.warm.flow.entity.FlowInstance;
+
+import org.dromara.common.core.utils.SpringUtils;
+import org.springframework.stereotype.Service;
+import org.dromara.warm.flow.mapper.FlowInstanceMapper;
+
+
+
+/**
+ * 流程实例Service业务层处理
+ *
+ * @author warm
+ * @since 2023-03-29
+ */
+@Service
+public class InsService extends WarmServiceImpl {
+ public FlowInstance start(String businessId, FlowParams flowParams) {
+ AssertUtil.isNull(flowParams.getFlowCode(), ExceptionCons.NULL_FLOW_CODE);
+ AssertUtil.isEmpty(businessId, ExceptionCons.NULL_BUSINESS_ID);
+ // 获取已发布的流程节点
+ FlowDefinition definition = FlowEngine.defService().getPublishByFlowCode(flowParams.getFlowCode());
+ AssertUtil.isNull(definition, ExceptionCons.NOT_FOUNT_DEF);
+ FlowCombine flowCombine = FlowEngine.defService().getFlowCombine(definition);
+ // 获取开始节点
+ FlowNode startNode = StreamUtils.findFirstValue(flowCombine.getAllNodes(), t -> NodeType.isStart(t.getNodeType()));
+ AssertUtil.isNull(startNode, ExceptionCons.LOST_START_NODE);
+
+ // 判断流程定义是否激活状态
+ AssertUtil.isTrue(definition.getActivityStatus().equals(ActivityStatus.SUSPENDED.getKey())
+ , ExceptionCons.NOT_DEFINITION_ACTIVITY);
+ flowParams.skipType(SkipType.PASS.getKey());
+
+ // 执行开始监听器
+ ListenerUtil.executeStart(new ListenerVariable(definition, null, startNode, flowParams.getVariable())
+ .setFlowParams(flowParams));
+
+
+ // 获取下一个节点,如果是网关节点,则重新获取后续节点
+ PathWayData pathWayData = new PathWayData().setDefId(startNode.getDefinitionId()).setSkipType(flowParams.getSkipType());
+ List nextNodes = FlowEngine.nodeService().getNextNodeList(startNode, null, flowParams.getSkipType(),
+ flowParams.getVariable(), pathWayData, flowCombine);
+
+ // 设置流程实例对象
+ FlowInstance instance = setStartInstance(nextNodes.get(0), businessId, flowParams);
+
+ // 设置历史任务
+ FlowHisTask hisTask = setHisTask(nextNodes, flowParams, startNode, instance.getId());
+
+ List addTasks = StreamUtils.toList(nextNodes, node -> FlowEngine.taskService()
+ .addTask(node, instance, definition, flowParams));
+
+ // 办理人变量替换
+ if (CollUtil.isNotEmpty(addTasks)) {
+ ExpressionUtil.evalVariable(addTasks, flowParams);
+ }
+
+ // 设置流程图元数据
+ pathWayData.getTargetNodes().addAll(nextNodes);
+ instance.setDefJson(FlowEngine.chartService().startMetadata(pathWayData));
+
+ // 执行分派监听器
+ ListenerUtil.executeAssignment(new ListenerVariable(definition, instance, startNode, flowParams.getVariable()
+ , null, nextNodes, addTasks).setFlowParams(flowParams));
+
+ // 开启流程,保存流程信息
+ saveFlowInfo(instance, addTasks, hisTask, flowParams);
+
+ // 执行完成和创建监听器
+ ListenerUtil.endCreateListener(new ListenerVariable(definition, instance, startNode, flowParams.getVariable()
+ , null, nextNodes, addTasks).setFlowParams(flowParams));
+
+ return instance;
+ }
+
+ public List listByDefIds(List defIds) {
+ return getMapper().getByDefIds(defIds);
+ }
+
+ public boolean remove(List instanceIds) {
+ return toRemoveTask(instanceIds);
+ }
+
+ public List getByDefId(Long definitionId) {
+ return list(new FlowInstance().setDefinitionId(definitionId));
+ }
+
+ /**
+ * 设置历史任务
+ *
+ * @param nextNodes 下一节点集合
+ * @param flowParams 流程参数
+ * @param startNode 开始节点
+ * @param instanceId 流程实例id
+ */
+ private FlowHisTask setHisTask(List nextNodes, FlowParams flowParams, FlowNode startNode, Long instanceId) {
+ FlowTask startTask = new FlowTask()
+ .setInstanceId(instanceId)
+ .setDefinitionId(startNode.getDefinitionId())
+ .setNodeCode(startNode.getNodeCode())
+ .setNodeName(startNode.getNodeName())
+ .setNodeType(startNode.getNodeType());
+ FlowEngine.dataFillHandler().idFill(startTask);
+ // 开始任务转历史任务
+ return FlowEngine.hisTaskService().setSkipInsHis(startTask, nextNodes, flowParams);
+ }
+
+ /**
+ * 开启流程,保存流程信息
+ *
+ * @param instance 流程实例
+ * @param addTasks 新增任务
+ * @param hisTask 历史任务
+ */
+ private void saveFlowInfo(FlowInstance instance, List addTasks, FlowHisTask hisTask, FlowParams flowParams) {
+ FlowEngine.taskService().setInsFinishInfo(instance, addTasks, flowParams);
+ FlowEngine.hisTaskService().save(hisTask);
+ // 待办任务设置处理人
+ if (CollUtil.isNotEmpty(addTasks)) {
+ List users = FlowEngine.userService().taskAddUsers(addTasks);
+ FlowEngine.taskService().saveBatch(addTasks);
+ FlowEngine.userService().saveBatch(users);
+ }
+ save(instance);
+ }
+
+ /**
+ * 设置流程实例对象
+ *
+ * @param firstBetweenNode 第一个中间节点
+ * @param businessId 业务id
+ * @return FlowInstance
+ */
+ private FlowInstance setStartInstance(FlowNode firstBetweenNode, String businessId
+ , FlowParams flowParams) {
+ FlowInstance instance = new FlowInstance();
+ Date now = new Date();
+ FlowEngine.dataFillHandler().idFill(instance);
+ // 关联业务id,其实后面可以不用到业务id,传业务id目前来看只是为了批量创建流程的时候能创建出有区别化的流程,也是为了后期需要用到businessId。
+ instance.setDefinitionId(firstBetweenNode.getDefinitionId())
+ .setBusinessId(businessId)
+ .setNodeType(firstBetweenNode.getNodeType())
+ .setNodeCode(firstBetweenNode.getNodeCode())
+ .setNodeName(firstBetweenNode.getNodeName())
+ .setFlowStatus(StrUtil.emptyToDefault(flowParams.getFlowStatus(), FlowStatus.TOBESUBMIT.getKey()))
+ .setActivityStatus(ActivityStatus.ACTIVITY.getKey())
+ .setVariable(JsonUtil.objToStr(flowParams.getVariable()))
+ .setCreateTime(now)
+ .setUpdateTime(now)
+ .setCreateBy(flowParams.getHandler())
+ .setUpdateBy(flowParams.getHandler())
+ .setExt(flowParams.getExt());
+ return instance;
+ }
+
+ private boolean toRemoveTask(List