From 992adc8589cb36b65ffff60a7f9c4d4f67149433 Mon Sep 17 00:00:00 2001 From: ZETA <14016570+zeta_choi@user.noreply.gitee.com> Date: Mon, 3 Jun 2024 07:11:46 +0000 Subject: [PATCH] =?UTF-8?q?!164=20=E4=BB=8Evue=E7=89=88=E6=9C=AC=E8=BF=81?= =?UTF-8?q?=E7=A7=BBflowable=E5=88=B0cloud=E7=89=88=E6=9C=AC=20*=20update?= =?UTF-8?q?=20=E6=8C=89=E7=85=A7vue=E7=89=88=E9=80=BB=E8=BE=91=E6=94=B9?= =?UTF-8?q?=E9=80=A0=E5=B7=A5=E4=BD=9C=E6=B5=81=20*=20update=20=E5=8E=BB?= =?UTF-8?q?=E9=99=A4=E6=B3=9B=E5=8C=96=E8=B0=83=E7=94=A8=E9=80=BB=E8=BE=91?= =?UTF-8?q?=20*=20update=20=E6=9B=B4=E6=94=B9=20=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81=20=E6=B5=8B=E8=AF=95=E7=94=A8=E4=BE=8B=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E8=B7=AF=E5=BE=84=20*=20fix=20=E5=8E=BB=E9=99=A4?= =?UTF-8?q?=E6=97=A0=E7=94=A8=E4=BF=AE=E6=94=B9=EF=BC=8C=E8=BF=98=E5=8E=9F?= =?UTF-8?q?demo=EF=BC=8CtestLeave=E6=8C=AA=E5=85=A5=E5=B7=A5=E4=BD=9C?= =?UTF-8?q?=E6=B5=81=E6=A8=A1=E5=9D=97=20*=20fix=20=E6=B8=85=E7=90=86?= =?UTF-8?q?=E6=97=A0=E6=95=88=E4=BB=A3=E7=A0=81=EF=BC=8C=E8=BF=98=E5=8E=9F?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=20*=20update=20=E4=BF=AE=E6=94=B9=E7=9B=91?= =?UTF-8?q?=E5=90=AC=E5=99=A8=E5=B1=9E=E6=80=A7=E5=90=8D=20*=20update=20?= =?UTF-8?q?=E9=9B=86=E6=88=90=E5=B7=A5=E4=BD=9C=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bpmn/模型.zip | Bin 0 -> 8407 bytes config/nacos/datasource.yml | 4 + config/nacos/ruoyi-gateway.yml | 6 + config/nacos/ruoyi-workflow.yml | 44 + pom.xml | 26 + ruoyi-api/pom.xml | 1 + ruoyi-api/ruoyi-api-bom/pom.xml | 7 + .../resource/api/RemoteFileService.java | 9 + .../resource/api/RemoteFileServiceMock.java | 8 + .../resource/api/domain/RemoteFile.java | 10 + .../dromara/system/api/RemoteUserService.java | 18 + .../system/api/domain/dto/UserDTO.java | 73 ++ ruoyi-api/ruoyi-api-workflow/pom.xml | 33 + .../api/domain/RemoteWorkflowService.java | 86 ++ .../api/domain/bo/ProcessInstanceBo.java | 43 + .../api/domain/bo/ProcessInvalidBo.java | 31 + .../workflow/api/domain/bo/TaskUrgingBo.java | 34 + .../api/domain/dto/BusinessInstanceDTO.java | 72 ++ .../api/domain/event/ProcessEvent.java | 47 + .../api/domain/event/ProcessTaskEvent.java | 41 + .../common/mybatis/core/page/PageQuery.java | 15 +- .../core/impl/NicknameTranslationImpl.java | 2 +- ruoyi-example/ruoyi-demo/pom.xml | 1 + ruoyi-modules/pom.xml | 1 + .../resource/dubbo/RemoteFileServiceImpl.java | 20 +- .../system/dubbo/RemoteUserServiceImpl.java | 12 + .../system/service/ISysUserService.java | 7 + .../service/impl/SysUserServiceImpl.java | 7 + ruoyi-modules/ruoyi-workflow/pom.xml | 138 ++ .../workflow/RuoYiWorkflowApplication.java | 22 + .../common/constant/FlowConstant.java | 137 ++ .../common/enums/BusinessStatusEnum.java | 152 +++ .../workflow/common/enums/FormTypeEnum.java | 54 + .../common/enums/MessageTypeEnum.java | 51 + .../workflow/common/enums/TaskStatusEnum.java | 94 ++ .../controller/ActModelController.java | 154 +++ .../ActProcessDefinitionController.java | 147 +++ .../ActProcessInstanceController.java | 160 +++ .../controller/ActTaskController.java | 295 +++++ .../controller/WfCategoryController.java | 106 ++ .../WfDefinitionConfigController.java | 79 ++ .../controller/WfFormManageController.java | 114 ++ .../workflow/domain/ActHiProcinst.java | 152 +++ .../workflow/domain/ActHiTaskinst.java | 193 +++ .../dromara/workflow/domain/WfCategory.java | 52 + .../workflow/domain/WfDefinitionConfig.java | 56 + .../dromara/workflow/domain/WfFormManage.java | 51 + .../dromara/workflow/domain/WfNodeConfig.java | 61 + .../workflow/domain/WfTaskBackNode.java | 61 + .../workflow/domain/bo/AddMultiBo.java | 40 + .../workflow/domain/bo/BackProcessBo.java | 44 + .../workflow/domain/bo/CompleteTaskBo.java | 65 + .../workflow/domain/bo/DelegateBo.java | 38 + .../workflow/domain/bo/DeleteMultiBo.java | 52 + .../dromara/workflow/domain/bo/ModelBo.java | 66 + .../domain/bo/ProcessDefinitionBo.java | 34 + .../workflow/domain/bo/StartProcessBo.java | 49 + .../workflow/domain/bo/SysUserMultiBo.java | 39 + .../dromara/workflow/domain/bo/TaskBo.java | 33 + .../workflow/domain/bo/TerminationBo.java | 31 + .../workflow/domain/bo/TransmitBo.java | 37 + .../workflow/domain/bo/WfCategoryBo.java | 54 + .../domain/bo/WfDefinitionConfigBo.java | 59 + .../workflow/domain/bo/WfFormManageBo.java | 53 + .../workflow/domain/bo/WfNodeConfigBo.java | 63 + .../workflow/domain/vo/ActHistoryInfoVo.java | 89 ++ .../workflow/domain/vo/GraphicInfoVo.java | 47 + .../dromara/workflow/domain/vo/ModelVo.java | 48 + .../workflow/domain/vo/MultiInstanceVo.java | 33 + .../workflow/domain/vo/ParticipantVo.java | 43 + .../domain/vo/ProcessDefinitionVo.java | 70 ++ .../workflow/domain/vo/ProcessInstanceVo.java | 100 ++ .../dromara/workflow/domain/vo/TaskVo.java | 173 +++ .../workflow/domain/vo/VariableVo.java | 28 + .../workflow/domain/vo/WfCategoryVo.java | 58 + .../dromara/workflow/domain/vo/WfCopy.java | 29 + .../domain/vo/WfDefinitionConfigVo.java | 70 ++ .../workflow/domain/vo/WfFormManageVo.java | 63 + .../workflow/domain/vo/WfNodeConfigVo.java | 75 ++ .../dubbo/RemoteWorkflowServiceImpl.java | 92 ++ .../CustomDefaultProcessDiagramCanvas.java | 108 ++ .../CustomDefaultProcessDiagramGenerator.java | 1120 +++++++++++++++++ .../cmd/AddSequenceMultiInstanceCmd.java | 61 + .../workflow/flowable/cmd/AttachmentCmd.java | 64 + .../flowable/cmd/DeleteExecutionCmd.java | 36 + .../cmd/DeleteSequenceMultiInstanceCmd.java | 82 ++ .../cmd/ExecutionChildByExecutionIdCmd.java | 39 + .../flowable/cmd/UpdateBusinessStatusCmd.java | 37 + .../flowable/cmd/UpdateHiTaskInstCmd.java | 51 + .../flowable/config/FlowableConfig.java | 32 + .../config/GlobalFlowableListener.java | 139 ++ .../handler/FlowProcessEventHandler.java | 51 + .../handler/TaskTimeoutJobHandler.java | 38 + .../workflow/mapper/ActHiProcinstMapper.java | 16 + .../workflow/mapper/ActHiTaskinstMapper.java | 16 + .../workflow/mapper/ActTaskMapper.java | 47 + .../workflow/mapper/WfCategoryMapper.java | 15 + .../mapper/WfDefinitionConfigMapper.java | 15 + .../workflow/mapper/WfFormManageMapper.java | 15 + .../workflow/mapper/WfNodeConfigMapper.java | 15 + .../workflow/mapper/WfTaskBackNodeMapper.java | 13 + .../service/IActHiProcinstService.java | 31 + .../service/IActHiTaskinstService.java | 11 + .../workflow/service/IActModelService.java | 83 ++ .../service/IActProcessDefinitionService.java | 91 ++ .../service/IActProcessInstanceService.java | 110 ++ .../workflow/service/IActTaskService.java | 161 +++ .../workflow/service/IWfCategoryService.java | 51 + .../service/IWfDefinitionConfigService.java | 83 ++ .../service/IWfFormManageService.java | 81 ++ .../service/IWfNodeConfigService.java | 56 + .../service/IWfTaskBackNodeService.java | 65 + .../workflow/service/WorkflowService.java | 85 ++ .../impl/ActHiProcinstServiceImpl.java | 51 + .../impl/ActHiTaskinstServiceImpl.java | 18 + .../service/impl/ActModelServiceImpl.java | 424 +++++++ .../impl/ActProcessDefinitionServiceImpl.java | 438 +++++++ .../impl/ActProcessInstanceServiceImpl.java | 662 ++++++++++ .../service/impl/ActTaskServiceImpl.java | 857 +++++++++++++ .../service/impl/WfCategoryServiceImpl.java | 129 ++ .../impl/WfDefinitionConfigServiceImpl.java | 117 ++ .../service/impl/WfFormManageServiceImpl.java | 111 ++ .../service/impl/WfNodeConfigServiceImpl.java | 75 ++ .../impl/WfTaskBackNodeServiceImpl.java | 141 +++ .../service/impl/WorkflowServiceImpl.java | 124 ++ .../controller/TestLeaveController.java | 106 ++ .../workflow/testleave/domain/TestLeave.java | 62 + .../testleave/domain/bo/TestLeaveBo.java | 80 ++ .../testleave/domain/vo/TestLeaveVo.java | 77 ++ .../testleave/mapper/TestLeaveMapper.java | 15 + .../testleave/service/ITestLeaveService.java | 48 + .../service/impl/TestLeaveServiceImpl.java | 170 +++ .../dromara/workflow/utils/ModelUtils.java | 289 +++++ .../dromara/workflow/utils/QueryUtils.java | 169 +++ .../dromara/workflow/utils/WorkflowUtils.java | 366 ++++++ .../src/main/resources/application.yml | 34 + .../src/main/resources/banner.txt | 10 + .../src/main/resources/logback-plus.xml | 28 + .../main/resources/mapper/TestLeaveMapper.xml | 7 + .../src/main/resources/mapper/package-info.md | 3 + .../mapper/workflow/ActHiProcinstMapper.xml | 7 + .../mapper/workflow/ActHiTaskinstMapper.xml | 7 + .../mapper/workflow/ActTaskMapper.xml | 78 ++ .../mapper/workflow/WfCategoryMapper.xml | 7 + .../workflow/WfDefinitionConfigMapper.xml | 7 + .../mapper/workflow/WfFormManageMapper.xml | 7 + .../mapper/workflow/WfNodeConfigMapper.xml | 7 + .../mapper/workflow/WfTaskBackNodeMapper.xml | 7 + sql/oracle/oracle_ry_workflow.sql | 261 ++++ sql/postgres/postgres_ry_workflow.sql | 344 +++++ sql/ry-workflow.sql | 176 +++ 151 files changed, 13196 insertions(+), 3 deletions(-) create mode 100644 bpmn/模型.zip create mode 100644 config/nacos/ruoyi-workflow.yml create mode 100644 ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/dto/UserDTO.java create mode 100644 ruoyi-api/ruoyi-api-workflow/pom.xml create mode 100644 ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteWorkflowService.java create mode 100644 ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/ProcessInstanceBo.java create mode 100644 ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/ProcessInvalidBo.java create mode 100644 ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/TaskUrgingBo.java create mode 100644 ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/dto/BusinessInstanceDTO.java create mode 100644 ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessEvent.java create mode 100644 ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessTaskEvent.java create mode 100644 ruoyi-modules/ruoyi-workflow/pom.xml create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/RuoYiWorkflowApplication.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfCategoryController.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfDefinitionConfigController.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfFormManageController.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiProcinst.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiTaskinst.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfCategory.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfDefinitionConfig.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfFormManage.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfNodeConfig.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/AddMultiBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DelegateBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DeleteMultiBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ModelBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessDefinitionBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/SysUserMultiBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TerminationBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TransmitBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfCategoryBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfDefinitionConfigBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfFormManageBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfNodeConfigBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ActHistoryInfoVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GraphicInfoVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ModelVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/MultiInstanceVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ParticipantVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessDefinitionVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessInstanceVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TaskVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/VariableVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCategoryVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCopy.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfDefinitionConfigVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfFormManageVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfNodeConfigVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/dubbo/RemoteWorkflowServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramCanvas.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramGenerator.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AddSequenceMultiInstanceCmd.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AttachmentCmd.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteExecutionCmd.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteSequenceMultiInstanceCmd.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/ExecutionChildByExecutionIdCmd.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateBusinessStatusCmd.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateHiTaskInstCmd.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/FlowableConfig.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/GlobalFlowableListener.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/FlowProcessEventHandler.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/TaskTimeoutJobHandler.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiProcinstMapper.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiTaskinstMapper.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActTaskMapper.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfCategoryMapper.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfDefinitionConfigMapper.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfFormManageMapper.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfNodeConfigMapper.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiProcinstService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiTaskinstService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActModelService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessDefinitionService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessInstanceService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfCategoryService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfDefinitionConfigService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfFormManageService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfNodeConfigService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/WorkflowService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiProcinstServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiTaskinstServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActModelServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessDefinitionServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfCategoryServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfDefinitionConfigServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfFormManageServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfNodeConfigServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/controller/TestLeaveController.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/TestLeave.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/bo/TestLeaveBo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/vo/TestLeaveVo.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/mapper/TestLeaveMapper.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/service/ITestLeaveService.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/service/impl/TestLeaveServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/QueryUtils.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/WorkflowUtils.java create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/application.yml create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/banner.txt create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/logback-plus.xml create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/TestLeaveMapper.xml create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiProcinstMapper.xml create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiTaskinstMapper.xml create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfCategoryMapper.xml create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfDefinitionConfigMapper.xml create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfFormManageMapper.xml create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfNodeConfigMapper.xml create mode 100644 ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml create mode 100644 sql/oracle/oracle_ry_workflow.sql create mode 100644 sql/postgres/postgres_ry_workflow.sql create mode 100644 sql/ry-workflow.sql diff --git a/bpmn/模型.zip b/bpmn/模型.zip new file mode 100644 index 0000000000000000000000000000000000000000..f232cda1c6a0795d36c4e5fecf4d9e82d1652181 GIT binary patch literal 8407 zcmb7}Wl&t}x~&@;x8Ux9;I6@gySoH;cTI42C%7cITX1*xMuG&l#$7M#+;h)bd+oJP z)ty~6tG-#)<45pt{|sF?aSp3V~OdITRN3UL;&Ce>S>g7;ZbuzYnv+Ga1r@6kM^a-A9t8s znt$zxY$ucw*7Ivon45Llo87EvH93C~z;jmFxKA;5_q5d5{%CL%W4L6N+Uafns-4)m zwsL@LV6lWPDlQPpXVWA@S; zXL~mLQGLK(z1OamTDnIg6!zyeTABrjekC-$0(j_pEN|1h?9%-rcNiLllm%WmEy@&# z#;MEfqtf*RIYE&Cdt-$yHlm0Wx|$<0LyXM}iDEw;OX_dT3v*d%4ZH{Ln%COzf7KCqx*4x} zr?IE7t|y@N_0{A4^0Z7an~9{;%l)3{M*O8%o%mMV6A8V(0Ojsb zH+=3v?#&_iy(UoVBd~h<81DRtx;(Cr(jJn;!=3#Vlj$-XrJ@8H2gq{fdRi}zqtosY zZ18&jz%i+`0M6Tz6M#jGLe}=uk*?M(o~QDpKevY24PkdPZ{RaSh%bc6gtlPwpMo!t zuj+B`!0_O?Lr1F9ilhzK-_t_a8FuiW#>IDJ{VbsWe64x>R3v+!E zd!m-n>7(L^sw?F!e(fo6v_J8Zl%8fn$#Su6a!`W*Es#KBo9VOVP2v-M7%B`lY6n{* z<~&XXkvLyG$0(xGtM_5PyFghKCvU$43GpSzs^!+QGoGy5IJ-dMm$F;-wKnaatIL@( zM3&@Jh|=IPh>dE`DIW@#d%CIl%$!Btxmeqgj}!3x*Bt9sx75+>7@@J7I|Gbd)`gY zEun;tcKrC3&DkxgJQ=81eSn+9+R3qCgG#VfG?auG9p*FGs~*LN_4`KBIT7y8GL9IH z)sdBC%3=8?Z!eR4CtM`d65jmx4Kh_VGJBW1XLE?#d;D+{wC5}>7(&;3eDq$l>M^va zuZT3Sm`y-1Zd9T|aCe51gIG-9s1v%ba&0%X`qdatX2^w2MUEh_S6fE?B#3rcNe-I@ zxrAnbeaa*I%tD=l##Pu}jzYPem0T1@Cajo2SoGyI3^kmNOjxWsNK)mQM3sVvYJ|3y zlQcRi(B-zfv=3C9=O{FG#_U%ivgJ@X`QGgC4Khk1|?<7lHL zkG`wtyIRqLO7DpMq#ukvTT4+2f#f?NbCGx2fvaq?ajjmvp9(t*(ekO`S|jD%;o5q(1(L24{cw)7A) z%d+kn!Rvx)sXW5>5&*myHtUw3C}nm@t`k2=G))ze)N zI=D_&>pFJ{Hu*k^r*<}X&T@4vIx&Pr38Q#6{r1Zd+P{S|tkFF7ifKNAE<}BJL-#7b z0NQ_}du#F?O6=WY(NCm`%=P@>RTHJ8Ag0*ZlLL$m3od%Dp!71}A;$#~s5;%w!y)v6 z?7fJ2D-oedrhpV@UE3VhvM1;w2=xzin`|t7CN-!qDyji9)W;*EOSJg8NB$}{#fl}& z3qHm{HzjKnS{W0`}`39kJV9HveGCek}Yf%`bmYKncWMiiV7K=)lD!v1! zNGQChJg%!7PBvn@R&F3u7k8C*rc|4*35{yxZ(so%Cpyb#KAqR6Col-d-nV6FKhJuf z`wiUh6j5E`e#B~*kQ>1J6L18I5O7OA#o1}8mVIet???xgQcC;(9-FY5r!AuJudJC{ zNt!tdO+6YV0V#A=Y}O$Yu=pi>)aEqUvRFItc;mLyu3M5+LhfkU{On*bl%=7?H%5St za=hwzmZuQ|)`4n}G@Mll^9Z*9R5L5%f^}hf7Q$Meok88JC;+4NHN_&Vwk~O#aPV~YHmiRgnlE106kocvdY#aDa?LFBsiecjAo zi0ni+!nhtPbM1*!O6moM_3on?%`oY?ux-DDucOS+wM2b!QXr^+9l_MxLzI*3{%XeC*h&iJm2T7<18yT6;qizFbjI5c$`B@1TI=AH z1#+N+9p1Wf{`D`~=KbT-tKc!;ja&B@h=1kp`U$VA5flJ0ga!am{kwZ^{XJOn|C}?& zzpz)%euWdaV^0IFr!rdLDH*phvO(V8Kwh05T%GYrCTdKSoG89A5gE8Vx||Q)(ktHz zuGb5JAh?-_dE}5<)p_lqR-TifAO4J;+x~6t@w{zPq{HX;1bDU!WXAkGY@^c7HoXO= zMnpHLz4GO(yIn`;0lX$Y7#s@sejvJdfS(zBXgu)2wl{5ZFN5NYQ)(a21(IlM;<^1x zFDJvS&7!OE>FmpdP^th}CBo_JWx7M1+$?LyA^{<{F|MBuoC-`eFd1ZlZh%vfQQcYg z)D(Y|)O!7hSJW!SDyp1AeffbL2CJ)YCJZS|L0H6rG7IGkE9`rc4>49ECXv6AOx1`! z3boRhpIA+}MEg$#+K@oS$@>AF89%Zo#&O3fGZwF{Ox4S`EuboFi7*Eu>-F-OPw`*hj!R!OruaUg0>77EVsmt?RCTu8wpKNo! zYEO99U#8JgcxX$Kz{pm~m*rB({Wng@;v9GfF$yFB6erf8cLZ} zGWUwEm7Bc;S>w#jBd|Xa3k5`4&*g|wS(|xb$Q*Ey&~}v1F2&zLNg+XWmen~RunFBG zU*`3SDn$69G^y;eT^V-q3&N5mfEa6~XtY(3wT$JXO_vclluR_Njch^jk&aFTFE3lK zmibMe^TL-*@{Bnu3#2GtZ=w-N5oytc9Cay#dX5nwBv;R4`6;_HB&u+#^cg#z3*(+; z?fr_7HupdMiZ@kGHkV2aI#Rpcw(yaqKTZGgenMGRwMUFhDFQ+av(Ik1roSpLI894% ziILw8!K@GT!B* zKo>I$Hl1aun0U?NFkRYNe7FNMOd@-LCUPAS9}d;6zXG!Dh_KUGtTs9i57l(sOovF} z;!dKP#?K?4(hVkT-*s2p7+ubjLyi|`qU*wFDn~`Z3QWfb550B*s+3Ia34{5 zew^3QGYBwixQtIj1!LVyL7m>RE_rFjyk0X=PQ0$;mu?^X(5g8Aqf%8! z4t%gFz!Drtnrb@wV;;X96IB@Q?2HkA_63tBPZgv=3~6`+j?N^YWyP?=(Ex6WvlL$M z`J6I}iM3qFys0jtgPl$k)$~_8u51S{0;2bqsj~K4`hQ~TkC0FM`C()14O0wnx&I$R zet-Yy<6mte>%X8%#(qnbu=7?$6MJ(mn#7ZnLaT;CjBdHs2q#>Jy@qO{J_hXdfeCe{ z(hs^Go$0*EIqxTbIqFPf7*n0)zGWxmR?T3|mIi@hKLgu~;MdpnCw=Lo6CWD{m3<=< z9a|-u;d}tY_(HsCeg8WW@VaG~J1;K-5lVb8999qHe6@oPA=)N8r6p%Z@^Ot)t)55^ z%G`JFH(N-drf$+)s{XzAwzVBN4YHhIi8vS^k(Em}6?@n}+sNY#Fm`~|N(0CQ>RN9^ zF%ap23~vQM1;Q;U6$@bTq&4^~GVh8lPewAG9wb~|>1;pS<;uRMi{y|(ejpGdB;qN+ z^K!1q+D<_~sM8-uFj>aP--1OBL2fDBEf3;n=!k zBpOk$?94pRJ?>ZPgfeLtF^m@3+j$-_)%67`xByN%${yg1r(u~>V#@aehnID~+hCqZe@!^%!TXUU6LQR{tD6;KlIi>PdR9MQ zXdPcx7X3~`Kwb6cau_NUvv>$}iFgn`H`UZdi|}NS@dHBIqx=;1jFM=^sYha?i8PWV zbZe2$3_))AbjCJI^mbf3WWsd$+SF*~cG=`a<=2m`57!45_hLaR$Wz%rfRO91;Bb+Z zAGKvCDeiPy%{U1vLisjC8uBL1ahXw8{+H>cQ6Fzc2lvV@?KRfGyY}Ua=#pP$ZZ+9vjA(YTnkE zQn2{;*;a%Skz(;y1k==+UwhH4c-9ws9DCk zH8t^3KHbOcl#tkpM z=6GE+2)^qVeu$TeWHF)Ycr1+E6Fs)-qmfl?h;Hq)XLQ+k&y-6V8>?0_Y?XnQ z^cvd}9L{Fo9bkUp25dNtYzjc-znS4r1_~$dOZ(tqr>k@d4;oJ{)_omDCvzN?%}#y2 z%<$ZmdH|&w=8hZ6qQ=1REONLai!8js`ob9VB~Uic`d5>;kCEmF=JCdP)|gHLV&a1m1$(Ku&7alH8K&u7cfarU zvw8jVoRP(Xc;XR*K?m$nd@l1ry(>Mjc%F9cD-N0tEi{Q_nU56T+|fAUa0XJvjr@Rw z1et1w>?mhFACYriFbVi{Hb2m394bPsPJHvs_;!HrYD6-FK^0_j*re#FG$(6(EG)d} ze+bK)zA(9fZ)6F%KSnmy5EhKL|Jw2LxOu=tvM%uKa&z2ysV~^^VEw%9Uh@>~&F1T- z&cdJB8+L)};~CJ-H`afj7%7-zx;q9jSsc4`&C}Fx65Z9*AbMxG9=Bqt`93xSJO&qa z%DauGa9JTUO*lOdSI^VPuN7gElig`pE7;~;WSxJwhCBG1sA+SV+R2*~6tJlFZd7~Q zt-)+o4}2OOSZhY6l+QU#df|a>gRa5T?vw-I+4V z_p5fNhd$|Vp7MCU&>q;atOn-0feEcH%3!4nBtjTwlf`gJ^-YSS^)7gWsXXv~N@guR zLJ?~mtv0#{8^hU@6$Ytgg)g5@=UePPzwAG{gk%dv-Ij>M^Rv~Ig2_0+gqXwE(WxD& zRjo_lrnOJ;Bh~C48g2dNYs9%zHe-ZhqQw~o;&rjyj*_|fvXskIg9 zVsMqv%q$v1!b_&0p&6U7g~g=V{baWxs)&E7W>rclzGOJ+u6#i)+Qj8p=n2(7hM4LTzPK9!AZsN?@S8QDn5f`w(^Xi~z_yALt8D0FSElOS$^Dw}&mj zz~jAkLwAH|(6pe&cHUm^3{zmO=#IO{C$h*|2Xz%rMr!-D<66z!irO|4suqz36-=rg zPQ(dumuLf)uA|_3+F;d1HSEUV4IB9dw24BC_P84?c=rz6-!43FXEXEmxDzb+$ks2E z-6NbN3Bw(=8RJ?vlhf2su6&F4B>#$tV*lK0xwm@F7!LrT`S*ASn*A#vIR6R=tx>xb zam>|6I=B;KC&@$jx5Wsrkm3qQI_AE7?hu-o47sVm)fw%l$i`3DDesTNr_}02SB57J2din+33vFRAR-ywLqR#RD_s)*JeKEK4 z+1Ajxk{xn3xvU6t?EJY_LDBcozVd{nA9RjzfjDN|{dNL%1LcQAMDzu$W-#$eKm}t}DQ2af z;>*t_G*??)0s7r6llV*GZMreaB^+}8j)kL~rG$Rs+dsJqG6OwaGG!tYa{YRe(rqz? z--mUVOB#O6IjbUL9w=R9R<1JjCU7X_RG>XhMwiNzK5|=WtY=SV>}Y-2In^cpX1LEo zELtT*45uqGCF8aCf;17I8s`|cj=gWl9C?*=WaMRp`?-vkRA97kSjfk5xT{Vp%PI=m z6j3kNaUtTGrr_vdl1aa_wdL;M5PW+(sxva+wr1At^6_Ik(0(p8Hw>3|UQ85k zNqSW{)Rt?vB!eZ&reJxH)a}=H+kE9 z?|EmU3I4!#kZsN5|8eldX|mVEdkK(?A(;J*ST^_^11%YytYzsSD&-=+?##!CkrnaI zGL3|hoA<3=n|14@J}aW0Z*u0u%WGA5TCAJ?wVuiW9B;Eo6P_)iuh8X9zcM9y&J+TD z6Ww<2AlvC8N_)>e`dJMh6z15Tj1RgR<7W2QiWuU#g_%{HRCkodU$*lvpCMH1jju$D z5h{xhftVwd0y9wT9NsVZN8x8w1FayX5NDBZL5Q<;WHYe=xC_B%?3Na!h!yig!6BEM z3uny`Kce*bkg*LoRb4eEpDpDAS`v4X*TyJqFHzDWdq3%&Or7VUc<%jn4$WR@Xcn2c zOc!!c=W`+59;-wQ%0A@qcj*d*?it6)cPhq>iXa0t{8)8|I9@@aU*#OZZElrBA+N1v zXe8l()Fy5t4CRJsBw{AIg!Wsa6hiv01^=CXaAf1eZ`g+cQntvR zCC-g9u9i^dwiWncwjPc-tD#@WPdog(be!2O1F1%XBkT2igCh^lf?DPP%1G!TzKbXp zaei<~3oQ)LXS4ECXK=W3&{rCa)3${ct&q3+4I5tB!g>2KrFeY$)ZU0xoH7ofdG`y% z2Msnf124z25(aRgSn(>3i5DdXC3JiBJn1xDyeeGnQwN&KTjw^O;A)rr`4onipYnD+ zp?l(0Q3e7M6Y!r`)^A`2K>Tt0uj~I~f&EwEf7+(}q4$4@P8hziZakJe|#0@?Sl#l0Mrowar!@|4A4LT literal 0 HcmV?d00001 diff --git a/config/nacos/datasource.yml b/config/nacos/datasource.yml index ee003009..f3d8fdea 100644 --- a/config/nacos/datasource.yml +++ b/config/nacos/datasource.yml @@ -13,6 +13,10 @@ datasource: url: jdbc:mysql://localhost:3306/ry-job?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true username: root password: password + workflow: + url: jdbc:mysql://localhost:3306/ry-workflow?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&rewriteBatchedStatements=true&allowPublicKeyRetrieval=true + username: root + password: password # system-oracle: # url: jdbc:oracle:thin:@//localhost:1521/XE # username: ROOT diff --git a/config/nacos/ruoyi-gateway.yml b/config/nacos/ruoyi-gateway.yml index f818254f..4c449977 100644 --- a/config/nacos/ruoyi-gateway.yml +++ b/config/nacos/ruoyi-gateway.yml @@ -5,6 +5,7 @@ security: enabled: true excludeUrls: - /system/notice + - /workflow/model/editModelXml # 不校验白名单 ignore: whites: @@ -59,6 +60,11 @@ spring: - Path=/resource/** filters: - StripPrefix=1 + # workflow服务 + - id: ruoyi-workflow + uri: lb://ruoyi-workflow + predicates: + - Path=/workflow/** # 演示服务 - id: ruoyi-demo uri: lb://ruoyi-demo diff --git a/config/nacos/ruoyi-workflow.yml b/config/nacos/ruoyi-workflow.yml new file mode 100644 index 00000000..d87a2119 --- /dev/null +++ b/config/nacos/ruoyi-workflow.yml @@ -0,0 +1,44 @@ +spring: + datasource: + dynamic: + # 设置默认的数据源或者数据源组,默认值即为 master + primary: master + seata: false + datasource: + # 主库数据源 + master: + type: ${spring.datasource.type} + driver-class-name: com.mysql.cj.jdbc.Driver + url: ${datasource.workflow.url} + username: ${datasource.workflow.username} + password: ${datasource.workflow.password} +# oracle: +# type: ${spring.datasource.type} +# driverClassName: oracle.jdbc.OracleDriver +# url: ${datasource.system-oracle.url} +# username: ${datasource.system-oracle.username} +# password: ${datasource.system-oracle.password} +# postgres: +# type: ${spring.datasource.type} +# driverClassName: org.postgresql.Driver +# url: ${datasource.system-postgres.url} +# username: ${datasource.system-postgres.username} +# password: ${datasource.system-postgres.password} + +--- #flowable配置 +flowable: + async-executor-activate: false #关闭定时任务JOB + # 将databaseSchemaUpdate设置为true。当Flowable发现库与数据库表结构不一致时,会自动将数据库表结构升级至新版本。 + database-schema-update: false + activity-font-name: 宋体 + label-font-name: 宋体 + annotation-font-name: 宋体 + # 关闭各个模块生成表,目前只使用工作流基础表 + idm: + enabled: false + cmmn: + enabled: false + dmn: + enabled: false + app: + enabled: false diff --git a/pom.xml b/pom.xml index 61c186ab..a1285710 100644 --- a/pom.xml +++ b/pom.xml @@ -62,6 +62,11 @@ 3.11.0 3.1.2 1.3.0 + + + 7.0.0 + 6.8.0 + 1.10 @@ -161,6 +166,27 @@ import + + org.flowable + flowable-bom + ${flowable.version} + pom + import + + + + org.flowable + flowable-json-converter + ${flowable-json-convertor.version} + + + + + org.apache.xmlgraphics + batik-all + ${xmlgraphics.version} + + cn.dev33 sa-token-core diff --git a/ruoyi-api/pom.xml b/ruoyi-api/pom.xml index 2701751b..8b5f0b9e 100644 --- a/ruoyi-api/pom.xml +++ b/ruoyi-api/pom.xml @@ -12,6 +12,7 @@ ruoyi-api-bom ruoyi-api-system ruoyi-api-resource + ruoyi-api-workflow ruoyi-api diff --git a/ruoyi-api/ruoyi-api-bom/pom.xml b/ruoyi-api/ruoyi-api-bom/pom.xml index b0e99d37..2229c4a2 100644 --- a/ruoyi-api/ruoyi-api-bom/pom.xml +++ b/ruoyi-api/ruoyi-api-bom/pom.xml @@ -34,6 +34,13 @@ ${revision} + + + org.dromara + ruoyi-api-workflow + ${revision} + + diff --git a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileService.java b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileService.java index 6ab788e4..2204de62 100644 --- a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileService.java +++ b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileService.java @@ -3,6 +3,8 @@ package org.dromara.resource.api; import org.dromara.common.core.exception.ServiceException; import org.dromara.resource.api.domain.RemoteFile; +import java.util.List; + /** * 文件服务 * @@ -26,4 +28,11 @@ public interface RemoteFileService { */ String selectUrlByIds(String ossIds); + /** + * 通过ossId查询列表 + * + * @param ossIds ossId串逗号分隔 + * @return 列表 + */ + List selectByIds(String ossIds); } diff --git a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileServiceMock.java b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileServiceMock.java index c689f2d1..2b79e22f 100644 --- a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileServiceMock.java +++ b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/RemoteFileServiceMock.java @@ -4,6 +4,8 @@ import lombok.extern.slf4j.Slf4j; import org.dromara.common.core.utils.StringUtils; import org.dromara.resource.api.domain.RemoteFile; +import java.util.List; + /** * 文件服务(降级处理) * @@ -34,4 +36,10 @@ public class RemoteFileServiceMock implements RemoteFileService { return StringUtils.EMPTY; } + @Override + public List selectByIds(String ossIds) { + log.warn("服务调用异常 -> 降级处理"); + return List.of(); + } + } diff --git a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteFile.java b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteFile.java index ecc4b16a..7140fe63 100644 --- a/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteFile.java +++ b/ruoyi-api/ruoyi-api-resource/src/main/java/org/dromara/resource/api/domain/RemoteFile.java @@ -31,4 +31,14 @@ public class RemoteFile implements Serializable { */ private String url; + /** + * 原名 + */ + private String originalName; + + /** + * 文件后缀名 + */ + private String fileSuffix; + } diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java index 8c1de9d1..a03241ea 100644 --- a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/RemoteUserService.java @@ -3,9 +3,12 @@ package org.dromara.system.api; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.user.UserException; import org.dromara.system.api.domain.bo.RemoteUserBo; +import org.dromara.system.api.domain.dto.UserDTO; import org.dromara.system.api.model.LoginUser; import org.dromara.system.api.model.XcxLoginUser; +import java.util.List; + /** * 用户服务 * @@ -105,4 +108,19 @@ public interface RemoteUserService { */ void recordLoginInfo(Long userId, String ip); + /** + * 通过用户ID查询用户列表 + * + * @param userIds 用户ids + * @return 用户列表 + */ + List selectListByIds(List userIds); + + /** + * 通过角色ID查询用户ID + * + * @param roleIds 角色ids + * @return 用户ids + */ + List selectUserIdsByRoleIds(List roleIds); } diff --git a/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/dto/UserDTO.java b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/dto/UserDTO.java new file mode 100644 index 00000000..6badbb61 --- /dev/null +++ b/ruoyi-api/ruoyi-api-system/src/main/java/org/dromara/system/api/domain/dto/UserDTO.java @@ -0,0 +1,73 @@ +package org.dromara.system.api.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 用户 + * + * @author Michelle.Chung + */ +@Data +@NoArgsConstructor +public class UserDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户ID + */ + private Long userId; + + /** + * 部门ID + */ + private Long deptId; + + /** + * 用户账号 + */ + private String userName; + + /** + * 用户昵称 + */ + private String nickName; + + /** + * 用户类型(sys_user系统用户) + */ + private String userType; + + /** + * 用户邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String phonenumber; + + /** + * 用户性别(0男 1女 2未知) + */ + private String sex; + + /** + * 帐号状态(0正常 1停用) + */ + private String status; + + /** + * 创建时间 + */ + private Date createTime; + +} diff --git a/ruoyi-api/ruoyi-api-workflow/pom.xml b/ruoyi-api/ruoyi-api-workflow/pom.xml new file mode 100644 index 00000000..13992051 --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/pom.xml @@ -0,0 +1,33 @@ + + + + org.dromara + ruoyi-api + ${revision} + + 4.0.0 + + ruoyi-api-workflow + + + ruoyi-api-resource 资源服务接口模块 + + + + + + + org.dromara + ruoyi-common-core + + + + org.dromara + ruoyi-common-translation + + + + + diff --git a/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteWorkflowService.java b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteWorkflowService.java new file mode 100644 index 00000000..1a61d511 --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/RemoteWorkflowService.java @@ -0,0 +1,86 @@ +package org.dromara.workflow.api.domain; + +import org.dromara.workflow.api.domain.dto.BusinessInstanceDTO; + +import java.util.List; +import java.util.Map; + +/** + * 通用 工作流服务 + * + * @Author ZETA + * @Date 2024/6/3 + */ +public interface RemoteWorkflowService { + + /** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param businessKeys 业务id + * @return 结果 + */ + boolean deleteRunAndHisInstance(List businessKeys); + + /** + * 获取当前流程状态 + * + * @param taskId 任务id + */ + String getBusinessStatusByTaskId(String taskId); + + /** + * 获取当前流程状态 + * + * @param businessKey 业务id + */ + String getBusinessStatus(String businessKey); + + /** + * 获取流程实例对象 + * + * @param businessKey 业务id + */ + BusinessInstanceDTO getBusinessInstance(String businessKey); + + /** + * 获取流程实例对象 + * + * @param businessKeys 业务id集合 + */ + List getBusinessInstance(List businessKeys); + + /** + * 设置流程变量(全局变量) + * + * @param taskId 任务id + * @param variableName 变量名称 + * @param value 变量值 + */ + void setVariable(String taskId, String variableName, Object value); + + /** + * 设置流程变量(全局变量) + * + * @param taskId 任务id + * @param variables 流程变量 + */ + void setVariables(String taskId, Map variables); + + /** + * 设置流程变量(本地变量,非全局变量) + * + * @param taskId 任务id + * @param variableName 变量名称 + * @param value 变量值 + */ + void setVariableLocal(String taskId, String variableName, Object value); + + /** + * 设置流程变量(本地变量,非全局变量) + * + * @param taskId 任务id + * @param variables 流程变量 + */ + void setVariablesLocal(String taskId, Map variables); + +} diff --git a/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/ProcessInstanceBo.java b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/ProcessInstanceBo.java new file mode 100644 index 00000000..76d4cb01 --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/ProcessInstanceBo.java @@ -0,0 +1,43 @@ +package org.dromara.workflow.api.domain.bo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 流程实例请求对象 + * + * @author may + */ +@Data +public class ProcessInstanceBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程名称 + */ + private String name; + + /** + * 流程key + */ + private String key; + + /** + * 任务发起人 + */ + private String startUserId; + + /** + * 业务id + */ + private String businessKey; + + /** + * 模型分类 + */ + private String categoryCode; +} diff --git a/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/ProcessInvalidBo.java b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/ProcessInvalidBo.java new file mode 100644 index 00000000..baf779b5 --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/ProcessInvalidBo.java @@ -0,0 +1,31 @@ +package org.dromara.workflow.api.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 流程实例作废请求对象 + * + * @author may + */ +@Data +public class ProcessInvalidBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 业务id + */ + @NotBlank(message = "业务id不能为空", groups = {AddGroup.class}) + private String businessKey; + + /** + * 作废原因 + */ + private String deleteReason; +} diff --git a/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/TaskUrgingBo.java b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/TaskUrgingBo.java new file mode 100644 index 00000000..000d3b8b --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/bo/TaskUrgingBo.java @@ -0,0 +1,34 @@ +package org.dromara.workflow.api.domain.bo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 任务催办 + * + * @author may + */ +@Data +public class TaskUrgingBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程实例id + */ + private String processInstanceId; + + /** + * 消息类型 + */ + private List messageType; + + /** + * 催办内容(为空默认系统内置信息) + */ + private String message; +} diff --git a/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/dto/BusinessInstanceDTO.java b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/dto/BusinessInstanceDTO.java new file mode 100644 index 00000000..30c3e5de --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/dto/BusinessInstanceDTO.java @@ -0,0 +1,72 @@ +package org.dromara.workflow.api.domain.dto; + +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 业务与流程实例关联对象 + * + * @Author ZETA + * @Date 2024/6/3 + */ +@Data +@NoArgsConstructor +public class BusinessInstanceDTO implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程实例id + */ + private String id; + + /** + * 流程定义id + */ + private String processDefinitionId; + + /** + * 流程定义名称 + */ + private String name; + + /** + * 业务id + */ + private String businessKey; + + /** + * 租户id + */ + private String tenantId; + + /** + * 启动时间 + */ + private Date startTime; + + /** + * 结束时间 + */ + private Date endTime; + + /** + * 启动人id + */ + private String startUserId; + + /** + * 流程状态 + */ + private String businessStatus; + + /** + * 流程状态 + */ + private String businessStatusName; +} diff --git a/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessEvent.java b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessEvent.java new file mode 100644 index 00000000..e758ffe0 --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessEvent.java @@ -0,0 +1,47 @@ +package org.dromara.workflow.api.domain.event; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 总体流程监听 + * + * @author may + */ + +@Data +public class ProcessEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程定义key + */ + private String key; + + /** + * 业务id + */ + private String businessKey; + + /** + * 状态 + */ + private String status; + + /** + * 当为true时为申请人节点办理 + */ + private boolean submit; + + /** + * 请求体 + */ + private HttpServletRequest request; + + +} diff --git a/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessTaskEvent.java b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessTaskEvent.java new file mode 100644 index 00000000..e64b994b --- /dev/null +++ b/ruoyi-api/ruoyi-api-workflow/src/main/java/org/dromara/workflow/api/domain/event/ProcessTaskEvent.java @@ -0,0 +1,41 @@ +package org.dromara.workflow.api.domain.event; + +import jakarta.servlet.http.HttpServletRequest; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 流程办理监听 + * + * @author may + */ + +@Data +public class ProcessTaskEvent implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程定义key与流程节点标识(拼接方式:流程定义key_流程节点) + */ + private String keyNode; + + /** + * 任务id + */ + private String taskId; + + /** + * 业务id + */ + private String businessKey; + + /** + * 请求体 + */ + private HttpServletRequest request; + +} diff --git a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java index 8ef4a578..b7e18bb3 100644 --- a/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java +++ b/ruoyi-common/ruoyi-common-mybatis/src/main/java/org/dromara/common/mybatis/core/page/PageQuery.java @@ -4,10 +4,11 @@ import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.metadata.OrderItem; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.Data; +import lombok.NoArgsConstructor; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.sql.SqlUtil; -import lombok.Data; import java.io.Serial; import java.io.Serializable; @@ -21,6 +22,7 @@ import java.util.List; */ @Data +@NoArgsConstructor public class PageQuery implements Serializable { @Serial @@ -56,6 +58,13 @@ public class PageQuery implements Serializable { */ public static final int DEFAULT_PAGE_SIZE = Integer.MAX_VALUE; + public static final PageQuery DEFAULT_PAGE = new PageQuery(DEFAULT_PAGE_NUM, DEFAULT_PAGE_SIZE); + + private PageQuery(Integer pageNum, Integer pageSize) { + this.pageSize = pageSize; + this.pageNum = pageNum; + } + public Page build() { Integer pageNum = ObjectUtil.defaultIfNull(getPageNum(), DEFAULT_PAGE_NUM); Integer pageSize = ObjectUtil.defaultIfNull(getPageSize(), DEFAULT_PAGE_SIZE); @@ -111,4 +120,8 @@ public class PageQuery implements Serializable { return list; } + public Integer getFirstNum() { + return (pageNum - 1) * pageSize; + } + } diff --git a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java index b73478ec..9b60a233 100644 --- a/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java +++ b/ruoyi-common/ruoyi-common-translation/src/main/java/org/dromara/common/translation/core/impl/NicknameTranslationImpl.java @@ -21,6 +21,6 @@ public class NicknameTranslationImpl implements TranslationInterface { @Override public String translation(Object key, String other) { - return remoteUserService.selectNicknameById((Long) key); + return remoteUserService.selectNicknameById(Long.valueOf(String.valueOf(key))); } } diff --git a/ruoyi-example/ruoyi-demo/pom.xml b/ruoyi-example/ruoyi-demo/pom.xml index 390cb57d..1b359c06 100644 --- a/ruoyi-example/ruoyi-demo/pom.xml +++ b/ruoyi-example/ruoyi-demo/pom.xml @@ -108,6 +108,7 @@ spring-boot-starter-test test + diff --git a/ruoyi-modules/pom.xml b/ruoyi-modules/pom.xml index 0980f601..c9063627 100644 --- a/ruoyi-modules/pom.xml +++ b/ruoyi-modules/pom.xml @@ -13,6 +13,7 @@ ruoyi-gen ruoyi-job ruoyi-resource + ruoyi-workflow ruoyi-modules diff --git a/ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/dubbo/RemoteFileServiceImpl.java b/ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/dubbo/RemoteFileServiceImpl.java index a785b0ef..e9fed20a 100644 --- a/ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/dubbo/RemoteFileServiceImpl.java +++ b/ruoyi-modules/ruoyi-resource/src/main/java/org/dromara/resource/dubbo/RemoteFileServiceImpl.java @@ -1,20 +1,27 @@ package org.dromara.resource.dubbo; +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import cn.hutool.core.convert.Convert; import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.utils.StringUtils; import org.dromara.common.oss.core.OssClient; import org.dromara.common.oss.entity.UploadResult; import org.dromara.common.oss.factory.OssFactory; import org.dromara.resource.api.RemoteFileService; -import org.dromara.resource.api.domain.RemoteFile; import org.dromara.resource.domain.bo.SysOssBo; +import org.dromara.resource.domain.vo.SysOssVo; import org.dromara.resource.service.ISysOssService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.DubboService; +import org.dromara.resource.api.domain.RemoteFile; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; +import java.util.Map; + /** * 文件请求处理 * @@ -68,4 +75,15 @@ public class RemoteFileServiceImpl implements RemoteFileService { return sysOssService.selectUrlByIds(ossIds); } + /** + * 通过ossId查询列表 + * + * @param ossIds ossId串逗号分隔 + * @return 列表 + */ + public List selectByIds(String ossIds){ + List sysOssVos = sysOssService.listByIds(StringUtils.splitTo(ossIds, Convert::toLong)); + return BeanUtil.copyToList(sysOssVos, RemoteFile.class, + CopyOptions.create().setFieldMapping(Map.of("fileName", "name"))); + } } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java index 754baeaa..60d70f32 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/dubbo/RemoteUserServiceImpl.java @@ -14,6 +14,7 @@ import org.dromara.common.mybatis.helper.DataPermissionHelper; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.system.api.RemoteUserService; import org.dromara.system.api.domain.bo.RemoteUserBo; +import org.dromara.system.api.domain.dto.UserDTO; import org.dromara.system.api.model.LoginUser; import org.dromara.system.api.model.RoleDTO; import org.dromara.system.api.model.XcxLoginUser; @@ -234,4 +235,15 @@ public class RemoteUserServiceImpl implements RemoteUserService { DataPermissionHelper.ignore(() -> userMapper.updateById(sysUser)); } + @Override + public List selectListByIds(List userIds) { + List sysUserVos = userService.selectUserByIds(userIds, null); + return BeanUtil.copyToList(sysUserVos, UserDTO.class); + } + + @Override + public List selectUserIdsByRoleIds(List roleIds) { + return userService.selectUserIdsByRoleIds(roleIds); + } + } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java index ab37e3c5..5be2997c 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/ISysUserService.java @@ -252,4 +252,11 @@ public interface ISysUserService { */ List selectUserListByDept(Long deptId); + /** + * 通过角色ID查询用户ID + * + * @param roleIds 角色ids + * @return 用户ids + */ + List selectUserIdsByRoleIds(List roleIds); } diff --git a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java index 93325b11..7632cde7 100644 --- a/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java +++ b/ruoyi-modules/ruoyi-system/src/main/java/org/dromara/system/service/impl/SysUserServiceImpl.java @@ -553,6 +553,13 @@ public class SysUserServiceImpl implements ISysUserService { return baseMapper.selectVoList(lqw); } + @Override + public List selectUserIdsByRoleIds(List roleIds) { + List userRoles = userRoleMapper.selectList( + new LambdaQueryWrapper().in(SysUserRole::getRoleId, roleIds)); + return StreamUtils.toList(userRoles, SysUserRole::getUserId); + } + /** * 通过用户ID查询用户账户 * diff --git a/ruoyi-modules/ruoyi-workflow/pom.xml b/ruoyi-modules/ruoyi-workflow/pom.xml new file mode 100644 index 00000000..479b783d --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/pom.xml @@ -0,0 +1,138 @@ + + + + org.dromara + ruoyi-modules + ${revision} + + 4.0.0 + + ruoyi-workflow + + + 工作流模块 + + + + + + org.dromara + ruoyi-common-nacos + + + + + org.flowable + flowable-spring-boot-autoconfigure + + + org.flowable + flowable-spring-security + + + + + + org.flowable + flowable-spring-configurator + + + + org.flowable + flowable-spring-boot-starter-actuator + + + + + org.flowable + flowable-image-generator + + + + + org.flowable + flowable-json-converter + 6.8.0 + + + + + org.apache.xmlgraphics + batik-all + + + xalan + xalan + + + + + + org.dromara + ruoyi-common-websocket + + + + org.dromara + ruoyi-common-mail + + + + org.dromara + ruoyi-common-sms + + + + org.dromara + ruoyi-common-mybatis + + + org.dromara + ruoyi-common-web + + + org.dromara + ruoyi-common-log + + + org.dromara + ruoyi-common-idempotent + + + org.dromara + ruoyi-common-excel + + + org.dromara + ruoyi-common-translation + + + org.dromara + ruoyi-common-tenant + + + + org.dromara + ruoyi-common-dubbo + + + + org.dromara + ruoyi-common-tenant + + + + org.dromara + ruoyi-common-security + + + + org.dromara + ruoyi-api-workflow + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/RuoYiWorkflowApplication.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/RuoYiWorkflowApplication.java new file mode 100644 index 00000000..2ae9d2c7 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/RuoYiWorkflowApplication.java @@ -0,0 +1,22 @@ +package org.dromara.workflow; + +import org.apache.dubbo.config.spring.context.annotation.EnableDubbo; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.metrics.buffering.BufferingApplicationStartup; + +/** + * 系统模块 + * + * @author ruoyi + */ +@EnableDubbo +@SpringBootApplication +public class RuoYiWorkflowApplication { + public static void main(String[] args) { + SpringApplication application = new SpringApplication(RuoYiWorkflowApplication.class); + application.setApplicationStartup(new BufferingApplicationStartup(2048)); + application.run(args); + System.out.println("(♥◠‿◠)ノ゙ 工作流模块启动成功 ლ(´ڡ`ლ)゙ "); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java new file mode 100644 index 00000000..c3fcafa8 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/constant/FlowConstant.java @@ -0,0 +1,137 @@ +package org.dromara.workflow.common.constant; + + +/** + * 工作流常量 + * + * @author may + */ +public interface FlowConstant { + + String MESSAGE_CURRENT_TASK_IS_NULL = "当前任务不存在或你不是任务办理人!"; + + String MESSAGE_SUSPENDED = "当前任务已挂起不可审批!"; + + /** + * 连线 + */ + String SEQUENCE_FLOW = "sequenceFlow"; + + /** + * 并行网关 + */ + String PARALLEL_GATEWAY = "parallelGateway"; + + /** + * 排它网关 + */ + String EXCLUSIVE_GATEWAY = "exclusiveGateway"; + + /** + * 包含网关 + */ + String INCLUSIVE_GATEWAY = "inclusiveGateway"; + + /** + * 结束节点 + */ + String END_EVENT = "endEvent"; + + + /** + * 流程委派标识 + */ + String PENDING = "PENDING"; + + /** + * 候选人标识 + */ + String CANDIDATE = "candidate"; + + /** + * 会签任务总数 + */ + String NUMBER_OF_INSTANCES = "nrOfInstances"; + + /** + * 正在执行的会签总数 + */ + String NUMBER_OF_ACTIVE_INSTANCES = "nrOfActiveInstances"; + + /** + * 已完成的会签任务总数 + */ + String NUMBER_OF_COMPLETED_INSTANCES = "nrOfCompletedInstances"; + + /** + * 循环的索引值,可以使用elementIndexVariable属性修改loopCounter的变量名 + */ + String LOOP_COUNTER = "loopCounter"; + + String ZIP = "ZIP"; + + /** + * 业务与流程实例关联对象 + */ + String BUSINESS_INSTANCE_DTO = "businessInstanceDTO"; + + /** + * 流程定义配置 + */ + String WF_DEFINITION_CONFIG_VO = "wfDefinitionConfigVo"; + + /** + * 节点配置 + */ + String WF_NODE_CONFIG_VO = "wfNodeConfigVo"; + + /** + * 流程发起人 + */ + String INITIATOR = "initiator"; + + /** + * 流程实例id + */ + String PROCESS_INSTANCE_ID = "processInstanceId"; + + /** + * 业务id + */ + String BUSINESS_KEY = "businessKey"; + + /** + * 流程定义id + */ + String PROCESS_DEFINITION_ID = "processDefinitionId"; + + /** + * 开启跳过表达式变量 + */ + String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + + /** + * 模型标识key命名规范正则表达式 + */ + String MODEL_KEY_PATTERN = "^[a-zA-Z][a-zA-Z0-9_]{0,254}$"; + + /** + * 用户任务 + */ + String USER_TASK = "userTask"; + + /** + * 会签 + */ + String MULTI_INSTANCE = "multiInstance"; + + /** + * 是 + */ + String TRUE = "0"; + + /** + * 否 + */ + String FALSE = "1"; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java new file mode 100644 index 00000000..6eb6ffe6 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/BusinessStatusEnum.java @@ -0,0 +1,152 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; + +import java.util.Arrays; + +/** + * 业务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum BusinessStatusEnum { + /** + * 已撤销 + */ + CANCEL("cancel", "已撤销"), + /** + * 草稿 + */ + DRAFT("draft", "草稿"), + /** + * 待审核 + */ + WAITING("waiting", "待审核"), + /** + * 已完成 + */ + FINISH("finish", "已完成"), + /** + * 已作废 + */ + INVALID("invalid", "已作废"), + /** + * 已退回 + */ + BACK("back", "已退回"), + /** + * 已终止 + */ + TERMINATION("termination", "已终止"); + + /** + * 状态 + */ + private final String status; + + /** + * 描述 + */ + private final String desc; + + /** + * 获取业务状态 + * + * @param status 状态 + */ + public static String findByStatus(String status) { + if (StringUtils.isBlank(status)) { + return StrUtil.EMPTY; + } + return Arrays.stream(BusinessStatusEnum.values()) + .filter(statusEnum -> statusEnum.getStatus().equals(status)) + .findFirst() + .map(BusinessStatusEnum::getDesc) + .orElse(StrUtil.EMPTY); + } + + /** + * 启动流程校验 + * + * @param status 状态 + */ + public static void checkStartStatus(String status) { + if (WAITING.getStatus().equals(status)) { + throw new ServiceException("该单据已提交过申请,正在审批中!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 撤销流程校验 + * + * @param status 状态 + */ + public static void checkCancelStatus(String status) { + if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 驳回流程校验 + * + * @param status 状态 + */ + public static void checkBackStatus(String status) { + if (BACK.getStatus().equals(status)) { + throw new ServiceException("该单据已退回!"); + } else if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (CANCEL.getStatus().equals(status)) { + throw new ServiceException("该单据已撤销!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } + + /** + * 作废,终止流程校验 + * + * @param status 状态 + */ + public static void checkInvalidStatus(String status) { + if (FINISH.getStatus().equals(status)) { + throw new ServiceException("该单据已完成申请!"); + } else if (INVALID.getStatus().equals(status)) { + throw new ServiceException("该单据已作废!"); + } else if (TERMINATION.getStatus().equals(status)) { + throw new ServiceException("该单据已终止!"); + } else if (StringUtils.isBlank(status)) { + throw new ServiceException("流程状态为空!"); + } + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java new file mode 100644 index 00000000..083ab7b8 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/FormTypeEnum.java @@ -0,0 +1,54 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 任务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum FormTypeEnum { + /** + * 自定义表单 + */ + STATIC("static", "自定义表单"), + /** + * 动态表单 + */ + DYNAMIC("dynamic", "动态表单"); + + /** + * 类型 + */ + private final String type; + + /** + * 描述 + */ + private final String desc; + + /** + * 表单类型 + * + * @param formType 表单类型 + */ + public static String findByType(String formType) { + if (StringUtils.isBlank(formType)) { + return StrUtil.EMPTY; + } + + return Arrays.stream(FormTypeEnum.values()) + .filter(statusEnum -> statusEnum.getType().equals(formType)) + .findFirst() + .map(FormTypeEnum::getDesc) + .orElse(StrUtil.EMPTY); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java new file mode 100644 index 00000000..a2829582 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/MessageTypeEnum.java @@ -0,0 +1,51 @@ +package org.dromara.workflow.common.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * 消息类型枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum MessageTypeEnum { + /** + * 站内信 + */ + SYSTEM_MESSAGE("1", "站内信"), + /** + * 邮箱 + */ + EMAIL_MESSAGE("2", "邮箱"), + /** + * 短信 + */ + SMS_MESSAGE("3", "短信"); + + private final String code; + + private final String desc; + + private final static Map MESSAGE_TYPE_ENUM_MAP = new ConcurrentHashMap<>(MessageTypeEnum.values().length); + + static { + for (MessageTypeEnum messageType : MessageTypeEnum.values()) { + MESSAGE_TYPE_ENUM_MAP.put(messageType.code, messageType); + } + } + + /** + * 根据消息类型 code 获取 MessageTypeEnum + * @param code 消息类型code + * @return MessageTypeEnum + */ + public static MessageTypeEnum getByCode(String code) { + return MESSAGE_TYPE_ENUM_MAP.get(code); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java new file mode 100644 index 00000000..7b2f55c2 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/common/enums/TaskStatusEnum.java @@ -0,0 +1,94 @@ +package org.dromara.workflow.common.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; + +/** + * 任务状态枚举 + * + * @author may + */ +@Getter +@AllArgsConstructor +public enum TaskStatusEnum { + /** + * 撤销 + */ + CANCEL("cancel", "撤销"), + /** + * 通过 + */ + PASS("pass", "通过"), + /** + * 待审核 + */ + WAITING("waiting", "待审核"), + /** + * 作废 + */ + INVALID("invalid", "作废"), + /** + * 退回 + */ + BACK("back", "退回"), + /** + * 终止 + */ + TERMINATION("termination", "终止"), + /** + * 转办 + */ + TRANSFER("transfer", "转办"), + /** + * 委托 + */ + PENDING("pending", "委托"), + /** + * 抄送 + */ + COPY("copy", "抄送"), + /** + * 加签 + */ + SIGN("sign", "加签"), + /** + * 减签 + */ + SIGN_OFF("sign_off", "减签"), + /** + * 超时 + */ + TIMEOUT("timeout", "超时"); + + /** + * 状态 + */ + private final String status; + + /** + * 描述 + */ + private final String desc; + + /** + * 任务业务状态 + * + * @param status 状态 + */ + public static String findByStatus(String status) { + if (StringUtils.isBlank(status)) { + return StrUtil.EMPTY; + } + + return Arrays.stream(TaskStatusEnum.values()) + .filter(statusEnum -> statusEnum.getStatus().equals(status)) + .findFirst() + .map(TaskStatusEnum::getDesc) + .orElse(StrUtil.EMPTY); + } +} + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java new file mode 100644 index 00000000..b96f3432 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActModelController.java @@ -0,0 +1,154 @@ +package org.dromara.workflow.controller; + +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.dromara.resource.api.RemoteFileService; +import org.dromara.resource.api.domain.RemoteFile; +import org.dromara.system.api.model.LoginUser; +import org.dromara.workflow.domain.bo.ModelBo; +import org.dromara.workflow.domain.vo.ModelVo; +import org.dromara.workflow.service.IActModelService; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Model; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; + +/** + * 模型管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/model") +public class ActModelController extends BaseController { + + private final RepositoryService repositoryService; + + private final IActModelService actModelService; + + + /** + * 分页查询模型 + * + * @param modelBo 模型参数 + */ + @GetMapping("/list") + public TableDataInfo page(ModelBo modelBo, PageQuery pageQuery) { + RemoteFileService bean = SpringUtils.getBean(RemoteFileService.class); + List remoteFiles = bean.selectByIds("1"); + return actModelService.page(modelBo, pageQuery); + } + + /** + * 新增模型 + * + * @param modelBo 模型请求对象 + */ + @Log(title = "模型管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/save") + public R saveNewModel(@Validated(AddGroup.class) @RequestBody ModelBo modelBo) { + return toAjax(actModelService.saveNewModel(modelBo)); + } + + /** + * 查询模型 + * + * @param id 模型id + */ + @GetMapping("/getInfo/{id}") + public R getInfo(@NotBlank(message = "模型id不能为空") @PathVariable String id) { + return R.ok(actModelService.getInfo(id)); + } + + /** + * 修改模型信息 + * + * @param modelBo 模型数据 + */ + @Log(title = "模型管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping(value = "/update") + public R update(@RequestBody ModelBo modelBo) { + return toAjax(actModelService.update(modelBo)); + } + + /** + * 编辑XMl模型 + * + * @param modelBo 模型数据 + */ + @Log(title = "模型管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping(value = "/editModelXml") + public R editModel(@Validated(EditGroup.class) @RequestBody ModelBo modelBo) { + return toAjax(actModelService.editModelXml(modelBo)); + } + + /** + * 删除流程模型 + * + * @param ids 模型id + */ + @Log(title = "模型管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/{ids}") + @Transactional(rollbackFor = Exception.class) + public R delete(@NotEmpty(message = "主键不能为空") @PathVariable String[] ids) { + Arrays.stream(ids).parallel().forEachOrdered(repositoryService::deleteModel); + return R.ok(); + } + + /** + * 模型部署 + * + * @param id 模型id + */ + @Log(title = "模型管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/modelDeploy/{id}") + public R deploy(@NotBlank(message = "模型id不能为空") @PathVariable("id") String id) { + return toAjax(actModelService.modelDeploy(id)); + } + + /** + * 导出模型zip压缩包 + * + * @param modelIds 模型id + * @param response 相应 + */ + @GetMapping("/export/zip/{modelIds}") + public void exportZip(@NotEmpty(message = "模型id不能为空") @PathVariable List modelIds, + HttpServletResponse response) { + actModelService.exportZip(modelIds, response); + } + + /** + * 复制模型 + * + * @param modelBo 模型数据 + */ + @PostMapping("/copyModel") + public R copyModel(@RequestBody ModelBo modelBo) { + return toAjax(actModelService.copyModel(modelBo)); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java new file mode 100644 index 00000000..5198bd16 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessDefinitionController.java @@ -0,0 +1,147 @@ +package org.dromara.workflow.controller; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.bo.ProcessDefinitionBo; +import org.dromara.workflow.domain.vo.ProcessDefinitionVo; +import org.dromara.workflow.service.IActProcessDefinitionService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +/** + * 流程定义管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/processDefinition") +public class ActProcessDefinitionController extends BaseController { + + private final IActProcessDefinitionService actProcessDefinitionService; + + /** + * 分页查询 + * + * @param bo 参数 + */ + @GetMapping("/list") + public TableDataInfo page(ProcessDefinitionBo bo, PageQuery pageQuery) { + return actProcessDefinitionService.page(bo, pageQuery); + } + + /** + * 查询历史流程定义列表 + * + * @param key 流程定义key + */ + @GetMapping("/getListByKey/{key}") + public R> getListByKey(@NotEmpty(message = "流程定义key不能为空") @PathVariable String key) { + return R.ok("操作成功", actProcessDefinitionService.getListByKey(key)); + } + + /** + * 查看流程定义图片 + * + * @param processDefinitionId 流程定义id + */ + @GetMapping("/definitionImage/{processDefinitionId}") + public R definitionImage(@PathVariable String processDefinitionId) { + return R.ok("操作成功", actProcessDefinitionService.definitionImage(processDefinitionId)); + } + + /** + * 查看流程定义xml文件 + * + * @param processDefinitionId 流程定义id + */ + @GetMapping("/definitionXml/{processDefinitionId}") + public R> definitionXml(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + Map map = new HashMap<>(); + String xmlStr = actProcessDefinitionService.definitionXml(processDefinitionId); + map.put("xml", Arrays.asList(xmlStr.split("\n"))); + map.put("xmlStr", xmlStr); + return R.ok(map); + } + + /** + * 删除流程定义 + * + * @param deploymentIds 部署id + * @param processDefinitionIds 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{deploymentIds}/{processDefinitionIds}") + public R deleteDeployment(@NotNull(message = "流程部署id不能为空") @PathVariable List deploymentIds, + @NotNull(message = "流程定义id不能为空") @PathVariable List processDefinitionIds) { + return toAjax(actProcessDefinitionService.deleteDeployment(deploymentIds, processDefinitionIds)); + } + + /** + * 激活或者挂起流程定义 + * + * @param processDefinitionId 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/updateDefinitionState/{processDefinitionId}") + public R updateDefinitionState(@NotBlank(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + return toAjax(actProcessDefinitionService.updateDefinitionState(processDefinitionId)); + } + + /** + * 迁移流程定义 + * + * @param currentProcessDefinitionId 当前流程定义id + * @param fromProcessDefinitionId 需要迁移到的流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/migrationDefinition/{currentProcessDefinitionId}/{fromProcessDefinitionId}") + public R migrationDefinition(@NotBlank(message = "当前流程定义id") @PathVariable String currentProcessDefinitionId, + @NotBlank(message = "需要迁移到的流程定义id") @PathVariable String fromProcessDefinitionId) { + return toAjax(actProcessDefinitionService.migrationDefinition(currentProcessDefinitionId, fromProcessDefinitionId)); + } + + /** + * 流程定义转换为模型 + * + * @param processDefinitionId 流程定义id + */ + @Log(title = "流程定义管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/convertToModel/{processDefinitionId}") + public R convertToModel(@NotEmpty(message = "流程定义id不能为空") @PathVariable String processDefinitionId) { + return toAjax(actProcessDefinitionService.convertToModel(processDefinitionId)); + } + + /** + * 通过zip或xml部署流程定义 + * + * @param file 文件 + * @param categoryCode 分类 + */ + @Log(title = "流程定义管理", businessType = BusinessType.INSERT) + @PostMapping("/deployByFile") + public void deployByFile(@RequestParam("file") MultipartFile file, @RequestParam("categoryCode") String categoryCode) { + actProcessDefinitionService.deployByFile(file, categoryCode); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java new file mode 100644 index 00000000..db7a2bba --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActProcessInstanceController.java @@ -0,0 +1,160 @@ +package org.dromara.workflow.controller; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.api.domain.bo.ProcessInstanceBo; +import org.dromara.workflow.api.domain.bo.ProcessInvalidBo; +import org.dromara.workflow.api.domain.bo.TaskUrgingBo; +import org.dromara.workflow.domain.vo.ActHistoryInfoVo; +import org.dromara.workflow.domain.vo.ProcessInstanceVo; +import org.dromara.workflow.service.IActProcessInstanceService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 流程实例管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/processInstance") +public class ActProcessInstanceController extends BaseController { + + private final IActProcessInstanceService actProcessInstanceService; + + /** + * 分页查询正在运行的流程实例 + * + * @param bo 参数 + */ + @GetMapping("/getPageByRunning") + public TableDataInfo getPageByRunning(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByRunning(bo, pageQuery); + } + + /** + * 分页查询已结束的流程实例 + * + * @param bo 参数 + */ + @GetMapping("/getPageByFinish") + public TableDataInfo getPageByFinish(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByFinish(bo, pageQuery); + } + + /** + * 通过业务id获取历史流程图 + * + * @param businessKey 业务id + */ + @GetMapping("/getHistoryImage/{businessKey}") + public R getHistoryImage(@NotBlank(message = "业务id不能为空") @PathVariable String businessKey) { + return R.ok("操作成功", actProcessInstanceService.getHistoryImage(businessKey)); + } + + /** + * 通过业务id获取历史流程图运行中,历史等节点 + * + * @param businessKey 业务id + */ + @GetMapping("/getHistoryList/{businessKey}") + public R> getHistoryList(@NotBlank(message = "业务id不能为空") @PathVariable String businessKey) { + return R.ok("操作成功", actProcessInstanceService.getHistoryList(businessKey)); + } + + /** + * 获取审批记录 + * + * @param businessKey 业务id + */ + @GetMapping("/getHistoryRecord/{businessKey}") + public R> getHistoryRecord(@NotBlank(message = "业务id不能为空") @PathVariable String businessKey) { + return R.ok(actProcessInstanceService.getHistoryRecord(businessKey)); + } + + /** + * 作废流程实例,不会删除历史记录(删除运行中的实例) + * + * @param processInvalidBo 参数 + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @PostMapping("/deleteRunInstance") + public R deleteRunInstance(@Validated(AddGroup.class) @RequestBody ProcessInvalidBo processInvalidBo) { + return toAjax(actProcessInstanceService.deleteRunInstance(processInvalidBo)); + } + + /** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param businessKeys 业务id + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/deleteRunAndHisInstance/{businessKeys}") + public R deleteRunAndHisInstance(@NotNull(message = "业务id不能为空") @PathVariable String[] businessKeys) { + return toAjax(actProcessInstanceService.deleteRunAndHisInstance(Arrays.asList(businessKeys))); + } + + /** + * 已完成的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param businessKeys 业务id + */ + @Log(title = "流程实例管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @DeleteMapping("/deleteFinishAndHisInstance/{businessKeys}") + public R deleteFinishAndHisInstance(@NotNull(message = "业务id不能为空") @PathVariable String[] businessKeys) { + return toAjax(actProcessInstanceService.deleteFinishAndHisInstance(Arrays.asList(businessKeys))); + } + + /** + * 撤销流程申请 + * + * @param businessKey 业务id + */ + @Log(title = "流程实例管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/cancelProcessApply/{businessKey}") + public R cancelProcessApply(@NotBlank(message = "业务id不能为空") @PathVariable String businessKey) { + return toAjax(actProcessInstanceService.cancelProcessApply(businessKey)); + } + + /** + * 分页查询当前登录人单据 + * + * @param bo 参数 + */ + @GetMapping("/getPageByCurrent") + public TableDataInfo getPageByCurrent(ProcessInstanceBo bo, PageQuery pageQuery) { + return actProcessInstanceService.getPageByCurrent(bo, pageQuery); + } + + /** + * 任务催办(给当前任务办理人发送站内信,邮件,短信等) + * + * @param taskUrgingBo 任务催办 + */ + @Log(title = "流程实例管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/taskUrging") + public R taskUrging(@RequestBody TaskUrgingBo taskUrgingBo) { + return toAjax(actProcessInstanceService.taskUrging(taskUrgingBo)); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java new file mode 100644 index 00000000..7fc9b95f --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/ActTaskController.java @@ -0,0 +1,295 @@ +package org.dromara.workflow.controller; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import jakarta.validation.constraints.NotBlank; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.WfTaskBackNode; +import org.dromara.workflow.domain.bo.*; +import org.dromara.workflow.domain.vo.TaskVo; +import org.dromara.workflow.domain.vo.VariableVo; +import org.dromara.workflow.service.IActTaskService; +import org.dromara.workflow.service.IWfTaskBackNodeService; +import org.dromara.workflow.utils.QueryUtils; +import org.flowable.engine.TaskService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * 任务管理 控制层 + * + * @author may + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/task") +public class ActTaskController extends BaseController { + + private final IActTaskService actTaskService; + + private final TaskService taskService; + + private final IWfTaskBackNodeService wfTaskBackNodeService; + + + /** + * 启动任务 + * + * @param startProcessBo 启动流程参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/startWorkFlow") + public R> startWorkFlow(@Validated(AddGroup.class) @RequestBody StartProcessBo startProcessBo) { + Map map = actTaskService.startWorkFlow(startProcessBo); + return R.ok("提交成功", map); + } + + /** + * 办理任务 + * + * @param completeTaskBo 办理任务参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/completeTask") + public R completeTask(@Validated(AddGroup.class) @RequestBody CompleteTaskBo completeTaskBo) { + return toAjax(actTaskService.completeTask(completeTaskBo)); + } + + /** + * 查询当前用户的待办任务 + * + * @param taskBo 参数 + */ + @GetMapping("/getPageByTaskWait") + public TableDataInfo getPageByTaskWait(TaskBo taskBo, PageQuery pageQuery) { + return actTaskService.getPageByTaskWait(taskBo, pageQuery); + } + + /** + * 查询当前租户所有待办任务 + * + * @param taskBo 参数 + */ + @GetMapping("/getPageByAllTaskWait") + public TableDataInfo getPageByAllTaskWait(TaskBo taskBo, PageQuery pageQuery) { + return actTaskService.getPageByAllTaskWait(taskBo, pageQuery); + } + + /** + * 查询当前用户的已办任务 + * + * @param taskBo 参数 + */ + @GetMapping("/getPageByTaskFinish") + public TableDataInfo getPageByTaskFinish(TaskBo taskBo, PageQuery pageQuery) { + return actTaskService.getPageByTaskFinish(taskBo, pageQuery); + } + + /** + * 查询当前用户的抄送 + * + * @param taskBo 参数 + */ + @GetMapping("/getPageByTaskCopy") + public TableDataInfo getPageByTaskCopy(TaskBo taskBo, PageQuery pageQuery) { + return actTaskService.getPageByTaskCopy(taskBo, pageQuery); + } + + /** + * 查询当前租户所有已办任务 + * + * @param taskBo 参数 + */ + @GetMapping("/getPageByAllTaskFinish") + public TableDataInfo getPageByAllTaskFinish(TaskBo taskBo, PageQuery pageQuery) { + return actTaskService.getPageByAllTaskFinish(taskBo, pageQuery); + } + + /** + * 签收(拾取)任务 + * + * @param taskId 任务id + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/claim/{taskId}") + public R claimTask(@NotBlank(message = "任务id不能为空") @PathVariable String taskId) { + try { + taskService.claim(taskId, Convert.toStr(LoginHelper.getUserId())); + return R.ok(); + } catch (Exception e) { + e.printStackTrace(); + return R.fail("签收任务失败:" + e.getMessage()); + } + } + + /** + * 归还(拾取的)任务 + * + * @param taskId 任务id + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/returnTask/{taskId}") + public R returnTask(@NotBlank(message = "任务id不能为空") @PathVariable String taskId) { + try { + taskService.setAssignee(taskId, null); + return R.ok(); + } catch (Exception e) { + e.printStackTrace(); + return R.fail("归还任务失败:" + e.getMessage()); + } + } + + /** + * 委派任务 + * + * @param delegateBo 参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/delegateTask") + public R delegateTask(@Validated({AddGroup.class}) @RequestBody DelegateBo delegateBo) { + return toAjax(actTaskService.delegateTask(delegateBo)); + } + + /** + * 终止任务 + * + * @param terminationBo 参数 + */ + @Log(title = "任务管理", businessType = BusinessType.DELETE) + @RepeatSubmit() + @PostMapping("/terminationTask") + public R terminationTask(@RequestBody TerminationBo terminationBo) { + return toAjax(actTaskService.terminationTask(terminationBo)); + } + + /** + * 转办任务 + * + * @param transmitBo 参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/transferTask") + public R transferTask(@Validated({AddGroup.class}) @RequestBody TransmitBo transmitBo) { + return toAjax(actTaskService.transferTask(transmitBo)); + } + + /** + * 会签任务加签 + * + * @param addMultiBo 参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/addMultiInstanceExecution") + public R addMultiInstanceExecution(@Validated({AddGroup.class}) @RequestBody AddMultiBo addMultiBo) { + return toAjax(actTaskService.addMultiInstanceExecution(addMultiBo)); + } + + /** + * 会签任务减签 + * + * @param deleteMultiBo 参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/deleteMultiInstanceExecution") + public R deleteMultiInstanceExecution(@Validated({AddGroup.class}) @RequestBody DeleteMultiBo deleteMultiBo) { + return toAjax(actTaskService.deleteMultiInstanceExecution(deleteMultiBo)); + } + + /** + * 驳回审批 + * + * @param backProcessBo 参数 + */ + @Log(title = "任务管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/backProcess") + public R backProcess(@Validated({AddGroup.class}) @RequestBody BackProcessBo backProcessBo) { + return R.ok(actTaskService.backProcess(backProcessBo)); + } + + /** + * 获取当前任务 + * + * @param taskId 任务id + */ + @GetMapping("/getTaskById/{taskId}") + public R getTaskById(@PathVariable String taskId) { + return R.ok(QueryUtils.getTask(taskId)); + } + + + /** + * 修改任务办理人 + * + * @param taskIds 任务id + * @param userId 办理人id + */ + @Log(title = "任务管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping("/updateAssignee/{taskIds}/{userId}") + public R updateAssignee(@PathVariable String[] taskIds, @PathVariable String userId) { + return toAjax(actTaskService.updateAssignee(taskIds, userId)); + } + + /** + * 查询流程变量 + * + * @param taskId 任务id + */ + @GetMapping("/getInstanceVariable/{taskId}") + public R> getProcessInstVariable(@PathVariable String taskId) { + return R.ok(actTaskService.getInstanceVariable(taskId)); + } + + /** + * 获取可驳回得任务节点 + * + * @param processInstanceId 流程实例id + */ + @GetMapping("/getTaskNodeList/{processInstanceId}") + public R> getNodeList(@PathVariable String processInstanceId) { + return R.ok(CollUtil.reverse(wfTaskBackNodeService.getListByInstanceId(processInstanceId))); + } + + /** + * 查询工作流任务用户选择加签人员 + * + * @param taskId 任务id + */ + @GetMapping("/getTaskUserIdsByAddMultiInstance/{taskId}") + public R getTaskUserIdsByAddMultiInstance(@PathVariable String taskId) { + return R.ok(actTaskService.getTaskUserIdsByAddMultiInstance(taskId)); + } + + /** + * 查询工作流选择减签人员 + * + * @param taskId 任务id + */ + @GetMapping("/getListByDeleteMultiInstance/{taskId}") + public R> getListByDeleteMultiInstance(@PathVariable String taskId) { + return R.ok(actTaskService.getListByDeleteMultiInstance(taskId)); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfCategoryController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfCategoryController.java new file mode 100644 index 00000000..8dced892 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfCategoryController.java @@ -0,0 +1,106 @@ +package org.dromara.workflow.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.domain.bo.WfCategoryBo; +import org.dromara.workflow.domain.vo.WfCategoryVo; +import org.dromara.workflow.service.IWfCategoryService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 流程分类 + * + * @author may + * @date 2023-06-28 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/category") +public class WfCategoryController extends BaseController { + + private final IWfCategoryService wfCategoryService; + + /** + * 查询流程分类列表 + */ + @SaCheckPermission("workflow:category:list") + @GetMapping("/list") + public R> list(WfCategoryBo bo) { + List list = wfCategoryService.queryList(bo); + return R.ok(list); + + } + + /** + * 导出流程分类列表 + */ + @SaCheckPermission("workflow:category:export") + @Log(title = "流程分类", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(WfCategoryBo bo, HttpServletResponse response) { + List list = wfCategoryService.queryList(bo); + ExcelUtil.exportExcel(list, "流程分类", WfCategoryVo.class, response); + } + + /** + * 获取流程分类详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("workflow:category:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(wfCategoryService.queryById(id)); + } + + /** + * 新增流程分类 + */ + @SaCheckPermission("workflow:category:add") + @Log(title = "流程分类", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody WfCategoryBo bo) { + return toAjax(wfCategoryService.insertByBo(bo)); + } + + /** + * 修改流程分类 + */ + @SaCheckPermission("workflow:category:edit") + @Log(title = "流程分类", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody WfCategoryBo bo) { + return toAjax(wfCategoryService.updateByBo(bo)); + } + + /** + * 删除流程分类 + * + * @param ids 主键串 + */ + @SaCheckPermission("workflow:category:remove") + @Log(title = "流程分类", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(wfCategoryService.deleteWithValidByIds(List.of(ids), true)); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfDefinitionConfigController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfDefinitionConfigController.java new file mode 100644 index 00000000..176aba2e --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfDefinitionConfigController.java @@ -0,0 +1,79 @@ +package org.dromara.workflow.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.validation.constraints.*; +import org.dromara.workflow.domain.bo.WfDefinitionConfigBo; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.workflow.domain.vo.WfDefinitionConfigVo; +import org.dromara.workflow.service.IWfDefinitionConfigService; + +/** + * 流程定义配置 + * + * @author may + * @date 2024-03-18 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/definitionConfig") +public class WfDefinitionConfigController extends BaseController { + + private final IWfDefinitionConfigService wfDefinitionConfigService; + + + /** + * 获取流程定义配置详细信息 + * + * @param definitionId 主键 + */ + @GetMapping("/getByDefId/{definitionId}") + public R getByDefId(@NotBlank(message = "流程定义ID不能为空") + @PathVariable String definitionId) { + return R.ok(wfDefinitionConfigService.getByDefId(definitionId)); + } + + /** + * 新增流程定义配置 + */ + @Log(title = "流程定义配置", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping("/saveOrUpdate") + public R saveOrUpdate(@Validated(AddGroup.class) @RequestBody WfDefinitionConfigBo bo) { + return toAjax(wfDefinitionConfigService.saveOrUpdate(bo)); + } + + /** + * 删除流程定义配置 + * + * @param ids 主键串 + */ + @Log(title = "流程定义配置", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(wfDefinitionConfigService.deleteByIds(List.of(ids))); + } + + /** + * 查询流程定义配置排除当前查询的流程定义 + * + * @param tableName 表名 + * @param definitionId 流程定义id + */ + @GetMapping("/getByTableNameNotDefId/{tableName}/{definitionId}") + public R> getByTableNameNotDefId(@NotBlank(message = "表名不能为空") @PathVariable String tableName, + @NotBlank(message = "流程定义ID不能为空") @PathVariable String definitionId) { + return R.ok(wfDefinitionConfigService.getByTableNameNotDefId(tableName, definitionId)); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfFormManageController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfFormManageController.java new file mode 100644 index 00000000..198e233b --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/controller/WfFormManageController.java @@ -0,0 +1,114 @@ +package org.dromara.workflow.controller; + +import java.util.List; + +import lombok.RequiredArgsConstructor; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.*; +import cn.dev33.satoken.annotation.SaCheckPermission; +import org.springframework.web.bind.annotation.*; +import org.springframework.validation.annotation.Validated; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.web.core.BaseController; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.workflow.domain.vo.WfFormManageVo; +import org.dromara.workflow.domain.bo.WfFormManageBo; +import org.dromara.workflow.service.IWfFormManageService; +import org.dromara.common.mybatis.core.page.TableDataInfo; + +/** + * 表单管理 + * + * @author may + * @date 2024-03-29 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/formManage") +public class WfFormManageController extends BaseController { + + private final IWfFormManageService wfFormManageService; + + /** + * 查询表单管理列表 + */ + @SaCheckPermission("workflow:formManage:list") + @GetMapping("/list") + public TableDataInfo list(WfFormManageBo bo, PageQuery pageQuery) { + return wfFormManageService.queryPageList(bo, pageQuery); + } + + /** + * 查询表单管理列表 + */ + @SaCheckPermission("workflow:formManage:list") + @GetMapping("/list/selectList") + public R> selectList() { + return R.ok(wfFormManageService.selectList()); + } + + /** + * 导出表单管理列表 + */ + @SaCheckPermission("workflow:formManage:export") + @Log(title = "表单管理", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(WfFormManageBo bo, HttpServletResponse response) { + List list = wfFormManageService.queryList(bo); + ExcelUtil.exportExcel(list, "表单管理", WfFormManageVo.class, response); + } + + /** + * 获取表单管理详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("workflow:formManage:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(wfFormManageService.queryById(id)); + } + + /** + * 新增表单管理 + */ + @SaCheckPermission("workflow:formManage:add") + @Log(title = "表单管理", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody WfFormManageBo bo) { + return toAjax(wfFormManageService.insertByBo(bo)); + } + + /** + * 修改表单管理 + */ + @SaCheckPermission("workflow:formManage:edit") + @Log(title = "表单管理", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody WfFormManageBo bo) { + return toAjax(wfFormManageService.updateByBo(bo)); + } + + /** + * 删除表单管理 + * + * @param ids 主键串 + */ + @SaCheckPermission("workflow:formManage:remove") + @Log(title = "表单管理", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(wfFormManageService.deleteByIds(List.of(ids))); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiProcinst.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiProcinst.java new file mode 100644 index 00000000..e87fb925 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiProcinst.java @@ -0,0 +1,152 @@ +package org.dromara.workflow.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 流程实例对象 act_hi_procinst + * + * @author may + * @date 2023-07-22 + */ +@Data +@TableName("act_hi_procinst") +public class ActHiProcinst implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * + */ + @TableId(value = "ID_") + private String id; + + /** + * + */ + @TableField(value = "REV_") + private Long rev; + + /** + * + */ + @TableField(value = "PROC_INST_ID_") + private String procInstId; + + /** + * + */ + @TableField(value = "BUSINESS_KEY_") + private String businessKey; + + /** + * + */ + @TableField(value = "PROC_DEF_ID_") + private String procDefId; + + /** + * + */ + @TableField(value = "START_TIME_") + private Date startTime; + + /** + * + */ + @TableField(value = "END_TIME_") + private Date endTime; + + /** + * + */ + @TableField(value = "DURATION_") + private Long duration; + + /** + * + */ + @TableField(value = "START_USER_ID_") + private String startUserId; + + /** + * + */ + @TableField(value = "START_ACT_ID_") + private String startActId; + + /** + * + */ + @TableField(value = "END_ACT_ID_") + private String endActId; + + /** + * + */ + @TableField(value = "SUPER_PROCESS_INSTANCE_ID_") + private String superProcessInstanceId; + + /** + * + */ + @TableField(value = "DELETE_REASON_") + private String deleteReason; + + /** + * + */ + @TableField(value = "TENANT_ID_") + private String tenantId; + + /** + * + */ + @TableField(value = "NAME_") + private String name; + + /** + * + */ + @TableField(value = "CALLBACK_ID_") + private String callbackId; + + /** + * + */ + @TableField(value = "CALLBACK_TYPE_") + private String callbackType; + + /** + * + */ + @TableField(value = "REFERENCE_ID_") + private String referenceId; + + /** + * + */ + @TableField(value = "REFERENCE_TYPE_") + private String referenceType; + + /** + * + */ + @TableField(value = "PROPAGATED_STAGE_INST_ID_") + private String propagatedStageInstId; + + /** + * + */ + @TableField(value = "BUSINESS_STATUS_") + private String businessStatus; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiTaskinst.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiTaskinst.java new file mode 100644 index 00000000..abc17b54 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/ActHiTaskinst.java @@ -0,0 +1,193 @@ +package org.dromara.workflow.domain; + +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +import java.io.Serial; + +/** + * 流程历史任务对象 act_hi_taskinst + * + * @author may + * @date 2024-03-02 + */ +@Data +@TableName("act_hi_taskinst") +public class ActHiTaskinst implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * + */ + @TableId(value = "ID_") + private String id; + + /** + * 版本 + */ + @TableField(value = "REV_") + private Long rev; + + /** + * 流程定义id + */ + @TableField(value = "PROC_DEF_ID_") + private String procDefId; + + /** + * + */ + @TableField(value = "TASK_DEF_ID_") + private String taskDefId; + + /** + * 任务节点id + */ + @TableField(value = "TASK_DEF_KEY_") + private String taskDefKey; + + /** + * 流程实例id + */ + @TableField(value = "PROC_INST_ID_") + private String procInstId; + + /** + * 流程执行id + */ + @TableField(value = "EXECUTION_ID") + private String executionId; + + /** + * + */ + @TableField(value = "SCOPE_ID_") + private String scopeId; + + /** + * + */ + @TableField(value = "SUB_SCOPE_ID_") + private String subScopeId; + + /** + * 先用当前字段标识抄送类型 + */ + @TableField(value = "SCOPE_TYPE_") + private String scopeType; + + /** + * + */ + @TableField(value = "SCOPE_DEFINITION_ID_") + private String scopeDefinitionId; + + /** + * + */ + @TableField(value = "PROPAGATED_STAGE_INST_ID_") + private String propagatedStageInstId; + + /** + * 任务名称 + */ + @TableField(value = "NAME_") + private String name; + + /** + * 父级id + */ + @TableField(value = "PARENT_TASK_ID_") + private String parentTaskId; + + /** + * 描述 + */ + @TableField(value = "DESCRIPTION_") + private String description; + + /** + * 办理人 + */ + @TableField(value = "OWNER_") + private String owner; + + /** + * 办理人 + */ + @TableField(value = "ASSIGNEE_") + private String assignee; + + /** + * 开始事件 + */ + @TableField(value = "START_TIME_") + private Date startTime; + + /** + * 认领时间 + */ + @TableField(value = "CLAIM_TIME_") + private Date claimTime; + + /** + * 结束时间 + */ + @TableField(value = "END_TIME_") + private Date endTime; + + /** + * 持续时间 + */ + @TableField(value = "DURATION_") + private Long duration; + + /** + * 删除原因 + */ + @TableField(value = "DELETE_REASON_") + private String deleteReason; + + /** + * 优先级 + */ + @TableField(value = "PRIORITY_") + private Long priority; + + /** + * 到期时间 + */ + @TableField(value = "DUE_DATE_") + private Date dueDate; + + /** + * + */ + @TableField(value = "FORM_KEY_") + private String formKey; + + /** + * 分类 + */ + @TableField(value = "CATEGORY_") + private String category; + + /** + * 最后修改时间 + */ + @TableField(value = "LAST_UPDATED_TIME_") + private Date lastUpdatedTime; + + /** + * 租户id + */ + @TableField(value = "TENANT_ID_") + private String tenantId; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfCategory.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfCategory.java new file mode 100644 index 00000000..94a7cf50 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfCategory.java @@ -0,0 +1,52 @@ +package org.dromara.workflow.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.tenant.core.TenantEntity; + +import java.io.Serial; + +/** + * 流程分类对象 wf_category + * + * @author may + * @date 2023-06-27 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("wf_category") +public class WfCategory extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 分类名称 + */ + private String categoryName; + + /** + * 分类编码 + */ + private String categoryCode; + + /** + * 父级id + */ + private Long parentId; + + /** + * 排序 + */ + private Long sortNum; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfDefinitionConfig.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfDefinitionConfig.java new file mode 100644 index 00000000..11dcaa08 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfDefinitionConfig.java @@ -0,0 +1,56 @@ +package org.dromara.workflow.domain; + +import org.dromara.common.mybatis.core.domain.BaseEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 流程定义配置对象 wf_definition_config + * + * @author may + * @date 2024-03-18 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("wf_definition_config") +public class WfDefinitionConfig extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 表名 + */ + private String tableName; + + /** + * 流程定义ID + */ + private String definitionId; + + /** + * 流程KEY + */ + private String processKey; + + /** + * 流程版本 + */ + private Integer version; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfFormManage.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfFormManage.java new file mode 100644 index 00000000..47f0d7a5 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfFormManage.java @@ -0,0 +1,51 @@ +package org.dromara.workflow.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 表单管理对象 wf_form_manage + * + * @author may + * @date 2024-03-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("wf_form_manage") +public class WfFormManage extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 表单名称 + */ + private String formName; + + /** + * 表单类型 + */ + private String formType; + + /** + * 路由地址/表单ID + */ + private String router; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfNodeConfig.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfNodeConfig.java new file mode 100644 index 00000000..999425f5 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfNodeConfig.java @@ -0,0 +1,61 @@ +package org.dromara.workflow.domain; + +import org.dromara.common.tenant.core.TenantEntity; +import com.baomidou.mybatisplus.annotation.*; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.io.Serial; + +/** + * 节点配置对象 wf_node_config + * + * @author may + * @date 2024-03-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("wf_node_config") +public class WfNodeConfig extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 表单id + */ + private Long formId; + + /** + * 表单类型 + */ + private String formType; + + /** + * 节点名称 + */ + private String nodeName; + + /** + * 节点id + */ + private String nodeId; + + /** + * 流程定义id + */ + private String definitionId; + + /** + * 是否为申请人节点 (0是 1否) + */ + private String applyUserTask; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java new file mode 100644 index 00000000..6f597279 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/WfTaskBackNode.java @@ -0,0 +1,61 @@ +package org.dromara.workflow.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.tenant.core.TenantEntity; + +import java.io.Serial; + +/** + * 节点驳回记录 wf_task_back_node + * + * @author may + * @date 2024-03-13 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("wf_task_back_node") +public class WfTaskBackNode extends TenantEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 实例id + */ + private String instanceId; + + /** + * 节点id + */ + private String nodeId; + + /** + * 节点名称 + */ + private String nodeName; + + /** + * 排序 + */ + private Integer orderNo; + + /** + * 节点类型 + */ + private String taskType; + + /** + * 办理人 + */ + private String assignee; + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/AddMultiBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/AddMultiBo.java new file mode 100644 index 00000000..320ec64c --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/AddMultiBo.java @@ -0,0 +1,40 @@ +package org.dromara.workflow.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 加签参数请求 + * + * @author may + */ +@Data +public class AddMultiBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务ID + */ + @NotBlank(message = "任务ID不能为空", groups = AddGroup.class) + private String taskId; + + /** + * 加签人员id + */ + @NotEmpty(message = "加签人员不能为空", groups = AddGroup.class) + private List assignees; + + /** + * 加签人员名称 + */ + @NotEmpty(message = "加签人员不能为空", groups = AddGroup.class) + private List assigneeNames; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java new file mode 100644 index 00000000..d0f43692 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/BackProcessBo.java @@ -0,0 +1,44 @@ +package org.dromara.workflow.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + + +/** + * 驳回参数请求 + * + * @author may + */ +@Data +public class BackProcessBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务ID + */ + @NotBlank(message = "任务ID不能为空", groups = AddGroup.class) + private String taskId; + + /** + * 消息类型 + */ + private List messageType; + + /** + * 驳回的节点id(目前未使用,直接驳回到申请人) + */ + @NotBlank(message = "驳回的节点不能为空", groups = AddGroup.class) + private String targetActivityId; + + /** + * 办理意见 + */ + private String message; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java new file mode 100644 index 00000000..06239055 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/CompleteTaskBo.java @@ -0,0 +1,65 @@ +package org.dromara.workflow.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.workflow.domain.vo.WfCopy; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * 办理任务请求对象 + * + * @author may + */ +@Data +public class CompleteTaskBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务id + */ + @NotBlank(message = "任务id不能为空", groups = {AddGroup.class}) + private String taskId; + + /** + * 附件id + */ + private String fileId; + + /** + * 抄送人员 + */ + private List wfCopyList; + + /** + * 消息类型 + */ + private List messageType; + + /** + * 办理意见 + */ + private String message; + + /** + * 流程变量 + */ + private Map variables; + + public Map getVariables() { + if (variables == null) { + return new HashMap<>(16); + } + variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); + return variables; + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DelegateBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DelegateBo.java new file mode 100644 index 00000000..a6846a6d --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DelegateBo.java @@ -0,0 +1,38 @@ +package org.dromara.workflow.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 委派任务请求对象 + * + * @author may + */ +@Data +public class DelegateBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 委派人id + */ + @NotBlank(message = "委派人id不能为空", groups = {AddGroup.class}) + private String userId; + + /** + * 委派人名称 + */ + @NotBlank(message = "委派人名称不能为空", groups = {AddGroup.class}) + private String nickName; + + /** + * 任务id + */ + @NotBlank(message = "任务id不能为空", groups = {AddGroup.class}) + private String taskId; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DeleteMultiBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DeleteMultiBo.java new file mode 100644 index 00000000..e533167d --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/DeleteMultiBo.java @@ -0,0 +1,52 @@ +package org.dromara.workflow.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 减签参数请求 + * + * @author may + */ +@Data +public class DeleteMultiBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务ID + */ + @NotBlank(message = "任务ID不能为空", groups = AddGroup.class) + private String taskId; + + /** + * 减签人员 + */ + @NotEmpty(message = "减签人员不能为空", groups = AddGroup.class) + private List taskIds; + + /** + * 执行id + */ + @NotEmpty(message = "执行id不能为空", groups = AddGroup.class) + private List executionIds; + + /** + * 人员id + */ + @NotEmpty(message = "减签人员id不能为空", groups = AddGroup.class) + private List assigneeIds; + + /** + * 人员名称 + */ + @NotEmpty(message = "减签人员不能为空", groups = AddGroup.class) + private List assigneeNames; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ModelBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ModelBo.java new file mode 100644 index 00000000..efe9acdd --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ModelBo.java @@ -0,0 +1,66 @@ +package org.dromara.workflow.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.workflow.common.constant.FlowConstant; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 模型请求对象 + * + * @author may + */ +@Data +public class ModelBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 模型id + */ + @NotBlank(message = "模型ID不能为空", groups = {EditGroup.class}) + private String id; + + /** + * 模型名称 + */ + @NotBlank(message = "模型名称不能为空", groups = {AddGroup.class}) + private String name; + + /** + * 模型标识key + */ + @NotBlank(message = "模型标识key不能为空", groups = {AddGroup.class}) + @Pattern(regexp = FlowConstant.MODEL_KEY_PATTERN, message = "模型标识key只能字符或者下划线开头", groups = {AddGroup.class}) + private String key; + + /** + * 模型分类 + */ + @NotBlank(message = "模型分类不能为空", groups = {AddGroup.class}) + private String categoryCode; + + /** + * 模型XML + */ + @NotBlank(message = "模型XML不能为空", groups = {AddGroup.class}) + private String xml; + + /** + * 模型SVG图片 + */ + @NotBlank(message = "模型SVG不能为空", groups = {EditGroup.class}) + private String svg; + + /** + * 备注 + */ + private String description; + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessDefinitionBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessDefinitionBo.java new file mode 100644 index 00000000..2025932b --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/ProcessDefinitionBo.java @@ -0,0 +1,34 @@ +package org.dromara.workflow.domain.bo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 流程定义请求对象 + * + * @author may + */ +@Data +public class ProcessDefinitionBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程定义名称key + */ + private String key; + + /** + * 流程定义名称 + */ + private String name; + + /** + * 模型分类 + */ + private String categoryCode; + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java new file mode 100644 index 00000000..7af7935e --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/StartProcessBo.java @@ -0,0 +1,49 @@ +package org.dromara.workflow.domain.bo; + + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +/** + * 启动流程对象 + * + * @author may + */ +@Data +public class StartProcessBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 业务唯一值id + */ + @NotBlank(message = "业务ID不能为空", groups = {AddGroup.class}) + private String businessKey; + + /** + * 表名 + */ + @NotBlank(message = "表名不能为空", groups = {AddGroup.class}) + private String tableName; + + /** + * 流程变量,前端会提交一个元素{'entity': {业务详情数据对象}} + */ + private Map variables; + + public Map getVariables() { + if (variables == null) { + return new HashMap<>(16); + } + variables.entrySet().removeIf(entry -> Objects.isNull(entry.getValue())); + return variables; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/SysUserMultiBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/SysUserMultiBo.java new file mode 100644 index 00000000..e4d99e47 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/SysUserMultiBo.java @@ -0,0 +1,39 @@ +package org.dromara.workflow.domain.bo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 用户加签查询 + * + * @author may + */ +@Data +public class SysUserMultiBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 人员名称 + */ + private String userName; + + /** + * 人员名称 + */ + private String nickName; + + /** + * 部门id + */ + private String deptId; + + /** + * 任务id + */ + private String taskId; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskBo.java new file mode 100644 index 00000000..30374793 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TaskBo.java @@ -0,0 +1,33 @@ +package org.dromara.workflow.domain.bo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 任务请求对象 + * + * @author may + */ +@Data +public class TaskBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务名称 + */ + private String name; + + /** + * 流程定义名称 + */ + private String processDefinitionName; + + /** + * 流程定义key + */ + private String processDefinitionKey; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TerminationBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TerminationBo.java new file mode 100644 index 00000000..8f2206e8 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TerminationBo.java @@ -0,0 +1,31 @@ +package org.dromara.workflow.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 终止任务请求对象 + * + * @author may + */ +@Data +public class TerminationBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务id + */ + @NotBlank(message = "任务id为空", groups = AddGroup.class) + private String taskId; + + /** + * 审批意见 + */ + private String comment; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TransmitBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TransmitBo.java new file mode 100644 index 00000000..3eb6609f --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/TransmitBo.java @@ -0,0 +1,37 @@ +package org.dromara.workflow.domain.bo; + +import jakarta.validation.constraints.NotBlank; +import lombok.Data; +import org.dromara.common.core.validate.AddGroup; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 终转办务请求对象 + * + * @author may + */ +@Data +public class TransmitBo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务id + */ + @NotBlank(message = "任务id为空", groups = AddGroup.class) + private String taskId; + + /** + * 转办人id + */ + @NotBlank(message = "转办人不能为空", groups = AddGroup.class) + private String userId; + + /** + * 审批意见 + */ + private String comment; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfCategoryBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfCategoryBo.java new file mode 100644 index 00000000..69608fda --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfCategoryBo.java @@ -0,0 +1,54 @@ +package org.dromara.workflow.domain.bo; + +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.workflow.domain.WfCategory; + +/** + * 流程分类业务对象 wf_category + * + * @author may + * @date 2023-06-27 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = WfCategory.class, reverseConvertGenerate = false) +public class WfCategoryBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 分类名称 + */ + @NotBlank(message = "分类名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String categoryName; + + /** + * 分类编码 + */ + @NotBlank(message = "分类编码不能为空", groups = {AddGroup.class, EditGroup.class}) + private String categoryCode; + + /** + * 父级id + */ + @NotNull(message = "父级id不能为空", groups = {AddGroup.class, EditGroup.class}) + private Long parentId; + + /** + * 排序 + */ + private Long sortNum; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfDefinitionConfigBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfDefinitionConfigBo.java new file mode 100644 index 00000000..fac17709 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfDefinitionConfigBo.java @@ -0,0 +1,59 @@ +package org.dromara.workflow.domain.bo; + +import org.dromara.workflow.domain.WfDefinitionConfig; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 流程定义配置业务对象 wf_form_definition + * + * @author may + * @date 2024-03-18 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = WfDefinitionConfig.class, reverseConvertGenerate = false) +public class WfDefinitionConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 表名 + */ + @NotBlank(message = "表名不能为空", groups = {AddGroup.class}) + private String tableName; + + /** + * 流程定义ID + */ + @NotBlank(message = "流程定义ID不能为空", groups = {AddGroup.class}) + private String definitionId; + + /** + * 流程KEY + */ + @NotBlank(message = "流程KEY不能为空", groups = {AddGroup.class}) + private String processKey; + + /** + * 流程版本 + */ + @NotNull(message = "流程版本不能为空", groups = {AddGroup.class}) + private Integer version; + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfFormManageBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfFormManageBo.java new file mode 100644 index 00000000..8afc286d --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfFormManageBo.java @@ -0,0 +1,53 @@ +package org.dromara.workflow.domain.bo; + +import org.dromara.workflow.domain.WfFormManage; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 表单管理业务对象 wf_form_manage + * + * @author may + * @date 2024-03-29 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = WfFormManage.class, reverseConvertGenerate = false) +public class WfFormManageBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = { EditGroup.class }) + private Long id; + + /** + * 表单名称 + */ + @NotBlank(message = "表单名称不能为空", groups = { AddGroup.class, EditGroup.class }) + private String formName; + + /** + * 表单类型 + */ + @NotBlank(message = "表单类型不能为空", groups = { AddGroup.class, EditGroup.class }) + private String formType; + /** + * 路由地址/表单ID + */ + @NotBlank(message = "路由地址/表单ID不能为空", groups = { AddGroup.class, EditGroup.class }) + private String router; + + + /** + * 备注 + */ + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfNodeConfigBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfNodeConfigBo.java new file mode 100644 index 00000000..de518d3d --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/bo/WfNodeConfigBo.java @@ -0,0 +1,63 @@ +package org.dromara.workflow.domain.bo; + +import org.dromara.workflow.domain.WfNodeConfig; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import lombok.EqualsAndHashCode; +import jakarta.validation.constraints.*; + +/** + * 节点配置业务对象 wf_node_config + * + * @author may + * @date 2024-03-30 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = WfNodeConfig.class, reverseConvertGenerate = false) +public class WfNodeConfigBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 表单id + */ + private Long formId; + + /** + * 表单类型 + */ + private String formType; + + /** + * 节点名称 + */ + @NotBlank(message = "节点名称不能为空", groups = {AddGroup.class, EditGroup.class}) + private String nodeName; + + /** + * 节点id + */ + @NotBlank(message = "节点id不能为空", groups = {AddGroup.class, EditGroup.class}) + private String nodeId; + + /** + * 流程定义id + */ + @NotBlank(message = "流程定义id不能为空", groups = {AddGroup.class, EditGroup.class}) + private String definitionId; + + /** + * 是否为申请人节点 (0是 1否) + */ + @NotBlank(message = "是否为申请人节点不能为空", groups = {AddGroup.class, EditGroup.class}) + private String applyUserTask; + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ActHistoryInfoVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ActHistoryInfoVo.java new file mode 100644 index 00000000..31b97226 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ActHistoryInfoVo.java @@ -0,0 +1,89 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; +import org.flowable.engine.task.Attachment; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 流程审批记录视图 + * + * @author may + */ +@Data +public class ActHistoryInfoVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + /** + * 任务id + */ + private String id; + /** + * 节点id + */ + private String taskDefinitionKey; + /** + * 任务名称 + */ + private String name; + /** + * 流程实例id + */ + private String processInstanceId; + /** + * 开始时间 + */ + private Date startTime; + /** + * 结束时间 + */ + private Date endTime; + /** + * 运行时长 + */ + private String runDuration; + /** + * 状态 + */ + private String status; + /** + * 状态 + */ + private String statusName; + /** + * 办理人id + */ + private String assignee; + + /** + * 办理人名称 + */ + @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "assignee") + private String nickName; + + /** + * 办理人id + */ + private String owner; + + /** + * 审批信息id + */ + private String commentId; + + /** + * 审批信息 + */ + private String comment; + + /** + * 审批附件 + */ + private List attachmentList; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GraphicInfoVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GraphicInfoVo.java new file mode 100644 index 00000000..76361316 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/GraphicInfoVo.java @@ -0,0 +1,47 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 节点图形信息 + * + * @author may + */ +@Data +public class GraphicInfoVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + /** + * x坐标 + */ + private double x; + + /** + * y坐标 + */ + private double y; + + /** + * 节点高度 + */ + private double height; + + /** + * 节点宽度 + */ + private double width; + + /** + * 节点id + */ + private String nodeId; + + /** + * 节点名称 + */ + private String nodeName; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ModelVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ModelVo.java new file mode 100644 index 00000000..b2ce8110 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ModelVo.java @@ -0,0 +1,48 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 模型视图对象 + * + * @author may + */ +@Data +public class ModelVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 模型id + */ + private String id; + + /** + * 模型名称 + */ + private String name; + + /** + * 模型标识key + */ + private String key; + + /** + * 模型分类 + */ + private String categoryCode; + + /** + * 模型XML + */ + private String xml; + + /** + * 备注 + */ + private String description; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/MultiInstanceVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/MultiInstanceVo.java new file mode 100644 index 00000000..b9983960 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/MultiInstanceVo.java @@ -0,0 +1,33 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 多实例信息 + * + * @author may + */ +@Data +public class MultiInstanceVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 会签类型(串行,并行) + */ + private Object type; + + /** + * 会签人员KEY + */ + private String assignee; + + /** + * 会签人员集合KEY + */ + private String assigneeList; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ParticipantVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ParticipantVo.java new file mode 100644 index 00000000..c5876f68 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ParticipantVo.java @@ -0,0 +1,43 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** + * 参与者 + * + * @author may + */ +@Data +public class ParticipantVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 组id(角色id) + */ + private List groupIds; + + /** + * 候选人id(用户id) 当组id不为空时,将组内人员查出放入candidate + */ + private List candidate; + + /** + * 候选人名称(用户名称) 当组id不为空时,将组内人员查出放入candidateName + */ + private List candidateName; + + /** + * 是否认领标识 + * 当为空时默认当前任务不需要认领 + * 当为true时当前任务说明为候选模式并且有人已经认领了任务可以归还, + * 当为false时当前任务说明为候选模式该任务未认领, + */ + private Boolean claim; + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessDefinitionVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessDefinitionVo.java new file mode 100644 index 00000000..034adbb2 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessDefinitionVo.java @@ -0,0 +1,70 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 流程定义视图 + * + * @author may + */ +@Data +public class ProcessDefinitionVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程定义id + */ + private String id; + + /** + * 流程定义名称 + */ + private String name; + + /** + * 流程定义标识key + */ + private String key; + + /** + * 流程定义版本 + */ + private int version; + + /** + * 流程定义挂起或激活 1激活 2挂起 + */ + private int suspensionState; + + /** + * 流程xml名称 + */ + private String resourceName; + + /** + * 流程图片名称 + */ + private String diagramResourceName; + + /** + * 流程部署id + */ + private String deploymentId; + + /** + * 流程部署时间 + */ + private Date deploymentTime; + + /** + * 流程定义配置 + */ + private WfDefinitionConfigVo wfDefinitionConfigVo; + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessInstanceVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessInstanceVo.java new file mode 100644 index 00000000..ab3e7a13 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/ProcessInstanceVo.java @@ -0,0 +1,100 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * 流程实例视图 + * + * @author may + */ +@Data +public class ProcessInstanceVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 流程实例id + */ + private String id; + + /** + * 流程定义id + */ + private String processDefinitionId; + + /** + * 流程定义名称 + */ + private String processDefinitionName; + + /** + * 流程定义key + */ + private String processDefinitionKey; + + /** + * 流程定义版本 + */ + private Integer processDefinitionVersion; + + /** + * 部署id + */ + private String deploymentId; + + /** + * 业务id + */ + private String businessKey; + + /** + * 是否挂起 + */ + private Boolean isSuspended; + + /** + * 租户id + */ + private String tenantId; + + /** + * 启动时间 + */ + private Date startTime; + + /** + * 结束时间 + */ + private Date endTime; + + /** + * 启动人id + */ + private String startUserId; + + /** + * 流程状态 + */ + private String businessStatus; + + /** + * 流程状态 + */ + private String businessStatusName; + + /** + * 待办任务集合 + */ + private List taskVoList; + + /** + * 节点配置 + */ + private WfNodeConfigVo wfNodeConfigVo; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TaskVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TaskVo.java new file mode 100644 index 00000000..466e7769 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/TaskVo.java @@ -0,0 +1,173 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; +import org.dromara.common.translation.annotation.Translation; +import org.dromara.common.translation.constant.TransConstant; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + +/** + * 任务视图 + * + * @author may + */ +@Data +public class TaskVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 任务id + */ + private String id; + + /** + * 任务名称 + */ + private String name; + + /** + * 描述 + */ + private String description; + + /** + * 优先级 + */ + private Integer priority; + + /** + * 负责此任务的人员的用户id + */ + private String owner; + + /** + * 办理人id + */ + private Long assignee; + + /** + * 办理人 + */ + @Translation(type = TransConstant.USER_ID_TO_NICKNAME, mapper = "assignee") + private String assigneeName; + + + /** + * 流程实例id + */ + private String processInstanceId; + + /** + * 执行id + */ + private String executionId; + + /** + * 无用 + */ + private String taskDefinitionId; + + /** + * 流程定义id + */ + private String processDefinitionId; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 已办任务-创建时间 + */ + private Date startTime; + + /** + * 结束时间 + */ + private Date endTime; + + /** + * 节点id + */ + private String taskDefinitionKey; + + /** + * 任务截止日期 + */ + private Date dueDate; + + /** + * 流程类别 + */ + private String category; + + /** + * 父级任务id + */ + private String parentTaskId; + + /** + * 租户id + */ + private String tenantId; + + /** + * 认领时间 + */ + private Date claimTime; + + /** + * 流程状态 + */ + private String businessStatus; + + /** + * 流程状态 + */ + private String businessStatusName; + + /** + * 流程定义名称 + */ + private String processDefinitionName; + + /** + * 流程定义key + */ + private String processDefinitionKey; + + /** + * 流程定义版本 + */ + private Integer processDefinitionVersion; + + /** + * 参与者 + */ + private ParticipantVo participantVo; + + /** + * 是否会签 + */ + private Boolean multiInstance; + + /** + * 业务id + */ + private String businessKey; + + /** + * 流程定义配置 + */ + private WfDefinitionConfigVo wfDefinitionConfigVo; + + /** + * 节点配置 + */ + private WfNodeConfigVo wfNodeConfigVo; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/VariableVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/VariableVo.java new file mode 100644 index 00000000..6a26c828 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/VariableVo.java @@ -0,0 +1,28 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 流程变量 + * + * @author may + */ +@Data +public class VariableVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 变量key + */ + private String key; + + /** + * 变量值 + */ + private String value; +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCategoryVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCategoryVo.java new file mode 100644 index 00000000..362f6462 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCategoryVo.java @@ -0,0 +1,58 @@ +package org.dromara.workflow.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.workflow.domain.WfCategory; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 流程分类视图对象 wf_category + * + * @author may + * @date 2023-06-27 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = WfCategory.class) +public class WfCategoryVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 分类名称 + */ + @ExcelProperty(value = "分类名称") + private String categoryName; + + /** + * 分类编码 + */ + @ExcelProperty(value = "分类编码") + private String categoryCode; + + /** + * 父级id + */ + @ExcelProperty(value = "父级id") + private Long parentId; + + /** + * 排序 + */ + @ExcelProperty(value = "排序") + private Long sortNum; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCopy.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCopy.java new file mode 100644 index 00000000..88a5a218 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfCopy.java @@ -0,0 +1,29 @@ +package org.dromara.workflow.domain.vo; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + +/** + * 抄送 + * + * @author may + */ +@Data +public class WfCopy implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 用户id + */ + private Long userId; + + /** + * 用户名称 + */ + private String userName; + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfDefinitionConfigVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfDefinitionConfigVo.java new file mode 100644 index 00000000..9c7b0d7d --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfDefinitionConfigVo.java @@ -0,0 +1,70 @@ +package org.dromara.workflow.domain.vo; + +import org.dromara.workflow.domain.WfDefinitionConfig; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 流程定义配置视图对象 wf_definition_config + * + * @author may + * @date 2024-03-18 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = WfDefinitionConfig.class) +public class WfDefinitionConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 表名 + */ + @ExcelProperty(value = "表名") + private String tableName; + + /** + * 流程定义ID + */ + @ExcelProperty(value = "流程定义ID") + private String definitionId; + + /** + * 流程KEY + */ + @ExcelProperty(value = "流程KEY") + private String processKey; + + + /** + * 流程版本 + */ + @ExcelProperty(value = "流程版本") + private Integer version; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + /** + * 表单管理 + */ + private WfFormManageVo wfFormManageVo; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfFormManageVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfFormManageVo.java new file mode 100644 index 00000000..302df239 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfFormManageVo.java @@ -0,0 +1,63 @@ +package org.dromara.workflow.domain.vo; + +import org.dromara.workflow.domain.WfFormManage; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 表单管理视图对象 wf_form_manage + * + * @author may + * @date 2024-03-29 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = WfFormManage.class) +public class WfFormManageVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 表单名称 + */ + @ExcelProperty(value = "表单名称") + private String formName; + + /** + * 表单类型 + */ + @ExcelProperty(value = "表单类型") + private String formType; + + /** + * 表单类型名称 + */ + private String formTypeName; + + /** + * 路由地址/表单ID + */ + @ExcelProperty(value = "路由地址/表单ID") + private String router; + + /** + * 备注 + */ + @ExcelProperty(value = "备注") + private String remark; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfNodeConfigVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfNodeConfigVo.java new file mode 100644 index 00000000..89e9d9b3 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/domain/vo/WfNodeConfigVo.java @@ -0,0 +1,75 @@ +package org.dromara.workflow.domain.vo; + +import org.dromara.workflow.domain.WfNodeConfig; +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; + + +/** + * 节点配置视图对象 wf_node_config + * + * @author may + * @date 2024-03-30 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = WfNodeConfig.class) +public class WfNodeConfigVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 表单id + */ + @ExcelProperty(value = "表单id") + private Long formId; + + /** + * 表单类型 + */ + @ExcelProperty(value = "表单类型") + private String formType; + + /** + * 节点名称 + */ + @ExcelProperty(value = "节点名称") + private String nodeName; + + /** + * 节点id + */ + @ExcelProperty(value = "节点id") + private String nodeId; + + /** + * 流程定义id + */ + @ExcelProperty(value = "流程定义id") + private String definitionId; + + /** + * 是否为申请人节点 (0是 1否) + */ + @ExcelProperty(value = "是否为申请人节点 (0是 1否)") + private String applyUserTask; + + /** + * 表单管理 + */ + private WfFormManageVo wfFormManageVo; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/dubbo/RemoteWorkflowServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/dubbo/RemoteWorkflowServiceImpl.java new file mode 100644 index 00000000..5b990c19 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/dubbo/RemoteWorkflowServiceImpl.java @@ -0,0 +1,92 @@ +package org.dromara.workflow.dubbo; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.bean.copier.CopyOptions; +import lombok.RequiredArgsConstructor; +import org.apache.dubbo.config.annotation.DubboService; +import org.dromara.workflow.api.domain.RemoteWorkflowService; +import org.dromara.workflow.api.domain.dto.BusinessInstanceDTO; +import org.dromara.workflow.common.enums.BusinessStatusEnum; +import org.dromara.workflow.domain.ActHiProcinst; +import org.dromara.workflow.service.IActHiProcinstService; +import org.dromara.workflow.service.WorkflowService; + +import java.util.List; +import java.util.Map; + +/** + * RemoteWorkflowServiceImpl + * + * @Author ZETA + * @Date 2024/6/3 + */ +@DubboService +@RequiredArgsConstructor +public class RemoteWorkflowServiceImpl implements RemoteWorkflowService { + + private final WorkflowService workflowService; + private final IActHiProcinstService actHiProcinstService; + + @Override + public boolean deleteRunAndHisInstance(List businessKeys) { + return workflowService.deleteRunAndHisInstance(businessKeys); + } + + @Override + public String getBusinessStatusByTaskId(String taskId) { + return workflowService.getBusinessStatusByTaskId(taskId); + } + + @Override + public String getBusinessStatus(String businessKey) { + return workflowService.getBusinessStatus(businessKey); + } + + @Override + public BusinessInstanceDTO getBusinessInstance(String businessKey) { + + ActHiProcinst actHiProcinst = actHiProcinstService.selectByBusinessKey(businessKey); + if (actHiProcinst == null) { + BusinessInstanceDTO businessInstanceDTO = new BusinessInstanceDTO(); + businessInstanceDTO.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus()); + return businessInstanceDTO; + } + + BusinessInstanceDTO businessInstanceDTO = BeanUtil.toBean(actHiProcinst, BusinessInstanceDTO.class); + businessInstanceDTO.setBusinessStatusName(BusinessStatusEnum.findByStatus(businessInstanceDTO.getBusinessStatus())); + businessInstanceDTO.setProcessDefinitionId(actHiProcinst.getProcDefId()); + return businessInstanceDTO; + } + + @Override + public List getBusinessInstance(List businessKeys) { + List actHiProcinstList = actHiProcinstService.selectByBusinessKeyIn(businessKeys); + List businessInstanceList = BeanUtil.copyToList(actHiProcinstList, BusinessInstanceDTO.class, + CopyOptions.create().setFieldMapping(Map.of("procDefId", "processDefinitionId"))); + businessInstanceList.forEach(dto -> { + dto.setBusinessStatusName(BusinessStatusEnum.findByStatus(dto.getBusinessStatus())); + }); + + return businessInstanceList; + } + + @Override + public void setVariable(String taskId, String variableName, Object value) { + workflowService.setVariable(taskId, variableName, value); + } + + @Override + public void setVariables(String taskId, Map variables) { + workflowService.setVariables(taskId, variables); + } + + @Override + public void setVariableLocal(String taskId, String variableName, Object value) { + workflowService.setVariableLocal(taskId, variableName, value); + } + + @Override + public void setVariablesLocal(String taskId, Map variables) { + workflowService.setVariablesLocal(taskId, variables); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramCanvas.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramCanvas.java new file mode 100644 index 00000000..39fd9d36 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramCanvas.java @@ -0,0 +1,108 @@ +package org.dromara.workflow.flowable; + +import org.flowable.bpmn.model.AssociationDirection; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; + +import java.awt.*; +import java.awt.geom.Line2D; +import java.awt.geom.RoundRectangle2D; + +public class CustomDefaultProcessDiagramCanvas extends DefaultProcessDiagramCanvas { + //设置高亮线的颜色 这里我设置成绿色 + protected static Color HIGHLIGHT_SEQUENCEFLOW_COLOR = Color.GREEN; + + public CustomDefaultProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + } + + /** + * 画线颜色设置 + */ + public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, + AssociationDirection associationDirection, boolean highLighted, double scaleFactor) { + + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(CONNECTION_COLOR); + if (connectionType.equals("association")) { + g.setStroke(ASSOCIATION_STROKE); + } else if (highLighted) { + //设置线的颜色 + g.setPaint(HIGHLIGHT_SEQUENCEFLOW_COLOR); + g.setStroke(HIGHLIGHT_FLOW_STROKE); + } + + for (int i = 1; i < xPoints.length; i++) { + Integer sourceX = xPoints[i - 1]; + Integer sourceY = yPoints[i - 1]; + Integer targetX = xPoints[i]; + Integer targetY = yPoints[i]; + Line2D.Double line = new Line2D.Double(sourceX, sourceY, targetX, targetY); + g.draw(line); + } + + if (isDefault) { + Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]); + drawDefaultSequenceFlowIndicator(line, scaleFactor); + } + + if (conditional) { + Line2D.Double line = new Line2D.Double(xPoints[0], yPoints[0], xPoints[1], yPoints[1]); + drawConditionalSequenceFlowIndicator(line, scaleFactor); + } + + if (associationDirection == AssociationDirection.ONE || associationDirection == AssociationDirection.BOTH) { + Line2D.Double line = new Line2D.Double(xPoints[xPoints.length - 2], yPoints[xPoints.length - 2], xPoints[xPoints.length - 1], yPoints[xPoints.length - 1]); + drawArrowHead(line, scaleFactor); + } + if (associationDirection == AssociationDirection.BOTH) { + Line2D.Double line = new Line2D.Double(xPoints[1], yPoints[1], xPoints[0], yPoints[0]); + drawArrowHead(line, scaleFactor); + } + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 高亮节点设置 + */ + public void drawHighLight(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + //设置高亮节点的颜色 + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * @description: 高亮节点红色 + * @param: x + * @param: y + * @param: width + * @param: height + * @return: void + * @author: gssong + * @date: 2022/4/12 + */ + public void drawHighLightRed(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + //设置高亮节点的颜色 + g.setPaint(Color.green); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramGenerator.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramGenerator.java new file mode 100644 index 00000000..e4793a26 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/CustomDefaultProcessDiagramGenerator.java @@ -0,0 +1,1120 @@ +/* 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 + * + * http://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.workflow.flowable; + +import org.flowable.bpmn.model.Event; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; +import org.flowable.image.ProcessDiagramGenerator; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.InputStream; +import java.util.List; +import java.util.*; + +/** + * Class to generate an image based the diagram interchange information in a BPMN 2.0 process. + * + * @author Joram Barrez + * @author Tijs Rademakers + * @author Zheng Ji + */ +public class CustomDefaultProcessDiagramGenerator implements ProcessDiagramGenerator { + + protected Map, ActivityDrawInstruction> activityDrawInstructions = new HashMap<>(); + protected Map, ArtifactDrawInstruction> artifactDrawInstructions = new HashMap<>(); + + public CustomDefaultProcessDiagramGenerator() { + this(1.0); + } + + // The instructions on how to draw a certain construct is + // created statically and stored in a map for performance. + public CustomDefaultProcessDiagramGenerator(final double scaleFactor) { + // start event + activityDrawInstructions.put(StartEvent.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + StartEvent startEvent = (StartEvent) flowNode; + if (startEvent.getEventDefinitions() != null && !startEvent.getEventDefinitions().isEmpty()) { + EventDefinition eventDefinition = startEvent.getEventDefinitions().get(0); + if (eventDefinition instanceof TimerEventDefinition) { + processDiagramCanvas.drawTimerStartEvent(graphicInfo, scaleFactor); + } else if (eventDefinition instanceof ErrorEventDefinition) { + processDiagramCanvas.drawErrorStartEvent(graphicInfo, scaleFactor); + } else if (eventDefinition instanceof EscalationEventDefinition) { + processDiagramCanvas.drawEscalationStartEvent(graphicInfo, scaleFactor); + } else if (eventDefinition instanceof ConditionalEventDefinition) { + processDiagramCanvas.drawConditionalStartEvent(graphicInfo, scaleFactor); + } else if (eventDefinition instanceof SignalEventDefinition) { + processDiagramCanvas.drawSignalStartEvent(graphicInfo, scaleFactor); + } else if (eventDefinition instanceof MessageEventDefinition) { + processDiagramCanvas.drawMessageStartEvent(graphicInfo, scaleFactor); + } else { + processDiagramCanvas.drawNoneStartEvent(graphicInfo); + } + } else { + List eventTypeElements = startEvent.getExtensionElements().get("eventType"); + if (eventTypeElements != null && eventTypeElements.size() > 0) { + processDiagramCanvas.drawEventRegistryStartEvent(graphicInfo, scaleFactor); + + } else { + processDiagramCanvas.drawNoneStartEvent(graphicInfo); + } + } + } + }); + + // signal catch + activityDrawInstructions.put(IntermediateCatchEvent.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + IntermediateCatchEvent intermediateCatchEvent = (IntermediateCatchEvent) flowNode; + if (intermediateCatchEvent.getEventDefinitions() != null && !intermediateCatchEvent.getEventDefinitions().isEmpty()) { + + if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) { + processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, true, scaleFactor); + } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof TimerEventDefinition) { + processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, true, scaleFactor); + } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof MessageEventDefinition) { + processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, true, scaleFactor); + } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof ConditionalEventDefinition) { + processDiagramCanvas.drawCatchingConditionalEvent(flowNode.getName(), graphicInfo, true, scaleFactor); + } + } + } + }); + + // signal throw + activityDrawInstructions.put(ThrowEvent.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + ThrowEvent throwEvent = (ThrowEvent) flowNode; + if (throwEvent.getEventDefinitions() != null && !throwEvent.getEventDefinitions().isEmpty()) { + if (throwEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) { + processDiagramCanvas.drawThrowingSignalEvent(graphicInfo, scaleFactor); + } else if (throwEvent.getEventDefinitions().get(0) instanceof EscalationEventDefinition) { + processDiagramCanvas.drawThrowingEscalationEvent(graphicInfo, scaleFactor); + } else if (throwEvent.getEventDefinitions().get(0) instanceof CompensateEventDefinition) { + processDiagramCanvas.drawThrowingCompensateEvent(graphicInfo, scaleFactor); + } else { + processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor); + } + } else { + processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor); + } + } + }); + + // end event + activityDrawInstructions.put(EndEvent.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + EndEvent endEvent = (EndEvent) flowNode; + if (endEvent.getEventDefinitions() != null && !endEvent.getEventDefinitions().isEmpty()) { + if (endEvent.getEventDefinitions().get(0) instanceof ErrorEventDefinition) { + processDiagramCanvas.drawErrorEndEvent(flowNode.getName(), graphicInfo, scaleFactor); + } else if (endEvent.getEventDefinitions().get(0) instanceof EscalationEventDefinition) { + processDiagramCanvas.drawEscalationEndEvent(flowNode.getName(), graphicInfo, scaleFactor); + } else { + processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor); + } + } else { + processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor); + } + } + }); + + // task + activityDrawInstructions.put(Task.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawTask(flowNode.getName(), graphicInfo, scaleFactor); + } + }); + + // user task + activityDrawInstructions.put(UserTask.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawUserTask(flowNode.getName(), graphicInfo, scaleFactor); + } + }); + + // script task + activityDrawInstructions.put(ScriptTask.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawScriptTask(flowNode.getName(), graphicInfo, scaleFactor); + } + }); + + // service task + activityDrawInstructions.put(ServiceTask.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + ServiceTask serviceTask = (ServiceTask) flowNode; + if ("camel".equalsIgnoreCase(serviceTask.getType())) { + processDiagramCanvas.drawCamelTask(serviceTask.getName(), graphicInfo, scaleFactor); + }else if (ServiceTask.HTTP_TASK.equalsIgnoreCase(serviceTask.getType())) { + processDiagramCanvas.drawHttpTask(serviceTask.getName(), graphicInfo, scaleFactor); + } else if (ServiceTask.DMN_TASK.equalsIgnoreCase(serviceTask.getType())) { + processDiagramCanvas.drawDMNTask(serviceTask.getName(), graphicInfo, scaleFactor); + } else if (ServiceTask.SHELL_TASK.equalsIgnoreCase(serviceTask.getType())) { + processDiagramCanvas.drawShellTask(serviceTask.getName(), graphicInfo, scaleFactor); + } else { + processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor); + } + } + }); + + // http service task + activityDrawInstructions.put(HttpServiceTask.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawHttpTask(flowNode.getName(), graphicInfo, scaleFactor); + } + }); + + // receive task + activityDrawInstructions.put(ReceiveTask.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawReceiveTask(flowNode.getName(), graphicInfo, scaleFactor); + } + }); + + // send task + activityDrawInstructions.put(SendTask.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawSendTask(flowNode.getName(), graphicInfo, scaleFactor); + } + }); + + // manual task + activityDrawInstructions.put(ManualTask.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawManualTask(flowNode.getName(), graphicInfo, scaleFactor); + } + }); + + // send event service task + activityDrawInstructions.put(SendEventServiceTask.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawSendEventServiceTask(flowNode.getName(), graphicInfo, scaleFactor); + } + }); + + // external worker service task + activityDrawInstructions.put(ExternalWorkerServiceTask.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + ServiceTask serviceTask = (ServiceTask) flowNode; + processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor); + } + }); + + // case service task + activityDrawInstructions.put(CaseServiceTask.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawCaseServiceTask(flowNode.getName(), graphicInfo, scaleFactor); + } + }); + + // businessRuleTask task + activityDrawInstructions.put(BusinessRuleTask.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawBusinessRuleTask(flowNode.getName(), graphicInfo, scaleFactor); + } + }); + + // exclusive gateway + activityDrawInstructions.put(ExclusiveGateway.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawExclusiveGateway(graphicInfo, scaleFactor); + } + }); + + // inclusive gateway + activityDrawInstructions.put(InclusiveGateway.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawInclusiveGateway(graphicInfo, scaleFactor); + } + }); + + // parallel gateway + activityDrawInstructions.put(ParallelGateway.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawParallelGateway(graphicInfo, scaleFactor); + } + }); + + // event based gateway + activityDrawInstructions.put(EventGateway.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawEventBasedGateway(graphicInfo, scaleFactor); + } + }); + + // Boundary timer + activityDrawInstructions.put(BoundaryEvent.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + BoundaryEvent boundaryEvent = (BoundaryEvent) flowNode; + if (boundaryEvent.getEventDefinitions() != null && !boundaryEvent.getEventDefinitions().isEmpty()) { + EventDefinition eventDefinition = boundaryEvent.getEventDefinitions().get(0); + if (eventDefinition instanceof TimerEventDefinition) { + processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); + + } else if (eventDefinition instanceof ConditionalEventDefinition) { + processDiagramCanvas.drawCatchingConditionalEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); + + } else if (eventDefinition instanceof ErrorEventDefinition) { + processDiagramCanvas.drawCatchingErrorEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); + + } else if (eventDefinition instanceof EscalationEventDefinition) { + processDiagramCanvas.drawCatchingEscalationEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); + + } else if (eventDefinition instanceof SignalEventDefinition) { + processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); + + } else if (eventDefinition instanceof MessageEventDefinition) { + processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); + + } else if (eventDefinition instanceof CompensateEventDefinition) { + processDiagramCanvas.drawCatchingCompensateEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); + } + + } else { + List eventTypeElements = boundaryEvent.getExtensionElements().get("eventType"); + if (eventTypeElements != null && eventTypeElements.size() > 0) { + processDiagramCanvas.drawCatchingEventRegistryEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor); + } + } + } + }); + + // subprocess + activityDrawInstructions.put(SubProcess.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { + processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor); + } else { + processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor); + } + } + }); + + // transaction + activityDrawInstructions.put(Transaction.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { + processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor); + } else { + processDiagramCanvas.drawExpandedTransaction(flowNode.getName(), graphicInfo, scaleFactor); + } + } + }); + + // Event subprocess + activityDrawInstructions.put(EventSubProcess.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { + processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor); + } else { + processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor); + } + } + }); + + // Adhoc subprocess + activityDrawInstructions.put(AdhocSubProcess.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { + processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor); + } else { + processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor); + } + } + }); + + // call activity + activityDrawInstructions.put(CallActivity.class, new ActivityDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + processDiagramCanvas.drawCollapsedCallActivity(flowNode.getName(), graphicInfo, scaleFactor); + } + }); + + // text annotation + artifactDrawInstructions.put(TextAnnotation.class, new ArtifactDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(artifact.getId()); + TextAnnotation textAnnotation = (TextAnnotation) artifact; + processDiagramCanvas.drawTextAnnotation(textAnnotation.getText(), graphicInfo, scaleFactor); + } + }); + + // association + artifactDrawInstructions.put(Association.class, new ArtifactDrawInstruction() { + + @Override + public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) { + Association association = (Association) artifact; + String sourceRef = association.getSourceRef(); + String targetRef = association.getTargetRef(); + + // source and target can be instance of FlowElement or Artifact + BaseElement sourceElement = bpmnModel.getFlowElement(sourceRef); + BaseElement targetElement = bpmnModel.getFlowElement(targetRef); + if (sourceElement == null) { + sourceElement = bpmnModel.getArtifact(sourceRef); + } + if (targetElement == null) { + targetElement = bpmnModel.getArtifact(targetRef); + } + List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId()); + graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList); + int[] xPoints = new int[graphicInfoList.size()]; + int[] yPoints = new int[graphicInfoList.size()]; + for (int i = 1; i < graphicInfoList.size(); i++) { + GraphicInfo graphicInfo = graphicInfoList.get(i); + GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); + + if (i == 1) { + xPoints[0] = (int) previousGraphicInfo.getX(); + yPoints[0] = (int) previousGraphicInfo.getY(); + } + xPoints[i] = (int) graphicInfo.getX(); + yPoints[i] = (int) graphicInfo.getY(); + } + + AssociationDirection associationDirection = association.getAssociationDirection(); + processDiagramCanvas.drawAssociation(xPoints, yPoints, associationDirection, false, scaleFactor); + } + }); + } + + @Override + public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, + String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + + return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, + activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI).generateImage(imageType); + } + + @Override + public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, boolean drawSequenceFlowNameWithNoLabelDI) { + return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, 1.0, drawSequenceFlowNameWithNoLabelDI); + } + + @Override + public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, + List highLightedActivities, List highLightedFlows, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + + @Override + public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, boolean drawSequenceFlowNameWithNoLabelDI) { + return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList(), drawSequenceFlowNameWithNoLabelDI); + } + + @Override + public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList(), scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + + @Override + public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName, + String labelFontName, String annotationFontName, ClassLoader customClassLoader, boolean drawSequenceFlowNameWithNoLabelDI) { + + return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(), + activityFontName, labelFontName, annotationFontName, customClassLoader, 1.0, drawSequenceFlowNameWithNoLabelDI); + } + + @Override + public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName, + String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + + return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(), + activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + + @Override + public InputStream generatePngDiagram(BpmnModel bpmnModel, boolean drawSequenceFlowNameWithNoLabelDI) { + return generatePngDiagram(bpmnModel, 1.0, drawSequenceFlowNameWithNoLabelDI); + } + + @Override + public InputStream generatePngDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + return generateDiagram(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + + @Override + public InputStream generateJpgDiagram(BpmnModel bpmnModel) { + return generateJpgDiagram(bpmnModel, 1.0, false); + } + + @Override + public InputStream generateJpgDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + return generateDiagram(bpmnModel, "jpg", Collections.emptyList(), Collections.emptyList(), drawSequenceFlowNameWithNoLabelDI); + } + + public BufferedImage generateImage(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, + String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + + return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, + activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI).generateBufferedImage(imageType); + } + + public BufferedImage generateImage(BpmnModel bpmnModel, String imageType, + List highLightedActivities, List highLightedFlows, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + + return generateImage(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + + @Override + public BufferedImage generatePngImage(BpmnModel bpmnModel, double scaleFactor) { + return generateImage(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor, false); + } + + protected CustomDefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType, + List highLightedActivities, List highLightedFlows, + String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + + prepareBpmnModel(bpmnModel); + + CustomDefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + + // Draw pool shape, if process is participant in collaboration + for (Pool pool : bpmnModel.getPools()) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId()); + processDiagramCanvas.drawPoolOrLane(pool.getName(), graphicInfo, scaleFactor); + } + + // Draw lanes + for (Process process : bpmnModel.getProcesses()) { + for (Lane lane : process.getLanes()) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(lane.getId()); + processDiagramCanvas.drawPoolOrLane(lane.getName(), graphicInfo, scaleFactor); + } + } + + // Draw activities and their sequence-flows + for (Process process : bpmnModel.getProcesses()) { + for (FlowNode flowNode : process.findFlowElementsOfType(FlowNode.class)) { + if (!isPartOfCollapsedSubProcess(flowNode, bpmnModel)) { + drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + } + } + + // Draw artifacts + for (Process process : bpmnModel.getProcesses()) { + + for (Artifact artifact : process.getArtifacts()) { + drawArtifact(processDiagramCanvas, bpmnModel, artifact); + } + + List subProcesses = process.findFlowElementsOfType(SubProcess.class, true); + if (subProcesses != null) { + for (SubProcess subProcess : subProcesses) { + + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(subProcess.getId()); + if (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { + continue; + } + + if (!isPartOfCollapsedSubProcess(subProcess, bpmnModel)) { + for (Artifact subProcessArtifact : subProcess.getArtifacts()) { + drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact); + } + } + } + } + } + + return processDiagramCanvas; + } + + protected void prepareBpmnModel(BpmnModel bpmnModel) { + + // Need to make sure all elements have positive x and y. + // Check all graphicInfo and update the elements accordingly + + List allGraphicInfos = new ArrayList<>(); + if (bpmnModel.getLocationMap() != null) { + allGraphicInfos.addAll(bpmnModel.getLocationMap().values()); + } + if (bpmnModel.getLabelLocationMap() != null) { + allGraphicInfos.addAll(bpmnModel.getLabelLocationMap().values()); + } + if (bpmnModel.getFlowLocationMap() != null) { + for (List flowGraphicInfos : bpmnModel.getFlowLocationMap().values()) { + allGraphicInfos.addAll(flowGraphicInfos); + } + } + + if (allGraphicInfos.size() > 0) { + + boolean needsTranslationX = false; + boolean needsTranslationY = false; + + double lowestX = 0.0; + double lowestY = 0.0; + + // Collect lowest x and y + for (GraphicInfo graphicInfo : allGraphicInfos) { + + double x = graphicInfo.getX(); + double y = graphicInfo.getY(); + + if (x < lowestX) { + needsTranslationX = true; + lowestX = x; + } + if (y < lowestY) { + needsTranslationY = true; + lowestY = y; + } + + } + + // Update all graphicInfo objects + if (needsTranslationX || needsTranslationY) { + + double translationX = Math.abs(lowestX); + double translationY = Math.abs(lowestY); + + for (GraphicInfo graphicInfo : allGraphicInfos) { + if (needsTranslationX) { + graphicInfo.setX(graphicInfo.getX() + translationX); + } + if (needsTranslationY) { + graphicInfo.setY(graphicInfo.getY() + translationY); + } + } + } + + } + + } + + protected void drawActivity(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, + FlowNode flowNode, List highLightedActivities, List highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) { + + ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass()); + if (drawInstruction != null) { + + drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode); + + // Gather info on the multi instance marker + boolean multiInstanceSequential = false; + boolean multiInstanceParallel = false; + boolean collapsed = false; + if (flowNode instanceof Activity) { + Activity activity = (Activity) flowNode; + MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics(); + if (multiInstanceLoopCharacteristics != null) { + multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential(); + multiInstanceParallel = !multiInstanceSequential; + } + } + + // Gather info on the collapsed marker + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + if (flowNode instanceof SubProcess) { + collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded(); + } else if (flowNode instanceof CallActivity) { + collapsed = true; + } + + if (scaleFactor == 1.0) { + // Actually draw the markers + processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), + multiInstanceSequential, multiInstanceParallel, collapsed); + } + + // Draw highlighted activities + if (highLightedActivities.contains(flowNode.getId())) { + drawHighLightRed(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } else if (highLightedActivities.contains(Color.RED.toString() + flowNode.getId())) { + drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + + } else if (flowNode instanceof Task) { + activityDrawInstructions.get(Task.class).draw(processDiagramCanvas, bpmnModel, flowNode); + + if (highLightedActivities.contains(flowNode.getId())) { + drawHighLightRed(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } else if (highLightedActivities.contains(Color.RED.toString() + flowNode.getId())) { + drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + } + + // Outgoing transitions of activity + for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) { + boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId())); + String defaultFlow = null; + if (flowNode instanceof Activity) { + defaultFlow = ((Activity) flowNode).getDefaultFlow(); + } else if (flowNode instanceof Gateway) { + defaultFlow = ((Gateway) flowNode).getDefaultFlow(); + } + + boolean isDefault = false; + if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) { + isDefault = true; + } + boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && sequenceFlow.getConditionExpression().trim().length() > 0 && !(flowNode instanceof Gateway); + + String sourceRef = sequenceFlow.getSourceRef(); + String targetRef = sequenceFlow.getTargetRef(); + FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef); + FlowElement targetElement = bpmnModel.getFlowElement(targetRef); + List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId()); + if (graphicInfoList != null && graphicInfoList.size() > 0) { + graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList); + int[] xPoints = new int[graphicInfoList.size()]; + int[] yPoints = new int[graphicInfoList.size()]; + + for (int i = 1; i < graphicInfoList.size(); i++) { + GraphicInfo graphicInfo = graphicInfoList.get(i); + GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); + + if (i == 1) { + xPoints[0] = (int) previousGraphicInfo.getX(); + yPoints[0] = (int) previousGraphicInfo.getY(); + } + xPoints[i] = (int) graphicInfo.getX(); + yPoints[i] = (int) graphicInfo.getY(); + + } + + processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor); + + // Draw sequenceflow label + GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId()); + if (labelGraphicInfo != null) { + processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false); + } else { + if (drawSequenceFlowNameWithNoLabelDI) { + GraphicInfo lineCenter = getLineCenter(graphicInfoList); + processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false); + } + + } + } + } + + // Nested elements + if (flowNode instanceof FlowElementsContainer) { + for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) { + if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) { + drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement, + highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + } + } + } + + /** + * This method makes coordinates of connection flow better. + * + * @param processDiagramCanvas + * @param bpmnModel + * @param sourceElement + * @param targetElement + * @param graphicInfoList + * @return + */ + protected static List connectionPerfectionizer(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, BaseElement sourceElement, BaseElement targetElement, List graphicInfoList) { + GraphicInfo sourceGraphicInfo = bpmnModel.getGraphicInfo(sourceElement.getId()); + GraphicInfo targetGraphicInfo = bpmnModel.getGraphicInfo(targetElement.getId()); + + CustomDefaultProcessDiagramCanvas.SHAPE_TYPE sourceShapeType = getShapeType(sourceElement); + CustomDefaultProcessDiagramCanvas.SHAPE_TYPE targetShapeType = getShapeType(targetElement); + + return processDiagramCanvas.connectionPerfectionizer(sourceShapeType, targetShapeType, sourceGraphicInfo, targetGraphicInfo, graphicInfoList); + } + + /** + * This method returns shape type of base element.
+ * Each element can be presented as rectangle, rhombus, or ellipse. + * + * @param baseElement + * @return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE + */ + protected static CustomDefaultProcessDiagramCanvas.SHAPE_TYPE getShapeType(BaseElement baseElement) { + if (baseElement instanceof Task || baseElement instanceof Activity || baseElement instanceof TextAnnotation) { + return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Rectangle; + } else if (baseElement instanceof Gateway) { + return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Rhombus; + } else if (baseElement instanceof Event) { + return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Ellipse; + } else { + // unknown source element, just do not correct coordinates + } + return null; + } + + protected static GraphicInfo getLineCenter(List graphicInfoList) { + GraphicInfo gi = new GraphicInfo(); + + int[] xPoints = new int[graphicInfoList.size()]; + int[] yPoints = new int[graphicInfoList.size()]; + + double length = 0; + double[] lengths = new double[graphicInfoList.size()]; + lengths[0] = 0; + double m; + for (int i = 1; i < graphicInfoList.size(); i++) { + GraphicInfo graphicInfo = graphicInfoList.get(i); + GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); + + if (i == 1) { + xPoints[0] = (int) previousGraphicInfo.getX(); + yPoints[0] = (int) previousGraphicInfo.getY(); + } + xPoints[i] = (int) graphicInfo.getX(); + yPoints[i] = (int) graphicInfo.getY(); + + length += Math.sqrt( + Math.pow((int) graphicInfo.getX() - (int) previousGraphicInfo.getX(), 2) + + Math.pow((int) graphicInfo.getY() - (int) previousGraphicInfo.getY(), 2)); + lengths[i] = length; + } + m = length / 2; + int p1 = 0; + int p2 = 1; + for (int i = 1; i < lengths.length; i++) { + double len = lengths[i]; + p1 = i - 1; + p2 = i; + if (len > m) { + break; + } + } + + GraphicInfo graphicInfo1 = graphicInfoList.get(p1); + GraphicInfo graphicInfo2 = graphicInfoList.get(p2); + + double AB = (int) graphicInfo2.getX() - (int) graphicInfo1.getX(); + double OA = (int) graphicInfo2.getY() - (int) graphicInfo1.getY(); + double OB = lengths[p2] - lengths[p1]; + double ob = m - lengths[p1]; + double ab = AB * ob / OB; + double oa = OA * ob / OB; + + double mx = graphicInfo1.getX() + ab; + double my = graphicInfo1.getY() + oa; + + gi.setX(mx); + gi.setY(my); + return gi; + } + + protected void drawArtifact(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) { + + ArtifactDrawInstruction drawInstruction = artifactDrawInstructions.get(artifact.getClass()); + if (drawInstruction != null) { + drawInstruction.draw(processDiagramCanvas, bpmnModel, artifact); + } + } + + private static void drawHighLight(CustomDefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightRed(CustomDefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightRed((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + protected static CustomDefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType, + String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + + // We need to calculate maximum values to know how big the image will be in its entirety + double minX = Double.MAX_VALUE; + double maxX = 0; + double minY = Double.MAX_VALUE; + double maxY = 0; + + for (Pool pool : bpmnModel.getPools()) { + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId()); + minX = graphicInfo.getX(); + maxX = graphicInfo.getX() + graphicInfo.getWidth(); + minY = graphicInfo.getY(); + maxY = graphicInfo.getY() + graphicInfo.getHeight(); + } + + List flowNodes = gatherAllFlowNodes(bpmnModel); + for (FlowNode flowNode : flowNodes) { + + GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + + // width + if (flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth() > maxX) { + maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth(); + } + if (flowNodeGraphicInfo.getX() < minX) { + minX = flowNodeGraphicInfo.getX(); + } + // height + if (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) { + maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight(); + } + if (flowNodeGraphicInfo.getY() < minY) { + minY = flowNodeGraphicInfo.getY(); + } + + for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) { + List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId()); + if (graphicInfoList != null) { + for (GraphicInfo graphicInfo : graphicInfoList) { + // width + if (graphicInfo.getX() > maxX) { + maxX = graphicInfo.getX(); + } + if (graphicInfo.getX() < minX) { + minX = graphicInfo.getX(); + } + // height + if (graphicInfo.getY() > maxY) { + maxY = graphicInfo.getY(); + } + if (graphicInfo.getY() < minY) { + minY = graphicInfo.getY(); + } + } + } + } + } + + List artifacts = gatherAllArtifacts(bpmnModel); + for (Artifact artifact : artifacts) { + + GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact.getId()); + + if (artifactGraphicInfo != null) { + // width + if (artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth() > maxX) { + maxX = artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth(); + } + if (artifactGraphicInfo.getX() < minX) { + minX = artifactGraphicInfo.getX(); + } + // height + if (artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight() > maxY) { + maxY = artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight(); + } + if (artifactGraphicInfo.getY() < minY) { + minY = artifactGraphicInfo.getY(); + } + } + + List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId()); + if (graphicInfoList != null) { + for (GraphicInfo graphicInfo : graphicInfoList) { + // width + if (graphicInfo.getX() > maxX) { + maxX = graphicInfo.getX(); + } + if (graphicInfo.getX() < minX) { + minX = graphicInfo.getX(); + } + // height + if (graphicInfo.getY() > maxY) { + maxY = graphicInfo.getY(); + } + if (graphicInfo.getY() < minY) { + minY = graphicInfo.getY(); + } + } + } + } + + int nrOfLanes = 0; + for (Process process : bpmnModel.getProcesses()) { + for (Lane l : process.getLanes()) { + + nrOfLanes++; + + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId()); + // // width + if (graphicInfo.getX() + graphicInfo.getWidth() > maxX) { + maxX = graphicInfo.getX() + graphicInfo.getWidth(); + } + if (graphicInfo.getX() < minX) { + minX = graphicInfo.getX(); + } + // height + if (graphicInfo.getY() + graphicInfo.getHeight() > maxY) { + maxY = graphicInfo.getY() + graphicInfo.getHeight(); + } + if (graphicInfo.getY() < minY) { + minY = graphicInfo.getY(); + } + } + } + + // Special case, see https://activiti.atlassian.net/browse/ACT-1431 + if (flowNodes.isEmpty() && bpmnModel.getPools().isEmpty() && nrOfLanes == 0) { + // Nothing to show + minX = 0; + minY = 0; + } + + return new CustomDefaultProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, + imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + } + + protected static List gatherAllArtifacts(BpmnModel bpmnModel) { + List artifacts = new ArrayList<>(); + for (Process process : bpmnModel.getProcesses()) { + artifacts.addAll(process.getArtifacts()); + } + return artifacts; + } + + protected static List gatherAllFlowNodes(BpmnModel bpmnModel) { + List flowNodes = new ArrayList<>(); + for (Process process : bpmnModel.getProcesses()) { + flowNodes.addAll(gatherAllFlowNodes(process)); + } + return flowNodes; + } + + protected static List gatherAllFlowNodes(FlowElementsContainer flowElementsContainer) { + List flowNodes = new ArrayList<>(); + for (FlowElement flowElement : flowElementsContainer.getFlowElements()) { + if (flowElement instanceof FlowNode) { + flowNodes.add((FlowNode) flowElement); + } + if (flowElement instanceof FlowElementsContainer) { + flowNodes.addAll(gatherAllFlowNodes((FlowElementsContainer) flowElement)); + } + } + return flowNodes; + } + + protected boolean isPartOfCollapsedSubProcess(FlowElement flowElement, BpmnModel model) { + SubProcess subProcess = flowElement.getSubProcess(); + if (subProcess != null) { + GraphicInfo graphicInfo = model.getGraphicInfo(subProcess.getId()); + if (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) { + return true; + } + + return isPartOfCollapsedSubProcess(subProcess, model); + } + + return false; + } + + public Map, ActivityDrawInstruction> getActivityDrawInstructions() { + return activityDrawInstructions; + } + + public void setActivityDrawInstructions( + Map, ActivityDrawInstruction> activityDrawInstructions) { + this.activityDrawInstructions = activityDrawInstructions; + } + + public Map, ArtifactDrawInstruction> getArtifactDrawInstructions() { + return artifactDrawInstructions; + } + + public void setArtifactDrawInstructions( + Map, ArtifactDrawInstruction> artifactDrawInstructions) { + this.artifactDrawInstructions = artifactDrawInstructions; + } + + protected interface ActivityDrawInstruction { + void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode); + } + + protected interface ArtifactDrawInstruction { + void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AddSequenceMultiInstanceCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AddSequenceMultiInstanceCmd.java new file mode 100644 index 00000000..3cdfcf41 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AddSequenceMultiInstanceCmd.java @@ -0,0 +1,61 @@ +package org.dromara.workflow.flowable.cmd; + +import cn.hutool.core.collection.CollUtil; +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager; +import org.flowable.engine.impl.util.CommandContextUtil; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.dromara.workflow.common.constant.FlowConstant.NUMBER_OF_INSTANCES; + +/** + * 串行加签 + * + * @author may + */ +public class AddSequenceMultiInstanceCmd implements Command { + + /** + * 执行id + */ + private final String executionId; + + /** + * 会签人员集合KEY + */ + private final String assigneeList; + + /** + * 加签人员 + */ + private final List assignees; + + public AddSequenceMultiInstanceCmd(String executionId, String assigneeList, List assignees) { + this.executionId = executionId; + this.assigneeList = assigneeList; + this.assignees = assignees; + } + + @Override + public Void execute(CommandContext commandContext) { + ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(); + ExecutionEntity entity = executionEntityManager.findById(executionId); + // 多实例任务总数加 assignees.size() + if (entity.getVariable(NUMBER_OF_INSTANCES) instanceof Integer nrOfInstances) { + entity.setVariable(NUMBER_OF_INSTANCES, nrOfInstances + assignees.size()); + } + // 设置流程变量 + if (entity.getVariable(assigneeList) instanceof List userIds) { + CollUtil.addAll(userIds, assignees); + Map variables = new HashMap<>(16); + variables.put(assigneeList, userIds); + entity.setVariables(variables); + } + return null; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AttachmentCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AttachmentCmd.java new file mode 100644 index 00000000..070f2db8 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/AttachmentCmd.java @@ -0,0 +1,64 @@ +package org.dromara.workflow.flowable.cmd; + +import cn.hutool.core.collection.CollUtil; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.resource.api.RemoteFileService; +import org.dromara.resource.api.domain.RemoteFile; +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.impl.persistence.entity.AttachmentEntity; +import org.flowable.engine.impl.persistence.entity.AttachmentEntityManager; +import org.flowable.engine.impl.util.CommandContextUtil; + +import java.util.Date; +import java.util.List; + +/** + * 附件上传 + * + * @author may + */ +public class AttachmentCmd implements Command { + + private final String fileId; + + private final String taskId; + + private final String processInstanceId; + + public AttachmentCmd(String fileId, String taskId, String processInstanceId) { + this.fileId = fileId; + this.taskId = taskId; + this.processInstanceId = processInstanceId; + } + + @Override + public Boolean execute(CommandContext commandContext) { + try { + if (StringUtils.isNotBlank(fileId)) { + List ossList = SpringUtils.getBean(RemoteFileService.class).selectByIds(fileId); + if (CollUtil.isNotEmpty(ossList)) { + for (RemoteFile oss : ossList) { + AttachmentEntityManager attachmentEntityManager = CommandContextUtil.getAttachmentEntityManager(); + AttachmentEntity attachmentEntity = attachmentEntityManager.create(); + attachmentEntity.setRevision(1); + attachmentEntity.setUserId(LoginHelper.getUserId().toString()); + attachmentEntity.setName(oss.getOriginalName()); + attachmentEntity.setDescription(oss.getOriginalName()); + attachmentEntity.setType(oss.getFileSuffix()); + attachmentEntity.setTaskId(taskId); + attachmentEntity.setProcessInstanceId(processInstanceId); + attachmentEntity.setContentId(oss.getOssId().toString()); + attachmentEntity.setTime(new Date()); + attachmentEntityManager.insert(attachmentEntity); + } + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return true; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteExecutionCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteExecutionCmd.java new file mode 100644 index 00000000..215d3102 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteExecutionCmd.java @@ -0,0 +1,36 @@ +package org.dromara.workflow.flowable.cmd; + +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager; +import org.flowable.engine.impl.util.CommandContextUtil; + +import java.io.Serializable; + +/** + * 删除执行数据 + * + * @author may + */ +public class DeleteExecutionCmd implements Command, Serializable { + + /** + * 执行id + */ + private final String executionId; + + public DeleteExecutionCmd(String executionId) { + this.executionId = executionId; + } + + @Override + public Void execute(CommandContext commandContext) { + ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(); + ExecutionEntity entity = executionEntityManager.findById(executionId); + if (entity != null) { + executionEntityManager.deleteExecutionAndRelatedData(entity, "", false, false); + } + return null; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteSequenceMultiInstanceCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteSequenceMultiInstanceCmd.java new file mode 100644 index 00000000..6773eeff --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/DeleteSequenceMultiInstanceCmd.java @@ -0,0 +1,82 @@ +package org.dromara.workflow.flowable.cmd; + +import cn.hutool.core.util.ObjectUtil; +import lombok.AllArgsConstructor; +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager; +import org.flowable.engine.impl.util.CommandContextUtil; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.dromara.workflow.common.constant.FlowConstant.LOOP_COUNTER; +import static org.dromara.workflow.common.constant.FlowConstant.NUMBER_OF_INSTANCES; + + +/** + * 串行减签 + * + * @author may + */ +@AllArgsConstructor +public class DeleteSequenceMultiInstanceCmd implements Command { + + /** + * 当前节点审批人员id + */ + private final String currentUserId; + + /** + * 执行id + */ + private final String executionId; + + /** + * 会签人员集合KEY + */ + private final String assigneeList; + + /** + * 减签人员 + */ + private final List assignees; + + + @Override + @SuppressWarnings("unchecked") + public Void execute(CommandContext commandContext) { + ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(); + ExecutionEntity entity = executionEntityManager.findById(executionId); + // 设置流程变量 + List userIds = new ArrayList<>(); + List variable = (List) entity.getVariable(assigneeList); + for (Object o : variable) { + userIds.add(Long.valueOf(o.toString())); + } + List userIdList = new ArrayList<>(); + userIds.forEach(e -> { + Long userId = assignees.stream().filter(id -> ObjectUtil.equals(id, e)).findFirst().orElse(null); + if (userId == null) { + userIdList.add(e); + } + }); + // 当前任务执行位置 + int loopCounterIndex = -1; + for (int i = 0; i < userIdList.size(); i++) { + Long userId = userIdList.get(i); + if (currentUserId.equals(userId.toString())) { + loopCounterIndex = i; + } + } + Map variables = new HashMap<>(16); + variables.put(NUMBER_OF_INSTANCES, userIdList.size()); + variables.put(assigneeList, userIdList); + variables.put(LOOP_COUNTER, loopCounterIndex); + entity.setVariables(variables); + return null; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/ExecutionChildByExecutionIdCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/ExecutionChildByExecutionIdCmd.java new file mode 100644 index 00000000..1f3088bf --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/ExecutionChildByExecutionIdCmd.java @@ -0,0 +1,39 @@ +package org.dromara.workflow.flowable.cmd; + +import org.dromara.common.core.utils.StreamUtils; +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityManager; +import org.flowable.engine.impl.util.CommandContextUtil; + +import java.io.Serializable; +import java.util.List; + +/** + * 获取并行网关执行后保留的执行实例数据 + * + * @author may + */ +public class ExecutionChildByExecutionIdCmd implements Command>, Serializable { + + /** + * 当前任务执行实例id + */ + private final String executionId; + + public ExecutionChildByExecutionIdCmd(String executionId) { + this.executionId = executionId; + } + + @Override + public List execute(CommandContext commandContext) { + ExecutionEntityManager executionEntityManager = CommandContextUtil.getExecutionEntityManager(); + // 获取当前执行数据 + ExecutionEntity executionEntity = executionEntityManager.findById(executionId); + // 通过当前执行数据的父执行,查询所有子执行数据 + List allChildrenExecution = + executionEntityManager.collectChildren(executionEntity.getParent()); + return StreamUtils.filter(allChildrenExecution, e -> !e.isActive()); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateBusinessStatusCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateBusinessStatusCmd.java new file mode 100644 index 00000000..3ba120ae --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateBusinessStatusCmd.java @@ -0,0 +1,37 @@ +package org.dromara.workflow.flowable.cmd; + +import org.dromara.common.core.exception.ServiceException; +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntity; +import org.flowable.engine.impl.persistence.entity.HistoricProcessInstanceEntityManager; +import org.flowable.engine.impl.util.CommandContextUtil; + +/** + * 修改流程状态 + * + * @author may + */ +public class UpdateBusinessStatusCmd implements Command { + + private final String processInstanceId; + private final String status; + + public UpdateBusinessStatusCmd(String processInstanceId, String status) { + this.processInstanceId = processInstanceId; + this.status = status; + } + + @Override + public Boolean execute(CommandContext commandContext) { + try { + HistoricProcessInstanceEntityManager manager = CommandContextUtil.getHistoricProcessInstanceEntityManager(); + HistoricProcessInstanceEntity processInstance = manager.findById(processInstanceId); + processInstance.setBusinessStatus(status); + manager.update(processInstance); + return true; + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateHiTaskInstCmd.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateHiTaskInstCmd.java new file mode 100644 index 00000000..42f6d1c7 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/cmd/UpdateHiTaskInstCmd.java @@ -0,0 +1,51 @@ +package org.dromara.workflow.flowable.cmd; + +import org.dromara.common.core.exception.ServiceException; +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.task.service.HistoricTaskService; +import org.flowable.task.service.impl.persistence.entity.HistoricTaskInstanceEntity; + +import java.util.Date; +import java.util.List; + + +/** + * 修改流程历史 + * + * @author may + */ +public class UpdateHiTaskInstCmd implements Command { + + private final List taskIds; + + private final String processDefinitionId; + + private final String processInstanceId; + + public UpdateHiTaskInstCmd(List taskIds, String processDefinitionId, String processInstanceId) { + this.taskIds = taskIds; + this.processDefinitionId = processDefinitionId; + this.processInstanceId = processInstanceId; + } + + @Override + public Boolean execute(CommandContext commandContext) { + try { + HistoricTaskService historicTaskService = CommandContextUtil.getHistoricTaskService(); + for (String taskId : taskIds) { + HistoricTaskInstanceEntity historicTask = historicTaskService.getHistoricTask(taskId); + if (historicTask != null) { + historicTask.setProcessDefinitionId(processDefinitionId); + historicTask.setProcessInstanceId(processInstanceId); + historicTask.setCreateTime(new Date()); + CommandContextUtil.getHistoricTaskService().updateHistoricTask(historicTask, true); + } + } + return true; + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/FlowableConfig.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/FlowableConfig.java new file mode 100644 index 00000000..1494bf37 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/FlowableConfig.java @@ -0,0 +1,32 @@ +package org.dromara.workflow.flowable.config; + +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import org.dromara.workflow.flowable.handler.TaskTimeoutJobHandler; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; + +import java.util.Collections; + + +/** + * flowable配置 + * + * @author may + */ +@Configuration +public class FlowableConfig implements EngineConfigurationConfigurer { + + @Autowired + private GlobalFlowableListener globalFlowableListener; + @Autowired + private IdentifierGenerator identifierGenerator; + + @Override + public void configure(SpringProcessEngineConfiguration processEngineConfiguration) { + processEngineConfiguration.setIdGenerator(() -> identifierGenerator.nextId(null).toString()); + processEngineConfiguration.setEventListeners(Collections.singletonList(globalFlowableListener)); + processEngineConfiguration.addCustomJobHandler(new TaskTimeoutJobHandler()); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/GlobalFlowableListener.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/GlobalFlowableListener.java new file mode 100644 index 00000000..9bb971a8 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/config/GlobalFlowableListener.java @@ -0,0 +1,139 @@ +package org.dromara.workflow.flowable.config; + +import cn.hutool.core.collection.CollUtil; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.workflow.common.enums.TaskStatusEnum; +import org.dromara.workflow.flowable.handler.TaskTimeoutJobHandler; +import org.dromara.workflow.utils.QueryUtils; +import org.flowable.bpmn.model.BoundaryEvent; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.common.engine.api.delegate.event.*; +import org.flowable.common.engine.impl.cfg.TransactionState; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.task.Comment; +import org.flowable.job.service.TimerJobService; +import org.flowable.job.service.impl.persistence.entity.JobEntity; +import org.flowable.job.service.impl.persistence.entity.TimerJobEntity; +import org.flowable.task.api.Task; +import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; + + +/** + * 引擎调度监听 + * + * @author may + */ +@Component +public class GlobalFlowableListener implements FlowableEventListener { + + @Autowired + @Lazy + private TaskService taskService; + + @Autowired + @Lazy + private RuntimeService runtimeService; + + @Autowired + @Lazy + private RepositoryService repositoryService; + + @Value("${flowable.async-executor-activate}") + private boolean asyncExecutorActivate; + + @Override + public void onEvent(FlowableEvent flowableEvent) { + if (flowableEvent instanceof FlowableEngineEvent flowableEngineEvent) { + FlowableEngineEventType engineEventType = (FlowableEngineEventType) flowableEvent.getType(); + switch (engineEventType) { + case JOB_EXECUTION_SUCCESS -> jobExecutionSuccess((FlowableEngineEntityEvent) flowableEngineEvent); + case TASK_DUEDATE_CHANGED, TASK_CREATED -> { + FlowableEntityEvent flowableEntityEvent = (FlowableEntityEvent) flowableEngineEvent; + Object entityObject = flowableEntityEvent.getEntity(); + TaskEntity task = (TaskEntity) entityObject; + if (asyncExecutorActivate && task.getDueDate() != null && task.getDueDate().after(new Date())) { + //删除之前已经存在的定时任务 + TimerJobService timerJobService = CommandContextUtil.getTimerJobService(); + List timerJobEntityList = timerJobService.findTimerJobsByProcessInstanceId(task.getProcessInstanceId()); + if (!CollUtil.isEmpty(timerJobEntityList)) { + for (TimerJobEntity timerJobEntity : timerJobEntityList) { + String taskId = timerJobEntity.getJobHandlerConfiguration(); + if (task.getId().equals(taskId)) { + timerJobService.deleteTimerJob(timerJobEntity); + } + } + } + //创建job对象 + TimerJobEntity timer = timerJobService.createTimerJob(); + timer.setTenantId(TenantHelper.getTenantId()); + //设置job类型 + timer.setJobType(JobEntity.JOB_TYPE_TIMER); + timer.setJobHandlerType(TaskTimeoutJobHandler.TYPE); + timer.setDuedate(task.getDueDate()); + timer.setProcessInstanceId(task.getProcessInstanceId()); + //设置任务id + timer.setJobHandlerConfiguration(task.getId()); + //保存并触发事件 + timerJobService.scheduleTimerJob(timer); + } + } + } + } + } + + @Override + public boolean isFailOnException() { + return true; + } + + @Override + public boolean isFireOnTransactionLifecycleEvent() { + return false; + } + + @Override + public String getOnTransaction() { + return TransactionState.COMMITTED.name(); + } + + /** + * 处理边界定时事件自动审批记录 + * + * @param event 事件 + */ + protected void jobExecutionSuccess(FlowableEngineEntityEvent event) { + if (event != null && StringUtils.isNotBlank(event.getExecutionId())) { + Execution execution = runtimeService.createExecutionQuery().executionId(event.getExecutionId()).singleResult(); + if (execution != null) { + BpmnModel bpmnModel = repositoryService.getBpmnModel(event.getProcessDefinitionId()); + FlowElement flowElement = bpmnModel.getFlowElement(execution.getActivityId()); + if (flowElement instanceof BoundaryEvent) { + String attachedToRefId = ((BoundaryEvent) flowElement).getAttachedToRefId(); + List list = runtimeService.createExecutionQuery().activityId(attachedToRefId).list(); + for (Execution ex : list) { + Task task = QueryUtils.taskQuery().executionId(ex.getId()).singleResult(); + if (task != null) { + List taskComments = taskService.getTaskComments(task.getId()); + if (CollUtil.isEmpty(taskComments)) { + taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.PASS.getStatus(), "超时自动审批!"); + } + } + } + } + } + } + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/FlowProcessEventHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/FlowProcessEventHandler.java new file mode 100644 index 00000000..001fd30f --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/FlowProcessEventHandler.java @@ -0,0 +1,51 @@ +package org.dromara.workflow.flowable.handler; + +import org.dromara.common.core.utils.ServletUtils; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.workflow.api.domain.event.ProcessEvent; +import org.dromara.workflow.api.domain.event.ProcessTaskEvent; +import org.springframework.stereotype.Component; + +/** + * 流程监听服务 + * + * @author may + * @date 2024-06-02 + */ +@Component +public class FlowProcessEventHandler { + + /** + * 总体流程监听(例如: 提交 退回 撤销 终止 作废等) + * + * @param key 流程key + * @param businessKey 业务id + * @param status 状态 + * @param submit 当为true时为申请人节点办理 + */ + public void processHandler(String key, String businessKey, String status, boolean submit) { + ProcessEvent processEvent = new ProcessEvent(); + processEvent.setKey(key); + processEvent.setBusinessKey(businessKey); + processEvent.setStatus(status); + processEvent.setSubmit(submit); + processEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(processEvent); + } + + /** + * 执行办理任务监听 + * + * @param keyNode 流程定义key与流程节点标识(拼接方式:流程定义key_流程节点) + * @param taskId 任务id + * @param businessKey 业务id + */ + public void processTaskHandler(String keyNode, String taskId, String businessKey) { + ProcessTaskEvent processTaskEvent = new ProcessTaskEvent(); + processTaskEvent.setKeyNode(keyNode); + processTaskEvent.setTaskId(taskId); + processTaskEvent.setBusinessKey(businessKey); + processTaskEvent.setRequest(ServletUtils.getRequest()); + SpringUtils.context().publishEvent(processTaskEvent); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/TaskTimeoutJobHandler.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/TaskTimeoutJobHandler.java new file mode 100644 index 00000000..7685423f --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/flowable/handler/TaskTimeoutJobHandler.java @@ -0,0 +1,38 @@ +package org.dromara.workflow.flowable.handler; + +import org.dromara.workflow.common.enums.TaskStatusEnum; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.engine.TaskService; +import org.flowable.engine.impl.jobexecutor.TimerEventHandler; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.job.service.JobHandler; +import org.flowable.job.service.impl.persistence.entity.JobEntity; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.variable.api.delegate.VariableScope; + +/** + * 办理超时(过期)任务 + * + * @author may + */ +public class TaskTimeoutJobHandler extends TimerEventHandler implements JobHandler { + + public static final String TYPE = "taskTimeout"; + + @Override + public String getType() { + return TYPE; + } + + @Override + public void execute(JobEntity job, String configuration, VariableScope variableScope, CommandContext commandContext) { + TaskService taskService = CommandContextUtil.getProcessEngineConfiguration(commandContext) + .getTaskService(); + Task task = taskService.createTaskQuery().taskId(configuration).singleResult(); + if (task != null) { + taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.TIMEOUT.getStatus(), "超时自动审批!"); + taskService.complete(configuration); + } + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiProcinstMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiProcinstMapper.java new file mode 100644 index 00000000..a3a41c92 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiProcinstMapper.java @@ -0,0 +1,16 @@ +package org.dromara.workflow.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.workflow.domain.ActHiProcinst; + +/** + * 流程实例Mapper接口 + * + * @author may + * @date 2023-07-22 + */ +@InterceptorIgnore(tenantLine = "true") +public interface ActHiProcinstMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiTaskinstMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiTaskinstMapper.java new file mode 100644 index 00000000..63b394b6 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActHiTaskinstMapper.java @@ -0,0 +1,16 @@ +package org.dromara.workflow.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import org.dromara.workflow.domain.ActHiTaskinst; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 流程历史任务Mapper接口 + * + * @author may + * @date 2024-03-02 + */ +@InterceptorIgnore(tenantLine = "true") +public interface ActHiTaskinstMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActTaskMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActTaskMapper.java new file mode 100644 index 00000000..63c5ecb7 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/ActTaskMapper.java @@ -0,0 +1,47 @@ +package org.dromara.workflow.mapper; + +import com.baomidou.mybatisplus.annotation.InterceptorIgnore; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.annotations.Param; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.workflow.domain.vo.TaskVo; + + +/** + * 任务信息Mapper接口 + * + * @author may + * @date 2024-03-02 + */ +@InterceptorIgnore(tenantLine = "true") +public interface ActTaskMapper extends BaseMapperPlus { + /** + * 获取待办信息 + * + * @param page 分页 + * @param queryWrapper 条件 + * @return 结果 + */ + Page getTaskWaitByPage(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 获取已办 + * + * @param page 分页 + * @param queryWrapper 条件 + * @return 结果 + */ + Page getTaskFinishByPage(@Param("page") Page page, @Param(Constants.WRAPPER) Wrapper queryWrapper); + + /** + * 查询当前用户的抄送 + * + * @param page 分页 + * @param queryWrapper 条件 + * @return 结果 + */ + Page getTaskCopyByPage(@Param("page") Page page, @Param(Constants.WRAPPER) QueryWrapper queryWrapper); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfCategoryMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfCategoryMapper.java new file mode 100644 index 00000000..98aea024 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfCategoryMapper.java @@ -0,0 +1,15 @@ +package org.dromara.workflow.mapper; + +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.workflow.domain.WfCategory; +import org.dromara.workflow.domain.vo.WfCategoryVo; + +/** + * 流程分类Mapper接口 + * + * @author may + * @date 2023-06-27 + */ +public interface WfCategoryMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfDefinitionConfigMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfDefinitionConfigMapper.java new file mode 100644 index 00000000..ee208825 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfDefinitionConfigMapper.java @@ -0,0 +1,15 @@ +package org.dromara.workflow.mapper; + +import org.dromara.workflow.domain.WfDefinitionConfig; +import org.dromara.workflow.domain.vo.WfDefinitionConfigVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 流程定义配置Mapper接口 + * + * @author may + * @date 2024-03-18 + */ +public interface WfDefinitionConfigMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfFormManageMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfFormManageMapper.java new file mode 100644 index 00000000..acf8111a --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfFormManageMapper.java @@ -0,0 +1,15 @@ +package org.dromara.workflow.mapper; + +import org.dromara.workflow.domain.WfFormManage; +import org.dromara.workflow.domain.vo.WfFormManageVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 表单管理Mapper接口 + * + * @author may + * @date 2024-03-29 + */ +public interface WfFormManageMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfNodeConfigMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfNodeConfigMapper.java new file mode 100644 index 00000000..d2aecacb --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfNodeConfigMapper.java @@ -0,0 +1,15 @@ +package org.dromara.workflow.mapper; + +import org.dromara.workflow.domain.WfNodeConfig; +import org.dromara.workflow.domain.vo.WfNodeConfigVo; +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; + +/** + * 节点配置Mapper接口 + * + * @author may + * @date 2024-03-30 + */ +public interface WfNodeConfigMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java new file mode 100644 index 00000000..9b291feb --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/mapper/WfTaskBackNodeMapper.java @@ -0,0 +1,13 @@ +package org.dromara.workflow.mapper; + +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.workflow.domain.WfTaskBackNode; + +/** + * 节点驳回记录Mapper接口 + * + * @author may + * @date 2024-03-13 + */ +public interface WfTaskBackNodeMapper extends BaseMapperPlus { +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiProcinstService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiProcinstService.java new file mode 100644 index 00000000..e802c697 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiProcinstService.java @@ -0,0 +1,31 @@ +package org.dromara.workflow.service; + + +import org.dromara.workflow.domain.ActHiProcinst; + +import java.util.List; + +/** + * 流程实例Service接口 + * + * @author may + * @date 2023-07-22 + */ +public interface IActHiProcinstService { + + /** + * 按照业务id查询 + * + * @param businessKeys 业务id + * @return 结果 + */ + List selectByBusinessKeyIn(List businessKeys); + + /** + * 按照业务id查询 + * + * @param businessKey 业务id + * @return 结果 + */ + ActHiProcinst selectByBusinessKey(String businessKey); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiTaskinstService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiTaskinstService.java new file mode 100644 index 00000000..ad286e24 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActHiTaskinstService.java @@ -0,0 +1,11 @@ +package org.dromara.workflow.service; + + +/** + * 流程历史任务Service接口 + * + * @author may + * @date 2024-03-02 + */ +public interface IActHiTaskinstService { +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActModelService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActModelService.java new file mode 100644 index 00000000..4a6d1706 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActModelService.java @@ -0,0 +1,83 @@ +package org.dromara.workflow.service; + +import jakarta.servlet.http.HttpServletResponse; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.workflow.domain.bo.ModelBo; +import org.dromara.workflow.domain.vo.ModelVo; +import org.flowable.engine.repository.Model; + +import java.util.List; + + +/** + * 模型管理 服务层 + * + * @author may + */ +public interface IActModelService { + /** + * 分页查询模型 + * + * @param modelBo 模型参数 + * @param pageQuery 参数 + * @return 返回分页列表 + */ + TableDataInfo page(ModelBo modelBo, PageQuery pageQuery); + + /** + * 新增模型 + * + * @param modelBo 模型请求对象 + * @return 结果 + */ + boolean saveNewModel(ModelBo modelBo); + + /** + * 查询模型 + * + * @param modelId 模型id + * @return 模型数据 + */ + ModelVo getInfo(String modelId); + + /** + * 修改模型信息 + * + * @param modelBo 模型数据 + * @return 结果 + */ + boolean update(ModelBo modelBo); + + /** + * 编辑模型XML + * + * @param modelBo 模型数据 + * @return 结果 + */ + boolean editModelXml(ModelBo modelBo); + + /** + * 模型部署 + * + * @param id 模型id + * @return 结果 + */ + boolean modelDeploy(String id); + + /** + * 导出模型zip压缩包 + * + * @param modelIds 模型id + * @param response 响应 + */ + void exportZip(List modelIds, HttpServletResponse response); + + /** + * 复制模型 + * + * @param modelBo 模型数据 + * @return 结果 + */ + boolean copyModel(ModelBo modelBo); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessDefinitionService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessDefinitionService.java new file mode 100644 index 00000000..5d00e419 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessDefinitionService.java @@ -0,0 +1,91 @@ +package org.dromara.workflow.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.workflow.domain.bo.ProcessDefinitionBo; +import org.dromara.workflow.domain.vo.ProcessDefinitionVo; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +/** + * 流程定义 服务层 + * + * @author may + */ +public interface IActProcessDefinitionService { + /** + * 分页查询 + * + * @param processDefinitionBo 参数 + * @param pageQuery 分页 + * @return 返回分页列表 + */ + TableDataInfo page(ProcessDefinitionBo processDefinitionBo, PageQuery pageQuery); + + /** + * 查询历史流程定义列表 + * + * @param key 流程定义key + * @return 结果 + */ + List getListByKey(String key); + + /** + * 查看流程定义图片 + * + * @param processDefinitionId 流程定义id + * @return 结果 + */ + String definitionImage(String processDefinitionId); + + /** + * 查看流程定义xml文件 + * + * @param processDefinitionId 流程定义id + * @return 结果 + */ + String definitionXml(String processDefinitionId); + + /** + * 删除流程定义 + * + * @param deploymentIds 部署id + * @param processDefinitionIds 流程定义id + * @return 结果 + */ + boolean deleteDeployment(List deploymentIds, List processDefinitionIds); + + /** + * 激活或者挂起流程定义 + * + * @param processDefinitionId 流程定义id + * @return 结果 + */ + boolean updateDefinitionState(String processDefinitionId); + + /** + * 迁移流程定义 + * + * @param currentProcessDefinitionId 当前流程定义id + * @param fromProcessDefinitionId 需要迁移到的流程定义id + * @return 结果 + */ + boolean migrationDefinition(String currentProcessDefinitionId, String fromProcessDefinitionId); + + /** + * 流程定义转换为模型 + * + * @param processDefinitionId 流程定义id + * @return 结果 + */ + boolean convertToModel(String processDefinitionId); + + /** + * 通过zip或xml部署流程定义 + * + * @param file 文件 + * @param categoryCode 分类 + */ + void deployByFile(MultipartFile file, String categoryCode); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessInstanceService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessInstanceService.java new file mode 100644 index 00000000..2faeef1a --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActProcessInstanceService.java @@ -0,0 +1,110 @@ +package org.dromara.workflow.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.workflow.api.domain.bo.ProcessInstanceBo; +import org.dromara.workflow.api.domain.bo.ProcessInvalidBo; +import org.dromara.workflow.api.domain.bo.TaskUrgingBo; +import org.dromara.workflow.domain.vo.ActHistoryInfoVo; +import org.dromara.workflow.domain.vo.ProcessInstanceVo; + +import java.util.List; +import java.util.Map; + +/** + * 流程实例 服务层 + * + * @author may + */ +public interface IActProcessInstanceService { + /** + * 通过流程实例id获取历史流程图 + * + * @param businessKey 流程实例id + * @return 结果 + */ + String getHistoryImage(String businessKey); + + /** + * 通过业务id获取历史流程图运行中,历史等节点 + * + * @param businessKey 业务id + * @return 结果 + */ + Map getHistoryList(String businessKey); + + /** + * 分页查询正在运行的流程实例 + * + * @param processInstanceBo 参数 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo getPageByRunning(ProcessInstanceBo processInstanceBo, PageQuery pageQuery); + + /** + * 分页查询已结束的流程实例 + * + * @param processInstanceBo 参数 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo getPageByFinish(ProcessInstanceBo processInstanceBo, PageQuery pageQuery); + + /** + * 获取审批记录 + * + * @param businessKey 业务id + * @return 结果 + */ + List getHistoryRecord(String businessKey); + + /** + * 作废流程实例,不会删除历史记录(删除运行中的实例) + * + * @param processInvalidBo 参数 + * @return 结果 + */ + boolean deleteRunInstance(ProcessInvalidBo processInvalidBo); + + /** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param businessKeys 业务id + * @return 结果 + */ + boolean deleteRunAndHisInstance(List businessKeys); + + /** + * 已完成的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param businessKeys 业务id + * @return 结果 + */ + boolean deleteFinishAndHisInstance(List businessKeys); + + /** + * 撤销流程申请 + * + * @param businessKey 业务id + * @return 结果 + */ + boolean cancelProcessApply(String businessKey); + + /** + * 分页查询当前登录人单据 + * + * @param processInstanceBo 参数 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo getPageByCurrent(ProcessInstanceBo processInstanceBo, PageQuery pageQuery); + + /** + * 任务催办(给当前任务办理人发送站内信,邮件,短信等) + * + * @param taskUrgingBo 任务催办 + * @return 结果 + */ + boolean taskUrging(TaskUrgingBo taskUrgingBo); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java new file mode 100644 index 00000000..8e9f7636 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IActTaskService.java @@ -0,0 +1,161 @@ +package org.dromara.workflow.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.workflow.domain.bo.*; +import org.dromara.workflow.domain.vo.TaskVo; +import org.dromara.workflow.domain.vo.VariableVo; + +import java.util.List; +import java.util.Map; + +/** + * 任务 服务层 + * + * @author may + */ +public interface IActTaskService { + /** + * 启动任务 + * + * @param startProcessBo 启动流程参数 + * @return 结果 + */ + Map startWorkFlow(StartProcessBo startProcessBo); + + + /** + * 办理任务 + * + * @param completeTaskBo 办理任务参数 + * @return 结果 + */ + boolean completeTask(CompleteTaskBo completeTaskBo); + + /** + * 查询当前用户的待办任务 + * + * @param taskBo 参数 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo getPageByTaskWait(TaskBo taskBo, PageQuery pageQuery); + + /** + * 查询当前租户所有待办任务 + * + * @param taskBo 参数 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo getPageByAllTaskWait(TaskBo taskBo, PageQuery pageQuery); + + + /** + * 查询当前用户的已办任务 + * + * @param taskBo 参数 + * @param pageQuery 参数 + * @return 结果 + */ + TableDataInfo getPageByTaskFinish(TaskBo taskBo, PageQuery pageQuery); + + /** + * 查询当前用户的抄送 + * + * @param taskBo 参数 + * @param pageQuery 参数 + * @return 结果 + */ + TableDataInfo getPageByTaskCopy(TaskBo taskBo, PageQuery pageQuery); + + /** + * 查询当前租户所有已办任务 + * + * @param taskBo 参数 + * @param pageQuery 参数 + * @return 结果 + */ + TableDataInfo getPageByAllTaskFinish(TaskBo taskBo, PageQuery pageQuery); + + /** + * 委派任务 + * + * @param delegateBo 参数 + * @return 结果 + */ + boolean delegateTask(DelegateBo delegateBo); + + /** + * 终止任务 + * + * @param terminationBo 参数 + * @return 结果 + */ + boolean terminationTask(TerminationBo terminationBo); + + /** + * 转办任务 + * + * @param transmitBo 参数 + * @return 结果 + */ + boolean transferTask(TransmitBo transmitBo); + + /** + * 会签任务加签 + * + * @param addMultiBo 参数 + * @return 结果 + */ + boolean addMultiInstanceExecution(AddMultiBo addMultiBo); + + /** + * 会签任务减签 + * + * @param deleteMultiBo 参数 + * @return 结果 + */ + boolean deleteMultiInstanceExecution(DeleteMultiBo deleteMultiBo); + + /** + * 驳回审批 + * + * @param backProcessBo 参数 + * @return 流程实例id + */ + String backProcess(BackProcessBo backProcessBo); + + /** + * 修改任务办理人 + * + * @param taskIds 任务id + * @param userId 办理人id + * @return 结果 + */ + boolean updateAssignee(String[] taskIds, String userId); + + /** + * 查询流程变量 + * + * @param taskId 任务id + * @return 结果 + */ + List getInstanceVariable(String taskId); + + /** + * 查询工作流任务用户选择加签人员 + * + * @param taskId 任务id + * @return 结果 + */ + String getTaskUserIdsByAddMultiInstance(String taskId); + + /** + * 查询工作流选择减签人员 + * + * @param taskId 任务id + * @return 结果 + */ + List getListByDeleteMultiInstance(String taskId); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfCategoryService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfCategoryService.java new file mode 100644 index 00000000..acf0aa2b --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfCategoryService.java @@ -0,0 +1,51 @@ +package org.dromara.workflow.service; + +import org.dromara.workflow.domain.WfCategory; +import org.dromara.workflow.domain.bo.WfCategoryBo; +import org.dromara.workflow.domain.vo.WfCategoryVo; + +import java.util.Collection; +import java.util.List; + +/** + * 流程分类Service接口 + * + * @author may + * @date 2023-06-28 + */ +public interface IWfCategoryService { + + /** + * 查询流程分类 + */ + WfCategoryVo queryById(Long id); + + + /** + * 查询流程分类列表 + */ + List queryList(WfCategoryBo bo); + + /** + * 新增流程分类 + */ + Boolean insertByBo(WfCategoryBo bo); + + /** + * 修改流程分类 + */ + Boolean updateByBo(WfCategoryBo bo); + + /** + * 校验并批量删除流程分类信息 + */ + Boolean deleteWithValidByIds(Collection ids, Boolean isValid); + + /** + * 按照类别编码查询 + * + * @param categoryCode 分类比吗 + * @return 结果 + */ + WfCategory queryByCategoryCode(String categoryCode); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfDefinitionConfigService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfDefinitionConfigService.java new file mode 100644 index 00000000..fe5cf7ad --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfDefinitionConfigService.java @@ -0,0 +1,83 @@ +package org.dromara.workflow.service; + +import org.dromara.workflow.domain.vo.WfDefinitionConfigVo; +import org.dromara.workflow.domain.bo.WfDefinitionConfigBo; + +import java.util.Collection; +import java.util.List; + +/** + * 流程定义配置Service接口 + * + * @author may + * @date 2024-03-18 + */ +public interface IWfDefinitionConfigService { + + /** + * 查询流程定义配置 + * + * @param definitionId 流程定义id + * @return 结果 + */ + WfDefinitionConfigVo getByDefId(String definitionId); + + /** + * 查询流程定义配置 + * + * @param tableName 表名 + * @return 结果 + */ + WfDefinitionConfigVo getByTableNameLastVersion(String tableName); + + /** + * 查询流程定义配置 + * + * @param definitionId 流程定义id + * @param tableName 表名 + * @return 结果 + */ + WfDefinitionConfigVo getByDefIdAndTableName(String definitionId, String tableName); + + /** + * 查询流程定义配置排除当前查询的流程定义 + * + * @param definitionId 流程定义id + * @param tableName 表名 + * @return 结果 + */ + List getByTableNameNotDefId(String tableName, String definitionId); + + /** + * 查询流程定义配置列表 + * + * @param definitionIds 流程定义id + * @return 结果 + */ + List queryList(List definitionIds); + + + /** + * 新增流程定义配置 + * + * @param bo 参数 + * @return 结果 + */ + Boolean saveOrUpdate(WfDefinitionConfigBo bo); + + /** + * 删除 + * + * @param ids id + * @return 结果 + */ + Boolean deleteByIds(Collection ids); + + /** + * 按照流程定义id删除 + * + * @param ids 流程定义id + * @return 结果 + */ + Boolean deleteByDefIds(Collection ids); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfFormManageService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfFormManageService.java new file mode 100644 index 00000000..2ca2264b --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfFormManageService.java @@ -0,0 +1,81 @@ +package org.dromara.workflow.service; + +import org.dromara.workflow.domain.vo.WfFormManageVo; +import org.dromara.workflow.domain.bo.WfFormManageBo; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; + +import java.util.Collection; +import java.util.List; + +/** + * 表单管理Service接口 + * + * @author may + * @date 2024-03-29 + */ +public interface IWfFormManageService { + + /** + * 查询表单管理 + * + * @param id 主键 + * @return 结果 + */ + WfFormManageVo queryById(Long id); + + /** + * 查询表单管理 + * + * @param ids 主键 + * @return 结果 + */ + List queryByIds(List ids); + + /** + * 查询表单管理列表 + * + * @param bo 参数 + * @param pageQuery 分页 + * @return 结果 + */ + TableDataInfo queryPageList(WfFormManageBo bo, PageQuery pageQuery); + + /** + * 查询表单管理列表 + * + * @return 结果 + */ + List selectList(); + /** + * 查询表单管理列表 + * + * @param bo 参数 + * @return 结果 + */ + List queryList(WfFormManageBo bo); + + /** + * 新增表单管理 + * + * @param bo 参数 + * @return 结果 + */ + Boolean insertByBo(WfFormManageBo bo); + + /** + * 修改表单管理 + * + * @param bo 参数 + * @return 结果 + */ + Boolean updateByBo(WfFormManageBo bo); + + /** + * 批量删除表单管理信息 + * + * @param ids 主键 + * @return 结果 + */ + Boolean deleteByIds(Collection ids); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfNodeConfigService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfNodeConfigService.java new file mode 100644 index 00000000..5e64d64a --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfNodeConfigService.java @@ -0,0 +1,56 @@ +package org.dromara.workflow.service; + +import org.dromara.workflow.domain.WfNodeConfig; +import org.dromara.workflow.domain.vo.WfNodeConfigVo; + +import java.util.Collection; +import java.util.List; + +/** + * 节点配置Service接口 + * + * @author may + * @date 2024-03-30 + */ +public interface IWfNodeConfigService { + + /** + * 查询节点配置 + * + * @param id 主键 + * @return 结果 + */ + WfNodeConfigVo queryById(Long id); + + /** + * 保存节点配置 + * + * @param list 参数 + * @return 结果 + */ + Boolean saveOrUpdate(List list); + + /** + * 批量删除节点配置信息 + * + * @param ids 主键 + * @return 结果 + */ + Boolean deleteByIds(Collection ids); + + /** + * 按照流程定义id删除 + * + * @param ids 流程定义id + * @return 结果 + */ + Boolean deleteByDefIds(Collection ids); + + /** + * 按照流程定义id查询 + * + * @param ids 流程定义id + * @return 结果 + */ + List selectByDefIds(Collection ids); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java new file mode 100644 index 00000000..97f94067 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/IWfTaskBackNodeService.java @@ -0,0 +1,65 @@ +package org.dromara.workflow.service; + + +import org.dromara.workflow.domain.WfTaskBackNode; +import org.flowable.task.api.Task; + +import java.util.List; + +/** + * 节点驳回记录Service接口 + * + * @author may + * @date 2024-03-13 + */ +public interface IWfTaskBackNodeService { + + /** + * 记录审批节点 + * + * @param task 任务 + */ + void recordExecuteNode(Task task); + + /** + * 按流程实例id查询 + * + * @param processInstanceId 流程实例id + * @return 结果 + */ + List getListByInstanceId(String processInstanceId); + + /** + * 按照流程实例id,节点id查询 + * + * @param processInstanceId 流程实例id + * @param nodeId 节点id + * @return 结果 + */ + WfTaskBackNode getListByInstanceIdAndNodeId(String processInstanceId, String nodeId); + + /** + * 删除驳回后的节点 + * + * @param processInstanceId 流程实例id + * @param targetActivityId 节点id + * @return 结果 + */ + boolean deleteBackTaskNode(String processInstanceId, String targetActivityId); + + /** + * 按流程实例id删除 + * + * @param processInstanceId 流程实例id + * @return 结果 + */ + boolean deleteByInstanceId(String processInstanceId); + + /** + * 按流程实例id删除 + * + * @param processInstanceIds 流程实例id + * @return 结果 + */ + boolean deleteByInstanceIds(List processInstanceIds); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/WorkflowService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/WorkflowService.java new file mode 100644 index 00000000..eb24cd2e --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/WorkflowService.java @@ -0,0 +1,85 @@ +package org.dromara.workflow.service; + +import java.util.List; +import java.util.Map; + +/** + * 通用 工作流服务 + * + * @author may + */ +public interface WorkflowService { + + /** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param businessKeys 业务id + * @return 结果 + */ + boolean deleteRunAndHisInstance(List businessKeys); + + /** + * 获取当前流程状态 + * + * @param taskId 任务id + */ + String getBusinessStatusByTaskId(String taskId); + + /** + * 获取当前流程状态 + * + * @param businessKey 业务id + */ + String getBusinessStatus(String businessKey); + + /** + * 设置流程实例对象 + * + * @param obj 业务对象 + * @param businessKey 业务id + */ + void setBusinessInstanceDTO(Object obj, String businessKey); + + /** + * 设置流程实例对象 + * + * @param obj 业务对象 + * @param idList 业务id + * @param fieldName 主键属性名称 + */ + void setBusinessInstanceListDTO(Object obj, List idList, String fieldName); + + /** + * 设置流程变量(全局变量) + * + * @param taskId 任务id + * @param variableName 变量名称 + * @param value 变量值 + */ + void setVariable(String taskId, String variableName, Object value); + + /** + * 设置流程变量(全局变量) + * + * @param taskId 任务id + * @param variables 流程变量 + */ + void setVariables(String taskId, Map variables); + + /** + * 设置流程变量(本地变量,非全局变量) + * + * @param taskId 任务id + * @param variableName 变量名称 + * @param value 变量值 + */ + void setVariableLocal(String taskId, String variableName, Object value); + + /** + * 设置流程变量(本地变量,非全局变量) + * + * @param taskId 任务id + * @param variables 流程变量 + */ + void setVariablesLocal(String taskId, Map variables); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiProcinstServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiProcinstServiceImpl.java new file mode 100644 index 00000000..06d607b4 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiProcinstServiceImpl.java @@ -0,0 +1,51 @@ +package org.dromara.workflow.service.impl; + + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.workflow.domain.ActHiProcinst; +import org.dromara.workflow.mapper.ActHiProcinstMapper; +import org.dromara.workflow.service.IActHiProcinstService; +import org.springframework.stereotype.Service; + +import java.util.List; + + +/** + * 流程实例Service业务层处理 + * + * @author may + * @date 2023-07-22 + */ +@RequiredArgsConstructor +@Service +public class ActHiProcinstServiceImpl implements IActHiProcinstService { + + private final ActHiProcinstMapper baseMapper; + + /** + * 按照业务id查询 + * + * @param businessKeys 业务id + */ + @Override + public List selectByBusinessKeyIn(List businessKeys) { + return baseMapper.selectList(new LambdaQueryWrapper() + .in(ActHiProcinst::getBusinessKey, businessKeys) + .eq(TenantHelper.isEnable(), ActHiProcinst::getTenantId, TenantHelper.getTenantId())); + } + + /** + * 按照业务id查询 + * + * @param businessKey 业务id + */ + @Override + public ActHiProcinst selectByBusinessKey(String businessKey) { + return baseMapper.selectOne(new LambdaQueryWrapper() + .eq(ActHiProcinst::getBusinessKey, businessKey) + .eq(TenantHelper.isEnable(), ActHiProcinst::getTenantId, TenantHelper.getTenantId())); + + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiTaskinstServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiTaskinstServiceImpl.java new file mode 100644 index 00000000..5548f22f --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActHiTaskinstServiceImpl.java @@ -0,0 +1,18 @@ +package org.dromara.workflow.service.impl; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.dromara.workflow.service.IActHiTaskinstService; + + +/** + * 流程历史任务Service业务层处理 + * + * @author may + * @date 2024-03-02 + */ +@RequiredArgsConstructor +@Service +public class ActHiTaskinstServiceImpl implements IActHiTaskinstService { + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActModelServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActModelServiceImpl.java new file mode 100644 index 00000000..7ea53d81 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActModelServiceImpl.java @@ -0,0 +1,424 @@ +package org.dromara.workflow.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Validator; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.ZipUtil; +import cn.hutool.json.JSONUtil; +import com.alibaba.excel.util.StringUtils; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.apache.batik.transcoder.TranscoderInput; +import org.apache.batik.transcoder.TranscoderOutput; +import org.apache.batik.transcoder.image.PNGTranscoder; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.workflow.common.constant.FlowConstant; +import org.dromara.workflow.domain.WfNodeConfig; +import org.dromara.workflow.domain.bo.ModelBo; +import org.dromara.workflow.domain.bo.WfDefinitionConfigBo; +import org.dromara.workflow.domain.vo.ModelVo; +import org.dromara.workflow.domain.vo.WfDefinitionConfigVo; +import org.dromara.workflow.service.IActModelService; +import org.dromara.workflow.service.IWfDefinitionConfigService; +import org.dromara.workflow.service.IWfNodeConfigService; +import org.dromara.workflow.utils.ModelUtils; +import org.dromara.workflow.utils.QueryUtils; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ModelQuery; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.validation.ValidationError; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * 模型管理 服务层实现 + * + * @author may + */ +@RequiredArgsConstructor +@Service +public class ActModelServiceImpl implements IActModelService { + + private final RepositoryService repositoryService; + private final IWfNodeConfigService wfNodeConfigService; + private final IWfDefinitionConfigService wfDefinitionConfigService; + + /** + * 分页查询模型 + * + * @param modelBo 模型参数 + * @return 返回分页列表 + */ + @Override + public TableDataInfo page(ModelBo modelBo, PageQuery pageQuery) { + ModelQuery query = QueryUtils.modelQuery(); + if (StringUtils.isNotBlank(modelBo.getName())) { + query.modelNameLike("%" + modelBo.getName() + "%"); + } + if (StringUtils.isNotBlank(modelBo.getKey())) { + query.modelKey(modelBo.getKey()); + } + if (StringUtils.isNotBlank(modelBo.getCategoryCode())) { + query.modelCategory(modelBo.getCategoryCode()); + } + query.orderByLastUpdateTime().desc(); + // 创建时间降序排列 + query.orderByCreateTime().desc(); + // 分页查询 + List modelList = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize()); + // 总记录数 + long total = query.count(); + TableDataInfo build = TableDataInfo.build(); + build.setRows(modelList); + build.setTotal(total); + return build; + } + + /** + * 新增模型 + * + * @param modelBo 模型请求对象 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean saveNewModel(ModelBo modelBo) { + try { + int version = 0; + String key = modelBo.getKey(); + String name = modelBo.getName(); + String description = modelBo.getDescription(); + String categoryCode = modelBo.getCategoryCode(); + String xml = modelBo.getXml(); + Model checkModel = QueryUtils.modelQuery().modelKey(key).singleResult(); + if (ObjectUtil.isNotNull(checkModel)) { + throw new ServiceException("模型key已存在!"); + } + //初始空的模型 + Model model = repositoryService.newModel(); + model.setKey(key); + model.setName(name); + model.setVersion(version); + model.setCategory(categoryCode); + model.setMetaInfo(description); + model.setTenantId(TenantHelper.getTenantId()); + //保存初始化的模型基本信息数据 + repositoryService.saveModel(model); + repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(xml)); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 查询模型 + * + * @param id 模型id + * @return 模型数据 + */ + @Override + public ModelVo getInfo(String id) { + ModelVo modelVo = new ModelVo(); + Model model = repositoryService.getModel(id); + if (model != null) { + try { + byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId()); + modelVo.setXml(StrUtil.utf8Str(modelEditorSource)); + modelVo.setId(model.getId()); + modelVo.setKey(model.getKey()); + modelVo.setName(model.getName()); + modelVo.setCategoryCode(model.getCategory()); + modelVo.setDescription(model.getMetaInfo()); + return modelVo; + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } + return modelVo; + } + + /** + * 修改模型信息 + * + * @param modelBo 模型数据 + * @return 结果 + */ + @Override + public boolean update(ModelBo modelBo) { + try { + Model model = repositoryService.getModel(modelBo.getId()); + List list = QueryUtils.modelQuery().modelKey(modelBo.getKey()).list(); + list.stream().filter(e -> !e.getId().equals(model.getId())).findFirst().ifPresent(e -> { + throw new ServiceException("模型KEY已存在!"); + }); + model.setCategory(modelBo.getCategoryCode()); + model.setMetaInfo(modelBo.getDescription()); + repositoryService.saveModel(model); + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + return true; + } + + /** + * 编辑模型XML + * + * @param modelBo 模型数据 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean editModelXml(ModelBo modelBo) { + try { + String xml = modelBo.getXml(); + String svg = modelBo.getSvg(); + String modelId = modelBo.getId(); + String key = modelBo.getKey(); + String name = modelBo.getName(); + BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xml); + ModelUtils.checkBpmnModel(bpmnModel); + Model model = repositoryService.getModel(modelId); + List list = QueryUtils.modelQuery().modelKey(key).list(); + list.stream().filter(e -> !e.getId().equals(model.getId())).findFirst().ifPresent(e -> { + throw new ServiceException("模型KEY已存在!"); + }); + // 校验key命名规范 + if (!Validator.isMatchRegex(FlowConstant.MODEL_KEY_PATTERN, key)) { + throw new ServiceException("模型标识KEY只能字符或者下划线开头!"); + } + model.setKey(key); + model.setName(name); + model.setVersion(model.getVersion() + 1); + repositoryService.saveModel(model); + repositoryService.addModelEditorSource(model.getId(), StrUtil.utf8Bytes(xml)); + // 转换图片 + InputStream svgStream = new ByteArrayInputStream(StrUtil.utf8Bytes(svg)); + TranscoderInput input = new TranscoderInput(svgStream); + + PNGTranscoder transcoder = new PNGTranscoder(); + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + TranscoderOutput output = new TranscoderOutput(outStream); + + transcoder.transcode(input, output); + final byte[] result = outStream.toByteArray(); + repositoryService.addModelEditorSourceExtra(model.getId(), result); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 模型部署 + * + * @param id 模型id + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean modelDeploy(String id) { + try { + // 查询流程定义模型xml + byte[] xmlBytes = repositoryService.getModelEditorSource(id); + if (ArrayUtil.isEmpty(xmlBytes)) { + throw new ServiceException("模型数据为空,请先设计流程定义模型,再进行部署!"); + } + if (JSONUtil.isTypeJSON(new String(xmlBytes, StandardCharsets.UTF_8))) { + byte[] bytes = ModelUtils.bpmnJsonToXmlBytes(xmlBytes); + if (ArrayUtil.isEmpty(bytes)) { + throw new ServiceException("模型不能为空,请至少设计一条主线流程!"); + } + } + BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xmlBytes); + // 校验模型 + ModelUtils.checkBpmnModel(bpmnModel); + List validationErrors = repositoryService.validateProcess(bpmnModel); + if (CollUtil.isNotEmpty(validationErrors)) { + String errorMsg = validationErrors.stream().map(ValidationError::getProblem).distinct().collect(Collectors.joining(",")); + throw new ServiceException(errorMsg); + } + // 查询模型的基本信息 + Model model = repositoryService.getModel(id); + ProcessDefinition processDefinition = QueryUtils.definitionQuery().processDefinitionKey(model.getKey()).latestVersion().singleResult(); + // xml资源的名称 ,对应act_ge_bytearray表中的name_字段 + String processName = model.getName() + ".bpmn20.xml"; + // 调用部署相关的api方法进行部署流程定义 + Deployment deployment = repositoryService.createDeployment() + // 部署名称 + .name(model.getName()) + // 部署标识key + .key(model.getKey()) + // 部署流程分类 + .category(model.getCategory()) + // bpmn20.xml资源 + .addBytes(processName, xmlBytes) + // 租户id + .tenantId(TenantHelper.getTenantId()) + .deploy(); + + // 更新 部署id 到流程定义模型数据表中 + model.setDeploymentId(deployment.getId()); + repositoryService.saveModel(model); + // 更新分类 + ProcessDefinition definition = QueryUtils.definitionQuery().deploymentId(deployment.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory()); + //更新流程定义配置 + if (processDefinition != null) { + WfDefinitionConfigVo definitionVo = wfDefinitionConfigService.getByDefId(processDefinition.getId()); + if (definitionVo != null) { + wfDefinitionConfigService.deleteByDefIds(Collections.singletonList(processDefinition.getId())); + WfDefinitionConfigBo wfFormDefinition = new WfDefinitionConfigBo(); + wfFormDefinition.setDefinitionId(definition.getId()); + wfFormDefinition.setProcessKey(definition.getKey()); + wfFormDefinition.setTableName(definitionVo.getTableName()); + wfFormDefinition.setVersion(definition.getVersion()); + wfFormDefinition.setRemark(definitionVo.getRemark()); + wfDefinitionConfigService.saveOrUpdate(wfFormDefinition); + } + } + //更新流程节点配置表单 + List userTasks = ModelUtils.getUserTaskFlowElements(definition.getId()); + UserTask applyUserTask = ModelUtils.getApplyUserTask(definition.getId()); + List wfNodeConfigList = new ArrayList<>(); + for (UserTask userTask : userTasks) { + if (StringUtils.isNotBlank(userTask.getFormKey()) && userTask.getFormKey().contains(StrUtil.COLON)) { + WfNodeConfig wfNodeConfig = new WfNodeConfig(); + wfNodeConfig.setNodeId(userTask.getId()); + wfNodeConfig.setNodeName(userTask.getName()); + wfNodeConfig.setDefinitionId(definition.getId()); + String[] split = userTask.getFormKey().split(StrUtil.COLON); + wfNodeConfig.setFormType(split[0]); + wfNodeConfig.setFormId(Long.valueOf(split[1])); + wfNodeConfig.setApplyUserTask(applyUserTask.getId().equals(userTask.getId()) ? FlowConstant.TRUE : FlowConstant.FALSE); + wfNodeConfigList.add(wfNodeConfig); + } + } + if (CollUtil.isNotEmpty(wfNodeConfigList)) { + wfNodeConfigService.saveOrUpdate(wfNodeConfigList); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 导出模型zip压缩包 + * + * @param modelIds 模型id + * @param response 相应 + */ + @Override + public void exportZip(List modelIds, HttpServletResponse response) { + try (ZipOutputStream zos = ZipUtil.getZipOutputStream(response.getOutputStream(), StandardCharsets.UTF_8)) { + // 压缩包文件名 + String zipName = "模型不存在"; + // 查询模型基本信息 + for (String modelId : modelIds) { + Model model = repositoryService.getModel(modelId); + byte[] xmlBytes = repositoryService.getModelEditorSource(modelId); + if (ObjectUtil.isNotNull(model)) { + if (JSONUtil.isTypeJSON(new String(xmlBytes, StandardCharsets.UTF_8)) && ArrayUtil.isEmpty(ModelUtils.bpmnJsonToXmlBytes(xmlBytes))) { + zipName = "模型不能为空,请至少设计一条主线流程!"; + zos.putNextEntry(new ZipEntry(zipName + ".txt")); + zos.write(zipName.getBytes(StandardCharsets.UTF_8)); + } else if (ArrayUtil.isEmpty(xmlBytes)) { + zipName = "模型数据为空,请先设计流程定义模型,再进行部署!"; + zos.putNextEntry(new ZipEntry(zipName + ".txt")); + zos.write(zipName.getBytes(StandardCharsets.UTF_8)); + } else { + String fileName = model.getName() + "-" + model.getKey(); + // 压缩包文件名 + zipName = fileName + ".zip"; + // 将xml添加到压缩包中(指定xml文件名:请假流程.bpmn20.xml + zos.putNextEntry(new ZipEntry(fileName + ".bpmn20.xml")); + zos.write(xmlBytes); + } + } + } + response.setHeader("Content-Disposition", + "attachment; filename=" + URLEncoder.encode(zipName, StandardCharsets.UTF_8) + ".zip"); + response.addHeader("Access-Control-Expose-Headers", "Content-Disposition"); + // 刷出响应流 + response.flushBuffer(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 复制模型 + * + * @param modelBo 模型数据 + * @return 结果 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean copyModel(ModelBo modelBo) { + try { + String key = modelBo.getKey(); + if (StringUtils.isNotBlank(key)) { + // 查询模型 + Model model = repositoryService.createModelQuery().modelId(modelBo.getId()).singleResult(); + if (ObjectUtil.isNotNull(model)) { + byte[] modelEditorSource = repositoryService.getModelEditorSource(model.getId()); + List list = QueryUtils.modelQuery().modelKey(key).list(); + if (CollUtil.isNotEmpty(list)) { + throw new ServiceException("模型KEY已存在!"); + } + // 校验key命名规范 + if (!Validator.isMatchRegex(FlowConstant.MODEL_KEY_PATTERN, key)) { + throw new ServiceException("模型标识KEY只能字符或者下划线开头!"); + } + // 复制模型数据 + Model newModel = repositoryService.newModel(); + newModel.setKey(modelBo.getKey()); + newModel.setName(modelBo.getName()); + newModel.setCategory(modelBo.getCategoryCode()); + newModel.setVersion(1); + newModel.setMetaInfo(modelBo.getDescription()); + newModel.setTenantId(TenantHelper.getTenantId()); + String xml = StrUtil.utf8Str(modelEditorSource); + BpmnModel bpmnModel = ModelUtils.xmlToBpmnModel(xml); + Process mainProcess = bpmnModel.getMainProcess(); + mainProcess.setId(modelBo.getKey()); + mainProcess.setName(modelBo.getName()); + byte[] xmlBytes = new BpmnXMLConverter().convertToXML(bpmnModel); + repositoryService.saveModel(newModel); + repositoryService.addModelEditorSource(newModel.getId(), xmlBytes); + } + } + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + return true; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessDefinitionServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessDefinitionServiceImpl.java new file mode 100644 index 00000000..c37e0d01 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessDefinitionServiceImpl.java @@ -0,0 +1,438 @@ +package org.dromara.workflow.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.codec.Base64; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.workflow.common.constant.FlowConstant; +import org.dromara.workflow.domain.WfCategory; +import org.dromara.workflow.domain.WfDefinitionConfig; +import org.dromara.workflow.domain.WfNodeConfig; +import org.dromara.workflow.domain.bo.ProcessDefinitionBo; +import org.dromara.workflow.domain.bo.WfDefinitionConfigBo; +import org.dromara.workflow.domain.vo.ProcessDefinitionVo; +import org.dromara.workflow.domain.vo.WfDefinitionConfigVo; +import org.dromara.workflow.mapper.WfDefinitionConfigMapper; +import org.dromara.workflow.service.IActProcessDefinitionService; +import org.dromara.workflow.service.IWfCategoryService; +import org.dromara.workflow.service.IWfDefinitionConfigService; +import org.dromara.workflow.service.IWfNodeConfigService; +import org.dromara.workflow.utils.ModelUtils; +import org.dromara.workflow.utils.QueryUtils; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.ProcessMigrationService; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.bpmn.deployer.ResourceNameUtil; +import org.flowable.engine.repository.*; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +/** + * 流程定义 服务层实现 + * + * @author may + */ +@RequiredArgsConstructor +@Service +public class ActProcessDefinitionServiceImpl implements IActProcessDefinitionService { + + private final RepositoryService repositoryService; + private final ProcessMigrationService processMigrationService; + private final IWfCategoryService wfCategoryService; + private final IWfDefinitionConfigService wfDefinitionConfigService; + private final WfDefinitionConfigMapper wfDefinitionConfigMapper; + private final IWfNodeConfigService wfNodeConfigService; + + /** + * 分页查询 + * + * @param bo 参数 + * @return 返回分页列表 + */ + @Override + public TableDataInfo page(ProcessDefinitionBo bo, PageQuery pageQuery) { + ProcessDefinitionQuery query = QueryUtils.definitionQuery(); + if (StringUtils.isNotEmpty(bo.getKey())) { + query.processDefinitionKey(bo.getKey()); + } + if (StringUtils.isNotEmpty(bo.getCategoryCode())) { + query.processDefinitionCategory(bo.getCategoryCode()); + } + if (StringUtils.isNotEmpty(bo.getName())) { + query.processDefinitionNameLike("%" + bo.getName() + "%"); + } + query.orderByDeploymentId().desc(); + // 分页查询 + List processDefinitionVoList = new ArrayList<>(); + List definitionList = query.latestVersion().listPage(pageQuery.getFirstNum(), pageQuery.getPageSize()); + List deploymentList = null; + if (CollUtil.isNotEmpty(definitionList)) { + List deploymentIds = StreamUtils.toList(definitionList, ProcessDefinition::getDeploymentId); + deploymentList = QueryUtils.deploymentQuery(deploymentIds).list(); + } + if (CollUtil.isNotEmpty(definitionList)) { + List ids = StreamUtils.toList(definitionList, ProcessDefinition::getId); + List wfDefinitionConfigVos = wfDefinitionConfigService.queryList(ids); + for (ProcessDefinition processDefinition : definitionList) { + ProcessDefinitionVo processDefinitionVo = BeanUtil.toBean(processDefinition, ProcessDefinitionVo.class); + if (CollUtil.isNotEmpty(deploymentList)) { + // 部署时间 + deploymentList.stream().filter(e -> e.getId().equals(processDefinition.getDeploymentId())).findFirst().ifPresent(e -> { + processDefinitionVo.setDeploymentTime(e.getDeploymentTime()); + }); + } + if (CollUtil.isNotEmpty(wfDefinitionConfigVos)) { + wfDefinitionConfigVos.stream().filter(e -> e.getDefinitionId().equals(processDefinition.getId())).findFirst().ifPresent(processDefinitionVo::setWfDefinitionConfigVo); + } + processDefinitionVoList.add(processDefinitionVo); + } + } + // 总记录数 + long total = query.count(); + TableDataInfo build = TableDataInfo.build(); + build.setRows(processDefinitionVoList); + build.setTotal(total); + return build; + } + + /** + * 查询历史流程定义列表 + * + * @param key 流程定义key + */ + @Override + public List getListByKey(String key) { + List processDefinitionVoList = new ArrayList<>(); + ProcessDefinitionQuery query = QueryUtils.definitionQuery(); + List definitionList = query.processDefinitionKey(key).list(); + List deploymentList = null; + if (CollUtil.isNotEmpty(definitionList)) { + List deploymentIds = StreamUtils.toList(definitionList, ProcessDefinition::getDeploymentId); + deploymentList = QueryUtils.deploymentQuery(deploymentIds).list(); + } + if (CollUtil.isNotEmpty(definitionList)) { + List ids = StreamUtils.toList(definitionList, ProcessDefinition::getId); + List wfDefinitionConfigVos = wfDefinitionConfigService.queryList(ids); + for (ProcessDefinition processDefinition : definitionList) { + ProcessDefinitionVo processDefinitionVo = BeanUtil.toBean(processDefinition, ProcessDefinitionVo.class); + if (CollUtil.isNotEmpty(deploymentList)) { + // 部署时间 + deploymentList.stream().filter(e -> e.getId().equals(processDefinition.getDeploymentId())).findFirst().ifPresent(e -> { + processDefinitionVo.setDeploymentTime(e.getDeploymentTime()); + }); + if (CollUtil.isNotEmpty(wfDefinitionConfigVos)) { + wfDefinitionConfigVos.stream().filter(e -> e.getDefinitionId().equals(processDefinition.getId())).findFirst().ifPresent(processDefinitionVo::setWfDefinitionConfigVo); + } + } + processDefinitionVoList.add(processDefinitionVo); + } + } + return CollUtil.reverse(processDefinitionVoList); + } + + /** + * 查看流程定义图片 + * + * @param processDefinitionId 流程定义id + */ + @SneakyThrows + @Override + public String definitionImage(String processDefinitionId) { + InputStream inputStream = repositoryService.getProcessDiagram(processDefinitionId); + return Base64.encode(IoUtil.readBytes(inputStream)); + } + + /** + * 查看流程定义xml文件 + * + * @param processDefinitionId 流程定义id + */ + @Override + public String definitionXml(String processDefinitionId) { + StringBuilder xml = new StringBuilder(); + ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId); + InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName()); + xml.append(IoUtil.read(inputStream, StandardCharsets.UTF_8)); + return xml.toString(); + } + + /** + * 删除流程定义 + * + * @param deploymentIds 部署id + * @param processDefinitionIds 流程定义id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteDeployment(List deploymentIds, List processDefinitionIds) { + try { + List historicProcessInstances = QueryUtils.hisInstanceQuery().deploymentIdIn(deploymentIds).list(); + if (CollUtil.isNotEmpty(historicProcessInstances)) { + Set defIds = StreamUtils.toSet(historicProcessInstances, HistoricProcessInstance::getProcessDefinitionId); + List processDefinitions = QueryUtils.definitionQuery().processDefinitionIds(defIds).list(); + if (CollUtil.isNotEmpty(processDefinitions)) { + Set keys = StreamUtils.toSet(processDefinitions, ProcessDefinition::getKey); + throw new ServiceException("当前【" + String.join(",", keys) + "】流程定义已被使用不可删除!"); + } + } + //删除流程定义 + for (String deploymentId : deploymentIds) { + repositoryService.deleteDeployment(deploymentId); + } + //删除流程定义配置 + wfDefinitionConfigService.deleteByDefIds(processDefinitionIds); + //删除节点配置 + wfNodeConfigService.deleteByDefIds(processDefinitionIds); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 激活或者挂起流程定义 + * + * @param processDefinitionId 流程定义id + */ + @Override + public boolean updateDefinitionState(String processDefinitionId) { + try { + ProcessDefinition processDefinition = QueryUtils.definitionQuery() + .processDefinitionId(processDefinitionId).singleResult(); + //将当前为挂起状态更新为激活状态 + //参数说明:参数1:流程定义id,参数2:是否激活(true是否级联对应流程实例,激活了则对应流程实例都可以审批), + //参数3:什么时候激活,如果为null则立即激活,如果为具体时间则到达此时间后激活 + if (processDefinition.isSuspended()) { + repositoryService.activateProcessDefinitionById(processDefinitionId, true, null); + } else { + repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException("操作失败:" + e.getMessage()); + } + } + + /** + * 迁移流程定义 + * + * @param currentProcessDefinitionId 当前流程定义id + * @param fromProcessDefinitionId 需要迁移到的流程定义id + */ + + @Override + public boolean migrationDefinition(String currentProcessDefinitionId, String fromProcessDefinitionId) { + try { + // 迁移验证 + boolean migrationValid = processMigrationService.createProcessInstanceMigrationBuilder() + .migrateToProcessDefinition(currentProcessDefinitionId) + .validateMigrationOfProcessInstances(fromProcessDefinitionId) + .isMigrationValid(); + if (!migrationValid) { + throw new ServiceException("流程定义差异过大无法迁移,请修改流程图"); + } + // 已结束的流程实例不会迁移 + processMigrationService.createProcessInstanceMigrationBuilder() + .migrateToProcessDefinition(currentProcessDefinitionId) + .migrateProcessInstances(fromProcessDefinitionId); + return true; + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } + + /** + * 流程定义转换为模型 + * + * @param processDefinitionId 流程定义id + */ + @Override + public boolean convertToModel(String processDefinitionId) { + ProcessDefinition pd = QueryUtils.definitionQuery() + .processDefinitionId(processDefinitionId).singleResult(); + InputStream inputStream = repositoryService.getResourceAsStream(pd.getDeploymentId(), pd.getResourceName()); + ModelQuery query = QueryUtils.modelQuery(); + Model model = query.modelKey(pd.getKey()).singleResult(); + try { + if (ObjectUtil.isNotNull(model)) { + repositoryService.addModelEditorSource(model.getId(), IoUtil.readBytes(inputStream)); + } else { + Model modelData = repositoryService.newModel(); + modelData.setKey(pd.getKey()); + modelData.setName(pd.getName()); + modelData.setTenantId(pd.getTenantId()); + repositoryService.saveModel(modelData); + repositoryService.addModelEditorSource(modelData.getId(), IoUtil.readBytes(inputStream)); + } + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 通过zip或xml部署流程定义 + * + * @param file 文件 + * @param categoryCode 分类 + */ + @SneakyThrows + @Override + @Transactional(rollbackFor = Exception.class) + public void deployByFile(MultipartFile file, String categoryCode) { + + WfCategory wfCategory = wfCategoryService.queryByCategoryCode(categoryCode); + if (wfCategory == null) { + throw new ServiceException("流程分类不存在"); + } + // 文件后缀名 + String suffix = FileUtil.extName(file.getOriginalFilename()); + InputStream inputStream = file.getInputStream(); + if (FlowConstant.ZIP.equalsIgnoreCase(suffix)) { + ZipInputStream zipInputStream = null; + try { + zipInputStream = new ZipInputStream(inputStream); + ZipEntry zipEntry; + while ((zipEntry = zipInputStream.getNextEntry()) != null) { + String filename = zipEntry.getName(); + String[] splitFilename = filename.substring(0, filename.lastIndexOf(".")).split("-"); + //流程名称 + String processName = splitFilename[0]; + //流程key + String processKey = splitFilename[1]; + ProcessDefinition oldProcessDefinition = QueryUtils.definitionQuery().processDefinitionKey(processKey).latestVersion().singleResult(); + DeploymentBuilder builder = repositoryService.createDeployment(); + Deployment deployment = builder.addInputStream(filename, zipInputStream) + .tenantId(TenantHelper.getTenantId()) + .name(processName).key(processKey).category(categoryCode).deploy(); + ProcessDefinition definition = QueryUtils.definitionQuery().deploymentId(deployment.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), categoryCode); + setWfConfig(oldProcessDefinition, definition); + zipInputStream.closeEntry(); + } + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + if (zipInputStream != null) { + zipInputStream.close(); + } + } + //初始化配置数据(demo使用,不用可删除) + initWfDefConfig(); + } else { + String originalFilename = file.getOriginalFilename(); + String bpmnResourceSuffix = ResourceNameUtil.BPMN_RESOURCE_SUFFIXES[0]; + if (originalFilename.contains(bpmnResourceSuffix)) { + // 文件名 = 流程名称-流程key + String[] splitFilename = originalFilename.substring(0, originalFilename.lastIndexOf(".")).split("-"); + if (splitFilename.length < 2) { + throw new ServiceException("文件名 = 流程名称-流程KEY"); + } + //流程名称 + String processName = splitFilename[0]; + //流程key + String processKey = splitFilename[1]; + ProcessDefinition oldProcessDefinition = QueryUtils.definitionQuery().processDefinitionKey(processKey).latestVersion().singleResult(); + DeploymentBuilder builder = repositoryService.createDeployment(); + Deployment deployment = builder.addInputStream(originalFilename, inputStream) + .tenantId(TenantHelper.getTenantId()) + .name(processName).key(processKey).category(categoryCode).deploy(); + // 更新分类 + ProcessDefinition definition = QueryUtils.definitionQuery().deploymentId(deployment.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), categoryCode); + setWfConfig(oldProcessDefinition, definition); + } else { + throw new ServiceException("文件类型上传错误!"); + } + } + + } + + /** + * 初始化配置数据(demo使用,不用可删除) + */ + private void initWfDefConfig() { + List wfDefinitionConfigs = wfDefinitionConfigMapper.selectList(); + if (CollUtil.isEmpty(wfDefinitionConfigs)) { + ProcessDefinition processDefinition = QueryUtils.definitionQuery().processDefinitionKey("leave1").latestVersion().singleResult(); + if (processDefinition != null) { + WfDefinitionConfigBo wfDefinitionConfigBo = new WfDefinitionConfigBo(); + wfDefinitionConfigBo.setDefinitionId(processDefinition.getId()); + wfDefinitionConfigBo.setProcessKey(processDefinition.getKey()); + wfDefinitionConfigBo.setTableName("test_leave"); + wfDefinitionConfigBo.setVersion(processDefinition.getVersion()); + wfDefinitionConfigService.saveOrUpdate(wfDefinitionConfigBo); + } + } + + } + + /** + * 设置表单内容 + * + * @param oldProcessDefinition 部署前最新流程定义 + * @param definition 部署后最新流程定义 + */ + private void setWfConfig(ProcessDefinition oldProcessDefinition, ProcessDefinition definition) { + //更新流程定义表单 + if (oldProcessDefinition != null) { + WfDefinitionConfigVo definitionVo = wfDefinitionConfigService.getByDefId(oldProcessDefinition.getId()); + if (definitionVo != null) { + wfDefinitionConfigService.deleteByDefIds(Collections.singletonList(oldProcessDefinition.getId())); + WfDefinitionConfigBo wfDefinitionConfigBo = new WfDefinitionConfigBo(); + wfDefinitionConfigBo.setDefinitionId(definition.getId()); + wfDefinitionConfigBo.setProcessKey(definition.getKey()); + wfDefinitionConfigBo.setTableName(definitionVo.getTableName()); + wfDefinitionConfigBo.setVersion(definition.getVersion()); + wfDefinitionConfigBo.setRemark(definitionVo.getRemark()); + wfDefinitionConfigService.saveOrUpdate(wfDefinitionConfigBo); + } + } + //更新流程节点配置表单 + List userTasks = ModelUtils.getUserTaskFlowElements(definition.getId()); + UserTask applyUserTask = ModelUtils.getApplyUserTask(definition.getId()); + List wfNodeConfigList = new ArrayList<>(); + for (UserTask userTask : userTasks) { + if (StringUtils.isNotBlank(userTask.getFormKey()) && userTask.getFormKey().contains(StrUtil.COLON)) { + WfNodeConfig wfNodeConfig = new WfNodeConfig(); + wfNodeConfig.setNodeId(userTask.getId()); + wfNodeConfig.setNodeName(userTask.getName()); + wfNodeConfig.setDefinitionId(definition.getId()); + String[] split = userTask.getFormKey().split(StrUtil.COLON); + wfNodeConfig.setFormType(split[0]); + wfNodeConfig.setFormId(Long.valueOf(split[1])); + wfNodeConfig.setApplyUserTask(applyUserTask.getId().equals(userTask.getId()) ? FlowConstant.TRUE : FlowConstant.FALSE); + wfNodeConfigList.add(wfNodeConfig); + } + } + if (CollUtil.isNotEmpty(wfNodeConfigList)) { + wfNodeConfigService.saveOrUpdate(wfNodeConfigList); + } + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java new file mode 100644 index 00000000..68358766 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActProcessInstanceServiceImpl.java @@ -0,0 +1,662 @@ +package org.dromara.workflow.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.codec.Base64; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ObjectUtil; +import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.workflow.api.domain.bo.ProcessInstanceBo; +import org.dromara.workflow.api.domain.bo.ProcessInvalidBo; +import org.dromara.workflow.api.domain.bo.TaskUrgingBo; +import org.dromara.workflow.common.constant.FlowConstant; +import org.dromara.workflow.common.enums.BusinessStatusEnum; +import org.dromara.workflow.common.enums.TaskStatusEnum; +import org.dromara.workflow.domain.ActHiProcinst; +import org.dromara.workflow.domain.vo.*; +import org.dromara.workflow.flowable.CustomDefaultProcessDiagramGenerator; +import org.dromara.workflow.flowable.cmd.DeleteExecutionCmd; +import org.dromara.workflow.flowable.cmd.ExecutionChildByExecutionIdCmd; +import org.dromara.workflow.flowable.handler.FlowProcessEventHandler; +import org.dromara.workflow.service.IActHiProcinstService; +import org.dromara.workflow.service.IActProcessInstanceService; +import org.dromara.workflow.service.IWfNodeConfigService; +import org.dromara.workflow.service.IWfTaskBackNodeService; +import org.dromara.workflow.utils.QueryUtils; +import org.dromara.workflow.utils.WorkflowUtils; +import org.flowable.bpmn.model.*; +import org.flowable.engine.*; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.runtime.ProcessInstanceQuery; +import org.flowable.engine.task.Attachment; +import org.flowable.engine.task.Comment; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.awt.*; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 流程实例 服务层实现 + * + * @author may + */ +@Slf4j +@RequiredArgsConstructor +@Service +public class ActProcessInstanceServiceImpl implements IActProcessInstanceService { + + private final RepositoryService repositoryService; + private final RuntimeService runtimeService; + private final HistoryService historyService; + private final TaskService taskService; + private final IActHiProcinstService actHiProcinstService; + private final ManagementService managementService; + private final IWfTaskBackNodeService wfTaskBackNodeService; + private final IWfNodeConfigService wfNodeConfigService; + private final FlowProcessEventHandler flowProcessEventHandler; + + @Value("${flowable.activity-font-name}") + private String activityFontName; + + @Value("${flowable.label-font-name}") + private String labelFontName; + + @Value("${flowable.annotation-font-name}") + private String annotationFontName; + + /** + * 分页查询正在运行的流程实例 + * + * @param bo 参数 + */ + @Override + public TableDataInfo getPageByRunning(ProcessInstanceBo bo, PageQuery pageQuery) { + List list = new ArrayList<>(); + ProcessInstanceQuery query = QueryUtils.instanceQuery(); + if (StringUtils.isNotBlank(bo.getName())) { + query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%"); + } + if (StringUtils.isNotBlank(bo.getKey())) { + query.processDefinitionKey(bo.getKey()); + } + if (StringUtils.isNotBlank(bo.getStartUserId())) { + query.startedBy(bo.getStartUserId()); + } + if (StringUtils.isNotBlank(bo.getBusinessKey())) { + query.processInstanceBusinessKey(bo.getBusinessKey()); + } + if (StringUtils.isNotBlank(bo.getCategoryCode())) { + query.processDefinitionCategory(bo.getCategoryCode()); + } + query.orderByStartTime().desc(); + List processInstances = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize()); + for (ProcessInstance processInstance : processInstances) { + ProcessInstanceVo processInstanceVo = BeanUtil.toBean(processInstance, ProcessInstanceVo.class); + processInstanceVo.setIsSuspended(processInstance.isSuspended()); + processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstance.getBusinessStatus())); + list.add(processInstanceVo); + } + if (CollUtil.isNotEmpty(list)) { + List processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId); + List wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds); + for (ProcessInstanceVo processInstanceVo : list) { + if (CollUtil.isNotEmpty(wfNodeConfigVoList)) { + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo); + } + } + } + long count = query.count(); + TableDataInfo build = TableDataInfo.build(); + build.setRows(list); + build.setTotal(count); + return build; + } + + /** + * 分页查询已结束的流程实例 + * + * @param bo 参数 + */ + @Override + public TableDataInfo getPageByFinish(ProcessInstanceBo bo, PageQuery pageQuery) { + List list = new ArrayList<>(); + HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery() + .finished().orderByProcessInstanceEndTime().desc(); + if (StringUtils.isNotEmpty(bo.getName())) { + query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%"); + } + if (StringUtils.isNotBlank(bo.getKey())) { + query.processDefinitionKey(bo.getKey()); + } + if (StringUtils.isNotEmpty(bo.getStartUserId())) { + query.startedBy(bo.getStartUserId()); + } + if (StringUtils.isNotBlank(bo.getBusinessKey())) { + query.processInstanceBusinessKey(bo.getBusinessKey()); + } + if (StringUtils.isNotBlank(bo.getCategoryCode())) { + query.processDefinitionCategory(bo.getCategoryCode()); + } + List historicProcessInstances = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize()); + for (HistoricProcessInstance historicProcessInstance : historicProcessInstances) { + ProcessInstanceVo processInstanceVo = BeanUtil.toBean(historicProcessInstance, ProcessInstanceVo.class); + processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(historicProcessInstance.getBusinessStatus())); + list.add(processInstanceVo); + } + if (CollUtil.isNotEmpty(list)) { + List processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId); + List wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds); + for (ProcessInstanceVo processInstanceVo : list) { + if (CollUtil.isNotEmpty(wfNodeConfigVoList)) { + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo); + } + } + } + long count = query.count(); + TableDataInfo build = TableDataInfo.build(); + build.setRows(list); + build.setTotal(count); + return build; + } + + /** + * 通过业务id获取历史流程图 + * + * @param businessKey 业务id + */ + @SneakyThrows + @Override + public String getHistoryImage(String businessKey) { + String processDefinitionId; + // 获取当前的流程实例 + ProcessInstance processInstance = QueryUtils.businessKeyQuery(businessKey).singleResult(); + // 如果流程已经结束,则得到结束节点 + if (Objects.isNull(processInstance)) { + HistoricProcessInstance pi = QueryUtils.hisInstanceQuery().processInstanceBusinessKey(businessKey).singleResult(); + processDefinitionId = pi.getProcessDefinitionId(); + } else { + // 根据流程实例ID获得当前处于活动状态的ActivityId合集 + ProcessInstance pi = QueryUtils.instanceQuery(processInstance.getProcessInstanceId()).singleResult(); + processDefinitionId = pi.getProcessDefinitionId(); + } + + // 获得活动的节点 + List highLightedFlowList = QueryUtils.hisActivityInstanceQuery(processInstance.getProcessInstanceId()).orderByHistoricActivityInstanceStartTime().asc().list(); + + List highLightedFlows = new ArrayList<>(); + List highLightedNodes = new ArrayList<>(); + //高亮 + for (HistoricActivityInstance tempActivity : highLightedFlowList) { + if (FlowConstant.SEQUENCE_FLOW.equals(tempActivity.getActivityType())) { + //高亮线 + highLightedFlows.add(tempActivity.getActivityId()); + } else { + //高亮节点 + if (tempActivity.getEndTime() == null) { + highLightedNodes.add(Color.RED.toString() + tempActivity.getActivityId()); + } else { + highLightedNodes.add(tempActivity.getActivityId()); + } + } + } + List highLightedNodeList = new ArrayList<>(); + //运行中的节点 + List redNodeCollect = StreamUtils.filter(highLightedNodes, e -> e.contains(Color.RED.toString())); + //排除与运行中相同的节点 + for (String nodeId : highLightedNodes) { + if (!nodeId.contains(Color.RED.toString()) && !redNodeCollect.contains(Color.RED + nodeId)) { + highLightedNodeList.add(nodeId); + } + } + highLightedNodeList.addAll(redNodeCollect); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId); + CustomDefaultProcessDiagramGenerator diagramGenerator = new CustomDefaultProcessDiagramGenerator(); + InputStream inputStream = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedNodeList, highLightedFlows, activityFontName, labelFontName, annotationFontName, null, 1.0, true); + return Base64.encode(IoUtil.readBytes(inputStream)); + } + + /** + * 通过业务id获取历史流程图运行中,历史等节点 + * + * @param businessKey 业务id + */ + @Override + public Map getHistoryList(String businessKey) { + Map map = new HashMap<>(); + List> taskList = new ArrayList<>(); + HistoricProcessInstance historicProcessInstance = QueryUtils.hisBusinessKeyQuery(businessKey).singleResult(); + String processInstanceId = historicProcessInstance.getId(); + StringBuilder xml = new StringBuilder(); + ProcessDefinition processDefinition = repositoryService.getProcessDefinition(historicProcessInstance.getProcessDefinitionId()); + // 获取节点 + List highLightedFlowList = QueryUtils.hisActivityInstanceQuery(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list(); + for (HistoricActivityInstance tempActivity : highLightedFlowList) { + Map task = new HashMap<>(); + if (!FlowConstant.SEQUENCE_FLOW.equals(tempActivity.getActivityType()) && + !FlowConstant.PARALLEL_GATEWAY.equals(tempActivity.getActivityType()) && + !FlowConstant.EXCLUSIVE_GATEWAY.equals(tempActivity.getActivityType()) && + !FlowConstant.INCLUSIVE_GATEWAY.equals(tempActivity.getActivityType()) + ) { + task.put("key", tempActivity.getActivityId()); + task.put("completed", tempActivity.getEndTime() != null); + task.put("activityType", tempActivity.getActivityType()); + taskList.add(task); + } + } + ProcessInstance processInstance = QueryUtils.instanceQuery(processInstanceId).singleResult(); + if (processInstance != null) { + taskList = taskList.stream().filter(e -> !e.get("activityType").equals(FlowConstant.END_EVENT)).collect(Collectors.toList()); + } + //查询出运行中节点 + List> runtimeNodeList = taskList.stream().filter(e -> !(Boolean) e.get("completed")).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(runtimeNodeList)) { + Iterator> iterator = taskList.iterator(); + while (iterator.hasNext()) { + Map next = iterator.next(); + runtimeNodeList.stream().filter(t -> t.get("key").equals(next.get("key")) && (Boolean) next.get("completed")).findFirst().ifPresent(t -> iterator.remove()); + } + } + map.put("taskList", taskList); + List historyTaskList = getHistoryTaskList(processInstanceId); + map.put("historyList", historyTaskList); + InputStream inputStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName()); + xml.append(IoUtil.read(inputStream, StandardCharsets.UTF_8)); + map.put("xml", xml.toString()); + return map; + } + + /** + * 获取历史任务节点信息 + * + * @param processInstanceId 流程实例id + */ + private List getHistoryTaskList(String processInstanceId) { + //查询任务办理记录 + List list = QueryUtils.hisTaskInstanceQuery(processInstanceId).orderByHistoricTaskInstanceEndTime().desc().list(); + list = StreamUtils.sorted(list, Comparator.comparing(HistoricTaskInstance::getEndTime, Comparator.nullsFirst(Date::compareTo)).reversed()); + List actHistoryInfoVoList = new ArrayList<>(); + for (HistoricTaskInstance historicTaskInstance : list) { + ActHistoryInfoVo actHistoryInfoVo = new ActHistoryInfoVo(); + BeanUtils.copyProperties(historicTaskInstance, actHistoryInfoVo); + actHistoryInfoVo.setStatus(actHistoryInfoVo.getEndTime() == null ? "待处理" : "已处理"); + if (ObjectUtil.isNotEmpty(historicTaskInstance.getDurationInMillis())) { + actHistoryInfoVo.setRunDuration(getDuration(historicTaskInstance.getDurationInMillis())); + } + actHistoryInfoVoList.add(actHistoryInfoVo); + } + List historyInfoVoList = new ArrayList<>(); + Map> groupByKey = StreamUtils.groupByKey(actHistoryInfoVoList, ActHistoryInfoVo::getTaskDefinitionKey); + for (Map.Entry> entry : groupByKey.entrySet()) { + ActHistoryInfoVo historyInfoVo = new ActHistoryInfoVo(); + BeanUtils.copyProperties(entry.getValue().get(0), historyInfoVo); + actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() == null).findFirst() + .ifPresent(e -> { + historyInfoVo.setStatus("待处理"); + historyInfoVo.setStartTime(e.getStartTime()); + historyInfoVo.setEndTime(null); + historyInfoVo.setRunDuration(null); + if (ObjectUtil.isEmpty(e.getAssignee())) { + ParticipantVo participantVo = WorkflowUtils.getCurrentTaskParticipant(e.getId()); + if (ObjectUtil.isNotEmpty(participantVo) && CollUtil.isNotEmpty(participantVo.getCandidate())) { + historyInfoVo.setAssignee(StreamUtils.join(participantVo.getCandidate(), Convert::toStr)); + } + } + }); + historyInfoVoList.add(historyInfoVo); + } + return historyInfoVoList; + } + + /** + * 获取审批记录 + * + * @param businessKey 业务id + */ + @Override + public List getHistoryRecord(String businessKey) { + // 查询任务办理记录 + List list = QueryUtils.hisTaskBusinessKeyQuery(businessKey).orderByHistoricTaskInstanceEndTime().desc().list(); + list = StreamUtils.sorted(list, Comparator.comparing(HistoricTaskInstance::getEndTime, Comparator.nullsFirst(Date::compareTo)).reversed()); + HistoricProcessInstance historicProcessInstance = QueryUtils.hisBusinessKeyQuery(businessKey).singleResult(); + String processInstanceId = historicProcessInstance.getId(); + List actHistoryInfoVoList = new ArrayList<>(); + List processInstanceComments = taskService.getProcessInstanceComments(processInstanceId); + //附件 + List attachmentList = taskService.getProcessInstanceAttachments(processInstanceId); + for (HistoricTaskInstance historicTaskInstance : list) { + ActHistoryInfoVo actHistoryInfoVo = new ActHistoryInfoVo(); + BeanUtils.copyProperties(historicTaskInstance, actHistoryInfoVo); + if (actHistoryInfoVo.getEndTime() == null) { + actHistoryInfoVo.setStatus(TaskStatusEnum.WAITING.getStatus()); + actHistoryInfoVo.setStatusName(TaskStatusEnum.WAITING.getDesc()); + } + if (CollUtil.isNotEmpty(processInstanceComments)) { + processInstanceComments.stream().filter(e -> e.getTaskId().equals(historicTaskInstance.getId())).findFirst().ifPresent(e -> { + actHistoryInfoVo.setComment(e.getFullMessage()); + actHistoryInfoVo.setStatus(e.getType()); + actHistoryInfoVo.setStatusName(TaskStatusEnum.findByStatus(e.getType())); + }); + } + if (ObjectUtil.isNotEmpty(historicTaskInstance.getDurationInMillis())) { + actHistoryInfoVo.setRunDuration(getDuration(historicTaskInstance.getDurationInMillis())); + } + //附件 + if (CollUtil.isNotEmpty(attachmentList)) { + List attachments = attachmentList.stream().filter(e -> e.getTaskId().equals(historicTaskInstance.getId())).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(attachments)) { + actHistoryInfoVo.setAttachmentList(attachments); + } + } + //设置人员id + if (ObjectUtil.isEmpty(historicTaskInstance.getAssignee())) { + ParticipantVo participantVo = WorkflowUtils.getCurrentTaskParticipant(historicTaskInstance.getId()); + if (ObjectUtil.isNotEmpty(participantVo) && CollUtil.isNotEmpty(participantVo.getCandidate())) { + actHistoryInfoVo.setAssignee(StreamUtils.join(participantVo.getCandidate(), Convert::toStr)); + } + } + actHistoryInfoVoList.add(actHistoryInfoVo); + } + // 审批记录 + Map> groupByKey = StreamUtils.groupByKey(actHistoryInfoVoList, ActHistoryInfoVo::getTaskDefinitionKey); + for (Map.Entry> entry : groupByKey.entrySet()) { + ActHistoryInfoVo actHistoryInfoVo = BeanUtil.toBean(entry.getValue().get(0), ActHistoryInfoVo.class); + actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() != null).findFirst() + .ifPresent(e -> { + actHistoryInfoVo.setStatus("已处理"); + actHistoryInfoVo.setStartTime(e.getStartTime()); + }); + actHistoryInfoVoList.stream().filter(e -> e.getTaskDefinitionKey().equals(entry.getKey()) && e.getEndTime() == null).findFirst() + .ifPresent(e -> { + actHistoryInfoVo.setStatus("待处理"); + actHistoryInfoVo.setStartTime(e.getStartTime()); + actHistoryInfoVo.setEndTime(null); + actHistoryInfoVo.setRunDuration(null); + }); + } + List recordList = new ArrayList<>(); + // 待办理 + recordList.addAll(StreamUtils.filter(actHistoryInfoVoList, e -> e.getEndTime() == null)); + // 已办理 + recordList.addAll(StreamUtils.filter(actHistoryInfoVoList, e -> e.getEndTime() != null)); + + return recordList; + } + + /** + * 任务完成时间处理 + * + * @param time 时间 + */ + private String getDuration(long time) { + + long day = time / (24 * 60 * 60 * 1000); + long hour = (time / (60 * 60 * 1000) - day * 24); + long minute = ((time / (60 * 1000)) - day * 24 * 60 - hour * 60); + long second = (time / 1000 - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60); + + if (day > 0) { + return day + "天" + hour + "小时" + minute + "分钟"; + } + if (hour > 0) { + return hour + "小时" + minute + "分钟"; + } + if (minute > 0) { + return minute + "分钟"; + } + if (second > 0) { + return second + "秒"; + } else { + return 0 + "秒"; + } + } + + /** + * 作废流程实例,不会删除历史记录(删除运行中的实例) + * + * @param processInvalidBo 参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteRunInstance(ProcessInvalidBo processInvalidBo) { + try { + List list = QueryUtils.taskQuery().processInstanceBusinessKey(processInvalidBo.getBusinessKey()).list(); + String processInstanceId = list.get(0).getProcessInstanceId(); + List subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId())); + if (CollUtil.isNotEmpty(subTasks)) { + subTasks.forEach(e -> taskService.deleteTask(e.getId())); + } + String deleteReason = LoginHelper.getLoginUser().getNickname() + "作废了当前申请!"; + if (StringUtils.isNotBlank(processInvalidBo.getDeleteReason())) { + deleteReason = LoginHelper.getLoginUser().getNickname() + "作废理由:" + processInvalidBo.getDeleteReason(); + } + for (Task task : StreamUtils.filter(list, e -> StringUtils.isBlank(e.getParentTaskId()))) { + taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.INVALID.getStatus(), deleteReason); + } + HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(processInstanceId).singleResult(); + BusinessStatusEnum.checkInvalidStatus(historicProcessInstance.getBusinessStatus()); + runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.INVALID.getStatus()); + runtimeService.deleteProcessInstance(processInstanceId, deleteReason); + //流程作废监听 + flowProcessEventHandler.processHandler(historicProcessInstance.getProcessDefinitionKey(), + historicProcessInstance.getBusinessKey(), BusinessStatusEnum.INVALID.getStatus(), false); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param businessKeys 业务id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteRunAndHisInstance(List businessKeys) { + try { + // 1.删除运行中流程实例 + List actHiProcinsts = actHiProcinstService.selectByBusinessKeyIn(businessKeys); + if (CollUtil.isEmpty(actHiProcinsts)) { + log.warn("当前业务ID:{}查询到流程实例为空!", businessKeys); + return false; + } + List processInstanceIds = StreamUtils.toList(actHiProcinsts, ActHiProcinst::getId); + List list = QueryUtils.taskQuery(processInstanceIds).list(); + List subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId())); + if (CollUtil.isNotEmpty(subTasks)) { + subTasks.forEach(e -> taskService.deleteTask(e.getId())); + } + runtimeService.bulkDeleteProcessInstances(processInstanceIds, LoginHelper.getUserId() + "删除了当前流程申请"); + // 2.删除历史记录 + List historicProcessInstanceList = QueryUtils.hisInstanceQuery(new HashSet<>(processInstanceIds)).list(); + if (ObjectUtil.isNotEmpty(historicProcessInstanceList)) { + historyService.bulkDeleteHistoricProcessInstances(processInstanceIds); + } + wfTaskBackNodeService.deleteByInstanceIds(processInstanceIds); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 已完成的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param businessKeys 业务id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteFinishAndHisInstance(List businessKeys) { + try { + List actHiProcinsts = actHiProcinstService.selectByBusinessKeyIn(businessKeys); + if (CollUtil.isEmpty(actHiProcinsts)) { + log.warn("当前业务ID:{}查询到流程实例为空!", businessKeys); + return false; + } + List processInstanceIds = StreamUtils.toList(actHiProcinsts, ActHiProcinst::getId); + historyService.bulkDeleteHistoricProcessInstances(processInstanceIds); + wfTaskBackNodeService.deleteByInstanceIds(processInstanceIds); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 撤销流程申请 + * + * @param businessKey 业务id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean cancelProcessApply(String businessKey) { + try { + ProcessInstance processInstance = QueryUtils.businessKeyQuery(businessKey) + .startedBy(String.valueOf(LoginHelper.getUserId())).singleResult(); + if (ObjectUtil.isNull(processInstance)) { + throw new ServiceException("您不是流程发起人,撤销失败!"); + } + if (processInstance.isSuspended()) { + throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED); + } + String processInstanceId = processInstance.getId(); + BusinessStatusEnum.checkCancelStatus(processInstance.getBusinessStatus()); + List taskList = QueryUtils.taskQuery(processInstanceId).list(); + for (Task task : taskList) { + taskService.setAssignee(task.getId(), null); + taskService.addComment(task.getId(), processInstanceId, TaskStatusEnum.CANCEL.getStatus(), LoginHelper.getLoginUser().getNickname() + ":撤销申请"); + } + HistoricTaskInstance historicTaskInstance = QueryUtils.hisTaskInstanceQuery(processInstanceId).finished().orderByHistoricTaskInstanceEndTime().asc().list().get(0); + List nodeIds = StreamUtils.toList(taskList, Task::getTaskDefinitionKey); + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(processInstanceId) + .moveActivityIdsToSingleActivityId(nodeIds, historicTaskInstance.getTaskDefinitionKey()).changeState(); + Task task = QueryUtils.taskQuery(processInstanceId).list().get(0); + taskService.setAssignee(task.getId(), historicTaskInstance.getAssignee()); + //获取并行网关执行后保留的执行实例数据 + ExecutionChildByExecutionIdCmd childByExecutionIdCmd = new ExecutionChildByExecutionIdCmd(task.getExecutionId()); + List executionEntities = managementService.executeCommand(childByExecutionIdCmd); + //删除流程实例垃圾数据 + for (ExecutionEntity executionEntity : executionEntities) { + DeleteExecutionCmd deleteExecutionCmd = new DeleteExecutionCmd(executionEntity.getId()); + managementService.executeCommand(deleteExecutionCmd); + } + runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.CANCEL.getStatus()); + //流程作废监听 + flowProcessEventHandler.processHandler(processInstance.getProcessDefinitionKey(), + processInstance.getBusinessKey(), BusinessStatusEnum.CANCEL.getStatus(), false); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException("撤销失败:" + e.getMessage()); + } + } + + /** + * 分页查询当前登录人单据 + * + * @param bo 参数 + */ + @Override + public TableDataInfo getPageByCurrent(ProcessInstanceBo bo, PageQuery pageQuery) { + List list = new ArrayList<>(); + HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery(); + query.startedBy(String.valueOf(LoginHelper.getUserId())); + if (StringUtils.isNotBlank(bo.getName())) { + query.processInstanceNameLikeIgnoreCase("%" + bo.getName() + "%"); + } + if (StringUtils.isNotBlank(bo.getKey())) { + query.processDefinitionKey(bo.getKey()); + } + if (StringUtils.isNotBlank(bo.getBusinessKey())) { + query.processInstanceBusinessKey(bo.getBusinessKey()); + } + if (StringUtils.isNotBlank(bo.getCategoryCode())) { + query.processDefinitionCategory(bo.getCategoryCode()); + } + query.orderByProcessInstanceStartTime().desc(); + List historicProcessInstanceList = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize()); + List taskVoList = new ArrayList<>(); + if (CollUtil.isNotEmpty(historicProcessInstanceList)) { + List processInstanceIds = StreamUtils.toList(historicProcessInstanceList, HistoricProcessInstance::getId); + List taskList = QueryUtils.taskQuery(processInstanceIds).list(); + for (Task task : taskList) { + taskVoList.add(BeanUtil.toBean(task, TaskVo.class)); + } + } + for (HistoricProcessInstance processInstance : historicProcessInstanceList) { + ProcessInstanceVo processInstanceVo = BeanUtil.toBean(processInstance, ProcessInstanceVo.class); + processInstanceVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(processInstance.getBusinessStatus())); + if (CollUtil.isNotEmpty(taskVoList)) { + List collect = StreamUtils.filter(taskVoList, e -> e.getProcessInstanceId().equals(processInstance.getId())); + processInstanceVo.setTaskVoList(CollUtil.isNotEmpty(collect) ? collect : Collections.emptyList()); + } + list.add(processInstanceVo); + } + if (CollUtil.isNotEmpty(list)) { + List processDefinitionIds = StreamUtils.toList(list, ProcessInstanceVo::getProcessDefinitionId); + List wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds); + for (ProcessInstanceVo processInstanceVo : list) { + if (CollUtil.isNotEmpty(wfNodeConfigVoList)) { + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(processInstanceVo.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(processInstanceVo::setWfNodeConfigVo); + } + } + } + long count = query.count(); + TableDataInfo build = TableDataInfo.build(); + build.setRows(list); + build.setTotal(count); + return build; + } + + /** + * 任务催办(给当前任务办理人发送站内信,邮件,短信等) + * + * @param taskUrgingBo 任务催办 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean taskUrging(TaskUrgingBo taskUrgingBo) { + try { + ProcessInstance processInstance = QueryUtils.instanceQuery(taskUrgingBo.getProcessInstanceId()).singleResult(); + if (processInstance == null) { + throw new ServiceException("任务已结束!"); + } + String message = taskUrgingBo.getMessage(); + if (StringUtils.isBlank(message)) { + message = "您的【" + processInstance.getName() + "】单据还未审批,请您及时处理。"; + } + List list = QueryUtils.taskQuery(taskUrgingBo.getProcessInstanceId()).list(); + WorkflowUtils.sendMessage(list, processInstance.getName(), taskUrgingBo.getMessageType(), message); + } catch (ServiceException e) { + throw new ServiceException(e.getMessage()); + } + return true; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java new file mode 100644 index 00000000..6403ee95 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/ActTaskServiceImpl.java @@ -0,0 +1,857 @@ +package org.dromara.workflow.service.impl; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import org.apache.dubbo.config.annotation.DubboReference; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.system.api.RemoteUserService; +import org.dromara.system.api.domain.dto.UserDTO; +import org.dromara.system.api.model.RoleDTO; +import org.dromara.workflow.common.constant.FlowConstant; +import org.dromara.workflow.common.enums.BusinessStatusEnum; +import org.dromara.workflow.common.enums.TaskStatusEnum; +import org.dromara.workflow.domain.ActHiTaskinst; +import org.dromara.workflow.domain.WfTaskBackNode; +import org.dromara.workflow.domain.bo.*; +import org.dromara.workflow.domain.vo.*; +import org.dromara.workflow.flowable.cmd.*; +import org.dromara.workflow.flowable.handler.FlowProcessEventHandler; +import org.dromara.workflow.mapper.ActHiTaskinstMapper; +import org.dromara.workflow.mapper.ActTaskMapper; +import org.dromara.workflow.service.IActTaskService; +import org.dromara.workflow.service.IWfDefinitionConfigService; +import org.dromara.workflow.service.IWfNodeConfigService; +import org.dromara.workflow.service.IWfTaskBackNodeService; +import org.dromara.workflow.utils.ModelUtils; +import org.dromara.workflow.utils.QueryUtils; +import org.dromara.workflow.utils.WorkflowUtils; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.engine.*; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.identitylink.api.history.HistoricIdentityLink; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.flowable.variable.api.persistence.entity.VariableInstance; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.stream.Collectors; + +import static org.dromara.workflow.common.constant.FlowConstant.*; + +/** + * 任务 服务层实现 + * + * @author may + */ +@RequiredArgsConstructor +@Service +public class ActTaskServiceImpl implements IActTaskService { + + private final RuntimeService runtimeService; + private final TaskService taskService; + private final HistoryService historyService; + private final IdentityService identityService; + private final ManagementService managementService; + private final ActTaskMapper actTaskMapper; + private final IWfTaskBackNodeService wfTaskBackNodeService; + private final ActHiTaskinstMapper actHiTaskinstMapper; + private final IWfNodeConfigService wfNodeConfigService; + private final IWfDefinitionConfigService wfDefinitionConfigService; + @DubboReference + private final RemoteUserService userService; + private final FlowProcessEventHandler flowProcessEventHandler; + + /** + * 启动任务 + * + * @param startProcessBo 启动流程参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Map startWorkFlow(StartProcessBo startProcessBo) { + Map map = new HashMap<>(); + if (StringUtils.isBlank(startProcessBo.getBusinessKey())) { + throw new ServiceException("启动工作流时必须包含业务ID"); + } + // 判断当前业务是否启动过流程 + HistoricProcessInstanceQuery query = QueryUtils.hisInstanceQuery(); + HistoricProcessInstance historicProcessInstance = query.processInstanceBusinessKey(startProcessBo.getBusinessKey()).singleResult(); + if (ObjectUtil.isNotEmpty(historicProcessInstance)) { + BusinessStatusEnum.checkStartStatus(historicProcessInstance.getBusinessStatus()); + } + List taskResult = QueryUtils.taskQuery().processInstanceBusinessKey(startProcessBo.getBusinessKey()).list(); + if (CollUtil.isNotEmpty(taskResult)) { + if (CollUtil.isNotEmpty(startProcessBo.getVariables())) { + taskService.setVariables(taskResult.get(0).getId(), startProcessBo.getVariables()); + } + map.put(PROCESS_INSTANCE_ID, taskResult.get(0).getProcessInstanceId()); + map.put("taskId", taskResult.get(0).getId()); + return map; + } + WfDefinitionConfigVo wfDefinitionConfigVo = wfDefinitionConfigService.getByTableNameLastVersion(startProcessBo.getTableName()); + if (wfDefinitionConfigVo == null) { + throw new ServiceException("请到流程定义绑定业务表名与流程KEY!"); + } + // 设置启动人 + identityService.setAuthenticatedUserId(String.valueOf(LoginHelper.getUserId())); + Authentication.setAuthenticatedUserId(String.valueOf(LoginHelper.getUserId())); + // 启动流程实例(提交申请) + Map variables = startProcessBo.getVariables(); + // 启动跳过表达式 + variables.put(FLOWABLE_SKIP_EXPRESSION_ENABLED, true); + // 流程发起人 + variables.put(INITIATOR, (String.valueOf(LoginHelper.getUserId()))); + ProcessInstance pi; + try { + if (TenantHelper.isEnable()) { + pi = runtimeService.startProcessInstanceByKeyAndTenantId(wfDefinitionConfigVo.getProcessKey(), startProcessBo.getBusinessKey(), variables, TenantHelper.getTenantId()); + } else { + pi = runtimeService.startProcessInstanceByKey(wfDefinitionConfigVo.getProcessKey(), startProcessBo.getBusinessKey(), variables); + } + } catch (FlowableObjectNotFoundException e) { + throw new ServiceException("找不到当前【" + wfDefinitionConfigVo.getProcessKey() + "】流程定义!"); + } + // 将流程定义名称 作为 流程实例名称 + runtimeService.setProcessInstanceName(pi.getProcessInstanceId(), pi.getProcessDefinitionName()); + // 申请人执行流程 + List taskList = QueryUtils.taskQuery(pi.getId()).list(); + if (taskList.size() > 1) { + throw new ServiceException("请检查流程第一个环节是否为申请人!"); + } + + runtimeService.updateBusinessStatus(pi.getProcessInstanceId(), BusinessStatusEnum.DRAFT.getStatus()); + taskService.setAssignee(taskList.get(0).getId(), LoginHelper.getUserId().toString()); + taskService.setVariable(taskList.get(0).getId(), PROCESS_INSTANCE_ID, pi.getProcessInstanceId()); + taskService.setVariable(taskList.get(0).getId(), BUSINESS_KEY, pi.getBusinessKey()); + map.put("processInstanceId", pi.getProcessInstanceId()); + map.put("taskId", taskList.get(0).getId()); + return map; + } + + /** + * 办理任务 + * + * @param completeTaskBo 办理任务参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean completeTask(CompleteTaskBo completeTaskBo) { + try { + String userId = String.valueOf(LoginHelper.getUserId()); + Task task = WorkflowUtils.getTaskByCurrentUser(completeTaskBo.getTaskId()); + if (task == null) { + throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL); + } + if (task.isSuspended()) { + throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED); + } + ProcessInstance processInstance = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult(); + //办理委托任务 + if (ObjectUtil.isNotEmpty(task.getDelegationState()) && FlowConstant.PENDING.equals(task.getDelegationState().name())) { + taskService.resolveTask(completeTaskBo.getTaskId()); + TaskEntity newTask = WorkflowUtils.createNewTask(task); + taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.PASS.getStatus(), StringUtils.isNotBlank(completeTaskBo.getMessage()) ? completeTaskBo.getMessage() : StrUtil.EMPTY); + taskService.complete(newTask.getId()); + return true; + } + //附件上传 + AttachmentCmd attachmentCmd = new AttachmentCmd(completeTaskBo.getFileId(), task.getId(), task.getProcessInstanceId()); + managementService.executeCommand(attachmentCmd); + String businessStatus = WorkflowUtils.getBusinessStatus(processInstance.getBusinessKey()); + //流程提交监听 + if (BusinessStatusEnum.DRAFT.getStatus().equals(businessStatus) || BusinessStatusEnum.BACK.getStatus().equals(businessStatus) || BusinessStatusEnum.CANCEL.getStatus().equals(businessStatus)) { + flowProcessEventHandler.processHandler(processInstance.getProcessDefinitionKey(), processInstance.getBusinessKey(), businessStatus, true); + } + runtimeService.updateBusinessStatus(task.getProcessInstanceId(), BusinessStatusEnum.WAITING.getStatus()); + //办理监听 + String keyNode = processInstance.getProcessDefinitionKey() + "_" + task.getTaskDefinitionKey(); + flowProcessEventHandler.processTaskHandler(keyNode, task.getId(), processInstance.getBusinessKey()); + //办理意见 + taskService.addComment(completeTaskBo.getTaskId(), task.getProcessInstanceId(), TaskStatusEnum.PASS.getStatus(), StringUtils.isBlank(completeTaskBo.getMessage()) ? "同意" : completeTaskBo.getMessage()); + //办理任务 + taskService.setAssignee(task.getId(), userId); + if (CollUtil.isNotEmpty(completeTaskBo.getVariables())) { + taskService.complete(completeTaskBo.getTaskId(), completeTaskBo.getVariables()); + } else { + taskService.complete(completeTaskBo.getTaskId()); + } + //记录执行过的流程任务节点 + wfTaskBackNodeService.recordExecuteNode(task); + ProcessInstance pi = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult(); + if (pi == null) { + UpdateBusinessStatusCmd updateBusinessStatusCmd = new UpdateBusinessStatusCmd(task.getProcessInstanceId(), BusinessStatusEnum.FINISH.getStatus()); + managementService.executeCommand(updateBusinessStatusCmd); + flowProcessEventHandler.processHandler(processInstance.getProcessDefinitionKey(), processInstance.getBusinessKey(), + BusinessStatusEnum.FINISH.getStatus(), false); + } else { + List list = QueryUtils.taskQuery(task.getProcessInstanceId()).list(); + for (Task t : list) { + if (ModelUtils.isUserTask(t.getProcessDefinitionId(), t.getTaskDefinitionKey())) { + List links = historyService.getHistoricIdentityLinksForTask(t.getId()); + if (CollUtil.isEmpty(links) && StringUtils.isBlank(t.getAssignee())) { + throw new ServiceException("下一节点【" + t.getName() + "】没有办理人!"); + } + } + } + + if (CollUtil.isNotEmpty(list) && CollUtil.isNotEmpty(completeTaskBo.getWfCopyList())) { + TaskEntity newTask = WorkflowUtils.createNewTask(task); + taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.COPY.getStatus(), LoginHelper.getLoginUser().getNickname() + "【抄送】给" + String.join(",", StreamUtils.toList(completeTaskBo.getWfCopyList(), WfCopy::getUserName))); + taskService.complete(newTask.getId()); + List taskList = QueryUtils.taskQuery(task.getProcessInstanceId()).list(); + WorkflowUtils.createCopyTask(taskList, StreamUtils.toList(completeTaskBo.getWfCopyList(), WfCopy::getUserId)); + } + sendMessage(list, processInstance.getName(), completeTaskBo.getMessageType(), null); + } + return true; + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } + + /** + * 发送消息 + * + * @param list 任务 + * @param name 流程名称 + * @param messageType 消息类型 + * @param message 消息内容,为空则发送默认配置的消息内容 + */ + @Async + public void sendMessage(List list, String name, List messageType, String message) { + WorkflowUtils.sendMessage(list, name, messageType, message); + } + + /** + * 查询当前用户的待办任务 + * + * @param taskBo 参数 + */ + @Override + public TableDataInfo getPageByTaskWait(TaskBo taskBo, PageQuery pageQuery) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + List roles = LoginHelper.getLoginUser().getRoles(); + List roleIds = StreamUtils.toList(roles, e -> String.valueOf(e.getRoleId())); + String userId = String.valueOf(LoginHelper.getUserId()); + queryWrapper.eq("t.business_status_", BusinessStatusEnum.WAITING.getStatus()); + queryWrapper.eq(TenantHelper.isEnable(), "t.tenant_id_", TenantHelper.getTenantId()); + queryWrapper.and(w1 -> w1.eq("t.assignee_", userId).or(w2 -> w2.isNull("t.assignee_").apply("exists ( select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TASK_ID_ = t.ID_ and LINK.TYPE_ = 'candidate' " + "and (LINK.USER_ID_ = {0} or ( LINK.GROUP_ID_ IN " + getInParam(roleIds) + " ) ))", userId))); + if (StringUtils.isNotBlank(taskBo.getName())) { + queryWrapper.like("t.name_", taskBo.getName()); + } + if (StringUtils.isNotBlank(taskBo.getProcessDefinitionName())) { + queryWrapper.like("t.processDefinitionName", taskBo.getProcessDefinitionName()); + } + if (StringUtils.isNotBlank(taskBo.getProcessDefinitionKey())) { + queryWrapper.eq("t.processDefinitionKey", taskBo.getProcessDefinitionKey()); + } + Page page = actTaskMapper.getTaskWaitByPage(pageQuery.build(), queryWrapper); + + List taskList = page.getRecords(); + if (CollUtil.isNotEmpty(taskList)) { + List processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId); + List wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds); + for (TaskVo task : taskList) { + task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus())); + task.setParticipantVo(WorkflowUtils.getCurrentTaskParticipant(task.getId())); + task.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null); + if (CollUtil.isNotEmpty(wfNodeConfigVoList)) { + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo); + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo); + } + } + } + return TableDataInfo.build(page); + } + + private String getInParam(List param) { + StringBuilder sb = new StringBuilder(); + sb.append("("); + for (int i = 0; i < param.size(); i++) { + sb.append("'").append(param.get(i)).append("'"); + if (i != param.size() - 1) { + sb.append(","); + } + } + sb.append(")"); + return sb.toString(); + } + + /** + * 查询当前租户所有待办任务 + * + * @param taskBo 参数 + */ + @Override + public TableDataInfo getPageByAllTaskWait(TaskBo taskBo, PageQuery pageQuery) { + TaskQuery query = QueryUtils.taskQuery(); + if (StringUtils.isNotBlank(taskBo.getName())) { + query.taskNameLike("%" + taskBo.getName() + "%"); + } + if (StringUtils.isNotBlank(taskBo.getProcessDefinitionName())) { + query.processDefinitionNameLike("%" + taskBo.getProcessDefinitionName() + "%"); + } + if (StringUtils.isNotBlank(taskBo.getProcessDefinitionKey())) { + query.processDefinitionKey(taskBo.getProcessDefinitionKey()); + } + query.orderByTaskCreateTime().desc(); + List taskList = query.listPage(pageQuery.getFirstNum(), pageQuery.getPageSize()); + List processInstanceList = null; + if (CollUtil.isNotEmpty(taskList)) { + Set processInstanceIds = StreamUtils.toSet(taskList, Task::getProcessInstanceId); + processInstanceList = QueryUtils.instanceQuery(processInstanceIds).list(); + } + List list = new ArrayList<>(); + if (CollUtil.isNotEmpty(taskList)) { + List processDefinitionIds = StreamUtils.toList(taskList, Task::getProcessDefinitionId); + List wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds); + for (Task task : taskList) { + TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class); + if (CollUtil.isNotEmpty(processInstanceList)) { + processInstanceList.stream().filter(e -> e.getId().equals(task.getProcessInstanceId())).findFirst().ifPresent(e -> { + taskVo.setBusinessStatus(e.getBusinessStatus()); + taskVo.setBusinessStatusName(BusinessStatusEnum.findByStatus(taskVo.getBusinessStatus())); + taskVo.setProcessDefinitionKey(e.getProcessDefinitionKey()); + taskVo.setProcessDefinitionName(e.getProcessDefinitionName()); + taskVo.setProcessDefinitionVersion(e.getProcessDefinitionVersion()); + taskVo.setBusinessKey(e.getBusinessKey()); + }); + } + taskVo.setAssignee(StringUtils.isNotBlank(task.getAssignee()) ? Long.valueOf(task.getAssignee()) : null); + taskVo.setParticipantVo(WorkflowUtils.getCurrentTaskParticipant(task.getId())); + taskVo.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null); + if (CollUtil.isNotEmpty(wfNodeConfigVoList)) { + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(taskVo::setWfNodeConfigVo); + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(taskVo::setWfNodeConfigVo); + } + list.add(taskVo); + } + } + long count = query.count(); + TableDataInfo build = TableDataInfo.build(); + build.setRows(list); + build.setTotal(count); + return build; + } + + /** + * 查询当前用户的已办任务 + * + * @param taskBo 参数 + */ + @Override + public TableDataInfo getPageByTaskFinish(TaskBo taskBo, PageQuery pageQuery) { + String userId = String.valueOf(LoginHelper.getUserId()); + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.like(StringUtils.isNotBlank(taskBo.getName()), "t.name_", taskBo.getName()); + queryWrapper.like(StringUtils.isNotBlank(taskBo.getProcessDefinitionName()), "t.processDefinitionName", taskBo.getProcessDefinitionName()); + queryWrapper.eq(StringUtils.isNotBlank(taskBo.getProcessDefinitionKey()), "t.processDefinitionKey", taskBo.getProcessDefinitionKey()); + queryWrapper.eq("t.assignee_", userId); + Page page = actTaskMapper.getTaskFinishByPage(pageQuery.build(), queryWrapper); + + List taskList = page.getRecords(); + if (CollUtil.isNotEmpty(taskList)) { + List processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId); + List wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds); + for (TaskVo task : taskList) { + task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus())); + if (CollUtil.isNotEmpty(wfNodeConfigVoList)) { + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo); + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo); + } + } + } + return TableDataInfo.build(page); + } + + /** + * 查询当前用户的抄送 + * + * @param taskBo 参数 + */ + @Override + public TableDataInfo getPageByTaskCopy(TaskBo taskBo, PageQuery pageQuery) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + String userId = String.valueOf(LoginHelper.getUserId()); + if (StringUtils.isNotBlank(taskBo.getName())) { + queryWrapper.like("t.name_", taskBo.getName()); + } + if (StringUtils.isNotBlank(taskBo.getProcessDefinitionName())) { + queryWrapper.like("t.processDefinitionName", taskBo.getProcessDefinitionName()); + } + if (StringUtils.isNotBlank(taskBo.getProcessDefinitionKey())) { + queryWrapper.eq("t.processDefinitionKey", taskBo.getProcessDefinitionKey()); + } + queryWrapper.eq("t.assignee_", userId); + Page page = actTaskMapper.getTaskCopyByPage(pageQuery.build(), queryWrapper); + + List taskList = page.getRecords(); + if (CollUtil.isNotEmpty(taskList)) { + List processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId); + List wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds); + for (TaskVo task : taskList) { + task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus())); + if (CollUtil.isNotEmpty(wfNodeConfigVoList)) { + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo); + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo); + } + } + } + return TableDataInfo.build(page); + } + + /** + * 查询当前租户所有已办任务 + * + * @param taskBo 参数 + */ + @Override + public TableDataInfo getPageByAllTaskFinish(TaskBo taskBo, PageQuery pageQuery) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.like(StringUtils.isNotBlank(taskBo.getName()), "t.name_", taskBo.getName()); + queryWrapper.like(StringUtils.isNotBlank(taskBo.getProcessDefinitionName()), "t.processDefinitionName", taskBo.getProcessDefinitionName()); + queryWrapper.eq(StringUtils.isNotBlank(taskBo.getProcessDefinitionKey()), "t.processDefinitionKey", taskBo.getProcessDefinitionKey()); + Page page = actTaskMapper.getTaskFinishByPage(pageQuery.build(), queryWrapper); + + List taskList = page.getRecords(); + if (CollUtil.isNotEmpty(taskList)) { + List processDefinitionIds = StreamUtils.toList(taskList, TaskVo::getProcessDefinitionId); + List wfNodeConfigVoList = wfNodeConfigService.selectByDefIds(processDefinitionIds); + for (TaskVo task : taskList) { + task.setBusinessStatusName(BusinessStatusEnum.findByStatus(task.getBusinessStatus())); + if (CollUtil.isNotEmpty(wfNodeConfigVoList)) { + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && FlowConstant.TRUE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo); + wfNodeConfigVoList.stream().filter(e -> e.getDefinitionId().equals(task.getProcessDefinitionId()) && e.getNodeId().equals(task.getTaskDefinitionKey()) && FlowConstant.FALSE.equals(e.getApplyUserTask())).findFirst().ifPresent(task::setWfNodeConfigVo); + } + } + } + return TableDataInfo.build(page); + } + + /** + * 委派任务 + * + * @param delegateBo 参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean delegateTask(DelegateBo delegateBo) { + Task task = WorkflowUtils.getTaskByCurrentUser(delegateBo.getTaskId()); + + if (ObjectUtil.isEmpty(task)) { + throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL); + } + if (task.isSuspended()) { + throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED); + } + try { + TaskEntity newTask = WorkflowUtils.createNewTask(task); + taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.PENDING.getStatus(), "【" + LoginHelper.getLoginUser().getNickname() + "】委派给【" + delegateBo.getNickName() + "】"); + //委托任务 + taskService.delegateTask(delegateBo.getTaskId(), delegateBo.getUserId()); + //办理生成的任务记录 + taskService.complete(newTask.getId()); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 终止任务 + * + * @param terminationBo 参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean terminationTask(TerminationBo terminationBo) { + TaskQuery query = QueryUtils.taskQuery(); + Task task = query.taskId(terminationBo.getTaskId()).singleResult(); + + if (ObjectUtil.isEmpty(task)) { + throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL); + } + if (task.isSuspended()) { + throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED); + } + HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery().processInstanceId(task.getProcessInstanceId()).singleResult(); + BusinessStatusEnum.checkInvalidStatus(historicProcessInstance.getBusinessStatus()); + try { + if (StringUtils.isBlank(terminationBo.getComment())) { + terminationBo.setComment(LoginHelper.getLoginUser().getNickname() + "终止了申请"); + } else { + terminationBo.setComment(LoginHelper.getLoginUser().getNickname() + "终止了申请:" + terminationBo.getComment()); + } + taskService.addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.TERMINATION.getStatus(), terminationBo.getComment()); + List list = QueryUtils.taskQuery(task.getProcessInstanceId()).list(); + if (CollUtil.isNotEmpty(list)) { + List subTasks = StreamUtils.filter(list, e -> StringUtils.isNotBlank(e.getParentTaskId())); + if (CollUtil.isNotEmpty(subTasks)) { + subTasks.forEach(e -> taskService.deleteTask(e.getId())); + } + runtimeService.updateBusinessStatus(task.getProcessInstanceId(), BusinessStatusEnum.TERMINATION.getStatus()); + runtimeService.deleteProcessInstance(task.getProcessInstanceId(), StrUtil.EMPTY); + } + //流程终止监听 + flowProcessEventHandler.processHandler(historicProcessInstance.getProcessDefinitionKey(), + historicProcessInstance.getBusinessKey(), BusinessStatusEnum.TERMINATION.getStatus(), false); + return true; + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + } + + /** + * 转办任务 + * + * @param transmitBo 参数 + */ + @Override + public boolean transferTask(TransmitBo transmitBo) { + Task task = WorkflowUtils.getTaskByCurrentUser(transmitBo.getTaskId()); + if (ObjectUtil.isEmpty(task)) { + throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL); + } + if (task.isSuspended()) { + throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED); + } + try { + TaskEntity newTask = WorkflowUtils.createNewTask(task); + taskService.addComment(newTask.getId(), task.getProcessInstanceId(), TaskStatusEnum.TRANSFER.getStatus(), StringUtils.isNotBlank(transmitBo.getComment()) ? transmitBo.getComment() : LoginHelper.getUsername() + "转办了任务"); + taskService.complete(newTask.getId()); + taskService.setAssignee(task.getId(), transmitBo.getUserId()); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 会签任务加签 + * + * @param addMultiBo 参数 + */ + @Override + public boolean addMultiInstanceExecution(AddMultiBo addMultiBo) { + TaskQuery taskQuery = QueryUtils.taskQuery(); + taskQuery.taskId(addMultiBo.getTaskId()); + if (!LoginHelper.isSuperAdmin() && !LoginHelper.isTenantAdmin()) { + taskQuery.taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId())); + } + Task task = taskQuery.singleResult(); + if (ObjectUtil.isEmpty(task)) { + throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL); + } + if (task.isSuspended()) { + throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED); + } + String taskDefinitionKey = task.getTaskDefinitionKey(); + String processInstanceId = task.getProcessInstanceId(); + String processDefinitionId = task.getProcessDefinitionId(); + + try { + MultiInstanceVo multiInstanceVo = WorkflowUtils.isMultiInstance(processDefinitionId, taskDefinitionKey); + if (multiInstanceVo == null) { + throw new ServiceException("当前环节不是会签节点"); + } + if (multiInstanceVo.getType() instanceof ParallelMultiInstanceBehavior) { + for (Long assignee : addMultiBo.getAssignees()) { + runtimeService.addMultiInstanceExecution(taskDefinitionKey, processInstanceId, Collections.singletonMap(multiInstanceVo.getAssignee(), assignee)); + } + } else if (multiInstanceVo.getType() instanceof SequentialMultiInstanceBehavior) { + AddSequenceMultiInstanceCmd addSequenceMultiInstanceCmd = new AddSequenceMultiInstanceCmd(task.getExecutionId(), multiInstanceVo.getAssigneeList(), addMultiBo.getAssignees()); + managementService.executeCommand(addSequenceMultiInstanceCmd); + } + List assigneeNames = addMultiBo.getAssigneeNames(); + String username = LoginHelper.getUsername(); + TaskEntity newTask = WorkflowUtils.createNewTask(task); + taskService.addComment(newTask.getId(), processInstanceId, TaskStatusEnum.SIGN.getStatus(), username + "加签【" + String.join(StringUtils.SEPARATOR, assigneeNames) + "】"); + taskService.complete(newTask.getId()); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 会签任务减签 + * + * @param deleteMultiBo 参数 + */ + @Override + public boolean deleteMultiInstanceExecution(DeleteMultiBo deleteMultiBo) { + TaskQuery taskQuery = QueryUtils.taskQuery(); + taskQuery.taskId(deleteMultiBo.getTaskId()); + if (!LoginHelper.isSuperAdmin() && !LoginHelper.isTenantAdmin()) { + taskQuery.taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId())); + } + Task task = taskQuery.singleResult(); + if (ObjectUtil.isEmpty(task)) { + throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL); + } + if (task.isSuspended()) { + throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED); + } + String taskDefinitionKey = task.getTaskDefinitionKey(); + String processInstanceId = task.getProcessInstanceId(); + String processDefinitionId = task.getProcessDefinitionId(); + try { + MultiInstanceVo multiInstanceVo = WorkflowUtils.isMultiInstance(processDefinitionId, taskDefinitionKey); + if (multiInstanceVo == null) { + throw new ServiceException("当前环节不是会签节点"); + } + if (multiInstanceVo.getType() instanceof ParallelMultiInstanceBehavior) { + for (String executionId : deleteMultiBo.getExecutionIds()) { + runtimeService.deleteMultiInstanceExecution(executionId, false); + } + for (String taskId : deleteMultiBo.getTaskIds()) { + historyService.deleteHistoricTaskInstance(taskId); + } + } else if (multiInstanceVo.getType() instanceof SequentialMultiInstanceBehavior) { + DeleteSequenceMultiInstanceCmd deleteSequenceMultiInstanceCmd = new DeleteSequenceMultiInstanceCmd(task.getAssignee(), task.getExecutionId(), multiInstanceVo.getAssigneeList(), deleteMultiBo.getAssigneeIds()); + managementService.executeCommand(deleteSequenceMultiInstanceCmd); + } + List assigneeNames = deleteMultiBo.getAssigneeNames(); + String username = LoginHelper.getUsername(); + TaskEntity newTask = WorkflowUtils.createNewTask(task); + taskService.addComment(newTask.getId(), processInstanceId, TaskStatusEnum.SIGN_OFF.getStatus(), username + "减签【" + String.join(StringUtils.SEPARATOR, assigneeNames) + "】"); + taskService.complete(newTask.getId()); + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException(e.getMessage()); + } + } + + /** + * 驳回审批 + * + * @param backProcessBo 参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public String backProcess(BackProcessBo backProcessBo) { + String userId = String.valueOf(LoginHelper.getUserId()); + Task task = WorkflowUtils.getTaskByCurrentUser(backProcessBo.getTaskId()); + + if (ObjectUtil.isEmpty(task)) { + throw new ServiceException(FlowConstant.MESSAGE_CURRENT_TASK_IS_NULL); + } + if (task.isSuspended()) { + throw new ServiceException(FlowConstant.MESSAGE_SUSPENDED); + } + try { + String processInstanceId = task.getProcessInstanceId(); + ProcessInstance processInstance = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult(); + //获取并行网关执行后保留的执行实例数据 + ExecutionChildByExecutionIdCmd childByExecutionIdCmd = new ExecutionChildByExecutionIdCmd(task.getExecutionId()); + List executionEntities = managementService.executeCommand(childByExecutionIdCmd); + //校验单据 + BusinessStatusEnum.checkBackStatus(processInstance.getBusinessStatus()); + //判断是否有多个任务 + List taskList = QueryUtils.taskQuery(processInstanceId).list(); + String backTaskDefinitionKey = backProcessBo.getTargetActivityId(); + taskService.addComment(task.getId(), processInstanceId, TaskStatusEnum.BACK.getStatus(), StringUtils.isNotBlank(backProcessBo.getMessage()) ? backProcessBo.getMessage() : "退回"); + if (taskList.size() > 1) { + //当前多个任务驳回到单个节点 + runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveActivityIdsToSingleActivityId(taskList.stream().map(Task::getTaskDefinitionKey).distinct().collect(Collectors.toList()), backTaskDefinitionKey).changeState(); + ActHiTaskinst actHiTaskinst = new ActHiTaskinst(); + actHiTaskinst.setAssignee(userId); + actHiTaskinst.setId(task.getId()); + actHiTaskinstMapper.updateById(actHiTaskinst); + } else { + //当前单个节点驳回单个节点 + runtimeService.createChangeActivityStateBuilder().processInstanceId(processInstanceId).moveActivityIdTo(task.getTaskDefinitionKey(), backTaskDefinitionKey).changeState(); + } + //删除并行环节未办理记录 + MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()); + if (multiInstance == null && taskList.size() > 1) { + List tasks = StreamUtils.filter(taskList, e -> !e.getTaskDefinitionKey().equals(task.getTaskDefinitionKey())); + actHiTaskinstMapper.deleteBatchIds(StreamUtils.toList(tasks, Task::getId)); + } + + + List instanceList = QueryUtils.hisTaskInstanceQuery(processInstanceId).finished().orderByHistoricTaskInstanceEndTime().desc().list(); + List list = QueryUtils.taskQuery(processInstanceId).list(); + for (Task t : list) { + instanceList.stream().filter(e -> e.getTaskDefinitionKey().equals(t.getTaskDefinitionKey())).findFirst().ifPresent(e -> { + taskService.setAssignee(t.getId(), e.getAssignee()); + }); + } + //发送消息 + String message = "您的【" + processInstance.getName() + "】单据已经被驳回,请您注意查收。"; + sendMessage(list, processInstance.getName(), backProcessBo.getMessageType(), message); + //删除流程实例垃圾数据 + for (ExecutionEntity executionEntity : executionEntities) { + DeleteExecutionCmd deleteExecutionCmd = new DeleteExecutionCmd(executionEntity.getId()); + managementService.executeCommand(deleteExecutionCmd); + } + + WfTaskBackNode wfTaskBackNode = wfTaskBackNodeService.getListByInstanceIdAndNodeId(task.getProcessInstanceId(), backProcessBo.getTargetActivityId()); + if (ObjectUtil.isNotNull(wfTaskBackNode) && wfTaskBackNode.getOrderNo() == 0) { + runtimeService.updateBusinessStatus(processInstanceId, BusinessStatusEnum.BACK.getStatus()); + flowProcessEventHandler.processHandler(processInstance.getProcessDefinitionKey(), + processInstance.getBusinessKey(), BusinessStatusEnum.BACK.getStatus(), false); + } + //删除驳回后的流程节点 + wfTaskBackNodeService.deleteBackTaskNode(processInstanceId, backProcessBo.getTargetActivityId()); + } catch (Exception e) { + throw new ServiceException(e.getMessage()); + } + return task.getProcessInstanceId(); + } + + /** + * 修改任务办理人 + * + * @param taskIds 任务id + * @param userId 办理人id + */ + @Override + @Transactional(rollbackFor = Exception.class) + public boolean updateAssignee(String[] taskIds, String userId) { + try { + List list = QueryUtils.taskQuery().taskIds(Arrays.asList(taskIds)).list(); + for (Task task : list) { + taskService.setAssignee(task.getId(), userId); + } + } catch (Exception e) { + throw new ServiceException("修改失败:" + e.getMessage()); + } + return true; + } + + /** + * 查询流程变量 + * + * @param taskId 任务id + */ + @Override + public List getInstanceVariable(String taskId) { + List variableVoList = new ArrayList<>(); + Map variableInstances = taskService.getVariableInstances(taskId); + if (CollUtil.isNotEmpty(variableInstances)) { + for (Map.Entry entry : variableInstances.entrySet()) { + VariableVo variableVo = new VariableVo(); + variableVo.setKey(entry.getKey()); + variableVo.setValue(entry.getValue().getValue().toString()); + variableVoList.add(variableVo); + } + } + return variableVoList; + } + + /** + * 查询工作流任务用户选择加签人员 + * + * @param taskId 任务id + * @return + */ + @Override + @SuppressWarnings("unchecked") + public String getTaskUserIdsByAddMultiInstance(String taskId) { + Task task = QueryUtils.taskQuery().taskId(taskId).singleResult(); + if (task == null) { + throw new ServiceException("任务不存在"); + } + MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()); + if (multiInstance == null) { + return ""; + } + List userIds; + if (multiInstance.getType() instanceof SequentialMultiInstanceBehavior) { + userIds = (List) runtimeService.getVariable(task.getExecutionId(), multiInstance.getAssigneeList()); + } else { + List list = QueryUtils.taskQuery(task.getProcessInstanceId()).list(); + userIds = StreamUtils.toList(list, e -> Long.valueOf(e.getAssignee())); + } + return StringUtils.join(userIds, StringUtils.SEPARATOR); + } + + /** + * 查询工作流选择减签人员 + * + * @param taskId 任务id 任务id + */ + @Override + @SuppressWarnings("unchecked") + public List getListByDeleteMultiInstance(String taskId) { + Task task = QueryUtils.taskQuery().taskId(taskId).singleResult(); + List taskList = QueryUtils.taskQuery(task.getProcessInstanceId()).list(); + MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()); + List taskListVo = new ArrayList<>(); + if (multiInstance == null) { + return List.of(); + } + List assigneeList = new ArrayList<>(); + if (multiInstance.getType() instanceof SequentialMultiInstanceBehavior) { + List variable = (List) runtimeService.getVariable(task.getExecutionId(), multiInstance.getAssigneeList()); + for (Object o : variable) { + assigneeList.add(Long.valueOf(o.toString())); + } + } + + if (multiInstance.getType() instanceof SequentialMultiInstanceBehavior) { + List userIds = StreamUtils.filter(assigneeList, e -> !String.valueOf(e).equals(task.getAssignee())); + List userList = userService.selectListByIds(userIds); + for (Long userId : userIds) { + TaskVo taskVo = new TaskVo(); + taskVo.setId("串行会签"); + taskVo.setExecutionId("串行会签"); + taskVo.setProcessInstanceId(task.getProcessInstanceId()); + taskVo.setName(task.getName()); + taskVo.setAssignee(userId); + if (CollUtil.isNotEmpty(userList)) { + userList.stream().filter(u -> u.getUserId().toString().equals(userId.toString())).findFirst().ifPresent(u -> taskVo.setAssigneeName(u.getNickName())); + } + taskListVo.add(taskVo); + } + return taskListVo; + } else if (multiInstance.getType() instanceof ParallelMultiInstanceBehavior) { + List tasks = StreamUtils.filter(taskList, e -> StringUtils.isBlank(e.getParentTaskId()) && !e.getExecutionId().equals(task.getExecutionId()) && e.getTaskDefinitionKey().equals(task.getTaskDefinitionKey())); + if (CollUtil.isNotEmpty(tasks)) { + List userIds = StreamUtils.toList(tasks, e -> Long.valueOf(e.getAssignee())); + List userList = userService.selectListByIds(userIds); + for (Task t : tasks) { + TaskVo taskVo = new TaskVo(); + taskVo.setId(t.getId()); + taskVo.setExecutionId(t.getExecutionId()); + taskVo.setProcessInstanceId(t.getProcessInstanceId()); + taskVo.setName(t.getName()); + taskVo.setAssignee(Long.valueOf(t.getAssignee())); + if (CollUtil.isNotEmpty(userList)) { + userList.stream().filter(u -> u.getUserId().toString().equals(t.getAssignee())).findFirst().ifPresent(e -> taskVo.setAssigneeName(e.getNickName())); + } + taskListVo.add(taskVo); + } + return taskListVo; + } + } + return List.of(); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfCategoryServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfCategoryServiceImpl.java new file mode 100644 index 00000000..c5cb2888 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfCategoryServiceImpl.java @@ -0,0 +1,129 @@ +package org.dromara.workflow.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.workflow.domain.WfCategory; +import org.dromara.workflow.domain.bo.WfCategoryBo; +import org.dromara.workflow.domain.vo.WfCategoryVo; +import org.dromara.workflow.mapper.WfCategoryMapper; +import org.dromara.workflow.service.IWfCategoryService; +import org.dromara.workflow.utils.QueryUtils; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.List; + +/** + * 流程分类Service业务层处理 + * + * @author may + * @date 2023-06-28 + */ +@RequiredArgsConstructor +@Service +public class WfCategoryServiceImpl implements IWfCategoryService { + + private final WfCategoryMapper baseMapper; + + private final RepositoryService repositoryService; + + /** + * 查询流程分类 + */ + @Override + public WfCategoryVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + + /** + * 查询流程分类列表 + */ + @Override + public List queryList(WfCategoryBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(WfCategoryBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getCategoryName()), WfCategory::getCategoryName, bo.getCategoryName()); + lqw.eq(StringUtils.isNotBlank(bo.getCategoryCode()), WfCategory::getCategoryCode, bo.getCategoryCode()); + return lqw; + } + + /** + * 新增流程分类 + */ + @Override + public Boolean insertByBo(WfCategoryBo bo) { + WfCategory add = MapstructUtils.convert(bo, WfCategory.class); + validEntityBeforeSave(add); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改流程分类 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean updateByBo(WfCategoryBo bo) { + WfCategory update = MapstructUtils.convert(bo, WfCategory.class); + validEntityBeforeSave(update); + WfCategoryVo wfCategoryVo = baseMapper.selectVoById(bo.getId()); + List processDefinitionList = QueryUtils.definitionQuery().processDefinitionCategory(wfCategoryVo.getCategoryCode()).list(); + for (ProcessDefinition processDefinition : processDefinitionList) { + repositoryService.setProcessDefinitionCategory(processDefinition.getId(), bo.getCategoryCode()); + } + List deploymentList = QueryUtils.deploymentQuery().deploymentCategory(wfCategoryVo.getCategoryCode()).list(); + for (Deployment deployment : deploymentList) { + repositoryService.setDeploymentCategory(deployment.getId(), bo.getCategoryCode()); + } + List modelList = QueryUtils.modelQuery().modelCategory(wfCategoryVo.getCategoryCode()).list(); + for (Model model : modelList) { + model.setCategory(bo.getCategoryCode()); + repositoryService.saveModel(model); + } + return baseMapper.updateById(update) > 0; + } + + /** + * 保存前的数据校验 + */ + private void validEntityBeforeSave(WfCategory entity) { + //TODO 做一些数据校验,如唯一约束 + } + + /** + * 批量删除流程分类 + */ + @Override + public Boolean deleteWithValidByIds(Collection ids, Boolean isValid) { + if (isValid) { + //TODO 做一些业务上的校验,判断是否需要校验 + } + return baseMapper.deleteBatchIds(ids) > 0; + } + + /** + * 按照类别编码查询 + * + * @param categoryCode 分类比吗 + */ + @Override + public WfCategory queryByCategoryCode(String categoryCode) { + return baseMapper.selectOne(new LambdaQueryWrapper().eq(WfCategory::getCategoryCode, categoryCode)); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfDefinitionConfigServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfDefinitionConfigServiceImpl.java new file mode 100644 index 00000000..94788257 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfDefinitionConfigServiceImpl.java @@ -0,0 +1,117 @@ +package org.dromara.workflow.service.impl; + +import cn.hutool.core.collection.CollUtil; +import org.dromara.common.core.utils.MapstructUtils; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import org.dromara.workflow.domain.WfDefinitionConfig; +import org.dromara.workflow.domain.bo.WfDefinitionConfigBo; +import org.dromara.workflow.domain.vo.WfDefinitionConfigVo; +import org.dromara.workflow.service.IWfDefinitionConfigService; +import org.springframework.stereotype.Service; +import org.dromara.workflow.mapper.WfDefinitionConfigMapper; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Collection; + +/** + * 流程定义配置Service业务层处理 + * + * @author may + * @date 2024-03-18 + */ +@RequiredArgsConstructor +@Service +public class WfDefinitionConfigServiceImpl implements IWfDefinitionConfigService { + + private final WfDefinitionConfigMapper baseMapper; + + /** + * 查询流程定义配置 + */ + @Override + public WfDefinitionConfigVo getByDefId(String definitionId) { + return baseMapper.selectVoOne(new LambdaQueryWrapper().eq(WfDefinitionConfig::getDefinitionId, definitionId)); + } + + /** + * 查询流程定义配置 + * + * @param tableName 表名 + * @return 结果 + */ + @Override + public WfDefinitionConfigVo getByTableNameLastVersion(String tableName) { + List wfDefinitionConfigVos = baseMapper.selectVoList( + new LambdaQueryWrapper().eq(WfDefinitionConfig::getTableName, tableName).orderByDesc(WfDefinitionConfig::getVersion)); + if (CollUtil.isNotEmpty(wfDefinitionConfigVos)) { + return wfDefinitionConfigVos.get(0); + } + return null; + } + + /** + * 查询流程定义配置 + * + * @param definitionId 流程定义id + * @param tableName 表名 + * @return 结果 + */ + @Override + public WfDefinitionConfigVo getByDefIdAndTableName(String definitionId, String tableName) { + return baseMapper.selectVoOne(new LambdaQueryWrapper() + .eq(WfDefinitionConfig::getDefinitionId, definitionId) + .eq(WfDefinitionConfig::getTableName, tableName)); + } + + /** + * 查询流程定义配置排除当前查询的流程定义 + * + * @param tableName 表名 + * @param definitionId 流程定义id + */ + @Override + public List getByTableNameNotDefId(String tableName, String definitionId) { + return baseMapper.selectVoList(new LambdaQueryWrapper() + .eq(WfDefinitionConfig::getTableName, tableName) + .ne(WfDefinitionConfig::getDefinitionId, definitionId)); + } + + /** + * 查询流程定义配置列表 + */ + @Override + public List queryList(List definitionIds) { + return baseMapper.selectVoList(new LambdaQueryWrapper().in(WfDefinitionConfig::getDefinitionId, definitionIds)); + } + + /** + * 新增流程定义配置 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean saveOrUpdate(WfDefinitionConfigBo bo) { + WfDefinitionConfig add = MapstructUtils.convert(bo, WfDefinitionConfig.class); + baseMapper.delete(new LambdaQueryWrapper().eq(WfDefinitionConfig::getTableName, bo.getTableName())); + add.setTableName(add.getTableName().toLowerCase()); + boolean flag = baseMapper.insertOrUpdate(add); + if (baseMapper.insertOrUpdate(add)) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 批量删除流程定义配置 + */ + @Override + public Boolean deleteByIds(Collection ids) { + return baseMapper.deleteBatchIds(ids) > 0; + } + + @Override + public Boolean deleteByDefIds(Collection ids) { + return baseMapper.delete(new LambdaQueryWrapper().in(WfDefinitionConfig::getDefinitionId, ids)) > 0; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfFormManageServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfFormManageServiceImpl.java new file mode 100644 index 00000000..e0850d7a --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfFormManageServiceImpl.java @@ -0,0 +1,111 @@ +package org.dromara.workflow.service.impl; + +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.mybatis.core.page.PageQuery; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import lombok.RequiredArgsConstructor; +import org.dromara.workflow.common.enums.FormTypeEnum; +import org.springframework.stereotype.Service; +import org.dromara.workflow.domain.bo.WfFormManageBo; +import org.dromara.workflow.domain.vo.WfFormManageVo; +import org.dromara.workflow.domain.WfFormManage; +import org.dromara.workflow.mapper.WfFormManageMapper; +import org.dromara.workflow.service.IWfFormManageService; + +import java.util.List; +import java.util.Collection; + +/** + * 表单管理Service业务层处理 + * + * @author may + * @date 2024-03-29 + */ +@RequiredArgsConstructor +@Service +public class WfFormManageServiceImpl implements IWfFormManageService { + + private final WfFormManageMapper baseMapper; + + /** + * 查询表单管理 + */ + @Override + public WfFormManageVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + @Override + public List queryByIds(List ids) { + return baseMapper.selectVoBatchIds(ids); + } + + /** + * 查询表单管理列表 + */ + @Override + public TableDataInfo queryPageList(WfFormManageBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + return TableDataInfo.build(result); + } + + @Override + public List selectList() { + List wfFormManageVos = baseMapper.selectVoList(new LambdaQueryWrapper().orderByDesc(WfFormManage::getUpdateTime)); + for (WfFormManageVo wfFormManageVo : wfFormManageVos) { + wfFormManageVo.setFormTypeName(FormTypeEnum.findByType(wfFormManageVo.getFormType())); + } + return wfFormManageVos; + } + + /** + * 查询表单管理列表 + */ + @Override + public List queryList(WfFormManageBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(WfFormManageBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.like(StringUtils.isNotBlank(bo.getFormName()), WfFormManage::getFormName, bo.getFormName()); + lqw.eq(StringUtils.isNotBlank(bo.getFormType()), WfFormManage::getFormType, bo.getFormType()); + return lqw; + } + + /** + * 新增表单管理 + */ + @Override + public Boolean insertByBo(WfFormManageBo bo) { + WfFormManage add = MapstructUtils.convert(bo, WfFormManage.class); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + return flag; + } + + /** + * 修改表单管理 + */ + @Override + public Boolean updateByBo(WfFormManageBo bo) { + WfFormManage update = MapstructUtils.convert(bo, WfFormManage.class); + return baseMapper.updateById(update) > 0; + } + + /** + * 批量删除表单管理 + */ + @Override + public Boolean deleteByIds(Collection ids) { + return baseMapper.deleteBatchIds(ids) > 0; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfNodeConfigServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfNodeConfigServiceImpl.java new file mode 100644 index 00000000..c677102c --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfNodeConfigServiceImpl.java @@ -0,0 +1,75 @@ +package org.dromara.workflow.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.workflow.domain.vo.WfFormManageVo; +import org.dromara.workflow.service.IWfFormManageService; +import org.springframework.stereotype.Service; +import org.dromara.workflow.domain.vo.WfNodeConfigVo; +import org.dromara.workflow.domain.WfNodeConfig; +import org.dromara.workflow.mapper.WfNodeConfigMapper; +import org.dromara.workflow.service.IWfNodeConfigService; + +import java.util.Collection; +import java.util.List; + +/** + * 节点配置Service业务层处理 + * + * @author may + * @date 2024-03-30 + */ +@RequiredArgsConstructor +@Service +public class WfNodeConfigServiceImpl implements IWfNodeConfigService { + + private final WfNodeConfigMapper baseMapper; + private final IWfFormManageService wfFormManageService; + + /** + * 查询节点配置 + */ + @Override + public WfNodeConfigVo queryById(Long id) { + return baseMapper.selectVoById(id); + } + + /** + * 保存节点配置 + */ + @Override + public Boolean saveOrUpdate(List list) { + return baseMapper.insertOrUpdateBatch(list); + } + + /** + * 批量删除节点配置 + */ + @Override + public Boolean deleteByIds(Collection ids) { + return baseMapper.deleteBatchIds(ids) > 0; + } + + + + @Override + public Boolean deleteByDefIds(Collection ids) { + return baseMapper.delete(new LambdaQueryWrapper().in(WfNodeConfig::getDefinitionId, ids)) > 0; + } + + @Override + public List selectByDefIds(Collection ids) { + List wfNodeConfigVos = baseMapper.selectVoList(new LambdaQueryWrapper().in(WfNodeConfig::getDefinitionId, ids)); + if (CollUtil.isNotEmpty(wfNodeConfigVos)) { + List formIds = StreamUtils.toList(wfNodeConfigVos, WfNodeConfigVo::getFormId); + List wfFormManageVos = wfFormManageService.queryByIds(formIds); + for (WfNodeConfigVo wfNodeConfigVo : wfNodeConfigVos) { + wfFormManageVos.stream().filter(e -> ObjectUtil.equals(e.getId(), wfNodeConfigVo.getFormId())).findFirst().ifPresent(wfNodeConfigVo::setWfFormManageVo); + } + } + return wfNodeConfigVos; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java new file mode 100644 index 00000000..0992f6f0 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WfTaskBackNodeServiceImpl.java @@ -0,0 +1,141 @@ +package org.dromara.workflow.service.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.exception.ServiceException; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.workflow.domain.WfTaskBackNode; +import org.dromara.workflow.domain.vo.MultiInstanceVo; +import org.dromara.workflow.mapper.WfTaskBackNodeMapper; +import org.dromara.workflow.service.IWfTaskBackNodeService; +import org.dromara.workflow.utils.WorkflowUtils; +import org.flowable.task.api.Task; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +import static org.dromara.workflow.common.constant.FlowConstant.MULTI_INSTANCE; +import static org.dromara.workflow.common.constant.FlowConstant.USER_TASK; + + +/** + * 节点驳回记录Service业务层处理 + * + * @author may + * @date 2024-03-13 + */ +@RequiredArgsConstructor +@Service +public class WfTaskBackNodeServiceImpl implements IWfTaskBackNodeService { + + private final WfTaskBackNodeMapper wfTaskBackNodeMapper; + + @Override + @Transactional(rollbackFor = Exception.class) + public void recordExecuteNode(Task task) { + List list = getListByInstanceId(task.getProcessInstanceId()); + WfTaskBackNode wfTaskBackNode = new WfTaskBackNode(); + wfTaskBackNode.setNodeId(task.getTaskDefinitionKey()); + wfTaskBackNode.setNodeName(task.getName()); + wfTaskBackNode.setInstanceId(task.getProcessInstanceId()); + wfTaskBackNode.setAssignee(String.valueOf(LoginHelper.getUserId())); + MultiInstanceVo multiInstance = WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()); + if (ObjectUtil.isNotEmpty(multiInstance)) { + wfTaskBackNode.setTaskType(MULTI_INSTANCE); + } else { + wfTaskBackNode.setTaskType(USER_TASK); + } + if (CollUtil.isEmpty(list)) { + wfTaskBackNode.setOrderNo(0); + wfTaskBackNodeMapper.insert(wfTaskBackNode); + } else { + WfTaskBackNode taskNode = list.stream().filter(e -> e.getNodeId().equals(wfTaskBackNode.getNodeId()) && e.getOrderNo() == 0).findFirst().orElse(null); + if (ObjectUtil.isEmpty(taskNode)) { + wfTaskBackNode.setOrderNo(list.get(0).getOrderNo() + 1); + WfTaskBackNode node = getListByInstanceIdAndNodeId(wfTaskBackNode.getInstanceId(), wfTaskBackNode.getNodeId()); + if (ObjectUtil.isNotEmpty(node)) { + node.setAssignee(node.getAssignee() + StringUtils.SEPARATOR + LoginHelper.getUserId()); + wfTaskBackNodeMapper.updateById(node); + } else { + wfTaskBackNodeMapper.insert(wfTaskBackNode); + } + } + } + } + + @Override + public List getListByInstanceId(String processInstanceId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId); + wrapper.orderByDesc(WfTaskBackNode::getOrderNo); + return wfTaskBackNodeMapper.selectList(wrapper); + } + + @Override + public WfTaskBackNode getListByInstanceIdAndNodeId(String processInstanceId, String nodeId) { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId); + queryWrapper.eq(WfTaskBackNode::getNodeId, nodeId); + return wfTaskBackNodeMapper.selectOne(queryWrapper); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteBackTaskNode(String processInstanceId, String targetActivityId) { + try { + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId); + queryWrapper.eq(WfTaskBackNode::getNodeId, targetActivityId); + WfTaskBackNode actTaskNode = wfTaskBackNodeMapper.selectOne(queryWrapper); + if (ObjectUtil.isNotNull(actTaskNode)) { + Integer orderNo = actTaskNode.getOrderNo(); + List taskNodeList = getListByInstanceId(processInstanceId); + List ids = new ArrayList<>(); + if (CollUtil.isNotEmpty(taskNodeList)) { + for (WfTaskBackNode taskNode : taskNodeList) { + if (taskNode.getOrderNo() >= orderNo) { + ids.add(taskNode.getId()); + } + } + } + if (CollUtil.isNotEmpty(ids)) { + wfTaskBackNodeMapper.deleteBatchIds(ids); + } + } + return true; + } catch (Exception e) { + e.printStackTrace(); + throw new ServiceException("删除失败"); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public boolean deleteByInstanceId(String processInstanceId) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(WfTaskBackNode::getInstanceId, processInstanceId); + List list = wfTaskBackNodeMapper.selectList(wrapper); + int delete = wfTaskBackNodeMapper.delete(wrapper); + if (list.size() != delete) { + throw new ServiceException("删除失败"); + } + return true; + } + + @Override + public boolean deleteByInstanceIds(List processInstanceIds) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.in(WfTaskBackNode::getInstanceId, processInstanceIds); + List list = wfTaskBackNodeMapper.selectList(wrapper); + int delete = wfTaskBackNodeMapper.delete(wrapper); + if (list.size() != delete) { + throw new ServiceException("删除失败"); + } + return true; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java new file mode 100644 index 00000000..a6ee7faa --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/service/impl/WorkflowServiceImpl.java @@ -0,0 +1,124 @@ +package org.dromara.workflow.service.impl; + +import lombok.RequiredArgsConstructor; +import org.dromara.workflow.service.IActProcessInstanceService; +import org.dromara.workflow.service.WorkflowService; +import org.dromara.workflow.utils.WorkflowUtils; +import org.flowable.engine.RuntimeService; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; + +/** + * 通用 工作流服务实现 + * + * @author may + */ +@RequiredArgsConstructor +@Service +public class WorkflowServiceImpl implements WorkflowService { + + private final IActProcessInstanceService iActProcessInstanceService; + private final RuntimeService runtimeService; + + /** + * 运行中的实例 删除程实例,删除历史记录,删除业务与流程关联信息 + * + * @param businessKeys 业务id + * @return 结果 + */ + @Override + public boolean deleteRunAndHisInstance(List businessKeys) { + return iActProcessInstanceService.deleteRunAndHisInstance(businessKeys); + } + + /** + * 获取当前流程状态 + * + * @param taskId 任务id + */ + @Override + public String getBusinessStatusByTaskId(String taskId) { + return WorkflowUtils.getBusinessStatusByTaskId(taskId); + } + + /** + * 获取当前流程状态 + * + * @param businessKey 业务id + */ + @Override + public String getBusinessStatus(String businessKey) { + return WorkflowUtils.getBusinessStatus(businessKey); + } + + /** + * 设置流程实例对象 + * + * @param obj 业务对象 + * @param businessKey 业务id + */ + @Override + public void setBusinessInstanceDTO(Object obj, String businessKey) { + WorkflowUtils.setBusinessInstanceDTO(obj, businessKey); + } + + /** + * 设置流程实例对象 + * + * @param obj 业务对象 + * @param idList 业务id + * @param fieldName 主键属性名称 + */ + @Override + public void setBusinessInstanceListDTO(Object obj, List idList, String fieldName) { + WorkflowUtils.setBusinessInstanceListDTO(obj, idList, fieldName); + } + + /** + * 设置流程变量(全局变量) + * + * @param taskId 任务id + * @param variableName 变量名称 + * @param value 变量值 + */ + @Override + public void setVariable(String taskId, String variableName, Object value) { + runtimeService.setVariable(taskId, variableName, value); + } + + /** + * 设置流程变量(全局变量) + * + * @param taskId 任务id + * @param variables 流程变量 + */ + @Override + public void setVariables(String taskId, Map variables) { + runtimeService.setVariables(taskId, variables); + } + + /** + * 设置流程变量(本地变量,非全局变量) + * + * @param taskId 任务id + * @param variableName 变量名称 + * @param value 变量值 + */ + @Override + public void setVariableLocal(String taskId, String variableName, Object value) { + runtimeService.setVariableLocal(taskId, variableName, value); + } + + /** + * 设置流程变量(本地变量,非全局变量) + * + * @param taskId 任务id + * @param variables 流程变量 + */ + @Override + public void setVariablesLocal(String taskId, Map variables) { + runtimeService.setVariablesLocal(taskId, variables); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/controller/TestLeaveController.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/controller/TestLeaveController.java new file mode 100644 index 00000000..f069f389 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/controller/TestLeaveController.java @@ -0,0 +1,106 @@ +package org.dromara.workflow.testleave.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.RequiredArgsConstructor; +import org.dromara.common.core.domain.R; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.excel.utils.ExcelUtil; +import org.dromara.common.idempotent.annotation.RepeatSubmit; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.common.web.core.BaseController; +import org.dromara.workflow.testleave.domain.bo.TestLeaveBo; +import org.dromara.workflow.testleave.domain.vo.TestLeaveVo; +import org.dromara.workflow.testleave.service.ITestLeaveService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * 请假 + * + * @author may + * @date 2023-07-21 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/workflow/leave") +public class TestLeaveController extends BaseController { + + private final ITestLeaveService testLeaveService; + + /** + * 查询请假列表 + */ + @SaCheckPermission("workflow:leave:list") + @GetMapping("/list") + public TableDataInfo list(TestLeaveBo bo, PageQuery pageQuery) { + return testLeaveService.queryPageList(bo, pageQuery); + } + + /** + * 导出请假列表 + */ + @SaCheckPermission("workflow:leave:export") + @Log(title = "请假", businessType = BusinessType.EXPORT) + @PostMapping("/export") + public void export(TestLeaveBo bo, HttpServletResponse response) { + List list = testLeaveService.queryList(bo); + ExcelUtil.exportExcel(list, "请假", TestLeaveVo.class, response); + } + + /** + * 获取请假详细信息 + * + * @param id 主键 + */ + @SaCheckPermission("workflow:leave:query") + @GetMapping("/{id}") + public R getInfo(@NotNull(message = "主键不能为空") + @PathVariable Long id) { + return R.ok(testLeaveService.queryById(id)); + } + + /** + * 新增请假 + */ + @SaCheckPermission("workflow:leave:add") + @Log(title = "请假", businessType = BusinessType.INSERT) + @RepeatSubmit() + @PostMapping() + public R add(@Validated(AddGroup.class) @RequestBody TestLeaveBo bo) { + return R.ok(testLeaveService.insertByBo(bo)); + } + + /** + * 修改请假 + */ + @SaCheckPermission("workflow:leave:edit") + @Log(title = "请假", businessType = BusinessType.UPDATE) + @RepeatSubmit() + @PutMapping() + public R edit(@Validated(EditGroup.class) @RequestBody TestLeaveBo bo) { + return R.ok(testLeaveService.updateByBo(bo)); + } + + /** + * 删除请假 + * + * @param ids 主键串 + */ + @SaCheckPermission("workflow:leave:remove") + @Log(title = "请假", businessType = BusinessType.DELETE) + @DeleteMapping("/{ids}") + public R remove(@NotEmpty(message = "主键不能为空") + @PathVariable Long[] ids) { + return toAjax(testLeaveService.deleteWithValidByIds(List.of(ids))); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/TestLeave.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/TestLeave.java new file mode 100644 index 00000000..e48db561 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/TestLeave.java @@ -0,0 +1,62 @@ +package org.dromara.workflow.testleave.domain; + +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.mybatis.core.domain.BaseEntity; + +import java.io.Serial; +import java.util.Date; + +/** + * 请假对象 test_leave + * + * @author may + * @date 2023-07-21 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@TableName("test_leave") +public class TestLeave extends BaseEntity { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @TableId(value = "id") + private Long id; + + /** + * 请假类型 + */ + private String leaveType; + + /** + * 开始时间 + */ + private Date startDate; + + /** + * 结束时间 + */ + private Date endDate; + + /** + * 请假天数 + */ + private Integer leaveDays; + + /** + * 请假原因 + */ + private String remark; + + /** + * 状态 + */ + private String status; + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/bo/TestLeaveBo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/bo/TestLeaveBo.java new file mode 100644 index 00000000..1a677473 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/bo/TestLeaveBo.java @@ -0,0 +1,80 @@ +package org.dromara.workflow.testleave.domain.bo; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import lombok.EqualsAndHashCode; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.workflow.testleave.domain.TestLeave; + +import java.util.Date; + +/** + * 请假业务对象 test_leave + * + * @author may + * @date 2023-07-21 + */ +@Data +@EqualsAndHashCode(callSuper = true) +@AutoMapper(target = TestLeave.class, reverseConvertGenerate = false) +public class TestLeaveBo extends BaseEntity { + + /** + * 主键 + */ + @NotNull(message = "主键不能为空", groups = {EditGroup.class}) + private Long id; + + /** + * 请假类型 + */ + @NotBlank(message = "请假类型不能为空", groups = {AddGroup.class, EditGroup.class}) + private String leaveType; + + /** + * 开始时间 + */ + @NotNull(message = "开始时间不能为空", groups = {AddGroup.class, EditGroup.class}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date startDate; + + /** + * 结束时间 + */ + @NotNull(message = "结束时间不能为空", groups = {AddGroup.class, EditGroup.class}) + @JsonFormat(pattern = "yyyy-MM-dd") + private Date endDate; + + /** + * 请假天数 + */ + @NotNull(message = "请假天数不能为空", groups = {AddGroup.class, EditGroup.class}) + private Integer leaveDays; + + /** + * 开始时间 + */ + private Integer startLeaveDays; + + /** + * 结束时间 + */ + private Integer endLeaveDays; + + /** + * 请假原因 + */ + private String remark; + + /** + * 状态 + */ + private String status; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/vo/TestLeaveVo.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/vo/TestLeaveVo.java new file mode 100644 index 00000000..0b427fd8 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/domain/vo/TestLeaveVo.java @@ -0,0 +1,77 @@ +package org.dromara.workflow.testleave.domain.vo; + +import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; +import com.alibaba.excel.annotation.ExcelProperty; +import io.github.linpeilie.annotations.AutoMapper; +import lombok.Data; +import org.dromara.workflow.api.domain.dto.BusinessInstanceDTO; +import org.dromara.workflow.testleave.domain.TestLeave; + +import java.io.Serial; +import java.io.Serializable; +import java.util.Date; + + +/** + * 请假视图对象 test_leave + * + * @author may + * @date 2023-07-21 + */ +@Data +@ExcelIgnoreUnannotated +@AutoMapper(target = TestLeave.class) +public class TestLeaveVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + @ExcelProperty(value = "主键") + private Long id; + + /** + * 请假类型 + */ + @ExcelProperty(value = "请假类型") + private String leaveType; + + /** + * 开始时间 + */ + @ExcelProperty(value = "开始时间") + private Date startDate; + + /** + * 结束时间 + */ + @ExcelProperty(value = "结束时间") + private Date endDate; + + /** + * 请假天数 + */ + @ExcelProperty(value = "请假天数") + private Integer leaveDays; + + /** + * 备注 + */ + @ExcelProperty(value = "请假原因") + private String remark; + + /** + * 状态 + */ + @ExcelProperty(value = "状态") + private String status; + + /** + * 业务与流程实例关联对象 + */ + private BusinessInstanceDTO businessInstanceDTO; + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/mapper/TestLeaveMapper.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/mapper/TestLeaveMapper.java new file mode 100644 index 00000000..bce5d813 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/mapper/TestLeaveMapper.java @@ -0,0 +1,15 @@ +package org.dromara.workflow.testleave.mapper; + +import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.workflow.testleave.domain.TestLeave; +import org.dromara.workflow.testleave.domain.vo.TestLeaveVo; + +/** + * 请假Mapper接口 + * + * @author may + * @date 2023-07-21 + */ +public interface TestLeaveMapper extends BaseMapperPlus { + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/service/ITestLeaveService.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/service/ITestLeaveService.java new file mode 100644 index 00000000..472a5996 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/service/ITestLeaveService.java @@ -0,0 +1,48 @@ +package org.dromara.workflow.testleave.service; + +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.workflow.testleave.domain.bo.TestLeaveBo; +import org.dromara.workflow.testleave.domain.vo.TestLeaveVo; + +import java.util.Collection; +import java.util.List; + +/** + * 请假Service接口 + * + * @author may + * @date 2023-07-21 + */ +public interface ITestLeaveService { + + /** + * 查询请假 + */ + TestLeaveVo queryById(Long id); + + /** + * 查询请假列表 + */ + TableDataInfo queryPageList(TestLeaveBo bo, PageQuery pageQuery); + + /** + * 查询请假列表 + */ + List queryList(TestLeaveBo bo); + + /** + * 新增请假 + */ + TestLeaveVo insertByBo(TestLeaveBo bo); + + /** + * 修改请假 + */ + TestLeaveVo updateByBo(TestLeaveBo bo); + + /** + * 校验并批量删除请假信息 + */ + Boolean deleteWithValidByIds(Collection ids); +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/service/impl/TestLeaveServiceImpl.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/service/impl/TestLeaveServiceImpl.java new file mode 100644 index 00000000..61a3e339 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/testleave/service/impl/TestLeaveServiceImpl.java @@ -0,0 +1,170 @@ +package org.dromara.workflow.service.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.dubbo.config.annotation.DubboReference; +import org.dromara.common.core.utils.MapstructUtils; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.common.mybatis.core.page.PageQuery; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.workflow.api.domain.RemoteWorkflowService; +import org.dromara.workflow.api.domain.dto.BusinessInstanceDTO; +import org.dromara.workflow.api.domain.event.ProcessEvent; +import org.dromara.workflow.api.domain.event.ProcessTaskEvent; +import org.dromara.workflow.common.enums.BusinessStatusEnum; +import org.dromara.workflow.testleave.domain.TestLeave; +import org.dromara.workflow.testleave.domain.bo.TestLeaveBo; +import org.dromara.workflow.testleave.domain.vo.TestLeaveVo; +import org.dromara.workflow.testleave.mapper.TestLeaveMapper; +import org.dromara.workflow.testleave.service.ITestLeaveService; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Collection; +import java.util.List; + +/** + * 请假Service业务层处理 + * + * @author may + * @date 2023-07-21 + */ +@RequiredArgsConstructor +@Service +@Slf4j +public class TestLeaveServiceImpl implements ITestLeaveService { + + private final TestLeaveMapper baseMapper; + @DubboReference + private final RemoteWorkflowService workflowService; + + /** + * 查询请假 + */ + @Override + public TestLeaveVo queryById(Long id) { + TestLeaveVo testLeaveVo = baseMapper.selectVoById(id); + BusinessInstanceDTO businessInstance = workflowService.getBusinessInstance(String.valueOf(id)); + testLeaveVo.setBusinessInstanceDTO(businessInstance); + return testLeaveVo; + } + + /** + * 查询请假列表 + */ + @Override + public TableDataInfo queryPageList(TestLeaveBo bo, PageQuery pageQuery) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + TableDataInfo build = TableDataInfo.build(result); + List rows = build.getRows(); + if (CollUtil.isNotEmpty(rows)) { + List ids = StreamUtils.toList(rows, e -> String.valueOf(e.getId())); + List processInstances = workflowService.getBusinessInstance(ids); + for (TestLeaveVo vo : rows) { + BusinessInstanceDTO processInstanceDTO = null; + for (BusinessInstanceDTO processInstance : processInstances) { + if (String.valueOf(vo.getId()).equals(processInstance.getBusinessKey())) { + processInstanceDTO = processInstance; + break; + } + } + vo.setBusinessInstanceDTO(processInstanceDTO); + } + } + return build; + } + + /** + * 查询请假列表 + */ + @Override + public List queryList(TestLeaveBo bo) { + LambdaQueryWrapper lqw = buildQueryWrapper(bo); + return baseMapper.selectVoList(lqw); + } + + private LambdaQueryWrapper buildQueryWrapper(TestLeaveBo bo) { + LambdaQueryWrapper lqw = Wrappers.lambdaQuery(); + lqw.eq(StringUtils.isNotBlank(bo.getLeaveType()), TestLeave::getLeaveType, bo.getLeaveType()); + lqw.ge(bo.getStartLeaveDays() != null, TestLeave::getLeaveDays, bo.getStartLeaveDays()); + lqw.le(bo.getEndLeaveDays() != null, TestLeave::getLeaveDays, bo.getEndLeaveDays()); + lqw.orderByDesc(BaseEntity::getCreateTime); + return lqw; + } + + /** + * 新增请假 + */ + @Override + public TestLeaveVo insertByBo(TestLeaveBo bo) { + TestLeave add = MapstructUtils.convert(bo, TestLeave.class); + boolean flag = baseMapper.insert(add) > 0; + if (flag) { + bo.setId(add.getId()); + } + TestLeaveVo testLeaveVo = MapstructUtils.convert(add, TestLeaveVo.class); + BusinessInstanceDTO businessInstance = workflowService.getBusinessInstance(String.valueOf(add.getId())); + testLeaveVo.setBusinessInstanceDTO(businessInstance); + return testLeaveVo; + } + + /** + * 修改请假 + */ + @Override + public TestLeaveVo updateByBo(TestLeaveBo bo) { + TestLeave update = MapstructUtils.convert(bo, TestLeave.class); + baseMapper.updateById(update); + TestLeaveVo testLeaveVo = MapstructUtils.convert(update, TestLeaveVo.class); + BusinessInstanceDTO businessInstance = workflowService.getBusinessInstance(String.valueOf(update.getId())); + testLeaveVo.setBusinessInstanceDTO(businessInstance); + return testLeaveVo; + } + + /** + * 批量删除请假 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public Boolean deleteWithValidByIds(Collection ids) { + List idList = StreamUtils.toList(ids, String::valueOf); + workflowService.deleteRunAndHisInstance(idList); + return baseMapper.deleteBatchIds(ids) > 0; + } + + /** + * 总体流程监听(例如: 提交 退回 撤销 终止 作废等) + * + * @param processEvent 参数 + */ + @EventListener(condition = "#processEvent.key=='leave1'") + public void processHandler(ProcessEvent processEvent) { + log.info("当前任务执行了{}", processEvent.toString()); + TestLeave testLeave = baseMapper.selectById(Long.valueOf(processEvent.getBusinessKey())); + testLeave.setStatus(processEvent.getStatus()); + baseMapper.updateById(testLeave); + } + + /** + * 执行办理任务监听 + * + * @param processTaskEvent 参数 + */ + @EventListener(condition = "#processTaskEvent.keyNode=='leave1_Activity_14633hx'") + public void processTaskHandler(ProcessTaskEvent processTaskEvent) { + log.info("当前任务执行了{}", processTaskEvent.toString()); + TestLeave testLeave = baseMapper.selectById(Long.valueOf(processTaskEvent.getBusinessKey())); + testLeave.setStatus(BusinessStatusEnum.WAITING.getStatus()); + baseMapper.updateById(testLeave); + } + + +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java new file mode 100644 index 00000000..7c5377ed --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/ModelUtils.java @@ -0,0 +1,289 @@ +package org.dromara.workflow.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.json.utils.JsonUtils; +import org.dromara.workflow.domain.vo.MultiInstanceVo; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.editor.language.json.converter.BpmnJsonConverter; +import org.flowable.engine.ProcessEngine; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.rmi.ServerException; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 模型工具 + * + * @author may + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ModelUtils { + + private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class); + + public static BpmnModel xmlToBpmnModel(String xml) throws IOException { + if (xml == null) { + throw new ServerException("xml不能为空"); + } + try { + InputStream inputStream = new ByteArrayInputStream(StrUtil.utf8Bytes(xml)); + XMLInputFactory factory = XMLInputFactory.newInstance(); + XMLStreamReader reader = factory.createXMLStreamReader(inputStream); + return new BpmnXMLConverter().convertToBpmnModel(reader); + } catch (XMLStreamException e) { + throw new ServerException(e.getMessage()); + } + } + + /** + * bpmnModel转为xml + * + * @param jsonBytes json + */ + public static byte[] bpmnJsonToXmlBytes(byte[] jsonBytes) throws IOException { + if (jsonBytes == null) { + return new byte[0]; + } + // 1. json字节码转成 BpmnModel 对象 + ObjectMapper objectMapper = JsonUtils.getObjectMapper(); + JsonNode jsonNode = objectMapper.readTree(jsonBytes); + BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(jsonNode); + + if (bpmnModel.getProcesses().isEmpty()) { + return new byte[0]; + } + // 2.将bpmnModel转为xml + return new BpmnXMLConverter().convertToXML(bpmnModel); + } + + /** + * xml转为bpmnModel + * + * @param xmlBytes xml + */ + public static BpmnModel xmlToBpmnModel(byte[] xmlBytes) throws XMLStreamException { + ByteArrayInputStream byteArrayInputStream = IoUtil.toStream(xmlBytes); + XMLInputFactory xif = XMLInputFactory.newInstance(); + XMLStreamReader xtr = xif.createXMLStreamReader(byteArrayInputStream); + return new BpmnXMLConverter().convertToBpmnModel(xtr); + } + + /** + * 校验模型 + * + * @param bpmnModel bpmn模型 + */ + public static void checkBpmnModel(BpmnModel bpmnModel) throws ServerException { + Collection flowElements = bpmnModel.getMainProcess().getFlowElements(); + + checkBpmnNode(flowElements, false); + + List subProcessList = flowElements.stream().filter(SubProcess.class::isInstance).map(SubProcess.class::cast).collect(Collectors.toList()); + if (!CollUtil.isEmpty(subProcessList)) { + for (SubProcess subProcess : subProcessList) { + Collection subProcessFlowElements = subProcess.getFlowElements(); + checkBpmnNode(subProcessFlowElements, true); + } + } + List multiInstanceVoList = new ArrayList<>(); + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof UserTask && ObjectUtil.isNotEmpty(((UserTask) flowElement).getLoopCharacteristics()) && StringUtils.isNotBlank(((UserTask) flowElement).getLoopCharacteristics().getInputDataItem())) { + MultiInstanceVo multiInstanceVo = new MultiInstanceVo(); + multiInstanceVo.setAssigneeList(((UserTask) flowElement).getLoopCharacteristics().getInputDataItem()); + multiInstanceVoList.add(multiInstanceVo); + } + } + + if (CollectionUtil.isNotEmpty(multiInstanceVoList) && multiInstanceVoList.size() > 1) { + Map> assigneeListGroup = StreamUtils.groupByKey(multiInstanceVoList, MultiInstanceVo::getAssigneeList); + for (Map.Entry> entry : assigneeListGroup.entrySet()) { + List value = entry.getValue(); + if (CollectionUtil.isNotEmpty(value) && value.size() > 1) { + String key = entry.getKey(); + throw new ServerException("会签人员集合【" + key + "】重复,请重新设置集合KEY"); + } + } + } + } + + /** + * 校验bpmn节点是否合法 + * + * @param flowElements 节点集合 + * @param subtask 是否子流程 + */ + private static void checkBpmnNode(Collection flowElements, boolean subtask) throws ServerException { + + if (CollUtil.isEmpty(flowElements)) { + throw new ServerException(subtask ? "子流程必须存在节点" : "必须存在节点!"); + } + + List startEventList = flowElements.stream().filter(StartEvent.class::isInstance).map(StartEvent.class::cast).collect(Collectors.toList()); + if (CollUtil.isEmpty(startEventList)) { + throw new ServerException(subtask ? "子流程必须存在开始节点" : "必须存在开始节点!"); + } + + if (startEventList.size() > 1) { + throw new ServerException(subtask ? "子流程只能存在一个开始节点" : "只能存在一个开始节点!"); + } + + StartEvent startEvent = startEventList.get(0); + List outgoingFlows = startEvent.getOutgoingFlows(); + if (CollUtil.isEmpty(outgoingFlows)) { + throw new ServerException(subtask ? "子流程流程节点为空,请至少设计一条主线流程!" : "流程节点为空,请至少设计一条主线流程!"); + } + + FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); + if (!(targetFlowElement instanceof UserTask) && !subtask) { + throw new ServerException("开始节点后第一个节点必须是用户任务!"); + } + //开始节点后第一个节点申请人节点 + if ((targetFlowElement instanceof UserTask) && !subtask) { + UserTask userTask = (UserTask) targetFlowElement; + if (StringUtils.isBlank(userTask.getFormKey())) { + throw new ServerException("申请人节点必须选择表单!"); + } + } + List endEventList = flowElements.stream().filter(EndEvent.class::isInstance).map(EndEvent.class::cast).collect(Collectors.toList()); + if (CollUtil.isEmpty(endEventList)) { + throw new ServerException(subtask ? "子流程必须存在结束节点!" : "必须存在结束节点!"); + } + } + + /** + * 获取流程全部用户节点 + * + * @param processDefinitionId 流程定义id + */ + public static List getUserTaskFlowElements(String processDefinitionId) { + BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); + List list = new ArrayList<>(); + List processes = bpmnModel.getProcesses(); + Collection flowElements = processes.get(0).getFlowElements(); + buildUserTaskFlowElements(flowElements, list); + return list; + } + + /** + * 递归获取所有节点 + * + * @param flowElements 节点信息 + * @param list 集合 + */ + private static void buildUserTaskFlowElements(Collection flowElements, List list) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof SubProcess) { + Collection subFlowElements = ((SubProcess) flowElement).getFlowElements(); + buildUserTaskFlowElements(subFlowElements, list); + } else if (flowElement instanceof UserTask) { + list.add((UserTask) flowElement); + } + } + } + + /** + * 获取流程全部节点 + * + * @param processDefinitionId 流程定义id + */ + public static List getFlowElements(String processDefinitionId) { + BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); + List list = new ArrayList<>(); + List processes = bpmnModel.getProcesses(); + Collection flowElements = processes.get(0).getFlowElements(); + buildFlowElements(flowElements, list); + return list; + } + + /** + * 递归获取所有节点 + * + * @param flowElements 节点信息 + * @param list 集合 + */ + private static void buildFlowElements(Collection flowElements, List list) { + for (FlowElement flowElement : flowElements) { + list.add(flowElement); + if (flowElement instanceof SubProcess) { + Collection subFlowElements = ((SubProcess) flowElement).getFlowElements(); + buildFlowElements(subFlowElements, list); + } + } + } + + /** + * 获取全部扩展信息 + * + * @param processDefinitionId 流程定义id + */ + public static Map> getExtensionElements(String processDefinitionId) { + Map> map = new HashMap<>(); + List flowElements = getFlowElements(processDefinitionId); + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof UserTask && CollUtil.isNotEmpty(flowElement.getExtensionElements())) { + map.putAll(flowElement.getExtensionElements()); + } + } + return map; + } + + /** + * 获取某个节点的扩展信息 + * + * @param processDefinitionId 流程定义id + * @param flowElementId 节点id + */ + public static Map> getExtensionElement(String processDefinitionId, String flowElementId) { + BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); + Process process = bpmnModel.getMainProcess(); + FlowElement flowElement = process.getFlowElement(flowElementId); + return flowElement.getExtensionElements(); + } + + /** + * 判断当前节点是否为用户任务 + * + * @param processDefinitionId 流程定义id + * @param taskDefinitionKey 流程定义id + */ + public static boolean isUserTask(String processDefinitionId, String taskDefinitionKey) { + BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); + FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); + return flowNode instanceof UserTask; + } + + /** + * 获取申请人节点 + * + * @param processDefinitionId 流程定义id + * @return 结果 + */ + public static UserTask getApplyUserTask(String processDefinitionId) { + BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); + Collection flowElements = bpmnModel.getMainProcess().getFlowElements(); + List startEventList = flowElements.stream().filter(StartEvent.class::isInstance).map(StartEvent.class::cast).collect(Collectors.toList()); + StartEvent startEvent = startEventList.get(0); + List outgoingFlows = startEvent.getOutgoingFlows(); + FlowElement targetFlowElement = outgoingFlows.get(0).getTargetFlowElement(); + return (UserTask) targetFlowElement; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/QueryUtils.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/QueryUtils.java new file mode 100644 index 00000000..df928dc7 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/QueryUtils.java @@ -0,0 +1,169 @@ +package org.dromara.workflow.utils; + +import cn.hutool.core.bean.BeanUtil; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.workflow.domain.vo.TaskVo; +import org.flowable.engine.ProcessEngine; +import org.flowable.engine.history.HistoricActivityInstanceQuery; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.repository.DeploymentQuery; +import org.flowable.engine.repository.ModelQuery; +import org.flowable.engine.repository.ProcessDefinitionQuery; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.runtime.ProcessInstanceQuery; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +/** + * 查询工具 + * + * @author Lion Li + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class QueryUtils { + + private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class); + + public static ModelQuery modelQuery() { + ModelQuery query = PROCESS_ENGINE.getRepositoryService().createModelQuery(); + if (TenantHelper.isEnable()) { + query.modelTenantId(TenantHelper.getTenantId()); + } + return query; + } + + public static ProcessDefinitionQuery definitionQuery() { + ProcessDefinitionQuery query = PROCESS_ENGINE.getRepositoryService().createProcessDefinitionQuery(); + if (TenantHelper.isEnable()) { + query.processDefinitionTenantId(TenantHelper.getTenantId()); + } + return query; + } + + public static DeploymentQuery deploymentQuery() { + DeploymentQuery query = PROCESS_ENGINE.getRepositoryService().createDeploymentQuery(); + if (TenantHelper.isEnable()) { + query.deploymentTenantId(TenantHelper.getTenantId()); + } + return query; + } + + public static DeploymentQuery deploymentQuery(String deploymentId) { + return deploymentQuery().deploymentId(deploymentId); + } + + public static DeploymentQuery deploymentQuery(List deploymentIds) { + return deploymentQuery().deploymentIds(deploymentIds); + } + + public static HistoricTaskInstanceQuery hisTaskInstanceQuery() { + HistoricTaskInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricTaskInstanceQuery(); + if (TenantHelper.isEnable()) { + query.taskTenantId(TenantHelper.getTenantId()); + } + return query; + } + + public static HistoricTaskInstanceQuery hisTaskInstanceQuery(String processInstanceId) { + return hisTaskInstanceQuery().processInstanceId(processInstanceId); + } + + public static HistoricTaskInstanceQuery hisTaskBusinessKeyQuery(String businessKey) { + return hisTaskInstanceQuery().processInstanceBusinessKey(businessKey); + } + + public static ProcessInstanceQuery instanceQuery() { + ProcessInstanceQuery query = PROCESS_ENGINE.getRuntimeService().createProcessInstanceQuery(); + if (TenantHelper.isEnable()) { + query.processInstanceTenantId(TenantHelper.getTenantId()); + } + return query; + } + + public static ProcessInstanceQuery instanceQuery(String processInstanceId) { + return instanceQuery().processInstanceId(processInstanceId); + } + + public static ProcessInstanceQuery businessKeyQuery(String businessKey) { + return instanceQuery().processInstanceBusinessKey(businessKey); + } + + public static ProcessInstanceQuery instanceQuery(Set processInstanceIds) { + return instanceQuery().processInstanceIds(processInstanceIds); + } + + public static HistoricProcessInstanceQuery hisInstanceQuery() { + HistoricProcessInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricProcessInstanceQuery(); + if (TenantHelper.isEnable()) { + query.processInstanceTenantId(TenantHelper.getTenantId()); + } + return query; + } + + public static HistoricProcessInstanceQuery hisInstanceQuery(String processInstanceId) { + return hisInstanceQuery().processInstanceId(processInstanceId); + } + + public static HistoricProcessInstanceQuery hisBusinessKeyQuery(String businessKey) { + return hisInstanceQuery().processInstanceBusinessKey(businessKey); + } + + public static HistoricProcessInstanceQuery hisInstanceQuery(Set processInstanceIds) { + return hisInstanceQuery().processInstanceIds(processInstanceIds); + } + + public static HistoricActivityInstanceQuery hisActivityInstanceQuery() { + HistoricActivityInstanceQuery query = PROCESS_ENGINE.getHistoryService().createHistoricActivityInstanceQuery(); + if (TenantHelper.isEnable()) { + query.activityTenantId(TenantHelper.getTenantId()); + } + return query; + } + + public static HistoricActivityInstanceQuery hisActivityInstanceQuery(String processInstanceId) { + return hisActivityInstanceQuery().processInstanceId(processInstanceId); + } + + public static TaskQuery taskQuery() { + TaskQuery query = PROCESS_ENGINE.getTaskService().createTaskQuery(); + if (TenantHelper.isEnable()) { + query.taskTenantId(TenantHelper.getTenantId()); + } + return query; + } + + public static TaskQuery taskQuery(String processInstanceId) { + return taskQuery().processInstanceId(processInstanceId); + } + + public static TaskQuery taskQuery(Collection processInstanceIds) { + return taskQuery().processInstanceIdIn(processInstanceIds); + } + + /** + * 按照任务id查询当前任务 + * + * @param taskId 任务id + */ + public static TaskVo getTask(String taskId) { + Task task = PROCESS_ENGINE.getTaskService().createTaskQuery().taskId(taskId).singleResult(); + if (task == null) { + return null; + } + ProcessInstance processInstance = QueryUtils.instanceQuery(task.getProcessInstanceId()).singleResult(); + TaskVo taskVo = BeanUtil.toBean(task, TaskVo.class); + taskVo.setBusinessKey(processInstance.getBusinessKey()); + taskVo.setMultiInstance(WorkflowUtils.isMultiInstance(task.getProcessDefinitionId(), task.getTaskDefinitionKey()) != null); + String businessStatus = WorkflowUtils.getBusinessStatus(taskVo.getBusinessKey()); + taskVo.setBusinessStatus(businessStatus); + return taskVo; + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/WorkflowUtils.java b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/WorkflowUtils.java new file mode 100644 index 00000000..f181d30e --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/java/org/dromara/workflow/utils/WorkflowUtils.java @@ -0,0 +1,366 @@ +package org.dromara.workflow.utils; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import lombok.AccessLevel; +import lombok.NoArgsConstructor; +import org.dromara.common.core.utils.SpringUtils; +import org.dromara.common.core.utils.StreamUtils; +import org.dromara.common.core.utils.StringUtils; +import org.dromara.common.core.utils.reflect.ReflectUtils; +import org.dromara.common.mail.utils.MailUtils; +import org.dromara.common.satoken.utils.LoginHelper; +import org.dromara.common.tenant.helper.TenantHelper; +import org.dromara.common.websocket.dto.WebSocketMessageDto; +import org.dromara.common.websocket.utils.WebSocketUtils; +import org.dromara.system.api.RemoteUserService; +import org.dromara.system.api.domain.dto.UserDTO; +import org.dromara.system.api.model.RoleDTO; +import org.dromara.workflow.api.domain.dto.BusinessInstanceDTO; +import org.dromara.workflow.common.constant.FlowConstant; +import org.dromara.workflow.common.enums.BusinessStatusEnum; +import org.dromara.workflow.common.enums.MessageTypeEnum; +import org.dromara.workflow.common.enums.TaskStatusEnum; +import org.dromara.workflow.domain.ActHiProcinst; +import org.dromara.workflow.domain.ActHiTaskinst; +import org.dromara.workflow.domain.vo.MultiInstanceVo; +import org.dromara.workflow.domain.vo.ParticipantVo; +import org.dromara.workflow.flowable.cmd.UpdateHiTaskInstCmd; +import org.dromara.workflow.mapper.ActHiTaskinstMapper; +import org.dromara.workflow.service.IActHiProcinstService; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowNode; +import org.flowable.common.engine.api.delegate.Expression; +import org.flowable.engine.ProcessEngine; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.identitylink.api.history.HistoricIdentityLink; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.service.impl.persistence.entity.TaskEntity; + +import java.util.*; + +import static org.dromara.workflow.common.constant.FlowConstant.BUSINESS_INSTANCE_DTO; + +/** + * 工作流工具 + * + * @author may + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class WorkflowUtils { + + private static final ProcessEngine PROCESS_ENGINE = SpringUtils.getBean(ProcessEngine.class); + private static final RemoteUserService USER_SERVICE = SpringUtils.getBean(RemoteUserService.class); + private static final IActHiProcinstService ACT_HI_PROCINST_SERVICE = SpringUtils.getBean(IActHiProcinstService.class); + private static final ActHiTaskinstMapper ACT_HI_TASKINST_MAPPER = SpringUtils.getBean(ActHiTaskinstMapper.class); + + /** + * 创建一个新任务 + * + * @param currentTask 参数 + */ + public static TaskEntity createNewTask(Task currentTask) { + TaskEntity task = null; + if (ObjectUtil.isNotEmpty(currentTask)) { + task = (TaskEntity) PROCESS_ENGINE.getTaskService().newTask(); + task.setCategory(currentTask.getCategory()); + task.setDescription(currentTask.getDescription()); + task.setAssignee(currentTask.getAssignee()); + task.setName(currentTask.getName()); + task.setProcessDefinitionId(currentTask.getProcessDefinitionId()); + task.setProcessInstanceId(currentTask.getProcessInstanceId()); + task.setTaskDefinitionKey(currentTask.getTaskDefinitionKey()); + task.setPriority(currentTask.getPriority()); + task.setCreateTime(new Date()); + task.setTenantId(TenantHelper.getTenantId()); + PROCESS_ENGINE.getTaskService().saveTask(task); + } + if (ObjectUtil.isNotNull(task)) { + UpdateHiTaskInstCmd updateHiTaskInstCmd = new UpdateHiTaskInstCmd(Collections.singletonList(task.getId()), task.getProcessDefinitionId(), task.getProcessInstanceId()); + PROCESS_ENGINE.getManagementService().executeCommand(updateHiTaskInstCmd); + } + return task; + } + + /** + * 抄送任务 + * + * @param parentTaskList 父级任务 + * @param userIds 人员id + */ + public static void createCopyTask(List parentTaskList, List userIds) { + List list = new ArrayList<>(); + String tenantId = TenantHelper.getTenantId(); + for (Task parentTask : parentTaskList) { + for (Long userId : userIds) { + TaskEntity newTask = (TaskEntity) PROCESS_ENGINE.getTaskService().newTask(); + newTask.setParentTaskId(parentTask.getId()); + newTask.setAssignee(userId.toString()); + newTask.setName("【抄送】-" + parentTask.getName()); + newTask.setProcessDefinitionId(parentTask.getProcessDefinitionId()); + newTask.setProcessInstanceId(parentTask.getProcessInstanceId()); + newTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey()); + newTask.setTenantId(tenantId); + list.add(newTask); + } + } + PROCESS_ENGINE.getTaskService().bulkSaveTasks(list); + if (CollUtil.isNotEmpty(list) && CollUtil.isNotEmpty(parentTaskList)) { + String processInstanceId = parentTaskList.get(0).getProcessInstanceId(); + String processDefinitionId = parentTaskList.get(0).getProcessDefinitionId(); + List taskIds = StreamUtils.toList(list, Task::getId); + ActHiTaskinst actHiTaskinst = new ActHiTaskinst(); + actHiTaskinst.setProcDefId(processDefinitionId); + actHiTaskinst.setProcInstId(processInstanceId); + actHiTaskinst.setScopeType(TaskStatusEnum.COPY.getStatus()); + actHiTaskinst.setTenantId(tenantId); + LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); + updateWrapper.in(ActHiTaskinst::getId, taskIds); + ACT_HI_TASKINST_MAPPER.update(actHiTaskinst, updateWrapper); + for (Task task : list) { + PROCESS_ENGINE.getTaskService().addComment(task.getId(), task.getProcessInstanceId(), TaskStatusEnum.COPY.getStatus(), StrUtil.EMPTY); + } + } + } + + /** + * 获取当前任务参与者 + * + * @param taskId 任务id + */ + public static ParticipantVo getCurrentTaskParticipant(String taskId) { + ParticipantVo participantVo = new ParticipantVo(); + List linksForTask = PROCESS_ENGINE.getHistoryService().getHistoricIdentityLinksForTask(taskId); + Task task = QueryUtils.taskQuery().taskId(taskId).singleResult(); + if (task != null && CollUtil.isNotEmpty(linksForTask)) { + List groupList = StreamUtils.filter(linksForTask, e -> StringUtils.isNotBlank(e.getGroupId())); + if (CollUtil.isNotEmpty(groupList)) { + List groupIds = StreamUtils.toList(groupList, e -> Long.valueOf(e.getGroupId())); + List userIds = USER_SERVICE.selectUserIdsByRoleIds(groupIds); + if (CollUtil.isNotEmpty(userIds)) { + participantVo.setGroupIds(groupIds); + List userList = USER_SERVICE.selectListByIds(userIds); + if (CollUtil.isNotEmpty(userList)) { + List userIdList = StreamUtils.toList(userList, UserDTO::getUserId); + List nickNames = StreamUtils.toList(userList, UserDTO::getNickName); + participantVo.setCandidate(userIdList); + participantVo.setCandidateName(nickNames); + participantVo.setClaim(!StringUtils.isBlank(task.getAssignee())); + } + } + } else { + List candidateList = StreamUtils.filter(linksForTask, e -> FlowConstant.CANDIDATE.equals(e.getType())); + List userIdList = new ArrayList<>(); + for (HistoricIdentityLink historicIdentityLink : linksForTask) { + try { + userIdList.add(Long.valueOf(historicIdentityLink.getUserId())); + } catch (NumberFormatException ignored) { + + } + } + List userList = USER_SERVICE.selectListByIds(userIdList); + if (CollUtil.isNotEmpty(userList)) { + List userIds = StreamUtils.toList(userList, UserDTO::getUserId); + List nickNames = StreamUtils.toList(userList, UserDTO::getNickName); + participantVo.setCandidate(userIds); + participantVo.setCandidateName(nickNames); + // 判断当前任务是否具有多个办理人 + if (CollUtil.isNotEmpty(candidateList) && candidateList.size() > 1) { + // 如果 assignee 存在,则设置当前任务已经被认领 + participantVo.setClaim(StringUtils.isNotBlank(task.getAssignee())); + } + } + } + } + return participantVo; + } + + /** + * 判断当前节点是否为会签节点 + * + * @param processDefinitionId 流程定义id + * @param taskDefinitionKey 流程定义id + */ + public static MultiInstanceVo isMultiInstance(String processDefinitionId, String taskDefinitionKey) { + BpmnModel bpmnModel = PROCESS_ENGINE.getRepositoryService().getBpmnModel(processDefinitionId); + FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(taskDefinitionKey); + MultiInstanceVo multiInstanceVo = new MultiInstanceVo(); + //判断是否为并行会签节点 + if (flowNode.getBehavior() instanceof ParallelMultiInstanceBehavior behavior && behavior.getCollectionExpression() != null) { + Expression collectionExpression = behavior.getCollectionExpression(); + String assigneeList = collectionExpression.getExpressionText(); + String assignee = behavior.getCollectionElementVariable(); + multiInstanceVo.setType(behavior); + multiInstanceVo.setAssignee(assignee); + multiInstanceVo.setAssigneeList(assigneeList); + return multiInstanceVo; + //判断是否为串行会签节点 + } else if (flowNode.getBehavior() instanceof SequentialMultiInstanceBehavior behavior && behavior.getCollectionExpression() != null) { + Expression collectionExpression = behavior.getCollectionExpression(); + String assigneeList = collectionExpression.getExpressionText(); + String assignee = behavior.getCollectionElementVariable(); + multiInstanceVo.setType(behavior); + multiInstanceVo.setAssignee(assignee); + multiInstanceVo.setAssigneeList(assigneeList); + return multiInstanceVo; + } + return null; + } + + /** + * 获取当前流程状态 + * + * @param taskId 任务id + */ + public static String getBusinessStatusByTaskId(String taskId) { + HistoricTaskInstance historicTaskInstance = QueryUtils.hisTaskInstanceQuery().taskId(taskId).singleResult(); + HistoricProcessInstance historicProcessInstance = QueryUtils.hisInstanceQuery(historicTaskInstance.getProcessInstanceId()).singleResult(); + return historicProcessInstance.getBusinessStatus(); + } + + /** + * 获取当前流程状态 + * + * @param businessKey 业务id + */ + public static String getBusinessStatus(String businessKey) { + HistoricProcessInstance historicProcessInstance = QueryUtils.hisBusinessKeyQuery(businessKey).singleResult(); + return historicProcessInstance.getBusinessStatus(); + } + + /** + * 设置流程实例对象 + * + * @param obj 业务对象 + * @param businessKey 业务id + */ + public static void setBusinessInstanceDTO(Object obj, String businessKey) { + if (StringUtils.isBlank(businessKey) || obj == null) { + return; + } + ActHiProcinst actHiProcinst = ACT_HI_PROCINST_SERVICE.selectByBusinessKey(businessKey); + if (actHiProcinst == null) { + BusinessInstanceDTO businessInstanceDTO = new BusinessInstanceDTO(); + businessInstanceDTO.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus()); + ReflectUtils.invokeSetter(obj, BUSINESS_INSTANCE_DTO, businessInstanceDTO); + return; + } + BusinessInstanceDTO businessInstanceDTO = BeanUtil.toBean(actHiProcinst, BusinessInstanceDTO.class); + businessInstanceDTO.setBusinessStatusName(BusinessStatusEnum.findByStatus(businessInstanceDTO.getBusinessStatus())); + businessInstanceDTO.setProcessDefinitionId(actHiProcinst.getProcDefId()); + ReflectUtils.invokeSetter(obj, BUSINESS_INSTANCE_DTO, businessInstanceDTO); + } + + /** + * 设置流程实例对象 + * + * @param obj 业务对象 + * @param idList 业务id + * @param fieldName 主键属性名称 + */ + public static void setBusinessInstanceListDTO(Object obj, List idList, String fieldName) { + if (CollUtil.isEmpty(idList) || obj == null) { + return; + } + List actHiProcinstList = ACT_HI_PROCINST_SERVICE.selectByBusinessKeyIn(idList); + if (obj instanceof Collection collection) { + for (Object o : collection) { + String fieldValue = ReflectUtils.invokeGetter(o, fieldName).toString(); + if (CollUtil.isEmpty(actHiProcinstList)) { + BusinessInstanceDTO businessInstanceDTO = new BusinessInstanceDTO(); + businessInstanceDTO.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus()); + businessInstanceDTO.setBusinessStatusName(BusinessStatusEnum.findByStatus(businessInstanceDTO.getBusinessStatus())); + ReflectUtils.invokeSetter(o, BUSINESS_INSTANCE_DTO, businessInstanceDTO); + } else { + ActHiProcinst actHiProcinst = actHiProcinstList.stream().filter(e -> e.getBusinessKey().equals(fieldValue)).findFirst().orElse(null); + if (ObjectUtil.isNotEmpty(actHiProcinst)) { + BusinessInstanceDTO businessInstanceDTO = BeanUtil.toBean(actHiProcinst, BusinessInstanceDTO.class); + businessInstanceDTO.setBusinessStatusName(BusinessStatusEnum.findByStatus(businessInstanceDTO.getBusinessStatus())); + businessInstanceDTO.setProcessDefinitionId(actHiProcinst.getProcDefId()); + ReflectUtils.invokeSetter(o, BUSINESS_INSTANCE_DTO, businessInstanceDTO); + } else { + BusinessInstanceDTO businessInstanceDTO = new BusinessInstanceDTO(); + businessInstanceDTO.setBusinessStatus(BusinessStatusEnum.DRAFT.getStatus()); + businessInstanceDTO.setBusinessStatusName(BusinessStatusEnum.findByStatus(businessInstanceDTO.getBusinessStatus())); + ReflectUtils.invokeSetter(o, BUSINESS_INSTANCE_DTO, businessInstanceDTO); + } + } + } + } + } + + /** + * 发送消息 + * + * @param list 任务 + * @param name 流程名称 + * @param messageType 消息类型 + * @param message 消息内容,为空则发送默认配置的消息内容 + */ + public static void sendMessage(List list, String name, List messageType, String message) { + Set userIds = new HashSet<>(); + if (StringUtils.isBlank(message)) { + message = "有新的【" + name + "】单据已经提交至您的待办,请您及时处理。"; + } + for (Task t : list) { + ParticipantVo taskParticipant = WorkflowUtils.getCurrentTaskParticipant(t.getId()); + if (CollUtil.isNotEmpty(taskParticipant.getGroupIds())) { + List userIdList = USER_SERVICE.selectUserIdsByRoleIds(taskParticipant.getGroupIds()); + if (CollUtil.isNotEmpty(userIdList)) { + userIds.addAll(userIdList); + } + } + List candidate = taskParticipant.getCandidate(); + if (CollUtil.isNotEmpty(candidate)) { + userIds.addAll(candidate); + } + } + if (CollUtil.isNotEmpty(userIds)) { + List userList = USER_SERVICE.selectListByIds(new ArrayList<>(userIds)); + for (String code : messageType) { + MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(code); + if (ObjectUtil.isNotEmpty(messageTypeEnum)) { + switch (messageTypeEnum) { + case SYSTEM_MESSAGE: + WebSocketMessageDto dto = new WebSocketMessageDto(); + dto.setSessionKeys(new ArrayList<>(userIds)); + dto.setMessage(message); + WebSocketUtils.publishMessage(dto); + break; + case EMAIL_MESSAGE: + MailUtils.sendText(StreamUtils.join(userList, UserDTO::getEmail), "单据审批提醒", message); + break; + case SMS_MESSAGE: + //todo 短信发送 + break; + } + } + } + } + } + + /** + * 根据任务id查询 当前用户的任务,检查 当前人员 是否是该 taskId 的办理人 + * + * @param taskId 任务id + * @return 结果 + */ + public static Task getTaskByCurrentUser(String taskId) { + TaskQuery taskQuery = QueryUtils.taskQuery(); + taskQuery.taskId(taskId).taskCandidateOrAssigned(String.valueOf(LoginHelper.getUserId())); + + List roles = LoginHelper.getLoginUser().getRoles(); + if (CollUtil.isNotEmpty(roles)) { + List groupIds = StreamUtils.toList(roles, e -> String.valueOf(e.getRoleId())); + taskQuery.taskCandidateGroupIn(groupIds); + } + return taskQuery.singleResult(); + } +} diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/application.yml b/ruoyi-modules/ruoyi-workflow/src/main/resources/application.yml new file mode 100644 index 00000000..808604a5 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/application.yml @@ -0,0 +1,34 @@ +# Tomcat +server: + port: 9205 + +# Spring +spring: + application: + # 应用名称 + name: ruoyi-workflow + profiles: + # 环境配置 + active: @profiles.active@ + +--- # nacos 配置 +spring: + cloud: + nacos: + # nacos 服务地址 + server-addr: @nacos.server@ + username: @nacos.username@ + password: @nacos.password@ + discovery: + # 注册组 + group: @nacos.discovery.group@ + namespace: ${spring.profiles.active} + config: + # 配置组 + group: @nacos.config.group@ + namespace: ${spring.profiles.active} + config: + import: + - optional:nacos:application-common.yml + - optional:nacos:datasource.yml + - optional:nacos:${spring.application.name}.yml diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/banner.txt b/ruoyi-modules/ruoyi-workflow/src/main/resources/banner.txt new file mode 100644 index 00000000..fbd45f53 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/banner.txt @@ -0,0 +1,10 @@ +Spring Boot Version: ${spring-boot.version} +Spring Application Name: ${spring.application.name} + _ _ + (_) | | + _ __ _ _ ___ _ _ _ ______ ___ _ _ ___ | |_ ___ _ __ ___ +| '__|| | | | / _ \ | | | || ||______|/ __|| | | |/ __|| __| / _ \| '_ ` _ \ +| | | |_| || (_) || |_| || | \__ \| |_| |\__ \| |_ | __/| | | | | | +|_| \__,_| \___/ \__, ||_| |___/ \__, ||___/ \__| \___||_| |_| |_| + __/ | __/ | + |___/ |___/ \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/logback-plus.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/logback-plus.xml new file mode 100644 index 00000000..caaa3455 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/logback-plus.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + ${console.log.pattern} + utf-8 + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/TestLeaveMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/TestLeaveMapper.xml new file mode 100644 index 00000000..63804fbe --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/TestLeaveMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md new file mode 100644 index 00000000..c938b1e5 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/package-info.md @@ -0,0 +1,3 @@ +java包使用 `.` 分割 resource 目录使用 `/` 分割 +
+此文件目的 防止文件夹粘连找不到 `xml` 文件 \ No newline at end of file diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiProcinstMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiProcinstMapper.xml new file mode 100644 index 00000000..dd057857 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiProcinstMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiTaskinstMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiTaskinstMapper.xml new file mode 100644 index 00000000..7e73b603 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActHiTaskinstMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml new file mode 100644 index 00000000..d1508abc --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/ActTaskMapper.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfCategoryMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfCategoryMapper.xml new file mode 100644 index 00000000..4375cb2d --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfCategoryMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfDefinitionConfigMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfDefinitionConfigMapper.xml new file mode 100644 index 00000000..8d579f70 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfDefinitionConfigMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfFormManageMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfFormManageMapper.xml new file mode 100644 index 00000000..59221f82 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfFormManageMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfNodeConfigMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfNodeConfigMapper.xml new file mode 100644 index 00000000..b65194f8 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfNodeConfigMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml new file mode 100644 index 00000000..4a9179b6 --- /dev/null +++ b/ruoyi-modules/ruoyi-workflow/src/main/resources/mapper/workflow/WfTaskBackNodeMapper.xml @@ -0,0 +1,7 @@ + + + + + diff --git a/sql/oracle/oracle_ry_workflow.sql b/sql/oracle/oracle_ry_workflow.sql new file mode 100644 index 00000000..65474f45 --- /dev/null +++ b/sql/oracle/oracle_ry_workflow.sql @@ -0,0 +1,261 @@ +insert into sys_menu values('11616', '工作流' , '0', '6', 'workflow', '', '', '1', '0', 'M', '0', '0', '', 'workflow', 103, 1, sysdate, NULL, NULL, ''); +insert into sys_menu values('11617', '模型管理', '11616', '2', 'model', 'workflow/model/index', '', '1', '1', 'C', '0', '0', 'workflow:model:list', 'model', 103, 1, sysdate, NULL, NULL, ''); +insert into sys_menu values('11618', '我的任务', '0', '7', 'task', '', '', '1', '0', 'M', '0', '0', '', 'my-task', 103, 1, sysdate, NULL, NULL, ''); +insert into sys_menu values('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate, NULL, NULL, ''); +insert into sys_menu values('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, sysdate, NULL, NULL, ''); +insert into sys_menu values('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, sysdate, NULL, NULL, ''); +insert into sys_menu values('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, sysdate, NULL, NULL, ''); +insert into sys_menu values('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, sysdate, NULL, NULL, ''); +insert into sys_menu values('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, sysdate, NULL, NULL, ''); +insert into sys_menu values('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, sysdate, NULL, NULL, ''); +insert into sys_menu values('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, sysdate, NULL, NULL, ''); +insert into sys_menu values('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate, NULL, NULL, ''); + + +-- 流程分类管理相关按钮 +insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values ('11625', '流程分类修改', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit', '#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values ('11626', '流程分类删除', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove','#', 103, 1, sysdate, null, null, ''); +insert into sys_menu values ('11627', '流程分类导出', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export','#', 103, 1, sysdate, null, null, ''); + +-- 请假单信息 +create table TEST_LEAVE +( + ID NUMBER(20) not null + constraint PK_TEST_LEAVE + primary key, + LEAVE_TYPE VARCHAR2(255), + START_DATE DATE, + END_DATE DATE, + LEAVE_DAYS NUMBER(10), + REMARK VARCHAR2(255), + STATUS VARCHAR2(255), + CREATE_DEPT NUMBER(20), + CREATE_BY NUMBER(20), + CREATE_TIME DATE, + UPDATE_BY NUMBER(20), + UPDATE_TIME DATE, + TENANT_ID VARCHAR2(20) +); + +comment on table TEST_LEAVE is '请假申请表'; +comment on column TEST_LEAVE.ID is '主键'; +comment on column TEST_LEAVE.LEAVE_TYPE is '请假类型'; +comment on column TEST_LEAVE.START_DATE is '开始时间'; +comment on column TEST_LEAVE.END_DATE is '结束时间'; +comment on column TEST_LEAVE.LEAVE_DAYS is '请假天数'; +comment on column TEST_LEAVE.REMARK is '请假原因'; +comment on column TEST_LEAVE.STATUS is '状态'; +comment on column TEST_LEAVE.CREATE_DEPT is '创建部门'; +comment on column TEST_LEAVE.CREATE_BY is '创建者'; +comment on column TEST_LEAVE.CREATE_TIME is '创建时间'; +comment on column TEST_LEAVE.UPDATE_BY is '更新者'; +comment on column TEST_LEAVE.UPDATE_TIME is '更新时间'; +comment on column TEST_LEAVE.TENANT_ID is '租户编号'; + +-- 流程分类信息表 +create table WF_CATEGORY +( + ID NUMBER(20) not null + constraint PK_WF_CATEGORY + primary key, + CATEGORY_NAME VARCHAR2(255), + CATEGORY_CODE VARCHAR2(255) + constraint UNI_CATEGORY_CODE + unique, + PARENT_ID NUMBER(20), + SORT_NUM NUMBER(10), + TENANT_ID VARCHAR2(20), + CREATE_DEPT NUMBER(20), + CREATE_BY NUMBER(20), + CREATE_TIME DATE, + UPDATE_BY NUMBER(20), + UPDATE_TIME DATE +); + +comment on table WF_CATEGORY is '流程分类'; +comment on column WF_CATEGORY.ID is '主键'; +comment on column WF_CATEGORY.CATEGORY_NAME is '分类名称'; +comment on column WF_CATEGORY.CATEGORY_CODE is '分类编码'; +comment on column WF_CATEGORY.PARENT_ID is '父级id'; +comment on column WF_CATEGORY.SORT_NUM is '排序'; +comment on column WF_CATEGORY.TENANT_ID is '租户编号'; +comment on column WF_CATEGORY.CREATE_DEPT is '创建部门'; +comment on column WF_CATEGORY.CREATE_BY is '创建者'; +comment on column WF_CATEGORY.CREATE_TIME is '创建时间'; +comment on column WF_CATEGORY.UPDATE_BY is '更新者'; +comment on column WF_CATEGORY.UPDATE_TIME is '更新时间'; +INSERT INTO wf_category values (1, 'OA', 'OA', 0, 0, '000000', 103, 1, sysdate, 1, sysdate); + +create table WF_TASK_BACK_NODE +( + ID NUMBER(20) not null + constraint PK_WF_TASK_BACK_NODE + primary key, + NODE_ID VARCHAR2(255) not null, + NODE_NAME VARCHAR2(255) not null, + ORDER_NO NUMBER(20) not null, + INSTANCE_ID VARCHAR2(255) not null, + TASK_TYPE VARCHAR2(255) not null, + ASSIGNEE VARCHAR2(2000) not null, + TENANT_ID VARCHAR2(20), + CREATE_DEPT NUMBER(20), + CREATE_BY NUMBER(20), + CREATE_TIME DATE, + UPDATE_BY NUMBER(20), + UPDATE_TIME DATE +); +comment on table WF_TASK_BACK_NODE is '节点审批记录'; +comment on column WF_TASK_BACK_NODE.ID is '主键'; +comment on column WF_TASK_BACK_NODE.NODE_ID is '节点id'; +comment on column WF_TASK_BACK_NODE.NODE_NAME is '节点名称'; +comment on column WF_TASK_BACK_NODE.ORDER_NO is '排序'; +comment on column WF_TASK_BACK_NODE.INSTANCE_ID is '流程实例id'; +comment on column WF_TASK_BACK_NODE.TASK_TYPE is '节点类型'; +comment on column WF_TASK_BACK_NODE.ASSIGNEE is '审批人'; +comment on column WF_TASK_BACK_NODE.TENANT_ID is '租户编号'; +comment on column WF_TASK_BACK_NODE.CREATE_DEPT is '创建部门'; +comment on column WF_TASK_BACK_NODE.CREATE_BY is '创建者'; +comment on column WF_TASK_BACK_NODE.CREATE_TIME is '创建时间'; +comment on column WF_TASK_BACK_NODE.UPDATE_BY is '更新者'; +comment on column WF_TASK_BACK_NODE.UPDATE_TIME is '更新时间'; + +create table WF_DEFINITION_CONFIG +( + ID NUMBER(20) NOT NULL + CONSTRAINT PK_WF_DEFINITION_CONFIG + PRIMARY KEY, + TABLE_NAME VARCHAR2(255) NOT NULL, + DEFINITION_ID VARCHAR2(255) NOT NULL, + PROCESS_KEY VARCHAR2(255) NOT NULL, + VERSION NUMBER(10) NOT NULL, + REMARK VARCHAR2(500), + TENANT_ID VARCHAR2(20), + CREATE_DEPT NUMBER(20), + CREATE_BY NUMBER(20), + CREATE_TIME DATE, + UPDATE_BY NUMBER(20), + UPDATE_TIME DATE, + constraint uni_definition_id + unique (definition_id) +); +comment on table WF_DEFINITION_CONFIG is '流程定义配置'; +comment on column WF_DEFINITION_CONFIG.ID is '主键'; +comment on column WF_DEFINITION_CONFIG.TABLE_NAME is '表名'; +comment on column WF_DEFINITION_CONFIG.DEFINITION_ID is '流程定义ID'; +comment on column WF_DEFINITION_CONFIG.PROCESS_KEY is '流程KEY'; +comment on column WF_DEFINITION_CONFIG.VERSION is '流程版本'; +comment on column WF_DEFINITION_CONFIG.TENANT_ID is '租户编号'; +comment on column WF_DEFINITION_CONFIG.REMARK is '备注'; +comment on column WF_DEFINITION_CONFIG.CREATE_DEPT is '创建部门'; +comment on column WF_DEFINITION_CONFIG.CREATE_BY is '创建者'; +comment on column WF_DEFINITION_CONFIG.CREATE_TIME is '创建时间'; +comment on column WF_DEFINITION_CONFIG.UPDATE_BY is '更新者'; +comment on column WF_DEFINITION_CONFIG.UPDATE_TIME is '更新时间'; + +create table WF_FORM_MANAGE +( + ID NUMBER(20) NOT NULL + CONSTRAINT PK_WF_FORM_MANAGE + PRIMARY KEY, + FORM_NAME VARCHAR2(255) NOT NULL, + FORM_TYPE VARCHAR2(255) NOT NULL, + ROUTER VARCHAR2(255) NOT NULL, + REMARK VARCHAR2(500), + TENANT_ID VARCHAR2(20), + CREATE_DEPT NUMBER(20), + CREATE_BY NUMBER(20), + CREATE_TIME DATE, + UPDATE_BY NUMBER(20), + UPDATE_TIME DATE +); + +comment on table WF_FORM_MANAGE is '表单管理'; +comment on column WF_FORM_MANAGE.ID is '主键'; +comment on column WF_FORM_MANAGE.FORM_NAME is '表单名称'; +comment on column WF_FORM_MANAGE.FORM_TYPE is '表单类型'; +comment on column WF_FORM_MANAGE.ROUTER is '路由地址/表单ID'; +comment on column WF_FORM_MANAGE.REMARK is '备注'; +comment on column WF_FORM_MANAGE.TENANT_ID is '租户编号'; +comment on column WF_FORM_MANAGE.CREATE_DEPT is '创建部门'; +comment on column WF_FORM_MANAGE.CREATE_BY is '创建者'; +comment on column WF_FORM_MANAGE.CREATE_TIME is '创建时间'; +comment on column WF_FORM_MANAGE.UPDATE_BY is '更新者'; +comment on column WF_FORM_MANAGE.UPDATE_TIME is '更新时间'; + +insert into wf_form_manage(id, form_name, form_type, router, remark, tenant_id, create_dept, create_by, create_time, update_by, update_time) VALUES (1, '请假申请', 'static', '/workflow/leaveEdit/index', NULL, '000000', 103, 1, sysdate, 1, sysdate); + +create table WF_NODE_CONFIG +( + ID NUMBER(20) NOT NULL + CONSTRAINT PK_WF_NODE_CONFIG + PRIMARY KEY, + FORM_ID NUMBER(20), + FORM_TYPE VARCHAR2(255), + NODE_NAME VARCHAR2(255) NOT NULL, + NODE_ID VARCHAR2(255) NOT NULL, + DEFINITION_ID VARCHAR2(255) NOT NULL, + APPLY_USER_TASK CHAR(1) DEFAULT '0', + TENANT_ID VARCHAR2(20), + CREATE_DEPT NUMBER(20), + CREATE_BY NUMBER(20), + CREATE_TIME DATE, + UPDATE_BY NUMBER(20), + UPDATE_TIME DATE +); + +comment on table WF_NODE_CONFIG is '节点配置'; +comment on column WF_NODE_CONFIG.ID is '主键'; +comment on column WF_NODE_CONFIG.FORM_ID is '表单id'; +comment on column WF_NODE_CONFIG.FORM_TYPE is '表单类型'; +comment on column WF_NODE_CONFIG.NODE_ID is '节点id'; +comment on column WF_NODE_CONFIG.NODE_NAME is '节点名称'; +comment on column WF_NODE_CONFIG.DEFINITION_ID is '流程定义id'; +comment on column WF_NODE_CONFIG.APPLY_USER_TASK is '是否为申请人节点 (0是 1否)'; +comment on column WF_NODE_CONFIG.TENANT_ID is '租户编号'; +comment on column WF_NODE_CONFIG.CREATE_DEPT is '创建部门'; +comment on column WF_NODE_CONFIG.CREATE_BY is '创建者'; +comment on column WF_NODE_CONFIG.CREATE_TIME is '创建时间'; +comment on column WF_NODE_CONFIG.UPDATE_BY is '更新者'; +comment on column WF_NODE_CONFIG.UPDATE_TIME is '更新时间'; + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '请假申请', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'workflow:leave:list', '#', 103, 1, sysdate, NULL, NULL, '请假申请菜单'); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '请假申请查询', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:query', '#', 103, 1, sysdate, NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11640, '请假申请新增', 11638, 2, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:add', '#', 103, 1, sysdate, NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11641, '请假申请修改', 11638, 3, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:edit', '#', 103, 1, sysdate, NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11642, '请假申请删除', 11638, 4, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:remove', '#', 103, 1, sysdate, NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11643, '请假申请导出', 11638, 5, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:export', '#', 103, 1, sysdate, NULL, NULL, ''); + +INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (13, '000000', '业务状态', 'wf_business_status', 103, 1, sysdate, NULL, NULL, '业务状态列表'); +INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (14, '000000', '表单类型', 'wf_form_type', 103, 1, sysdate, NULL, NULL, '表单类型列表'); + +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (39, '000000', 1, '已撤销', 'cancel', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate, NULL, NULL, '已撤销'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (40, '000000', 2, '草稿', 'draft', 'wf_business_status', '', 'info', 'N', 103, 1, sysdate, NULL, NULL, '草稿'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (41, '000000', 3, '待审核', 'waiting', 'wf_business_status', '', 'primary', 'N', 103, 1,sysdate, NULL, NULL, '待审核'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (42, '000000', 4, '已完成', 'finish', 'wf_business_status', '', 'success', 'N', 103, 1, sysdate, NULL, NULL, '已完成'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (43, '000000', 5, '已作废', 'invalid', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate, NULL, NULL, '已作废'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (44, '000000', 6, '已退回', 'back', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate, NULL, NULL, '已退回'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (45, '000000', 7, '已终止', 'termination', 'wf_business_status', '', 'danger', 'N', 103, 1,sysdate, NULL, NULL, '已终止'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (46, '000000', 1, '自定义表单', 'static', 'wf_form_type', '', 'success', 'N', 103, 1, sysdate, NULL, NULL, '自定义表单'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (47, '000000', 2, '动态表单', 'dynamic', 'wf_form_type', '', 'primary', 'N', 103, 1, sysdate, NULL, NULL, '动态表单'); + +-- 表单管理 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11628, '表单管理', '11616', '5', 'formManage', 'workflow/formManage/index', 1, 0, 'C', '0', '0', 'workflow:formManage:list', 'tree-table', 103, 1, sysdate, null, null, '表单管理菜单'); + +-- 表单管理按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11644, '表单管理查询', 11628, '1', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:query', '', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11645, '表单管理新增', 11628, '2', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:add', '', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11646, '表单管理修改', 11628, '3', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:edit', '', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11647, '表单管理删除', 11628, '4', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:remove', '', 103, 1, sysdate, null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11648, '表单管理导出', 11628, '5', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:export', 'tree-table', 103, 1, sysdate, null, null, ''); diff --git a/sql/postgres/postgres_ry_workflow.sql b/sql/postgres/postgres_ry_workflow.sql new file mode 100644 index 00000000..a17b665e --- /dev/null +++ b/sql/postgres/postgres_ry_workflow.sql @@ -0,0 +1,344 @@ +insert into sys_menu values('11616', '工作流' , '0', '6', 'workflow', '', '', '1', '0', 'M', '0', '0', '', 'workflow', 103, 1, now(), NULL, NULL, ''); +insert into sys_menu values('11617', '模型管理', '11616', '2', 'model', 'workflow/model/index', '', '1', '1', 'C', '0', '0', 'workflow:model:list', 'model', 103, 1, now(), NULL, NULL, ''); +insert into sys_menu values('11618', '我的任务', '0', '7', 'task', '', '', '1', '0', 'M', '0', '0', '', 'my-task', 103, 1, now(), NULL, NULL, ''); +insert into sys_menu values('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, now(), NULL, NULL, ''); +insert into sys_menu values('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, now(), NULL, NULL, ''); +insert into sys_menu values('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, now(), NULL, NULL, ''); +insert into sys_menu values('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, now(), NULL, NULL, ''); +insert into sys_menu values('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, now(), NULL, NULL, ''); +insert into sys_menu values('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, now(), NULL, NULL, ''); +insert into sys_menu values('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, now(), NULL, NULL, ''); +insert into sys_menu values('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, now(), NULL, NULL, ''); +insert into sys_menu values('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, now(), NULL, NULL, ''); + + +-- 流程分类管理相关按钮 +insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values ('11625', '流程分类修改', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit', '#', 103, 1, now(), null, null, ''); +insert into sys_menu values ('11626', '流程分类删除', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove','#', 103, 1, now(), null, null, ''); +insert into sys_menu values ('11627', '流程分类导出', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export','#', 103, 1, now(), null, null, ''); +-- 请假单信息 +create table test_leave +( + id bigint not null + constraint test_leave_pk + primary key, + leave_type varchar(255), + start_date timestamp, + end_date timestamp, + leave_days bigint, + remark varchar(255), + status varchar(255), + create_dept bigint, + create_by bigint, + create_time timestamp, + update_by bigint, + update_time timestamp, + tenant_id varchar(20) +); + +comment on table test_leave is '请假申请表'; + +comment on column test_leave.id is '主键'; + +comment on column test_leave.leave_type is '请假类型'; + +comment on column test_leave.start_date is '开始时间'; + +comment on column test_leave.end_date is '结束时间'; + +comment on column test_leave.remark is '请假原因'; + +comment on column test_leave.status is '状态'; + +comment on column test_leave.create_dept is '创建部门'; + +comment on column test_leave.create_by is '创建者'; + +comment on column test_leave.create_time is '创建时间'; + +comment on column test_leave.update_by is '更新者'; + +comment on column test_leave.update_time is '更新时间'; + +comment on column test_leave.tenant_id is '租户编码'; + +alter table test_leave + owner to postgres; + +-- 流程分类信息表 +create table wf_category +( + id bigint not null + constraint wf_category_pk + primary key, + category_name varchar(255), + category_code varchar(255), + parent_id bigint, + sort_num bigint, + tenant_id varchar(20), + create_dept bigint, + create_by bigint, + create_time timestamp, + update_by bigint, + update_time timestamp +); + +comment on table wf_category is '流程分类'; + +comment on column wf_category.id is '主键'; + +comment on column wf_category.category_name is '分类名称'; + +comment on column wf_category.category_code is '分类编码'; + +comment on column wf_category.parent_id is '父级id'; + +comment on column wf_category.sort_num is '排序'; + +comment on column wf_category.tenant_id is '租户id'; + +comment on column wf_category.create_dept is '创建部门'; + +comment on column wf_category.create_by is '创建者'; + +comment on column wf_category.create_time is '创建时间'; + +comment on column wf_category.update_by is '修改者'; + +comment on column wf_category.update_time is '修改时间'; + +alter table wf_category + owner to postgres; + +create unique index uni_category_code + on wf_category (category_code); + +INSERT INTO wf_category values (1, 'OA', 'OA', 0, 0, '000000', 103, 1, now(), 1, now()); + +create table wf_task_back_node +( + id bigint not null + constraint pk_wf_task_back_node + primary key, + node_id varchar(255) not null, + node_name varchar(255) not null, + order_no bigint not null, + instance_id varchar(255) not null, + task_type varchar(255) not null, + assignee varchar(2000) not null, + tenant_id varchar(20), + create_dept bigint, + create_by bigint, + create_time timestamp, + update_by bigint, + update_time timestamp +); + +comment on table wf_task_back_node is '节点审批记录'; + +comment on column wf_task_back_node.id is '主键'; + +comment on column wf_task_back_node.node_id is '节点id'; + +comment on column wf_task_back_node.node_name is '节点名称'; + +comment on column wf_task_back_node.order_no is '排序'; + +comment on column wf_task_back_node.instance_id is '流程实例id'; + +comment on column wf_task_back_node.task_type is '节点类型'; + +comment on column wf_task_back_node.assignee is '审批人'; + +comment on column wf_task_back_node.tenant_id is '租户id'; + +comment on column wf_task_back_node.create_dept is '创建部门'; + +comment on column wf_task_back_node.create_by is '创建者'; + +comment on column wf_task_back_node.create_time is '创建时间'; + +comment on column wf_task_back_node.update_by is '修改者'; + +comment on column wf_task_back_node.update_time is '修改时间'; + +alter table wf_task_back_node + owner to postgres; + +create table wf_definition_config +( + id bigint not null + constraint pk_wf_definition_config + primary key, + table_name varchar(255) not null, + definition_id varchar(255) not null, + process_key varchar(255) not null, + version bigint not null, + tenant_id varchar(20), + create_dept bigint, + create_by bigint, + create_time timestamp, + update_by bigint, + update_time timestamp +); + +comment on table wf_definition_config is '流程定义配置'; + +comment on column wf_definition_config.id is '主键'; + +comment on column wf_definition_config.table_name is '表名'; + +comment on column wf_definition_config.definition_id is '流程定义ID'; + +comment on column wf_definition_config.process_key is '流程KEY'; + +comment on column wf_definition_config.version is '流程版本'; + +comment on column wf_definition_config.tenant_id is '租户id'; + +comment on column wf_definition_config.create_dept is '创建部门'; + +comment on column wf_definition_config.create_by is '创建者'; + +comment on column wf_definition_config.create_time is '创建时间'; + +comment on column wf_definition_config.update_by is '修改者'; + +comment on column wf_definition_config.update_time is '修改时间'; + +alter table wf_definition_config + owner to postgres; +create unique index uni_definition_id + on wf_definition_config (definition_id); + +create table wf_form_manage +( + id bigint not null + constraint pk_wf_form_manage + primary key, + form_name varchar(255) not null, + form_type varchar(255) not null, + router varchar(255) not null, + remark varchar(500), + tenant_id varchar(20), + create_dept bigint, + create_by bigint, + create_time timestamp, + update_by bigint, + update_time timestamp +); + +comment on table wf_form_manage is '表单管理'; + +comment on column wf_form_manage.id is '主键'; + +comment on column wf_form_manage.form_name is '表单名称'; + +comment on column wf_form_manage.form_type is '表单类型'; + +comment on column wf_form_manage.router is '路由地址/表单ID'; + +comment on column wf_form_manage.remark is '备注'; + +comment on column wf_form_manage.tenant_id is '租户id'; + +comment on column wf_form_manage.create_dept is '创建部门'; + +comment on column wf_form_manage.create_by is '创建者'; + +comment on column wf_form_manage.create_time is '创建时间'; + +comment on column wf_form_manage.update_by is '修改者'; + +comment on column wf_form_manage.update_time is '修改时间'; + +insert into wf_form_manage(id, form_name, form_type, router, remark, tenant_id, create_dept, create_by, create_time, update_by, update_time) VALUES (1, '请假申请', 'static', '/workflow/leaveEdit/index', NULL, '000000', 103, 1, now(), 1, now()); + +create table wf_node_config +( + id bigint not null + constraint pk_wf_node_config + primary key, + form_id bigint, + form_type varchar(255), + node_name varchar(255) not null, + node_id varchar(255) not null, + definition_id varchar(255) not null, + apply_user_task char(1) default '0', + tenant_id varchar(20), + create_dept bigint, + create_by bigint, + create_time timestamp, + update_by bigint, + update_time timestamp +); + +comment on table wf_node_config is '节点配置'; + +comment on column wf_node_config.id is '主键'; + +comment on column wf_node_config.form_id is '表单id'; + +comment on column wf_node_config.form_type is '表单类型'; + +comment on column wf_node_config.node_id is '节点id'; + +comment on column wf_node_config.node_name is '节点名称'; + +comment on column wf_node_config.definition_id is '流程定义id'; + +comment on column wf_node_config.apply_user_task is '是否为申请人节点 (0是 1否)'; + +comment on column wf_node_config.tenant_id is '租户id'; + +comment on column wf_node_config.create_dept is '创建部门'; + +comment on column wf_node_config.create_by is '创建者'; + +comment on column wf_node_config.create_time is '创建时间'; + +comment on column wf_node_config.update_by is '修改者'; + +comment on column wf_node_config.update_time is '修改时间'; + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '请假申请', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'workflow:leave:list', '#', 103, 1, now(), NULL, NULL, '请假申请菜单'); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '请假申请查询', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:query', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11640, '请假申请新增', 11638, 2, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:add', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11641, '请假申请修改', 11638, 3, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:edit', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11642, '请假申请删除', 11638, 4, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:remove', '#', 103, 1, now(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11643, '请假申请导出', 11638, 5, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:export', '#', 103, 1, now(), NULL, NULL, ''); + +INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (13, '000000', '业务状态', 'wf_business_status', 103, 1, now(), NULL, NULL, '业务状态列表'); +INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (14, '000000', '表单类型', 'wf_form_type', 103, 1, now(), NULL, NULL, '表单类型列表'); + +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (39, '000000', 1, '已撤销', 'cancel', 'wf_business_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '已撤销'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (40, '000000', 2, '草稿', 'draft', 'wf_business_status', '', 'info', 'N', 103, 1, now(), NULL, NULL, '草稿'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (41, '000000', 3, '待审核', 'waiting', 'wf_business_status', '', 'primary', 'N', 103, 1,now(), NULL, NULL, '待审核'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (42, '000000', 4, '已完成', 'finish', 'wf_business_status', '', 'success', 'N', 103, 1, now(), NULL, NULL, '已完成'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (43, '000000', 5, '已作废', 'invalid', 'wf_business_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '已作废'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (44, '000000', 6, '已退回', 'back', 'wf_business_status', '', 'danger', 'N', 103, 1, now(), NULL, NULL, '已退回'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (45, '000000', 7, '已终止', 'termination', 'wf_business_status', '', 'danger', 'N', 103, 1,now(), NULL, NULL, '已终止'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (46, '000000', 1, '自定义表单', 'static', 'wf_form_type', '', 'success', 'N', 103, 1, now(), NULL, NULL, '自定义表单'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (47, '000000', 2, '动态表单', 'dynamic', 'wf_form_type', '', 'primary', 'N', 103, 1, now(), NULL, NULL, '动态表单'); + +-- 表单管理 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11628, '表单管理', '11616', '5', 'formManage', 'workflow/formManage/index', 1, 0, 'C', '0', '0', 'workflow:formManage:list', 'tree-table', 103, 1, now(), null, null, '表单管理菜单'); + +-- 表单管理按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11644, '表单管理查询', 11628, '1', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:query', '', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11645, '表单管理新增', 11628, '2', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:add', '', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11646, '表单管理修改', 11628, '3', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:edit', '', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11647, '表单管理删除', 11628, '4', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:remove', '', 103, 1, now(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11648, '表单管理导出', 11628, '5', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:export', 'tree-table', 103, 1, now(), null, null, ''); diff --git a/sql/ry-workflow.sql b/sql/ry-workflow.sql new file mode 100644 index 00000000..e8dc798a --- /dev/null +++ b/sql/ry-workflow.sql @@ -0,0 +1,176 @@ +insert into sys_menu values('11616', '工作流' , '0', '6', 'workflow', '', '', '1', '0', 'M', '0', '0', '', 'workflow', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values('11617', '模型管理', '11616', '2', 'model', 'workflow/model/index', '', '1', '1', 'C', '0', '0', 'workflow:model:list', 'model', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values('11618', '我的任务', '0', '7', 'task', '', '', '1', '0', 'M', '0', '0', '', 'my-task', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values('11619', '我的待办', '11618', '2', 'taskWaiting', 'workflow/task/taskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values('11632', '我的已办', '11618', '3', 'taskFinish', 'workflow/task/taskFinish', '', '1', '1', 'C', '0', '0', '', 'finish', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values('11633', '我的抄送', '11618', '4', 'taskCopyList', 'workflow/task/taskCopyList', '', '1', '1', 'C', '0', '0', '', 'my-copy', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values('11620', '流程定义', '11616', '3', 'processDefinition', 'workflow/processDefinition/index', '', '1', '1', 'C', '0', '0', '', 'process-definition', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values('11621', '流程实例', '11630', '1', 'processInstance', 'workflow/processInstance/index', '', '1', '1', 'C', '0', '0', '', 'tree-table', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values('11622', '流程分类', '11616', '1', 'category', 'workflow/category/index', '', '1', '0', 'C', '0', '0', 'workflow:category:list', 'category', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values('11629', '我发起的', '11618', '1', 'myDocument', 'workflow/task/myDocument', '', '1', '1', 'C', '0', '0', '', 'guide', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values('11630', '流程监控', '11616', '4', 'monitor', '', '', '1', '0', 'M', '0', '0', '', 'monitor', 103, 1, sysdate(), NULL, NULL, ''); +insert into sys_menu values('11631', '待办任务', '11630', '2', 'allTaskWaiting', 'workflow/task/allTaskWaiting', '', '1', '1', 'C', '0', '0', '', 'waiting', 103, 1, sysdate(), NULL, NULL, ''); + + +-- 流程分类管理相关按钮 +insert into sys_menu values ('11623', '流程分类查询', '11622', '1', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:query', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('11624', '流程分类新增', '11622', '2', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:add', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('11625', '流程分类修改', '11622', '3', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:edit', '#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('11626', '流程分类删除', '11622', '4', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:remove','#', 103, 1, sysdate(), null, null, ''); +insert into sys_menu values ('11627', '流程分类导出', '11622', '5', '#', '', '', 1, 0, 'F', '0', '0', 'workflow:category:export','#', 103, 1, sysdate(), null, null, ''); +-- 请假单信息 +create table test_leave +( + id bigint not null comment '主键', + leave_type varchar(255) not null comment '请假类型', + start_date datetime not null comment '开始时间', + end_date datetime not null comment '结束时间', + leave_days int(10) not null comment '请假天数', + remark varchar(255) null comment '请假原因', + status varchar(255) null comment '状态', + create_dept bigint null comment '创建部门', + create_by bigint null comment '创建者', + create_time datetime null comment '创建时间', + update_by bigint null comment '更新者', + update_time datetime null comment '更新时间', + tenant_id varchar(20) null comment '租户编号', + PRIMARY KEY (id) USING BTREE +) ENGINE = InnoDB COMMENT = '请假申请表'; + +-- 流程分类信息表 +create table wf_category +( + id bigint not null comment '主键' + primary key, + category_name varchar(255) null comment '分类名称', + category_code varchar(255) null comment '分类编码', + parent_id bigint null comment '父级id', + sort_num int(19) null comment '排序', + tenant_id varchar(20) null comment '租户编号', + create_dept bigint null comment '创建部门', + create_by bigint null comment '创建者', + create_time datetime null comment '创建时间', + update_by bigint null comment '更新者', + update_time datetime null comment '更新时间', + constraint uni_category_code + unique (category_code) +) engine=innodb comment= '流程分类'; +INSERT INTO wf_category values (1, 'OA', 'OA', 0, 0, '000000', 103, 1, sysdate(), 1, sysdate()); + +create table wf_task_back_node +( + id bigint not null + primary key, + node_id varchar(255) not null comment '节点id', + node_name varchar(255) not null comment '节点名称', + order_no int not null comment '排序', + instance_id varchar(255) null comment '流程实例id', + task_type varchar(255) not null comment '节点类型', + assignee varchar(2000) not null comment '审批人', + tenant_id varchar(20) null comment '租户编号', + create_dept bigint null comment '创建部门', + create_by bigint null comment '创建者', + create_time datetime null comment '创建时间', + update_by bigint null comment '更新者', + update_time datetime null comment '更新时间' +) + comment '节点审批记录'; + +create table wf_definition_config +( + id bigint not null comment '主键' + primary key, + table_name varchar(255) not null comment '表名', + definition_id varchar(255) not null comment '流程定义ID', + process_key varchar(255) not null comment '流程KEY', + version int(10) not null comment '流程版本', + create_dept bigint null comment '创建部门', + create_by bigint null comment '创建者', + create_time datetime null comment '创建时间', + update_by bigint null comment '更新者', + update_time datetime null comment '更新时间', + remark varchar(500) default '' null comment '备注', + tenant_id varchar(20) null comment '租户编号', + constraint uni_definition_id + unique (definition_id) +) + comment '流程定义配置'; + +create table wf_form_manage +( + id bigint not null comment '主键' + primary key, + form_name varchar(255) not null comment '表单名称', + form_type varchar(255) not null comment '表单类型', + router varchar(255) not null comment '路由地址/表单ID', + remark varchar(500) null comment '备注', + tenant_id varchar(20) null comment '租户编号', + create_dept bigint null comment '创建部门', + create_by bigint null comment '创建者', + create_time datetime null comment '创建时间', + update_by bigint null comment '更新者', + update_time datetime null comment '更新时间' +) + comment '表单管理'; + +insert into wf_form_manage(id, form_name, form_type, router, remark, tenant_id, create_dept, create_by, create_time, update_by, update_time) VALUES (1, '请假申请', 'static', '/workflow/leaveEdit/index', NULL, '000000', 103, 1, sysdate(), 1, sysdate()); + +create table wf_node_config +( + id bigint not null comment '主键' + primary key, + form_id bigint null comment '表单id', + form_type varchar(255) null comment '表单类型', + node_name varchar(255) not null comment '节点名称', + node_id varchar(255) not null comment '节点id', + definition_id varchar(255) not null comment '流程定义id', + apply_user_task char(1) default '0' comment '是否为申请人节点 (0是 1否)', + create_dept bigint null comment '创建部门', + create_by bigint null comment '创建者', + create_time datetime null comment '创建时间', + update_by bigint null comment '更新者', + update_time datetime null comment '更新时间', + tenant_id varchar(20) null comment '租户编号' +) + comment '节点配置'; + + +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11638, '请假申请', 5, 1, 'leave', 'workflow/leave/index', 1, 0, 'C', '0', '0', 'workflow:leave:list', '#', 103, 1, sysdate(), NULL, NULL, '请假申请菜单'); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11639, '请假申请查询', 11638, 1, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:query', '#', 103, 1, sysdate(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11640, '请假申请新增', 11638, 2, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:add', '#', 103, 1, sysdate(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11641, '请假申请修改', 11638, 3, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:edit', '#', 103, 1, sysdate(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11642, '请假申请删除', 11638, 4, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:remove', '#', 103, 1, sysdate(), NULL, NULL, ''); +INSERT INTO sys_menu(menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (11643, '请假申请导出', 11638, 5, '#', '', 1, 0, 'F', '0', '0', 'workflow:leave:export', '#', 103, 1, sysdate(), NULL, NULL, ''); + +INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (13, '000000', '业务状态', 'wf_business_status', 103, 1, sysdate(), NULL, NULL, '业务状态列表'); +INSERT INTO sys_dict_type(dict_id, tenant_id, dict_name, dict_type, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (14, '000000', '表单类型', 'wf_form_type', 103, 1, sysdate(), NULL, NULL, '表单类型列表'); + +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (39, '000000', 1, '已撤销', 'cancel', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '已撤销'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (40, '000000', 2, '草稿', 'draft', 'wf_business_status', '', 'info', 'N', 103, 1, sysdate(), NULL, NULL, '草稿'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (41, '000000', 3, '待审核', 'waiting', 'wf_business_status', '', 'primary', 'N', 103, 1,sysdate(), NULL, NULL, '待审核'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (42, '000000', 4, '已完成', 'finish', 'wf_business_status', '', 'success', 'N', 103, 1, sysdate(), NULL, NULL, '已完成'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (43, '000000', 5, '已作废', 'invalid', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '已作废'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (44, '000000', 6, '已退回', 'back', 'wf_business_status', '', 'danger', 'N', 103, 1, sysdate(), NULL, NULL, '已退回'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (45, '000000', 7, '已终止', 'termination', 'wf_business_status', '', 'danger', 'N', 103, 1,sysdate(), NULL, NULL, '已终止'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (46, '000000', 1, '自定义表单', 'static', 'wf_form_type', '', 'success', 'N', 103, 1, sysdate(), NULL, NULL, '自定义表单'); +INSERT INTO sys_dict_data(dict_code, tenant_id, dict_sort, dict_label, dict_value, dict_type, css_class, list_class, is_default, create_dept, create_by, create_time, update_by, update_time, remark) VALUES (47, '000000', 2, '动态表单', 'dynamic', 'wf_form_type', '', 'primary', 'N', 103, 1, sysdate(), NULL, NULL, '动态表单'); + +-- 表单管理 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11628, '表单管理', '11616', '5', 'formManage', 'workflow/formManage/index', 1, 0, 'C', '0', '0', 'workflow:formManage:list', 'tree-table', 103, 1, sysdate(), null, null, '表单管理菜单'); + +-- 表单管理按钮 SQL +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11644, '表单管理查询', 11628, '1', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:query', '', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11645, '表单管理新增', 11628, '2', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:add', '', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11646, '表单管理修改', 11628, '3', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:edit', '', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11647, '表单管理删除', 11628, '4', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:remove', '', 103, 1, sysdate(), null, null, ''); + +insert into sys_menu (menu_id, menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_dept, create_by, create_time, update_by, update_time, remark) +values(11648, '表单管理导出', 11628, '5', '#', '', 1, 0, 'F', '0', '0', 'workflow:formManage:export', 'tree-table', 103, 1, sysdate(), null, null, '');