From ce7c25f6a2b1bac86c4e37515342592be5e3f0ad Mon Sep 17 00:00:00 2001 From: chenbowen Date: Mon, 22 Sep 2025 03:09:15 +0800 Subject: [PATCH 01/35] =?UTF-8?q?1.=20=E6=89=8B=E5=8A=A8=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E9=87=8D=E5=A4=8D=E8=A2=AB=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E7=9A=84=E6=96=87=E4=BB=B6=EF=BC=8C=E5=B9=B6=E7=BB=9F=E4=B8=80?= =?UTF-8?q?=E5=8C=85=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 27 + zt-module-bpm-api/pom.xml | 47 + .../event/BpmProcessInstanceStatusEvent.java | 41 + ...BpmProcessInstanceStatusEventListener.java | 34 + .../zt/plat/module/bpm/api/package-info.java | 4 + .../bpm/api/task/BpmProcessInstanceApi.java | 27 + .../dto/BpmProcessInstanceCreateReqDTO.java | 35 + .../plat/module/bpm/enums/ApiConstants.java | 23 + .../module/bpm/enums/DictTypeConstants.java | 10 + .../module/bpm/enums/ErrorCodeConstants.java | 87 + .../definition/BpmAutoApproveTypeEnum.java | 32 + .../definition/BpmBoundaryEventTypeEnum.java | 27 + ...ildProcessMultiInstanceSourceTypeEnum.java | 37 + ...BpmChildProcessStartUserEmptyTypeEnum.java | 36 + .../BpmChildProcessStartUserTypeEnum.java | 35 + .../definition/BpmDelayTimerTypeEnum.java | 31 + .../definition/BpmFieldPermissionEnum.java | 33 + .../BpmHttpRequestParamTypeEnum.java | 31 + .../definition/BpmModelFormTypeEnum.java | 32 + .../enums/definition/BpmModelTypeEnum.java | 31 + .../BpmProcessListenerTypeEnum.java | 21 + .../BpmProcessListenerValueTypeEnum.java | 22 + .../BpmSimpleModeConditionTypeEnum.java | 36 + .../BpmSimpleModelNodeTypeEnum.java | 70 + .../enums/definition/BpmTriggerTypeEnum.java | 46 + .../BpmUserTaskApproveMethodEnum.java | 47 + .../BpmUserTaskApproveTypeEnum.java | 31 + ...BpmUserTaskAssignEmptyHandlerTypeEnum.java | 33 + ...serTaskAssignStartUserHandlerTypeEnum.java | 31 + .../BpmUserTaskRejectHandlerTypeEnum.java | 35 + .../BpmUserTaskTimeoutHandlerTypeEnum.java | 32 + .../bpm/enums/message/BpmMessageEnum.java | 27 + .../bpm/enums/task/BpmCommentTypeEnum.java | 46 + .../task/BpmProcessInstanceStatusEnum.java | 50 + .../module/bpm/enums/task/BpmReasonEnum.java | 52 + .../bpm/enums/task/BpmTaskSignTypeEnum.java | 47 + .../bpm/enums/task/BpmTaskStatusEnum.java | 70 + zt-module-bpm-server/Dockerfile | 19 + zt-module-bpm-server/pom.xml | 142 ++ .../druid/pool/DruidPooledStatement.java | 778 +++++++ .../plat/module/bpm/BpmServerApplication.java | 30 + .../zt/plat/module/bpm/api/package-info.java | 4 + .../api/task/BpmProcessInstanceApiImpl.java | 31 + .../admin/base/dept/DeptSimpleBaseVO.java | 15 + .../controller/admin/base/package-info.java | 4 + .../admin/base/user/UserSimpleBaseVO.java | 22 + .../definition/BpmCategoryController.java | 95 + .../admin/definition/BpmFormController.java | 83 + .../admin/definition/BpmModelController.java | 200 ++ .../BpmProcessDefinitionController.java | 133 ++ .../BpmProcessExpressionController.java | 73 + .../BpmProcessListenerController.java | 73 + .../definition/BpmUserGroupController.java | 83 + .../vo/category/BpmCategoryPageReqVO.java | 32 + .../vo/category/BpmCategoryRespVO.java | 33 + .../vo/category/BpmCategorySaveReqVO.java | 37 + .../BpmProcessExpressionPageReqVO.java | 33 + .../BpmProcessExpressionRespVO.java | 30 + .../BpmProcessExpressionSaveReqVO.java | 27 + .../definition/vo/form/BpmFormFieldVO.java | 24 + .../definition/vo/form/BpmFormPageReqVO.java | 14 + .../definition/vo/form/BpmFormRespVO.java | 39 + .../definition/vo/form/BpmFormSaveReqVO.java | 35 + .../vo/group/BpmUserGroupPageReqVO.java | 28 + .../vo/group/BpmUserGroupRespVO.java | 31 + .../vo/group/BpmUserGroupSaveReqVO.java | 31 + .../listener/BpmProcessListenerPageReqVO.java | 30 + .../vo/listener/BpmProcessListenerRespVO.java | 36 + .../listener/BpmProcessListenerSaveReqVO.java | 39 + .../vo/model/BpmModeUpdateBpmnReqVO.java | 19 + .../vo/model/BpmModelMetaInfoVO.java | 180 ++ .../definition/vo/model/BpmModelRespVO.java | 57 + .../vo/model/BpmModelSaveReqVO.java | 34 + .../vo/model/BpmModelUpdateStateReqVO.java | 19 + .../vo/model/simple/BpmSimpleModelNodeVO.java | 526 +++++ .../simple/BpmSimpleModelUpdateReqVO.java | 23 + .../BpmProcessDefinitionPageReqVO.java | 14 + .../process/BpmProcessDefinitionRespVO.java | 71 + .../admin/oa/BpmOALeaveController.http | 12 + .../admin/oa/BpmOALeaveController.java | 62 + .../bpm/controller/admin/oa/package-info.java | 5 + .../admin/oa/vo/BpmOALeaveCreateReqVO.java | 43 + .../admin/oa/vo/BpmOALeavePageReqVO.java | 29 + .../admin/oa/vo/BpmOALeaveRespVO.java | 36 + .../task/BpmProcessInstanceController.http | 16 + .../task/BpmProcessInstanceController.java | 202 ++ .../BpmProcessInstanceCopyController.java | 89 + .../admin/task/BpmTaskController.java | 252 ++ .../task/vo/activity/BpmActivityRespVO.java | 25 + .../vo/cc/BpmProcessInstanceCopyRespVO.java | 48 + .../vo/instance/BpmApprovalDetailReqVO.java | 40 + .../vo/instance/BpmApprovalDetailRespVO.java | 112 + ...BpmProcessInstanceBpmnModelViewRespVO.java | 43 + .../BpmProcessInstanceCancelReqVO.java | 19 + .../BpmProcessInstanceCopyPageReqVO.java | 23 + .../BpmProcessInstanceCreateReqVO.java | 24 + .../instance/BpmProcessInstancePageReqVO.java | 45 + .../vo/instance/BpmProcessInstanceRespVO.java | 86 + .../task/vo/task/BpmTaskApproveReqVO.java | 33 + .../admin/task/vo/task/BpmTaskCopyReqVO.java | 23 + .../task/vo/task/BpmTaskDelegateReqVO.java | 24 + .../admin/task/vo/task/BpmTaskPageReqVO.java | 28 + .../task/vo/task/BpmTaskRejectReqVO.java | 18 + .../admin/task/vo/task/BpmTaskRespVO.java | 130 ++ .../task/vo/task/BpmTaskReturnReqVO.java | 23 + .../task/vo/task/BpmTaskSignCreateReqVO.java | 29 + .../task/vo/task/BpmTaskSignDeleteReqVO.java | 19 + .../task/vo/task/BpmTaskTransferReqVO.java | 24 + .../bpm/controller/app/package-info.java | 4 + .../module/bpm/controller/package-info.java | 6 + .../convert/definition/BpmModelConvert.java | 132 ++ .../BpmProcessDefinitionConvert.java | 99 + .../convert/message/BpmMessageConvert.java | 21 + .../plat/module/bpm/convert/package-info.java | 6 + .../task/BpmProcessInstanceConvert.java | 298 +++ .../bpm/convert/task/BpmTaskConvert.java | 233 ++ ...道 Spring Boot 对象转换 MapStruct 入门》.md | 1 + .../dataobject/definition/BpmCategoryDO.java | 54 + .../dal/dataobject/definition/BpmFormDO.java | 57 + .../BpmProcessDefinitionInfoDO.java | 219 ++ .../definition/BpmProcessExpressionDO.java | 45 + .../definition/BpmProcessListenerDO.java | 74 + .../dataobject/definition/BpmUserGroupDO.java | 52 + .../bpm/dal/dataobject/oa/BpmOALeaveDO.java | 78 + .../task/BpmProcessInstanceCopyDO.java | 98 + .../dal/mysql/category/BpmCategoryMapper.java | 46 + .../dal/mysql/definition/BpmFormMapper.java | 25 + .../BpmProcessDefinitionInfoMapper.java | 27 + .../BpmProcessExpressionMapper.java | 26 + .../definition/BpmProcessListenerMapper.java | 27 + .../mysql/definition/BpmUserGroupMapper.java | 32 + .../bpm/dal/mysql/oa/BpmOALeaveMapper.java | 29 + .../task/BpmProcessInstanceCopyMapper.java | 25 + .../bpm/dal/redis/BpmProcessIdRedisDAO.java | 62 + .../bpm/dal/redis/RedisKeyConstants.java | 15 + .../config/BpmFlowableConfiguration.java | 95 + .../behavior/BpmActivityBehaviorFactory.java | 44 + .../BpmParallelMultiInstanceBehavior.java | 91 + .../BpmSequentialMultiInstanceBehavior.java | 95 + .../behavior/BpmUserTaskActivityBehavior.java | 86 + .../candidate/BpmTaskCandidateInvoker.java | 207 ++ .../candidate/BpmTaskCandidateStrategy.java | 85 + .../BpmTaskAssignLeaderExpression.java | 79 + .../BpmTaskAssignStartUserExpression.java | 36 + ...actBpmTaskCandidateDeptLeaderStrategy.java | 95 + ...askCandidateApproveUserSelectStrategy.java | 78 + ...mTaskCandidateDeptLeaderMultiStrategy.java | 45 + .../BpmTaskCandidateDeptLeaderStrategy.java | 45 + .../BpmTaskCandidateDeptMemberStrategy.java | 48 + ...idateStartUserDeptLeaderMultiStrategy.java | 70 + ...kCandidateStartUserDeptLeaderStrategy.java | 71 + ...mTaskCandidateStartUserSelectStrategy.java | 73 + ...pmTaskCandidateFormDeptLeaderStrategy.java | 56 + .../BpmTaskCandidateFormUserStrategy.java | 47 + .../BpmTaskCandidateAssignEmptyStrategy.java | 73 + .../BpmTaskCandidateExpressionStrategy.java | 58 + .../user/BpmTaskCandidateGroupStrategy.java | 46 + .../user/BpmTaskCandidatePostStrategy.java | 48 + .../user/BpmTaskCandidateRoleStrategy.java | 43 + .../BpmTaskCandidateStartUserStrategy.java | 57 + .../user/BpmTaskCandidateUserStrategy.java | 39 + ...riableConvertByTypeExpressionFunction.java | 32 + .../enums/BpmTaskCandidateStrategyEnum.java | 59 + .../core/enums/BpmnModelConstants.java | 146 ++ .../core/enums/BpmnVariableConstants.java | 99 + .../BpmProcessInstanceEventPublisher.java | 24 + .../core/listener/BpmCopyTaskDelegate.java | 47 + .../BpmProcessInstanceEventListener.java | 54 + .../core/listener/BpmTaskEventListener.java | 125 + .../core/listener/BpmTriggerTaskDelegate.java | 55 + .../DemoDelegateClassExecutionListener.java | 21 + ...moDelegateExpressionExecutionListener.java | 23 + ...DemoSpringExpressionExecutionListener.java | 21 + .../task/DemoDelegateClassTaskListener.java | 20 + .../DemoDelegateExpressionTaskListener.java | 22 + .../DemoSpringExpressionTaskListener.java | 20 + .../core/util/BpmHttpRequestUtils.java | 158 ++ .../flowable/core/util/BpmnModelUtils.java | 1025 +++++++++ .../flowable/core/util/FlowableUtils.java | 362 +++ .../flowable/core/util/SimpleModelUtils.java | 1007 ++++++++ .../module/bpm/framework/package-info.java | 6 + .../rpc/config/RpcConfiguration.java | 17 + .../bpm/framework/rpc/package-info.java | 4 + .../config/SecurityConfiguration.java | 40 + .../framework/security/core/package-info.java | 4 + .../web/config/BpmWebConfiguration.java | 28 + .../framework/web/core/FlowableWebFilter.java | 36 + .../bpm/framework/web/package-info.java | 4 + .../com/zt/plat/module/bpm/package-info.java | 12 + .../definition/BpmCategoryService.java | 92 + .../definition/BpmCategoryServiceImpl.java | 130 ++ .../service/definition/BpmFormService.java | 85 + .../definition/BpmFormServiceImpl.java | 114 + .../service/definition/BpmModelService.java | 134 ++ .../definition/BpmModelServiceImpl.java | 436 ++++ .../BpmProcessDefinitionService.java | 181 ++ .../BpmProcessDefinitionServiceImpl.java | 248 ++ .../BpmProcessExpressionService.java | 54 + .../BpmProcessExpressionServiceImpl.java | 70 + .../definition/BpmProcessListenerService.java | 54 + .../BpmProcessListenerServiceImpl.java | 102 + .../definition/BpmUserGroupService.java | 82 + .../definition/BpmUserGroupServiceImpl.java | 107 + .../definition/dto/BpmFormFieldRespDTO.java | 25 + .../dto/BpmModelMetaInfoRespDTO.java | 46 + .../dto/BpmProcessDefinitionCreateReqDTO.java | 81 + .../service/message/BpmMessageService.java | 46 + .../message/BpmMessageServiceImpl.java | 79 + ...eSendWhenProcessInstanceApproveReqDTO.java | 26 + ...geSendWhenProcessInstanceRejectReqDTO.java | 32 + .../BpmMessageSendWhenTaskCreatedReqDTO.java | 45 + .../BpmMessageSendWhenTaskTimeoutReqDTO.java | 41 + .../bpm/service/oa/BpmOALeaveService.java | 52 + .../bpm/service/oa/BpmOALeaveServiceImpl.java | 89 + .../oa/listener/BpmOALeaveStatusListener.java | 33 + .../task/BpmProcessInstanceCopyService.java | 60 + .../BpmProcessInstanceCopyServiceImpl.java | 96 + .../task/BpmProcessInstanceService.java | 191 ++ .../task/BpmProcessInstanceServiceImpl.java | 964 ++++++++ .../bpm/service/task/BpmTaskService.java | 316 +++ .../bpm/service/task/BpmTaskServiceImpl.java | 1535 +++++++++++++ .../listener/BpmCallActivityListener.java | 96 + .../task/listener/BpmUserTaskListener.java | 59 + .../bpm/service/task/trigger/BpmTrigger.java | 30 + .../trigger/form/BpmFormDeleteTrigger.java | 73 + .../trigger/form/BpmFormUpdateTrigger.java | 66 + .../http/BpmAbstractHttpRequestTrigger.java | 14 + .../trigger/http/BpmHttpCallbackTrigger.java | 50 + .../http/BpmSyncHttpRequestTrigger.java | 45 + .../liquibase/database/core/DmDatabase.java | 530 +++++ .../liquibase/datatype/core/BooleanType.java | 148 ++ .../impl/AbstractEngineConfiguration.java | 2038 +++++++++++++++++ .../main/resources/META-INF/package-info.md | 1 + .../services/liquibase.database.Database | 20 + .../src/main/resources/application-dev.yaml | 94 + .../src/main/resources/application-local.yaml | 111 + .../src/main/resources/application.yaml | 150 ++ .../src/main/resources/logback-spring.xml | 76 + .../BpmTaskCandidateInvokerTest.java | 274 +++ .../BpmTaskAssignLeaderExpressionTest.java | 107 + ...kCandidateDeptLeaderMultiStrategyTest.java | 45 + ...pmTaskCandidateDeptLeaderStrategyTest.java | 44 + ...pmTaskCandidateDeptMemberStrategyTest.java | 47 + ...eStartUserDeptLeaderMultiStrategyTest.java | 85 + ...didateStartUserDeptLeaderStrategyTest.java | 85 + ...kCandidateStartUserSelectStrategyTest.java | 68 + ...mTaskCandidateAssignEmptyStrategyTest.java | 88 + ...pmTaskCandidateExpressionStrategyTest.java | 61 + .../BpmTaskCandidateGroupStrategyTest.java | 44 + .../BpmTaskCandidatePostStrategyTest.java | 48 + .../BpmTaskCandidateRoleStrategyTest.java | 44 + ...BpmTaskCandidateStartUserStrategyTest.java | 56 + .../BpmTaskCandidateUserStrategyTest.java | 31 + .../category/BpmCategoryServiceImplTest.java | 136 ++ .../definition/BpmFormServiceTest.java | 144 ++ .../definition/BpmUserGroupServiceTest.java | 129 ++ .../test/resources/application-unit-test.yaml | 45 + .../src/test/resources/logback.xml | 4 + .../src/test/resources/sql/clean.sql | 3 + .../src/test/resources/sql/create_tables.sql | 43 + 260 files changed, 24095 insertions(+) create mode 100644 pom.xml create mode 100644 zt-module-bpm-api/pom.xml create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java create mode 100644 zt-module-bpm-server/Dockerfile create mode 100644 zt-module-bpm-server/pom.xml create mode 100644 zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java create mode 100644 zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java create mode 100644 zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java create mode 100644 zt-module-bpm-server/src/main/resources/META-INF/package-info.md create mode 100644 zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database create mode 100644 zt-module-bpm-server/src/main/resources/application-dev.yaml create mode 100644 zt-module-bpm-server/src/main/resources/application-local.yaml create mode 100644 zt-module-bpm-server/src/main/resources/application.yaml create mode 100644 zt-module-bpm-server/src/main/resources/logback-spring.xml create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java create mode 100644 zt-module-bpm-server/src/test/resources/application-unit-test.yaml create mode 100644 zt-module-bpm-server/src/test/resources/logback.xml create mode 100644 zt-module-bpm-server/src/test/resources/sql/clean.sql create mode 100644 zt-module-bpm-server/src/test/resources/sql/create_tables.sql diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..302788b --- /dev/null +++ b/pom.xml @@ -0,0 +1,27 @@ + + + + com.zt.plat + zt + ${revision} + + 4.0.0 + + zt-module-bpm-api + zt-module-bpm-server + + zt-module-bpm + pom + + ${project.artifactId} + + bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能。 + 例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等 + bpm 解释:https://baike.baidu.com/item/BPM/1933 + + 工作流基于 Flowable 6 实现,分成流程定义、流程表单、流程实例、流程任务等功能模块。 + + + diff --git a/zt-module-bpm-api/pom.xml b/zt-module-bpm-api/pom.xml new file mode 100644 index 0000000..2aa75a5 --- /dev/null +++ b/zt-module-bpm-api/pom.xml @@ -0,0 +1,47 @@ + + + + com.zt.plat + zt-module-bpm + ${revision} + + 4.0.0 + zt-module-bpm-api + jar + + ${project.artifactId} + + bpm 模块 API,暴露给其它模块调用 + + + + + com.zt.plat + zt-common + + + + + org.springdoc + springdoc-openapi-starter-webmvc-api + provided + + + + + org.springframework.boot + spring-boot-starter-validation + true + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + true + + + + diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java new file mode 100644 index 0000000..4a4316c --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java @@ -0,0 +1,41 @@ +package com.zt.plat.module.bpm.api.event; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.context.ApplicationEvent; + +/** + * 流程实例的状态(结果)发生变化的 Event + * + * @author ZT + */ +@SuppressWarnings("ALL") +@Data +public class BpmProcessInstanceStatusEvent extends ApplicationEvent { + + /** + * 流程实例的编号 + */ + @NotNull(message = "流程实例的编号不能为空") + private String id; + /** + * 流程实例的 key + */ + @NotNull(message = "流程实例的 key 不能为空") + private String processDefinitionKey; + /** + * 流程实例的结果 + */ + @NotNull(message = "流程实例的状态不能为空") + private Integer status; + /** + * 流程实例对应的业务标识 + * 例如说,请假 + */ + private String businessKey; + + public BpmProcessInstanceStatusEvent(Object source) { + super(source); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java new file mode 100644 index 0000000..553096a --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java @@ -0,0 +1,34 @@ +package com.zt.plat.module.bpm.api.event; + +import org.springframework.context.ApplicationListener; + +import java.util.List; + +/** + * {@link BpmProcessInstanceStatusEvent} 的监听器 + * + * @author ZT + */ +public abstract class BpmProcessInstanceStatusEventListener + implements ApplicationListener { + + @Override + public final void onApplicationEvent(BpmProcessInstanceStatusEvent event) { + if (getProcessDefinitionKey().contains(event.getProcessDefinitionKey())){ + onEvent(event); + } + } + + /** + * @return 返回监听的流程定义 Key + */ + protected abstract List getProcessDefinitionKey(); + + /** + * 处理事件 + * + * @param event 事件 + */ + protected abstract void onEvent(BpmProcessInstanceStatusEvent event); + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java new file mode 100644 index 0000000..10d2e79 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java @@ -0,0 +1,4 @@ +/** + * bpm API 包,定义暴露给其它模块的 API + */ +package com.zt.plat.module.bpm.api; diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java new file mode 100644 index 0000000..061e677 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.bpm.api.task; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.enums.ApiConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = +@Tag(name = "RPC 服务 - 流程实例") +public interface BpmProcessInstanceApi { + + String PREFIX = ApiConstants.PREFIX + "/process-instance"; + + @PostMapping(PREFIX + "/create") + @Operation(summary = "创建流程实例(提供给内部),返回实例编号") + @Parameter(name = "userId", description = "用户编号", required = true, example = "1") + CommonResult createProcessInstance(@RequestParam("userId") Long userId, + @Valid @RequestBody BpmProcessInstanceCreateReqDTO reqDTO); + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java new file mode 100644 index 0000000..24b08a4 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java @@ -0,0 +1,35 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "RPC 服务 - 流程实例的创建 Request DTO") +@Data +public class BpmProcessInstanceCreateReqDTO { + + @Schema(description = "流程定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "leave") + @NotEmpty(message = "流程定义的标识不能为空") + private String processDefinitionKey; + + @Schema(description = "变量实例", requiredMode = Schema.RequiredMode.REQUIRED) + private Map variables; + + @Schema(description = "业务的唯一标识", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "业务的唯一标识不能为空") + private String businessKey; // 例如说,请假申请的编号。通过它,可以查询到对应的实例 + + /** + * 发起人自选审批人 Map + * + * key:taskKey 任务编码 + * value:审批人的数组 + * 例如:{ taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批 + */ + @Schema(description = "发起人自选审批人 Map") + private Map> startUserSelectAssignees; + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java new file mode 100644 index 0000000..3c4bac9 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.enums; + +import com.zt.plat.framework.common.enums.RpcConstants; + +/** + * API 相关的枚举 + * + * @author ZT + */ +public class ApiConstants { + + /** + * 服务名 + * + * 注意,需要保证和 spring.application.name 保持一致 + */ + public static final String NAME = "bpm-server"; + + public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/bpm"; + + public static final String VERSION = "1.0.0"; + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java new file mode 100644 index 0000000..13dcf72 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java @@ -0,0 +1,10 @@ +package com.zt.plat.module.bpm.enums; + +/** + * BPM 字典类型的枚举类 + * + * @author ZT + */ +public interface DictTypeConstants { + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java new file mode 100644 index 0000000..b9db1ed --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java @@ -0,0 +1,87 @@ +package com.zt.plat.module.bpm.enums; + +import com.zt.plat.framework.common.exception.ErrorCode; + +/** + * Bpm 错误码枚举类 + *

+ * bpm 系统,使用 1-009-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== 通用流程处理 模块 1-009-000-000 ========== + + // ========== OA 流程模块 1-009-001-000 ========== + ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1_009_001_001, "请假申请不存在"); + + // ========== 流程模型 1-009-002-000 ========== + ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "已经存在流程标识为【{}】的流程"); + ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_009_002_001, "流程模型不存在"); + ErrorCode MODEL_KEY_VALID = new ErrorCode(1_009_002_002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!"); + ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"); + ErrorCode MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," + + "原因:用户任务({})未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置"); + ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件"); + ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在"); + ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程({})的管理员"); + ErrorCode MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR = new ErrorCode(1_009_002_008, "部署流程失败,原因:首个任务({})的审批人不能是【审批人自选】"); + + // ========== 流程定义 1-009-003-000 ========== + ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); + ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图"); + ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, "流程定义不存在"); + ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1_009_003_003, "流程定义处于挂起状态"); + + // ========== 流程实例 1-009-004-000 ========== + ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_004_000, "流程实例不存在"); + ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中"); + ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的"); + ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "任务({})的候选人未配置"); + ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在"); + ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); + ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); + ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); + ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置"); + ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消"); + + // ========== 流程任务 1-009-005-000 ========== + ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); + ErrorCode TASK_NOT_EXISTS = new ErrorCode(1_009_005_002, "流程任务不存在"); + ErrorCode TASK_IS_PENDING = new ErrorCode(1_009_005_003, "当前任务处于挂起状态,不能操作"); + ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_004, " 目标节点不存在"); + ErrorCode TASK_RETURN_FAIL_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_006, "退回任务失败,目标节点是在并行网关上或非同一路线上,不可跳转"); + ErrorCode TASK_DELEGATE_FAIL_USER_REPEAT = new ErrorCode(1_009_005_007, "任务委派失败,委派人和当前审批人为同一人"); + ErrorCode TASK_DELEGATE_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_008, "任务委派失败,被委派人不存在"); + ErrorCode TASK_SIGN_CREATE_USER_NOT_EXIST = new ErrorCode(1_009_005_009, "任务加签:选择的用户不存在"); + ErrorCode TASK_SIGN_CREATE_TYPE_ERROR = new ErrorCode(1_009_005_010, "任务加签:当前任务已经{},不能{}"); + ErrorCode TASK_SIGN_CREATE_USER_REPEAT = new ErrorCode(1_009_005_011, "任务加签失败,加签人与现有审批人[{}]重复"); + ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务"); + ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人"); + ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在"); + ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); + ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); + ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); + + // ========== 动态表单模块 1-009-010-000 ========== + ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在"); + ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1_009_010_001, "表单项({}) 和 ({}) 使用了相同的字段名({})"); + + // ========== 用户组模块 1-009-011-000 ========== + ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1_009_011_000, "用户分组不存在"); + ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1_009_011_001, "名字为【{}】的用户分组已被禁用"); + + // ========== 用户组模块 1-009-012-000 ========== + ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_009_012_000, "流程分类不存在"); + ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(1_009_012_001, "流程分类名字【{}】重复"); + ErrorCode CATEGORY_CODE_DUPLICATE = new ErrorCode(1_009_012_002, "流程分类编码【{}】重复"); + + // ========== BPM 流程监听器 1-009-013-000 ========== + ErrorCode PROCESS_LISTENER_NOT_EXISTS = new ErrorCode(1_009_013_000, "流程监听器不存在"); + ErrorCode PROCESS_LISTENER_CLASS_NOT_FOUND = new ErrorCode(1_009_013_001, "流程监听器类({})不存在"); + ErrorCode PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR = new ErrorCode(1_009_013_002, "流程监听器类({})没有实现接口({})"); + ErrorCode PROCESS_LISTENER_EXPRESSION_INVALID = new ErrorCode(1_009_013_003, "流程监听器表达式({})不合法"); + + // ========== BPM 流程表达式 1-009-014-000 ========== + ErrorCode PROCESS_EXPRESSION_NOT_EXISTS = new ErrorCode(1_009_014_000, "流程表达式不存在"); + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java new file mode 100644 index 0000000..2611d38 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 自动去重的类型的枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmAutoApproveTypeEnum implements ArrayValuable { + + NONE(0, "不自动通过"), + APPROVE_ALL(1, "仅审批一次,后续重复的审批节点均自动通过"), + APPROVE_SEQUENT(2, "仅针对连续审批的节点自动通过"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmAutoApproveTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java new file mode 100644 index 0000000..d168f66 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 边界事件 (boundary event) 自定义类型枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmBoundaryEventTypeEnum { + + USER_TASK_TIMEOUT(1, "用户任务超时"), + DELAY_TIMER_TIMEOUT(2, "延迟器超时"), + CHILD_PROCESS_TIMEOUT(3, "子流程超时"); + + private final Integer type; + private final String name; + + public static BpmBoundaryEventTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values()); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java new file mode 100644 index 0000000..68574cb --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java @@ -0,0 +1,37 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 子流程多实例来源类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmChildProcessMultiInstanceSourceTypeEnum implements ArrayValuable { + + FIXED_QUANTITY(1, "固定数量"), + NUMBER_FORM(2, "数字表单"), + MULTIPLE_FORM(3, "多选表单"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessMultiInstanceSourceTypeEnum::getType).toArray(Integer[]::new); + + public static BpmChildProcessMultiInstanceSourceTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java new file mode 100644 index 0000000..5b13462 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 当子流程发起人为空时类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmChildProcessStartUserEmptyTypeEnum implements ArrayValuable { + + MAIN_PROCESS_START_USER(1, "同主流程发起人"), + CHILD_PROCESS_ADMIN(2, "子流程管理员"), + MAIN_PROCESS_ADMIN(3, "主流程管理员"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserEmptyTypeEnum::getType).toArray(Integer[]::new); + + public static BpmChildProcessStartUserEmptyTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java new file mode 100644 index 0000000..8470a72 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java @@ -0,0 +1,35 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 子流程发起人类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmChildProcessStartUserTypeEnum implements ArrayValuable { + + MAIN_PROCESS_START_USER(1, "同主流程发起人"), + FROM_FORM(2, "表单"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserTypeEnum::getType).toArray(Integer[]::new); + + public static BpmChildProcessStartUserTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java new file mode 100644 index 0000000..aabaef0 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 延迟器类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmDelayTimerTypeEnum implements ArrayValuable { + + FIXED_TIME_DURATION(1, "固定时长"), + FIXED_DATE_TIME(2, "固定日期"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmDelayTimerTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java new file mode 100644 index 0000000..8571f90 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 表单权限的枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmFieldPermissionEnum { + + READ(1, "只读"), + WRITE(2, "可编辑"), + NONE(3, "隐藏"); + + /** + * 权限 + */ + private final Integer permission; + /** + * 名字 + */ + private final String name; + + public static BpmFieldPermissionEnum valueOf(Integer permission) { + return ArrayUtil.firstMatch(item -> item.getPermission().equals(permission), values()); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java new file mode 100644 index 0000000..dd1fc50 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM HTTP 请求参数设置类型。用于 Simple 设计器任务监听器和触发器配置。 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmHttpRequestParamTypeEnum implements ArrayValuable { + + FIXED_VALUE(1, "固定值"), + FROM_FORM(2, "表单"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmHttpRequestParamTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java new file mode 100644 index 0000000..f28b1ed --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 模型的表单类型的枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmModelFormTypeEnum implements ArrayValuable { + + NORMAL(10, "流程表单"), // 对应 BpmFormDO + CUSTOM(20, "业务表单") // 业务自己定义的表单,自己进行数据的存储 + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmModelFormTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java new file mode 100644 index 0000000..259b365 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 模型的类型的枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmModelTypeEnum implements ArrayValuable { + + BPMN(10, "BPMN 设计器"), // https://bpmn.io/toolkit/bpmn-js/ + SIMPLE(20, "SIMPLE 设计器"); // 参考钉钉、飞书工作流的设计器 + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmModelTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java new file mode 100644 index 0000000..b1ecfe4 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java @@ -0,0 +1,21 @@ +package com.zt.plat.module.bpm.enums.definition; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 流程监听器的类型 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmProcessListenerTypeEnum { + + EXECUTION("execution", "执行监听器"), + TASK("task", "任务执行器"); + + private final String type; + private final String name; + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java new file mode 100644 index 0000000..7ff961b --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java @@ -0,0 +1,22 @@ +package com.zt.plat.module.bpm.enums.definition; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 流程监听器的值类型 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmProcessListenerValueTypeEnum { + + CLASS("class", "Java 类"), + DELEGATE_EXPRESSION("delegateExpression", "代理表达式"), + EXPRESSION("expression", "表达式"); + + private final String type; + private final String name; + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java new file mode 100644 index 0000000..b8c8ee2 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 仿钉钉的流程器设计器条件节点的条件类型 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmSimpleModeConditionTypeEnum implements ArrayValuable { + + EXPRESSION(1, "条件表达式"), + RULE(2, "条件规则"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmSimpleModeConditionTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + + private final String name; + + public static BpmSimpleModeConditionTypeEnum valueOf(Integer type) { + return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java new file mode 100644 index 0000000..6590a4c --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java @@ -0,0 +1,70 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; + +/** + * 仿钉钉的流程器设计器的模型节点类型 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable { + + // 0 ~ 1 开始和结束 + START_NODE(0, "开始", "startEvent"), + END_NODE(1, "结束", "endEvent"), + + // 10 ~ 49 各种节点 + START_USER_NODE(10, "发起人", "userTask"), // 发起人节点。前端的开始节点,Id 固定 + APPROVE_NODE(11, "审批人", "userTask"), + COPY_NODE(12, "抄送人", "serviceTask"), + TRANSACTOR_NODE(13, "办理人", "userTask"), + + DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), + TRIGGER_NODE(15, "触发器", "serviceTask"), + + CHILD_PROCESS(20, "子流程", "callActivity"), + + // 50 ~ 条件分支 + CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 + CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"), + PARALLEL_BRANCH_NODE(52, "并行分支", "parallelGateway"), + INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"), + ROUTER_BRANCH_NODE(54, "路由分支", "exclusiveGateway") + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmSimpleModelNodeTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + private final String name; + private final String bpmnType; + + /** + * 判断是否为分支节点 + * + * @param type 节点类型 + */ + public static boolean isBranchNode(Integer type) { + return Objects.equals(CONDITION_BRANCH_NODE.getType(), type) + || Objects.equals(PARALLEL_BRANCH_NODE.getType(), type) + || Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type) + || Objects.equals(ROUTER_BRANCH_NODE.getType(), type); + } + + public static BpmSimpleModelNodeTypeEnum valueOf(Integer type) { + return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java new file mode 100644 index 0000000..72c3c39 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM Simple 触发器类型枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmTriggerTypeEnum implements ArrayValuable { + + HTTP_REQUEST(1, "发起 HTTP 请求"), // BPM => 业务,流程继续执行,无需等待业务 + HTTP_CALLBACK(2, "接收 HTTP 回调"), // BPM => 业务 => BPM,流程卡主,等待业务回调 + + FORM_UPDATE(10, "更新流程表单数据"), + FORM_DELETE(11, "删除流程表单数据"), + ; + + /** + * 触发器执行动作类型 + */ + private final Integer type; + + /** + * 触发器执行动作描述 + */ + private final String desc; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTriggerTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static BpmTriggerTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java new file mode 100644 index 0000000..065035d --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java @@ -0,0 +1,47 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 多人审批方式的枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmUserTaskApproveMethodEnum implements ArrayValuable { + + RANDOM(1, "随机挑选一人审批", null), + RATIO(2, "多人会签(按通过比例)", "${ nrOfCompletedInstances/nrOfInstances >= %s}"), // 会签(按通过比例) + ANY(3, "多人或签(一人通过或拒绝)", "${ nrOfCompletedInstances > 0 }"), // 或签(通过只需一人,拒绝只需一人) + SEQUENTIAL(4, "依次审批", "${ nrOfCompletedInstances >= nrOfInstances }"); // 依次审批 + + /** + * 审批方式 + */ + private final Integer method; + /** + * 名字 + */ + private final String name; + /** + * 完成表达式 + */ + private final String completionCondition; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskApproveMethodEnum::getMethod).toArray(Integer[]::new); + + public static BpmUserTaskApproveMethodEnum valueOf(Integer method) { + return ArrayUtil.firstMatch(item -> item.getMethod().equals(method), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java new file mode 100644 index 0000000..df40e18 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 用户任务的审批类型枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmUserTaskApproveTypeEnum implements ArrayValuable { + + USER(1), // 人工审批 + AUTO_APPROVE(2), // 自动通过 + AUTO_REJECT(3); // 自动拒绝 + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskApproveTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java new file mode 100644 index 0000000..237ef8e --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * BPM 用户任务的审批人为空时,处理类型枚举 + * + * @author ZT + */ +@RequiredArgsConstructor +@Getter +public enum BpmUserTaskAssignEmptyHandlerTypeEnum implements ArrayValuable { + + APPROVE(1), // 自动通过 + REJECT(2), // 自动拒绝 + ASSIGN_USER(3), // 指定人员审批 + ASSIGN_ADMIN(4), // 转交给流程管理员 + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskAssignEmptyHandlerTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java new file mode 100644 index 0000000..ffed389 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * BPM 用户任务的审批人与发起人相同时,处理类型枚举 + * + * @author ZT + */ +@RequiredArgsConstructor +@Getter +public enum BpmUserTaskAssignStartUserHandlerTypeEnum implements ArrayValuable { + + START_USER_AUDIT(1), // 由发起人对自己审批 + SKIP(2), // 自动跳过【参考飞书】:1)如果当前节点还有其他审批人,则交由其他审批人进行审批;2)如果当前节点没有其他审批人,则该节点自动通过 + TRANSFER_DEPT_LEADER(3); // 转交给部门负责人审批【参考飞书】:若部门负责人为空,则自动通过 + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskAssignStartUserHandlerTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java new file mode 100644 index 0000000..f251971 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java @@ -0,0 +1,35 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 用户任务拒绝处理类型枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmUserTaskRejectHandlerTypeEnum implements ArrayValuable { + + FINISH_PROCESS_INSTANCE(1, "终止流程"), + RETURN_USER_TASK(2, "驳回到指定任务节点"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskRejectHandlerTypeEnum::getType).toArray(Integer[]::new); + + public static BpmUserTaskRejectHandlerTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java new file mode 100644 index 0000000..8f9460e --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 用户任务超时处理类型枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmUserTaskTimeoutHandlerTypeEnum implements ArrayValuable { + + REMINDER(1,"自动提醒"), + APPROVE(2, "自动同意"), + REJECT(3, "自动拒绝"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskTimeoutHandlerTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java new file mode 100644 index 0000000..d8db9ae --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.bpm.enums.message; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Bpm 消息的枚举 + * + * @author ZT + */ +@AllArgsConstructor +@Getter +public enum BpmMessageEnum { + + PROCESS_INSTANCE_APPROVE("bpm_process_instance_approve"), // 流程任务被审批通过时,发送给申请人 + PROCESS_INSTANCE_REJECT("bpm_process_instance_reject"), // 流程任务被审批不通过时,发送给申请人 + TASK_ASSIGNED("bpm_task_assigned"), // 任务被分配时,发送给审批人 + TASK_TIMEOUT("bpm_task_timeout"); // 任务审批超时时,发送给审批人 + + /** + * 短信模板的标识 + * + * 关联 SmsTemplateDO 的 code 属性 + */ + private final String smsTemplateCode; + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java new file mode 100644 index 0000000..51a5ffa --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.enums.task; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程任务的 Comment 评论类型枚举 + * + * @author kehaiyou + */ +@Getter +@AllArgsConstructor +public enum BpmCommentTypeEnum { + + APPROVE("1", "审批通过", "审批通过,原因是:{}"), + REJECT("2", "不通过", "审批不通过:原因是:{}"), + CANCEL("3", "已取消", "系统自动取消,原因是:{}"), + RETURN("4", "退回", "任务被退回,原因是:{}"), + DELEGATE_START("5", "委派发起", "[{}]将任务委派给[{}],委派理由为:{}"), + DELEGATE_END("6", "委派完成", "[{}]完成委派任务,任务重新回到[{}]手中,审批建议为:{}"), + TRANSFER("7", "转派", "[{}]将任务转派给[{}],转派理由为:{}"), + ADD_SIGN("8", "加签", "[{}]{}给了[{}],理由为:{}"), + SUB_SIGN("9", "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"), + ; + + /** + * 操作类型 + * + * 由于 BPM Comment 类型为 String,所以这里就不使用 Integer + */ + private final String type; + /** + * 操作名字 + */ + private final String name; + /** + * 操作描述 + */ + private final String comment; + + public String formatComment(Object... params) { + return StrUtil.format(comment, params); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java new file mode 100644 index 0000000..411e9ad --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java @@ -0,0 +1,50 @@ +package com.zt.plat.module.bpm.enums.task; + +import com.zt.plat.framework.common.core.ArrayValuable; +import com.zt.plat.framework.common.util.object.ObjectUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 流程实例 ProcessInstance 的状态 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmProcessInstanceStatusEnum implements ArrayValuable { + + NOT_START(-1, "未开始"), + RUNNING(1, "审批中"), + APPROVE(2, "审批通过"), + REJECT(3, "审批不通过"), + CANCEL(4, "已取消"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmProcessInstanceStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 状态 + */ + private final Integer status; + /** + * 描述 + */ + private final String desc; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static boolean isRejectStatus(Integer status) { + return REJECT.getStatus().equals(status); + } + + public static boolean isProcessEndStatus(Integer status) { + return ObjectUtils.equalsAny(status, + APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus()); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java new file mode 100644 index 0000000..8bf768f --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java @@ -0,0 +1,52 @@ +package com.zt.plat.module.bpm.enums.task; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程实例/任务的的处理原因枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmReasonEnum { + + // ========== 流程实例的独有原因 ========== + + REJECT_TASK("审批不通过任务,原因:{}"), // 场景:用户审批不通过任务。修改文案时,需要注意 isRejectReason 方法 + CANCEL_PROCESS_INSTANCE_BY_START_USER("用户主动取消流程,原因:{}"), // 场景:用户主动取消流程 + CANCEL_PROCESS_INSTANCE_BY_ADMIN("管理员【{}】取消流程,原因:{}"), // 场景:管理员取消流程 + CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS("子流程自动取消,原因:主流程已取消"), + + // ========== 流程任务的独有原因 ========== + + CANCEL_BY_SYSTEM("系统自动取消"), // 场景:非常多,比如说:1)多任务审批已经满足条件,无需审批该任务;2)流程实例被取消,无需审批该任务;等等 + TIMEOUT_APPROVE("审批超时,系统自动通过"), + TIMEOUT_REJECT("审批超时,系统自动不通过"), + ASSIGN_START_USER_APPROVE("审批人与提交人为同一人时,自动通过"), + ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"), + ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE("发起人节点首次自动通过"), // 目前仅“子流程”使用 + ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"), + ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"), + ASSIGN_EMPTY_APPROVE("审批人为空,自动通过"), + ASSIGN_EMPTY_REJECT("审批人为空,自动不通过"), + APPROVE_TYPE_AUTO_APPROVE("非人工审核,自动通过"), + APPROVE_TYPE_AUTO_REJECT("非人工审核,自动不通过"), + CANCEL_BY_PROCESS_CLEAN("进程清理自动取消"), + ; + + private final String reason; + + /** + * 格式化理由 + * + * @param args 参数 + * @return 理由 + */ + public String format(Object... args) { + return StrUtil.format(reason, args); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java new file mode 100644 index 0000000..40aa2b1 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java @@ -0,0 +1,47 @@ +package com.zt.plat.module.bpm.enums.task; + +import cn.hutool.core.util.ArrayUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程任务的加签类型枚举 + * + * @author kehaiyou + */ +@Getter +@AllArgsConstructor +public enum BpmTaskSignTypeEnum { + + /** + * 向前加签,需要前置任务审批完成,才回到原审批人 + */ + BEFORE("before", "向前加签"), + /** + * 向后加签,需要后置任务全部审批完,才会通过原审批人节点 + */ + AFTER("after", "向后加签"); + + /** + * 类型 + */ + private final String type; + /** + * 名字 + */ + private final String name; + + public static String nameOfType(String type) { + for (BpmTaskSignTypeEnum value : values()) { + if (value.type.equals(type)) { + return value.name; + } + } + return null; + } + + public static BpmTaskSignTypeEnum of(String type) { + return ArrayUtil.firstMatch(value -> value.getType().equals(type), values()); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java new file mode 100644 index 0000000..51ca7af --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java @@ -0,0 +1,70 @@ +package com.zt.plat.module.bpm.enums.task; + +import cn.hutool.core.util.ObjUtil; +import com.zt.plat.framework.common.util.object.ObjectUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程任务 Task 的状态枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmTaskStatusEnum { + + NOT_START(-1, "未开始"), + RUNNING(1, "审批中"), + APPROVE(2, "审批通过"), + REJECT(3, "审批不通过"), + CANCEL(4, "已取消"), + + RETURN(5, "已退回"), + + /** + * 使用场景: + * 1. 任务被向后【加签】时,它在审批通过后,会变成 APPROVING 这个状态,然后等到【加签】出来的任务都被审批后,才会变成 APPROVE 审批通过 + */ + APPROVING(7, "审批通过中"), + /** + * 使用场景: + * 1. 任务被向前【加签】时,它会变成 WAIT 状态,需要等待【加签】出来的任务被审批后,它才能继续变为 RUNNING 继续审批 + * 2. 任务被向后【加签】时,【加签】出来的任务处于 WAIT 状态,它们需要等待该任务被审批后,它们才能继续变为 RUNNING 继续审批 + */ + WAIT(0, "待审批"); + + /** + * 状态 + *

+ * 如果新增时,注意 {@link #isEndStatus(Integer)} 是否需要变更 + */ + private final Integer status; + /** + * 名字 + */ + private final String name; + + public static boolean isRejectStatus(Integer status) { + return REJECT.getStatus().equals(status); + } + + /** + * 判断该状态是否已经处于 End 最终状态 + *

+ * 主要用于一些状态更新的逻辑,如果已经是最终状态,就不再进行更新 + * + * @param status 状态 + * @return 是否 + */ + public static boolean isEndStatus(Integer status) { + return ObjectUtils.equalsAny(status, + APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus(), + RETURN.getStatus(), APPROVING.getStatus()); + } + + public static boolean isCancelStatus(Integer status) { + return ObjUtil.equal(status, CANCEL.getStatus()); + } + +} diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile new file mode 100644 index 0000000..230aa5c --- /dev/null +++ b/zt-module-bpm-server/Dockerfile @@ -0,0 +1,19 @@ +## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 + +FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre + +## 创建目录,并使用它作为工作目录 +RUN mkdir -p /zt-module-bpm-server +WORKDIR /zt-module-bpm-server +## 将后端项目的 Jar 文件,复制到镜像中 +COPY ./target/zt-module-bpm-server.jar app.jar + +## 设置 TZ 时区 +## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 +ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m" + +## 暴露后端项目的 48080 端口 +EXPOSE 48083 + +## 启动后端项目 +CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar diff --git a/zt-module-bpm-server/pom.xml b/zt-module-bpm-server/pom.xml new file mode 100644 index 0000000..06a5ac4 --- /dev/null +++ b/zt-module-bpm-server/pom.xml @@ -0,0 +1,142 @@ + + + + com.zt.plat + zt-module-bpm + ${revision} + + 4.0.0 + zt-module-bpm-server + + ${project.artifactId} + + bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能,基于 Flowable 6 版本实现。 + 例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等 + + + + + com.zt.plat + zt-spring-boot-starter-env + + + + + com.zt.plat + zt-module-bpm-api + ${revision} + + + com.zt.plat + zt-module-system-api + ${revision} + + + + + com.zt.plat + zt-spring-boot-starter-biz-data-permission + + + com.zt.plat + zt-spring-boot-starter-biz-tenant + + + + + com.zt.plat + zt-spring-boot-starter-security + + + + + com.zt.plat + zt-spring-boot-starter-mybatis + + + + com.zt.plat + zt-spring-boot-starter-redis + + + + + com.zt.plat + zt-spring-boot-starter-rpc + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + + + + + + + com.zt.plat + zt-spring-boot-starter-test + + + + + com.zt.plat + zt-spring-boot-starter-monitor + + + + + com.zt.plat + zt-spring-boot-starter-excel + + + + + org.flowable + flowable-spring-boot-starter-process + + + org.flowable + flowable-spring-boot-starter-actuator + + + com.zt.plat + zt-spring-boot-starter-biz-business + ${revision} + compile + + + + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + + diff --git a/zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java b/zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java new file mode 100644 index 0000000..fe4cc83 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java @@ -0,0 +1,778 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package com.alibaba.druid.pool; + +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.druid.VERSION; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; +import com.alibaba.druid.util.JdbcUtils; +import com.alibaba.druid.util.MySqlUtils; + +import java.net.SocketTimeoutException; +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + +public class DruidPooledStatement extends PoolableWrapper implements Statement { + private static final Log LOG = LogFactory.getLog(DruidPooledStatement.class); + private final Statement stmt; + protected DruidPooledConnection conn; + protected List resultSetTrace; + protected boolean closed; + protected int fetchRowPeak = -1; + protected int exceptionCount; + + public DruidPooledStatement(DruidPooledConnection conn, Statement stmt) { + super(stmt); + this.conn = conn; + this.stmt = stmt; + } + + protected void addResultSetTrace(ResultSet resultSet) { + if (this.resultSetTrace == null) { + this.resultSetTrace = new ArrayList(1); + } else if (this.resultSetTrace.size() > 0) { + int lastIndex = this.resultSetTrace.size() - 1; + ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(lastIndex); + + try { + if (lastResultSet.isClosed()) { + this.resultSetTrace.set(lastIndex, resultSet); + return; + } + } catch (SQLException var5) { + } + } + + this.resultSetTrace.add(resultSet); + } + + protected void recordFetchRowCount(int fetchRowCount) { + if (this.fetchRowPeak < fetchRowCount) { + this.fetchRowPeak = fetchRowCount; + } + + } + + public int getFetchRowPeak() { + return this.fetchRowPeak; + } + + protected SQLException checkException(Throwable error) throws SQLException { + String sql = null; + if (this instanceof DruidPooledPreparedStatement) { + sql = ((DruidPooledPreparedStatement)this).getSql(); + } + + this.handleSocketTimeout(error); + ++this.exceptionCount; + return this.conn.handleException(error, sql); + } + + protected SQLException checkException(Throwable error, String sql) throws SQLException { + this.handleSocketTimeout(error); + ++this.exceptionCount; + return this.conn.handleException(error, sql); + } + + protected void handleSocketTimeout(Throwable error) throws SQLException { + if (this.conn != null && this.conn.transactionInfo == null && this.conn.holder != null) { + DruidDataSource dataSource = null; + DruidConnectionHolder holder = this.conn.holder; + if (holder.dataSource instanceof DruidDataSource) { + dataSource = (DruidDataSource)holder.dataSource; + } + + if (dataSource != null) { + if (dataSource.killWhenSocketReadTimeout) { + SQLException sqlException = null; + if (error instanceof SQLException) { + sqlException = (SQLException)error; + } + + if (sqlException != null) { + Throwable cause = error.getCause(); + boolean socketReadTimeout = cause instanceof SocketTimeoutException && "Read timed out".equals(cause.getMessage()); + if (socketReadTimeout) { + if (JdbcUtils.isMysqlDbType(dataSource.dbTypeName)) { + String killQuery = MySqlUtils.buildKillQuerySql(this.conn.getConnection(), (SQLException)error); + if (killQuery != null) { + DruidPooledConnection killQueryConn = null; + Statement killQueryStmt = null; + + try { + killQueryConn = dataSource.getConnection(1000L); + if (killQueryConn != null) { + killQueryStmt = killQueryConn.createStatement(); + killQueryStmt.execute(killQuery); + if (LOG.isDebugEnabled()) { + LOG.debug(killQuery + " success."); + } + + return; + } + } catch (Exception ex) { + LOG.warn(killQuery + " error.", ex); + return; + } finally { + JdbcUtils.close(killQueryStmt); + JdbcUtils.close(killQueryConn); + } + + } + } + } + } + } + } + } + } + + public DruidPooledConnection getPoolableConnection() { + return this.conn; + } + + public Statement getStatement() { + return this.stmt; + } + + protected void checkOpen() throws SQLException { + if (this.closed) { + Throwable disableError = null; + if (this.conn != null) { + disableError = this.conn.getDisableError(); + } + + if (disableError != null) { + throw new SQLException("statement is closed", disableError); + } else { + throw new SQLException("statement is closed"); + } + } + } + + protected void clearResultSet() { + if (this.resultSetTrace != null) { + for(ResultSet rs : this.resultSetTrace) { + try { + if (!rs.isClosed()) { + rs.close(); + } + } catch (SQLException ex) { + LOG.error("clearResultSet error", ex); + } + } + + this.resultSetTrace.clear(); + } + } + + public void incrementExecuteCount() { + DruidPooledConnection conn = this.getPoolableConnection(); + if (conn != null) { + DruidConnectionHolder holder = conn.getConnectionHolder(); + if (holder != null) { + DruidAbstractDataSource dataSource = holder.getDataSource(); + if (dataSource != null) { + dataSource.incrementExecuteCount(); + } + } + } + } + + public void incrementExecuteBatchCount() { + DruidPooledConnection conn = this.getPoolableConnection(); + if (conn != null) { + DruidConnectionHolder holder = conn.getConnectionHolder(); + if (holder != null) { + if (holder.getDataSource() != null) { + DruidAbstractDataSource dataSource = holder.getDataSource(); + if (dataSource != null) { + dataSource.incrementExecuteBatchCount(); + } + } + } + } + } + + public void incrementExecuteUpdateCount() { + DruidPooledConnection conn = this.getPoolableConnection(); + if (conn != null) { + DruidConnectionHolder holder = conn.getConnectionHolder(); + if (holder != null) { + DruidAbstractDataSource dataSource = holder.getDataSource(); + if (dataSource != null) { + dataSource.incrementExecuteUpdateCount(); + } + } + } + } + + public void incrementExecuteQueryCount() { + DruidPooledConnection conn = this.conn; + if (conn != null) { + DruidConnectionHolder holder = conn.holder; + if (holder != null) { + DruidAbstractDataSource dataSource = holder.dataSource; + if (dataSource != null) { + ++dataSource.executeQueryCount; + } + } + } + } + + protected void transactionRecord(String sql) throws SQLException { + this.conn.transactionRecord(sql); + } + + public final ResultSet executeQuery(String sql) throws SQLException { + this.checkOpen(); + this.incrementExecuteQueryCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + ResultSet var3; + try { + ResultSet rs = this.stmt.executeQuery(sql); + if (rs != null) { + DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs); + this.addResultSetTrace(poolableResultSet); + DruidPooledResultSet var4 = poolableResultSet; + return var4; + } + + var3 = rs; + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final int executeUpdate(String sql) throws SQLException { + this.checkOpen(); + this.incrementExecuteUpdateCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + int var2; + try { + var2 = this.stmt.executeUpdate(sql); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var2; + } + + protected final void errorCheck(Throwable t) { + String errorClassName = t.getClass().getName(); + if (errorClassName.endsWith(".CommunicationsException") && this.conn.holder != null && this.conn.holder.dataSource.testWhileIdle) { + DruidConnectionHolder holder = this.conn.holder; + DruidAbstractDataSource dataSource = holder.dataSource; + long currentTimeMillis = System.currentTimeMillis(); + long lastActiveTimeMillis = holder.lastActiveTimeMillis; + if (lastActiveTimeMillis < holder.lastKeepTimeMillis) { + lastActiveTimeMillis = holder.lastKeepTimeMillis; + } + + long idleMillis = currentTimeMillis - lastActiveTimeMillis; + long lastValidIdleMillis = currentTimeMillis - holder.lastActiveTimeMillis; + String errorMsg = "CommunicationsException, druid version " + VERSION.getVersionNumber() + ", jdbcUrl : " + dataSource.jdbcUrl + ", testWhileIdle " + dataSource.testWhileIdle + ", idle millis " + idleMillis + ", minIdle " + dataSource.minIdle + ", poolingCount " + dataSource.getPoolingCount() + ", timeBetweenEvictionRunsMillis " + dataSource.timeBetweenEvictionRunsMillis + ", lastValidIdleMillis " + lastValidIdleMillis + ", driver " + dataSource.driver.getClass().getName(); + if (dataSource.exceptionSorter != null) { + errorMsg = errorMsg + ", exceptionSorter " + dataSource.exceptionSorter.getClass().getName(); + } + + LOG.error(errorMsg); + } + + } + + public final int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + this.checkOpen(); + this.incrementExecuteUpdateCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + int var3; + try { + var3 = this.stmt.executeUpdate(sql, autoGeneratedKeys); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + this.checkOpen(); + this.incrementExecuteUpdateCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + int var3; + try { + var3 = this.stmt.executeUpdate(sql, columnIndexes); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final int executeUpdate(String sql, String[] columnNames) throws SQLException { + this.checkOpen(); + this.incrementExecuteUpdateCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + int var3; + try { + var3 = this.stmt.executeUpdate(sql, columnNames); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + this.checkOpen(); + this.incrementExecuteCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + boolean var3; + try { + var3 = this.stmt.execute(sql, autoGeneratedKeys); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final boolean execute(String sql, int[] columnIndexes) throws SQLException { + this.checkOpen(); + this.incrementExecuteCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + boolean var3; + try { + var3 = this.stmt.execute(sql, columnIndexes); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final boolean execute(String sql, String[] columnNames) throws SQLException { + this.checkOpen(); + this.incrementExecuteCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + boolean var3; + try { + var3 = this.stmt.execute(sql, columnNames); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + + public int getMaxFieldSize() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getMaxFieldSize(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public void close() throws SQLException { + if (!this.closed) { + this.clearResultSet(); + if (this.stmt != null) { + this.stmt.close(); + } + + this.closed = true; + DruidConnectionHolder connHolder = this.conn.getConnectionHolder(); + if (connHolder != null) { + connHolder.removeTrace(this); + } + + } + } + + public void setMaxFieldSize(int max) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setMaxFieldSize(max); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getMaxRows() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getMaxRows(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public void setMaxRows(int max) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setMaxRows(max); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final void setEscapeProcessing(boolean enable) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setEscapeProcessing(enable); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getQueryTimeout() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getQueryTimeout(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public void setQueryTimeout(int seconds) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setQueryTimeout(seconds); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final void cancel() throws SQLException { + this.checkOpen(); + + try { + this.stmt.cancel(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final SQLWarning getWarnings() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getWarnings(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final void clearWarnings() throws SQLException { + this.checkOpen(); + + try { + this.stmt.clearWarnings(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final void setCursorName(String name) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setCursorName(name); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + @Override + public final boolean execute(String sql) throws SQLException { + checkOpen(); + + incrementExecuteCount(); + transactionRecord(sql); + + try { + if (StringUtils.isNotEmpty(sql)){ + sql = sql.replace("TRUE", "1"); + sql = sql.replace("FALSE", "0"); + } + return stmt.execute(sql); + } catch (Throwable t) { + errorCheck(t); + throw checkException(t, sql); + } + } + + public final ResultSet getResultSet() throws SQLException { + this.checkOpen(); + + try { + ResultSet rs = this.stmt.getResultSet(); + if (rs == null) { + return null; + } else { + DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs); + this.addResultSetTrace(poolableResultSet); + return poolableResultSet; + } + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getUpdateCount() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getUpdateCount(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final boolean getMoreResults() throws SQLException { + this.checkOpen(); + + try { + boolean moreResults = this.stmt.getMoreResults(); + if (this.resultSetTrace != null && this.resultSetTrace.size() > 0) { + ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(this.resultSetTrace.size() - 1); + if (lastResultSet instanceof DruidPooledResultSet) { + DruidPooledResultSet pooledResultSet = (DruidPooledResultSet)lastResultSet; + pooledResultSet.closed = true; + } + } + + return moreResults; + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public void setFetchDirection(int direction) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setFetchDirection(direction); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getFetchDirection() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getFetchDirection(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public void setFetchSize(int rows) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setFetchSize(rows); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getFetchSize() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getFetchSize(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getResultSetConcurrency() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getResultSetConcurrency(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getResultSetType() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getResultSetType(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final void addBatch(String sql) throws SQLException { + this.checkOpen(); + this.transactionRecord(sql); + + try { + this.stmt.addBatch(sql); + } catch (Throwable t) { + throw this.checkException(t, sql); + } + } + + public final void clearBatch() throws SQLException { + if (!this.closed) { + try { + this.stmt.clearBatch(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + } + + public int[] executeBatch() throws SQLException { + this.checkOpen(); + this.incrementExecuteBatchCount(); + this.conn.beforeExecute(); + + int[] var1; + try { + var1 = this.stmt.executeBatch(); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t); + } finally { + this.conn.afterExecute(); + } + + return var1; + } + + public final Connection getConnection() throws SQLException { + this.checkOpen(); + return this.conn; + } + + public final boolean getMoreResults(int current) throws SQLException { + this.checkOpen(); + + try { + boolean results = this.stmt.getMoreResults(current); + if (this.resultSetTrace != null && this.resultSetTrace.size() > 0) { + ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(this.resultSetTrace.size() - 1); + if (lastResultSet instanceof DruidPooledResultSet) { + DruidPooledResultSet pooledResultSet = (DruidPooledResultSet)lastResultSet; + pooledResultSet.closed = true; + } + } + + return results; + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final ResultSet getGeneratedKeys() throws SQLException { + this.checkOpen(); + + try { + ResultSet rs = this.stmt.getGeneratedKeys(); + DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs); + this.addResultSetTrace(poolableResultSet); + return poolableResultSet; + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getResultSetHoldability() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getResultSetHoldability(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final boolean isClosed() throws SQLException { + return this.closed; + } + + public final void setPoolable(boolean poolable) throws SQLException { + if (!poolable) { + throw new SQLException("not support"); + } + } + + public final boolean isPoolable() throws SQLException { + return false; + } + + public String toString() { + return this.stmt.toString(); + } + + public void closeOnCompletion() throws SQLException { + this.stmt.closeOnCompletion(); + } + + public boolean isCloseOnCompletion() throws SQLException { + return this.stmt.isCloseOnCompletion(); + } +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java new file mode 100644 index 0000000..f9ce039 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java @@ -0,0 +1,30 @@ +package com.zt.plat.module.bpm; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 项目的启动类 + * + * 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + * 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + * 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + * + * @author ZT + */ +@SpringBootApplication +public class BpmServerApplication { + + public static void main(String[] args) { + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + + SpringApplication.run(BpmServerApplication.class, args); + + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java new file mode 100644 index 0000000..7c3b4c4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java @@ -0,0 +1,4 @@ +/** + * bpm API 实现类,定义暴露给其它模块的 API + */ +package com.zt.plat.module.bpm.api; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java new file mode 100644 index 0000000..261b750 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.api.task; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +/** + * Flowable 流程实例 Api 实现类 + * + * @author ZT + * @author jason + */ +@RestController +@Validated +public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public CommonResult createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) { + return success(processInstanceService.createProcessInstance(userId, reqDTO)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java new file mode 100644 index 0000000..b0c466b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java @@ -0,0 +1,15 @@ +package com.zt.plat.module.bpm.controller.admin.base.dept; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "部门精简信息 VO") +@Data +public class DeptSimpleBaseVO { + + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术部") + private String name; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java new file mode 100644 index 0000000..141c17f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java @@ -0,0 +1,4 @@ +/** + * 基础包,放一些通用的 VO 类 + */ +package com.zt.plat.module.bpm.controller.admin.base; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java new file mode 100644 index 0000000..6fcbb96 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java @@ -0,0 +1,22 @@ +package com.zt.plat.module.bpm.controller.admin.base.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户精简信息 VO") +@Data +public class UserSimpleBaseVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String nickname; + @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png") + private String avatar; + + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long deptId; + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部") + private String deptName; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java new file mode 100644 index 0000000..b318b4b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java @@ -0,0 +1,95 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.service.definition.BpmCategoryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Comparator; +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - BPM 流程分类") +@RestController +@RequestMapping("/bpm/category") +@Validated +public class BpmCategoryController { + + @Resource + private BpmCategoryService categoryService; + + @PostMapping("/create") + @Operation(summary = "创建流程分类") + @PreAuthorize("@ss.hasPermission('bpm:category:create')") + public CommonResult createCategory(@Valid @RequestBody BpmCategorySaveReqVO createReqVO) { + return success(categoryService.createCategory(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新流程分类") + @PreAuthorize("@ss.hasPermission('bpm:category:update')") + public CommonResult updateCategory(@Valid @RequestBody BpmCategorySaveReqVO updateReqVO) { + categoryService.updateCategory(updateReqVO); + return success(true); + } + + @PutMapping("/update-sort-batch") + @Operation(summary = "批量更新流程分类的排序") + @Parameter(name = "ids", description = "分类编号列表", required = true, example = "1,2,3") + @PreAuthorize("@ss.hasPermission('bpm:category:update')") + public CommonResult updateCategorySortBatch(@RequestParam("ids") List ids) { + categoryService.updateCategorySortBatch(ids); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除流程分类") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:category:delete')") + public CommonResult deleteCategory(@RequestParam("id") Long id) { + categoryService.deleteCategory(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得流程分类") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:category:query')") + public CommonResult getCategory(@RequestParam("id") Long id) { + BpmCategoryDO category = categoryService.getCategory(id); + return success(BeanUtils.toBean(category, BpmCategoryRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得流程分类分页") + @PreAuthorize("@ss.hasPermission('bpm:category:query')") + public CommonResult> getCategoryPage(@Valid BpmCategoryPageReqVO pageReqVO) { + PageResult pageResult = categoryService.getCategoryPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, BpmCategoryRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获取流程分类的精简信息列表", description = "只包含被开启的分类,主要用于前端的下拉选项") + public CommonResult> getCategorySimpleList() { + List list = categoryService.getCategoryListByStatus(CommonStatusEnum.ENABLE.getStatus()); + list.sort(Comparator.comparingInt(BpmCategoryDO::getSort)); + return success(convertList(list, category -> new BpmCategoryRespVO().setId(category.getId()) + .setName(category.getName()).setCode(category.getCode()))); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java new file mode 100644 index 0000000..fa47346 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java @@ -0,0 +1,83 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - 动态表单") +@RestController +@RequestMapping("/bpm/form") +@Validated +public class BpmFormController { + + @Resource + private BpmFormService formService; + + @PostMapping("/create") + @Operation(summary = "创建动态表单") + @PreAuthorize("@ss.hasPermission('bpm:form:create')") + public CommonResult createForm(@Valid @RequestBody BpmFormSaveReqVO createReqVO) { + return success(formService.createForm(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新动态表单") + @PreAuthorize("@ss.hasPermission('bpm:form:update')") + public CommonResult updateForm(@Valid @RequestBody BpmFormSaveReqVO updateReqVO) { + formService.updateForm(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除动态表单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:form:delete')") + public CommonResult deleteForm(@RequestParam("id") Long id) { + formService.deleteForm(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得动态表单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:form:query')") + public CommonResult getForm(@RequestParam("id") Long id) { + BpmFormDO form = formService.getForm(id); + return success(BeanUtils.toBean(form, BpmFormRespVO.class)); + } + + @GetMapping({"/list-all-simple", "/simple-list"}) + @Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框") + public CommonResult> getFormSimpleList() { + List list = formService.getFormList(); + return success(convertList(list, formDO -> // 只返回 id、name 字段 + new BpmFormRespVO().setId(formDO.getId()).setName(formDO.getName()))); + } + + @GetMapping("/page") + @Operation(summary = "获得动态表单分页") + @PreAuthorize("@ss.hasPermission('bpm:form:query')") + public CommonResult> getFormPage(@Valid BpmFormPageReqVO pageVO) { + PageResult pageResult = formService.getFormPage(pageVO); + return success(BeanUtils.toBean(pageResult, BpmFormRespVO.class)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java new file mode 100644 index 0000000..f787066 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java @@ -0,0 +1,200 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.*; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO; +import com.zt.plat.module.bpm.convert.definition.BpmModelConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.service.definition.BpmCategoryService; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import com.zt.plat.module.bpm.service.definition.BpmModelService; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 流程模型") +@RestController +@RequestMapping("/bpm/model") +@Validated +public class BpmModelController { + + @Resource + private BpmModelService modelService; + @Resource + private BpmFormService formService; + @Resource + private BpmCategoryService categoryService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @GetMapping("/list") + @Operation(summary = "获得模型分页") + @Parameter(name = "name", description = "模型名称", example = "芋艿") + public CommonResult> getModelList(@RequestParam(value = "name", required = false) String name) { + List list = modelService.getModelList(name); + if (CollUtil.isEmpty(list)) { + return success(Collections.emptyList()); + } + + // 获得 Form 表单 + Set formIds = convertSet(list, model -> { + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); + return metaInfo != null ? metaInfo.getFormId() : null; + }); + Map formMap = formService.getFormMap(formIds); + // 获得 Category Map + Map categoryMap = categoryService.getCategoryMap( + convertSet(list, Model::getCategory)); + // 获得 Deployment Map + Map deploymentMap = processDefinitionService.getDeploymentMap( + convertSet(list, Model::getDeploymentId)); + // 获得 ProcessDefinition Map + List processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds( + deploymentMap.keySet()); + Map processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId); + // 获得 User Map、Dept Map + Set userIds = convertSetByFlatMap(list, model -> { + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); + return metaInfo != null ? metaInfo.getStartUserIds().stream() : Stream.empty(); + }); + Map userMap = adminUserApi.getUserMap(userIds); + Set deptIds = convertSetByFlatMap(list, model -> { + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); + return metaInfo != null && metaInfo.getStartDeptIds() != null ? metaInfo.getStartDeptIds().stream() : Stream.empty(); + }); + Map deptMap = deptApi.getDeptMap(deptIds); + return success(BpmModelConvert.INSTANCE.buildModelList(list, + formMap, categoryMap, deploymentMap, processDefinitionMap, userMap, deptMap)); + } + + @GetMapping("/get") + @Operation(summary = "获得模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:model:query')") + public CommonResult getModel(@RequestParam("id") String id) { + Model model = modelService.getModel(id); + if (model == null) { + return null; + } + byte[] bpmnBytes = modelService.getModelBpmnXML(id); + BpmSimpleModelNodeVO simpleModel = modelService.getSimpleModel(id); + return success(BpmModelConvert.INSTANCE.buildModel(model, bpmnBytes, simpleModel)); + } + + @PostMapping("/create") + @Operation(summary = "新建模型") + @PreAuthorize("@ss.hasPermission('bpm:model:create')") + public CommonResult createModel(@Valid @RequestBody BpmModelSaveReqVO createRetVO) { + return success(modelService.createModel(createRetVO)); + } + + @PutMapping("/update") + @Operation(summary = "修改模型") + @PreAuthorize("@ss.hasPermission('bpm:model:update')") + public CommonResult updateModel(@Valid @RequestBody BpmModelSaveReqVO modelVO) { + modelService.updateModel(getLoginUserId(), modelVO); + return success(true); + } + + @PutMapping("/update-sort-batch") + @Operation(summary = "批量修改模型排序") + @Parameter(name = "ids", description = "编号数组", required = true, example = "1,2,3") + public CommonResult updateModelSortBatch(@RequestParam("ids") List ids) { + modelService.updateModelSortBatch(getLoginUserId(), ids); + return success(true); + } + + @PostMapping("/deploy") + @Operation(summary = "部署模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:model:deploy')") + public CommonResult deployModel(@RequestParam("id") String id) { + modelService.deployModel(getLoginUserId(), id); + return success(true); + } + + @PutMapping("/update-state") + @Operation(summary = "修改模型的状态", description = "实际更新的部署的流程定义的状态") + @PreAuthorize("@ss.hasPermission('bpm:model:update')") + public CommonResult updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) { + modelService.updateModelState(getLoginUserId(), reqVO.getId(), reqVO.getState()); + return success(true); + } + + @Deprecated + @PutMapping("/update-bpmn") + @Operation(summary = "修改模型的 BPMN") + @PreAuthorize("@ss.hasPermission('bpm:model:update')") + public CommonResult updateModelBpmn(@Valid @RequestBody BpmModeUpdateBpmnReqVO reqVO) { + modelService.updateModelBpmnXml(reqVO.getId(), reqVO.getBpmnXml()); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:model:delete')") + public CommonResult deleteModel(@RequestParam("id") String id) { + modelService.deleteModel(getLoginUserId(), id); + return success(true); + } + + @DeleteMapping("/clean") + @Operation(summary = "清理模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:model:clean')") + public CommonResult cleanModel(@RequestParam("id") String id) { + modelService.cleanModel(getLoginUserId(), id); + return success(true); + } + + // ========== 仿钉钉/飞书的精简模型 ========= + + @GetMapping("/simple/get") + @Operation(summary = "获得仿钉钉流程设计模型") + @Parameter(name = "modelId", description = "流程模型编号", required = true, example = "a2c5eee0-eb6c-11ee-abf4-0c37967c420a") + public CommonResult getSimpleModel(@RequestParam("id") String modelId){ + return success(modelService.getSimpleModel(modelId)); + } + + @Deprecated + @PostMapping("/simple/update") + @Operation(summary = "保存仿钉钉流程设计模型") + @PreAuthorize("@ss.hasPermission('bpm:model:update')") + public CommonResult updateSimpleModel(@Valid @RequestBody BpmSimpleModelUpdateReqVO reqVO) { + modelService.updateSimpleModel(getLoginUserId(), reqVO); + return success(Boolean.TRUE); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java new file mode 100644 index 0000000..e5ee29a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java @@ -0,0 +1,133 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.zt.plat.module.bpm.convert.definition.BpmProcessDefinitionConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.service.definition.BpmCategoryService; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; +import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 流程定义") +@RestController +@RequestMapping("/bpm/process-definition") +@Validated +public class BpmProcessDefinitionController { + + @Resource + private BpmProcessDefinitionService processDefinitionService; + @Resource + private BpmFormService formService; + @Resource + private BpmCategoryService categoryService; + + @GetMapping("/page") + @Operation(summary = "获得流程定义分页") + @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')") + public CommonResult> getProcessDefinitionPage( + BpmProcessDefinitionPageReqVO pageReqVO) { + PageResult pageResult = processDefinitionService.getProcessDefinitionPage(pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 获得 Category Map + Map categoryMap = categoryService.getCategoryMap( + convertSet(pageResult.getList(), ProcessDefinition::getCategory)); + // 获得 Deployment Map + Map deploymentMap = processDefinitionService.getDeploymentMap( + convertSet(pageResult.getList(), ProcessDefinition::getDeploymentId)); + // 获得 BpmProcessDefinitionInfoDO Map + Map processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), ProcessDefinition::getId)); + // 获得 Form Map + Map formMap = formService.getFormMap( + convertSet(processDefinitionMap.values(), BpmProcessDefinitionInfoDO::getFormId)); + return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionPage( + pageResult, deploymentMap, processDefinitionMap, formMap, categoryMap)); + } + + @GetMapping ("/list") + @Operation(summary = "获得流程定义列表") + @Parameter(name = "suspensionState", description = "挂起状态", required = true, example = "1") // 参见 Flowable SuspensionState 枚举 + public CommonResult> getProcessDefinitionList( + @RequestParam("suspensionState") Integer suspensionState) { + // 1.1 获得开启的流程定义 + List list = processDefinitionService.getProcessDefinitionListBySuspensionState(suspensionState); + if (CollUtil.isEmpty(list)) { + return success(Collections.emptyList()); + } + // 1.2 移除不可见的流程定义 + Map processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(list, ProcessDefinition::getId)); + Long userId = getLoginUserId(); + list.removeIf(processDefinition -> { + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionMap.get(processDefinition.getId()); + return processDefinitionInfo == null // 不存在 + || Boolean.FALSE.equals(processDefinitionInfo.getVisible()) // visible 不可见 + || !processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId); // 无权限发起 + }); + + // 2. 拼接 VO 返回 + return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionList( + list, null, processDefinitionMap, null, null)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得流程定义精简列表", description = "只包含未挂起的流程,主要用于前端的下拉选项") + public CommonResult> getSimpleProcessDefinitionList() { + // 只查询未挂起的流程 + List list = processDefinitionService.getProcessDefinitionListBySuspensionState( + SuspensionState.ACTIVE.getStateCode()); + // 拼接 VO 返回,只返回 id、name、key + return success(convertList(list, definition -> new BpmProcessDefinitionRespVO() + .setId(definition.getId()).setName(definition.getName()).setKey(definition.getKey()))); + } + + @GetMapping ("/get") + @Operation(summary = "获得流程定义") + @Parameter(name = "id", description = "流程编号", required = true, example = "1024") + @Parameter(name = "key", description = "流程定义标识", required = true, example = "1024") + public CommonResult getProcessDefinition( + @RequestParam(value = "id", required = false) String id, + @RequestParam(value = "key", required = false) String key) { + ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id) + : processDefinitionService.getActiveProcessDefinition(key); + if (processDefinition == null) { + return success(null); + } + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId()); + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId()); + return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition( + processDefinition, null, processDefinitionInfo, null, null, bpmnModel)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java new file mode 100644 index 0000000..08fcb42 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; +import com.zt.plat.module.bpm.service.definition.BpmProcessExpressionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - BPM 流程表达式") +@RestController +@RequestMapping("/bpm/process-expression") +@Validated +public class BpmProcessExpressionController { + + @Resource + private BpmProcessExpressionService processExpressionService; + + @PostMapping("/create") + @Operation(summary = "创建流程表达式") + @PreAuthorize("@ss.hasPermission('bpm:process-expression:create')") + public CommonResult createProcessExpression(@Valid @RequestBody BpmProcessExpressionSaveReqVO createReqVO) { + return success(processExpressionService.createProcessExpression(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新流程表达式") + @PreAuthorize("@ss.hasPermission('bpm:process-expression:update')") + public CommonResult updateProcessExpression(@Valid @RequestBody BpmProcessExpressionSaveReqVO updateReqVO) { + processExpressionService.updateProcessExpression(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除流程表达式") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-expression:delete')") + public CommonResult deleteProcessExpression(@RequestParam("id") Long id) { + processExpressionService.deleteProcessExpression(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得流程表达式") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:process-expression:query')") + public CommonResult getProcessExpression(@RequestParam("id") Long id) { + BpmProcessExpressionDO processExpression = processExpressionService.getProcessExpression(id); + return success(BeanUtils.toBean(processExpression, BpmProcessExpressionRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得流程表达式分页") + @PreAuthorize("@ss.hasPermission('bpm:process-expression:query')") + public CommonResult> getProcessExpressionPage( + @Valid BpmProcessExpressionPageReqVO pageReqVO) { + PageResult pageResult = processExpressionService.getProcessExpressionPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, BpmProcessExpressionRespVO.class)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java new file mode 100644 index 0000000..3077a91 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; +import com.zt.plat.module.bpm.service.definition.BpmProcessListenerService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - BPM 流程监听器") +@RestController +@RequestMapping("/bpm/process-listener") +@Validated +public class BpmProcessListenerController { + + @Resource + private BpmProcessListenerService processListenerService; + + @PostMapping("/create") + @Operation(summary = "创建流程监听器") + @PreAuthorize("@ss.hasPermission('bpm:process-listener:create')") + public CommonResult createProcessListener(@Valid @RequestBody BpmProcessListenerSaveReqVO createReqVO) { + return success(processListenerService.createProcessListener(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新流程监听器") + @PreAuthorize("@ss.hasPermission('bpm:process-listener:update')") + public CommonResult updateProcessListener(@Valid @RequestBody BpmProcessListenerSaveReqVO updateReqVO) { + processListenerService.updateProcessListener(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除流程监听器") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-listener:delete')") + public CommonResult deleteProcessListener(@RequestParam("id") Long id) { + processListenerService.deleteProcessListener(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得流程监听器") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:process-listener:query')") + public CommonResult getProcessListener(@RequestParam("id") Long id) { + BpmProcessListenerDO processListener = processListenerService.getProcessListener(id); + return success(BeanUtils.toBean(processListener, BpmProcessListenerRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得流程监听器分页") + @PreAuthorize("@ss.hasPermission('bpm:process-listener:query')") + public CommonResult> getProcessListenerPage( + @Valid BpmProcessListenerPageReqVO pageReqVO) { + PageResult pageResult = processListenerService.getProcessListenerPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, BpmProcessListenerRespVO.class)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java new file mode 100644 index 0000000..226e5da --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java @@ -0,0 +1,83 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import com.zt.plat.module.bpm.service.definition.BpmUserGroupService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - 用户组") +@RestController +@RequestMapping("/bpm/user-group") +@Validated +public class BpmUserGroupController { + + @Resource + private BpmUserGroupService userGroupService; + + @PostMapping("/create") + @Operation(summary = "创建用户组") + @PreAuthorize("@ss.hasPermission('bpm:user-group:create')") + public CommonResult createUserGroup(@Valid @RequestBody BpmUserGroupSaveReqVO createReqVO) { + return success(userGroupService.createUserGroup(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新用户组") + @PreAuthorize("@ss.hasPermission('bpm:user-group:update')") + public CommonResult updateUserGroup(@Valid @RequestBody BpmUserGroupSaveReqVO updateReqVO) { + userGroupService.updateUserGroup(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除用户组") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:user-group:delete')") + public CommonResult deleteUserGroup(@RequestParam("id") Long id) { + userGroupService.deleteUserGroup(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得用户组") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:user-group:query')") + public CommonResult getUserGroup(@RequestParam("id") Long id) { + BpmUserGroupDO userGroup = userGroupService.getUserGroup(id); + return success(BeanUtils.toBean(userGroup, BpmUserGroupRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得用户组分页") + @PreAuthorize("@ss.hasPermission('bpm:user-group:query')") + public CommonResult> getUserGroupPage(@Valid BpmUserGroupPageReqVO pageVO) { + PageResult pageResult = userGroupService.getUserGroupPage(pageVO); + return success(BeanUtils.toBean(pageResult, BpmUserGroupRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获取用户组精简信息列表", description = "只包含被开启的用户组,主要用于前端的下拉选项") + public CommonResult> getUserGroupSimpleList() { + List list = userGroupService.getUserGroupListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, group -> new BpmUserGroupRespVO().setId(group.getId()).setName(group.getName()))); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java new file mode 100644 index 0000000..5de14e2 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.category; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - BPM 流程分类分页 Request VO") +@Data +public class BpmCategoryPageReqVO extends PageParam { + + @Schema(description = "分类名", example = "王五") + private String name; + + @Schema(description = "分类标志", example = "OA") + private String code; + + @Schema(description = "分类状态", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java new file mode 100644 index 0000000..5f3db13 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.category; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - BPM 流程分类 Response VO") +@Data +public class BpmCategoryRespVO { + + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") + private Long id; + + @Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + private String name; + + @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") + private String code; + + @Schema(description = "分类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") + private String description; + + @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED) + private Integer sort; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java new file mode 100644 index 0000000..0203453 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java @@ -0,0 +1,37 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.category; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - BPM 流程分类新增/修改 Request VO") +@Data +public class BpmCategorySaveReqVO { + + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") + private Long id; + + @Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @NotEmpty(message = "分类名不能为空") + private String name; + + @Schema(description = "分类描述", example = "你猜") + private String description; + + @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") + @NotEmpty(message = "分类标志不能为空") + private String code; + + @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "分类状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "分类排序不能为空") + private Integer sort; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java new file mode 100644 index 0000000..1d3bbed --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.expression; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - BPM 流程表达式分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmProcessExpressionPageReqVO extends PageParam { + + @Schema(description = "表达式名字", example = "李四") + private String name; + + @Schema(description = "表达式状态", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java new file mode 100644 index 0000000..2bb959b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java @@ -0,0 +1,30 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.expression; + +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - BPM 流程表达式 Response VO") +@Data +public class BpmProcessExpressionRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3870") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "表达式名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @ExcelProperty("表达式名字") + private String name; + + @Schema(description = "表达式状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "表达式", requiredMode = Schema.RequiredMode.REQUIRED) + private String expression; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java new file mode 100644 index 0000000..a5771d1 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.expression; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - BPM 流程表达式新增/修改 Request VO") +@Data +public class BpmProcessExpressionSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3870") + private Long id; + + @Schema(description = "表达式名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @NotEmpty(message = "表达式名字不能为空") + private String name; + + @Schema(description = "表达式状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "表达式状态不能为空") + private Integer status; + + @Schema(description = "表达式", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "表达式不能为空") + private String expression; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java new file mode 100644 index 0000000..53af985 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java @@ -0,0 +1,24 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.form; + +import lombok.Data; + +/** + * 流程表单字段 VO + */ +@Data +public class BpmFormFieldVO { + + /** + * 字段类型 + */ + private String type; + /** + * 字段标识 + */ + private String field; + /** + * 字段标题 + */ + private String title; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java new file mode 100644 index 0000000..437f67c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java @@ -0,0 +1,14 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.form; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 动态表单分页 Request VO") +@Data +public class BpmFormPageReqVO extends PageParam { + + @Schema(description = "表单名称", example = "芋道") + private String name; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java new file mode 100644 index 0000000..42295df --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java @@ -0,0 +1,39 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 动态表单 Response VO") +@Data +public class BpmFormRespVO { + + @Schema(description = "表单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @NotNull(message = "表单名称不能为空") + private String name; + + @Schema(description = "表单的配置-JSON 字符串", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "表单的配置不能为空") + private String conf; + + @Schema(description = "表单项的数组-JSON 字符串的数组", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "表单项的数组不能为空") + private List fields; + + @Schema(description = "表单状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "表单状态不能为空") + private Integer status; // 参见 CommonStatusEnum 枚举 + + @Schema(description = "备注", example = "我是备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java new file mode 100644 index 0000000..faa420b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java @@ -0,0 +1,35 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 动态表单创建/更新 Request VO") +@Data +public class BpmFormSaveReqVO { + + @Schema(description = "表单编号", example = "1024") + private Long id; + + @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @NotNull(message = "表单名称不能为空") + private String name; + + @Schema(description = "表单的配置-JSON 字符串", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "表单的配置不能为空") + private String conf; + + @Schema(description = "表单项的数组-JSON 字符串的数组", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "表单项的数组不能为空") + private List fields; + + @Schema(description = "表单状态-参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "表单状态不能为空") + private Integer status; + + @Schema(description = "备注", example = "我是备注") + private String remark; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java new file mode 100644 index 0000000..a146e35 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java @@ -0,0 +1,28 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.group; + +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.util.date.DateUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 用户组分页 Request VO") +@Data +public class BpmUserGroupPageReqVO extends PageParam { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "组名", example = "芋道") + private String name; + + @Schema(description = "状态", example = "1") + private Integer status; + + @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java new file mode 100644 index 0000000..b356351 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.group; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Set; + +@Schema(description = "管理后台 - 用户组 Response VO") +@Data +public class BpmUserGroupRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + private String description; + + @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") + private Set userIds; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java new file mode 100644 index 0000000..ce2c7d9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.group; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.Set; + +@Schema(description = "管理后台 - 用户组创建/修改 Request VO") +@Data +public class BpmUserGroupSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @NotNull(message = "组名不能为空") + private String name; + + @Schema(description = "描述", example = "芋道源码") + private String description; + + @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") + @NotNull(message = "成员编号数组不能为空") + private Set userIds; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + private Integer status; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java new file mode 100644 index 0000000..13b8a73 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java @@ -0,0 +1,30 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.listener; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - BPM 流程监听器分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmProcessListenerPageReqVO extends PageParam { + + @Schema(description = "监听器名字", example = "赵六") + private String name; + + @Schema(description = "监听器类型", example = "execution") + private String type; + + @Schema(description = "监听事件", example = "start") + private String event; + + @Schema(description = "状态", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java new file mode 100644 index 0000000..7bfdde8 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.listener; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - BPM 流程监听器 Response VO") +@Data +public class BpmProcessListenerRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13089") + private Long id; + + @Schema(description = "监听器名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + private String name; + + @Schema(description = "监听器类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "execution") + private String type; + + @Schema(description = "监听器状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "监听事件", requiredMode = Schema.RequiredMode.REQUIRED, example = "start") + private String event; + + @Schema(description = "监听器值类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "class") + private String valueType; + + @Schema(description = "监听器值", requiredMode = Schema.RequiredMode.REQUIRED) + private String value; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java new file mode 100644 index 0000000..908caa9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java @@ -0,0 +1,39 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.listener; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - BPM 流程监听器新增/修改 Request VO") +@Data +public class BpmProcessListenerSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13089") + private Long id; + + @Schema(description = "监听器名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @NotEmpty(message = "监听器名字不能为空") + private String name; + + @Schema(description = "监听器类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "execution") + @NotEmpty(message = "监听器类型不能为空") + private String type; + + @Schema(description = "监听器状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "监听器状态不能为空") + private Integer status; + + @Schema(description = "监听事件", requiredMode = Schema.RequiredMode.REQUIRED, example = "start") + @NotEmpty(message = "监听事件不能为空") + private String event; + + @Schema(description = "监听器值类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "class") + @NotEmpty(message = "监听器值类型不能为空") + private String valueType; + + @Schema(description = "监听器值", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "监听器值不能为空") + private String value; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java new file mode 100644 index 0000000..03e0e5f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java @@ -0,0 +1,19 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 流程模型的更新 BPMN XML Request VO") +@Data +public class BpmModeUpdateBpmnReqVO { + + @Schema(description = "流程编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "流程编号不能为空") + private String id; + + @Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "BPMN XML 不能为空") + private String bpmnXml; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java new file mode 100644 index 0000000..cabc1fd --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -0,0 +1,180 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model; + +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.framework.common.validation.InEnum; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.enums.definition.BpmAutoApproveTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import java.util.List; + +/** + * BPM 流程 MetaInfo Response DTO + * 主要用于 { Model#setMetaInfo(String)} 的存储 + * + * 最终,它的字段和 + * {@link com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} + * 是一致的 + * + * @author ZT + */ +@Data +public class BpmModelMetaInfoVO { + + @Schema(description = "流程图标", example = "https://www.iocoder.cn/zt.jpg") + @URL(message = "流程图标格式不正确") + private String icon; + + @Schema(description = "流程描述", example = "我是描述") + private String description; + + @Schema(description = "流程类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(BpmModelTypeEnum.class) + @NotNull(message = "流程类型不能为空") + private Integer type; + + @Schema(description = "表单类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(BpmModelFormTypeEnum.class) + @NotNull(message = "表单类型不能为空") + private Integer formType; + @Schema(description = "表单编号", example = "1024") + private Long formId; // formType 为 NORMAL 使用,必须非空 + + @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create") + private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空 + @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view") + private String formCustomViewPath; // 表单类型为 CUSTOM 时,必须非空 + + @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否可见不能为空") + private Boolean visible; + + @Schema(description = "可发起用户编号数组", example = "[1,2,3]") + private List startUserIds; + + @Schema(description = "可发起部门编号数组", example = "[2,4,6]") + private List startDeptIds; + + @Schema(description = "可管理用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2,4,6]") + @NotEmpty(message = "可管理用户编号数组不能为空") + private List managerUserIds; + + @Schema(description = "排序", example = "1") + private Long sort; // 创建时,后端自动生成 + + @Schema(description = "允许撤销审批中的申请", example = "true") + private Boolean allowCancelRunningProcess; + + @Schema(description = "流程 ID 规则", example = "{}") + private ProcessIdRule processIdRule; + + @Schema(description = "自动去重类型", example = "1") + @InEnum(BpmAutoApproveTypeEnum.class) + private Integer autoApprovalType; + + @Schema(description = "标题设置", example = "{}") + private TitleSetting titleSetting; + + @Schema(description = "摘要设置", example = "{}") + private SummarySetting summarySetting; + + @Schema(description = "流程前置通知设置", example = "{}") + private HttpRequestSetting processBeforeTriggerSetting; + + @Schema(description = "流程后置通知设置", example = "{}") + private HttpRequestSetting processAfterTriggerSetting; + + @Schema(description = "任务前置通知设置", example = "{}") + private HttpRequestSetting taskBeforeTriggerSetting; + + @Schema(description = "任务后置通知设置", example = "{}") + private HttpRequestSetting taskAfterTriggerSetting; + + @Schema(description = "流程 ID 规则") + @Data + @Valid + public static class ProcessIdRule { + + @Schema(description = "是否启用", example = "false") + @NotNull(message = "是否启用不能为空") + private Boolean enable; + + @Schema(description = "前缀", example = "XX") + private String prefix; + + @Schema(description = "中缀", example = "20250120") + private String infix; // 精确到日、精确到时、精确到分、精确到秒 + + @Schema(description = "后缀", example = "YY") + private String postfix; + + @Schema(description = "序列长度", example = "5") + @NotNull(message = "序列长度不能为空") + private Integer length; + + } + + @Schema(description = "标题设置") + @Data + @Valid + public static class TitleSetting { + + @Schema(description = "是否自定义", example = "false") + @NotNull(message = "是否自定义不能为空") + private Boolean enable; + + @Schema(description = "标题", example = "流程标题") + private String title; + + } + + @Schema(description = "摘要设置") + @Data + @Valid + public static class SummarySetting { + + @Schema(description = "是否自定义", example = "false") + @NotNull(message = "是否自定义不能为空") + private Boolean enable; + + @Schema(description = "摘要字段数组", example = "[]") + private List summary; + + } + + @Schema(description = "http 请求通知设置", example = "{}") + @Data + public static class HttpRequestSetting { + + @Schema(description = "请求路径", example = "http://127.0.0.1") + @NotEmpty(message = "请求 URL 不能为空") + @URL(message = "请求 URL 格式不正确") + private String url; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List header; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List body; + + /** + * 请求返回处理设置,用于修改流程表单值 + *

+ * key:表示要修改的流程表单字段名(name) + * value:接口返回的字段名 + */ + @Schema(description = "请求返回处理设置", example = "[]") + private List> response; + + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java new file mode 100644 index 0000000..12d6ac1 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java @@ -0,0 +1,57 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model; + +import com.zt.plat.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 流程模型 Response VO") +@Data +public class BpmModelRespVO extends BpmModelMetaInfoVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_zt") + private String key; + + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "流程图标", example = "https://www.iocoder.cn/zt.jpg") + private String icon; + + @Schema(description = "流程分类编号", example = "1") + private String category; + @Schema(description = "流程分类名字", example = "请假") + private String categoryName; + + @Schema(description = "表单名字", example = "请假表单") + private String formName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "可发起的用户数组") + private List startUsers; + + @Schema(description = "可发起的部门数组") + private List startDepts; + + @Schema(description = "BPMN XML") + private String bpmnXml; + + @Schema(description = "仿钉钉流程设计模型对象") + private BpmSimpleModelNodeVO simpleModel; + + /** + * 最新部署的流程定义 + */ + private BpmProcessDefinitionRespVO processDefinition; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java new file mode 100644 index 0000000..279519c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java @@ -0,0 +1,34 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model; + +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 流程模型的保存 Request VO") +@Data +public class BpmModelSaveReqVO extends BpmModelMetaInfoVO { + + @Schema(description = "编号", example = "1024") + private String id; + + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_zt") + @NotEmpty(message = "流程标识不能为空") + private String key; + + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @NotEmpty(message = "流程名称不能为空") + private String name; + + @Schema(description = "流程分类", example = "1") + private String category; + + @Schema(description = "BPMN XML") + private String bpmnXml; + + @Schema(description = "仿钉钉流程设计模型对象") + @Valid + private BpmSimpleModelNodeVO simpleModel; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java new file mode 100644 index 0000000..cd7fea1 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java @@ -0,0 +1,19 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 流程模型更新状态 Request VO") +@Data +public class BpmModelUpdateStateReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "编号不能为空") + private String id; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + private Integer state; // 参见 Flowable SuspensionState 枚举 + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java new file mode 100644 index 0000000..235e062 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -0,0 +1,526 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.framework.common.validation.InEnum; +import com.zt.plat.module.bpm.enums.definition.*; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.flowable.bpmn.model.IOParameter; +import org.hibernate.validator.constraints.URL; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Schema(description = "管理后台 - 仿钉钉流程设计模型节点 VO") +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class BpmSimpleModelNodeVO { + + @Schema(description = "模型节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartEvent_1") + @NotEmpty(message = "模型节点编号不能为空") + private String id; + + @Schema(description = "模型节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "模型节点类型不能为空") + @InEnum(BpmSimpleModelNodeTypeEnum.class) + private Integer type; + + @Schema(description = "模型节点名称", example = "领导审批") + private String name; + + @Schema(description = "节点展示内容", example = "指定成员: 芋道源码") + private String showText; + + @Schema(description = "子节点") + private BpmSimpleModelNodeVO childNode; // 补充说明:在该模型下,子节点有且仅有一个,不会有多个 + + @Schema(description = "候选人策略", example = "30") + @InEnum(BpmTaskCandidateStrategyEnum.class) + private Integer candidateStrategy; // 用于审批,抄送节点 + + @Schema(description = "候选人参数") + private String candidateParam; // 用于审批,抄送节点 + + @Schema(description = "审批节点类型", example = "1") + @InEnum(BpmUserTaskApproveTypeEnum.class) + private Integer approveType; // 用于审批节点 + + @Schema(description = "多人审批方式", example = "1") + @InEnum(BpmUserTaskApproveMethodEnum.class) + private Integer approveMethod; // 用于审批节点 + + @Schema(description = "通过比例", example = "100") + private Integer approveRatio; // 通过比例,当多人审批方式为:多人会签(按通过比例) 需要设置 + + @Schema(description = "表单权限", example = "[]") + private List> fieldsPermission; + + @Schema(description = "操作按钮设置", example = "[]") + private List buttonsSetting; // 用于审批节点 + + @Schema(description = "是否需要签名", example = "false") + private Boolean signEnable; + + @Schema(description = "是否填写审批意见", example = "false") + private Boolean reasonRequire; + + /** + * 审批节点拒绝处理 + */ + private RejectHandler rejectHandler; + + /** + * 审批节点超时处理 + */ + private TimeoutHandler timeoutHandler; + + @Schema(description = "审批节点的审批人与发起人相同时,对应的处理类型", example = "1") + @InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class) + private Integer assignStartUserHandlerType; + + /** + * 空处理策略 + */ + private AssignEmptyHandler assignEmptyHandler; + + /** + * 创建任务监听器 + */ + private ListenerHandler taskCreateListener; + /** + * 指派任务监听器 + */ + private ListenerHandler taskAssignListener; + /** + * 完成任务监听器 + */ + private ListenerHandler taskCompleteListener; + + @Schema(description = "延迟器设置", example = "{}") + private DelaySetting delaySetting; + + @Schema(description = "条件节点") + private List conditionNodes; // 补充说明:有且仅有条件、并行、包容分支会使用 + + /** + * 条件节点设置 + */ + private ConditionSetting conditionSetting; // 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE + + @Schema(description = "路由分支组", example = "[]") + private List routerGroups; + + @Schema(description = "路由分支默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true + @JsonIgnore + private String routerDefaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE + + /** + * 触发器节点设置 + */ + private TriggerSetting triggerSetting; + + @Schema(description = "附加节点 Id", example = "UserTask_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true + @JsonIgnore + private String attachNodeId; // 目前用于触发器节点(HTTP 回调)。需要 UserTask 和 ReceiveTask(附加节点) 来完成 + + /** + * 子流程设置 + */ + private ChildProcessSetting childProcessSetting; + + @Schema(description = "任务监听器") + @Valid + @Data + public static class ListenerHandler { + + @Schema(description = "是否开启任务监听器", example = "false") + @NotNull(message = "是否开启任务监听器不能为空") + private Boolean enable; + + @Schema(description = "请求路径", example = "http://xxxxx") + private String path; + + @Schema(description = "请求头", example = "[]") + private List header; + + @Schema(description = "请求体", example = "[]") + private List body; + + } + + @Schema(description = "HTTP 请求参数设置") + @Data + public static class HttpRequestParam { + + @Schema(description = "值类型", example = "1") + @InEnum(BpmHttpRequestParamTypeEnum.class) + @NotNull(message = "值类型不能为空") + private Integer type; + + @Schema(description = "键", example = "xxx") + @NotEmpty(message = "键不能为空") + private String key; + + @Schema(description = "值", example = "xxx") + @NotEmpty(message = "值不能为空") + private String value; + + } + + @Schema(description = "审批节点拒绝处理策略") + @Data + public static class RejectHandler { + + @Schema(description = "拒绝处理类型", example = "1") + @InEnum(BpmUserTaskRejectHandlerTypeEnum.class) + private Integer type; + + @Schema(description = "任务拒绝后驳回的节点 Id", example = "Activity_1") + private String returnNodeId; + } + + @Schema(description = "审批节点超时处理策略") + @Valid + @Data + public static class TimeoutHandler { + + @Schema(description = "是否开启超时处理", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否开启超时处理不能为空") + private Boolean enable; + + @Schema(description = "任务超时未处理的行为", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "任务超时未处理的行为不能为空") + @InEnum(BpmUserTaskTimeoutHandlerTypeEnum.class) + private Integer type; + + @Schema(description = "超时时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "PT6H") + @NotEmpty(message = "超时时间不能为空") + private String timeDuration; + + @Schema(description = "最大提醒次数", example = "1") + private Integer maxRemindCount; + } + + @Schema(description = "空处理策略") + @Data + @Valid + public static class AssignEmptyHandler { + + @Schema(description = "空处理类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "空处理类型不能为空") + @InEnum(BpmUserTaskAssignEmptyHandlerTypeEnum.class) + private Integer type; + + @Schema(description = "指定人员审批的用户编号数组", example = "1") + private List userIds; + } + + @Schema(description = "操作按钮设置") + @Data + @Valid + public static class OperationButtonSetting { + + // TODO @jason:是不是按钮的标识?id 会和数据库的 id 自增有点模糊,key 标识会更合理一点点哈。 + @Schema(description = "按钮 Id", example = "1") + private Integer id; + + @Schema(description = "显示名称", example = "审批") + private String displayName; + + @Schema(description = "是否启用", example = "true") + private Boolean enable; + } + + @Schema(description = "条件设置") + @Data + @Valid + // 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE + public static class ConditionSetting { + + @Schema(description = "条件类型", example = "1") + @InEnum(BpmSimpleModeConditionTypeEnum.class) + private Integer conditionType; + + @Schema(description = "条件表达式", example = "${day>3}") + private String conditionExpression; + + @Schema(description = "是否默认条件", example = "true") + private Boolean defaultFlow; + + /** + * 条件组 + */ + private ConditionGroups conditionGroups; + } + + @Schema(description = "条件组") + @Data + @Valid + public static class ConditionGroups { + + @Schema(description = "条件组下的条件关系是否为与关系", example = "true") + @NotNull(message = "条件关系不能为空") + private Boolean and; + + @Schema(description = "条件组下的条件", example = "[]") + @NotEmpty(message = "条件不能为空") + private List conditions; + } + + @Schema(description = "条件") + @Data + @Valid + public static class Condition { + + @Schema(description = "条件下的规则关系是否为与关系", example = "true") + @NotNull(message = "规则关系不能为空") + private Boolean and; + + @Schema(description = "条件下的规则", example = "[]") + @NotEmpty(message = "规则不能为空") + private List rules; + } + + @Schema(description = "条件规则") + @Data + @Valid + public static class ConditionRule { + + @Schema(description = "运行符号", example = "==") + @NotEmpty(message = "运行符号不能为空") + private String opCode; + + @Schema(description = "运算符左边的值,例如某个流程变量", example = "startUserId") + @NotEmpty(message = "运算符左边的值不能为空") + private String leftSide; + + @Schema(description = "运算符右边的值", example = "1") + @NotEmpty(message = "运算符右边的值不能为空") + private String rightSide; + } + + @Schema(description = "延迟器") + @Data + @Valid + public static class DelaySetting { + + @Schema(description = "延迟时间类型", example = "1") + @NotNull(message = "延迟时间类型不能为空") + @InEnum(BpmDelayTimerTypeEnum.class) + private Integer delayType; + + @Schema(description = "延迟时间表达式", example = "PT1H,2025-01-01T00:00:00") + @NotEmpty(message = "延迟时间表达式不能为空") + private String delayTime; + } + + @Schema(description = "路由分支") + @Data + @Valid + public static class RouterSetting { + + @Schema(description = "节点 Id", example = "Activity_xxx") // 跳转到该节点 + @NotEmpty(message = "节点 Id 不能为空") + private String nodeId; + + @Schema(description = "条件类型", example = "1") + @InEnum(BpmSimpleModeConditionTypeEnum.class) + @NotNull(message = "条件类型不能为空") + private Integer conditionType; + + @Schema(description = "条件表达式", example = "${day>3}") + private String conditionExpression; + + @Schema(description = "条件组", example = "{}") + private ConditionGroups conditionGroups; + } + + @Schema(description = "触发器节点配置") + @Data + @Valid + public static class TriggerSetting { + + @Schema(description = "触发器类型", example = "1") + @InEnum(BpmTriggerTypeEnum.class) + @NotNull(message = "触发器类型不能为空") + private Integer type; + + /** + * http 请求触发器设置 + */ + @Valid + private HttpRequestTriggerSetting httpRequestSetting; + + /** + * 流程表单触发器设置 + */ + private List formSettings; + + @Schema(description = "http 请求触发器设置", example = "{}") + @Data + public static class HttpRequestTriggerSetting { + + @Schema(description = "请求路径", example = "http://127.0.0.1") + @NotEmpty(message = "请求 URL 不能为空") + @URL(message = "请求 URL 格式不正确") + private String url; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List header; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List body; + + /** + * 请求返回处理设置,用于修改流程表单值 + *

+ * key:表示要修改的流程表单字段名(name) + * value:接口返回的字段名 + */ + @Schema(description = "请求返回处理设置", example = "[]") + private List> response; + + /** + * Http 回调请求,需要指定回调任务 Key,用于回调执行 + */ + @Schema(description = "回调任务 Key", example = "xxx", hidden = true) + private String callbackTaskDefineKey; + + } + + @Schema(description = "流程表单触发器设置", example = "{}") + @Data + public static class FormTriggerSetting { + + @Schema(description = "条件类型", example = "1") + @InEnum(BpmSimpleModeConditionTypeEnum.class) + private Integer conditionType; + + @Schema(description = "条件表达式", example = "${day>3}") + private String conditionExpression; + + @Schema(description = "条件组", example = "{}") + private ConditionGroups conditionGroups; + + @Schema(description = "修改的表单字段", example = "{}") + private Map updateFormFields; + + @Schema(description = "删除表单字段", example = "[]") + private Set deleteFields; + } + } + + @Schema(description = "子流程节点配置") + @Data + @Valid + public static class ChildProcessSetting { + + @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") + @NotEmpty(message = "被调用流程不能为空") + private String calledProcessDefinitionKey; + + @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") + @NotEmpty(message = "被调用流程名称不能为空") + private String calledProcessDefinitionName; + + @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否异步不能为空") + private Boolean async; + + @Schema(description = "输入参数(主->子)", example = "[]") + private List inVariables; + + @Schema(description = "输出参数(子->主)", example = "[]") + private List outVariables; + + @Schema(description = "是否自动跳过子流程发起节点", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否自动跳过子流程发起节点不能为空") + private Boolean skipStartUserNode; + + @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + @NotNull(message = "子流程发起人配置不能为空") + private StartUserSetting startUserSetting; + + @Schema(description = "超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + private TimeoutSetting timeoutSetting; + + @Schema(description = "多实例设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + private MultiInstanceSetting multiInstanceSetting; + + @Schema(description = "子流程发起人配置") + @Data + @Valid + public static class StartUserSetting { + + @Schema(description = "子流程发起人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "子流程发起人类型") + @InEnum(BpmChildProcessStartUserTypeEnum.class) + private Integer type; + + @Schema(description = "表单", example = "xxx") + private String formField; + + @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "当子流程发起人为空时类型不能为空") + @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class) + private Integer emptyType; + + } + + @Schema(description = "超时设置") + @Data + @Valid + public static class TimeoutSetting { + + @Schema(description = "是否开启超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否开启超时设置不能为空") + private Boolean enable; + + @Schema(description = "时间类型", example = "1") + @InEnum(BpmDelayTimerTypeEnum.class) + private Integer type; + + @Schema(description = "时间表达式", example = "PT1H,2025-01-01T00:00:00") + private String timeExpression; + + } + + @Schema(description = "多实例设置") + @Data + @Valid + public static class MultiInstanceSetting { + + @Schema(description = "是否开启多实例", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否开启多实例不能为空") + private Boolean enable; + + @Schema(description = "是否串行", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否串行不能为空") + private Boolean sequential; + + @Schema(description = "完成比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "完成比例不能为空") + private Integer approveRatio; + + @Schema(description = "多实例来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "多实例来源类型不能为空") + @InEnum(BpmChildProcessMultiInstanceSourceTypeEnum.class) + private Integer sourceType; + + @Schema(description = "多实例来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "多实例来源不能为空") + private String source; + + } + + } +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java new file mode 100644 index 0000000..43981ba --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +// TODO @jason:需要考虑,如果某个节点的配置不正确,需要有提示;具体怎么实现,可以讨论下; +@Schema(description = "管理后台 - 仿钉钉流程设计模型的新增/修改 Request VO") +@Data +public class BpmSimpleModelUpdateReqVO { + + @Schema(description = "流程模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotEmpty(message = "流程模型编号不能为空") + private String id; // 对应 Flowable act_re_model 表 ID_ 字段 + + @Schema(description = "仿钉钉流程设计模型对象", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "仿钉钉流程设计模型对象不能为空") + @Valid + private BpmSimpleModelNodeVO simpleModel; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java new file mode 100644 index 0000000..8c61e28 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java @@ -0,0 +1,14 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.process; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 流程定义分页 Request VO") +@Data +public class BpmProcessDefinitionPageReqVO extends PageParam { + + @Schema(description = "标识-精准匹配", example = "process1641042089407") + private String key; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java new file mode 100644 index 0000000..5e65c0f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -0,0 +1,71 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.process; + +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 流程定义 Response VO") +@Data +public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer version; + + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao") + private String key; + + @Schema(description = "流程分类", example = "1") + private String category; + @Schema(description = "流程分类名字", example = "请假") + private String categoryName; + + @Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer modelType; // 参见 BpmModelTypeEnum 枚举类 + + @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") + private String modelId; + + @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private String formConf; + @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private List formFields; + @Schema(description = "表单名字", example = "请假表单") + private String formName; + + @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer suspensionState; // 参见 SuspensionState 枚举 + + @Schema(description = "部署时间") + private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回 + + @Schema(description = "BPMN XML") + private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回 + + @Schema(description = "SIMPLE 设计器模型数据 json 格式") + private String simpleModel; // 非必须返回 + + @Schema(description = "流程定义排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long sort; + + @Schema(description = "BPMN UserTask 用户任务") + @Data + public static class UserTask { + + @Schema(description = "任务标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "sudo") + private String id; + + @Schema(description = "任务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + private String name; + + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http new file mode 100644 index 0000000..96bbf96 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http @@ -0,0 +1,12 @@ +### 请求 /bpm/oa/leave/create 接口 => 成功 +POST {{baseUrl}}/bpm/oa/leave/create +Content-Type: application/json +tenant-id: 1 +Authorization: Bearer {{token}} + +{ + "startTime": "2022-03-01", + "endTime": "2022-03-05", + "type": 1, + "reason": "我要请假啦啦啦!" +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java new file mode 100644 index 0000000..51d5094 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java @@ -0,0 +1,62 @@ +package com.zt.plat.module.bpm.controller.admin.oa; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveRespVO; +import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; +import com.zt.plat.module.bpm.service.oa.BpmOALeaveService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +/** + * OA 请假申请 Controller,用于演示自己存储数据,接入工作流的例子 + * + * @author jason + * @author ZT + */ +@Tag(name = "管理后台 - OA 请假申请") +@RestController +@RequestMapping("/bpm/oa/leave") +@Validated +public class BpmOALeaveController { + + @Resource + private BpmOALeaveService leaveService; + + @PostMapping("/create") + @PreAuthorize("@ss.hasPermission('bpm:oa-leave:create')") + @Operation(summary = "创建请求申请") + public CommonResult createLeave(@Valid @RequestBody BpmOALeaveCreateReqVO createReqVO) { + return success(leaveService.createLeave(getLoginUserId(), createReqVO)); + } + + @GetMapping("/get") + @PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')") + @Operation(summary = "获得请假申请") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + public CommonResult getLeave(@RequestParam("id") Long id) { + BpmOALeaveDO leave = leaveService.getLeave(id); + return success(BeanUtils.toBean(leave, BpmOALeaveRespVO.class)); + } + + @GetMapping("/page") + @PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')") + @Operation(summary = "获得请假申请分页") + public CommonResult> getLeavePage(@Valid BpmOALeavePageReqVO pageVO) { + PageResult pageResult = leaveService.getLeavePage(getLoginUserId(), pageVO); + return success(BeanUtils.toBean(pageResult, BpmOALeaveRespVO.class)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java new file mode 100644 index 0000000..5f65c5b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java @@ -0,0 +1,5 @@ +/** + * OA 示例,用于演示外部业务接入 BPM 工作流的示例 + * 一般的接入方式,只需要调用 接口,后续 Admin 用户在管理后台的【待办事务】进行审批 + */ +package com.zt.plat.module.bpm.controller.admin.oa; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java new file mode 100644 index 0000000..4078b99 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java @@ -0,0 +1,43 @@ +package com.zt.plat.module.bpm.controller.admin.oa.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 请假申请创建 Request VO") +@Data +public class BpmOALeaveCreateReqVO { + + @Schema(description = "请假的开始时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "开始时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime startTime; + + @Schema(description = "请假的结束时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "结束时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime endTime; + + @Schema(description = "请假类型-参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码") + private String reason; + + @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") + private Map> startUserSelectAssignees; + + @AssertTrue(message = "结束时间,需要在开始时间之后") + public boolean isEndTimeValid() { + return !getEndTime().isBefore(getStartTime()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java new file mode 100644 index 0000000..f4226a4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java @@ -0,0 +1,29 @@ +package com.zt.plat.module.bpm.controller.admin.oa.vo; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 请假申请分页 Request VO") +@Data +public class BpmOALeavePageReqVO extends PageParam { + + @Schema(description = "状态", example = "1") + private Integer status; // 参见 BpmProcessInstanceResultEnum 枚举 + + @Schema(description = "请假类型,参见 bpm_oa_type", example = "1") + private Integer type; + + @Schema(description = "原因,模糊匹配", example = "阅读芋道源码") + private String reason; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "申请时间") + private LocalDateTime[] createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java new file mode 100644 index 0000000..02a5555 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.controller.admin.oa.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 请假申请 Response VO") +@Data +public class BpmOALeaveRespVO { + + @Schema(description = "请假表单主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "请假类型,参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码") + private String reason; + + @Schema(description = "申请时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "请假的开始时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime startTime; + + @Schema(description = "请假的结束时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime endTime; + + @Schema(description = "流程编号") + private String processInstanceId; + + @Schema(description = "审批结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http new file mode 100644 index 0000000..c690827 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http @@ -0,0 +1,16 @@ +### 请求 /bpm/process-instance/get-bpmn 接口 => 成功 +GET {{baseUrl}}/bpm/process-instance/get-bpmn-model-view?id=1d5fb5a6-85f8-11ef-b717-7e93075f94e3 +Content-Type: application/json +tenant-id: 1 +Authorization: Bearer {{token}} + +### 请求 /bpm/process-instance/get-bpmn 接口 => 失败 +#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=1d5fb5a6-85f8-11ef-b717-7e93075f94e3 +#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=3ee5c5ba-904a-11ef-a76e-b2ed5d6ef911 +#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=f630dfa2-8f92-11ef-947c-ba5e239a6eb4 +#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=9de8bdbf-9133-11ef-ae97-eaf49df1f932 +#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=dd2188eb-9394-11ef-a039-7a9ac3d9eb6b +GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processDefinitionId=test-auto:1:c70a799a-9394-11ef-a039-7a9ac3d9eb6b +Content-Type: application/json +tenant-id: 1 +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java new file mode 100644 index 0000000..53ea6fa --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -0,0 +1,202 @@ +package com.zt.plat.module.bpm.controller.admin.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; +import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.service.definition.BpmCategoryService; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.bpm.service.task.BpmTaskService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.task.api.Task; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 流程实例") // 流程实例,通过流程定义创建的一次“申请” +@RestController +@RequestMapping("/bpm/process-instance") +@Validated +public class BpmProcessInstanceController { + + @Resource + private BpmProcessInstanceService processInstanceService; + @Resource + private BpmTaskService taskService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + @Resource + private BpmCategoryService categoryService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @GetMapping("/my-page") + @Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + public CommonResult> getProcessInstanceMyPage( + @Valid BpmProcessInstancePageReqVO pageReqVO) { + PageResult pageResult = processInstanceService.getProcessInstancePage( + getLoginUserId(), pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 拼接返回 + Map> taskMap = taskService.getTaskMapByProcessInstanceIds( + convertList(pageResult.getList(), HistoricProcessInstance::getId)); + Map processDefinitionMap = processDefinitionService.getProcessDefinitionMap( + convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + Map categoryMap = categoryService.getCategoryMap( + convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + Set userIds = convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId())); + userIds.addAll(convertSetByFlatMap(taskMap.values(), + tasks -> tasks.stream().map(Task::getAssignee).filter(StrUtil::isNotBlank).map(Long::parseLong))); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, + processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap)); + } + + @GetMapping("/manager-page") + @Operation(summary = "获得管理流程实例的分页列表", description = "在【流程实例】菜单中,进行调用") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:manager-query')") + public CommonResult> getProcessInstanceManagerPage( + @Valid BpmProcessInstancePageReqVO pageReqVO) { + PageResult pageResult = processInstanceService.getProcessInstancePage( + null, pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 拼接返回 + Map> taskMap = taskService.getTaskMapByProcessInstanceIds( + convertList(pageResult.getList(), HistoricProcessInstance::getId)); + Map processDefinitionMap = processDefinitionService.getProcessDefinitionMap( + convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + Map categoryMap = categoryService.getCategoryMap( + convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); + // 发起人信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId()))); + Map deptMap = deptApi.getDeptMap( + convertSet(userMap.values(), DeptUtil::getDeptId)); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, + processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap)); + } + + @PostMapping("/create") + @Operation(summary = "新建流程实例") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + public CommonResult createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) { + return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO)); + } + + @GetMapping("/get") + @Operation(summary = "获得指定流程实例", description = "在【流程详细】界面中,进行调用") + @Parameter(name = "id", description = "流程实例的编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + public CommonResult getProcessInstance(@RequestParam("id") String id) { + HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(id); + if (processInstance == null) { + return success(null); + } + + // 拼接返回 + ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( + processInstance.getProcessDefinitionId()); + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo( + processInstance.getProcessDefinitionId()); + AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData(); + DeptRespDTO dept = null; + if (startUser != null) { + Long deptId = DeptUtil.getDeptId(startUser); + if (deptId > 0) { + dept = deptApi.getDept(deptId).getCheckedData(); + } + } + return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance, + processDefinition, processDefinitionInfo, startUser, dept)); + } + + @DeleteMapping("/cancel-by-start-user") + @Operation(summary = "用户取消流程实例", description = "取消发起的流程") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')") + public CommonResult cancelProcessInstanceByStartUser( + @Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) { + processInstanceService.cancelProcessInstanceByStartUser(getLoginUserId(), cancelReqVO); + return success(true); + } + + @DeleteMapping("/cancel-by-admin") + @Operation(summary = "管理员取消流程实例", description = "管理员撤回流程") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel-by-admin')") + public CommonResult cancelProcessInstanceByManager( + @Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) { + processInstanceService.cancelProcessInstanceByAdmin(getLoginUserId(), cancelReqVO); + return success(true); + } + + @GetMapping("/get-approval-detail") + @Operation(summary = "获得审批详情") + @Parameter(name = "id", description = "流程实例的编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") + public CommonResult getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); + } + return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); + } + + @GetMapping("/get-next-approval-nodes") + @Operation(summary = "获取下一个执行的流程节点") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") + public CommonResult> getNextApprovalNodes(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); + } + return success(processInstanceService.getNextApprovalNodes(getLoginUserId(), reqVO)); + } + + @GetMapping("/get-bpmn-model-view") + @Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用") + @Parameter(name = "id", description = "流程实例的编号", required = true) + public CommonResult getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) { + return success(processInstanceService.getProcessInstanceBpmnModelView(id)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java new file mode 100644 index 0000000..bd644ca --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java @@ -0,0 +1,89 @@ +package com.zt.plat.module.bpm.controller.admin.task; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.MapUtils; +import com.zt.plat.framework.common.util.date.DateUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.cc.BpmProcessInstanceCopyRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.flowable.engine.history.HistoricProcessInstance; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; +import java.util.stream.Stream; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 流程实例抄送") +@RestController +@RequestMapping("/bpm/process-instance/copy") +@Validated +public class BpmProcessInstanceCopyController { + + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; + @Resource + private BpmProcessInstanceService processInstanceService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + + @Resource + private AdminUserApi adminUserApi; + + @GetMapping("/page") + @Operation(summary = "获得抄送流程分页列表") + @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:query')") + public CommonResult> getProcessInstanceCopyPage( + @Valid BpmProcessInstanceCopyPageReqVO pageReqVO) { + PageResult pageResult = processInstanceCopyService.getProcessInstanceCopyPage( + getLoginUserId(), pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(new PageResult<>(pageResult.getTotal())); + } + + // 拼接返回 + Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap( + convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId)); + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(), + copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator())))); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessDefinitionId)); + return success(convertPage(pageResult, copy -> { + BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class); + MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()), + user -> copyVO.setStartUser(BeanUtils.toBean(user, UserSimpleBaseVO.class))); + MapUtils.findAndThen(userMap, copy.getStartUserId(), + user -> copyVO.setCreateUser(BeanUtils.toBean(user, UserSimpleBaseVO.class))); + MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(), + processInstance -> { + copyVO.setSummary(FlowableUtils.getSummary( + processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()), + processInstance.getProcessVariables())); + copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime())); + }); + return copyVO; + })); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java new file mode 100644 index 0000000..54db1af --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java @@ -0,0 +1,252 @@ +package com.zt.plat.module.bpm.controller.admin.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.*; +import com.zt.plat.module.bpm.convert.task.BpmTaskConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.bpm.service.task.BpmTaskService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.getLoginUserId; + +/** + * @author chenbowen + */ +@Tag(name = "管理后台 - 流程任务实例") +@RestController +@RequestMapping("/bpm/task") +@Validated +public class BpmTaskController { + + @Resource + private BpmTaskService taskService; + @Resource + private BpmProcessInstanceService processInstanceService; + @Resource + private BpmFormService formService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @GetMapping("todo-page") + @Operation(summary = "获取 Todo 待办任务分页") + @PreAuthorize("@ss.hasPermission('bpm:task:query')") + public CommonResult> getTaskTodoPage(@Valid BpmTaskPageReqVO pageVO) { + PageResult pageResult = taskService.getTaskTodoPage(getLoginUserId(), pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + + // 拼接数据 + Map processInstanceMap = processInstanceService.getProcessInstanceMap( + convertSet(pageResult.getList(), Task::getProcessInstanceId)); + Map userMap = adminUserApi.getUserMap( + convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), Task::getProcessDefinitionId)); + return success(BpmTaskConvert.INSTANCE.buildTodoTaskPage(pageResult, processInstanceMap, userMap, processDefinitionInfoMap)); + } + + @GetMapping("done-page") + @Operation(summary = "获取 Done 已办任务分页") + @PreAuthorize("@ss.hasPermission('bpm:task:query')") + public CommonResult> getTaskDonePage(@Valid BpmTaskPageReqVO pageVO) { + PageResult pageResult = taskService.getTaskDonePage(getLoginUserId(), pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + + // 拼接数据 + Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap( + convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); + Map userMap = adminUserApi.getUserMap( + convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); + return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null, processDefinitionInfoMap)); + } + + @GetMapping("manager-page") + @Operation(summary = "获取全部任务的分页", description = "用于【流程任务】菜单") + @PreAuthorize("@ss.hasPermission('bpm:task:mananger-query')") + public CommonResult> getTaskManagerPage(@Valid BpmTaskPageReqVO pageVO) { + PageResult pageResult = taskService.getTaskPage(getLoginUserId(), pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + + // 拼接数据 + Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap( + convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); + // 获得 User 和 Dept Map + Set userIds = convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())); + userIds.addAll(convertSet(pageResult.getList(), task -> NumberUtils.parseLong(task.getAssignee()))); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); + return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap, processDefinitionInfoMap)); + } + + @GetMapping("/list-by-process-instance-id") + @Operation(summary = "获得指定流程实例的任务列表", description = "包括完成的、未完成的") + @Parameter(name = "processInstanceId", description = "流程实例的编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:task:query')") + public CommonResult> getTaskListByProcessInstanceId( + @RequestParam("processInstanceId") String processInstanceId) { + List taskList = taskService.getTaskListByProcessInstanceId(processInstanceId, true); + if (CollUtil.isEmpty(taskList)) { + return success(Collections.emptyList()); + } + + // 拼接数据 + Set userIds = convertSetByFlatMap(taskList, task -> + Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner()))); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + // 获得 Form Map + Map formMap = formService.getFormMap( + convertSet(taskList, task -> { + String formKey = task.getFormKey(); + if (StrUtil.isBlank(formKey)) { + return 0L; + } + try { + return Long.parseLong(formKey); + } catch (NumberFormatException e) { + // 如果 formKey 不是数字(比如是URL),返回0L + return 0L; + } + })); + return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, + formMap, userMap, deptMap)); + } + + @PutMapping("/approve") + @Operation(summary = "通过任务") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) { + taskService.approveTask(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/reject") + @Operation(summary = "不通过任务") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) { + taskService.rejectTask(getLoginUserId(), reqVO); + return success(true); + } + + @GetMapping("/list-by-return") + @Operation(summary = "获取所有可退回的节点", description = "用于【流程详情】的【退回】按钮") + @Parameter(name = "taskId", description = "当前任务ID", required = true) + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult> getTaskListByReturn(@RequestParam("id") String id) { + List userTaskList = taskService.getUserTaskListByReturn(id); + return success(convertList(userTaskList, userTask -> // 只返回 id 和 name + new BpmTaskRespVO().setName(userTask.getName()).setTaskDefinitionKey(userTask.getId()))); + } + + @PutMapping("/return") + @Operation(summary = "退回任务", description = "用于【流程详情】的【退回】按钮") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) { + taskService.returnTask(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/delegate") + @Operation(summary = "委派任务", description = "用于【流程详情】的【委派】按钮") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) { + taskService.delegateTask(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/transfer") + @Operation(summary = "转派任务", description = "用于【流程详情】的【转派】按钮") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult transferTask(@Valid @RequestBody BpmTaskTransferReqVO reqVO) { + taskService.transferTask(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/create-sign") + @Operation(summary = "加签", description = "before 前加签,after 后加签") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult createSignTask(@Valid @RequestBody BpmTaskSignCreateReqVO reqVO) { + taskService.createSignTask(getLoginUserId(), reqVO); + return success(true); + } + + @DeleteMapping("/delete-sign") + @Operation(summary = "减签") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult deleteSignTask(@Valid @RequestBody BpmTaskSignDeleteReqVO reqVO) { + taskService.deleteSignTask(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/copy") + @Operation(summary = "抄送任务") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult copyTask(@Valid @RequestBody BpmTaskCopyReqVO reqVO) { + taskService.copyTask(getLoginUserId(), reqVO); + return success(true); + } + + @GetMapping("/list-by-parent-task-id") + @Operation(summary = "获得指定父级任务的子任务列表") // 目前用于,减签的时候,获得子任务列表 + @Parameter(name = "parentTaskId", description = "父级任务编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:task:query')") + public CommonResult> getTaskListByParentTaskId(@RequestParam("parentTaskId") String parentTaskId) { + List taskList = taskService.getTaskListByParentTaskId(parentTaskId); + if (CollUtil.isEmpty(taskList)) { + return success(Collections.emptyList()); + } + // 拼接数据 + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(taskList, + user -> Stream.of(NumberUtils.parseLong(user.getAssignee()), NumberUtils.parseLong(user.getOwner())))); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + return success(BpmTaskConvert.INSTANCE.buildTaskListByParentTaskId(taskList, userMap, deptMap)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java new file mode 100644 index 0000000..444aeae --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java @@ -0,0 +1,25 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.activity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 流程活动的 Response VO") +@Data +public class BpmActivityRespVO { + + @Schema(description = "流程活动的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String key; + @Schema(description = "流程活动的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartEvent") + private String type; + + @Schema(description = "流程活动的开始时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime startTime; + @Schema(description = "流程活动的结束时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime endTime; + + @Schema(description = "关联的流程任务的编号", example = "2048") + private String taskId; // 关联的流程任务,只有 UserTask 等类型才有 + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java new file mode 100644 index 0000000..b968f9a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java @@ -0,0 +1,48 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.cc; + +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 流程实例抄送的分页 Item Response VO") +@Data +public class BpmProcessInstanceCopyRespVO { + + @Schema(description = "抄送主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "发起人", requiredMode = Schema.RequiredMode.REQUIRED) + private UserSimpleBaseVO startUser; + + @Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "A233") + private String processInstanceId; + @Schema(description = "流程实例的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试") + private String processInstanceName; + @Schema(description = "流程实例的发起时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime processInstanceStartTime; + + @Schema(description = "流程活动的编号", requiredMode = Schema.RequiredMode.REQUIRED) + private String activityId; + @Schema(description = "流程活动的名字", requiredMode = Schema.RequiredMode.REQUIRED) + private String activityName; + + @Schema(description = "流程活动的编号") + private String taskId; + + @Schema(description = "抄送人意见") + private String reason; + + @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED) + private UserSimpleBaseVO createUser; + + @Schema(description = "抄送时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "流程摘要", example = "[]") + private List> summary; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java new file mode 100644 index 0000000..3f37d7a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java @@ -0,0 +1,40 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; +import lombok.Data; + +import java.util.Map; + +@Schema(description = "管理后台 - 审批详情 Request VO") +@Data +public class BpmApprovalDetailReqVO { + + @Schema(description = "流程定义的编号", example = "1024") + private String processDefinitionId; // 使用场景:发起流程时,传流程定义 ID + + @Schema(description = "流程变量") + private Map processVariables; // 使用场景:同 processDefinitionId,用于流程预测 + + @Schema(description = "流程变量") + private String processVariablesStr; // 解决 GET 无法传递对象的问题,最终转换成 processVariables 变量 + + @Schema(description = "流程实例的编号", example = "1024") + private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID + + // TODO @芋艿:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 + @Schema(description = "流程活动编号", example = "StartUserNode") + private String activityId; // 用于获取表单权限。1)发起流程时,传“发起人节点” activityId 可获取发起人的表单权限;2)从抄送列表界面进来时,传抄送的 activityId 可获取抄送人的表单权限; + + @Schema(description = "流程任务编号", example = "95f2f08b-621b-11ef-bf39-00ff4722db8b") + private String taskId; // 用于获取表单权限。1)从待审批/已审批界面进来时,传递 taskId 任务编号,可获取任务节点的变得权限 + + @AssertTrue(message = "流程定义的编号和流程实例的编号不能同时为空") + @JsonIgnore + public boolean isValidProcessParam() { + return StrUtil.isNotEmpty(processDefinitionId) || StrUtil.isNotEmpty(processInstanceId); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java new file mode 100644 index 0000000..4e444f4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java @@ -0,0 +1,112 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + + +@Schema(description = "管理后台 - 审批详情 Response VO") +@Data +public class BpmApprovalDetailRespVO { + + @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 + + @Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List activityNodes; + + @Schema(description = "表单字段权限") + private Map formFieldsPermission; + + @Schema(description = "待办任务") + private BpmTaskRespVO todoTask; + + /** + * 所属流程定义信息 + */ + private BpmProcessDefinitionRespVO processDefinition; + + /** + * 所属流程实例信息 + */ + private BpmProcessInstanceRespVO processInstance; + + @Schema(description = "活动节点信息") + @Data + public static class ActivityNode { + + @Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartUserNode") + private String id; + + @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "发起人") + private String name; + + @Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer nodeType; // 参见 BpmSimpleModelNodeType 枚举 + + @Schema(description = "节点状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer status; // 参见 BpmTaskStatusEnum 枚举 + + @Schema(description = "节点的开始时间") + private LocalDateTime startTime; + @Schema(description = "节点的结束时间") + private LocalDateTime endTime; + + @Schema(description = "审批节点的任务信息") + private List tasks; + + @Schema(description = "候选人策略", example = "35") + private Integer candidateStrategy; // 参见 BpmTaskCandidateStrategyEnum 枚举。主要用于发起时,审批节点、抄送节点自选 + + @Schema(description = "候选人用户 ID 列表", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 candidateUsers + private List candidateUserIds; + + @Schema(description = "候选人用户列表") + private List candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表 + + @Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf") + private String processInstanceId; // 当且仅当,该节点是子流程节点时,才会有值(CallActivity 的 processInstanceId 字段) + + } + + @Schema(description = "活动节点的任务信息") + @Data + public static class ActivityNodeTask { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String id; + + @Schema(description = "任务所属人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser + private Long owner; + + @Schema(description = "任务所属人", example = "1024") + private UserSimpleBaseVO ownerUser; + + @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser + private Long assignee; + + @Schema(description = "任务分配人", example = "2048") + private UserSimpleBaseVO assigneeUser; + + @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; // 参见 BpmTaskStatusEnum 枚举 + + @Schema(description = "审批意见", example = "同意") + private String reason; + + @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") + private String signPicUrl; + + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java new file mode 100644 index 0000000..bf520a0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java @@ -0,0 +1,43 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; +import java.util.Set; + +@Schema(description = "管理后台 - 流程示例的 BPMN 视图 Response VO") +@Data +public class BpmProcessInstanceBpmnModelViewRespVO { + + // ========== 基本信息 ========== + + @Schema(description = "流程实例信息", requiredMode = Schema.RequiredMode.REQUIRED) + private BpmProcessInstanceRespVO processInstance; + + @Schema(description = "任务列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List tasks; + + @Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED) + private String bpmnXml; + + @Schema(description = "SIMPLE 模型") + private BpmSimpleModelNodeVO simpleModel; + + // ========== 进度信息 ========== + + @Schema(description = "进行中的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set unfinishedTaskActivityIds; // 只包括 UserTask + + @Schema(description = "已经完成的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set finishedTaskActivityIds; // 包括 UserTask、Gateway 等,不包括 SequenceFlow + + @Schema(description = "已经完成的连线节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set finishedSequenceFlowActivityIds; // 只包括 SequenceFlow + + @Schema(description = "已经拒绝的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set rejectedTaskActivityIds; // 只包括 UserTask + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java new file mode 100644 index 0000000..b9d19e2 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java @@ -0,0 +1,19 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 流程实例的取消 Request VO") +@Data +public class BpmProcessInstanceCancelReqVO { + + @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "流程实例的编号不能为空") + private String id; + + @Schema(description = "取消原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "不请假了!") + @NotEmpty(message = "取消原因不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java new file mode 100644 index 0000000..b7956e4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 流程实例抄送的分页 Request VO") +@Data +public class BpmProcessInstanceCopyPageReqVO extends PageParam { + + @Schema(description = "流程名称", example = "芋道") + private String processInstanceName; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java new file mode 100644 index 0000000..bb46b1f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java @@ -0,0 +1,24 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 流程实例的创建 Request VO") +@Data +public class BpmProcessInstanceCreateReqVO { + + @Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "流程定义编号不能为空") + private String processDefinitionId; + + @Schema(description = "变量实例(动态表单)") + private Map variables; + + @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") + private Map> startUserSelectAssignees; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java new file mode 100644 index 0000000..33875b6 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.validation.InEnum; +import com.zt.plat.module.bpm.enums.task.BpmProcessInstanceStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 流程实例分页 Request VO") +@Data +public class BpmProcessInstancePageReqVO extends PageParam { + + @Schema(description = "流程名称", example = "芋道") + private String name; + + @Schema(description = "流程定义的标识", example = "2048") + private String processDefinitionKey; // 精准匹配 + + @Schema(description = "流程实例的状态", example = "1") + @InEnum(BpmProcessInstanceStatusEnum.class) + private Integer status; + + @Schema(description = "流程分类", example = "1") + private String category; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "结束时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] endTime; + + @Schema(description = "发起用户编号", example = "1024") + private Long startUserId; // 注意,只有在【流程实例】菜单,才使用该参数 + + @Schema(description = "动态表单字段查询 JSON Str", example = "{}") + private String formFieldsParams; // SpringMVC 在 get 请求下,无法方便的定义 Map 类型的参数,所以通过 String 接收后,逻辑里面转换 + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java new file mode 100644 index 0000000..a56e268 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java @@ -0,0 +1,86 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 流程实例的 Response VO") +@Data +public class BpmProcessInstanceRespVO { + + @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "流程摘要") + private List> summary; // 只有流程表单,才有摘要! + + @Schema(description = "流程分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String category; + @Schema(description = "流程分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "请假") + private String categoryName; + + @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 + + @Schema(description = "发起时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime startTime; + + @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime endTime; + + @Schema(description = "持续时间", example = "1000") + private Long durationInMillis; + + @Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED) + private Map formVariables; + + @Schema(description = "业务的唯一标识-例如说,请假申请的编号", example = "1") + private String businessKey; + + /** + * 发起流程的用户 + */ + private UserSimpleBaseVO startUser; + + @Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private String processDefinitionId; + /** + * 流程定义 + */ + private BpmProcessDefinitionRespVO processDefinition; + + /** + * 当前审批中的任务 + */ + private List tasks; // 仅在流程实例分页才返回 + + @Schema(description = "流程任务") + @Data + public static class Task { + + @Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser + private Long assignee; + + @Schema(description = "任务分配人", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + private UserSimpleBaseVO assigneeUser; + + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java new file mode 100644 index 0000000..7d56754 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 通过流程任务的 Request VO") +@Data +public class BpmTaskApproveReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "审批意见", example = "不错不错!") + private String reason; + + @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") + private String signPicUrl; + + @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) + private Map variables; + + @Schema(description = "下一个节点审批人", example = "{nodeId:[1, 2]}") + private Map> nextAssignees; // 为什么是 Map,而不是 List 呢?因为下一个节点可能是多个,例如说并行网关的情况 + + // 新增任务变量实例,业务表单 + @Schema(description = "任务变量实例,业务表单", example = "{'formField1': 'value1', 'formField2': 'value2'}") + private Map taskVariables; +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java new file mode 100644 index 0000000..1775be0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.Collection; + +@Schema(description = "管理后台 - 抄送流程任务的 Request VO") +@Data +public class BpmTaskCopyReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "抄送的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]") + @NotEmpty(message = "抄送用户不能为空") + private Collection copyUserIds; + + @Schema(description = "抄送意见", example = "帮忙看看!") + private String reason; +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java new file mode 100644 index 0000000..21aa42a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java @@ -0,0 +1,24 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 委派流程任务的 Request VO") +@Data +public class BpmTaskDelegateReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "被委派人 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "被委派人 ID 不能为空") + private Long delegateUserId; + + @Schema(description = "委派原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "做不了决定,需要你先帮忙瞅瞅") + @NotEmpty(message = "委派原因不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java new file mode 100644 index 0000000..c7c1721 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java @@ -0,0 +1,28 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.util.date.DateUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 流程任务的的分页 Request VO") // 待办、已办,都使用该分页 +@Data +public class BpmTaskPageReqVO extends PageParam { + + @Schema(description = "流程任务名", example = "芋道") + private String name; + + @Schema(description = "流程分类", example = "1") + private String category; + + @Schema(description = "流程定义的标识", example = "2048") + private String processDefinitionKey; // 精准匹配 + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java new file mode 100644 index 0000000..8482f48 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java @@ -0,0 +1,18 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 不通过流程任务的 Request VO") +@Data +public class BpmTaskRejectReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java new file mode 100644 index 0000000..e5318c5 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -0,0 +1,130 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 流程任务 Response VO") +@Data +public class BpmTaskRespVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "任务名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime endTime; + + @Schema(description = "持续时间", example = "1000") + private Long durationInMillis; + + @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer status; // 参见 BpmTaskStatusEnum 枚举 + + @Schema(description = "审批理由", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private String reason; + + @Schema(description = "任务负责人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser + private Long owner; + /** + * 负责人的用户信息 + */ + private UserSimpleBaseVO ownerUser; + + @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser + private Long assignee; + /** + * 审核的用户信息 + */ + private UserSimpleBaseVO assigneeUser; + + @Schema(description = "任务定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "Activity_one") + private String taskDefinitionKey; + + @Schema(description = "所属流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8888") + private String processInstanceId; + /** + * 所属流程实例 + */ + private ProcessInstance processInstance; + + @Schema(description = "父任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String parentTaskId; + @Schema(description = "子任务列表(由加签生成)", requiredMode = Schema.RequiredMode.REQUIRED, example = "childrenTask") + private List children; // 由加签生成,包含多层子任务 + + @Schema(description = "表单编号", example = "1024") + private Long formId; + @Schema(description = "表单路由", example = "1024") + private String formPath; + @Schema(description = "表单名字", example = "请假表单") + private String formName; + @Schema(description = "表单的配置,JSON 字符串") + private String formConf; + @Schema(description = "表单项的数组") + private List formFields; + @Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED) + private Map formVariables; + @Schema(description = "操作按钮设置值") + private Map buttonsSetting; + + @Schema(description = "是否需要签名", example = "false") + private Boolean signEnable; + + @Schema(description = "是否填写审批意见", example = "false") + private Boolean reasonRequire; + + @Schema(description = "节点类型", example = "10") + private Integer nodeType; // 参见 BpmSimpleModelNodeTypeEnum 枚举。 + + @Data + @Schema(description = "流程实例") + public static class ProcessInstance { + + @Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "流程实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "提交时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private String processDefinitionId; + + @Schema(description = "流程摘要", example = "[]") + private List> summary; // 只有流程表单,才有摘要! + + /** + * 发起人的用户信息 + */ + private UserSimpleBaseVO startUser; + + } + + @Data + @Schema(description = "操作按钮设置") + public static class OperationButtonSetting { + + @Schema(description = "显示名称", example = "审批") + private String displayName; + + @Schema(description = "是否启用", example = "true") + private Boolean enable; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java new file mode 100644 index 0000000..22982ab --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 退回流程任务的 Request VO") +@Data +public class BpmTaskReturnReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "退回到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotEmpty(message = "退回到的任务 Key 不能为空") + private String targetTaskDefinitionKey; + + @Schema(description = "退回意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回") + @NotEmpty(message = "退回意见不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java new file mode 100644 index 0000000..0acf4bd --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java @@ -0,0 +1,29 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.Set; + +@Schema(description = "管理后台 - 加签任务的创建(加签) Request VO") +@Data +public class BpmTaskSignCreateReqVO { + + @Schema(description = "需要加签的任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "加签的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888") + @NotEmpty(message = "加签用户不能为空") + private Set userIds; + + @Schema(description = "加签类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "before") + @NotEmpty(message = "加签类型不能为空") + private String type; // 参见 BpmTaskSignTypeEnum 枚举 + + @Schema(description = "加签原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "需要加签") + @NotEmpty(message = "加签原因不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java new file mode 100644 index 0000000..a848485 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java @@ -0,0 +1,19 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 加签任务的删除(减签) Request VO") +@Data +public class BpmTaskSignDeleteReqVO { + + @Schema(description = "被减签的任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "加签原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "需要减签") + @NotEmpty(message = "加签原因不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java new file mode 100644 index 0000000..e55bb73 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java @@ -0,0 +1,24 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 流程任务的转办 Request VO") +@Data +public class BpmTaskTransferReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "新审批人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "新审批人的用户编号不能为空") + private Long assigneeUserId; + + @Schema(description = "转办原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "做不了决定,需要你先帮忙瞅瞅") + @NotEmpty(message = "转办原因不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java new file mode 100644 index 0000000..0513848 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package com.zt.plat.module.bpm.controller.app; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java new file mode 100644 index 0000000..52ffdf8 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 RESTful API 给前端: + * 1. admin 包:提供给管理后台 zt-ui-admin 前端项目 + * 2. app 包:提供给用户 APP zt-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分 + */ +package com.zt.plat.module.bpm.controller; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java new file mode 100644 index 0000000..262a598 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java @@ -0,0 +1,132 @@ +package com.zt.plat.module.bpm.convert.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.util.date.DateUtils; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 流程模型 Convert + * + * @author yunlongn + */ +@Mapper +public interface BpmModelConvert { + + BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class); + + default List buildModelList(List list, + Map formMap, + Map categoryMap, + Map deploymentMap, + Map processDefinitionMap, + Map userMap, + Map deptMap) { + List result = convertList(list, model -> { + BpmModelMetaInfoVO metaInfo = parseMetaInfo(model); + BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null; + BpmCategoryDO category = categoryMap.get(model.getCategory()); + Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null; + ProcessDefinition processDefinition = model.getDeploymentId() != null ? + processDefinitionMap.get(model.getDeploymentId()) : null; + List startUsers = metaInfo != null ? convertList(metaInfo.getStartUserIds(), userMap::get) : null; + List startDepts = metaInfo != null ? convertList(metaInfo.getStartDeptIds(), deptMap::get) : null; + return buildModel0(model, metaInfo, form, category, deployment, processDefinition, startUsers, startDepts); + }); + // 排序 + result.sort(Comparator.comparing(BpmModelMetaInfoVO::getSort)); + return result; + } + + default BpmModelRespVO buildModel(Model model, byte[] bpmnBytes, BpmSimpleModelNodeVO simpleModel) { + BpmModelMetaInfoVO metaInfo = parseMetaInfo(model); + BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null, null, null); + if (ArrayUtil.isNotEmpty(bpmnBytes)) { + modelVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnBytes)); + } + modelVO.setSimpleModel(simpleModel); + return modelVO; + } + + default BpmModelRespVO buildModel0(Model model, + BpmModelMetaInfoVO metaInfo, BpmFormDO form, BpmCategoryDO category, + Deployment deployment, ProcessDefinition processDefinition, + List startUsers, List startDepts) { + BpmModelRespVO modelRespVO = new BpmModelRespVO().setId(model.getId()).setName(model.getName()) + .setKey(model.getKey()).setCategory(model.getCategory()) + .setCreateTime(DateUtils.of(model.getCreateTime())); + // Form + BeanUtils.copyProperties(metaInfo, modelRespVO); + if (form != null) { + modelRespVO.setFormName(form.getName()); + } + // Category + if (category != null) { + modelRespVO.setCategoryName(category.getName()); + } + // ProcessDefinition + if (processDefinition != null) { + modelRespVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)); + modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ? + SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); + if (deployment != null) { + modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime())); + } + } + // User、Dept + modelRespVO.setStartUsers(BeanUtils.toBean(startUsers, UserSimpleBaseVO.class)) + .setStartDepts(BeanUtils.toBean(startDepts, DeptSimpleBaseVO.class)); + return modelRespVO; + } + + default void copyToModel(Model model, BpmModelSaveReqVO reqVO) { + model.setName(reqVO.getName()); + model.setKey(reqVO.getKey()); + model.setCategory(reqVO.getCategory()); + model.setMetaInfo(JsonUtils.toJsonString(BeanUtils.toBean(reqVO, BpmModelMetaInfoVO.class))); + } + + default BpmModelMetaInfoVO parseMetaInfo(Model model) { + BpmModelMetaInfoVO vo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoVO.class); + if (vo == null) { + return null; + } + if (vo.getManagerUserIds() == null) { + vo.setManagerUserIds(Collections.emptyList()); + } + if (vo.getStartUserIds() == null) { + vo.setStartUserIds(Collections.emptyList()); + } + // 如果为空,兜底处理,使用 createTime 创建时间 + if (vo.getSort() == null) { + vo.setSort(model.getCreateTime().getTime()); + } + return vo; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java new file mode 100644 index 0000000..86b256c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java @@ -0,0 +1,99 @@ +package com.zt.plat.module.bpm.convert.definition; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.map.MapUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * Bpm 流程定义的 Convert + * + * @author yunlong.li + */ +@Mapper +public interface BpmProcessDefinitionConvert { + + BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class); + + default PageResult buildProcessDefinitionPage(PageResult page, + Map deploymentMap, + Map processDefinitionInfoMap, + Map formMap, + Map categoryMap) { + List list = buildProcessDefinitionList(page.getList(), deploymentMap, processDefinitionInfoMap, formMap, categoryMap); + return new PageResult<>(list, page.getTotal()); + } + + default List buildProcessDefinitionList(List list, + Map deploymentMap, + Map processDefinitionInfoMap, + Map formMap, + Map categoryMap) { + List result = CollectionUtils.convertList(list, definition -> { + Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class); + BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class); + BpmFormDO form = null; + if (processDefinitionInfo != null) { + form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class); + } + BpmCategoryDO category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategoryDO.class); + return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null); + }); + // 排序 + result.sort(Comparator.comparing(BpmProcessDefinitionRespVO::getSort)); + return result; + } + + default BpmProcessDefinitionRespVO buildProcessDefinition(ProcessDefinition definition, + Deployment deployment, + BpmProcessDefinitionInfoDO processDefinitionInfo, + BpmFormDO form, + BpmCategoryDO category, + BpmnModel bpmnModel) { + BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class); + respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); + // Deployment + if (deployment != null) { + respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())); + } + // BpmProcessDefinitionInfoDO + if (processDefinitionInfo != null) { + copyTo(processDefinitionInfo, respVO); + // Form + if (form != null) { + respVO.setFormName(form.getName()); + } + } + // Category + if (category != null) { + respVO.setCategoryName(category.getName()); + } + // BpmnModel + if (bpmnModel != null) { + respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel)); + } + return respVO; + } + + @Mapping(source = "from.id", target = "to.id", ignore = true) + void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java new file mode 100644 index 0000000..41de133 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java @@ -0,0 +1,21 @@ +package com.zt.plat.module.bpm.convert.message; + +import com.zt.plat.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.Map; + +@Mapper +public interface BpmMessageConvert { + + BpmMessageConvert INSTANCE = Mappers.getMapper(BpmMessageConvert.class); + + @Mapping(target = "mobile", ignore = true) + @Mapping(source = "userId", target = "userId") + @Mapping(source = "templateCode", target = "templateCode") + @Mapping(source = "templateParams", target = "templateParams") + SmsSendSingleToUserReqDTO convert(Long userId, String templateCode, Map templateParams); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java new file mode 100644 index 0000000..b72f713 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 POJO 类的实体转换 + * + * 目前使用 MapStruct 框架 + */ +package com.zt.plat.module.bpm.convert; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java new file mode 100644 index 0000000..791e694 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java @@ -0,0 +1,298 @@ +package com.zt.plat.module.bpm.convert.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.MapUtils; +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceBpmnModelViewRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import com.zt.plat.module.bpm.convert.definition.BpmProcessDefinitionConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * 流程实例 Convert + * + * @author ZT + */ +@Mapper +public interface BpmProcessInstanceConvert { + + BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class); + + default PageResult buildProcessInstancePage(PageResult pageResult, + Map processDefinitionMap, + Map categoryMap, + Map> taskMap, + Map userMap, + Map deptMap, + Map processDefinitionInfoMap) { + PageResult vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstanceRespVO.class); + for (int i = 0; i < pageResult.getList().size(); i++) { + BpmProcessInstanceRespVO respVO = vpPageResult.getList().get(i); + respVO.setStatus(FlowableUtils.getProcessInstanceStatus(pageResult.getList().get(i))); + MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(), + processDefinition -> respVO.setCategory(processDefinition.getCategory()) + .setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class))); + MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName())); + respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceRespVO.Task.class)); + // user + if (userMap != null) { + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(pageResult.getList().get(i).getStartUserId())); + if (startUser != null) { + respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); + MapUtils.findAndThen(deptMap, DeptUtil.getDeptId(startUser), dept -> respVO.getStartUser().setDeptName(dept.getName())); + } + if (CollUtil.isNotEmpty(respVO.getTasks())) { + respVO.getTasks().forEach(task -> { + AdminUserRespDTO assigneeUser = userMap.get(task.getAssignee()); + if (assigneeUser!= null) { + task.setAssigneeUser(BeanUtils.toBean(assigneeUser, UserSimpleBaseVO.class)); + MapUtils.findAndThen(deptMap, DeptUtil.getDeptId(assigneeUser), dept -> task.getAssigneeUser().setDeptName(dept.getName())); + } + }); + } + } + // 摘要 + respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(respVO.getProcessDefinitionId()), + pageResult.getList().get(i).getProcessVariables())); + // 表单 + respVO.setFormVariables(pageResult.getList().get(i).getProcessVariables()); + } + return vpPageResult; + } + + default BpmProcessInstanceRespVO buildProcessInstance(HistoricProcessInstance processInstance, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + AdminUserRespDTO startUser, + DeptRespDTO dept) { + BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class); + respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance)) + .setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance)); + // definition + respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)); + copyTo(processDefinitionInfo, respVO.getProcessDefinition()); + // user + if (startUser != null) { + respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); + if (dept != null) { + respVO.getStartUser().setDeptName(dept.getName()); + } + } + return respVO; + } + + @Mapping(source = "from.id", target = "to.id", ignore = true) + void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); + + default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) { + return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status) + .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey()); + } + + default BpmMessageSendWhenProcessInstanceApproveReqDTO buildProcessInstanceApproveMessage(ProcessInstance instance) { + return new BpmMessageSendWhenProcessInstanceApproveReqDTO() + .setStartUserId(NumberUtils.parseLong(instance.getStartUserId())) + .setProcessInstanceId(instance.getId()) + .setProcessInstanceName(instance.getName()); + } + + default BpmMessageSendWhenProcessInstanceRejectReqDTO buildProcessInstanceRejectMessage(ProcessInstance instance, String reason) { + return new BpmMessageSendWhenProcessInstanceRejectReqDTO() + .setProcessInstanceName(instance.getName()) + .setProcessInstanceId(instance.getId()) + .setReason(reason) + .setStartUserId(NumberUtils.parseLong(instance.getStartUserId())); + } + + default BpmProcessInstanceBpmnModelViewRespVO buildProcessInstanceBpmnModelView(HistoricProcessInstance processInstance, + List taskInstances, + BpmnModel bpmnModel, + BpmSimpleModelNodeVO simpleModel, + Set unfinishedTaskActivityIds, + Set finishedTaskActivityIds, + Set finishedSequenceFlowActivityIds, + Set rejectTaskActivityIds, + Map userMap, + Map deptMap) { + BpmProcessInstanceBpmnModelViewRespVO respVO = new BpmProcessInstanceBpmnModelViewRespVO(); + // 基本信息 + respVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class, o -> o + .setStatus(FlowableUtils.getProcessInstanceStatus(processInstance))) + .setStartUser(buildUser(processInstance.getStartUserId(), userMap, deptMap))); + respVO.setTasks(convertList(taskInstances, task -> BeanUtils.toBean(task, BpmTaskRespVO.class) + .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)) + .setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap)) + .setOwnerUser(buildUser(task.getOwner(), userMap, deptMap)))); + respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel)); + respVO.setSimpleModel(simpleModel); + // 进度信息 + respVO.setUnfinishedTaskActivityIds(unfinishedTaskActivityIds) + .setFinishedTaskActivityIds(finishedTaskActivityIds) + .setFinishedSequenceFlowActivityIds(finishedSequenceFlowActivityIds) + .setRejectedTaskActivityIds(rejectTaskActivityIds); + return respVO; + } + + default UserSimpleBaseVO buildUser(String userIdStr, + Map userMap, + Map deptMap) { + if (StrUtil.isEmpty(userIdStr)) { + return null; + } + Long userId = NumberUtils.parseLong(userIdStr); + return buildUser(userId, userMap, deptMap); + } + + default UserSimpleBaseVO buildUser(Long userId, + Map userMap, + Map deptMap) { + if (userId == null) { + return null; + } + AdminUserRespDTO user = userMap.get(userId); + if (user == null) { + return null; + } + UserSimpleBaseVO userVO = BeanUtils.toBean(user, UserSimpleBaseVO.class); + Long deptId = DeptUtil.getDeptId(user); + DeptRespDTO dept = deptId != null ? deptMap.get(deptId) : null; + if (dept != null) { + userVO.setDeptName(dept.getName()); + } + return userVO; + } + + default BpmApprovalDetailRespVO.ActivityNodeTask buildApprovalTaskInfo(HistoricTaskInstance task) { + if (task == null) { + return null; + } + return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class) + .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)) + .setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task)); + } + + default Set parseUserIds(HistoricProcessInstance processInstance, + List activityNodes, + BpmTaskRespVO todoTask) { + Set userIds = new HashSet<>(); + if (processInstance != null) { + userIds.add(NumberUtils.parseLong(processInstance.getStartUserId())); + } + for (BpmApprovalDetailRespVO.ActivityNode activityNode : activityNodes) { + CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getAssignee)); + CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getOwner)); + CollUtil.addAll(userIds, activityNode.getCandidateUserIds()); + } + if (todoTask != null) { + CollUtil.addIfAbsent(userIds, todoTask.getAssignee()); + CollUtil.addIfAbsent(userIds, todoTask.getOwner()); + if (CollUtil.isNotEmpty(todoTask.getChildren())) { + CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getAssignee)); + CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getOwner)); + } + } + return userIds; + } + + default Set parseUserIds02(HistoricProcessInstance processInstance, + List tasks) { + Set userIds = SetUtils.asSet(Long.valueOf(processInstance.getStartUserId())); + tasks.forEach(task -> { + CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getAssignee()))); + CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getOwner()))); + }); + return userIds; + } + + default BpmApprovalDetailRespVO buildApprovalDetail(BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List activityNodes, + BpmTaskRespVO todoTask, + Map formFieldsPermission, + Map userMap, + Map deptMap) { + // 1.1 流程实例 + BpmProcessInstanceRespVO processInstanceResp = null; + if (processInstance != null) { + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); + Long deptId = DeptUtil.getDeptId(startUser); + DeptRespDTO dept = deptMap.get(deptId); + processInstanceResp = buildProcessInstance(processInstance, null, null, startUser, dept); + } + + // 1.2 流程定义 + BpmProcessDefinitionRespVO definitionResp = BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition( + processDefinition, null, processDefinitionInfo, null, null, bpmnModel); + + // 1.3 流程节点 + activityNodes.forEach(approveNode -> { + if (approveNode.getTasks() != null) { + approveNode.getTasks().forEach(task -> { + task.setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap)); + task.setOwnerUser(buildUser(task.getOwner(), userMap, deptMap)); + }); + } + approveNode.setCandidateUsers(convertList(approveNode.getCandidateUserIds(), userId -> buildUser(userId, userMap, deptMap))); + }); + + // 1.4 待办任务 + if (todoTask != null) { + todoTask.setAssigneeUser(buildUser(todoTask.getAssignee(), userMap, deptMap)); + todoTask.setOwnerUser(buildUser(todoTask.getOwner(), userMap, deptMap)); + if (CollUtil.isNotEmpty(todoTask.getChildren())) { + todoTask.getChildren().forEach(childTask -> { + childTask.setAssigneeUser(buildUser(childTask.getAssignee(), userMap, deptMap)); + childTask.setOwnerUser(buildUser(childTask.getOwner(), userMap, deptMap)); + }); + } + } + + // 2. 拼接起来 + return new BpmApprovalDetailRespVO().setStatus(processInstanceStatus) + .setProcessDefinition(definitionResp) + .setProcessInstance(processInstanceResp) + .setFormFieldsPermission(formFieldsPermission) + .setTodoTask(todoTask) + .setActivityNodes(activityNodes); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java new file mode 100644 index 0000000..5926d84 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java @@ -0,0 +1,233 @@ +package com.zt.plat.module.bpm.convert.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.framework.common.util.date.DateUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; +import static com.zt.plat.framework.common.util.collection.MapUtils.findAndThen; + +/** + * Bpm 任务 Convert + * + * @author ZT + */ +@Mapper +public interface BpmTaskConvert { + + BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class); + + default PageResult buildTodoTaskPage(PageResult pageResult, + Map processInstanceMap, + Map userMap, + Map processDefinitionInfoMap) { + return BeanUtils.toBean(pageResult, BpmTaskRespVO.class, taskVO -> { + ProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId()); + if (processInstance == null) { + return; + } + taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class)); + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); + taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); + taskVO.getProcessInstance().setCreateTime(DateUtils.of(processInstance.getStartTime())); + // 摘要 + taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()), + processInstance.getProcessVariables())); + }); + } + + default PageResult buildTaskPage(PageResult pageResult, + Map processInstanceMap, + Map userMap, + Map deptMap, + Map processDefinitionInfoMap) { + List taskVOList = CollectionUtils.convertList(pageResult.getList(), task -> { + BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); + taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)); + // 用户信息 + AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee())); + if (assignUser != null) { + taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class)); + findAndThen(deptMap, DeptUtil.getDeptId(assignUser), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName())); + } + // 流程实例 + HistoricProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId()); + if (processInstance != null) { + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); + taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class)); + taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); + // 摘要 + taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()), + processInstance.getProcessVariables())); + } + return taskVO; + }); + return new PageResult<>(taskVOList, pageResult.getTotal()); + } + + default List buildTaskListByProcessInstanceId(List taskList, + Map formMap, + Map userMap, + Map deptMap) { + return CollectionUtils.convertList(taskList, task -> { + // 特殊:已取消的任务,不返回 + BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); + Integer taskStatus = FlowableUtils.getTaskStatus(task); + if (BpmTaskStatusEnum.isCancelStatus(taskStatus)) { + return null; + } + taskVO.setStatus(taskStatus).setReason(FlowableUtils.getTaskReason(task)); + // 表单信息 + BpmFormDO form = null; + try { + Long formId = NumberUtils.parseLong(task.getFormKey()); + form = MapUtil.get(formMap, formId, BpmFormDO.class); + } catch (NumberFormatException e) { + // 如果 formKey 不是数字(比如是URL),设置 formPath + taskVO.setFormPath(task.getFormKey()); + taskVO.setFormVariables(FlowableUtils.getTaskFormVariable(task)); + } + if (form != null) { + taskVO.setFormId(form.getId()).setFormName(form.getName()).setFormConf(form.getConf()) + .setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task)); + } + // 用户信息 + buildTaskAssignee(taskVO, task.getAssignee(), userMap, deptMap); + buildTaskOwner(taskVO, task.getOwner(), userMap, deptMap); + return taskVO; + }); + } + + default List buildTaskListByParentTaskId(List taskList, + Map userMap, + Map deptMap) { + return convertList(taskList, task -> BeanUtils.toBean(task, BpmTaskRespVO.class, taskVO -> { + AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee())); + if (assignUser != null) { + taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class)); + DeptRespDTO dept = deptMap.get(DeptUtil.getDeptId(assignUser)); + if (dept != null) { + taskVO.getAssigneeUser().setDeptName(dept.getName()); + } + } + AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner())); + if (ownerUser != null) { + taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class)); + findAndThen(deptMap, DeptUtil.getDeptId(ownerUser), dept -> taskVO.getOwnerUser().setDeptName(dept.getName())); + } + })); + } + + default BpmTaskRespVO buildTodoTask(Task todoTask, List childrenTasks, + Map buttonsSetting, + BpmFormDO form) { + BpmTaskRespVO bpmTaskRespVO = BeanUtils.toBean(todoTask, BpmTaskRespVO.class) + .setStatus(FlowableUtils.getTaskStatus(todoTask)).setReason(FlowableUtils.getTaskReason(todoTask)) + .setButtonsSetting(buttonsSetting) + .setChildren(convertList(childrenTasks, childTask -> BeanUtils.toBean(childTask, BpmTaskRespVO.class) + .setStatus(FlowableUtils.getTaskStatus(childTask)))); + if (form != null) { + bpmTaskRespVO.setFormId(form.getId()).setFormName(form.getName()) + .setFormConf(form.getConf()).setFormFields(form.getFields()); + }else{ + // 任务级别的业务表单 + bpmTaskRespVO.setFormPath(todoTask.getFormKey()); + } + return bpmTaskRespVO; + } + + default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser, + Task task) { + BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO(); + reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId()) + .setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId()) + .setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName()) + .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())); + return reqDTO; + } + + default void buildTaskOwner(BpmTaskRespVO task, String taskOwner, + Map userMap, + Map deptMap) { + AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(taskOwner)); + if (ownerUser != null) { + task.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class)); + findAndThen(deptMap, DeptUtil.getDeptId(ownerUser), dept -> task.getOwnerUser().setDeptName(dept.getName())); + } + } + + default void buildTaskChildren(BpmTaskRespVO task, Map> childrenTaskMap, + Map userMap, Map deptMap) { + List childTasks = childrenTaskMap.get(task.getId()); + if (CollUtil.isNotEmpty(childTasks)) { + task.setChildren( + convertList(childTasks, childTask -> { + BpmTaskRespVO childTaskVO = BeanUtils.toBean(childTask, BpmTaskRespVO.class); + childTaskVO.setStatus(FlowableUtils.getTaskStatus(childTask)); + buildTaskOwner(childTaskVO, childTask.getOwner(), userMap, deptMap); + buildTaskAssignee(childTaskVO, childTask.getAssignee(), userMap, deptMap); + return childTaskVO; + }) + ); + } + } + + default void buildTaskAssignee(BpmTaskRespVO task, String taskAssignee, + Map userMap, + Map deptMap) { + AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(taskAssignee)); + if (assignUser != null) { + task.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class)); + findAndThen(deptMap, DeptUtil.getDeptId(assignUser), dept -> task.getAssigneeUser().setDeptName(dept.getName())); + } + } + + /** + * 将父任务的属性,拷贝到子任务(加签任务) + *

+ * 为什么不使用 mapstruct 映射?因为 TaskEntityImpl 还有很多其他属性,这里我们只设置我们需要的。 + * 使用 mapstruct 会将里面嵌套的各个属性值都设置进去,会出现意想不到的问题。 + * + * @param parentTask 父任务 + * @param childTask 加签任务 + */ + default void copyTo(TaskEntityImpl parentTask, TaskEntityImpl childTask) { + childTask.setName(parentTask.getName()); + childTask.setDescription(parentTask.getDescription()); + childTask.setCategory(parentTask.getCategory()); + childTask.setParentTaskId(parentTask.getId()); + childTask.setProcessDefinitionId(parentTask.getProcessDefinitionId()); + childTask.setProcessInstanceId(parentTask.getProcessInstanceId()); + childTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey()); + childTask.setTaskDefinitionId(parentTask.getTaskDefinitionId()); + childTask.setPriority(parentTask.getPriority()); + childTask.setCreateTime(new Date()); + childTask.setTenantId(parentTask.getTenantId()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md new file mode 100644 index 0000000..d796f41 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md @@ -0,0 +1 @@ + diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java new file mode 100644 index 0000000..a8251da --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java @@ -0,0 +1,54 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BPM 流程分类 DO + * + * @author ZT + */ +@TableName("bpm_category") +@KeySequence("bpm_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmCategoryDO extends BaseDO { + + /** + * 分类编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 分类名 + */ + private String name; + /** + * 分类标志 + */ + private String code; + /** + * 分类描述 + */ + private String description; + /** + * 分类状态 + * + * 枚举 {@link com.zt.plat.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 分类排序 + */ + private Integer sort; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java new file mode 100644 index 0000000..22c69b3 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java @@ -0,0 +1,57 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * BPM 工作流的表单定义 + * 用于工作流的申请表单,需要动态配置的场景 + * + * @author ZT + */ +@TableName(value = "bpm_form", autoResultMap = true) +@KeySequence("bpm_form_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmFormDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 表单名 + */ + private String name; + /** + * 状态 + */ + private Integer status; + /** + * 表单的配置 + */ + private String conf; + /** + * 表单项的数组 + * + * 目前直接将 https://github.com/JakHuang/form-generator 生成的 JSON 串,直接保存 + * 定义:https://github.com/JakHuang/form-generator/issues/46 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List fields; + /** + * 备注 + */ + private String remark; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java new file mode 100644 index 0000000..b65c192 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -0,0 +1,219 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import com.zt.plat.framework.mybatis.core.type.LongListTypeHandler; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.enums.definition.BpmAutoApproveTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; + +import java.util.List; + +/** + * BPM 流程定义的拓信息 + * 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表 + * + * @author ZT + */ +@TableName(value = "bpm_process_definition_info", autoResultMap = true) +@KeySequence("bpm_process_definition_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmProcessDefinitionInfoDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 流程定义的编号 + * + * 关联 {@link ProcessDefinition#getId()} 属性 + */ + private String processDefinitionId; + /** + * 流程模型的编号 + * + * 关联 {@link Model#getId()} 属性 + */ + private String modelId; + /** + * 流程模型的类型 + * + * 枚举 {@link BpmModelTypeEnum} + */ + private Integer modelType; + + /** + * 流程分类的编码 + * + * 关联 {@link BpmCategoryDO#getCode()} + * + * 为什么要存储?原因是,{@link ProcessDefinition#getCategory()} 无法设置 + */ + private String category; + /** + * 图标 + */ + private String icon; + /** + * 描述 + */ + private String description; + + /** + * 表单类型 + * + * 枚举 {@link BpmModelFormTypeEnum} + */ + private Integer formType; + /** + * 动态表单编号 + * + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + * + * 关联 {@link BpmFormDO#getId()} + */ + private Long formId; + /** + * 表单的配置 + * + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + * + * 冗余 {@link BpmFormDO#getConf()} + */ + private String formConf; + /** + * 表单项的数组 + * + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + * + * 冗余 {@link BpmFormDO#getFields()} + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List formFields; + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + * + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomCreatePath; + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + * + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomViewPath; + + /** + * SIMPLE 设计器模型数据 json 格式 + * + * 目的:当使用仿钉钉设计器时。流程模型发布的时候,需要保存流程模型设计器的快照数据。 + */ + private String simpleModel; + /** + * 是否可见 + * + * 目的:如果 false 不可见,则不展示在“发起流程”的列表里 + */ + private Boolean visible; + /** + * 排序值 + */ + private Long sort; + + /** + * 可发起用户编号数组 + * + * 关联 {@link AdminUserRespDTO#getId()} 字段的数组 + * + * 如果为空,则表示“全部可以发起”! + * + * 它和 {@link #visible} 的区别在于: + * 1. {@link #visible} 只是决定是否可见。即使不可见,还是可以发起 + * 2. startUserIds 决定某个用户是否可以发起。如果该用户不可发起,则他也是不可见的 + */ + @TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤 + private List startUserIds; + + /** + * 可发起部门编号数组 + * + * 关联 {@link AdminUserRespDTO#getDeptId()} 字段的数组 + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List startDeptIds; + + /** + * 可管理用户编号数组 + * + * 关联 {@link AdminUserRespDTO#getId()} 字段的数组 + */ + @TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤 + private List managerUserIds; + + /** + * 是否允许撤销审批中的申请 + */ + private Boolean allowCancelRunningProcess; + + /** + * 流程 ID 规则 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.ProcessIdRule processIdRule; + + /** + * 自动去重类型 + * + * 枚举 {@link BpmAutoApproveTypeEnum} + */ + private Integer autoApprovalType; + + /** + * 标题设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.TitleSetting titleSetting; + /** + * 摘要设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.SummarySetting summarySetting; + + /** + * 流程前置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting processBeforeTriggerSetting; + /** + * 流程后置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting processAfterTriggerSetting; + + /** + * 任务前置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting taskBeforeTriggerSetting; + + /** + * 任务后置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting taskAfterTriggerSetting; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java new file mode 100644 index 0000000..441f3cb --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import lombok.*; + +/** + * BPM 流程表达式 DO + * + * @author ZT + */ +@TableName("bpm_process_expression") +@KeySequence("bpm_process_expression_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmProcessExpressionDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 表达式名字 + */ + private String name; + /** + * 表达式状态 + * + * 枚举 {@link TODO common_status 对应的类} + */ + private Integer status; + /** + * 表达式 + */ + private String expression; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java new file mode 100644 index 0000000..9c0f05c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java @@ -0,0 +1,74 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import com.zt.plat.module.bpm.enums.definition.BpmProcessListenerTypeEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BPM 流程监听器 DO + * + * 目的:本质上它是流程监听器的模版,用于 BPMN 在设计时,直接选择这些模版 + * + * @author ZT + */ +@TableName(value = "bpm_process_listener") +@KeySequence("bpm_process_listener_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmProcessListenerDO extends BaseDO { + + /** + * 主键 ID,自增 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 监听器名字 + */ + private String name; + /** + * 状态 + * + * 枚举 {@link com.zt.plat.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 监听类型 + * + * 枚举 {@link BpmProcessListenerTypeEnum} + * + * 1. execution:ExecutionListener 执行监听器 + * 2. task:TaskListener 任务监听器 + */ + private String type; + /** + * 监听事件 + * + * execution 时:start、end + * task 时:create 创建、assignment 指派、complete 完成、delete 删除、update 更新、timeout 超时 + */ + private String event; + + /** + * 值类型 + * + * 1. class:Java 类,ExecutionListener 需要 {@link org.flowable.engine.delegate.JavaDelegate},TaskListener 需要 {@link org.flowable.engine.delegate.TaskListener} + * 2. delegateExpression:委托表达式,在 class 的基础上,需要注册到 Spring 容器里,后续表达式通过 Spring Bean 名称即可 + * 3. expression:表达式,一个普通类的普通方法,将这个普通类注册到 Spring 容器中,然后表达式中还可以执行这个类中的方法 + */ + private String valueType; + /** + * 值 + */ + private String value; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java new file mode 100644 index 0000000..5b5692f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java @@ -0,0 +1,52 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Set; + +/** + * BPM 用户组 + * + * @author ZT + */ +@TableName(value = "bpm_user_group", autoResultMap = true) +@KeySequence("bpm_user_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmUserGroupDO extends BaseDO { + + /** + * 编号,自增 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 组名 + */ + private String name; + /** + * 描述 + */ + private String description; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 成员用户编号数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Set userIds; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java new file mode 100644 index 0000000..fa17735 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java @@ -0,0 +1,78 @@ +package com.zt.plat.module.bpm.dal.dataobject.oa; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * OA 请假申请 DO + * + * {@link #day} 请假天数,目前先简单做。一般是分成请假上午和下午,可以是 1 整天,可以是 0.5 半天 + * + * @author jason + * @author ZT + */ +@TableName("bpm_oa_leave") +@KeySequence("bpm_oa_leave_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmOALeaveDO extends BaseDO { + + /** + * 请假表单主键 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 申请人的用户编号 + * + * 关联 AdminUserDO 的 id 属性 + */ + private Long userId; + /** + * 请假类型 + */ + private String type; + /** + * 原因 + */ + private String reason; + /** + * 开始时间 + */ + private LocalDateTime startTime; + /** + * 结束时间 + */ + private LocalDateTime endTime; + /** + * 请假天数 + */ + private Long day; + /** + * 审批结果 + * + * 枚举 {@link BpmTaskStatusEnum} + * 考虑到简单,所以直接复用了 BpmProcessInstanceStatusEnum 枚举,也可以自己定义一个枚举哈 + */ + private Integer status; + + /** + * 对应的流程编号 + * + * 关联 ProcessInstance 的 id 属性 + */ + private String processInstanceId; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java new file mode 100644 index 0000000..779d11d --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java @@ -0,0 +1,98 @@ +package com.zt.plat.module.bpm.dal.dataobject.task; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.flowable.bpmn.model.FlowNode; +import org.flowable.task.api.history.HistoricTaskInstance; + +/** + * 流程抄送 DO + * + * @author kyle + * @since 2024-01-22 + */ +@TableName(value = "bpm_process_instance_copy", autoResultMap = true) +@KeySequence("bpm_process_instance_copy_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmProcessInstanceCopyDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 发起人 Id + * + * 冗余 ProcessInstance 的 startUserId 字段 + */ + private Long startUserId; + /** + * 流程名 + * + * 冗余 ProcessInstance 的 name 字段 + */ + private String processInstanceName; + /** + * 流程实例的编号 + * + * 关联 ProcessInstance 的 id 属性 + */ + private String processInstanceId; + /** + * 流程实例的流程定义编号 + * + * 关联 ProcessInstance 的 processDefinitionId 属性 + */ + private String processDefinitionId; + /** + * 流程分类 + * + * 冗余 ProcessInstance 的 category 字段 + */ + private String category; + /** + * 流程活动的编号 + *

+ * + * 冗余 {@link FlowNode#getId()},对应 BPMN XML 节点编号 + * 原因:用于查询抄送节点的表单字段权限。因为仿钉钉/飞书的抄送节点 (ServiceTask),没有 taskId,只有 activityId + */ + private String activityId; + /** + * 流程活动的名字 + * + * 冗余 {@link FlowNode#getName()} + */ + private String activityName; + /** + * 流程活动的编号 + * + * 关联 {@link HistoricTaskInstance#getId()} + */ + private String taskId; + + /** + * 用户编号(被抄送的用户编号) + * + * 关联 system_users 的 id 属性 + */ + private Long userId; + + /** + * 抄送意见 + */ + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java new file mode 100644 index 0000000..5c36ae4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.dal.mysql.category; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * BPM 流程分类 Mapper + * + * @author ZT + */ +@Mapper +public interface BpmCategoryMapper extends BaseMapperX { + + default PageResult selectPage(BpmCategoryPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(BpmCategoryDO::getName, reqVO.getName()) + .likeIfPresent(BpmCategoryDO::getCode, reqVO.getCode()) + .eqIfPresent(BpmCategoryDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(BpmCategoryDO::getCreateTime, reqVO.getCreateTime()) + .orderByAsc(BpmCategoryDO::getSort)); + } + + default BpmCategoryDO selectByName(String name) { + return selectOne(BpmCategoryDO::getName, name); + } + + default BpmCategoryDO selectByCode(String code) { + return selectOne(BpmCategoryDO::getCode, code); + } + + default List selectListByCode(Collection codes) { + return selectList(BpmCategoryDO::getCode, codes); + } + + default List selectListByStatus(Integer status) { + return selectList(BpmCategoryDO::getStatus, status); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java new file mode 100644 index 0000000..db73b65 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java @@ -0,0 +1,25 @@ +package com.zt.plat.module.bpm.dal.mysql.definition; + + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.QueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 动态表单 Mapper + * + * @author 风里雾里 + */ +@Mapper +public interface BpmFormMapper extends BaseMapperX { + + default PageResult selectPage(BpmFormPageReqVO reqVO) { + return selectPage(reqVO, new QueryWrapperX() + .likeIfPresent("name", reqVO.getName()) + .orderByDesc("id")); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java new file mode 100644 index 0000000..5d5be48 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.bpm.dal.mysql.definition; + +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface BpmProcessDefinitionInfoMapper extends BaseMapperX { + + default List selectListByProcessDefinitionIds(Collection processDefinitionIds) { + return selectList(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionIds); + } + + default BpmProcessDefinitionInfoDO selectByProcessDefinitionId(String processDefinitionId) { + return selectOne(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionId); + } + + default void updateByModelId(String modelId, BpmProcessDefinitionInfoDO updateObj) { + update(updateObj, + new LambdaQueryWrapperX().eq(BpmProcessDefinitionInfoDO::getModelId, modelId)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java new file mode 100644 index 0000000..4551fd3 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java @@ -0,0 +1,26 @@ +package com.zt.plat.module.bpm.dal.mysql.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * BPM 流程表达式 Mapper + * + * @author ZT + */ +@Mapper +public interface BpmProcessExpressionMapper extends BaseMapperX { + + default PageResult selectPage(BpmProcessExpressionPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(BpmProcessExpressionDO::getName, reqVO.getName()) + .eqIfPresent(BpmProcessExpressionDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(BpmProcessExpressionDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmProcessExpressionDO::getId)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java new file mode 100644 index 0000000..6f6c89e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.bpm.dal.mysql.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * BPM 流程监听器 Mapper + * + * @author ZT + */ +@Mapper +public interface BpmProcessListenerMapper extends BaseMapperX { + + default PageResult selectPage(BpmProcessListenerPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(BpmProcessListenerDO::getName, reqVO.getName()) + .eqIfPresent(BpmProcessListenerDO::getType, reqVO.getType()) + .eqIfPresent(BpmProcessListenerDO::getEvent, reqVO.getEvent()) + .eqIfPresent(BpmProcessListenerDO::getStatus, reqVO.getStatus()) + .orderByDesc(BpmProcessListenerDO::getId)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java new file mode 100644 index 0000000..4c52531 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.dal.mysql.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 用户组 Mapper + * + * @author ZT + */ +@Mapper +public interface BpmUserGroupMapper extends BaseMapperX { + + default PageResult selectPage(BpmUserGroupPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(BpmUserGroupDO::getName, reqVO.getName()) + .eqIfPresent(BpmUserGroupDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(BpmUserGroupDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmUserGroupDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(BpmUserGroupDO::getStatus, status); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java new file mode 100644 index 0000000..7e0f439 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java @@ -0,0 +1,29 @@ +package com.zt.plat.module.bpm.dal.mysql.oa; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 请假申请 Mapper + * + * @author jason + * @author ZT + */ +@Mapper +public interface BpmOALeaveMapper extends BaseMapperX { + + default PageResult selectPage(Long userId, BpmOALeavePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(BpmOALeaveDO::getUserId, userId) + .eqIfPresent(BpmOALeaveDO::getStatus, reqVO.getStatus()) + .eqIfPresent(BpmOALeaveDO::getType, reqVO.getType()) + .likeIfPresent(BpmOALeaveDO::getReason, reqVO.getReason()) + .betweenIfPresent(BpmOALeaveDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmOALeaveDO::getId)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java new file mode 100644 index 0000000..61a73c3 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java @@ -0,0 +1,25 @@ +package com.zt.plat.module.bpm.dal.mysql.task; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface BpmProcessInstanceCopyMapper extends BaseMapperX { + + default PageResult selectPage(Long loginUserId, BpmProcessInstanceCopyPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(BpmProcessInstanceCopyDO::getUserId, loginUserId) + .likeIfPresent(BpmProcessInstanceCopyDO::getProcessInstanceName, reqVO.getProcessInstanceName()) + .betweenIfPresent(BpmProcessInstanceCopyDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmProcessInstanceCopyDO::getId)); + } + + default void deleteByProcessInstanceId(String processInstanceId) { + delete(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java new file mode 100644 index 0000000..77e3a30 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java @@ -0,0 +1,62 @@ +package com.zt.plat.module.bpm.dal.redis; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import jakarta.annotation.Resource; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import java.time.Duration; +import java.time.LocalDateTime; + +import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN; +import static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN; + +/** + * BPM 流程 Id 编码的 Redis DAO + * + * @author Lesan + */ +@Repository +public class BpmProcessIdRedisDAO { + + @Resource + private StringRedisTemplate stringRedisTemplate; + + /** + * 生成序号,使用定义的 processIdRule 规则生成 + * + * @param processIdRule 规则 + * @return 序号 + */ + public String generate(BpmModelMetaInfoVO.ProcessIdRule processIdRule) { + // 生成日期前缀 + String infix = ""; + switch (processIdRule.getInfix()) { + case "DAY": + infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN); + break; + case "HOUR": + infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + "HH"); + break; + case "MINUTE": + infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + "HHmm"); + break; + case "SECOND": + infix = DateUtil.format(LocalDateTime.now(), PURE_DATETIME_PATTERN); + break; + } + + // 生成序号 + String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix(); + String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix; + Long no = stringRedisTemplate.opsForValue().increment(key); + if (StrUtil.isNotEmpty(infix)) { + // 特殊:没有前缀,则不能过期,不能每次都是从 0 开始 + stringRedisTemplate.expire(key, Duration.ofDays(1L)); + } + return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java new file mode 100644 index 0000000..9146bc9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java @@ -0,0 +1,15 @@ +package com.zt.plat.module.bpm.dal.redis; + +/** + * BPM Redis Key 枚举类 + * + * @author ZT + */ +public interface RedisKeyConstants { + + /** + * 流程 ID 的缓存 + */ + String BPM_PROCESS_ID = "bpm:process_id:"; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java new file mode 100644 index 0000000..f0d5b49 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java @@ -0,0 +1,95 @@ +package com.zt.plat.module.bpm.framework.flowable.config; + +import cn.hutool.core.collection.ListUtil; +import com.zt.plat.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; +import com.zt.plat.module.system.api.user.AdminUserApi; +import org.flowable.common.engine.api.delegate.FlowableFunctionDelegate; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncListenableTaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.List; + +/** + * BPM 模块的 Flowable 配置类 + * + * @author jason + */ +@Configuration(proxyBeanMethods = false) +public class BpmFlowableConfiguration { + + /** + * 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean + * + * 如果不创建,会导致项目启动时,Flowable 报错的问题 + */ + @Bean(name = "applicationTaskExecutor") + @ConditionalOnMissingBean(name = "applicationTaskExecutor") + public AsyncListenableTaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(8); + executor.setMaxPoolSize(8); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("flowable-task-Executor-"); + executor.setAwaitTerminationSeconds(30); + executor.setWaitForTasksToCompleteOnShutdown(true); + executor.setAllowCoreThreadTimeOut(true); + executor.initialize(); + return executor; + } + + /** + * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类: + * + * 1. 设置各种监听器 + * 2. 设置自定义的 ActivityBehaviorFactory 实现 + */ + @Bean + public EngineConfigurationConfigurer bpmProcessEngineConfigurationConfigurer( + ObjectProvider listeners, + ObjectProvider customFlowableFunctionDelegates, + BpmActivityBehaviorFactory bpmActivityBehaviorFactory) { + return configuration -> { + // 注册监听器,例如说 BpmActivityEventListener + configuration.setEventListeners(ListUtil.toList(listeners.iterator())); + // 设置 ActivityBehaviorFactory 实现类,用于流程任务的审核人的自定义 + configuration.setActivityBehaviorFactory(bpmActivityBehaviorFactory); + // 设置自定义的函数 + configuration.setCustomFlowableFunctionDelegates(ListUtil.toList(customFlowableFunctionDelegates.stream().iterator())); + }; + } + + // =========== 审批人相关的 Bean ========== + + @Bean + public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskCandidateInvoker bpmTaskCandidateInvoker) { + BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory(); + bpmActivityBehaviorFactory.setTaskCandidateInvoker(bpmTaskCandidateInvoker); + return bpmActivityBehaviorFactory; + } + + @Bean + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") // adminUserApi 可以注入成功 + public BpmTaskCandidateInvoker bpmTaskCandidateInvoker(List strategyList, + AdminUserApi adminUserApi) { + return new BpmTaskCandidateInvoker(strategyList, adminUserApi); + } + + // =========== 自己拓展的 Bean ========== + + @Bean + public BpmProcessInstanceEventPublisher processInstanceEventPublisher(ApplicationEventPublisher publisher) { + return new BpmProcessInstanceEventPublisher(publisher); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java new file mode 100644 index 0000000..ec9cd8f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java @@ -0,0 +1,44 @@ +package com.zt.plat.module.bpm.framework.flowable.core.behavior; + +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import lombok.Setter; +import org.flowable.bpmn.model.Activity; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; +import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory; + +/** + * 自定义的 ActivityBehaviorFactory 实现类,目的如下: + * 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配 + * + * @author ZT + */ +@Setter +public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory { + + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Override + public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) { + return new BpmUserTaskActivityBehavior(userTask) + .setTaskCandidateInvoker(taskCandidateInvoker); + } + + @Override + public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity, + AbstractBpmnActivityBehavior behavior) { + return new BpmParallelMultiInstanceBehavior(activity, behavior) + .setTaskCandidateInvoker(taskCandidateInvoker); + } + + @Override + public SequentialMultiInstanceBehavior createSequentialMultiInstanceBehavior(Activity activity, + AbstractBpmnActivityBehavior behavior) { + return new BpmSequentialMultiInstanceBehavior(activity, behavior) + .setTaskCandidateInvoker(taskCandidateInvoker); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java new file mode 100644 index 0000000..2df2ce7 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java @@ -0,0 +1,91 @@ +package com.zt.plat.module.bpm.framework.flowable.core.behavior; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import lombok.Setter; +import org.flowable.bpmn.model.Activity; +import org.flowable.bpmn.model.CallActivity; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; + +import java.util.List; +import java.util.Set; + +/** + * 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配 + * 第一步,基于分配规则,计算出分配任务的【多个】候选人们。 + * 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它 + * + * @author kemengkai + * @since 2022-04-21 16:57 + */ +@Setter +public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior { + + private BpmTaskCandidateInvoker taskCandidateInvoker; + + public BpmParallelMultiInstanceBehavior(Activity activity, + AbstractBpmnActivityBehavior innerActivityBehavior) { + super(activity, innerActivityBehavior); + } + + /** + * 重写该方法,主要实现两个功能: + * 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式,而是采用自己定义的 + * 2. 获得任务的处理人,并设置到 collectionVariable 中,用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人 + * + * 注意,多个任务实例,每个任务实例对应一个处理人,所以返回的数量就是任务处理人的数量 + * + * @param execution 执行任务 + * @return 数量 + */ + @Override + protected int resolveNrOfInstances(DelegateExecution execution) { + // 情况一:UserTask 节点 + if (execution.getCurrentFlowElement() instanceof UserTask) { + // 第一步,设置 collectionVariable 和 CollectionVariable + // 从 execution.getVariable() 读取所有任务处理人的 key + super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); + // 从 execution.getVariable() 读取当前所有任务处理的人的 key + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); + + // 第二步,获取任务的所有处理人 + @SuppressWarnings("unchecked") + Set assigneeUserIds = (Set) execution.getVariable(super.collectionVariable, Set.class); + if (assigneeUserIds == null) { + assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution); + if (CollUtil.isEmpty(assigneeUserIds)) { + // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过! + // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务 + // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时 + assigneeUserIds = SetUtils.asSet((Long) null); + } + execution.setVariableLocal(super.collectionVariable, assigneeUserIds); + } + return assigneeUserIds.size(); + } + + // 情况二:CallActivity 节点 + if (execution.getCurrentFlowElement() instanceof CallActivity) { + FlowElement flowElement = execution.getCurrentFlowElement(); + Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); + } + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); + } + } + + return super.resolveNrOfInstances(execution); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java new file mode 100644 index 0000000..2f7f76d --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java @@ -0,0 +1,95 @@ +package com.zt.plat.module.bpm.framework.flowable.core.behavior; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import lombok.Setter; +import org.flowable.bpmn.model.Activity; +import org.flowable.bpmn.model.CallActivity; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; + +import java.util.List; +import java.util.Set; + +/** + * 自定义的【串行】的【多个】流程任务的 assignee 负责人的分配 + * + * 本质上,实现和 {@link BpmParallelMultiInstanceBehavior} 一样,只是继承的类不一样 + * + * @author ZT + */ +@Setter +public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceBehavior { + + private BpmTaskCandidateInvoker taskCandidateInvoker; + + public BpmSequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) { + super(activity, innerActivityBehavior); + } + + /** + * 逻辑和 {@link BpmParallelMultiInstanceBehavior#resolveNrOfInstances(DelegateExecution)} 类似 + * + * 差异的点:是在【第二步】的时候,需要返回 LinkedHashSet 集合!因为它需要有序! + */ + @Override + protected int resolveNrOfInstances(DelegateExecution execution) { + // 情况一:UserTask 节点 + if (execution.getCurrentFlowElement() instanceof UserTask) { + // 第一步,设置 collectionVariable 和 CollectionVariable + // 从 execution.getVariable() 读取所有任务处理人的 key + super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); + // 从 execution.getVariable() 读取当前所有任务处理的人的 key + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); + + // 第二步,获取任务的所有处理人 + // 不使用 execution.getVariable 原因:目前依次审批任务回退后 collectionVariable 变量没有清理, 如果重新进入该任务不会重新分配审批人 + @SuppressWarnings("unchecked") + Set assigneeUserIds = (Set) execution.getVariableLocal(super.collectionVariable, Set.class); + if (assigneeUserIds == null) { + assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution); + if (CollUtil.isEmpty(assigneeUserIds)) { + // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过! + // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务 + // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时 + assigneeUserIds = SetUtils.asSet((Long) null); + } + execution.setVariableLocal(super.collectionVariable, assigneeUserIds); + } + return assigneeUserIds.size(); + } + + // 情况二:CallActivity 节点 + if (execution.getCurrentFlowElement() instanceof CallActivity) { + FlowElement flowElement = execution.getCurrentFlowElement(); + Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); + } + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); + } + } + + return super.resolveNrOfInstances(execution); + } + + @Override + protected void executeOriginalBehavior(DelegateExecution execution, ExecutionEntity multiInstanceRootExecution, int loopCounter) { + // 参见 https://gitee.com/zhijiantianya/zt-cloud/issues/IC239F + super.collectionExpression = null; + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); + super.executeOriginalBehavior(execution, multiInstanceRootExecution, loopCounter); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java new file mode 100644 index 0000000..8d29c54 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java @@ -0,0 +1,86 @@ +package com.zt.plat.module.bpm.framework.flowable.core.behavior; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.RandomUtil; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.UserTask; +import org.flowable.common.engine.impl.el.ExpressionManager; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.impl.util.TaskHelper; +import org.flowable.engine.interceptor.CreateUserTaskBeforeContext; +import org.flowable.task.service.TaskService; +import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Set; + +/** + * 自定义的【单个】流程任务的 assignee 负责人的分配 + * 第一步,基于分配规则,计算出分配任务的【单个】候选人。如果找不到,则直接报业务异常,不继续执行后续的流程; + * 第二步,随机选择一个候选人,则选择作为 assignee 负责人。 + * + * @author ZT + */ +@Slf4j +public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { + + @Setter + private BpmTaskCandidateInvoker taskCandidateInvoker; + + public BpmUserTaskActivityBehavior(UserTask userTask) { + super(userTask); + } + + @Override + @Transactional(rollbackFor = Exception.class) + protected void handleAssignments(TaskService taskService, String assignee, String owner, + List candidateUsers, List candidateGroups, TaskEntity task, ExpressionManager expressionManager, + DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { + // 第一步,获得任务的候选用户 + Long assigneeUserId = calculateTaskCandidateUsers(execution); + // 第二步,设置作为负责人 + if (assigneeUserId != null) { + TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); + } + } + + private Long calculateTaskCandidateUsers(DelegateExecution execution) { + // 情况一,如果是多实例的任务,例如说会签、或签等情况,则从 Variable 中获取。 + // 顺序审批可见 BpmSequentialMultiInstanceBehavior,并发审批可见 BpmSequentialMultiInstanceBehavior + if (super.multiInstanceActivityBehavior != null) { + return execution.getVariable(super.multiInstanceActivityBehavior.getCollectionElementVariable(), Long.class); + } + + // 情况二,如果非多实例的任务,则计算任务处理人 + // 第一步,先计算可处理该任务的处理人们 + Set candidateUserIds = taskCandidateInvoker.calculateUsersByTask(execution); + if (CollUtil.isEmpty(candidateUserIds)) { + return null; + } + // 第二步,后随机选择一个任务的处理人 + // 疑问:为什么一定要选择一个任务处理人? + // 解答:项目对 bpm 的任务是责任到人,所以每个任务有且仅有一个处理人。 + // 如果希望一个任务可以同时被多个人处理,可以考虑使用 BpmParallelMultiInstanceBehavior 实现的会签 or 或签。 + int index = RandomUtil.randomInt(candidateUserIds.size()); + return CollUtil.get(candidateUserIds, index); + } + + @Override + protected void handleCategory(CreateUserTaskBeforeContext beforeContext, ExpressionManager expressionManager, + TaskEntity task, DelegateExecution execution) { + ProcessDefinitionEntity processDefinitionEntity = CommandContextUtil.getProcessDefinitionEntityManager().findById(execution.getProcessDefinitionId()); + if (processDefinitionEntity == null) { + log.warn("[handleCategory][任务编号({}) 找不到流程定义({})]", task.getId(), execution.getProcessDefinitionId()); + return; + } + task.setCategory(processDefinitionEntity.getCategory()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java new file mode 100644 index 0000000..cdfee97 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java @@ -0,0 +1,207 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.google.common.annotations.VisibleForTesting; +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.util.object.ObjectUtils; +import com.zt.plat.framework.datapermission.core.annotation.DataPermission; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.CallActivity; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; + +import java.util.*; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG; + +/** + * {@link BpmTaskCandidateStrategy} 的调用者,用于调用对应的策略,实现任务的候选人的计算 + * + * @author ZT + */ +@Slf4j +public class BpmTaskCandidateInvoker { + + private final Map strategyMap = new HashMap<>(); + + private final AdminUserApi adminUserApi; + + public BpmTaskCandidateInvoker(List strategyList, + AdminUserApi adminUserApi) { + strategyList.forEach(strategy -> { + BpmTaskCandidateStrategy oldStrategy = strategyMap.put(strategy.getStrategy(), strategy); + Assert.isNull(oldStrategy, "策略(%s) 重复", strategy.getStrategy()); + }); + this.adminUserApi = adminUserApi; + } + + /** + * 校验流程模型的任务分配规则全部都配置了 + * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去! + * + * @param bpmnBytes BPMN XML + */ + public void validateBpmnConfig(byte[] bpmnBytes) { + BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); + assert bpmnModel != null; + List userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); + // 遍历所有的 UserTask,校验审批人配置 + userTaskList.forEach(userTask -> { + // 1.1 非人工审批,无需校验审批人配置 + Integer approveType = BpmnModelUtils.parseApproveType(userTask); + if (ObjectUtils.equalsAny(approveType, + BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), + BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { + return; + } + // 1.2 非空校验 + Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask); + String param = BpmnModelUtils.parseCandidateParam(userTask); + if (strategy == null) { + throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName()); + } + BpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy); + if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) { + throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName()); + } + // 2. 具体策略校验 + getCandidateStrategy(strategy).validateParam(param); + }); + } + + /** + * 计算任务的候选人 + * + * @param execution 执行任务 + * @return 用户编号集合 + */ + @DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人 + public Set calculateUsersByTask(DelegateExecution execution) { + // 注意:解决极端情况下,Flowable 异步调用,导致租户 id 丢失的情况 + // 例如说,SIMPLE 延迟器在 trigger 的时候!!! + return FlowableUtils.execute(execution.getTenantId(), () -> { + // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 + FlowElement flowElement = execution.getCurrentFlowElement(); + Integer approveType = BpmnModelUtils.parseApproveType(flowElement); + if (ObjectUtils.equalsAny(approveType, + BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), + BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { + return new HashSet<>(); + } + + // 1.1 计算任务的候选人 + Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); + String param = BpmnModelUtils.parseCandidateParam(flowElement); + Set userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param); + // 1.2 移除被禁用的用户 + removeDisableUsers(userIds); + + // 2. 候选人为空时,根据“审批人为空”的配置补充 + if (CollUtil.isEmpty(userIds)) { + userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy()) + .calculateUsersByTask(execution, param); + // ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!! + } + + // 3. 移除发起人的用户 + ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class) + .getProcessInstance(execution.getProcessInstanceId()); + Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId()); + removeStartUserIfSkip(userIds, flowElement, Long.valueOf(processInstance.getStartUserId())); + return userIds; + }); + } + + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, + Long startUserId, String processDefinitionId, Map processVariables) { + // 如果是 CallActivity 子流程,不进行计算候选人 + FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); + if (flowElement instanceof CallActivity) { + return new HashSet<>(); + } + // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 + Integer approveType = BpmnModelUtils.parseApproveType(flowElement); + if (ObjectUtils.equalsAny(approveType, + BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), + BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { + return new HashSet<>(); + } + + // 1.1 计算任务的候选人 + Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); + String param = BpmnModelUtils.parseCandidateParam(flowElement); + Set userIds = getCandidateStrategy(strategy).calculateUsersByActivity(bpmnModel, activityId, param, + startUserId, processDefinitionId, processVariables); + // 1.2 移除被禁用的用户 + removeDisableUsers(userIds); + + // 2. 候选人为空时,根据“审批人为空”的配置补充 + if (CollUtil.isEmpty(userIds)) { + userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy()) + .calculateUsersByActivity(bpmnModel, activityId, param, startUserId, processDefinitionId, processVariables); + // ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!! + } + + // 3. 移除发起人的用户 + removeStartUserIfSkip(userIds, flowElement, startUserId); + return userIds; + } + + @VisibleForTesting + void removeDisableUsers(Set assigneeUserIds) { + if (CollUtil.isEmpty(assigneeUserIds)) { + return; + } + Map userMap = adminUserApi.getUserMap(assigneeUserIds); + assigneeUserIds.removeIf(id -> { + AdminUserRespDTO user = userMap.get(id); + return user == null || CommonStatusEnum.isDisable(user.getStatus()); + }); + } + + /** + * 如果“审批人与发起人相同时”,配置了 SKIP 跳过,则移除发起人 + * + * 注意:如果只有一个候选人,则不处理,避免无法审批 + * + * @param assigneeUserIds 当前分配的候选人 + * @param flowElement 当前节点 + * @param startUserId 发起人 + */ + @VisibleForTesting + void removeStartUserIfSkip(Set assigneeUserIds, FlowElement flowElement, Long startUserId) { + if (CollUtil.size(assigneeUserIds) <= 1) { + return; + } + Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(flowElement); + if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { + return; + } + assigneeUserIds.remove(startUserId); + } + + private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) { + BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy); + Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy); + BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum); + Assert.notNull(strategyObj, "策略(%s) 不存在", strategy); + return strategyObj; + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java new file mode 100644 index 0000000..2d916be --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java @@ -0,0 +1,85 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate; + +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; + +import java.util.Map; +import java.util.Set; + +/** + * BPM 任务的候选人的策略接口 + *

+ * 例如说:分配审批人 + * + * @author ZT + */ +public interface BpmTaskCandidateStrategy { + + /** + * 对应策略 + * + * @return 策略 + */ + BpmTaskCandidateStrategyEnum getStrategy(); + + /** + * 校验参数 + * + * @param param 参数 + */ + void validateParam(String param); + + /** + * 是否一定要输入参数 + * + * @return 是否 + */ + default boolean isParamRequired() { + return true; + } + + /** + * 基于候选人参数,获得任务的候选用户们 + * + * 注意:实现 calculateUsers 系列方法时,有两种选择: + * 1. 只重写 calculateUsers 默认方法 + * 2. 都重写 calculateUsersByTask 和 calculateUsersByActivity 两个方法 + * + * @param param 执行任务 + * @return 用户编号集合 + */ + default Set calculateUsers(String param) { + throw new UnsupportedOperationException("该分配方法未实现,请检查!"); + } + + /** + * 基于【执行任务】,获得任务的候选用户们 + * + * @param execution 执行任务 + * @return 用户编号集合 + */ + default Set calculateUsersByTask(DelegateExecution execution, String param) { + return calculateUsers(param); + } + + /** + * 基于【流程活动】,获得任务的候选用户们 + *

+ * 目的:用于获取未执行节点的候选用户们 + * + * @param bpmnModel 流程图 + * @param activityId 活动 ID (对应 Bpmn XML id) + * @param param 节点的参数 + * @param startUserId 流程发起人编号 + * @param processDefinitionId 流程定义编号 + * @param processVariables 流程变量 + * @return 用户编号集合 + */ + @SuppressWarnings("unused") + default Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + return calculateUsers(param); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java new file mode 100644 index 0000000..fd34457 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java @@ -0,0 +1,79 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.expression; + +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static java.util.Collections.emptySet; + +/** + * 分配给发起人的 Leader 审批的 Expression 流程表达式 + * 目前 Leader 的定义是,发起人所在部门的 Leader + * + * @author ZT + */ +@Component +public class BpmTaskAssignLeaderExpression { + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @Resource + private BpmProcessInstanceService processInstanceService; + + /** + * 计算审批的候选人 + * + * @param execution 流程执行实体 + * @param level 指定级别 + * @return 指定级别的领导 + */ + public Set calculateUsers(DelegateExecution execution, int level) { + Assert.isTrue(level > 0, "level 必须大于 0"); + // 获得发起人 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + // 获得对应 leve 的部门 + DeptRespDTO dept = null; + for (int i = 0; i < level; i++) { + // 获得 level 对应的部门 + if (dept == null) { + dept = getStartUserDept(startUserId); + if (dept == null) { // 找不到发起人的部门,所以无法使用该规则 + return emptySet(); + } + } else { + DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()).getCheckedData(); + if (parentDept == null) { // 找不到父级部门,所以只好结束寻找。原因是:例如说,级别比较高的人,所在部门层级比较少 + break; + } + dept = parentDept; + } + } + return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet(); + } + + private DeptRespDTO getStartUserDept(Long startUserId) { + AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData(); + Long deptId = DeptUtil.getDeptId(startUser); + if (deptId == 0L) { // 找不到部门,所以无法使用该规则 + return null; + } + return deptApi.getDept(deptId).getCheckedData(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java new file mode 100644 index 0000000..bee41e7 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.expression; + +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Component; + +import java.util.Set; + +/** + * 分配给发起人审批的 Expression 流程表达式 + * + * @author ZT + */ +@Component +public class BpmTaskAssignStartUserExpression { + + @Resource + private BpmProcessInstanceService processInstanceService; + + /** + * 计算审批的候选人 + * + * @param execution 流程执行实体 + * @return 发起人 + */ + public Set calculateUsers(ExecutionEntityImpl execution) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + return SetUtils.asSet(startUserId); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java new file mode 100644 index 0000000..9c0a760 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java @@ -0,0 +1,95 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * 部门的负责人 {@link BpmTaskCandidateStrategy} 抽象类 + * + * @author jason + */ +public abstract class AbstractBpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy { + + @Resource + protected DeptApi deptApi; + @Resource + protected AdminUserApi adminUserApi; + + /** + * 获得指定层级的部门负责人,只有第 level 的负责人 + * + * @param dept 指定部门 + * @param level 第几级 + * @return 部门负责人的编号 + */ + protected Long getAssignLevelDeptLeaderId(DeptRespDTO dept, Integer level) { + Assert.isTrue(level > 0, "level 必须大于 0"); + if (dept == null) { + return null; + } + DeptRespDTO currentDept = dept; + for (int i = 1; i < level; i++) { + DeptRespDTO parentDept = deptApi.getDept(currentDept.getParentId()).getCheckedData(); + if (parentDept == null) { // 找不到父级部门,到了最高级。返回最高级的部门负责人 + break; + } + currentDept = parentDept; + } + return currentDept.getLeaderUserId(); + } + + /** + * 获得连续层级的部门负责人,包含 [1, level] 的负责人 + * + * @param deptIds 指定部门编号数组 + * @param level 最大层级 + * @return 连续部门负责人 Id + */ + protected Set getMultiLevelDeptLeaderIds(List deptIds, Integer level) { + Assert.isTrue(level > 0, "level 必须大于 0"); + if (CollUtil.isEmpty(deptIds)) { + return new HashSet<>(); + } + Set deptLeaderIds = new LinkedHashSet<>(); // 保证有序 + for (Long deptId : deptIds) { + DeptRespDTO dept = deptApi.getDept(deptId).getCheckedData(); + for (int i = 0; i < level; i++) { + if (dept.getLeaderUserId() != null) { + deptLeaderIds.add(dept.getLeaderUserId()); + } + DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()).getCheckedData(); + if (parentDept == null) { // 找不到父级部门. 已经到了最高层级了 + break; + } + dept = parentDept; + } + } + return deptLeaderIds; + } + + /** + * 获取发起人的部门 + * + * @param startUserId 发起人 Id + */ + protected DeptRespDTO getStartUserDept(Long startUserId) { + AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData(); + if (CollUtil.isEmpty(startUser.getDeptIds())) { // 找不到部门 + return null; + } + return deptApi.getDept(DeptUtil.getDeptId(startUser)).getCheckedData(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java new file mode 100644 index 0000000..e56d6b9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -0,0 +1,78 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import com.google.common.collect.Sets; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +/** + * 审批人自选 {@link BpmTaskCandidateUserStrategy} 实现类 + * 审批人在审批时选择下一个节点的审批人 + * + * @author smallNorthLee + */ +@Component +public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT; + } + + @Override + public void validateParam(String param) {} + + @Override + public boolean isParamRequired() { + return false; + } + + @Override + public LinkedHashSet calculateUsersByTask(DelegateExecution execution, String param) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId()); + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance); + Assert.notNull(approveUserSelectAssignees, "流程实例({}) 的下一个执行节点审批人不能为空", + execution.getProcessInstanceId()); + if (approveUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } + // 获得审批人 + List assignees = approveUserSelectAssignees.get(execution.getCurrentActivityId()); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + + @Override + public LinkedHashSet calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + if (processVariables == null) { + return Sets.newLinkedHashSet(); + } + // 流程预测时会使用,允许审批人为空,如果为空前端会弹出提示选择下一个节点审批人,避免流程无法进行,审批时会真正校验节点是否配置审批人 + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); + if (approveUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } + // 获得审批人 + List assignees = approveUserSelectAssignees.get(activityId); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java new file mode 100644 index 0000000..f851e66 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +/** + * 连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.MULTI_DEPT_LEADER_MULTI; + } + + @Override + public void validateParam(String param) { + // 参数格式: | 分隔:1)左边为部门(多个部门用 , 分隔)。2)右边为部门层级 + String[] params = param.split("\\|"); + Assert.isTrue(params.length == 2, "参数格式不匹配"); + List deptIds = StrUtils.splitToLong(params[0], ","); + int level = Integer.parseInt(params[1]); + // 校验部门存在 + deptApi.validateDeptList(deptIds).checkError(); + Assert.isTrue(level > 0, "部门层级必须大于 0"); + } + + @Override + public Set calculateUsers(String param) { + String[] params = param.split("\\|"); + List deptIds = StrUtils.splitToLong(params[0], ","); + int level = Integer.parseInt(params[1]); + return super.getMultiLevelDeptLeaderIds(deptIds, level); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java new file mode 100644 index 0000000..ad1c93b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * 部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy { + + @Resource + private DeptApi deptApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.DEPT_LEADER; + } + + @Override + public void validateParam(String param) { + Set deptIds = StrUtils.splitToLongSet(param); + deptApi.validateDeptList(deptIds).checkError(); + } + + @Override + public Set calculateUsers(String param) { + Set deptIds = StrUtils.splitToLongSet(param); + List depts = deptApi.getDeptList(deptIds).getCheckedData(); + return convertSet(depts, DeptRespDTO::getLeaderUserId); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java new file mode 100644 index 0000000..aa2c3f0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java @@ -0,0 +1,48 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * 部门的成员 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateDeptMemberStrategy implements BpmTaskCandidateStrategy { + + @Resource + private DeptApi deptApi; + @Resource + private AdminUserApi adminUserApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.DEPT_MEMBER; + } + + @Override + public void validateParam(String param) { + Set deptIds = StrUtils.splitToLongSet(param); + deptApi.validateDeptList(deptIds).checkError(); + } + + @Override + public Set calculateUsers(String param) { + Set deptIds = StrUtils.splitToLongSet(param); + List users = adminUserApi.getUserListByDeptIds(deptIds).getCheckedData(); + return convertSet(users, AdminUserRespDTO::getId); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java new file mode 100644 index 0000000..04e4963 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java @@ -0,0 +1,70 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static cn.hutool.core.collection.ListUtil.toList; + +/** + * 发起人连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Resource + @Lazy + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER_MULTI; + } + + @Override + public void validateParam(String param) { + int level = Integer.parseInt(param); // 参数是部门的层级 + Assert.isTrue(level > 0, "部门的层级必须大于 0"); + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + int level = Integer.parseInt(param); // 参数是部门的层级 + // 获得流程发起人 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + // 获取发起人的 multi 部门负责人 + DeptRespDTO dept = super.getStartUserDept(startUserId); + if (dept == null) { + return new HashSet<>(); + } + return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + int level = Integer.parseInt(param); // 参数是部门的层级 + DeptRespDTO dept = super.getStartUserDept(startUserId); + if (dept == null) { + return new HashSet<>(); + } + return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java new file mode 100644 index 0000000..9670fa9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java @@ -0,0 +1,71 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; + +/** + * 发起人的部门负责人, 可以是上级部门负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateStartUserDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Resource + @Lazy // 避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER; + } + + @Override + public void validateParam(String param) { + // 参数是部门的层级 + Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0"); + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + // 获得流程发起人 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + // 获取发起人的部门负责人 + return getStartUserDeptLeader(startUserId, param); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + // 获取发起人的部门负责人 + return getStartUserDeptLeader(startUserId, param); + } + + private Set getStartUserDeptLeader(Long startUserId, String param) { + int level = Integer.parseInt(param); // 参数是部门的层级 + DeptRespDTO dept = super.getStartUserDept(startUserId); + if (dept == null) { + return new HashSet<>(); + } + Long deptLeaderId = super.getAssignLevelDeptLeaderId(dept, level); + return deptLeaderId != null ? asSet(deptLeaderId) : new HashSet<>(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java new file mode 100644 index 0000000..6aab05c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import com.google.common.collect.Sets; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +/** + * 发起人自选 {@link BpmTaskCandidateUserStrategy} 实现类 + * + * @author ZT + */ +@Component +public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.START_USER_SELECT; + } + + @Override + public void validateParam(String param) {} + + @Override + public boolean isParamRequired() { + return false; + } + + @Override + public LinkedHashSet calculateUsersByTask(DelegateExecution execution, String param) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId()); + Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance); + Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", + execution.getProcessInstanceId()); + // 获得审批人 + List assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + + @Override + public LinkedHashSet calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + if (processVariables == null) { + return Sets.newLinkedHashSet(); + } + Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processVariables); + if (startUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } + // 获得审批人 + List assignees = startUserSelectAssignees.get(activityId); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java new file mode 100644 index 0000000..119b59d --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java @@ -0,0 +1,56 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.form; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept.AbstractBpmTaskCandidateDeptLeaderStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.Set; + +/** + * 表单内部门负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateFormDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.FORM_DEPT_LEADER; + } + + @Override + public void validateParam(String param) { + // 参数格式: | 分隔:1)左边为表单内部门字段。2)右边为部门层级 + String[] params = param.split("\\|"); + Assert.isTrue(params.length == 2, "参数格式不匹配"); + Assert.notEmpty(param, "表单内部门字段不能为空"); + int level = Integer.parseInt(params[1]); + Assert.isTrue(level > 0, "部门层级必须大于 0"); + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + String[] params = param.split("\\|"); + Object result = execution.getVariable(params[0]); + int level = Integer.parseInt(params[1]); + return super.getMultiLevelDeptLeaderIds(Convert.toList(Long.class, result), level); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, + String param, Long startUserId, String processDefinitionId, + Map processVariables) { + String[] params = param.split("\\|"); + Object result = processVariables == null ? null : processVariables.get(params[0]); + int level = Integer.parseInt(params[1]); + return super.getMultiLevelDeptLeaderIds(Convert.toList(Long.class, result), level); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java new file mode 100644 index 0000000..435717b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java @@ -0,0 +1,47 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.form; + +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.Set; + +/** + * 表单内用户字段 {@link BpmTaskCandidateUserStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateFormUserStrategy implements BpmTaskCandidateStrategy { + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.FORM_USER; + } + + @Override + public void validateParam(String param) { + Assert.notEmpty(param, "表单内用户字段不能为空"); + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + Object result = execution.getVariable(param); + return CollectionUtils.toLinkedHashSet(Long.class, result); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, + String param, Long startUserId, String processDefinitionId, + Map processVariables) { + Object result = processVariables == null ? null : processVariables.get(param); + return CollectionUtils.toLinkedHashSet(Long.class, result); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java new file mode 100644 index 0000000..8c360bc --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; + +import cn.hutool.core.lang.Assert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * 审批人为空 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateAssignEmptyStrategy implements BpmTaskCandidateStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessDefinitionService processDefinitionService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY; + } + + @Override + public void validateParam(String param) { + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + return getCandidateUsers(execution.getProcessDefinitionId(), execution.getCurrentFlowElement()); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); + return getCandidateUsers(processDefinitionId, flowElement); + } + + private Set getCandidateUsers(String processDefinitionId, FlowElement flowElement) { + // 情况一:指定人员审批 + Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(flowElement); + if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType())) { + return new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(flowElement)); + } + + // 情况二:流程管理员 + if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType())) { + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processDefinitionId); + Assert.notNull(processDefinition, "流程定义({})不存在", processDefinitionId); + return new HashSet<>(processDefinition.getManagerUserIds()); + } + + // 都不满足,还是返回空 + return new HashSet<>(); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java new file mode 100644 index 0000000..aeae390 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java @@ -0,0 +1,58 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; + +import com.google.common.collect.Sets; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * 流程表达式 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author ZT + */ +@Component +@Slf4j +public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrategy { + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.EXPRESSION; + } + + @Override + public void validateParam(String param) { + // do nothing 因为它基本做不了校验 + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + Object result = FlowableUtils.getExpressionValue(execution, param); + return CollectionUtils.toLinkedHashSet(Long.class, result); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + Map variables = processVariables == null ? new HashMap<>() : processVariables; + try { + Object result = FlowableUtils.getExpressionValue(variables, param); + return CollectionUtils.toLinkedHashSet(Long.class, result); + } catch (FlowableException ex) { + // 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常, + log.warn("[calculateUsersByActivity][表达式({}) 变量({}) 解析报错", param, variables, ex); + // 不能预测候选人,返回空列表, 避免流程无法进行 + return Sets.newHashSet(); + } + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java new file mode 100644 index 0000000..5bc1232 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.service.definition.BpmUserGroupService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +/** + * 用户组 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateGroupStrategy implements BpmTaskCandidateStrategy { + + @Resource + private BpmUserGroupService userGroupService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.USER_GROUP; + } + + @Override + public void validateParam(String param) { + Set groupIds = StrUtils.splitToLongSet(param); + userGroupService.validUserGroups(groupIds); + } + + @Override + public Set calculateUsers(String param) { + Set groupIds = StrUtils.splitToLongSet(param); + List groups = userGroupService.getUserGroupList(groupIds); + return convertSetByFlatMap(groups, BpmUserGroupDO::getUserIds, Collection::stream); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java new file mode 100644 index 0000000..74edf8a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java @@ -0,0 +1,48 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.system.api.dept.PostApi; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * 岗位 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidatePostStrategy implements BpmTaskCandidateStrategy { + + @Resource + private PostApi postApi; + @Resource + private AdminUserApi adminUserApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.POST; + } + + @Override + public void validateParam(String param) { + Set postIds = StrUtils.splitToLongSet(param); + postApi.validPostList(postIds); + } + + @Override + public Set calculateUsers(String param) { + Set postIds = StrUtils.splitToLongSet(param); + List users = adminUserApi.getUserListByPostIds(postIds).getCheckedData(); + return convertSet(users, AdminUserRespDTO::getId); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java new file mode 100644 index 0000000..f8223d9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java @@ -0,0 +1,43 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.system.api.permission.PermissionApi; +import com.zt.plat.module.system.api.permission.RoleApi; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.Set; + +/** + * 角色 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy { + + @Resource + private RoleApi roleApi; + @Resource + private PermissionApi permissionApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.ROLE; + } + + @Override + public void validateParam(String param) { + Set roleIds = StrUtils.splitToLongSet(param); + roleApi.validRoleList(roleIds); + } + + @Override + public Set calculateUsers(String param) { + Set roleIds = StrUtils.splitToLongSet(param); + return permissionApi.getUserRoleIdListByRoleIds(roleIds).getCheckedData(); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java new file mode 100644 index 0000000..ed527fa --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java @@ -0,0 +1,57 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.Set; + +/** + * 发起人自己 {@link BpmTaskCandidateUserStrategy} 实现类 + *

+ * 适合场景:用于需要发起人信息复核等场景 + * + * @author jason + */ +@Component +public class BpmTaskCandidateStartUserStrategy implements BpmTaskCandidateStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.START_USER; + } + + @Override + public void validateParam(String param) { + } + + @Override + public boolean isParamRequired() { + return false; + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + return SetUtils.asSet(Long.valueOf(processInstance.getStartUserId())); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + return SetUtils.asSet(startUserId); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java new file mode 100644 index 0000000..f6a64ee --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java @@ -0,0 +1,39 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import cn.hutool.core.text.StrPool; +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.system.api.user.AdminUserApi; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashSet; + +/** + * 用户 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateUserStrategy implements BpmTaskCandidateStrategy { + + @Resource + private AdminUserApi adminUserApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.USER; + } + + @Override + public void validateParam(String param) { + adminUserApi.validateUserList(StrUtils.splitToLongSet(param)).checkError(); + } + + @Override + public LinkedHashSet calculateUsers(String param) { + return new LinkedHashSet<>(StrUtils.splitToLong(param, StrPool.COMMA)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java new file mode 100644 index 0000000..c5db881 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.framework.flowable.core.el; + +import org.flowable.common.engine.api.variable.VariableContainer; +import org.flowable.common.engine.impl.el.function.AbstractFlowableVariableExpressionFunction; +import org.springframework.stereotype.Component; + +/** + * 根据流程变量 variable 的类型,转换参数的值 + * + * 目前用于 ConditionNodeConvert 的 buildConditionExpression 方法中 + * + * @author jason + */ +@Component +public class VariableConvertByTypeExpressionFunction extends AbstractFlowableVariableExpressionFunction { + + public VariableConvertByTypeExpressionFunction() { + super("convertByType"); + } + + public static Object convertByType(VariableContainer variableContainer, String variableName, Object parmaValue) { + Object variable = variableContainer.getVariable(variableName); + if (variable != null && parmaValue != null) { + // 如果值不是字符串类型,流程变量的类型是字符串,把值转成字符串 + if (!(parmaValue instanceof String) && variable instanceof String ) { + return parmaValue.toString(); + } + } + return parmaValue; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java new file mode 100644 index 0000000..9a8b399 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java @@ -0,0 +1,59 @@ +package com.zt.plat.module.bpm.framework.flowable.core.enums; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 任务的候选人策略枚举 + * + * 例如说:分配给指定人审批 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmTaskCandidateStrategyEnum implements ArrayValuable { + + ROLE(10, "角色"), + DEPT_MEMBER(20, "部门的成员"), // 包括负责人 + DEPT_LEADER(21, "部门的负责人"), + MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), + POST(22, "岗位"), + USER(30, "用户"), + APPROVE_USER_SELECT(34, "审批人自身"), // 当前审批人,可在审批时,选择下一个节点的审批人 + START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时,选择此节点的审批人 + START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 + START_USER_DEPT_LEADER(37, "发起人部门负责人"), + START_USER_DEPT_LEADER_MULTI(38, "发起人连续多级部门的负责人"), + USER_GROUP(40, "用户组"), + FORM_USER(50, "表单内用户字段"), + FORM_DEPT_LEADER(51, "表单内部门负责人"), + EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager + ASSIGN_EMPTY(1, "审批人为空"), + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTaskCandidateStrategyEnum::getStrategy).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer strategy; + /** + * 描述 + */ + private final String description; + + public static BpmTaskCandidateStrategyEnum valueOf(Integer strategy) { + return ArrayUtil.firstMatch(o -> o.getStrategy().equals(strategy), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java new file mode 100644 index 0000000..de18134 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java @@ -0,0 +1,146 @@ +package com.zt.plat.module.bpm.framework.flowable.core.enums; + +import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; + +/** + * BPMN XML 常量信息 + * + * @author ZT + */ +public interface BpmnModelConstants { + + String BPMN_FILE_SUFFIX = ".bpmn"; + + /** + * BPMN 中的命名空间 + */ + String NAMESPACE = "http://flowable.org/bpmn"; + + /** + * BPMN UserTask 的扩展属性,用于标记候选人策略 + */ + String USER_TASK_CANDIDATE_STRATEGY = "candidateStrategy"; + /** + * BPMN UserTask 的扩展属性,用于标记候选人参数 + */ + String USER_TASK_CANDIDATE_PARAM = "candidateParam"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记边界事件类型 + */ + String BOUNDARY_EVENT_TYPE = "boundaryEventType"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务超时执行动作 + */ + String USER_TASK_TIMEOUT_HANDLER_TYPE = "timeoutHandlerType"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务的审批人与发起人相同时,对应的处理类型 + */ + String USER_TASK_ASSIGN_START_USER_HANDLER_TYPE = "assignStartUserHandlerType"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务的空处理类型 + */ + String USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE = "assignEmptyHandlerType"; + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务的空处理的指定用户编号数组 + */ + String USER_TASK_ASSIGN_USER_IDS = "assignEmptyUserIds"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝处理类型 + */ + String USER_TASK_REJECT_HANDLER_TYPE = "rejectHandlerType"; + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝后的退回的任务 Id + */ + String USER_TASK_REJECT_RETURN_TASK_ID = "rejectReturnTaskId"; + + /** + * BPMN UserTask 的扩展属性,用于标记用户任务的审批类型 + */ + String USER_TASK_APPROVE_TYPE = "approveType"; + + /** + * BPMN UserTask 的扩展属性,用于标记用户任务的审批方式 + */ + String USER_TASK_APPROVE_METHOD = "approveMethod"; + + /** + * BPMN Child Process 的扩展属性,用于标记多实例来源类型 + */ + String CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE = "childProcessMultiInstanceSourceType"; + + /** + * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 + */ + String FORM_FIELD_PERMISSION_ELEMENT = "fieldsPermission"; + + /** + * BPMN ExtensionElement Attribute, 用于标记表单字段 + */ + String FORM_FIELD_PERMISSION_ELEMENT_FIELD_ATTRIBUTE = "field"; + /** + * BPMN ExtensionElement Attribute, 用于标记表单权限 + */ + String FORM_FIELD_PERMISSION_ELEMENT_PERMISSION_ATTRIBUTE = "permission"; + + /** + * BPMN ExtensionElement 操作按钮设置元素, 用于审批节点操作按钮设置 + */ + String BUTTON_SETTING_ELEMENT = "buttonsSetting"; + + /** + * BPMN ExtensionElement Attribute, 用于标记按钮编号 + */ + String BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE = "id"; + + /** + * BPMN ExtensionElement Attribute, 用于标记按钮显示名称 + */ + String BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE = "displayName"; + + /** + * BPMN ExtensionElement Attribute, 用于标记按钮是否启用 + */ + String BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE = "enable"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记触发器的类型 + */ + String TRIGGER_TYPE = "triggerType"; + /** + * BPMN ExtensionElement 的扩展属性,用于标记触发器参数 + */ + String TRIGGER_PARAM = "triggerParam"; + + /** + * BPMN Start Event Node Id + */ + String START_EVENT_NODE_ID = "StartEvent"; + + /** + * 发起人节点 ID + */ + String START_USER_NODE_ID = "StartUserNode"; + + /** + * 是否需要签名 + */ + String SIGN_ENABLE = "signEnable"; + + /** + * 审批意见是否必填 + */ + String REASON_REQUIRE = "reasonRequire"; + + /** + * 节点类型 + * + * 目前只有 {@link BpmModelTypeEnum#SIMPLE} 的 UserTask 节点会设置该属性,用于区分是审批节点、还是办理节点 + */ + String NODE_TYPE = "nodeType"; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java new file mode 100644 index 0000000..0e6a697 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -0,0 +1,99 @@ +package com.zt.plat.module.bpm.framework.flowable.core.enums; + +import org.flowable.engine.runtime.ProcessInstance; + +/** + * BPM Variable 通用常量 + * + * @author ZT + */ +public class BpmnVariableConstants { + + /** + * 流程实例的变量 - 状态 + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS"; + /** + * 流程实例的变量 - 理由 + * + * 例如说:审批不通过的理由(目前审核通过暂时不会记录) + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_REASON = "PROCESS_REASON"; + /** + * 流程实例的变量 - 发起用户选择的审批人 Map + * + * @see ProcessInstance#getProcessVariables() + * @see BpmTaskCandidateStrategyEnum#START_USER_SELECT + */ + public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; + /** + * 流程实例的变量 - 审批人选择的审批人 Map + * + * @see ProcessInstance#getProcessVariables() + * @see BpmTaskCandidateStrategyEnum#APPROVE_USER_SELECT + */ + public static final String PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES = "PROCESS_APPROVE_USER_SELECT_ASSIGNEES"; + /** + * 流程实例的变量 - 发起用户 ID + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_START_USER_ID = "PROCESS_START_USER_ID"; + /** + * 流程实例的变量 - 用于判断流程实例变量节点是否驳回. 格式 RETURN_FLAG_{节点 id} + * + * 目的是:驳回到发起节点时,因为审批人与发起人相同,所以被自动通过。但是,此时还是希望不要自动通过 + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s"; + /** + * 流程实例的变量 - 是否跳过表达式 + * + * @see ProcessInstance#getProcessVariables() + * @see Flowable/Activiti之SkipExpression 完成自动审批 + */ + public static final String PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + + /** + * 流程实例的变量 - 用于判断流程是否需要跳过发起人节点 + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE = "PROCESS_SKIP_START_USER_NODE"; + + /** + * 流程实例的变量 - 流程开始时间 + * + * 【非存储变量】用于部分需要 format 的场景,例如说:流程实例的自定义标题 + */ + public static final String PROCESS_START_TIME = "PROCESS_START_TIME"; + /** + * 流程实例的变量 - 流程定义名称 + */ + public static final String PROCESS_DEFINITION_NAME = "PROCESS_DEFINITION_NAME"; + + /** + * 任务的变量 - 状态 + * + * @see org.flowable.task.api.Task#getTaskLocalVariables() + */ + public static final String TASK_VARIABLE_STATUS = "TASK_STATUS"; + /** + * 任务的变量 - 理由 + * + * 例如说:审批通过、不通过的理由 + * + * @see org.flowable.task.api.Task#getTaskLocalVariables() + */ + public static final String TASK_VARIABLE_REASON = "TASK_REASON"; + /** + * 任务变量 - 签名图片 URL + */ + public static final String TASK_SIGN_PIC_URL = "TASK_SIGN_PIC_URL"; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java new file mode 100644 index 0000000..5ec3654 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java @@ -0,0 +1,24 @@ +package com.zt.plat.module.bpm.framework.flowable.core.event; + +import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.validation.annotation.Validated; + +/** + * {@link BpmProcessInstanceStatusEvent} 的生产者 + * + * @author ZT + */ +@AllArgsConstructor +@Validated +public class BpmProcessInstanceEventPublisher { + + private final ApplicationEventPublisher publisher; + + public void sendProcessInstanceResultEvent(@Valid BpmProcessInstanceStatusEvent event) { + publisher.publishEvent(event); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java new file mode 100644 index 0000000..a821f05 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java @@ -0,0 +1,47 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.springframework.stereotype.Component; + +import java.util.Set; + +import static com.zt.plat.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate.BEAN_NAME; + +/** + * 处理抄送用户的 {@link JavaDelegate} 的实现类 + *

+ * 目前只有仿钉钉/飞书模式的【抄送节点】使用 + * + * @author jason + */ +@Component(BEAN_NAME) +public class BpmCopyTaskDelegate implements JavaDelegate { + + public static final String BEAN_NAME = "bpmCopyTaskDelegate"; + + @Resource + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; + + @Override + public void execute(DelegateExecution execution) { + // 1. 获得抄送人 + Set userIds = taskCandidateInvoker.calculateUsersByTask(execution); + if (CollUtil.isEmpty(userIds)) { + return; + } + // 2. 执行抄送 + FlowElement currentFlowElement = execution.getCurrentFlowElement(); + processInstanceCopyService.createProcessInstanceCopy(userIds, null, execution.getProcessInstanceId(), + currentFlowElement.getId(), currentFlowElement.getName(), null); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java new file mode 100644 index 0000000..cc56501 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java @@ -0,0 +1,54 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener; + +import com.google.common.collect.ImmutableSet; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.flowable.engine.delegate.event.FlowableCancelledEvent; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.Set; + +/** + * 监听 {@link ProcessInstance} 的状态变更,更新其对应的 status 状态 + * + * @author jason + */ +@Component +public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener { + + public static final Set PROCESS_INSTANCE_EVENTS = ImmutableSet.builder() + .add(FlowableEngineEventType.PROCESS_CREATED) + .add(FlowableEngineEventType.PROCESS_COMPLETED) + .add(FlowableEngineEventType.PROCESS_CANCELLED) + .build(); + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + public BpmProcessInstanceEventListener(){ + super(PROCESS_INSTANCE_EVENTS); + } + + @Override + protected void processCreated(FlowableEngineEntityEvent event) { + processInstanceService.processProcessInstanceCreated((ProcessInstance)event.getEntity()); + } + + @Override + protected void processCompleted(FlowableEngineEntityEvent event) { + processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity()); + } + + @Override // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法 + protected void processCancelled(FlowableCancelledEvent event) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId()); + processInstanceService.processProcessInstanceCompleted(processInstance); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java new file mode 100644 index 0000000..b26585b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java @@ -0,0 +1,125 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.google.common.collect.ImmutableSet; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.enums.definition.BpmBoundaryEventTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.service.definition.BpmModelService; +import com.zt.plat.module.bpm.service.task.BpmTaskService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +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.FlowableEngineEntityEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.job.api.Job; +import org.flowable.task.api.Task; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +/** + * 监听 {@link Task} 的开始与完成 + * + * @author jason + */ +@Component +@Slf4j +public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmModelService modelService; + @Resource + @Lazy // 解决循环依赖 + private BpmTaskService taskService; + + public static final Set TASK_EVENTS = ImmutableSet.builder() + .add(FlowableEngineEventType.TASK_CREATED) + .add(FlowableEngineEventType.TASK_ASSIGNED) + .add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,这里仅处理任务后置通知。 + .add(FlowableEngineEventType.ACTIVITY_CANCELLED) + .add(FlowableEngineEventType.TIMER_FIRED) // 监听审批超时 + .build(); + + public BpmTaskEventListener() { + super(TASK_EVENTS); + } + + @Override + protected void taskCreated(FlowableEngineEntityEvent event) { + taskService.processTaskCreated((Task) event.getEntity()); + } + + @Override + protected void taskAssigned(FlowableEngineEntityEvent event) { + taskService.processTaskAssigned((Task) event.getEntity()); + } + + @Override + protected void taskCompleted(FlowableEngineEntityEvent event) { + taskService.processTaskCompleted((Task) event.getEntity()); + } + + @Override + protected void activityCancelled(FlowableActivityCancelledEvent event) { + List activityList = taskService.getHistoricActivityListByExecutionId(event.getExecutionId()); + if (CollUtil.isEmpty(activityList)) { + log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId()); + return; + } + // 遍历处理 + activityList.forEach(activity -> { + if (StrUtil.isEmpty(activity.getTaskId())) { + return; + } + taskService.processTaskCanceled(activity.getTaskId()); + }); + } + + @Override + @SuppressWarnings("PatternVariableCanBeUsed") + protected void timerFired(FlowableEngineEntityEvent event) { + // 1.1 只处理 BoundaryEvent 边界计时时间 + String processDefinitionId = event.getProcessDefinitionId(); + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); + Job entity = (Job) event.getEntity(); + FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId()); + if (!(element instanceof BoundaryEvent)) { + return; + } + // 1.2 判断是否为超时处理 + BoundaryEvent boundaryEvent = (BoundaryEvent) element; + String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, + BpmnModelConstants.BOUNDARY_EVENT_TYPE); + BpmBoundaryEventTypeEnum bpmTimerBoundaryEventType = BpmBoundaryEventTypeEnum.typeOf(NumberUtils.parseInt(boundaryEventType)); + + // 2. 处理超时 + if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT)) { + // 2.1 用户任务超时处理 + String timeoutHandlerType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, + BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_TYPE); + String taskKey = boundaryEvent.getAttachedToRefId(); + taskService.processTaskTimeout(event.getProcessInstanceId(), taskKey, NumberUtils.parseInt(timeoutHandlerType)); + // 2.2 延迟器超时处理 + } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) { + String taskKey = boundaryEvent.getAttachedToRefId(); + taskService.triggerTask(event.getProcessInstanceId(), taskKey); + // 2.3 子流程超时处理 + } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT)) { + String taskKey = boundaryEvent.getAttachedToRefId(); + taskService.processChildProcessTimeout(event.getProcessInstanceId(), taskKey); + } + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java new file mode 100644 index 0000000..f88ce20 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java @@ -0,0 +1,55 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener; + +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.springframework.stereotype.Component; + +import java.util.EnumMap; +import java.util.List; + +import static com.zt.plat.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate.BEAN_NAME; + + +/** + * 处理触发器任务 {@link JavaDelegate} 的实现类 + *

+ * 目前只有 Simple 设计器【触发器节点】使用 + * + * @author jason + */ +@Component(BEAN_NAME) +@Slf4j +public class BpmTriggerTaskDelegate implements JavaDelegate { + + public static final String BEAN_NAME = "bpmTriggerTaskDelegate"; + + @Resource + private List triggers; + + private final EnumMap triggerMap = new EnumMap<>(BpmTriggerTypeEnum.class); + + @PostConstruct + private void init() { + triggers.forEach(trigger -> triggerMap.put(trigger.getType(), trigger)); + } + + @Override + public void execute(DelegateExecution execution) { + FlowElement flowElement = execution.getCurrentFlowElement(); + BpmTriggerTypeEnum bpmTriggerType = BpmnModelUtils.parserTriggerType(flowElement); + BpmTrigger bpmTrigger = triggerMap.get(bpmTriggerType); + if (bpmTrigger == null) { + log.error("[execute][FlowElement({}), {} 找不到匹配的触发器]", execution.getCurrentActivityId(), flowElement); + return; + } + bpmTrigger.execute(execution.getProcessInstanceId(), BpmnModelUtils.parserTriggerParam(flowElement)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java new file mode 100644 index 0000000..84763fe --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java @@ -0,0 +1,21 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.exection; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; + +/** + * 类型为 class 的 ExecutionListener 监听器示例 + * + * @author ZT + */ +@Slf4j +public class DemoDelegateClassExecutionListener implements JavaDelegate { + + @Override + public void execute(DelegateExecution execution) { + log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(), + execution.getCurrentFlowableListener().getFieldExtensions()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java new file mode 100644 index 0000000..64dba65 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.exection; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.springframework.stereotype.Component; + +/** + * 类型为 delegateExpression 的 ExecutionListener 监听器示例 + * + * 和 {@link DemoDelegateClassExecutionListener} 的差异是,需要注册到 Spring 中 + */ +@Component +@Slf4j +public class DemoDelegateExpressionExecutionListener implements JavaDelegate { + + @Override + public void execute(DelegateExecution execution) { + log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(), + execution.getCurrentFlowableListener().getFieldExtensions()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java new file mode 100644 index 0000000..c6e5b99 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java @@ -0,0 +1,21 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.exection; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +/** + * 类型为 expression 的 ExecutionListener 监听器示例 + * + * 和 {@link DemoDelegateClassExecutionListener} 的差异是,需要注册到 Spring 中,但不用实现 {@link org.flowable.engine.delegate.JavaDelegate} 接口 + */ +@Component +@Slf4j +public class DemoSpringExpressionExecutionListener { + + public void execute(DelegateExecution execution) { + log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(), + execution.getCurrentFlowableListener().getFieldExtensions()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java new file mode 100644 index 0000000..6b4e94e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java @@ -0,0 +1,20 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.task; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.TaskListener; +import org.flowable.task.service.delegate.DelegateTask; + +/** + * 类型为 class 的 TaskListener 监听器示例 + * + * @author ZT + */ +@Slf4j +public class DemoDelegateClassTaskListener implements TaskListener { + + @Override + public void notify(DelegateTask delegateTask) { + log.info("[execute][task({}) 被调用]", delegateTask.getId()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java new file mode 100644 index 0000000..41fa05b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java @@ -0,0 +1,22 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.task; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.TaskListener; +import org.flowable.task.service.delegate.DelegateTask; +import org.springframework.stereotype.Component; + +/** + * 类型为 delegateExpression 的 TaskListener 监听器示例 + * + * @author ZT + */ +@Component +@Slf4j +public class DemoDelegateExpressionTaskListener implements TaskListener { + + @Override + public void notify(DelegateTask delegateTask) { + log.info("[execute][task({}) 被调用]", delegateTask.getId()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java new file mode 100644 index 0000000..b9a4ccf --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java @@ -0,0 +1,20 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.task; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.task.service.delegate.DelegateTask; +import org.springframework.stereotype.Component; + +/** + * 类型为 expression 的 TaskListener 监听器示例 + * + * @author ZT + */ +@Slf4j +@Component +public class DemoSpringExpressionTaskListener { + + public void notify(DelegateTask delegateTask) { + log.info("[execute][task({}) 被调用]", delegateTask.getId()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java new file mode 100644 index 0000000..583e804 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java @@ -0,0 +1,158 @@ +package com.zt.plat.module.bpm.framework.flowable.core.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.spring.SpringUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR; + +/** + * 工作流发起 HTTP 请求工具类 + * + * @author ZT + */ +@Slf4j +public class BpmHttpRequestUtils { + + public static void executeBpmHttpRequest(ProcessInstance processInstance, + String url, + List headerParams, + List bodyParams, + Boolean handleResponse, + List> response) { + RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); + BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class); + + // 1.1 设置请求头 + MultiValueMap headers = buildHttpHeaders(processInstance, headerParams); + // 1.2 设置请求体 + MultiValueMap body = buildHttpBody(processInstance, bodyParams); + + // 2. 发起请求 + ResponseEntity responseEntity = sendHttpRequest(url, headers, body, restTemplate); + + // 3. 处理返回 + if (Boolean.FALSE.equals(handleResponse)) { + return; + } + // 3.1 判断是否需要解析返回值 + if (responseEntity == null + || StrUtil.isEmpty(responseEntity.getBody()) + || !responseEntity.getStatusCode().is2xxSuccessful() + || CollUtil.isEmpty(response)) { + return; + } + // 3.2 解析返回值, 返回值必须符合 CommonResult 规范。 + CommonResult> respResult = JsonUtils.parseObjectQuietly(responseEntity.getBody(), + new TypeReference<>() {}); + if (respResult == null || !respResult.isSuccess()) { + return; + } + // 3.3 获取需要更新的流程变量 + Map updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), response); + // 3.4 更新流程变量 + if (CollUtil.isNotEmpty(updateVariables)) { + processInstanceService.updateProcessInstanceVariables(processInstance.getId(), updateVariables); + } + } + + public static ResponseEntity sendHttpRequest(String url, + MultiValueMap headers, + MultiValueMap body, + RestTemplate restTemplate) { + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + ResponseEntity responseEntity; + try { + responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); + } catch (RestClientException e) { + log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); + throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR); + } + return responseEntity; + } + + public static MultiValueMap buildHttpHeaders(ProcessInstance processInstance, + List headerSettings) { + Map processVariables = processInstance.getProcessVariables(); + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); + addHttpRequestParam(headers, headerSettings, processVariables); + return headers; + } + + public static MultiValueMap buildHttpBody(ProcessInstance processInstance, + List bodySettings) { + Map processVariables = processInstance.getProcessVariables(); + MultiValueMap body = new LinkedMultiValueMap<>(); + addHttpRequestParam(body, bodySettings, processVariables); + body.add("processInstanceId", processInstance.getId()); + return body; + } + + /** + * 从请求返回值获取需要更新的流程变量 + * + * @param result 请求返回结果 + * @param responseSettings 返回设置 + * @return 需要更新的流程变量 + */ + public static Map getNeedUpdatedVariablesFromResponse(Map result, + List> responseSettings) { + Map updateVariables = new HashMap<>(); + if (CollUtil.isEmpty(result)) { + return updateVariables; + } + responseSettings.forEach(responseSetting -> { + if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) { + updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue())); + } + }); + return updateVariables; + } + + /** + * 添加 HTTP 请求参数。请求头或者请求体 + * + * @param params HTTP 请求参数 + * @param paramSettings HTTP 请求参数设置 + * @param processVariables 流程变量 + */ + public static void addHttpRequestParam(MultiValueMap params, + List paramSettings, + Map processVariables) { + if (CollUtil.isEmpty(paramSettings)) { + return; + } + paramSettings.forEach(item -> { + if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) { + params.add(item.getKey(), item.getValue()); + } else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) { + params.add(item.getKey(), processVariables.get(item.getValue()).toString()); + } + }); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java new file mode 100644 index 0000000..932afa7 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -0,0 +1,1025 @@ +package com.zt.plat.module.bpm.framework.flowable.core.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import com.google.common.collect.Maps; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import com.zt.plat.module.bpm.enums.definition.*; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.impl.util.io.BytesStreamSource; +import org.flowable.engine.impl.el.FixedValue; + +import java.util.*; + +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX; + +/** + * BPMN Model 操作工具类。目前分成三部分: + * + * 1. BPMN 修改 + 解析元素相关的方法 + * 2. BPMN 简单查找相关的方法 + * 3. BPMN 复杂遍历相关的方法 + * 4. BPMN 流程预测相关的方法 + * + * @author ZT + */ +@Slf4j +public class BpmnModelUtils { + + // ========== BPMN 修改 + 解析元素相关的方法 ========== + + public static void addExtensionElement(FlowElement element, String name, String value) { + if (value == null) { + return; + } + ExtensionElement extensionElement = new ExtensionElement(); + extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); + extensionElement.setElementText(value); + extensionElement.setName(name); + element.addExtensionElement(extensionElement); + } + + public static void addExtensionElement(FlowElement element, String name, Integer value) { + if (value == null) { + return; + } + addExtensionElement(element, name, String.valueOf(value)); + } + + public static void addExtensionElementJson(FlowElement element, String name, Object value) { + if (value == null) { + return; + } + addExtensionElement(element, name, JsonUtils.toJsonString(value)); + } + + public static void addExtensionElement(FlowElement element, String name, Map attributes) { + if (attributes == null) { + return; + } + ExtensionElement extensionElement = new ExtensionElement(); + extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); + extensionElement.setName(name); + attributes.forEach((key, value) -> { + ExtensionAttribute extensionAttribute = new ExtensionAttribute(key, value); + extensionElement.addAttribute(extensionAttribute); + }); + element.addExtensionElement(extensionElement); + } + + /** + * 解析扩展元素 + * + * @param flowElement 节点 + * @param elementName 元素名称 + * @return 扩展元素 + */ + public static String parseExtensionElement(FlowElement flowElement, String elementName) { + if (flowElement == null) { + return null; + } + ExtensionElement element = CollUtil.getFirst(flowElement.getExtensionElements().get(elementName)); + return element != null ? element.getElementText() : null; + } + + /** + * 给节点添加候选人元素 + * + * @param candidateStrategy 候选人策略 + * @param candidateParam 候选人参数,允许空 + * @param flowElement 节点 + */ + public static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) { + addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY, + candidateStrategy == null ? null : candidateStrategy.toString()); + addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, candidateParam); + } + + /** + * 解析候选人策略 + * + * @param userTask 任务节点 + * @return 候选人策略 + */ + public static Integer parseCandidateStrategy(FlowElement userTask) { + Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue( + BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); + // TODO @芋艿 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限 + if (candidateStrategy == null) { + ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); + candidateStrategy = element != null ? NumberUtils.parseInt(element.getElementText()) : null; + } + return candidateStrategy; + } + + /** + * 解析候选人参数 + * + * @param userTask 任务节点 + * @return 候选人参数 + */ + public static String parseCandidateParam(FlowElement userTask) { + String candidateParam = userTask.getAttributeValue( + BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM); + if (candidateParam == null) { + ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)); + candidateParam = element != null ? element.getElementText() : null; + } + return candidateParam; + } + + /** + * 解析审批类型 + * + * @see BpmUserTaskApproveTypeEnum + * @param userTask 任务节点 + * @return 审批类型 + */ + public static Integer parseApproveType(FlowElement userTask) { + return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE)); + } + + /** + * 解析子流程多实例来源类型 + * + * @see BpmChildProcessMultiInstanceSourceTypeEnum + * @param element 任务节点 + * @return 多实例来源类型 + */ + public static Integer parseMultiInstanceSourceType(FlowElement element) { + return NumberUtils.parseInt(parseExtensionElement(element, BpmnModelConstants.CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE)); + } + + /** + * 添加任务拒绝处理元素 + * + * @param rejectHandler 任务拒绝处理 + * @param userTask 任务节点 + */ + public static void addTaskRejectElements(BpmSimpleModelNodeVO.RejectHandler rejectHandler, UserTask userTask) { + if (rejectHandler == null) { + return; + } + addExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE, StrUtil.toStringOrNull(rejectHandler.getType())); + addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId()); + } + + /** + * 解析任务拒绝处理类型 + * + * @param userTask 任务节点 + * @return 任务拒绝处理类型 + */ + public static BpmUserTaskRejectHandlerTypeEnum parseRejectHandlerType(FlowElement userTask) { + Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE)); + return BpmUserTaskRejectHandlerTypeEnum.typeOf(rejectHandlerType); + } + + /** + * 解析任务拒绝返回任务节点 ID + * + * @param flowElement 任务节点 + * @return 任务拒绝返回任务节点 ID + */ + public static String parseReturnTaskId(FlowElement flowElement) { + return parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID); + } + + /** + * 给节点添加用户任务的审批人与发起人相同时,处理类型枚举 + * + * @see BpmUserTaskAssignStartUserHandlerTypeEnum + * @param assignStartUserHandlerType 发起人处理类型 + * @param userTask 任务节点 + */ + public static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) { + if (assignStartUserHandlerType == null) { + return; + } + addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString()); + } + + /** + * 给节点添加用户任务的审批人为空时,处理类型枚举 + * + * @see BpmUserTaskAssignEmptyHandlerTypeEnum + * @param emptyHandler 空处理 + * @param userTask 任务节点 + */ + public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) { + if (emptyHandler == null) { + return; + } + addExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE, StrUtil.toStringOrNull(emptyHandler.getType())); + addExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS, StrUtil.join(",", emptyHandler.getUserIds())); + } + + /** + * 解析用户任务的审批人与发起人相同时,处理类型枚举 + * + * @param userTask 任务节点 + * @return 处理类型枚举 + */ + public static Integer parseAssignStartUserHandlerType(FlowElement userTask) { + return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE)); + } + + /** + * 解析用户任务的审批人为空时,处理类型枚举 + * + * @param userTask 任务节点 + * @return 处理类型枚举 + */ + public static Integer parseAssignEmptyHandlerType(FlowElement userTask) { + return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE)); + } + + /** + * 解析用户任务的审批人为空时,处理用户 ID 数组 + * + * @param userTask 任务节点 + * @return 处理用户 ID 数组 + */ + public static List parseAssignEmptyHandlerUserIds(FlowElement userTask) { + return StrUtils.splitToLong(parseExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS), ","); + } + + /** + * 给节点添加表单字段权限元素 + * + * @param fieldsPermissions 表单字段权限 + * @param flowElement 节点 + */ + public static void addFormFieldsPermission(List> fieldsPermissions, FlowElement flowElement) { + if (CollUtil.isNotEmpty(fieldsPermissions)) { + fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FORM_FIELD_PERMISSION_ELEMENT, item)); + } + } + + /** + * 解析表单字段权限 + * + * @param bpmnModel bpmnModel 对象 + * @param flowElementId 元素 ID + * @return 表单字段权限 + */ + public static Map parseFormFieldsPermission(BpmnModel bpmnModel, String flowElementId) { + if (bpmnModel == null || StrUtil.isEmpty(flowElementId)) { + return null; + } + FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); + if (flowElement == null) { + return null; + } + List extensionElements = flowElement.getExtensionElements().get(FORM_FIELD_PERMISSION_ELEMENT); + if (CollUtil.isEmpty(extensionElements)) { + return null; + } + Map fieldsPermission = MapUtil.newHashMap(); + extensionElements.forEach(element -> { + String field = element.getAttributeValue(null, FORM_FIELD_PERMISSION_ELEMENT_FIELD_ATTRIBUTE); + String permission = element.getAttributeValue(null, FORM_FIELD_PERMISSION_ELEMENT_PERMISSION_ATTRIBUTE); + if (StrUtil.isNotEmpty(field) && StrUtil.isNotEmpty(permission)) { + fieldsPermission.put(field, permission); + } + }); + return fieldsPermission; + } + + /** + * 给节点添加操作按钮设置元素 + */ + public static void addButtonsSetting(List buttonsSetting, UserTask userTask) { + if (CollUtil.isNotEmpty(buttonsSetting)) { + List> list = CollectionUtils.convertList(buttonsSetting, item -> { + Map settingMap = Maps.newHashMapWithExpectedSize(3); + settingMap.put(BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE, String.valueOf(item.getId())); + settingMap.put(BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE, item.getDisplayName()); + settingMap.put(BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE, String.valueOf(item.getEnable())); + return settingMap; + }); + list.forEach(item -> addExtensionElement(userTask, BUTTON_SETTING_ELEMENT, item)); + } + } + + /** + * 解析操作按钮设置 + * + * @param bpmnModel bpmnModel 对象 + * @param flowElementId 元素 ID + * @return 操作按钮设置 + */ + public static Map parseButtonsSetting(BpmnModel bpmnModel, String flowElementId) { + FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); + if (flowElement == null) { + return null; + } + List extensionElements = flowElement.getExtensionElements().get(BUTTON_SETTING_ELEMENT); + if (CollUtil.isEmpty(extensionElements)) { + return null; + } + Map buttonSettings = Maps.newHashMapWithExpectedSize(extensionElements.size()); + extensionElements.forEach(element -> { + String id = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE); + String displayName = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE); + String enable = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE); + if (StrUtil.isNotEmpty(id)) { + BpmTaskRespVO.OperationButtonSetting setting = new BpmTaskRespVO.OperationButtonSetting(); + buttonSettings.put(Integer.valueOf(id), setting.setDisplayName(displayName).setEnable(Boolean.parseBoolean(enable))); + } + }); + return buttonSettings; + } + + /** + * 解析边界事件扩展元素 + * + * @param boundaryEvent 边界事件 + * @param customElement 元素 + * @return 扩展元素 + */ + public static String parseBoundaryEventExtensionElement(BoundaryEvent boundaryEvent, String customElement) { + if (boundaryEvent == null) { + return null; + } + ExtensionElement extensionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(customElement)); + return Optional.ofNullable(extensionElement).map(ExtensionElement::getElementText).orElse(null); + } + + public static void addSignEnable(Boolean signEnable, FlowElement userTask) { + addExtensionElement(userTask, SIGN_ENABLE, + ObjUtil.isNotNull(signEnable) ? signEnable.toString() : Boolean.FALSE.toString()); + } + + public static Boolean parseSignEnable(BpmnModel bpmnModel, String flowElementId) { + FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); + if (flowElement == null) { + return false; + } + List extensionElements = flowElement.getExtensionElements().get(SIGN_ENABLE); + if (CollUtil.isEmpty(extensionElements)) { + return false; + } + return Convert.toBool(extensionElements.get(0).getElementText(), false); + } + + public static void addReasonRequire(Boolean reasonRequire, FlowElement userTask) { + addExtensionElement(userTask, REASON_REQUIRE, + ObjUtil.isNotNull(reasonRequire) ? reasonRequire.toString() : Boolean.FALSE.toString()); + } + + public static Boolean parseReasonRequire(BpmnModel bpmnModel, String flowElementId) { + FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); + if (flowElement == null) { + return false; + } + List extensionElements = flowElement.getExtensionElements().get(REASON_REQUIRE); + if (CollUtil.isEmpty(extensionElements)) { + return false; + } + return Convert.toBool(extensionElements.get(0).getElementText(), false); + } + + public static void addListenerConfig(FlowableListener flowableListener, BpmSimpleModelNodeVO.ListenerHandler handler) { + FieldExtension fieldExtension = new FieldExtension(); + fieldExtension.setFieldName("listenerConfig"); + fieldExtension.setStringValue(JsonUtils.toJsonString(handler)); + flowableListener.getFieldExtensions().add(fieldExtension); + } + + public static BpmSimpleModelNodeVO.ListenerHandler parseListenerConfig(FixedValue fixedValue) { + String expressionText = fixedValue.getExpressionText(); + Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); + return JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ListenerHandler.class); + } + + public static BpmTriggerTypeEnum parserTriggerType(FlowElement flowElement) { + Integer triggerType = NumberUtils.parseInt(parseExtensionElement(flowElement, TRIGGER_TYPE)); + return BpmTriggerTypeEnum.typeOf(triggerType); + } + + public static String parserTriggerParam(FlowElement flowElement) { + return parseExtensionElement(flowElement, TRIGGER_PARAM); + } + + /** + * 给节点添加节点类型 + * + * @param nodeType 节点类型 + * @param flowElement 节点 + */ + public static void addNodeType(Integer nodeType, FlowElement flowElement) { + addExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE, nodeType); + } + + /** + * 解析节点类型 + * + * @param flowElement 节点 + * @return 节点类型 + */ + public static Integer parseNodeType(FlowElement flowElement) { + return NumberUtils.parseInt(parseExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE)); + } + + // ========== BPM 简单查找相关的方法 ========== + + /** + * 根据节点,获取入口连线 + * + * @param source 起始节点 + * @return 入口连线列表 + */ + public static List getElementIncomingFlows(FlowElement source) { + if (source instanceof FlowNode) { + return ((FlowNode) source).getIncomingFlows(); + } + return new ArrayList<>(); + } + + /** + * 根据节点,获取出口连线 + * + * @param source 起始节点 + * @return 出口连线列表 + */ + public static List getElementOutgoingFlows(FlowElement source) { + if (source instanceof FlowNode) { + return ((FlowNode) source).getOutgoingFlows(); + } + return new ArrayList<>(); + } + + /** + * 获取流程元素信息 + * + * @param model bpmnModel 对象 + * @param flowElementId 元素 ID + * @return 元素信息 + */ + public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { + Process process = model.getMainProcess(); + return process.getFlowElement(flowElementId); + } + + /** + * 获得 BPMN 流程中,指定的元素们 + * + * @param model 模型 + * @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等 + * @return 元素们 + */ + @SuppressWarnings("unchecked") + public static List getBpmnModelElements(BpmnModel model, Class clazz) { + List result = new ArrayList<>(); + model.getProcesses().forEach(process -> process.getFlowElements().forEach(flowElement -> { + if (flowElement.getClass().isAssignableFrom(clazz)) { + result.add((T) flowElement); + } + })); + return result; + } + + public static StartEvent getStartEvent(BpmnModel model) { + Process process = model.getMainProcess(); + // 从 initialFlowElement 找 + FlowElement startElement = process.getInitialFlowElement(); + if (startElement instanceof StartEvent) { + return (StartEvent) startElement; + } + // 从 flowElementList 找 + return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent); + } + + public static EndEvent getEndEvent(BpmnModel model) { + Process process = model.getMainProcess(); + // 从 flowElementList 找 endEvent + return (EndEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof EndEvent); + } + + public static BpmnModel getBpmnModel(byte[] bpmnBytes) { + if (ArrayUtil.isEmpty(bpmnBytes)) { + return null; + } + BpmnXMLConverter converter = new BpmnXMLConverter(); + // 补充说明:由于在 Flowable 中自定义了属性,所以 validateSchema 传递 false + return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false); + } + + public static String getBpmnXml(BpmnModel model) { + if (model == null) { + return null; + } + BpmnXMLConverter converter = new BpmnXMLConverter(); + return StrUtil.utf8Str(converter.convertToXML(model)); + } + + public static String getBpmnXml(byte[] bpmnBytes) { + if (ArrayUtil.isEmpty(bpmnBytes)) { + return null; + } + return StrUtil.utf8Str(bpmnBytes); + } + + // ========== BPMN 复杂遍历相关的方法 ========== + + /** + * 找到 source 节点之前的所有用户任务节点 + * + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 已找到的用户任务节点 + * @return 用户任务节点 数组 + */ + public static List getPreviousUserTaskList(FlowElement source, Set hasSequenceFlow, List userTaskList) { + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + if (sequenceFlows == null) { + return userTaskList; + } + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 类型为用户节点,则新增父级节点 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); + } + // 类型为子流程,则添加子流程开始节点出口处相连的节点 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + // 获取子流程用户任务节点 + List childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (CollUtil.isNotEmpty(childUserTaskList)) { + userTaskList.addAll(childUserTaskList); + } + } + // 继续迭代 + userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); + } + return userTaskList; + } + + /** + * 迭代获取子流程用户任务节点 + * + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return 用户任务节点 + */ + public static List findChildProcessUserTaskList(FlowElement source, Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (sequenceFlows == null) { + return userTaskList; + } + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 + if (sequenceFlow.getTargetFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); + continue; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + List childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (CollUtil.isNotEmpty(childUserTaskList)) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); + } + return userTaskList; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接退回到子流程中的情况,但存在从子流程出去到父流程情况 + * + * @param source 起始节点 + * @param target 目标节点 + * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 + * @return 结果 + */ + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set visitedElements) { + visitedElements = visitedElements == null ? new HashSet<>() : visitedElements; + // 不能是开始事件和子流程 + if (source instanceof StartEvent && isInEventSubprocess(source)) { + return false; + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + if (CollUtil.isEmpty(sequenceFlows)) { + return true; + } + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (visitedElements.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + visitedElements.add(sequenceFlow.getId()); + // 这条线路存在目标节点,这条线路完成,进入下个线路 + FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement(); + if (target.getId().equals(sourceFlowElement.getId())) { + continue; + } + // 如果目标节点为并行网关,则不继续 + if (sourceFlowElement instanceof ParallelGateway) { + return false; + } + // 否则就继续迭代 + if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) { + return false; + } + } + return true; + } + + /** + * 判断当前节点是否属于不同的子流程 + * + * @param flowElement 被判断的节点 + * @return true 表示属于子流程 + */ + private static boolean isInEventSubprocess(FlowElement flowElement) { + FlowElementsContainer flowElementsContainer = flowElement.getParentContainer(); + while (flowElementsContainer != null) { + if (flowElementsContainer instanceof EventSubProcess) { + return true; + } + + if (flowElementsContainer instanceof FlowElement) { + flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer(); + } else { + flowElementsContainer = null; + } + } + return false; + } + + /** + * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 + * + * @param source 起始节点 + * @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return 子级任务节点列表 + */ + public static List iteratorFindChildUserTasks(FlowElement source, List runTaskKeyList, + Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (sequenceFlows == null) { + return userTaskList; + } + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 + if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) { + userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); + continue; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + List childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (CollUtil.isNotEmpty(childUserTaskList)) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + return userTaskList; + } + + // ========== BPMN 流程预测相关的方法 ========== + + /** + * 流程预测,返回 StartEvent、UserTask、ServiceTask、EndEvent 节点元素,最终是 List 串行结果 + * + * @param bpmnModel BPMN 图 + * @param variables 变量 + * @return 节点元素数组 + */ + public static List simulateProcess(BpmnModel bpmnModel, Map variables) { + List resultElements = new ArrayList<>(); + Set visitElements = new HashSet<>(); + + // 从 StartEvent 开始遍历 + StartEvent startEvent = getStartEvent(bpmnModel); + simulateNextFlowElements(startEvent, variables, resultElements, visitElements); + + // 将 EndEvent 放在末尾。原因是,DFS 遍历,可能 EndEvent 在 resultElements 中 + List endEvents = CollUtil.removeWithAddIf(resultElements, + flowElement -> flowElement instanceof EndEvent); + resultElements.addAll(endEvents); + return resultElements; + } + + @SuppressWarnings("PatternVariableCanBeUsed") + private static void simulateNextFlowElements(FlowElement currentElement, Map variables, + List resultElements, Set visitElements) { + // 如果为空,或者已经遍历过,则直接结束 + if (currentElement == null) { + return; + } + if (visitElements.contains(currentElement)) { + return; + } + visitElements.add(currentElement); + + // 情况:StartEvent/EndEvent/UserTask/ServiceTask + if (currentElement instanceof StartEvent + || currentElement instanceof EndEvent + || currentElement instanceof UserTask + || currentElement instanceof ServiceTask) { + // 添加元素 + FlowNode flowNode = (FlowNode) currentElement; + resultElements.add(flowNode); + // 遍历子节点 + flowNode.getOutgoingFlows().forEach( + nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); + return; + } + + // 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的 + if (currentElement instanceof ExclusiveGateway) { + // 查找满足条件的 SequenceFlow 路径 + SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway((Gateway) currentElement, variables); + // 遍历满足条件的 SequenceFlow 路径 + if (matchSequenceFlow != null) { + simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements); + } + } + // 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的 + else if (currentElement instanceof InclusiveGateway) { + // 查找满足条件的 SequenceFlow 路径 + Collection matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway((Gateway) currentElement, variables); + // 遍历满足条件的 SequenceFlow 路径 + matchSequenceFlows.forEach( + flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements)); + } + // 情况:ParallelGateway 并行,都满足,都走 + else if (currentElement instanceof ParallelGateway) { + Gateway gateway = (Gateway) currentElement; + // 遍历子节点 + gateway.getOutgoingFlows().forEach( + nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); + } + } + + /** + * 根据当前节点,获取下一个节点 + * + * @param currentElement 当前节点 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + */ + @SuppressWarnings("PatternVariableCanBeUsed") + public static List getNextFlowNodes(FlowElement currentElement, BpmnModel bpmnModel, + Map variables){ + List nextFlowNodes = new ArrayList<>(); // 下一个执行的流程节点集合 + FlowNode currentNode = (FlowNode) currentElement; // 当前执行节点的基本属性 + List outgoingFlows = currentNode.getOutgoingFlows(); // 当前节点的关联节点 + if (CollUtil.isEmpty(outgoingFlows)) { + log.warn("[getNextFlowNodes][当前节点({}) 的 outgoingFlows 为空]", currentNode.getId()); + return nextFlowNodes; + } + + // 遍历每个出口流 + for (SequenceFlow outgoingFlow : outgoingFlows) { + // 获取目标节点的基本属性 + FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef()); + if (targetElement == null) { + continue; + } + // 如果是结束节点,直接返回 + if (targetElement instanceof EndEvent) { + break; + } + // 情况一:处理不同类型的网关 + if (targetElement instanceof Gateway) { + Gateway gateway = (Gateway) targetElement; + if (gateway instanceof ExclusiveGateway) { + handleExclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); + } else if (gateway instanceof InclusiveGateway) { + handleInclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); + } else if (gateway instanceof ParallelGateway) { + handleParallelGateway(gateway, bpmnModel, variables, nextFlowNodes); + } + } else { + // 情况二:如果不是网关,直接添加到下一个节点列表 + nextFlowNodes.add((FlowNode) targetElement); + } + } + return nextFlowNodes; + } + + /** + * 处理排它网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { + // 查找满足条件的 SequenceFlow 路径 + SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway(gateway, variables); + // 遍历满足条件的 SequenceFlow 路径 + if (matchSequenceFlow != null) { + FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + } + } + + /** + * 处理排它网关(Exclusive Gateway),选择符合条件的路径 + * + * @param gateway 排他网关 + * @param variables 流程变量 + * @return 符合条件的路径 + */ + private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { + SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); + if (matchSequenceFlow == null) { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); + // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 + if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) { + matchSequenceFlow = gateway.getOutgoingFlows().get(0); + } + } + return matchSequenceFlow; + } + + /** + * 处理包容网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { + // 查找满足条件的 SequenceFlow 路径集合 + Collection matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway(gateway, variables); + // 遍历满足条件的 SequenceFlow 路径,获取目标节点 + matchSequenceFlows.forEach(flow -> { + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + }); + } + + /** + * 处理排它网关(Inclusive Gateway),选择符合条件的路径 + * + * @param gateway 排他网关 + * @param variables 流程变量 + * @return 符合条件的路径 + */ + private static Collection findMatchSequenceFlowsByInclusiveGateway(Gateway gateway, Map variables) { + // 查找满足条件的 SequenceFlow 路径 + Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && evalConditionExpress(variables, flow.getConditionExpression())); + if (CollUtil.isEmpty(matchSequenceFlows)) { + matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); + // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 + if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) { + matchSequenceFlows = gateway.getOutgoingFlows(); + } + } + return matchSequenceFlows; + } + + + /** + * 处理并行网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { + // 并行网关,遍历所有出口路径,获取目标节点 + gateway.getOutgoingFlows().forEach(flow -> { + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + }); + } + + /** + * 计算条件表达式是否为 true 满足条件 + * + * @param variables 流程实例 + * @param expression 条件表达式 + * @return 是否满足条件 + */ + public static boolean evalConditionExpress(Map variables, String expression) { + if (expression == null) { + return Boolean.FALSE; + } + // 如果 variables 为空,则创建一个的原因?可能 expression 的计算,不依赖于 variables + if (variables == null) { + variables = new HashMap<>(); + } + + // 执行计算 + try { + Object result = FlowableUtils.getExpressionValue(variables, expression); + return Boolean.TRUE.equals(result); + } catch (FlowableException ex) { + // 为什么使用 info 日志?原因是,expression 如果从 variables 取不到值,会报错。实际这种情况下,可以忽略 + log.info("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", expression, variables, ex); + return Boolean.FALSE; + } + } + + @SuppressWarnings("PatternVariableCanBeUsed") + public static boolean isSequentialUserTask(FlowElement flowElement) { + if (!(flowElement instanceof UserTask)) { + return false; + } + UserTask userTask = (UserTask) flowElement; + MultiInstanceLoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics(); + return loopCharacteristics != null && loopCharacteristics.isSequential(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java new file mode 100644 index 0000000..7d54ce5 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -0,0 +1,362 @@ +package com.zt.plat.module.bpm.framework.flowable.core.util; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.tenant.core.context.TenantContextHolder; +import com.zt.plat.framework.tenant.core.util.TenantUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormFieldVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; +import lombok.SneakyThrows; +import org.flowable.common.engine.api.delegate.Expression; +import org.flowable.common.engine.api.variable.VariableContainer; +import org.flowable.common.engine.impl.el.ExpressionManager; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.common.engine.impl.variable.MapDelegateVariableContainer; +import org.flowable.engine.ManagementService; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.TaskInfo; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.stream.Collectors; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +/** + * Flowable 相关的工具方法 + * + * @author ZT + */ +public class FlowableUtils { + + // ========== User 相关的工具方法 ========== + + public static void setAuthenticatedUserId(Long userId) { + Authentication.setAuthenticatedUserId(String.valueOf(userId)); + } + + public static void clearAuthenticatedUserId() { + Authentication.setAuthenticatedUserId(null); + } + + public static V executeAuthenticatedUserId(Long userId, Callable callable) { + setAuthenticatedUserId(userId); + try { + return callable.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + clearAuthenticatedUserId(); + } + } + + public static String getTenantId() { + Long tenantId = TenantContextHolder.getTenantId(); + return tenantId != null ? String.valueOf(tenantId) : ProcessEngineConfiguration.NO_TENANT_ID; + } + + public static void execute(String tenantIdStr, Runnable runnable) { + if (ObjectUtil.isEmpty(tenantIdStr) + || Objects.equals(tenantIdStr, ProcessEngineConfiguration.NO_TENANT_ID)) { + runnable.run(); + } else { + Long tenantId = Long.valueOf(tenantIdStr); + TenantUtils.execute(tenantId, runnable); + } + } + + @SneakyThrows + public static V execute(String tenantIdStr, Callable callable) { + if (ObjectUtil.isEmpty(tenantIdStr) + || Objects.equals(tenantIdStr, ProcessEngineConfiguration.NO_TENANT_ID)) { + return callable.call(); + } else { + Long tenantId = Long.valueOf(tenantIdStr); + return TenantUtils.execute(tenantId, callable); + } + } + + // ========== Execution 相关的工具方法 ========== + + /** + * 格式化多实例(并签、或签)的 collectionVariable 变量(多实例对应的多审批人列表) + * + * @param activityId 活动编号 + * @return collectionVariable 变量 + */ + public static String formatExecutionCollectionVariable(String activityId) { + return activityId + "_assignees"; + } + + /** + * 格式化多实例(并签、或签)的 collectionElementVariable 变量(当前实例对应的一个审批人) + * + * @param activityId 活动编号 + * @return collectionElementVariable 变量 + */ + public static String formatExecutionCollectionElementVariable(String activityId) { + return activityId + "_assignee"; + } + + // ========== ProcessInstance 相关的工具方法 ========== + + public static Integer getProcessInstanceStatus(ProcessInstance processInstance) { + return getProcessInstanceStatus(processInstance.getProcessVariables()); + } + + public static Integer getProcessInstanceStatus(HistoricProcessInstance processInstance) { + return getProcessInstanceStatus(processInstance.getProcessVariables()); + } + + /** + * 获得流程实例的状态 + * + * @param processVariables 流程实例的 variables + * @return 状态 + */ + private static Integer getProcessInstanceStatus(Map processVariables) { + return (Integer) processVariables.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + } + + /** + * 获得流程实例的审批原因 + * + * @param processInstance 流程实例 + * @return 审批原因 + */ + public static String getProcessInstanceReason(HistoricProcessInstance processInstance) { + return (String) processInstance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON); + } + + /** + * 获得流程实例的表单 + * + * @param processInstance 流程实例 + * @return 表单 + */ + public static Map getProcessInstanceFormVariable(ProcessInstance processInstance) { + Map processVariables = new HashMap<>(processInstance.getProcessVariables()); + return filterProcessInstanceFormVariable(processVariables); + } + + /** + * 获得流程实例的表单 + * + * @param processInstance 流程实例 + * @return 表单 + */ + public static Map getProcessInstanceFormVariable(HistoricProcessInstance processInstance) { + Map processVariables = new HashMap<>(processInstance.getProcessVariables()); + return filterProcessInstanceFormVariable(processVariables); + } + + /** + * 过滤流程实例的表单 + * + * 为什么要过滤?目前使用 processVariables 存储所有流程实例的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示 + * + * @param processVariables 流程实例的 variables + * @return 过滤后的表单 + */ + public static Map filterProcessInstanceFormVariable(Map processVariables) { + processVariables.remove(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + return processVariables; + } + + /** + * 获得流程实例的发起用户选择的审批人 Map + * + * @param processInstance 流程实例 + * @return 发起用户选择的审批人 Map + */ + public static Map> getStartUserSelectAssignees(ProcessInstance processInstance) { + return processInstance != null ? getStartUserSelectAssignees(processInstance.getProcessVariables()) : null; + } + + /** + * 获得流程实例的发起用户选择的审批人 Map + * + * @param processVariables 流程变量 + * @return 发起用户选择的审批人 Map + */ + @SuppressWarnings("unchecked") + public static Map> getStartUserSelectAssignees(Map processVariables) { + if (processVariables == null) { + return new HashMap<>(); + } + return (Map>) processVariables.get( + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); + } + + /** + * 获得流程实例的审批用户选择的下一个节点的审批人 Map + * + * @param processInstance 流程实例 + * @return 审批用户选择的下一个节点的审批人Map + */ + public static Map> getApproveUserSelectAssignees(ProcessInstance processInstance) { + return processInstance != null ? getApproveUserSelectAssignees(processInstance.getProcessVariables()) : null; + } + + /** + * 获得流程实例的审批用户选择的下一个节点的审批人 Map + * + * @param processVariables 流程变量 + * @return 审批用户选择的下一个节点的审批人Map Map + */ + @SuppressWarnings("unchecked") + public static Map> getApproveUserSelectAssignees(Map processVariables) { + if (processVariables == null) { + return new HashMap<>(); + } + return (Map>) processVariables.get( + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); + } + + /** + * 获得流程实例的摘要 + * + * 仅有 {@link BpmModelFormTypeEnum#getType()} 表单,才有摘要。 + * 原因是,只有它才有表单项的配置,从而可以根据配置,展示摘要。 + * + * @param processDefinitionInfo 流程定义 + * @param processVariables 流程实例的 variables + * @return 摘要 + */ + public static List> getSummary(BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables) { + // 只有流程表单才会显示摘要! + if (ObjectUtil.isNull(processDefinitionInfo) + || !BpmModelFormTypeEnum.NORMAL.getType().equals(processDefinitionInfo.getFormType())) { + return null; + } + + // 解析表单配置 + Map formFieldsMap = new HashMap<>(); + processDefinitionInfo.getFormFields().forEach(formFieldStr -> { + BpmFormFieldVO formField = JsonUtils.parseObject(formFieldStr, BpmFormFieldVO.class); + if (formField != null) { + formFieldsMap.put(formField.getField(), formField); + } + }); + + // 情况一:当自定义了摘要 + if (ObjectUtil.isNotNull(processDefinitionInfo.getSummarySetting()) + && Boolean.TRUE.equals(processDefinitionInfo.getSummarySetting().getEnable())) { + return convertList(processDefinitionInfo.getSummarySetting().getSummary(), item -> { + BpmFormFieldVO formField = formFieldsMap.get(item); + if (formField != null) { + return new KeyValue(formField.getTitle(), + processVariables.getOrDefault(item, "").toString()); + } + return null; + }); + } + + // 情况二:默认摘要展示前三个表单字段 + return formFieldsMap.entrySet().stream() + .limit(3) + .map(entry -> new KeyValue<>(entry.getValue().getTitle(), + MapUtil.getStr(processVariables, entry.getValue().getField(), ""))) + .collect(Collectors.toList()); + } + + // ========== Task 相关的工具方法 ========== + + /** + * 获得任务的状态 + * + * @param task 任务 + * @return 状态 + */ + public static Integer getTaskStatus(TaskInfo task) { + return (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); + } + + /** + * 获得任务的审批原因 + * + * @param task 任务 + * @return 审批原因 + */ + public static String getTaskReason(TaskInfo task) { + return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_REASON); + } + + /** + * 获得任务的签名图片 URL + * + * @param task 任务 + * @return 签名图片 URL + */ + public static String getTaskSignPicUrl(TaskInfo task) { + return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_SIGN_PIC_URL); + } + + /** + * 获得任务的表单 + * + * @param task 任务 + * @return 表单 + */ + public static Map getTaskFormVariable(TaskInfo task) { + Map formVariables = new HashMap<>(task.getTaskLocalVariables()); + filterTaskFormVariable(formVariables); + return formVariables; + } + + /** + * 过滤任务的表单 + * + * 为什么要过滤?目前使用 taskLocalVariables 存储所有任务的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示 + * + * @param taskLocalVariables 任务的 taskLocalVariables + * @return 过滤后的表单 + */ + public static Map filterTaskFormVariable(Map taskLocalVariables) { + taskLocalVariables.remove(BpmnVariableConstants.TASK_VARIABLE_STATUS); + taskLocalVariables.remove(BpmnVariableConstants.TASK_VARIABLE_REASON); + return taskLocalVariables; + } + + // ========== Expression 相关的工具方法 ========== + + private static Object getExpressionValue(VariableContainer variableContainer, String expressionString, + ProcessEngineConfigurationImpl processEngineConfiguration) { + assert processEngineConfiguration != null; + ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager(); + assert expressionManager != null; + Expression expression = expressionManager.createExpression(expressionString); + return expression.getValue(variableContainer); + } + + public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) { + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); + if (processEngineConfiguration != null) { + return getExpressionValue(variableContainer, expressionString, processEngineConfiguration); + } + // 如果 ProcessEngineConfigurationImpl 获取不到,则需要通过 ManagementService 来获取 + ManagementService managementService = SpringUtil.getBean(ManagementService.class); + assert managementService != null; + return managementService.executeCommand(context -> + getExpressionValue(variableContainer, expressionString, CommandContextUtil.getProcessEngineConfiguration())); + } + + public static Object getExpressionValue(Map variable, String expressionString) { + VariableContainer variableContainer = new MapDelegateVariableContainer(variable, VariableContainer.empty()); + return getExpressionValue(variableContainer, expressionString); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java new file mode 100644 index 0000000..e7b5533 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -0,0 +1,1007 @@ +package com.zt.plat.module.bpm.framework.flowable.core.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.*; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; +import com.zt.plat.module.bpm.enums.definition.*; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; +import com.zt.plat.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; +import com.zt.plat.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate; +import com.zt.plat.module.bpm.service.task.listener.BpmCallActivityListener; +import com.zt.plat.module.bpm.service.task.listener.BpmUserTaskListener; +import org.flowable.bpmn.BpmnAutoLayout; +import org.flowable.bpmn.constants.BpmnXMLConstants; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.engine.delegate.ExecutionListener; +import org.flowable.engine.delegate.TaskListener; + +import java.util.*; + +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; +import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; +import static java.util.Arrays.asList; + +/** + * 仿钉钉/飞书的模型相关的工具方法 + *

+ * 1. 核心的逻辑实现,可见 {@link #buildBpmnModel(String, String, BpmSimpleModelNodeVO)} 方法 + * 2. 所有的 BpmSimpleModelNodeVO 转换成 BPMN FlowNode 元素,可见 {@link NodeConvert} 实现类 + * + * @author jason + */ +public class SimpleModelUtils { + + private static final Map NODE_CONVERTS = MapUtil.newHashMap(); + + static { + List converts = asList(new StartNodeConvert(), new EndNodeConvert(), + new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(), + new DelayTimerNodeConvert(), new TriggerNodeConvert(), + new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(), + new ChildProcessConvert()); + converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); + } + + /** + * 仿钉钉流程设计模型数据结构(json)转换成 Bpmn Model + *

+ * 整体逻辑如下: + * 1. 创建:BpmnModel、Process 对象 + * 2. 转换:将 BpmSimpleModelNodeVO 转换成 BPMN FlowNode 元素 + * 3. 连接:构建并添加节点之间的连线 Sequence Flow + * + * @param processId 流程标识 + * @param processName 流程名称 + * @param simpleModelNode 仿钉钉流程设计模型数据结构 + * @return Bpmn Model + */ + public static BpmnModel buildBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) { + // 1. 创建 BpmnModel + BpmnModel bpmnModel = new BpmnModel(); + bpmnModel.setTargetNamespace(BpmnXMLConstants.BPMN2_NAMESPACE); // 设置命名空间。不加这个,解析 Message 会报 NPE 异常 + // 创建 Process 对象 + Process process = new Process(); + process.setId(processId); + process.setName(processName); + process.setExecutable(Boolean.TRUE); + bpmnModel.addProcess(process); + + // 2.1 创建 StartNode 节点 + // 原因是:目前前端的第一个节点是“发起人节点”,所以这里构建一个 StartNode,用于创建 Bpmn 的 StartEvent 节点 + BpmSimpleModelNodeVO startNode = buildStartNode(); + startNode.setChildNode(simpleModelNode); + // 2.2 将前端传递的 simpleModelNode 数据结构(json),转换成从 BPMN FlowNode 元素,并添加到 Main Process 中 + traverseNodeToBuildFlowNode(startNode, process); + + // 3. 构建并添加节点之间的连线 Sequence Flow + EndEvent endEvent = getEndEvent(bpmnModel); + traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId()); + + // 4. 自动布局 + new BpmnAutoLayout(bpmnModel).execute(); + return bpmnModel; + } + + private static BpmSimpleModelNodeVO buildStartNode() { + return new BpmSimpleModelNodeVO().setId(START_EVENT_NODE_ID) + .setName(BpmSimpleModelNodeTypeEnum.START_NODE.getName()) + .setType(BpmSimpleModelNodeTypeEnum.START_NODE.getType()); + } + + /** + * 遍历节点,构建 FlowNode 元素 + * + * @param node SIMPLE 节点 + * @param process BPMN 流程 + */ + private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) { + // 1. 判断是否有效节点 + if (!isValidNode(node)) { + return; + } + BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType()); + Assert.notNull(nodeType, "模型节点类型({})不支持", node.getType()); + + // 2. 处理当前节点 + NodeConvert nodeConvert = NODE_CONVERTS.get(nodeType); + Assert.notNull(nodeConvert, "模型节点类型的转换器({})不存在", node.getType()); + List flowElements = nodeConvert.convertList(node); + flowElements.forEach(process::addFlowElement); + + // 3.1 情况一:如果当前是分支节点,并且存在条件节点,则处理每个条件的子节点 + if (BpmSimpleModelNodeTypeEnum.isBranchNode(node.getType()) + && CollUtil.isNotEmpty(node.getConditionNodes())) { + // 注意:这里的 item.getChildNode() 处理的是每个条件的子节点,不是处理条件 + node.getConditionNodes().forEach(item -> traverseNodeToBuildFlowNode(item.getChildNode(), process)); + } + + // 3.2 情况二:如果有“子”节点,则递归处理子节点 + traverseNodeToBuildFlowNode(node.getChildNode(), process); + } + + /** + * 遍历节点,构建 SequenceFlow 元素 + * + * @param process Bpmn 流程 + * @param node 当前节点 + * @param targetNodeId 目标节点 ID + */ + private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { + // 1.1 无效节点返回 + if (!isValidNode(node)) { + return; + } + // 1.2 END_NODE 直接返回 + BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType()); + Assert.notNull(nodeType, "模型节点类型不支持"); + if (nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { + return; + } + + // 2.1 情况一:普通节点 + if (!BpmSimpleModelNodeTypeEnum.isBranchNode(node.getType())) { + traverseNormalNodeToBuildSequenceFlow(process, node, targetNodeId); + } else { + // 2.2 情况二:分支节点 + traverseBranchNodeToBuildSequenceFlow(process, node, targetNodeId); + } + } + + /** + * 遍历普通(非条件)节点,构建 SequenceFlow 元素 + * + * @param process Bpmn 流程 + * @param node 当前节点 + * @param targetNodeId 目标节点 ID + */ + private static void traverseNormalNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { + BpmSimpleModelNodeVO childNode = node.getChildNode(); + boolean isChildNodeValid = isValidNode(childNode); + // 情况一:有“子”节点,则建立连线 + // 情况二:没有“子节点”,则直接跟 targetNodeId 建立连线。例如说,结束节点、条件分支(分支节点的孩子节点或聚合节点)的最后一个节点 + String finalTargetNodeId = isChildNodeValid ? childNode.getId() : targetNodeId; + + // 如果没有附加节点:则直接建立连线 + if (StrUtil.isEmpty(node.getAttachNodeId())) { + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId); + process.addFlowElement(sequenceFlow); + } else { + // 如果有附加节点:需要先建立和附加节点的连线,再建立附加节点和目标节点的连线。例如说,触发器节点(HTTP 回调) + List sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), finalTargetNodeId); + sequenceFlows.forEach(process::addFlowElement); + } + + // 因为有子节点,递归调用后续子节点 + if (isChildNodeValid) { + traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId); + } + } + + /** + * 构建有附加节点的连线 + * + * @param nodeId 当前节点 ID + * @param attachNodeId 附属节点 ID + * @param targetNodeId 目标节点 ID + */ + private static List buildAttachNodeSequenceFlow(String nodeId, String attachNodeId, String targetNodeId) { + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(nodeId, attachNodeId, null, null, null); + SequenceFlow attachSequenceFlow = buildBpmnSequenceFlow(attachNodeId, targetNodeId, null, null, null); + return CollUtil.newArrayList(sequenceFlow, attachSequenceFlow); + } + + /** + * 遍历条件节点,构建 SequenceFlow 元素 + * + * @param process Bpmn 流程 + * @param node 当前节点 + * @param targetNodeId 目标节点 ID + */ + private static void traverseBranchNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { + BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType()); + BpmSimpleModelNodeVO childNode = node.getChildNode(); + List conditionNodes = node.getConditionNodes(); + // TODO @芋艿 路由分支没有conditionNodes 这里注释会影响吗?@jason:一起帮忙瞅瞅! +// Assert.notEmpty(conditionNodes, "分支节点的条件节点不能为空"); + // 分支终点节点 ID + String branchEndNodeId = null; + if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) { // 条件分支或路由分支 + // 分两种情况 1. 分支节点有孩子节点为孩子节点 Id 2. 分支节点孩子为无效节点时 (分支嵌套且为分支最后一个节点) 为分支终点节点 ID + branchEndNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; + } else if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { // 并行分支或包容分支 + // 分支节点:分支终点节点 Id 为程序创建的网关集合节点。目前不会从前端传入。 + branchEndNodeId = buildGatewayJoinId(node.getId()); + } + Assert.notEmpty(branchEndNodeId, "分支终点节点 Id 不能为空"); + + // 3. 遍历分支节点 + if (nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) { + // 路由分支遍历 + for (BpmSimpleModelNodeVO.RouterSetting router : node.getRouterGroups()) { + SequenceFlow sequenceFlow = RouteBranchNodeConvert.buildSequenceFlow(node.getId(), router); + process.addFlowElement(sequenceFlow); + } + } else { + // 下面的注释,以如下情况举例子。分支 1:A->B->C->D->E,分支 2:A->D->E。其中,A 为分支节点, D 为 A 孩子节点 + for (BpmSimpleModelNodeVO item : conditionNodes) { + Assert.isTrue(Objects.equals(item.getType(), BpmSimpleModelNodeTypeEnum.CONDITION_NODE.getType()), + "条件节点类型({})不符合", item.getType()); + BpmSimpleModelNodeVO conditionChildNode = item.getChildNode(); + // 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况 + if (isValidNode(conditionChildNode)) { + // 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线 + SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), item); + process.addFlowElement(sequenceFlow); + // 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线 + traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId); + } else { + // 3.2 分支没有后续节点。例如说,建立 A->D 的连线 + SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, item); + process.addFlowElement(sequenceFlow); + } + } + } + + // 4.1 如果是并行分支、包容分支,由于是程序创建的聚合网关,需要手工创建聚合网关和下一个节点的连线 + if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { + String nextNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId); + process.addFlowElement(sequenceFlow); + // 4.2 如果是路由分支,需要连接后续节点为默认路由 + } else if (nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) { + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, node.getRouterDefaultFlowId(), + null, null); + process.addFlowElement(sequenceFlow); + } + + // 5. 递归调用后续节点 继续递归。例如说,建立 D->E 的连线 + traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId); + } + + private static SequenceFlow buildBpmnSequenceFlow(String sourceId, String targetId) { + return buildBpmnSequenceFlow(sourceId, targetId, null, null, null); + } + + private static SequenceFlow buildBpmnSequenceFlow(String sourceId, String targetId, + String sequenceFlowId, String sequenceFlowName, + String conditionExpression) { + Assert.notEmpty(sourceId, "sourceId 不能为空"); + Assert.notEmpty(targetId, "targetId 不能为空"); + // TODO @jason:如果 sequenceFlowId 不存在的时候,是不是要生成一个默认的 sequenceFlowId? @芋艿: 貌似不需要,Flowable 会默认生成;TODO @jason:建议还是搞一个,主要是后续好排查问题。 + // TODO @jason:如果 name 不存在的时候,是不是要生成一个默认的 name? @芋艿: 不需要生成默认的吧? 这个会在流程图展示的, 一般用户填写的。不好生成默认的吧;TODO @jason:建议还是搞一个,主要是后续好排查问题。 + SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId); + if (StrUtil.isNotEmpty(sequenceFlowId)) { + sequenceFlow.setId(sequenceFlowId); + } + if (StrUtil.isNotEmpty(sequenceFlowName)) { + sequenceFlow.setName(sequenceFlowName); + } + if (StrUtil.isNotEmpty(conditionExpression)) { + sequenceFlow.setConditionExpression(conditionExpression); + } + return sequenceFlow; + } + + public static boolean isValidNode(BpmSimpleModelNodeVO node) { + return node != null && node.getId() != null; + } + + public static boolean isSequentialApproveNode(BpmSimpleModelNodeVO node) { + return BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType().equals(node.getType()) + && BpmUserTaskApproveMethodEnum.SEQUENTIAL.getMethod().equals(node.getApproveMethod()); + } + + // ========== 各种 convert 节点的方法: BpmSimpleModelNodeVO => BPMN FlowElement ========== + + private interface NodeConvert { + + default List convertList(BpmSimpleModelNodeVO node) { + return Collections.singletonList(convert(node)); + } + + default FlowElement convert(BpmSimpleModelNodeVO node) { + throw new UnsupportedOperationException("请实现该方法"); + } + + BpmSimpleModelNodeTypeEnum getType(); + + } + + private static class StartNodeConvert implements NodeConvert { + + @Override + public StartEvent convert(BpmSimpleModelNodeVO node) { + StartEvent startEvent = new StartEvent(); + startEvent.setId(node.getId()); + startEvent.setName(node.getName()); + return startEvent; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.START_NODE; + } + + } + + private static class EndNodeConvert implements NodeConvert { + + @Override + public EndEvent convert(BpmSimpleModelNodeVO node) { + EndEvent endEvent = new EndEvent(); + endEvent.setId(node.getId()); + endEvent.setName(node.getName()); + // TODO @芋艿 + jason:要不要加一个终止定义? + return endEvent; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.END_NODE; + } + + } + + private static class StartUserNodeConvert implements NodeConvert { + + @Override + public UserTask convert(BpmSimpleModelNodeVO node) { + UserTask userTask = new UserTask(); + userTask.setId(node.getId()); + userTask.setName(node.getName()); + + // 人工审批 + addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, BpmUserTaskApproveTypeEnum.USER.getType()); + // 候选人策略为发起人自己 + addCandidateElements(BpmTaskCandidateStrategyEnum.START_USER.getStrategy(), null, userTask); + // 添加表单字段权限属性元素 + addFormFieldsPermission(node.getFieldsPermission(), userTask); + // 添加操作按钮配置属性元素 + addButtonsSetting(node.getButtonsSetting(), userTask); + // 使用自动通过策略 + // TODO @芋艿 复用了SKIP, 是否需要新加一个策略;TODO @芋艿:【回复】是不是应该类似飞书,搞个草稿状态。待定;还有一种策略,不标记自动通过,而是首次发起后,第一个节点,自动通过; + addAssignStartUserHandlerType(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType(), userTask); + return userTask; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.START_USER_NODE; + } + + } + + private static class ApproveNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + List flowElements = new ArrayList<>(2); + // 1. 构建用户任务 + UserTask userTask = buildBpmnUserTask(node); + flowElements.add(userTask); + + // 2. 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理 + if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) { + BoundaryEvent boundaryEvent = buildUserTaskTimeoutBoundaryEvent(userTask, node.getTimeoutHandler()); + flowElements.add(boundaryEvent); + } + return flowElements; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.APPROVE_NODE; + } + + /** + * 添加 UserTask 用户的审批超时 BoundaryEvent 事件 + * + * @param userTask 审批任务 + * @param timeoutHandler 超时处理器 + * @return BoundaryEvent 超时事件 + */ + private BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask, + BpmSimpleModelNodeVO.TimeoutHandler timeoutHandler) { + // 1. 创建 Timeout Boundary Event + String timeCycle = null; + if (Objects.equals(BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType(), timeoutHandler.getType()) && + timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) { + timeCycle = String.format("R%d/%s", + timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration()); + } + BoundaryEvent boundaryEvent = buildTimeoutBoundaryEvent(userTask, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT.getType(), + timeoutHandler.getTimeDuration(), timeCycle, null); + + // 2 添加超时执行动作元素 + addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, timeoutHandler.getType()); + return boundaryEvent; + } + + private UserTask buildBpmnUserTask(BpmSimpleModelNodeVO node) { + UserTask userTask = new UserTask(); + userTask.setId(node.getId()); + userTask.setName(node.getName()); + + // 如果不是审批人节点,则直接返回 + addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, node.getApproveType()); + if (ObjectUtil.notEqual(node.getApproveType(), BpmUserTaskApproveTypeEnum.USER.getType())) { + return userTask; + } + + // 添加候选人元素 + addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask); + // 添加表单字段权限属性元素 + addFormFieldsPermission(node.getFieldsPermission(), userTask); + // 添加操作按钮配置属性元素 + addButtonsSetting(node.getButtonsSetting(), userTask); + // 处理多实例(审批方式) + processMultiInstanceLoopCharacteristics(node.getApproveMethod(), node.getApproveRatio(), userTask); + // 添加任务被拒绝的处理元素 + addTaskRejectElements(node.getRejectHandler(), userTask); + // 添加用户任务的审批人与发起人相同时的处理元素 + addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask); + // 添加用户任务的空处理元素 + addAssignEmptyHandlerType(node.getAssignEmptyHandler(), userTask); + // 设置审批任务的截止时间 + if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) { + userTask.setDueDate(node.getTimeoutHandler().getTimeDuration()); + } + // 设置监听器 + addUserTaskListener(node, userTask); + // 添加是否需要签名 + addSignEnable(node.getSignEnable(), userTask); + // 审批意见 + addReasonRequire(node.getReasonRequire(), userTask); + // 节点类型 + addNodeType(node.getType(), userTask); + return userTask; + } + + private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) { + List flowableListeners = new ArrayList<>(3); + if (node.getTaskCreateListener() != null + && Boolean.TRUE.equals(node.getTaskCreateListener().getEnable())) { + FlowableListener flowableListener = new FlowableListener(); + flowableListener.setEvent(TaskListener.EVENTNAME_CREATE); + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); + addListenerConfig(flowableListener, node.getTaskCreateListener()); + flowableListeners.add(flowableListener); + } + if (node.getTaskAssignListener() != null + && Boolean.TRUE.equals(node.getTaskAssignListener().getEnable())) { + FlowableListener flowableListener = new FlowableListener(); + flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT); + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); + addListenerConfig(flowableListener, node.getTaskAssignListener()); + flowableListeners.add(flowableListener); + } + if (node.getTaskCompleteListener() != null + && Boolean.TRUE.equals(node.getTaskCompleteListener().getEnable())) { + FlowableListener flowableListener = new FlowableListener(); + flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE); + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); + addListenerConfig(flowableListener, node.getTaskCompleteListener()); + flowableListeners.add(flowableListener); + } + if (CollUtil.isNotEmpty(flowableListeners)) { + userTask.setTaskListeners(flowableListeners); + } + } + + private void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) { + BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod); + Assert.notNull(approveMethodEnum, "审批方式({})不能为空", approveMethodEnum); + // 添加审批方式的扩展属性 + addExtensionElement(userTask, USER_TASK_APPROVE_METHOD, approveMethod); + if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RANDOM) { + // 随机审批,不需要设置多实例属性 + return; + } + + // 处理多实例审批方式 + MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); + // 设置 collectionVariable。本系统用不到,仅仅为了 Flowable 校验不报错 + multiInstanceCharacteristics.setInputDataItem("${coll_userList}"); + if (approveMethodEnum == BpmUserTaskApproveMethodEnum.ANY) { + multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition()); + multiInstanceCharacteristics.setSequential(false); + } else if (approveMethodEnum == BpmUserTaskApproveMethodEnum.SEQUENTIAL) { + multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition()); + multiInstanceCharacteristics.setSequential(true); + multiInstanceCharacteristics.setLoopCardinality("1"); + } else if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RATIO) { + Assert.notNull(approveRatio, "通过比例不能为空"); + multiInstanceCharacteristics.setCompletionCondition( + String.format(approveMethodEnum.getCompletionCondition(), String.format("%.2f", approveRatio / 100D))); + multiInstanceCharacteristics.setSequential(false); + } + userTask.setLoopCharacteristics(multiInstanceCharacteristics); + } + + } + + private static class TransactorNodeConvert extends ApproveNodeConvert { + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE; + } + + } + + private static class CopyNodeConvert implements NodeConvert { + + @Override + public ServiceTask convert(BpmSimpleModelNodeVO node) { + ServiceTask serviceTask = new ServiceTask(); + serviceTask.setId(node.getId()); + serviceTask.setName(node.getName()); + serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + serviceTask.setImplementation("${" + BpmCopyTaskDelegate.BEAN_NAME + "}"); + + // 添加抄送候选人元素 + addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), serviceTask); + // 添加表单字段权限属性元素 + addFormFieldsPermission(node.getFieldsPermission(), serviceTask); + return serviceTask; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.COPY_NODE; + } + + } + + private static class ConditionBranchNodeConvert implements NodeConvert { + + @Override + public ExclusiveGateway convert(BpmSimpleModelNodeVO node) { + ExclusiveGateway exclusiveGateway = new ExclusiveGateway(); + exclusiveGateway.setId(node.getId()); + // TODO @jason:setName + + // 设置默认的序列流(条件) + BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(), + item -> BooleanUtil.isTrue(item.getConditionSetting().getDefaultFlow())); + Assert.notNull(defaultSeqFlow, "条件分支节点({})的默认序列流不能为空", node.getId()); + exclusiveGateway.setDefaultFlow(defaultSeqFlow.getId()); + return exclusiveGateway; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE; + } + + } + + private static class ParallelBranchNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + ParallelGateway parallelGateway = new ParallelGateway(); + parallelGateway.setId(node.getId()); + // TODO @jason:setName + + // 并行聚合网关由程序创建,前端不需要传入 + ParallelGateway joinParallelGateway = new ParallelGateway(); + joinParallelGateway.setId(buildGatewayJoinId(node.getId())); + // TODO @jason:setName + return CollUtil.newArrayList(parallelGateway, joinParallelGateway); + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE; + } + + } + + private static class InclusiveBranchNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + InclusiveGateway inclusiveGateway = new InclusiveGateway(); + inclusiveGateway.setId(node.getId()); + // 设置默认的序列流(条件) + BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(), + item -> BooleanUtil.isTrue(item.getConditionSetting().getDefaultFlow())); + Assert.notNull(defaultSeqFlow, "包容分支节点({})的默认序列流不能为空", node.getId()); + inclusiveGateway.setDefaultFlow(defaultSeqFlow.getId()); + // TODO @jason:setName + + // 并行聚合网关由程序创建,前端不需要传入 + InclusiveGateway joinInclusiveGateway = new InclusiveGateway(); + joinInclusiveGateway.setId(buildGatewayJoinId(node.getId())); + // TODO @jason:setName + return CollUtil.newArrayList(inclusiveGateway, joinInclusiveGateway); + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE; + } + + } + + public static class ConditionNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + // 原因是:正常情况下,它不会被调用到 + throw new UnsupportedOperationException("条件节点不支持转换"); + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.CONDITION_NODE; + } + + public static SequenceFlow buildSequenceFlow(String sourceId, String targetId, + BpmSimpleModelNodeVO node) { + String conditionExpression = buildConditionExpression(node.getConditionSetting()); + return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression); + } + } + + /** + * 构造条件表达式 + */ + public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { + // 并行网关不需要设置条件 + if (conditionSetting == null) { + return null; + } + return buildConditionExpression(conditionSetting.getConditionType(), conditionSetting.getConditionExpression(), + conditionSetting.getConditionGroups()); + } + + public static String buildConditionExpression(BpmSimpleModelNodeVO.RouterSetting routerSetting) { + return buildConditionExpression(routerSetting.getConditionType(), routerSetting.getConditionExpression(), + routerSetting.getConditionGroups()); + } + + public static String buildConditionExpression(Integer conditionType, String conditionExpression, ConditionGroups conditionGroups) { + BpmSimpleModeConditionTypeEnum conditionTypeEnum = BpmSimpleModeConditionTypeEnum.valueOf(conditionType); + if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.EXPRESSION) { + return conditionExpression; + } + if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.RULE) { + if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) { + return null; + } + List strConditionGroups = CollectionUtils.convertList(conditionGroups.getConditions(), item -> { + if (CollUtil.isEmpty(item.getRules())) { + return ""; + } + // 构造规则表达式 + List list = CollectionUtils.convertList(item.getRules(), (rule) -> { + String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide() + : "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号 + return String.format(" %s %s var:convertByType(%s,%s)", rule.getLeftSide(), rule.getOpCode(), rule.getLeftSide(), rightSide); + }); + // 构造条件组的表达式 + Boolean and = item.getAnd(); + return "(" + CollUtil.join(list, and ? " && " : " || ") + ")"; + }); + return String.format("${%s}", CollUtil.join(strConditionGroups, conditionGroups.getAnd() ? " && " : " || ")); + } + return null; + } + + public static class DelayTimerNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + List flowElements = new ArrayList<>(2); + // 1. 构建接收任务,通过接收任务可卡住节点 + ReceiveTask receiveTask = new ReceiveTask(); + receiveTask.setId(node.getId()); + receiveTask.setName(node.getName()); + flowElements.add(receiveTask); + + // 2. 添加接收任务的 Timer Boundary Event + if (node.getDelaySetting() != null) { + BoundaryEvent boundaryEvent = null; + if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { + boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), + node.getDelaySetting().getDelayTime(), null, null); + } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { + boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), + null, null, node.getDelaySetting().getDelayTime()); + } else { + throw new UnsupportedOperationException("不支持的延迟类型:" + node.getDelaySetting()); + } + flowElements.add(boundaryEvent); + } + return flowElements; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.DELAY_TIMER_NODE; + } + } + + public static class TriggerNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + Assert.notNull(node.getTriggerSetting(), "触发器节点设置不能为空"); + List flowElements = new ArrayList<>(2); + // HTTP 回调请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 + if (BpmTriggerTypeEnum.HTTP_CALLBACK.getType().equals(node.getTriggerSetting().getType())) { + Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 回调请求设置不能为空"); + ReceiveTask receiveTask = new ReceiveTask(); + receiveTask.setId("Activity_" + IdUtil.fastUUID()); + receiveTask.setName("HTTP 回调"); + node.setAttachNodeId(receiveTask.getId()); + flowElements.add(receiveTask); + // 重要:设置 callbackTaskDefineKey,用于 HTTP 回调 + node.getTriggerSetting().getHttpRequestSetting().setCallbackTaskDefineKey(receiveTask.getId()); + } + + // 触发器使用 ServiceTask 来实现 + ServiceTask serviceTask = new ServiceTask(); + serviceTask.setId(node.getId()); + serviceTask.setName(node.getName()); + serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + serviceTask.setImplementation("${" + BpmTriggerTaskDelegate.BEAN_NAME + "}"); + addExtensionElement(serviceTask, TRIGGER_TYPE, node.getTriggerSetting().getType()); + if (node.getTriggerSetting().getHttpRequestSetting() != null) { + addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting()); + } + if (node.getTriggerSetting().getFormSettings() != null) { + addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getFormSettings()); + } + flowElements.add(serviceTask); + return flowElements; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.TRIGGER_NODE; + } + } + + public static class RouteBranchNodeConvert implements NodeConvert { + + @Override + public ExclusiveGateway convert(BpmSimpleModelNodeVO node) { + ExclusiveGateway exclusiveGateway = new ExclusiveGateway(); + exclusiveGateway.setId(node.getId()); + + // 设置默认的序列流(条件) + node.setRouterDefaultFlowId("Flow_" + IdUtil.fastUUID()); + exclusiveGateway.setDefaultFlow(node.getRouterDefaultFlowId()); + return exclusiveGateway; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE; + } + + public static SequenceFlow buildSequenceFlow(String nodeId, BpmSimpleModelNodeVO.RouterSetting router) { + String conditionExpression = SimpleModelUtils.buildConditionExpression(router); + return buildBpmnSequenceFlow(nodeId, router.getNodeId(), null, null, conditionExpression); + } + + } + + private static class ChildProcessConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + List flowElements = new ArrayList<>(2); + BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting(); + List inVariables = childProcessSetting.getInVariables() == null ? + new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariables()); + CallActivity callActivity = new CallActivity(); + callActivity.setId(node.getId()); + callActivity.setName(node.getName()); + callActivity.setCalledElementType("key"); + // 1. 是否异步 + if (node.getChildProcessSetting().getAsync()) { + callActivity.setAsynchronous(true); + } + + // 2. 调用的子流程 + callActivity.setCalledElement(childProcessSetting.getCalledProcessDefinitionKey()); + callActivity.setProcessInstanceName(childProcessSetting.getCalledProcessDefinitionName()); + + // 3. 是否自动跳过子流程发起节点 + IOParameter ioParameter = new IOParameter(); + ioParameter.setSourceExpression(childProcessSetting.getSkipStartUserNode().toString()); + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + inVariables.add(ioParameter); + + // 4. 【默认需要传递的一些变量】流程状态 + ioParameter = new IOParameter(); + ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + inVariables.add(ioParameter); + + // 5. 主→子变量传递、子->主变量传递 + callActivity.setInParameters(inVariables); + if (ArrayUtil.isNotEmpty(childProcessSetting.getOutVariables()) && ObjUtil.notEqual(childProcessSetting.getAsync(), Boolean.TRUE)) { + callActivity.setOutParameters(childProcessSetting.getOutVariables()); + } + + // 6. 子流程发起人配置 + List executionListeners = new ArrayList<>(); + FlowableListener flowableListener = new FlowableListener(); + flowableListener.setEvent(ExecutionListener.EVENTNAME_START); + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + flowableListener.setImplementation(BpmCallActivityListener.DELEGATE_EXPRESSION); + FieldExtension fieldExtension = new FieldExtension(); + fieldExtension.setFieldName("listenerConfig"); + fieldExtension.setStringValue(JsonUtils.toJsonString(childProcessSetting.getStartUserSetting())); + flowableListener.getFieldExtensions().add(fieldExtension); + executionListeners.add(flowableListener); + callActivity.setExecutionListeners(executionListeners); + + // 7. 超时设置 + if (childProcessSetting.getTimeoutSetting() != null && Boolean.TRUE.equals(childProcessSetting.getTimeoutSetting().getEnable())) { + BoundaryEvent boundaryEvent = null; + if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { + boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), + childProcessSetting.getTimeoutSetting().getTimeExpression(), null, null); + } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { + boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType(), + null, null, childProcessSetting.getTimeoutSetting().getTimeExpression()); + } + flowElements.add(boundaryEvent); + } + + // 8. 多实例 + if (childProcessSetting.getMultiInstanceSetting() != null && Boolean.TRUE.equals(childProcessSetting.getMultiInstanceSetting().getEnable())) { + MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); + multiInstanceCharacteristics.setSequential(childProcessSetting.getMultiInstanceSetting().getSequential()); + if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY.getType())) { + multiInstanceCharacteristics.setLoopCardinality(childProcessSetting.getMultiInstanceSetting().getSource()); + } + if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType()) || + childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { + multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource()); + } + multiInstanceCharacteristics.setCompletionCondition(String.format(BpmUserTaskApproveMethodEnum.RATIO.getCompletionCondition(), + String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getApproveRatio() / 100D))); + callActivity.setLoopCharacteristics(multiInstanceCharacteristics); + addExtensionElement(callActivity, CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE, childProcessSetting.getMultiInstanceSetting().getSourceType()); + } + + // 添加节点类型 + addNodeType(node.getType(), callActivity); + flowElements.add(callActivity); + return flowElements; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.CHILD_PROCESS; + } + + } + + private static String buildGatewayJoinId(String id) { + return id + "_join"; + } + + private static BoundaryEvent buildTimeoutBoundaryEvent(Activity attachedToRef, Integer type, + String timeDuration, String timeCycle, String timeDate) { + // 1.1 定时器边界事件 + BoundaryEvent boundaryEvent = new BoundaryEvent(); + boundaryEvent.setId("Event-" + IdUtil.fastUUID()); + boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断 + boundaryEvent.setAttachedToRef(attachedToRef); + // 1.2 定义超时时间表达式 + TimerEventDefinition eventDefinition = new TimerEventDefinition(); + if (ObjUtil.isNotNull(timeDuration)) { + eventDefinition.setTimeDuration(timeDuration); + } + if (ObjUtil.isNotNull(timeDuration)) { + eventDefinition.setTimeCycle(timeCycle); + } + if (ObjUtil.isNotNull(timeDate)) { + eventDefinition.setTimeDate(timeDate); + } + boundaryEvent.addEventDefinition(eventDefinition); + + // 2. 添加定时器边界事件类型 + addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, type); + return boundaryEvent; + } + + // ========== SIMPLE 流程预测相关的方法 ========== + + public static List simulateProcess(BpmSimpleModelNodeVO rootNode, Map variables) { + List resultNodes = new ArrayList<>(); + + // 从头开始遍历 + simulateNextNode(rootNode, variables, resultNodes); + return resultNodes; + } + + private static void simulateNextNode(BpmSimpleModelNodeVO currentNode, Map variables, + List resultNodes) { + // 如果不合法(包括为空),则直接结束 + if (!isValidNode(currentNode)) { + return; + } + BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(currentNode.getType()); + Assert.notNull(nodeType, "模型节点类型不支持"); + + // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE/TRANSACTOR_NODE + if (nodeType == BpmSimpleModelNodeTypeEnum.START_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.START_USER_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS + || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { + // 添加元素 + resultNodes.add(currentNode); + } + + // 情况:CONDITION_BRANCH_NODE 排它,只有一个满足条件的。如果没有,就走默认的 + if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) { + // 查找满足条件的 BpmSimpleModelNodeVO 节点 + BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) + && evalConditionExpress(variables, conditionNode.getConditionSetting())); + if (matchConditionNode == null) { + matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), + conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); + } + Assert.notNull(matchConditionNode, "找不到条件节点({})", currentNode); + // 遍历满足条件的 BpmSimpleModelNodeVO 节点 + simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes); + } + + // 情况:INCLUSIVE_BRANCH_NODE 包容,多个满足条件的。如果没有,就走默认的 + if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { + // 查找满足条件的 BpmSimpleModelNodeVO 节点 + Collection matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) + && evalConditionExpress(variables, conditionNode.getConditionSetting())); + if (CollUtil.isEmpty(matchConditionNodes)) { + matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), + conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); + } + Assert.isTrue(!matchConditionNodes.isEmpty(), "找不到条件节点({})", currentNode); + // 遍历满足条件的 BpmSimpleModelNodeVO 节点 + matchConditionNodes.forEach(matchConditionNode -> + simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes)); + } + + // 情况:PARALLEL_BRANCH_NODE 并行,都满足,都走 + if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE) { + // 遍历所有 BpmSimpleModelNodeVO 节点 + currentNode.getConditionNodes().forEach(matchConditionNode -> + simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes)); + } + + // 遍历子节点 + simulateNextNode(currentNode.getChildNode(), variables, resultNodes); + } + + public static boolean evalConditionExpress(Map variables, BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { + return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java new file mode 100644 index 0000000..73f8350 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java @@ -0,0 +1,6 @@ +/** + * 属于 bpm 模块的 framework 封装 + * + * @author ZT + */ +package com.zt.plat.module.bpm.framework; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java new file mode 100644 index 0000000..0be19c0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java @@ -0,0 +1,17 @@ +package com.zt.plat.module.bpm.framework.rpc.config; + +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.PostApi; +import com.zt.plat.module.system.api.dict.DictDataApi; +import com.zt.plat.module.system.api.permission.PermissionApi; +import com.zt.plat.module.system.api.permission.RoleApi; +import com.zt.plat.module.system.api.sms.SmsSendApi; +import com.zt.plat.module.system.api.user.AdminUserApi; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +@Configuration(value = "bpmRpcConfiguration", proxyBeanMethods = false) +@EnableFeignClients(clients = {RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, + PermissionApi.class}) +public class RpcConfiguration { +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java new file mode 100644 index 0000000..4a733c4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package com.zt.plat.module.bpm.framework.rpc; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java new file mode 100644 index 0000000..eb6e099 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java @@ -0,0 +1,40 @@ +package com.zt.plat.module.bpm.framework.security.config; + +import com.zt.plat.framework.security.config.AuthorizeRequestsCustomizer; +import com.zt.plat.module.bpm.enums.ApiConstants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; + +/** + * Bpm 模块的 Security 配置 + */ +@Configuration(proxyBeanMethods = false, value = "bpmSecurityConfiguration") +public class SecurityConfiguration { + + @Bean("bpmAuthorizeRequestsCustomizer") + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { + return new AuthorizeRequestsCustomizer() { + + @Override + public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { + // TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案 + // Swagger 接口文档 + registry.requestMatchers("/v3/api-docs/**").permitAll() + .requestMatchers("/webjars/**").permitAll() + .requestMatchers("/swagger-ui").permitAll() + .requestMatchers("/swagger-ui/**").permitAll(); + // Druid 监控 + registry.requestMatchers("/druid/**").permitAll(); + // Spring Boot Actuator 的安全配置 + registry.requestMatchers("/actuator").permitAll() + .requestMatchers("/actuator/**").permitAll(); + // RPC 服务的安全配置 + registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); + } + + }; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java new file mode 100644 index 0000000..a637358 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package com.zt.plat.module.bpm.framework.security.core; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java new file mode 100644 index 0000000..ad4c2c0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java @@ -0,0 +1,28 @@ +package com.zt.plat.module.bpm.framework.web.config; + +import com.zt.plat.framework.common.enums.WebFilterOrderEnum; +import com.zt.plat.module.bpm.framework.web.core.FlowableWebFilter; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * bpm 模块的 web 组件的 Configuration + * + * @author ZT + */ +@Configuration(proxyBeanMethods = false) +public class BpmWebConfiguration { + + /** + * 配置 Flowable Web 过滤器 + */ + @Bean + public FilterRegistrationBean flowableWebFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new FlowableWebFilter()); + registrationBean.setOrder(WebFilterOrderEnum.FLOWABLE_FILTER); + return registrationBean; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java new file mode 100644 index 0000000..4699de5 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.framework.web.core; + +import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +/** + * Flowable Web 过滤器,将 userId 设置到 {@link org.flowable.common.engine.impl.identity.Authentication} 中 + * + * @author jason + */ +public class FlowableWebFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException { + try { + // 设置工作流的用户 + Long userId = SecurityFrameworkUtils.getLoginUserId(); + if (userId != null) { + FlowableUtils.setAuthenticatedUserId(userId); + } + // 过滤 + chain.doFilter(request, response); + } finally { + // 清理 + FlowableUtils.clearAuthenticatedUserId(); + } + } +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java new file mode 100644 index 0000000..62601dc --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java @@ -0,0 +1,4 @@ +/** + * bpm 模块的 web 配置 + */ +package com.zt.plat.module.bpm.framework.web; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java new file mode 100644 index 0000000..4039eb8 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java @@ -0,0 +1,12 @@ +/** + * bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能,基于 Flowable 6 版本实现。 + * 例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等 + * + * bpm 解释:https://baike.baidu.com/item/BPM/1933 + * + * 1. Controller URL:以 /bpm/ 开头,避免和其它 Module 冲突 + * 2. DataObject 表名:以 bpm_ 开头,方便在数据库中区分 + * + * 注意,由于 Bpm 模块下,容易和其它模块重名,所以类名都加载 Bpm 的前缀~ + */ +package com.zt.plat.module.bpm; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java new file mode 100644 index 0000000..6e24f44 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java @@ -0,0 +1,92 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * BPM 流程分类 Service 接口 + * + * @author ZT + */ +public interface BpmCategoryService { + + /** + * 创建流程分类 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createCategory(@Valid BpmCategorySaveReqVO createReqVO); + + /** + * 更新流程分类 + * + * @param updateReqVO 更新信息 + */ + void updateCategory(@Valid BpmCategorySaveReqVO updateReqVO); + + /** + * 删除流程分类 + * + * @param id 编号 + */ + void deleteCategory(Long id); + + /** + * 获得流程分类 + * + * @param id 编号 + * @return BPM 流程分类 + */ + BpmCategoryDO getCategory(Long id); + + /** + * 获得流程分类分页 + * + * @param pageReqVO 分页查询 + * @return 流程分类分页 + */ + PageResult getCategoryPage(BpmCategoryPageReqVO pageReqVO); + + /** + * 获得流程分类 Map,基于指定编码 + * + * @param codes 编号数组 + * @return 流程分类 Map + */ + default Map getCategoryMap(Collection codes) { + return convertMap(getCategoryListByCode(codes), BpmCategoryDO::getCode); + } + + /** + * 获得流程分类列表,基于指定编码 + * + * @return 流程分类列表 + */ + List getCategoryListByCode(Collection codes); + + /** + * 获得流程分类列表,基于指定状态 + * + * @param status 状态 + * @return 流程分类列表 + */ + List getCategoryListByStatus(Integer status); + + /** + * 批量更新流程分类的排序:每个分类的 sort 值,从 0 开始递增 + * + * @param ids 分类编号列表 + */ + void updateCategorySortBatch(List ids); + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java new file mode 100644 index 0000000..d111015 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java @@ -0,0 +1,130 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.mysql.category.BpmCategoryMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; + +/** + * BPM 流程分类 Service 实现类 + * + * @author ZT + */ +@Service +@Validated +public class BpmCategoryServiceImpl implements BpmCategoryService { + + @Resource + private BpmCategoryMapper bpmCategoryMapper; + + @Override + public Long createCategory(BpmCategorySaveReqVO createReqVO) { + // 校验唯一 + validateCategoryNameUnique(createReqVO); + validateCategoryCodeUnique(createReqVO); + // 插入 + BpmCategoryDO category = BeanUtils.toBean(createReqVO, BpmCategoryDO.class); + bpmCategoryMapper.insert(category); + return category.getId(); + } + + @Override + public void updateCategory(BpmCategorySaveReqVO updateReqVO) { + // 校验存在 + validateCategoryExists(updateReqVO.getId()); + validateCategoryNameUnique(updateReqVO); + validateCategoryCodeUnique(updateReqVO); + // 更新 + BpmCategoryDO updateObj = BeanUtils.toBean(updateReqVO, BpmCategoryDO.class); + bpmCategoryMapper.updateById(updateObj); + } + + private void validateCategoryNameUnique(BpmCategorySaveReqVO updateReqVO) { + BpmCategoryDO category = bpmCategoryMapper.selectByName(updateReqVO.getName()); + if (category == null + || ObjUtil.equal(category.getId(), updateReqVO.getId())) { + return; + } + throw exception(CATEGORY_NAME_DUPLICATE, updateReqVO.getName()); + } + + private void validateCategoryCodeUnique(BpmCategorySaveReqVO updateReqVO) { + BpmCategoryDO category = bpmCategoryMapper.selectByCode(updateReqVO.getCode()); + if (category == null + || ObjUtil.equal(category.getId(), updateReqVO.getId())) { + return; + } + throw exception(CATEGORY_CODE_DUPLICATE, updateReqVO.getCode()); + } + + @Override + public void deleteCategory(Long id) { + // 校验存在 + validateCategoryExists(id); + // 删除 + bpmCategoryMapper.deleteById(id); + } + + private void validateCategoryExists(Long id) { + if (bpmCategoryMapper.selectById(id) == null) { + throw exception(CATEGORY_NOT_EXISTS); + } + } + + @Override + public BpmCategoryDO getCategory(Long id) { + return bpmCategoryMapper.selectById(id); + } + + @Override + public PageResult getCategoryPage(BpmCategoryPageReqVO pageReqVO) { + return bpmCategoryMapper.selectPage(pageReqVO); + } + + @Override + public List getCategoryListByCode(Collection codes) { + if (CollUtil.isEmpty(codes)) { + return Collections.emptyList(); + } + return bpmCategoryMapper.selectListByCode(codes); + } + + @Override + public List getCategoryListByStatus(Integer status) { + return bpmCategoryMapper.selectListByStatus(status); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateCategorySortBatch(List ids) { + // 校验分类都存在 + List categories = bpmCategoryMapper.selectByIds(ids); + if (categories.size() != ids.size()) { + throw exception(CATEGORY_NOT_EXISTS); + } + + // 批量更新排序 + List updateList = IntStream.range(0, ids.size()) + .mapToObj(index -> new BpmCategoryDO().setId(ids.get(index)).setSort(index)) + .collect(Collectors.toList()); + bpmCategoryMapper.updateBatch(updateList); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java new file mode 100644 index 0000000..6a5a8c0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java @@ -0,0 +1,85 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + + +/** + * 动态表单 Service 接口 + * + * @author @风里雾里 + */ +public interface BpmFormService { + + /** + * 创建动态表单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createForm(@Valid BpmFormSaveReqVO createReqVO); + + /** + * 更新动态表单 + * + * @param updateReqVO 更新信息 + */ + void updateForm(@Valid BpmFormSaveReqVO updateReqVO); + + /** + * 删除动态表单 + * + * @param id 编号 + */ + void deleteForm(Long id); + + /** + * 获得动态表单 + * + * @param id 编号 + * @return 动态表单 + */ + BpmFormDO getForm(Long id); + + /** + * 获得动态表单列表 + * + * @return 动态表单列表 + */ + List getFormList(); + + /** + * 获得动态表单列表 + * + * @param ids 编号 + * @return 动态表单列表 + */ + List getFormList(Collection ids); + + /** + * 获得动态表单 Map + * + * @param ids 编号 + * @return 动态表单 Map + */ + default Map getFormMap(Collection ids) { + return CollectionUtils.convertMap(this.getFormList(ids), BpmFormDO::getId); + } + + /** + * 获得动态表单分页 + * + * @param pageReqVO 分页查询 + * @return 动态表单分页 + */ + PageResult getFormPage(BpmFormPageReqVO pageReqVO); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java new file mode 100644 index 0000000..73511ad --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java @@ -0,0 +1,114 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmFormMapper; +import com.zt.plat.module.bpm.enums.ErrorCodeConstants; +import com.zt.plat.module.bpm.service.definition.dto.BpmFormFieldRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; + +/** + * 动态表单 Service 实现类 + * + * @author 风里雾里 + */ +@Service +@Validated +public class BpmFormServiceImpl implements BpmFormService { + + @Resource + private BpmFormMapper formMapper; + + @Override + public Long createForm(BpmFormSaveReqVO createReqVO) { + this.validateFields(createReqVO.getFields()); + // 插入 + BpmFormDO form = BeanUtils.toBean(createReqVO, BpmFormDO.class); + formMapper.insert(form); + // 返回 + return form.getId(); + } + + @Override + public void updateForm(BpmFormSaveReqVO updateReqVO) { + validateFields(updateReqVO.getFields()); + // 校验存在 + validateFormExists(updateReqVO.getId()); + // 更新 + BpmFormDO updateObj = BeanUtils.toBean(updateReqVO, BpmFormDO.class); + formMapper.updateById(updateObj); + } + + @Override + public void deleteForm(Long id) { + // 校验存在 + this.validateFormExists(id); + // 删除 + formMapper.deleteById(id); + } + + private void validateFormExists(Long id) { + if (formMapper.selectById(id) == null) { + throw exception(ErrorCodeConstants.FORM_NOT_EXISTS); + } + } + + @Override + public BpmFormDO getForm(Long id) { + return formMapper.selectById(id); + } + + @Override + public List getFormList() { + return formMapper.selectList(); + } + + @Override + public List getFormList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return formMapper.selectBatchIds(ids); + } + + @Override + public PageResult getFormPage(BpmFormPageReqVO pageReqVO) { + return formMapper.selectPage(pageReqVO); + } + + /** + * 校验 Field,避免 field 重复 + * + * @param fields field 数组 + */ + private void validateFields(List fields) { + if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验 + return; + } + Map fieldMap = new HashMap<>(); // key 是 vModel,value 是 label + for (String field : fields) { + BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class); + Assert.notNull(fieldDTO); + String oldLabel = fieldMap.put(fieldDTO.getVModel(), fieldDTO.getLabel()); + // 如果不存在,则直接返回 + if (oldLabel == null) { + continue; + } + // 如果存在,则报错 + throw exception(ErrorCodeConstants.FORM_FIELD_REPEAT, oldLabel, fieldDTO.getLabel(), fieldDTO.getVModel()); + } + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java new file mode 100644 index 0000000..a9f8b1d --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java @@ -0,0 +1,134 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO; +import jakarta.validation.Valid; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Model; + +import java.util.List; + +/** + * 流程模型接口 + * + * @author yunlongn + */ +public interface BpmModelService { + + /** + * 获得流程模型列表 + * + * @param name 模型名称 + * @return 流程模型列表 + */ + List getModelList(String name); + + /** + * 创建流程模型 + * + * @param modelVO 创建信息 + * @return 创建的流程模型的编号 + */ + String createModel(@Valid BpmModelSaveReqVO modelVO); + + /** + * 获得流程模块 + * + * @param id 编号 + * @return 流程模型 + */ + Model getModel(String id); + + /** + * 获得流程模型的 BPMN XML + * + * @param id 编号 + * @return BPMN XML + */ + byte[] getModelBpmnXML(String id); + + /** + * 修改流程模型的 BPMN XML + * + * @param id 编号 + * @param bpmnXml BPMN XML + */ + void updateModelBpmnXml(String id, String bpmnXml); + + /** + * 修改流程模型 + * + * @param userId 用户编号 + * @param updateReqVO 更新信息 + */ + void updateModel(Long userId, @Valid BpmModelSaveReqVO updateReqVO); + + /** + * 批量更新模型排序 + * + * @param userId 用户编号 + * @param ids 编号列表 + */ + void updateModelSortBatch(Long userId, List ids); + + /** + * 将流程模型,部署成一个流程定义 + * + * @param userId 用户编号 + * @param id 编号 + */ + void deployModel(Long userId, String id); + + /** + * 删除模型 + * + * @param userId 用户编号 + * @param id 编号 + */ + void deleteModel(Long userId, String id); + + /** + * 清理模型,包括流程实例 + * + * @param userId 用户编号 + * @param id 编号 + */ + void cleanModel(Long userId, String id); + + /** + * 修改模型的状态,实际更新的部署的流程定义的状态 + * + * @param userId 用户编号 + * @param id 编号 + * @param state 状态 + */ + void updateModelState(Long userId, String id, Integer state); + + /** + * 获得流程定义编号对应的 BPMN Model + * + * @param processDefinitionId 流程定义编号 + * @return BPMN Model + */ + BpmnModel getBpmnModelByDefinitionId(String processDefinitionId); + + // ========== 仿钉钉/飞书的精简模型 ========= + + /** + * 获取仿钉钉流程设计模型结构 + * + * @param modelId 流程模型编号 + * @return 仿钉钉流程设计模型结构 + */ + BpmSimpleModelNodeVO getSimpleModel(String modelId); + + /** + * 更新仿钉钉流程设计模型 + * + * @param userId 用户编号 + * @param reqVO 请求信息 + */ + void updateSimpleModel(Long userId, @Valid BpmSimpleModelUpdateReqVO reqVO); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java new file mode 100644 index 0000000..d65084c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java @@ -0,0 +1,436 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.validation.ValidationUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO; +import com.zt.plat.module.bpm.convert.definition.BpmModelConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; +import com.zt.plat.module.bpm.enums.task.BpmReasonEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.StartEvent; +import org.flowable.bpmn.model.UserTask; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.HistoryService; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ModelQuery; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; +import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseCandidateStrategy; + +/** + * 流程模型实现:主要进行 Flowable {@link Model} 的维护 + * + * @author yunlongn + * @author ZT + * @author jason + */ +@Service +@Validated +@Slf4j +public class BpmModelServiceImpl implements BpmModelService { + + @Resource + private RepositoryService repositoryService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + @Resource + private BpmFormService bpmFormService; + + @Resource + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Resource + private HistoryService historyService; + @Resource + private RuntimeService runtimeService; + @Resource + private TaskService taskService; + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; + + @Override + public List getModelList(String name) { + ModelQuery modelQuery = repositoryService.createModelQuery(); + if (StrUtil.isNotEmpty(name)) { + modelQuery.modelNameLike("%" + name + "%"); + } + modelQuery.modelTenantId(FlowableUtils.getTenantId()); + return modelQuery.list(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String createModel(@Valid BpmModelSaveReqVO createReqVO) { + if (!ValidationUtils.isXmlNCName(createReqVO.getKey())) { + throw exception(MODEL_KEY_VALID); + } + // 1. 校验流程标识已经存在 + Model keyModel = getModelByKey(createReqVO.getKey()); + if (keyModel != null) { + throw exception(MODEL_KEY_EXISTS, createReqVO.getKey()); + } + + // 2. 创建 Model 对象 + createReqVO.setSort(System.currentTimeMillis()); // 使用当前时间,作为排序 + Model model = repositoryService.newModel(); + BpmModelConvert.INSTANCE.copyToModel(model, createReqVO); + model.setTenantId(FlowableUtils.getTenantId()); + + // 3. 保存模型 + saveModel(model, createReqVO); + return model.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 + public void updateModel(Long userId, BpmModelSaveReqVO updateReqVO) { + // 1. 校验流程模型存在 + Model model = validateModelManager(updateReqVO.getId(), userId); + + // 2. 填充 Model 信息 + BpmModelConvert.INSTANCE.copyToModel(model, updateReqVO); + + // 3. 保存模型 + saveModel(model, updateReqVO); + } + + /** + * 保存模型的基本信息、流程图 + * + * @param model 模型 + * @param saveReqVO 保存信息 + */ + private void saveModel(Model model, BpmModelSaveReqVO saveReqVO) { + // 1. 保存模型的基础信息 + repositoryService.saveModel(model); + + // 2. 保存流程图 + if (ObjUtil.equals(BpmModelTypeEnum.BPMN.getType(), saveReqVO.getType()) + && StrUtil.isNotEmpty(saveReqVO.getBpmnXml())) { + updateModelBpmnXml(model.getId(), saveReqVO.getBpmnXml()); + } else if (ObjUtil.equals(BpmModelTypeEnum.SIMPLE.getType(), saveReqVO.getType()) + && saveReqVO.getSimpleModel() != null) { + // JSON 转换成 bpmnModel + BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(), + saveReqVO.getSimpleModel()); + // 保存 Bpmn XML + updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel)); + // 保存 JSON 数据 + updateModelSimpleJson(model.getId(), saveReqVO.getSimpleModel()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateModelSortBatch(Long userId, List ids) { + // 1.1 校验流程模型存在 + List models = repositoryService.createModelQuery() + .modelTenantId(FlowableUtils.getTenantId()).list(); + models.removeIf(model -> !ids.contains(model.getId())); + if (ids.size() != models.size()) { + throw exception(MODEL_NOT_EXISTS); + } + Map modelMap = convertMap(models, Model::getId); + // 1.2 校验是否为管理员 + ids.forEach(id -> validateModelManager(id, userId)); + + // 保存排序 + long sort = System.currentTimeMillis(); // 使用时间戳 - i 作为排序 + for (int i = ids.size() - 1; i > 0; i--) { + Model model = modelMap.get(ids.get(i)); + // 更新模型 + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model).setSort(sort); + model.setMetaInfo(JsonUtils.toJsonString(metaInfo)); + repositoryService.saveModel(model); + // 更新排序 + processDefinitionService.updateProcessDefinitionSortByModelId(model.getId(), sort); + sort--; + } + } + + private Model validateModelExists(String id) { + Model model = repositoryService.getModel(id); + if (model == null) { + throw exception(MODEL_NOT_EXISTS); + } + return model; + } + + /** + * 校验是否有流程模型的管理权限 + * + * @param id 流程模型编号 + * @param userId 用户编号 + * @return 流程模型 + */ + private Model validateModelManager(String id, Long userId) { + Model model = validateModelExists(id); + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); + if (metaInfo == null || !CollUtil.contains(metaInfo.getManagerUserIds(), userId)) { + throw exception(MODEL_UPDATE_FAIL_NOT_MANAGER, model.getName()); + } + return model; + } + + @Override + @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 + public void deployModel(Long userId, String id) { + // 1.1 校验流程模型存在 + Model model = validateModelManager(id, userId); + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); + // 1.2 校验流程图 + byte[] bpmnBytes = getModelBpmnXML(model.getId()); + validateBpmnXml(bpmnBytes, metaInfo.getType()); + // 1.3 校验表单已配 + BpmFormDO form = validateFormConfig(metaInfo); + // 1.4 校验任务分配规则已配置 + taskCandidateInvoker.validateBpmnConfig(bpmnBytes); + // 1.5 获取仿钉钉流程设计器模型数据 + String simpleJson = getModelSimpleJson(model.getId()); + + // 2.1 创建流程定义 + String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, simpleJson, + form); + + // 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。 + updateProcessDefinitionSuspended(model.getDeploymentId()); + + // 2.3 更新 model 的 deploymentId,进行关联 + ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId); + model.setDeploymentId(definition.getDeploymentId()); + repositoryService.saveModel(model); + } + + private void validateBpmnXml(byte[] bpmnBytes, Integer type) { + BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); + if (bpmnModel == null) { + throw exception(MODEL_NOT_EXISTS); + } + // 1. 没有 StartEvent + StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel); + if (startEvent == null) { + throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); + } + // 2. 校验 UserTask 的 name 都配置了 + List userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); + userTasks.forEach(userTask -> { + if (StrUtil.isEmpty(userTask.getName())) { + throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); + } + }); + // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选”,BPMN 设计器,校验第一个用户任务节点,SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点 + UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1); + if (firUserTask == null) { + return; + } + Integer candidateStrategy = parseCandidateStrategy(firUserTask); + if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { + throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, firUserTask.getName()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteModel(Long userId, String id) { + // 校验流程模型存在 + Model model = validateModelManager(id, userId); + + // 执行删除 + repositoryService.deleteModel(id); + // 禁用流程定义 + updateProcessDefinitionSuspended(model.getDeploymentId()); + } + + @Override + public void cleanModel(Long userId, String id) { + // 1. 校验流程模型存在 + Model model = validateModelManager(id, userId); + + // 2. 清理所有流程数据 + // 2.1 先取消所有正在运行的流程 + List processInstances = runtimeService.createProcessInstanceQuery() + .processDefinitionKey(model.getKey()).list(); + processInstances.forEach(processInstance -> { + runtimeService.deleteProcessInstance(processInstance.getId(), + BpmReasonEnum.CANCEL_BY_SYSTEM.getReason()); + historyService.deleteHistoricProcessInstance(processInstance.getId()); + processInstanceCopyService.deleteProcessInstanceCopy(processInstance.getId()); + }); + // 2.2 再从历史中删除所有相关的流程数据 + List historicProcessInstances = historyService.createHistoricProcessInstanceQuery() + .processDefinitionKey(model.getKey()).list(); + historicProcessInstances.forEach(historicProcessInstance -> { + historyService.deleteHistoricProcessInstance(historicProcessInstance.getId()); + processInstanceCopyService.deleteProcessInstanceCopy(historicProcessInstance.getId()); + }); + // 2.3 清理所有 Task + List tasks = taskService.createTaskQuery() + .processDefinitionKey(model.getKey()).list(); + tasks.forEach(task -> taskService.deleteTask(task.getId(),BpmReasonEnum.CANCEL_BY_PROCESS_CLEAN.getReason())); + } + + @Override + public void updateModelState(Long userId, String id, Integer state) { + // 1.1 校验流程模型存在 + Model model = validateModelManager(id, userId); + // 1.2 校验流程定义存在 + ProcessDefinition definition = processDefinitionService + .getProcessDefinitionByDeploymentId(model.getDeploymentId()); + if (definition == null) { + throw exception(PROCESS_DEFINITION_NOT_EXISTS); + } + + // 2. 更新状态 + processDefinitionService.updateProcessDefinitionState(definition.getId(), state); + } + + @Override + public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) { + return repositoryService.getBpmnModel(processDefinitionId); + } + + @Override + public BpmSimpleModelNodeVO getSimpleModel(String modelId) { + Model model = validateModelExists(modelId); + // 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ ,获取仿钉钉快搭模型的 JSON 数据 + String json = getModelSimpleJson(model.getId()); + return JsonUtils.parseObject(json, BpmSimpleModelNodeVO.class); + } + + @Override + public void updateSimpleModel(Long userId, BpmSimpleModelUpdateReqVO reqVO) { + // 1. 校验流程模型存在 + Model model = validateModelManager(reqVO.getId(), userId); + + // 2.1 JSON 转换成 bpmnModel + BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModel()); + // 2.2 保存 Bpmn XML + updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel)); + // 2.3 保存 JSON 数据 + updateModelSimpleJson(model.getId(), reqVO.getSimpleModel()); + } + + /** + * 校验流程表单已配置 + * + * @param metaInfo 流程模型元数据 + * @return 表单配置 + */ + private BpmFormDO validateFormConfig(BpmModelMetaInfoVO metaInfo) { + if (metaInfo == null || metaInfo.getFormType() == null) { + throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); + } + // 校验表单存在 + if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) { + if (metaInfo.getFormId() == null) { + throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); + } + BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId()); + if (form == null) { + throw exception(FORM_NOT_EXISTS); + } + return form; + } else { + if (StrUtil.isEmpty(metaInfo.getFormCustomCreatePath()) + || StrUtil.isEmpty(metaInfo.getFormCustomViewPath())) { + throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); + } + return null; + } + } + + @Override + public void updateModelBpmnXml(String id, String bpmnXml) { + if (StrUtil.isEmpty(bpmnXml)) { + return; + } + repositoryService.addModelEditorSource(id, StrUtil.utf8Bytes(bpmnXml)); + } + + @SuppressWarnings("JavaExistingMethodCanBeUsed") + private String getModelSimpleJson(String id) { + byte[] bytes = repositoryService.getModelEditorSourceExtra(id); + if (ArrayUtil.isEmpty(bytes)) { + return null; + } + return StrUtil.utf8Str(bytes); + } + + private void updateModelSimpleJson(String id, BpmSimpleModelNodeVO node) { + if (node == null) { + return; + } + byte[] bytes = JsonUtils.toJsonByte(node); + repositoryService.addModelEditorSourceExtra(id, bytes); + } + + /** + * 挂起 deploymentId 对应的流程定义 + *

+ * 注意:这里一个 deploymentId 只关联一个流程定义 + * + * @param deploymentId 流程发布Id + */ + private void updateProcessDefinitionSuspended(String deploymentId) { + if (StrUtil.isEmpty(deploymentId)) { + return; + } + ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId); + if (oldDefinition == null) { + return; + } + processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), + SuspensionState.SUSPENDED.getStateCode()); + } + + private Model getModelByKey(String key) { + return repositoryService.createModelQuery() + .modelTenantId(FlowableUtils.getTenantId()) + .modelKey(key).singleResult(); + } + + @Override + public Model getModel(String id) { + return repositoryService.getModel(id); + } + + @Override + public byte[] getModelBpmnXML(String id) { + return repositoryService.getModelEditorSource(id); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java new file mode 100644 index 0000000..f819b48 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java @@ -0,0 +1,181 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * 流程定义接口 + * + * @author yunlong.li + * @author ZJQ + * @author ZT + */ +public interface BpmProcessDefinitionService { + + /** + * 获得流程定义分页 + * + * @param pageReqVO 分页入参 + * @return 流程定义 Page + */ + PageResult getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageReqVO); + + /** + * 获得流程定义列表 + * + * @param suspensionState 中断状态 + * @return 流程定义列表 + */ + List getProcessDefinitionListBySuspensionState(Integer suspensionState); + + /** + * 基于流程模型,创建流程定义 + * + * @param model 流程模型 + * @param modelMetaInfo 流程模型元信息 + * @param bpmnBytes BPMN XML 字节数组 + * @param simpleJson SIMPLE Model JSON + * @param form 表单 + * @return 流程编号 + */ + String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo, + byte[] bpmnBytes, String simpleJson, BpmFormDO form); + + /** + * 更新流程定义状态 + * + * @param id 流程定义的编号 + * @param state 状态 + */ + void updateProcessDefinitionState(String id, Integer state); + + /** + * 更新模型编号 + * + * @param modelId 流程定义编号 + * @param sort 排序 + */ + void updateProcessDefinitionSortByModelId(String modelId, Long sort); + + /** + * 获得流程定义对应的 BPMN + * + * @param id 流程定义编号 + * @return BPMN + */ + BpmnModel getProcessDefinitionBpmnModel(String id); + + /** + * 获得流程定义的信息 + * + * @param id 流程定义编号 + * @return 流程定义信息 + */ + BpmProcessDefinitionInfoDO getProcessDefinitionInfo(String id); + + /** + * 获得流程定义的信息 List + * + * @param ids 流程定义编号数组 + * @return 流程额定义信息数组 + */ + List getProcessDefinitionInfoList(Collection ids); + + default Map getProcessDefinitionInfoMap(Set ids) { + return convertMap(getProcessDefinitionInfoList(ids), BpmProcessDefinitionInfoDO::getProcessDefinitionId); + } + + /** + * 获得流程定义编号对应的 ProcessDefinition + * + * @param id 流程定义编号 + * @return 流程定义 + */ + ProcessDefinition getProcessDefinition(String id); + + /** + * 获得 ids 对应的 ProcessDefinition 数组 + * + * @param ids 编号的数组 + * @return 流程定义的数组 + */ + List getProcessDefinitionList(Set ids); + + default Map getProcessDefinitionMap(Set ids) { + return convertMap(getProcessDefinitionList(ids), ProcessDefinition::getId); + } + + /** + * 获得 deploymentId 对应的 ProcessDefinition + * + * @param deploymentId 部署编号 + * @return 流程定义 + */ + ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId); + + /** + * 获得 deploymentIds 对应的 ProcessDefinition 数组 + * + * @param deploymentIds 部署编号的数组 + * @return 流程定义的数组 + */ + List getProcessDefinitionListByDeploymentIds(Set deploymentIds); + + /** + * 获得流程定义标识对应的激活的流程定义 + * + * @param key 流程定义的标识 + * @return 流程定义 + */ + ProcessDefinition getActiveProcessDefinition(String key); + + /** + * 判断用户是否可以使用该流程定义,进行流程的发起 + * + * @param processDefinition 流程定义 + * @param userId 用户编号 + * @return 是否可以发起流程 + */ + boolean canUserStartProcessDefinition(BpmProcessDefinitionInfoDO processDefinition, Long userId); + + /** + * 获得 ids 对应的 Deployment Map + * + * @param ids 部署编号的数组 + * @return 流程部署 Map + */ + default Map getDeploymentMap(Set ids) { + return convertMap(getDeploymentList(ids), Deployment::getId); + } + + /** + * 获得 ids 对应的 Deployment 数组 + * + * @param ids 部署编号的数组 + * @return 流程部署的数组 + */ + List getDeploymentList(Set ids); + + /** + * 获得 id 对应的 Deployment + * + * @param id 部署编号 + * @return 流程部署 + */ + Deployment getDeployment(String id); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java new file mode 100644 index 0000000..f2f1d6e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java @@ -0,0 +1,248 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.framework.common.util.object.PageUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmProcessDefinitionInfoMapper; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.impl.db.SuspensionState; +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.flowable.engine.repository.ProcessDefinitionQuery; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.addIfNotNull; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; +import static java.util.Collections.emptyList; + +/** + * 流程定义实现 + * 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护 + * + * @author yunlongn + * @author ZJQ + * @author ZT + */ +@Service +@Validated +@Slf4j +public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionService { + + @Resource + private RepositoryService repositoryService; + + @Resource + private BpmProcessDefinitionInfoMapper processDefinitionMapper; + + @Resource + private AdminUserApi adminUserApi; + + @Override + public ProcessDefinition getProcessDefinition(String id) { + return repositoryService.getProcessDefinition(id); + } + + @Override + public List getProcessDefinitionList(Set ids) { + return repositoryService.createProcessDefinitionQuery().processDefinitionIds(ids).list(); + } + + @Override + public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) { + if (StrUtil.isEmpty(deploymentId)) { + return null; + } + return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult(); + } + + @Override + public List getProcessDefinitionListByDeploymentIds(Set deploymentIds) { + if (CollUtil.isEmpty(deploymentIds)) { + return emptyList(); + } + return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list(); + } + + @Override + public ProcessDefinition getActiveProcessDefinition(String key) { + return repositoryService.createProcessDefinitionQuery() + .processDefinitionTenantId(FlowableUtils.getTenantId()) + .processDefinitionKey(key).active().singleResult(); + } + + @Override + public boolean canUserStartProcessDefinition(BpmProcessDefinitionInfoDO processDefinition, Long userId) { + if (processDefinition == null) { + return false; + } + + // 校验用户是否在允许发起的用户列表中 + if (CollUtil.isNotEmpty(processDefinition.getStartUserIds())) { + return processDefinition.getStartUserIds().contains(userId); + } + + // 校验用户是否在允许发起的部门列表中 + if (CollUtil.isNotEmpty(processDefinition.getStartDeptIds())) { + AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); + return user != null + && DeptUtil.getDeptId(user) > 0L + && processDefinition.getStartDeptIds().contains(DeptUtil.getDeptId(user)); + } + + // 都为空,则所有人都可以发起 + return true; + } + + @Override + public List getDeploymentList(Set ids) { + if (CollUtil.isEmpty(ids)) { + return emptyList(); + } + List list = new ArrayList<>(ids.size()); + for (String id : ids) { + addIfNotNull(list, getDeployment(id)); + } + return list; + } + + @Override + public Deployment getDeployment(String id) { + if (StrUtil.isEmpty(id)) { + return null; + } + return repositoryService.createDeploymentQuery().deploymentId(id).singleResult(); + } + + @Override + public String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo, + byte[] bpmnBytes, String simpleJson, BpmFormDO form) { + // 创建 Deployment 部署 + Deployment deploy = repositoryService.createDeployment() + .key(model.getKey()).name(model.getName()).category(model.getCategory()) + .addBytes(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnBytes) + .tenantId(FlowableUtils.getTenantId()) + .disableSchemaValidation() // 禁用 XML Schema 验证,因为有自定义的属性 + .deploy(); + + // 设置 ProcessDefinition 的 category 分类 + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery() + .deploymentId(deploy.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory()); + // 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 的 id 和 name 决定 + // 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。 + // 否则,会导致 ProcessDefinition 的分页无法查询到。 + if (!Objects.equals(definition.getKey(), model.getKey())) { + throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, model.getKey(), definition.getKey()); + } + if (!Objects.equals(definition.getName(), model.getName())) { + throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, model.getName(), definition.getName()); + } + + // 插入拓展表 + BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class) + .setModelId(model.getId()).setCategory(model.getCategory()).setProcessDefinitionId(definition.getId()) + .setModelType(modelMetaInfo.getType()).setSimpleModel(simpleJson); + if (form != null) { + definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf()); + } + processDefinitionMapper.insert(definitionDO); + return definition.getId(); + } + + @Override + public void updateProcessDefinitionState(String id, Integer state) { + ProcessDefinition processDefinition = repositoryService.getProcessDefinition(id); + if (processDefinition == null) { + throw exception(PROCESS_DEFINITION_NOT_EXISTS); + } + + // 激活 + if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { + if (processDefinition.isSuspended()) { + repositoryService.activateProcessDefinitionById(id, false, null); + } + return; + } + // 挂起 + if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { + // suspendProcessInstances = false,进行中的任务,不进行挂起。 + // 原因:只要新的流程不允许发起即可,老流程继续可以执行。 + if (!processDefinition.isSuspended()) { + repositoryService.suspendProcessDefinitionById(id, false, null); + } + return; + } + log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state); + } + + @Override + public void updateProcessDefinitionSortByModelId(String modelId, Long sort) { + processDefinitionMapper.updateByModelId(modelId, new BpmProcessDefinitionInfoDO().setSort(sort)); + } + + @Override + public BpmnModel getProcessDefinitionBpmnModel(String id) { + return repositoryService.getBpmnModel(id); + } + + @Override + public BpmProcessDefinitionInfoDO getProcessDefinitionInfo(String id) { + return processDefinitionMapper.selectByProcessDefinitionId(id); + } + + @Override + public List getProcessDefinitionInfoList(Collection ids) { + return processDefinitionMapper.selectListByProcessDefinitionIds(ids); + } + + @Override + public PageResult getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageVO) { + ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery(); + query.processDefinitionTenantId(FlowableUtils.getTenantId()); + if (StrUtil.isNotBlank(pageVO.getKey())) { + query.processDefinitionKey(pageVO.getKey()); + } + // 执行查询 + long count = query.count(); + if (count == 0) { + return PageResult.empty(count); + } + List list = query.orderByProcessDefinitionVersion().desc() + .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + return new PageResult<>(list, count); + } + + @Override + public List getProcessDefinitionListBySuspensionState(Integer suspensionState) { + // 拼接查询条件 + ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery(); + if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), suspensionState)) { + query.suspended(); + } else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), suspensionState)) { + query.active(); + } + // 执行查询 + query.processDefinitionTenantId(FlowableUtils.getTenantId()); + return query.list(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java new file mode 100644 index 0000000..448300c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java @@ -0,0 +1,54 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; +import jakarta.validation.Valid; + +/** + * BPM 流程表达式 Service 接口 + * + * @author ZT + */ +public interface BpmProcessExpressionService { + + /** + * 创建流程表达式 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createProcessExpression(@Valid BpmProcessExpressionSaveReqVO createReqVO); + + /** + * 更新流程表达式 + * + * @param updateReqVO 更新信息 + */ + void updateProcessExpression(@Valid BpmProcessExpressionSaveReqVO updateReqVO); + + /** + * 删除流程表达式 + * + * @param id 编号 + */ + void deleteProcessExpression(Long id); + + /** + * 获得流程表达式 + * + * @param id 编号 + * @return 流程表达式 + */ + BpmProcessExpressionDO getProcessExpression(Long id); + + /** + * 获得流程表达式分页 + * + * @param pageReqVO 分页查询 + * @return 流程表达式分页 + */ + PageResult getProcessExpressionPage(BpmProcessExpressionPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java new file mode 100644 index 0000000..e367aff --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java @@ -0,0 +1,70 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmProcessExpressionMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.PROCESS_EXPRESSION_NOT_EXISTS; + +/** + * BPM 流程表达式 Service 实现类 + * + * @author ZT + */ +@Service +@Validated +public class BpmProcessExpressionServiceImpl implements BpmProcessExpressionService { + + @Resource + private BpmProcessExpressionMapper processExpressionMapper; + + @Override + public Long createProcessExpression(BpmProcessExpressionSaveReqVO createReqVO) { + // 插入 + BpmProcessExpressionDO processExpression = BeanUtils.toBean(createReqVO, BpmProcessExpressionDO.class); + processExpressionMapper.insert(processExpression); + // 返回 + return processExpression.getId(); + } + + @Override + public void updateProcessExpression(BpmProcessExpressionSaveReqVO updateReqVO) { + // 校验存在 + validateProcessExpressionExists(updateReqVO.getId()); + // 更新 + BpmProcessExpressionDO updateObj = BeanUtils.toBean(updateReqVO, BpmProcessExpressionDO.class); + processExpressionMapper.updateById(updateObj); + } + + @Override + public void deleteProcessExpression(Long id) { + // 校验存在 + validateProcessExpressionExists(id); + // 删除 + processExpressionMapper.deleteById(id); + } + + private void validateProcessExpressionExists(Long id) { + if (processExpressionMapper.selectById(id) == null) { + throw exception(PROCESS_EXPRESSION_NOT_EXISTS); + } + } + + @Override + public BpmProcessExpressionDO getProcessExpression(Long id) { + return processExpressionMapper.selectById(id); + } + + @Override + public PageResult getProcessExpressionPage(BpmProcessExpressionPageReqVO pageReqVO) { + return processExpressionMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java new file mode 100644 index 0000000..9f111a7 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java @@ -0,0 +1,54 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; +import jakarta.validation.Valid; + +/** + * BPM 流程监听器 Service 接口 + * + * @author ZT + */ +public interface BpmProcessListenerService { + + /** + * 创建流程监听器 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createProcessListener(@Valid BpmProcessListenerSaveReqVO createReqVO); + + /** + * 更新流程监听器 + * + * @param updateReqVO 更新信息 + */ + void updateProcessListener(@Valid BpmProcessListenerSaveReqVO updateReqVO); + + /** + * 删除流程监听器 + * + * @param id 编号 + */ + void deleteProcessListener(Long id); + + /** + * 获得流程监听器 + * + * @param id 编号 + * @return 流程监听器 + */ + BpmProcessListenerDO getProcessListener(Long id); + + /** + * 获得流程监听器分页 + * + * @param pageReqVO 分页查询 + * @return 流程监听器分页 + */ + PageResult getProcessListenerPage(BpmProcessListenerPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java new file mode 100644 index 0000000..916949b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java @@ -0,0 +1,102 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmProcessListenerMapper; +import com.zt.plat.module.bpm.enums.definition.BpmProcessListenerTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmProcessListenerValueTypeEnum; +import jakarta.annotation.Resource; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.delegate.TaskListener; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; + +/** + * BPM 流程监听器 Service 实现类 + * + * @author ZT + */ +@Service +@Validated +public class BpmProcessListenerServiceImpl implements BpmProcessListenerService { + + @Resource + private BpmProcessListenerMapper processListenerMapper; + + @Override + public Long createProcessListener(BpmProcessListenerSaveReqVO createReqVO) { + // 校验 + validateCreateProcessListenerValue(createReqVO); + // 插入 + BpmProcessListenerDO processListener = BeanUtils.toBean(createReqVO, BpmProcessListenerDO.class); + processListenerMapper.insert(processListener); + return processListener.getId(); + } + + @Override + public void updateProcessListener(BpmProcessListenerSaveReqVO updateReqVO) { + // 校验存在 + validateProcessListenerExists(updateReqVO.getId()); + validateCreateProcessListenerValue(updateReqVO); + // 更新 + BpmProcessListenerDO updateObj = BeanUtils.toBean(updateReqVO, BpmProcessListenerDO.class); + processListenerMapper.updateById(updateObj); + } + + private void validateCreateProcessListenerValue(BpmProcessListenerSaveReqVO createReqVO) { + // class 类型 + if (createReqVO.getValueType().equals(BpmProcessListenerValueTypeEnum.CLASS.getType())) { + try { + Class clazz = Class.forName(createReqVO.getValue()); + if (createReqVO.getType().equals(BpmProcessListenerTypeEnum.EXECUTION.getType()) + && !JavaDelegate.class.isAssignableFrom(clazz)) { + throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(), + JavaDelegate.class.getName()); + } else if (createReqVO.getType().equals(BpmProcessListenerTypeEnum.TASK.getType()) + && !TaskListener.class.isAssignableFrom(clazz)) { + throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(), + TaskListener.class.getName()); + } + } catch (ClassNotFoundException e) { + throw exception(PROCESS_LISTENER_CLASS_NOT_FOUND, createReqVO.getValue()); + } + return; + } + // 表达式 + if (!StrUtil.startWith(createReqVO.getValue(), "${") || !StrUtil.endWith(createReqVO.getValue(), "}")) { + throw exception(PROCESS_LISTENER_EXPRESSION_INVALID, createReqVO.getValue()); + } + } + + @Override + public void deleteProcessListener(Long id) { + // 校验存在 + validateProcessListenerExists(id); + // 删除 + processListenerMapper.deleteById(id); + } + + private void validateProcessListenerExists(Long id) { + if (processListenerMapper.selectById(id) == null) { + throw exception(PROCESS_LISTENER_NOT_EXISTS); + } + } + + @Override + public BpmProcessListenerDO getProcessListener(Long id) { + return processListenerMapper.selectById(id); + } + + @Override + public PageResult getProcessListenerPage(BpmProcessListenerPageReqVO pageReqVO) { + return processListenerMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java new file mode 100644 index 0000000..40bfea3 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java @@ -0,0 +1,82 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * 用户组 Service 接口 + * + * @author ZT + */ +public interface BpmUserGroupService { + + /** + * 创建用户组 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createUserGroup(@Valid BpmUserGroupSaveReqVO createReqVO); + + /** + * 更新用户组 + * + * @param updateReqVO 更新信息 + */ + void updateUserGroup(@Valid BpmUserGroupSaveReqVO updateReqVO); + + /** + * 删除用户组 + * + * @param id 编号 + */ + void deleteUserGroup(Long id); + + /** + * 获得用户组 + * + * @param id 编号 + * @return 用户组 + */ + BpmUserGroupDO getUserGroup(Long id); + + /** + * 获得用户组列表 + * + * @param ids 编号 + * @return 用户组列表 + */ + List getUserGroupList(Collection ids); + + /** + * 获得指定状态的用户组列表 + * + * @param status 状态 + * @return 用户组列表 + */ + List getUserGroupListByStatus(Integer status); + + /** + * 获得用户组分页 + * + * @param pageReqVO 分页查询 + * @return 用户组分页 + */ + PageResult getUserGroupPage(BpmUserGroupPageReqVO pageReqVO); + + /** + * 校验用户组们是否有效。如下情况,视为无效: + * 1. 用户组编号不存在 + * 2. 用户组被禁用 + * + * @param ids 用户组编号数组 + */ + void validUserGroups(Collection ids); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java new file mode 100644 index 0000000..f0e6ddb --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java @@ -0,0 +1,107 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmUserGroupMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.USER_GROUP_IS_DISABLE; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS; + +/** + * 用户组 Service 实现类 + * + * @author ZT + */ +@Service +@Validated +public class BpmUserGroupServiceImpl implements BpmUserGroupService { + + @Resource + private BpmUserGroupMapper userGroupMapper; + + @Override + public Long createUserGroup(BpmUserGroupSaveReqVO createReqVO) { + BpmUserGroupDO userGroup = BeanUtils.toBean(createReqVO, BpmUserGroupDO.class); + userGroupMapper.insert(userGroup); + return userGroup.getId(); + } + + @Override + public void updateUserGroup(BpmUserGroupSaveReqVO updateReqVO) { + // 校验存在 + validateUserGroupExists(updateReqVO.getId()); + // 更新 + BpmUserGroupDO updateObj = BeanUtils.toBean(updateReqVO, BpmUserGroupDO.class); + userGroupMapper.updateById(updateObj); + } + + @Override + public void deleteUserGroup(Long id) { + // 校验存在 + this.validateUserGroupExists(id); + // 删除 + userGroupMapper.deleteById(id); + } + + private void validateUserGroupExists(Long id) { + if (userGroupMapper.selectById(id) == null) { + throw exception(USER_GROUP_NOT_EXISTS); + } + } + + @Override + public BpmUserGroupDO getUserGroup(Long id) { + return userGroupMapper.selectById(id); + } + + @Override + public List getUserGroupList(Collection ids) { + return userGroupMapper.selectBatchIds(ids); + } + + + @Override + public List getUserGroupListByStatus(Integer status) { + return userGroupMapper.selectListByStatus(status); + } + + @Override + public PageResult getUserGroupPage(BpmUserGroupPageReqVO pageReqVO) { + return userGroupMapper.selectPage(pageReqVO); + } + + @Override + public void validUserGroups(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 获得用户组信息 + List userGroups = userGroupMapper.selectBatchIds(ids); + Map userGroupMap = convertMap(userGroups, BpmUserGroupDO::getId); + // 校验 + ids.forEach(id -> { + BpmUserGroupDO userGroup = userGroupMap.get(id); + if (userGroup == null) { + throw exception(USER_GROUP_NOT_EXISTS); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(userGroup.getStatus())) { + throw exception(USER_GROUP_IS_DISABLE, userGroup.getName()); + } + }); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java new file mode 100644 index 0000000..f99ab3a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java @@ -0,0 +1,25 @@ +package com.zt.plat.module.bpm.service.definition.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * Bpm 表单的 Field 表单项 Response DTO + * 字段的定义,可见 https://github.com/JakHuang/form-generator/issues/46 文档 + * + * @author ZT + */ +@Data +public class BpmFormFieldRespDTO { + + /** + * 表单标题 + */ + private String label; + /** + * 表单字段的属性名,可自定义 + */ + @JsonProperty(value = "vModel") + private String vModel; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java new file mode 100644 index 0000000..69767d9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.service.definition.dto; + +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import lombok.Data; + +/** + * BPM 流程 MetaInfo Response DTO + * 主要用于 { Model#setMetaInfo(String)} 的存储 + * + * 最终,它的字段和 {@link com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} 是一致的 + * + * @author ZT + */ +@Data +public class BpmModelMetaInfoRespDTO { + + /** + * 流程图标 + */ + private String icon; + /** + * 流程描述 + */ + private String description; + + /** + * 表单类型 + */ + private Integer formType; + /** + * 表单编号 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private Long formId; + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomCreatePath; + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomViewPath; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java new file mode 100644 index 0000000..2132a21 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java @@ -0,0 +1,81 @@ +package com.zt.plat.module.bpm.service.definition.dto; + +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * 流程定义创建 Request DTO + */ +@Data +public class BpmProcessDefinitionCreateReqDTO { + + // ========== 模型相关 ========== + + /** + * 流程模型的编号 + */ + @NotEmpty(message = "流程模型编号不能为空") + private String modelId; + /** + * 流程标识 + */ + @NotEmpty(message = "流程标识不能为空") + private String key; + /** + * 流程名称 + */ + @NotEmpty(message = "流程名称不能为空") + private String name; + /** + * 流程描述 + */ + private String description; + /** + * 流程分类 + */ + @NotEmpty(message = "流程分类不能为空") + private String category; + /** + * BPMN XML + */ + @NotEmpty(message = "BPMN XML 不能为空") + private byte[] bpmnBytes; + + // ========== 表单相关 ========== + + /** + * 表单类型 + */ + @NotNull(message = "表单类型不能为空") + private Integer formType; + /** + * 动态表单编号 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private Long formId; + /** + * 表单的配置 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private String formConf; + /** + * 表单项的数组 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private List formFields; + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomCreatePath; + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomViewPath; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java new file mode 100644 index 0000000..1698829 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.service.message; + +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO; +import jakarta.validation.Valid; + +/** + * BPM 消息 Service 接口 + * + * TODO 芋艿:未来支持消息的可配置;不同的流程,在什么场景下,需要发送什么消息,消息的内容是什么; + * + * @author ZT + */ +public interface BpmMessageService { + + /** + * 发送流程实例被通过的消息 + * + * @param reqDTO 发送信息 + */ + void sendMessageWhenProcessInstanceApprove(@Valid BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO); + + /** + * 发送流程实例被不通过的消息 + * + * @param reqDTO 发送信息 + */ + void sendMessageWhenProcessInstanceReject(@Valid BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO); + + /** + * 发送任务被分配的消息 + * + * @param reqDTO 发送信息 + */ + void sendMessageWhenTaskAssigned(@Valid BpmMessageSendWhenTaskCreatedReqDTO reqDTO); + + /** + * 发送任务审批超时的消息 + * + * @param reqDTO 发送信息 + */ + void sendMessageWhenTaskTimeout(@Valid BpmMessageSendWhenTaskTimeoutReqDTO reqDTO); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java new file mode 100644 index 0000000..9650cb7 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java @@ -0,0 +1,79 @@ +package com.zt.plat.module.bpm.service.message; + +import com.zt.plat.framework.web.config.WebProperties; +import com.zt.plat.module.bpm.convert.message.BpmMessageConvert; +import com.zt.plat.module.bpm.enums.message.BpmMessageEnum; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO; +import com.zt.plat.module.system.api.sms.SmsSendApi; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.HashMap; +import java.util.Map; + +/** + * BPM 消息 Service 实现类 + * + * @author ZT + */ +@Service +@Validated +@Slf4j +public class BpmMessageServiceImpl implements BpmMessageService { + + @Resource + private SmsSendApi smsSendApi; + + @Resource + private WebProperties webProperties; + + @Override + public void sendMessageWhenProcessInstanceApprove(BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO) { + Map templateParams = new HashMap<>(); + templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); + templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); + smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(), + BpmMessageEnum.PROCESS_INSTANCE_APPROVE.getSmsTemplateCode(), templateParams)).checkError(); + } + + @Override + public void sendMessageWhenProcessInstanceReject(BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO) { + Map templateParams = new HashMap<>(); + templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); + templateParams.put("reason", reqDTO.getReason()); + templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); + smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(), + BpmMessageEnum.PROCESS_INSTANCE_REJECT.getSmsTemplateCode(), templateParams)).checkError(); + } + + @Override + public void sendMessageWhenTaskAssigned(BpmMessageSendWhenTaskCreatedReqDTO reqDTO) { + Map templateParams = new HashMap<>(); + templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); + templateParams.put("taskName", reqDTO.getTaskName()); + templateParams.put("startUserNickname", reqDTO.getStartUserNickname()); + templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); + smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(), + BpmMessageEnum.TASK_ASSIGNED.getSmsTemplateCode(), templateParams)).checkError(); + } + + @Override + public void sendMessageWhenTaskTimeout(BpmMessageSendWhenTaskTimeoutReqDTO reqDTO) { + Map templateParams = new HashMap<>(); + templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); + templateParams.put("taskName", reqDTO.getTaskName()); + templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); + smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(), + BpmMessageEnum.TASK_TIMEOUT.getSmsTemplateCode(), templateParams)).checkError(); + } + + private String getProcessInstanceDetailUrl(String taskId) { + return webProperties.getAdminUi().getUrl() + "/bpm/process-instance/detail?id=" + taskId; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java new file mode 100644 index 0000000..d06cbf4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java @@ -0,0 +1,26 @@ +package com.zt.plat.module.bpm.service.message.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * BPM 发送流程实例被通过 Request DTO + */ +@Data +public class BpmMessageSendWhenProcessInstanceApproveReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + @NotNull(message = "发起人的用户编号") + private Long startUserId; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java new file mode 100644 index 0000000..cf4bd29 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.service.message.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * BPM 发送流程实例被不通过 Request DTO + */ +@Data +public class BpmMessageSendWhenProcessInstanceRejectReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + @NotNull(message = "发起人的用户编号") + private Long startUserId; + + /** + * 不通过理由 + */ + @NotEmpty(message = "不通过理由不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java new file mode 100644 index 0000000..03adf51 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.service.message.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * BPM 发送任务被分配 Request DTO + */ +@Data +public class BpmMessageSendWhenTaskCreatedReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + @NotNull(message = "发起人的用户编号") + private Long startUserId; + @NotEmpty(message = "发起人的昵称") + private String startUserNickname; + + /** + * 流程任务的编号 + */ + @NotEmpty(message = "流程任务的编号不能为空") + private String taskId; + /** + * 流程任务的名字 + */ + @NotEmpty(message = "流程任务的名字不能为空") + private String taskName; + + /** + * 审批人的用户编号 + */ + @NotNull(message = "审批人的用户编号不能为空") + private Long assigneeUserId; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java new file mode 100644 index 0000000..f9a9067 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java @@ -0,0 +1,41 @@ +package com.zt.plat.module.bpm.service.message.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * BPM 发送任务审批超时 Request DTO + */ +@Data +public class BpmMessageSendWhenTaskTimeoutReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + + /** + * 流程任务的编号 + */ + @NotEmpty(message = "流程任务的编号不能为空") + private String taskId; + /** + * 流程任务的名字 + */ + @NotEmpty(message = "流程任务的名字不能为空") + private String taskName; + + /** + * 审批人的用户编号 + */ + @NotNull(message = "审批人的用户编号不能为空") + private Long assigneeUserId; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java new file mode 100644 index 0000000..bf19ddb --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java @@ -0,0 +1,52 @@ +package com.zt.plat.module.bpm.service.oa; + + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; +import jakarta.validation.Valid; + +/** + * 请假申请 Service 接口 + * + * @author jason + * @author ZT + */ +public interface BpmOALeaveService { + + /** + * 创建请假申请 + * + * @param userId 用户编号 + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createLeave(Long userId, @Valid BpmOALeaveCreateReqVO createReqVO); + + /** + * 更新请假申请的状态 + * + * @param id 编号 + * @param status 结果 + */ + void updateLeaveStatus(Long id, Integer status); + + /** + * 获得请假申请 + * + * @param id 编号 + * @return 请假申请 + */ + BpmOALeaveDO getLeave(Long id); + + /** + * 获得请假申请分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页查询 + * @return 请假申请分页 + */ + PageResult getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java new file mode 100644 index 0000000..fec6e27 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java @@ -0,0 +1,89 @@ +package com.zt.plat.module.bpm.service.oa; + +import cn.hutool.core.date.LocalDateTimeUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.api.task.BpmProcessInstanceApi; +import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; +import com.zt.plat.module.bpm.dal.mysql.oa.BpmOALeaveMapper; +import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.HashMap; +import java.util.Map; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.OA_LEAVE_NOT_EXISTS; + +/** + * OA 请假申请 Service 实现类 + * + * @author jason + * @author ZT + */ +@Service +@Validated +public class BpmOALeaveServiceImpl implements BpmOALeaveService { + + /** + * OA 请假对应的流程定义 KEY + */ + public static final String PROCESS_KEY = "oa_leave"; + + @Resource + private BpmOALeaveMapper leaveMapper; + + @Resource + private BpmProcessInstanceApi processInstanceApi; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createLeave(Long userId, BpmOALeaveCreateReqVO createReqVO) { + // 插入 OA 请假单 + long day = LocalDateTimeUtil.between(createReqVO.getStartTime(), createReqVO.getEndTime()).toDays(); + BpmOALeaveDO leave = BeanUtils.toBean(createReqVO, BpmOALeaveDO.class) + .setUserId(userId).setDay(day).setStatus(BpmTaskStatusEnum.RUNNING.getStatus()); + leaveMapper.insert(leave); + + // 发起 BPM 流程 + Map processInstanceVariables = new HashMap<>(); + processInstanceVariables.put("day", day); + String processInstanceId = processInstanceApi.createProcessInstance(userId, + new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY) + .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId())) + .setStartUserSelectAssignees(createReqVO.getStartUserSelectAssignees())).getCheckedData(); + + // 将工作流的编号,更新到 OA 请假单中 + leaveMapper.updateById(new BpmOALeaveDO().setId(leave.getId()).setProcessInstanceId(processInstanceId)); + return leave.getId(); + } + + @Override + public void updateLeaveStatus(Long id, Integer status) { + validateLeaveExists(id); + leaveMapper.updateById(new BpmOALeaveDO().setId(id).setStatus(status)); + } + + private void validateLeaveExists(Long id) { + if (leaveMapper.selectById(id) == null) { + throw exception(OA_LEAVE_NOT_EXISTS); + } + } + + @Override + public BpmOALeaveDO getLeave(Long id) { + return leaveMapper.selectById(id); + } + + @Override + public PageResult getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO) { + return leaveMapper.selectPage(userId, pageReqVO); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java new file mode 100644 index 0000000..122a6ff --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.service.oa.listener; + +import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; +import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEventListener; +import com.zt.plat.module.bpm.service.oa.BpmOALeaveService; +import com.zt.plat.module.bpm.service.oa.BpmOALeaveServiceImpl; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * OA 请假单的结果的监听器实现类 + * + * @author ZT + */ +@Component +public class BpmOALeaveStatusListener extends BpmProcessInstanceStatusEventListener { + + @Resource + private BpmOALeaveService leaveService; + + @Override + protected List getProcessDefinitionKey() { + return List.of(BpmOALeaveServiceImpl.PROCESS_KEY); + } + + @Override + protected void onEvent(BpmProcessInstanceStatusEvent event) { + leaveService.updateLeaveStatus(Long.parseLong(event.getBusinessKey()), event.getStatus()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java new file mode 100644 index 0000000..e5f06b6 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java @@ -0,0 +1,60 @@ +package com.zt.plat.module.bpm.service.task; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; +import jakarta.validation.constraints.NotEmpty; +import org.flowable.bpmn.model.FlowNode; + +import java.util.Collection; + +/** + * 流程抄送 Service 接口 + * + * 现在是在审批的时候进行流程抄送 + */ +public interface BpmProcessInstanceCopyService { + + /** + * 【管理员】流程实例的抄送 + * + * @param userIds 抄送的用户编号 + * @param reason 抄送意见 + * @param taskId 流程任务编号 + */ + void createProcessInstanceCopy(Collection userIds, String reason, String taskId); + + /** + * 【自动抄送】流程实例的抄送 + * + * @param userIds 抄送的用户编号 + * @param reason 抄送意见 + * @param processInstanceId 流程编号 + * @param activityId 流程活动编号(对应 {@link FlowNode#getId()}) + * @param activityName 任务编号(对应 {@link FlowNode#getName()}) + * @param taskId 任务编号,允许空 + */ + void createProcessInstanceCopy(Collection userIds, String reason, + @NotEmpty(message = "流程实例编号不能为空") String processInstanceId, + @NotEmpty(message = "流程活动编号不能为空") String activityId, + @NotEmpty(message = "流程活动名字不能为空") String activityName, + String taskId); + + /** + * 获得抄送的流程的分页 + * + * @param userId 当前登录用户 + * @param pageReqVO 分页请求 + * @return 抄送的分页结果 + */ + PageResult getProcessInstanceCopyPage(Long userId, + BpmProcessInstanceCopyPageReqVO pageReqVO); + + /** + * 删除抄送流程 + * + * @param processInstanceId 流程实例 ID + */ + void deleteProcessInstanceCopy(String processInstanceId); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java new file mode 100644 index 0000000..dd842b9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java @@ -0,0 +1,96 @@ +package com.zt.plat.module.bpm.service.task; + +import cn.hutool.core.util.ObjectUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; +import com.zt.plat.module.bpm.dal.mysql.task.BpmProcessInstanceCopyMapper; +import com.zt.plat.module.bpm.enums.ErrorCodeConstants; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 流程抄送 Service 实现类 + * + * @author kyle + */ +@Service +@Validated +@Slf4j +public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopyService { + + @Resource + private BpmProcessInstanceCopyMapper processInstanceCopyMapper; + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmTaskService taskService; + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessDefinitionService processDefinitionService; + + @Override + public void createProcessInstanceCopy(Collection userIds, String reason, String taskId) { + Task task = taskService.getTask(taskId); + if (ObjectUtil.isNull(task)) { + throw exception(ErrorCodeConstants.TASK_NOT_EXISTS); + } + // 执行抄送 + createProcessInstanceCopy(userIds, reason, + task.getProcessInstanceId(), task.getTaskDefinitionKey(), task.getId(), task.getName()); + } + + @Override + public void createProcessInstanceCopy(Collection userIds, String reason, String processInstanceId, + String activityId, String activityName, String taskId) { + // 1.1 校验流程实例存在 + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + if (processInstance == null) { + throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); + } + // 1.2 校验流程定义存在 + ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( + processInstance.getProcessDefinitionId()); + if (processDefinition == null) { + throw exception(ErrorCodeConstants.PROCESS_DEFINITION_NOT_EXISTS); + } + + // 2. 创建抄送流程 + List copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO() + .setUserId(userId).setReason(reason).setStartUserId(Long.valueOf(processInstance.getStartUserId())) + .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName()) + .setCategory(processDefinition.getCategory()).setTaskId(taskId) + .setActivityId(activityId).setActivityName(activityName) + .setProcessDefinitionId(processInstance.getProcessDefinitionId())); + processInstanceCopyMapper.insertBatch(copyList); + } + + @Override + public PageResult getProcessInstanceCopyPage(Long userId, + BpmProcessInstanceCopyPageReqVO pageReqVO) { + return processInstanceCopyMapper.selectPage(userId, pageReqVO); + } + + @Override + public void deleteProcessInstanceCopy(String processInstanceId) { + processInstanceCopyMapper.deleteByProcessInstanceId(processInstanceId); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java new file mode 100644 index 0000000..98af5ee --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java @@ -0,0 +1,191 @@ +package com.zt.plat.module.bpm.service.task; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; +import jakarta.validation.Valid; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * 流程实例 Service 接口 + * + * @author ZT + */ +public interface BpmProcessInstanceService { + + // ========== Query 查询相关方法 ========== + + /** + * 获得流程实例 + * + * @param id 流程实例的编号 + * @return 流程实例 + */ + ProcessInstance getProcessInstance(String id); + + /** + * 获得流程实例列表 + * + * @param ids 流程实例的编号集合 + * @return 流程实例列表 + */ + List getProcessInstances(Set ids); + + /** + * 获得流程实例 Map + * + * @param ids 流程实例的编号集合 + * @return 流程实例列表 Map + */ + default Map getProcessInstanceMap(Set ids) { + return convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId); + } + + /** + * 获得历史的流程实例 + * + * @param id 流程实例的编号 + * @return 历史的流程实例 + */ + HistoricProcessInstance getHistoricProcessInstance(String id); + + /** + * 获得历史的流程实例列表 + * + * @param ids 流程实例的编号集合 + * @return 历史的流程实例列表 + */ + List getHistoricProcessInstances(Set ids); + + /** + * 获得历史的流程实例 Map + * + * @param ids 流程实例的编号集合 + * @return 历史的流程实例列表 Map + */ + default Map getHistoricProcessInstanceMap(Set ids) { + return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId); + } + + /** + * 获得流程实例的分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页请求 + * @return 流程实例的分页 + */ + PageResult getProcessInstancePage(Long userId, + @Valid BpmProcessInstancePageReqVO pageReqVO); + + /** + * 获取审批详情。 + *

+ * 可以是准备发起的流程、进行中的流程、已经结束的流程 + * + * @param loginUserId 登录人的用户编号 + * @param reqVO 请求信息 + * @return 流程实例的进度 + */ + BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + + /** + * 获取下一个执行节点信息 + * + * @param loginUserId 登录人的用户编号 + * @param reqVO 请求信息 + * @return 下一个执行节点信息 + */ + List getNextApprovalNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + + /** + * 获取流程实例的 BPMN 模型视图 + * + * @param id 流程实例的编号 + * @return BPMN 模型视图 + */ + BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id); + + // ========== Update 写入相关方法 ========== + + /** + * 创建流程实例(提供给前端) + * + * @param userId 用户编号 + * @param createReqVO 创建信息 + * @return 实例的编号 + */ + String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO); + + /** + * 创建流程实例(提供给内部) + * + * @param userId 用户编号 + * @param createReqDTO 创建信息 + * @return 实例的编号 + */ + String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO); + + /** + * 发起人取消流程实例 + * + * @param userId 用户编号 + * @param cancelReqVO 取消信息 + */ + void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO); + + /** + * 管理员取消流程实例 + * + * @param userId 用户编号 + * @param cancelReqVO 取消信息 + */ + void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO); + + /** + * 更新 ProcessInstance 为不通过 + * + * @param processInstance 流程实例 + * @param reason 理由。例如说,审批不通过时,需要传递该值 + */ + void updateProcessInstanceReject(ProcessInstance processInstance, String reason); + + /** + * 更新 ProcessInstance 的变量 + * + * @param id 流程编号 + * @param variables 流程变量 + */ + void updateProcessInstanceVariables(String id, Map variables); + + /** + * 删除 ProcessInstance 的变量 + * + * @param id 流程编号 + * @param variableNames 流程变量名 + */ + void removeProcessInstanceVariables(String id, Collection variableNames); + + // ========== Event 事件相关方法 ========== + + /** + * 处理 ProcessInstance 完成事件,例如说:审批通过、不通过、取消 + * + * @param instance 流程任务 + */ + void processProcessInstanceCompleted(ProcessInstance instance); + + /** + * 处理 ProcessInstance 开始事件,例如说:流程前置通知 + * + * @param instance 流程任务 + */ + void processProcessInstanceCreated(ProcessInstance instance); +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java new file mode 100644 index 0000000..fc01ac7 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -0,0 +1,964 @@ +package com.zt.plat.module.bpm.service.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.date.DateUtils; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.object.ObjectUtils; +import com.zt.plat.framework.common.util.object.PageUtils; +import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNodeTask; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.dal.redis.BpmProcessIdRedisDAO; +import com.zt.plat.module.bpm.enums.ErrorCodeConstants; +import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmSimpleModelNodeTypeEnum; +import com.zt.plat.module.bpm.enums.task.BpmProcessInstanceStatusEnum; +import com.zt.plat.module.bpm.enums.task.BpmReasonEnum; +import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; +import com.zt.plat.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.message.BpmMessageService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.constants.BpmnXMLConstants; +import org.flowable.bpmn.model.*; +import org.flowable.engine.HistoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.runtime.ProcessInstanceBuilder; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; +import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseNodeType; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.flowable.bpmn.constants.BpmnXMLConstants.*; + +/** + * 流程实例 Service 实现类 + *

+ * ProcessDefinition & ProcessInstance & Execution & Task 的关系: + * 1. + *

+ * HistoricProcessInstance & ProcessInstance 的关系: + * 1. + *

+ * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 + * + * @author ZT + */ +@Service +@Validated +@Slf4j +public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { + + @Resource + private RuntimeService runtimeService; + @Resource + private HistoryService historyService; + + @Resource + private BpmProcessDefinitionService processDefinitionService; + @Resource + @Lazy // 避免循环依赖 + private BpmTaskService taskService; + @Resource + private BpmMessageService messageService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @Resource + private BpmProcessInstanceEventPublisher processInstanceEventPublisher; + + @Resource + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Resource + private BpmProcessIdRedisDAO processIdRedisDAO; + + // ========== Query 查询相关方法 ========== + + @Override + public ProcessInstance getProcessInstance(String id) { + return runtimeService.createProcessInstanceQuery() + .includeProcessVariables() + .processInstanceId(id) + .singleResult(); + } + + @Override + public List getProcessInstances(Set ids) { + return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables().list(); + } + + @Override + public HistoricProcessInstance getHistoricProcessInstance(String id) { + return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables() + .singleResult(); + } + + @Override + public List getHistoricProcessInstances(Set ids) { + return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables() + .list(); + } + + private Map getFormFieldsPermission(BpmnModel bpmnModel, + String activityId, String taskId) { + // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id + if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) { + activityId = Optional.ofNullable(taskService.getHistoricTask(taskId)) + .map(HistoricTaskInstance::getTaskDefinitionKey).orElse(null); + } + if (StrUtil.isEmpty(activityId)) { + return null; + } + + // 2. 从 BpmnModel 中解析表单字段权限 + return BpmnModelUtils.parseFormFieldsPermission(bpmnModel, activityId); + } + + @Override + public BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, BpmApprovalDetailReqVO reqVO) { + // 1.1 从 reqVO 中,读取公共变量 + Long startUserId = loginUserId; // 流程发起人 + HistoricProcessInstance historicProcessInstance = null; // 流程实例 + Integer processInstanceStatus = BpmProcessInstanceStatusEnum.NOT_START.getStatus(); // 流程状态 + Map processVariables = new HashMap<>(); // 流程变量 + // 1.2 如果是流程已发起的场景,则使用流程实例的数据 + if (reqVO.getProcessInstanceId() != null) { + historicProcessInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId()); + if (historicProcessInstance == null) { + throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); + } + startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); + processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); + // 合并 DB 和前端传递的流量变量,以前端的为主 + if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) { + processVariables.putAll(historicProcessInstance.getProcessVariables()); + } + } + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { + processVariables.putAll(reqVO.getProcessVariables()); + } + // 1.3 读取其它相关数据 + ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( + historicProcessInstance != null ? historicProcessInstance.getProcessDefinitionId() + : reqVO.getProcessDefinitionId()); + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService + .getProcessDefinitionInfo(processDefinition.getId()); + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId()); + + // 2.1 已结束 + 进行中的活动节点 + List endActivityNodes = null; // 已结束的审批信息 + List runActivityNodes = null; // 进行中的审批信息 + List activities = null; // 流程实例列表 + if (reqVO.getProcessInstanceId() != null) { + activities = taskService.getActivityListByProcessInstanceId(reqVO.getProcessInstanceId()); + List tasks = taskService.getTaskListByProcessInstanceId(reqVO.getProcessInstanceId(), + true); + endActivityNodes = getEndActivityNodeList(startUserId, bpmnModel, processDefinitionInfo, + historicProcessInstance, processInstanceStatus, activities, tasks); + runActivityNodes = getRunApproveNodeList(startUserId, bpmnModel, processDefinition, processVariables, + activities, tasks); + } + + // 2.2 流程已经结束,直接 return,无需预测 + if (BpmProcessInstanceStatusEnum.isProcessEndStatus(processInstanceStatus)) { + return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, + historicProcessInstance, + processInstanceStatus, endActivityNodes, runActivityNodes, null, null); + } + + // 3.1 计算当前登录用户的待办任务 + BpmTaskRespVO todoTask = taskService.getTodoTask(loginUserId, reqVO.getTaskId(), reqVO.getProcessInstanceId()); + // 3.2 预测未运行节点的审批信息 + List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, + processDefinitionInfo, + processVariables, activities); + // 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点 + if (ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) { + simulateActivityNodes.removeIf(node -> + BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); + } + + // 4. 拼接最终数据 + return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance, + processInstanceStatus, endActivityNodes, runActivityNodes, simulateActivityNodes, todoTask); + } + + @Override + public List getNextApprovalNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { + // 1.1 校验任务存在,且是当前用户的 + Task task = taskService.validateTask(loginUserId, reqVO.getTaskId()); + // 1.2 校验流程实例存在 + ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); + if (historicProcessInstance == null) { + throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); + } + // 1.3 校验BpmnModel + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); + if (bpmnModel == null) { + return null; + } + + // 2. 设置流程变量 + Map processVariables = new HashMap<>(); + // 2.1 获取历史中流程变量 + if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) { + processVariables.putAll(historicProcessInstance.getProcessVariables()); + } + // 2.2 合并前端传递的流程变量,以前端为准 + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { + processVariables.putAll(reqVO.getProcessVariables()); + } + + // 3. 获取下一个将要执行的节点集合 + FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); + List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); + List nextActivityNodes = convertList(nextFlowNodes, node -> new ActivityNode().setId(node.getId()) + .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) + .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) + .setCandidateUserIds(getTaskCandidateUserList(bpmnModel, node.getId(), + loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables))); + if (CollUtil.isNotEmpty(nextActivityNodes)) { + return nextActivityNodes; + } + + // 4. 拼接基础信息 + Map userMap = adminUserApi.getUserMap( + convertSetByFlatMap(nextActivityNodes, ActivityNode::getCandidateUserIds, Collection::stream)); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + nextActivityNodes.forEach(node -> node.setCandidateUsers(convertList(node.getCandidateUserIds(), userId -> { + AdminUserRespDTO user = userMap.get(userId); + if (user != null) { + return BpmProcessInstanceConvert.INSTANCE.buildUser(userId, userMap, deptMap); + } + return null; + }))); + return nextActivityNodes; + } + + @Override + @SuppressWarnings("unchecked") + public PageResult getProcessInstancePage(Long userId, + BpmProcessInstancePageReqVO pageReqVO) { + // 1. 构建查询条件 + HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .includeProcessVariables() + .processInstanceTenantId(FlowableUtils.getTenantId()) + .orderByProcessInstanceStartTime().desc(); + if (userId != null) { // 【我的流程】菜单时,需要传递该字段 + processInstanceQuery.startedBy(String.valueOf(userId)); + } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 + processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); + } + if (StrUtil.isNotEmpty(pageReqVO.getName())) { + processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); + } + if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionKey())) { + processInstanceQuery.processDefinitionKey(pageReqVO.getProcessDefinitionKey()); + } + if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { + processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); + } + if (pageReqVO.getStatus() != null) { + processInstanceQuery.variableValueEquals(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, + pageReqVO.getStatus()); + } + if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { + processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); + processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); + } + if (ArrayUtil.isNotEmpty(pageReqVO.getEndTime())) { + processInstanceQuery.finishedAfter(DateUtils.of(pageReqVO.getEndTime()[0])); + processInstanceQuery.finishedBefore(DateUtils.of(pageReqVO.getEndTime()[1])); + } + // 表单字段查询 + Map formFieldsParams = JsonUtils.parseObject(pageReqVO.getFormFieldsParams(), Map.class); + if (CollUtil.isNotEmpty(formFieldsParams)) { + formFieldsParams.forEach((key, value) -> { + if (StrUtil.isEmpty(String.valueOf(value))) { + return; + } + // TODO @lesan:应支持多种类型的查询方式,目前只有字符串全等 + processInstanceQuery.variableValueEquals(key, value); + }); + } + + // 2.1 查询数量 + long processInstanceCount = processInstanceQuery.count(); + if (processInstanceCount == 0) { + return PageResult.empty(processInstanceCount); + } + // 2.2 查询列表 + List processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), + pageReqVO.getPageSize()); + return new PageResult<>(processInstanceList, processInstanceCount); + } + + /** + * 拼接审批详情的最终数据 + *

+ * 主要是,拼接审批人的用户信息、部门信息 + */ + private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO, + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List endApprovalNodeInfos, + List runningApprovalNodeInfos, + List simulateApprovalNodeInfos, + BpmTaskRespVO todoTask) { + // 1. 获取所有需要读取用户信息的 userIds + List approveNodes = newArrayList( + asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos)); + Set userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds(processInstance, approveNodes, todoTask); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + + // 2. 表单权限 + String taskId = reqVO.getTaskId() == null && todoTask != null ? todoTask.getId() : reqVO.getTaskId(); + Map formFieldsPermission = getFormFieldsPermission(bpmnModel, reqVO.getActivityId(), taskId); + + // 3. 拼接数据 + return BpmProcessInstanceConvert.INSTANCE.buildApprovalDetail(bpmnModel, processDefinition, + processDefinitionInfo, processInstance, + processInstanceStatus, approveNodes, todoTask, formFieldsPermission, userMap, deptMap); + } + + /** + * 获得【已结束】的活动节点们 + */ + private List getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, + List activities, List tasks) { + // 遍历 tasks 列表,只处理已结束的 UserTask + // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities 的话,它无法成为一个节点 + List endTasks = filterList(tasks, task -> task.getEndTime() != null); + List approvalNodes = convertList(endTasks, task -> { + FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) + .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) + ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() + : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) + .setStatus(FlowableUtils.getTaskStatus(task)) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) + .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) + .setTasks(singletonList(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task))); + // 如果是取消状态,则跳过 + if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) { + return null; + } + return activityNode; + }); + + // 遍历 activities,只处理已结束的 StartEvent、EndEvent + List endActivities = filterList(activities, activity -> activity.getEndTime() != null + && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_EVENT_START, ELEMENT_CALL_ACTIVITY, ELEMENT_EVENT_END))); + endActivities.forEach(activity -> { + // StartEvent:只处理 BPMN 的场景。因为,SIMPLE 情况下,已经有 START_USER_NODE 节点 + if (ELEMENT_EVENT_START.equals(activity.getActivityType()) + && BpmModelTypeEnum.BPMN.getType().equals(processDefinitionInfo.getModelType())) { + ActivityNodeTask startTask = new ActivityNodeTask().setId(BpmnModelConstants.START_USER_NODE_ID) + .setAssignee(startUserId).setStatus(BpmTaskStatusEnum.APPROVE.getStatus()); + ActivityNode startNode = new ActivityNode().setId(startTask.getId()) + .setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName()) + .setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()) + .setStatus(startTask.getStatus()).setTasks(ListUtil.of(startTask)) + .setStartTime(DateUtils.of(activity.getStartTime())) + .setEndTime(DateUtils.of(activity.getEndTime())); + approvalNodes.add(0, startNode); + return; + } + // EndEvent + if (ELEMENT_EVENT_END.equals(activity.getActivityType())) { + if (BpmProcessInstanceStatusEnum.isRejectStatus(processInstanceStatus)) { + // 拒绝情况下,不需要展示 EndEvent 结束节点。原因是:前端已经展示 x 效果,无需重复展示 + return; + } + ActivityNode endNode = new ActivityNode().setId(activity.getId()) + .setName(BpmSimpleModelNodeTypeEnum.END_NODE.getName()) + .setNodeType(BpmSimpleModelNodeTypeEnum.END_NODE.getType()).setStatus(processInstanceStatus) + .setStartTime(DateUtils.of(activity.getStartTime())) + .setEndTime(DateUtils.of(activity.getEndTime())); + String reason = FlowableUtils.getProcessInstanceReason(historicProcessInstance); + if (StrUtil.isNotEmpty(reason)) { + endNode.setTasks(singletonList(new ActivityNodeTask().setId(endNode.getId()) + .setStatus(endNode.getStatus()).setReason(reason))); + } + approvalNodes.add(endNode); + } + // CallActivity + if (ELEMENT_CALL_ACTIVITY.equals(activity.getActivityType())) { + ActivityNode callActivity = new ActivityNode().setId(activity.getId()) + .setName(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getName()) + .setNodeType(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType()).setStatus(processInstanceStatus) + .setStartTime(DateUtils.of(activity.getStartTime())) + .setEndTime(DateUtils.of(activity.getEndTime())) + .setProcessInstanceId(activity.getProcessInstanceId()); + approvalNodes.add(callActivity); + } + }); + + // 按照时间排序 + approvalNodes.sort(Comparator.comparing(ActivityNode::getStartTime)); + return approvalNodes; + } + + /** + * 获得【进行中】的活动节点们 + */ + private List getRunApproveNodeList(Long startUserId, + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + Map processVariables, + List activities, + List tasks) { + // 构建运行中的任务、子流程,基于 activityId 分组 + List runActivities = filterList(activities, activity -> activity.getEndTime() == null + && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); + Map> runningTaskMap = convertMultiMap(runActivities, + HistoricActivityInstance::getActivityId); + + // 按照 activityId 分组,构建 ApprovalNodeInfo 节点 + Map taskMap = convertMap(tasks, HistoricTaskInstance::getId); + return convertList(runningTaskMap.entrySet(), entry -> { + String activityId = entry.getKey(); + List taskActivities = entry.getValue(); + // 构建活动节点 + FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); + HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 + ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) + .setName(firstActivity.getActivityName()) + .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”和"子流程"的识别 + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) + .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) + .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime())) + .setTasks(new ArrayList<>()); + // 处理每个任务的 tasks 属性 + for (HistoricActivityInstance activity : taskActivities) { + HistoricTaskInstance task = taskMap.get(activity.getTaskId()); + // 特殊情况:子流程节点 ChildProcess 仅存在于 activity 中,并且没有自身的 task,需要跳过执行 + // TODO @芋艿:后续看看怎么优化! + if (task == null) { + continue; + } + activityNode.getTasks().add(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task)); + // 加签子任务,需要过滤掉已经完成的加签子任务 + List childrenTasks = filterList( + taskService.getAllChildrenTaskListByParentTaskId(activity.getTaskId(), tasks), + childTask -> childTask.getEndTime() == null); + if (CollUtil.isNotEmpty(childrenTasks)) { + activityNode.getTasks().addAll( + convertList(childrenTasks, BpmProcessInstanceConvert.INSTANCE::buildApprovalTaskInfo)); + } + } + // 处理每个任务的 candidateUsers 属性:如果是依次审批,需要预测它的后续审批人。因为 Task 是审批完一个,创建一个新的 Task + if (BpmnModelUtils.isSequentialUserTask(flowNode)) { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, flowNode.getId(), + startUserId, processDefinition.getId(), processVariables); + // 截取当前审批人位置后面的候选人,不包含当前审批人 + ActivityNodeTask approvalTaskInfo = CollUtil.getFirst(activityNode.getTasks()); + Assert.notNull(approvalTaskInfo, "任务不能为空"); + int index = CollUtil.indexOf(candidateUserIds, + userId -> ObjectUtils.equalsAny(userId, approvalTaskInfo.getOwner(), + approvalTaskInfo.getAssignee())); // 委派或者向前加签情况,需要先比较 owner + activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size())); + } + if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(activityNode.getNodeType())) { + activityNode.setProcessInstanceId(firstActivity.getProcessInstanceId()); + } + return activityNode; + }); + } + + /** + * 获得【预测(未来)】的活动节点们 + */ + private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, + BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables, + List activities) { + // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance + // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 + Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); + // 情况一:BPMN 设计器 + if (Objects.equals(BpmModelTypeEnum.BPMN.getType(), processDefinitionInfo.getModelType())) { + List flowElements = BpmnModelUtils.simulateProcess(bpmnModel, processVariables); + return convertList(flowElements, flowElement -> buildNotRunApproveNodeForBpmn(startUserId, bpmnModel, + processDefinitionInfo, processVariables, flowElement, runActivityIds)); + } + // 情况二:SIMPLE 设计器 + if (Objects.equals(BpmModelTypeEnum.SIMPLE.getType(), processDefinitionInfo.getModelType())) { + BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), + BpmSimpleModelNodeVO.class); + List simpleNodes = SimpleModelUtils.simulateProcess(simpleModel, processVariables); + return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(startUserId, bpmnModel, + processDefinitionInfo, processVariables, simpleNode, runActivityIds)); + } + throw new IllegalArgumentException("未知设计器类型:" + processDefinitionInfo.getModelType()); + } + + private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + BpmSimpleModelNodeVO node, Set runActivityIds) { + // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance + // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 + if (runActivityIds.contains(node.getId())) { + return null; + } + + ActivityNode activityNode = new ActivityNode().setId(node.getId()).setName(node.getName()) + .setNodeType(node.getType()).setCandidateStrategy(node.getCandidateStrategy()) + .setStatus(BpmTaskStatusEnum.NOT_START.getStatus()); + + // 1. 开始节点/审批节点 + if (ObjectUtils.equalsAny(node.getType(), + BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType(), + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType(), + BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE.getType())) { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), + startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); + activityNode.setCandidateUserIds(candidateUserIds); + return activityNode; + } + + // 2. 结束节点 + if (BpmSimpleModelNodeTypeEnum.END_NODE.getType().equals(node.getType())) { + return activityNode; + } + + // 3. 抄送节点 + if (CollUtil.isEmpty(runActivityIds) && // 流程发起时:需要展示抄送节点,用于选择抄送人 + BpmSimpleModelNodeTypeEnum.COPY_NODE.getType().equals(node.getType())) { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), + startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); + activityNode.setCandidateUserIds(candidateUserIds); + return activityNode; + } + + // 4. 子流程节点 + if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType())) { + return activityNode; + } + return null; + } + + private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + FlowElement node, Set runActivityIds) { + if (runActivityIds.contains(node.getId())) { + return null; + } + ActivityNode activityNode = new ActivityNode().setId(node.getId()) + .setStatus(BpmTaskStatusEnum.NOT_START.getStatus()); + + // 1. 开始节点 + if (node instanceof StartEvent) { + return activityNode.setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName()) + .setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()); + } + + // 2. 审批节点 + if (node instanceof UserTask) { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), + startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); + return activityNode.setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) + .setCandidateUserIds(candidateUserIds); + } + + // 3. 结束节点 + if (node instanceof EndEvent) { + return activityNode.setName(BpmSimpleModelNodeTypeEnum.END_NODE.getName()) + .setNodeType(BpmSimpleModelNodeTypeEnum.END_NODE.getType()); + } + return null; + } + + private List getTaskCandidateUserList(BpmnModel bpmnModel, String activityId, + Long startUserId, String processDefinitionId, Map processVariables) { + Set userIds = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, + startUserId, processDefinitionId, processVariables); + return new ArrayList<>(userIds); + } + + @Override + public BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id) { + // 1.1 获得流程实例 + HistoricProcessInstance processInstance = getHistoricProcessInstance(id); + if (processInstance == null) { + return null; + } + // 1.2 获得流程定义 + BpmnModel bpmnModel = processDefinitionService + .getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()); + if (bpmnModel == null) { + return null; + } + BpmSimpleModelNodeVO simpleModel = null; + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo( + processInstance.getProcessDefinitionId()); + if (processDefinitionInfo != null + && BpmModelTypeEnum.SIMPLE.getType().equals(processDefinitionInfo.getModelType())) { + simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), BpmSimpleModelNodeVO.class); + } + // 1.3 获得流程实例对应的活动实例列表 + 任务列表 + List activities = taskService.getActivityListByProcessInstanceId(id); + List tasks = taskService.getTaskListByProcessInstanceId(id, true); + + // 2.1 拼接进度信息 + Set unfinishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, + activityInstance -> activityInstance.getEndTime() == null); + Set finishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, + activityInstance -> activityInstance.getEndTime() != null + && ObjectUtil.notEqual(activityInstance.getActivityType(), + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); + Set finishedSequenceFlowActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, + activityInstance -> activityInstance.getEndTime() != null + && ObjectUtil.equals(activityInstance.getActivityType(), + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); + // 特殊:会签情况下,会有部分已完成(审批)、部分未完成(待审批),此时需要 finishedTaskActivityIds 移除掉 + finishedTaskActivityIds.removeAll(unfinishedTaskActivityIds); + // 特殊:如果流程实例被拒绝,则需要计算是哪个活动节点。 + // 注意,只取最后一个。因为会存在多次拒绝的情况,拒绝驳回到指定节点 + Set rejectTaskActivityIds = CollUtil.newHashSet(); + if (BpmProcessInstanceStatusEnum.isRejectStatus(FlowableUtils.getProcessInstanceStatus(processInstance))) { + tasks.stream() + .filter(task -> BpmTaskStatusEnum.isRejectStatus(FlowableUtils.getTaskStatus(task))) + .max(Comparator.comparing(HistoricTaskInstance::getEndTime)) + .ifPresent(reject -> rejectTaskActivityIds.add(reject.getTaskDefinitionKey())); + finishedTaskActivityIds.removeAll(rejectTaskActivityIds); + } + + // 2.2 拼接基础信息 + Set userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds02(processInstance, tasks); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + return BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceBpmnModelView(processInstance, tasks, bpmnModel, + simpleModel, + unfinishedTaskActivityIds, finishedTaskActivityIds, finishedSequenceFlowActivityIds, + rejectTaskActivityIds, + userMap, deptMap); + } + + // ========== Update 写入相关方法 ========== + + @Override + @Transactional(rollbackFor = Exception.class) + public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { + // 获得流程定义 + ProcessDefinition definition = processDefinitionService + .getProcessDefinition(createReqVO.getProcessDefinitionId()); + // 发起流程 + return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, + createReqVO.getStartUserSelectAssignees()); + } + + @Override + public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { + return FlowableUtils.executeAuthenticatedUserId(userId, () -> { + // 获得流程定义 + ProcessDefinition definition = processDefinitionService + .getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); + // 发起流程 + return createProcessInstance0(userId, definition, createReqDTO.getVariables(), + createReqDTO.getBusinessKey(), + createReqDTO.getStartUserSelectAssignees()); + }); + } + + private String createProcessInstance0(Long userId, ProcessDefinition definition, + Map variables, String businessKey, + Map> startUserSelectAssignees) { + // 1.1 校验流程定义 + if (definition == null) { + throw exception(PROCESS_DEFINITION_NOT_EXISTS); + } + if (definition.isSuspended()) { + throw exception(PROCESS_DEFINITION_IS_SUSPENDED); + } + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService + .getProcessDefinitionInfo(definition.getId()); + if (processDefinitionInfo == null) { + throw exception(PROCESS_DEFINITION_NOT_EXISTS); + } + // 1.2 校验是否能够发起 + if (!processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId)) { + throw exception(PROCESS_INSTANCE_START_USER_CAN_START); + } + // 1.3 校验发起人自选审批人 + validateStartUserSelectAssignees(userId, definition, startUserSelectAssignees, variables); + + // 2. 创建流程实例 + if (variables == null) { + variables = new HashMap<>(); + } + FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 + BpmProcessInstanceStatusEnum.RUNNING.getStatus()); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 true,不影响没配置 skipExpression 的节点 + if (CollUtil.isNotEmpty(startUserSelectAssignees)) { + // 设置流程变量,发起人自选审批人 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, + startUserSelectAssignees); + } + + // 3. 创建流程 + ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder() + .processDefinitionId(definition.getId()) + .businessKey(businessKey) + .variables(variables); + // 3.1 创建流程 ID + BpmModelMetaInfoVO.ProcessIdRule processIdRule = processDefinitionInfo.getProcessIdRule(); + if (processIdRule != null && Boolean.TRUE.equals(processIdRule.getEnable())) { + processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule)); + } + // 3.2 流程名称 + BpmModelMetaInfoVO.TitleSetting titleSetting = processDefinitionInfo.getTitleSetting(); + if (titleSetting != null && Boolean.TRUE.equals(titleSetting.getEnable())) { + AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); + Map cloneVariables = new HashMap<>(variables); + cloneVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, user.getNickname()); + cloneVariables.put(BpmnVariableConstants.PROCESS_START_TIME, DateUtil.now()); + cloneVariables.put(BpmnVariableConstants.PROCESS_DEFINITION_NAME, definition.getName().trim()); + processInstanceBuilder.name(StrUtil.format(titleSetting.getTitle(), cloneVariables)); + } else { + processInstanceBuilder.name(definition.getName().trim()); + } + // 3.3 发起流程实例 + ProcessInstance instance = processInstanceBuilder.start(); + return instance.getId(); + } + + private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, + Map> startUserSelectAssignees, + Map variables) { + // 1. 获取预测的节点信息 + BpmApprovalDetailRespVO detailRespVO = getApprovalDetail(userId, new BpmApprovalDetailReqVO() + .setProcessDefinitionId(definition.getId()) + .setProcessVariables(variables)); + List activityNodes = detailRespVO.getActivityNodes(); + if (CollUtil.isEmpty(activityNodes)) { + return; + } + + // 2.1 移除掉不是发起人自选审批人节点 + activityNodes.removeIf(task -> + ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 + activityNodes.forEach(task -> { + List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; + if (CollUtil.isEmpty(assignees)) { + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, task.getName()); + } + Map userMap = adminUserApi.getUserMap(assignees); + assignees.forEach(assignee -> { + if (userMap.get(assignee) == null) { + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, task.getName(), assignee); + } + }); + }); + } + + @Override + public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { + // 1.1 校验流程实例存在 + ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); + } + // 1.2 只能取消自己的 + if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { + throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); + } + // 1.3 校验允许撤销审批中的申请 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService + .getProcessDefinitionInfo(instance.getProcessDefinitionId()); + Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo); + if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消 + && Boolean.FALSE.equals(processDefinitionInfo.getAllowCancelRunningProcess())) { + throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW); + } + // 1.4 子流程不允许取消 + if (StrUtil.isNotBlank(instance.getSuperExecutionId())) { + throw exception(PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW); + } + + // 2. 取消流程 + updateProcessInstanceCancel(cancelReqVO.getId(), + BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); + } + + @Override + public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { + // 1.1 校验流程实例存在 + ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); + } + + // 2. 取消流程 + AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); + updateProcessInstanceCancel(cancelReqVO.getId(), + BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); + } + + private void updateProcessInstanceCancel(String id, String reason) { + // 1. 更新流程实例 status + runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, + BpmProcessInstanceStatusEnum.CANCEL.getStatus()); + runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON, reason); + + // 2. 取消所有子流程 + List childProcessInstances = runtimeService.createProcessInstanceQuery() + .superProcessInstanceId(id).list(); + childProcessInstances.forEach(processInstance -> updateProcessInstanceCancel( + processInstance.getProcessInstanceId(), BpmReasonEnum.CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS.getReason())); + + // 3. 结束流程 + taskService.moveTaskToEnd(id, reason); + } + + @Override + public void updateProcessInstanceReject(ProcessInstance processInstance, String reason) { + runtimeService.setVariable(processInstance.getProcessInstanceId(), + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, + BpmProcessInstanceStatusEnum.REJECT.getStatus()); + runtimeService.setVariable(processInstance.getProcessInstanceId(), + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON, + BpmReasonEnum.REJECT_TASK.format(reason)); + } + + @Override + public void updateProcessInstanceVariables(String id, Map variables) { + runtimeService.setVariables(id, variables); + } + + @Override + public void removeProcessInstanceVariables(String id, Collection variableNames) { + runtimeService.removeVariables(id, variableNames); + } + + // ========== Event 事件相关方法 ========== + + @Override + public void processProcessInstanceCompleted(ProcessInstance instance) { + // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 + FlowableUtils.execute(instance.getTenantId(), () -> { + // 1.1 获取当前状态 + Integer status = (Integer) instance.getProcessVariables() + .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + String reason = (String) instance.getProcessVariables() + .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON); + // 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态 + // 为什么这么处理?因为流程完成,并且完成了,说明审批通过了 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { + status = BpmProcessInstanceStatusEnum.APPROVE.getStatus(); + runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, + status); + } + + // 2. 发送对应的消息通知 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { + messageService.sendMessageWhenProcessInstanceApprove( + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); + } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) { + messageService.sendMessageWhenProcessInstanceReject( + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason)); + } + + // 3. 发送流程实例的状态事件 + processInstanceEventPublisher.sendProcessInstanceResultEvent( + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); + + // 4. 流程后置通知 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. + getProcessDefinitionInfo(instance.getProcessDefinitionId()); + if (ObjUtil.isNotNull(processDefinitionInfo) && + ObjUtil.isNotNull(processDefinitionInfo.getProcessAfterTriggerSetting())) { + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting(); + + BpmHttpRequestUtils.executeBpmHttpRequest(instance, + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); + } + } + }); + } + + @Override + public void processProcessInstanceCreated(ProcessInstance instance) { + // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 + FlowableUtils.execute(instance.getTenantId(), () -> { + // 流程前置通知 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. + getProcessDefinitionInfo(instance.getProcessDefinitionId()); + if (ObjUtil.isNull(processDefinitionInfo) || + ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) { + return; + } + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting(); + BpmHttpRequestUtils.executeBpmHttpRequest(instance, + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); + }); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java new file mode 100644 index 0000000..8211bfe --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java @@ -0,0 +1,316 @@ +package com.zt.plat.module.bpm.service.task; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.*; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum; +import jakarta.validation.Valid; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskInfo; +import org.flowable.task.api.history.HistoricTaskInstance; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 流程任务实例 Service 接口 + * + * @author jason + * @author ZT + */ +public interface BpmTaskService { + + // ========== Query 查询相关方法 ========== + + /** + * 获得待办的流程任务分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页请求 + * @return 流程任务分页 + */ + PageResult getTaskTodoPage(Long userId, BpmTaskPageReqVO pageReqVO); + + /** + * 获得用户(待办)的任务: + * 1. 根据 taskId 查询待办任务 + * 2. 如果任务不存在(或者已审核),获取指定流程下,首个需要处理任务 + * + * @param userId 用户编号 + * @param taskId 任务编号 + * @param processInstanceId 流程实例编号 + * @return 待办任务 + */ + BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId); + + /** + * 获得已办的流程任务分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页请求 + * @return 流程任务分页 + */ + PageResult getTaskDonePage(Long userId, BpmTaskPageReqVO pageReqVO); + + /** + * 获得全部的流程任务分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页请求 + * @return 流程任务分页 + */ + PageResult getTaskPage(Long userId, BpmTaskPageReqVO pageReqVO); + + /** + * 获得流程任务 Map + * + * @param processInstanceIds 流程实例的编号数组 + * @return 流程任务 Map + */ + default Map> getTaskMapByProcessInstanceIds(List processInstanceIds) { + return CollectionUtils.convertMultiMap(getTasksByProcessInstanceIds(processInstanceIds), + Task::getProcessInstanceId); + } + + /** + * 获得流程任务列表 + * + * @param processInstanceIds 流程实例的编号数组 + * @return 流程任务列表 + */ + List getTasksByProcessInstanceIds(List processInstanceIds); + + /** + * 获得指定流程实例的流程任务列表,包括所有状态的 + * + * @param processInstanceId 流程实例的编号 + * @param asc 是否升序 + * @return 流程任务列表 + */ + List getTaskListByProcessInstanceId(String processInstanceId, Boolean asc); + + /** + * 校验任务是否存在,并且是否是分配给自己的任务 + * + * @param userId 用户 id + * @param taskId task id + */ + Task validateTask(Long userId, String taskId); + + /** + * 获取任务 + * + * @param id 任务编号 + * @return 任务 + */ + Task getTask(String id); + + /** + * 获取历史任务 + * + * @param id 任务编号 + * @return 历史任务 + */ + HistoricTaskInstance getHistoricTask(String id); + + /** + * 获取历史任务列表 + * + * @param taskIds 任务编号集合 + * @return 历史任务列表 + */ + List getHistoricTasks(Collection taskIds); + + /** + * 根据条件查询正在进行中的任务 + * + * @param processInstanceId 流程实例编号,不允许为空 + * @param assigned 是否分配了审批人,允许空 + * @param taskDefineKey 任务定义 Key,允许空 + */ + List getRunningTaskListByProcessInstanceId(String processInstanceId, + Boolean assigned, + String taskDefineKey); + + /** + * 获取当前任务的可退回的 UserTask 集合 + * + * @param id 当前的任务 ID + * @return 可以退回的节点列表 + */ + List getUserTaskListByReturn(String id); + + /** + * 获取指定任务的子任务列表(多层) + * + * @param parentTaskId 父任务 ID + * @param tasks 任务列表 + * @return 子任务列表 + */ + List getAllChildrenTaskListByParentTaskId(String parentTaskId, List tasks); + + /** + * 获取指定任务的子任务列表 + * + * @param parentTaskId 父任务ID + * @return 子任务列表 + */ + List getTaskListByParentTaskId(String parentTaskId); + + /** + * 获得指定流程实例的活动实例列表 + * + * @param processInstanceId 流程实例的编号 + * @return 活动实例列表 + */ + List getActivityListByProcessInstanceId(String processInstanceId); + + /** + * 获得执行编号对应的活动实例 + * + * @param executionId 执行编号 + * @return 活动实例 + */ + List getHistoricActivityListByExecutionId(String executionId); + + // ========== Update 写入相关方法 ========== + + /** + * 通过任务 + * + * @param userId 用户编号 + * @param reqVO 通过请求 + */ + void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO); + + /** + * 不通过任务 + * + * @param userId 用户编号 + * @param reqVO 不通过请求 + */ + void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO); + + /** + * 将流程任务分配给指定用户 + * + * @param userId 用户编号 + * @param reqVO 分配请求 + */ + void transferTask(Long userId, BpmTaskTransferReqVO reqVO); + + /** + * 将指定流程实例的、进行中的流程任务,移动到结束节点 + * + * @param processInstanceId 流程编号 + * @param reason 原因 + */ + void moveTaskToEnd(String processInstanceId, String reason); + + /** + * 将任务退回到指定的 targetDefinitionKey 位置 + * + * @param userId 用户编号 + * @param reqVO 退回的任务key和当前所在的任务ID + */ + void returnTask(Long userId, BpmTaskReturnReqVO reqVO); + + /** + * 将指定任务委派给其他人处理,等接收人处理后再回到原审批人手中审批 + * + * @param userId 用户编号 + * @param reqVO 被委派人和被委派的任务编号理由参数 + */ + void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO); + + /** + * 任务加签 + * + * @param userId 被加签的用户和任务 ID,加签类型 + * @param reqVO 当前用户 ID + */ + void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO); + + /** + * 任务减签 + * + * @param userId 当前用户ID + * @param reqVO 被减签的任务 ID,理由 + */ + void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO); + + /** + * 抄送任务 + * + * @param userId 用户编号 + * @param reqVO 通过请求 + */ + void copyTask(Long userId, @Valid BpmTaskCopyReqVO reqVO); + + // ========== Event 事件相关方法 ========== + + /** + * 处理 Task 创建事件,目前是 + *

+ * 1. 更新它的状态为审批中 + * 2. 处理自动通过的情况,例如说:1)无审批人时,是否自动通过、不通过;2)非【人工审核】时,是否自动通过、不通过 + *

+ * 注意:它的触发时机,晚于 {@link #processTaskAssigned(Task)} 之后 + * + * @param task 任务实体 + */ + void processTaskCreated(Task task); + + /** + * 处理 Task 取消事件,目前是更新它的状态为已取消 + * + * @param taskId 任务的编号 + */ + void processTaskCanceled(String taskId); + + /** + * 处理 Task 设置审批人事件,目前是发送审批消息 + * + * @param task 任务实体 + */ + void processTaskAssigned(Task task); + + /** + * 处理 Task 完成事件,目前是发送任务后置通知 + * + * @param task 任务实体 + */ + void processTaskCompleted(Task task); + + /** + * 处理 Task 审批超时事件,可能会处理多个当前审批中的任务 + * + * @param processInstanceId 流程示例编号 + * @param taskDefineKey 任务 Key + * @param handlerType 处理类型,参见 {@link BpmUserTaskTimeoutHandlerTypeEnum} + */ + void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType); + + /** + * 处理 ChildProcess 子流程的审批超时事件 + * + * @param processInstanceId 流程示例编号 + * @param taskDefineKey 任务 Key + */ + void processChildProcessTimeout(String processInstanceId, String taskDefineKey); + + /** + * 触发流程任务 (ReceiveTask) 的执行 + *

+ * 1. Simple 模型 HTTP 回调请求触发器节点的回调,触发流程继续执行 + * 2. Simple 模型延迟器节点,到时触发流程继续执行 + * + * @param processInstanceId 流程示例编号 + * @param taskDefineKey 任务 Key + */ + void triggerTask(String processInstanceId, String taskDefineKey); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java new file mode 100644 index 0000000..1c44b8c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java @@ -0,0 +1,1535 @@ +package com.zt.plat.module.bpm.service.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.*; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.json.JSONUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.date.DateUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.framework.common.util.object.ObjectUtils; +import com.zt.plat.framework.common.util.object.PageUtils; +import com.zt.plat.framework.datapermission.core.annotation.DataPermission; +import com.zt.plat.framework.web.core.util.WebFrameworkUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.*; +import com.zt.plat.module.bpm.convert.task.BpmTaskConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.definition.*; +import com.zt.plat.module.bpm.enums.task.BpmCommentTypeEnum; +import com.zt.plat.module.bpm.enums.task.BpmReasonEnum; +import com.zt.plat.module.bpm.enums.task.BpmTaskSignTypeEnum; +import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import com.zt.plat.module.bpm.service.definition.BpmModelService; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.message.BpmMessageService; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.*; +import org.flowable.engine.HistoryService; +import org.flowable.engine.ManagementService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.runtime.ActivityInstance; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.DelegationState; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskInfo; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; +import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +import java.util.*; +import java.util.stream.Stream; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; +import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; + +/** + * 流程任务实例 Service 实现类 + * + * @author ZT + * @author jason + */ +@Slf4j +@Service +public class BpmTaskServiceImpl implements BpmTaskService { + + @Resource + private TaskService taskService; + @Resource + private HistoryService historyService; + @Resource + private RuntimeService runtimeService; + @Resource + private ManagementService managementService; + + @Resource + private BpmProcessInstanceService processInstanceService; + @Resource + private BpmProcessDefinitionService bpmProcessDefinitionService; + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; + @Resource + private BpmModelService modelService; + @Resource + private BpmMessageService messageService; + @Resource + private BpmFormService formService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + // ========== Query 查询相关方法 ========== + + @Override + public PageResult getTaskTodoPage(Long userId, BpmTaskPageReqVO pageVO) { + TaskQuery taskQuery = taskService.createTaskQuery() + .taskAssignee(String.valueOf(userId)) // 分配给自己 + .active() + .includeProcessVariables() + .orderByTaskCreateTime().desc(); // 创建时间倒序 + if (StrUtil.isNotBlank(pageVO.getName())) { + taskQuery.taskNameLike("%" + pageVO.getName() + "%"); + } + if (StrUtil.isNotEmpty(pageVO.getCategory())) { + taskQuery.taskCategory(pageVO.getCategory()); + } + if (StrUtil.isNotEmpty(pageVO.getProcessDefinitionKey())) { + taskQuery.processDefinitionKey(pageVO.getProcessDefinitionKey()); + } + if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { + taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); + taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); + } + long count = taskQuery.count(); + if (count == 0) { + return PageResult.empty(); + } + List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + return new PageResult<>(tasks, count); + } + + @Override + public BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId) { + // 1.1 获取指定的用户待办任务 + Task todoTask = getMyTodoTask(userId, taskId); + // 1.2 获取不到,则获取该流程实例下,第一个用户的待办任务 + if (todoTask == null) { + todoTask = getMyFirstTodoTask(userId, processInstanceId); + } + if (todoTask == null) { + return null; + } + + // 2. 查询该任务的子任务 + List childrenTasks = getAllChildrenTaskListByParentTaskId(todoTask.getId(), CollUtil.newArrayList(todoTask)); + + // 3. 转换返回 + BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(todoTask.getProcessDefinitionId()); + Map buttonsSetting = BpmnModelUtils.parseButtonsSetting( + bpmnModel, todoTask.getTaskDefinitionKey()); + Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey()); + Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey()); + Integer nodeType = parseNodeType(BpmnModelUtils.getFlowElementById(bpmnModel, todoTask.getTaskDefinitionKey())); + + // 4. 任务表单 + BpmFormDO taskForm = null; + if (StrUtil.isNotBlank(todoTask.getFormKey())) { + try { + Long formId = Long.parseLong(todoTask.getFormKey()); + taskForm = formService.getForm(formId); + } catch (NumberFormatException e) { + // 如果 formKey 不是数字(比如是URL),则不处理表单 + taskForm = null; + } + } + + return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm) + .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire); + } + + /** + * 获得用户指定 taskId 任务编号的“待办”(未审批、且可审核)的任务 + * + * @param userId 用户编号 + * @param taskId 任务编号 + * @return 任务 + */ + private Task getMyTodoTask(Long userId, String taskId) { + if (StrUtil.isEmpty(taskId)) { + return null; + } + Task task = getTask(taskId); + if (task == null) { + return null; + } + if (!isAssignUserTask(userId, task) && !isAddSignUserTask(userId, task)) { + return null; + } + return task; + } + + /** + * 获得用户指定 processInstanceId 流程编号下的首个“待办”(未审批、且可审核)的任务 + * + * @param userId 用户编号 + * @param processInstanceId 流程编号 + * @return 任务 + */ + private Task getMyFirstTodoTask(Long userId, String processInstanceId) { + if (processInstanceId == null) { + return null; + } + // 1. 查询所有任务 + List tasks = taskService.createTaskQuery() + .active() + .processInstanceId(processInstanceId) + .includeTaskLocalVariables() + .includeProcessVariables() + .orderByTaskCreateTime().asc() // 按创建时间升序 + .list(); + + // 2. 查询我的首个任务 + return CollUtil.findOne(tasks, task -> { + return isAssignUserTask(userId, task) // 当前用户为审批人 + || isAddSignUserTask(userId, task); // 当前用户为加签人(为了减签) + }); + } + + @Override + public PageResult getTaskDonePage(Long userId, BpmTaskPageReqVO pageVO) { + HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery() + .finished() // 已完成 + .taskAssignee(String.valueOf(userId)) // 分配给自己 + .includeTaskLocalVariables() + .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序 + if (StrUtil.isNotBlank(pageVO.getName())) { + taskQuery.taskNameLike("%" + pageVO.getName() + "%"); + } + if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { + taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); + taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); + } + // 执行查询 + long count = taskQuery.count(); + if (count == 0) { + return PageResult.empty(); + } + List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + + // 特殊:强制移除自动完成的“发起人”节点 + // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤 + tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID)); + return new PageResult<>(tasks, count); + } + + @Override + public PageResult getTaskPage(Long userId, BpmTaskPageReqVO pageVO) { + HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery() + .includeTaskLocalVariables() + .taskTenantId(FlowableUtils.getTenantId()) + .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序 + if (StrUtil.isNotBlank(pageVO.getName())) { + taskQuery.taskNameLike("%" + pageVO.getName() + "%"); + } + if (StrUtil.isNotEmpty(pageVO.getCategory())) { + taskQuery.taskCategory(pageVO.getCategory()); + } + if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { + taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); + taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); + } + // 执行查询 + long count = taskQuery.count(); + if (count == 0) { + return PageResult.empty(); + } + List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + return new PageResult<>(tasks, count); + } + + @Override + public List getTasksByProcessInstanceIds(List processInstanceIds) { + if (CollUtil.isEmpty(processInstanceIds)) { + return Collections.emptyList(); + } + return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list(); + } + + @Override + public List getTaskListByProcessInstanceId(String processInstanceId, Boolean asc) { + HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery() + .includeTaskLocalVariables() + .processInstanceId(processInstanceId); + if (Boolean.TRUE.equals(asc)) { + query.orderByHistoricTaskInstanceStartTime().asc(); + } else { + query.orderByHistoricTaskInstanceStartTime().desc(); + } + return query.list(); + } + + @Override + public Task validateTask(Long userId, String taskId) { + Task task = validateTaskExist(taskId); + // 为什么判断 assignee 非空的情况下? + // 例如说:在审批人为空时,我们会有“自动审批通过”的策略,此时 userId 为 null,允许通过 + if (StrUtil.isNotBlank(task.getAssignee()) + && ObjectUtil.notEqual(userId, NumberUtils.parseLong(task.getAssignee()))) { + throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); + } + return task; + } + + private Task validateTaskExist(String id) { + Task task = getTask(id); + if (task == null) { + throw exception(TASK_NOT_EXISTS); + } + return task; + } + + @Override + public Task getTask(String id) { + return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult(); + } + + @Override + public HistoricTaskInstance getHistoricTask(String id) { + return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult(); + } + + @Override + public List getHistoricTasks(Collection taskIds) { + return historyService.createHistoricTaskInstanceQuery().taskIds(taskIds).includeTaskLocalVariables().list(); + } + + @Override + public List getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String defineKey) { + Assert.notNull(processInstanceId, "processInstanceId 不能为空"); + TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId).active() + .includeTaskLocalVariables(); + if (BooleanUtil.isTrue(assigned)) { + taskQuery.taskAssigned(); + } else if (BooleanUtil.isFalse(assigned)) { + taskQuery.taskUnassigned(); + } + if (StrUtil.isNotEmpty(defineKey)) { + taskQuery.taskDefinitionKey(defineKey); + } + return taskQuery.list(); + } + + @Override + public List getUserTaskListByReturn(String id) { + // 1.1 校验当前任务 task 存在 + Task task = validateTaskExist(id); + // 1.2 根据流程定义获取流程模型信息 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); + FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + if (source == null) { + throw exception(TASK_NOT_EXISTS); + } + + // 2.1 查询该任务的前置任务节点的 key 集合 + List previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null); + if (CollUtil.isEmpty(previousUserList)) { + return Collections.emptyList(); + } + // 2.2 过滤:只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回 + previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null)); + return previousUserList; + } + + @Override + public List getAllChildrenTaskListByParentTaskId(String parentTaskId, List tasks) { + if (CollUtil.isEmpty(tasks)) { + return Collections.emptyList(); + } + Map> parentTaskMap = convertMultiMap( + filterList(tasks, task -> StrUtil.isNotEmpty(task.getParentTaskId())), TaskInfo::getParentTaskId); + if (CollUtil.isEmpty(parentTaskMap)) { + return Collections.emptyList(); + } + + List result = new ArrayList<>(); + // 1. 递归获取子级 + Stack stack = new Stack<>(); + stack.push(parentTaskId); + // 2. 递归遍历 + for (int i = 0; i < Short.MAX_VALUE; i++) { + if (stack.isEmpty()) { + break; + } + // 2.1 获取子任务们 + String taskId = stack.pop(); + List childTaskList = filterList(tasks, task -> StrUtil.equals(task.getParentTaskId(), taskId)); + // 2.2 如果非空,则添加到 stack 进一步递归 + if (CollUtil.isNotEmpty(childTaskList)) { + stack.addAll(convertList(childTaskList, TaskInfo::getId)); + result.addAll(childTaskList); + } + } + return result; + } + + /** + * 获得所有子任务列表 + * + * @param parentTask 父任务 + * @return 所有子任务列表 + */ + private List getAllChildTaskList(Task parentTask) { + List result = new ArrayList<>(); + // 1. 递归获取子级 + Stack stack = new Stack<>(); + stack.push(parentTask); + // 2. 递归遍历 + for (int i = 0; i < Short.MAX_VALUE; i++) { + if (stack.isEmpty()) { + break; + } + // 2.1 获取子任务们 + Task task = stack.pop(); + List childTaskList = getTaskListByParentTaskId(task.getId()); + // 2.2 如果非空,则添加到 stack 进一步递归 + if (CollUtil.isNotEmpty(childTaskList)) { + stack.addAll(childTaskList); + result.addAll(childTaskList); + } + } + return result; + } + + @Override + public List getTaskListByParentTaskId(String parentTaskId) { + String tableName = managementService.getTableName(TaskEntity.class); + // taskService.createTaskQuery() 没有 parentId 参数,所以写 sql 查询 + String sql = "select ID_,NAME_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}"; + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list(); + } + + /** + * 获取子任务个数 + * + * @param parentTaskId 父任务 ID + * @return 剩余子任务个数 + */ + private Long getTaskCountByParentTaskId(String parentTaskId) { + String tableName = managementService.getTableName(TaskEntity.class); + String sql = "SELECT COUNT(1) from " + tableName + " WHERE PARENT_TASK_ID_=#{parentTaskId}"; + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count(); + } + + /** + * 获得任务根任务的父任务编号 + * + * @param task 任务 + * @return 根任务的父任务编号 + */ + private String getTaskRootParentId(Task task) { + if (task == null || task.getParentTaskId() == null) { + return null; + } + for (int i = 0; i < Short.MAX_VALUE; i++) { + Task parentTask = getTask(task.getParentTaskId()); + if (parentTask == null) { + return null; + } + if (parentTask.getParentTaskId() == null) { + return parentTask.getId(); + } + task = parentTask; + } + throw new IllegalArgumentException(String.format("Task(%s) 层级过深,无法获取父节点编号", task.getId())); + } + + @Override + public List getActivityListByProcessInstanceId(String processInstanceId) { + return historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId) + .orderByHistoricActivityInstanceStartTime().asc().list(); + } + + @Override + public List getHistoricActivityListByExecutionId(String executionId) { + return historyService.createHistoricActivityInstanceQuery().executionId(executionId).list(); + } + + /** + * 判断指定用户,是否是当前任务的审批人 + * + * @param userId 用户编号 + * @param task 任务 + * @return 是否 + */ + private boolean isAssignUserTask(Long userId, Task task) { + Long assignee = NumberUtil.parseLong(task.getAssignee(), null); + return ObjectUtil.equals(userId, assignee); + } + + /** + * 判断指定用户,是否是当前任务的拥有人 + * + * @param userId 用户编号 + * @param task 任务 + * @return 是否 + */ + private boolean isOwnerUserTask(Long userId, Task task) { + Long assignee = NumberUtil.parseLong(task.getOwner(), null); + return ObjectUtil.equal(userId, assignee); + } + + /** + * 判断指定用户,是否是当前任务的加签人 + * + * @param userId 用户 Id + * @param task 任务 + * @return 是否 + */ + private boolean isAddSignUserTask(Long userId, Task task) { + return (isAssignUserTask(userId, task) || isOwnerUserTask(userId, task)) + && BpmTaskSignTypeEnum.of(task.getScopeType()) != null; + } + + // ========== Update 写入相关方法 ========== + + @Override + @Transactional(rollbackFor = Exception.class) + public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) { + // 1.1 校验任务存在 + Task task = validateTask(userId, reqVO.getId()); + // 1.2 校验流程实例存在 + ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + // 1.3 校验签名 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); + Boolean signEnable = parseSignEnable(bpmnModel, task.getTaskDefinitionKey()); + if (signEnable && StrUtil.isEmpty(reqVO.getSignPicUrl())) { + throw exception(TASK_SIGNATURE_NOT_EXISTS); + } + // 1.4 校验审批意见 + Boolean reasonRequire = parseReasonRequire(bpmnModel, task.getTaskDefinitionKey()); + if (reasonRequire && StrUtil.isEmpty(reqVO.getReason())) { + throw exception(TASK_REASON_REQUIRE); + } + + // 情况一:被委派的任务,不调用 complete 去完成任务 + if (DelegationState.PENDING.equals(task.getDelegationState())) { + approveDelegateTask(reqVO, task); + return; + } + + // 情况二:审批有【后】加签的任务 + if (BpmTaskSignTypeEnum.AFTER.getType().equals(task.getScopeType())) { + approveAfterSignTask(task, reqVO); + return; + } + + // 情况三:审批普通的任务。大多数情况下,都是这样 + // 2.1 更新 task 状态、原因、签字 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason()); + if (signEnable) { + taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_SIGN_PIC_URL, reqVO.getSignPicUrl()); + } + // 2.2 添加评论 + taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), + BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); + + // 3. 设置流程变量。如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时 variables 一定会为空 + // 场景一:A 节点发起,B 节点表单无可编辑字段,审批通过时,C 节点需要流程变量获取下一个执行节点,但因为 B 节点无可编辑的字段,variables 为空,流程可能出现问题。 + // 场景二:A 节点发起,B 节点只有某一个字段可编辑(比如 day),但 C 节点需要多个节点。 + // (比如 work + day 变量,在发起时填写,因为 B 节点只有 day 的编辑权限,在审批后,variables 会缺少 work 的值) + Map processVariables = new HashMap<>(); + if (CollUtil.isNotEmpty(instance.getProcessVariables())) { // 获取历史中流程变量 + processVariables.putAll(instance.getProcessVariables()); + } + if (CollUtil.isNotEmpty(reqVO.getVariables())) { // 合并前端传递的流程变量,以前端为准 + processVariables.putAll(reqVO.getVariables()); + } + + // 如果任务变量不为空,设置任务级别的变量实例信息 + if (CollUtil.isNotEmpty(reqVO.getTaskVariables())) { + Map taskVariables = new HashMap<>(); + taskVariables.put("taskVariables", JSONUtil.toJsonStr(reqVO.getTaskVariables())); + taskService.setVariablesLocal(task.getId(), taskVariables); + } + + // 4. 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 + Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables, + bpmnModel, reqVO.getNextAssignees(), instance); + runtimeService.setVariables(task.getProcessInstanceId(), variables); + + // 5. 调用 BPM complete 去完成任务 + taskService.complete(task.getId(), variables, true); + + // 【加签专属】处理加签任务 + handleParentTaskIfSign(task.getParentTaskId()); + } + + /** + * 校验选择的下一个节点的审批人,是否合法 + * + * 1. 是否有漏选:没有选择审批人 + * 2. 是否有多选:非下一个节点 + * + * @param taskDefinitionKey 当前任务节点标识 + * @param variables 流程变量 + * @param bpmnModel 流程模型 + * @param nextAssignees 下一个节点审批人集合(参数) + * @param processInstance 流程实例 + */ + @SuppressWarnings("unchecked") + private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, + Map> nextAssignees, ProcessInstance processInstance) { + // simple 设计器第一个节点默认为发起人节点,不校验是否存在审批人 + if (Objects.equals(taskDefinitionKey, START_USER_NODE_ID)) { + return variables; + } + // 1. 获取下一个将要执行的节点集合 + FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); + List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + + // 2. 校验选择的下一个节点的审批人,是否合法 + for (FlowNode nextFlowNode : nextFlowNodes) { + Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); + // 2.1 情况一:如果节点中的审批人策略为 发起人自选 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { + // 特殊:如果当前节点已经存在审批人,则不允许覆盖 + Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + if (startUserSelectAssignees != null && CollUtil.isNotEmpty(startUserSelectAssignees.get(nextFlowNode.getId()))) { + continue; + } + // 如果节点存在,但未配置审批人 + List assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; + if (CollUtil.isEmpty(assignees)) { + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); + } + + // 设置 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES + if (startUserSelectAssignees == null) { + startUserSelectAssignees = new HashMap<>(); + } + startUserSelectAssignees.put(nextFlowNode.getId(), assignees); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); + continue; + } + + // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { + // 如果节点存在,但未配置审批人 + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); + List assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; + if (CollUtil.isEmpty(assignees)) { + throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); + } + + // 设置 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES + if (approveUserSelectAssignees == null) { + approveUserSelectAssignees = new HashMap<>(); + } + approveUserSelectAssignees.put(nextFlowNode.getId(), assignees); + Map> existingApproveUserSelectAssignees = (Map>) variables.get( + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); + if (CollUtil.isNotEmpty(existingApproveUserSelectAssignees)) { + approveUserSelectAssignees.putAll(existingApproveUserSelectAssignees); + } + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, approveUserSelectAssignees); + } + } + return variables; + } + + /** + * 审批通过存在“后加签”的任务。 + *

+ * 注意:该任务不能马上完成,需要一个中间状态(APPROVING),并激活剩余所有子任务(PROCESS)为可审批处理 + * 如果马上完成,则会触发下一个任务,甚至如果没有下一个任务则流程实例就直接结束了! + * + * @param task 当前任务 + * @param reqVO 前端请求参数 + */ + private void approveAfterSignTask(Task task, BpmTaskApproveReqVO reqVO) { + // 更新父 task 状态 + 原因 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVING.getStatus(), reqVO.getReason()); + + // 2. 激活子任务 + List childrenTaskList = getTaskListByParentTaskId(task.getId()); + for (Task childrenTask : childrenTaskList) { + taskService.resolveTask(childrenTask.getId()); + // 更新子 task 状态 + updateTaskStatus(childrenTask.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); + } + } + + /** + * 如果父任务是有前后【加签】的任务,如果它【加签】出来的子任务都被处理,需要处理父任务: + *

+ * 1. 如果是【向前】加签,则需要重新激活父任务,让它可以被审批 + * 2. 如果是【向后】加签,则需要完成父任务,让它完成审批 + * + * @param parentTaskId 父任务编号 + */ + private void handleParentTaskIfSign(String parentTaskId) { + if (StrUtil.isBlank(parentTaskId)) { + return; + } + // 1.1 判断是否还有子任务。如果没有,就不处理 + Long childrenTaskCount = getTaskCountByParentTaskId(parentTaskId); + if (childrenTaskCount > 0) { + return; + } + // 1.2 只处理加签的父任务 + Task parentTask = validateTaskExist(parentTaskId); + String scopeType = parentTask.getScopeType(); + if (BpmTaskSignTypeEnum.of(scopeType) == null) { + return; + } + + // 2. 子任务已处理完成,清空 scopeType 字段,修改 parentTask 信息,方便后续可以继续向前后向后加签 + TaskEntityImpl parentTaskImpl = (TaskEntityImpl) parentTask; + parentTaskImpl.setScopeType(null); + taskService.saveTask(parentTaskImpl); + + // 3.1 情况一:处理向【向前】加签 + if (BpmTaskSignTypeEnum.BEFORE.getType().equals(scopeType)) { + // 3.1.1 owner 重新赋值给父任务的 assignee,这样它就可以被审批 + taskService.resolveTask(parentTaskId); + // 3.1.2 更新流程任务 status + updateTaskStatus(parentTaskId, BpmTaskStatusEnum.RUNNING.getStatus()); + // 3.2 情况二:处理向【向后】加签 + } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(scopeType)) { + // 只有 parentTask 处于 APPROVING 的情况下,才可以继续 complete 完成 + // 否则,一个未审批的 parentTask 任务,在加签出来的任务都被减签的情况下,就直接完成审批,这样会存在问题 + Integer status = (Integer) parentTask.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); + if (ObjectUtil.notEqual(status, BpmTaskStatusEnum.APPROVING.getStatus())) { + return; + } + // 3.2.2 完成自己(因为它已经没有子任务,所以也可以完成) + updateTaskStatus(parentTaskId, BpmTaskStatusEnum.APPROVE.getStatus()); + taskService.complete(parentTaskId); + } + + // 4. 递归处理父任务 + handleParentTaskIfSign(parentTask.getParentTaskId()); + } + + /** + * 审批被委派的任务 + * + * @param reqVO 前端请求参数,包含当前任务ID,审批意见等 + * @param task 当前被审批的任务 + */ + private void approveDelegateTask(BpmTaskApproveReqVO reqVO, Task task) { + // 1. 添加审批意见 + AdminUserRespDTO currentUser = adminUserApi.getUser(WebFrameworkUtils.getLoginUserId()).getCheckedData(); + AdminUserRespDTO ownerUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())).getCheckedData(); // 发起委托的用户 + Assert.notNull(ownerUser, "委派任务找不到原审批人,需要检查数据"); + taskService.addComment(reqVO.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_END.getType(), + BpmCommentTypeEnum.DELEGATE_END.formatComment(currentUser.getNickname(), ownerUser.getNickname(), reqVO.getReason())); + + // 2.1 调用 resolveTask 完成任务。 + // 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()):将 owner 设置为 assignee + taskService.resolveTask(task.getId()); + // 2.2 更新 task 状态 + 原因 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus(), reqVO.getReason()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) { + // 1.1 校验任务存在 + Task task = validateTask(userId, reqVO.getId()); + // 1.2 校验流程实例存在 + ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + + // 2.1 更新流程任务为不通过 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason()); + // 2.2 添加流程评论 + taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), + BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); + // 2.3 如果当前任务时被加签的,则加它的根任务也标记成未通过 + // 疑问:为什么要标记未通过呢? + // 回答:例如说 A 任务被向前加签除 B 任务时,B 任务被审批不通过,此时 A 会被取消。而 zt-ui-admin-vue3 不展示“已取消”的任务,导致展示不出审批不通过的细节。 + if (task.getParentTaskId() != null) { + String rootParentId = getTaskRootParentId(task); + updateTaskStatusAndReason(rootParentId, BpmTaskStatusEnum.REJECT.getStatus(), + BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); + taskService.addComment(rootParentId, task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), + BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); + } + + // 3. 根据不同的 RejectHandler 处理策略 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + // 3.1 情况一:驳回到指定的任务节点 + BpmUserTaskRejectHandlerTypeEnum userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement); + if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerTypeEnum.RETURN_USER_TASK) { + String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement); + Assert.notNull(returnTaskId, "退回的节点不能为空"); + returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId()) + .setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason())); + return; + } + // 3.2 情况二:直接结束,审批不通过 + processInstanceService.updateProcessInstanceReject(instance, reqVO.getReason()); // 标记不通过 + moveTaskToEnd(task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); // 结束流程 + } + + /** + * 更新流程任务的 status 状态 + * + * @param id 任务编号 + * @param status 状态 + */ + private void updateTaskStatus(String id, Integer status) { + taskService.setVariableLocal(id, BpmnVariableConstants.TASK_VARIABLE_STATUS, status); + } + + /** + * 更新流程任务的 status 状态、reason 理由 + * + * @param id 任务编号 + * @param status 状态 + * @param reason 理由(审批通过、审批不通过的理由) + */ + private void updateTaskStatusAndReason(String id, Integer status, String reason) { + updateTaskStatus(id, status); + taskService.setVariableLocal(id, BpmnVariableConstants.TASK_VARIABLE_REASON, reason); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void returnTask(Long userId, BpmTaskReturnReqVO reqVO) { + // 1.1 当前任务 task + Task task = validateTask(userId, reqVO.getId()); + if (task.isSuspended()) { + throw exception(TASK_IS_PENDING); + } + // 1.2 校验源头和目标节点的关系,并返回目标元素 + FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(), + reqVO.getTargetTaskDefinitionKey(), task.getProcessDefinitionId()); + + // 2. 调用 Flowable 框架的退回逻辑 + returnTask(userId, task, targetElement, reqVO); + } + + /** + * 退回流程节点时,校验目标任务节点是否可退回 + * + * @param sourceKey 当前任务节点 Key + * @param targetKey 目标任务节点 key + * @param processDefinitionId 当前流程定义 ID + * @return 目标任务节点元素 + */ + private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) { + // 1.1 获取流程模型信息 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); + // 1.3 获取当前任务节点元素 + FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey); + // 1.3 获取跳转的节点元素 + FlowElement target = BpmnModelUtils.getFlowElementById(bpmnModel, targetKey); + if (target == null) { + throw exception(TASK_TARGET_NODE_NOT_EXISTS); + } + + // 2.2 只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回 + if (!BpmnModelUtils.isSequentialReachable(source, target, null)) { + throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR); + } + return target; + } + + /** + * 执行退回逻辑 + * + * @param userId 用户编号 + * @param currentTask 当前退回的任务 + * @param targetElement 需要退回到的目标任务 + * @param reqVO 前端参数封装 + */ + public void returnTask(Long userId, Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) { + // 1. 获得所有需要回撤的任务 taskDefinitionKey,用于稍后的 moveActivityIdsToSingleActivityId 回撤 + // 1.1 获取所有正常进行的任务节点 Key + List taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list(); + List runTaskKeyList = convertList(taskList, Task::getTaskDefinitionKey); + // 1.2 通过 targetElement 的出口连线,计算在 runTaskKeyList 有哪些 key 需要被撤回 + // 为什么不直接使用 runTaskKeyList 呢?因为可能存在多个审批分支,例如说:A -> B -> C 和 D -> F,而只要 C 撤回到 A,需要排除掉 F + List returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null); + List returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId); + + List runExecutionIds = new ArrayList<>(); + // 2. 给当前要被退回的 task 数组,设置退回意见 + taskList.forEach(task -> { + // 需要排除掉,不需要设置退回意见的任务 + if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) { + return; + } + runExecutionIds.add(task.getExecutionId()); + + // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 + if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 + // 2.1.1 添加评论 + taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN.getType(), + BpmCommentTypeEnum.RETURN.formatComment(reqVO.getReason())); + // 2.1.2 更新 task 状态 + 原因 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason()); + } else { // 情况二:别人的任务,进行 CANCEL 标记 + processTaskCanceled(task.getId()); + } + }); + + // 3. 设置流程变量节点驳回标记:用于驳回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略。导致自动通过 + runtimeService.setVariable(currentTask.getProcessInstanceId(), + String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE); + // 4. 执行驳回 + // 使用 moveExecutionsToSingleActivityId 替换 moveActivityIdsToSingleActivityId 原因: + // 当多实例任务回退的时候有问题。相关 issue: https://github.com/flowable/flowable-engine/issues/3944 + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(currentTask.getProcessInstanceId()) + .moveExecutionsToSingleActivityId(runExecutionIds, reqVO.getTargetTaskDefinitionKey()) + .changeState(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO) { + String taskId = reqVO.getId(); + // 1.1 校验任务 + Task task = validateTask(userId, reqVO.getId()); + if (task.getAssignee().equals(reqVO.getDelegateUserId().toString())) { // 校验当前审批人和被委派人不是同一人 + throw exception(TASK_DELEGATE_FAIL_USER_REPEAT); + } + // 1.2 校验目标用户存在 + AdminUserRespDTO delegateUser = adminUserApi.getUser(reqVO.getDelegateUserId()).getCheckedData(); + if (delegateUser == null) { + throw exception(TASK_DELEGATE_FAIL_USER_NOT_EXISTS); + } + + // 2. 添加委托意见 + AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData(); + taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_START.getType(), + BpmCommentTypeEnum.DELEGATE_START.formatComment(currentUser.getNickname(), delegateUser.getNickname(), reqVO.getReason())); + + // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) + taskService.setOwner(taskId, task.getAssignee()); + // 3.2 执行委派,将任务委派给 delegateUser + taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString()); + // 补充说明:委托不单独设置状态。如果需要,可通过 Task 的 DelegationState 字段,判断是否为 DelegationState.PENDING 委托中 + } + + @Override + public void transferTask(Long userId, BpmTaskTransferReqVO reqVO) { + String taskId = reqVO.getId(); + // 1.1 校验任务 + Task task = validateTask(userId, reqVO.getId()); + if (task.getAssignee().equals(reqVO.getAssigneeUserId().toString())) { // 校验当前审批人和被转派人不是同一人 + throw exception(TASK_TRANSFER_FAIL_USER_REPEAT); + } + // 1.2 校验目标用户存在 + AdminUserRespDTO assigneeUser = adminUserApi.getUser(reqVO.getAssigneeUserId()).getCheckedData(); + if (assigneeUser == null) { + throw exception(TASK_TRANSFER_FAIL_USER_NOT_EXISTS); + } + + // 2. 添加委托意见 + AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData(); + taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.TRANSFER.getType(), + BpmCommentTypeEnum.TRANSFER.formatComment(currentUser.getNickname(), assigneeUser.getNickname(), reqVO.getReason())); + + // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) + taskService.setOwner(taskId, task.getAssignee()); + // 3.2 执行转派(审批人),将任务转派给 assigneeUser + // 委托( delegate)和转派(transfer)的差别,就在这块的调用!!!! + taskService.setAssignee(taskId, reqVO.getAssigneeUserId().toString()); + } + + @Override + public void moveTaskToEnd(String processInstanceId, String reason) { + List taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null); + if (CollUtil.isEmpty(taskList)) { + return; + } + + // 1. 其它未结束的任务,直接取消 + // 疑问:为什么不通过 updateTaskStatusWhenCanceled 监听取消,而是直接提前调用呢? + // 回答:详细见 updateTaskStatusWhenCanceled 的方法,加签的场景 + taskList.forEach(task -> { + Integer otherTaskStatus = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); + if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) { + return; + } + processTaskCanceled(task.getId()); + }); + + // 2. 终止流程 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId()); + List activityIds = CollUtil.newArrayList(convertSet(taskList, Task::getTaskDefinitionKey)); + EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); + Assert.notNull(endEvent, "结束节点不能为空"); + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(processInstanceId) + .moveActivityIdsToSingleActivityId(activityIds, endEvent.getId()) + .changeState(); + + // 3. 特殊:如果跳转到 EndEvent 流程还未结束, 执行 deleteProcessInstance 方法 + // TODO 芋艿:目前发现并行分支情况下,会存在这个情况,后续看看有没更好的方案; + List executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list(); + if (CollUtil.isNotEmpty(executions)) { + log.warn("[moveTaskToEnd][执行跳转到 EndEvent 后, 流程实例未结束,强制执行 deleteProcessInstance 方法]"); + runtimeService.deleteProcessInstance(processInstanceId, reason); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO) { + // 1. 获取和校验任务 + TaskEntityImpl taskEntity = validateTaskCanCreateSign(userId, reqVO); + List userList = adminUserApi.getUserList(reqVO.getUserIds()).getCheckedData(); + if (CollUtil.isEmpty(userList)) { + throw exception(TASK_SIGN_CREATE_USER_NOT_EXIST); + } + + // 2. 处理当前任务 + // 2.1 开启计数功能,主要用于为了让表 ACT_RU_TASK 中的 SUB_TASK_COUNT_ 字段记录下总共有多少子任务,后续可能有用 + taskEntity.setCountEnabled(true); + // 2.2 向前加签,设置 owner,置空 assign。等子任务都完成后,再调用 resolveTask 重新将 owner 设置为 assign + // 原因是:不能和向前加签的子任务一起审批,需要等前面的子任务都完成才能审批 + if (reqVO.getType().equals(BpmTaskSignTypeEnum.BEFORE.getType())) { + taskEntity.setOwner(taskEntity.getAssignee()); + taskEntity.setAssignee(null); + } + // 2.4 记录加签方式,完成任务时需要用到判断 + taskEntity.setScopeType(reqVO.getType()); + // 2.5 保存当前任务修改后的值 + taskService.saveTask(taskEntity); + // 2.6 更新 task 状态为 WAIT,只有在向前加签的时候 + if (reqVO.getType().equals(BpmTaskSignTypeEnum.BEFORE.getType())) { + updateTaskStatus(taskEntity.getId(), BpmTaskStatusEnum.WAIT.getStatus()); + } + + // 3. 创建加签任务 + createSignTaskList(convertList(reqVO.getUserIds(), String::valueOf), taskEntity); + + // 4. 记录加签的评论到 task 任务 + AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData(); + String comment = StrUtil.format(BpmCommentTypeEnum.ADD_SIGN.getComment(), + currentUser.getNickname(), BpmTaskSignTypeEnum.nameOfType(reqVO.getType()), + String.join(",", convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason()); + taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(), BpmCommentTypeEnum.ADD_SIGN.getType(), comment); + } + + /** + * 校验任务是否可以加签,主要校验加签类型是否一致: + *

+ * 1. 如果存在“向前加签”的任务,则不能“向后加签” + * 2. 如果存在“向后加签”的任务,则不能“向前加签” + * + * @param userId 当前用户 ID + * @param reqVO 请求参数,包含任务 ID 和加签类型 + * @return 当前任务 + */ + private TaskEntityImpl validateTaskCanCreateSign(Long userId, BpmTaskSignCreateReqVO reqVO) { + TaskEntityImpl taskEntity = (TaskEntityImpl) validateTask(userId, reqVO.getId()); + // 向前加签和向后加签不能同时存在 + if (taskEntity.getScopeType() != null + && ObjectUtil.notEqual(taskEntity.getScopeType(), reqVO.getType())) { + throw exception(TASK_SIGN_CREATE_TYPE_ERROR, + BpmTaskSignTypeEnum.nameOfType(taskEntity.getScopeType()), BpmTaskSignTypeEnum.nameOfType(reqVO.getType())); + } + + // 同一个 key 的任务,审批人不重复 + List taskList = taskService.createTaskQuery().processInstanceId(taskEntity.getProcessInstanceId()) + .taskDefinitionKey(taskEntity.getTaskDefinitionKey()).list(); + List currentAssigneeList = convertListByFlatMap(taskList, task -> // 需要考虑 owner 的情况,因为向后加签时,它暂时没 assignee 而是 owner + Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner()))); + if (CollUtil.containsAny(currentAssigneeList, reqVO.getUserIds())) { + List userList = adminUserApi.getUserList(CollUtil.intersection(currentAssigneeList, reqVO.getUserIds())).getCheckedData(); + throw exception(TASK_SIGN_CREATE_USER_REPEAT, String.join(",", convertList(userList, AdminUserRespDTO::getNickname))); + } + return taskEntity; + } + + /** + * 创建加签子任务 + * + * @param userIds 被加签的用户 ID + * @param taskEntity 被加签的任务 + */ + private void createSignTaskList(List userIds, TaskEntityImpl taskEntity) { + if (CollUtil.isEmpty(userIds)) { + return; + } + // 创建加签人的新任务,全部基于 taskEntity 为父任务来创建 + for (String addSignId : userIds) { + if (StrUtil.isBlank(addSignId)) { + continue; + } + createSignTask(taskEntity, addSignId); + } + } + + /** + * 创建加签子任务 + * + * @param parentTask 父任务 + * @param assignee 子任务的执行人 + */ + private void createSignTask(TaskEntityImpl parentTask, String assignee) { + // 1. 生成子任务 + TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID()); + BpmTaskConvert.INSTANCE.copyTo(parentTask, task); + + // 2.1 向前加签,设置审批人 + if (BpmTaskSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) { + task.setAssignee(assignee); + // 2.2 向后加签,设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 + } else { + task.setOwner(assignee); + } + // 2.3 保存子任务 + taskService.saveTask(task); + + // 3. 向后前签,设置子任务的状态为 WAIT,因为需要等父任务审批完 + if (BpmTaskSignTypeEnum.AFTER.getType().equals(parentTask.getScopeType())) { + updateTaskStatus(task.getId(), BpmTaskStatusEnum.WAIT.getStatus()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + @SuppressWarnings("DataFlowIssue") + public void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO) { + // 1.1 校验 task 可以被减签 + Task task = validateTaskCanSignDelete(reqVO.getId()); + // 1.2 校验取消人存在 + AdminUserRespDTO cancelUser = null; + if (StrUtil.isNotBlank(task.getAssignee())) { + cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getAssignee())).getCheckedData(); + } + if (cancelUser == null && StrUtil.isNotBlank(task.getOwner())) { + cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())).getCheckedData(); + } + Assert.notNull(cancelUser, "任务中没有所有者和审批人,数据错误"); + + // 2.1 获得子任务列表,包括子任务的子任务 + List childTaskList = getAllChildTaskList(task); + childTaskList.add(task); + // 2.2 更新子任务为已取消 + String cancelReason = StrUtil.format("任务被取消,原因:由于[{}]操作[减签],", cancelUser.getNickname()); + childTaskList.forEach(childTask -> updateTaskStatusAndReason(childTask.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), cancelReason)); + // 2.3 删除任务和所有子任务 + taskService.deleteTasks(convertList(childTaskList, Task::getId)); + + // 3. 记录日志到父任务中。先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录 + AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); + taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(), BpmCommentTypeEnum.SUB_SIGN.getType(), + StrUtil.format(BpmCommentTypeEnum.SUB_SIGN.getComment(), user.getNickname(), cancelUser.getNickname())); + + // 4. 处理当前任务的父任务 + handleParentTaskIfSign(task.getParentTaskId()); + } + + @Override + public void copyTask(Long userId, BpmTaskCopyReqVO reqVO) { + processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getReason(), reqVO.getId()); + } + + /** + * 校验任务是否能被减签 + * + * @param id 任务编号 + * @return 任务信息 + */ + private Task validateTaskCanSignDelete(String id) { + Task task = validateTaskExist(id); + if (task.getParentTaskId() == null) { + throw exception(TASK_SIGN_DELETE_NO_PARENT); + } + Task parentTask = getTask(task.getParentTaskId()); + if (parentTask == null) { + throw exception(TASK_SIGN_DELETE_NO_PARENT); + } + if (BpmTaskSignTypeEnum.of(parentTask.getScopeType()) == null) { + throw exception(TASK_SIGN_DELETE_NO_PARENT); + } + return task; + } + + // ========== Event 事件相关方法 ========== + + @Override + public void processTaskCreated(Task task) { + // 1. 设置为待办中 + Integer status = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); + if (status != null) { + log.error("[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]", task.getId(), status); + return; + } + updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); + + ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (processInstance == null) { + log.error("[processTaskCreated][taskId({}) 没有找到流程实例]", task.getId()); + return; + } + BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService. + getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); + if (processDefinitionInfo == null) { + log.error("[processTaskCreated][processDefinitionId({}) 没有找到流程定义]", processInstance.getProcessDefinitionId()); + return; + } + + // 2. 任务前置通知 + if (ObjUtil.isNotNull(processDefinitionInfo.getTaskBeforeTriggerSetting())){ + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskBeforeTriggerSetting(); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); + } + + // 3. 处理自动通过的情况,例如说:1)无审批人时,是否自动通过、不通过;2)非【人工审核】时,是否自动通过、不通过 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + Integer approveType = BpmnModelUtils.parseApproveType(userTaskElement); + Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(userTaskElement); + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + + /** + * 特殊情况:部分情况下,TransactionSynchronizationManager 注册 afterCommit 监听时,不会被调用,但是 afterCompletion 可以 + * 例如说:第一个 task 就是配置【自动通过】或者【自动拒绝】时 + * 参见 issue 反馈 + */ + @Override + public void afterCompletion(int transactionStatus) { + // 回滚情况,直接返回 + if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) { + return; + } + // 特殊情况:第一个 task 【自动通过】时,第二个任务设置审批人时 transactionStatus 会为 STATUS_UNKNOWN,不知道啥原因 + if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_UNKNOWN) + && getTask(task.getId()) == null) { + return; + } + // 特殊情况一:【人工审核】审批人为空,根据配置是否要自动通过、自动拒绝 + if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.USER.getType())) { + // 如果有审批人、或者拥有人,则说明不满足情况一,不自动通过、不自动拒绝 + if (!ObjectUtil.isAllEmpty(task.getAssignee(), task.getOwner())) { + return; + } + if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.APPROVE.getType())) { + getSelf().approveTask(null, new BpmTaskApproveReqVO() + .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_APPROVE.getReason())); + } else if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.REJECT.getType())) { + getSelf().rejectTask(null, new BpmTaskRejectReqVO() + .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_REJECT.getReason())); + } + // 特殊情况二:【自动审核】审批类型为自动通过、不通过 + } else { + if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType())) { + getSelf().approveTask(null, new BpmTaskApproveReqVO() + .setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_APPROVE.getReason())); + } else if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { + getSelf().rejectTask(null, new BpmTaskRejectReqVO() + .setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_REJECT.getReason())); + } + } + } + + }); + } + + /** + * 重要补充说明:该方法目前主要有两个情况会调用到: + *

+ * 1. 或签场景 + 审批通过:一个或签有多个审批时,如果 A 审批通过,其它或签 B、C 等任务会被 Flowable 自动删除,此时需要通过该方法更新状态为已取消 + * 2. 审批不通过:在 {@link #rejectTask(Long, BpmTaskRejectReqVO)} 不通过时,对于加签的任务,不会被 Flowable 删除,此时需要通过该方法更新状态为已取消 + */ + @Override + public void processTaskCanceled(String taskId) { + Task task = getTask(taskId); + // 1. 可能只是活动,不是任务,所以查询不到 + if (task == null) { + log.error("[updateTaskStatusWhenCanceled][taskId({}) 任务不存在]", taskId); + return; + } + + // 2. 更新 task 状态 + 原因 + Integer status = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); + if (BpmTaskStatusEnum.isEndStatus(status)) { + log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status); + return; + } + updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_SYSTEM.getReason()); + // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 + } + + @Override + @DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人 + public void processTaskAssigned(Task task) { + // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + + /** + * 特殊情况:部分情况下,TransactionSynchronizationManager 注册 afterCommit 监听时,不会被调用,但是 afterCompletion 可以 + * 例如说:第一个 task 就是配置【自动通过】或者【自动拒绝】时 + * 参见 issue 反馈 + */ + @Override + public void afterCompletion(int transactionStatus) { + // 回滚情况,直接返回 + if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) { + return; + } + // 特殊情况:第一个 task 【自动通过】时,第二个任务设置审批人时 transactionStatus 会为 STATUS_UNKNOWN,不知道啥原因 + if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_UNKNOWN) + && getTask(task.getId()) == null) { + return; + } + if (StrUtil.isEmpty(task.getAssignee())) { + log.error("[processTaskAssigned][taskId({}) 没有分配到负责人]", task.getId()); + return; + } + ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (processInstance == null) { + log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId()); + return; + } + + // 自动去重,通过自动审批的方式 TODO @芋艿 驳回的情况得考虑一下;@lesan:驳回后,又自动审批么? + BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId()); + if (processDefinitionInfo == null) { + log.error("[processTaskAssigned][taskId({}) 没有找到流程定义({})]", task.getId(), task.getProcessDefinitionId()); + return; + } + if (processDefinitionInfo.getAutoApprovalType() != null) { + HistoricTaskInstanceQuery sameAssigneeQuery = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(task.getProcessInstanceId()) + .taskAssignee(task.getAssignee()) // 相同审批人 + .taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus()) + .finished(); + if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType()) + && sameAssigneeQuery.count() > 0) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName())); + return; + } + if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) { + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); + if (bpmnModel == null) { + log.error("[processTaskAssigned][taskId({}) 没有找到流程模型({})]", task.getId(), task.getProcessDefinitionId()); + return; + } + List sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingFlows( // 获取所有上一个节点 + BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())), + SequenceFlow::getSourceRef); + if (sameAssigneeQuery.taskDefinitionKeys(sourceTaskIds).count() > 0) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getName())); + return; + } + } + } + + // 获取发起人节点 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); + if (bpmnModel == null) { + log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); + return; + } + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 + // TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 + Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), + String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); + Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), + PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); + if (userTaskElement.getId().equals(START_USER_NODE_ID) + && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 + || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 + && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); + return; + } + // 当不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 + if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID) + && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { + if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { + Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); + + // 情况一:自动跳过 + if (ObjectUtils.equalsAny(assignStartUserHandlerType, + BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason())); + return; + } + // 情况二:转交给部门负责人审批 + if (ObjectUtils.equalsAny(assignStartUserHandlerType, + BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) { + AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData(); + Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId()); + Long deptId = startUser.getDeptIds().stream().findAny().orElse(null); + DeptRespDTO dept = deptId != null ? deptApi.getDept(deptId).getCheckedData() : null; + Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), deptId); + // 找不到部门负责人的情况下,自动审批通过 + // noinspection DataFlowIssue + if (dept.getLeaderUserId() == null) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason())); + return; + } + // 找得到部门负责人的情况下,修改负责人 + if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) { + getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO() + .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId()) + .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason())); + return; + } + // 如果部门负责人是自己,还是自己审批吧~ + } + } + } + // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 + FlowableUtils.execute(processInstance.getTenantId(), () -> { + AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData(); + messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); + }); + } + + }); + } + + @Override + public void processTaskCompleted(Task task) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (processInstance == null) { + log.error("[processTaskCompleted][taskId({}) 没有找到流程实例]", task.getId()); + return; + } + BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService. + getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); + if (processDefinitionInfo == null) { + log.error("[processTaskCompleted][processDefinitionId({}) 没有找到流程定义]", processInstance.getProcessDefinitionId()); + return; + } + + // 任务后置通知 + if (ObjUtil.isNotNull(processDefinitionInfo.getTaskAfterTriggerSetting())){ + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskAfterTriggerSetting(); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + if (processInstance == null) { + log.error("[processTaskTimeout][processInstanceId({}) 没有找到流程实例]", processInstanceId); + return; + } + List taskList = getRunningTaskListByProcessInstanceId(processInstanceId, true, taskDefineKey); + // TODO 优化:未来需要考虑加签的情况 + if (CollUtil.isEmpty(taskList)) { + log.error("[processTaskTimeout][processInstanceId({}) 定义Key({}) 没有找到任务]", processInstanceId, taskDefineKey); + return; + } + + taskList.forEach(task -> FlowableUtils.execute(task.getTenantId(), () -> { + // 情况一:自动提醒 + if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType())) { + messageService.sendMessageWhenTaskTimeout(new BpmMessageSendWhenTaskTimeoutReqDTO() + .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName()) + .setTaskId(task.getId()).setTaskName(task.getName()).setAssigneeUserId(Long.parseLong(task.getAssignee()))); + return; + } + + // 情况二:自动同意 + if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.APPROVE.getType())) { + approveTask(Long.parseLong(task.getAssignee()), + new BpmTaskApproveReqVO().setId(task.getId()).setReason(BpmReasonEnum.TIMEOUT_APPROVE.getReason())); + return; + } + + // 情况三:自动拒绝 + if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.REJECT.getType())) { + rejectTask(Long.parseLong(task.getAssignee()), + new BpmTaskRejectReqVO().setId(task.getId()).setReason(BpmReasonEnum.REJECT_TASK.getReason())); + } + })); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void processChildProcessTimeout(String processInstanceId, String taskDefineKey) { + List activityInstances = runtimeService.createActivityInstanceQuery() + .processInstanceId(processInstanceId) + .activityId(taskDefineKey).list(); + activityInstances.forEach(activityInstance -> FlowableUtils.execute(activityInstance.getTenantId(), + () -> moveTaskToEnd(activityInstance.getCalledProcessInstanceId(), BpmReasonEnum.TIMEOUT_APPROVE.getReason()))); + } + + @Override + public void triggerTask(String processInstanceId, String taskDefineKey) { + Execution execution = runtimeService.createExecutionQuery() + .processInstanceId(processInstanceId) + .activityId(taskDefineKey) + .singleResult(); + if (execution == null) { + log.error("[triggerTask][processInstanceId({}) activityId({}) 没有找到执行活动]", processInstanceId, taskDefineKey); + return; + } + + // 若存在直接触发接收任务,执行后续节点 + FlowableUtils.execute(execution.getTenantId(), + () -> runtimeService.trigger(execution.getId())); + } + + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private BpmTaskServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java new file mode 100644 index 0000000..e458af4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java @@ -0,0 +1,96 @@ +package com.zt.plat.module.bpm.service.task.listener; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.definition.BpmChildProcessStartUserEmptyTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmChildProcessStartUserTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.ExecutionListener; +import org.flowable.engine.impl.el.FixedValue; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * BPM 子流程监听器:设置流程的发起人 + * + * @author Lesan + */ +@Component +@Slf4j +public class BpmCallActivityListener implements ExecutionListener { + + public static final String DELEGATE_EXPRESSION = "${bpmCallActivityListener}"; + + @Setter + private FixedValue listenerConfig; + + @Resource + private BpmProcessDefinitionService processDefinitionService; + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public void notify(DelegateExecution execution) { + String expressionText = listenerConfig.getExpressionText(); + Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); + BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject( + expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class); + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getRootProcessInstanceId()); + + // 1. 当发起人来源为主流程发起人时,并兜底 startUserSetting 为空时 + if (startUserSetting == null + || startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) { + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); + return; + } + + // 2. 当发起人来源为表单时 + if (startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) { + String formFieldValue = MapUtil.getStr(processInstance.getProcessVariables(), startUserSetting.getFormField()); + // 2.1 当表单值为空时 + if (StrUtil.isEmpty(formFieldValue)) { + // 2.1.1 来自主流程发起人 + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) { + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); + return; + } + // 2.1.2 来自子流程管理员 + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) { + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId()); + List managerUserIds = processDefinition.getManagerUserIds(); + FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); + return; + } + // 2.1.3 来自主流程管理员 + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) { + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); + List managerUserIds = processDefinition.getManagerUserIds(); + FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); + return; + } + } + // 2.2 使用表单值,并兜底字符串转 Long 失败时使用主流程发起人 + try { + FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); + } catch (Exception e) { + log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]", + DELEGATE_EXPRESSION, formFieldValue); + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); + } + } + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java new file mode 100644 index 0000000..2102560 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java @@ -0,0 +1,59 @@ +package com.zt.plat.module.bpm.service.task.listener; + +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.TaskListener; +import org.flowable.engine.impl.el.FixedValue; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.service.delegate.DelegateTask; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig; + +// TODO @芋艿:可能会想换个包地址 +/** + * BPM 用户任务通用监听器 + * + * @author Lesan + */ +@Component +@Slf4j +@Scope("prototype") +public class BpmUserTaskListener implements TaskListener { + + public static final String DELEGATE_EXPRESSION = "${bpmUserTaskListener}"; + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Setter + private FixedValue listenerConfig; + + @Override + public void notify(DelegateTask delegateTask) { + // 1. 获取所需基础信息 + ProcessInstance processInstance = processInstanceService.getProcessInstance(delegateTask.getProcessInstanceId()); + BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig); + + // 2. 发起请求 + // TODO @芋艿:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去; + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getProcessInstanceId())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getAssignee())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskDefinitionKey") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getTaskDefinitionKey())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getId())); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + listenerHandler.getPath(), listenerHandler.getHeader(), listenerHandler.getBody(), false, null); + + // 3. 是否需要后续操作?TODO 芋艿:待定! + } +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java new file mode 100644 index 0000000..02fb2d5 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java @@ -0,0 +1,30 @@ +package com.zt.plat.module.bpm.service.task.trigger; + +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; + +// TODO @芋艿:可能会想换个包地址 +/** + * BPM 触发器接口 + *

+ * 处理不同的动作 + * + * @author jason + */ +public interface BpmTrigger { + + /** + * 对应触发器类型 + * + * @return 触发器类型 + */ + BpmTriggerTypeEnum getType(); + + /** + * 触发器执行 + * + * @param processInstanceId 流程实例编号 + * @param param 触发器参数 + */ + void execute(String processInstanceId, String param); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java new file mode 100644 index 0000000..60d3073 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.bpm.service.task.trigger.form; + +import cn.hutool.core.collection.CollUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * BPM 删除流程表单数据触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmFormDeleteTrigger implements BpmTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.FORM_DELETE; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析删除流程表单数据配置 + List settings = JsonUtils.parseObject(param, new TypeReference<>() {}); + if (CollUtil.isEmpty(settings)) { + log.error("[execute][流程({}) 删除流程表单数据触发器配置为空]", processInstanceId); + return; + } + + // 2. 获取流程变量 + Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); + + // 3.1 获取需要删除的表单字段 + Set deleteFields = new HashSet<>(); + settings.forEach(setting -> { + if (CollUtil.isEmpty(setting.getDeleteFields())) { + return; + } + // 配置了条件,判断条件是否满足 + boolean isFieldDeletedNeeded = true; + if (setting.getConditionType() != null) { + String conditionExpression = SimpleModelUtils.buildConditionExpression( + setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups()); + isFieldDeletedNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); + } + if (isFieldDeletedNeeded) { + deleteFields.addAll(setting.getDeleteFields()); + } + }); + + // 3.2 删除流程变量 + if (CollUtil.isNotEmpty(deleteFields)) { + processInstanceService.removeProcessInstanceVariables(processInstanceId, deleteFields); + } + } +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java new file mode 100644 index 0000000..79a48d9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java @@ -0,0 +1,66 @@ +package com.zt.plat.module.bpm.service.task.trigger.form; + +import cn.hutool.core.collection.CollUtil; +import com.fasterxml.jackson.core.type.TypeReference; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.FormTriggerSetting; +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * BPM 更新流程表单触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmFormUpdateTrigger implements BpmTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.FORM_UPDATE; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析更新流程表单配置 + List settings = JsonUtils.parseObject(param, new TypeReference<>() {}); + if (CollUtil.isEmpty(settings)) { + log.error("[execute][流程({}) 更新流程表单触发器配置为空]", processInstanceId); + return; + } + + // 2. 获取流程变量 + Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); + + // 3. 更新流程变量 + for (FormTriggerSetting setting : settings) { + if (CollUtil.isEmpty(setting.getUpdateFormFields())) { + continue; + } + // 配置了条件,判断条件是否满足 + boolean isFormUpdateNeeded = true; + if (setting.getConditionType() != null) { + String conditionExpression = SimpleModelUtils.buildConditionExpression( + setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups()); + isFormUpdateNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); + } + // 更新流程表单 + if (isFormUpdateNeeded) { + processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields()); + } + } + } +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java new file mode 100644 index 0000000..676b260 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java @@ -0,0 +1,14 @@ +package com.zt.plat.module.bpm.service.task.trigger.http; + +import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; +import lombok.extern.slf4j.Slf4j; + +/** + * BPM 发送 HTTP 请求触发器抽象类 + * + * @author jason + */ +@Slf4j +public abstract class BpmAbstractHttpRequestTrigger implements BpmTrigger { + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java new file mode 100644 index 0000000..76292d8 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java @@ -0,0 +1,50 @@ +package com.zt.plat.module.bpm.service.task.trigger.http; + +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Component; + +/** + * BPM HTTP 回调触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.HTTP_CALLBACK; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析 http 请求配置 + BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, + BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting.class); + if (setting == null) { + log.error("[execute][流程({}) HTTP 回调触发器配置为空]", processInstanceId); + return; + } + + // 2. 发起请求 + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + setting.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam() + .setKey("taskDefineKey") // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(setting.getCallbackTaskDefineKey())); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), setting.getHeader(), setting.getBody(), false, null); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java new file mode 100644 index 0000000..f612660 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.service.task.trigger.http; + +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting; +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Component; + +/** + * BPM 发送同步 HTTP 请求触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.HTTP_REQUEST; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析 http 请求配置 + HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, HttpRequestTriggerSetting.class); + if (setting == null) { + log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId); + return; + } + + // 2. 发起请求 + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java b/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java new file mode 100644 index 0000000..e7dc5bf --- /dev/null +++ b/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java @@ -0,0 +1,530 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package liquibase.database.core; + +import liquibase.CatalogAndSchema; +import liquibase.GlobalConfiguration; +import liquibase.Scope; +import liquibase.database.AbstractJdbcDatabase; +import liquibase.database.DatabaseConnection; +import liquibase.database.OfflineConnection; +import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.DatabaseException; +import liquibase.exception.UnexpectedLiquibaseException; +import liquibase.exception.ValidationErrors; +import liquibase.executor.ExecutorService; +import liquibase.statement.DatabaseFunction; +import liquibase.statement.SequenceCurrentValueFunction; +import liquibase.statement.SequenceNextValueFunction; +import liquibase.statement.UniqueConstraint; +import liquibase.statement.core.RawCallStatement; +import liquibase.statement.core.RawParameterizedSqlStatement; +import liquibase.structure.DatabaseObject; +import liquibase.structure.core.*; +import liquibase.util.JdbcUtil; +import liquibase.util.StringUtil; +import org.apache.commons.lang3.StringUtils; + +import java.lang.reflect.Method; +import java.sql.*; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class DmDatabase extends AbstractJdbcDatabase { + private static final String PROXY_USER_REGEX = ".*(?:thin|oci)\\:(.+)/@.*"; + public static final Pattern PROXY_USER_PATTERN = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*"); + private static final String VERSION_REGEX = "(\\d+)\\.(\\d+)\\..*"; + private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\..*"); + public static final String PRODUCT_NAME = "DM DBMS"; + private static final ResourceBundle coreBundle = ResourceBundle.getBundle("liquibase/i18n/liquibase-core"); + protected final int SHORT_IDENTIFIERS_LENGTH = 30; + protected final int LONG_IDENTIFIERS_LEGNTH = 128; + public static final int ORACLE_12C_MAJOR_VERSION = 12; + public static final int ORACLE_23C_MAJOR_VERSION = 23; + private final Set reservedWords = new HashSet(); + private Set userDefinedTypes; + private Map savedSessionNlsSettings; + private Boolean canAccessDbaRecycleBin; + private Integer databaseMajorVersion; + private Integer databaseMinorVersion; + + public DmDatabase() { + super.unquotedObjectsAreUppercased = true; + super.setCurrentDateTimeFunction("SYSTIMESTAMP"); + this.dateFunctions.add(new DatabaseFunction("SYSDATE")); + this.dateFunctions.add(new DatabaseFunction("SYSTIMESTAMP")); + this.dateFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP")); + super.sequenceNextValueFunction = "%s.nextval"; + super.sequenceCurrentValueFunction = "%s.currval"; + } + + public int getPriority() { + return 1; + } + + private void tryProxySession(String url, Connection con) { + Matcher m = PROXY_USER_PATTERN.matcher(url); + if (m.matches()) { + Properties props = new Properties(); + props.put("PROXY_USER_NAME", m.group(1)); + + try { + Method method = con.getClass().getMethod("openProxySession", Integer.TYPE, Properties.class); + method.setAccessible(true); + method.invoke(con, 1, props); + } catch (Exception e) { + Scope.getCurrentScope().getLog(this.getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage()); + return; + } + + try { + Method method = con.getClass().getMethod("isProxySession"); + method.setAccessible(true); + boolean b = (Boolean)method.invoke(con); + if (!b) { + Scope.getCurrentScope().getLog(this.getClass()).info("Proxy session not established on OracleDatabase: "); + } + } catch (Exception e) { + Scope.getCurrentScope().getLog(this.getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage()); + } + } + + } + + public void setConnection(DatabaseConnection conn) { + this.reservedWords.addAll(Arrays.asList("GROUP", "USER", "SESSION", "PASSWORD", "RESOURCE", "START", "SIZE", "UID", "DESC", "ORDER")); + Connection sqlConn = null; + if (!(conn instanceof OfflineConnection)) { + try { + if (conn instanceof JdbcConnection) { + sqlConn = ((JdbcConnection)conn).getWrappedConnection(); + } + } catch (Exception e) { + throw new UnexpectedLiquibaseException(e); + } + + if (sqlConn != null) { + this.tryProxySession(conn.getURL(), sqlConn); + + try { + this.reservedWords.addAll(Arrays.asList(sqlConn.getMetaData().getSQLKeywords().toUpperCase().split(",\\s*"))); + } catch (SQLException e) { + Scope.getCurrentScope().getLog(this.getClass()).info("Could get sql keywords on OracleDatabase: " + e.getMessage()); + } + + try { + Method method = sqlConn.getClass().getMethod("setRemarksReporting", Boolean.TYPE); + method.setAccessible(true); + method.invoke(sqlConn, true); + } catch (Exception e) { + Scope.getCurrentScope().getLog(this.getClass()).info("Could not set remarks reporting on OracleDatabase: " + e.getMessage()); + } + + CallableStatement statement = null; + + try { + statement = sqlConn.prepareCall("{call DBMS_UTILITY.DB_VERSION(?,?)}"); + statement.registerOutParameter(1, 12); + statement.registerOutParameter(2, 12); + statement.execute(); + String compatibleVersion = statement.getString(2); + if (compatibleVersion != null) { + Matcher majorVersionMatcher = VERSION_PATTERN.matcher(compatibleVersion); + if (majorVersionMatcher.matches()) { + this.databaseMajorVersion = Integer.valueOf(majorVersionMatcher.group(1)); + this.databaseMinorVersion = Integer.valueOf(majorVersionMatcher.group(2)); + } + } + } catch (SQLException e) { + String message = "Cannot read from DBMS_UTILITY.DB_VERSION: " + e.getMessage(); + Scope.getCurrentScope().getLog(this.getClass()).info("Could not set check compatibility mode on OracleDatabase, assuming not running in any sort of compatibility mode: " + message); + } finally { + JdbcUtil.closeStatement(statement); + } + + if (GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue() != null) { + int timeoutValue = (Integer)GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue(); + Scope.getCurrentScope().getLog(this.getClass()).fine("Setting DDL_LOCK_TIMEOUT value to " + timeoutValue); + String sql = "ALTER SESSION SET DDL_LOCK_TIMEOUT=" + timeoutValue; + PreparedStatement ddlLockTimeoutStatement = null; + + try { + ddlLockTimeoutStatement = sqlConn.prepareStatement(sql); + ddlLockTimeoutStatement.execute(); + } catch (SQLException sqle) { + Scope.getCurrentScope().getUI().sendErrorMessage("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + sqle.getMessage(), sqle); + Scope.getCurrentScope().getLog(this.getClass()).warning("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + sqle.getMessage(), sqle); + } finally { + JdbcUtil.closeStatement(ddlLockTimeoutStatement); + } + } + } + } + + super.setConnection(conn); + } + + public String getShortName() { + return "dm"; + } + + protected String getDefaultDatabaseProductName() { + return PRODUCT_NAME; + } + + public int getDatabaseMajorVersion() throws DatabaseException { + return this.databaseMajorVersion == null ? super.getDatabaseMajorVersion() : this.databaseMajorVersion; + } + + public int getDatabaseMinorVersion() throws DatabaseException { + return this.databaseMinorVersion == null ? super.getDatabaseMinorVersion() : this.databaseMinorVersion; + } + + public Integer getDefaultPort() { + return 5236; + } + + public String getJdbcCatalogName(CatalogAndSchema schema) { + return null; + } + + public String getJdbcSchemaName(CatalogAndSchema schema) { + return this.correctObjectName(schema.getCatalogName() == null ? schema.getSchemaName() : schema.getCatalogName(), Schema.class); + } + + protected String getAutoIncrementClause(String generationType, Boolean defaultOnNull) { + if (StringUtil.isEmpty(generationType)) { + return super.getAutoIncrementClause(); + } else { + String autoIncrementClause = "GENERATED %s AS IDENTITY"; + String generationStrategy = generationType; + if (Boolean.TRUE.equals(defaultOnNull) && generationType.toUpperCase().equals("BY DEFAULT")) { + generationStrategy = generationType + " ON NULL"; + } + + return String.format(autoIncrementClause, generationStrategy); + } + } + + public String generatePrimaryKeyName(String tableName) { + return tableName.length() > 27 ? "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27) : "PK_" + tableName.toUpperCase(Locale.US); + } + + public boolean supportsInitiallyDeferrableColumns() { + return true; + } + + public boolean isReservedWord(String objectName) { + return this.reservedWords.contains(objectName.toUpperCase()); + } + + public boolean supportsSequences() { + return true; + } + + public boolean supports(Class object) { + return Schema.class.isAssignableFrom(object) ? false : super.supports(object); + } + + public boolean supportsSchemas() { + return false; + } + + protected String getConnectionCatalogName() throws DatabaseException { + if (this.getConnection() instanceof OfflineConnection) { + return this.getConnection().getCatalog(); + } else if (!(this.getConnection() instanceof JdbcConnection)) { + return this.defaultCatalogName; + } else { + try { + return (String)((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForObject(new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"), String.class); + } catch (Exception e) { + Scope.getCurrentScope().getLog(this.getClass()).info("Error getting default schema", e); + return null; + } + } + } + + public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException { + return "oracle".equalsIgnoreCase(conn.getDatabaseProductName()); + } + + public String getDefaultDriver(String url) { + return url.startsWith("jdbc:dm") ? "dm.jdbc.driver.DmDriver" : null; + } + + public String getDefaultCatalogName() { + String defaultCatalogName = super.getDefaultCatalogName(); + if (Boolean.TRUE.equals(GlobalConfiguration.PRESERVE_SCHEMA_CASE.getCurrentValue())) { + return defaultCatalogName; + } else { + return defaultCatalogName == null ? null : defaultCatalogName.toUpperCase(Locale.US); + } + } + + public String getDateLiteral(String isoDate) { + String normalLiteral = super.getDateLiteral(isoDate); + if (this.isDateOnly(isoDate)) { + return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD')"; + } else if (this.isTimeOnly(isoDate)) { + return "TO_DATE(" + normalLiteral + ", 'HH24:MI:SS')"; + } else if (this.isTimestamp(isoDate)) { + return "TO_TIMESTAMP(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS.FF')"; + } else if (this.isDateTime(isoDate)) { + int seppos = normalLiteral.lastIndexOf(46); + if (seppos != -1) { + normalLiteral = normalLiteral.substring(0, seppos) + "'"; + } + + return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS')"; + } else { + return "UNSUPPORTED:" + isoDate; + } + } + + public boolean isSystemObject(DatabaseObject example) { + if (example == null) { + return false; + } else if (this.isLiquibaseObject(example)) { + return false; + } else { + if (example instanceof Schema) { + if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) { + return true; + } + + if ("SYSTEM".equals(example.getSchema().getCatalogName()) || "SYS".equals(example.getSchema().getCatalogName()) || "CTXSYS".equals(example.getSchema().getCatalogName()) || "XDB".equals(example.getSchema().getCatalogName())) { + return true; + } + } else if (this.isSystemObject(example.getSchema())) { + return true; + } + + if (example instanceof Catalog) { + if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) { + return true; + } + } else if (example.getName() != null) { + if (example.getName().startsWith("BIN$")) { + boolean filteredInOriginalQuery = this.canAccessDbaRecycleBin(); + if (!filteredInOriginalQuery) { + filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName()); + } + + if (!filteredInOriginalQuery) { + return true; + } + + return !(example instanceof PrimaryKey) && !(example instanceof Index) && !(example instanceof UniqueConstraint); + } + + if (example.getName().startsWith("AQ$")) { + return true; + } + + if (example.getName().startsWith("DR$")) { + return true; + } + + if (example.getName().startsWith("SYS_IOT_OVER")) { + return true; + } + + if ((example.getName().startsWith("MDRT_") || example.getName().startsWith("MDRS_")) && example.getName().endsWith("$")) { + return true; + } + + if (example.getName().startsWith("MLOG$_")) { + return true; + } + + if (example.getName().startsWith("RUPD$_")) { + return true; + } + + if (example.getName().startsWith("WM$_")) { + return true; + } + + if ("CREATE$JAVA$LOB$TABLE".equals(example.getName())) { + return true; + } + + if ("JAVA$CLASS$MD5$TABLE".equals(example.getName())) { + return true; + } + + if (example.getName().startsWith("ISEQ$$_")) { + return true; + } + + if (example.getName().startsWith("USLOG$")) { + return true; + } + + if (example.getName().startsWith("SYS_FBA")) { + return true; + } + } + + return super.isSystemObject(example); + } + } + + public boolean supportsTablespaces() { + return true; + } + + public boolean supportsAutoIncrement() { + boolean isAutoIncrementSupported = false; + + try { + if (this.getDatabaseMajorVersion() >= 12) { + isAutoIncrementSupported = true; + } + } catch (DatabaseException var3) { + isAutoIncrementSupported = false; + } + + return isAutoIncrementSupported; + } + + public boolean supportsRestrictForeignKeys() { + return false; + } + + public int getDataTypeMaxParameters(String dataTypeName) { + if ("BINARY_FLOAT".equals(dataTypeName.toUpperCase())) { + return 0; + } else { + return "BINARY_DOUBLE".equals(dataTypeName.toUpperCase()) ? 0 : super.getDataTypeMaxParameters(dataTypeName); + } + } + + public String getSystemTableWhereClause(String tableNameColumn) { + List clauses = new ArrayList(Arrays.asList("BIN$", "AQ$", "DR$", "SYS_IOT_OVER", "MLOG$_", "RUPD$_", "WM$_", "ISEQ$$_", "USLOG$", "SYS_FBA")); + clauses.replaceAll((s) -> tableNameColumn + " NOT LIKE '" + s + "%'"); + return "(" + StringUtil.join(clauses, " AND ") + ")"; + } + + public boolean jdbcCallsCatalogsSchemas() { + return true; + } + + public Set getUserDefinedTypes() { + if (this.userDefinedTypes == null) { + this.userDefinedTypes = new HashSet(); + if (this.getConnection() != null && !(this.getConnection() instanceof OfflineConnection)) { + try { + try { + this.userDefinedTypes.addAll(((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForList(new RawParameterizedSqlStatement("SELECT DISTINCT TYPE_NAME FROM ALL_TYPES"), String.class)); + } catch (DatabaseException var2) { + this.userDefinedTypes.addAll(((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForList(new RawParameterizedSqlStatement("SELECT TYPE_NAME FROM USER_TYPES"), String.class)); + } + } catch (DatabaseException var3) { + } + } + } + + return this.userDefinedTypes; + } + + public String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) { + if (databaseFunction != null && "current_timestamp".equalsIgnoreCase(databaseFunction.toString())) { + return databaseFunction.toString(); + } else if (!(databaseFunction instanceof SequenceNextValueFunction) && !(databaseFunction instanceof SequenceCurrentValueFunction)) { + return super.generateDatabaseFunctionValue(databaseFunction); + } else { + String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction); + return quotedSeq.replaceFirst("\"([^.\"]+)\\.([^.\"]+)\"", "\"$1\".\"$2\""); + } + } + + public ValidationErrors validate() { + ValidationErrors errors = super.validate(); + DatabaseConnection connection = this.getConnection(); + if (connection != null && !(connection instanceof OfflineConnection)) { + if (!this.canAccessDbaRecycleBin()) { + errors.addWarning(this.getDbaRecycleBinWarning()); + } + + return errors; + } else { + Scope.getCurrentScope().getLog(this.getClass()).info("Cannot validate offline database"); + return errors; + } + } + + public String getDbaRecycleBinWarning() { + return "Liquibase needs to access the DBA_RECYCLEBIN table so we can automatically handle the case where constraints are deleted and restored. Since Oracle doesn't properly restore the original table names referenced in the constraint, we use the information from the DBA_RECYCLEBIN to automatically correct this issue.\n\nThe user you used to connect to the database (" + this.getConnection().getConnectionUserName() + ") needs to have \"SELECT ON SYS.DBA_RECYCLEBIN\" permissions set before we can perform this operation. Please run the following SQL to set the appropriate permissions, and try running the command again.\n\n GRANT SELECT ON SYS.DBA_RECYCLEBIN TO " + this.getConnection().getConnectionUserName() + ";"; + } + + public boolean canAccessDbaRecycleBin() { + if (this.canAccessDbaRecycleBin == null) { + DatabaseConnection connection = this.getConnection(); + if (connection == null || connection instanceof OfflineConnection) { + return false; + } + + Statement statement = null; + + try { + statement = ((JdbcConnection)connection).createStatement(); + ResultSet resultSet = statement.executeQuery("select 1 from dba_recyclebin where 0=1"); + resultSet.close(); + this.canAccessDbaRecycleBin = true; + } catch (Exception var7) { + if (var7 instanceof SQLException && var7.getMessage().startsWith("ORA-00942")) { + this.canAccessDbaRecycleBin = false; + } else { + Scope.getCurrentScope().getLog(this.getClass()).warning("Cannot check dba_recyclebin access", var7); + this.canAccessDbaRecycleBin = false; + } + } finally { + JdbcUtil.close((ResultSet)null, statement); + } + } + + return this.canAccessDbaRecycleBin; + } + + public boolean supportsNotNullConstraintNames() { + return true; + } + + public boolean isValidOracleIdentifier(String identifier, Class type) { + if (identifier != null && identifier.length() >= 1) { + if (!identifier.matches("^(i?)[A-Z][A-Z0-9\\$\\_\\#]*$")) { + return false; + } else { + return identifier.length() <= 128; + } + } else { + return false; + } + } + + public int getIdentifierMaximumLength() { + try { + if (this.getDatabaseMajorVersion() < 12) { + return 30; + } else { + return this.getDatabaseMajorVersion() == 12 && this.getDatabaseMinorVersion() <= 1 ? 30 : 128; + } + } catch (DatabaseException ex) { + throw new UnexpectedLiquibaseException("Cannot determine the Oracle database version number", ex); + } + } + + public boolean supportsDatabaseChangeLogHistory() { + return true; + } + + public String correctObjectName(String objectName, Class objectType) { + return objectType.equals(Column.class) && StringUtils.startsWithIgnoreCase(objectName, "int") ? "NUMBER(*, 0)" : super.correctObjectName(objectName, objectType); + } +} diff --git a/zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java b/zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java new file mode 100644 index 0000000..b2c55f0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java @@ -0,0 +1,148 @@ +package liquibase.datatype.core; + +import liquibase.change.core.LoadDataChange; +import liquibase.database.Database; +import liquibase.database.core.*; +import liquibase.datatype.DataTypeInfo; +import liquibase.datatype.DatabaseDataType; +import liquibase.datatype.LiquibaseDataType; +import liquibase.exception.UnexpectedLiquibaseException; +import liquibase.statement.DatabaseFunction; +import liquibase.util.StringUtil; + +import java.util.Locale; + +@DataTypeInfo(name = "boolean", aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, minParameters = 0, maxParameters = 0, priority = LiquibaseDataType.PRIORITY_DEFAULT) +public class BooleanType extends LiquibaseDataType { + + @Override + public DatabaseDataType toDatabaseDataType(Database database) { + String originalDefinition = StringUtil.trimToEmpty(getRawDefinition()); +// if ((database instanceof Firebird3Database)) { +// return new DatabaseDataType("BOOLEAN"); +// } + + if ((database instanceof AbstractDb2Database) || (database instanceof FirebirdDatabase)) { + return new DatabaseDataType("SMALLINT"); + } else if (database instanceof MSSQLDatabase) { + return new DatabaseDataType(database.escapeDataTypeName("bit")); + } else if (database instanceof MySQLDatabase) { + if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) { + return new DatabaseDataType("BIT", getParameters()); + } + return new DatabaseDataType("BIT", 1); + } else if (database instanceof OracleDatabase) { + return new DatabaseDataType("NUMBER", 1); + } else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase)) { + return new DatabaseDataType("BIT"); + } else if (database instanceof DerbyDatabase) { + if (((DerbyDatabase) database).supportsBooleanDataType()) { + return new DatabaseDataType("BOOLEAN"); + } else { + return new DatabaseDataType("SMALLINT"); + } + } else if (database.getClass().isAssignableFrom(DB2Database.class)) { + if (((DB2Database) database).supportsBooleanDataType()) + return new DatabaseDataType("BOOLEAN"); + else + return new DatabaseDataType("SMALLINT"); + } else if (database instanceof HsqlDatabase) { + return new DatabaseDataType("BOOLEAN"); + } else if (database instanceof PostgresDatabase) { + if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) { + return new DatabaseDataType("BIT", getParameters()); + } + } else if(database instanceof DmDatabase) { + return new DatabaseDataType("bit"); + } + + return super.toDatabaseDataType(database); + } + + @Override + public String objectToSql(Object value, Database database) { + if ((value == null) || "null".equals(value.toString().toLowerCase(Locale.US))) { + return null; + } + + String returnValue; + if (value instanceof String) { + value = ((String) value).replaceAll("'", ""); + if ("true".equals(((String) value).toLowerCase(Locale.US)) || "1".equals(value) || "b'1'".equals(((String) value).toLowerCase(Locale.US)) || "t".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) { + returnValue = this.getTrueBooleanValue(database); + } else if ("false".equals(((String) value).toLowerCase(Locale.US)) || "0".equals(value) || "b'0'".equals( + ((String) value).toLowerCase(Locale.US)) || "f".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) { + returnValue = this.getFalseBooleanValue(database); + } else { + throw new UnexpectedLiquibaseException("Unknown boolean value: " + value); + } + } else if (value instanceof Long) { + if (Long.valueOf(1).equals(value)) { + returnValue = this.getTrueBooleanValue(database); + } else { + returnValue = this.getFalseBooleanValue(database); + } + } else if (value instanceof Number) { + if (value.equals(1) || "1".equals(value.toString()) || "1.0".equals(value.toString())) { + returnValue = this.getTrueBooleanValue(database); + } else { + returnValue = this.getFalseBooleanValue(database); + } + } else if (value instanceof DatabaseFunction) { + return value.toString(); + } else if (value instanceof Boolean) { + if (((Boolean) value)) { + returnValue = this.getTrueBooleanValue(database); + } else { + returnValue = this.getFalseBooleanValue(database); + } + } else { + throw new UnexpectedLiquibaseException("Cannot convert type " + value.getClass() + " to a boolean value"); + } + + return returnValue; + } + + protected boolean isNumericBoolean(Database database) { + if (database instanceof DerbyDatabase) { + return !((DerbyDatabase) database).supportsBooleanDataType(); + } else if (database.getClass().isAssignableFrom(DB2Database.class)) { + return !((DB2Database) database).supportsBooleanDataType(); + } + return (database instanceof Db2zDatabase) || (database instanceof DB2Database) || (database instanceof FirebirdDatabase) || (database instanceof + MSSQLDatabase) || (database instanceof MySQLDatabase) || (database instanceof OracleDatabase) || + (database instanceof SQLiteDatabase) || (database instanceof SybaseASADatabase) || (database instanceof + SybaseDatabase) || (database instanceof DmDatabase); + } + + /** + * The database-specific value to use for "false" "boolean" columns. + */ + public String getFalseBooleanValue(Database database) { + if (isNumericBoolean(database)) { + return "0"; + } + if (database instanceof InformixDatabase) { + return "'f'"; + } + return "FALSE"; + } + + /** + * The database-specific value to use for "true" "boolean" columns. + */ + public String getTrueBooleanValue(Database database) { + if (isNumericBoolean(database)) { + return "1"; + } + if (database instanceof InformixDatabase) { + return "'t'"; + } + return "TRUE"; + } + + @Override + public LoadDataChange.LOAD_DATA_TYPE getLoadTypeName() { + return LoadDataChange.LOAD_DATA_TYPE.BOOLEAN; + } +} diff --git a/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java b/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java new file mode 100644 index 0000000..790e65e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java @@ -0,0 +1,2038 @@ +/* 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.flowable.common.engine.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.builder.xml.XMLConfigBuilder; +import org.apache.ibatis.builder.xml.XMLMapperBuilder; +import org.apache.ibatis.datasource.pooled.PooledDataSource; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory; +import org.apache.ibatis.transaction.TransactionFactory; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; +import org.apache.ibatis.transaction.managed.ManagedTransactionFactory; +import org.apache.ibatis.type.*; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.common.engine.api.engine.EngineLifecycleListener; +import org.flowable.common.engine.impl.agenda.AgendaOperationExecutionListener; +import org.flowable.common.engine.impl.agenda.AgendaOperationRunner; +import org.flowable.common.engine.impl.cfg.CommandExecutorImpl; +import org.flowable.common.engine.impl.cfg.IdGenerator; +import org.flowable.common.engine.impl.cfg.TransactionContextFactory; +import org.flowable.common.engine.impl.cfg.standalone.StandaloneMybatisTransactionContextFactory; +import org.flowable.common.engine.impl.db.*; +import org.flowable.common.engine.impl.event.EventDispatchAction; +import org.flowable.common.engine.impl.event.FlowableEventDispatcherImpl; +import org.flowable.common.engine.impl.interceptor.*; +import org.flowable.common.engine.impl.lock.LockManager; +import org.flowable.common.engine.impl.lock.LockManagerImpl; +import org.flowable.common.engine.impl.logging.LoggingListener; +import org.flowable.common.engine.impl.logging.LoggingSession; +import org.flowable.common.engine.impl.logging.LoggingSessionFactory; +import org.flowable.common.engine.impl.persistence.GenericManagerFactory; +import org.flowable.common.engine.impl.persistence.StrongUuidGenerator; +import org.flowable.common.engine.impl.persistence.cache.EntityCache; +import org.flowable.common.engine.impl.persistence.cache.EntityCacheImpl; +import org.flowable.common.engine.impl.persistence.entity.*; +import org.flowable.common.engine.impl.persistence.entity.data.ByteArrayDataManager; +import org.flowable.common.engine.impl.persistence.entity.data.PropertyDataManager; +import org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisByteArrayDataManager; +import org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisPropertyDataManager; +import org.flowable.common.engine.impl.runtime.Clock; +import org.flowable.common.engine.impl.service.CommonEngineServiceImpl; +import org.flowable.common.engine.impl.util.DefaultClockImpl; +import org.flowable.common.engine.impl.util.IoUtil; +import org.flowable.common.engine.impl.util.ReflectUtil; +import org.flowable.eventregistry.api.EventRegistryEventConsumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.naming.InitialContext; +import javax.sql.DataSource; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.sql.*; +import java.time.Duration; +import java.util.*; + +public abstract class AbstractEngineConfiguration { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + /** The tenant id indicating 'no tenant' */ + public static final String NO_TENANT_ID = ""; + + /** + * Checks the version of the DB schema against the library when the form engine is being created and throws an exception if the versions don't match. + */ + public static final String DB_SCHEMA_UPDATE_FALSE = "false"; + public static final String DB_SCHEMA_UPDATE_CREATE = "create"; + public static final String DB_SCHEMA_UPDATE_CREATE_DROP = "create-drop"; + + /** + * Creates the schema when the form engine is being created and drops the schema when the form engine is being closed. + */ + public static final String DB_SCHEMA_UPDATE_DROP_CREATE = "drop-create"; + + /** + * Upon building of the process engine, a check is performed and an update of the schema is performed if it is necessary. + */ + public static final String DB_SCHEMA_UPDATE_TRUE = "true"; + + protected boolean forceCloseMybatisConnectionPool = true; + + protected String databaseType; + protected String jdbcDriver = "org.h2.Driver"; + protected String jdbcUrl = "jdbc:h2:tcp://localhost/~/flowable"; + protected String jdbcUsername = "sa"; + protected String jdbcPassword = ""; + protected String dataSourceJndiName; + protected int jdbcMaxActiveConnections = 16; + protected int jdbcMaxIdleConnections = 8; + protected int jdbcMaxCheckoutTime; + protected int jdbcMaxWaitTime; + protected boolean jdbcPingEnabled; + protected String jdbcPingQuery; + protected int jdbcPingConnectionNotUsedFor; + protected int jdbcDefaultTransactionIsolationLevel; + protected DataSource dataSource; + protected SchemaManager commonSchemaManager; + protected SchemaManager schemaManager; + protected Command schemaManagementCmd; + + protected String databaseSchemaUpdate = DB_SCHEMA_UPDATE_FALSE; + + /** + * Whether to use a lock when performing the database schema create or update operations. + */ + protected boolean useLockForDatabaseSchemaUpdate = false; + + protected String xmlEncoding = "UTF-8"; + + // COMMAND EXECUTORS /////////////////////////////////////////////// + + protected CommandExecutor commandExecutor; + protected Collection defaultCommandInterceptors; + protected CommandConfig defaultCommandConfig; + protected CommandConfig schemaCommandConfig; + protected CommandContextFactory commandContextFactory; + protected CommandInterceptor commandInvoker; + + protected AgendaOperationRunner agendaOperationRunner = (commandContext, runnable) -> runnable.run(); + protected Collection agendaOperationExecutionListeners; + + protected List customPreCommandInterceptors; + protected List customPostCommandInterceptors; + protected List commandInterceptors; + + protected Map engineConfigurations = new HashMap<>(); + protected Map serviceConfigurations = new HashMap<>(); + + protected ClassLoader classLoader; + /** + * Either use Class.forName or ClassLoader.loadClass for class loading. See http://forums.activiti.org/content/reflectutilloadclass-and-custom- classloader + */ + protected boolean useClassForNameClassLoading = true; + + protected List engineLifecycleListeners; + + // Event Registry ////////////////////////////////////////////////// + protected Map eventRegistryEventConsumers = new HashMap<>(); + + // MYBATIS SQL SESSION FACTORY ///////////////////////////////////// + + protected boolean isDbHistoryUsed = true; + protected DbSqlSessionFactory dbSqlSessionFactory; + protected SqlSessionFactory sqlSessionFactory; + protected TransactionFactory transactionFactory; + protected TransactionContextFactory transactionContextFactory; + + /** + * If set to true, enables bulk insert (grouping sql inserts together). Default true. + * For some databases (eg DB2+z/OS) needs to be set to false. + */ + protected boolean isBulkInsertEnabled = true; + + /** + * Some databases have a limit of how many parameters one sql insert can have (eg SQL Server, 2000 params (!= insert statements) ). Tweak this parameter in case of exceptions indicating too much + * is being put into one bulk insert, or make it higher if your database can cope with it and there are inserts with a huge amount of data. + *

+ * By default: 100 (55 for mssql server as it has a hard limit of 2000 parameters in a statement) + */ + protected int maxNrOfStatementsInBulkInsert = 100; + + public int DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER = 55; // currently Execution has most params (35). 2000 / 35 = 57. + + protected String mybatisMappingFile; + protected Set> customMybatisMappers; + protected Set customMybatisXMLMappers; + protected List customMybatisInterceptors; + + protected Set dependentEngineMyBatisXmlMappers; + protected List dependentEngineMybatisTypeAliasConfigs; + protected List dependentEngineMybatisTypeHandlerConfigs; + + // SESSION FACTORIES /////////////////////////////////////////////// + protected List customSessionFactories; + protected Map, SessionFactory> sessionFactories; + + protected boolean enableEventDispatcher = true; + protected FlowableEventDispatcher eventDispatcher; + protected List eventListeners; + protected Map> typedEventListeners; + protected List additionalEventDispatchActions; + + protected LoggingListener loggingListener; + + protected boolean transactionsExternallyManaged; + + /** + * Flag that can be set to configure or not a relational database is used. This is useful for custom implementations that do not use relational databases at all. + * + * If true (default), the {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will be used to determine what needs to happen wrt the database schema. + * + * If false, no validation or schema creation will be done. That means that the database schema must have been created 'manually' before but the engine does not validate whether the schema is + * correct. The {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will not be used. + */ + protected boolean usingRelationalDatabase = true; + + /** + * Flag that can be set to configure whether or not a schema is used. This is useful for custom implementations that do not use relational databases at all. + * Setting {@link #usingRelationalDatabase} to true will automatically imply using a schema. + */ + protected boolean usingSchemaMgmt = true; + + /** + * Allows configuring a database table prefix which is used for all runtime operations of the process engine. For example, if you specify a prefix named 'PRE1.', Flowable will query for executions + * in a table named 'PRE1.ACT_RU_EXECUTION_'. + * + *

+ * NOTE: the prefix is not respected by automatic database schema management. If you use {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_CREATE_DROP} or + * {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_TRUE}, Flowable will create the database tables using the default names, regardless of the prefix configured here. + */ + protected String databaseTablePrefix = ""; + + /** + * Escape character for doing wildcard searches. + * + * This will be added at then end of queries that include for example a LIKE clause. For example: SELECT * FROM table WHERE column LIKE '%\%%' ESCAPE '\'; + */ + protected String databaseWildcardEscapeCharacter; + + /** + * database catalog to use + */ + protected String databaseCatalog = ""; + + /** + * In some situations you want to set the schema to use for table checks / generation if the database metadata doesn't return that correctly, see https://jira.codehaus.org/browse/ACT-1220, + * https://jira.codehaus.org/browse/ACT-1062 + */ + protected String databaseSchema; + + /** + * Set to true in case the defined databaseTablePrefix is a schema-name, instead of an actual table name prefix. This is relevant for checking if Flowable-tables exist, the databaseTablePrefix + * will not be used here - since the schema is taken into account already, adding a prefix for the table-check will result in wrong table-names. + */ + protected boolean tablePrefixIsSchema; + + /** + * Set to true if the latest version of a definition should be retrieved, ignoring a possible parent deployment id value + */ + protected boolean alwaysLookupLatestDefinitionVersion; + + /** + * Set to true if by default lookups should fallback to the default tenant (an empty string by default or a defined tenant value) + */ + protected boolean fallbackToDefaultTenant; + + /** + * Default tenant provider that is executed when looking up definitions, in case the global or local fallback to default tenant value is true + */ + protected DefaultTenantProvider defaultTenantProvider = (tenantId, scope, scopeKey) -> NO_TENANT_ID; + + /** + * Enables the MyBatis plugin that logs the execution time of sql statements. + */ + protected boolean enableLogSqlExecutionTime; + + protected Properties databaseTypeMappings = getDefaultDatabaseTypeMappings(); + + /** + * Duration between the checks when acquiring a lock. + */ + protected Duration lockPollRate = Duration.ofSeconds(10); + + /** + * Duration to wait for the DB Schema lock before giving up. + */ + protected Duration schemaLockWaitTime = Duration.ofMinutes(5); + + // DATA MANAGERS ////////////////////////////////////////////////////////////////// + + protected PropertyDataManager propertyDataManager; + protected ByteArrayDataManager byteArrayDataManager; + protected TableDataManager tableDataManager; + + // ENTITY MANAGERS //////////////////////////////////////////////////////////////// + + protected PropertyEntityManager propertyEntityManager; + protected ByteArrayEntityManager byteArrayEntityManager; + + protected List customPreDeployers; + protected List customPostDeployers; + protected List deployers; + + // CONFIGURATORS //////////////////////////////////////////////////////////// + + protected boolean enableConfiguratorServiceLoader = true; // Enabled by default. In certain environments this should be set to false (eg osgi) + protected List configurators; // The injected configurators + protected List allConfigurators; // Including auto-discovered configurators + protected EngineConfigurator idmEngineConfigurator; + protected EngineConfigurator eventRegistryConfigurator; + + public static final String PRODUCT_NAME_POSTGRES = "PostgreSQL"; + public static final String PRODUCT_NAME_CRDB = "CockroachDB"; + + public static final String DATABASE_TYPE_H2 = "h2"; + public static final String DATABASE_TYPE_HSQL = "hsql"; + public static final String DATABASE_TYPE_MYSQL = "mysql"; + public static final String DATABASE_TYPE_ORACLE = "oracle"; + public static final String DATABASE_TYPE_POSTGRES = "postgres"; + public static final String DATABASE_TYPE_MSSQL = "mssql"; + public static final String DATABASE_TYPE_DB2 = "db2"; + public static final String DATABASE_TYPE_COCKROACHDB = "cockroachdb"; + + public static Properties getDefaultDatabaseTypeMappings() { + Properties databaseTypeMappings = new Properties(); + databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2); + databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL); + databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL); + databaseTypeMappings.setProperty("MariaDB", DATABASE_TYPE_MYSQL); + databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE); + databaseTypeMappings.setProperty(PRODUCT_NAME_POSTGRES, DATABASE_TYPE_POSTGRES); + databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL); + databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXPPC64LE", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty(PRODUCT_NAME_CRDB, DATABASE_TYPE_COCKROACHDB); + databaseTypeMappings.setProperty("DM DBMS", DATABASE_TYPE_ORACLE);// 加入达梦支持 可以直接使用ORACLE或者MYSQL的 + return databaseTypeMappings; + } + + protected Map beans; + + protected IdGenerator idGenerator; + protected boolean usePrefixId; + + protected Clock clock; + protected ObjectMapper objectMapper; + + // Variables + + public static final int DEFAULT_GENERIC_MAX_LENGTH_STRING = 4000; + public static final int DEFAULT_ORACLE_MAX_LENGTH_STRING = 2000; + + /** + * Define a max length for storing String variable types in the database. Mainly used for the Oracle NVARCHAR2 limit of 2000 characters + */ + protected int maxLengthStringVariableType = -1; + + protected void initEngineConfigurations() { + addEngineConfiguration(getEngineCfgKey(), getEngineScopeType(), this); + } + + // DataSource + // /////////////////////////////////////////////////////////////// + + protected void initDataSource() { + if (dataSource == null) { + if (dataSourceJndiName != null) { + try { + dataSource = (DataSource) new InitialContext().lookup(dataSourceJndiName); + } catch (Exception e) { + throw new FlowableException("couldn't lookup datasource from " + dataSourceJndiName + ": " + e.getMessage(), e); + } + + } else if (jdbcUrl != null) { + if ((jdbcDriver == null) || (jdbcUsername == null)) { + throw new FlowableException("DataSource or JDBC properties have to be specified in a process engine configuration"); + } + + logger.debug("initializing datasource to db: {}", jdbcUrl); + + if (logger.isInfoEnabled()) { + logger.info("Configuring Datasource with following properties (omitted password for security)"); + logger.info("datasource driver : {}", jdbcDriver); + logger.info("datasource url : {}", jdbcUrl); + logger.info("datasource user name : {}", jdbcUsername); + } + + PooledDataSource pooledDataSource = new PooledDataSource(this.getClass().getClassLoader(), jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword); + + if (jdbcMaxActiveConnections > 0) { + pooledDataSource.setPoolMaximumActiveConnections(jdbcMaxActiveConnections); + } + if (jdbcMaxIdleConnections > 0) { + pooledDataSource.setPoolMaximumIdleConnections(jdbcMaxIdleConnections); + } + if (jdbcMaxCheckoutTime > 0) { + pooledDataSource.setPoolMaximumCheckoutTime(jdbcMaxCheckoutTime); + } + if (jdbcMaxWaitTime > 0) { + pooledDataSource.setPoolTimeToWait(jdbcMaxWaitTime); + } + if (jdbcPingEnabled) { + pooledDataSource.setPoolPingEnabled(true); + if (jdbcPingQuery != null) { + pooledDataSource.setPoolPingQuery(jdbcPingQuery); + } + pooledDataSource.setPoolPingConnectionsNotUsedFor(jdbcPingConnectionNotUsedFor); + } + if (jdbcDefaultTransactionIsolationLevel > 0) { + pooledDataSource.setDefaultTransactionIsolationLevel(jdbcDefaultTransactionIsolationLevel); + } + dataSource = pooledDataSource; + } + } + + if (databaseType == null) { + initDatabaseType(); + } + } + + public void initDatabaseType() { + Connection connection = null; + try { + connection = dataSource.getConnection(); + DatabaseMetaData databaseMetaData = connection.getMetaData(); + String databaseProductName = databaseMetaData.getDatabaseProductName(); + logger.debug("database product name: '{}'", databaseProductName); + + // CRDB does not expose the version through the jdbc driver, so we need to fetch it through version(). + if (PRODUCT_NAME_POSTGRES.equalsIgnoreCase(databaseProductName)) { + try (PreparedStatement preparedStatement = connection.prepareStatement("select version() as version;"); + ResultSet resultSet = preparedStatement.executeQuery()) { + String version = null; + if (resultSet.next()) { + version = resultSet.getString("version"); + } + + if (StringUtils.isNotEmpty(version) && version.toLowerCase().startsWith(PRODUCT_NAME_CRDB.toLowerCase())) { + databaseProductName = PRODUCT_NAME_CRDB; + logger.info("CockroachDB version '{}' detected", version); + } + } + } + + databaseType = databaseTypeMappings.getProperty(databaseProductName); + if (databaseType == null) { + throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'"); + } + logger.debug("using database type: {}", databaseType); + + } catch (SQLException e) { + throw new RuntimeException("Exception while initializing Database connection", e); + } finally { + try { + if (connection != null) { + connection.close(); + } + } catch (SQLException e) { + logger.error("Exception while closing the Database connection", e); + } + } + + // Special care for MSSQL, as it has a hard limit of 2000 params per statement (incl bulk statement). + // Especially with executions, with 100 as default, this limit is passed. + if (DATABASE_TYPE_MSSQL.equals(databaseType)) { + maxNrOfStatementsInBulkInsert = DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER; + } + } + + public void initSchemaManager() { + if (this.commonSchemaManager == null) { + this.commonSchemaManager = new CommonDbSchemaManager(); + } + } + + // session factories //////////////////////////////////////////////////////// + + public void addSessionFactory(SessionFactory sessionFactory) { + sessionFactories.put(sessionFactory.getSessionType(), sessionFactory); + } + + public void initCommandContextFactory() { + if (commandContextFactory == null) { + commandContextFactory = new CommandContextFactory(); + } + } + + public void initTransactionContextFactory() { + if (transactionContextFactory == null) { + transactionContextFactory = new StandaloneMybatisTransactionContextFactory(); + } + } + + public void initCommandExecutors() { + initDefaultCommandConfig(); + initSchemaCommandConfig(); + initCommandInvoker(); + initCommandInterceptors(); + initCommandExecutor(); + } + + + public void initDefaultCommandConfig() { + if (defaultCommandConfig == null) { + defaultCommandConfig = new CommandConfig(); + } + } + + public void initSchemaCommandConfig() { + if (schemaCommandConfig == null) { + schemaCommandConfig = new CommandConfig(); + } + } + + public void initCommandInvoker() { + if (commandInvoker == null) { + commandInvoker = new DefaultCommandInvoker(); + } + } + + public void initCommandInterceptors() { + if (commandInterceptors == null) { + commandInterceptors = new ArrayList<>(); + if (customPreCommandInterceptors != null) { + commandInterceptors.addAll(customPreCommandInterceptors); + } + commandInterceptors.addAll(getDefaultCommandInterceptors()); + if (customPostCommandInterceptors != null) { + commandInterceptors.addAll(customPostCommandInterceptors); + } + commandInterceptors.add(commandInvoker); + } + } + + public Collection getDefaultCommandInterceptors() { + if (defaultCommandInterceptors == null) { + List interceptors = new ArrayList<>(); + interceptors.add(new LogInterceptor()); + + if (DATABASE_TYPE_COCKROACHDB.equals(databaseType)) { + interceptors.add(new CrDbRetryInterceptor()); + } + + CommandInterceptor transactionInterceptor = createTransactionInterceptor(); + if (transactionInterceptor != null) { + interceptors.add(transactionInterceptor); + } + + if (commandContextFactory != null) { + String engineCfgKey = getEngineCfgKey(); + CommandContextInterceptor commandContextInterceptor = new CommandContextInterceptor(commandContextFactory, + classLoader, useClassForNameClassLoading, clock, objectMapper); + engineConfigurations.put(engineCfgKey, this); + commandContextInterceptor.setEngineCfgKey(engineCfgKey); + commandContextInterceptor.setEngineConfigurations(engineConfigurations); + interceptors.add(commandContextInterceptor); + } + + if (transactionContextFactory != null) { + interceptors.add(new TransactionContextInterceptor(transactionContextFactory)); + } + + List additionalCommandInterceptors = getAdditionalDefaultCommandInterceptors(); + if (additionalCommandInterceptors != null) { + interceptors.addAll(additionalCommandInterceptors); + } + + defaultCommandInterceptors = interceptors; + } + return defaultCommandInterceptors; + } + + public abstract String getEngineCfgKey(); + + public abstract String getEngineScopeType(); + + public List getAdditionalDefaultCommandInterceptors() { + return null; + } + + public void initCommandExecutor() { + if (commandExecutor == null) { + CommandInterceptor first = initInterceptorChain(commandInterceptors); + commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first); + } + } + + public CommandInterceptor initInterceptorChain(List chain) { + if (chain == null || chain.isEmpty()) { + throw new FlowableException("invalid command interceptor chain configuration: " + chain); + } + for (int i = 0; i < chain.size() - 1; i++) { + chain.get(i).setNext(chain.get(i + 1)); + } + return chain.get(0); + } + + public abstract CommandInterceptor createTransactionInterceptor(); + + + public void initBeans() { + if (beans == null) { + beans = new HashMap<>(); + } + } + + // id generator + // ///////////////////////////////////////////////////////////// + + public void initIdGenerator() { + if (idGenerator == null) { + idGenerator = new StrongUuidGenerator(); + } + } + + public void initObjectMapper() { + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + } + } + + public void initClock() { + if (clock == null) { + clock = new DefaultClockImpl(); + } + } + + // Data managers /////////////////////////////////////////////////////////// + + public void initDataManagers() { + if (propertyDataManager == null) { + propertyDataManager = new MybatisPropertyDataManager(idGenerator); + } + + if (byteArrayDataManager == null) { + byteArrayDataManager = new MybatisByteArrayDataManager(idGenerator); + } + } + + // Entity managers ////////////////////////////////////////////////////////// + + public void initEntityManagers() { + if (propertyEntityManager == null) { + propertyEntityManager = new PropertyEntityManagerImpl(this, propertyDataManager); + } + + if (byteArrayEntityManager == null) { + byteArrayEntityManager = new ByteArrayEntityManagerImpl(byteArrayDataManager, getEngineCfgKey(), this::getEventDispatcher); + } + + if (tableDataManager == null) { + tableDataManager = new TableDataManagerImpl(this); + } + } + + // services + // ///////////////////////////////////////////////////////////////// + + protected void initService(Object service) { + if (service instanceof CommonEngineServiceImpl) { + ((CommonEngineServiceImpl) service).setCommandExecutor(commandExecutor); + } + } + + // myBatis SqlSessionFactory + // //////////////////////////////////////////////// + + public void initSessionFactories() { + if (sessionFactories == null) { + sessionFactories = new HashMap<>(); + + if (usingRelationalDatabase) { + initDbSqlSessionFactory(); + } + + addSessionFactory(new GenericManagerFactory(EntityCache.class, EntityCacheImpl.class)); + + if (isLoggingSessionEnabled()) { + if (!sessionFactories.containsKey(LoggingSession.class)) { + LoggingSessionFactory loggingSessionFactory = new LoggingSessionFactory(); + loggingSessionFactory.setLoggingListener(loggingListener); + loggingSessionFactory.setObjectMapper(objectMapper); + sessionFactories.put(LoggingSession.class, loggingSessionFactory); + } + } + + commandContextFactory.setSessionFactories(sessionFactories); + + } else { + if (usingRelationalDatabase) { + initDbSqlSessionFactoryEntitySettings(); + } + } + + if (customSessionFactories != null) { + for (SessionFactory sessionFactory : customSessionFactories) { + addSessionFactory(sessionFactory); + } + } + } + + public void initDbSqlSessionFactory() { + if (dbSqlSessionFactory == null) { + dbSqlSessionFactory = createDbSqlSessionFactory(); + } + dbSqlSessionFactory.setDatabaseType(databaseType); + dbSqlSessionFactory.setSqlSessionFactory(sqlSessionFactory); + dbSqlSessionFactory.setDbHistoryUsed(isDbHistoryUsed); + dbSqlSessionFactory.setDatabaseTablePrefix(databaseTablePrefix); + dbSqlSessionFactory.setTablePrefixIsSchema(tablePrefixIsSchema); + dbSqlSessionFactory.setDatabaseCatalog(databaseCatalog); + dbSqlSessionFactory.setDatabaseSchema(databaseSchema); + dbSqlSessionFactory.setMaxNrOfStatementsInBulkInsert(maxNrOfStatementsInBulkInsert); + + initDbSqlSessionFactoryEntitySettings(); + + addSessionFactory(dbSqlSessionFactory); + } + + public DbSqlSessionFactory createDbSqlSessionFactory() { + return new DbSqlSessionFactory(usePrefixId); + } + + protected abstract void initDbSqlSessionFactoryEntitySettings(); + + protected void defaultInitDbSqlSessionFactoryEntitySettings(List> insertOrder, List> deleteOrder) { + if (insertOrder != null) { + for (Class clazz : insertOrder) { + dbSqlSessionFactory.getInsertionOrder().add(clazz); + + if (isBulkInsertEnabled) { + dbSqlSessionFactory.getBulkInserteableEntityClasses().add(clazz); + } + } + } + + if (deleteOrder != null) { + for (Class clazz : deleteOrder) { + dbSqlSessionFactory.getDeletionOrder().add(clazz); + } + } + } + + public void initTransactionFactory() { + if (transactionFactory == null) { + if (transactionsExternallyManaged) { + transactionFactory = new ManagedTransactionFactory(); + Properties properties = new Properties(); + properties.put("closeConnection", "false"); + this.transactionFactory.setProperties(properties); + } else { + transactionFactory = new JdbcTransactionFactory(); + } + } + } + + public void initSqlSessionFactory() { + if (sqlSessionFactory == null) { + InputStream inputStream = null; + try { + inputStream = getMyBatisXmlConfigurationStream(); + + Environment environment = new Environment("default", transactionFactory, dataSource); + Reader reader = new InputStreamReader(inputStream); + Properties properties = new Properties(); + properties.put("prefix", databaseTablePrefix); + + String wildcardEscapeClause = ""; + if ((databaseWildcardEscapeCharacter != null) && (databaseWildcardEscapeCharacter.length() != 0)) { + wildcardEscapeClause = " escape '" + databaseWildcardEscapeCharacter + "'"; + } + properties.put("wildcardEscapeClause", wildcardEscapeClause); + + // set default properties + properties.put("limitBefore", ""); + properties.put("limitAfter", ""); + properties.put("limitBetween", ""); + properties.put("limitBeforeNativeQuery", ""); + properties.put("limitAfterNativeQuery", ""); + properties.put("blobType", "BLOB"); + properties.put("boolValue", "TRUE"); + + if (databaseType != null) { + properties.load(getResourceAsStream(pathToEngineDbProperties())); + } + + Configuration configuration = initMybatisConfiguration(environment, reader, properties); + sqlSessionFactory = new DefaultSqlSessionFactory(configuration); + + } catch (Exception e) { + throw new FlowableException("Error while building ibatis SqlSessionFactory: " + e.getMessage(), e); + } finally { + IoUtil.closeSilently(inputStream); + } + } else { + // This is needed when the SQL Session Factory is created by another engine. + // When custom XML Mappers are registered with this engine they need to be loaded in the configuration as well + applyCustomMybatisCustomizations(sqlSessionFactory.getConfiguration()); + } + } + + public String pathToEngineDbProperties() { + return "org/flowable/common/db/properties/" + databaseType + ".properties"; + } + + public Configuration initMybatisConfiguration(Environment environment, Reader reader, Properties properties) { + XMLConfigBuilder parser = new XMLConfigBuilder(reader, "", properties); + Configuration configuration = parser.getConfiguration(); + + if (databaseType != null) { + configuration.setDatabaseId(databaseType); + } + + configuration.setEnvironment(environment); + + initMybatisTypeHandlers(configuration); + initCustomMybatisInterceptors(configuration); + if (isEnableLogSqlExecutionTime()) { + initMyBatisLogSqlExecutionTimePlugin(configuration); + } + + configuration = parseMybatisConfiguration(parser); + return configuration; + } + + public void initCustomMybatisMappers(Configuration configuration) { + if (getCustomMybatisMappers() != null) { + for (Class clazz : getCustomMybatisMappers()) { + if (!configuration.hasMapper(clazz)) { + configuration.addMapper(clazz); + } + } + } + } + + public void initMybatisTypeHandlers(Configuration configuration) { + // When mapping into Map there is currently a problem with MyBatis. + // It will return objects which are driver specific. + // Therefore we are registering the mappings between Object.class and the specific jdbc type here. + // see https://github.com/mybatis/mybatis-3/issues/2216 for more info + TypeHandlerRegistry handlerRegistry = configuration.getTypeHandlerRegistry(); + + handlerRegistry.register(Object.class, JdbcType.BOOLEAN, new BooleanTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.BIT, new BooleanTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.TINYINT, new ByteTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.SMALLINT, new ShortTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.INTEGER, new IntegerTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.FLOAT, new FloatTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.DOUBLE, new DoubleTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.CHAR, new StringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.CLOB, new ClobTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.VARCHAR, new StringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.LONGVARCHAR, new StringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NVARCHAR, new NStringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NCHAR, new NStringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NCLOB, new NClobTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.BIGINT, new LongTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.REAL, new BigDecimalTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.DECIMAL, new BigDecimalTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NUMERIC, new BigDecimalTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.BLOB, new BlobInputStreamTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.LONGVARBINARY, new BlobTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.DATE, new DateOnlyTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.TIME, new TimeOnlyTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.TIMESTAMP, new DateTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.SQLXML, new SqlxmlTypeHandler()); + } + + public void initCustomMybatisInterceptors(Configuration configuration) { + if (customMybatisInterceptors!=null){ + for (Interceptor interceptor :customMybatisInterceptors){ + configuration.addInterceptor(interceptor); + } + } + } + + public void initMyBatisLogSqlExecutionTimePlugin(Configuration configuration) { + configuration.addInterceptor(new LogSqlExecutionTimePlugin()); + } + + public Configuration parseMybatisConfiguration(XMLConfigBuilder parser) { + Configuration configuration = parser.parse(); + + applyCustomMybatisCustomizations(configuration); + return configuration; + } + + protected void applyCustomMybatisCustomizations(Configuration configuration) { + initCustomMybatisMappers(configuration); + + if (dependentEngineMybatisTypeAliasConfigs != null) { + for (MybatisTypeAliasConfigurator typeAliasConfig : dependentEngineMybatisTypeAliasConfigs) { + typeAliasConfig.configure(configuration.getTypeAliasRegistry()); + } + } + if (dependentEngineMybatisTypeHandlerConfigs != null) { + for (MybatisTypeHandlerConfigurator typeHandlerConfig : dependentEngineMybatisTypeHandlerConfigs) { + typeHandlerConfig.configure(configuration.getTypeHandlerRegistry()); + } + } + + parseDependentEngineMybatisXMLMappers(configuration); + parseCustomMybatisXMLMappers(configuration); + } + + public void parseCustomMybatisXMLMappers(Configuration configuration) { + if (getCustomMybatisXMLMappers() != null) { + for (String resource : getCustomMybatisXMLMappers()) { + parseMybatisXmlMapping(configuration, resource); + } + } + } + + public void parseDependentEngineMybatisXMLMappers(Configuration configuration) { + if (getDependentEngineMyBatisXmlMappers() != null) { + for (String resource : getDependentEngineMyBatisXmlMappers()) { + parseMybatisXmlMapping(configuration, resource); + } + } + } + + protected void parseMybatisXmlMapping(Configuration configuration, String resource) { + // see XMLConfigBuilder.mapperElement() + XMLMapperBuilder mapperParser = new XMLMapperBuilder(getResourceAsStream(resource), configuration, resource, configuration.getSqlFragments()); + mapperParser.parse(); + } + + protected InputStream getResourceAsStream(String resource) { + ClassLoader classLoader = getClassLoader(); + if (classLoader != null) { + return getClassLoader().getResourceAsStream(resource); + } else { + return this.getClass().getClassLoader().getResourceAsStream(resource); + } + } + + public void setMybatisMappingFile(String file) { + this.mybatisMappingFile = file; + } + + public String getMybatisMappingFile() { + return mybatisMappingFile; + } + + public abstract InputStream getMyBatisXmlConfigurationStream(); + + public void initConfigurators() { + + allConfigurators = new ArrayList<>(); + allConfigurators.addAll(getEngineSpecificEngineConfigurators()); + + // Configurators that are explicitly added to the config + if (configurators != null) { + allConfigurators.addAll(configurators); + } + + // Auto discovery through ServiceLoader + if (enableConfiguratorServiceLoader) { + ClassLoader classLoader = getClassLoader(); + if (classLoader == null) { + classLoader = ReflectUtil.getClassLoader(); + } + + ServiceLoader configuratorServiceLoader = ServiceLoader.load(EngineConfigurator.class, classLoader); + int nrOfServiceLoadedConfigurators = 0; + for (EngineConfigurator configurator : configuratorServiceLoader) { + allConfigurators.add(configurator); + nrOfServiceLoadedConfigurators++; + } + + if (nrOfServiceLoadedConfigurators > 0) { + logger.info("Found {} auto-discoverable Process Engine Configurator{}", nrOfServiceLoadedConfigurators, nrOfServiceLoadedConfigurators > 1 ? "s" : ""); + } + + if (!allConfigurators.isEmpty()) { + + // Order them according to the priorities (useful for dependent + // configurator) + allConfigurators.sort(new Comparator() { + + @Override + public int compare(EngineConfigurator configurator1, EngineConfigurator configurator2) { + int priority1 = configurator1.getPriority(); + int priority2 = configurator2.getPriority(); + + if (priority1 < priority2) { + return -1; + } else if (priority1 > priority2) { + return 1; + } + return 0; + } + }); + + // Execute the configurators + logger.info("Found {} Engine Configurators in total:", allConfigurators.size()); + for (EngineConfigurator configurator : allConfigurators) { + logger.info("{} (priority:{})", configurator.getClass(), configurator.getPriority()); + } + + } + + } + } + + public void close() { + if (forceCloseMybatisConnectionPool && dataSource instanceof PooledDataSource) { + /* + * When the datasource is created by a Flowable engine (i.e. it's an instance of PooledDataSource), + * the connection pool needs to be closed when closing the engine. + * Note that calling forceCloseAll() multiple times (as is the case when running with multiple engine) is ok. + */ + ((PooledDataSource) dataSource).forceCloseAll(); + } + } + + protected List getEngineSpecificEngineConfigurators() { + // meant to be overridden if needed + return Collections.emptyList(); + } + + public void configuratorsBeforeInit() { + for (EngineConfigurator configurator : allConfigurators) { + logger.info("Executing beforeInit() of {} (priority:{})", configurator.getClass(), configurator.getPriority()); + configurator.beforeInit(this); + } + } + + public void configuratorsAfterInit() { + for (EngineConfigurator configurator : allConfigurators) { + logger.info("Executing configure() of {} (priority:{})", configurator.getClass(), configurator.getPriority()); + configurator.configure(this); + } + } + + public LockManager getLockManager(String lockName) { + return new LockManagerImpl(commandExecutor, lockName, getLockPollRate(), getEngineCfgKey()); + } + + // getters and setters + // ////////////////////////////////////////////////////// + + public abstract String getEngineName(); + + public ClassLoader getClassLoader() { + return classLoader; + } + + public AbstractEngineConfiguration setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + return this; + } + + public boolean isUseClassForNameClassLoading() { + return useClassForNameClassLoading; + } + + public AbstractEngineConfiguration setUseClassForNameClassLoading(boolean useClassForNameClassLoading) { + this.useClassForNameClassLoading = useClassForNameClassLoading; + return this; + } + + public void addEngineLifecycleListener(EngineLifecycleListener engineLifecycleListener) { + if (this.engineLifecycleListeners == null) { + this.engineLifecycleListeners = new ArrayList<>(); + } + this.engineLifecycleListeners.add(engineLifecycleListener); + } + + public List getEngineLifecycleListeners() { + return engineLifecycleListeners; + } + + public AbstractEngineConfiguration setEngineLifecycleListeners(List engineLifecycleListeners) { + this.engineLifecycleListeners = engineLifecycleListeners; + return this; + } + + public String getDatabaseType() { + return databaseType; + } + + public AbstractEngineConfiguration setDatabaseType(String databaseType) { + this.databaseType = databaseType; + return this; + } + + public DataSource getDataSource() { + return dataSource; + } + + public AbstractEngineConfiguration setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + return this; + } + + public SchemaManager getSchemaManager() { + return schemaManager; + } + + public AbstractEngineConfiguration setSchemaManager(SchemaManager schemaManager) { + this.schemaManager = schemaManager; + return this; + } + + public SchemaManager getCommonSchemaManager() { + return commonSchemaManager; + } + + public AbstractEngineConfiguration setCommonSchemaManager(SchemaManager commonSchemaManager) { + this.commonSchemaManager = commonSchemaManager; + return this; + } + + public Command getSchemaManagementCmd() { + return schemaManagementCmd; + } + + public AbstractEngineConfiguration setSchemaManagementCmd(Command schemaManagementCmd) { + this.schemaManagementCmd = schemaManagementCmd; + return this; + } + + public String getJdbcDriver() { + return jdbcDriver; + } + + public AbstractEngineConfiguration setJdbcDriver(String jdbcDriver) { + this.jdbcDriver = jdbcDriver; + return this; + } + + public String getJdbcUrl() { + return jdbcUrl; + } + + public AbstractEngineConfiguration setJdbcUrl(String jdbcUrl) { + this.jdbcUrl = jdbcUrl; + return this; + } + + public String getJdbcUsername() { + return jdbcUsername; + } + + public AbstractEngineConfiguration setJdbcUsername(String jdbcUsername) { + this.jdbcUsername = jdbcUsername; + return this; + } + + public String getJdbcPassword() { + return jdbcPassword; + } + + public AbstractEngineConfiguration setJdbcPassword(String jdbcPassword) { + this.jdbcPassword = jdbcPassword; + return this; + } + + public int getJdbcMaxActiveConnections() { + return jdbcMaxActiveConnections; + } + + public AbstractEngineConfiguration setJdbcMaxActiveConnections(int jdbcMaxActiveConnections) { + this.jdbcMaxActiveConnections = jdbcMaxActiveConnections; + return this; + } + + public int getJdbcMaxIdleConnections() { + return jdbcMaxIdleConnections; + } + + public AbstractEngineConfiguration setJdbcMaxIdleConnections(int jdbcMaxIdleConnections) { + this.jdbcMaxIdleConnections = jdbcMaxIdleConnections; + return this; + } + + public int getJdbcMaxCheckoutTime() { + return jdbcMaxCheckoutTime; + } + + public AbstractEngineConfiguration setJdbcMaxCheckoutTime(int jdbcMaxCheckoutTime) { + this.jdbcMaxCheckoutTime = jdbcMaxCheckoutTime; + return this; + } + + public int getJdbcMaxWaitTime() { + return jdbcMaxWaitTime; + } + + public AbstractEngineConfiguration setJdbcMaxWaitTime(int jdbcMaxWaitTime) { + this.jdbcMaxWaitTime = jdbcMaxWaitTime; + return this; + } + + public boolean isJdbcPingEnabled() { + return jdbcPingEnabled; + } + + public AbstractEngineConfiguration setJdbcPingEnabled(boolean jdbcPingEnabled) { + this.jdbcPingEnabled = jdbcPingEnabled; + return this; + } + + public int getJdbcPingConnectionNotUsedFor() { + return jdbcPingConnectionNotUsedFor; + } + + public AbstractEngineConfiguration setJdbcPingConnectionNotUsedFor(int jdbcPingConnectionNotUsedFor) { + this.jdbcPingConnectionNotUsedFor = jdbcPingConnectionNotUsedFor; + return this; + } + + public int getJdbcDefaultTransactionIsolationLevel() { + return jdbcDefaultTransactionIsolationLevel; + } + + public AbstractEngineConfiguration setJdbcDefaultTransactionIsolationLevel(int jdbcDefaultTransactionIsolationLevel) { + this.jdbcDefaultTransactionIsolationLevel = jdbcDefaultTransactionIsolationLevel; + return this; + } + + public String getJdbcPingQuery() { + return jdbcPingQuery; + } + + public AbstractEngineConfiguration setJdbcPingQuery(String jdbcPingQuery) { + this.jdbcPingQuery = jdbcPingQuery; + return this; + } + + public String getDataSourceJndiName() { + return dataSourceJndiName; + } + + public AbstractEngineConfiguration setDataSourceJndiName(String dataSourceJndiName) { + this.dataSourceJndiName = dataSourceJndiName; + return this; + } + + public CommandConfig getSchemaCommandConfig() { + return schemaCommandConfig; + } + + public AbstractEngineConfiguration setSchemaCommandConfig(CommandConfig schemaCommandConfig) { + this.schemaCommandConfig = schemaCommandConfig; + return this; + } + + public boolean isTransactionsExternallyManaged() { + return transactionsExternallyManaged; + } + + public AbstractEngineConfiguration setTransactionsExternallyManaged(boolean transactionsExternallyManaged) { + this.transactionsExternallyManaged = transactionsExternallyManaged; + return this; + } + + public Map getBeans() { + return beans; + } + + public AbstractEngineConfiguration setBeans(Map beans) { + this.beans = beans; + return this; + } + + public IdGenerator getIdGenerator() { + return idGenerator; + } + + public AbstractEngineConfiguration setIdGenerator(IdGenerator idGenerator) { + this.idGenerator = idGenerator; + return this; + } + + public boolean isUsePrefixId() { + return usePrefixId; + } + + public AbstractEngineConfiguration setUsePrefixId(boolean usePrefixId) { + this.usePrefixId = usePrefixId; + return this; + } + + public String getXmlEncoding() { + return xmlEncoding; + } + + public AbstractEngineConfiguration setXmlEncoding(String xmlEncoding) { + this.xmlEncoding = xmlEncoding; + return this; + } + + public CommandConfig getDefaultCommandConfig() { + return defaultCommandConfig; + } + + public AbstractEngineConfiguration setDefaultCommandConfig(CommandConfig defaultCommandConfig) { + this.defaultCommandConfig = defaultCommandConfig; + return this; + } + + public CommandExecutor getCommandExecutor() { + return commandExecutor; + } + + public AbstractEngineConfiguration setCommandExecutor(CommandExecutor commandExecutor) { + this.commandExecutor = commandExecutor; + return this; + } + + public CommandContextFactory getCommandContextFactory() { + return commandContextFactory; + } + + public AbstractEngineConfiguration setCommandContextFactory(CommandContextFactory commandContextFactory) { + this.commandContextFactory = commandContextFactory; + return this; + } + + public CommandInterceptor getCommandInvoker() { + return commandInvoker; + } + + public AbstractEngineConfiguration setCommandInvoker(CommandInterceptor commandInvoker) { + this.commandInvoker = commandInvoker; + return this; + } + + public AgendaOperationRunner getAgendaOperationRunner() { + return agendaOperationRunner; + } + + public AbstractEngineConfiguration setAgendaOperationRunner(AgendaOperationRunner agendaOperationRunner) { + this.agendaOperationRunner = agendaOperationRunner; + return this; + } + + public Collection getAgendaOperationExecutionListeners() { + return agendaOperationExecutionListeners; + } + + public AbstractEngineConfiguration addAgendaOperationExecutionListener(AgendaOperationExecutionListener listener) { + if (this.agendaOperationExecutionListeners == null) { + this.agendaOperationExecutionListeners = new ArrayList<>(); + } + this.agendaOperationExecutionListeners.add(listener); + return this; + } + + public AbstractEngineConfiguration setAgendaOperationExecutionListeners(Collection agendaOperationExecutionListeners) { + this.agendaOperationExecutionListeners = agendaOperationExecutionListeners; + return this; + } + + public List getCustomPreCommandInterceptors() { + return customPreCommandInterceptors; + } + + public AbstractEngineConfiguration addCustomPreCommandInterceptor(CommandInterceptor commandInterceptor) { + if (this.customPreCommandInterceptors == null) { + this.customPreCommandInterceptors = new ArrayList<>(); + } + this.customPreCommandInterceptors.add(commandInterceptor); + return this; + } + + public AbstractEngineConfiguration setCustomPreCommandInterceptors(List customPreCommandInterceptors) { + this.customPreCommandInterceptors = customPreCommandInterceptors; + return this; + } + + public List getCustomPostCommandInterceptors() { + return customPostCommandInterceptors; + } + + public AbstractEngineConfiguration addCustomPostCommandInterceptor(CommandInterceptor commandInterceptor) { + if (this.customPostCommandInterceptors == null) { + this.customPostCommandInterceptors = new ArrayList<>(); + } + this.customPostCommandInterceptors.add(commandInterceptor); + return this; + } + + public AbstractEngineConfiguration setCustomPostCommandInterceptors(List customPostCommandInterceptors) { + this.customPostCommandInterceptors = customPostCommandInterceptors; + return this; + } + + public List getCommandInterceptors() { + return commandInterceptors; + } + + public AbstractEngineConfiguration setCommandInterceptors(List commandInterceptors) { + this.commandInterceptors = commandInterceptors; + return this; + } + + public Map getEngineConfigurations() { + return engineConfigurations; + } + + public AbstractEngineConfiguration setEngineConfigurations(Map engineConfigurations) { + this.engineConfigurations = engineConfigurations; + return this; + } + + public void addEngineConfiguration(String key, String scopeType, AbstractEngineConfiguration engineConfiguration) { + if (engineConfigurations == null) { + engineConfigurations = new HashMap<>(); + } + engineConfigurations.put(key, engineConfiguration); + engineConfigurations.put(scopeType, engineConfiguration); + } + + public Map getServiceConfigurations() { + return serviceConfigurations; + } + + public AbstractEngineConfiguration setServiceConfigurations(Map serviceConfigurations) { + this.serviceConfigurations = serviceConfigurations; + return this; + } + + public void addServiceConfiguration(String key, AbstractServiceConfiguration serviceConfiguration) { + if (serviceConfigurations == null) { + serviceConfigurations = new HashMap<>(); + } + serviceConfigurations.put(key, serviceConfiguration); + } + + public Map getEventRegistryEventConsumers() { + return eventRegistryEventConsumers; + } + + public AbstractEngineConfiguration setEventRegistryEventConsumers(Map eventRegistryEventConsumers) { + this.eventRegistryEventConsumers = eventRegistryEventConsumers; + return this; + } + + public void addEventRegistryEventConsumer(String key, EventRegistryEventConsumer eventRegistryEventConsumer) { + if (eventRegistryEventConsumers == null) { + eventRegistryEventConsumers = new HashMap<>(); + } + eventRegistryEventConsumers.put(key, eventRegistryEventConsumer); + } + + public AbstractEngineConfiguration setDefaultCommandInterceptors(Collection defaultCommandInterceptors) { + this.defaultCommandInterceptors = defaultCommandInterceptors; + return this; + } + + public SqlSessionFactory getSqlSessionFactory() { + return sqlSessionFactory; + } + + public AbstractEngineConfiguration setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { + this.sqlSessionFactory = sqlSessionFactory; + return this; + } + + public boolean isDbHistoryUsed() { + return isDbHistoryUsed; + } + + public AbstractEngineConfiguration setDbHistoryUsed(boolean isDbHistoryUsed) { + this.isDbHistoryUsed = isDbHistoryUsed; + return this; + } + + public DbSqlSessionFactory getDbSqlSessionFactory() { + return dbSqlSessionFactory; + } + + public AbstractEngineConfiguration setDbSqlSessionFactory(DbSqlSessionFactory dbSqlSessionFactory) { + this.dbSqlSessionFactory = dbSqlSessionFactory; + return this; + } + + public TransactionFactory getTransactionFactory() { + return transactionFactory; + } + + public AbstractEngineConfiguration setTransactionFactory(TransactionFactory transactionFactory) { + this.transactionFactory = transactionFactory; + return this; + } + + public TransactionContextFactory getTransactionContextFactory() { + return transactionContextFactory; + } + + public AbstractEngineConfiguration setTransactionContextFactory(TransactionContextFactory transactionContextFactory) { + this.transactionContextFactory = transactionContextFactory; + return this; + } + + public int getMaxNrOfStatementsInBulkInsert() { + return maxNrOfStatementsInBulkInsert; + } + + public AbstractEngineConfiguration setMaxNrOfStatementsInBulkInsert(int maxNrOfStatementsInBulkInsert) { + this.maxNrOfStatementsInBulkInsert = maxNrOfStatementsInBulkInsert; + return this; + } + + public boolean isBulkInsertEnabled() { + return isBulkInsertEnabled; + } + + public AbstractEngineConfiguration setBulkInsertEnabled(boolean isBulkInsertEnabled) { + this.isBulkInsertEnabled = isBulkInsertEnabled; + return this; + } + + public Set> getCustomMybatisMappers() { + return customMybatisMappers; + } + + public AbstractEngineConfiguration setCustomMybatisMappers(Set> customMybatisMappers) { + this.customMybatisMappers = customMybatisMappers; + return this; + } + + public Set getCustomMybatisXMLMappers() { + return customMybatisXMLMappers; + } + + public AbstractEngineConfiguration setCustomMybatisXMLMappers(Set customMybatisXMLMappers) { + this.customMybatisXMLMappers = customMybatisXMLMappers; + return this; + } + + public Set getDependentEngineMyBatisXmlMappers() { + return dependentEngineMyBatisXmlMappers; + } + + public AbstractEngineConfiguration setCustomMybatisInterceptors(List customMybatisInterceptors) { + this.customMybatisInterceptors = customMybatisInterceptors; + return this; + } + + public List getCustomMybatisInterceptors() { + return customMybatisInterceptors; + } + + public AbstractEngineConfiguration setDependentEngineMyBatisXmlMappers(Set dependentEngineMyBatisXmlMappers) { + this.dependentEngineMyBatisXmlMappers = dependentEngineMyBatisXmlMappers; + return this; + } + + public List getDependentEngineMybatisTypeAliasConfigs() { + return dependentEngineMybatisTypeAliasConfigs; + } + + public AbstractEngineConfiguration setDependentEngineMybatisTypeAliasConfigs(List dependentEngineMybatisTypeAliasConfigs) { + this.dependentEngineMybatisTypeAliasConfigs = dependentEngineMybatisTypeAliasConfigs; + return this; + } + + public List getDependentEngineMybatisTypeHandlerConfigs() { + return dependentEngineMybatisTypeHandlerConfigs; + } + + public AbstractEngineConfiguration setDependentEngineMybatisTypeHandlerConfigs(List dependentEngineMybatisTypeHandlerConfigs) { + this.dependentEngineMybatisTypeHandlerConfigs = dependentEngineMybatisTypeHandlerConfigs; + return this; + } + + public List getCustomSessionFactories() { + return customSessionFactories; + } + + public AbstractEngineConfiguration addCustomSessionFactory(SessionFactory sessionFactory) { + if (customSessionFactories == null) { + customSessionFactories = new ArrayList<>(); + } + customSessionFactories.add(sessionFactory); + return this; + } + + public AbstractEngineConfiguration setCustomSessionFactories(List customSessionFactories) { + this.customSessionFactories = customSessionFactories; + return this; + } + + public boolean isUsingRelationalDatabase() { + return usingRelationalDatabase; + } + + public AbstractEngineConfiguration setUsingRelationalDatabase(boolean usingRelationalDatabase) { + this.usingRelationalDatabase = usingRelationalDatabase; + return this; + } + + public boolean isUsingSchemaMgmt() { + return usingSchemaMgmt; + } + + public AbstractEngineConfiguration setUsingSchemaMgmt(boolean usingSchema) { + this.usingSchemaMgmt = usingSchema; + return this; + } + + public String getDatabaseTablePrefix() { + return databaseTablePrefix; + } + + public AbstractEngineConfiguration setDatabaseTablePrefix(String databaseTablePrefix) { + this.databaseTablePrefix = databaseTablePrefix; + return this; + } + + public String getDatabaseWildcardEscapeCharacter() { + return databaseWildcardEscapeCharacter; + } + + public AbstractEngineConfiguration setDatabaseWildcardEscapeCharacter(String databaseWildcardEscapeCharacter) { + this.databaseWildcardEscapeCharacter = databaseWildcardEscapeCharacter; + return this; + } + + public String getDatabaseCatalog() { + return databaseCatalog; + } + + public AbstractEngineConfiguration setDatabaseCatalog(String databaseCatalog) { + this.databaseCatalog = databaseCatalog; + return this; + } + + public String getDatabaseSchema() { + return databaseSchema; + } + + public AbstractEngineConfiguration setDatabaseSchema(String databaseSchema) { + this.databaseSchema = databaseSchema; + return this; + } + + public boolean isTablePrefixIsSchema() { + return tablePrefixIsSchema; + } + + public AbstractEngineConfiguration setTablePrefixIsSchema(boolean tablePrefixIsSchema) { + this.tablePrefixIsSchema = tablePrefixIsSchema; + return this; + } + + public boolean isAlwaysLookupLatestDefinitionVersion() { + return alwaysLookupLatestDefinitionVersion; + } + + public AbstractEngineConfiguration setAlwaysLookupLatestDefinitionVersion(boolean alwaysLookupLatestDefinitionVersion) { + this.alwaysLookupLatestDefinitionVersion = alwaysLookupLatestDefinitionVersion; + return this; + } + + public boolean isFallbackToDefaultTenant() { + return fallbackToDefaultTenant; + } + + public AbstractEngineConfiguration setFallbackToDefaultTenant(boolean fallbackToDefaultTenant) { + this.fallbackToDefaultTenant = fallbackToDefaultTenant; + return this; + } + + public AbstractEngineConfiguration setDefaultTenantValue(String defaultTenantValue) { + this.defaultTenantProvider = (tenantId, scope, scopeKey) -> defaultTenantValue; + return this; + } + + public DefaultTenantProvider getDefaultTenantProvider() { + return defaultTenantProvider; + } + + public AbstractEngineConfiguration setDefaultTenantProvider(DefaultTenantProvider defaultTenantProvider) { + this.defaultTenantProvider = defaultTenantProvider; + return this; + } + + public boolean isEnableLogSqlExecutionTime() { + return enableLogSqlExecutionTime; + } + + public void setEnableLogSqlExecutionTime(boolean enableLogSqlExecutionTime) { + this.enableLogSqlExecutionTime = enableLogSqlExecutionTime; + } + + public Map, SessionFactory> getSessionFactories() { + return sessionFactories; + } + + public AbstractEngineConfiguration setSessionFactories(Map, SessionFactory> sessionFactories) { + this.sessionFactories = sessionFactories; + return this; + } + + public String getDatabaseSchemaUpdate() { + return databaseSchemaUpdate; + } + + public AbstractEngineConfiguration setDatabaseSchemaUpdate(String databaseSchemaUpdate) { + this.databaseSchemaUpdate = databaseSchemaUpdate; + return this; + } + + public boolean isUseLockForDatabaseSchemaUpdate() { + return useLockForDatabaseSchemaUpdate; + } + + public AbstractEngineConfiguration setUseLockForDatabaseSchemaUpdate(boolean useLockForDatabaseSchemaUpdate) { + this.useLockForDatabaseSchemaUpdate = useLockForDatabaseSchemaUpdate; + return this; + } + + public boolean isEnableEventDispatcher() { + return enableEventDispatcher; + } + + public AbstractEngineConfiguration setEnableEventDispatcher(boolean enableEventDispatcher) { + this.enableEventDispatcher = enableEventDispatcher; + return this; + } + + public FlowableEventDispatcher getEventDispatcher() { + return eventDispatcher; + } + + public AbstractEngineConfiguration setEventDispatcher(FlowableEventDispatcher eventDispatcher) { + this.eventDispatcher = eventDispatcher; + return this; + } + + public List getEventListeners() { + return eventListeners; + } + + public AbstractEngineConfiguration setEventListeners(List eventListeners) { + this.eventListeners = eventListeners; + return this; + } + + public Map> getTypedEventListeners() { + return typedEventListeners; + } + + public AbstractEngineConfiguration setTypedEventListeners(Map> typedEventListeners) { + this.typedEventListeners = typedEventListeners; + return this; + } + + public List getAdditionalEventDispatchActions() { + return additionalEventDispatchActions; + } + + public AbstractEngineConfiguration setAdditionalEventDispatchActions(List additionalEventDispatchActions) { + this.additionalEventDispatchActions = additionalEventDispatchActions; + return this; + } + + public void initEventDispatcher() { + if (this.eventDispatcher == null) { + this.eventDispatcher = new FlowableEventDispatcherImpl(); + } + + initAdditionalEventDispatchActions(); + + this.eventDispatcher.setEnabled(enableEventDispatcher); + + initEventListeners(); + initTypedEventListeners(); + } + + protected void initEventListeners() { + if (eventListeners != null) { + for (FlowableEventListener listenerToAdd : eventListeners) { + this.eventDispatcher.addEventListener(listenerToAdd); + } + } + } + + protected void initAdditionalEventDispatchActions() { + if (this.additionalEventDispatchActions == null) { + this.additionalEventDispatchActions = new ArrayList<>(); + } + } + + protected void initTypedEventListeners() { + if (typedEventListeners != null) { + for (Map.Entry> listenersToAdd : typedEventListeners.entrySet()) { + // Extract types from the given string + FlowableEngineEventType[] types = FlowableEngineEventType.getTypesFromString(listenersToAdd.getKey()); + + for (FlowableEventListener listenerToAdd : listenersToAdd.getValue()) { + this.eventDispatcher.addEventListener(listenerToAdd, types); + } + } + } + } + + public boolean isLoggingSessionEnabled() { + return loggingListener != null; + } + + public LoggingListener getLoggingListener() { + return loggingListener; + } + + public void setLoggingListener(LoggingListener loggingListener) { + this.loggingListener = loggingListener; + } + + public Clock getClock() { + return clock; + } + + public AbstractEngineConfiguration setClock(Clock clock) { + this.clock = clock; + return this; + } + + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + public AbstractEngineConfiguration setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + return this; + } + + public int getMaxLengthString() { + if (maxLengthStringVariableType == -1) { + if ("oracle".equalsIgnoreCase(databaseType)) { + return DEFAULT_ORACLE_MAX_LENGTH_STRING; + } else { + return DEFAULT_GENERIC_MAX_LENGTH_STRING; + } + } else { + return maxLengthStringVariableType; + } + } + + public int getMaxLengthStringVariableType() { + return maxLengthStringVariableType; + } + + public AbstractEngineConfiguration setMaxLengthStringVariableType(int maxLengthStringVariableType) { + this.maxLengthStringVariableType = maxLengthStringVariableType; + return this; + } + + public PropertyDataManager getPropertyDataManager() { + return propertyDataManager; + } + + public Duration getLockPollRate() { + return lockPollRate; + } + + public AbstractEngineConfiguration setLockPollRate(Duration lockPollRate) { + this.lockPollRate = lockPollRate; + return this; + } + + public Duration getSchemaLockWaitTime() { + return schemaLockWaitTime; + } + + public void setSchemaLockWaitTime(Duration schemaLockWaitTime) { + this.schemaLockWaitTime = schemaLockWaitTime; + } + + public AbstractEngineConfiguration setPropertyDataManager(PropertyDataManager propertyDataManager) { + this.propertyDataManager = propertyDataManager; + return this; + } + + public PropertyEntityManager getPropertyEntityManager() { + return propertyEntityManager; + } + + public AbstractEngineConfiguration setPropertyEntityManager(PropertyEntityManager propertyEntityManager) { + this.propertyEntityManager = propertyEntityManager; + return this; + } + + public ByteArrayDataManager getByteArrayDataManager() { + return byteArrayDataManager; + } + + public AbstractEngineConfiguration setByteArrayDataManager(ByteArrayDataManager byteArrayDataManager) { + this.byteArrayDataManager = byteArrayDataManager; + return this; + } + + public ByteArrayEntityManager getByteArrayEntityManager() { + return byteArrayEntityManager; + } + + public AbstractEngineConfiguration setByteArrayEntityManager(ByteArrayEntityManager byteArrayEntityManager) { + this.byteArrayEntityManager = byteArrayEntityManager; + return this; + } + + public TableDataManager getTableDataManager() { + return tableDataManager; + } + + public AbstractEngineConfiguration setTableDataManager(TableDataManager tableDataManager) { + this.tableDataManager = tableDataManager; + return this; + } + + public List getDeployers() { + return deployers; + } + + public AbstractEngineConfiguration setDeployers(List deployers) { + this.deployers = deployers; + return this; + } + + public List getCustomPreDeployers() { + return customPreDeployers; + } + + public AbstractEngineConfiguration setCustomPreDeployers(List customPreDeployers) { + this.customPreDeployers = customPreDeployers; + return this; + } + + public List getCustomPostDeployers() { + return customPostDeployers; + } + + public AbstractEngineConfiguration setCustomPostDeployers(List customPostDeployers) { + this.customPostDeployers = customPostDeployers; + return this; + } + + public boolean isEnableConfiguratorServiceLoader() { + return enableConfiguratorServiceLoader; + } + + public AbstractEngineConfiguration setEnableConfiguratorServiceLoader(boolean enableConfiguratorServiceLoader) { + this.enableConfiguratorServiceLoader = enableConfiguratorServiceLoader; + return this; + } + + public List getConfigurators() { + return configurators; + } + + public AbstractEngineConfiguration addConfigurator(EngineConfigurator configurator) { + if (configurators == null) { + configurators = new ArrayList<>(); + } + configurators.add(configurator); + return this; + } + + /** + * @return All {@link EngineConfigurator} instances. Will only contain values after init of the engine. + * Use the {@link #getConfigurators()} or {@link #addConfigurator(EngineConfigurator)} methods otherwise. + */ + public List getAllConfigurators() { + return allConfigurators; + } + + public AbstractEngineConfiguration setConfigurators(List configurators) { + this.configurators = configurators; + return this; + } + + public EngineConfigurator getIdmEngineConfigurator() { + return idmEngineConfigurator; + } + + public AbstractEngineConfiguration setIdmEngineConfigurator(EngineConfigurator idmEngineConfigurator) { + this.idmEngineConfigurator = idmEngineConfigurator; + return this; + } + + public EngineConfigurator getEventRegistryConfigurator() { + return eventRegistryConfigurator; + } + + public AbstractEngineConfiguration setEventRegistryConfigurator(EngineConfigurator eventRegistryConfigurator) { + this.eventRegistryConfigurator = eventRegistryConfigurator; + return this; + } + + public AbstractEngineConfiguration setForceCloseMybatisConnectionPool(boolean forceCloseMybatisConnectionPool) { + this.forceCloseMybatisConnectionPool = forceCloseMybatisConnectionPool; + return this; + } + + public boolean isForceCloseMybatisConnectionPool() { + return forceCloseMybatisConnectionPool; + } +} diff --git a/zt-module-bpm-server/src/main/resources/META-INF/package-info.md b/zt-module-bpm-server/src/main/resources/META-INF/package-info.md new file mode 100644 index 0000000..1932c7a --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/META-INF/package-info.md @@ -0,0 +1 @@ +防止IDEA将`.`和`/`混为一谈 \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database b/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database new file mode 100644 index 0000000..0ccf224 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database @@ -0,0 +1,20 @@ +liquibase.database.core.CockroachDatabase +liquibase.database.core.DB2Database +liquibase.database.core.Db2zDatabase +liquibase.database.core.DerbyDatabase +#liquibase.database.core.Firebird3Database +liquibase.database.core.FirebirdDatabase +liquibase.database.core.H2Database +liquibase.database.core.HsqlDatabase +liquibase.database.core.InformixDatabase +liquibase.database.core.Ingres9Database +liquibase.database.core.MSSQLDatabase +liquibase.database.core.MariaDBDatabase +liquibase.database.core.MockDatabase +liquibase.database.core.MySQLDatabase +liquibase.database.core.OracleDatabase +liquibase.database.core.PostgresDatabase +liquibase.database.core.SQLiteDatabase +liquibase.database.core.SybaseASADatabase +liquibase.database.core.SybaseDatabase +liquibase.database.core.UnsupportedDatabase diff --git a/zt-module-bpm-server/src/main/resources/application-dev.yaml b/zt-module-bpm-server/src/main/resources/application-dev.yaml new file mode 100644 index 0000000..b6b318c --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/application-dev.yaml @@ -0,0 +1,94 @@ +--- #################### 数据库相关配置 #################### +spring: + # 数据源配置项 + autoconfigure: + exclude: + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 5 # 初始连接数 + min-idle: 10 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: jygk-test + password: Zgty@0527 + slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: jygk-test + password: Zgty@0527 + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 172.16.46.63 # 地址 + port: 30379 # 端口 + database: 1 # 数据库索引 +# password: 123456 # 密码,建议生产环境开启 + +--- #################### MQ 消息队列相关配置 #################### + +--- #################### 定时任务相关配置 #################### +xxl: + job: + admin: + addresses: http://172.16.46.63:30082/xxl-job-admin # 调度中心部署跟地址 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +--- #################### 芋道相关配置 #################### + + diff --git a/zt-module-bpm-server/src/main/resources/application-local.yaml b/zt-module-bpm-server/src/main/resources/application-local.yaml new file mode 100644 index 0000000..2bf6df5 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/application-local.yaml @@ -0,0 +1,111 @@ +--- #################### 数据库相关配置 #################### +spring: + # 数据源配置项 + autoconfigure: + exclude: + - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: jygk-test + password: Zgty@0527 + slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: jygk-test + password: Zgty@0527 + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 172.16.46.64 # 地址 + port: 30379 # 端口 + database: 0 # 数据库索引 +# password: 123456 # 密码,建议生产环境开启 + +--- #################### MQ 消息队列相关配置 #################### + +--- #################### 定时任务相关配置 #################### + +xxl: + job: + enabled: false # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://172.16.46.63:30082/xxl-job-admin # 调度中心部署跟地址 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +# 日志文件配置 +logging: + level: + # 配置自己写的 MyBatis Mapper 打印日志 + com.zt.plat.module.bpm.dal.mysql: debug + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +zt: + env: # 多环境的配置项 + tag: ${HOSTNAME} + security: + mock-enable: true + access-log: # 访问日志的配置项 + enable: false \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/resources/application.yaml b/zt-module-bpm-server/src/main/resources/application.yaml new file mode 100644 index 0000000..07020c5 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/application.yaml @@ -0,0 +1,150 @@ +spring: + application: + name: bpm-server + + profiles: + active: ${env.name} + #统一nacos配置,使用 profile 管理 + cloud: + nacos: + server-addr: ${config.server-addr} # Nacos 服务器地址 + username: ${config.username} # Nacos 账号 + password: ${config.password} # Nacos 密码 + discovery: # 【配置中心】配置项 + namespace: ${config.namespace} # 命名空间。这里使用 maven Profile 资源过滤进行动态替换 + group: ${config.group} # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # 服务实例的版本号,可用于灰度发布 + config: # 【注册中心】配置项 + namespace: ${config.namespace} # 命名空间。这里使用 maven Profile 资源过滤进行动态替换 + group: ${config.group} # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + + main: + allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 + allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务 + + config: + import: + - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置 + - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置 + + # Servlet 配置 + servlet: + # 文件上传相关配置项 + multipart: + max-file-size: 16MB # 单个文件大小 + max-request-size: 32MB # 设置总上传的文件大小 + + # Jackson 配置项 + jackson: + serialization: + write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 + write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 + fail-on-empty-beans: false # 允许序列化无属性的 Bean + + # Cache 配置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 小时 + +server: + port: 48083 + +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + +--- #################### 接口文档配置 #################### + +springdoc: + api-docs: + enabled: true # 1. 是否开启 Swagger 接文档的元数据 + path: /v3/api-docs + swagger-ui: + enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面 + path: /swagger-ui + default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: false # TODO 芋艿:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 + setting: + language: zh_cn + +# 工作流 Flowable 配置 +flowable: + # 1. false: 默认值,Flowable 启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常 + # 2. true: 启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表 + # 3. create_drop: 启动时自动创建表,关闭时自动删除表 + # 4. drop_create: 启动时,删除旧表,再创建新表 + database-schema-update: false # 设置为 false,可通过 https://github.com/flowable/flowable-sql 初始化 + db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置 + check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程 + history-level: audit # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数 + +# MyBatis Plus 的配置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 + # id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库 + # id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库 + # id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制台的 Banner 打印 + type-aliases-package: ${zt.info.base-package}.dal.dataobject + encryptor: + password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成 + +mybatis-plus-join: + banner: false # 关闭控制台的 Banner 打印 + +# Spring Data Redis 配置 +spring: + data: + redis: + repositories: + enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度 + +# VO 转换(数据翻译)相关 +easy-trans: + is-enable-global: true # 启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口 + +--- #################### RPC 远程调用相关配置 #################### + +--- #################### MQ 消息队列相关配置 #################### + +--- #################### 定时任务相关配置 #################### + +xxl: + job: + executor: + appname: ${spring.application.name} # 执行器 AppName + logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 + accessToken: default_token # 执行器通讯TOKEN + +--- #################### 芋道相关配置 #################### + +zt: + info: + version: 1.0.0 + base-package: com.zt.plat.module.bpm + web: + admin-ui: + url: http://dashboard.zt.iocoder.cn # Admin 管理后台 UI 的地址 + xss: + enable: false + exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 + swagger: + title: 管理后台 + description: 提供管理员管理的所有功能 + version: ${zt.info.version} + tenant: # 多租户相关配置项 + enable: true + +debug: false diff --git a/zt-module-bpm-server/src/main/resources/logback-spring.xml b/zt-module-bpm-server/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..0e55141 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/logback-spring.xml @@ -0,0 +1,76 @@ + + + + + + + + + +       + + + ${PATTERN_DEFAULT} + + + + + + + + + + ${PATTERN_DEFAULT} + + + + ${LOG_FILE} + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30} + + + + + + 0 + + 256 + + + + + + + + ${PATTERN_DEFAULT} + + + + + + + + + + + + + + + + + + + + + + diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java new file mode 100644 index 0000000..83a42f8 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java @@ -0,0 +1,274 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other.BpmTaskCandidateAssignEmptyStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.ExtensionElement; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Spy; +import org.mockito.internal.util.collections.Sets; + +import java.util.*; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomString; +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +/** + * {@link BpmTaskCandidateInvoker} 的单元测试 + * + * @author ZT + */ +public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest { + + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Mock + private AdminUserApi adminUserApi; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Spy + private BpmTaskCandidateStrategy userStrategy; + @Mock + private BpmTaskCandidateAssignEmptyStrategy emptyStrategy; + + @Spy + private List strategyList; + + @BeforeEach + public void setUp() { + userStrategy = new BpmTaskCandidateUserStrategy(); // 创建 strategy 实例 + when(emptyStrategy.getStrategy()).thenReturn(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY); + strategyList = List.of(userStrategy, emptyStrategy); // 创建 strategyList + taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi); + } + + /** + * 场景:成功计算到候选人,但是移除了发起人的用户 + */ + @Test + public void testCalculateUsersByTask_some() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + // 准备参数 + String param = "1,2"; + DelegateExecution execution = mock(DelegateExecution.class); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + String processInstanceId = randomString(); + when(execution.getProcessInstanceId()).thenReturn(processInstanceId); + when(execution.getCurrentFlowElement()).thenReturn(userTask); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString()); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM))) + .thenReturn(param); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 移除发起人的用户 + springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class)) + .thenReturn(processInstanceService); + ProcessInstance processInstance = mock(ProcessInstance.class); + when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn("1"); + mockFlowElementExtensionElement(userTask, BpmnModelConstants.USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, + String.valueOf(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())); + + // 调用 + Set results = taskCandidateInvoker.calculateUsersByTask(execution); + // 断言 + assertEquals(asSet(2L), results); + } + } + + /** + * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配 + */ + @Test + public void testCalculateUsersByTask_none() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + // 准备参数 + String param = "1,2"; + DelegateExecution execution = mock(DelegateExecution.class); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + String processInstanceId = randomString(); + when(execution.getProcessInstanceId()).thenReturn(processInstanceId); + when(execution.getCurrentFlowElement()).thenReturn(userTask); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString()); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM))) + .thenReturn(param); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 方法(empty) + when(emptyStrategy.calculateUsersByTask(same(execution), same(param))) + .thenReturn(Sets.newSet(2L)); + // mock 移除发起人的用户 + springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class)) + .thenReturn(processInstanceService); + ProcessInstance processInstance = mock(ProcessInstance.class); + when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn("1"); + + // 调用 + Set results = taskCandidateInvoker.calculateUsersByTask(execution); + // 断言 + assertEquals(asSet(2L), results); + } + } + + /** + * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配 + */ + @Test + public void testCalculateUsersByActivity_some() { + try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { + // 准备参数 + String param = "1,2"; + BpmnModel bpmnModel = mock(BpmnModel.class); + String activityId = randomString(); + Long startUserId = 1L; + String processDefinitionId = randomString(); + Map processVariables = new HashMap<>(); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy()); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask))) + .thenReturn(param); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 移除发起人的用户 + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignStartUserHandlerType(same(userTask))) + .thenReturn(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType()); + + // 调用 + Set results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, + startUserId, processDefinitionId, processVariables); + // 断言 + assertEquals(asSet(2L), results); + } + } + + /** + * 场景:成功计算到候选人,但是移除了发起人的用户 + */ + @Test + public void testCalculateUsersByActivity_none() { + try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { + // 准备参数 + String param = "1,2"; + BpmnModel bpmnModel = mock(BpmnModel.class); + String activityId = randomString(); + Long startUserId = 1L; + String processDefinitionId = randomString(); + Map processVariables = new HashMap<>(); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy()); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask))) + .thenReturn(param); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 方法(empty) + when(emptyStrategy.calculateUsersByActivity(same(bpmnModel), eq(activityId), + eq(param), same(startUserId), same(processDefinitionId), same(processVariables))) + .thenReturn(Sets.newSet(2L)); + + // 调用 + Set results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, + startUserId, processDefinitionId, processVariables); + // 断言 + assertEquals(asSet(2L), results); + } + } + + private static void mockFlowElementExtensionElement(FlowElement element, String name, String value) { + if (value == null) { + return; + } + ExtensionElement extensionElement = new ExtensionElement(); + extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); + extensionElement.setElementText(value); + extensionElement.setName(name); + // mock + Map> extensionElements = element.getExtensionElements(); + if (extensionElements == null) { + extensionElements = new LinkedHashMap<>(); + } + extensionElements.put(name, Collections.singletonList(extensionElement)); + when(element.getExtensionElements()).thenReturn(extensionElements); + } + + @Test + public void testRemoveDisableUsers() { + // 准备参数. 1L 可以找到;2L 是禁用的;3L 找不到 + Set assigneeUserIds = asSet(1L, 2L, 3L); + // mock 方法 + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap); + + // 调用 + taskCandidateInvoker.removeDisableUsers(assigneeUserIds); + // 断言 + assertEquals(asSet(1L), assigneeUserIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java new file mode 100644 index 0000000..d7d1151 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java @@ -0,0 +1,107 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.expression; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Collections; +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskAssignLeaderExpression expression; + + @Mock + private AdminUserApi adminUserApi; + @Mock + private DeptApi deptApi; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Test + public void testCalculateUsers_noDept() { + // 准备参数 + DelegateExecution execution = mockDelegateExecution(1L); + // mock 方法(startUser) + AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptIds(Collections.singletonList(10L))); + when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser)); + // mock 方法(getStartUserDept)没有部门 + when(deptApi.getDept(eq(10L))).thenReturn(success(null)); + + // 调用 + Set result = expression.calculateUsers(execution, 1); + // 断言 + assertEquals(0, result.size()); + } + + @Test + public void testCalculateUsers_noParentDept() { + // 准备参数 + DelegateExecution execution = mockDelegateExecution(1L); + // mock 方法(startUser) + AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptIds(Collections.singletonList(10L))); + when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser)); + DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L) + .setLeaderUserId(20L)); + // mock 方法(getDept) + when(deptApi.getDept(eq(10L))).thenReturn(success(startUserDept)); + when(deptApi.getDept(eq(100L))).thenReturn(success(null)); + + // 调用 + Set result = expression.calculateUsers(execution, 2); + // 断言 + assertEquals(asSet(20L), result); + } + + @Test + public void testCalculateUsers_existParentDept() { + // 准备参数 + DelegateExecution execution = mockDelegateExecution(1L); + // mock 方法(startUser) + AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptIds(Collections.singletonList(10L))); + when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser)); + DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L) + .setLeaderUserId(20L)); + when(deptApi.getDept(eq(10L))).thenReturn(success(startUserDept)); + // mock 方法(父 dept) + DeptRespDTO parentDept = randomPojo(DeptRespDTO.class, o -> o.setId(100L).setParentId(1000L) + .setLeaderUserId(200L)); + when(deptApi.getDept(eq(100L))).thenReturn(success(parentDept)); + + // 调用 + Set result = expression.calculateUsers(execution, 2); + // 断言 + assertEquals(asSet(200L), result); + } + + @SuppressWarnings("SameParameterValue") + private DelegateExecution mockDelegateExecution(Long startUserId) { + ExecutionEntityImpl execution = new ExecutionEntityImpl(); + execution.setProcessInstanceId(randomString()); + // mock 返回 startUserId + ExecutionEntityImpl processInstance = new ExecutionEntityImpl(); + processInstance.setStartUserId(String.valueOf(startUserId)); + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))) + .thenReturn(processInstance); + return execution; + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java new file mode 100644 index 0000000..63d4c39 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import org.assertj.core.util.Sets; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.stubbing.Answer; + +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateDeptLeaderMultiStrategy strategy; + + @Mock + private DeptApi deptApi; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "10,20|2"; + // mock 方法 + when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult>) invocationOnMock -> { + Long deptId = invocationOnMock.getArgument(0); + return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1))); + }); + + // 调用 + Set userIds = strategy.calculateUsers(param); + // 断言结果 + assertEquals(Sets.newLinkedHashSet(11L, 1001L, 21L, 2001L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java new file mode 100644 index 0000000..a7bbbf1 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java @@ -0,0 +1,44 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import org.assertj.core.util.Sets; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateDeptLeaderStrategy strategy; + + @Mock + private DeptApi deptApi; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "10,20"; + // mock 方法 + when(deptApi.getDeptList(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList( + randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(10L).setLeaderUserId(11L)), + randomPojo(DeptRespDTO.class, o -> o.setId(20L).setParentId(20L).setLeaderUserId(21L))))); + + // 调用 + Set userIds = strategy.calculateUsers(param); + // 断言结果 + assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java new file mode 100644 index 0000000..443eeb7 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java @@ -0,0 +1,47 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.assertj.core.util.Sets; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateDeptMemberStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateDeptMemberStrategy strategy; + + @Mock + private DeptApi deptApi; + @Mock + private AdminUserApi adminUserApi; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "10,20"; + // mock 方法 + when(adminUserApi.getUserListByDeptIds(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList( + randomPojo(AdminUserRespDTO.class, o -> o.setId(11L)), + randomPojo(AdminUserRespDTO.class, o -> o.setId(21L))))); + + // 调用 + Set userIds = strategy.calculateUsers(param); + // 断言结果 + assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java new file mode 100644 index 0000000..4a34602 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java @@ -0,0 +1,85 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.assertj.core.util.Sets; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.stubbing.Answer; + +import java.util.Collections; +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateStartUserDeptLeaderMultiStrategy strategy; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Mock + private AdminUserApi adminUserApi; + @Mock + private DeptApi deptApi; + + @Test + public void testCalculateUsersByTask() { + // 准备参数 + String param = "2"; + // mock 方法(获得流程发起人) + Long startUserId = 1L; + ProcessInstance processInstance = mock(ProcessInstance.class); + DelegateExecution execution = mock(DelegateExecution.class); + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn(startUserId.toString()); + // mock 方法(获取发起人的 multi 部门负责人) + mockGetStartUserDept(startUserId); + + // 调用 + Set userIds = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds); + } + + @Test + public void testCalculateUsersByActivity() { + // 准备参数 + String param = "2"; + // mock 方法 + Long startUserId = 1L; + mockGetStartUserDept(startUserId); + + // 调用 + Set userIds = strategy.calculateUsersByActivity(null, null, param, + startUserId, null, null); + // 断言 + assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds); + } + + private void mockGetStartUserDept(Long startUserId) { + when(adminUserApi.getUser(eq(startUserId))).thenReturn( + success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptIds(Collections.singletonList(10L))))); + when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult>) invocationOnMock -> { + Long deptId = invocationOnMock.getArgument(0); + return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1))); + }); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java new file mode 100644 index 0000000..f829b9e --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java @@ -0,0 +1,85 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.assertj.core.util.Sets; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.stubbing.Answer; + +import java.util.Collections; +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateStartUserDeptLeaderStrategy strategy; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Mock + private AdminUserApi adminUserApi; + @Mock + private DeptApi deptApi; + + @Test + public void testCalculateUsersByTask() { + // 准备参数 + String param = "2"; + // mock 方法(获得流程发起人) + Long startUserId = 1L; + ProcessInstance processInstance = mock(ProcessInstance.class); + DelegateExecution execution = mock(DelegateExecution.class); + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn(startUserId.toString()); + // mock 方法(获取发起人的部门负责人) + mockGetStartUserDeptLeader(startUserId); + + // 调用 + Set userIds = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(Sets.newLinkedHashSet(1001L), userIds); + } + + @Test + public void testGetStartUserDeptLeader() { + // 准备参数 + String param = "2"; + // mock 方法 + Long startUserId = 1L; + mockGetStartUserDeptLeader(startUserId); + + // 调用 + Set userIds = strategy.calculateUsersByActivity(null, null, param, + startUserId, null, null); + // 断言 + assertEquals(Sets.newLinkedHashSet(1001L), userIds); + } + + private void mockGetStartUserDeptLeader(Long startUserId) { + when(adminUserApi.getUser(eq(startUserId))).thenReturn( + success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptIds(Collections.singletonList(10L))))); + when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult>) invocationOnMock -> { + Long deptId = invocationOnMock.getArgument(0); + return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1))); + }); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java new file mode 100644 index 0000000..2769280 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java @@ -0,0 +1,68 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.map.MapUtil; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import org.assertj.core.util.Sets; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateStartUserSelectStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateStartUserSelectStrategy strategy; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Test + public void testCalculateUsersByTask() { + // 准备参数 + String param = "2"; + // mock 方法(获得流程发起人) + ProcessInstance processInstance = mock(ProcessInstance.class); + DelegateExecution execution = mock(DelegateExecution.class); + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); + when(execution.getCurrentActivityId()).thenReturn("activity_001"); + // mock 方法(FlowableUtils) + Map processVariables = new HashMap<>(); + processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, + MapUtil.of("activity_001", List.of(1L, 2L))); + when(processInstance.getProcessVariables()).thenReturn(processVariables); + + // 调用 + Set userIds = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds); + } + + @Test + public void testCalculateUsersByActivity() { + // 准备参数 + String activityId = "activity_001"; + Map processVariables = new HashMap<>(); + processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, + MapUtil.of("activity_001", List.of(1L, 2L))); + + // 调用 + Set userIds = strategy.calculateUsersByActivity(null, activityId, null, + null, null, processVariables); + // 断言 + assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java new file mode 100644 index 0000000..1bf5725 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java @@ -0,0 +1,88 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; + +import cn.hutool.core.collection.ListUtil; +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.engine.delegate.DelegateExecution; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; + +import java.util.Set; + +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +public class BpmTaskCandidateAssignEmptyStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateAssignEmptyStrategy strategy; + + @Mock + private BpmProcessDefinitionService processDefinitionService; + + @Test + public void testCalculateUsersByTask() { + try (MockedStatic flowableUtilMockedStatic = mockStatic(FlowableUtils.class); + MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { + // 准备参数 + DelegateExecution execution = mock(DelegateExecution.class); + String param = randomString(); + // mock 方法(execution) + String processDefinitionId = randomString(); + when(execution.getProcessDefinitionId()).thenReturn(processDefinitionId); + FlowElement flowElement = mock(FlowElement.class); + when(execution.getCurrentFlowElement()).thenReturn(flowElement); + // mock 方法(parseAssignEmptyHandlerType) + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement))) + .thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType()); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerUserIds(same(flowElement))) + .thenReturn(ListUtil.of(1L, 2L)); + + // 调用 + Set userIds = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(SetUtils.asSet(1L, 2L), userIds); + } + + } + + @Test + public void testCalculateUsersByActivity() { + try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { + // 准备参数 + String processDefinitionId = randomString(); + String activityId = randomString(); + String param = randomString(); + // mock 方法(getFlowElementById) + FlowElement flowElement = mock(FlowElement.class); + BpmnModel bpmnModel = mock(BpmnModel.class); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))) + .thenReturn(flowElement); + // mock 方法(parseAssignEmptyHandlerType) + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement))) + .thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType()); + // mock 方法(getProcessDefinitionInfo) + BpmProcessDefinitionInfoDO processDefinition = randomPojo(BpmProcessDefinitionInfoDO.class, + o -> o.setManagerUserIds(ListUtil.of(1L, 2L))); + when(processDefinitionService.getProcessDefinitionInfo(eq(processDefinitionId))).thenReturn(processDefinition); + + // 调用 + Set userIds = strategy.calculateUsersByActivity(bpmnModel, activityId, param, + null, processDefinitionId, null); + // 断言 + assertEquals(SetUtils.asSet(1L, 2L), userIds); + } + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java new file mode 100644 index 0000000..d9ad569 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java @@ -0,0 +1,61 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import org.flowable.engine.delegate.DelegateExecution; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.MockedStatic; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +@Disabled // TODO 芋艿:临时注释 +public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateExpressionStrategy strategy; + + @Test + public void testCalculateUsersByTask() { + try (MockedStatic flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) { + // 准备参数 + String param = "1,2"; + DelegateExecution execution = mock(DelegateExecution.class); + // mock 方法 + flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(execution), eq(param))) + .thenReturn(asSet(1L, 2L)); + + // 调用 + Set results = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(asSet(1L, 2L), results); + } + } + + @Test + public void testCalculateUsersByActivity() { + try (MockedStatic flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) { + // 准备参数 + String param = "1,2"; + Map processVariables = new HashMap<>(); + // mock 方法 + flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(processVariables), eq(param))) + .thenReturn(asSet(1L, 2L)); + + // 调用 + Set results = strategy.calculateUsersByActivity(null, null, param, + null, null, processVariables); + // 断言 + assertEquals(asSet(1L, 2L), results); + } + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java new file mode 100644 index 0000000..1bb03b5 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java @@ -0,0 +1,44 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import com.zt.plat.module.bpm.service.definition.BpmUserGroupService; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Arrays; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@Disabled // TODO 芋艿:临时注释 +public class BpmTaskCandidateGroupStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateGroupStrategy strategy; + + @Mock + private BpmUserGroupService userGroupService; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "1,2"; + // mock 方法 + BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(11L, 12L))); + BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(21L, 22L))); + when(userGroupService.getUserGroupList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(userGroup1, userGroup2)); + + // 调用 + Set userIds = strategy.calculateUsersByTask(null, param); + // 断言 + assertEquals(asSet(11L, 12L, 21L, 22L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java new file mode 100644 index 0000000..8560052 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java @@ -0,0 +1,48 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.api.dept.PostApi; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.List; +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@Disabled // TODO 芋艿:临时注释 +public class BpmTaskCandidatePostStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidatePostStrategy strategy; + + @Mock + private PostApi postApi; + @Mock + private AdminUserApi adminUserApi; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "1,2"; + // mock 方法 + List users = convertList(asSet(11L, 22L), + id -> new AdminUserRespDTO().setId(id)); + when(adminUserApi.getUserListByPostIds(eq(asSet(1L, 2L)))).thenReturn(success(users)); + + // 调用 + Set userIds = strategy.calculateUsersByTask(null, param); + // 断言 + assertEquals(asSet(11L, 22L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java new file mode 100644 index 0000000..9af421a --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java @@ -0,0 +1,44 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.api.permission.PermissionApi; +import com.zt.plat.module.system.api.permission.RoleApi; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@Disabled // TODO 芋艿:临时注释 +public class BpmTaskCandidateRoleStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateRoleStrategy strategy; + + @Mock + private RoleApi roleApi; + @Mock + private PermissionApi permissionApi; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "1,2"; + // mock 方法 + when(permissionApi.getUserRoleIdListByRoleIds(eq(asSet(1L, 2L)))) + .thenReturn(success(asSet(11L, 22L))); + + // 调用 + Set userIds = strategy.calculateUsersByTask(null, param); + // 断言 + assertEquals(asSet(11L, 22L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java new file mode 100644 index 0000000..79c08ba --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java @@ -0,0 +1,56 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import org.assertj.core.util.Sets; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateStartUserStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateStartUserStrategy strategy; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Test + public void testCalculateUsersByTask() { + // 准备参数 + String param = "2"; + // mock 方法(获得流程发起人) + Long startUserId = 1L; + ProcessInstance processInstance = mock(ProcessInstance.class); + DelegateExecution execution = mock(DelegateExecution.class); + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn(startUserId.toString()); + + // 调用 + Set userIds = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(Sets.newLinkedHashSet(startUserId), userIds); + } + + @Test + public void testCalculateUsersByActivity() { + // 准备参数 + Long startUserId = 1L; + + // 调用 + Set userIds = strategy.calculateUsersByActivity(null, null, null, + startUserId, null, null); + // 断言 + assertEquals(Sets.newLinkedHashSet(startUserId), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java new file mode 100644 index 0000000..91e19c5 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; + +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Disabled // TODO 芋艿:临时注释 +public class BpmTaskCandidateUserStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateUserStrategy strategy; + + @Test + public void test() { + // 准备参数 + String param = "1,2"; + + // 调用 + Set userIds = strategy.calculateUsersByTask(null, param); + // 断言 + assertEquals(asSet(1L, 2L), userIds); + } + + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java new file mode 100644 index 0000000..621d68e --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java @@ -0,0 +1,136 @@ +package com.zt.plat.module.bpm.service.category; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.test.core.ut.BaseDbUnitTest; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.mysql.category.BpmCategoryMapper; +import com.zt.plat.module.bpm.service.definition.BpmCategoryServiceImpl; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import static com.zt.plat.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static com.zt.plat.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static com.zt.plat.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static com.zt.plat.framework.test.core.util.AssertUtils.assertPojoEquals; +import static com.zt.plat.framework.test.core.util.AssertUtils.assertServiceException; +import static com.zt.plat.framework.test.core.util.RandomUtils.*; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link BpmCategoryServiceImpl} 的单元测试类 + * + * @author ZT + */ +@Import(BpmCategoryServiceImpl.class) +public class BpmCategoryServiceImplTest extends BaseDbUnitTest { + + @Resource + private BpmCategoryServiceImpl categoryService; + + @Resource + private BpmCategoryMapper categoryMapper; + + @Test + public void testCreateCategory_success() { + // 准备参数 + BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null) + .setStatus(randomCommonStatus()); + + // 调用 + Long categoryId = categoryService.createCategory(createReqVO); + // 断言 + assertNotNull(categoryId); + // 校验记录的属性是否正确 + BpmCategoryDO category = categoryMapper.selectById(categoryId); + assertPojoEquals(createReqVO, category, "id"); + } + + @Test + public void testUpdateCategory_success() { + // mock 数据 + BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class); + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 + // 准备参数 + BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> { + o.setId(dbCategory.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + }); + + // 调用 + categoryService.updateCategory(updateReqVO); + // 校验是否更新正确 + BpmCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, category); + } + + @Test + public void testUpdateCategory_notExists() { + // 准备参数 + BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS); + } + + @Test + public void testDeleteCategory_success() { + // mock 数据 + BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class); + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbCategory.getId(); + + // 调用 + categoryService.deleteCategory(id); + // 校验数据不存在了 + assertNull(categoryMapper.selectById(id)); + } + + @Test + public void testDeleteCategory_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS); + } + + @Test + public void testGetCategoryPage() { + // mock 数据 + BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class, o -> { // 等会查询到 + o.setName("芋头"); + o.setCode("xiaodun"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2023, 2, 2)); + }); + categoryMapper.insert(dbCategory); + // 测试 name 不匹配 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName("小盾"))); + // 测试 code 不匹配 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode("tudou"))); + // 测试 status 不匹配 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(buildTime(2024, 2, 2)))); + // 准备参数 + BpmCategoryPageReqVO reqVO = new BpmCategoryPageReqVO(); + reqVO.setName("芋"); + reqVO.setCode("xiao"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = categoryService.getCategoryPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbCategory, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java new file mode 100644 index 0000000..02160d9 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java @@ -0,0 +1,144 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.util.RandomUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.test.core.ut.BaseDbUnitTest; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmFormMapper; +import com.zt.plat.module.bpm.service.definition.dto.BpmFormFieldRespDTO; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.zt.plat.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static com.zt.plat.framework.test.core.util.AssertUtils.assertPojoEquals; +import static com.zt.plat.framework.test.core.util.AssertUtils.assertServiceException; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomLongId; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.FORM_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link BpmFormServiceImpl} 的单元测试类 + * + * @author ZT + */ +@Import(BpmFormServiceImpl.class) +public class BpmFormServiceTest extends BaseDbUnitTest { + + @Resource + private BpmFormServiceImpl formService; + + @Resource + private BpmFormMapper formMapper; + + @Test + public void testCreateForm_success() { + // 准备参数 + BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { + o.setConf("{}"); + o.setFields(randomFields()); + }); + + // 调用 + Long formId = formService.createForm(reqVO); + // 断言 + assertNotNull(formId); + // 校验记录的属性是否正确 + BpmFormDO form = formMapper.selectById(formId); + assertPojoEquals(reqVO, form); + } + + @Test + public void testUpdateForm_success() { + // mock 数据 + BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> { + o.setConf("{}"); + o.setFields(randomFields()); + }); + formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据 + // 准备参数 + BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { + o.setId(dbForm.getId()); // 设置更新的 ID + o.setConf("{'zt': 'yuanma'}"); + o.setFields(randomFields()); + }); + + // 调用 + formService.updateForm(reqVO); + // 校验是否更新正确 + BpmFormDO form = formMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, form); + } + + @Test + public void testUpdateForm_notExists() { + // 准备参数 + BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { + o.setConf("{'zt': 'yuanma'}"); + o.setFields(randomFields()); + }); + + // 调用, 并断言异常 + assertServiceException(() -> formService.updateForm(reqVO), FORM_NOT_EXISTS); + } + + @Test + public void testDeleteForm_success() { + // mock 数据 + BpmFormDO dbForm = randomPojo(BpmFormDO.class); + formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbForm.getId(); + + // 调用 + formService.deleteForm(id); + // 校验数据不存在了 + assertNull(formMapper.selectById(id)); + } + + @Test + public void testDeleteForm_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> formService.deleteForm(id), FORM_NOT_EXISTS); + } + + @Test + public void testGetFormPage() { + // mock 数据 + BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> { // 等会查询到 + o.setName("芋道源码"); + }); + formMapper.insert(dbForm); + // 测试 name 不匹配 + formMapper.insert(cloneIgnoreId(dbForm, o -> o.setName("源码"))); + // 准备参数 + BpmFormPageReqVO reqVO = new BpmFormPageReqVO(); + reqVO.setName("芋道"); + + // 调用 + PageResult pageResult = formService.getFormPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbForm, pageResult.getList().get(0)); + } + + private List randomFields() { + int size = RandomUtil.randomInt(1, 3); + return Stream.iterate(0, i -> i).limit(size) + .map(i -> JsonUtils.toJsonString(randomPojo(BpmFormFieldRespDTO.class))) + .collect(Collectors.toList()); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java new file mode 100644 index 0000000..34a5205 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java @@ -0,0 +1,129 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.test.core.ut.BaseDbUnitTest; +import com.zt.plat.framework.test.core.util.AssertUtils; +import com.zt.plat.framework.test.core.util.RandomUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmUserGroupMapper; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static com.zt.plat.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS; + +/** + * {@link BpmUserGroupServiceImpl} 的单元测试类 + * + * @author ZT + */ +@Import(BpmUserGroupServiceImpl.class) +public class BpmUserGroupServiceTest extends BaseDbUnitTest { + + @Resource + private BpmUserGroupServiceImpl userGroupService; + + @Resource + private BpmUserGroupMapper userGroupMapper; + + @Test + public void testCreateUserGroup_success() { + // 准备参数 + BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class); + + // 调用 + Long userGroupId = userGroupService.createUserGroup(reqVO); + // 断言 + Assertions.assertNotNull(userGroupId); + // 校验记录的属性是否正确 + BpmUserGroupDO userGroup = userGroupMapper.selectById(userGroupId); + AssertUtils.assertPojoEquals(reqVO, userGroup); + } + + @Test + public void testUpdateUserGroup_success() { + // mock 数据 + BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class); + userGroupMapper.insert(dbUserGroup);// @Sql: 先插入出一条存在的数据 + // 准备参数 + BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class, o -> { + o.setId(dbUserGroup.getId()); // 设置更新的 ID + }); + + // 调用 + userGroupService.updateUserGroup(reqVO); + // 校验是否更新正确 + BpmUserGroupDO userGroup = userGroupMapper.selectById(reqVO.getId()); // 获取最新的 + AssertUtils.assertPojoEquals(reqVO, userGroup); + } + + @Test + public void testUpdateUserGroup_notExists() { + // 准备参数 + BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class); + + // 调用, 并断言异常 + AssertUtils.assertServiceException(() -> userGroupService.updateUserGroup(reqVO), USER_GROUP_NOT_EXISTS); + } + + @Test + public void testDeleteUserGroup_success() { + // mock 数据 + BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class); + userGroupMapper.insert(dbUserGroup);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbUserGroup.getId(); + + // 调用 + userGroupService.deleteUserGroup(id); + // 校验数据不存在了 + Assertions.assertNull(userGroupMapper.selectById(id)); + } + + @Test + public void testDeleteUserGroup_notExists() { + // 准备参数 + Long id = RandomUtils.randomLongId(); + + // 调用, 并断言异常 + AssertUtils.assertServiceException(() -> userGroupService.deleteUserGroup(id), USER_GROUP_NOT_EXISTS); + } + + @Test + public void testGetUserGroupPage() { + // mock 数据 + BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class, o -> { // 等会查询到 + o.setName("芋道源码"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2021, 11, 11)); + }); + userGroupMapper.insert(dbUserGroup); + // 测试 name 不匹配 + userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("芋道"))); + // 测试 status 不匹配 + userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setCreateTime(buildTime(2021, 12, 12)))); + // 准备参数 + BpmUserGroupPageReqVO reqVO = new BpmUserGroupPageReqVO(); + reqVO.setName("源码"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime((new LocalDateTime[]{buildTime(2021, 11, 10),buildTime(2021, 11, 12)})); + + // 调用 + PageResult pageResult = userGroupService.getUserGroupPage(reqVO); + // 断言 + Assertions.assertEquals(1, pageResult.getTotal()); + Assertions.assertEquals(1, pageResult.getList().size()); + AssertUtils.assertPojoEquals(dbUserGroup, pageResult.getList().get(0)); + } + +} diff --git a/zt-module-bpm-server/src/test/resources/application-unit-test.yaml b/zt-module-bpm-server/src/test/resources/application-unit-test.yaml new file mode 100644 index 0000000..a669ee0 --- /dev/null +++ b/zt-module-bpm-server/src/test/resources/application-unit-test.yaml @@ -0,0 +1,45 @@ +spring: + main: + lazy-initialization: true # 开启懒加载,加快速度 + banner-mode: off # 单元测试,禁用 Banner + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + datasource: + name: ruoyi-vue-pro + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 + driver-class-name: org.h2.Driver + username: sa + password: + druid: + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 + initial-size: 1 # 单元测试,配置为 1,提升启动速度 + sql: + init: + schema-locations: classpath:/sql/create_tables.sql + +mybatis-plus: + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 + type-aliases-package: ${zt.info.base-package}.dal.dataobject + global-config: + db-config: + id-type: AUTO # H2 主键递增 + +--- #################### 定时任务相关配置 #################### + +--- #################### 配置中心相关配置 #################### + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项(单元测试,禁用 Lock4j) + +--- #################### 监控相关配置 #################### + +--- #################### 芋道相关配置 #################### + +# 芋道配置项,设置当前项目所有自定义的配置 +zt: + info: + base-package: com.zt.plat.module.bpm diff --git a/zt-module-bpm-server/src/test/resources/logback.xml b/zt-module-bpm-server/src/test/resources/logback.xml new file mode 100644 index 0000000..daf756b --- /dev/null +++ b/zt-module-bpm-server/src/test/resources/logback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/zt-module-bpm-server/src/test/resources/sql/clean.sql b/zt-module-bpm-server/src/test/resources/sql/clean.sql new file mode 100644 index 0000000..d4f93bb --- /dev/null +++ b/zt-module-bpm-server/src/test/resources/sql/clean.sql @@ -0,0 +1,3 @@ +DELETE FROM "bpm_form"; +DELETE FROM "bpm_user_group"; +DELETE FROM "bpm_category"; diff --git a/zt-module-bpm-server/src/test/resources/sql/create_tables.sql b/zt-module-bpm-server/src/test/resources/sql/create_tables.sql new file mode 100644 index 0000000..1034962 --- /dev/null +++ b/zt-module-bpm-server/src/test/resources/sql/create_tables.sql @@ -0,0 +1,43 @@ +CREATE TABLE IF NOT EXISTS "bpm_user_group" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(63) NOT NULL, + "description" varchar(255) NOT NULL, + "status" tinyint NOT NULL, + "user_ids" varchar(255) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '用户组'; + +CREATE TABLE IF NOT EXISTS "bpm_category" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(63) NOT NULL, + "code" varchar(63) NOT NULL, + "description" varchar(255) NOT NULL, + "status" tinyint NOT NULL, + "sort" int NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '分类'; + +CREATE TABLE IF NOT EXISTS "bpm_form" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(63) NOT NULL, + "status" tinyint NOT NULL, + "fields" varchar(255) NOT NULL, + "conf" varchar(255) NOT NULL, + "remark" varchar(255), + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '动态表单'; From 8ca9ac7ce8f00ae175983dc46c8d5c2f3c2b9f38 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Mon, 22 Sep 2025 11:55:27 +0800 Subject: [PATCH 02/35] =?UTF-8?q?1.=20=E7=BB=9F=E4=B8=80=E5=8C=85=E5=90=8D?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 27 + zt-module-bpm-api/pom.xml | 47 + .../event/BpmProcessInstanceStatusEvent.java | 41 + ...BpmProcessInstanceStatusEventListener.java | 34 + .../zt/plat/module/bpm/api/package-info.java | 4 + .../bpm/api/task/BpmProcessInstanceApi.java | 28 + .../dto/BpmProcessInstanceCreateReqDTO.java | 36 + .../plat/module/bpm/enums/ApiConstants.java | 23 + .../module/bpm/enums/DictTypeConstants.java | 10 + .../module/bpm/enums/ErrorCodeConstants.java | 87 + .../definition/BpmAutoApproveTypeEnum.java | 32 + .../definition/BpmBoundaryEventTypeEnum.java | 27 + ...ildProcessMultiInstanceSourceTypeEnum.java | 37 + ...BpmChildProcessStartUserEmptyTypeEnum.java | 36 + .../BpmChildProcessStartUserTypeEnum.java | 35 + .../definition/BpmDelayTimerTypeEnum.java | 31 + .../definition/BpmFieldPermissionEnum.java | 33 + .../BpmHttpRequestParamTypeEnum.java | 31 + .../definition/BpmModelFormTypeEnum.java | 32 + .../enums/definition/BpmModelTypeEnum.java | 31 + .../BpmProcessListenerTypeEnum.java | 21 + .../BpmProcessListenerValueTypeEnum.java | 22 + .../BpmSimpleModeConditionTypeEnum.java | 36 + .../BpmSimpleModelNodeTypeEnum.java | 70 + .../enums/definition/BpmTriggerTypeEnum.java | 46 + .../BpmUserTaskApproveMethodEnum.java | 47 + .../BpmUserTaskApproveTypeEnum.java | 31 + ...BpmUserTaskAssignEmptyHandlerTypeEnum.java | 33 + ...serTaskAssignStartUserHandlerTypeEnum.java | 31 + .../BpmUserTaskRejectHandlerTypeEnum.java | 35 + .../BpmUserTaskTimeoutHandlerTypeEnum.java | 32 + .../bpm/enums/message/BpmMessageEnum.java | 27 + .../bpm/enums/task/BpmCommentTypeEnum.java | 46 + .../task/BpmProcessInstanceStatusEnum.java | 50 + .../module/bpm/enums/task/BpmReasonEnum.java | 52 + .../bpm/enums/task/BpmTaskSignTypeEnum.java | 47 + .../bpm/enums/task/BpmTaskStatusEnum.java | 70 + zt-module-bpm-server/Dockerfile | 19 + zt-module-bpm-server/pom.xml | 142 ++ .../druid/pool/DruidPooledStatement.java | 781 ++++++ .../plat/module/bpm/BpmServerApplication.java | 30 + .../zt/plat/module/bpm/api/package-info.java | 4 + .../api/task/BpmProcessInstanceApiImpl.java | 32 + .../admin/base/dept/DeptSimpleBaseVO.java | 15 + .../controller/admin/base/package-info.java | 4 + .../admin/base/user/UserSimpleBaseVO.java | 22 + .../definition/BpmCategoryController.java | 95 + .../admin/definition/BpmFormController.java | 83 + .../admin/definition/BpmModelController.java | 200 ++ .../BpmProcessDefinitionController.java | 133 ++ .../BpmProcessExpressionController.java | 73 + .../BpmProcessListenerController.java | 73 + .../definition/BpmUserGroupController.java | 83 + .../vo/category/BpmCategoryPageReqVO.java | 32 + .../vo/category/BpmCategoryRespVO.java | 33 + .../vo/category/BpmCategorySaveReqVO.java | 37 + .../BpmProcessExpressionPageReqVO.java | 33 + .../BpmProcessExpressionRespVO.java | 30 + .../BpmProcessExpressionSaveReqVO.java | 27 + .../definition/vo/form/BpmFormFieldVO.java | 24 + .../definition/vo/form/BpmFormPageReqVO.java | 14 + .../definition/vo/form/BpmFormRespVO.java | 39 + .../definition/vo/form/BpmFormSaveReqVO.java | 35 + .../vo/group/BpmUserGroupPageReqVO.java | 28 + .../vo/group/BpmUserGroupRespVO.java | 31 + .../vo/group/BpmUserGroupSaveReqVO.java | 31 + .../listener/BpmProcessListenerPageReqVO.java | 30 + .../vo/listener/BpmProcessListenerRespVO.java | 36 + .../listener/BpmProcessListenerSaveReqVO.java | 39 + .../vo/model/BpmModeUpdateBpmnReqVO.java | 19 + .../vo/model/BpmModelMetaInfoVO.java | 180 ++ .../definition/vo/model/BpmModelRespVO.java | 57 + .../vo/model/BpmModelSaveReqVO.java | 34 + .../vo/model/BpmModelUpdateStateReqVO.java | 19 + .../vo/model/simple/BpmSimpleModelNodeVO.java | 526 +++++ .../simple/BpmSimpleModelUpdateReqVO.java | 23 + .../BpmProcessDefinitionPageReqVO.java | 14 + .../process/BpmProcessDefinitionRespVO.java | 71 + .../admin/oa/BpmOALeaveController.http | 12 + .../admin/oa/BpmOALeaveController.java | 62 + .../bpm/controller/admin/oa/package-info.java | 5 + .../admin/oa/vo/BpmOALeaveCreateReqVO.java | 43 + .../admin/oa/vo/BpmOALeavePageReqVO.java | 29 + .../admin/oa/vo/BpmOALeaveRespVO.java | 36 + .../task/BpmProcessInstanceController.http | 16 + .../task/BpmProcessInstanceController.java | 202 ++ .../BpmProcessInstanceCopyController.java | 89 + .../admin/task/BpmTaskController.java | 252 ++ .../task/vo/activity/BpmActivityRespVO.java | 25 + .../vo/cc/BpmProcessInstanceCopyRespVO.java | 48 + .../vo/instance/BpmApprovalDetailReqVO.java | 40 + .../vo/instance/BpmApprovalDetailRespVO.java | 112 + ...BpmProcessInstanceBpmnModelViewRespVO.java | 43 + .../BpmProcessInstanceCancelReqVO.java | 19 + .../BpmProcessInstanceCopyPageReqVO.java | 23 + .../BpmProcessInstanceCreateReqVO.java | 24 + .../instance/BpmProcessInstancePageReqVO.java | 45 + .../vo/instance/BpmProcessInstanceRespVO.java | 86 + .../task/vo/task/BpmTaskApproveReqVO.java | 33 + .../admin/task/vo/task/BpmTaskCopyReqVO.java | 23 + .../task/vo/task/BpmTaskDelegateReqVO.java | 24 + .../admin/task/vo/task/BpmTaskPageReqVO.java | 28 + .../task/vo/task/BpmTaskRejectReqVO.java | 18 + .../admin/task/vo/task/BpmTaskRespVO.java | 130 + .../task/vo/task/BpmTaskReturnReqVO.java | 23 + .../task/vo/task/BpmTaskSignCreateReqVO.java | 29 + .../task/vo/task/BpmTaskSignDeleteReqVO.java | 19 + .../task/vo/task/BpmTaskTransferReqVO.java | 24 + .../bpm/controller/app/package-info.java | 4 + .../module/bpm/controller/package-info.java | 6 + .../convert/definition/BpmModelConvert.java | 132 ++ .../BpmProcessDefinitionConvert.java | 99 + .../convert/message/BpmMessageConvert.java | 21 + .../plat/module/bpm/convert/package-info.java | 6 + .../task/BpmProcessInstanceConvert.java | 298 +++ .../bpm/convert/task/BpmTaskConvert.java | 233 ++ ...道 Spring Boot 对象转换 MapStruct 入门》.md | 1 + .../dataobject/definition/BpmCategoryDO.java | 54 + .../dal/dataobject/definition/BpmFormDO.java | 57 + .../BpmProcessDefinitionInfoDO.java | 219 ++ .../definition/BpmProcessExpressionDO.java | 45 + .../definition/BpmProcessListenerDO.java | 74 + .../dataobject/definition/BpmUserGroupDO.java | 52 + .../bpm/dal/dataobject/oa/BpmOALeaveDO.java | 78 + .../task/BpmProcessInstanceCopyDO.java | 98 + .../dal/mysql/category/BpmCategoryMapper.java | 46 + .../dal/mysql/definition/BpmFormMapper.java | 25 + .../BpmProcessDefinitionInfoMapper.java | 27 + .../BpmProcessExpressionMapper.java | 26 + .../definition/BpmProcessListenerMapper.java | 27 + .../mysql/definition/BpmUserGroupMapper.java | 32 + .../bpm/dal/mysql/oa/BpmOALeaveMapper.java | 29 + .../task/BpmProcessInstanceCopyMapper.java | 25 + .../bpm/dal/redis/BpmProcessIdRedisDAO.java | 61 + .../bpm/dal/redis/RedisKeyConstants.java | 15 + .../config/BpmFlowableConfiguration.java | 95 + .../behavior/BpmActivityBehaviorFactory.java | 44 + .../BpmParallelMultiInstanceBehavior.java | 91 + .../BpmSequentialMultiInstanceBehavior.java | 95 + .../behavior/BpmUserTaskActivityBehavior.java | 86 + .../candidate/BpmTaskCandidateInvoker.java | 207 ++ .../candidate/BpmTaskCandidateStrategy.java | 85 + .../BpmTaskAssignLeaderExpression.java | 79 + .../BpmTaskAssignStartUserExpression.java | 36 + ...actBpmTaskCandidateDeptLeaderStrategy.java | 95 + ...askCandidateApproveUserSelectStrategy.java | 78 + ...mTaskCandidateDeptLeaderMultiStrategy.java | 45 + .../BpmTaskCandidateDeptLeaderStrategy.java | 45 + .../BpmTaskCandidateDeptMemberStrategy.java | 48 + ...idateStartUserDeptLeaderMultiStrategy.java | 70 + ...kCandidateStartUserDeptLeaderStrategy.java | 71 + ...mTaskCandidateStartUserSelectStrategy.java | 73 + ...pmTaskCandidateFormDeptLeaderStrategy.java | 56 + .../BpmTaskCandidateFormUserStrategy.java | 47 + .../BpmTaskCandidateAssignEmptyStrategy.java | 73 + .../BpmTaskCandidateExpressionStrategy.java | 58 + .../user/BpmTaskCandidateGroupStrategy.java | 46 + .../user/BpmTaskCandidatePostStrategy.java | 48 + .../user/BpmTaskCandidateRoleStrategy.java | 43 + .../BpmTaskCandidateStartUserStrategy.java | 57 + .../user/BpmTaskCandidateUserStrategy.java | 39 + ...riableConvertByTypeExpressionFunction.java | 32 + .../enums/BpmTaskCandidateStrategyEnum.java | 59 + .../core/enums/BpmnModelConstants.java | 146 ++ .../core/enums/BpmnVariableConstants.java | 99 + .../BpmProcessInstanceEventPublisher.java | 24 + .../core/listener/BpmCopyTaskDelegate.java | 47 + .../BpmProcessInstanceEventListener.java | 54 + .../core/listener/BpmTaskEventListener.java | 125 + .../core/listener/BpmTriggerTaskDelegate.java | 55 + .../DemoDelegateClassExecutionListener.java | 21 + ...moDelegateExpressionExecutionListener.java | 23 + ...DemoSpringExpressionExecutionListener.java | 21 + .../task/DemoDelegateClassTaskListener.java | 20 + .../DemoDelegateExpressionTaskListener.java | 22 + .../DemoSpringExpressionTaskListener.java | 20 + .../core/util/BpmHttpRequestUtils.java | 158 ++ .../flowable/core/util/BpmnModelUtils.java | 1025 ++++++++ .../flowable/core/util/FlowableUtils.java | 362 +++ .../flowable/core/util/SimpleModelUtils.java | 1007 ++++++++ .../module/bpm/framework/package-info.java | 6 + .../rpc/config/RpcConfiguration.java | 17 + .../bpm/framework/rpc/package-info.java | 4 + .../config/SecurityConfiguration.java | 40 + .../framework/security/core/package-info.java | 4 + .../web/config/BpmWebConfiguration.java | 30 + .../framework/web/core/FlowableWebFilter.java | 36 + .../bpm/framework/web/package-info.java | 4 + .../com/zt/plat/module/bpm/package-info.java | 12 + .../definition/BpmCategoryService.java | 92 + .../definition/BpmCategoryServiceImpl.java | 130 + .../service/definition/BpmFormService.java | 85 + .../definition/BpmFormServiceImpl.java | 114 + .../service/definition/BpmModelService.java | 134 ++ .../definition/BpmModelServiceImpl.java | 432 ++++ .../BpmProcessDefinitionService.java | 181 ++ .../BpmProcessDefinitionServiceImpl.java | 248 ++ .../BpmProcessExpressionService.java | 54 + .../BpmProcessExpressionServiceImpl.java | 70 + .../definition/BpmProcessListenerService.java | 54 + .../BpmProcessListenerServiceImpl.java | 102 + .../definition/BpmUserGroupService.java | 82 + .../definition/BpmUserGroupServiceImpl.java | 107 + .../definition/dto/BpmFormFieldRespDTO.java | 25 + .../dto/BpmModelMetaInfoRespDTO.java | 46 + .../dto/BpmProcessDefinitionCreateReqDTO.java | 81 + .../service/message/BpmMessageService.java | 46 + .../message/BpmMessageServiceImpl.java | 79 + ...eSendWhenProcessInstanceApproveReqDTO.java | 26 + ...geSendWhenProcessInstanceRejectReqDTO.java | 32 + .../BpmMessageSendWhenTaskCreatedReqDTO.java | 45 + .../BpmMessageSendWhenTaskTimeoutReqDTO.java | 41 + .../bpm/service/oa/BpmOALeaveService.java | 52 + .../bpm/service/oa/BpmOALeaveServiceImpl.java | 89 + .../oa/listener/BpmOALeaveStatusListener.java | 33 + .../task/BpmProcessInstanceCopyService.java | 60 + .../BpmProcessInstanceCopyServiceImpl.java | 96 + .../task/BpmProcessInstanceService.java | 191 ++ .../task/BpmProcessInstanceServiceImpl.java | 964 ++++++++ .../bpm/service/task/BpmTaskService.java | 316 +++ .../bpm/service/task/BpmTaskServiceImpl.java | 1535 ++++++++++++ .../listener/BpmCallActivityListener.java | 96 + .../task/listener/BpmUserTaskListener.java | 59 + .../bpm/service/task/trigger/BpmTrigger.java | 30 + .../trigger/form/BpmFormDeleteTrigger.java | 73 + .../trigger/form/BpmFormUpdateTrigger.java | 66 + .../http/BpmAbstractHttpRequestTrigger.java | 14 + .../trigger/http/BpmHttpCallbackTrigger.java | 51 + .../http/BpmSyncHttpRequestTrigger.java | 46 + .../liquibase/database/core/DmDatabase.java | 546 +++++ .../liquibase/datatype/core/BooleanType.java | 149 ++ .../impl/AbstractEngineConfiguration.java | 2094 +++++++++++++++++ .../main/resources/META-INF/package-info.md | 1 + .../services/liquibase.database.Database | 20 + .../src/main/resources/application-dev.yaml | 94 + .../src/main/resources/application-local.yaml | 111 + .../src/main/resources/application.yaml | 150 ++ .../src/main/resources/logback-spring.xml | 76 + .../BpmTaskCandidateInvokerTest.java | 274 +++ .../BpmTaskAssignLeaderExpressionTest.java | 107 + ...kCandidateDeptLeaderMultiStrategyTest.java | 45 + ...pmTaskCandidateDeptLeaderStrategyTest.java | 44 + ...pmTaskCandidateDeptMemberStrategyTest.java | 47 + ...eStartUserDeptLeaderMultiStrategyTest.java | 85 + ...didateStartUserDeptLeaderStrategyTest.java | 85 + ...kCandidateStartUserSelectStrategyTest.java | 68 + ...mTaskCandidateAssignEmptyStrategyTest.java | 88 + ...pmTaskCandidateExpressionStrategyTest.java | 61 + .../BpmTaskCandidateGroupStrategyTest.java | 44 + .../BpmTaskCandidatePostStrategyTest.java | 48 + .../BpmTaskCandidateRoleStrategyTest.java | 44 + ...BpmTaskCandidateStartUserStrategyTest.java | 56 + .../BpmTaskCandidateUserStrategyTest.java | 31 + .../category/BpmCategoryServiceImplTest.java | 136 ++ .../definition/BpmFormServiceTest.java | 144 ++ .../definition/BpmUserGroupServiceTest.java | 130 + .../test/resources/application-unit-test.yaml | 45 + .../src/test/resources/logback.xml | 4 + .../src/test/resources/sql/clean.sql | 3 + .../src/test/resources/sql/create_tables.sql | 43 + 260 files changed, 24174 insertions(+) create mode 100644 pom.xml create mode 100644 zt-module-bpm-api/pom.xml create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java create mode 100644 zt-module-bpm-server/Dockerfile create mode 100644 zt-module-bpm-server/pom.xml create mode 100644 zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java create mode 100644 zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java create mode 100644 zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java create mode 100644 zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java create mode 100644 zt-module-bpm-server/src/main/resources/META-INF/package-info.md create mode 100644 zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database create mode 100644 zt-module-bpm-server/src/main/resources/application-dev.yaml create mode 100644 zt-module-bpm-server/src/main/resources/application-local.yaml create mode 100644 zt-module-bpm-server/src/main/resources/application.yaml create mode 100644 zt-module-bpm-server/src/main/resources/logback-spring.xml create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java create mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java create mode 100644 zt-module-bpm-server/src/test/resources/application-unit-test.yaml create mode 100644 zt-module-bpm-server/src/test/resources/logback.xml create mode 100644 zt-module-bpm-server/src/test/resources/sql/clean.sql create mode 100644 zt-module-bpm-server/src/test/resources/sql/create_tables.sql diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..302788b --- /dev/null +++ b/pom.xml @@ -0,0 +1,27 @@ + + + + com.zt.plat + zt + ${revision} + + 4.0.0 + + zt-module-bpm-api + zt-module-bpm-server + + zt-module-bpm + pom + + ${project.artifactId} + + bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能。 + 例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等 + bpm 解释:https://baike.baidu.com/item/BPM/1933 + + 工作流基于 Flowable 6 实现,分成流程定义、流程表单、流程实例、流程任务等功能模块。 + + + diff --git a/zt-module-bpm-api/pom.xml b/zt-module-bpm-api/pom.xml new file mode 100644 index 0000000..2aa75a5 --- /dev/null +++ b/zt-module-bpm-api/pom.xml @@ -0,0 +1,47 @@ + + + + com.zt.plat + zt-module-bpm + ${revision} + + 4.0.0 + zt-module-bpm-api + jar + + ${project.artifactId} + + bpm 模块 API,暴露给其它模块调用 + + + + + com.zt.plat + zt-common + + + + + org.springdoc + springdoc-openapi-starter-webmvc-api + provided + + + + + org.springframework.boot + spring-boot-starter-validation + true + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + true + + + + diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java new file mode 100644 index 0000000..4a4316c --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java @@ -0,0 +1,41 @@ +package com.zt.plat.module.bpm.api.event; + +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.context.ApplicationEvent; + +/** + * 流程实例的状态(结果)发生变化的 Event + * + * @author ZT + */ +@SuppressWarnings("ALL") +@Data +public class BpmProcessInstanceStatusEvent extends ApplicationEvent { + + /** + * 流程实例的编号 + */ + @NotNull(message = "流程实例的编号不能为空") + private String id; + /** + * 流程实例的 key + */ + @NotNull(message = "流程实例的 key 不能为空") + private String processDefinitionKey; + /** + * 流程实例的结果 + */ + @NotNull(message = "流程实例的状态不能为空") + private Integer status; + /** + * 流程实例对应的业务标识 + * 例如说,请假 + */ + private String businessKey; + + public BpmProcessInstanceStatusEvent(Object source) { + super(source); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java new file mode 100644 index 0000000..553096a --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java @@ -0,0 +1,34 @@ +package com.zt.plat.module.bpm.api.event; + +import org.springframework.context.ApplicationListener; + +import java.util.List; + +/** + * {@link BpmProcessInstanceStatusEvent} 的监听器 + * + * @author ZT + */ +public abstract class BpmProcessInstanceStatusEventListener + implements ApplicationListener { + + @Override + public final void onApplicationEvent(BpmProcessInstanceStatusEvent event) { + if (getProcessDefinitionKey().contains(event.getProcessDefinitionKey())){ + onEvent(event); + } + } + + /** + * @return 返回监听的流程定义 Key + */ + protected abstract List getProcessDefinitionKey(); + + /** + * 处理事件 + * + * @param event 事件 + */ + protected abstract void onEvent(BpmProcessInstanceStatusEvent event); + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java new file mode 100644 index 0000000..10d2e79 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java @@ -0,0 +1,4 @@ +/** + * bpm API 包,定义暴露给其它模块的 API + */ +package com.zt.plat.module.bpm.api; diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java new file mode 100644 index 0000000..28d062e --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java @@ -0,0 +1,28 @@ +package com.zt.plat.module.bpm.api.task; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.enums.ApiConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import jakarta.validation.Valid; + +@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = +@Tag(name = "RPC 服务 - 流程实例") +public interface BpmProcessInstanceApi { + + String PREFIX = ApiConstants.PREFIX + "/process-instance"; + + @PostMapping(PREFIX + "/create") + @Operation(summary = "创建流程实例(提供给内部),返回实例编号") + @Parameter(name = "userId", description = "用户编号", required = true, example = "1") + CommonResult createProcessInstance(@RequestParam("userId") Long userId, + @Valid @RequestBody BpmProcessInstanceCreateReqDTO reqDTO); + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java new file mode 100644 index 0000000..efecfda --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import jakarta.validation.constraints.NotEmpty; + +import java.util.List; +import java.util.Map; + +@Schema(description = "RPC 服务 - 流程实例的创建 Request DTO") +@Data +public class BpmProcessInstanceCreateReqDTO { + + @Schema(description = "流程定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "leave") + @NotEmpty(message = "流程定义的标识不能为空") + private String processDefinitionKey; + + @Schema(description = "变量实例", requiredMode = Schema.RequiredMode.REQUIRED) + private Map variables; + + @Schema(description = "业务的唯一标识", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "业务的唯一标识不能为空") + private String businessKey; // 例如说,请假申请的编号。通过它,可以查询到对应的实例 + + /** + * 发起人自选审批人 Map + * + * key:taskKey 任务编码 + * value:审批人的数组 + * 例如:{ taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批 + */ + @Schema(description = "发起人自选审批人 Map") + private Map> startUserSelectAssignees; + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java new file mode 100644 index 0000000..3c4bac9 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.enums; + +import com.zt.plat.framework.common.enums.RpcConstants; + +/** + * API 相关的枚举 + * + * @author ZT + */ +public class ApiConstants { + + /** + * 服务名 + * + * 注意,需要保证和 spring.application.name 保持一致 + */ + public static final String NAME = "bpm-server"; + + public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/bpm"; + + public static final String VERSION = "1.0.0"; + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java new file mode 100644 index 0000000..13dcf72 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java @@ -0,0 +1,10 @@ +package com.zt.plat.module.bpm.enums; + +/** + * BPM 字典类型的枚举类 + * + * @author ZT + */ +public interface DictTypeConstants { + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java new file mode 100644 index 0000000..b9db1ed --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java @@ -0,0 +1,87 @@ +package com.zt.plat.module.bpm.enums; + +import com.zt.plat.framework.common.exception.ErrorCode; + +/** + * Bpm 错误码枚举类 + *

+ * bpm 系统,使用 1-009-000-000 段 + */ +public interface ErrorCodeConstants { + + // ========== 通用流程处理 模块 1-009-000-000 ========== + + // ========== OA 流程模块 1-009-001-000 ========== + ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1_009_001_001, "请假申请不存在"); + + // ========== 流程模型 1-009-002-000 ========== + ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "已经存在流程标识为【{}】的流程"); + ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_009_002_001, "流程模型不存在"); + ErrorCode MODEL_KEY_VALID = new ErrorCode(1_009_002_002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!"); + ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"); + ErrorCode MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," + + "原因:用户任务({})未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置"); + ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件"); + ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在"); + ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程({})的管理员"); + ErrorCode MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR = new ErrorCode(1_009_002_008, "部署流程失败,原因:首个任务({})的审批人不能是【审批人自选】"); + + // ========== 流程定义 1-009-003-000 ========== + ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); + ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图"); + ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, "流程定义不存在"); + ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1_009_003_003, "流程定义处于挂起状态"); + + // ========== 流程实例 1-009-004-000 ========== + ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_004_000, "流程实例不存在"); + ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中"); + ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的"); + ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "任务({})的候选人未配置"); + ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在"); + ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); + ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); + ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); + ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置"); + ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消"); + + // ========== 流程任务 1-009-005-000 ========== + ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); + ErrorCode TASK_NOT_EXISTS = new ErrorCode(1_009_005_002, "流程任务不存在"); + ErrorCode TASK_IS_PENDING = new ErrorCode(1_009_005_003, "当前任务处于挂起状态,不能操作"); + ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_004, " 目标节点不存在"); + ErrorCode TASK_RETURN_FAIL_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_006, "退回任务失败,目标节点是在并行网关上或非同一路线上,不可跳转"); + ErrorCode TASK_DELEGATE_FAIL_USER_REPEAT = new ErrorCode(1_009_005_007, "任务委派失败,委派人和当前审批人为同一人"); + ErrorCode TASK_DELEGATE_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_008, "任务委派失败,被委派人不存在"); + ErrorCode TASK_SIGN_CREATE_USER_NOT_EXIST = new ErrorCode(1_009_005_009, "任务加签:选择的用户不存在"); + ErrorCode TASK_SIGN_CREATE_TYPE_ERROR = new ErrorCode(1_009_005_010, "任务加签:当前任务已经{},不能{}"); + ErrorCode TASK_SIGN_CREATE_USER_REPEAT = new ErrorCode(1_009_005_011, "任务加签失败,加签人与现有审批人[{}]重复"); + ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务"); + ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人"); + ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在"); + ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); + ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); + ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); + + // ========== 动态表单模块 1-009-010-000 ========== + ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在"); + ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1_009_010_001, "表单项({}) 和 ({}) 使用了相同的字段名({})"); + + // ========== 用户组模块 1-009-011-000 ========== + ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1_009_011_000, "用户分组不存在"); + ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1_009_011_001, "名字为【{}】的用户分组已被禁用"); + + // ========== 用户组模块 1-009-012-000 ========== + ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_009_012_000, "流程分类不存在"); + ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(1_009_012_001, "流程分类名字【{}】重复"); + ErrorCode CATEGORY_CODE_DUPLICATE = new ErrorCode(1_009_012_002, "流程分类编码【{}】重复"); + + // ========== BPM 流程监听器 1-009-013-000 ========== + ErrorCode PROCESS_LISTENER_NOT_EXISTS = new ErrorCode(1_009_013_000, "流程监听器不存在"); + ErrorCode PROCESS_LISTENER_CLASS_NOT_FOUND = new ErrorCode(1_009_013_001, "流程监听器类({})不存在"); + ErrorCode PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR = new ErrorCode(1_009_013_002, "流程监听器类({})没有实现接口({})"); + ErrorCode PROCESS_LISTENER_EXPRESSION_INVALID = new ErrorCode(1_009_013_003, "流程监听器表达式({})不合法"); + + // ========== BPM 流程表达式 1-009-014-000 ========== + ErrorCode PROCESS_EXPRESSION_NOT_EXISTS = new ErrorCode(1_009_014_000, "流程表达式不存在"); + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java new file mode 100644 index 0000000..2611d38 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 自动去重的类型的枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmAutoApproveTypeEnum implements ArrayValuable { + + NONE(0, "不自动通过"), + APPROVE_ALL(1, "仅审批一次,后续重复的审批节点均自动通过"), + APPROVE_SEQUENT(2, "仅针对连续审批的节点自动通过"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmAutoApproveTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java new file mode 100644 index 0000000..d168f66 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 边界事件 (boundary event) 自定义类型枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmBoundaryEventTypeEnum { + + USER_TASK_TIMEOUT(1, "用户任务超时"), + DELAY_TIMER_TIMEOUT(2, "延迟器超时"), + CHILD_PROCESS_TIMEOUT(3, "子流程超时"); + + private final Integer type; + private final String name; + + public static BpmBoundaryEventTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values()); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java new file mode 100644 index 0000000..68574cb --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java @@ -0,0 +1,37 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 子流程多实例来源类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmChildProcessMultiInstanceSourceTypeEnum implements ArrayValuable { + + FIXED_QUANTITY(1, "固定数量"), + NUMBER_FORM(2, "数字表单"), + MULTIPLE_FORM(3, "多选表单"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessMultiInstanceSourceTypeEnum::getType).toArray(Integer[]::new); + + public static BpmChildProcessMultiInstanceSourceTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java new file mode 100644 index 0000000..5b13462 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 当子流程发起人为空时类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmChildProcessStartUserEmptyTypeEnum implements ArrayValuable { + + MAIN_PROCESS_START_USER(1, "同主流程发起人"), + CHILD_PROCESS_ADMIN(2, "子流程管理员"), + MAIN_PROCESS_ADMIN(3, "主流程管理员"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserEmptyTypeEnum::getType).toArray(Integer[]::new); + + public static BpmChildProcessStartUserEmptyTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java new file mode 100644 index 0000000..8470a72 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java @@ -0,0 +1,35 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 子流程发起人类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmChildProcessStartUserTypeEnum implements ArrayValuable { + + MAIN_PROCESS_START_USER(1, "同主流程发起人"), + FROM_FORM(2, "表单"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserTypeEnum::getType).toArray(Integer[]::new); + + public static BpmChildProcessStartUserTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java new file mode 100644 index 0000000..aabaef0 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 延迟器类型枚举 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmDelayTimerTypeEnum implements ArrayValuable { + + FIXED_TIME_DURATION(1, "固定时长"), + FIXED_DATE_TIME(2, "固定日期"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmDelayTimerTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java new file mode 100644 index 0000000..8571f90 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 表单权限的枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmFieldPermissionEnum { + + READ(1, "只读"), + WRITE(2, "可编辑"), + NONE(3, "隐藏"); + + /** + * 权限 + */ + private final Integer permission; + /** + * 名字 + */ + private final String name; + + public static BpmFieldPermissionEnum valueOf(Integer permission) { + return ArrayUtil.firstMatch(item -> item.getPermission().equals(permission), values()); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java new file mode 100644 index 0000000..dd1fc50 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM HTTP 请求参数设置类型。用于 Simple 设计器任务监听器和触发器配置。 + * + * @author Lesan + */ +@Getter +@AllArgsConstructor +public enum BpmHttpRequestParamTypeEnum implements ArrayValuable { + + FIXED_VALUE(1, "固定值"), + FROM_FORM(2, "表单"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmHttpRequestParamTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java new file mode 100644 index 0000000..f28b1ed --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 模型的表单类型的枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmModelFormTypeEnum implements ArrayValuable { + + NORMAL(10, "流程表单"), // 对应 BpmFormDO + CUSTOM(20, "业务表单") // 业务自己定义的表单,自己进行数据的存储 + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmModelFormTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java new file mode 100644 index 0000000..259b365 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 模型的类型的枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmModelTypeEnum implements ArrayValuable { + + BPMN(10, "BPMN 设计器"), // https://bpmn.io/toolkit/bpmn-js/ + SIMPLE(20, "SIMPLE 设计器"); // 参考钉钉、飞书工作流的设计器 + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmModelTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + private final String name; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java new file mode 100644 index 0000000..b1ecfe4 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java @@ -0,0 +1,21 @@ +package com.zt.plat.module.bpm.enums.definition; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 流程监听器的类型 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmProcessListenerTypeEnum { + + EXECUTION("execution", "执行监听器"), + TASK("task", "任务执行器"); + + private final String type; + private final String name; + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java new file mode 100644 index 0000000..7ff961b --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java @@ -0,0 +1,22 @@ +package com.zt.plat.module.bpm.enums.definition; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * BPM 流程监听器的值类型 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmProcessListenerValueTypeEnum { + + CLASS("class", "Java 类"), + DELEGATE_EXPRESSION("delegateExpression", "代理表达式"), + EXPRESSION("expression", "表达式"); + + private final String type; + private final String name; + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java new file mode 100644 index 0000000..b8c8ee2 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 仿钉钉的流程器设计器条件节点的条件类型 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmSimpleModeConditionTypeEnum implements ArrayValuable { + + EXPRESSION(1, "条件表达式"), + RULE(2, "条件规则"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmSimpleModeConditionTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + + private final String name; + + public static BpmSimpleModeConditionTypeEnum valueOf(Integer type) { + return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java new file mode 100644 index 0000000..6590a4c --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java @@ -0,0 +1,70 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; +import java.util.Objects; + +/** + * 仿钉钉的流程器设计器的模型节点类型 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable { + + // 0 ~ 1 开始和结束 + START_NODE(0, "开始", "startEvent"), + END_NODE(1, "结束", "endEvent"), + + // 10 ~ 49 各种节点 + START_USER_NODE(10, "发起人", "userTask"), // 发起人节点。前端的开始节点,Id 固定 + APPROVE_NODE(11, "审批人", "userTask"), + COPY_NODE(12, "抄送人", "serviceTask"), + TRANSACTOR_NODE(13, "办理人", "userTask"), + + DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), + TRIGGER_NODE(15, "触发器", "serviceTask"), + + CHILD_PROCESS(20, "子流程", "callActivity"), + + // 50 ~ 条件分支 + CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 + CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"), + PARALLEL_BRANCH_NODE(52, "并行分支", "parallelGateway"), + INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"), + ROUTER_BRANCH_NODE(54, "路由分支", "exclusiveGateway") + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmSimpleModelNodeTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + private final String name; + private final String bpmnType; + + /** + * 判断是否为分支节点 + * + * @param type 节点类型 + */ + public static boolean isBranchNode(Integer type) { + return Objects.equals(CONDITION_BRANCH_NODE.getType(), type) + || Objects.equals(PARALLEL_BRANCH_NODE.getType(), type) + || Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type) + || Objects.equals(ROUTER_BRANCH_NODE.getType(), type); + } + + public static BpmSimpleModelNodeTypeEnum valueOf(Integer type) { + return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java new file mode 100644 index 0000000..72c3c39 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM Simple 触发器类型枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmTriggerTypeEnum implements ArrayValuable { + + HTTP_REQUEST(1, "发起 HTTP 请求"), // BPM => 业务,流程继续执行,无需等待业务 + HTTP_CALLBACK(2, "接收 HTTP 回调"), // BPM => 业务 => BPM,流程卡主,等待业务回调 + + FORM_UPDATE(10, "更新流程表单数据"), + FORM_DELETE(11, "删除流程表单数据"), + ; + + /** + * 触发器执行动作类型 + */ + private final Integer type; + + /** + * 触发器执行动作描述 + */ + private final String desc; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTriggerTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static BpmTriggerTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java new file mode 100644 index 0000000..065035d --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java @@ -0,0 +1,47 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 多人审批方式的枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmUserTaskApproveMethodEnum implements ArrayValuable { + + RANDOM(1, "随机挑选一人审批", null), + RATIO(2, "多人会签(按通过比例)", "${ nrOfCompletedInstances/nrOfInstances >= %s}"), // 会签(按通过比例) + ANY(3, "多人或签(一人通过或拒绝)", "${ nrOfCompletedInstances > 0 }"), // 或签(通过只需一人,拒绝只需一人) + SEQUENTIAL(4, "依次审批", "${ nrOfCompletedInstances >= nrOfInstances }"); // 依次审批 + + /** + * 审批方式 + */ + private final Integer method; + /** + * 名字 + */ + private final String name; + /** + * 完成表达式 + */ + private final String completionCondition; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskApproveMethodEnum::getMethod).toArray(Integer[]::new); + + public static BpmUserTaskApproveMethodEnum valueOf(Integer method) { + return ArrayUtil.firstMatch(item -> item.getMethod().equals(method), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java new file mode 100644 index 0000000..df40e18 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 用户任务的审批类型枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmUserTaskApproveTypeEnum implements ArrayValuable { + + USER(1), // 人工审批 + AUTO_APPROVE(2), // 自动通过 + AUTO_REJECT(3); // 自动拒绝 + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskApproveTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java new file mode 100644 index 0000000..237ef8e --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * BPM 用户任务的审批人为空时,处理类型枚举 + * + * @author ZT + */ +@RequiredArgsConstructor +@Getter +public enum BpmUserTaskAssignEmptyHandlerTypeEnum implements ArrayValuable { + + APPROVE(1), // 自动通过 + REJECT(2), // 自动拒绝 + ASSIGN_USER(3), // 指定人员审批 + ASSIGN_ADMIN(4), // 转交给流程管理员 + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskAssignEmptyHandlerTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java new file mode 100644 index 0000000..ffed389 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +import java.util.Arrays; + +/** + * BPM 用户任务的审批人与发起人相同时,处理类型枚举 + * + * @author ZT + */ +@RequiredArgsConstructor +@Getter +public enum BpmUserTaskAssignStartUserHandlerTypeEnum implements ArrayValuable { + + START_USER_AUDIT(1), // 由发起人对自己审批 + SKIP(2), // 自动跳过【参考飞书】:1)如果当前节点还有其他审批人,则交由其他审批人进行审批;2)如果当前节点没有其他审批人,则该节点自动通过 + TRANSFER_DEPT_LEADER(3); // 转交给部门负责人审批【参考飞书】:若部门负责人为空,则自动通过 + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskAssignStartUserHandlerTypeEnum::getType).toArray(Integer[]::new); + + private final Integer type; + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java new file mode 100644 index 0000000..f251971 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java @@ -0,0 +1,35 @@ +package com.zt.plat.module.bpm.enums.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 用户任务拒绝处理类型枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmUserTaskRejectHandlerTypeEnum implements ArrayValuable { + + FINISH_PROCESS_INSTANCE(1, "终止流程"), + RETURN_USER_TASK(2, "驳回到指定任务节点"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskRejectHandlerTypeEnum::getType).toArray(Integer[]::new); + + public static BpmUserTaskRejectHandlerTypeEnum typeOf(Integer type) { + return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java new file mode 100644 index 0000000..8f9460e --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.enums.definition; + +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 用户任务超时处理类型枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmUserTaskTimeoutHandlerTypeEnum implements ArrayValuable { + + REMINDER(1,"自动提醒"), + APPROVE(2, "自动同意"), + REJECT(3, "自动拒绝"); + + private final Integer type; + private final String name; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskTimeoutHandlerTypeEnum::getType).toArray(Integer[]::new); + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java new file mode 100644 index 0000000..d8db9ae --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.bpm.enums.message; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * Bpm 消息的枚举 + * + * @author ZT + */ +@AllArgsConstructor +@Getter +public enum BpmMessageEnum { + + PROCESS_INSTANCE_APPROVE("bpm_process_instance_approve"), // 流程任务被审批通过时,发送给申请人 + PROCESS_INSTANCE_REJECT("bpm_process_instance_reject"), // 流程任务被审批不通过时,发送给申请人 + TASK_ASSIGNED("bpm_task_assigned"), // 任务被分配时,发送给审批人 + TASK_TIMEOUT("bpm_task_timeout"); // 任务审批超时时,发送给审批人 + + /** + * 短信模板的标识 + * + * 关联 SmsTemplateDO 的 code 属性 + */ + private final String smsTemplateCode; + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java new file mode 100644 index 0000000..51a5ffa --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.enums.task; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程任务的 Comment 评论类型枚举 + * + * @author kehaiyou + */ +@Getter +@AllArgsConstructor +public enum BpmCommentTypeEnum { + + APPROVE("1", "审批通过", "审批通过,原因是:{}"), + REJECT("2", "不通过", "审批不通过:原因是:{}"), + CANCEL("3", "已取消", "系统自动取消,原因是:{}"), + RETURN("4", "退回", "任务被退回,原因是:{}"), + DELEGATE_START("5", "委派发起", "[{}]将任务委派给[{}],委派理由为:{}"), + DELEGATE_END("6", "委派完成", "[{}]完成委派任务,任务重新回到[{}]手中,审批建议为:{}"), + TRANSFER("7", "转派", "[{}]将任务转派给[{}],转派理由为:{}"), + ADD_SIGN("8", "加签", "[{}]{}给了[{}],理由为:{}"), + SUB_SIGN("9", "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"), + ; + + /** + * 操作类型 + * + * 由于 BPM Comment 类型为 String,所以这里就不使用 Integer + */ + private final String type; + /** + * 操作名字 + */ + private final String name; + /** + * 操作描述 + */ + private final String comment; + + public String formatComment(Object... params) { + return StrUtil.format(comment, params); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java new file mode 100644 index 0000000..411e9ad --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java @@ -0,0 +1,50 @@ +package com.zt.plat.module.bpm.enums.task; + +import com.zt.plat.framework.common.core.ArrayValuable; +import com.zt.plat.framework.common.util.object.ObjectUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * 流程实例 ProcessInstance 的状态 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmProcessInstanceStatusEnum implements ArrayValuable { + + NOT_START(-1, "未开始"), + RUNNING(1, "审批中"), + APPROVE(2, "审批通过"), + REJECT(3, "审批不通过"), + CANCEL(4, "已取消"); + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmProcessInstanceStatusEnum::getStatus).toArray(Integer[]::new); + + /** + * 状态 + */ + private final Integer status; + /** + * 描述 + */ + private final String desc; + + @Override + public Integer[] array() { + return ARRAYS; + } + + public static boolean isRejectStatus(Integer status) { + return REJECT.getStatus().equals(status); + } + + public static boolean isProcessEndStatus(Integer status) { + return ObjectUtils.equalsAny(status, + APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus()); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java new file mode 100644 index 0000000..8bf768f --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java @@ -0,0 +1,52 @@ +package com.zt.plat.module.bpm.enums.task; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程实例/任务的的处理原因枚举 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmReasonEnum { + + // ========== 流程实例的独有原因 ========== + + REJECT_TASK("审批不通过任务,原因:{}"), // 场景:用户审批不通过任务。修改文案时,需要注意 isRejectReason 方法 + CANCEL_PROCESS_INSTANCE_BY_START_USER("用户主动取消流程,原因:{}"), // 场景:用户主动取消流程 + CANCEL_PROCESS_INSTANCE_BY_ADMIN("管理员【{}】取消流程,原因:{}"), // 场景:管理员取消流程 + CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS("子流程自动取消,原因:主流程已取消"), + + // ========== 流程任务的独有原因 ========== + + CANCEL_BY_SYSTEM("系统自动取消"), // 场景:非常多,比如说:1)多任务审批已经满足条件,无需审批该任务;2)流程实例被取消,无需审批该任务;等等 + TIMEOUT_APPROVE("审批超时,系统自动通过"), + TIMEOUT_REJECT("审批超时,系统自动不通过"), + ASSIGN_START_USER_APPROVE("审批人与提交人为同一人时,自动通过"), + ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"), + ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE("发起人节点首次自动通过"), // 目前仅“子流程”使用 + ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"), + ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"), + ASSIGN_EMPTY_APPROVE("审批人为空,自动通过"), + ASSIGN_EMPTY_REJECT("审批人为空,自动不通过"), + APPROVE_TYPE_AUTO_APPROVE("非人工审核,自动通过"), + APPROVE_TYPE_AUTO_REJECT("非人工审核,自动不通过"), + CANCEL_BY_PROCESS_CLEAN("进程清理自动取消"), + ; + + private final String reason; + + /** + * 格式化理由 + * + * @param args 参数 + * @return 理由 + */ + public String format(Object... args) { + return StrUtil.format(reason, args); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java new file mode 100644 index 0000000..40aa2b1 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java @@ -0,0 +1,47 @@ +package com.zt.plat.module.bpm.enums.task; + +import cn.hutool.core.util.ArrayUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程任务的加签类型枚举 + * + * @author kehaiyou + */ +@Getter +@AllArgsConstructor +public enum BpmTaskSignTypeEnum { + + /** + * 向前加签,需要前置任务审批完成,才回到原审批人 + */ + BEFORE("before", "向前加签"), + /** + * 向后加签,需要后置任务全部审批完,才会通过原审批人节点 + */ + AFTER("after", "向后加签"); + + /** + * 类型 + */ + private final String type; + /** + * 名字 + */ + private final String name; + + public static String nameOfType(String type) { + for (BpmTaskSignTypeEnum value : values()) { + if (value.type.equals(type)) { + return value.name; + } + } + return null; + } + + public static BpmTaskSignTypeEnum of(String type) { + return ArrayUtil.firstMatch(value -> value.getType().equals(type), values()); + } + +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java new file mode 100644 index 0000000..51ca7af --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java @@ -0,0 +1,70 @@ +package com.zt.plat.module.bpm.enums.task; + +import cn.hutool.core.util.ObjUtil; +import com.zt.plat.framework.common.util.object.ObjectUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程任务 Task 的状态枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmTaskStatusEnum { + + NOT_START(-1, "未开始"), + RUNNING(1, "审批中"), + APPROVE(2, "审批通过"), + REJECT(3, "审批不通过"), + CANCEL(4, "已取消"), + + RETURN(5, "已退回"), + + /** + * 使用场景: + * 1. 任务被向后【加签】时,它在审批通过后,会变成 APPROVING 这个状态,然后等到【加签】出来的任务都被审批后,才会变成 APPROVE 审批通过 + */ + APPROVING(7, "审批通过中"), + /** + * 使用场景: + * 1. 任务被向前【加签】时,它会变成 WAIT 状态,需要等待【加签】出来的任务被审批后,它才能继续变为 RUNNING 继续审批 + * 2. 任务被向后【加签】时,【加签】出来的任务处于 WAIT 状态,它们需要等待该任务被审批后,它们才能继续变为 RUNNING 继续审批 + */ + WAIT(0, "待审批"); + + /** + * 状态 + *

+ * 如果新增时,注意 {@link #isEndStatus(Integer)} 是否需要变更 + */ + private final Integer status; + /** + * 名字 + */ + private final String name; + + public static boolean isRejectStatus(Integer status) { + return REJECT.getStatus().equals(status); + } + + /** + * 判断该状态是否已经处于 End 最终状态 + *

+ * 主要用于一些状态更新的逻辑,如果已经是最终状态,就不再进行更新 + * + * @param status 状态 + * @return 是否 + */ + public static boolean isEndStatus(Integer status) { + return ObjectUtils.equalsAny(status, + APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus(), + RETURN.getStatus(), APPROVING.getStatus()); + } + + public static boolean isCancelStatus(Integer status) { + return ObjUtil.equal(status, CANCEL.getStatus()); + } + +} diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile new file mode 100644 index 0000000..9cdca32 --- /dev/null +++ b/zt-module-bpm-server/Dockerfile @@ -0,0 +1,19 @@ +## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 + +FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre + +## 创建目录,并使用它作为工作目录 +RUN mkdir -p /cloud-module-bpm-server +WORKDIR /cloud-module-bpm-server +## 将后端项目的 Jar 文件,复制到镜像中 +COPY ./target/cloud-module-bpm-server.jar app.jar + +## 设置 TZ 时区 +## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 +ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m" + +## 暴露后端项目的 48080 端口 +EXPOSE 48083 + +## 启动后端项目 +CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar diff --git a/zt-module-bpm-server/pom.xml b/zt-module-bpm-server/pom.xml new file mode 100644 index 0000000..06a5ac4 --- /dev/null +++ b/zt-module-bpm-server/pom.xml @@ -0,0 +1,142 @@ + + + + com.zt.plat + zt-module-bpm + ${revision} + + 4.0.0 + zt-module-bpm-server + + ${project.artifactId} + + bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能,基于 Flowable 6 版本实现。 + 例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等 + + + + + com.zt.plat + zt-spring-boot-starter-env + + + + + com.zt.plat + zt-module-bpm-api + ${revision} + + + com.zt.plat + zt-module-system-api + ${revision} + + + + + com.zt.plat + zt-spring-boot-starter-biz-data-permission + + + com.zt.plat + zt-spring-boot-starter-biz-tenant + + + + + com.zt.plat + zt-spring-boot-starter-security + + + + + com.zt.plat + zt-spring-boot-starter-mybatis + + + + com.zt.plat + zt-spring-boot-starter-redis + + + + + com.zt.plat + zt-spring-boot-starter-rpc + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + + + + + + + com.zt.plat + zt-spring-boot-starter-test + + + + + com.zt.plat + zt-spring-boot-starter-monitor + + + + + com.zt.plat + zt-spring-boot-starter-excel + + + + + org.flowable + flowable-spring-boot-starter-process + + + org.flowable + flowable-spring-boot-starter-actuator + + + com.zt.plat + zt-spring-boot-starter-biz-business + ${revision} + compile + + + + + + ${project.artifactId} + + + + org.springframework.boot + spring-boot-maven-plugin + ${spring.boot.version} + + + + repackage + + + + + + + diff --git a/zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java b/zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java new file mode 100644 index 0000000..1c86d9e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java @@ -0,0 +1,781 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package com.alibaba.druid.pool; + +import com.alibaba.cloud.commons.lang.StringUtils; +import com.alibaba.druid.VERSION; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; +import com.alibaba.druid.util.JdbcUtils; +import com.alibaba.druid.util.MySqlUtils; +import java.net.SocketTimeoutException; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.SQLWarning; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.List; + +public class DruidPooledStatement extends PoolableWrapper implements Statement { + private static final Log LOG = LogFactory.getLog(DruidPooledStatement.class); + private final Statement stmt; + protected DruidPooledConnection conn; + protected List resultSetTrace; + protected boolean closed; + protected int fetchRowPeak = -1; + protected int exceptionCount; + + public DruidPooledStatement(DruidPooledConnection conn, Statement stmt) { + super(stmt); + this.conn = conn; + this.stmt = stmt; + } + + protected void addResultSetTrace(ResultSet resultSet) { + if (this.resultSetTrace == null) { + this.resultSetTrace = new ArrayList(1); + } else if (this.resultSetTrace.size() > 0) { + int lastIndex = this.resultSetTrace.size() - 1; + ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(lastIndex); + + try { + if (lastResultSet.isClosed()) { + this.resultSetTrace.set(lastIndex, resultSet); + return; + } + } catch (SQLException var5) { + } + } + + this.resultSetTrace.add(resultSet); + } + + protected void recordFetchRowCount(int fetchRowCount) { + if (this.fetchRowPeak < fetchRowCount) { + this.fetchRowPeak = fetchRowCount; + } + + } + + public int getFetchRowPeak() { + return this.fetchRowPeak; + } + + protected SQLException checkException(Throwable error) throws SQLException { + String sql = null; + if (this instanceof DruidPooledPreparedStatement) { + sql = ((DruidPooledPreparedStatement)this).getSql(); + } + + this.handleSocketTimeout(error); + ++this.exceptionCount; + return this.conn.handleException(error, sql); + } + + protected SQLException checkException(Throwable error, String sql) throws SQLException { + this.handleSocketTimeout(error); + ++this.exceptionCount; + return this.conn.handleException(error, sql); + } + + protected void handleSocketTimeout(Throwable error) throws SQLException { + if (this.conn != null && this.conn.transactionInfo == null && this.conn.holder != null) { + DruidDataSource dataSource = null; + DruidConnectionHolder holder = this.conn.holder; + if (holder.dataSource instanceof DruidDataSource) { + dataSource = (DruidDataSource)holder.dataSource; + } + + if (dataSource != null) { + if (dataSource.killWhenSocketReadTimeout) { + SQLException sqlException = null; + if (error instanceof SQLException) { + sqlException = (SQLException)error; + } + + if (sqlException != null) { + Throwable cause = error.getCause(); + boolean socketReadTimeout = cause instanceof SocketTimeoutException && "Read timed out".equals(cause.getMessage()); + if (socketReadTimeout) { + if (JdbcUtils.isMysqlDbType(dataSource.dbTypeName)) { + String killQuery = MySqlUtils.buildKillQuerySql(this.conn.getConnection(), (SQLException)error); + if (killQuery != null) { + DruidPooledConnection killQueryConn = null; + Statement killQueryStmt = null; + + try { + killQueryConn = dataSource.getConnection(1000L); + if (killQueryConn != null) { + killQueryStmt = killQueryConn.createStatement(); + killQueryStmt.execute(killQuery); + if (LOG.isDebugEnabled()) { + LOG.debug(killQuery + " success."); + } + + return; + } + } catch (Exception ex) { + LOG.warn(killQuery + " error.", ex); + return; + } finally { + JdbcUtils.close(killQueryStmt); + JdbcUtils.close(killQueryConn); + } + + } + } + } + } + } + } + } + } + + public DruidPooledConnection getPoolableConnection() { + return this.conn; + } + + public Statement getStatement() { + return this.stmt; + } + + protected void checkOpen() throws SQLException { + if (this.closed) { + Throwable disableError = null; + if (this.conn != null) { + disableError = this.conn.getDisableError(); + } + + if (disableError != null) { + throw new SQLException("statement is closed", disableError); + } else { + throw new SQLException("statement is closed"); + } + } + } + + protected void clearResultSet() { + if (this.resultSetTrace != null) { + for(ResultSet rs : this.resultSetTrace) { + try { + if (!rs.isClosed()) { + rs.close(); + } + } catch (SQLException ex) { + LOG.error("clearResultSet error", ex); + } + } + + this.resultSetTrace.clear(); + } + } + + public void incrementExecuteCount() { + DruidPooledConnection conn = this.getPoolableConnection(); + if (conn != null) { + DruidConnectionHolder holder = conn.getConnectionHolder(); + if (holder != null) { + DruidAbstractDataSource dataSource = holder.getDataSource(); + if (dataSource != null) { + dataSource.incrementExecuteCount(); + } + } + } + } + + public void incrementExecuteBatchCount() { + DruidPooledConnection conn = this.getPoolableConnection(); + if (conn != null) { + DruidConnectionHolder holder = conn.getConnectionHolder(); + if (holder != null) { + if (holder.getDataSource() != null) { + DruidAbstractDataSource dataSource = holder.getDataSource(); + if (dataSource != null) { + dataSource.incrementExecuteBatchCount(); + } + } + } + } + } + + public void incrementExecuteUpdateCount() { + DruidPooledConnection conn = this.getPoolableConnection(); + if (conn != null) { + DruidConnectionHolder holder = conn.getConnectionHolder(); + if (holder != null) { + DruidAbstractDataSource dataSource = holder.getDataSource(); + if (dataSource != null) { + dataSource.incrementExecuteUpdateCount(); + } + } + } + } + + public void incrementExecuteQueryCount() { + DruidPooledConnection conn = this.conn; + if (conn != null) { + DruidConnectionHolder holder = conn.holder; + if (holder != null) { + DruidAbstractDataSource dataSource = holder.dataSource; + if (dataSource != null) { + ++dataSource.executeQueryCount; + } + } + } + } + + protected void transactionRecord(String sql) throws SQLException { + this.conn.transactionRecord(sql); + } + + public final ResultSet executeQuery(String sql) throws SQLException { + this.checkOpen(); + this.incrementExecuteQueryCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + ResultSet var3; + try { + ResultSet rs = this.stmt.executeQuery(sql); + if (rs != null) { + DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs); + this.addResultSetTrace(poolableResultSet); + DruidPooledResultSet var4 = poolableResultSet; + return var4; + } + + var3 = rs; + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final int executeUpdate(String sql) throws SQLException { + this.checkOpen(); + this.incrementExecuteUpdateCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + int var2; + try { + var2 = this.stmt.executeUpdate(sql); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var2; + } + + protected final void errorCheck(Throwable t) { + String errorClassName = t.getClass().getName(); + if (errorClassName.endsWith(".CommunicationsException") && this.conn.holder != null && this.conn.holder.dataSource.testWhileIdle) { + DruidConnectionHolder holder = this.conn.holder; + DruidAbstractDataSource dataSource = holder.dataSource; + long currentTimeMillis = System.currentTimeMillis(); + long lastActiveTimeMillis = holder.lastActiveTimeMillis; + if (lastActiveTimeMillis < holder.lastKeepTimeMillis) { + lastActiveTimeMillis = holder.lastKeepTimeMillis; + } + + long idleMillis = currentTimeMillis - lastActiveTimeMillis; + long lastValidIdleMillis = currentTimeMillis - holder.lastActiveTimeMillis; + String errorMsg = "CommunicationsException, druid version " + VERSION.getVersionNumber() + ", jdbcUrl : " + dataSource.jdbcUrl + ", testWhileIdle " + dataSource.testWhileIdle + ", idle millis " + idleMillis + ", minIdle " + dataSource.minIdle + ", poolingCount " + dataSource.getPoolingCount() + ", timeBetweenEvictionRunsMillis " + dataSource.timeBetweenEvictionRunsMillis + ", lastValidIdleMillis " + lastValidIdleMillis + ", driver " + dataSource.driver.getClass().getName(); + if (dataSource.exceptionSorter != null) { + errorMsg = errorMsg + ", exceptionSorter " + dataSource.exceptionSorter.getClass().getName(); + } + + LOG.error(errorMsg); + } + + } + + public final int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + this.checkOpen(); + this.incrementExecuteUpdateCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + int var3; + try { + var3 = this.stmt.executeUpdate(sql, autoGeneratedKeys); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + this.checkOpen(); + this.incrementExecuteUpdateCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + int var3; + try { + var3 = this.stmt.executeUpdate(sql, columnIndexes); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final int executeUpdate(String sql, String[] columnNames) throws SQLException { + this.checkOpen(); + this.incrementExecuteUpdateCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + int var3; + try { + var3 = this.stmt.executeUpdate(sql, columnNames); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + this.checkOpen(); + this.incrementExecuteCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + boolean var3; + try { + var3 = this.stmt.execute(sql, autoGeneratedKeys); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final boolean execute(String sql, int[] columnIndexes) throws SQLException { + this.checkOpen(); + this.incrementExecuteCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + boolean var3; + try { + var3 = this.stmt.execute(sql, columnIndexes); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + public final boolean execute(String sql, String[] columnNames) throws SQLException { + this.checkOpen(); + this.incrementExecuteCount(); + this.transactionRecord(sql); + this.conn.beforeExecute(); + + boolean var3; + try { + var3 = this.stmt.execute(sql, columnNames); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t, sql); + } finally { + this.conn.afterExecute(); + } + + return var3; + } + + + public int getMaxFieldSize() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getMaxFieldSize(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public void close() throws SQLException { + if (!this.closed) { + this.clearResultSet(); + if (this.stmt != null) { + this.stmt.close(); + } + + this.closed = true; + DruidConnectionHolder connHolder = this.conn.getConnectionHolder(); + if (connHolder != null) { + connHolder.removeTrace(this); + } + + } + } + + public void setMaxFieldSize(int max) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setMaxFieldSize(max); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getMaxRows() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getMaxRows(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public void setMaxRows(int max) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setMaxRows(max); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final void setEscapeProcessing(boolean enable) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setEscapeProcessing(enable); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getQueryTimeout() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getQueryTimeout(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public void setQueryTimeout(int seconds) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setQueryTimeout(seconds); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final void cancel() throws SQLException { + this.checkOpen(); + + try { + this.stmt.cancel(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final SQLWarning getWarnings() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getWarnings(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final void clearWarnings() throws SQLException { + this.checkOpen(); + + try { + this.stmt.clearWarnings(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final void setCursorName(String name) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setCursorName(name); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + @Override + public final boolean execute(String sql) throws SQLException { + checkOpen(); + + incrementExecuteCount(); + transactionRecord(sql); + + try { + if (StringUtils.isNotEmpty(sql)){ + sql = sql.replace("TRUE", "1"); + sql = sql.replace("FALSE", "0"); + } + return stmt.execute(sql); + } catch (Throwable t) { + errorCheck(t); + throw checkException(t, sql); + } + } + + public final ResultSet getResultSet() throws SQLException { + this.checkOpen(); + + try { + ResultSet rs = this.stmt.getResultSet(); + if (rs == null) { + return null; + } else { + DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs); + this.addResultSetTrace(poolableResultSet); + return poolableResultSet; + } + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getUpdateCount() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getUpdateCount(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final boolean getMoreResults() throws SQLException { + this.checkOpen(); + + try { + boolean moreResults = this.stmt.getMoreResults(); + if (this.resultSetTrace != null && this.resultSetTrace.size() > 0) { + ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(this.resultSetTrace.size() - 1); + if (lastResultSet instanceof DruidPooledResultSet) { + DruidPooledResultSet pooledResultSet = (DruidPooledResultSet)lastResultSet; + pooledResultSet.closed = true; + } + } + + return moreResults; + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public void setFetchDirection(int direction) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setFetchDirection(direction); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getFetchDirection() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getFetchDirection(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public void setFetchSize(int rows) throws SQLException { + this.checkOpen(); + + try { + this.stmt.setFetchSize(rows); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getFetchSize() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getFetchSize(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getResultSetConcurrency() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getResultSetConcurrency(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getResultSetType() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getResultSetType(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final void addBatch(String sql) throws SQLException { + this.checkOpen(); + this.transactionRecord(sql); + + try { + this.stmt.addBatch(sql); + } catch (Throwable t) { + throw this.checkException(t, sql); + } + } + + public final void clearBatch() throws SQLException { + if (!this.closed) { + try { + this.stmt.clearBatch(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + } + + public int[] executeBatch() throws SQLException { + this.checkOpen(); + this.incrementExecuteBatchCount(); + this.conn.beforeExecute(); + + int[] var1; + try { + var1 = this.stmt.executeBatch(); + } catch (Throwable t) { + this.errorCheck(t); + throw this.checkException(t); + } finally { + this.conn.afterExecute(); + } + + return var1; + } + + public final Connection getConnection() throws SQLException { + this.checkOpen(); + return this.conn; + } + + public final boolean getMoreResults(int current) throws SQLException { + this.checkOpen(); + + try { + boolean results = this.stmt.getMoreResults(current); + if (this.resultSetTrace != null && this.resultSetTrace.size() > 0) { + ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(this.resultSetTrace.size() - 1); + if (lastResultSet instanceof DruidPooledResultSet) { + DruidPooledResultSet pooledResultSet = (DruidPooledResultSet)lastResultSet; + pooledResultSet.closed = true; + } + } + + return results; + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final ResultSet getGeneratedKeys() throws SQLException { + this.checkOpen(); + + try { + ResultSet rs = this.stmt.getGeneratedKeys(); + DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs); + this.addResultSetTrace(poolableResultSet); + return poolableResultSet; + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final int getResultSetHoldability() throws SQLException { + this.checkOpen(); + + try { + return this.stmt.getResultSetHoldability(); + } catch (Throwable t) { + throw this.checkException(t); + } + } + + public final boolean isClosed() throws SQLException { + return this.closed; + } + + public final void setPoolable(boolean poolable) throws SQLException { + if (!poolable) { + throw new SQLException("not support"); + } + } + + public final boolean isPoolable() throws SQLException { + return false; + } + + public String toString() { + return this.stmt.toString(); + } + + public void closeOnCompletion() throws SQLException { + this.stmt.closeOnCompletion(); + } + + public boolean isCloseOnCompletion() throws SQLException { + return this.stmt.isCloseOnCompletion(); + } +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java new file mode 100644 index 0000000..f9ce039 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java @@ -0,0 +1,30 @@ +package com.zt.plat.module.bpm; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 项目的启动类 + * + * 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + * 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + * 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + * + * @author ZT + */ +@SpringBootApplication +public class BpmServerApplication { + + public static void main(String[] args) { + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + + SpringApplication.run(BpmServerApplication.class, args); + + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java new file mode 100644 index 0000000..7c3b4c4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java @@ -0,0 +1,4 @@ +/** + * bpm API 实现类,定义暴露给其它模块的 API + */ +package com.zt.plat.module.bpm.api; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java new file mode 100644 index 0000000..52fa15f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.api.task; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +/** + * Flowable 流程实例 Api 实现类 + * + * @author ZT + * @author jason + */ +@RestController +@Validated +public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public CommonResult createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) { + return success(processInstanceService.createProcessInstance(userId, reqDTO)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java new file mode 100644 index 0000000..b0c466b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java @@ -0,0 +1,15 @@ +package com.zt.plat.module.bpm.controller.admin.base.dept; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "部门精简信息 VO") +@Data +public class DeptSimpleBaseVO { + + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术部") + private String name; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java new file mode 100644 index 0000000..141c17f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java @@ -0,0 +1,4 @@ +/** + * 基础包,放一些通用的 VO 类 + */ +package com.zt.plat.module.bpm.controller.admin.base; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java new file mode 100644 index 0000000..6fcbb96 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java @@ -0,0 +1,22 @@ +package com.zt.plat.module.bpm.controller.admin.base.user; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "用户精简信息 VO") +@Data +public class UserSimpleBaseVO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String nickname; + @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png") + private String avatar; + + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long deptId; + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部") + private String deptName; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java new file mode 100644 index 0000000..b318b4b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java @@ -0,0 +1,95 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.service.definition.BpmCategoryService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Comparator; +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - BPM 流程分类") +@RestController +@RequestMapping("/bpm/category") +@Validated +public class BpmCategoryController { + + @Resource + private BpmCategoryService categoryService; + + @PostMapping("/create") + @Operation(summary = "创建流程分类") + @PreAuthorize("@ss.hasPermission('bpm:category:create')") + public CommonResult createCategory(@Valid @RequestBody BpmCategorySaveReqVO createReqVO) { + return success(categoryService.createCategory(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新流程分类") + @PreAuthorize("@ss.hasPermission('bpm:category:update')") + public CommonResult updateCategory(@Valid @RequestBody BpmCategorySaveReqVO updateReqVO) { + categoryService.updateCategory(updateReqVO); + return success(true); + } + + @PutMapping("/update-sort-batch") + @Operation(summary = "批量更新流程分类的排序") + @Parameter(name = "ids", description = "分类编号列表", required = true, example = "1,2,3") + @PreAuthorize("@ss.hasPermission('bpm:category:update')") + public CommonResult updateCategorySortBatch(@RequestParam("ids") List ids) { + categoryService.updateCategorySortBatch(ids); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除流程分类") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:category:delete')") + public CommonResult deleteCategory(@RequestParam("id") Long id) { + categoryService.deleteCategory(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得流程分类") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:category:query')") + public CommonResult getCategory(@RequestParam("id") Long id) { + BpmCategoryDO category = categoryService.getCategory(id); + return success(BeanUtils.toBean(category, BpmCategoryRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得流程分类分页") + @PreAuthorize("@ss.hasPermission('bpm:category:query')") + public CommonResult> getCategoryPage(@Valid BpmCategoryPageReqVO pageReqVO) { + PageResult pageResult = categoryService.getCategoryPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, BpmCategoryRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获取流程分类的精简信息列表", description = "只包含被开启的分类,主要用于前端的下拉选项") + public CommonResult> getCategorySimpleList() { + List list = categoryService.getCategoryListByStatus(CommonStatusEnum.ENABLE.getStatus()); + list.sort(Comparator.comparingInt(BpmCategoryDO::getSort)); + return success(convertList(list, category -> new BpmCategoryRespVO().setId(category.getId()) + .setName(category.getName()).setCode(category.getCode()))); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java new file mode 100644 index 0000000..fa47346 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java @@ -0,0 +1,83 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - 动态表单") +@RestController +@RequestMapping("/bpm/form") +@Validated +public class BpmFormController { + + @Resource + private BpmFormService formService; + + @PostMapping("/create") + @Operation(summary = "创建动态表单") + @PreAuthorize("@ss.hasPermission('bpm:form:create')") + public CommonResult createForm(@Valid @RequestBody BpmFormSaveReqVO createReqVO) { + return success(formService.createForm(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新动态表单") + @PreAuthorize("@ss.hasPermission('bpm:form:update')") + public CommonResult updateForm(@Valid @RequestBody BpmFormSaveReqVO updateReqVO) { + formService.updateForm(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除动态表单") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:form:delete')") + public CommonResult deleteForm(@RequestParam("id") Long id) { + formService.deleteForm(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得动态表单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:form:query')") + public CommonResult getForm(@RequestParam("id") Long id) { + BpmFormDO form = formService.getForm(id); + return success(BeanUtils.toBean(form, BpmFormRespVO.class)); + } + + @GetMapping({"/list-all-simple", "/simple-list"}) + @Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框") + public CommonResult> getFormSimpleList() { + List list = formService.getFormList(); + return success(convertList(list, formDO -> // 只返回 id、name 字段 + new BpmFormRespVO().setId(formDO.getId()).setName(formDO.getName()))); + } + + @GetMapping("/page") + @Operation(summary = "获得动态表单分页") + @PreAuthorize("@ss.hasPermission('bpm:form:query')") + public CommonResult> getFormPage(@Valid BpmFormPageReqVO pageVO) { + PageResult pageResult = formService.getFormPage(pageVO); + return success(BeanUtils.toBean(pageResult, BpmFormRespVO.class)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java new file mode 100644 index 0000000..f787066 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java @@ -0,0 +1,200 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.*; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO; +import com.zt.plat.module.bpm.convert.definition.BpmModelConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.service.definition.BpmCategoryService; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import com.zt.plat.module.bpm.service.definition.BpmModelService; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 流程模型") +@RestController +@RequestMapping("/bpm/model") +@Validated +public class BpmModelController { + + @Resource + private BpmModelService modelService; + @Resource + private BpmFormService formService; + @Resource + private BpmCategoryService categoryService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @GetMapping("/list") + @Operation(summary = "获得模型分页") + @Parameter(name = "name", description = "模型名称", example = "芋艿") + public CommonResult> getModelList(@RequestParam(value = "name", required = false) String name) { + List list = modelService.getModelList(name); + if (CollUtil.isEmpty(list)) { + return success(Collections.emptyList()); + } + + // 获得 Form 表单 + Set formIds = convertSet(list, model -> { + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); + return metaInfo != null ? metaInfo.getFormId() : null; + }); + Map formMap = formService.getFormMap(formIds); + // 获得 Category Map + Map categoryMap = categoryService.getCategoryMap( + convertSet(list, Model::getCategory)); + // 获得 Deployment Map + Map deploymentMap = processDefinitionService.getDeploymentMap( + convertSet(list, Model::getDeploymentId)); + // 获得 ProcessDefinition Map + List processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds( + deploymentMap.keySet()); + Map processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId); + // 获得 User Map、Dept Map + Set userIds = convertSetByFlatMap(list, model -> { + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); + return metaInfo != null ? metaInfo.getStartUserIds().stream() : Stream.empty(); + }); + Map userMap = adminUserApi.getUserMap(userIds); + Set deptIds = convertSetByFlatMap(list, model -> { + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); + return metaInfo != null && metaInfo.getStartDeptIds() != null ? metaInfo.getStartDeptIds().stream() : Stream.empty(); + }); + Map deptMap = deptApi.getDeptMap(deptIds); + return success(BpmModelConvert.INSTANCE.buildModelList(list, + formMap, categoryMap, deploymentMap, processDefinitionMap, userMap, deptMap)); + } + + @GetMapping("/get") + @Operation(summary = "获得模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:model:query')") + public CommonResult getModel(@RequestParam("id") String id) { + Model model = modelService.getModel(id); + if (model == null) { + return null; + } + byte[] bpmnBytes = modelService.getModelBpmnXML(id); + BpmSimpleModelNodeVO simpleModel = modelService.getSimpleModel(id); + return success(BpmModelConvert.INSTANCE.buildModel(model, bpmnBytes, simpleModel)); + } + + @PostMapping("/create") + @Operation(summary = "新建模型") + @PreAuthorize("@ss.hasPermission('bpm:model:create')") + public CommonResult createModel(@Valid @RequestBody BpmModelSaveReqVO createRetVO) { + return success(modelService.createModel(createRetVO)); + } + + @PutMapping("/update") + @Operation(summary = "修改模型") + @PreAuthorize("@ss.hasPermission('bpm:model:update')") + public CommonResult updateModel(@Valid @RequestBody BpmModelSaveReqVO modelVO) { + modelService.updateModel(getLoginUserId(), modelVO); + return success(true); + } + + @PutMapping("/update-sort-batch") + @Operation(summary = "批量修改模型排序") + @Parameter(name = "ids", description = "编号数组", required = true, example = "1,2,3") + public CommonResult updateModelSortBatch(@RequestParam("ids") List ids) { + modelService.updateModelSortBatch(getLoginUserId(), ids); + return success(true); + } + + @PostMapping("/deploy") + @Operation(summary = "部署模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:model:deploy')") + public CommonResult deployModel(@RequestParam("id") String id) { + modelService.deployModel(getLoginUserId(), id); + return success(true); + } + + @PutMapping("/update-state") + @Operation(summary = "修改模型的状态", description = "实际更新的部署的流程定义的状态") + @PreAuthorize("@ss.hasPermission('bpm:model:update')") + public CommonResult updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) { + modelService.updateModelState(getLoginUserId(), reqVO.getId(), reqVO.getState()); + return success(true); + } + + @Deprecated + @PutMapping("/update-bpmn") + @Operation(summary = "修改模型的 BPMN") + @PreAuthorize("@ss.hasPermission('bpm:model:update')") + public CommonResult updateModelBpmn(@Valid @RequestBody BpmModeUpdateBpmnReqVO reqVO) { + modelService.updateModelBpmnXml(reqVO.getId(), reqVO.getBpmnXml()); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:model:delete')") + public CommonResult deleteModel(@RequestParam("id") String id) { + modelService.deleteModel(getLoginUserId(), id); + return success(true); + } + + @DeleteMapping("/clean") + @Operation(summary = "清理模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:model:clean')") + public CommonResult cleanModel(@RequestParam("id") String id) { + modelService.cleanModel(getLoginUserId(), id); + return success(true); + } + + // ========== 仿钉钉/飞书的精简模型 ========= + + @GetMapping("/simple/get") + @Operation(summary = "获得仿钉钉流程设计模型") + @Parameter(name = "modelId", description = "流程模型编号", required = true, example = "a2c5eee0-eb6c-11ee-abf4-0c37967c420a") + public CommonResult getSimpleModel(@RequestParam("id") String modelId){ + return success(modelService.getSimpleModel(modelId)); + } + + @Deprecated + @PostMapping("/simple/update") + @Operation(summary = "保存仿钉钉流程设计模型") + @PreAuthorize("@ss.hasPermission('bpm:model:update')") + public CommonResult updateSimpleModel(@Valid @RequestBody BpmSimpleModelUpdateReqVO reqVO) { + modelService.updateSimpleModel(getLoginUserId(), reqVO); + return success(Boolean.TRUE); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java new file mode 100644 index 0000000..e5ee29a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java @@ -0,0 +1,133 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.zt.plat.module.bpm.convert.definition.BpmProcessDefinitionConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.service.definition.BpmCategoryService; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; +import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 流程定义") +@RestController +@RequestMapping("/bpm/process-definition") +@Validated +public class BpmProcessDefinitionController { + + @Resource + private BpmProcessDefinitionService processDefinitionService; + @Resource + private BpmFormService formService; + @Resource + private BpmCategoryService categoryService; + + @GetMapping("/page") + @Operation(summary = "获得流程定义分页") + @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')") + public CommonResult> getProcessDefinitionPage( + BpmProcessDefinitionPageReqVO pageReqVO) { + PageResult pageResult = processDefinitionService.getProcessDefinitionPage(pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 获得 Category Map + Map categoryMap = categoryService.getCategoryMap( + convertSet(pageResult.getList(), ProcessDefinition::getCategory)); + // 获得 Deployment Map + Map deploymentMap = processDefinitionService.getDeploymentMap( + convertSet(pageResult.getList(), ProcessDefinition::getDeploymentId)); + // 获得 BpmProcessDefinitionInfoDO Map + Map processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), ProcessDefinition::getId)); + // 获得 Form Map + Map formMap = formService.getFormMap( + convertSet(processDefinitionMap.values(), BpmProcessDefinitionInfoDO::getFormId)); + return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionPage( + pageResult, deploymentMap, processDefinitionMap, formMap, categoryMap)); + } + + @GetMapping ("/list") + @Operation(summary = "获得流程定义列表") + @Parameter(name = "suspensionState", description = "挂起状态", required = true, example = "1") // 参见 Flowable SuspensionState 枚举 + public CommonResult> getProcessDefinitionList( + @RequestParam("suspensionState") Integer suspensionState) { + // 1.1 获得开启的流程定义 + List list = processDefinitionService.getProcessDefinitionListBySuspensionState(suspensionState); + if (CollUtil.isEmpty(list)) { + return success(Collections.emptyList()); + } + // 1.2 移除不可见的流程定义 + Map processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(list, ProcessDefinition::getId)); + Long userId = getLoginUserId(); + list.removeIf(processDefinition -> { + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionMap.get(processDefinition.getId()); + return processDefinitionInfo == null // 不存在 + || Boolean.FALSE.equals(processDefinitionInfo.getVisible()) // visible 不可见 + || !processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId); // 无权限发起 + }); + + // 2. 拼接 VO 返回 + return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionList( + list, null, processDefinitionMap, null, null)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获得流程定义精简列表", description = "只包含未挂起的流程,主要用于前端的下拉选项") + public CommonResult> getSimpleProcessDefinitionList() { + // 只查询未挂起的流程 + List list = processDefinitionService.getProcessDefinitionListBySuspensionState( + SuspensionState.ACTIVE.getStateCode()); + // 拼接 VO 返回,只返回 id、name、key + return success(convertList(list, definition -> new BpmProcessDefinitionRespVO() + .setId(definition.getId()).setName(definition.getName()).setKey(definition.getKey()))); + } + + @GetMapping ("/get") + @Operation(summary = "获得流程定义") + @Parameter(name = "id", description = "流程编号", required = true, example = "1024") + @Parameter(name = "key", description = "流程定义标识", required = true, example = "1024") + public CommonResult getProcessDefinition( + @RequestParam(value = "id", required = false) String id, + @RequestParam(value = "key", required = false) String key) { + ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id) + : processDefinitionService.getActiveProcessDefinition(key); + if (processDefinition == null) { + return success(null); + } + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId()); + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId()); + return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition( + processDefinition, null, processDefinitionInfo, null, null, bpmnModel)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java new file mode 100644 index 0000000..08fcb42 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; +import com.zt.plat.module.bpm.service.definition.BpmProcessExpressionService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - BPM 流程表达式") +@RestController +@RequestMapping("/bpm/process-expression") +@Validated +public class BpmProcessExpressionController { + + @Resource + private BpmProcessExpressionService processExpressionService; + + @PostMapping("/create") + @Operation(summary = "创建流程表达式") + @PreAuthorize("@ss.hasPermission('bpm:process-expression:create')") + public CommonResult createProcessExpression(@Valid @RequestBody BpmProcessExpressionSaveReqVO createReqVO) { + return success(processExpressionService.createProcessExpression(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新流程表达式") + @PreAuthorize("@ss.hasPermission('bpm:process-expression:update')") + public CommonResult updateProcessExpression(@Valid @RequestBody BpmProcessExpressionSaveReqVO updateReqVO) { + processExpressionService.updateProcessExpression(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除流程表达式") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-expression:delete')") + public CommonResult deleteProcessExpression(@RequestParam("id") Long id) { + processExpressionService.deleteProcessExpression(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得流程表达式") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:process-expression:query')") + public CommonResult getProcessExpression(@RequestParam("id") Long id) { + BpmProcessExpressionDO processExpression = processExpressionService.getProcessExpression(id); + return success(BeanUtils.toBean(processExpression, BpmProcessExpressionRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得流程表达式分页") + @PreAuthorize("@ss.hasPermission('bpm:process-expression:query')") + public CommonResult> getProcessExpressionPage( + @Valid BpmProcessExpressionPageReqVO pageReqVO) { + PageResult pageResult = processExpressionService.getProcessExpressionPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, BpmProcessExpressionRespVO.class)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java new file mode 100644 index 0000000..3077a91 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; +import com.zt.plat.module.bpm.service.definition.BpmProcessListenerService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - BPM 流程监听器") +@RestController +@RequestMapping("/bpm/process-listener") +@Validated +public class BpmProcessListenerController { + + @Resource + private BpmProcessListenerService processListenerService; + + @PostMapping("/create") + @Operation(summary = "创建流程监听器") + @PreAuthorize("@ss.hasPermission('bpm:process-listener:create')") + public CommonResult createProcessListener(@Valid @RequestBody BpmProcessListenerSaveReqVO createReqVO) { + return success(processListenerService.createProcessListener(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新流程监听器") + @PreAuthorize("@ss.hasPermission('bpm:process-listener:update')") + public CommonResult updateProcessListener(@Valid @RequestBody BpmProcessListenerSaveReqVO updateReqVO) { + processListenerService.updateProcessListener(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除流程监听器") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-listener:delete')") + public CommonResult deleteProcessListener(@RequestParam("id") Long id) { + processListenerService.deleteProcessListener(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得流程监听器") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:process-listener:query')") + public CommonResult getProcessListener(@RequestParam("id") Long id) { + BpmProcessListenerDO processListener = processListenerService.getProcessListener(id); + return success(BeanUtils.toBean(processListener, BpmProcessListenerRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得流程监听器分页") + @PreAuthorize("@ss.hasPermission('bpm:process-listener:query')") + public CommonResult> getProcessListenerPage( + @Valid BpmProcessListenerPageReqVO pageReqVO) { + PageResult pageResult = processListenerService.getProcessListenerPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, BpmProcessListenerRespVO.class)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java new file mode 100644 index 0000000..226e5da --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java @@ -0,0 +1,83 @@ +package com.zt.plat.module.bpm.controller.admin.definition; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import com.zt.plat.module.bpm.service.definition.BpmUserGroupService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +@Tag(name = "管理后台 - 用户组") +@RestController +@RequestMapping("/bpm/user-group") +@Validated +public class BpmUserGroupController { + + @Resource + private BpmUserGroupService userGroupService; + + @PostMapping("/create") + @Operation(summary = "创建用户组") + @PreAuthorize("@ss.hasPermission('bpm:user-group:create')") + public CommonResult createUserGroup(@Valid @RequestBody BpmUserGroupSaveReqVO createReqVO) { + return success(userGroupService.createUserGroup(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新用户组") + @PreAuthorize("@ss.hasPermission('bpm:user-group:update')") + public CommonResult updateUserGroup(@Valid @RequestBody BpmUserGroupSaveReqVO updateReqVO) { + userGroupService.updateUserGroup(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除用户组") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:user-group:delete')") + public CommonResult deleteUserGroup(@RequestParam("id") Long id) { + userGroupService.deleteUserGroup(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得用户组") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('bpm:user-group:query')") + public CommonResult getUserGroup(@RequestParam("id") Long id) { + BpmUserGroupDO userGroup = userGroupService.getUserGroup(id); + return success(BeanUtils.toBean(userGroup, BpmUserGroupRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得用户组分页") + @PreAuthorize("@ss.hasPermission('bpm:user-group:query')") + public CommonResult> getUserGroupPage(@Valid BpmUserGroupPageReqVO pageVO) { + PageResult pageResult = userGroupService.getUserGroupPage(pageVO); + return success(BeanUtils.toBean(pageResult, BpmUserGroupRespVO.class)); + } + + @GetMapping("/simple-list") + @Operation(summary = "获取用户组精简信息列表", description = "只包含被开启的用户组,主要用于前端的下拉选项") + public CommonResult> getUserGroupSimpleList() { + List list = userGroupService.getUserGroupListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(convertList(list, group -> new BpmUserGroupRespVO().setId(group.getId()).setName(group.getName()))); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java new file mode 100644 index 0000000..5de14e2 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.category; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - BPM 流程分类分页 Request VO") +@Data +public class BpmCategoryPageReqVO extends PageParam { + + @Schema(description = "分类名", example = "王五") + private String name; + + @Schema(description = "分类标志", example = "OA") + private String code; + + @Schema(description = "分类状态", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java new file mode 100644 index 0000000..5f3db13 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.category; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - BPM 流程分类 Response VO") +@Data +public class BpmCategoryRespVO { + + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") + private Long id; + + @Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + private String name; + + @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") + private String code; + + @Schema(description = "分类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") + private String description; + + @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED) + private Integer sort; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java new file mode 100644 index 0000000..0203453 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java @@ -0,0 +1,37 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.category; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - BPM 流程分类新增/修改 Request VO") +@Data +public class BpmCategorySaveReqVO { + + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") + private Long id; + + @Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @NotEmpty(message = "分类名不能为空") + private String name; + + @Schema(description = "分类描述", example = "你猜") + private String description; + + @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") + @NotEmpty(message = "分类标志不能为空") + private String code; + + @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "分类状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "分类排序不能为空") + private Integer sort; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java new file mode 100644 index 0000000..1d3bbed --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.expression; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - BPM 流程表达式分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmProcessExpressionPageReqVO extends PageParam { + + @Schema(description = "表达式名字", example = "李四") + private String name; + + @Schema(description = "表达式状态", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java new file mode 100644 index 0000000..2bb959b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java @@ -0,0 +1,30 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.expression; + +import com.alibaba.excel.annotation.ExcelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - BPM 流程表达式 Response VO") +@Data +public class BpmProcessExpressionRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3870") + @ExcelProperty("编号") + private Long id; + + @Schema(description = "表达式名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @ExcelProperty("表达式名字") + private String name; + + @Schema(description = "表达式状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "表达式", requiredMode = Schema.RequiredMode.REQUIRED) + private String expression; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java new file mode 100644 index 0000000..a5771d1 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.expression; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - BPM 流程表达式新增/修改 Request VO") +@Data +public class BpmProcessExpressionSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3870") + private Long id; + + @Schema(description = "表达式名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") + @NotEmpty(message = "表达式名字不能为空") + private String name; + + @Schema(description = "表达式状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "表达式状态不能为空") + private Integer status; + + @Schema(description = "表达式", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "表达式不能为空") + private String expression; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java new file mode 100644 index 0000000..53af985 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java @@ -0,0 +1,24 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.form; + +import lombok.Data; + +/** + * 流程表单字段 VO + */ +@Data +public class BpmFormFieldVO { + + /** + * 字段类型 + */ + private String type; + /** + * 字段标识 + */ + private String field; + /** + * 字段标题 + */ + private String title; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java new file mode 100644 index 0000000..4160512 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java @@ -0,0 +1,14 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.form; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 动态表单分页 Request VO") +@Data +public class BpmFormPageReqVO extends PageParam { + + @Schema(description = "表单名称", example = "ZT") + private String name; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java new file mode 100644 index 0000000..1950d7a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java @@ -0,0 +1,39 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 动态表单 Response VO") +@Data +public class BpmFormRespVO { + + @Schema(description = "表单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @NotNull(message = "表单名称不能为空") + private String name; + + @Schema(description = "表单的配置-JSON 字符串", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "表单的配置不能为空") + private String conf; + + @Schema(description = "表单项的数组-JSON 字符串的数组", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "表单项的数组不能为空") + private List fields; + + @Schema(description = "表单状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "表单状态不能为空") + private Integer status; // 参见 CommonStatusEnum 枚举 + + @Schema(description = "备注", example = "我是备注") + private String remark; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java new file mode 100644 index 0000000..5953485 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java @@ -0,0 +1,35 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.form; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +@Schema(description = "管理后台 - 动态表单创建/更新 Request VO") +@Data +public class BpmFormSaveReqVO { + + @Schema(description = "表单编号", example = "1024") + private Long id; + + @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @NotNull(message = "表单名称不能为空") + private String name; + + @Schema(description = "表单的配置-JSON 字符串", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "表单的配置不能为空") + private String conf; + + @Schema(description = "表单项的数组-JSON 字符串的数组", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "表单项的数组不能为空") + private List fields; + + @Schema(description = "表单状态-参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "表单状态不能为空") + private Integer status; + + @Schema(description = "备注", example = "我是备注") + private String remark; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java new file mode 100644 index 0000000..25a0c2f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java @@ -0,0 +1,28 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.group; + +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.util.date.DateUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 用户组分页 Request VO") +@Data +public class BpmUserGroupPageReqVO extends PageParam { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "组名", example = "ZT") + private String name; + + @Schema(description = "状态", example = "1") + private Integer status; + + @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "创建时间") + private LocalDateTime[] createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java new file mode 100644 index 0000000..7bff95d --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.group; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Set; + +@Schema(description = "管理后台 - 用户组 Response VO") +@Data +public class BpmUserGroupRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + private String name; + + @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT源码") + private String description; + + @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") + private Set userIds; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java new file mode 100644 index 0000000..ceccee3 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.group; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.Set; + +@Schema(description = "管理后台 - 用户组创建/修改 Request VO") +@Data +public class BpmUserGroupSaveReqVO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @NotNull(message = "组名不能为空") + private String name; + + @Schema(description = "描述", example = "ZT源码") + private String description; + + @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") + @NotNull(message = "成员编号数组不能为空") + private Set userIds; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + private Integer status; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java new file mode 100644 index 0000000..13b8a73 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java @@ -0,0 +1,30 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.listener; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "管理后台 - BPM 流程监听器分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmProcessListenerPageReqVO extends PageParam { + + @Schema(description = "监听器名字", example = "赵六") + private String name; + + @Schema(description = "监听器类型", example = "execution") + private String type; + + @Schema(description = "监听事件", example = "start") + private String event; + + @Schema(description = "状态", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java new file mode 100644 index 0000000..7bfdde8 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.listener; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - BPM 流程监听器 Response VO") +@Data +public class BpmProcessListenerRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13089") + private Long id; + + @Schema(description = "监听器名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + private String name; + + @Schema(description = "监听器类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "execution") + private String type; + + @Schema(description = "监听器状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "监听事件", requiredMode = Schema.RequiredMode.REQUIRED, example = "start") + private String event; + + @Schema(description = "监听器值类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "class") + private String valueType; + + @Schema(description = "监听器值", requiredMode = Schema.RequiredMode.REQUIRED) + private String value; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java new file mode 100644 index 0000000..908caa9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java @@ -0,0 +1,39 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.listener; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - BPM 流程监听器新增/修改 Request VO") +@Data +public class BpmProcessListenerSaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13089") + private Long id; + + @Schema(description = "监听器名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") + @NotEmpty(message = "监听器名字不能为空") + private String name; + + @Schema(description = "监听器类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "execution") + @NotEmpty(message = "监听器类型不能为空") + private String type; + + @Schema(description = "监听器状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "监听器状态不能为空") + private Integer status; + + @Schema(description = "监听事件", requiredMode = Schema.RequiredMode.REQUIRED, example = "start") + @NotEmpty(message = "监听事件不能为空") + private String event; + + @Schema(description = "监听器值类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "class") + @NotEmpty(message = "监听器值类型不能为空") + private String valueType; + + @Schema(description = "监听器值", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "监听器值不能为空") + private String value; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java new file mode 100644 index 0000000..03e0e5f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java @@ -0,0 +1,19 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 流程模型的更新 BPMN XML Request VO") +@Data +public class BpmModeUpdateBpmnReqVO { + + @Schema(description = "流程编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "流程编号不能为空") + private String id; + + @Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "BPMN XML 不能为空") + private String bpmnXml; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java new file mode 100644 index 0000000..8c9176f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -0,0 +1,180 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model; + +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.framework.common.validation.InEnum; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.enums.definition.BpmAutoApproveTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.hibernate.validator.constraints.URL; + +import java.util.List; + +/** + * BPM 流程 MetaInfo Response DTO + * 主要用于 { Model#setMetaInfo(String)} 的存储 + * + * 最终,它的字段和 + * {@link com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} + * 是一致的 + * + * @author ZT + */ +@Data +public class BpmModelMetaInfoVO { + + @Schema(description = "流程图标", example = "https://www.iocoder.cn/cloud.jpg") + @URL(message = "流程图标格式不正确") + private String icon; + + @Schema(description = "流程描述", example = "我是描述") + private String description; + + @Schema(description = "流程类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(BpmModelTypeEnum.class) + @NotNull(message = "流程类型不能为空") + private Integer type; + + @Schema(description = "表单类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + @InEnum(BpmModelFormTypeEnum.class) + @NotNull(message = "表单类型不能为空") + private Integer formType; + @Schema(description = "表单编号", example = "1024") + private Long formId; // formType 为 NORMAL 使用,必须非空 + + @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create") + private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空 + @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view") + private String formCustomViewPath; // 表单类型为 CUSTOM 时,必须非空 + + @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否可见不能为空") + private Boolean visible; + + @Schema(description = "可发起用户编号数组", example = "[1,2,3]") + private List startUserIds; + + @Schema(description = "可发起部门编号数组", example = "[2,4,6]") + private List startDeptIds; + + @Schema(description = "可管理用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2,4,6]") + @NotEmpty(message = "可管理用户编号数组不能为空") + private List managerUserIds; + + @Schema(description = "排序", example = "1") + private Long sort; // 创建时,后端自动生成 + + @Schema(description = "允许撤销审批中的申请", example = "true") + private Boolean allowCancelRunningProcess; + + @Schema(description = "流程 ID 规则", example = "{}") + private ProcessIdRule processIdRule; + + @Schema(description = "自动去重类型", example = "1") + @InEnum(BpmAutoApproveTypeEnum.class) + private Integer autoApprovalType; + + @Schema(description = "标题设置", example = "{}") + private TitleSetting titleSetting; + + @Schema(description = "摘要设置", example = "{}") + private SummarySetting summarySetting; + + @Schema(description = "流程前置通知设置", example = "{}") + private HttpRequestSetting processBeforeTriggerSetting; + + @Schema(description = "流程后置通知设置", example = "{}") + private HttpRequestSetting processAfterTriggerSetting; + + @Schema(description = "任务前置通知设置", example = "{}") + private HttpRequestSetting taskBeforeTriggerSetting; + + @Schema(description = "任务后置通知设置", example = "{}") + private HttpRequestSetting taskAfterTriggerSetting; + + @Schema(description = "流程 ID 规则") + @Data + @Valid + public static class ProcessIdRule { + + @Schema(description = "是否启用", example = "false") + @NotNull(message = "是否启用不能为空") + private Boolean enable; + + @Schema(description = "前缀", example = "XX") + private String prefix; + + @Schema(description = "中缀", example = "20250120") + private String infix; // 精确到日、精确到时、精确到分、精确到秒 + + @Schema(description = "后缀", example = "YY") + private String postfix; + + @Schema(description = "序列长度", example = "5") + @NotNull(message = "序列长度不能为空") + private Integer length; + + } + + @Schema(description = "标题设置") + @Data + @Valid + public static class TitleSetting { + + @Schema(description = "是否自定义", example = "false") + @NotNull(message = "是否自定义不能为空") + private Boolean enable; + + @Schema(description = "标题", example = "流程标题") + private String title; + + } + + @Schema(description = "摘要设置") + @Data + @Valid + public static class SummarySetting { + + @Schema(description = "是否自定义", example = "false") + @NotNull(message = "是否自定义不能为空") + private Boolean enable; + + @Schema(description = "摘要字段数组", example = "[]") + private List summary; + + } + + @Schema(description = "http 请求通知设置", example = "{}") + @Data + public static class HttpRequestSetting { + + @Schema(description = "请求路径", example = "http://127.0.0.1") + @NotEmpty(message = "请求 URL 不能为空") + @URL(message = "请求 URL 格式不正确") + private String url; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List header; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List body; + + /** + * 请求返回处理设置,用于修改流程表单值 + *

+ * key:表示要修改的流程表单字段名(name) + * value:接口返回的字段名 + */ + @Schema(description = "请求返回处理设置", example = "[]") + private List> response; + + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java new file mode 100644 index 0000000..4a79092 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java @@ -0,0 +1,57 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model; + +import com.zt.plat.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 流程模型 Response VO") +@Data +public class BpmModelRespVO extends BpmModelMetaInfoVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_cloud") + private String key; + + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + private String name; + + @Schema(description = "流程图标", example = "https://www.iocoder.cn/cloud.jpg") + private String icon; + + @Schema(description = "流程分类编号", example = "1") + private String category; + @Schema(description = "流程分类名字", example = "请假") + private String categoryName; + + @Schema(description = "表单名字", example = "请假表单") + private String formName; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "可发起的用户数组") + private List startUsers; + + @Schema(description = "可发起的部门数组") + private List startDepts; + + @Schema(description = "BPMN XML") + private String bpmnXml; + + @Schema(description = "仿钉钉流程设计模型对象") + private BpmSimpleModelNodeVO simpleModel; + + /** + * 最新部署的流程定义 + */ + private BpmProcessDefinitionRespVO processDefinition; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java new file mode 100644 index 0000000..b347b48 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java @@ -0,0 +1,34 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model; + +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 流程模型的保存 Request VO") +@Data +public class BpmModelSaveReqVO extends BpmModelMetaInfoVO { + + @Schema(description = "编号", example = "1024") + private String id; + + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_cloud") + @NotEmpty(message = "流程标识不能为空") + private String key; + + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @NotEmpty(message = "流程名称不能为空") + private String name; + + @Schema(description = "流程分类", example = "1") + private String category; + + @Schema(description = "BPMN XML") + private String bpmnXml; + + @Schema(description = "仿钉钉流程设计模型对象") + @Valid + private BpmSimpleModelNodeVO simpleModel; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java new file mode 100644 index 0000000..cd7fea1 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java @@ -0,0 +1,19 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 流程模型更新状态 Request VO") +@Data +public class BpmModelUpdateStateReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotNull(message = "编号不能为空") + private String id; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + private Integer state; // 参见 Flowable SuspensionState 枚举 + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java new file mode 100644 index 0000000..cd5177a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -0,0 +1,526 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple; + +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.framework.common.validation.InEnum; +import com.zt.plat.module.bpm.enums.definition.*; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.flowable.bpmn.model.IOParameter; +import org.hibernate.validator.constraints.URL; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Schema(description = "管理后台 - 仿钉钉流程设计模型节点 VO") +@Data +@JsonInclude(JsonInclude.Include.NON_NULL) +public class BpmSimpleModelNodeVO { + + @Schema(description = "模型节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartEvent_1") + @NotEmpty(message = "模型节点编号不能为空") + private String id; + + @Schema(description = "模型节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "模型节点类型不能为空") + @InEnum(BpmSimpleModelNodeTypeEnum.class) + private Integer type; + + @Schema(description = "模型节点名称", example = "领导审批") + private String name; + + @Schema(description = "节点展示内容", example = "指定成员: ZT源码") + private String showText; + + @Schema(description = "子节点") + private BpmSimpleModelNodeVO childNode; // 补充说明:在该模型下,子节点有且仅有一个,不会有多个 + + @Schema(description = "候选人策略", example = "30") + @InEnum(BpmTaskCandidateStrategyEnum.class) + private Integer candidateStrategy; // 用于审批,抄送节点 + + @Schema(description = "候选人参数") + private String candidateParam; // 用于审批,抄送节点 + + @Schema(description = "审批节点类型", example = "1") + @InEnum(BpmUserTaskApproveTypeEnum.class) + private Integer approveType; // 用于审批节点 + + @Schema(description = "多人审批方式", example = "1") + @InEnum(BpmUserTaskApproveMethodEnum.class) + private Integer approveMethod; // 用于审批节点 + + @Schema(description = "通过比例", example = "100") + private Integer approveRatio; // 通过比例,当多人审批方式为:多人会签(按通过比例) 需要设置 + + @Schema(description = "表单权限", example = "[]") + private List> fieldsPermission; + + @Schema(description = "操作按钮设置", example = "[]") + private List buttonsSetting; // 用于审批节点 + + @Schema(description = "是否需要签名", example = "false") + private Boolean signEnable; + + @Schema(description = "是否填写审批意见", example = "false") + private Boolean reasonRequire; + + /** + * 审批节点拒绝处理 + */ + private RejectHandler rejectHandler; + + /** + * 审批节点超时处理 + */ + private TimeoutHandler timeoutHandler; + + @Schema(description = "审批节点的审批人与发起人相同时,对应的处理类型", example = "1") + @InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class) + private Integer assignStartUserHandlerType; + + /** + * 空处理策略 + */ + private AssignEmptyHandler assignEmptyHandler; + + /** + * 创建任务监听器 + */ + private ListenerHandler taskCreateListener; + /** + * 指派任务监听器 + */ + private ListenerHandler taskAssignListener; + /** + * 完成任务监听器 + */ + private ListenerHandler taskCompleteListener; + + @Schema(description = "延迟器设置", example = "{}") + private DelaySetting delaySetting; + + @Schema(description = "条件节点") + private List conditionNodes; // 补充说明:有且仅有条件、并行、包容分支会使用 + + /** + * 条件节点设置 + */ + private ConditionSetting conditionSetting; // 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE + + @Schema(description = "路由分支组", example = "[]") + private List routerGroups; + + @Schema(description = "路由分支默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true + @JsonIgnore + private String routerDefaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE + + /** + * 触发器节点设置 + */ + private TriggerSetting triggerSetting; + + @Schema(description = "附加节点 Id", example = "UserTask_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true + @JsonIgnore + private String attachNodeId; // 目前用于触发器节点(HTTP 回调)。需要 UserTask 和 ReceiveTask(附加节点) 来完成 + + /** + * 子流程设置 + */ + private ChildProcessSetting childProcessSetting; + + @Schema(description = "任务监听器") + @Valid + @Data + public static class ListenerHandler { + + @Schema(description = "是否开启任务监听器", example = "false") + @NotNull(message = "是否开启任务监听器不能为空") + private Boolean enable; + + @Schema(description = "请求路径", example = "http://xxxxx") + private String path; + + @Schema(description = "请求头", example = "[]") + private List header; + + @Schema(description = "请求体", example = "[]") + private List body; + + } + + @Schema(description = "HTTP 请求参数设置") + @Data + public static class HttpRequestParam { + + @Schema(description = "值类型", example = "1") + @InEnum(BpmHttpRequestParamTypeEnum.class) + @NotNull(message = "值类型不能为空") + private Integer type; + + @Schema(description = "键", example = "xxx") + @NotEmpty(message = "键不能为空") + private String key; + + @Schema(description = "值", example = "xxx") + @NotEmpty(message = "值不能为空") + private String value; + + } + + @Schema(description = "审批节点拒绝处理策略") + @Data + public static class RejectHandler { + + @Schema(description = "拒绝处理类型", example = "1") + @InEnum(BpmUserTaskRejectHandlerTypeEnum.class) + private Integer type; + + @Schema(description = "任务拒绝后驳回的节点 Id", example = "Activity_1") + private String returnNodeId; + } + + @Schema(description = "审批节点超时处理策略") + @Valid + @Data + public static class TimeoutHandler { + + @Schema(description = "是否开启超时处理", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否开启超时处理不能为空") + private Boolean enable; + + @Schema(description = "任务超时未处理的行为", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "任务超时未处理的行为不能为空") + @InEnum(BpmUserTaskTimeoutHandlerTypeEnum.class) + private Integer type; + + @Schema(description = "超时时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "PT6H") + @NotEmpty(message = "超时时间不能为空") + private String timeDuration; + + @Schema(description = "最大提醒次数", example = "1") + private Integer maxRemindCount; + } + + @Schema(description = "空处理策略") + @Data + @Valid + public static class AssignEmptyHandler { + + @Schema(description = "空处理类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "空处理类型不能为空") + @InEnum(BpmUserTaskAssignEmptyHandlerTypeEnum.class) + private Integer type; + + @Schema(description = "指定人员审批的用户编号数组", example = "1") + private List userIds; + } + + @Schema(description = "操作按钮设置") + @Data + @Valid + public static class OperationButtonSetting { + + // TODO @jason:是不是按钮的标识?id 会和数据库的 id 自增有点模糊,key 标识会更合理一点点哈。 + @Schema(description = "按钮 Id", example = "1") + private Integer id; + + @Schema(description = "显示名称", example = "审批") + private String displayName; + + @Schema(description = "是否启用", example = "true") + private Boolean enable; + } + + @Schema(description = "条件设置") + @Data + @Valid + // 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE + public static class ConditionSetting { + + @Schema(description = "条件类型", example = "1") + @InEnum(BpmSimpleModeConditionTypeEnum.class) + private Integer conditionType; + + @Schema(description = "条件表达式", example = "${day>3}") + private String conditionExpression; + + @Schema(description = "是否默认条件", example = "true") + private Boolean defaultFlow; + + /** + * 条件组 + */ + private ConditionGroups conditionGroups; + } + + @Schema(description = "条件组") + @Data + @Valid + public static class ConditionGroups { + + @Schema(description = "条件组下的条件关系是否为与关系", example = "true") + @NotNull(message = "条件关系不能为空") + private Boolean and; + + @Schema(description = "条件组下的条件", example = "[]") + @NotEmpty(message = "条件不能为空") + private List conditions; + } + + @Schema(description = "条件") + @Data + @Valid + public static class Condition { + + @Schema(description = "条件下的规则关系是否为与关系", example = "true") + @NotNull(message = "规则关系不能为空") + private Boolean and; + + @Schema(description = "条件下的规则", example = "[]") + @NotEmpty(message = "规则不能为空") + private List rules; + } + + @Schema(description = "条件规则") + @Data + @Valid + public static class ConditionRule { + + @Schema(description = "运行符号", example = "==") + @NotEmpty(message = "运行符号不能为空") + private String opCode; + + @Schema(description = "运算符左边的值,例如某个流程变量", example = "startUserId") + @NotEmpty(message = "运算符左边的值不能为空") + private String leftSide; + + @Schema(description = "运算符右边的值", example = "1") + @NotEmpty(message = "运算符右边的值不能为空") + private String rightSide; + } + + @Schema(description = "延迟器") + @Data + @Valid + public static class DelaySetting { + + @Schema(description = "延迟时间类型", example = "1") + @NotNull(message = "延迟时间类型不能为空") + @InEnum(BpmDelayTimerTypeEnum.class) + private Integer delayType; + + @Schema(description = "延迟时间表达式", example = "PT1H,2025-01-01T00:00:00") + @NotEmpty(message = "延迟时间表达式不能为空") + private String delayTime; + } + + @Schema(description = "路由分支") + @Data + @Valid + public static class RouterSetting { + + @Schema(description = "节点 Id", example = "Activity_xxx") // 跳转到该节点 + @NotEmpty(message = "节点 Id 不能为空") + private String nodeId; + + @Schema(description = "条件类型", example = "1") + @InEnum(BpmSimpleModeConditionTypeEnum.class) + @NotNull(message = "条件类型不能为空") + private Integer conditionType; + + @Schema(description = "条件表达式", example = "${day>3}") + private String conditionExpression; + + @Schema(description = "条件组", example = "{}") + private ConditionGroups conditionGroups; + } + + @Schema(description = "触发器节点配置") + @Data + @Valid + public static class TriggerSetting { + + @Schema(description = "触发器类型", example = "1") + @InEnum(BpmTriggerTypeEnum.class) + @NotNull(message = "触发器类型不能为空") + private Integer type; + + /** + * http 请求触发器设置 + */ + @Valid + private HttpRequestTriggerSetting httpRequestSetting; + + /** + * 流程表单触发器设置 + */ + private List formSettings; + + @Schema(description = "http 请求触发器设置", example = "{}") + @Data + public static class HttpRequestTriggerSetting { + + @Schema(description = "请求路径", example = "http://127.0.0.1") + @NotEmpty(message = "请求 URL 不能为空") + @URL(message = "请求 URL 格式不正确") + private String url; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List header; + + @Schema(description = "请求头参数设置", example = "[]") + @Valid + private List body; + + /** + * 请求返回处理设置,用于修改流程表单值 + *

+ * key:表示要修改的流程表单字段名(name) + * value:接口返回的字段名 + */ + @Schema(description = "请求返回处理设置", example = "[]") + private List> response; + + /** + * Http 回调请求,需要指定回调任务 Key,用于回调执行 + */ + @Schema(description = "回调任务 Key", example = "xxx", hidden = true) + private String callbackTaskDefineKey; + + } + + @Schema(description = "流程表单触发器设置", example = "{}") + @Data + public static class FormTriggerSetting { + + @Schema(description = "条件类型", example = "1") + @InEnum(BpmSimpleModeConditionTypeEnum.class) + private Integer conditionType; + + @Schema(description = "条件表达式", example = "${day>3}") + private String conditionExpression; + + @Schema(description = "条件组", example = "{}") + private ConditionGroups conditionGroups; + + @Schema(description = "修改的表单字段", example = "{}") + private Map updateFormFields; + + @Schema(description = "删除表单字段", example = "[]") + private Set deleteFields; + } + } + + @Schema(description = "子流程节点配置") + @Data + @Valid + public static class ChildProcessSetting { + + @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") + @NotEmpty(message = "被调用流程不能为空") + private String calledProcessDefinitionKey; + + @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") + @NotEmpty(message = "被调用流程名称不能为空") + private String calledProcessDefinitionName; + + @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否异步不能为空") + private Boolean async; + + @Schema(description = "输入参数(主->子)", example = "[]") + private List inVariables; + + @Schema(description = "输出参数(子->主)", example = "[]") + private List outVariables; + + @Schema(description = "是否自动跳过子流程发起节点", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否自动跳过子流程发起节点不能为空") + private Boolean skipStartUserNode; + + @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + @NotNull(message = "子流程发起人配置不能为空") + private StartUserSetting startUserSetting; + + @Schema(description = "超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + private TimeoutSetting timeoutSetting; + + @Schema(description = "多实例设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") + private MultiInstanceSetting multiInstanceSetting; + + @Schema(description = "子流程发起人配置") + @Data + @Valid + public static class StartUserSetting { + + @Schema(description = "子流程发起人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "子流程发起人类型") + @InEnum(BpmChildProcessStartUserTypeEnum.class) + private Integer type; + + @Schema(description = "表单", example = "xxx") + private String formField; + + @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "当子流程发起人为空时类型不能为空") + @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class) + private Integer emptyType; + + } + + @Schema(description = "超时设置") + @Data + @Valid + public static class TimeoutSetting { + + @Schema(description = "是否开启超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否开启超时设置不能为空") + private Boolean enable; + + @Schema(description = "时间类型", example = "1") + @InEnum(BpmDelayTimerTypeEnum.class) + private Integer type; + + @Schema(description = "时间表达式", example = "PT1H,2025-01-01T00:00:00") + private String timeExpression; + + } + + @Schema(description = "多实例设置") + @Data + @Valid + public static class MultiInstanceSetting { + + @Schema(description = "是否开启多实例", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否开启多实例不能为空") + private Boolean enable; + + @Schema(description = "是否串行", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + @NotNull(message = "是否串行不能为空") + private Boolean sequential; + + @Schema(description = "完成比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") + @NotNull(message = "完成比例不能为空") + private Integer approveRatio; + + @Schema(description = "多实例来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "多实例来源类型不能为空") + @InEnum(BpmChildProcessMultiInstanceSourceTypeEnum.class) + private Integer sourceType; + + @Schema(description = "多实例来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "多实例来源不能为空") + private String source; + + } + + } +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java new file mode 100644 index 0000000..43981ba --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +// TODO @jason:需要考虑,如果某个节点的配置不正确,需要有提示;具体怎么实现,可以讨论下; +@Schema(description = "管理后台 - 仿钉钉流程设计模型的新增/修改 Request VO") +@Data +public class BpmSimpleModelUpdateReqVO { + + @Schema(description = "流程模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotEmpty(message = "流程模型编号不能为空") + private String id; // 对应 Flowable act_re_model 表 ID_ 字段 + + @Schema(description = "仿钉钉流程设计模型对象", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "仿钉钉流程设计模型对象不能为空") + @Valid + private BpmSimpleModelNodeVO simpleModel; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java new file mode 100644 index 0000000..8c61e28 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java @@ -0,0 +1,14 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.process; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "管理后台 - 流程定义分页 Request VO") +@Data +public class BpmProcessDefinitionPageReqVO extends PageParam { + + @Schema(description = "标识-精准匹配", example = "process1641042089407") + private String key; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java new file mode 100644 index 0000000..f0d2892 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -0,0 +1,71 @@ +package com.zt.plat.module.bpm.controller.admin.definition.vo.process; + +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 流程定义 Response VO") +@Data +public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer version; + + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + private String name; + + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao") + private String key; + + @Schema(description = "流程分类", example = "1") + private String category; + @Schema(description = "流程分类名字", example = "请假") + private String categoryName; + + @Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") + private Integer modelType; // 参见 BpmModelTypeEnum 枚举类 + + @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") + private String modelId; + + @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private String formConf; + @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) + private List formFields; + @Schema(description = "表单名字", example = "请假表单") + private String formName; + + @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer suspensionState; // 参见 SuspensionState 枚举 + + @Schema(description = "部署时间") + private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回 + + @Schema(description = "BPMN XML") + private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回 + + @Schema(description = "SIMPLE 设计器模型数据 json 格式") + private String simpleModel; // 非必须返回 + + @Schema(description = "流程定义排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long sort; + + @Schema(description = "BPMN UserTask 用户任务") + @Data + public static class UserTask { + + @Schema(description = "任务标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "sudo") + private String id; + + @Schema(description = "任务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + private String name; + + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http new file mode 100644 index 0000000..96bbf96 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http @@ -0,0 +1,12 @@ +### 请求 /bpm/oa/leave/create 接口 => 成功 +POST {{baseUrl}}/bpm/oa/leave/create +Content-Type: application/json +tenant-id: 1 +Authorization: Bearer {{token}} + +{ + "startTime": "2022-03-01", + "endTime": "2022-03-05", + "type": 1, + "reason": "我要请假啦啦啦!" +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java new file mode 100644 index 0000000..51d5094 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java @@ -0,0 +1,62 @@ +package com.zt.plat.module.bpm.controller.admin.oa; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveRespVO; +import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; +import com.zt.plat.module.bpm.service.oa.BpmOALeaveService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +/** + * OA 请假申请 Controller,用于演示自己存储数据,接入工作流的例子 + * + * @author jason + * @author ZT + */ +@Tag(name = "管理后台 - OA 请假申请") +@RestController +@RequestMapping("/bpm/oa/leave") +@Validated +public class BpmOALeaveController { + + @Resource + private BpmOALeaveService leaveService; + + @PostMapping("/create") + @PreAuthorize("@ss.hasPermission('bpm:oa-leave:create')") + @Operation(summary = "创建请求申请") + public CommonResult createLeave(@Valid @RequestBody BpmOALeaveCreateReqVO createReqVO) { + return success(leaveService.createLeave(getLoginUserId(), createReqVO)); + } + + @GetMapping("/get") + @PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')") + @Operation(summary = "获得请假申请") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + public CommonResult getLeave(@RequestParam("id") Long id) { + BpmOALeaveDO leave = leaveService.getLeave(id); + return success(BeanUtils.toBean(leave, BpmOALeaveRespVO.class)); + } + + @GetMapping("/page") + @PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')") + @Operation(summary = "获得请假申请分页") + public CommonResult> getLeavePage(@Valid BpmOALeavePageReqVO pageVO) { + PageResult pageResult = leaveService.getLeavePage(getLoginUserId(), pageVO); + return success(BeanUtils.toBean(pageResult, BpmOALeaveRespVO.class)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java new file mode 100644 index 0000000..5f65c5b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java @@ -0,0 +1,5 @@ +/** + * OA 示例,用于演示外部业务接入 BPM 工作流的示例 + * 一般的接入方式,只需要调用 接口,后续 Admin 用户在管理后台的【待办事务】进行审批 + */ +package com.zt.plat.module.bpm.controller.admin.oa; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java new file mode 100644 index 0000000..00779aa --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java @@ -0,0 +1,43 @@ +package com.zt.plat.module.bpm.controller.admin.oa.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; +import jakarta.validation.constraints.NotNull; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 请假申请创建 Request VO") +@Data +public class BpmOALeaveCreateReqVO { + + @Schema(description = "请假的开始时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "开始时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime startTime; + + @Schema(description = "请假的结束时间", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "结束时间不能为空") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime endTime; + + @Schema(description = "请假类型-参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读ZT源码") + private String reason; + + @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") + private Map> startUserSelectAssignees; + + @AssertTrue(message = "结束时间,需要在开始时间之后") + public boolean isEndTimeValid() { + return !getEndTime().isBefore(getStartTime()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java new file mode 100644 index 0000000..31c9f1b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java @@ -0,0 +1,29 @@ +package com.zt.plat.module.bpm.controller.admin.oa.vo; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 请假申请分页 Request VO") +@Data +public class BpmOALeavePageReqVO extends PageParam { + + @Schema(description = "状态", example = "1") + private Integer status; // 参见 BpmProcessInstanceResultEnum 枚举 + + @Schema(description = "请假类型,参见 bpm_oa_type", example = "1") + private Integer type; + + @Schema(description = "原因,模糊匹配", example = "阅读ZT源码") + private String reason; + + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + @Schema(description = "申请时间") + private LocalDateTime[] createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java new file mode 100644 index 0000000..ae43fcd --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.controller.admin.oa.vo; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 请假申请 Response VO") +@Data +public class BpmOALeaveRespVO { + + @Schema(description = "请假表单主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "请假类型,参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读ZT源码") + private String reason; + + @Schema(description = "申请时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "请假的开始时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime startTime; + + @Schema(description = "请假的结束时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime endTime; + + @Schema(description = "流程编号") + private String processInstanceId; + + @Schema(description = "审批结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http new file mode 100644 index 0000000..c690827 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http @@ -0,0 +1,16 @@ +### 请求 /bpm/process-instance/get-bpmn 接口 => 成功 +GET {{baseUrl}}/bpm/process-instance/get-bpmn-model-view?id=1d5fb5a6-85f8-11ef-b717-7e93075f94e3 +Content-Type: application/json +tenant-id: 1 +Authorization: Bearer {{token}} + +### 请求 /bpm/process-instance/get-bpmn 接口 => 失败 +#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=1d5fb5a6-85f8-11ef-b717-7e93075f94e3 +#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=3ee5c5ba-904a-11ef-a76e-b2ed5d6ef911 +#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=f630dfa2-8f92-11ef-947c-ba5e239a6eb4 +#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=9de8bdbf-9133-11ef-ae97-eaf49df1f932 +#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=dd2188eb-9394-11ef-a039-7a9ac3d9eb6b +GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processDefinitionId=test-auto:1:c70a799a-9394-11ef-a039-7a9ac3d9eb6b +Content-Type: application/json +tenant-id: 1 +Authorization: Bearer {{token}} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java new file mode 100644 index 0000000..53ea6fa --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -0,0 +1,202 @@ +package com.zt.plat.module.bpm.controller.admin.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; +import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.service.definition.BpmCategoryService; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.bpm.service.task.BpmTaskService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.task.api.Task; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 流程实例") // 流程实例,通过流程定义创建的一次“申请” +@RestController +@RequestMapping("/bpm/process-instance") +@Validated +public class BpmProcessInstanceController { + + @Resource + private BpmProcessInstanceService processInstanceService; + @Resource + private BpmTaskService taskService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + @Resource + private BpmCategoryService categoryService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @GetMapping("/my-page") + @Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + public CommonResult> getProcessInstanceMyPage( + @Valid BpmProcessInstancePageReqVO pageReqVO) { + PageResult pageResult = processInstanceService.getProcessInstancePage( + getLoginUserId(), pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 拼接返回 + Map> taskMap = taskService.getTaskMapByProcessInstanceIds( + convertList(pageResult.getList(), HistoricProcessInstance::getId)); + Map processDefinitionMap = processDefinitionService.getProcessDefinitionMap( + convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + Map categoryMap = categoryService.getCategoryMap( + convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + Set userIds = convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId())); + userIds.addAll(convertSetByFlatMap(taskMap.values(), + tasks -> tasks.stream().map(Task::getAssignee).filter(StrUtil::isNotBlank).map(Long::parseLong))); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, + processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap)); + } + + @GetMapping("/manager-page") + @Operation(summary = "获得管理流程实例的分页列表", description = "在【流程实例】菜单中,进行调用") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:manager-query')") + public CommonResult> getProcessInstanceManagerPage( + @Valid BpmProcessInstancePageReqVO pageReqVO) { + PageResult pageResult = processInstanceService.getProcessInstancePage( + null, pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty(pageResult.getTotal())); + } + + // 拼接返回 + Map> taskMap = taskService.getTaskMapByProcessInstanceIds( + convertList(pageResult.getList(), HistoricProcessInstance::getId)); + Map processDefinitionMap = processDefinitionService.getProcessDefinitionMap( + convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + Map categoryMap = categoryService.getCategoryMap( + convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); + // 发起人信息 + Map userMap = adminUserApi.getUserMap( + convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId()))); + Map deptMap = deptApi.getDeptMap( + convertSet(userMap.values(), DeptUtil::getDeptId)); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, + processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap)); + } + + @PostMapping("/create") + @Operation(summary = "新建流程实例") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + public CommonResult createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) { + return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO)); + } + + @GetMapping("/get") + @Operation(summary = "获得指定流程实例", description = "在【流程详细】界面中,进行调用") + @Parameter(name = "id", description = "流程实例的编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + public CommonResult getProcessInstance(@RequestParam("id") String id) { + HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(id); + if (processInstance == null) { + return success(null); + } + + // 拼接返回 + ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( + processInstance.getProcessDefinitionId()); + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo( + processInstance.getProcessDefinitionId()); + AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData(); + DeptRespDTO dept = null; + if (startUser != null) { + Long deptId = DeptUtil.getDeptId(startUser); + if (deptId > 0) { + dept = deptApi.getDept(deptId).getCheckedData(); + } + } + return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance, + processDefinition, processDefinitionInfo, startUser, dept)); + } + + @DeleteMapping("/cancel-by-start-user") + @Operation(summary = "用户取消流程实例", description = "取消发起的流程") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')") + public CommonResult cancelProcessInstanceByStartUser( + @Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) { + processInstanceService.cancelProcessInstanceByStartUser(getLoginUserId(), cancelReqVO); + return success(true); + } + + @DeleteMapping("/cancel-by-admin") + @Operation(summary = "管理员取消流程实例", description = "管理员撤回流程") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel-by-admin')") + public CommonResult cancelProcessInstanceByManager( + @Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) { + processInstanceService.cancelProcessInstanceByAdmin(getLoginUserId(), cancelReqVO); + return success(true); + } + + @GetMapping("/get-approval-detail") + @Operation(summary = "获得审批详情") + @Parameter(name = "id", description = "流程实例的编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") + public CommonResult getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); + } + return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); + } + + @GetMapping("/get-next-approval-nodes") + @Operation(summary = "获取下一个执行的流程节点") + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + @SuppressWarnings("unchecked") + public CommonResult> getNextApprovalNodes(@Valid BpmApprovalDetailReqVO reqVO) { + if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); + } + return success(processInstanceService.getNextApprovalNodes(getLoginUserId(), reqVO)); + } + + @GetMapping("/get-bpmn-model-view") + @Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用") + @Parameter(name = "id", description = "流程实例的编号", required = true) + public CommonResult getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) { + return success(processInstanceService.getProcessInstanceBpmnModelView(id)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java new file mode 100644 index 0000000..bd644ca --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java @@ -0,0 +1,89 @@ +package com.zt.plat.module.bpm.controller.admin.task; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.MapUtils; +import com.zt.plat.framework.common.util.date.DateUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.cc.BpmProcessInstanceCopyRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.flowable.engine.history.HistoricProcessInstance; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Map; +import java.util.stream.Stream; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; + +@Tag(name = "管理后台 - 流程实例抄送") +@RestController +@RequestMapping("/bpm/process-instance/copy") +@Validated +public class BpmProcessInstanceCopyController { + + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; + @Resource + private BpmProcessInstanceService processInstanceService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + + @Resource + private AdminUserApi adminUserApi; + + @GetMapping("/page") + @Operation(summary = "获得抄送流程分页列表") + @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:query')") + public CommonResult> getProcessInstanceCopyPage( + @Valid BpmProcessInstanceCopyPageReqVO pageReqVO) { + PageResult pageResult = processInstanceCopyService.getProcessInstanceCopyPage( + getLoginUserId(), pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(new PageResult<>(pageResult.getTotal())); + } + + // 拼接返回 + Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap( + convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId)); + Map userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(), + copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator())))); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessDefinitionId)); + return success(convertPage(pageResult, copy -> { + BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class); + MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()), + user -> copyVO.setStartUser(BeanUtils.toBean(user, UserSimpleBaseVO.class))); + MapUtils.findAndThen(userMap, copy.getStartUserId(), + user -> copyVO.setCreateUser(BeanUtils.toBean(user, UserSimpleBaseVO.class))); + MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(), + processInstance -> { + copyVO.setSummary(FlowableUtils.getSummary( + processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()), + processInstance.getProcessVariables())); + copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime())); + }); + return copyVO; + })); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java new file mode 100644 index 0000000..54db1af --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java @@ -0,0 +1,252 @@ +package com.zt.plat.module.bpm.controller.admin.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.*; +import com.zt.plat.module.bpm.convert.task.BpmTaskConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.bpm.service.task.BpmTaskService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.getLoginUserId; + +/** + * @author chenbowen + */ +@Tag(name = "管理后台 - 流程任务实例") +@RestController +@RequestMapping("/bpm/task") +@Validated +public class BpmTaskController { + + @Resource + private BpmTaskService taskService; + @Resource + private BpmProcessInstanceService processInstanceService; + @Resource + private BpmFormService formService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @GetMapping("todo-page") + @Operation(summary = "获取 Todo 待办任务分页") + @PreAuthorize("@ss.hasPermission('bpm:task:query')") + public CommonResult> getTaskTodoPage(@Valid BpmTaskPageReqVO pageVO) { + PageResult pageResult = taskService.getTaskTodoPage(getLoginUserId(), pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + + // 拼接数据 + Map processInstanceMap = processInstanceService.getProcessInstanceMap( + convertSet(pageResult.getList(), Task::getProcessInstanceId)); + Map userMap = adminUserApi.getUserMap( + convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), Task::getProcessDefinitionId)); + return success(BpmTaskConvert.INSTANCE.buildTodoTaskPage(pageResult, processInstanceMap, userMap, processDefinitionInfoMap)); + } + + @GetMapping("done-page") + @Operation(summary = "获取 Done 已办任务分页") + @PreAuthorize("@ss.hasPermission('bpm:task:query')") + public CommonResult> getTaskDonePage(@Valid BpmTaskPageReqVO pageVO) { + PageResult pageResult = taskService.getTaskDonePage(getLoginUserId(), pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + + // 拼接数据 + Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap( + convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); + Map userMap = adminUserApi.getUserMap( + convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); + return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null, processDefinitionInfoMap)); + } + + @GetMapping("manager-page") + @Operation(summary = "获取全部任务的分页", description = "用于【流程任务】菜单") + @PreAuthorize("@ss.hasPermission('bpm:task:mananger-query')") + public CommonResult> getTaskManagerPage(@Valid BpmTaskPageReqVO pageVO) { + PageResult pageResult = taskService.getTaskPage(getLoginUserId(), pageVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(PageResult.empty()); + } + + // 拼接数据 + Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap( + convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); + // 获得 User 和 Dept Map + Set userIds = convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())); + userIds.addAll(convertSet(pageResult.getList(), task -> NumberUtils.parseLong(task.getAssignee()))); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( + convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); + return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap, processDefinitionInfoMap)); + } + + @GetMapping("/list-by-process-instance-id") + @Operation(summary = "获得指定流程实例的任务列表", description = "包括完成的、未完成的") + @Parameter(name = "processInstanceId", description = "流程实例的编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:task:query')") + public CommonResult> getTaskListByProcessInstanceId( + @RequestParam("processInstanceId") String processInstanceId) { + List taskList = taskService.getTaskListByProcessInstanceId(processInstanceId, true); + if (CollUtil.isEmpty(taskList)) { + return success(Collections.emptyList()); + } + + // 拼接数据 + Set userIds = convertSetByFlatMap(taskList, task -> + Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner()))); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + // 获得 Form Map + Map formMap = formService.getFormMap( + convertSet(taskList, task -> { + String formKey = task.getFormKey(); + if (StrUtil.isBlank(formKey)) { + return 0L; + } + try { + return Long.parseLong(formKey); + } catch (NumberFormatException e) { + // 如果 formKey 不是数字(比如是URL),返回0L + return 0L; + } + })); + return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, + formMap, userMap, deptMap)); + } + + @PutMapping("/approve") + @Operation(summary = "通过任务") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) { + taskService.approveTask(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/reject") + @Operation(summary = "不通过任务") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) { + taskService.rejectTask(getLoginUserId(), reqVO); + return success(true); + } + + @GetMapping("/list-by-return") + @Operation(summary = "获取所有可退回的节点", description = "用于【流程详情】的【退回】按钮") + @Parameter(name = "taskId", description = "当前任务ID", required = true) + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult> getTaskListByReturn(@RequestParam("id") String id) { + List userTaskList = taskService.getUserTaskListByReturn(id); + return success(convertList(userTaskList, userTask -> // 只返回 id 和 name + new BpmTaskRespVO().setName(userTask.getName()).setTaskDefinitionKey(userTask.getId()))); + } + + @PutMapping("/return") + @Operation(summary = "退回任务", description = "用于【流程详情】的【退回】按钮") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) { + taskService.returnTask(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/delegate") + @Operation(summary = "委派任务", description = "用于【流程详情】的【委派】按钮") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) { + taskService.delegateTask(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/transfer") + @Operation(summary = "转派任务", description = "用于【流程详情】的【转派】按钮") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult transferTask(@Valid @RequestBody BpmTaskTransferReqVO reqVO) { + taskService.transferTask(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/create-sign") + @Operation(summary = "加签", description = "before 前加签,after 后加签") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult createSignTask(@Valid @RequestBody BpmTaskSignCreateReqVO reqVO) { + taskService.createSignTask(getLoginUserId(), reqVO); + return success(true); + } + + @DeleteMapping("/delete-sign") + @Operation(summary = "减签") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult deleteSignTask(@Valid @RequestBody BpmTaskSignDeleteReqVO reqVO) { + taskService.deleteSignTask(getLoginUserId(), reqVO); + return success(true); + } + + @PutMapping("/copy") + @Operation(summary = "抄送任务") + @PreAuthorize("@ss.hasPermission('bpm:task:update')") + public CommonResult copyTask(@Valid @RequestBody BpmTaskCopyReqVO reqVO) { + taskService.copyTask(getLoginUserId(), reqVO); + return success(true); + } + + @GetMapping("/list-by-parent-task-id") + @Operation(summary = "获得指定父级任务的子任务列表") // 目前用于,减签的时候,获得子任务列表 + @Parameter(name = "parentTaskId", description = "父级任务编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:task:query')") + public CommonResult> getTaskListByParentTaskId(@RequestParam("parentTaskId") String parentTaskId) { + List taskList = taskService.getTaskListByParentTaskId(parentTaskId); + if (CollUtil.isEmpty(taskList)) { + return success(Collections.emptyList()); + } + // 拼接数据 + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(taskList, + user -> Stream.of(NumberUtils.parseLong(user.getAssignee()), NumberUtils.parseLong(user.getOwner())))); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + return success(BpmTaskConvert.INSTANCE.buildTaskListByParentTaskId(taskList, userMap, deptMap)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java new file mode 100644 index 0000000..444aeae --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java @@ -0,0 +1,25 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.activity; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 流程活动的 Response VO") +@Data +public class BpmActivityRespVO { + + @Schema(description = "流程活动的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String key; + @Schema(description = "流程活动的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartEvent") + private String type; + + @Schema(description = "流程活动的开始时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime startTime; + @Schema(description = "流程活动的结束时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime endTime; + + @Schema(description = "关联的流程任务的编号", example = "2048") + private String taskId; // 关联的流程任务,只有 UserTask 等类型才有 + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java new file mode 100644 index 0000000..b968f9a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java @@ -0,0 +1,48 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.cc; + +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "管理后台 - 流程实例抄送的分页 Item Response VO") +@Data +public class BpmProcessInstanceCopyRespVO { + + @Schema(description = "抄送主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private Long id; + + @Schema(description = "发起人", requiredMode = Schema.RequiredMode.REQUIRED) + private UserSimpleBaseVO startUser; + + @Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "A233") + private String processInstanceId; + @Schema(description = "流程实例的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试") + private String processInstanceName; + @Schema(description = "流程实例的发起时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime processInstanceStartTime; + + @Schema(description = "流程活动的编号", requiredMode = Schema.RequiredMode.REQUIRED) + private String activityId; + @Schema(description = "流程活动的名字", requiredMode = Schema.RequiredMode.REQUIRED) + private String activityName; + + @Schema(description = "流程活动的编号") + private String taskId; + + @Schema(description = "抄送人意见") + private String reason; + + @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED) + private UserSimpleBaseVO createUser; + + @Schema(description = "抄送时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "流程摘要", example = "[]") + private List> summary; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java new file mode 100644 index 0000000..3f37d7a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java @@ -0,0 +1,40 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; +import lombok.Data; + +import java.util.Map; + +@Schema(description = "管理后台 - 审批详情 Request VO") +@Data +public class BpmApprovalDetailReqVO { + + @Schema(description = "流程定义的编号", example = "1024") + private String processDefinitionId; // 使用场景:发起流程时,传流程定义 ID + + @Schema(description = "流程变量") + private Map processVariables; // 使用场景:同 processDefinitionId,用于流程预测 + + @Schema(description = "流程变量") + private String processVariablesStr; // 解决 GET 无法传递对象的问题,最终转换成 processVariables 变量 + + @Schema(description = "流程实例的编号", example = "1024") + private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID + + // TODO @芋艿:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 + @Schema(description = "流程活动编号", example = "StartUserNode") + private String activityId; // 用于获取表单权限。1)发起流程时,传“发起人节点” activityId 可获取发起人的表单权限;2)从抄送列表界面进来时,传抄送的 activityId 可获取抄送人的表单权限; + + @Schema(description = "流程任务编号", example = "95f2f08b-621b-11ef-bf39-00ff4722db8b") + private String taskId; // 用于获取表单权限。1)从待审批/已审批界面进来时,传递 taskId 任务编号,可获取任务节点的变得权限 + + @AssertTrue(message = "流程定义的编号和流程实例的编号不能同时为空") + @JsonIgnore + public boolean isValidProcessParam() { + return StrUtil.isNotEmpty(processDefinitionId) || StrUtil.isNotEmpty(processInstanceId); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java new file mode 100644 index 0000000..a23c868 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java @@ -0,0 +1,112 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + + +@Schema(description = "管理后台 - 审批详情 Response VO") +@Data +public class BpmApprovalDetailRespVO { + + @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 + + @Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List activityNodes; + + @Schema(description = "表单字段权限") + private Map formFieldsPermission; + + @Schema(description = "待办任务") + private BpmTaskRespVO todoTask; + + /** + * 所属流程定义信息 + */ + private BpmProcessDefinitionRespVO processDefinition; + + /** + * 所属流程实例信息 + */ + private BpmProcessInstanceRespVO processInstance; + + @Schema(description = "活动节点信息") + @Data + public static class ActivityNode { + + @Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartUserNode") + private String id; + + @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "发起人") + private String name; + + @Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer nodeType; // 参见 BpmSimpleModelNodeType 枚举 + + @Schema(description = "节点状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer status; // 参见 BpmTaskStatusEnum 枚举 + + @Schema(description = "节点的开始时间") + private LocalDateTime startTime; + @Schema(description = "节点的结束时间") + private LocalDateTime endTime; + + @Schema(description = "审批节点的任务信息") + private List tasks; + + @Schema(description = "候选人策略", example = "35") + private Integer candidateStrategy; // 参见 BpmTaskCandidateStrategyEnum 枚举。主要用于发起时,审批节点、抄送节点自选 + + @Schema(description = "候选人用户 ID 列表", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 candidateUsers + private List candidateUserIds; + + @Schema(description = "候选人用户列表") + private List candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表 + + @Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf") + private String processInstanceId; // 当且仅当,该节点是子流程节点时,才会有值(CallActivity 的 processInstanceId 字段) + + } + + @Schema(description = "活动节点的任务信息") + @Data + public static class ActivityNodeTask { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String id; + + @Schema(description = "任务所属人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser + private Long owner; + + @Schema(description = "任务所属人", example = "1024") + private UserSimpleBaseVO ownerUser; + + @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser + private Long assignee; + + @Schema(description = "任务分配人", example = "2048") + private UserSimpleBaseVO assigneeUser; + + @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; // 参见 BpmTaskStatusEnum 枚举 + + @Schema(description = "审批意见", example = "同意") + private String reason; + + @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") + private String signPicUrl; + + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java new file mode 100644 index 0000000..bf520a0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java @@ -0,0 +1,43 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; +import java.util.Set; + +@Schema(description = "管理后台 - 流程示例的 BPMN 视图 Response VO") +@Data +public class BpmProcessInstanceBpmnModelViewRespVO { + + // ========== 基本信息 ========== + + @Schema(description = "流程实例信息", requiredMode = Schema.RequiredMode.REQUIRED) + private BpmProcessInstanceRespVO processInstance; + + @Schema(description = "任务列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List tasks; + + @Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED) + private String bpmnXml; + + @Schema(description = "SIMPLE 模型") + private BpmSimpleModelNodeVO simpleModel; + + // ========== 进度信息 ========== + + @Schema(description = "进行中的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set unfinishedTaskActivityIds; // 只包括 UserTask + + @Schema(description = "已经完成的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set finishedTaskActivityIds; // 包括 UserTask、Gateway 等,不包括 SequenceFlow + + @Schema(description = "已经完成的连线节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set finishedSequenceFlowActivityIds; // 只包括 SequenceFlow + + @Schema(description = "已经拒绝的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set rejectedTaskActivityIds; // 只包括 UserTask + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java new file mode 100644 index 0000000..b9d19e2 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java @@ -0,0 +1,19 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 流程实例的取消 Request VO") +@Data +public class BpmProcessInstanceCancelReqVO { + + @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "流程实例的编号不能为空") + private String id; + + @Schema(description = "取消原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "不请假了!") + @NotEmpty(message = "取消原因不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java new file mode 100644 index 0000000..2aff881 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 流程实例抄送的分页 Request VO") +@Data +public class BpmProcessInstanceCopyPageReqVO extends PageParam { + + @Schema(description = "流程名称", example = "ZT") + private String processInstanceName; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java new file mode 100644 index 0000000..bb46b1f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java @@ -0,0 +1,24 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 流程实例的创建 Request VO") +@Data +public class BpmProcessInstanceCreateReqVO { + + @Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "流程定义编号不能为空") + private String processDefinitionId; + + @Schema(description = "变量实例(动态表单)") + private Map variables; + + @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") + private Map> startUserSelectAssignees; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java new file mode 100644 index 0000000..130d75b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.validation.InEnum; +import com.zt.plat.module.bpm.enums.task.BpmProcessInstanceStatusEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - 流程实例分页 Request VO") +@Data +public class BpmProcessInstancePageReqVO extends PageParam { + + @Schema(description = "流程名称", example = "ZT") + private String name; + + @Schema(description = "流程定义的标识", example = "2048") + private String processDefinitionKey; // 精准匹配 + + @Schema(description = "流程实例的状态", example = "1") + @InEnum(BpmProcessInstanceStatusEnum.class) + private Integer status; + + @Schema(description = "流程分类", example = "1") + private String category; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + + @Schema(description = "结束时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] endTime; + + @Schema(description = "发起用户编号", example = "1024") + private Long startUserId; // 注意,只有在【流程实例】菜单,才使用该参数 + + @Schema(description = "动态表单字段查询 JSON Str", example = "{}") + private String formFieldsParams; // SpringMVC 在 get 请求下,无法方便的定义 Map 类型的参数,所以通过 String 接收后,逻辑里面转换 + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java new file mode 100644 index 0000000..14cc77a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java @@ -0,0 +1,86 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 流程实例的 Response VO") +@Data +public class BpmProcessInstanceRespVO { + + @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + private String name; + + @Schema(description = "流程摘要") + private List> summary; // 只有流程表单,才有摘要! + + @Schema(description = "流程分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String category; + @Schema(description = "流程分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "请假") + private String categoryName; + + @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 + + @Schema(description = "发起时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime startTime; + + @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime endTime; + + @Schema(description = "持续时间", example = "1000") + private Long durationInMillis; + + @Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED) + private Map formVariables; + + @Schema(description = "业务的唯一标识-例如说,请假申请的编号", example = "1") + private String businessKey; + + /** + * 发起流程的用户 + */ + private UserSimpleBaseVO startUser; + + @Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private String processDefinitionId; + /** + * 流程定义 + */ + private BpmProcessDefinitionRespVO processDefinition; + + /** + * 当前审批中的任务 + */ + private List tasks; // 仅在流程实例分页才返回 + + @Schema(description = "流程任务") + @Data + public static class Task { + + @Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + private String name; + + @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser + private Long assignee; + + @Schema(description = "任务分配人", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + private UserSimpleBaseVO assigneeUser; + + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java new file mode 100644 index 0000000..7d56754 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 通过流程任务的 Request VO") +@Data +public class BpmTaskApproveReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "审批意见", example = "不错不错!") + private String reason; + + @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") + private String signPicUrl; + + @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) + private Map variables; + + @Schema(description = "下一个节点审批人", example = "{nodeId:[1, 2]}") + private Map> nextAssignees; // 为什么是 Map,而不是 List 呢?因为下一个节点可能是多个,例如说并行网关的情况 + + // 新增任务变量实例,业务表单 + @Schema(description = "任务变量实例,业务表单", example = "{'formField1': 'value1', 'formField2': 'value2'}") + private Map taskVariables; +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java new file mode 100644 index 0000000..1775be0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.Collection; + +@Schema(description = "管理后台 - 抄送流程任务的 Request VO") +@Data +public class BpmTaskCopyReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "抄送的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]") + @NotEmpty(message = "抄送用户不能为空") + private Collection copyUserIds; + + @Schema(description = "抄送意见", example = "帮忙看看!") + private String reason; +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java new file mode 100644 index 0000000..21aa42a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java @@ -0,0 +1,24 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 委派流程任务的 Request VO") +@Data +public class BpmTaskDelegateReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "被委派人 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "被委派人 ID 不能为空") + private Long delegateUserId; + + @Schema(description = "委派原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "做不了决定,需要你先帮忙瞅瞅") + @NotEmpty(message = "委派原因不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java new file mode 100644 index 0000000..5c1ee94 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java @@ -0,0 +1,28 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.util.date.DateUtils; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 流程任务的的分页 Request VO") // 待办、已办,都使用该分页 +@Data +public class BpmTaskPageReqVO extends PageParam { + + @Schema(description = "流程任务名", example = "ZT") + private String name; + + @Schema(description = "流程分类", example = "1") + private String category; + + @Schema(description = "流程定义的标识", example = "2048") + private String processDefinitionKey; // 精准匹配 + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java new file mode 100644 index 0000000..8482f48 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java @@ -0,0 +1,18 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 不通过流程任务的 Request VO") +@Data +public class BpmTaskRejectReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java new file mode 100644 index 0000000..fb86f57 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -0,0 +1,130 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 流程任务 Response VO") +@Data +public class BpmTaskRespVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "任务名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + private String name; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime endTime; + + @Schema(description = "持续时间", example = "1000") + private Long durationInMillis; + + @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private Integer status; // 参见 BpmTaskStatusEnum 枚举 + + @Schema(description = "审批理由", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") + private String reason; + + @Schema(description = "任务负责人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser + private Long owner; + /** + * 负责人的用户信息 + */ + private UserSimpleBaseVO ownerUser; + + @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser + private Long assignee; + /** + * 审核的用户信息 + */ + private UserSimpleBaseVO assigneeUser; + + @Schema(description = "任务定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "Activity_one") + private String taskDefinitionKey; + + @Schema(description = "所属流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8888") + private String processInstanceId; + /** + * 所属流程实例 + */ + private ProcessInstance processInstance; + + @Schema(description = "父任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String parentTaskId; + @Schema(description = "子任务列表(由加签生成)", requiredMode = Schema.RequiredMode.REQUIRED, example = "childrenTask") + private List children; // 由加签生成,包含多层子任务 + + @Schema(description = "表单编号", example = "1024") + private Long formId; + @Schema(description = "表单路由", example = "1024") + private String formPath; + @Schema(description = "表单名字", example = "请假表单") + private String formName; + @Schema(description = "表单的配置,JSON 字符串") + private String formConf; + @Schema(description = "表单项的数组") + private List formFields; + @Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED) + private Map formVariables; + @Schema(description = "操作按钮设置值") + private Map buttonsSetting; + + @Schema(description = "是否需要签名", example = "false") + private Boolean signEnable; + + @Schema(description = "是否填写审批意见", example = "false") + private Boolean reasonRequire; + + @Schema(description = "节点类型", example = "10") + private Integer nodeType; // 参见 BpmSimpleModelNodeTypeEnum 枚举。 + + @Data + @Schema(description = "流程实例") + public static class ProcessInstance { + + @Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "流程实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + private String name; + + @Schema(description = "提交时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + + @Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + private String processDefinitionId; + + @Schema(description = "流程摘要", example = "[]") + private List> summary; // 只有流程表单,才有摘要! + + /** + * 发起人的用户信息 + */ + private UserSimpleBaseVO startUser; + + } + + @Data + @Schema(description = "操作按钮设置") + public static class OperationButtonSetting { + + @Schema(description = "显示名称", example = "审批") + private String displayName; + + @Schema(description = "是否启用", example = "true") + private Boolean enable; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java new file mode 100644 index 0000000..22982ab --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 退回流程任务的 Request VO") +@Data +public class BpmTaskReturnReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "退回到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotEmpty(message = "退回到的任务 Key 不能为空") + private String targetTaskDefinitionKey; + + @Schema(description = "退回意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回") + @NotEmpty(message = "退回意见不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java new file mode 100644 index 0000000..0acf4bd --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java @@ -0,0 +1,29 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.Set; + +@Schema(description = "管理后台 - 加签任务的创建(加签) Request VO") +@Data +public class BpmTaskSignCreateReqVO { + + @Schema(description = "需要加签的任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "加签的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888") + @NotEmpty(message = "加签用户不能为空") + private Set userIds; + + @Schema(description = "加签类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "before") + @NotEmpty(message = "加签类型不能为空") + private String type; // 参见 BpmTaskSignTypeEnum 枚举 + + @Schema(description = "加签原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "需要加签") + @NotEmpty(message = "加签原因不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java new file mode 100644 index 0000000..a848485 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java @@ -0,0 +1,19 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "管理后台 - 加签任务的删除(减签) Request VO") +@Data +public class BpmTaskSignDeleteReqVO { + + @Schema(description = "被减签的任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "加签原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "需要减签") + @NotEmpty(message = "加签原因不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java new file mode 100644 index 0000000..e55bb73 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java @@ -0,0 +1,24 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.task; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "管理后台 - 流程任务的转办 Request VO") +@Data +public class BpmTaskTransferReqVO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "新审批人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") + @NotNull(message = "新审批人的用户编号不能为空") + private Long assigneeUserId; + + @Schema(description = "转办原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "做不了决定,需要你先帮忙瞅瞅") + @NotEmpty(message = "转办原因不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java new file mode 100644 index 0000000..0513848 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package com.zt.plat.module.bpm.controller.app; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java new file mode 100644 index 0000000..b6413c1 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 RESTful API 给前端: + * 1. admin 包:提供给管理后台 cloud-ui-admin 前端项目 + * 2. app 包:提供给用户 APP cloud-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分 + */ +package com.zt.plat.module.bpm.controller; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java new file mode 100644 index 0000000..262a598 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java @@ -0,0 +1,132 @@ +package com.zt.plat.module.bpm.convert.definition; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.util.date.DateUtils; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 流程模型 Convert + * + * @author yunlongn + */ +@Mapper +public interface BpmModelConvert { + + BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class); + + default List buildModelList(List list, + Map formMap, + Map categoryMap, + Map deploymentMap, + Map processDefinitionMap, + Map userMap, + Map deptMap) { + List result = convertList(list, model -> { + BpmModelMetaInfoVO metaInfo = parseMetaInfo(model); + BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null; + BpmCategoryDO category = categoryMap.get(model.getCategory()); + Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null; + ProcessDefinition processDefinition = model.getDeploymentId() != null ? + processDefinitionMap.get(model.getDeploymentId()) : null; + List startUsers = metaInfo != null ? convertList(metaInfo.getStartUserIds(), userMap::get) : null; + List startDepts = metaInfo != null ? convertList(metaInfo.getStartDeptIds(), deptMap::get) : null; + return buildModel0(model, metaInfo, form, category, deployment, processDefinition, startUsers, startDepts); + }); + // 排序 + result.sort(Comparator.comparing(BpmModelMetaInfoVO::getSort)); + return result; + } + + default BpmModelRespVO buildModel(Model model, byte[] bpmnBytes, BpmSimpleModelNodeVO simpleModel) { + BpmModelMetaInfoVO metaInfo = parseMetaInfo(model); + BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null, null, null); + if (ArrayUtil.isNotEmpty(bpmnBytes)) { + modelVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnBytes)); + } + modelVO.setSimpleModel(simpleModel); + return modelVO; + } + + default BpmModelRespVO buildModel0(Model model, + BpmModelMetaInfoVO metaInfo, BpmFormDO form, BpmCategoryDO category, + Deployment deployment, ProcessDefinition processDefinition, + List startUsers, List startDepts) { + BpmModelRespVO modelRespVO = new BpmModelRespVO().setId(model.getId()).setName(model.getName()) + .setKey(model.getKey()).setCategory(model.getCategory()) + .setCreateTime(DateUtils.of(model.getCreateTime())); + // Form + BeanUtils.copyProperties(metaInfo, modelRespVO); + if (form != null) { + modelRespVO.setFormName(form.getName()); + } + // Category + if (category != null) { + modelRespVO.setCategoryName(category.getName()); + } + // ProcessDefinition + if (processDefinition != null) { + modelRespVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)); + modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ? + SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); + if (deployment != null) { + modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime())); + } + } + // User、Dept + modelRespVO.setStartUsers(BeanUtils.toBean(startUsers, UserSimpleBaseVO.class)) + .setStartDepts(BeanUtils.toBean(startDepts, DeptSimpleBaseVO.class)); + return modelRespVO; + } + + default void copyToModel(Model model, BpmModelSaveReqVO reqVO) { + model.setName(reqVO.getName()); + model.setKey(reqVO.getKey()); + model.setCategory(reqVO.getCategory()); + model.setMetaInfo(JsonUtils.toJsonString(BeanUtils.toBean(reqVO, BpmModelMetaInfoVO.class))); + } + + default BpmModelMetaInfoVO parseMetaInfo(Model model) { + BpmModelMetaInfoVO vo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoVO.class); + if (vo == null) { + return null; + } + if (vo.getManagerUserIds() == null) { + vo.setManagerUserIds(Collections.emptyList()); + } + if (vo.getStartUserIds() == null) { + vo.setStartUserIds(Collections.emptyList()); + } + // 如果为空,兜底处理,使用 createTime 创建时间 + if (vo.getSort() == null) { + vo.setSort(model.getCreateTime().getTime()); + } + return vo; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java new file mode 100644 index 0000000..86b256c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java @@ -0,0 +1,99 @@ +package com.zt.plat.module.bpm.convert.definition; + +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.map.MapUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * Bpm 流程定义的 Convert + * + * @author yunlong.li + */ +@Mapper +public interface BpmProcessDefinitionConvert { + + BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class); + + default PageResult buildProcessDefinitionPage(PageResult page, + Map deploymentMap, + Map processDefinitionInfoMap, + Map formMap, + Map categoryMap) { + List list = buildProcessDefinitionList(page.getList(), deploymentMap, processDefinitionInfoMap, formMap, categoryMap); + return new PageResult<>(list, page.getTotal()); + } + + default List buildProcessDefinitionList(List list, + Map deploymentMap, + Map processDefinitionInfoMap, + Map formMap, + Map categoryMap) { + List result = CollectionUtils.convertList(list, definition -> { + Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class); + BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class); + BpmFormDO form = null; + if (processDefinitionInfo != null) { + form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class); + } + BpmCategoryDO category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategoryDO.class); + return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null); + }); + // 排序 + result.sort(Comparator.comparing(BpmProcessDefinitionRespVO::getSort)); + return result; + } + + default BpmProcessDefinitionRespVO buildProcessDefinition(ProcessDefinition definition, + Deployment deployment, + BpmProcessDefinitionInfoDO processDefinitionInfo, + BpmFormDO form, + BpmCategoryDO category, + BpmnModel bpmnModel) { + BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class); + respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); + // Deployment + if (deployment != null) { + respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())); + } + // BpmProcessDefinitionInfoDO + if (processDefinitionInfo != null) { + copyTo(processDefinitionInfo, respVO); + // Form + if (form != null) { + respVO.setFormName(form.getName()); + } + } + // Category + if (category != null) { + respVO.setCategoryName(category.getName()); + } + // BpmnModel + if (bpmnModel != null) { + respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel)); + } + return respVO; + } + + @Mapping(source = "from.id", target = "to.id", ignore = true) + void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java new file mode 100644 index 0000000..41de133 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java @@ -0,0 +1,21 @@ +package com.zt.plat.module.bpm.convert.message; + +import com.zt.plat.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + +import java.util.Map; + +@Mapper +public interface BpmMessageConvert { + + BpmMessageConvert INSTANCE = Mappers.getMapper(BpmMessageConvert.class); + + @Mapping(target = "mobile", ignore = true) + @Mapping(source = "userId", target = "userId") + @Mapping(source = "templateCode", target = "templateCode") + @Mapping(source = "templateParams", target = "templateParams") + SmsSendSingleToUserReqDTO convert(Long userId, String templateCode, Map templateParams); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java new file mode 100644 index 0000000..b72f713 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java @@ -0,0 +1,6 @@ +/** + * 提供 POJO 类的实体转换 + * + * 目前使用 MapStruct 框架 + */ +package com.zt.plat.module.bpm.convert; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java new file mode 100644 index 0000000..791e694 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java @@ -0,0 +1,298 @@ +package com.zt.plat.module.bpm.convert.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.MapUtils; +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceBpmnModelViewRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import com.zt.plat.module.bpm.convert.definition.BpmProcessDefinitionConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * 流程实例 Convert + * + * @author ZT + */ +@Mapper +public interface BpmProcessInstanceConvert { + + BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class); + + default PageResult buildProcessInstancePage(PageResult pageResult, + Map processDefinitionMap, + Map categoryMap, + Map> taskMap, + Map userMap, + Map deptMap, + Map processDefinitionInfoMap) { + PageResult vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstanceRespVO.class); + for (int i = 0; i < pageResult.getList().size(); i++) { + BpmProcessInstanceRespVO respVO = vpPageResult.getList().get(i); + respVO.setStatus(FlowableUtils.getProcessInstanceStatus(pageResult.getList().get(i))); + MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(), + processDefinition -> respVO.setCategory(processDefinition.getCategory()) + .setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class))); + MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName())); + respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceRespVO.Task.class)); + // user + if (userMap != null) { + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(pageResult.getList().get(i).getStartUserId())); + if (startUser != null) { + respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); + MapUtils.findAndThen(deptMap, DeptUtil.getDeptId(startUser), dept -> respVO.getStartUser().setDeptName(dept.getName())); + } + if (CollUtil.isNotEmpty(respVO.getTasks())) { + respVO.getTasks().forEach(task -> { + AdminUserRespDTO assigneeUser = userMap.get(task.getAssignee()); + if (assigneeUser!= null) { + task.setAssigneeUser(BeanUtils.toBean(assigneeUser, UserSimpleBaseVO.class)); + MapUtils.findAndThen(deptMap, DeptUtil.getDeptId(assigneeUser), dept -> task.getAssigneeUser().setDeptName(dept.getName())); + } + }); + } + } + // 摘要 + respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(respVO.getProcessDefinitionId()), + pageResult.getList().get(i).getProcessVariables())); + // 表单 + respVO.setFormVariables(pageResult.getList().get(i).getProcessVariables()); + } + return vpPageResult; + } + + default BpmProcessInstanceRespVO buildProcessInstance(HistoricProcessInstance processInstance, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + AdminUserRespDTO startUser, + DeptRespDTO dept) { + BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class); + respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance)) + .setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance)); + // definition + respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)); + copyTo(processDefinitionInfo, respVO.getProcessDefinition()); + // user + if (startUser != null) { + respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); + if (dept != null) { + respVO.getStartUser().setDeptName(dept.getName()); + } + } + return respVO; + } + + @Mapping(source = "from.id", target = "to.id", ignore = true) + void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); + + default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) { + return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status) + .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey()); + } + + default BpmMessageSendWhenProcessInstanceApproveReqDTO buildProcessInstanceApproveMessage(ProcessInstance instance) { + return new BpmMessageSendWhenProcessInstanceApproveReqDTO() + .setStartUserId(NumberUtils.parseLong(instance.getStartUserId())) + .setProcessInstanceId(instance.getId()) + .setProcessInstanceName(instance.getName()); + } + + default BpmMessageSendWhenProcessInstanceRejectReqDTO buildProcessInstanceRejectMessage(ProcessInstance instance, String reason) { + return new BpmMessageSendWhenProcessInstanceRejectReqDTO() + .setProcessInstanceName(instance.getName()) + .setProcessInstanceId(instance.getId()) + .setReason(reason) + .setStartUserId(NumberUtils.parseLong(instance.getStartUserId())); + } + + default BpmProcessInstanceBpmnModelViewRespVO buildProcessInstanceBpmnModelView(HistoricProcessInstance processInstance, + List taskInstances, + BpmnModel bpmnModel, + BpmSimpleModelNodeVO simpleModel, + Set unfinishedTaskActivityIds, + Set finishedTaskActivityIds, + Set finishedSequenceFlowActivityIds, + Set rejectTaskActivityIds, + Map userMap, + Map deptMap) { + BpmProcessInstanceBpmnModelViewRespVO respVO = new BpmProcessInstanceBpmnModelViewRespVO(); + // 基本信息 + respVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class, o -> o + .setStatus(FlowableUtils.getProcessInstanceStatus(processInstance))) + .setStartUser(buildUser(processInstance.getStartUserId(), userMap, deptMap))); + respVO.setTasks(convertList(taskInstances, task -> BeanUtils.toBean(task, BpmTaskRespVO.class) + .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)) + .setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap)) + .setOwnerUser(buildUser(task.getOwner(), userMap, deptMap)))); + respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel)); + respVO.setSimpleModel(simpleModel); + // 进度信息 + respVO.setUnfinishedTaskActivityIds(unfinishedTaskActivityIds) + .setFinishedTaskActivityIds(finishedTaskActivityIds) + .setFinishedSequenceFlowActivityIds(finishedSequenceFlowActivityIds) + .setRejectedTaskActivityIds(rejectTaskActivityIds); + return respVO; + } + + default UserSimpleBaseVO buildUser(String userIdStr, + Map userMap, + Map deptMap) { + if (StrUtil.isEmpty(userIdStr)) { + return null; + } + Long userId = NumberUtils.parseLong(userIdStr); + return buildUser(userId, userMap, deptMap); + } + + default UserSimpleBaseVO buildUser(Long userId, + Map userMap, + Map deptMap) { + if (userId == null) { + return null; + } + AdminUserRespDTO user = userMap.get(userId); + if (user == null) { + return null; + } + UserSimpleBaseVO userVO = BeanUtils.toBean(user, UserSimpleBaseVO.class); + Long deptId = DeptUtil.getDeptId(user); + DeptRespDTO dept = deptId != null ? deptMap.get(deptId) : null; + if (dept != null) { + userVO.setDeptName(dept.getName()); + } + return userVO; + } + + default BpmApprovalDetailRespVO.ActivityNodeTask buildApprovalTaskInfo(HistoricTaskInstance task) { + if (task == null) { + return null; + } + return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class) + .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)) + .setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task)); + } + + default Set parseUserIds(HistoricProcessInstance processInstance, + List activityNodes, + BpmTaskRespVO todoTask) { + Set userIds = new HashSet<>(); + if (processInstance != null) { + userIds.add(NumberUtils.parseLong(processInstance.getStartUserId())); + } + for (BpmApprovalDetailRespVO.ActivityNode activityNode : activityNodes) { + CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getAssignee)); + CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getOwner)); + CollUtil.addAll(userIds, activityNode.getCandidateUserIds()); + } + if (todoTask != null) { + CollUtil.addIfAbsent(userIds, todoTask.getAssignee()); + CollUtil.addIfAbsent(userIds, todoTask.getOwner()); + if (CollUtil.isNotEmpty(todoTask.getChildren())) { + CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getAssignee)); + CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getOwner)); + } + } + return userIds; + } + + default Set parseUserIds02(HistoricProcessInstance processInstance, + List tasks) { + Set userIds = SetUtils.asSet(Long.valueOf(processInstance.getStartUserId())); + tasks.forEach(task -> { + CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getAssignee()))); + CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getOwner()))); + }); + return userIds; + } + + default BpmApprovalDetailRespVO buildApprovalDetail(BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List activityNodes, + BpmTaskRespVO todoTask, + Map formFieldsPermission, + Map userMap, + Map deptMap) { + // 1.1 流程实例 + BpmProcessInstanceRespVO processInstanceResp = null; + if (processInstance != null) { + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); + Long deptId = DeptUtil.getDeptId(startUser); + DeptRespDTO dept = deptMap.get(deptId); + processInstanceResp = buildProcessInstance(processInstance, null, null, startUser, dept); + } + + // 1.2 流程定义 + BpmProcessDefinitionRespVO definitionResp = BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition( + processDefinition, null, processDefinitionInfo, null, null, bpmnModel); + + // 1.3 流程节点 + activityNodes.forEach(approveNode -> { + if (approveNode.getTasks() != null) { + approveNode.getTasks().forEach(task -> { + task.setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap)); + task.setOwnerUser(buildUser(task.getOwner(), userMap, deptMap)); + }); + } + approveNode.setCandidateUsers(convertList(approveNode.getCandidateUserIds(), userId -> buildUser(userId, userMap, deptMap))); + }); + + // 1.4 待办任务 + if (todoTask != null) { + todoTask.setAssigneeUser(buildUser(todoTask.getAssignee(), userMap, deptMap)); + todoTask.setOwnerUser(buildUser(todoTask.getOwner(), userMap, deptMap)); + if (CollUtil.isNotEmpty(todoTask.getChildren())) { + todoTask.getChildren().forEach(childTask -> { + childTask.setAssigneeUser(buildUser(childTask.getAssignee(), userMap, deptMap)); + childTask.setOwnerUser(buildUser(childTask.getOwner(), userMap, deptMap)); + }); + } + } + + // 2. 拼接起来 + return new BpmApprovalDetailRespVO().setStatus(processInstanceStatus) + .setProcessDefinition(definitionResp) + .setProcessInstance(processInstanceResp) + .setFormFieldsPermission(formFieldsPermission) + .setTodoTask(todoTask) + .setActivityNodes(activityNodes); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java new file mode 100644 index 0000000..5926d84 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java @@ -0,0 +1,233 @@ +package com.zt.plat.module.bpm.convert.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.framework.common.util.date.DateUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; +import static com.zt.plat.framework.common.util.collection.MapUtils.findAndThen; + +/** + * Bpm 任务 Convert + * + * @author ZT + */ +@Mapper +public interface BpmTaskConvert { + + BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class); + + default PageResult buildTodoTaskPage(PageResult pageResult, + Map processInstanceMap, + Map userMap, + Map processDefinitionInfoMap) { + return BeanUtils.toBean(pageResult, BpmTaskRespVO.class, taskVO -> { + ProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId()); + if (processInstance == null) { + return; + } + taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class)); + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); + taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); + taskVO.getProcessInstance().setCreateTime(DateUtils.of(processInstance.getStartTime())); + // 摘要 + taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()), + processInstance.getProcessVariables())); + }); + } + + default PageResult buildTaskPage(PageResult pageResult, + Map processInstanceMap, + Map userMap, + Map deptMap, + Map processDefinitionInfoMap) { + List taskVOList = CollectionUtils.convertList(pageResult.getList(), task -> { + BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); + taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)); + // 用户信息 + AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee())); + if (assignUser != null) { + taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class)); + findAndThen(deptMap, DeptUtil.getDeptId(assignUser), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName())); + } + // 流程实例 + HistoricProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId()); + if (processInstance != null) { + AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); + taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class)); + taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); + // 摘要 + taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()), + processInstance.getProcessVariables())); + } + return taskVO; + }); + return new PageResult<>(taskVOList, pageResult.getTotal()); + } + + default List buildTaskListByProcessInstanceId(List taskList, + Map formMap, + Map userMap, + Map deptMap) { + return CollectionUtils.convertList(taskList, task -> { + // 特殊:已取消的任务,不返回 + BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); + Integer taskStatus = FlowableUtils.getTaskStatus(task); + if (BpmTaskStatusEnum.isCancelStatus(taskStatus)) { + return null; + } + taskVO.setStatus(taskStatus).setReason(FlowableUtils.getTaskReason(task)); + // 表单信息 + BpmFormDO form = null; + try { + Long formId = NumberUtils.parseLong(task.getFormKey()); + form = MapUtil.get(formMap, formId, BpmFormDO.class); + } catch (NumberFormatException e) { + // 如果 formKey 不是数字(比如是URL),设置 formPath + taskVO.setFormPath(task.getFormKey()); + taskVO.setFormVariables(FlowableUtils.getTaskFormVariable(task)); + } + if (form != null) { + taskVO.setFormId(form.getId()).setFormName(form.getName()).setFormConf(form.getConf()) + .setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task)); + } + // 用户信息 + buildTaskAssignee(taskVO, task.getAssignee(), userMap, deptMap); + buildTaskOwner(taskVO, task.getOwner(), userMap, deptMap); + return taskVO; + }); + } + + default List buildTaskListByParentTaskId(List taskList, + Map userMap, + Map deptMap) { + return convertList(taskList, task -> BeanUtils.toBean(task, BpmTaskRespVO.class, taskVO -> { + AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee())); + if (assignUser != null) { + taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class)); + DeptRespDTO dept = deptMap.get(DeptUtil.getDeptId(assignUser)); + if (dept != null) { + taskVO.getAssigneeUser().setDeptName(dept.getName()); + } + } + AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner())); + if (ownerUser != null) { + taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class)); + findAndThen(deptMap, DeptUtil.getDeptId(ownerUser), dept -> taskVO.getOwnerUser().setDeptName(dept.getName())); + } + })); + } + + default BpmTaskRespVO buildTodoTask(Task todoTask, List childrenTasks, + Map buttonsSetting, + BpmFormDO form) { + BpmTaskRespVO bpmTaskRespVO = BeanUtils.toBean(todoTask, BpmTaskRespVO.class) + .setStatus(FlowableUtils.getTaskStatus(todoTask)).setReason(FlowableUtils.getTaskReason(todoTask)) + .setButtonsSetting(buttonsSetting) + .setChildren(convertList(childrenTasks, childTask -> BeanUtils.toBean(childTask, BpmTaskRespVO.class) + .setStatus(FlowableUtils.getTaskStatus(childTask)))); + if (form != null) { + bpmTaskRespVO.setFormId(form.getId()).setFormName(form.getName()) + .setFormConf(form.getConf()).setFormFields(form.getFields()); + }else{ + // 任务级别的业务表单 + bpmTaskRespVO.setFormPath(todoTask.getFormKey()); + } + return bpmTaskRespVO; + } + + default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser, + Task task) { + BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO(); + reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId()) + .setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId()) + .setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName()) + .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())); + return reqDTO; + } + + default void buildTaskOwner(BpmTaskRespVO task, String taskOwner, + Map userMap, + Map deptMap) { + AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(taskOwner)); + if (ownerUser != null) { + task.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class)); + findAndThen(deptMap, DeptUtil.getDeptId(ownerUser), dept -> task.getOwnerUser().setDeptName(dept.getName())); + } + } + + default void buildTaskChildren(BpmTaskRespVO task, Map> childrenTaskMap, + Map userMap, Map deptMap) { + List childTasks = childrenTaskMap.get(task.getId()); + if (CollUtil.isNotEmpty(childTasks)) { + task.setChildren( + convertList(childTasks, childTask -> { + BpmTaskRespVO childTaskVO = BeanUtils.toBean(childTask, BpmTaskRespVO.class); + childTaskVO.setStatus(FlowableUtils.getTaskStatus(childTask)); + buildTaskOwner(childTaskVO, childTask.getOwner(), userMap, deptMap); + buildTaskAssignee(childTaskVO, childTask.getAssignee(), userMap, deptMap); + return childTaskVO; + }) + ); + } + } + + default void buildTaskAssignee(BpmTaskRespVO task, String taskAssignee, + Map userMap, + Map deptMap) { + AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(taskAssignee)); + if (assignUser != null) { + task.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class)); + findAndThen(deptMap, DeptUtil.getDeptId(assignUser), dept -> task.getAssigneeUser().setDeptName(dept.getName())); + } + } + + /** + * 将父任务的属性,拷贝到子任务(加签任务) + *

+ * 为什么不使用 mapstruct 映射?因为 TaskEntityImpl 还有很多其他属性,这里我们只设置我们需要的。 + * 使用 mapstruct 会将里面嵌套的各个属性值都设置进去,会出现意想不到的问题。 + * + * @param parentTask 父任务 + * @param childTask 加签任务 + */ + default void copyTo(TaskEntityImpl parentTask, TaskEntityImpl childTask) { + childTask.setName(parentTask.getName()); + childTask.setDescription(parentTask.getDescription()); + childTask.setCategory(parentTask.getCategory()); + childTask.setParentTaskId(parentTask.getId()); + childTask.setProcessDefinitionId(parentTask.getProcessDefinitionId()); + childTask.setProcessInstanceId(parentTask.getProcessInstanceId()); + childTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey()); + childTask.setTaskDefinitionId(parentTask.getTaskDefinitionId()); + childTask.setPriority(parentTask.getPriority()); + childTask.setCreateTime(new Date()); + childTask.setTenantId(parentTask.getTenantId()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md new file mode 100644 index 0000000..6565cb0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md @@ -0,0 +1 @@ + diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java new file mode 100644 index 0000000..193ffb4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java @@ -0,0 +1,54 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BPM 流程分类 DO + * + * @author ZT + */ +@TableName("bpm_category") +@KeySequence("bpm_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmCategoryDO extends BaseDO { + + /** + * 分类编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 分类名 + */ + private String name; + /** + * 分类标志 + */ + private String code; + /** + * 分类描述 + */ + private String description; + /** + * 分类状态 + * + * 枚举 {@link com.zt.plat.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 分类排序 + */ + private Integer sort; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java new file mode 100644 index 0000000..649d191 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java @@ -0,0 +1,57 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.List; + +/** + * BPM 工作流的表单定义 + * 用于工作流的申请表单,需要动态配置的场景 + * + * @author ZT + */ +@TableName(value = "bpm_form", autoResultMap = true) +@KeySequence("bpm_form_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmFormDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 表单名 + */ + private String name; + /** + * 状态 + */ + private Integer status; + /** + * 表单的配置 + */ + private String conf; + /** + * 表单项的数组 + * + * 目前直接将 https://github.com/JakHuang/form-generator 生成的 JSON 串,直接保存 + * 定义:https://github.com/JakHuang/form-generator/issues/46 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List fields; + /** + * 备注 + */ + private String remark; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java new file mode 100644 index 0000000..0ebef4b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -0,0 +1,219 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import com.zt.plat.framework.mybatis.core.type.LongListTypeHandler; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.enums.definition.BpmAutoApproveTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; + +import java.util.List; + +/** + * BPM 流程定义的拓信息 + * 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表 + * + * @author ZT + */ +@TableName(value = "bpm_process_definition_info", autoResultMap = true) +@KeySequence("bpm_process_definition_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmProcessDefinitionInfoDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 流程定义的编号 + * + * 关联 {@link ProcessDefinition#getId()} 属性 + */ + private String processDefinitionId; + /** + * 流程模型的编号 + * + * 关联 {@link Model#getId()} 属性 + */ + private String modelId; + /** + * 流程模型的类型 + * + * 枚举 {@link BpmModelTypeEnum} + */ + private Integer modelType; + + /** + * 流程分类的编码 + * + * 关联 {@link BpmCategoryDO#getCode()} + * + * 为什么要存储?原因是,{@link ProcessDefinition#getCategory()} 无法设置 + */ + private String category; + /** + * 图标 + */ + private String icon; + /** + * 描述 + */ + private String description; + + /** + * 表单类型 + * + * 枚举 {@link BpmModelFormTypeEnum} + */ + private Integer formType; + /** + * 动态表单编号 + * + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + * + * 关联 {@link BpmFormDO#getId()} + */ + private Long formId; + /** + * 表单的配置 + * + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + * + * 冗余 {@link BpmFormDO#getConf()} + */ + private String formConf; + /** + * 表单项的数组 + * + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + * + * 冗余 {@link BpmFormDO#getFields()} + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List formFields; + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + * + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomCreatePath; + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + * + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomViewPath; + + /** + * SIMPLE 设计器模型数据 json 格式 + * + * 目的:当使用仿钉钉设计器时。流程模型发布的时候,需要保存流程模型设计器的快照数据。 + */ + private String simpleModel; + /** + * 是否可见 + * + * 目的:如果 false 不可见,则不展示在“发起流程”的列表里 + */ + private Boolean visible; + /** + * 排序值 + */ + private Long sort; + + /** + * 可发起用户编号数组 + * + * 关联 {@link AdminUserRespDTO#getId()} 字段的数组 + * + * 如果为空,则表示“全部可以发起”! + * + * 它和 {@link #visible} 的区别在于: + * 1. {@link #visible} 只是决定是否可见。即使不可见,还是可以发起 + * 2. startUserIds 决定某个用户是否可以发起。如果该用户不可发起,则他也是不可见的 + */ + @TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤 + private List startUserIds; + + /** + * 可发起部门编号数组 + * + * 关联 {@link AdminUserRespDTO#getDeptId()} 字段的数组 + */ + @TableField(typeHandler = LongListTypeHandler.class) + private List startDeptIds; + + /** + * 可管理用户编号数组 + * + * 关联 {@link AdminUserRespDTO#getId()} 字段的数组 + */ + @TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤 + private List managerUserIds; + + /** + * 是否允许撤销审批中的申请 + */ + private Boolean allowCancelRunningProcess; + + /** + * 流程 ID 规则 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.ProcessIdRule processIdRule; + + /** + * 自动去重类型 + * + * 枚举 {@link BpmAutoApproveTypeEnum} + */ + private Integer autoApprovalType; + + /** + * 标题设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.TitleSetting titleSetting; + /** + * 摘要设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.SummarySetting summarySetting; + + /** + * 流程前置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting processBeforeTriggerSetting; + /** + * 流程后置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting processAfterTriggerSetting; + + /** + * 任务前置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting taskBeforeTriggerSetting; + + /** + * 任务后置通知设置 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private BpmModelMetaInfoVO.HttpRequestSetting taskAfterTriggerSetting; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java new file mode 100644 index 0000000..29afb73 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.*; + +/** + * BPM 流程表达式 DO + * + * @author ZT + */ +@TableName("bpm_process_expression") +@KeySequence("bpm_process_expression_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmProcessExpressionDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 表达式名字 + */ + private String name; + /** + * 表达式状态 + * + * 枚举 {@link TODO common_status 对应的类} + */ + private Integer status; + /** + * 表达式 + */ + private String expression; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java new file mode 100644 index 0000000..ed36d7b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java @@ -0,0 +1,74 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import com.zt.plat.module.bpm.enums.definition.BpmProcessListenerTypeEnum; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * BPM 流程监听器 DO + * + * 目的:本质上它是流程监听器的模版,用于 BPMN 在设计时,直接选择这些模版 + * + * @author ZT + */ +@TableName(value = "bpm_process_listener") +@KeySequence("bpm_process_listener_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmProcessListenerDO extends BaseDO { + + /** + * 主键 ID,自增 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 监听器名字 + */ + private String name; + /** + * 状态 + * + * 枚举 {@link com.zt.plat.framework.common.enums.CommonStatusEnum} + */ + private Integer status; + /** + * 监听类型 + * + * 枚举 {@link BpmProcessListenerTypeEnum} + * + * 1. execution:ExecutionListener 执行监听器 + * 2. task:TaskListener 任务监听器 + */ + private String type; + /** + * 监听事件 + * + * execution 时:start、end + * task 时:create 创建、assignment 指派、complete 完成、delete 删除、update 更新、timeout 超时 + */ + private String event; + + /** + * 值类型 + * + * 1. class:Java 类,ExecutionListener 需要 {@link org.flowable.engine.delegate.JavaDelegate},TaskListener 需要 {@link org.flowable.engine.delegate.TaskListener} + * 2. delegateExpression:委托表达式,在 class 的基础上,需要注册到 Spring 容器里,后续表达式通过 Spring Bean 名称即可 + * 3. expression:表达式,一个普通类的普通方法,将这个普通类注册到 Spring 容器中,然后表达式中还可以执行这个类中的方法 + */ + private String valueType; + /** + * 值 + */ + private String value; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java new file mode 100644 index 0000000..bbe42d2 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java @@ -0,0 +1,52 @@ +package com.zt.plat.module.bpm.dal.dataobject.definition; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.*; +import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Set; + +/** + * BPM 用户组 + * + * @author ZT + */ +@TableName(value = "bpm_user_group", autoResultMap = true) +@KeySequence("bpm_user_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmUserGroupDO extends BaseDO { + + /** + * 编号,自增 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 组名 + */ + private String name; + /** + * 描述 + */ + private String description; + /** + * 状态 + * + * 枚举 {@link CommonStatusEnum} + */ + private Integer status; + /** + * 成员用户编号数组 + */ + @TableField(typeHandler = JacksonTypeHandler.class) + private Set userIds; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java new file mode 100644 index 0000000..76dabd2 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java @@ -0,0 +1,78 @@ +package com.zt.plat.module.bpm.dal.dataobject.oa; + +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +/** + * OA 请假申请 DO + * + * {@link #day} 请假天数,目前先简单做。一般是分成请假上午和下午,可以是 1 整天,可以是 0.5 半天 + * + * @author jason + * @author ZT + */ +@TableName("bpm_oa_leave") +@KeySequence("bpm_oa_leave_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmOALeaveDO extends BaseDO { + + /** + * 请假表单主键 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + /** + * 申请人的用户编号 + * + * 关联 AdminUserDO 的 id 属性 + */ + private Long userId; + /** + * 请假类型 + */ + private String type; + /** + * 原因 + */ + private String reason; + /** + * 开始时间 + */ + private LocalDateTime startTime; + /** + * 结束时间 + */ + private LocalDateTime endTime; + /** + * 请假天数 + */ + private Long day; + /** + * 审批结果 + * + * 枚举 {@link BpmTaskStatusEnum} + * 考虑到简单,所以直接复用了 BpmProcessInstanceStatusEnum 枚举,也可以自己定义一个枚举哈 + */ + private Integer status; + + /** + * 对应的流程编号 + * + * 关联 ProcessInstance 的 id 属性 + */ + private String processInstanceId; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java new file mode 100644 index 0000000..ef06a3e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java @@ -0,0 +1,98 @@ +package com.zt.plat.module.bpm.dal.dataobject.task; + +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.KeySequence; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.flowable.bpmn.model.FlowNode; +import org.flowable.task.api.history.HistoricTaskInstance; + +/** + * 流程抄送 DO + * + * @author kyle + * @since 2024-01-22 + */ +@TableName(value = "bpm_process_instance_copy", autoResultMap = true) +@KeySequence("bpm_process_instance_copy_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class BpmProcessInstanceCopyDO extends BaseDO { + + /** + * 编号 + */ + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** + * 发起人 Id + * + * 冗余 ProcessInstance 的 startUserId 字段 + */ + private Long startUserId; + /** + * 流程名 + * + * 冗余 ProcessInstance 的 name 字段 + */ + private String processInstanceName; + /** + * 流程实例的编号 + * + * 关联 ProcessInstance 的 id 属性 + */ + private String processInstanceId; + /** + * 流程实例的流程定义编号 + * + * 关联 ProcessInstance 的 processDefinitionId 属性 + */ + private String processDefinitionId; + /** + * 流程分类 + * + * 冗余 ProcessInstance 的 category 字段 + */ + private String category; + /** + * 流程活动的编号 + *

+ * + * 冗余 {@link FlowNode#getId()},对应 BPMN XML 节点编号 + * 原因:用于查询抄送节点的表单字段权限。因为仿钉钉/飞书的抄送节点 (ServiceTask),没有 taskId,只有 activityId + */ + private String activityId; + /** + * 流程活动的名字 + * + * 冗余 {@link FlowNode#getName()} + */ + private String activityName; + /** + * 流程活动的编号 + * + * 关联 {@link HistoricTaskInstance#getId()} + */ + private String taskId; + + /** + * 用户编号(被抄送的用户编号) + * + * 关联 system_users 的 id 属性 + */ + private Long userId; + + /** + * 抄送意见 + */ + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java new file mode 100644 index 0000000..5c36ae4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.dal.mysql.category; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +/** + * BPM 流程分类 Mapper + * + * @author ZT + */ +@Mapper +public interface BpmCategoryMapper extends BaseMapperX { + + default PageResult selectPage(BpmCategoryPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(BpmCategoryDO::getName, reqVO.getName()) + .likeIfPresent(BpmCategoryDO::getCode, reqVO.getCode()) + .eqIfPresent(BpmCategoryDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(BpmCategoryDO::getCreateTime, reqVO.getCreateTime()) + .orderByAsc(BpmCategoryDO::getSort)); + } + + default BpmCategoryDO selectByName(String name) { + return selectOne(BpmCategoryDO::getName, name); + } + + default BpmCategoryDO selectByCode(String code) { + return selectOne(BpmCategoryDO::getCode, code); + } + + default List selectListByCode(Collection codes) { + return selectList(BpmCategoryDO::getCode, codes); + } + + default List selectListByStatus(Integer status) { + return selectList(BpmCategoryDO::getStatus, status); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java new file mode 100644 index 0000000..db73b65 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java @@ -0,0 +1,25 @@ +package com.zt.plat.module.bpm.dal.mysql.definition; + + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.QueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 动态表单 Mapper + * + * @author 风里雾里 + */ +@Mapper +public interface BpmFormMapper extends BaseMapperX { + + default PageResult selectPage(BpmFormPageReqVO reqVO) { + return selectPage(reqVO, new QueryWrapperX() + .likeIfPresent("name", reqVO.getName()) + .orderByDesc("id")); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java new file mode 100644 index 0000000..5d5be48 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.bpm.dal.mysql.definition; + +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Collection; +import java.util.List; + +@Mapper +public interface BpmProcessDefinitionInfoMapper extends BaseMapperX { + + default List selectListByProcessDefinitionIds(Collection processDefinitionIds) { + return selectList(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionIds); + } + + default BpmProcessDefinitionInfoDO selectByProcessDefinitionId(String processDefinitionId) { + return selectOne(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionId); + } + + default void updateByModelId(String modelId, BpmProcessDefinitionInfoDO updateObj) { + update(updateObj, + new LambdaQueryWrapperX().eq(BpmProcessDefinitionInfoDO::getModelId, modelId)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java new file mode 100644 index 0000000..4551fd3 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java @@ -0,0 +1,26 @@ +package com.zt.plat.module.bpm.dal.mysql.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * BPM 流程表达式 Mapper + * + * @author ZT + */ +@Mapper +public interface BpmProcessExpressionMapper extends BaseMapperX { + + default PageResult selectPage(BpmProcessExpressionPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(BpmProcessExpressionDO::getName, reqVO.getName()) + .eqIfPresent(BpmProcessExpressionDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(BpmProcessExpressionDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmProcessExpressionDO::getId)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java new file mode 100644 index 0000000..6f6c89e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java @@ -0,0 +1,27 @@ +package com.zt.plat.module.bpm.dal.mysql.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * BPM 流程监听器 Mapper + * + * @author ZT + */ +@Mapper +public interface BpmProcessListenerMapper extends BaseMapperX { + + default PageResult selectPage(BpmProcessListenerPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(BpmProcessListenerDO::getName, reqVO.getName()) + .eqIfPresent(BpmProcessListenerDO::getType, reqVO.getType()) + .eqIfPresent(BpmProcessListenerDO::getEvent, reqVO.getEvent()) + .eqIfPresent(BpmProcessListenerDO::getStatus, reqVO.getStatus()) + .orderByDesc(BpmProcessListenerDO::getId)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java new file mode 100644 index 0000000..4c52531 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.dal.mysql.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + * 用户组 Mapper + * + * @author ZT + */ +@Mapper +public interface BpmUserGroupMapper extends BaseMapperX { + + default PageResult selectPage(BpmUserGroupPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(BpmUserGroupDO::getName, reqVO.getName()) + .eqIfPresent(BpmUserGroupDO::getStatus, reqVO.getStatus()) + .betweenIfPresent(BpmUserGroupDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmUserGroupDO::getId)); + } + + default List selectListByStatus(Integer status) { + return selectList(BpmUserGroupDO::getStatus, status); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java new file mode 100644 index 0000000..7e0f439 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java @@ -0,0 +1,29 @@ +package com.zt.plat.module.bpm.dal.mysql.oa; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * 请假申请 Mapper + * + * @author jason + * @author ZT + */ +@Mapper +public interface BpmOALeaveMapper extends BaseMapperX { + + default PageResult selectPage(Long userId, BpmOALeavePageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(BpmOALeaveDO::getUserId, userId) + .eqIfPresent(BpmOALeaveDO::getStatus, reqVO.getStatus()) + .eqIfPresent(BpmOALeaveDO::getType, reqVO.getType()) + .likeIfPresent(BpmOALeaveDO::getReason, reqVO.getReason()) + .betweenIfPresent(BpmOALeaveDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmOALeaveDO::getId)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java new file mode 100644 index 0000000..61a73c3 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java @@ -0,0 +1,25 @@ +package com.zt.plat.module.bpm.dal.mysql.task; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; +import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface BpmProcessInstanceCopyMapper extends BaseMapperX { + + default PageResult selectPage(Long loginUserId, BpmProcessInstanceCopyPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .eqIfPresent(BpmProcessInstanceCopyDO::getUserId, loginUserId) + .likeIfPresent(BpmProcessInstanceCopyDO::getProcessInstanceName, reqVO.getProcessInstanceName()) + .betweenIfPresent(BpmProcessInstanceCopyDO::getCreateTime, reqVO.getCreateTime()) + .orderByDesc(BpmProcessInstanceCopyDO::getId)); + } + + default void deleteByProcessInstanceId(String processInstanceId) { + delete(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java new file mode 100644 index 0000000..b6b0f69 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java @@ -0,0 +1,61 @@ +package com.zt.plat.module.bpm.dal.redis; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import jakarta.annotation.Resource; +import org.springframework.data.redis.core.StringRedisTemplate; +import org.springframework.stereotype.Repository; + +import java.time.Duration; +import java.time.LocalDateTime; + +import static cn.hutool.core.date.DatePattern.*; + +/** + * BPM 流程 Id 编码的 Redis DAO + * + * @author Lesan + */ +@Repository +public class BpmProcessIdRedisDAO { + + @Resource + private StringRedisTemplate stringRedisTemplate; + + /** + * 生成序号,使用定义的 processIdRule 规则生成 + * + * @param processIdRule 规则 + * @return 序号 + */ + public String generate(BpmModelMetaInfoVO.ProcessIdRule processIdRule) { + // 生成日期前缀 + String infix = ""; + switch (processIdRule.getInfix()) { + case "DAY": + infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN); + break; + case "HOUR": + infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + "HH"); + break; + case "MINUTE": + infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + "HHmm"); + break; + case "SECOND": + infix = DateUtil.format(LocalDateTime.now(), PURE_DATETIME_PATTERN); + break; + } + + // 生成序号 + String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix(); + String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix; + Long no = stringRedisTemplate.opsForValue().increment(key); + if (StrUtil.isNotEmpty(infix)) { + // 特殊:没有前缀,则不能过期,不能每次都是从 0 开始 + stringRedisTemplate.expire(key, Duration.ofDays(1L)); + } + return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java new file mode 100644 index 0000000..9146bc9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java @@ -0,0 +1,15 @@ +package com.zt.plat.module.bpm.dal.redis; + +/** + * BPM Redis Key 枚举类 + * + * @author ZT + */ +public interface RedisKeyConstants { + + /** + * 流程 ID 的缓存 + */ + String BPM_PROCESS_ID = "bpm:process_id:"; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java new file mode 100644 index 0000000..f0d5b49 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java @@ -0,0 +1,95 @@ +package com.zt.plat.module.bpm.framework.flowable.config; + +import cn.hutool.core.collection.ListUtil; +import com.zt.plat.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; +import com.zt.plat.module.system.api.user.AdminUserApi; +import org.flowable.common.engine.api.delegate.FlowableFunctionDelegate; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncListenableTaskExecutor; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.util.List; + +/** + * BPM 模块的 Flowable 配置类 + * + * @author jason + */ +@Configuration(proxyBeanMethods = false) +public class BpmFlowableConfiguration { + + /** + * 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean + * + * 如果不创建,会导致项目启动时,Flowable 报错的问题 + */ + @Bean(name = "applicationTaskExecutor") + @ConditionalOnMissingBean(name = "applicationTaskExecutor") + public AsyncListenableTaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(8); + executor.setMaxPoolSize(8); + executor.setQueueCapacity(100); + executor.setThreadNamePrefix("flowable-task-Executor-"); + executor.setAwaitTerminationSeconds(30); + executor.setWaitForTasksToCompleteOnShutdown(true); + executor.setAllowCoreThreadTimeOut(true); + executor.initialize(); + return executor; + } + + /** + * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类: + * + * 1. 设置各种监听器 + * 2. 设置自定义的 ActivityBehaviorFactory 实现 + */ + @Bean + public EngineConfigurationConfigurer bpmProcessEngineConfigurationConfigurer( + ObjectProvider listeners, + ObjectProvider customFlowableFunctionDelegates, + BpmActivityBehaviorFactory bpmActivityBehaviorFactory) { + return configuration -> { + // 注册监听器,例如说 BpmActivityEventListener + configuration.setEventListeners(ListUtil.toList(listeners.iterator())); + // 设置 ActivityBehaviorFactory 实现类,用于流程任务的审核人的自定义 + configuration.setActivityBehaviorFactory(bpmActivityBehaviorFactory); + // 设置自定义的函数 + configuration.setCustomFlowableFunctionDelegates(ListUtil.toList(customFlowableFunctionDelegates.stream().iterator())); + }; + } + + // =========== 审批人相关的 Bean ========== + + @Bean + public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskCandidateInvoker bpmTaskCandidateInvoker) { + BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory(); + bpmActivityBehaviorFactory.setTaskCandidateInvoker(bpmTaskCandidateInvoker); + return bpmActivityBehaviorFactory; + } + + @Bean + @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") // adminUserApi 可以注入成功 + public BpmTaskCandidateInvoker bpmTaskCandidateInvoker(List strategyList, + AdminUserApi adminUserApi) { + return new BpmTaskCandidateInvoker(strategyList, adminUserApi); + } + + // =========== 自己拓展的 Bean ========== + + @Bean + public BpmProcessInstanceEventPublisher processInstanceEventPublisher(ApplicationEventPublisher publisher) { + return new BpmProcessInstanceEventPublisher(publisher); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java new file mode 100644 index 0000000..ec9cd8f --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java @@ -0,0 +1,44 @@ +package com.zt.plat.module.bpm.framework.flowable.core.behavior; + +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import lombok.Setter; +import org.flowable.bpmn.model.Activity; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; +import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory; + +/** + * 自定义的 ActivityBehaviorFactory 实现类,目的如下: + * 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配 + * + * @author ZT + */ +@Setter +public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory { + + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Override + public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) { + return new BpmUserTaskActivityBehavior(userTask) + .setTaskCandidateInvoker(taskCandidateInvoker); + } + + @Override + public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity, + AbstractBpmnActivityBehavior behavior) { + return new BpmParallelMultiInstanceBehavior(activity, behavior) + .setTaskCandidateInvoker(taskCandidateInvoker); + } + + @Override + public SequentialMultiInstanceBehavior createSequentialMultiInstanceBehavior(Activity activity, + AbstractBpmnActivityBehavior behavior) { + return new BpmSequentialMultiInstanceBehavior(activity, behavior) + .setTaskCandidateInvoker(taskCandidateInvoker); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java new file mode 100644 index 0000000..2df2ce7 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java @@ -0,0 +1,91 @@ +package com.zt.plat.module.bpm.framework.flowable.core.behavior; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import lombok.Setter; +import org.flowable.bpmn.model.Activity; +import org.flowable.bpmn.model.CallActivity; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; + +import java.util.List; +import java.util.Set; + +/** + * 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配 + * 第一步,基于分配规则,计算出分配任务的【多个】候选人们。 + * 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它 + * + * @author kemengkai + * @since 2022-04-21 16:57 + */ +@Setter +public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior { + + private BpmTaskCandidateInvoker taskCandidateInvoker; + + public BpmParallelMultiInstanceBehavior(Activity activity, + AbstractBpmnActivityBehavior innerActivityBehavior) { + super(activity, innerActivityBehavior); + } + + /** + * 重写该方法,主要实现两个功能: + * 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式,而是采用自己定义的 + * 2. 获得任务的处理人,并设置到 collectionVariable 中,用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人 + * + * 注意,多个任务实例,每个任务实例对应一个处理人,所以返回的数量就是任务处理人的数量 + * + * @param execution 执行任务 + * @return 数量 + */ + @Override + protected int resolveNrOfInstances(DelegateExecution execution) { + // 情况一:UserTask 节点 + if (execution.getCurrentFlowElement() instanceof UserTask) { + // 第一步,设置 collectionVariable 和 CollectionVariable + // 从 execution.getVariable() 读取所有任务处理人的 key + super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); + // 从 execution.getVariable() 读取当前所有任务处理的人的 key + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); + + // 第二步,获取任务的所有处理人 + @SuppressWarnings("unchecked") + Set assigneeUserIds = (Set) execution.getVariable(super.collectionVariable, Set.class); + if (assigneeUserIds == null) { + assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution); + if (CollUtil.isEmpty(assigneeUserIds)) { + // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过! + // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务 + // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时 + assigneeUserIds = SetUtils.asSet((Long) null); + } + execution.setVariableLocal(super.collectionVariable, assigneeUserIds); + } + return assigneeUserIds.size(); + } + + // 情况二:CallActivity 节点 + if (execution.getCurrentFlowElement() instanceof CallActivity) { + FlowElement flowElement = execution.getCurrentFlowElement(); + Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); + } + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); + } + } + + return super.resolveNrOfInstances(execution); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java new file mode 100644 index 0000000..44b4398 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java @@ -0,0 +1,95 @@ +package com.zt.plat.module.bpm.framework.flowable.core.behavior; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import lombok.Setter; +import org.flowable.bpmn.model.Activity; +import org.flowable.bpmn.model.CallActivity; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.engine.impl.persistence.entity.ExecutionEntity; + +import java.util.List; +import java.util.Set; + +/** + * 自定义的【串行】的【多个】流程任务的 assignee 负责人的分配 + * + * 本质上,实现和 {@link BpmParallelMultiInstanceBehavior} 一样,只是继承的类不一样 + * + * @author ZT + */ +@Setter +public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceBehavior { + + private BpmTaskCandidateInvoker taskCandidateInvoker; + + public BpmSequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) { + super(activity, innerActivityBehavior); + } + + /** + * 逻辑和 {@link BpmParallelMultiInstanceBehavior#resolveNrOfInstances(DelegateExecution)} 类似 + * + * 差异的点:是在【第二步】的时候,需要返回 LinkedHashSet 集合!因为它需要有序! + */ + @Override + protected int resolveNrOfInstances(DelegateExecution execution) { + // 情况一:UserTask 节点 + if (execution.getCurrentFlowElement() instanceof UserTask) { + // 第一步,设置 collectionVariable 和 CollectionVariable + // 从 execution.getVariable() 读取所有任务处理人的 key + super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); + // 从 execution.getVariable() 读取当前所有任务处理的人的 key + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); + + // 第二步,获取任务的所有处理人 + // 不使用 execution.getVariable 原因:目前依次审批任务回退后 collectionVariable 变量没有清理, 如果重新进入该任务不会重新分配审批人 + @SuppressWarnings("unchecked") + Set assigneeUserIds = (Set) execution.getVariableLocal(super.collectionVariable, Set.class); + if (assigneeUserIds == null) { + assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution); + if (CollUtil.isEmpty(assigneeUserIds)) { + // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过! + // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务 + // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时 + assigneeUserIds = SetUtils.asSet((Long) null); + } + execution.setVariableLocal(super.collectionVariable, assigneeUserIds); + } + return assigneeUserIds.size(); + } + + // 情况二:CallActivity 节点 + if (execution.getCurrentFlowElement() instanceof CallActivity) { + FlowElement flowElement = execution.getCurrentFlowElement(); + Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); + } + if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { + return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); + } + } + + return super.resolveNrOfInstances(execution); + } + + @Override + protected void executeOriginalBehavior(DelegateExecution execution, ExecutionEntity multiInstanceRootExecution, int loopCounter) { + // 参见 https://gitee.com/zhijiantianya/cloud-cloud/issues/IC239F + super.collectionExpression = null; + super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); + super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); + super.executeOriginalBehavior(execution, multiInstanceRootExecution, loopCounter); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java new file mode 100644 index 0000000..8d29c54 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java @@ -0,0 +1,86 @@ +package com.zt.plat.module.bpm.framework.flowable.core.behavior; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.RandomUtil; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.UserTask; +import org.flowable.common.engine.impl.el.ExpressionManager; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.impl.util.TaskHelper; +import org.flowable.engine.interceptor.CreateUserTaskBeforeContext; +import org.flowable.task.service.TaskService; +import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.Set; + +/** + * 自定义的【单个】流程任务的 assignee 负责人的分配 + * 第一步,基于分配规则,计算出分配任务的【单个】候选人。如果找不到,则直接报业务异常,不继续执行后续的流程; + * 第二步,随机选择一个候选人,则选择作为 assignee 负责人。 + * + * @author ZT + */ +@Slf4j +public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { + + @Setter + private BpmTaskCandidateInvoker taskCandidateInvoker; + + public BpmUserTaskActivityBehavior(UserTask userTask) { + super(userTask); + } + + @Override + @Transactional(rollbackFor = Exception.class) + protected void handleAssignments(TaskService taskService, String assignee, String owner, + List candidateUsers, List candidateGroups, TaskEntity task, ExpressionManager expressionManager, + DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { + // 第一步,获得任务的候选用户 + Long assigneeUserId = calculateTaskCandidateUsers(execution); + // 第二步,设置作为负责人 + if (assigneeUserId != null) { + TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); + } + } + + private Long calculateTaskCandidateUsers(DelegateExecution execution) { + // 情况一,如果是多实例的任务,例如说会签、或签等情况,则从 Variable 中获取。 + // 顺序审批可见 BpmSequentialMultiInstanceBehavior,并发审批可见 BpmSequentialMultiInstanceBehavior + if (super.multiInstanceActivityBehavior != null) { + return execution.getVariable(super.multiInstanceActivityBehavior.getCollectionElementVariable(), Long.class); + } + + // 情况二,如果非多实例的任务,则计算任务处理人 + // 第一步,先计算可处理该任务的处理人们 + Set candidateUserIds = taskCandidateInvoker.calculateUsersByTask(execution); + if (CollUtil.isEmpty(candidateUserIds)) { + return null; + } + // 第二步,后随机选择一个任务的处理人 + // 疑问:为什么一定要选择一个任务处理人? + // 解答:项目对 bpm 的任务是责任到人,所以每个任务有且仅有一个处理人。 + // 如果希望一个任务可以同时被多个人处理,可以考虑使用 BpmParallelMultiInstanceBehavior 实现的会签 or 或签。 + int index = RandomUtil.randomInt(candidateUserIds.size()); + return CollUtil.get(candidateUserIds, index); + } + + @Override + protected void handleCategory(CreateUserTaskBeforeContext beforeContext, ExpressionManager expressionManager, + TaskEntity task, DelegateExecution execution) { + ProcessDefinitionEntity processDefinitionEntity = CommandContextUtil.getProcessDefinitionEntityManager().findById(execution.getProcessDefinitionId()); + if (processDefinitionEntity == null) { + log.warn("[handleCategory][任务编号({}) 找不到流程定义({})]", task.getId(), execution.getProcessDefinitionId()); + return; + } + task.setCategory(processDefinitionEntity.getCategory()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java new file mode 100644 index 0000000..54310ef --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java @@ -0,0 +1,207 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.util.object.ObjectUtils; +import com.zt.plat.framework.datapermission.core.annotation.DataPermission; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import com.google.common.annotations.VisibleForTesting; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.CallActivity; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; + +import java.util.*; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG; + +/** + * {@link BpmTaskCandidateStrategy} 的调用者,用于调用对应的策略,实现任务的候选人的计算 + * + * @author ZT + */ +@Slf4j +public class BpmTaskCandidateInvoker { + + private final Map strategyMap = new HashMap<>(); + + private final AdminUserApi adminUserApi; + + public BpmTaskCandidateInvoker(List strategyList, + AdminUserApi adminUserApi) { + strategyList.forEach(strategy -> { + BpmTaskCandidateStrategy oldStrategy = strategyMap.put(strategy.getStrategy(), strategy); + Assert.isNull(oldStrategy, "策略(%s) 重复", strategy.getStrategy()); + }); + this.adminUserApi = adminUserApi; + } + + /** + * 校验流程模型的任务分配规则全部都配置了 + * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去! + * + * @param bpmnBytes BPMN XML + */ + public void validateBpmnConfig(byte[] bpmnBytes) { + BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); + assert bpmnModel != null; + List userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); + // 遍历所有的 UserTask,校验审批人配置 + userTaskList.forEach(userTask -> { + // 1.1 非人工审批,无需校验审批人配置 + Integer approveType = BpmnModelUtils.parseApproveType(userTask); + if (ObjectUtils.equalsAny(approveType, + BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), + BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { + return; + } + // 1.2 非空校验 + Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask); + String param = BpmnModelUtils.parseCandidateParam(userTask); + if (strategy == null) { + throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName()); + } + BpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy); + if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) { + throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName()); + } + // 2. 具体策略校验 + getCandidateStrategy(strategy).validateParam(param); + }); + } + + /** + * 计算任务的候选人 + * + * @param execution 执行任务 + * @return 用户编号集合 + */ + @DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人 + public Set calculateUsersByTask(DelegateExecution execution) { + // 注意:解决极端情况下,Flowable 异步调用,导致租户 id 丢失的情况 + // 例如说,SIMPLE 延迟器在 trigger 的时候!!! + return FlowableUtils.execute(execution.getTenantId(), () -> { + // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 + FlowElement flowElement = execution.getCurrentFlowElement(); + Integer approveType = BpmnModelUtils.parseApproveType(flowElement); + if (ObjectUtils.equalsAny(approveType, + BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), + BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { + return new HashSet<>(); + } + + // 1.1 计算任务的候选人 + Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); + String param = BpmnModelUtils.parseCandidateParam(flowElement); + Set userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param); + // 1.2 移除被禁用的用户 + removeDisableUsers(userIds); + + // 2. 候选人为空时,根据“审批人为空”的配置补充 + if (CollUtil.isEmpty(userIds)) { + userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy()) + .calculateUsersByTask(execution, param); + // ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!! + } + + // 3. 移除发起人的用户 + ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class) + .getProcessInstance(execution.getProcessInstanceId()); + Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId()); + removeStartUserIfSkip(userIds, flowElement, Long.valueOf(processInstance.getStartUserId())); + return userIds; + }); + } + + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, + Long startUserId, String processDefinitionId, Map processVariables) { + // 如果是 CallActivity 子流程,不进行计算候选人 + FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); + if (flowElement instanceof CallActivity) { + return new HashSet<>(); + } + // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 + Integer approveType = BpmnModelUtils.parseApproveType(flowElement); + if (ObjectUtils.equalsAny(approveType, + BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), + BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { + return new HashSet<>(); + } + + // 1.1 计算任务的候选人 + Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); + String param = BpmnModelUtils.parseCandidateParam(flowElement); + Set userIds = getCandidateStrategy(strategy).calculateUsersByActivity(bpmnModel, activityId, param, + startUserId, processDefinitionId, processVariables); + // 1.2 移除被禁用的用户 + removeDisableUsers(userIds); + + // 2. 候选人为空时,根据“审批人为空”的配置补充 + if (CollUtil.isEmpty(userIds)) { + userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy()) + .calculateUsersByActivity(bpmnModel, activityId, param, startUserId, processDefinitionId, processVariables); + // ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!! + } + + // 3. 移除发起人的用户 + removeStartUserIfSkip(userIds, flowElement, startUserId); + return userIds; + } + + @VisibleForTesting + void removeDisableUsers(Set assigneeUserIds) { + if (CollUtil.isEmpty(assigneeUserIds)) { + return; + } + Map userMap = adminUserApi.getUserMap(assigneeUserIds); + assigneeUserIds.removeIf(id -> { + AdminUserRespDTO user = userMap.get(id); + return user == null || CommonStatusEnum.isDisable(user.getStatus()); + }); + } + + /** + * 如果“审批人与发起人相同时”,配置了 SKIP 跳过,则移除发起人 + * + * 注意:如果只有一个候选人,则不处理,避免无法审批 + * + * @param assigneeUserIds 当前分配的候选人 + * @param flowElement 当前节点 + * @param startUserId 发起人 + */ + @VisibleForTesting + void removeStartUserIfSkip(Set assigneeUserIds, FlowElement flowElement, Long startUserId) { + if (CollUtil.size(assigneeUserIds) <= 1) { + return; + } + Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(flowElement); + if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { + return; + } + assigneeUserIds.remove(startUserId); + } + + private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) { + BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy); + Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy); + BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum); + Assert.notNull(strategyObj, "策略(%s) 不存在", strategy); + return strategyObj; + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java new file mode 100644 index 0000000..2d916be --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java @@ -0,0 +1,85 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate; + +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; + +import java.util.Map; +import java.util.Set; + +/** + * BPM 任务的候选人的策略接口 + *

+ * 例如说:分配审批人 + * + * @author ZT + */ +public interface BpmTaskCandidateStrategy { + + /** + * 对应策略 + * + * @return 策略 + */ + BpmTaskCandidateStrategyEnum getStrategy(); + + /** + * 校验参数 + * + * @param param 参数 + */ + void validateParam(String param); + + /** + * 是否一定要输入参数 + * + * @return 是否 + */ + default boolean isParamRequired() { + return true; + } + + /** + * 基于候选人参数,获得任务的候选用户们 + * + * 注意:实现 calculateUsers 系列方法时,有两种选择: + * 1. 只重写 calculateUsers 默认方法 + * 2. 都重写 calculateUsersByTask 和 calculateUsersByActivity 两个方法 + * + * @param param 执行任务 + * @return 用户编号集合 + */ + default Set calculateUsers(String param) { + throw new UnsupportedOperationException("该分配方法未实现,请检查!"); + } + + /** + * 基于【执行任务】,获得任务的候选用户们 + * + * @param execution 执行任务 + * @return 用户编号集合 + */ + default Set calculateUsersByTask(DelegateExecution execution, String param) { + return calculateUsers(param); + } + + /** + * 基于【流程活动】,获得任务的候选用户们 + *

+ * 目的:用于获取未执行节点的候选用户们 + * + * @param bpmnModel 流程图 + * @param activityId 活动 ID (对应 Bpmn XML id) + * @param param 节点的参数 + * @param startUserId 流程发起人编号 + * @param processDefinitionId 流程定义编号 + * @param processVariables 流程变量 + * @return 用户编号集合 + */ + @SuppressWarnings("unused") + default Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + return calculateUsers(param); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java new file mode 100644 index 0000000..fd34457 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java @@ -0,0 +1,79 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.expression; + +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Component; +import org.springframework.util.Assert; + +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static java.util.Collections.emptySet; + +/** + * 分配给发起人的 Leader 审批的 Expression 流程表达式 + * 目前 Leader 的定义是,发起人所在部门的 Leader + * + * @author ZT + */ +@Component +public class BpmTaskAssignLeaderExpression { + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @Resource + private BpmProcessInstanceService processInstanceService; + + /** + * 计算审批的候选人 + * + * @param execution 流程执行实体 + * @param level 指定级别 + * @return 指定级别的领导 + */ + public Set calculateUsers(DelegateExecution execution, int level) { + Assert.isTrue(level > 0, "level 必须大于 0"); + // 获得发起人 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + // 获得对应 leve 的部门 + DeptRespDTO dept = null; + for (int i = 0; i < level; i++) { + // 获得 level 对应的部门 + if (dept == null) { + dept = getStartUserDept(startUserId); + if (dept == null) { // 找不到发起人的部门,所以无法使用该规则 + return emptySet(); + } + } else { + DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()).getCheckedData(); + if (parentDept == null) { // 找不到父级部门,所以只好结束寻找。原因是:例如说,级别比较高的人,所在部门层级比较少 + break; + } + dept = parentDept; + } + } + return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet(); + } + + private DeptRespDTO getStartUserDept(Long startUserId) { + AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData(); + Long deptId = DeptUtil.getDeptId(startUser); + if (deptId == 0L) { // 找不到部门,所以无法使用该规则 + return null; + } + return deptApi.getDept(deptId).getCheckedData(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java new file mode 100644 index 0000000..bee41e7 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.expression; + +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Component; + +import java.util.Set; + +/** + * 分配给发起人审批的 Expression 流程表达式 + * + * @author ZT + */ +@Component +public class BpmTaskAssignStartUserExpression { + + @Resource + private BpmProcessInstanceService processInstanceService; + + /** + * 计算审批的候选人 + * + * @param execution 流程执行实体 + * @return 发起人 + */ + public Set calculateUsers(ExecutionEntityImpl execution) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + return SetUtils.asSet(startUserId); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java new file mode 100644 index 0000000..9c0a760 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java @@ -0,0 +1,95 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +/** + * 部门的负责人 {@link BpmTaskCandidateStrategy} 抽象类 + * + * @author jason + */ +public abstract class AbstractBpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy { + + @Resource + protected DeptApi deptApi; + @Resource + protected AdminUserApi adminUserApi; + + /** + * 获得指定层级的部门负责人,只有第 level 的负责人 + * + * @param dept 指定部门 + * @param level 第几级 + * @return 部门负责人的编号 + */ + protected Long getAssignLevelDeptLeaderId(DeptRespDTO dept, Integer level) { + Assert.isTrue(level > 0, "level 必须大于 0"); + if (dept == null) { + return null; + } + DeptRespDTO currentDept = dept; + for (int i = 1; i < level; i++) { + DeptRespDTO parentDept = deptApi.getDept(currentDept.getParentId()).getCheckedData(); + if (parentDept == null) { // 找不到父级部门,到了最高级。返回最高级的部门负责人 + break; + } + currentDept = parentDept; + } + return currentDept.getLeaderUserId(); + } + + /** + * 获得连续层级的部门负责人,包含 [1, level] 的负责人 + * + * @param deptIds 指定部门编号数组 + * @param level 最大层级 + * @return 连续部门负责人 Id + */ + protected Set getMultiLevelDeptLeaderIds(List deptIds, Integer level) { + Assert.isTrue(level > 0, "level 必须大于 0"); + if (CollUtil.isEmpty(deptIds)) { + return new HashSet<>(); + } + Set deptLeaderIds = new LinkedHashSet<>(); // 保证有序 + for (Long deptId : deptIds) { + DeptRespDTO dept = deptApi.getDept(deptId).getCheckedData(); + for (int i = 0; i < level; i++) { + if (dept.getLeaderUserId() != null) { + deptLeaderIds.add(dept.getLeaderUserId()); + } + DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()).getCheckedData(); + if (parentDept == null) { // 找不到父级部门. 已经到了最高层级了 + break; + } + dept = parentDept; + } + } + return deptLeaderIds; + } + + /** + * 获取发起人的部门 + * + * @param startUserId 发起人 Id + */ + protected DeptRespDTO getStartUserDept(Long startUserId) { + AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData(); + if (CollUtil.isEmpty(startUser.getDeptIds())) { // 找不到部门 + return null; + } + return deptApi.getDept(DeptUtil.getDeptId(startUser)).getCheckedData(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java new file mode 100644 index 0000000..bfd429c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java @@ -0,0 +1,78 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.google.common.collect.Sets; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +/** + * 审批人自选 {@link BpmTaskCandidateUserStrategy} 实现类 + * 审批人在审批时选择下一个节点的审批人 + * + * @author smallNorthLee + */ +@Component +public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT; + } + + @Override + public void validateParam(String param) {} + + @Override + public boolean isParamRequired() { + return false; + } + + @Override + public LinkedHashSet calculateUsersByTask(DelegateExecution execution, String param) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId()); + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance); + Assert.notNull(approveUserSelectAssignees, "流程实例({}) 的下一个执行节点审批人不能为空", + execution.getProcessInstanceId()); + if (approveUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } + // 获得审批人 + List assignees = approveUserSelectAssignees.get(execution.getCurrentActivityId()); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + + @Override + public LinkedHashSet calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + if (processVariables == null) { + return Sets.newLinkedHashSet(); + } + // 流程预测时会使用,允许审批人为空,如果为空前端会弹出提示选择下一个节点审批人,避免流程无法进行,审批时会真正校验节点是否配置审批人 + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); + if (approveUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } + // 获得审批人 + List assignees = approveUserSelectAssignees.get(activityId); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java new file mode 100644 index 0000000..f851e66 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +/** + * 连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.MULTI_DEPT_LEADER_MULTI; + } + + @Override + public void validateParam(String param) { + // 参数格式: | 分隔:1)左边为部门(多个部门用 , 分隔)。2)右边为部门层级 + String[] params = param.split("\\|"); + Assert.isTrue(params.length == 2, "参数格式不匹配"); + List deptIds = StrUtils.splitToLong(params[0], ","); + int level = Integer.parseInt(params[1]); + // 校验部门存在 + deptApi.validateDeptList(deptIds).checkError(); + Assert.isTrue(level > 0, "部门层级必须大于 0"); + } + + @Override + public Set calculateUsers(String param) { + String[] params = param.split("\\|"); + List deptIds = StrUtils.splitToLong(params[0], ","); + int level = Integer.parseInt(params[1]); + return super.getMultiLevelDeptLeaderIds(deptIds, level); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java new file mode 100644 index 0000000..ad1c93b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * 部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy { + + @Resource + private DeptApi deptApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.DEPT_LEADER; + } + + @Override + public void validateParam(String param) { + Set deptIds = StrUtils.splitToLongSet(param); + deptApi.validateDeptList(deptIds).checkError(); + } + + @Override + public Set calculateUsers(String param) { + Set deptIds = StrUtils.splitToLongSet(param); + List depts = deptApi.getDeptList(deptIds).getCheckedData(); + return convertSet(depts, DeptRespDTO::getLeaderUserId); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java new file mode 100644 index 0000000..aa2c3f0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java @@ -0,0 +1,48 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * 部门的成员 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateDeptMemberStrategy implements BpmTaskCandidateStrategy { + + @Resource + private DeptApi deptApi; + @Resource + private AdminUserApi adminUserApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.DEPT_MEMBER; + } + + @Override + public void validateParam(String param) { + Set deptIds = StrUtils.splitToLongSet(param); + deptApi.validateDeptList(deptIds).checkError(); + } + + @Override + public Set calculateUsers(String param) { + Set deptIds = StrUtils.splitToLongSet(param); + List users = adminUserApi.getUserListByDeptIds(deptIds).getCheckedData(); + return convertSet(users, AdminUserRespDTO::getId); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java new file mode 100644 index 0000000..04e4963 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java @@ -0,0 +1,70 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static cn.hutool.core.collection.ListUtil.toList; + +/** + * 发起人连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Resource + @Lazy + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER_MULTI; + } + + @Override + public void validateParam(String param) { + int level = Integer.parseInt(param); // 参数是部门的层级 + Assert.isTrue(level > 0, "部门的层级必须大于 0"); + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + int level = Integer.parseInt(param); // 参数是部门的层级 + // 获得流程发起人 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + // 获取发起人的 multi 部门负责人 + DeptRespDTO dept = super.getStartUserDept(startUserId); + if (dept == null) { + return new HashSet<>(); + } + return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + int level = Integer.parseInt(param); // 参数是部门的层级 + DeptRespDTO dept = super.getStartUserDept(startUserId); + if (dept == null) { + return new HashSet<>(); + } + return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java new file mode 100644 index 0000000..9670fa9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java @@ -0,0 +1,71 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; + +/** + * 发起人的部门负责人, 可以是上级部门负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateStartUserDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Resource + @Lazy // 避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER; + } + + @Override + public void validateParam(String param) { + // 参数是部门的层级 + Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0"); + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + // 获得流程发起人 + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); + // 获取发起人的部门负责人 + return getStartUserDeptLeader(startUserId, param); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + // 获取发起人的部门负责人 + return getStartUserDeptLeader(startUserId, param); + } + + private Set getStartUserDeptLeader(Long startUserId, String param) { + int level = Integer.parseInt(param); // 参数是部门的层级 + DeptRespDTO dept = super.getStartUserDept(startUserId); + if (dept == null) { + return new HashSet<>(); + } + Long deptLeaderId = super.getAssignLevelDeptLeaderId(dept, level); + return deptLeaderId != null ? asSet(deptLeaderId) : new HashSet<>(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java new file mode 100644 index 0000000..189e5db --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.google.common.collect.Sets; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + +/** + * 发起人自选 {@link BpmTaskCandidateUserStrategy} 实现类 + * + * @author ZT + */ +@Component +public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.START_USER_SELECT; + } + + @Override + public void validateParam(String param) {} + + @Override + public boolean isParamRequired() { + return false; + } + + @Override + public LinkedHashSet calculateUsersByTask(DelegateExecution execution, String param) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId()); + Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance); + Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", + execution.getProcessInstanceId()); + // 获得审批人 + List assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + + @Override + public LinkedHashSet calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + if (processVariables == null) { + return Sets.newLinkedHashSet(); + } + Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processVariables); + if (startUserSelectAssignees == null) { + return Sets.newLinkedHashSet(); + } + // 获得审批人 + List assignees = startUserSelectAssignees.get(activityId); + return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java new file mode 100644 index 0000000..119b59d --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java @@ -0,0 +1,56 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.form; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept.AbstractBpmTaskCandidateDeptLeaderStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.Set; + +/** + * 表单内部门负责人 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateFormDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.FORM_DEPT_LEADER; + } + + @Override + public void validateParam(String param) { + // 参数格式: | 分隔:1)左边为表单内部门字段。2)右边为部门层级 + String[] params = param.split("\\|"); + Assert.isTrue(params.length == 2, "参数格式不匹配"); + Assert.notEmpty(param, "表单内部门字段不能为空"); + int level = Integer.parseInt(params[1]); + Assert.isTrue(level > 0, "部门层级必须大于 0"); + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + String[] params = param.split("\\|"); + Object result = execution.getVariable(params[0]); + int level = Integer.parseInt(params[1]); + return super.getMultiLevelDeptLeaderIds(Convert.toList(Long.class, result), level); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, + String param, Long startUserId, String processDefinitionId, + Map processVariables) { + String[] params = param.split("\\|"); + Object result = processVariables == null ? null : processVariables.get(params[0]); + int level = Integer.parseInt(params[1]); + return super.getMultiLevelDeptLeaderIds(Convert.toList(Long.class, result), level); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java new file mode 100644 index 0000000..435717b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java @@ -0,0 +1,47 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.form; + +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.Set; + +/** + * 表单内用户字段 {@link BpmTaskCandidateUserStrategy} 实现类 + * + * @author jason + */ +@Component +public class BpmTaskCandidateFormUserStrategy implements BpmTaskCandidateStrategy { + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.FORM_USER; + } + + @Override + public void validateParam(String param) { + Assert.notEmpty(param, "表单内用户字段不能为空"); + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + Object result = execution.getVariable(param); + return CollectionUtils.toLinkedHashSet(Long.class, result); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, + String param, Long startUserId, String processDefinitionId, + Map processVariables) { + Object result = processVariables == null ? null : processVariables.get(param); + return CollectionUtils.toLinkedHashSet(Long.class, result); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java new file mode 100644 index 0000000..8c360bc --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; + +import cn.hutool.core.lang.Assert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * 审批人为空 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateAssignEmptyStrategy implements BpmTaskCandidateStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessDefinitionService processDefinitionService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY; + } + + @Override + public void validateParam(String param) { + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + return getCandidateUsers(execution.getProcessDefinitionId(), execution.getCurrentFlowElement()); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); + return getCandidateUsers(processDefinitionId, flowElement); + } + + private Set getCandidateUsers(String processDefinitionId, FlowElement flowElement) { + // 情况一:指定人员审批 + Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(flowElement); + if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType())) { + return new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(flowElement)); + } + + // 情况二:流程管理员 + if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType())) { + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processDefinitionId); + Assert.notNull(processDefinition, "流程定义({})不存在", processDefinitionId); + return new HashSet<>(processDefinition.getManagerUserIds()); + } + + // 都不满足,还是返回空 + return new HashSet<>(); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java new file mode 100644 index 0000000..8350a8a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java @@ -0,0 +1,58 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; + +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.google.common.collect.Sets; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * 流程表达式 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author ZT + */ +@Component +@Slf4j +public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrategy { + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.EXPRESSION; + } + + @Override + public void validateParam(String param) { + // do nothing 因为它基本做不了校验 + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + Object result = FlowableUtils.getExpressionValue(execution, param); + return CollectionUtils.toLinkedHashSet(Long.class, result); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + Map variables = processVariables == null ? new HashMap<>() : processVariables; + try { + Object result = FlowableUtils.getExpressionValue(variables, param); + return CollectionUtils.toLinkedHashSet(Long.class, result); + } catch (FlowableException ex) { + // 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常, + log.warn("[calculateUsersByActivity][表达式({}) 变量({}) 解析报错", param, variables, ex); + // 不能预测候选人,返回空列表, 避免流程无法进行 + return Sets.newHashSet(); + } + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java new file mode 100644 index 0000000..5bc1232 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.service.definition.BpmUserGroupService; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +/** + * 用户组 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateGroupStrategy implements BpmTaskCandidateStrategy { + + @Resource + private BpmUserGroupService userGroupService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.USER_GROUP; + } + + @Override + public void validateParam(String param) { + Set groupIds = StrUtils.splitToLongSet(param); + userGroupService.validUserGroups(groupIds); + } + + @Override + public Set calculateUsers(String param) { + Set groupIds = StrUtils.splitToLongSet(param); + List groups = userGroupService.getUserGroupList(groupIds); + return convertSetByFlatMap(groups, BpmUserGroupDO::getUserIds, Collection::stream); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java new file mode 100644 index 0000000..74edf8a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java @@ -0,0 +1,48 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.system.api.dept.PostApi; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; + +/** + * 岗位 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidatePostStrategy implements BpmTaskCandidateStrategy { + + @Resource + private PostApi postApi; + @Resource + private AdminUserApi adminUserApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.POST; + } + + @Override + public void validateParam(String param) { + Set postIds = StrUtils.splitToLongSet(param); + postApi.validPostList(postIds); + } + + @Override + public Set calculateUsers(String param) { + Set postIds = StrUtils.splitToLongSet(param); + List users = adminUserApi.getUserListByPostIds(postIds).getCheckedData(); + return convertSet(users, AdminUserRespDTO::getId); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java new file mode 100644 index 0000000..f8223d9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java @@ -0,0 +1,43 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.system.api.permission.PermissionApi; +import com.zt.plat.module.system.api.permission.RoleApi; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.Set; + +/** + * 角色 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy { + + @Resource + private RoleApi roleApi; + @Resource + private PermissionApi permissionApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.ROLE; + } + + @Override + public void validateParam(String param) { + Set roleIds = StrUtils.splitToLongSet(param); + roleApi.validRoleList(roleIds); + } + + @Override + public Set calculateUsers(String param) { + Set roleIds = StrUtils.splitToLongSet(param); + return permissionApi.getUserRoleIdListByRoleIds(roleIds).getCheckedData(); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java new file mode 100644 index 0000000..ed527fa --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java @@ -0,0 +1,57 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.Set; + +/** + * 发起人自己 {@link BpmTaskCandidateUserStrategy} 实现类 + *

+ * 适合场景:用于需要发起人信息复核等场景 + * + * @author jason + */ +@Component +public class BpmTaskCandidateStartUserStrategy implements BpmTaskCandidateStrategy { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.START_USER; + } + + @Override + public void validateParam(String param) { + } + + @Override + public boolean isParamRequired() { + return false; + } + + @Override + public Set calculateUsersByTask(DelegateExecution execution, String param) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); + return SetUtils.asSet(Long.valueOf(processInstance.getStartUserId())); + } + + @Override + public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, + Long startUserId, String processDefinitionId, Map processVariables) { + return SetUtils.asSet(startUserId); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java new file mode 100644 index 0000000..f6a64ee --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java @@ -0,0 +1,39 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import cn.hutool.core.text.StrPool; +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.system.api.user.AdminUserApi; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.LinkedHashSet; + +/** + * 用户 {@link BpmTaskCandidateStrategy} 实现类 + * + * @author kyle + */ +@Component +public class BpmTaskCandidateUserStrategy implements BpmTaskCandidateStrategy { + + @Resource + private AdminUserApi adminUserApi; + + @Override + public BpmTaskCandidateStrategyEnum getStrategy() { + return BpmTaskCandidateStrategyEnum.USER; + } + + @Override + public void validateParam(String param) { + adminUserApi.validateUserList(StrUtils.splitToLongSet(param)).checkError(); + } + + @Override + public LinkedHashSet calculateUsers(String param) { + return new LinkedHashSet<>(StrUtils.splitToLong(param, StrPool.COMMA)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java new file mode 100644 index 0000000..c5db881 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.framework.flowable.core.el; + +import org.flowable.common.engine.api.variable.VariableContainer; +import org.flowable.common.engine.impl.el.function.AbstractFlowableVariableExpressionFunction; +import org.springframework.stereotype.Component; + +/** + * 根据流程变量 variable 的类型,转换参数的值 + * + * 目前用于 ConditionNodeConvert 的 buildConditionExpression 方法中 + * + * @author jason + */ +@Component +public class VariableConvertByTypeExpressionFunction extends AbstractFlowableVariableExpressionFunction { + + public VariableConvertByTypeExpressionFunction() { + super("convertByType"); + } + + public static Object convertByType(VariableContainer variableContainer, String variableName, Object parmaValue) { + Object variable = variableContainer.getVariable(variableName); + if (variable != null && parmaValue != null) { + // 如果值不是字符串类型,流程变量的类型是字符串,把值转成字符串 + if (!(parmaValue instanceof String) && variable instanceof String ) { + return parmaValue.toString(); + } + } + return parmaValue; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java new file mode 100644 index 0000000..9a8b399 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java @@ -0,0 +1,59 @@ +package com.zt.plat.module.bpm.framework.flowable.core.enums; + +import cn.hutool.core.util.ArrayUtil; +import com.zt.plat.framework.common.core.ArrayValuable; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.Arrays; + +/** + * BPM 任务的候选人策略枚举 + * + * 例如说:分配给指定人审批 + * + * @author ZT + */ +@Getter +@AllArgsConstructor +public enum BpmTaskCandidateStrategyEnum implements ArrayValuable { + + ROLE(10, "角色"), + DEPT_MEMBER(20, "部门的成员"), // 包括负责人 + DEPT_LEADER(21, "部门的负责人"), + MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), + POST(22, "岗位"), + USER(30, "用户"), + APPROVE_USER_SELECT(34, "审批人自身"), // 当前审批人,可在审批时,选择下一个节点的审批人 + START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时,选择此节点的审批人 + START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 + START_USER_DEPT_LEADER(37, "发起人部门负责人"), + START_USER_DEPT_LEADER_MULTI(38, "发起人连续多级部门的负责人"), + USER_GROUP(40, "用户组"), + FORM_USER(50, "表单内用户字段"), + FORM_DEPT_LEADER(51, "表单内部门负责人"), + EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager + ASSIGN_EMPTY(1, "审批人为空"), + ; + + public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTaskCandidateStrategyEnum::getStrategy).toArray(Integer[]::new); + + /** + * 类型 + */ + private final Integer strategy; + /** + * 描述 + */ + private final String description; + + public static BpmTaskCandidateStrategyEnum valueOf(Integer strategy) { + return ArrayUtil.firstMatch(o -> o.getStrategy().equals(strategy), values()); + } + + @Override + public Integer[] array() { + return ARRAYS; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java new file mode 100644 index 0000000..de18134 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java @@ -0,0 +1,146 @@ +package com.zt.plat.module.bpm.framework.flowable.core.enums; + +import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; + +/** + * BPMN XML 常量信息 + * + * @author ZT + */ +public interface BpmnModelConstants { + + String BPMN_FILE_SUFFIX = ".bpmn"; + + /** + * BPMN 中的命名空间 + */ + String NAMESPACE = "http://flowable.org/bpmn"; + + /** + * BPMN UserTask 的扩展属性,用于标记候选人策略 + */ + String USER_TASK_CANDIDATE_STRATEGY = "candidateStrategy"; + /** + * BPMN UserTask 的扩展属性,用于标记候选人参数 + */ + String USER_TASK_CANDIDATE_PARAM = "candidateParam"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记边界事件类型 + */ + String BOUNDARY_EVENT_TYPE = "boundaryEventType"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务超时执行动作 + */ + String USER_TASK_TIMEOUT_HANDLER_TYPE = "timeoutHandlerType"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务的审批人与发起人相同时,对应的处理类型 + */ + String USER_TASK_ASSIGN_START_USER_HANDLER_TYPE = "assignStartUserHandlerType"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务的空处理类型 + */ + String USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE = "assignEmptyHandlerType"; + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务的空处理的指定用户编号数组 + */ + String USER_TASK_ASSIGN_USER_IDS = "assignEmptyUserIds"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝处理类型 + */ + String USER_TASK_REJECT_HANDLER_TYPE = "rejectHandlerType"; + /** + * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝后的退回的任务 Id + */ + String USER_TASK_REJECT_RETURN_TASK_ID = "rejectReturnTaskId"; + + /** + * BPMN UserTask 的扩展属性,用于标记用户任务的审批类型 + */ + String USER_TASK_APPROVE_TYPE = "approveType"; + + /** + * BPMN UserTask 的扩展属性,用于标记用户任务的审批方式 + */ + String USER_TASK_APPROVE_METHOD = "approveMethod"; + + /** + * BPMN Child Process 的扩展属性,用于标记多实例来源类型 + */ + String CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE = "childProcessMultiInstanceSourceType"; + + /** + * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 + */ + String FORM_FIELD_PERMISSION_ELEMENT = "fieldsPermission"; + + /** + * BPMN ExtensionElement Attribute, 用于标记表单字段 + */ + String FORM_FIELD_PERMISSION_ELEMENT_FIELD_ATTRIBUTE = "field"; + /** + * BPMN ExtensionElement Attribute, 用于标记表单权限 + */ + String FORM_FIELD_PERMISSION_ELEMENT_PERMISSION_ATTRIBUTE = "permission"; + + /** + * BPMN ExtensionElement 操作按钮设置元素, 用于审批节点操作按钮设置 + */ + String BUTTON_SETTING_ELEMENT = "buttonsSetting"; + + /** + * BPMN ExtensionElement Attribute, 用于标记按钮编号 + */ + String BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE = "id"; + + /** + * BPMN ExtensionElement Attribute, 用于标记按钮显示名称 + */ + String BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE = "displayName"; + + /** + * BPMN ExtensionElement Attribute, 用于标记按钮是否启用 + */ + String BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE = "enable"; + + /** + * BPMN ExtensionElement 的扩展属性,用于标记触发器的类型 + */ + String TRIGGER_TYPE = "triggerType"; + /** + * BPMN ExtensionElement 的扩展属性,用于标记触发器参数 + */ + String TRIGGER_PARAM = "triggerParam"; + + /** + * BPMN Start Event Node Id + */ + String START_EVENT_NODE_ID = "StartEvent"; + + /** + * 发起人节点 ID + */ + String START_USER_NODE_ID = "StartUserNode"; + + /** + * 是否需要签名 + */ + String SIGN_ENABLE = "signEnable"; + + /** + * 审批意见是否必填 + */ + String REASON_REQUIRE = "reasonRequire"; + + /** + * 节点类型 + * + * 目前只有 {@link BpmModelTypeEnum#SIMPLE} 的 UserTask 节点会设置该属性,用于区分是审批节点、还是办理节点 + */ + String NODE_TYPE = "nodeType"; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java new file mode 100644 index 0000000..0e6a697 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java @@ -0,0 +1,99 @@ +package com.zt.plat.module.bpm.framework.flowable.core.enums; + +import org.flowable.engine.runtime.ProcessInstance; + +/** + * BPM Variable 通用常量 + * + * @author ZT + */ +public class BpmnVariableConstants { + + /** + * 流程实例的变量 - 状态 + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS"; + /** + * 流程实例的变量 - 理由 + * + * 例如说:审批不通过的理由(目前审核通过暂时不会记录) + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_REASON = "PROCESS_REASON"; + /** + * 流程实例的变量 - 发起用户选择的审批人 Map + * + * @see ProcessInstance#getProcessVariables() + * @see BpmTaskCandidateStrategyEnum#START_USER_SELECT + */ + public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; + /** + * 流程实例的变量 - 审批人选择的审批人 Map + * + * @see ProcessInstance#getProcessVariables() + * @see BpmTaskCandidateStrategyEnum#APPROVE_USER_SELECT + */ + public static final String PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES = "PROCESS_APPROVE_USER_SELECT_ASSIGNEES"; + /** + * 流程实例的变量 - 发起用户 ID + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_START_USER_ID = "PROCESS_START_USER_ID"; + /** + * 流程实例的变量 - 用于判断流程实例变量节点是否驳回. 格式 RETURN_FLAG_{节点 id} + * + * 目的是:驳回到发起节点时,因为审批人与发起人相同,所以被自动通过。但是,此时还是希望不要自动通过 + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s"; + /** + * 流程实例的变量 - 是否跳过表达式 + * + * @see ProcessInstance#getProcessVariables() + * @see Flowable/Activiti之SkipExpression 完成自动审批 + */ + public static final String PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + + /** + * 流程实例的变量 - 用于判断流程是否需要跳过发起人节点 + * + * @see ProcessInstance#getProcessVariables() + */ + public static final String PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE = "PROCESS_SKIP_START_USER_NODE"; + + /** + * 流程实例的变量 - 流程开始时间 + * + * 【非存储变量】用于部分需要 format 的场景,例如说:流程实例的自定义标题 + */ + public static final String PROCESS_START_TIME = "PROCESS_START_TIME"; + /** + * 流程实例的变量 - 流程定义名称 + */ + public static final String PROCESS_DEFINITION_NAME = "PROCESS_DEFINITION_NAME"; + + /** + * 任务的变量 - 状态 + * + * @see org.flowable.task.api.Task#getTaskLocalVariables() + */ + public static final String TASK_VARIABLE_STATUS = "TASK_STATUS"; + /** + * 任务的变量 - 理由 + * + * 例如说:审批通过、不通过的理由 + * + * @see org.flowable.task.api.Task#getTaskLocalVariables() + */ + public static final String TASK_VARIABLE_REASON = "TASK_REASON"; + /** + * 任务变量 - 签名图片 URL + */ + public static final String TASK_SIGN_PIC_URL = "TASK_SIGN_PIC_URL"; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java new file mode 100644 index 0000000..5ec3654 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java @@ -0,0 +1,24 @@ +package com.zt.plat.module.bpm.framework.flowable.core.event; + +import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.validation.annotation.Validated; + +/** + * {@link BpmProcessInstanceStatusEvent} 的生产者 + * + * @author ZT + */ +@AllArgsConstructor +@Validated +public class BpmProcessInstanceEventPublisher { + + private final ApplicationEventPublisher publisher; + + public void sendProcessInstanceResultEvent(@Valid BpmProcessInstanceStatusEvent event) { + publisher.publishEvent(event); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java new file mode 100644 index 0000000..a821f05 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java @@ -0,0 +1,47 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; +import jakarta.annotation.Resource; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.springframework.stereotype.Component; + +import java.util.Set; + +import static com.zt.plat.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate.BEAN_NAME; + +/** + * 处理抄送用户的 {@link JavaDelegate} 的实现类 + *

+ * 目前只有仿钉钉/飞书模式的【抄送节点】使用 + * + * @author jason + */ +@Component(BEAN_NAME) +public class BpmCopyTaskDelegate implements JavaDelegate { + + public static final String BEAN_NAME = "bpmCopyTaskDelegate"; + + @Resource + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; + + @Override + public void execute(DelegateExecution execution) { + // 1. 获得抄送人 + Set userIds = taskCandidateInvoker.calculateUsersByTask(execution); + if (CollUtil.isEmpty(userIds)) { + return; + } + // 2. 执行抄送 + FlowElement currentFlowElement = execution.getCurrentFlowElement(); + processInstanceCopyService.createProcessInstanceCopy(userIds, null, execution.getProcessInstanceId(), + currentFlowElement.getId(), currentFlowElement.getName(), null); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java new file mode 100644 index 0000000..7c4c0e9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java @@ -0,0 +1,54 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener; + +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.google.common.collect.ImmutableSet; +import jakarta.annotation.Resource; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.flowable.engine.delegate.event.FlowableCancelledEvent; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.Set; + +/** + * 监听 {@link ProcessInstance} 的状态变更,更新其对应的 status 状态 + * + * @author jason + */ +@Component +public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener { + + public static final Set PROCESS_INSTANCE_EVENTS = ImmutableSet.builder() + .add(FlowableEngineEventType.PROCESS_CREATED) + .add(FlowableEngineEventType.PROCESS_COMPLETED) + .add(FlowableEngineEventType.PROCESS_CANCELLED) + .build(); + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + + public BpmProcessInstanceEventListener(){ + super(PROCESS_INSTANCE_EVENTS); + } + + @Override + protected void processCreated(FlowableEngineEntityEvent event) { + processInstanceService.processProcessInstanceCreated((ProcessInstance)event.getEntity()); + } + + @Override + protected void processCompleted(FlowableEngineEntityEvent event) { + processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity()); + } + + @Override // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法 + protected void processCancelled(FlowableCancelledEvent event) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId()); + processInstanceService.processProcessInstanceCompleted(processInstance); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java new file mode 100644 index 0000000..eda5244 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java @@ -0,0 +1,125 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.module.bpm.enums.definition.BpmBoundaryEventTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.service.definition.BpmModelService; +import com.zt.plat.module.bpm.service.task.BpmTaskService; +import com.google.common.collect.ImmutableSet; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +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.FlowableEngineEntityEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.job.api.Job; +import org.flowable.task.api.Task; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Set; + +/** + * 监听 {@link Task} 的开始与完成 + * + * @author jason + */ +@Component +@Slf4j +public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmModelService modelService; + @Resource + @Lazy // 解决循环依赖 + private BpmTaskService taskService; + + public static final Set TASK_EVENTS = ImmutableSet.builder() + .add(FlowableEngineEventType.TASK_CREATED) + .add(FlowableEngineEventType.TASK_ASSIGNED) + .add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,这里仅处理任务后置通知。 + .add(FlowableEngineEventType.ACTIVITY_CANCELLED) + .add(FlowableEngineEventType.TIMER_FIRED) // 监听审批超时 + .build(); + + public BpmTaskEventListener() { + super(TASK_EVENTS); + } + + @Override + protected void taskCreated(FlowableEngineEntityEvent event) { + taskService.processTaskCreated((Task) event.getEntity()); + } + + @Override + protected void taskAssigned(FlowableEngineEntityEvent event) { + taskService.processTaskAssigned((Task) event.getEntity()); + } + + @Override + protected void taskCompleted(FlowableEngineEntityEvent event) { + taskService.processTaskCompleted((Task) event.getEntity()); + } + + @Override + protected void activityCancelled(FlowableActivityCancelledEvent event) { + List activityList = taskService.getHistoricActivityListByExecutionId(event.getExecutionId()); + if (CollUtil.isEmpty(activityList)) { + log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId()); + return; + } + // 遍历处理 + activityList.forEach(activity -> { + if (StrUtil.isEmpty(activity.getTaskId())) { + return; + } + taskService.processTaskCanceled(activity.getTaskId()); + }); + } + + @Override + @SuppressWarnings("PatternVariableCanBeUsed") + protected void timerFired(FlowableEngineEntityEvent event) { + // 1.1 只处理 BoundaryEvent 边界计时时间 + String processDefinitionId = event.getProcessDefinitionId(); + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); + Job entity = (Job) event.getEntity(); + FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId()); + if (!(element instanceof BoundaryEvent)) { + return; + } + // 1.2 判断是否为超时处理 + BoundaryEvent boundaryEvent = (BoundaryEvent) element; + String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, + BpmnModelConstants.BOUNDARY_EVENT_TYPE); + BpmBoundaryEventTypeEnum bpmTimerBoundaryEventType = BpmBoundaryEventTypeEnum.typeOf(NumberUtils.parseInt(boundaryEventType)); + + // 2. 处理超时 + if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT)) { + // 2.1 用户任务超时处理 + String timeoutHandlerType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, + BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_TYPE); + String taskKey = boundaryEvent.getAttachedToRefId(); + taskService.processTaskTimeout(event.getProcessInstanceId(), taskKey, NumberUtils.parseInt(timeoutHandlerType)); + // 2.2 延迟器超时处理 + } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) { + String taskKey = boundaryEvent.getAttachedToRefId(); + taskService.triggerTask(event.getProcessInstanceId(), taskKey); + // 2.3 子流程超时处理 + } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT)) { + String taskKey = boundaryEvent.getAttachedToRefId(); + taskService.processChildProcessTimeout(event.getProcessInstanceId(), taskKey); + } + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java new file mode 100644 index 0000000..f88ce20 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java @@ -0,0 +1,55 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener; + +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.springframework.stereotype.Component; + +import java.util.EnumMap; +import java.util.List; + +import static com.zt.plat.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate.BEAN_NAME; + + +/** + * 处理触发器任务 {@link JavaDelegate} 的实现类 + *

+ * 目前只有 Simple 设计器【触发器节点】使用 + * + * @author jason + */ +@Component(BEAN_NAME) +@Slf4j +public class BpmTriggerTaskDelegate implements JavaDelegate { + + public static final String BEAN_NAME = "bpmTriggerTaskDelegate"; + + @Resource + private List triggers; + + private final EnumMap triggerMap = new EnumMap<>(BpmTriggerTypeEnum.class); + + @PostConstruct + private void init() { + triggers.forEach(trigger -> triggerMap.put(trigger.getType(), trigger)); + } + + @Override + public void execute(DelegateExecution execution) { + FlowElement flowElement = execution.getCurrentFlowElement(); + BpmTriggerTypeEnum bpmTriggerType = BpmnModelUtils.parserTriggerType(flowElement); + BpmTrigger bpmTrigger = triggerMap.get(bpmTriggerType); + if (bpmTrigger == null) { + log.error("[execute][FlowElement({}), {} 找不到匹配的触发器]", execution.getCurrentActivityId(), flowElement); + return; + } + bpmTrigger.execute(execution.getProcessInstanceId(), BpmnModelUtils.parserTriggerParam(flowElement)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java new file mode 100644 index 0000000..84763fe --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java @@ -0,0 +1,21 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.exection; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; + +/** + * 类型为 class 的 ExecutionListener 监听器示例 + * + * @author ZT + */ +@Slf4j +public class DemoDelegateClassExecutionListener implements JavaDelegate { + + @Override + public void execute(DelegateExecution execution) { + log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(), + execution.getCurrentFlowableListener().getFieldExtensions()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java new file mode 100644 index 0000000..64dba65 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java @@ -0,0 +1,23 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.exection; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.JavaDelegate; +import org.springframework.stereotype.Component; + +/** + * 类型为 delegateExpression 的 ExecutionListener 监听器示例 + * + * 和 {@link DemoDelegateClassExecutionListener} 的差异是,需要注册到 Spring 中 + */ +@Component +@Slf4j +public class DemoDelegateExpressionExecutionListener implements JavaDelegate { + + @Override + public void execute(DelegateExecution execution) { + log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(), + execution.getCurrentFlowableListener().getFieldExtensions()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java new file mode 100644 index 0000000..c6e5b99 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java @@ -0,0 +1,21 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.exection; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.springframework.stereotype.Component; + +/** + * 类型为 expression 的 ExecutionListener 监听器示例 + * + * 和 {@link DemoDelegateClassExecutionListener} 的差异是,需要注册到 Spring 中,但不用实现 {@link org.flowable.engine.delegate.JavaDelegate} 接口 + */ +@Component +@Slf4j +public class DemoSpringExpressionExecutionListener { + + public void execute(DelegateExecution execution) { + log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(), + execution.getCurrentFlowableListener().getFieldExtensions()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java new file mode 100644 index 0000000..6b4e94e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java @@ -0,0 +1,20 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.task; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.TaskListener; +import org.flowable.task.service.delegate.DelegateTask; + +/** + * 类型为 class 的 TaskListener 监听器示例 + * + * @author ZT + */ +@Slf4j +public class DemoDelegateClassTaskListener implements TaskListener { + + @Override + public void notify(DelegateTask delegateTask) { + log.info("[execute][task({}) 被调用]", delegateTask.getId()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java new file mode 100644 index 0000000..41fa05b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java @@ -0,0 +1,22 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.task; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.TaskListener; +import org.flowable.task.service.delegate.DelegateTask; +import org.springframework.stereotype.Component; + +/** + * 类型为 delegateExpression 的 TaskListener 监听器示例 + * + * @author ZT + */ +@Component +@Slf4j +public class DemoDelegateExpressionTaskListener implements TaskListener { + + @Override + public void notify(DelegateTask delegateTask) { + log.info("[execute][task({}) 被调用]", delegateTask.getId()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java new file mode 100644 index 0000000..b9a4ccf --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java @@ -0,0 +1,20 @@ +package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.task; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.task.service.delegate.DelegateTask; +import org.springframework.stereotype.Component; + +/** + * 类型为 expression 的 TaskListener 监听器示例 + * + * @author ZT + */ +@Slf4j +@Component +public class DemoSpringExpressionTaskListener { + + public void notify(DelegateTask delegateTask) { + log.info("[execute][task({}) 被调用]", delegateTask.getId()); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java new file mode 100644 index 0000000..de5b2a4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java @@ -0,0 +1,158 @@ +package com.zt.plat.module.bpm.framework.flowable.core.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.spring.SpringUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.fasterxml.jackson.core.type.TypeReference; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR; + +/** + * 工作流发起 HTTP 请求工具类 + * + * @author ZT + */ +@Slf4j +public class BpmHttpRequestUtils { + + public static void executeBpmHttpRequest(ProcessInstance processInstance, + String url, + List headerParams, + List bodyParams, + Boolean handleResponse, + List> response) { + RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); + BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class); + + // 1.1 设置请求头 + MultiValueMap headers = buildHttpHeaders(processInstance, headerParams); + // 1.2 设置请求体 + MultiValueMap body = buildHttpBody(processInstance, bodyParams); + + // 2. 发起请求 + ResponseEntity responseEntity = sendHttpRequest(url, headers, body, restTemplate); + + // 3. 处理返回 + if (Boolean.FALSE.equals(handleResponse)) { + return; + } + // 3.1 判断是否需要解析返回值 + if (responseEntity == null + || StrUtil.isEmpty(responseEntity.getBody()) + || !responseEntity.getStatusCode().is2xxSuccessful() + || CollUtil.isEmpty(response)) { + return; + } + // 3.2 解析返回值, 返回值必须符合 CommonResult 规范。 + CommonResult> respResult = JsonUtils.parseObjectQuietly(responseEntity.getBody(), + new TypeReference<>() {}); + if (respResult == null || !respResult.isSuccess()) { + return; + } + // 3.3 获取需要更新的流程变量 + Map updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), response); + // 3.4 更新流程变量 + if (CollUtil.isNotEmpty(updateVariables)) { + processInstanceService.updateProcessInstanceVariables(processInstance.getId(), updateVariables); + } + } + + public static ResponseEntity sendHttpRequest(String url, + MultiValueMap headers, + MultiValueMap body, + RestTemplate restTemplate) { + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + ResponseEntity responseEntity; + try { + responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); + log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); + } catch (RestClientException e) { + log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); + throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR); + } + return responseEntity; + } + + public static MultiValueMap buildHttpHeaders(ProcessInstance processInstance, + List headerSettings) { + Map processVariables = processInstance.getProcessVariables(); + MultiValueMap headers = new LinkedMultiValueMap<>(); + headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); + addHttpRequestParam(headers, headerSettings, processVariables); + return headers; + } + + public static MultiValueMap buildHttpBody(ProcessInstance processInstance, + List bodySettings) { + Map processVariables = processInstance.getProcessVariables(); + MultiValueMap body = new LinkedMultiValueMap<>(); + addHttpRequestParam(body, bodySettings, processVariables); + body.add("processInstanceId", processInstance.getId()); + return body; + } + + /** + * 从请求返回值获取需要更新的流程变量 + * + * @param result 请求返回结果 + * @param responseSettings 返回设置 + * @return 需要更新的流程变量 + */ + public static Map getNeedUpdatedVariablesFromResponse(Map result, + List> responseSettings) { + Map updateVariables = new HashMap<>(); + if (CollUtil.isEmpty(result)) { + return updateVariables; + } + responseSettings.forEach(responseSetting -> { + if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) { + updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue())); + } + }); + return updateVariables; + } + + /** + * 添加 HTTP 请求参数。请求头或者请求体 + * + * @param params HTTP 请求参数 + * @param paramSettings HTTP 请求参数设置 + * @param processVariables 流程变量 + */ + public static void addHttpRequestParam(MultiValueMap params, + List paramSettings, + Map processVariables) { + if (CollUtil.isEmpty(paramSettings)) { + return; + } + paramSettings.forEach(item -> { + if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) { + params.add(item.getKey(), item.getValue()); + } else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) { + params.add(item.getKey(), processVariables.get(item.getValue()).toString()); + } + }); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java new file mode 100644 index 0000000..1be0cd5 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -0,0 +1,1025 @@ +package com.zt.plat.module.bpm.framework.flowable.core.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.framework.common.util.string.StrUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import com.zt.plat.module.bpm.enums.definition.*; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import com.google.common.collect.Maps; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.impl.util.io.BytesStreamSource; +import org.flowable.engine.impl.el.FixedValue; + +import java.util.*; + +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX; + +/** + * BPMN Model 操作工具类。目前分成三部分: + * + * 1. BPMN 修改 + 解析元素相关的方法 + * 2. BPMN 简单查找相关的方法 + * 3. BPMN 复杂遍历相关的方法 + * 4. BPMN 流程预测相关的方法 + * + * @author ZT + */ +@Slf4j +public class BpmnModelUtils { + + // ========== BPMN 修改 + 解析元素相关的方法 ========== + + public static void addExtensionElement(FlowElement element, String name, String value) { + if (value == null) { + return; + } + ExtensionElement extensionElement = new ExtensionElement(); + extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); + extensionElement.setElementText(value); + extensionElement.setName(name); + element.addExtensionElement(extensionElement); + } + + public static void addExtensionElement(FlowElement element, String name, Integer value) { + if (value == null) { + return; + } + addExtensionElement(element, name, String.valueOf(value)); + } + + public static void addExtensionElementJson(FlowElement element, String name, Object value) { + if (value == null) { + return; + } + addExtensionElement(element, name, JsonUtils.toJsonString(value)); + } + + public static void addExtensionElement(FlowElement element, String name, Map attributes) { + if (attributes == null) { + return; + } + ExtensionElement extensionElement = new ExtensionElement(); + extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); + extensionElement.setName(name); + attributes.forEach((key, value) -> { + ExtensionAttribute extensionAttribute = new ExtensionAttribute(key, value); + extensionElement.addAttribute(extensionAttribute); + }); + element.addExtensionElement(extensionElement); + } + + /** + * 解析扩展元素 + * + * @param flowElement 节点 + * @param elementName 元素名称 + * @return 扩展元素 + */ + public static String parseExtensionElement(FlowElement flowElement, String elementName) { + if (flowElement == null) { + return null; + } + ExtensionElement element = CollUtil.getFirst(flowElement.getExtensionElements().get(elementName)); + return element != null ? element.getElementText() : null; + } + + /** + * 给节点添加候选人元素 + * + * @param candidateStrategy 候选人策略 + * @param candidateParam 候选人参数,允许空 + * @param flowElement 节点 + */ + public static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) { + addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY, + candidateStrategy == null ? null : candidateStrategy.toString()); + addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, candidateParam); + } + + /** + * 解析候选人策略 + * + * @param userTask 任务节点 + * @return 候选人策略 + */ + public static Integer parseCandidateStrategy(FlowElement userTask) { + Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue( + BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); + // TODO @芋艿 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限 + if (candidateStrategy == null) { + ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); + candidateStrategy = element != null ? NumberUtils.parseInt(element.getElementText()) : null; + } + return candidateStrategy; + } + + /** + * 解析候选人参数 + * + * @param userTask 任务节点 + * @return 候选人参数 + */ + public static String parseCandidateParam(FlowElement userTask) { + String candidateParam = userTask.getAttributeValue( + BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM); + if (candidateParam == null) { + ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)); + candidateParam = element != null ? element.getElementText() : null; + } + return candidateParam; + } + + /** + * 解析审批类型 + * + * @see BpmUserTaskApproveTypeEnum + * @param userTask 任务节点 + * @return 审批类型 + */ + public static Integer parseApproveType(FlowElement userTask) { + return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE)); + } + + /** + * 解析子流程多实例来源类型 + * + * @see BpmChildProcessMultiInstanceSourceTypeEnum + * @param element 任务节点 + * @return 多实例来源类型 + */ + public static Integer parseMultiInstanceSourceType(FlowElement element) { + return NumberUtils.parseInt(parseExtensionElement(element, BpmnModelConstants.CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE)); + } + + /** + * 添加任务拒绝处理元素 + * + * @param rejectHandler 任务拒绝处理 + * @param userTask 任务节点 + */ + public static void addTaskRejectElements(BpmSimpleModelNodeVO.RejectHandler rejectHandler, UserTask userTask) { + if (rejectHandler == null) { + return; + } + addExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE, StrUtil.toStringOrNull(rejectHandler.getType())); + addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId()); + } + + /** + * 解析任务拒绝处理类型 + * + * @param userTask 任务节点 + * @return 任务拒绝处理类型 + */ + public static BpmUserTaskRejectHandlerTypeEnum parseRejectHandlerType(FlowElement userTask) { + Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE)); + return BpmUserTaskRejectHandlerTypeEnum.typeOf(rejectHandlerType); + } + + /** + * 解析任务拒绝返回任务节点 ID + * + * @param flowElement 任务节点 + * @return 任务拒绝返回任务节点 ID + */ + public static String parseReturnTaskId(FlowElement flowElement) { + return parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID); + } + + /** + * 给节点添加用户任务的审批人与发起人相同时,处理类型枚举 + * + * @see BpmUserTaskAssignStartUserHandlerTypeEnum + * @param assignStartUserHandlerType 发起人处理类型 + * @param userTask 任务节点 + */ + public static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) { + if (assignStartUserHandlerType == null) { + return; + } + addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString()); + } + + /** + * 给节点添加用户任务的审批人为空时,处理类型枚举 + * + * @see BpmUserTaskAssignEmptyHandlerTypeEnum + * @param emptyHandler 空处理 + * @param userTask 任务节点 + */ + public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) { + if (emptyHandler == null) { + return; + } + addExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE, StrUtil.toStringOrNull(emptyHandler.getType())); + addExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS, StrUtil.join(",", emptyHandler.getUserIds())); + } + + /** + * 解析用户任务的审批人与发起人相同时,处理类型枚举 + * + * @param userTask 任务节点 + * @return 处理类型枚举 + */ + public static Integer parseAssignStartUserHandlerType(FlowElement userTask) { + return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE)); + } + + /** + * 解析用户任务的审批人为空时,处理类型枚举 + * + * @param userTask 任务节点 + * @return 处理类型枚举 + */ + public static Integer parseAssignEmptyHandlerType(FlowElement userTask) { + return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE)); + } + + /** + * 解析用户任务的审批人为空时,处理用户 ID 数组 + * + * @param userTask 任务节点 + * @return 处理用户 ID 数组 + */ + public static List parseAssignEmptyHandlerUserIds(FlowElement userTask) { + return StrUtils.splitToLong(parseExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS), ","); + } + + /** + * 给节点添加表单字段权限元素 + * + * @param fieldsPermissions 表单字段权限 + * @param flowElement 节点 + */ + public static void addFormFieldsPermission(List> fieldsPermissions, FlowElement flowElement) { + if (CollUtil.isNotEmpty(fieldsPermissions)) { + fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FORM_FIELD_PERMISSION_ELEMENT, item)); + } + } + + /** + * 解析表单字段权限 + * + * @param bpmnModel bpmnModel 对象 + * @param flowElementId 元素 ID + * @return 表单字段权限 + */ + public static Map parseFormFieldsPermission(BpmnModel bpmnModel, String flowElementId) { + if (bpmnModel == null || StrUtil.isEmpty(flowElementId)) { + return null; + } + FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); + if (flowElement == null) { + return null; + } + List extensionElements = flowElement.getExtensionElements().get(FORM_FIELD_PERMISSION_ELEMENT); + if (CollUtil.isEmpty(extensionElements)) { + return null; + } + Map fieldsPermission = MapUtil.newHashMap(); + extensionElements.forEach(element -> { + String field = element.getAttributeValue(null, FORM_FIELD_PERMISSION_ELEMENT_FIELD_ATTRIBUTE); + String permission = element.getAttributeValue(null, FORM_FIELD_PERMISSION_ELEMENT_PERMISSION_ATTRIBUTE); + if (StrUtil.isNotEmpty(field) && StrUtil.isNotEmpty(permission)) { + fieldsPermission.put(field, permission); + } + }); + return fieldsPermission; + } + + /** + * 给节点添加操作按钮设置元素 + */ + public static void addButtonsSetting(List buttonsSetting, UserTask userTask) { + if (CollUtil.isNotEmpty(buttonsSetting)) { + List> list = CollectionUtils.convertList(buttonsSetting, item -> { + Map settingMap = Maps.newHashMapWithExpectedSize(3); + settingMap.put(BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE, String.valueOf(item.getId())); + settingMap.put(BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE, item.getDisplayName()); + settingMap.put(BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE, String.valueOf(item.getEnable())); + return settingMap; + }); + list.forEach(item -> addExtensionElement(userTask, BUTTON_SETTING_ELEMENT, item)); + } + } + + /** + * 解析操作按钮设置 + * + * @param bpmnModel bpmnModel 对象 + * @param flowElementId 元素 ID + * @return 操作按钮设置 + */ + public static Map parseButtonsSetting(BpmnModel bpmnModel, String flowElementId) { + FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); + if (flowElement == null) { + return null; + } + List extensionElements = flowElement.getExtensionElements().get(BUTTON_SETTING_ELEMENT); + if (CollUtil.isEmpty(extensionElements)) { + return null; + } + Map buttonSettings = Maps.newHashMapWithExpectedSize(extensionElements.size()); + extensionElements.forEach(element -> { + String id = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE); + String displayName = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE); + String enable = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE); + if (StrUtil.isNotEmpty(id)) { + BpmTaskRespVO.OperationButtonSetting setting = new BpmTaskRespVO.OperationButtonSetting(); + buttonSettings.put(Integer.valueOf(id), setting.setDisplayName(displayName).setEnable(Boolean.parseBoolean(enable))); + } + }); + return buttonSettings; + } + + /** + * 解析边界事件扩展元素 + * + * @param boundaryEvent 边界事件 + * @param customElement 元素 + * @return 扩展元素 + */ + public static String parseBoundaryEventExtensionElement(BoundaryEvent boundaryEvent, String customElement) { + if (boundaryEvent == null) { + return null; + } + ExtensionElement extensionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(customElement)); + return Optional.ofNullable(extensionElement).map(ExtensionElement::getElementText).orElse(null); + } + + public static void addSignEnable(Boolean signEnable, FlowElement userTask) { + addExtensionElement(userTask, SIGN_ENABLE, + ObjUtil.isNotNull(signEnable) ? signEnable.toString() : Boolean.FALSE.toString()); + } + + public static Boolean parseSignEnable(BpmnModel bpmnModel, String flowElementId) { + FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); + if (flowElement == null) { + return false; + } + List extensionElements = flowElement.getExtensionElements().get(SIGN_ENABLE); + if (CollUtil.isEmpty(extensionElements)) { + return false; + } + return Convert.toBool(extensionElements.get(0).getElementText(), false); + } + + public static void addReasonRequire(Boolean reasonRequire, FlowElement userTask) { + addExtensionElement(userTask, REASON_REQUIRE, + ObjUtil.isNotNull(reasonRequire) ? reasonRequire.toString() : Boolean.FALSE.toString()); + } + + public static Boolean parseReasonRequire(BpmnModel bpmnModel, String flowElementId) { + FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); + if (flowElement == null) { + return false; + } + List extensionElements = flowElement.getExtensionElements().get(REASON_REQUIRE); + if (CollUtil.isEmpty(extensionElements)) { + return false; + } + return Convert.toBool(extensionElements.get(0).getElementText(), false); + } + + public static void addListenerConfig(FlowableListener flowableListener, BpmSimpleModelNodeVO.ListenerHandler handler) { + FieldExtension fieldExtension = new FieldExtension(); + fieldExtension.setFieldName("listenerConfig"); + fieldExtension.setStringValue(JsonUtils.toJsonString(handler)); + flowableListener.getFieldExtensions().add(fieldExtension); + } + + public static BpmSimpleModelNodeVO.ListenerHandler parseListenerConfig(FixedValue fixedValue) { + String expressionText = fixedValue.getExpressionText(); + Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); + return JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ListenerHandler.class); + } + + public static BpmTriggerTypeEnum parserTriggerType(FlowElement flowElement) { + Integer triggerType = NumberUtils.parseInt(parseExtensionElement(flowElement, TRIGGER_TYPE)); + return BpmTriggerTypeEnum.typeOf(triggerType); + } + + public static String parserTriggerParam(FlowElement flowElement) { + return parseExtensionElement(flowElement, TRIGGER_PARAM); + } + + /** + * 给节点添加节点类型 + * + * @param nodeType 节点类型 + * @param flowElement 节点 + */ + public static void addNodeType(Integer nodeType, FlowElement flowElement) { + addExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE, nodeType); + } + + /** + * 解析节点类型 + * + * @param flowElement 节点 + * @return 节点类型 + */ + public static Integer parseNodeType(FlowElement flowElement) { + return NumberUtils.parseInt(parseExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE)); + } + + // ========== BPM 简单查找相关的方法 ========== + + /** + * 根据节点,获取入口连线 + * + * @param source 起始节点 + * @return 入口连线列表 + */ + public static List getElementIncomingFlows(FlowElement source) { + if (source instanceof FlowNode) { + return ((FlowNode) source).getIncomingFlows(); + } + return new ArrayList<>(); + } + + /** + * 根据节点,获取出口连线 + * + * @param source 起始节点 + * @return 出口连线列表 + */ + public static List getElementOutgoingFlows(FlowElement source) { + if (source instanceof FlowNode) { + return ((FlowNode) source).getOutgoingFlows(); + } + return new ArrayList<>(); + } + + /** + * 获取流程元素信息 + * + * @param model bpmnModel 对象 + * @param flowElementId 元素 ID + * @return 元素信息 + */ + public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { + Process process = model.getMainProcess(); + return process.getFlowElement(flowElementId); + } + + /** + * 获得 BPMN 流程中,指定的元素们 + * + * @param model 模型 + * @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等 + * @return 元素们 + */ + @SuppressWarnings("unchecked") + public static List getBpmnModelElements(BpmnModel model, Class clazz) { + List result = new ArrayList<>(); + model.getProcesses().forEach(process -> process.getFlowElements().forEach(flowElement -> { + if (flowElement.getClass().isAssignableFrom(clazz)) { + result.add((T) flowElement); + } + })); + return result; + } + + public static StartEvent getStartEvent(BpmnModel model) { + Process process = model.getMainProcess(); + // 从 initialFlowElement 找 + FlowElement startElement = process.getInitialFlowElement(); + if (startElement instanceof StartEvent) { + return (StartEvent) startElement; + } + // 从 flowElementList 找 + return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent); + } + + public static EndEvent getEndEvent(BpmnModel model) { + Process process = model.getMainProcess(); + // 从 flowElementList 找 endEvent + return (EndEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof EndEvent); + } + + public static BpmnModel getBpmnModel(byte[] bpmnBytes) { + if (ArrayUtil.isEmpty(bpmnBytes)) { + return null; + } + BpmnXMLConverter converter = new BpmnXMLConverter(); + // 补充说明:由于在 Flowable 中自定义了属性,所以 validateSchema 传递 false + return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false); + } + + public static String getBpmnXml(BpmnModel model) { + if (model == null) { + return null; + } + BpmnXMLConverter converter = new BpmnXMLConverter(); + return StrUtil.utf8Str(converter.convertToXML(model)); + } + + public static String getBpmnXml(byte[] bpmnBytes) { + if (ArrayUtil.isEmpty(bpmnBytes)) { + return null; + } + return StrUtil.utf8Str(bpmnBytes); + } + + // ========== BPMN 复杂遍历相关的方法 ========== + + /** + * 找到 source 节点之前的所有用户任务节点 + * + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 已找到的用户任务节点 + * @return 用户任务节点 数组 + */ + public static List getPreviousUserTaskList(FlowElement source, Set hasSequenceFlow, List userTaskList) { + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + if (sequenceFlows == null) { + return userTaskList; + } + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 类型为用户节点,则新增父级节点 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); + } + // 类型为子流程,则添加子流程开始节点出口处相连的节点 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + // 获取子流程用户任务节点 + List childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (CollUtil.isNotEmpty(childUserTaskList)) { + userTaskList.addAll(childUserTaskList); + } + } + // 继续迭代 + userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); + } + return userTaskList; + } + + /** + * 迭代获取子流程用户任务节点 + * + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return 用户任务节点 + */ + public static List findChildProcessUserTaskList(FlowElement source, Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (sequenceFlows == null) { + return userTaskList; + } + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 + if (sequenceFlow.getTargetFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); + continue; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + List childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (CollUtil.isNotEmpty(childUserTaskList)) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); + } + return userTaskList; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接退回到子流程中的情况,但存在从子流程出去到父流程情况 + * + * @param source 起始节点 + * @param target 目标节点 + * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 + * @return 结果 + */ + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set visitedElements) { + visitedElements = visitedElements == null ? new HashSet<>() : visitedElements; + // 不能是开始事件和子流程 + if (source instanceof StartEvent && isInEventSubprocess(source)) { + return false; + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + if (CollUtil.isEmpty(sequenceFlows)) { + return true; + } + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (visitedElements.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + visitedElements.add(sequenceFlow.getId()); + // 这条线路存在目标节点,这条线路完成,进入下个线路 + FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement(); + if (target.getId().equals(sourceFlowElement.getId())) { + continue; + } + // 如果目标节点为并行网关,则不继续 + if (sourceFlowElement instanceof ParallelGateway) { + return false; + } + // 否则就继续迭代 + if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) { + return false; + } + } + return true; + } + + /** + * 判断当前节点是否属于不同的子流程 + * + * @param flowElement 被判断的节点 + * @return true 表示属于子流程 + */ + private static boolean isInEventSubprocess(FlowElement flowElement) { + FlowElementsContainer flowElementsContainer = flowElement.getParentContainer(); + while (flowElementsContainer != null) { + if (flowElementsContainer instanceof EventSubProcess) { + return true; + } + + if (flowElementsContainer instanceof FlowElement) { + flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer(); + } else { + flowElementsContainer = null; + } + } + return false; + } + + /** + * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 + * + * @param source 起始节点 + * @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return 子级任务节点列表 + */ + public static List iteratorFindChildUserTasks(FlowElement source, List runTaskKeyList, + Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (sequenceFlows == null) { + return userTaskList; + } + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 + if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) { + userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); + continue; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + List childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (CollUtil.isNotEmpty(childUserTaskList)) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + return userTaskList; + } + + // ========== BPMN 流程预测相关的方法 ========== + + /** + * 流程预测,返回 StartEvent、UserTask、ServiceTask、EndEvent 节点元素,最终是 List 串行结果 + * + * @param bpmnModel BPMN 图 + * @param variables 变量 + * @return 节点元素数组 + */ + public static List simulateProcess(BpmnModel bpmnModel, Map variables) { + List resultElements = new ArrayList<>(); + Set visitElements = new HashSet<>(); + + // 从 StartEvent 开始遍历 + StartEvent startEvent = getStartEvent(bpmnModel); + simulateNextFlowElements(startEvent, variables, resultElements, visitElements); + + // 将 EndEvent 放在末尾。原因是,DFS 遍历,可能 EndEvent 在 resultElements 中 + List endEvents = CollUtil.removeWithAddIf(resultElements, + flowElement -> flowElement instanceof EndEvent); + resultElements.addAll(endEvents); + return resultElements; + } + + @SuppressWarnings("PatternVariableCanBeUsed") + private static void simulateNextFlowElements(FlowElement currentElement, Map variables, + List resultElements, Set visitElements) { + // 如果为空,或者已经遍历过,则直接结束 + if (currentElement == null) { + return; + } + if (visitElements.contains(currentElement)) { + return; + } + visitElements.add(currentElement); + + // 情况:StartEvent/EndEvent/UserTask/ServiceTask + if (currentElement instanceof StartEvent + || currentElement instanceof EndEvent + || currentElement instanceof UserTask + || currentElement instanceof ServiceTask) { + // 添加元素 + FlowNode flowNode = (FlowNode) currentElement; + resultElements.add(flowNode); + // 遍历子节点 + flowNode.getOutgoingFlows().forEach( + nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); + return; + } + + // 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的 + if (currentElement instanceof ExclusiveGateway) { + // 查找满足条件的 SequenceFlow 路径 + SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway((Gateway) currentElement, variables); + // 遍历满足条件的 SequenceFlow 路径 + if (matchSequenceFlow != null) { + simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements); + } + } + // 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的 + else if (currentElement instanceof InclusiveGateway) { + // 查找满足条件的 SequenceFlow 路径 + Collection matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway((Gateway) currentElement, variables); + // 遍历满足条件的 SequenceFlow 路径 + matchSequenceFlows.forEach( + flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements)); + } + // 情况:ParallelGateway 并行,都满足,都走 + else if (currentElement instanceof ParallelGateway) { + Gateway gateway = (Gateway) currentElement; + // 遍历子节点 + gateway.getOutgoingFlows().forEach( + nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); + } + } + + /** + * 根据当前节点,获取下一个节点 + * + * @param currentElement 当前节点 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + */ + @SuppressWarnings("PatternVariableCanBeUsed") + public static List getNextFlowNodes(FlowElement currentElement, BpmnModel bpmnModel, + Map variables){ + List nextFlowNodes = new ArrayList<>(); // 下一个执行的流程节点集合 + FlowNode currentNode = (FlowNode) currentElement; // 当前执行节点的基本属性 + List outgoingFlows = currentNode.getOutgoingFlows(); // 当前节点的关联节点 + if (CollUtil.isEmpty(outgoingFlows)) { + log.warn("[getNextFlowNodes][当前节点({}) 的 outgoingFlows 为空]", currentNode.getId()); + return nextFlowNodes; + } + + // 遍历每个出口流 + for (SequenceFlow outgoingFlow : outgoingFlows) { + // 获取目标节点的基本属性 + FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef()); + if (targetElement == null) { + continue; + } + // 如果是结束节点,直接返回 + if (targetElement instanceof EndEvent) { + break; + } + // 情况一:处理不同类型的网关 + if (targetElement instanceof Gateway) { + Gateway gateway = (Gateway) targetElement; + if (gateway instanceof ExclusiveGateway) { + handleExclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); + } else if (gateway instanceof InclusiveGateway) { + handleInclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); + } else if (gateway instanceof ParallelGateway) { + handleParallelGateway(gateway, bpmnModel, variables, nextFlowNodes); + } + } else { + // 情况二:如果不是网关,直接添加到下一个节点列表 + nextFlowNodes.add((FlowNode) targetElement); + } + } + return nextFlowNodes; + } + + /** + * 处理排它网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { + // 查找满足条件的 SequenceFlow 路径 + SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway(gateway, variables); + // 遍历满足条件的 SequenceFlow 路径 + if (matchSequenceFlow != null) { + FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + } + } + + /** + * 处理排它网关(Exclusive Gateway),选择符合条件的路径 + * + * @param gateway 排他网关 + * @param variables 流程变量 + * @return 符合条件的路径 + */ + private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { + SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && (evalConditionExpress(variables, flow.getConditionExpression()))); + if (matchSequenceFlow == null) { + matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), + flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); + // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 + if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) { + matchSequenceFlow = gateway.getOutgoingFlows().get(0); + } + } + return matchSequenceFlow; + } + + /** + * 处理包容网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { + // 查找满足条件的 SequenceFlow 路径集合 + Collection matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway(gateway, variables); + // 遍历满足条件的 SequenceFlow 路径,获取目标节点 + matchSequenceFlows.forEach(flow -> { + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + }); + } + + /** + * 处理排它网关(Inclusive Gateway),选择符合条件的路径 + * + * @param gateway 排他网关 + * @param variables 流程变量 + * @return 符合条件的路径 + */ + private static Collection findMatchSequenceFlowsByInclusiveGateway(Gateway gateway, Map variables) { + // 查找满足条件的 SequenceFlow 路径 + Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) + && evalConditionExpress(variables, flow.getConditionExpression())); + if (CollUtil.isEmpty(matchSequenceFlows)) { + matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), + flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); + // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 + if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) { + matchSequenceFlows = gateway.getOutgoingFlows(); + } + } + return matchSequenceFlows; + } + + + /** + * 处理并行网关 + * + * @param gateway 排他网关 + * @param bpmnModel BPMN模型 + * @param variables 流程变量 + * @param nextFlowNodes 下一个执行的流程节点集合 + */ + private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel, + Map variables, List nextFlowNodes) { + // 并行网关,遍历所有出口路径,获取目标节点 + gateway.getOutgoingFlows().forEach(flow -> { + FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); + if (targetElement instanceof FlowNode) { + nextFlowNodes.add((FlowNode) targetElement); + } + }); + } + + /** + * 计算条件表达式是否为 true 满足条件 + * + * @param variables 流程实例 + * @param expression 条件表达式 + * @return 是否满足条件 + */ + public static boolean evalConditionExpress(Map variables, String expression) { + if (expression == null) { + return Boolean.FALSE; + } + // 如果 variables 为空,则创建一个的原因?可能 expression 的计算,不依赖于 variables + if (variables == null) { + variables = new HashMap<>(); + } + + // 执行计算 + try { + Object result = FlowableUtils.getExpressionValue(variables, expression); + return Boolean.TRUE.equals(result); + } catch (FlowableException ex) { + // 为什么使用 info 日志?原因是,expression 如果从 variables 取不到值,会报错。实际这种情况下,可以忽略 + log.info("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", expression, variables, ex); + return Boolean.FALSE; + } + } + + @SuppressWarnings("PatternVariableCanBeUsed") + public static boolean isSequentialUserTask(FlowElement flowElement) { + if (!(flowElement instanceof UserTask)) { + return false; + } + UserTask userTask = (UserTask) flowElement; + MultiInstanceLoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics(); + return loopCharacteristics != null && loopCharacteristics.isSequential(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java new file mode 100644 index 0000000..7d54ce5 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java @@ -0,0 +1,362 @@ +package com.zt.plat.module.bpm.framework.flowable.core.util; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.zt.plat.framework.common.core.KeyValue; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.tenant.core.context.TenantContextHolder; +import com.zt.plat.framework.tenant.core.util.TenantUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormFieldVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; +import lombok.SneakyThrows; +import org.flowable.common.engine.api.delegate.Expression; +import org.flowable.common.engine.api.variable.VariableContainer; +import org.flowable.common.engine.impl.el.ExpressionManager; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.common.engine.impl.variable.MapDelegateVariableContainer; +import org.flowable.engine.ManagementService; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; +import org.flowable.engine.impl.util.CommandContextUtil; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.TaskInfo; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.stream.Collectors; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +/** + * Flowable 相关的工具方法 + * + * @author ZT + */ +public class FlowableUtils { + + // ========== User 相关的工具方法 ========== + + public static void setAuthenticatedUserId(Long userId) { + Authentication.setAuthenticatedUserId(String.valueOf(userId)); + } + + public static void clearAuthenticatedUserId() { + Authentication.setAuthenticatedUserId(null); + } + + public static V executeAuthenticatedUserId(Long userId, Callable callable) { + setAuthenticatedUserId(userId); + try { + return callable.call(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + clearAuthenticatedUserId(); + } + } + + public static String getTenantId() { + Long tenantId = TenantContextHolder.getTenantId(); + return tenantId != null ? String.valueOf(tenantId) : ProcessEngineConfiguration.NO_TENANT_ID; + } + + public static void execute(String tenantIdStr, Runnable runnable) { + if (ObjectUtil.isEmpty(tenantIdStr) + || Objects.equals(tenantIdStr, ProcessEngineConfiguration.NO_TENANT_ID)) { + runnable.run(); + } else { + Long tenantId = Long.valueOf(tenantIdStr); + TenantUtils.execute(tenantId, runnable); + } + } + + @SneakyThrows + public static V execute(String tenantIdStr, Callable callable) { + if (ObjectUtil.isEmpty(tenantIdStr) + || Objects.equals(tenantIdStr, ProcessEngineConfiguration.NO_TENANT_ID)) { + return callable.call(); + } else { + Long tenantId = Long.valueOf(tenantIdStr); + return TenantUtils.execute(tenantId, callable); + } + } + + // ========== Execution 相关的工具方法 ========== + + /** + * 格式化多实例(并签、或签)的 collectionVariable 变量(多实例对应的多审批人列表) + * + * @param activityId 活动编号 + * @return collectionVariable 变量 + */ + public static String formatExecutionCollectionVariable(String activityId) { + return activityId + "_assignees"; + } + + /** + * 格式化多实例(并签、或签)的 collectionElementVariable 变量(当前实例对应的一个审批人) + * + * @param activityId 活动编号 + * @return collectionElementVariable 变量 + */ + public static String formatExecutionCollectionElementVariable(String activityId) { + return activityId + "_assignee"; + } + + // ========== ProcessInstance 相关的工具方法 ========== + + public static Integer getProcessInstanceStatus(ProcessInstance processInstance) { + return getProcessInstanceStatus(processInstance.getProcessVariables()); + } + + public static Integer getProcessInstanceStatus(HistoricProcessInstance processInstance) { + return getProcessInstanceStatus(processInstance.getProcessVariables()); + } + + /** + * 获得流程实例的状态 + * + * @param processVariables 流程实例的 variables + * @return 状态 + */ + private static Integer getProcessInstanceStatus(Map processVariables) { + return (Integer) processVariables.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + } + + /** + * 获得流程实例的审批原因 + * + * @param processInstance 流程实例 + * @return 审批原因 + */ + public static String getProcessInstanceReason(HistoricProcessInstance processInstance) { + return (String) processInstance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON); + } + + /** + * 获得流程实例的表单 + * + * @param processInstance 流程实例 + * @return 表单 + */ + public static Map getProcessInstanceFormVariable(ProcessInstance processInstance) { + Map processVariables = new HashMap<>(processInstance.getProcessVariables()); + return filterProcessInstanceFormVariable(processVariables); + } + + /** + * 获得流程实例的表单 + * + * @param processInstance 流程实例 + * @return 表单 + */ + public static Map getProcessInstanceFormVariable(HistoricProcessInstance processInstance) { + Map processVariables = new HashMap<>(processInstance.getProcessVariables()); + return filterProcessInstanceFormVariable(processVariables); + } + + /** + * 过滤流程实例的表单 + * + * 为什么要过滤?目前使用 processVariables 存储所有流程实例的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示 + * + * @param processVariables 流程实例的 variables + * @return 过滤后的表单 + */ + public static Map filterProcessInstanceFormVariable(Map processVariables) { + processVariables.remove(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + return processVariables; + } + + /** + * 获得流程实例的发起用户选择的审批人 Map + * + * @param processInstance 流程实例 + * @return 发起用户选择的审批人 Map + */ + public static Map> getStartUserSelectAssignees(ProcessInstance processInstance) { + return processInstance != null ? getStartUserSelectAssignees(processInstance.getProcessVariables()) : null; + } + + /** + * 获得流程实例的发起用户选择的审批人 Map + * + * @param processVariables 流程变量 + * @return 发起用户选择的审批人 Map + */ + @SuppressWarnings("unchecked") + public static Map> getStartUserSelectAssignees(Map processVariables) { + if (processVariables == null) { + return new HashMap<>(); + } + return (Map>) processVariables.get( + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); + } + + /** + * 获得流程实例的审批用户选择的下一个节点的审批人 Map + * + * @param processInstance 流程实例 + * @return 审批用户选择的下一个节点的审批人Map + */ + public static Map> getApproveUserSelectAssignees(ProcessInstance processInstance) { + return processInstance != null ? getApproveUserSelectAssignees(processInstance.getProcessVariables()) : null; + } + + /** + * 获得流程实例的审批用户选择的下一个节点的审批人 Map + * + * @param processVariables 流程变量 + * @return 审批用户选择的下一个节点的审批人Map Map + */ + @SuppressWarnings("unchecked") + public static Map> getApproveUserSelectAssignees(Map processVariables) { + if (processVariables == null) { + return new HashMap<>(); + } + return (Map>) processVariables.get( + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); + } + + /** + * 获得流程实例的摘要 + * + * 仅有 {@link BpmModelFormTypeEnum#getType()} 表单,才有摘要。 + * 原因是,只有它才有表单项的配置,从而可以根据配置,展示摘要。 + * + * @param processDefinitionInfo 流程定义 + * @param processVariables 流程实例的 variables + * @return 摘要 + */ + public static List> getSummary(BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables) { + // 只有流程表单才会显示摘要! + if (ObjectUtil.isNull(processDefinitionInfo) + || !BpmModelFormTypeEnum.NORMAL.getType().equals(processDefinitionInfo.getFormType())) { + return null; + } + + // 解析表单配置 + Map formFieldsMap = new HashMap<>(); + processDefinitionInfo.getFormFields().forEach(formFieldStr -> { + BpmFormFieldVO formField = JsonUtils.parseObject(formFieldStr, BpmFormFieldVO.class); + if (formField != null) { + formFieldsMap.put(formField.getField(), formField); + } + }); + + // 情况一:当自定义了摘要 + if (ObjectUtil.isNotNull(processDefinitionInfo.getSummarySetting()) + && Boolean.TRUE.equals(processDefinitionInfo.getSummarySetting().getEnable())) { + return convertList(processDefinitionInfo.getSummarySetting().getSummary(), item -> { + BpmFormFieldVO formField = formFieldsMap.get(item); + if (formField != null) { + return new KeyValue(formField.getTitle(), + processVariables.getOrDefault(item, "").toString()); + } + return null; + }); + } + + // 情况二:默认摘要展示前三个表单字段 + return formFieldsMap.entrySet().stream() + .limit(3) + .map(entry -> new KeyValue<>(entry.getValue().getTitle(), + MapUtil.getStr(processVariables, entry.getValue().getField(), ""))) + .collect(Collectors.toList()); + } + + // ========== Task 相关的工具方法 ========== + + /** + * 获得任务的状态 + * + * @param task 任务 + * @return 状态 + */ + public static Integer getTaskStatus(TaskInfo task) { + return (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); + } + + /** + * 获得任务的审批原因 + * + * @param task 任务 + * @return 审批原因 + */ + public static String getTaskReason(TaskInfo task) { + return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_REASON); + } + + /** + * 获得任务的签名图片 URL + * + * @param task 任务 + * @return 签名图片 URL + */ + public static String getTaskSignPicUrl(TaskInfo task) { + return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_SIGN_PIC_URL); + } + + /** + * 获得任务的表单 + * + * @param task 任务 + * @return 表单 + */ + public static Map getTaskFormVariable(TaskInfo task) { + Map formVariables = new HashMap<>(task.getTaskLocalVariables()); + filterTaskFormVariable(formVariables); + return formVariables; + } + + /** + * 过滤任务的表单 + * + * 为什么要过滤?目前使用 taskLocalVariables 存储所有任务的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示 + * + * @param taskLocalVariables 任务的 taskLocalVariables + * @return 过滤后的表单 + */ + public static Map filterTaskFormVariable(Map taskLocalVariables) { + taskLocalVariables.remove(BpmnVariableConstants.TASK_VARIABLE_STATUS); + taskLocalVariables.remove(BpmnVariableConstants.TASK_VARIABLE_REASON); + return taskLocalVariables; + } + + // ========== Expression 相关的工具方法 ========== + + private static Object getExpressionValue(VariableContainer variableContainer, String expressionString, + ProcessEngineConfigurationImpl processEngineConfiguration) { + assert processEngineConfiguration != null; + ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager(); + assert expressionManager != null; + Expression expression = expressionManager.createExpression(expressionString); + return expression.getValue(variableContainer); + } + + public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) { + ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); + if (processEngineConfiguration != null) { + return getExpressionValue(variableContainer, expressionString, processEngineConfiguration); + } + // 如果 ProcessEngineConfigurationImpl 获取不到,则需要通过 ManagementService 来获取 + ManagementService managementService = SpringUtil.getBean(ManagementService.class); + assert managementService != null; + return managementService.executeCommand(context -> + getExpressionValue(variableContainer, expressionString, CommandContextUtil.getProcessEngineConfiguration())); + } + + public static Object getExpressionValue(Map variable, String expressionString) { + VariableContainer variableContainer = new MapDelegateVariableContainer(variable, VariableContainer.empty()); + return getExpressionValue(variableContainer, expressionString); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java new file mode 100644 index 0000000..3f2408d --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -0,0 +1,1007 @@ +package com.zt.plat.module.bpm.framework.flowable.core.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.*; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; +import com.zt.plat.module.bpm.enums.definition.*; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; +import com.zt.plat.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; +import com.zt.plat.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate; +import com.zt.plat.module.bpm.service.task.listener.BpmCallActivityListener; +import com.zt.plat.module.bpm.service.task.listener.BpmUserTaskListener; +import org.flowable.bpmn.BpmnAutoLayout; +import org.flowable.bpmn.constants.BpmnXMLConstants; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; +import org.flowable.engine.delegate.ExecutionListener; +import org.flowable.engine.delegate.TaskListener; + +import java.util.*; + +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; +import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; +import static java.util.Arrays.asList; + +/** + * 仿钉钉/飞书的模型相关的工具方法 + *

+ * 1. 核心的逻辑实现,可见 {@link #buildBpmnModel(String, String, BpmSimpleModelNodeVO)} 方法 + * 2. 所有的 BpmSimpleModelNodeVO 转换成 BPMN FlowNode 元素,可见 {@link NodeConvert} 实现类 + * + * @author jason + */ +public class SimpleModelUtils { + + private static final Map NODE_CONVERTS = MapUtil.newHashMap(); + + static { + List converts = asList(new StartNodeConvert(), new EndNodeConvert(), + new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(), + new DelayTimerNodeConvert(), new TriggerNodeConvert(), + new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(), + new ChildProcessConvert()); + converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); + } + + /** + * 仿钉钉流程设计模型数据结构(json)转换成 Bpmn Model + *

+ * 整体逻辑如下: + * 1. 创建:BpmnModel、Process 对象 + * 2. 转换:将 BpmSimpleModelNodeVO 转换成 BPMN FlowNode 元素 + * 3. 连接:构建并添加节点之间的连线 Sequence Flow + * + * @param processId 流程标识 + * @param processName 流程名称 + * @param simpleModelNode 仿钉钉流程设计模型数据结构 + * @return Bpmn Model + */ + public static BpmnModel buildBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) { + // 1. 创建 BpmnModel + BpmnModel bpmnModel = new BpmnModel(); + bpmnModel.setTargetNamespace(BpmnXMLConstants.BPMN2_NAMESPACE); // 设置命名空间。不加这个,解析 Message 会报 NPE 异常 + // 创建 Process 对象 + Process process = new Process(); + process.setId(processId); + process.setName(processName); + process.setExecutable(Boolean.TRUE); + bpmnModel.addProcess(process); + + // 2.1 创建 StartNode 节点 + // 原因是:目前前端的第一个节点是“发起人节点”,所以这里构建一个 StartNode,用于创建 Bpmn 的 StartEvent 节点 + BpmSimpleModelNodeVO startNode = buildStartNode(); + startNode.setChildNode(simpleModelNode); + // 2.2 将前端传递的 simpleModelNode 数据结构(json),转换成从 BPMN FlowNode 元素,并添加到 Main Process 中 + traverseNodeToBuildFlowNode(startNode, process); + + // 3. 构建并添加节点之间的连线 Sequence Flow + EndEvent endEvent = getEndEvent(bpmnModel); + traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId()); + + // 4. 自动布局 + new BpmnAutoLayout(bpmnModel).execute(); + return bpmnModel; + } + + private static BpmSimpleModelNodeVO buildStartNode() { + return new BpmSimpleModelNodeVO().setId(START_EVENT_NODE_ID) + .setName(BpmSimpleModelNodeTypeEnum.START_NODE.getName()) + .setType(BpmSimpleModelNodeTypeEnum.START_NODE.getType()); + } + + /** + * 遍历节点,构建 FlowNode 元素 + * + * @param node SIMPLE 节点 + * @param process BPMN 流程 + */ + private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) { + // 1. 判断是否有效节点 + if (!isValidNode(node)) { + return; + } + BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType()); + Assert.notNull(nodeType, "模型节点类型({})不支持", node.getType()); + + // 2. 处理当前节点 + NodeConvert nodeConvert = NODE_CONVERTS.get(nodeType); + Assert.notNull(nodeConvert, "模型节点类型的转换器({})不存在", node.getType()); + List flowElements = nodeConvert.convertList(node); + flowElements.forEach(process::addFlowElement); + + // 3.1 情况一:如果当前是分支节点,并且存在条件节点,则处理每个条件的子节点 + if (BpmSimpleModelNodeTypeEnum.isBranchNode(node.getType()) + && CollUtil.isNotEmpty(node.getConditionNodes())) { + // 注意:这里的 item.getChildNode() 处理的是每个条件的子节点,不是处理条件 + node.getConditionNodes().forEach(item -> traverseNodeToBuildFlowNode(item.getChildNode(), process)); + } + + // 3.2 情况二:如果有“子”节点,则递归处理子节点 + traverseNodeToBuildFlowNode(node.getChildNode(), process); + } + + /** + * 遍历节点,构建 SequenceFlow 元素 + * + * @param process Bpmn 流程 + * @param node 当前节点 + * @param targetNodeId 目标节点 ID + */ + private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { + // 1.1 无效节点返回 + if (!isValidNode(node)) { + return; + } + // 1.2 END_NODE 直接返回 + BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType()); + Assert.notNull(nodeType, "模型节点类型不支持"); + if (nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { + return; + } + + // 2.1 情况一:普通节点 + if (!BpmSimpleModelNodeTypeEnum.isBranchNode(node.getType())) { + traverseNormalNodeToBuildSequenceFlow(process, node, targetNodeId); + } else { + // 2.2 情况二:分支节点 + traverseBranchNodeToBuildSequenceFlow(process, node, targetNodeId); + } + } + + /** + * 遍历普通(非条件)节点,构建 SequenceFlow 元素 + * + * @param process Bpmn 流程 + * @param node 当前节点 + * @param targetNodeId 目标节点 ID + */ + private static void traverseNormalNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { + BpmSimpleModelNodeVO childNode = node.getChildNode(); + boolean isChildNodeValid = isValidNode(childNode); + // 情况一:有“子”节点,则建立连线 + // 情况二:没有“子节点”,则直接跟 targetNodeId 建立连线。例如说,结束节点、条件分支(分支节点的孩子节点或聚合节点)的最后一个节点 + String finalTargetNodeId = isChildNodeValid ? childNode.getId() : targetNodeId; + + // 如果没有附加节点:则直接建立连线 + if (StrUtil.isEmpty(node.getAttachNodeId())) { + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId); + process.addFlowElement(sequenceFlow); + } else { + // 如果有附加节点:需要先建立和附加节点的连线,再建立附加节点和目标节点的连线。例如说,触发器节点(HTTP 回调) + List sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), finalTargetNodeId); + sequenceFlows.forEach(process::addFlowElement); + } + + // 因为有子节点,递归调用后续子节点 + if (isChildNodeValid) { + traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId); + } + } + + /** + * 构建有附加节点的连线 + * + * @param nodeId 当前节点 ID + * @param attachNodeId 附属节点 ID + * @param targetNodeId 目标节点 ID + */ + private static List buildAttachNodeSequenceFlow(String nodeId, String attachNodeId, String targetNodeId) { + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(nodeId, attachNodeId, null, null, null); + SequenceFlow attachSequenceFlow = buildBpmnSequenceFlow(attachNodeId, targetNodeId, null, null, null); + return CollUtil.newArrayList(sequenceFlow, attachSequenceFlow); + } + + /** + * 遍历条件节点,构建 SequenceFlow 元素 + * + * @param process Bpmn 流程 + * @param node 当前节点 + * @param targetNodeId 目标节点 ID + */ + private static void traverseBranchNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { + BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType()); + BpmSimpleModelNodeVO childNode = node.getChildNode(); + List conditionNodes = node.getConditionNodes(); + // TODO @芋艿 路由分支没有conditionNodes 这里注释会影响吗?@jason:一起帮忙瞅瞅! +// Assert.notEmpty(conditionNodes, "分支节点的条件节点不能为空"); + // 分支终点节点 ID + String branchEndNodeId = null; + if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) { // 条件分支或路由分支 + // 分两种情况 1. 分支节点有孩子节点为孩子节点 Id 2. 分支节点孩子为无效节点时 (分支嵌套且为分支最后一个节点) 为分支终点节点 ID + branchEndNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; + } else if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { // 并行分支或包容分支 + // 分支节点:分支终点节点 Id 为程序创建的网关集合节点。目前不会从前端传入。 + branchEndNodeId = buildGatewayJoinId(node.getId()); + } + Assert.notEmpty(branchEndNodeId, "分支终点节点 Id 不能为空"); + + // 3. 遍历分支节点 + if (nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) { + // 路由分支遍历 + for (BpmSimpleModelNodeVO.RouterSetting router : node.getRouterGroups()) { + SequenceFlow sequenceFlow = RouteBranchNodeConvert.buildSequenceFlow(node.getId(), router); + process.addFlowElement(sequenceFlow); + } + } else { + // 下面的注释,以如下情况举例子。分支 1:A->B->C->D->E,分支 2:A->D->E。其中,A 为分支节点, D 为 A 孩子节点 + for (BpmSimpleModelNodeVO item : conditionNodes) { + Assert.isTrue(Objects.equals(item.getType(), BpmSimpleModelNodeTypeEnum.CONDITION_NODE.getType()), + "条件节点类型({})不符合", item.getType()); + BpmSimpleModelNodeVO conditionChildNode = item.getChildNode(); + // 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况 + if (isValidNode(conditionChildNode)) { + // 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线 + SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), item); + process.addFlowElement(sequenceFlow); + // 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线 + traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId); + } else { + // 3.2 分支没有后续节点。例如说,建立 A->D 的连线 + SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, item); + process.addFlowElement(sequenceFlow); + } + } + } + + // 4.1 如果是并行分支、包容分支,由于是程序创建的聚合网关,需要手工创建聚合网关和下一个节点的连线 + if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { + String nextNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId); + process.addFlowElement(sequenceFlow); + // 4.2 如果是路由分支,需要连接后续节点为默认路由 + } else if (nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) { + SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, node.getRouterDefaultFlowId(), + null, null); + process.addFlowElement(sequenceFlow); + } + + // 5. 递归调用后续节点 继续递归。例如说,建立 D->E 的连线 + traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId); + } + + private static SequenceFlow buildBpmnSequenceFlow(String sourceId, String targetId) { + return buildBpmnSequenceFlow(sourceId, targetId, null, null, null); + } + + private static SequenceFlow buildBpmnSequenceFlow(String sourceId, String targetId, + String sequenceFlowId, String sequenceFlowName, + String conditionExpression) { + Assert.notEmpty(sourceId, "sourceId 不能为空"); + Assert.notEmpty(targetId, "targetId 不能为空"); + // TODO @jason:如果 sequenceFlowId 不存在的时候,是不是要生成一个默认的 sequenceFlowId? @芋艿: 貌似不需要,Flowable 会默认生成;TODO @jason:建议还是搞一个,主要是后续好排查问题。 + // TODO @jason:如果 name 不存在的时候,是不是要生成一个默认的 name? @芋艿: 不需要生成默认的吧? 这个会在流程图展示的, 一般用户填写的。不好生成默认的吧;TODO @jason:建议还是搞一个,主要是后续好排查问题。 + SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId); + if (StrUtil.isNotEmpty(sequenceFlowId)) { + sequenceFlow.setId(sequenceFlowId); + } + if (StrUtil.isNotEmpty(sequenceFlowName)) { + sequenceFlow.setName(sequenceFlowName); + } + if (StrUtil.isNotEmpty(conditionExpression)) { + sequenceFlow.setConditionExpression(conditionExpression); + } + return sequenceFlow; + } + + public static boolean isValidNode(BpmSimpleModelNodeVO node) { + return node != null && node.getId() != null; + } + + public static boolean isSequentialApproveNode(BpmSimpleModelNodeVO node) { + return BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType().equals(node.getType()) + && BpmUserTaskApproveMethodEnum.SEQUENTIAL.getMethod().equals(node.getApproveMethod()); + } + + // ========== 各种 convert 节点的方法: BpmSimpleModelNodeVO => BPMN FlowElement ========== + + private interface NodeConvert { + + default List convertList(BpmSimpleModelNodeVO node) { + return Collections.singletonList(convert(node)); + } + + default FlowElement convert(BpmSimpleModelNodeVO node) { + throw new UnsupportedOperationException("请实现该方法"); + } + + BpmSimpleModelNodeTypeEnum getType(); + + } + + private static class StartNodeConvert implements NodeConvert { + + @Override + public StartEvent convert(BpmSimpleModelNodeVO node) { + StartEvent startEvent = new StartEvent(); + startEvent.setId(node.getId()); + startEvent.setName(node.getName()); + return startEvent; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.START_NODE; + } + + } + + private static class EndNodeConvert implements NodeConvert { + + @Override + public EndEvent convert(BpmSimpleModelNodeVO node) { + EndEvent endEvent = new EndEvent(); + endEvent.setId(node.getId()); + endEvent.setName(node.getName()); + // TODO @芋艿 + jason:要不要加一个终止定义? + return endEvent; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.END_NODE; + } + + } + + private static class StartUserNodeConvert implements NodeConvert { + + @Override + public UserTask convert(BpmSimpleModelNodeVO node) { + UserTask userTask = new UserTask(); + userTask.setId(node.getId()); + userTask.setName(node.getName()); + + // 人工审批 + addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, BpmUserTaskApproveTypeEnum.USER.getType()); + // 候选人策略为发起人自己 + addCandidateElements(BpmTaskCandidateStrategyEnum.START_USER.getStrategy(), null, userTask); + // 添加表单字段权限属性元素 + addFormFieldsPermission(node.getFieldsPermission(), userTask); + // 添加操作按钮配置属性元素 + addButtonsSetting(node.getButtonsSetting(), userTask); + // 使用自动通过策略 + // TODO @芋艿 复用了SKIP, 是否需要新加一个策略;TODO @芋艿:【回复】是不是应该类似飞书,搞个草稿状态。待定;还有一种策略,不标记自动通过,而是首次发起后,第一个节点,自动通过; + addAssignStartUserHandlerType(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType(), userTask); + return userTask; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.START_USER_NODE; + } + + } + + private static class ApproveNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + List flowElements = new ArrayList<>(2); + // 1. 构建用户任务 + UserTask userTask = buildBpmnUserTask(node); + flowElements.add(userTask); + + // 2. 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理 + if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) { + BoundaryEvent boundaryEvent = buildUserTaskTimeoutBoundaryEvent(userTask, node.getTimeoutHandler()); + flowElements.add(boundaryEvent); + } + return flowElements; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.APPROVE_NODE; + } + + /** + * 添加 UserTask 用户的审批超时 BoundaryEvent 事件 + * + * @param userTask 审批任务 + * @param timeoutHandler 超时处理器 + * @return BoundaryEvent 超时事件 + */ + private BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask, + BpmSimpleModelNodeVO.TimeoutHandler timeoutHandler) { + // 1. 创建 Timeout Boundary Event + String timeCycle = null; + if (Objects.equals(BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType(), timeoutHandler.getType()) && + timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) { + timeCycle = String.format("R%d/%s", + timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration()); + } + BoundaryEvent boundaryEvent = buildTimeoutBoundaryEvent(userTask, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT.getType(), + timeoutHandler.getTimeDuration(), timeCycle, null); + + // 2 添加超时执行动作元素 + addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, timeoutHandler.getType()); + return boundaryEvent; + } + + private UserTask buildBpmnUserTask(BpmSimpleModelNodeVO node) { + UserTask userTask = new UserTask(); + userTask.setId(node.getId()); + userTask.setName(node.getName()); + + // 如果不是审批人节点,则直接返回 + addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, node.getApproveType()); + if (ObjectUtil.notEqual(node.getApproveType(), BpmUserTaskApproveTypeEnum.USER.getType())) { + return userTask; + } + + // 添加候选人元素 + addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask); + // 添加表单字段权限属性元素 + addFormFieldsPermission(node.getFieldsPermission(), userTask); + // 添加操作按钮配置属性元素 + addButtonsSetting(node.getButtonsSetting(), userTask); + // 处理多实例(审批方式) + processMultiInstanceLoopCharacteristics(node.getApproveMethod(), node.getApproveRatio(), userTask); + // 添加任务被拒绝的处理元素 + addTaskRejectElements(node.getRejectHandler(), userTask); + // 添加用户任务的审批人与发起人相同时的处理元素 + addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask); + // 添加用户任务的空处理元素 + addAssignEmptyHandlerType(node.getAssignEmptyHandler(), userTask); + // 设置审批任务的截止时间 + if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) { + userTask.setDueDate(node.getTimeoutHandler().getTimeDuration()); + } + // 设置监听器 + addUserTaskListener(node, userTask); + // 添加是否需要签名 + addSignEnable(node.getSignEnable(), userTask); + // 审批意见 + addReasonRequire(node.getReasonRequire(), userTask); + // 节点类型 + addNodeType(node.getType(), userTask); + return userTask; + } + + private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) { + List flowableListeners = new ArrayList<>(3); + if (node.getTaskCreateListener() != null + && Boolean.TRUE.equals(node.getTaskCreateListener().getEnable())) { + FlowableListener flowableListener = new FlowableListener(); + flowableListener.setEvent(TaskListener.EVENTNAME_CREATE); + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); + addListenerConfig(flowableListener, node.getTaskCreateListener()); + flowableListeners.add(flowableListener); + } + if (node.getTaskAssignListener() != null + && Boolean.TRUE.equals(node.getTaskAssignListener().getEnable())) { + FlowableListener flowableListener = new FlowableListener(); + flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT); + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); + addListenerConfig(flowableListener, node.getTaskAssignListener()); + flowableListeners.add(flowableListener); + } + if (node.getTaskCompleteListener() != null + && Boolean.TRUE.equals(node.getTaskCompleteListener().getEnable())) { + FlowableListener flowableListener = new FlowableListener(); + flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE); + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); + addListenerConfig(flowableListener, node.getTaskCompleteListener()); + flowableListeners.add(flowableListener); + } + if (CollUtil.isNotEmpty(flowableListeners)) { + userTask.setTaskListeners(flowableListeners); + } + } + + private void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) { + BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod); + Assert.notNull(approveMethodEnum, "审批方式({})不能为空", approveMethodEnum); + // 添加审批方式的扩展属性 + addExtensionElement(userTask, USER_TASK_APPROVE_METHOD, approveMethod); + if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RANDOM) { + // 随机审批,不需要设置多实例属性 + return; + } + + // 处理多实例审批方式 + MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); + // 设置 collectionVariable。本系统用不到,仅仅为了 Flowable 校验不报错 + multiInstanceCharacteristics.setInputDataItem("${coll_userList}"); + if (approveMethodEnum == BpmUserTaskApproveMethodEnum.ANY) { + multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition()); + multiInstanceCharacteristics.setSequential(false); + } else if (approveMethodEnum == BpmUserTaskApproveMethodEnum.SEQUENTIAL) { + multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition()); + multiInstanceCharacteristics.setSequential(true); + multiInstanceCharacteristics.setLoopCardinality("1"); + } else if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RATIO) { + Assert.notNull(approveRatio, "通过比例不能为空"); + multiInstanceCharacteristics.setCompletionCondition( + String.format(approveMethodEnum.getCompletionCondition(), String.format("%.2f", approveRatio / 100D))); + multiInstanceCharacteristics.setSequential(false); + } + userTask.setLoopCharacteristics(multiInstanceCharacteristics); + } + + } + + private static class TransactorNodeConvert extends ApproveNodeConvert { + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE; + } + + } + + private static class CopyNodeConvert implements NodeConvert { + + @Override + public ServiceTask convert(BpmSimpleModelNodeVO node) { + ServiceTask serviceTask = new ServiceTask(); + serviceTask.setId(node.getId()); + serviceTask.setName(node.getName()); + serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + serviceTask.setImplementation("${" + BpmCopyTaskDelegate.BEAN_NAME + "}"); + + // 添加抄送候选人元素 + addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), serviceTask); + // 添加表单字段权限属性元素 + addFormFieldsPermission(node.getFieldsPermission(), serviceTask); + return serviceTask; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.COPY_NODE; + } + + } + + private static class ConditionBranchNodeConvert implements NodeConvert { + + @Override + public ExclusiveGateway convert(BpmSimpleModelNodeVO node) { + ExclusiveGateway exclusiveGateway = new ExclusiveGateway(); + exclusiveGateway.setId(node.getId()); + // TODO @jason:setName + + // 设置默认的序列流(条件) + BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(), + item -> BooleanUtil.isTrue(item.getConditionSetting().getDefaultFlow())); + Assert.notNull(defaultSeqFlow, "条件分支节点({})的默认序列流不能为空", node.getId()); + exclusiveGateway.setDefaultFlow(defaultSeqFlow.getId()); + return exclusiveGateway; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE; + } + + } + + private static class ParallelBranchNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + ParallelGateway parallelGateway = new ParallelGateway(); + parallelGateway.setId(node.getId()); + // TODO @jason:setName + + // 并行聚合网关由程序创建,前端不需要传入 + ParallelGateway joinParallelGateway = new ParallelGateway(); + joinParallelGateway.setId(buildGatewayJoinId(node.getId())); + // TODO @jason:setName + return CollUtil.newArrayList(parallelGateway, joinParallelGateway); + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE; + } + + } + + private static class InclusiveBranchNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + InclusiveGateway inclusiveGateway = new InclusiveGateway(); + inclusiveGateway.setId(node.getId()); + // 设置默认的序列流(条件) + BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(), + item -> BooleanUtil.isTrue(item.getConditionSetting().getDefaultFlow())); + Assert.notNull(defaultSeqFlow, "包容分支节点({})的默认序列流不能为空", node.getId()); + inclusiveGateway.setDefaultFlow(defaultSeqFlow.getId()); + // TODO @jason:setName + + // 并行聚合网关由程序创建,前端不需要传入 + InclusiveGateway joinInclusiveGateway = new InclusiveGateway(); + joinInclusiveGateway.setId(buildGatewayJoinId(node.getId())); + // TODO @jason:setName + return CollUtil.newArrayList(inclusiveGateway, joinInclusiveGateway); + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE; + } + + } + + public static class ConditionNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + // 原因是:正常情况下,它不会被调用到 + throw new UnsupportedOperationException("条件节点不支持转换"); + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.CONDITION_NODE; + } + + public static SequenceFlow buildSequenceFlow(String sourceId, String targetId, + BpmSimpleModelNodeVO node) { + String conditionExpression = buildConditionExpression(node.getConditionSetting()); + return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression); + } + } + + /** + * 构造条件表达式 + */ + public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { + // 并行网关不需要设置条件 + if (conditionSetting == null) { + return null; + } + return buildConditionExpression(conditionSetting.getConditionType(), conditionSetting.getConditionExpression(), + conditionSetting.getConditionGroups()); + } + + public static String buildConditionExpression(BpmSimpleModelNodeVO.RouterSetting routerSetting) { + return buildConditionExpression(routerSetting.getConditionType(), routerSetting.getConditionExpression(), + routerSetting.getConditionGroups()); + } + + public static String buildConditionExpression(Integer conditionType, String conditionExpression, ConditionGroups conditionGroups) { + BpmSimpleModeConditionTypeEnum conditionTypeEnum = BpmSimpleModeConditionTypeEnum.valueOf(conditionType); + if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.EXPRESSION) { + return conditionExpression; + } + if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.RULE) { + if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) { + return null; + } + List strConditionGroups = CollectionUtils.convertList(conditionGroups.getConditions(), item -> { + if (CollUtil.isEmpty(item.getRules())) { + return ""; + } + // 构造规则表达式 + List list = CollectionUtils.convertList(item.getRules(), (rule) -> { + String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide() + : "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号 + return String.format(" %s %s var:convertByType(%s,%s)", rule.getLeftSide(), rule.getOpCode(), rule.getLeftSide(), rightSide); + }); + // 构造条件组的表达式 + Boolean and = item.getAnd(); + return "(" + CollUtil.join(list, and ? " && " : " || ") + ")"; + }); + return String.format("${%s}", CollUtil.join(strConditionGroups, conditionGroups.getAnd() ? " && " : " || ")); + } + return null; + } + + public static class DelayTimerNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + List flowElements = new ArrayList<>(2); + // 1. 构建接收任务,通过接收任务可卡住节点 + ReceiveTask receiveTask = new ReceiveTask(); + receiveTask.setId(node.getId()); + receiveTask.setName(node.getName()); + flowElements.add(receiveTask); + + // 2. 添加接收任务的 Timer Boundary Event + if (node.getDelaySetting() != null) { + BoundaryEvent boundaryEvent = null; + if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { + boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), + node.getDelaySetting().getDelayTime(), null, null); + } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { + boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), + null, null, node.getDelaySetting().getDelayTime()); + } else { + throw new UnsupportedOperationException("不支持的延迟类型:" + node.getDelaySetting()); + } + flowElements.add(boundaryEvent); + } + return flowElements; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.DELAY_TIMER_NODE; + } + } + + public static class TriggerNodeConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + Assert.notNull(node.getTriggerSetting(), "触发器节点设置不能为空"); + List flowElements = new ArrayList<>(2); + // HTTP 回调请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 + if (BpmTriggerTypeEnum.HTTP_CALLBACK.getType().equals(node.getTriggerSetting().getType())) { + Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 回调请求设置不能为空"); + ReceiveTask receiveTask = new ReceiveTask(); + receiveTask.setId("Activity_" + IdUtil.fastUUID()); + receiveTask.setName("HTTP 回调"); + node.setAttachNodeId(receiveTask.getId()); + flowElements.add(receiveTask); + // 重要:设置 callbackTaskDefineKey,用于 HTTP 回调 + node.getTriggerSetting().getHttpRequestSetting().setCallbackTaskDefineKey(receiveTask.getId()); + } + + // 触发器使用 ServiceTask 来实现 + ServiceTask serviceTask = new ServiceTask(); + serviceTask.setId(node.getId()); + serviceTask.setName(node.getName()); + serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + serviceTask.setImplementation("${" + BpmTriggerTaskDelegate.BEAN_NAME + "}"); + addExtensionElement(serviceTask, TRIGGER_TYPE, node.getTriggerSetting().getType()); + if (node.getTriggerSetting().getHttpRequestSetting() != null) { + addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting()); + } + if (node.getTriggerSetting().getFormSettings() != null) { + addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getFormSettings()); + } + flowElements.add(serviceTask); + return flowElements; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.TRIGGER_NODE; + } + } + + public static class RouteBranchNodeConvert implements NodeConvert { + + @Override + public ExclusiveGateway convert(BpmSimpleModelNodeVO node) { + ExclusiveGateway exclusiveGateway = new ExclusiveGateway(); + exclusiveGateway.setId(node.getId()); + + // 设置默认的序列流(条件) + node.setRouterDefaultFlowId("Flow_" + IdUtil.fastUUID()); + exclusiveGateway.setDefaultFlow(node.getRouterDefaultFlowId()); + return exclusiveGateway; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE; + } + + public static SequenceFlow buildSequenceFlow(String nodeId, BpmSimpleModelNodeVO.RouterSetting router) { + String conditionExpression = SimpleModelUtils.buildConditionExpression(router); + return buildBpmnSequenceFlow(nodeId, router.getNodeId(), null, null, conditionExpression); + } + + } + + private static class ChildProcessConvert implements NodeConvert { + + @Override + public List convertList(BpmSimpleModelNodeVO node) { + List flowElements = new ArrayList<>(2); + BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting(); + List inVariables = childProcessSetting.getInVariables() == null ? + new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariables()); + CallActivity callActivity = new CallActivity(); + callActivity.setId(node.getId()); + callActivity.setName(node.getName()); + callActivity.setCalledElementType("key"); + // 1. 是否异步 + if (node.getChildProcessSetting().getAsync()) { + callActivity.setAsynchronous(true); + } + + // 2. 调用的子流程 + callActivity.setCalledElement(childProcessSetting.getCalledProcessDefinitionKey()); + callActivity.setProcessInstanceName(childProcessSetting.getCalledProcessDefinitionName()); + + // 3. 是否自动跳过子流程发起节点 + IOParameter ioParameter = new IOParameter(); + ioParameter.setSourceExpression(childProcessSetting.getSkipStartUserNode().toString()); + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); + inVariables.add(ioParameter); + + // 4. 【默认需要传递的一些变量】流程状态 + ioParameter = new IOParameter(); + ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + inVariables.add(ioParameter); + + // 5. 主→子变量传递、子->主变量传递 + callActivity.setInParameters(inVariables); + if (ArrayUtil.isNotEmpty(childProcessSetting.getOutVariables()) && ObjUtil.notEqual(childProcessSetting.getAsync(), Boolean.TRUE)) { + callActivity.setOutParameters(childProcessSetting.getOutVariables()); + } + + // 6. 子流程发起人配置 + List executionListeners = new ArrayList<>(); + FlowableListener flowableListener = new FlowableListener(); + flowableListener.setEvent(ExecutionListener.EVENTNAME_START); + flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); + flowableListener.setImplementation(BpmCallActivityListener.DELEGATE_EXPRESSION); + FieldExtension fieldExtension = new FieldExtension(); + fieldExtension.setFieldName("listenerConfig"); + fieldExtension.setStringValue(JsonUtils.toJsonString(childProcessSetting.getStartUserSetting())); + flowableListener.getFieldExtensions().add(fieldExtension); + executionListeners.add(flowableListener); + callActivity.setExecutionListeners(executionListeners); + + // 7. 超时设置 + if (childProcessSetting.getTimeoutSetting() != null && Boolean.TRUE.equals(childProcessSetting.getTimeoutSetting().getEnable())) { + BoundaryEvent boundaryEvent = null; + if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { + boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), + childProcessSetting.getTimeoutSetting().getTimeExpression(), null, null); + } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { + boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType(), + null, null, childProcessSetting.getTimeoutSetting().getTimeExpression()); + } + flowElements.add(boundaryEvent); + } + + // 8. 多实例 + if (childProcessSetting.getMultiInstanceSetting() != null && Boolean.TRUE.equals(childProcessSetting.getMultiInstanceSetting().getEnable())) { + MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); + multiInstanceCharacteristics.setSequential(childProcessSetting.getMultiInstanceSetting().getSequential()); + if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY.getType())) { + multiInstanceCharacteristics.setLoopCardinality(childProcessSetting.getMultiInstanceSetting().getSource()); + } + if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType()) || + childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { + multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource()); + } + multiInstanceCharacteristics.setCompletionCondition(String.format(BpmUserTaskApproveMethodEnum.RATIO.getCompletionCondition(), + String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getApproveRatio() / 100D))); + callActivity.setLoopCharacteristics(multiInstanceCharacteristics); + addExtensionElement(callActivity, CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE, childProcessSetting.getMultiInstanceSetting().getSourceType()); + } + + // 添加节点类型 + addNodeType(node.getType(), callActivity); + flowElements.add(callActivity); + return flowElements; + } + + @Override + public BpmSimpleModelNodeTypeEnum getType() { + return BpmSimpleModelNodeTypeEnum.CHILD_PROCESS; + } + + } + + private static String buildGatewayJoinId(String id) { + return id + "_join"; + } + + private static BoundaryEvent buildTimeoutBoundaryEvent(Activity attachedToRef, Integer type, + String timeDuration, String timeCycle, String timeDate) { + // 1.1 定时器边界事件 + BoundaryEvent boundaryEvent = new BoundaryEvent(); + boundaryEvent.setId("Event-" + IdUtil.fastUUID()); + boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断 + boundaryEvent.setAttachedToRef(attachedToRef); + // 1.2 定义超时时间表达式 + TimerEventDefinition eventDefinition = new TimerEventDefinition(); + if (ObjUtil.isNotNull(timeDuration)) { + eventDefinition.setTimeDuration(timeDuration); + } + if (ObjUtil.isNotNull(timeDuration)) { + eventDefinition.setTimeCycle(timeCycle); + } + if (ObjUtil.isNotNull(timeDate)) { + eventDefinition.setTimeDate(timeDate); + } + boundaryEvent.addEventDefinition(eventDefinition); + + // 2. 添加定时器边界事件类型 + addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, type); + return boundaryEvent; + } + + // ========== SIMPLE 流程预测相关的方法 ========== + + public static List simulateProcess(BpmSimpleModelNodeVO rootNode, Map variables) { + List resultNodes = new ArrayList<>(); + + // 从头开始遍历 + simulateNextNode(rootNode, variables, resultNodes); + return resultNodes; + } + + private static void simulateNextNode(BpmSimpleModelNodeVO currentNode, Map variables, + List resultNodes) { + // 如果不合法(包括为空),则直接结束 + if (!isValidNode(currentNode)) { + return; + } + BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(currentNode.getType()); + Assert.notNull(nodeType, "模型节点类型不支持"); + + // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE/TRANSACTOR_NODE + if (nodeType == BpmSimpleModelNodeTypeEnum.START_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.START_USER_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE + || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS + || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { + // 添加元素 + resultNodes.add(currentNode); + } + + // 情况:CONDITION_BRANCH_NODE 排它,只有一个满足条件的。如果没有,就走默认的 + if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) { + // 查找满足条件的 BpmSimpleModelNodeVO 节点 + BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) + && evalConditionExpress(variables, conditionNode.getConditionSetting())); + if (matchConditionNode == null) { + matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), + conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); + } + Assert.notNull(matchConditionNode, "找不到条件节点({})", currentNode); + // 遍历满足条件的 BpmSimpleModelNodeVO 节点 + simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes); + } + + // 情况:INCLUSIVE_BRANCH_NODE 包容,多个满足条件的。如果没有,就走默认的 + if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { + // 查找满足条件的 BpmSimpleModelNodeVO 节点 + Collection matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), + conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) + && evalConditionExpress(variables, conditionNode.getConditionSetting())); + if (CollUtil.isEmpty(matchConditionNodes)) { + matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), + conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); + } + Assert.isTrue(!matchConditionNodes.isEmpty(), "找不到条件节点({})", currentNode); + // 遍历满足条件的 BpmSimpleModelNodeVO 节点 + matchConditionNodes.forEach(matchConditionNode -> + simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes)); + } + + // 情况:PARALLEL_BRANCH_NODE 并行,都满足,都走 + if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE) { + // 遍历所有 BpmSimpleModelNodeVO 节点 + currentNode.getConditionNodes().forEach(matchConditionNode -> + simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes)); + } + + // 遍历子节点 + simulateNextNode(currentNode.getChildNode(), variables, resultNodes); + } + + public static boolean evalConditionExpress(Map variables, BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { + return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting)); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java new file mode 100644 index 0000000..73f8350 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java @@ -0,0 +1,6 @@ +/** + * 属于 bpm 模块的 framework 封装 + * + * @author ZT + */ +package com.zt.plat.module.bpm.framework; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java new file mode 100644 index 0000000..0be19c0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java @@ -0,0 +1,17 @@ +package com.zt.plat.module.bpm.framework.rpc.config; + +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.PostApi; +import com.zt.plat.module.system.api.dict.DictDataApi; +import com.zt.plat.module.system.api.permission.PermissionApi; +import com.zt.plat.module.system.api.permission.RoleApi; +import com.zt.plat.module.system.api.sms.SmsSendApi; +import com.zt.plat.module.system.api.user.AdminUserApi; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +@Configuration(value = "bpmRpcConfiguration", proxyBeanMethods = false) +@EnableFeignClients(clients = {RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, + PermissionApi.class}) +public class RpcConfiguration { +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java new file mode 100644 index 0000000..4a733c4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package com.zt.plat.module.bpm.framework.rpc; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java new file mode 100644 index 0000000..eb6e099 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java @@ -0,0 +1,40 @@ +package com.zt.plat.module.bpm.framework.security.config; + +import com.zt.plat.framework.security.config.AuthorizeRequestsCustomizer; +import com.zt.plat.module.bpm.enums.ApiConstants; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; + +/** + * Bpm 模块的 Security 配置 + */ +@Configuration(proxyBeanMethods = false, value = "bpmSecurityConfiguration") +public class SecurityConfiguration { + + @Bean("bpmAuthorizeRequestsCustomizer") + public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { + return new AuthorizeRequestsCustomizer() { + + @Override + public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { + // TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案 + // Swagger 接口文档 + registry.requestMatchers("/v3/api-docs/**").permitAll() + .requestMatchers("/webjars/**").permitAll() + .requestMatchers("/swagger-ui").permitAll() + .requestMatchers("/swagger-ui/**").permitAll(); + // Druid 监控 + registry.requestMatchers("/druid/**").permitAll(); + // Spring Boot Actuator 的安全配置 + registry.requestMatchers("/actuator").permitAll() + .requestMatchers("/actuator/**").permitAll(); + // RPC 服务的安全配置 + registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); + } + + }; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java new file mode 100644 index 0000000..a637358 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java @@ -0,0 +1,4 @@ +/** + * 占位 + */ +package com.zt.plat.module.bpm.framework.security.core; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java new file mode 100644 index 0000000..e113b8e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java @@ -0,0 +1,30 @@ +package com.zt.plat.module.bpm.framework.web.config; + +import com.zt.plat.framework.common.enums.WebFilterOrderEnum; +import com.zt.plat.framework.swagger.config.CloudSwaggerAutoConfiguration; +import com.zt.plat.module.bpm.framework.web.core.FlowableWebFilter; +import org.springdoc.core.models.GroupedOpenApi; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * bpm 模块的 web 组件的 Configuration + * + * @author ZT + */ +@Configuration(proxyBeanMethods = false) +public class BpmWebConfiguration { + + /** + * 配置 Flowable Web 过滤器 + */ + @Bean + public FilterRegistrationBean flowableWebFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new FlowableWebFilter()); + registrationBean.setOrder(WebFilterOrderEnum.FLOWABLE_FILTER); + return registrationBean; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java new file mode 100644 index 0000000..4699de5 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java @@ -0,0 +1,36 @@ +package com.zt.plat.module.bpm.framework.web.core; + +import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +/** + * Flowable Web 过滤器,将 userId 设置到 {@link org.flowable.common.engine.impl.identity.Authentication} 中 + * + * @author jason + */ +public class FlowableWebFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException { + try { + // 设置工作流的用户 + Long userId = SecurityFrameworkUtils.getLoginUserId(); + if (userId != null) { + FlowableUtils.setAuthenticatedUserId(userId); + } + // 过滤 + chain.doFilter(request, response); + } finally { + // 清理 + FlowableUtils.clearAuthenticatedUserId(); + } + } +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java new file mode 100644 index 0000000..62601dc --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java @@ -0,0 +1,4 @@ +/** + * bpm 模块的 web 配置 + */ +package com.zt.plat.module.bpm.framework.web; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java new file mode 100644 index 0000000..4039eb8 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java @@ -0,0 +1,12 @@ +/** + * bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能,基于 Flowable 6 版本实现。 + * 例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等 + * + * bpm 解释:https://baike.baidu.com/item/BPM/1933 + * + * 1. Controller URL:以 /bpm/ 开头,避免和其它 Module 冲突 + * 2. DataObject 表名:以 bpm_ 开头,方便在数据库中区分 + * + * 注意,由于 Bpm 模块下,容易和其它模块重名,所以类名都加载 Bpm 的前缀~ + */ +package com.zt.plat.module.bpm; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java new file mode 100644 index 0000000..6e24f44 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java @@ -0,0 +1,92 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * BPM 流程分类 Service 接口 + * + * @author ZT + */ +public interface BpmCategoryService { + + /** + * 创建流程分类 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createCategory(@Valid BpmCategorySaveReqVO createReqVO); + + /** + * 更新流程分类 + * + * @param updateReqVO 更新信息 + */ + void updateCategory(@Valid BpmCategorySaveReqVO updateReqVO); + + /** + * 删除流程分类 + * + * @param id 编号 + */ + void deleteCategory(Long id); + + /** + * 获得流程分类 + * + * @param id 编号 + * @return BPM 流程分类 + */ + BpmCategoryDO getCategory(Long id); + + /** + * 获得流程分类分页 + * + * @param pageReqVO 分页查询 + * @return 流程分类分页 + */ + PageResult getCategoryPage(BpmCategoryPageReqVO pageReqVO); + + /** + * 获得流程分类 Map,基于指定编码 + * + * @param codes 编号数组 + * @return 流程分类 Map + */ + default Map getCategoryMap(Collection codes) { + return convertMap(getCategoryListByCode(codes), BpmCategoryDO::getCode); + } + + /** + * 获得流程分类列表,基于指定编码 + * + * @return 流程分类列表 + */ + List getCategoryListByCode(Collection codes); + + /** + * 获得流程分类列表,基于指定状态 + * + * @param status 状态 + * @return 流程分类列表 + */ + List getCategoryListByStatus(Integer status); + + /** + * 批量更新流程分类的排序:每个分类的 sort 值,从 0 开始递增 + * + * @param ids 分类编号列表 + */ + void updateCategorySortBatch(List ids); + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java new file mode 100644 index 0000000..d111015 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java @@ -0,0 +1,130 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.mysql.category.BpmCategoryMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; + +/** + * BPM 流程分类 Service 实现类 + * + * @author ZT + */ +@Service +@Validated +public class BpmCategoryServiceImpl implements BpmCategoryService { + + @Resource + private BpmCategoryMapper bpmCategoryMapper; + + @Override + public Long createCategory(BpmCategorySaveReqVO createReqVO) { + // 校验唯一 + validateCategoryNameUnique(createReqVO); + validateCategoryCodeUnique(createReqVO); + // 插入 + BpmCategoryDO category = BeanUtils.toBean(createReqVO, BpmCategoryDO.class); + bpmCategoryMapper.insert(category); + return category.getId(); + } + + @Override + public void updateCategory(BpmCategorySaveReqVO updateReqVO) { + // 校验存在 + validateCategoryExists(updateReqVO.getId()); + validateCategoryNameUnique(updateReqVO); + validateCategoryCodeUnique(updateReqVO); + // 更新 + BpmCategoryDO updateObj = BeanUtils.toBean(updateReqVO, BpmCategoryDO.class); + bpmCategoryMapper.updateById(updateObj); + } + + private void validateCategoryNameUnique(BpmCategorySaveReqVO updateReqVO) { + BpmCategoryDO category = bpmCategoryMapper.selectByName(updateReqVO.getName()); + if (category == null + || ObjUtil.equal(category.getId(), updateReqVO.getId())) { + return; + } + throw exception(CATEGORY_NAME_DUPLICATE, updateReqVO.getName()); + } + + private void validateCategoryCodeUnique(BpmCategorySaveReqVO updateReqVO) { + BpmCategoryDO category = bpmCategoryMapper.selectByCode(updateReqVO.getCode()); + if (category == null + || ObjUtil.equal(category.getId(), updateReqVO.getId())) { + return; + } + throw exception(CATEGORY_CODE_DUPLICATE, updateReqVO.getCode()); + } + + @Override + public void deleteCategory(Long id) { + // 校验存在 + validateCategoryExists(id); + // 删除 + bpmCategoryMapper.deleteById(id); + } + + private void validateCategoryExists(Long id) { + if (bpmCategoryMapper.selectById(id) == null) { + throw exception(CATEGORY_NOT_EXISTS); + } + } + + @Override + public BpmCategoryDO getCategory(Long id) { + return bpmCategoryMapper.selectById(id); + } + + @Override + public PageResult getCategoryPage(BpmCategoryPageReqVO pageReqVO) { + return bpmCategoryMapper.selectPage(pageReqVO); + } + + @Override + public List getCategoryListByCode(Collection codes) { + if (CollUtil.isEmpty(codes)) { + return Collections.emptyList(); + } + return bpmCategoryMapper.selectListByCode(codes); + } + + @Override + public List getCategoryListByStatus(Integer status) { + return bpmCategoryMapper.selectListByStatus(status); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateCategorySortBatch(List ids) { + // 校验分类都存在 + List categories = bpmCategoryMapper.selectByIds(ids); + if (categories.size() != ids.size()) { + throw exception(CATEGORY_NOT_EXISTS); + } + + // 批量更新排序 + List updateList = IntStream.range(0, ids.size()) + .mapToObj(index -> new BpmCategoryDO().setId(ids.get(index)).setSort(index)) + .collect(Collectors.toList()); + bpmCategoryMapper.updateBatch(updateList); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java new file mode 100644 index 0000000..6a5a8c0 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java @@ -0,0 +1,85 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + + +/** + * 动态表单 Service 接口 + * + * @author @风里雾里 + */ +public interface BpmFormService { + + /** + * 创建动态表单 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createForm(@Valid BpmFormSaveReqVO createReqVO); + + /** + * 更新动态表单 + * + * @param updateReqVO 更新信息 + */ + void updateForm(@Valid BpmFormSaveReqVO updateReqVO); + + /** + * 删除动态表单 + * + * @param id 编号 + */ + void deleteForm(Long id); + + /** + * 获得动态表单 + * + * @param id 编号 + * @return 动态表单 + */ + BpmFormDO getForm(Long id); + + /** + * 获得动态表单列表 + * + * @return 动态表单列表 + */ + List getFormList(); + + /** + * 获得动态表单列表 + * + * @param ids 编号 + * @return 动态表单列表 + */ + List getFormList(Collection ids); + + /** + * 获得动态表单 Map + * + * @param ids 编号 + * @return 动态表单 Map + */ + default Map getFormMap(Collection ids) { + return CollectionUtils.convertMap(this.getFormList(ids), BpmFormDO::getId); + } + + /** + * 获得动态表单分页 + * + * @param pageReqVO 分页查询 + * @return 动态表单分页 + */ + PageResult getFormPage(BpmFormPageReqVO pageReqVO); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java new file mode 100644 index 0000000..73511ad --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java @@ -0,0 +1,114 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmFormMapper; +import com.zt.plat.module.bpm.enums.ErrorCodeConstants; +import com.zt.plat.module.bpm.service.definition.dto.BpmFormFieldRespDTO; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; + +/** + * 动态表单 Service 实现类 + * + * @author 风里雾里 + */ +@Service +@Validated +public class BpmFormServiceImpl implements BpmFormService { + + @Resource + private BpmFormMapper formMapper; + + @Override + public Long createForm(BpmFormSaveReqVO createReqVO) { + this.validateFields(createReqVO.getFields()); + // 插入 + BpmFormDO form = BeanUtils.toBean(createReqVO, BpmFormDO.class); + formMapper.insert(form); + // 返回 + return form.getId(); + } + + @Override + public void updateForm(BpmFormSaveReqVO updateReqVO) { + validateFields(updateReqVO.getFields()); + // 校验存在 + validateFormExists(updateReqVO.getId()); + // 更新 + BpmFormDO updateObj = BeanUtils.toBean(updateReqVO, BpmFormDO.class); + formMapper.updateById(updateObj); + } + + @Override + public void deleteForm(Long id) { + // 校验存在 + this.validateFormExists(id); + // 删除 + formMapper.deleteById(id); + } + + private void validateFormExists(Long id) { + if (formMapper.selectById(id) == null) { + throw exception(ErrorCodeConstants.FORM_NOT_EXISTS); + } + } + + @Override + public BpmFormDO getForm(Long id) { + return formMapper.selectById(id); + } + + @Override + public List getFormList() { + return formMapper.selectList(); + } + + @Override + public List getFormList(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return Collections.emptyList(); + } + return formMapper.selectBatchIds(ids); + } + + @Override + public PageResult getFormPage(BpmFormPageReqVO pageReqVO) { + return formMapper.selectPage(pageReqVO); + } + + /** + * 校验 Field,避免 field 重复 + * + * @param fields field 数组 + */ + private void validateFields(List fields) { + if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验 + return; + } + Map fieldMap = new HashMap<>(); // key 是 vModel,value 是 label + for (String field : fields) { + BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class); + Assert.notNull(fieldDTO); + String oldLabel = fieldMap.put(fieldDTO.getVModel(), fieldDTO.getLabel()); + // 如果不存在,则直接返回 + if (oldLabel == null) { + continue; + } + // 如果存在,则报错 + throw exception(ErrorCodeConstants.FORM_FIELD_REPEAT, oldLabel, fieldDTO.getLabel(), fieldDTO.getVModel()); + } + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java new file mode 100644 index 0000000..a9f8b1d --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java @@ -0,0 +1,134 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO; +import jakarta.validation.Valid; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Model; + +import java.util.List; + +/** + * 流程模型接口 + * + * @author yunlongn + */ +public interface BpmModelService { + + /** + * 获得流程模型列表 + * + * @param name 模型名称 + * @return 流程模型列表 + */ + List getModelList(String name); + + /** + * 创建流程模型 + * + * @param modelVO 创建信息 + * @return 创建的流程模型的编号 + */ + String createModel(@Valid BpmModelSaveReqVO modelVO); + + /** + * 获得流程模块 + * + * @param id 编号 + * @return 流程模型 + */ + Model getModel(String id); + + /** + * 获得流程模型的 BPMN XML + * + * @param id 编号 + * @return BPMN XML + */ + byte[] getModelBpmnXML(String id); + + /** + * 修改流程模型的 BPMN XML + * + * @param id 编号 + * @param bpmnXml BPMN XML + */ + void updateModelBpmnXml(String id, String bpmnXml); + + /** + * 修改流程模型 + * + * @param userId 用户编号 + * @param updateReqVO 更新信息 + */ + void updateModel(Long userId, @Valid BpmModelSaveReqVO updateReqVO); + + /** + * 批量更新模型排序 + * + * @param userId 用户编号 + * @param ids 编号列表 + */ + void updateModelSortBatch(Long userId, List ids); + + /** + * 将流程模型,部署成一个流程定义 + * + * @param userId 用户编号 + * @param id 编号 + */ + void deployModel(Long userId, String id); + + /** + * 删除模型 + * + * @param userId 用户编号 + * @param id 编号 + */ + void deleteModel(Long userId, String id); + + /** + * 清理模型,包括流程实例 + * + * @param userId 用户编号 + * @param id 编号 + */ + void cleanModel(Long userId, String id); + + /** + * 修改模型的状态,实际更新的部署的流程定义的状态 + * + * @param userId 用户编号 + * @param id 编号 + * @param state 状态 + */ + void updateModelState(Long userId, String id, Integer state); + + /** + * 获得流程定义编号对应的 BPMN Model + * + * @param processDefinitionId 流程定义编号 + * @return BPMN Model + */ + BpmnModel getBpmnModelByDefinitionId(String processDefinitionId); + + // ========== 仿钉钉/飞书的精简模型 ========= + + /** + * 获取仿钉钉流程设计模型结构 + * + * @param modelId 流程模型编号 + * @return 仿钉钉流程设计模型结构 + */ + BpmSimpleModelNodeVO getSimpleModel(String modelId); + + /** + * 更新仿钉钉流程设计模型 + * + * @param userId 用户编号 + * @param reqVO 请求信息 + */ + void updateSimpleModel(Long userId, @Valid BpmSimpleModelUpdateReqVO reqVO); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java new file mode 100644 index 0000000..f4815c2 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java @@ -0,0 +1,432 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.validation.ValidationUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO; +import com.zt.plat.module.bpm.convert.definition.BpmModelConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; +import com.zt.plat.module.bpm.enums.task.BpmReasonEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.*; +import org.flowable.common.engine.impl.db.SuspensionState; +import org.flowable.engine.HistoryService; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ModelQuery; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; +import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseCandidateStrategy; + +/** + * 流程模型实现:主要进行 Flowable {@link Model} 的维护 + * + * @author yunlongn + * @author ZT + * @author jason + */ +@Service +@Validated +@Slf4j +public class BpmModelServiceImpl implements BpmModelService { + + @Resource + private RepositoryService repositoryService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + @Resource + private BpmFormService bpmFormService; + + @Resource + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Resource + private HistoryService historyService; + @Resource + private RuntimeService runtimeService; + @Resource + private TaskService taskService; + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; + + @Override + public List getModelList(String name) { + ModelQuery modelQuery = repositoryService.createModelQuery(); + if (StrUtil.isNotEmpty(name)) { + modelQuery.modelNameLike("%" + name + "%"); + } + modelQuery.modelTenantId(FlowableUtils.getTenantId()); + return modelQuery.list(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public String createModel(@Valid BpmModelSaveReqVO createReqVO) { + if (!ValidationUtils.isXmlNCName(createReqVO.getKey())) { + throw exception(MODEL_KEY_VALID); + } + // 1. 校验流程标识已经存在 + Model keyModel = getModelByKey(createReqVO.getKey()); + if (keyModel != null) { + throw exception(MODEL_KEY_EXISTS, createReqVO.getKey()); + } + + // 2. 创建 Model 对象 + createReqVO.setSort(System.currentTimeMillis()); // 使用当前时间,作为排序 + Model model = repositoryService.newModel(); + BpmModelConvert.INSTANCE.copyToModel(model, createReqVO); + model.setTenantId(FlowableUtils.getTenantId()); + + // 3. 保存模型 + saveModel(model, createReqVO); + return model.getId(); + } + + @Override + @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 + public void updateModel(Long userId, BpmModelSaveReqVO updateReqVO) { + // 1. 校验流程模型存在 + Model model = validateModelManager(updateReqVO.getId(), userId); + + // 2. 填充 Model 信息 + BpmModelConvert.INSTANCE.copyToModel(model, updateReqVO); + + // 3. 保存模型 + saveModel(model, updateReqVO); + } + + /** + * 保存模型的基本信息、流程图 + * + * @param model 模型 + * @param saveReqVO 保存信息 + */ + private void saveModel(Model model, BpmModelSaveReqVO saveReqVO) { + // 1. 保存模型的基础信息 + repositoryService.saveModel(model); + + // 2. 保存流程图 + if (ObjUtil.equals(BpmModelTypeEnum.BPMN.getType(), saveReqVO.getType()) + && StrUtil.isNotEmpty(saveReqVO.getBpmnXml())) { + updateModelBpmnXml(model.getId(), saveReqVO.getBpmnXml()); + } else if (ObjUtil.equals(BpmModelTypeEnum.SIMPLE.getType(), saveReqVO.getType()) + && saveReqVO.getSimpleModel() != null) { + // JSON 转换成 bpmnModel + BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(), + saveReqVO.getSimpleModel()); + // 保存 Bpmn XML + updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel)); + // 保存 JSON 数据 + updateModelSimpleJson(model.getId(), saveReqVO.getSimpleModel()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateModelSortBatch(Long userId, List ids) { + // 1.1 校验流程模型存在 + List models = repositoryService.createModelQuery() + .modelTenantId(FlowableUtils.getTenantId()).list(); + models.removeIf(model -> !ids.contains(model.getId())); + if (ids.size() != models.size()) { + throw exception(MODEL_NOT_EXISTS); + } + Map modelMap = convertMap(models, Model::getId); + // 1.2 校验是否为管理员 + ids.forEach(id -> validateModelManager(id, userId)); + + // 保存排序 + long sort = System.currentTimeMillis(); // 使用时间戳 - i 作为排序 + for (int i = ids.size() - 1; i > 0; i--) { + Model model = modelMap.get(ids.get(i)); + // 更新模型 + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model).setSort(sort); + model.setMetaInfo(JsonUtils.toJsonString(metaInfo)); + repositoryService.saveModel(model); + // 更新排序 + processDefinitionService.updateProcessDefinitionSortByModelId(model.getId(), sort); + sort--; + } + } + + private Model validateModelExists(String id) { + Model model = repositoryService.getModel(id); + if (model == null) { + throw exception(MODEL_NOT_EXISTS); + } + return model; + } + + /** + * 校验是否有流程模型的管理权限 + * + * @param id 流程模型编号 + * @param userId 用户编号 + * @return 流程模型 + */ + private Model validateModelManager(String id, Long userId) { + Model model = validateModelExists(id); + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); + if (metaInfo == null || !CollUtil.contains(metaInfo.getManagerUserIds(), userId)) { + throw exception(MODEL_UPDATE_FAIL_NOT_MANAGER, model.getName()); + } + return model; + } + + @Override + @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 + public void deployModel(Long userId, String id) { + // 1.1 校验流程模型存在 + Model model = validateModelManager(id, userId); + BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); + // 1.2 校验流程图 + byte[] bpmnBytes = getModelBpmnXML(model.getId()); + validateBpmnXml(bpmnBytes, metaInfo.getType()); + // 1.3 校验表单已配 + BpmFormDO form = validateFormConfig(metaInfo); + // 1.4 校验任务分配规则已配置 + taskCandidateInvoker.validateBpmnConfig(bpmnBytes); + // 1.5 获取仿钉钉流程设计器模型数据 + String simpleJson = getModelSimpleJson(model.getId()); + + // 2.1 创建流程定义 + String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, simpleJson, + form); + + // 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。 + updateProcessDefinitionSuspended(model.getDeploymentId()); + + // 2.3 更新 model 的 deploymentId,进行关联 + ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId); + model.setDeploymentId(definition.getDeploymentId()); + repositoryService.saveModel(model); + } + + private void validateBpmnXml(byte[] bpmnBytes, Integer type) { + BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); + if (bpmnModel == null) { + throw exception(MODEL_NOT_EXISTS); + } + // 1. 没有 StartEvent + StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel); + if (startEvent == null) { + throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); + } + // 2. 校验 UserTask 的 name 都配置了 + List userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); + userTasks.forEach(userTask -> { + if (StrUtil.isEmpty(userTask.getName())) { + throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); + } + }); + // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选”,BPMN 设计器,校验第一个用户任务节点,SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点 + UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1); + if (firUserTask == null) { + return; + } + Integer candidateStrategy = parseCandidateStrategy(firUserTask); + if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { + throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, firUserTask.getName()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteModel(Long userId, String id) { + // 校验流程模型存在 + Model model = validateModelManager(id, userId); + + // 执行删除 + repositoryService.deleteModel(id); + // 禁用流程定义 + updateProcessDefinitionSuspended(model.getDeploymentId()); + } + + @Override + public void cleanModel(Long userId, String id) { + // 1. 校验流程模型存在 + Model model = validateModelManager(id, userId); + + // 2. 清理所有流程数据 + // 2.1 先取消所有正在运行的流程 + List processInstances = runtimeService.createProcessInstanceQuery() + .processDefinitionKey(model.getKey()).list(); + processInstances.forEach(processInstance -> { + runtimeService.deleteProcessInstance(processInstance.getId(), + BpmReasonEnum.CANCEL_BY_SYSTEM.getReason()); + historyService.deleteHistoricProcessInstance(processInstance.getId()); + processInstanceCopyService.deleteProcessInstanceCopy(processInstance.getId()); + }); + // 2.2 再从历史中删除所有相关的流程数据 + List historicProcessInstances = historyService.createHistoricProcessInstanceQuery() + .processDefinitionKey(model.getKey()).list(); + historicProcessInstances.forEach(historicProcessInstance -> { + historyService.deleteHistoricProcessInstance(historicProcessInstance.getId()); + processInstanceCopyService.deleteProcessInstanceCopy(historicProcessInstance.getId()); + }); + // 2.3 清理所有 Task + List tasks = taskService.createTaskQuery() + .processDefinitionKey(model.getKey()).list(); + tasks.forEach(task -> taskService.deleteTask(task.getId(),BpmReasonEnum.CANCEL_BY_PROCESS_CLEAN.getReason())); + } + + @Override + public void updateModelState(Long userId, String id, Integer state) { + // 1.1 校验流程模型存在 + Model model = validateModelManager(id, userId); + // 1.2 校验流程定义存在 + ProcessDefinition definition = processDefinitionService + .getProcessDefinitionByDeploymentId(model.getDeploymentId()); + if (definition == null) { + throw exception(PROCESS_DEFINITION_NOT_EXISTS); + } + + // 2. 更新状态 + processDefinitionService.updateProcessDefinitionState(definition.getId(), state); + } + + @Override + public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) { + return repositoryService.getBpmnModel(processDefinitionId); + } + + @Override + public BpmSimpleModelNodeVO getSimpleModel(String modelId) { + Model model = validateModelExists(modelId); + // 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ ,获取仿钉钉快搭模型的 JSON 数据 + String json = getModelSimpleJson(model.getId()); + return JsonUtils.parseObject(json, BpmSimpleModelNodeVO.class); + } + + @Override + public void updateSimpleModel(Long userId, BpmSimpleModelUpdateReqVO reqVO) { + // 1. 校验流程模型存在 + Model model = validateModelManager(reqVO.getId(), userId); + + // 2.1 JSON 转换成 bpmnModel + BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModel()); + // 2.2 保存 Bpmn XML + updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel)); + // 2.3 保存 JSON 数据 + updateModelSimpleJson(model.getId(), reqVO.getSimpleModel()); + } + + /** + * 校验流程表单已配置 + * + * @param metaInfo 流程模型元数据 + * @return 表单配置 + */ + private BpmFormDO validateFormConfig(BpmModelMetaInfoVO metaInfo) { + if (metaInfo == null || metaInfo.getFormType() == null) { + throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); + } + // 校验表单存在 + if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) { + if (metaInfo.getFormId() == null) { + throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); + } + BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId()); + if (form == null) { + throw exception(FORM_NOT_EXISTS); + } + return form; + } else { + if (StrUtil.isEmpty(metaInfo.getFormCustomCreatePath()) + || StrUtil.isEmpty(metaInfo.getFormCustomViewPath())) { + throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); + } + return null; + } + } + + @Override + public void updateModelBpmnXml(String id, String bpmnXml) { + if (StrUtil.isEmpty(bpmnXml)) { + return; + } + repositoryService.addModelEditorSource(id, StrUtil.utf8Bytes(bpmnXml)); + } + + @SuppressWarnings("JavaExistingMethodCanBeUsed") + private String getModelSimpleJson(String id) { + byte[] bytes = repositoryService.getModelEditorSourceExtra(id); + if (ArrayUtil.isEmpty(bytes)) { + return null; + } + return StrUtil.utf8Str(bytes); + } + + private void updateModelSimpleJson(String id, BpmSimpleModelNodeVO node) { + if (node == null) { + return; + } + byte[] bytes = JsonUtils.toJsonByte(node); + repositoryService.addModelEditorSourceExtra(id, bytes); + } + + /** + * 挂起 deploymentId 对应的流程定义 + *

+ * 注意:这里一个 deploymentId 只关联一个流程定义 + * + * @param deploymentId 流程发布Id + */ + private void updateProcessDefinitionSuspended(String deploymentId) { + if (StrUtil.isEmpty(deploymentId)) { + return; + } + ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId); + if (oldDefinition == null) { + return; + } + processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), + SuspensionState.SUSPENDED.getStateCode()); + } + + private Model getModelByKey(String key) { + return repositoryService.createModelQuery() + .modelTenantId(FlowableUtils.getTenantId()) + .modelKey(key).singleResult(); + } + + @Override + public Model getModel(String id) { + return repositoryService.getModel(id); + } + + @Override + public byte[] getModelBpmnXML(String id) { + return repositoryService.getModelEditorSource(id); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java new file mode 100644 index 0000000..f819b48 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java @@ -0,0 +1,181 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.Model; +import org.flowable.engine.repository.ProcessDefinition; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * 流程定义接口 + * + * @author yunlong.li + * @author ZJQ + * @author ZT + */ +public interface BpmProcessDefinitionService { + + /** + * 获得流程定义分页 + * + * @param pageReqVO 分页入参 + * @return 流程定义 Page + */ + PageResult getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageReqVO); + + /** + * 获得流程定义列表 + * + * @param suspensionState 中断状态 + * @return 流程定义列表 + */ + List getProcessDefinitionListBySuspensionState(Integer suspensionState); + + /** + * 基于流程模型,创建流程定义 + * + * @param model 流程模型 + * @param modelMetaInfo 流程模型元信息 + * @param bpmnBytes BPMN XML 字节数组 + * @param simpleJson SIMPLE Model JSON + * @param form 表单 + * @return 流程编号 + */ + String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo, + byte[] bpmnBytes, String simpleJson, BpmFormDO form); + + /** + * 更新流程定义状态 + * + * @param id 流程定义的编号 + * @param state 状态 + */ + void updateProcessDefinitionState(String id, Integer state); + + /** + * 更新模型编号 + * + * @param modelId 流程定义编号 + * @param sort 排序 + */ + void updateProcessDefinitionSortByModelId(String modelId, Long sort); + + /** + * 获得流程定义对应的 BPMN + * + * @param id 流程定义编号 + * @return BPMN + */ + BpmnModel getProcessDefinitionBpmnModel(String id); + + /** + * 获得流程定义的信息 + * + * @param id 流程定义编号 + * @return 流程定义信息 + */ + BpmProcessDefinitionInfoDO getProcessDefinitionInfo(String id); + + /** + * 获得流程定义的信息 List + * + * @param ids 流程定义编号数组 + * @return 流程额定义信息数组 + */ + List getProcessDefinitionInfoList(Collection ids); + + default Map getProcessDefinitionInfoMap(Set ids) { + return convertMap(getProcessDefinitionInfoList(ids), BpmProcessDefinitionInfoDO::getProcessDefinitionId); + } + + /** + * 获得流程定义编号对应的 ProcessDefinition + * + * @param id 流程定义编号 + * @return 流程定义 + */ + ProcessDefinition getProcessDefinition(String id); + + /** + * 获得 ids 对应的 ProcessDefinition 数组 + * + * @param ids 编号的数组 + * @return 流程定义的数组 + */ + List getProcessDefinitionList(Set ids); + + default Map getProcessDefinitionMap(Set ids) { + return convertMap(getProcessDefinitionList(ids), ProcessDefinition::getId); + } + + /** + * 获得 deploymentId 对应的 ProcessDefinition + * + * @param deploymentId 部署编号 + * @return 流程定义 + */ + ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId); + + /** + * 获得 deploymentIds 对应的 ProcessDefinition 数组 + * + * @param deploymentIds 部署编号的数组 + * @return 流程定义的数组 + */ + List getProcessDefinitionListByDeploymentIds(Set deploymentIds); + + /** + * 获得流程定义标识对应的激活的流程定义 + * + * @param key 流程定义的标识 + * @return 流程定义 + */ + ProcessDefinition getActiveProcessDefinition(String key); + + /** + * 判断用户是否可以使用该流程定义,进行流程的发起 + * + * @param processDefinition 流程定义 + * @param userId 用户编号 + * @return 是否可以发起流程 + */ + boolean canUserStartProcessDefinition(BpmProcessDefinitionInfoDO processDefinition, Long userId); + + /** + * 获得 ids 对应的 Deployment Map + * + * @param ids 部署编号的数组 + * @return 流程部署 Map + */ + default Map getDeploymentMap(Set ids) { + return convertMap(getDeploymentList(ids), Deployment::getId); + } + + /** + * 获得 ids 对应的 Deployment 数组 + * + * @param ids 部署编号的数组 + * @return 流程部署的数组 + */ + List getDeploymentList(Set ids); + + /** + * 获得 id 对应的 Deployment + * + * @param id 部署编号 + * @return 流程部署 + */ + Deployment getDeployment(String id); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java new file mode 100644 index 0000000..f2f1d6e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java @@ -0,0 +1,248 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.framework.common.util.object.PageUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmProcessDefinitionInfoMapper; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.common.engine.impl.db.SuspensionState; +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.flowable.engine.repository.ProcessDefinitionQuery; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.addIfNotNull; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; +import static java.util.Collections.emptyList; + +/** + * 流程定义实现 + * 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护 + * + * @author yunlongn + * @author ZJQ + * @author ZT + */ +@Service +@Validated +@Slf4j +public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionService { + + @Resource + private RepositoryService repositoryService; + + @Resource + private BpmProcessDefinitionInfoMapper processDefinitionMapper; + + @Resource + private AdminUserApi adminUserApi; + + @Override + public ProcessDefinition getProcessDefinition(String id) { + return repositoryService.getProcessDefinition(id); + } + + @Override + public List getProcessDefinitionList(Set ids) { + return repositoryService.createProcessDefinitionQuery().processDefinitionIds(ids).list(); + } + + @Override + public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) { + if (StrUtil.isEmpty(deploymentId)) { + return null; + } + return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult(); + } + + @Override + public List getProcessDefinitionListByDeploymentIds(Set deploymentIds) { + if (CollUtil.isEmpty(deploymentIds)) { + return emptyList(); + } + return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list(); + } + + @Override + public ProcessDefinition getActiveProcessDefinition(String key) { + return repositoryService.createProcessDefinitionQuery() + .processDefinitionTenantId(FlowableUtils.getTenantId()) + .processDefinitionKey(key).active().singleResult(); + } + + @Override + public boolean canUserStartProcessDefinition(BpmProcessDefinitionInfoDO processDefinition, Long userId) { + if (processDefinition == null) { + return false; + } + + // 校验用户是否在允许发起的用户列表中 + if (CollUtil.isNotEmpty(processDefinition.getStartUserIds())) { + return processDefinition.getStartUserIds().contains(userId); + } + + // 校验用户是否在允许发起的部门列表中 + if (CollUtil.isNotEmpty(processDefinition.getStartDeptIds())) { + AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); + return user != null + && DeptUtil.getDeptId(user) > 0L + && processDefinition.getStartDeptIds().contains(DeptUtil.getDeptId(user)); + } + + // 都为空,则所有人都可以发起 + return true; + } + + @Override + public List getDeploymentList(Set ids) { + if (CollUtil.isEmpty(ids)) { + return emptyList(); + } + List list = new ArrayList<>(ids.size()); + for (String id : ids) { + addIfNotNull(list, getDeployment(id)); + } + return list; + } + + @Override + public Deployment getDeployment(String id) { + if (StrUtil.isEmpty(id)) { + return null; + } + return repositoryService.createDeploymentQuery().deploymentId(id).singleResult(); + } + + @Override + public String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo, + byte[] bpmnBytes, String simpleJson, BpmFormDO form) { + // 创建 Deployment 部署 + Deployment deploy = repositoryService.createDeployment() + .key(model.getKey()).name(model.getName()).category(model.getCategory()) + .addBytes(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnBytes) + .tenantId(FlowableUtils.getTenantId()) + .disableSchemaValidation() // 禁用 XML Schema 验证,因为有自定义的属性 + .deploy(); + + // 设置 ProcessDefinition 的 category 分类 + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery() + .deploymentId(deploy.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory()); + // 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 的 id 和 name 决定 + // 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。 + // 否则,会导致 ProcessDefinition 的分页无法查询到。 + if (!Objects.equals(definition.getKey(), model.getKey())) { + throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, model.getKey(), definition.getKey()); + } + if (!Objects.equals(definition.getName(), model.getName())) { + throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, model.getName(), definition.getName()); + } + + // 插入拓展表 + BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class) + .setModelId(model.getId()).setCategory(model.getCategory()).setProcessDefinitionId(definition.getId()) + .setModelType(modelMetaInfo.getType()).setSimpleModel(simpleJson); + if (form != null) { + definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf()); + } + processDefinitionMapper.insert(definitionDO); + return definition.getId(); + } + + @Override + public void updateProcessDefinitionState(String id, Integer state) { + ProcessDefinition processDefinition = repositoryService.getProcessDefinition(id); + if (processDefinition == null) { + throw exception(PROCESS_DEFINITION_NOT_EXISTS); + } + + // 激活 + if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { + if (processDefinition.isSuspended()) { + repositoryService.activateProcessDefinitionById(id, false, null); + } + return; + } + // 挂起 + if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { + // suspendProcessInstances = false,进行中的任务,不进行挂起。 + // 原因:只要新的流程不允许发起即可,老流程继续可以执行。 + if (!processDefinition.isSuspended()) { + repositoryService.suspendProcessDefinitionById(id, false, null); + } + return; + } + log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state); + } + + @Override + public void updateProcessDefinitionSortByModelId(String modelId, Long sort) { + processDefinitionMapper.updateByModelId(modelId, new BpmProcessDefinitionInfoDO().setSort(sort)); + } + + @Override + public BpmnModel getProcessDefinitionBpmnModel(String id) { + return repositoryService.getBpmnModel(id); + } + + @Override + public BpmProcessDefinitionInfoDO getProcessDefinitionInfo(String id) { + return processDefinitionMapper.selectByProcessDefinitionId(id); + } + + @Override + public List getProcessDefinitionInfoList(Collection ids) { + return processDefinitionMapper.selectListByProcessDefinitionIds(ids); + } + + @Override + public PageResult getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageVO) { + ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery(); + query.processDefinitionTenantId(FlowableUtils.getTenantId()); + if (StrUtil.isNotBlank(pageVO.getKey())) { + query.processDefinitionKey(pageVO.getKey()); + } + // 执行查询 + long count = query.count(); + if (count == 0) { + return PageResult.empty(count); + } + List list = query.orderByProcessDefinitionVersion().desc() + .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + return new PageResult<>(list, count); + } + + @Override + public List getProcessDefinitionListBySuspensionState(Integer suspensionState) { + // 拼接查询条件 + ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery(); + if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), suspensionState)) { + query.suspended(); + } else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), suspensionState)) { + query.active(); + } + // 执行查询 + query.processDefinitionTenantId(FlowableUtils.getTenantId()); + return query.list(); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java new file mode 100644 index 0000000..448300c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java @@ -0,0 +1,54 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; +import jakarta.validation.Valid; + +/** + * BPM 流程表达式 Service 接口 + * + * @author ZT + */ +public interface BpmProcessExpressionService { + + /** + * 创建流程表达式 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createProcessExpression(@Valid BpmProcessExpressionSaveReqVO createReqVO); + + /** + * 更新流程表达式 + * + * @param updateReqVO 更新信息 + */ + void updateProcessExpression(@Valid BpmProcessExpressionSaveReqVO updateReqVO); + + /** + * 删除流程表达式 + * + * @param id 编号 + */ + void deleteProcessExpression(Long id); + + /** + * 获得流程表达式 + * + * @param id 编号 + * @return 流程表达式 + */ + BpmProcessExpressionDO getProcessExpression(Long id); + + /** + * 获得流程表达式分页 + * + * @param pageReqVO 分页查询 + * @return 流程表达式分页 + */ + PageResult getProcessExpressionPage(BpmProcessExpressionPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java new file mode 100644 index 0000000..e367aff --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java @@ -0,0 +1,70 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmProcessExpressionMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.PROCESS_EXPRESSION_NOT_EXISTS; + +/** + * BPM 流程表达式 Service 实现类 + * + * @author ZT + */ +@Service +@Validated +public class BpmProcessExpressionServiceImpl implements BpmProcessExpressionService { + + @Resource + private BpmProcessExpressionMapper processExpressionMapper; + + @Override + public Long createProcessExpression(BpmProcessExpressionSaveReqVO createReqVO) { + // 插入 + BpmProcessExpressionDO processExpression = BeanUtils.toBean(createReqVO, BpmProcessExpressionDO.class); + processExpressionMapper.insert(processExpression); + // 返回 + return processExpression.getId(); + } + + @Override + public void updateProcessExpression(BpmProcessExpressionSaveReqVO updateReqVO) { + // 校验存在 + validateProcessExpressionExists(updateReqVO.getId()); + // 更新 + BpmProcessExpressionDO updateObj = BeanUtils.toBean(updateReqVO, BpmProcessExpressionDO.class); + processExpressionMapper.updateById(updateObj); + } + + @Override + public void deleteProcessExpression(Long id) { + // 校验存在 + validateProcessExpressionExists(id); + // 删除 + processExpressionMapper.deleteById(id); + } + + private void validateProcessExpressionExists(Long id) { + if (processExpressionMapper.selectById(id) == null) { + throw exception(PROCESS_EXPRESSION_NOT_EXISTS); + } + } + + @Override + public BpmProcessExpressionDO getProcessExpression(Long id) { + return processExpressionMapper.selectById(id); + } + + @Override + public PageResult getProcessExpressionPage(BpmProcessExpressionPageReqVO pageReqVO) { + return processExpressionMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java new file mode 100644 index 0000000..9f111a7 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java @@ -0,0 +1,54 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; +import jakarta.validation.Valid; + +/** + * BPM 流程监听器 Service 接口 + * + * @author ZT + */ +public interface BpmProcessListenerService { + + /** + * 创建流程监听器 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createProcessListener(@Valid BpmProcessListenerSaveReqVO createReqVO); + + /** + * 更新流程监听器 + * + * @param updateReqVO 更新信息 + */ + void updateProcessListener(@Valid BpmProcessListenerSaveReqVO updateReqVO); + + /** + * 删除流程监听器 + * + * @param id 编号 + */ + void deleteProcessListener(Long id); + + /** + * 获得流程监听器 + * + * @param id 编号 + * @return 流程监听器 + */ + BpmProcessListenerDO getProcessListener(Long id); + + /** + * 获得流程监听器分页 + * + * @param pageReqVO 分页查询 + * @return 流程监听器分页 + */ + PageResult getProcessListenerPage(BpmProcessListenerPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java new file mode 100644 index 0000000..916949b --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java @@ -0,0 +1,102 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmProcessListenerMapper; +import com.zt.plat.module.bpm.enums.definition.BpmProcessListenerTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmProcessListenerValueTypeEnum; +import jakarta.annotation.Resource; +import org.flowable.engine.delegate.JavaDelegate; +import org.flowable.engine.delegate.TaskListener; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; + +/** + * BPM 流程监听器 Service 实现类 + * + * @author ZT + */ +@Service +@Validated +public class BpmProcessListenerServiceImpl implements BpmProcessListenerService { + + @Resource + private BpmProcessListenerMapper processListenerMapper; + + @Override + public Long createProcessListener(BpmProcessListenerSaveReqVO createReqVO) { + // 校验 + validateCreateProcessListenerValue(createReqVO); + // 插入 + BpmProcessListenerDO processListener = BeanUtils.toBean(createReqVO, BpmProcessListenerDO.class); + processListenerMapper.insert(processListener); + return processListener.getId(); + } + + @Override + public void updateProcessListener(BpmProcessListenerSaveReqVO updateReqVO) { + // 校验存在 + validateProcessListenerExists(updateReqVO.getId()); + validateCreateProcessListenerValue(updateReqVO); + // 更新 + BpmProcessListenerDO updateObj = BeanUtils.toBean(updateReqVO, BpmProcessListenerDO.class); + processListenerMapper.updateById(updateObj); + } + + private void validateCreateProcessListenerValue(BpmProcessListenerSaveReqVO createReqVO) { + // class 类型 + if (createReqVO.getValueType().equals(BpmProcessListenerValueTypeEnum.CLASS.getType())) { + try { + Class clazz = Class.forName(createReqVO.getValue()); + if (createReqVO.getType().equals(BpmProcessListenerTypeEnum.EXECUTION.getType()) + && !JavaDelegate.class.isAssignableFrom(clazz)) { + throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(), + JavaDelegate.class.getName()); + } else if (createReqVO.getType().equals(BpmProcessListenerTypeEnum.TASK.getType()) + && !TaskListener.class.isAssignableFrom(clazz)) { + throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(), + TaskListener.class.getName()); + } + } catch (ClassNotFoundException e) { + throw exception(PROCESS_LISTENER_CLASS_NOT_FOUND, createReqVO.getValue()); + } + return; + } + // 表达式 + if (!StrUtil.startWith(createReqVO.getValue(), "${") || !StrUtil.endWith(createReqVO.getValue(), "}")) { + throw exception(PROCESS_LISTENER_EXPRESSION_INVALID, createReqVO.getValue()); + } + } + + @Override + public void deleteProcessListener(Long id) { + // 校验存在 + validateProcessListenerExists(id); + // 删除 + processListenerMapper.deleteById(id); + } + + private void validateProcessListenerExists(Long id) { + if (processListenerMapper.selectById(id) == null) { + throw exception(PROCESS_LISTENER_NOT_EXISTS); + } + } + + @Override + public BpmProcessListenerDO getProcessListener(Long id) { + return processListenerMapper.selectById(id); + } + + @Override + public PageResult getProcessListenerPage(BpmProcessListenerPageReqVO pageReqVO) { + return processListenerMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java new file mode 100644 index 0000000..40bfea3 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java @@ -0,0 +1,82 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import jakarta.validation.Valid; + +import java.util.Collection; +import java.util.List; + +/** + * 用户组 Service 接口 + * + * @author ZT + */ +public interface BpmUserGroupService { + + /** + * 创建用户组 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createUserGroup(@Valid BpmUserGroupSaveReqVO createReqVO); + + /** + * 更新用户组 + * + * @param updateReqVO 更新信息 + */ + void updateUserGroup(@Valid BpmUserGroupSaveReqVO updateReqVO); + + /** + * 删除用户组 + * + * @param id 编号 + */ + void deleteUserGroup(Long id); + + /** + * 获得用户组 + * + * @param id 编号 + * @return 用户组 + */ + BpmUserGroupDO getUserGroup(Long id); + + /** + * 获得用户组列表 + * + * @param ids 编号 + * @return 用户组列表 + */ + List getUserGroupList(Collection ids); + + /** + * 获得指定状态的用户组列表 + * + * @param status 状态 + * @return 用户组列表 + */ + List getUserGroupListByStatus(Integer status); + + /** + * 获得用户组分页 + * + * @param pageReqVO 分页查询 + * @return 用户组分页 + */ + PageResult getUserGroupPage(BpmUserGroupPageReqVO pageReqVO); + + /** + * 校验用户组们是否有效。如下情况,视为无效: + * 1. 用户组编号不存在 + * 2. 用户组被禁用 + * + * @param ids 用户组编号数组 + */ + void validUserGroups(Collection ids); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java new file mode 100644 index 0000000..f0e6ddb --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java @@ -0,0 +1,107 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmUserGroupMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.USER_GROUP_IS_DISABLE; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS; + +/** + * 用户组 Service 实现类 + * + * @author ZT + */ +@Service +@Validated +public class BpmUserGroupServiceImpl implements BpmUserGroupService { + + @Resource + private BpmUserGroupMapper userGroupMapper; + + @Override + public Long createUserGroup(BpmUserGroupSaveReqVO createReqVO) { + BpmUserGroupDO userGroup = BeanUtils.toBean(createReqVO, BpmUserGroupDO.class); + userGroupMapper.insert(userGroup); + return userGroup.getId(); + } + + @Override + public void updateUserGroup(BpmUserGroupSaveReqVO updateReqVO) { + // 校验存在 + validateUserGroupExists(updateReqVO.getId()); + // 更新 + BpmUserGroupDO updateObj = BeanUtils.toBean(updateReqVO, BpmUserGroupDO.class); + userGroupMapper.updateById(updateObj); + } + + @Override + public void deleteUserGroup(Long id) { + // 校验存在 + this.validateUserGroupExists(id); + // 删除 + userGroupMapper.deleteById(id); + } + + private void validateUserGroupExists(Long id) { + if (userGroupMapper.selectById(id) == null) { + throw exception(USER_GROUP_NOT_EXISTS); + } + } + + @Override + public BpmUserGroupDO getUserGroup(Long id) { + return userGroupMapper.selectById(id); + } + + @Override + public List getUserGroupList(Collection ids) { + return userGroupMapper.selectBatchIds(ids); + } + + + @Override + public List getUserGroupListByStatus(Integer status) { + return userGroupMapper.selectListByStatus(status); + } + + @Override + public PageResult getUserGroupPage(BpmUserGroupPageReqVO pageReqVO) { + return userGroupMapper.selectPage(pageReqVO); + } + + @Override + public void validUserGroups(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return; + } + // 获得用户组信息 + List userGroups = userGroupMapper.selectBatchIds(ids); + Map userGroupMap = convertMap(userGroups, BpmUserGroupDO::getId); + // 校验 + ids.forEach(id -> { + BpmUserGroupDO userGroup = userGroupMap.get(id); + if (userGroup == null) { + throw exception(USER_GROUP_NOT_EXISTS); + } + if (!CommonStatusEnum.ENABLE.getStatus().equals(userGroup.getStatus())) { + throw exception(USER_GROUP_IS_DISABLE, userGroup.getName()); + } + }); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java new file mode 100644 index 0000000..f99ab3a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java @@ -0,0 +1,25 @@ +package com.zt.plat.module.bpm.service.definition.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * Bpm 表单的 Field 表单项 Response DTO + * 字段的定义,可见 https://github.com/JakHuang/form-generator/issues/46 文档 + * + * @author ZT + */ +@Data +public class BpmFormFieldRespDTO { + + /** + * 表单标题 + */ + private String label; + /** + * 表单字段的属性名,可自定义 + */ + @JsonProperty(value = "vModel") + private String vModel; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java new file mode 100644 index 0000000..69767d9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.service.definition.dto; + +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import lombok.Data; + +/** + * BPM 流程 MetaInfo Response DTO + * 主要用于 { Model#setMetaInfo(String)} 的存储 + * + * 最终,它的字段和 {@link com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} 是一致的 + * + * @author ZT + */ +@Data +public class BpmModelMetaInfoRespDTO { + + /** + * 流程图标 + */ + private String icon; + /** + * 流程描述 + */ + private String description; + + /** + * 表单类型 + */ + private Integer formType; + /** + * 表单编号 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private Long formId; + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomCreatePath; + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomViewPath; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java new file mode 100644 index 0000000..2132a21 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java @@ -0,0 +1,81 @@ +package com.zt.plat.module.bpm.service.definition.dto; + +import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +import java.util.List; + +/** + * 流程定义创建 Request DTO + */ +@Data +public class BpmProcessDefinitionCreateReqDTO { + + // ========== 模型相关 ========== + + /** + * 流程模型的编号 + */ + @NotEmpty(message = "流程模型编号不能为空") + private String modelId; + /** + * 流程标识 + */ + @NotEmpty(message = "流程标识不能为空") + private String key; + /** + * 流程名称 + */ + @NotEmpty(message = "流程名称不能为空") + private String name; + /** + * 流程描述 + */ + private String description; + /** + * 流程分类 + */ + @NotEmpty(message = "流程分类不能为空") + private String category; + /** + * BPMN XML + */ + @NotEmpty(message = "BPMN XML 不能为空") + private byte[] bpmnBytes; + + // ========== 表单相关 ========== + + /** + * 表单类型 + */ + @NotNull(message = "表单类型不能为空") + private Integer formType; + /** + * 动态表单编号 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private Long formId; + /** + * 表单的配置 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private String formConf; + /** + * 表单项的数组 + * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 + */ + private List formFields; + /** + * 自定义表单的提交路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomCreatePath; + /** + * 自定义表单的查看路径,使用 Vue 的路由地址 + * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 + */ + private String formCustomViewPath; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java new file mode 100644 index 0000000..1698829 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.service.message; + +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO; +import jakarta.validation.Valid; + +/** + * BPM 消息 Service 接口 + * + * TODO 芋艿:未来支持消息的可配置;不同的流程,在什么场景下,需要发送什么消息,消息的内容是什么; + * + * @author ZT + */ +public interface BpmMessageService { + + /** + * 发送流程实例被通过的消息 + * + * @param reqDTO 发送信息 + */ + void sendMessageWhenProcessInstanceApprove(@Valid BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO); + + /** + * 发送流程实例被不通过的消息 + * + * @param reqDTO 发送信息 + */ + void sendMessageWhenProcessInstanceReject(@Valid BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO); + + /** + * 发送任务被分配的消息 + * + * @param reqDTO 发送信息 + */ + void sendMessageWhenTaskAssigned(@Valid BpmMessageSendWhenTaskCreatedReqDTO reqDTO); + + /** + * 发送任务审批超时的消息 + * + * @param reqDTO 发送信息 + */ + void sendMessageWhenTaskTimeout(@Valid BpmMessageSendWhenTaskTimeoutReqDTO reqDTO); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java new file mode 100644 index 0000000..9650cb7 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java @@ -0,0 +1,79 @@ +package com.zt.plat.module.bpm.service.message; + +import com.zt.plat.framework.web.config.WebProperties; +import com.zt.plat.module.bpm.convert.message.BpmMessageConvert; +import com.zt.plat.module.bpm.enums.message.BpmMessageEnum; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO; +import com.zt.plat.module.system.api.sms.SmsSendApi; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.HashMap; +import java.util.Map; + +/** + * BPM 消息 Service 实现类 + * + * @author ZT + */ +@Service +@Validated +@Slf4j +public class BpmMessageServiceImpl implements BpmMessageService { + + @Resource + private SmsSendApi smsSendApi; + + @Resource + private WebProperties webProperties; + + @Override + public void sendMessageWhenProcessInstanceApprove(BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO) { + Map templateParams = new HashMap<>(); + templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); + templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); + smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(), + BpmMessageEnum.PROCESS_INSTANCE_APPROVE.getSmsTemplateCode(), templateParams)).checkError(); + } + + @Override + public void sendMessageWhenProcessInstanceReject(BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO) { + Map templateParams = new HashMap<>(); + templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); + templateParams.put("reason", reqDTO.getReason()); + templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); + smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(), + BpmMessageEnum.PROCESS_INSTANCE_REJECT.getSmsTemplateCode(), templateParams)).checkError(); + } + + @Override + public void sendMessageWhenTaskAssigned(BpmMessageSendWhenTaskCreatedReqDTO reqDTO) { + Map templateParams = new HashMap<>(); + templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); + templateParams.put("taskName", reqDTO.getTaskName()); + templateParams.put("startUserNickname", reqDTO.getStartUserNickname()); + templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); + smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(), + BpmMessageEnum.TASK_ASSIGNED.getSmsTemplateCode(), templateParams)).checkError(); + } + + @Override + public void sendMessageWhenTaskTimeout(BpmMessageSendWhenTaskTimeoutReqDTO reqDTO) { + Map templateParams = new HashMap<>(); + templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); + templateParams.put("taskName", reqDTO.getTaskName()); + templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); + smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(), + BpmMessageEnum.TASK_TIMEOUT.getSmsTemplateCode(), templateParams)).checkError(); + } + + private String getProcessInstanceDetailUrl(String taskId) { + return webProperties.getAdminUi().getUrl() + "/bpm/process-instance/detail?id=" + taskId; + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java new file mode 100644 index 0000000..d06cbf4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java @@ -0,0 +1,26 @@ +package com.zt.plat.module.bpm.service.message.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * BPM 发送流程实例被通过 Request DTO + */ +@Data +public class BpmMessageSendWhenProcessInstanceApproveReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + @NotNull(message = "发起人的用户编号") + private Long startUserId; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java new file mode 100644 index 0000000..cf4bd29 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.service.message.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * BPM 发送流程实例被不通过 Request DTO + */ +@Data +public class BpmMessageSendWhenProcessInstanceRejectReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + @NotNull(message = "发起人的用户编号") + private Long startUserId; + + /** + * 不通过理由 + */ + @NotEmpty(message = "不通过理由不能为空") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java new file mode 100644 index 0000000..03adf51 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.service.message.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * BPM 发送任务被分配 Request DTO + */ +@Data +public class BpmMessageSendWhenTaskCreatedReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + @NotNull(message = "发起人的用户编号") + private Long startUserId; + @NotEmpty(message = "发起人的昵称") + private String startUserNickname; + + /** + * 流程任务的编号 + */ + @NotEmpty(message = "流程任务的编号不能为空") + private String taskId; + /** + * 流程任务的名字 + */ + @NotEmpty(message = "流程任务的名字不能为空") + private String taskName; + + /** + * 审批人的用户编号 + */ + @NotNull(message = "审批人的用户编号不能为空") + private Long assigneeUserId; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java new file mode 100644 index 0000000..f9a9067 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java @@ -0,0 +1,41 @@ +package com.zt.plat.module.bpm.service.message.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +/** + * BPM 发送任务审批超时 Request DTO + */ +@Data +public class BpmMessageSendWhenTaskTimeoutReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + + /** + * 流程任务的编号 + */ + @NotEmpty(message = "流程任务的编号不能为空") + private String taskId; + /** + * 流程任务的名字 + */ + @NotEmpty(message = "流程任务的名字不能为空") + private String taskName; + + /** + * 审批人的用户编号 + */ + @NotNull(message = "审批人的用户编号不能为空") + private Long assigneeUserId; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java new file mode 100644 index 0000000..bf19ddb --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java @@ -0,0 +1,52 @@ +package com.zt.plat.module.bpm.service.oa; + + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; +import jakarta.validation.Valid; + +/** + * 请假申请 Service 接口 + * + * @author jason + * @author ZT + */ +public interface BpmOALeaveService { + + /** + * 创建请假申请 + * + * @param userId 用户编号 + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createLeave(Long userId, @Valid BpmOALeaveCreateReqVO createReqVO); + + /** + * 更新请假申请的状态 + * + * @param id 编号 + * @param status 结果 + */ + void updateLeaveStatus(Long id, Integer status); + + /** + * 获得请假申请 + * + * @param id 编号 + * @return 请假申请 + */ + BpmOALeaveDO getLeave(Long id); + + /** + * 获得请假申请分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页查询 + * @return 请假申请分页 + */ + PageResult getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java new file mode 100644 index 0000000..fec6e27 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java @@ -0,0 +1,89 @@ +package com.zt.plat.module.bpm.service.oa; + +import cn.hutool.core.date.LocalDateTimeUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.api.task.BpmProcessInstanceApi; +import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO; +import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; +import com.zt.plat.module.bpm.dal.mysql.oa.BpmOALeaveMapper; +import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.HashMap; +import java.util.Map; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.OA_LEAVE_NOT_EXISTS; + +/** + * OA 请假申请 Service 实现类 + * + * @author jason + * @author ZT + */ +@Service +@Validated +public class BpmOALeaveServiceImpl implements BpmOALeaveService { + + /** + * OA 请假对应的流程定义 KEY + */ + public static final String PROCESS_KEY = "oa_leave"; + + @Resource + private BpmOALeaveMapper leaveMapper; + + @Resource + private BpmProcessInstanceApi processInstanceApi; + + @Override + @Transactional(rollbackFor = Exception.class) + public Long createLeave(Long userId, BpmOALeaveCreateReqVO createReqVO) { + // 插入 OA 请假单 + long day = LocalDateTimeUtil.between(createReqVO.getStartTime(), createReqVO.getEndTime()).toDays(); + BpmOALeaveDO leave = BeanUtils.toBean(createReqVO, BpmOALeaveDO.class) + .setUserId(userId).setDay(day).setStatus(BpmTaskStatusEnum.RUNNING.getStatus()); + leaveMapper.insert(leave); + + // 发起 BPM 流程 + Map processInstanceVariables = new HashMap<>(); + processInstanceVariables.put("day", day); + String processInstanceId = processInstanceApi.createProcessInstance(userId, + new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY) + .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId())) + .setStartUserSelectAssignees(createReqVO.getStartUserSelectAssignees())).getCheckedData(); + + // 将工作流的编号,更新到 OA 请假单中 + leaveMapper.updateById(new BpmOALeaveDO().setId(leave.getId()).setProcessInstanceId(processInstanceId)); + return leave.getId(); + } + + @Override + public void updateLeaveStatus(Long id, Integer status) { + validateLeaveExists(id); + leaveMapper.updateById(new BpmOALeaveDO().setId(id).setStatus(status)); + } + + private void validateLeaveExists(Long id) { + if (leaveMapper.selectById(id) == null) { + throw exception(OA_LEAVE_NOT_EXISTS); + } + } + + @Override + public BpmOALeaveDO getLeave(Long id) { + return leaveMapper.selectById(id); + } + + @Override + public PageResult getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO) { + return leaveMapper.selectPage(userId, pageReqVO); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java new file mode 100644 index 0000000..122a6ff --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.service.oa.listener; + +import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; +import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEventListener; +import com.zt.plat.module.bpm.service.oa.BpmOALeaveService; +import com.zt.plat.module.bpm.service.oa.BpmOALeaveServiceImpl; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * OA 请假单的结果的监听器实现类 + * + * @author ZT + */ +@Component +public class BpmOALeaveStatusListener extends BpmProcessInstanceStatusEventListener { + + @Resource + private BpmOALeaveService leaveService; + + @Override + protected List getProcessDefinitionKey() { + return List.of(BpmOALeaveServiceImpl.PROCESS_KEY); + } + + @Override + protected void onEvent(BpmProcessInstanceStatusEvent event) { + leaveService.updateLeaveStatus(Long.parseLong(event.getBusinessKey()), event.getStatus()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java new file mode 100644 index 0000000..e5f06b6 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java @@ -0,0 +1,60 @@ +package com.zt.plat.module.bpm.service.task; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; +import jakarta.validation.constraints.NotEmpty; +import org.flowable.bpmn.model.FlowNode; + +import java.util.Collection; + +/** + * 流程抄送 Service 接口 + * + * 现在是在审批的时候进行流程抄送 + */ +public interface BpmProcessInstanceCopyService { + + /** + * 【管理员】流程实例的抄送 + * + * @param userIds 抄送的用户编号 + * @param reason 抄送意见 + * @param taskId 流程任务编号 + */ + void createProcessInstanceCopy(Collection userIds, String reason, String taskId); + + /** + * 【自动抄送】流程实例的抄送 + * + * @param userIds 抄送的用户编号 + * @param reason 抄送意见 + * @param processInstanceId 流程编号 + * @param activityId 流程活动编号(对应 {@link FlowNode#getId()}) + * @param activityName 任务编号(对应 {@link FlowNode#getName()}) + * @param taskId 任务编号,允许空 + */ + void createProcessInstanceCopy(Collection userIds, String reason, + @NotEmpty(message = "流程实例编号不能为空") String processInstanceId, + @NotEmpty(message = "流程活动编号不能为空") String activityId, + @NotEmpty(message = "流程活动名字不能为空") String activityName, + String taskId); + + /** + * 获得抄送的流程的分页 + * + * @param userId 当前登录用户 + * @param pageReqVO 分页请求 + * @return 抄送的分页结果 + */ + PageResult getProcessInstanceCopyPage(Long userId, + BpmProcessInstanceCopyPageReqVO pageReqVO); + + /** + * 删除抄送流程 + * + * @param processInstanceId 流程实例 ID + */ + void deleteProcessInstanceCopy(String processInstanceId); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java new file mode 100644 index 0000000..dd842b9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java @@ -0,0 +1,96 @@ +package com.zt.plat.module.bpm.service.task; + +import cn.hutool.core.util.ObjectUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; +import com.zt.plat.module.bpm.dal.mysql.task.BpmProcessInstanceCopyMapper; +import com.zt.plat.module.bpm.enums.ErrorCodeConstants; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import java.util.Collection; +import java.util.List; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; + +/** + * 流程抄送 Service 实现类 + * + * @author kyle + */ +@Service +@Validated +@Slf4j +public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopyService { + + @Resource + private BpmProcessInstanceCopyMapper processInstanceCopyMapper; + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmTaskService taskService; + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessInstanceService processInstanceService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private BpmProcessDefinitionService processDefinitionService; + + @Override + public void createProcessInstanceCopy(Collection userIds, String reason, String taskId) { + Task task = taskService.getTask(taskId); + if (ObjectUtil.isNull(task)) { + throw exception(ErrorCodeConstants.TASK_NOT_EXISTS); + } + // 执行抄送 + createProcessInstanceCopy(userIds, reason, + task.getProcessInstanceId(), task.getTaskDefinitionKey(), task.getId(), task.getName()); + } + + @Override + public void createProcessInstanceCopy(Collection userIds, String reason, String processInstanceId, + String activityId, String activityName, String taskId) { + // 1.1 校验流程实例存在 + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + if (processInstance == null) { + throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); + } + // 1.2 校验流程定义存在 + ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( + processInstance.getProcessDefinitionId()); + if (processDefinition == null) { + throw exception(ErrorCodeConstants.PROCESS_DEFINITION_NOT_EXISTS); + } + + // 2. 创建抄送流程 + List copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO() + .setUserId(userId).setReason(reason).setStartUserId(Long.valueOf(processInstance.getStartUserId())) + .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName()) + .setCategory(processDefinition.getCategory()).setTaskId(taskId) + .setActivityId(activityId).setActivityName(activityName) + .setProcessDefinitionId(processInstance.getProcessDefinitionId())); + processInstanceCopyMapper.insertBatch(copyList); + } + + @Override + public PageResult getProcessInstanceCopyPage(Long userId, + BpmProcessInstanceCopyPageReqVO pageReqVO) { + return processInstanceCopyMapper.selectPage(userId, pageReqVO); + } + + @Override + public void deleteProcessInstanceCopy(String processInstanceId) { + processInstanceCopyMapper.deleteByProcessInstanceId(processInstanceId); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java new file mode 100644 index 0000000..98af5ee --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java @@ -0,0 +1,191 @@ +package com.zt.plat.module.bpm.service.task; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; +import jakarta.validation.Valid; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; + +/** + * 流程实例 Service 接口 + * + * @author ZT + */ +public interface BpmProcessInstanceService { + + // ========== Query 查询相关方法 ========== + + /** + * 获得流程实例 + * + * @param id 流程实例的编号 + * @return 流程实例 + */ + ProcessInstance getProcessInstance(String id); + + /** + * 获得流程实例列表 + * + * @param ids 流程实例的编号集合 + * @return 流程实例列表 + */ + List getProcessInstances(Set ids); + + /** + * 获得流程实例 Map + * + * @param ids 流程实例的编号集合 + * @return 流程实例列表 Map + */ + default Map getProcessInstanceMap(Set ids) { + return convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId); + } + + /** + * 获得历史的流程实例 + * + * @param id 流程实例的编号 + * @return 历史的流程实例 + */ + HistoricProcessInstance getHistoricProcessInstance(String id); + + /** + * 获得历史的流程实例列表 + * + * @param ids 流程实例的编号集合 + * @return 历史的流程实例列表 + */ + List getHistoricProcessInstances(Set ids); + + /** + * 获得历史的流程实例 Map + * + * @param ids 流程实例的编号集合 + * @return 历史的流程实例列表 Map + */ + default Map getHistoricProcessInstanceMap(Set ids) { + return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId); + } + + /** + * 获得流程实例的分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页请求 + * @return 流程实例的分页 + */ + PageResult getProcessInstancePage(Long userId, + @Valid BpmProcessInstancePageReqVO pageReqVO); + + /** + * 获取审批详情。 + *

+ * 可以是准备发起的流程、进行中的流程、已经结束的流程 + * + * @param loginUserId 登录人的用户编号 + * @param reqVO 请求信息 + * @return 流程实例的进度 + */ + BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + + /** + * 获取下一个执行节点信息 + * + * @param loginUserId 登录人的用户编号 + * @param reqVO 请求信息 + * @return 下一个执行节点信息 + */ + List getNextApprovalNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); + + /** + * 获取流程实例的 BPMN 模型视图 + * + * @param id 流程实例的编号 + * @return BPMN 模型视图 + */ + BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id); + + // ========== Update 写入相关方法 ========== + + /** + * 创建流程实例(提供给前端) + * + * @param userId 用户编号 + * @param createReqVO 创建信息 + * @return 实例的编号 + */ + String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO); + + /** + * 创建流程实例(提供给内部) + * + * @param userId 用户编号 + * @param createReqDTO 创建信息 + * @return 实例的编号 + */ + String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO); + + /** + * 发起人取消流程实例 + * + * @param userId 用户编号 + * @param cancelReqVO 取消信息 + */ + void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO); + + /** + * 管理员取消流程实例 + * + * @param userId 用户编号 + * @param cancelReqVO 取消信息 + */ + void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO); + + /** + * 更新 ProcessInstance 为不通过 + * + * @param processInstance 流程实例 + * @param reason 理由。例如说,审批不通过时,需要传递该值 + */ + void updateProcessInstanceReject(ProcessInstance processInstance, String reason); + + /** + * 更新 ProcessInstance 的变量 + * + * @param id 流程编号 + * @param variables 流程变量 + */ + void updateProcessInstanceVariables(String id, Map variables); + + /** + * 删除 ProcessInstance 的变量 + * + * @param id 流程编号 + * @param variableNames 流程变量名 + */ + void removeProcessInstanceVariables(String id, Collection variableNames); + + // ========== Event 事件相关方法 ========== + + /** + * 处理 ProcessInstance 完成事件,例如说:审批通过、不通过、取消 + * + * @param instance 流程任务 + */ + void processProcessInstanceCompleted(ProcessInstance instance); + + /** + * 处理 ProcessInstance 开始事件,例如说:流程前置通知 + * + * @param instance 流程任务 + */ + void processProcessInstanceCreated(ProcessInstance instance); +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java new file mode 100644 index 0000000..fc01ac7 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -0,0 +1,964 @@ +package com.zt.plat.module.bpm.service.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.date.DateUtils; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.object.ObjectUtils; +import com.zt.plat.framework.common.util.object.PageUtils; +import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNodeTask; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; +import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.dal.redis.BpmProcessIdRedisDAO; +import com.zt.plat.module.bpm.enums.ErrorCodeConstants; +import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmSimpleModelNodeTypeEnum; +import com.zt.plat.module.bpm.enums.task.BpmProcessInstanceStatusEnum; +import com.zt.plat.module.bpm.enums.task.BpmReasonEnum; +import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; +import com.zt.plat.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.message.BpmMessageService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.constants.BpmnXMLConstants; +import org.flowable.bpmn.model.*; +import org.flowable.engine.HistoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.runtime.ProcessInstanceBuilder; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.validation.annotation.Validated; + +import java.util.*; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; +import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseNodeType; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.flowable.bpmn.constants.BpmnXMLConstants.*; + +/** + * 流程实例 Service 实现类 + *

+ * ProcessDefinition & ProcessInstance & Execution & Task 的关系: + * 1. + *

+ * HistoricProcessInstance & ProcessInstance 的关系: + * 1. + *

+ * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 + * + * @author ZT + */ +@Service +@Validated +@Slf4j +public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { + + @Resource + private RuntimeService runtimeService; + @Resource + private HistoryService historyService; + + @Resource + private BpmProcessDefinitionService processDefinitionService; + @Resource + @Lazy // 避免循环依赖 + private BpmTaskService taskService; + @Resource + private BpmMessageService messageService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @Resource + private BpmProcessInstanceEventPublisher processInstanceEventPublisher; + + @Resource + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Resource + private BpmProcessIdRedisDAO processIdRedisDAO; + + // ========== Query 查询相关方法 ========== + + @Override + public ProcessInstance getProcessInstance(String id) { + return runtimeService.createProcessInstanceQuery() + .includeProcessVariables() + .processInstanceId(id) + .singleResult(); + } + + @Override + public List getProcessInstances(Set ids) { + return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables().list(); + } + + @Override + public HistoricProcessInstance getHistoricProcessInstance(String id) { + return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables() + .singleResult(); + } + + @Override + public List getHistoricProcessInstances(Set ids) { + return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables() + .list(); + } + + private Map getFormFieldsPermission(BpmnModel bpmnModel, + String activityId, String taskId) { + // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id + if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) { + activityId = Optional.ofNullable(taskService.getHistoricTask(taskId)) + .map(HistoricTaskInstance::getTaskDefinitionKey).orElse(null); + } + if (StrUtil.isEmpty(activityId)) { + return null; + } + + // 2. 从 BpmnModel 中解析表单字段权限 + return BpmnModelUtils.parseFormFieldsPermission(bpmnModel, activityId); + } + + @Override + public BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, BpmApprovalDetailReqVO reqVO) { + // 1.1 从 reqVO 中,读取公共变量 + Long startUserId = loginUserId; // 流程发起人 + HistoricProcessInstance historicProcessInstance = null; // 流程实例 + Integer processInstanceStatus = BpmProcessInstanceStatusEnum.NOT_START.getStatus(); // 流程状态 + Map processVariables = new HashMap<>(); // 流程变量 + // 1.2 如果是流程已发起的场景,则使用流程实例的数据 + if (reqVO.getProcessInstanceId() != null) { + historicProcessInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId()); + if (historicProcessInstance == null) { + throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); + } + startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); + processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); + // 合并 DB 和前端传递的流量变量,以前端的为主 + if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) { + processVariables.putAll(historicProcessInstance.getProcessVariables()); + } + } + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { + processVariables.putAll(reqVO.getProcessVariables()); + } + // 1.3 读取其它相关数据 + ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( + historicProcessInstance != null ? historicProcessInstance.getProcessDefinitionId() + : reqVO.getProcessDefinitionId()); + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService + .getProcessDefinitionInfo(processDefinition.getId()); + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId()); + + // 2.1 已结束 + 进行中的活动节点 + List endActivityNodes = null; // 已结束的审批信息 + List runActivityNodes = null; // 进行中的审批信息 + List activities = null; // 流程实例列表 + if (reqVO.getProcessInstanceId() != null) { + activities = taskService.getActivityListByProcessInstanceId(reqVO.getProcessInstanceId()); + List tasks = taskService.getTaskListByProcessInstanceId(reqVO.getProcessInstanceId(), + true); + endActivityNodes = getEndActivityNodeList(startUserId, bpmnModel, processDefinitionInfo, + historicProcessInstance, processInstanceStatus, activities, tasks); + runActivityNodes = getRunApproveNodeList(startUserId, bpmnModel, processDefinition, processVariables, + activities, tasks); + } + + // 2.2 流程已经结束,直接 return,无需预测 + if (BpmProcessInstanceStatusEnum.isProcessEndStatus(processInstanceStatus)) { + return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, + historicProcessInstance, + processInstanceStatus, endActivityNodes, runActivityNodes, null, null); + } + + // 3.1 计算当前登录用户的待办任务 + BpmTaskRespVO todoTask = taskService.getTodoTask(loginUserId, reqVO.getTaskId(), reqVO.getProcessInstanceId()); + // 3.2 预测未运行节点的审批信息 + List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, + processDefinitionInfo, + processVariables, activities); + // 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点 + if (ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) { + simulateActivityNodes.removeIf(node -> + BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); + } + + // 4. 拼接最终数据 + return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance, + processInstanceStatus, endActivityNodes, runActivityNodes, simulateActivityNodes, todoTask); + } + + @Override + public List getNextApprovalNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { + // 1.1 校验任务存在,且是当前用户的 + Task task = taskService.validateTask(loginUserId, reqVO.getTaskId()); + // 1.2 校验流程实例存在 + ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); + if (historicProcessInstance == null) { + throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); + } + // 1.3 校验BpmnModel + BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); + if (bpmnModel == null) { + return null; + } + + // 2. 设置流程变量 + Map processVariables = new HashMap<>(); + // 2.1 获取历史中流程变量 + if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) { + processVariables.putAll(historicProcessInstance.getProcessVariables()); + } + // 2.2 合并前端传递的流程变量,以前端为准 + if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { + processVariables.putAll(reqVO.getProcessVariables()); + } + + // 3. 获取下一个将要执行的节点集合 + FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); + List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); + List nextActivityNodes = convertList(nextFlowNodes, node -> new ActivityNode().setId(node.getId()) + .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) + .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) + .setCandidateUserIds(getTaskCandidateUserList(bpmnModel, node.getId(), + loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables))); + if (CollUtil.isNotEmpty(nextActivityNodes)) { + return nextActivityNodes; + } + + // 4. 拼接基础信息 + Map userMap = adminUserApi.getUserMap( + convertSetByFlatMap(nextActivityNodes, ActivityNode::getCandidateUserIds, Collection::stream)); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + nextActivityNodes.forEach(node -> node.setCandidateUsers(convertList(node.getCandidateUserIds(), userId -> { + AdminUserRespDTO user = userMap.get(userId); + if (user != null) { + return BpmProcessInstanceConvert.INSTANCE.buildUser(userId, userMap, deptMap); + } + return null; + }))); + return nextActivityNodes; + } + + @Override + @SuppressWarnings("unchecked") + public PageResult getProcessInstancePage(Long userId, + BpmProcessInstancePageReqVO pageReqVO) { + // 1. 构建查询条件 + HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .includeProcessVariables() + .processInstanceTenantId(FlowableUtils.getTenantId()) + .orderByProcessInstanceStartTime().desc(); + if (userId != null) { // 【我的流程】菜单时,需要传递该字段 + processInstanceQuery.startedBy(String.valueOf(userId)); + } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 + processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); + } + if (StrUtil.isNotEmpty(pageReqVO.getName())) { + processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); + } + if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionKey())) { + processInstanceQuery.processDefinitionKey(pageReqVO.getProcessDefinitionKey()); + } + if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { + processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); + } + if (pageReqVO.getStatus() != null) { + processInstanceQuery.variableValueEquals(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, + pageReqVO.getStatus()); + } + if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { + processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); + processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); + } + if (ArrayUtil.isNotEmpty(pageReqVO.getEndTime())) { + processInstanceQuery.finishedAfter(DateUtils.of(pageReqVO.getEndTime()[0])); + processInstanceQuery.finishedBefore(DateUtils.of(pageReqVO.getEndTime()[1])); + } + // 表单字段查询 + Map formFieldsParams = JsonUtils.parseObject(pageReqVO.getFormFieldsParams(), Map.class); + if (CollUtil.isNotEmpty(formFieldsParams)) { + formFieldsParams.forEach((key, value) -> { + if (StrUtil.isEmpty(String.valueOf(value))) { + return; + } + // TODO @lesan:应支持多种类型的查询方式,目前只有字符串全等 + processInstanceQuery.variableValueEquals(key, value); + }); + } + + // 2.1 查询数量 + long processInstanceCount = processInstanceQuery.count(); + if (processInstanceCount == 0) { + return PageResult.empty(processInstanceCount); + } + // 2.2 查询列表 + List processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), + pageReqVO.getPageSize()); + return new PageResult<>(processInstanceList, processInstanceCount); + } + + /** + * 拼接审批详情的最终数据 + *

+ * 主要是,拼接审批人的用户信息、部门信息 + */ + private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO, + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance processInstance, + Integer processInstanceStatus, + List endApprovalNodeInfos, + List runningApprovalNodeInfos, + List simulateApprovalNodeInfos, + BpmTaskRespVO todoTask) { + // 1. 获取所有需要读取用户信息的 userIds + List approveNodes = newArrayList( + asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos)); + Set userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds(processInstance, approveNodes, todoTask); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + + // 2. 表单权限 + String taskId = reqVO.getTaskId() == null && todoTask != null ? todoTask.getId() : reqVO.getTaskId(); + Map formFieldsPermission = getFormFieldsPermission(bpmnModel, reqVO.getActivityId(), taskId); + + // 3. 拼接数据 + return BpmProcessInstanceConvert.INSTANCE.buildApprovalDetail(bpmnModel, processDefinition, + processDefinitionInfo, processInstance, + processInstanceStatus, approveNodes, todoTask, formFieldsPermission, userMap, deptMap); + } + + /** + * 获得【已结束】的活动节点们 + */ + private List getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel, + BpmProcessDefinitionInfoDO processDefinitionInfo, + HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, + List activities, List tasks) { + // 遍历 tasks 列表,只处理已结束的 UserTask + // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities 的话,它无法成为一个节点 + List endTasks = filterList(tasks, task -> task.getEndTime() != null); + List approvalNodes = convertList(endTasks, task -> { + FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) + .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) + ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() + : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) + .setStatus(FlowableUtils.getTaskStatus(task)) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) + .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) + .setTasks(singletonList(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task))); + // 如果是取消状态,则跳过 + if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) { + return null; + } + return activityNode; + }); + + // 遍历 activities,只处理已结束的 StartEvent、EndEvent + List endActivities = filterList(activities, activity -> activity.getEndTime() != null + && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_EVENT_START, ELEMENT_CALL_ACTIVITY, ELEMENT_EVENT_END))); + endActivities.forEach(activity -> { + // StartEvent:只处理 BPMN 的场景。因为,SIMPLE 情况下,已经有 START_USER_NODE 节点 + if (ELEMENT_EVENT_START.equals(activity.getActivityType()) + && BpmModelTypeEnum.BPMN.getType().equals(processDefinitionInfo.getModelType())) { + ActivityNodeTask startTask = new ActivityNodeTask().setId(BpmnModelConstants.START_USER_NODE_ID) + .setAssignee(startUserId).setStatus(BpmTaskStatusEnum.APPROVE.getStatus()); + ActivityNode startNode = new ActivityNode().setId(startTask.getId()) + .setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName()) + .setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()) + .setStatus(startTask.getStatus()).setTasks(ListUtil.of(startTask)) + .setStartTime(DateUtils.of(activity.getStartTime())) + .setEndTime(DateUtils.of(activity.getEndTime())); + approvalNodes.add(0, startNode); + return; + } + // EndEvent + if (ELEMENT_EVENT_END.equals(activity.getActivityType())) { + if (BpmProcessInstanceStatusEnum.isRejectStatus(processInstanceStatus)) { + // 拒绝情况下,不需要展示 EndEvent 结束节点。原因是:前端已经展示 x 效果,无需重复展示 + return; + } + ActivityNode endNode = new ActivityNode().setId(activity.getId()) + .setName(BpmSimpleModelNodeTypeEnum.END_NODE.getName()) + .setNodeType(BpmSimpleModelNodeTypeEnum.END_NODE.getType()).setStatus(processInstanceStatus) + .setStartTime(DateUtils.of(activity.getStartTime())) + .setEndTime(DateUtils.of(activity.getEndTime())); + String reason = FlowableUtils.getProcessInstanceReason(historicProcessInstance); + if (StrUtil.isNotEmpty(reason)) { + endNode.setTasks(singletonList(new ActivityNodeTask().setId(endNode.getId()) + .setStatus(endNode.getStatus()).setReason(reason))); + } + approvalNodes.add(endNode); + } + // CallActivity + if (ELEMENT_CALL_ACTIVITY.equals(activity.getActivityType())) { + ActivityNode callActivity = new ActivityNode().setId(activity.getId()) + .setName(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getName()) + .setNodeType(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType()).setStatus(processInstanceStatus) + .setStartTime(DateUtils.of(activity.getStartTime())) + .setEndTime(DateUtils.of(activity.getEndTime())) + .setProcessInstanceId(activity.getProcessInstanceId()); + approvalNodes.add(callActivity); + } + }); + + // 按照时间排序 + approvalNodes.sort(Comparator.comparing(ActivityNode::getStartTime)); + return approvalNodes; + } + + /** + * 获得【进行中】的活动节点们 + */ + private List getRunApproveNodeList(Long startUserId, + BpmnModel bpmnModel, + ProcessDefinition processDefinition, + Map processVariables, + List activities, + List tasks) { + // 构建运行中的任务、子流程,基于 activityId 分组 + List runActivities = filterList(activities, activity -> activity.getEndTime() == null + && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); + Map> runningTaskMap = convertMultiMap(runActivities, + HistoricActivityInstance::getActivityId); + + // 按照 activityId 分组,构建 ApprovalNodeInfo 节点 + Map taskMap = convertMap(tasks, HistoricTaskInstance::getId); + return convertList(runningTaskMap.entrySet(), entry -> { + String activityId = entry.getKey(); + List taskActivities = entry.getValue(); + // 构建活动节点 + FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); + HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 + ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) + .setName(firstActivity.getActivityName()) + .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”和"子流程"的识别 + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) + .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) + .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime())) + .setTasks(new ArrayList<>()); + // 处理每个任务的 tasks 属性 + for (HistoricActivityInstance activity : taskActivities) { + HistoricTaskInstance task = taskMap.get(activity.getTaskId()); + // 特殊情况:子流程节点 ChildProcess 仅存在于 activity 中,并且没有自身的 task,需要跳过执行 + // TODO @芋艿:后续看看怎么优化! + if (task == null) { + continue; + } + activityNode.getTasks().add(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task)); + // 加签子任务,需要过滤掉已经完成的加签子任务 + List childrenTasks = filterList( + taskService.getAllChildrenTaskListByParentTaskId(activity.getTaskId(), tasks), + childTask -> childTask.getEndTime() == null); + if (CollUtil.isNotEmpty(childrenTasks)) { + activityNode.getTasks().addAll( + convertList(childrenTasks, BpmProcessInstanceConvert.INSTANCE::buildApprovalTaskInfo)); + } + } + // 处理每个任务的 candidateUsers 属性:如果是依次审批,需要预测它的后续审批人。因为 Task 是审批完一个,创建一个新的 Task + if (BpmnModelUtils.isSequentialUserTask(flowNode)) { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, flowNode.getId(), + startUserId, processDefinition.getId(), processVariables); + // 截取当前审批人位置后面的候选人,不包含当前审批人 + ActivityNodeTask approvalTaskInfo = CollUtil.getFirst(activityNode.getTasks()); + Assert.notNull(approvalTaskInfo, "任务不能为空"); + int index = CollUtil.indexOf(candidateUserIds, + userId -> ObjectUtils.equalsAny(userId, approvalTaskInfo.getOwner(), + approvalTaskInfo.getAssignee())); // 委派或者向前加签情况,需要先比较 owner + activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size())); + } + if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(activityNode.getNodeType())) { + activityNode.setProcessInstanceId(firstActivity.getProcessInstanceId()); + } + return activityNode; + }); + } + + /** + * 获得【预测(未来)】的活动节点们 + */ + private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, + BpmProcessDefinitionInfoDO processDefinitionInfo, + Map processVariables, + List activities) { + // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance + // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 + Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); + // 情况一:BPMN 设计器 + if (Objects.equals(BpmModelTypeEnum.BPMN.getType(), processDefinitionInfo.getModelType())) { + List flowElements = BpmnModelUtils.simulateProcess(bpmnModel, processVariables); + return convertList(flowElements, flowElement -> buildNotRunApproveNodeForBpmn(startUserId, bpmnModel, + processDefinitionInfo, processVariables, flowElement, runActivityIds)); + } + // 情况二:SIMPLE 设计器 + if (Objects.equals(BpmModelTypeEnum.SIMPLE.getType(), processDefinitionInfo.getModelType())) { + BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), + BpmSimpleModelNodeVO.class); + List simpleNodes = SimpleModelUtils.simulateProcess(simpleModel, processVariables); + return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(startUserId, bpmnModel, + processDefinitionInfo, processVariables, simpleNode, runActivityIds)); + } + throw new IllegalArgumentException("未知设计器类型:" + processDefinitionInfo.getModelType()); + } + + private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + BpmSimpleModelNodeVO node, Set runActivityIds) { + // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance + // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 + if (runActivityIds.contains(node.getId())) { + return null; + } + + ActivityNode activityNode = new ActivityNode().setId(node.getId()).setName(node.getName()) + .setNodeType(node.getType()).setCandidateStrategy(node.getCandidateStrategy()) + .setStatus(BpmTaskStatusEnum.NOT_START.getStatus()); + + // 1. 开始节点/审批节点 + if (ObjectUtils.equalsAny(node.getType(), + BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType(), + BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType(), + BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE.getType())) { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), + startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); + activityNode.setCandidateUserIds(candidateUserIds); + return activityNode; + } + + // 2. 结束节点 + if (BpmSimpleModelNodeTypeEnum.END_NODE.getType().equals(node.getType())) { + return activityNode; + } + + // 3. 抄送节点 + if (CollUtil.isEmpty(runActivityIds) && // 流程发起时:需要展示抄送节点,用于选择抄送人 + BpmSimpleModelNodeTypeEnum.COPY_NODE.getType().equals(node.getType())) { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), + startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); + activityNode.setCandidateUserIds(candidateUserIds); + return activityNode; + } + + // 4. 子流程节点 + if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType())) { + return activityNode; + } + return null; + } + + private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, + BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, + FlowElement node, Set runActivityIds) { + if (runActivityIds.contains(node.getId())) { + return null; + } + ActivityNode activityNode = new ActivityNode().setId(node.getId()) + .setStatus(BpmTaskStatusEnum.NOT_START.getStatus()); + + // 1. 开始节点 + if (node instanceof StartEvent) { + return activityNode.setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName()) + .setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()); + } + + // 2. 审批节点 + if (node instanceof UserTask) { + List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), + startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); + return activityNode.setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) + .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) + .setCandidateUserIds(candidateUserIds); + } + + // 3. 结束节点 + if (node instanceof EndEvent) { + return activityNode.setName(BpmSimpleModelNodeTypeEnum.END_NODE.getName()) + .setNodeType(BpmSimpleModelNodeTypeEnum.END_NODE.getType()); + } + return null; + } + + private List getTaskCandidateUserList(BpmnModel bpmnModel, String activityId, + Long startUserId, String processDefinitionId, Map processVariables) { + Set userIds = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, + startUserId, processDefinitionId, processVariables); + return new ArrayList<>(userIds); + } + + @Override + public BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id) { + // 1.1 获得流程实例 + HistoricProcessInstance processInstance = getHistoricProcessInstance(id); + if (processInstance == null) { + return null; + } + // 1.2 获得流程定义 + BpmnModel bpmnModel = processDefinitionService + .getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()); + if (bpmnModel == null) { + return null; + } + BpmSimpleModelNodeVO simpleModel = null; + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo( + processInstance.getProcessDefinitionId()); + if (processDefinitionInfo != null + && BpmModelTypeEnum.SIMPLE.getType().equals(processDefinitionInfo.getModelType())) { + simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), BpmSimpleModelNodeVO.class); + } + // 1.3 获得流程实例对应的活动实例列表 + 任务列表 + List activities = taskService.getActivityListByProcessInstanceId(id); + List tasks = taskService.getTaskListByProcessInstanceId(id, true); + + // 2.1 拼接进度信息 + Set unfinishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, + activityInstance -> activityInstance.getEndTime() == null); + Set finishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, + activityInstance -> activityInstance.getEndTime() != null + && ObjectUtil.notEqual(activityInstance.getActivityType(), + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); + Set finishedSequenceFlowActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, + activityInstance -> activityInstance.getEndTime() != null + && ObjectUtil.equals(activityInstance.getActivityType(), + BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); + // 特殊:会签情况下,会有部分已完成(审批)、部分未完成(待审批),此时需要 finishedTaskActivityIds 移除掉 + finishedTaskActivityIds.removeAll(unfinishedTaskActivityIds); + // 特殊:如果流程实例被拒绝,则需要计算是哪个活动节点。 + // 注意,只取最后一个。因为会存在多次拒绝的情况,拒绝驳回到指定节点 + Set rejectTaskActivityIds = CollUtil.newHashSet(); + if (BpmProcessInstanceStatusEnum.isRejectStatus(FlowableUtils.getProcessInstanceStatus(processInstance))) { + tasks.stream() + .filter(task -> BpmTaskStatusEnum.isRejectStatus(FlowableUtils.getTaskStatus(task))) + .max(Comparator.comparing(HistoricTaskInstance::getEndTime)) + .ifPresent(reject -> rejectTaskActivityIds.add(reject.getTaskDefinitionKey())); + finishedTaskActivityIds.removeAll(rejectTaskActivityIds); + } + + // 2.2 拼接基础信息 + Set userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds02(processInstance, tasks); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + return BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceBpmnModelView(processInstance, tasks, bpmnModel, + simpleModel, + unfinishedTaskActivityIds, finishedTaskActivityIds, finishedSequenceFlowActivityIds, + rejectTaskActivityIds, + userMap, deptMap); + } + + // ========== Update 写入相关方法 ========== + + @Override + @Transactional(rollbackFor = Exception.class) + public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { + // 获得流程定义 + ProcessDefinition definition = processDefinitionService + .getProcessDefinition(createReqVO.getProcessDefinitionId()); + // 发起流程 + return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, + createReqVO.getStartUserSelectAssignees()); + } + + @Override + public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { + return FlowableUtils.executeAuthenticatedUserId(userId, () -> { + // 获得流程定义 + ProcessDefinition definition = processDefinitionService + .getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); + // 发起流程 + return createProcessInstance0(userId, definition, createReqDTO.getVariables(), + createReqDTO.getBusinessKey(), + createReqDTO.getStartUserSelectAssignees()); + }); + } + + private String createProcessInstance0(Long userId, ProcessDefinition definition, + Map variables, String businessKey, + Map> startUserSelectAssignees) { + // 1.1 校验流程定义 + if (definition == null) { + throw exception(PROCESS_DEFINITION_NOT_EXISTS); + } + if (definition.isSuspended()) { + throw exception(PROCESS_DEFINITION_IS_SUSPENDED); + } + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService + .getProcessDefinitionInfo(definition.getId()); + if (processDefinitionInfo == null) { + throw exception(PROCESS_DEFINITION_NOT_EXISTS); + } + // 1.2 校验是否能够发起 + if (!processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId)) { + throw exception(PROCESS_INSTANCE_START_USER_CAN_START); + } + // 1.3 校验发起人自选审批人 + validateStartUserSelectAssignees(userId, definition, startUserSelectAssignees, variables); + + // 2. 创建流程实例 + if (variables == null) { + variables = new HashMap<>(); + } + FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 + BpmProcessInstanceStatusEnum.RUNNING.getStatus()); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 true,不影响没配置 skipExpression 的节点 + if (CollUtil.isNotEmpty(startUserSelectAssignees)) { + // 设置流程变量,发起人自选审批人 + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, + startUserSelectAssignees); + } + + // 3. 创建流程 + ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder() + .processDefinitionId(definition.getId()) + .businessKey(businessKey) + .variables(variables); + // 3.1 创建流程 ID + BpmModelMetaInfoVO.ProcessIdRule processIdRule = processDefinitionInfo.getProcessIdRule(); + if (processIdRule != null && Boolean.TRUE.equals(processIdRule.getEnable())) { + processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule)); + } + // 3.2 流程名称 + BpmModelMetaInfoVO.TitleSetting titleSetting = processDefinitionInfo.getTitleSetting(); + if (titleSetting != null && Boolean.TRUE.equals(titleSetting.getEnable())) { + AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); + Map cloneVariables = new HashMap<>(variables); + cloneVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, user.getNickname()); + cloneVariables.put(BpmnVariableConstants.PROCESS_START_TIME, DateUtil.now()); + cloneVariables.put(BpmnVariableConstants.PROCESS_DEFINITION_NAME, definition.getName().trim()); + processInstanceBuilder.name(StrUtil.format(titleSetting.getTitle(), cloneVariables)); + } else { + processInstanceBuilder.name(definition.getName().trim()); + } + // 3.3 发起流程实例 + ProcessInstance instance = processInstanceBuilder.start(); + return instance.getId(); + } + + private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, + Map> startUserSelectAssignees, + Map variables) { + // 1. 获取预测的节点信息 + BpmApprovalDetailRespVO detailRespVO = getApprovalDetail(userId, new BpmApprovalDetailReqVO() + .setProcessDefinitionId(definition.getId()) + .setProcessVariables(variables)); + List activityNodes = detailRespVO.getActivityNodes(); + if (CollUtil.isEmpty(activityNodes)) { + return; + } + + // 2.1 移除掉不是发起人自选审批人节点 + activityNodes.removeIf(task -> + ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); + // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 + activityNodes.forEach(task -> { + List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; + if (CollUtil.isEmpty(assignees)) { + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, task.getName()); + } + Map userMap = adminUserApi.getUserMap(assignees); + assignees.forEach(assignee -> { + if (userMap.get(assignee) == null) { + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, task.getName(), assignee); + } + }); + }); + } + + @Override + public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { + // 1.1 校验流程实例存在 + ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); + } + // 1.2 只能取消自己的 + if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { + throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); + } + // 1.3 校验允许撤销审批中的申请 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService + .getProcessDefinitionInfo(instance.getProcessDefinitionId()); + Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo); + if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消 + && Boolean.FALSE.equals(processDefinitionInfo.getAllowCancelRunningProcess())) { + throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW); + } + // 1.4 子流程不允许取消 + if (StrUtil.isNotBlank(instance.getSuperExecutionId())) { + throw exception(PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW); + } + + // 2. 取消流程 + updateProcessInstanceCancel(cancelReqVO.getId(), + BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); + } + + @Override + public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { + // 1.1 校验流程实例存在 + ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); + } + + // 2. 取消流程 + AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); + updateProcessInstanceCancel(cancelReqVO.getId(), + BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); + } + + private void updateProcessInstanceCancel(String id, String reason) { + // 1. 更新流程实例 status + runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, + BpmProcessInstanceStatusEnum.CANCEL.getStatus()); + runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON, reason); + + // 2. 取消所有子流程 + List childProcessInstances = runtimeService.createProcessInstanceQuery() + .superProcessInstanceId(id).list(); + childProcessInstances.forEach(processInstance -> updateProcessInstanceCancel( + processInstance.getProcessInstanceId(), BpmReasonEnum.CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS.getReason())); + + // 3. 结束流程 + taskService.moveTaskToEnd(id, reason); + } + + @Override + public void updateProcessInstanceReject(ProcessInstance processInstance, String reason) { + runtimeService.setVariable(processInstance.getProcessInstanceId(), + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, + BpmProcessInstanceStatusEnum.REJECT.getStatus()); + runtimeService.setVariable(processInstance.getProcessInstanceId(), + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON, + BpmReasonEnum.REJECT_TASK.format(reason)); + } + + @Override + public void updateProcessInstanceVariables(String id, Map variables) { + runtimeService.setVariables(id, variables); + } + + @Override + public void removeProcessInstanceVariables(String id, Collection variableNames) { + runtimeService.removeVariables(id, variableNames); + } + + // ========== Event 事件相关方法 ========== + + @Override + public void processProcessInstanceCompleted(ProcessInstance instance) { + // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 + FlowableUtils.execute(instance.getTenantId(), () -> { + // 1.1 获取当前状态 + Integer status = (Integer) instance.getProcessVariables() + .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); + String reason = (String) instance.getProcessVariables() + .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON); + // 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态 + // 为什么这么处理?因为流程完成,并且完成了,说明审批通过了 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { + status = BpmProcessInstanceStatusEnum.APPROVE.getStatus(); + runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, + status); + } + + // 2. 发送对应的消息通知 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { + messageService.sendMessageWhenProcessInstanceApprove( + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); + } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) { + messageService.sendMessageWhenProcessInstanceReject( + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason)); + } + + // 3. 发送流程实例的状态事件 + processInstanceEventPublisher.sendProcessInstanceResultEvent( + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); + + // 4. 流程后置通知 + if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. + getProcessDefinitionInfo(instance.getProcessDefinitionId()); + if (ObjUtil.isNotNull(processDefinitionInfo) && + ObjUtil.isNotNull(processDefinitionInfo.getProcessAfterTriggerSetting())) { + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting(); + + BpmHttpRequestUtils.executeBpmHttpRequest(instance, + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); + } + } + }); + } + + @Override + public void processProcessInstanceCreated(ProcessInstance instance) { + // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 + FlowableUtils.execute(instance.getTenantId(), () -> { + // 流程前置通知 + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. + getProcessDefinitionInfo(instance.getProcessDefinitionId()); + if (ObjUtil.isNull(processDefinitionInfo) || + ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) { + return; + } + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting(); + BpmHttpRequestUtils.executeBpmHttpRequest(instance, + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); + }); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java new file mode 100644 index 0000000..8211bfe --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java @@ -0,0 +1,316 @@ +package com.zt.plat.module.bpm.service.task; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.collection.CollectionUtils; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.*; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum; +import jakarta.validation.Valid; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskInfo; +import org.flowable.task.api.history.HistoricTaskInstance; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * 流程任务实例 Service 接口 + * + * @author jason + * @author ZT + */ +public interface BpmTaskService { + + // ========== Query 查询相关方法 ========== + + /** + * 获得待办的流程任务分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页请求 + * @return 流程任务分页 + */ + PageResult getTaskTodoPage(Long userId, BpmTaskPageReqVO pageReqVO); + + /** + * 获得用户(待办)的任务: + * 1. 根据 taskId 查询待办任务 + * 2. 如果任务不存在(或者已审核),获取指定流程下,首个需要处理任务 + * + * @param userId 用户编号 + * @param taskId 任务编号 + * @param processInstanceId 流程实例编号 + * @return 待办任务 + */ + BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId); + + /** + * 获得已办的流程任务分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页请求 + * @return 流程任务分页 + */ + PageResult getTaskDonePage(Long userId, BpmTaskPageReqVO pageReqVO); + + /** + * 获得全部的流程任务分页 + * + * @param userId 用户编号 + * @param pageReqVO 分页请求 + * @return 流程任务分页 + */ + PageResult getTaskPage(Long userId, BpmTaskPageReqVO pageReqVO); + + /** + * 获得流程任务 Map + * + * @param processInstanceIds 流程实例的编号数组 + * @return 流程任务 Map + */ + default Map> getTaskMapByProcessInstanceIds(List processInstanceIds) { + return CollectionUtils.convertMultiMap(getTasksByProcessInstanceIds(processInstanceIds), + Task::getProcessInstanceId); + } + + /** + * 获得流程任务列表 + * + * @param processInstanceIds 流程实例的编号数组 + * @return 流程任务列表 + */ + List getTasksByProcessInstanceIds(List processInstanceIds); + + /** + * 获得指定流程实例的流程任务列表,包括所有状态的 + * + * @param processInstanceId 流程实例的编号 + * @param asc 是否升序 + * @return 流程任务列表 + */ + List getTaskListByProcessInstanceId(String processInstanceId, Boolean asc); + + /** + * 校验任务是否存在,并且是否是分配给自己的任务 + * + * @param userId 用户 id + * @param taskId task id + */ + Task validateTask(Long userId, String taskId); + + /** + * 获取任务 + * + * @param id 任务编号 + * @return 任务 + */ + Task getTask(String id); + + /** + * 获取历史任务 + * + * @param id 任务编号 + * @return 历史任务 + */ + HistoricTaskInstance getHistoricTask(String id); + + /** + * 获取历史任务列表 + * + * @param taskIds 任务编号集合 + * @return 历史任务列表 + */ + List getHistoricTasks(Collection taskIds); + + /** + * 根据条件查询正在进行中的任务 + * + * @param processInstanceId 流程实例编号,不允许为空 + * @param assigned 是否分配了审批人,允许空 + * @param taskDefineKey 任务定义 Key,允许空 + */ + List getRunningTaskListByProcessInstanceId(String processInstanceId, + Boolean assigned, + String taskDefineKey); + + /** + * 获取当前任务的可退回的 UserTask 集合 + * + * @param id 当前的任务 ID + * @return 可以退回的节点列表 + */ + List getUserTaskListByReturn(String id); + + /** + * 获取指定任务的子任务列表(多层) + * + * @param parentTaskId 父任务 ID + * @param tasks 任务列表 + * @return 子任务列表 + */ + List getAllChildrenTaskListByParentTaskId(String parentTaskId, List tasks); + + /** + * 获取指定任务的子任务列表 + * + * @param parentTaskId 父任务ID + * @return 子任务列表 + */ + List getTaskListByParentTaskId(String parentTaskId); + + /** + * 获得指定流程实例的活动实例列表 + * + * @param processInstanceId 流程实例的编号 + * @return 活动实例列表 + */ + List getActivityListByProcessInstanceId(String processInstanceId); + + /** + * 获得执行编号对应的活动实例 + * + * @param executionId 执行编号 + * @return 活动实例 + */ + List getHistoricActivityListByExecutionId(String executionId); + + // ========== Update 写入相关方法 ========== + + /** + * 通过任务 + * + * @param userId 用户编号 + * @param reqVO 通过请求 + */ + void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO); + + /** + * 不通过任务 + * + * @param userId 用户编号 + * @param reqVO 不通过请求 + */ + void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO); + + /** + * 将流程任务分配给指定用户 + * + * @param userId 用户编号 + * @param reqVO 分配请求 + */ + void transferTask(Long userId, BpmTaskTransferReqVO reqVO); + + /** + * 将指定流程实例的、进行中的流程任务,移动到结束节点 + * + * @param processInstanceId 流程编号 + * @param reason 原因 + */ + void moveTaskToEnd(String processInstanceId, String reason); + + /** + * 将任务退回到指定的 targetDefinitionKey 位置 + * + * @param userId 用户编号 + * @param reqVO 退回的任务key和当前所在的任务ID + */ + void returnTask(Long userId, BpmTaskReturnReqVO reqVO); + + /** + * 将指定任务委派给其他人处理,等接收人处理后再回到原审批人手中审批 + * + * @param userId 用户编号 + * @param reqVO 被委派人和被委派的任务编号理由参数 + */ + void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO); + + /** + * 任务加签 + * + * @param userId 被加签的用户和任务 ID,加签类型 + * @param reqVO 当前用户 ID + */ + void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO); + + /** + * 任务减签 + * + * @param userId 当前用户ID + * @param reqVO 被减签的任务 ID,理由 + */ + void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO); + + /** + * 抄送任务 + * + * @param userId 用户编号 + * @param reqVO 通过请求 + */ + void copyTask(Long userId, @Valid BpmTaskCopyReqVO reqVO); + + // ========== Event 事件相关方法 ========== + + /** + * 处理 Task 创建事件,目前是 + *

+ * 1. 更新它的状态为审批中 + * 2. 处理自动通过的情况,例如说:1)无审批人时,是否自动通过、不通过;2)非【人工审核】时,是否自动通过、不通过 + *

+ * 注意:它的触发时机,晚于 {@link #processTaskAssigned(Task)} 之后 + * + * @param task 任务实体 + */ + void processTaskCreated(Task task); + + /** + * 处理 Task 取消事件,目前是更新它的状态为已取消 + * + * @param taskId 任务的编号 + */ + void processTaskCanceled(String taskId); + + /** + * 处理 Task 设置审批人事件,目前是发送审批消息 + * + * @param task 任务实体 + */ + void processTaskAssigned(Task task); + + /** + * 处理 Task 完成事件,目前是发送任务后置通知 + * + * @param task 任务实体 + */ + void processTaskCompleted(Task task); + + /** + * 处理 Task 审批超时事件,可能会处理多个当前审批中的任务 + * + * @param processInstanceId 流程示例编号 + * @param taskDefineKey 任务 Key + * @param handlerType 处理类型,参见 {@link BpmUserTaskTimeoutHandlerTypeEnum} + */ + void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType); + + /** + * 处理 ChildProcess 子流程的审批超时事件 + * + * @param processInstanceId 流程示例编号 + * @param taskDefineKey 任务 Key + */ + void processChildProcessTimeout(String processInstanceId, String taskDefineKey); + + /** + * 触发流程任务 (ReceiveTask) 的执行 + *

+ * 1. Simple 模型 HTTP 回调请求触发器节点的回调,触发流程继续执行 + * 2. Simple 模型延迟器节点,到时触发流程继续执行 + * + * @param processInstanceId 流程示例编号 + * @param taskDefineKey 任务 Key + */ + void triggerTask(String processInstanceId, String taskDefineKey); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java new file mode 100644 index 0000000..42ef5d4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java @@ -0,0 +1,1535 @@ +package com.zt.plat.module.bpm.service.task; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.*; +import cn.hutool.extra.spring.SpringUtil; +import cn.hutool.json.JSONUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.date.DateUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.framework.common.util.object.ObjectUtils; +import com.zt.plat.framework.common.util.object.PageUtils; +import com.zt.plat.framework.datapermission.core.annotation.DataPermission; +import com.zt.plat.framework.web.core.util.WebFrameworkUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.*; +import com.zt.plat.module.bpm.convert.task.BpmTaskConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.definition.*; +import com.zt.plat.module.bpm.enums.task.BpmCommentTypeEnum; +import com.zt.plat.module.bpm.enums.task.BpmReasonEnum; +import com.zt.plat.module.bpm.enums.task.BpmTaskSignTypeEnum; +import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import com.zt.plat.module.bpm.service.definition.BpmModelService; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.message.BpmMessageService; +import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.*; +import org.flowable.engine.HistoryService; +import org.flowable.engine.ManagementService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.runtime.ActivityInstance; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.DelegationState; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskInfo; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; +import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +import java.util.*; +import java.util.stream.Stream; + +import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; +import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; +import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; + +/** + * 流程任务实例 Service 实现类 + * + * @author ZT + * @author jason + */ +@Slf4j +@Service +public class BpmTaskServiceImpl implements BpmTaskService { + + @Resource + private TaskService taskService; + @Resource + private HistoryService historyService; + @Resource + private RuntimeService runtimeService; + @Resource + private ManagementService managementService; + + @Resource + private BpmProcessInstanceService processInstanceService; + @Resource + private BpmProcessDefinitionService bpmProcessDefinitionService; + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; + @Resource + private BpmModelService modelService; + @Resource + private BpmMessageService messageService; + @Resource + private BpmFormService formService; + + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + // ========== Query 查询相关方法 ========== + + @Override + public PageResult getTaskTodoPage(Long userId, BpmTaskPageReqVO pageVO) { + TaskQuery taskQuery = taskService.createTaskQuery() + .taskAssignee(String.valueOf(userId)) // 分配给自己 + .active() + .includeProcessVariables() + .orderByTaskCreateTime().desc(); // 创建时间倒序 + if (StrUtil.isNotBlank(pageVO.getName())) { + taskQuery.taskNameLike("%" + pageVO.getName() + "%"); + } + if (StrUtil.isNotEmpty(pageVO.getCategory())) { + taskQuery.taskCategory(pageVO.getCategory()); + } + if (StrUtil.isNotEmpty(pageVO.getProcessDefinitionKey())) { + taskQuery.processDefinitionKey(pageVO.getProcessDefinitionKey()); + } + if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { + taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); + taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); + } + long count = taskQuery.count(); + if (count == 0) { + return PageResult.empty(); + } + List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + return new PageResult<>(tasks, count); + } + + @Override + public BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId) { + // 1.1 获取指定的用户待办任务 + Task todoTask = getMyTodoTask(userId, taskId); + // 1.2 获取不到,则获取该流程实例下,第一个用户的待办任务 + if (todoTask == null) { + todoTask = getMyFirstTodoTask(userId, processInstanceId); + } + if (todoTask == null) { + return null; + } + + // 2. 查询该任务的子任务 + List childrenTasks = getAllChildrenTaskListByParentTaskId(todoTask.getId(), CollUtil.newArrayList(todoTask)); + + // 3. 转换返回 + BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(todoTask.getProcessDefinitionId()); + Map buttonsSetting = BpmnModelUtils.parseButtonsSetting( + bpmnModel, todoTask.getTaskDefinitionKey()); + Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey()); + Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey()); + Integer nodeType = parseNodeType(BpmnModelUtils.getFlowElementById(bpmnModel, todoTask.getTaskDefinitionKey())); + + // 4. 任务表单 + BpmFormDO taskForm = null; + if (StrUtil.isNotBlank(todoTask.getFormKey())) { + try { + Long formId = Long.parseLong(todoTask.getFormKey()); + taskForm = formService.getForm(formId); + } catch (NumberFormatException e) { + // 如果 formKey 不是数字(比如是URL),则不处理表单 + taskForm = null; + } + } + + return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm) + .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire); + } + + /** + * 获得用户指定 taskId 任务编号的“待办”(未审批、且可审核)的任务 + * + * @param userId 用户编号 + * @param taskId 任务编号 + * @return 任务 + */ + private Task getMyTodoTask(Long userId, String taskId) { + if (StrUtil.isEmpty(taskId)) { + return null; + } + Task task = getTask(taskId); + if (task == null) { + return null; + } + if (!isAssignUserTask(userId, task) && !isAddSignUserTask(userId, task)) { + return null; + } + return task; + } + + /** + * 获得用户指定 processInstanceId 流程编号下的首个“待办”(未审批、且可审核)的任务 + * + * @param userId 用户编号 + * @param processInstanceId 流程编号 + * @return 任务 + */ + private Task getMyFirstTodoTask(Long userId, String processInstanceId) { + if (processInstanceId == null) { + return null; + } + // 1. 查询所有任务 + List tasks = taskService.createTaskQuery() + .active() + .processInstanceId(processInstanceId) + .includeTaskLocalVariables() + .includeProcessVariables() + .orderByTaskCreateTime().asc() // 按创建时间升序 + .list(); + + // 2. 查询我的首个任务 + return CollUtil.findOne(tasks, task -> { + return isAssignUserTask(userId, task) // 当前用户为审批人 + || isAddSignUserTask(userId, task); // 当前用户为加签人(为了减签) + }); + } + + @Override + public PageResult getTaskDonePage(Long userId, BpmTaskPageReqVO pageVO) { + HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery() + .finished() // 已完成 + .taskAssignee(String.valueOf(userId)) // 分配给自己 + .includeTaskLocalVariables() + .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序 + if (StrUtil.isNotBlank(pageVO.getName())) { + taskQuery.taskNameLike("%" + pageVO.getName() + "%"); + } + if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { + taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); + taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); + } + // 执行查询 + long count = taskQuery.count(); + if (count == 0) { + return PageResult.empty(); + } + List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + + // 特殊:强制移除自动完成的“发起人”节点 + // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤 + tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID)); + return new PageResult<>(tasks, count); + } + + @Override + public PageResult getTaskPage(Long userId, BpmTaskPageReqVO pageVO) { + HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery() + .includeTaskLocalVariables() + .taskTenantId(FlowableUtils.getTenantId()) + .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序 + if (StrUtil.isNotBlank(pageVO.getName())) { + taskQuery.taskNameLike("%" + pageVO.getName() + "%"); + } + if (StrUtil.isNotEmpty(pageVO.getCategory())) { + taskQuery.taskCategory(pageVO.getCategory()); + } + if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { + taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); + taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); + } + // 执行查询 + long count = taskQuery.count(); + if (count == 0) { + return PageResult.empty(); + } + List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); + return new PageResult<>(tasks, count); + } + + @Override + public List getTasksByProcessInstanceIds(List processInstanceIds) { + if (CollUtil.isEmpty(processInstanceIds)) { + return Collections.emptyList(); + } + return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list(); + } + + @Override + public List getTaskListByProcessInstanceId(String processInstanceId, Boolean asc) { + HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery() + .includeTaskLocalVariables() + .processInstanceId(processInstanceId); + if (Boolean.TRUE.equals(asc)) { + query.orderByHistoricTaskInstanceStartTime().asc(); + } else { + query.orderByHistoricTaskInstanceStartTime().desc(); + } + return query.list(); + } + + @Override + public Task validateTask(Long userId, String taskId) { + Task task = validateTaskExist(taskId); + // 为什么判断 assignee 非空的情况下? + // 例如说:在审批人为空时,我们会有“自动审批通过”的策略,此时 userId 为 null,允许通过 + if (StrUtil.isNotBlank(task.getAssignee()) + && ObjectUtil.notEqual(userId, NumberUtils.parseLong(task.getAssignee()))) { + throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); + } + return task; + } + + private Task validateTaskExist(String id) { + Task task = getTask(id); + if (task == null) { + throw exception(TASK_NOT_EXISTS); + } + return task; + } + + @Override + public Task getTask(String id) { + return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult(); + } + + @Override + public HistoricTaskInstance getHistoricTask(String id) { + return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult(); + } + + @Override + public List getHistoricTasks(Collection taskIds) { + return historyService.createHistoricTaskInstanceQuery().taskIds(taskIds).includeTaskLocalVariables().list(); + } + + @Override + public List getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String defineKey) { + Assert.notNull(processInstanceId, "processInstanceId 不能为空"); + TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId).active() + .includeTaskLocalVariables(); + if (BooleanUtil.isTrue(assigned)) { + taskQuery.taskAssigned(); + } else if (BooleanUtil.isFalse(assigned)) { + taskQuery.taskUnassigned(); + } + if (StrUtil.isNotEmpty(defineKey)) { + taskQuery.taskDefinitionKey(defineKey); + } + return taskQuery.list(); + } + + @Override + public List getUserTaskListByReturn(String id) { + // 1.1 校验当前任务 task 存在 + Task task = validateTaskExist(id); + // 1.2 根据流程定义获取流程模型信息 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); + FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + if (source == null) { + throw exception(TASK_NOT_EXISTS); + } + + // 2.1 查询该任务的前置任务节点的 key 集合 + List previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null); + if (CollUtil.isEmpty(previousUserList)) { + return Collections.emptyList(); + } + // 2.2 过滤:只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回 + previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null)); + return previousUserList; + } + + @Override + public List getAllChildrenTaskListByParentTaskId(String parentTaskId, List tasks) { + if (CollUtil.isEmpty(tasks)) { + return Collections.emptyList(); + } + Map> parentTaskMap = convertMultiMap( + filterList(tasks, task -> StrUtil.isNotEmpty(task.getParentTaskId())), TaskInfo::getParentTaskId); + if (CollUtil.isEmpty(parentTaskMap)) { + return Collections.emptyList(); + } + + List result = new ArrayList<>(); + // 1. 递归获取子级 + Stack stack = new Stack<>(); + stack.push(parentTaskId); + // 2. 递归遍历 + for (int i = 0; i < Short.MAX_VALUE; i++) { + if (stack.isEmpty()) { + break; + } + // 2.1 获取子任务们 + String taskId = stack.pop(); + List childTaskList = filterList(tasks, task -> StrUtil.equals(task.getParentTaskId(), taskId)); + // 2.2 如果非空,则添加到 stack 进一步递归 + if (CollUtil.isNotEmpty(childTaskList)) { + stack.addAll(convertList(childTaskList, TaskInfo::getId)); + result.addAll(childTaskList); + } + } + return result; + } + + /** + * 获得所有子任务列表 + * + * @param parentTask 父任务 + * @return 所有子任务列表 + */ + private List getAllChildTaskList(Task parentTask) { + List result = new ArrayList<>(); + // 1. 递归获取子级 + Stack stack = new Stack<>(); + stack.push(parentTask); + // 2. 递归遍历 + for (int i = 0; i < Short.MAX_VALUE; i++) { + if (stack.isEmpty()) { + break; + } + // 2.1 获取子任务们 + Task task = stack.pop(); + List childTaskList = getTaskListByParentTaskId(task.getId()); + // 2.2 如果非空,则添加到 stack 进一步递归 + if (CollUtil.isNotEmpty(childTaskList)) { + stack.addAll(childTaskList); + result.addAll(childTaskList); + } + } + return result; + } + + @Override + public List getTaskListByParentTaskId(String parentTaskId) { + String tableName = managementService.getTableName(TaskEntity.class); + // taskService.createTaskQuery() 没有 parentId 参数,所以写 sql 查询 + String sql = "select ID_,NAME_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}"; + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list(); + } + + /** + * 获取子任务个数 + * + * @param parentTaskId 父任务 ID + * @return 剩余子任务个数 + */ + private Long getTaskCountByParentTaskId(String parentTaskId) { + String tableName = managementService.getTableName(TaskEntity.class); + String sql = "SELECT COUNT(1) from " + tableName + " WHERE PARENT_TASK_ID_=#{parentTaskId}"; + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count(); + } + + /** + * 获得任务根任务的父任务编号 + * + * @param task 任务 + * @return 根任务的父任务编号 + */ + private String getTaskRootParentId(Task task) { + if (task == null || task.getParentTaskId() == null) { + return null; + } + for (int i = 0; i < Short.MAX_VALUE; i++) { + Task parentTask = getTask(task.getParentTaskId()); + if (parentTask == null) { + return null; + } + if (parentTask.getParentTaskId() == null) { + return parentTask.getId(); + } + task = parentTask; + } + throw new IllegalArgumentException(String.format("Task(%s) 层级过深,无法获取父节点编号", task.getId())); + } + + @Override + public List getActivityListByProcessInstanceId(String processInstanceId) { + return historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId) + .orderByHistoricActivityInstanceStartTime().asc().list(); + } + + @Override + public List getHistoricActivityListByExecutionId(String executionId) { + return historyService.createHistoricActivityInstanceQuery().executionId(executionId).list(); + } + + /** + * 判断指定用户,是否是当前任务的审批人 + * + * @param userId 用户编号 + * @param task 任务 + * @return 是否 + */ + private boolean isAssignUserTask(Long userId, Task task) { + Long assignee = NumberUtil.parseLong(task.getAssignee(), null); + return ObjectUtil.equals(userId, assignee); + } + + /** + * 判断指定用户,是否是当前任务的拥有人 + * + * @param userId 用户编号 + * @param task 任务 + * @return 是否 + */ + private boolean isOwnerUserTask(Long userId, Task task) { + Long assignee = NumberUtil.parseLong(task.getOwner(), null); + return ObjectUtil.equal(userId, assignee); + } + + /** + * 判断指定用户,是否是当前任务的加签人 + * + * @param userId 用户 Id + * @param task 任务 + * @return 是否 + */ + private boolean isAddSignUserTask(Long userId, Task task) { + return (isAssignUserTask(userId, task) || isOwnerUserTask(userId, task)) + && BpmTaskSignTypeEnum.of(task.getScopeType()) != null; + } + + // ========== Update 写入相关方法 ========== + + @Override + @Transactional(rollbackFor = Exception.class) + public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) { + // 1.1 校验任务存在 + Task task = validateTask(userId, reqVO.getId()); + // 1.2 校验流程实例存在 + ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + // 1.3 校验签名 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); + Boolean signEnable = parseSignEnable(bpmnModel, task.getTaskDefinitionKey()); + if (signEnable && StrUtil.isEmpty(reqVO.getSignPicUrl())) { + throw exception(TASK_SIGNATURE_NOT_EXISTS); + } + // 1.4 校验审批意见 + Boolean reasonRequire = parseReasonRequire(bpmnModel, task.getTaskDefinitionKey()); + if (reasonRequire && StrUtil.isEmpty(reqVO.getReason())) { + throw exception(TASK_REASON_REQUIRE); + } + + // 情况一:被委派的任务,不调用 complete 去完成任务 + if (DelegationState.PENDING.equals(task.getDelegationState())) { + approveDelegateTask(reqVO, task); + return; + } + + // 情况二:审批有【后】加签的任务 + if (BpmTaskSignTypeEnum.AFTER.getType().equals(task.getScopeType())) { + approveAfterSignTask(task, reqVO); + return; + } + + // 情况三:审批普通的任务。大多数情况下,都是这样 + // 2.1 更新 task 状态、原因、签字 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason()); + if (signEnable) { + taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_SIGN_PIC_URL, reqVO.getSignPicUrl()); + } + // 2.2 添加评论 + taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), + BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); + + // 3. 设置流程变量。如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时 variables 一定会为空 + // 场景一:A 节点发起,B 节点表单无可编辑字段,审批通过时,C 节点需要流程变量获取下一个执行节点,但因为 B 节点无可编辑的字段,variables 为空,流程可能出现问题。 + // 场景二:A 节点发起,B 节点只有某一个字段可编辑(比如 day),但 C 节点需要多个节点。 + // (比如 work + day 变量,在发起时填写,因为 B 节点只有 day 的编辑权限,在审批后,variables 会缺少 work 的值) + Map processVariables = new HashMap<>(); + if (CollUtil.isNotEmpty(instance.getProcessVariables())) { // 获取历史中流程变量 + processVariables.putAll(instance.getProcessVariables()); + } + if (CollUtil.isNotEmpty(reqVO.getVariables())) { // 合并前端传递的流程变量,以前端为准 + processVariables.putAll(reqVO.getVariables()); + } + + // 如果任务变量不为空,设置任务级别的变量实例信息 + if (CollUtil.isNotEmpty(reqVO.getTaskVariables())) { + Map taskVariables = new HashMap<>(); + taskVariables.put("taskVariables", JSONUtil.toJsonStr(reqVO.getTaskVariables())); + taskService.setVariablesLocal(task.getId(), taskVariables); + } + + // 4. 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 + Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables, + bpmnModel, reqVO.getNextAssignees(), instance); + runtimeService.setVariables(task.getProcessInstanceId(), variables); + + // 5. 调用 BPM complete 去完成任务 + taskService.complete(task.getId(), variables, true); + + // 【加签专属】处理加签任务 + handleParentTaskIfSign(task.getParentTaskId()); + } + + /** + * 校验选择的下一个节点的审批人,是否合法 + * + * 1. 是否有漏选:没有选择审批人 + * 2. 是否有多选:非下一个节点 + * + * @param taskDefinitionKey 当前任务节点标识 + * @param variables 流程变量 + * @param bpmnModel 流程模型 + * @param nextAssignees 下一个节点审批人集合(参数) + * @param processInstance 流程实例 + */ + @SuppressWarnings("unchecked") + private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, + Map> nextAssignees, ProcessInstance processInstance) { + // simple 设计器第一个节点默认为发起人节点,不校验是否存在审批人 + if (Objects.equals(taskDefinitionKey, START_USER_NODE_ID)) { + return variables; + } + // 1. 获取下一个将要执行的节点集合 + FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); + List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); + + // 2. 校验选择的下一个节点的审批人,是否合法 + for (FlowNode nextFlowNode : nextFlowNodes) { + Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); + // 2.1 情况一:如果节点中的审批人策略为 发起人自选 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { + // 特殊:如果当前节点已经存在审批人,则不允许覆盖 + Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); + if (startUserSelectAssignees != null && CollUtil.isNotEmpty(startUserSelectAssignees.get(nextFlowNode.getId()))) { + continue; + } + // 如果节点存在,但未配置审批人 + List assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; + if (CollUtil.isEmpty(assignees)) { + throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); + } + + // 设置 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES + if (startUserSelectAssignees == null) { + startUserSelectAssignees = new HashMap<>(); + } + startUserSelectAssignees.put(nextFlowNode.getId(), assignees); + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); + continue; + } + + // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 + if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { + // 如果节点存在,但未配置审批人 + Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); + List assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; + if (CollUtil.isEmpty(assignees)) { + throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); + } + + // 设置 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES + if (approveUserSelectAssignees == null) { + approveUserSelectAssignees = new HashMap<>(); + } + approveUserSelectAssignees.put(nextFlowNode.getId(), assignees); + Map> existingApproveUserSelectAssignees = (Map>) variables.get( + BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); + if (CollUtil.isNotEmpty(existingApproveUserSelectAssignees)) { + approveUserSelectAssignees.putAll(existingApproveUserSelectAssignees); + } + variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, approveUserSelectAssignees); + } + } + return variables; + } + + /** + * 审批通过存在“后加签”的任务。 + *

+ * 注意:该任务不能马上完成,需要一个中间状态(APPROVING),并激活剩余所有子任务(PROCESS)为可审批处理 + * 如果马上完成,则会触发下一个任务,甚至如果没有下一个任务则流程实例就直接结束了! + * + * @param task 当前任务 + * @param reqVO 前端请求参数 + */ + private void approveAfterSignTask(Task task, BpmTaskApproveReqVO reqVO) { + // 更新父 task 状态 + 原因 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVING.getStatus(), reqVO.getReason()); + + // 2. 激活子任务 + List childrenTaskList = getTaskListByParentTaskId(task.getId()); + for (Task childrenTask : childrenTaskList) { + taskService.resolveTask(childrenTask.getId()); + // 更新子 task 状态 + updateTaskStatus(childrenTask.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); + } + } + + /** + * 如果父任务是有前后【加签】的任务,如果它【加签】出来的子任务都被处理,需要处理父任务: + *

+ * 1. 如果是【向前】加签,则需要重新激活父任务,让它可以被审批 + * 2. 如果是【向后】加签,则需要完成父任务,让它完成审批 + * + * @param parentTaskId 父任务编号 + */ + private void handleParentTaskIfSign(String parentTaskId) { + if (StrUtil.isBlank(parentTaskId)) { + return; + } + // 1.1 判断是否还有子任务。如果没有,就不处理 + Long childrenTaskCount = getTaskCountByParentTaskId(parentTaskId); + if (childrenTaskCount > 0) { + return; + } + // 1.2 只处理加签的父任务 + Task parentTask = validateTaskExist(parentTaskId); + String scopeType = parentTask.getScopeType(); + if (BpmTaskSignTypeEnum.of(scopeType) == null) { + return; + } + + // 2. 子任务已处理完成,清空 scopeType 字段,修改 parentTask 信息,方便后续可以继续向前后向后加签 + TaskEntityImpl parentTaskImpl = (TaskEntityImpl) parentTask; + parentTaskImpl.setScopeType(null); + taskService.saveTask(parentTaskImpl); + + // 3.1 情况一:处理向【向前】加签 + if (BpmTaskSignTypeEnum.BEFORE.getType().equals(scopeType)) { + // 3.1.1 owner 重新赋值给父任务的 assignee,这样它就可以被审批 + taskService.resolveTask(parentTaskId); + // 3.1.2 更新流程任务 status + updateTaskStatus(parentTaskId, BpmTaskStatusEnum.RUNNING.getStatus()); + // 3.2 情况二:处理向【向后】加签 + } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(scopeType)) { + // 只有 parentTask 处于 APPROVING 的情况下,才可以继续 complete 完成 + // 否则,一个未审批的 parentTask 任务,在加签出来的任务都被减签的情况下,就直接完成审批,这样会存在问题 + Integer status = (Integer) parentTask.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); + if (ObjectUtil.notEqual(status, BpmTaskStatusEnum.APPROVING.getStatus())) { + return; + } + // 3.2.2 完成自己(因为它已经没有子任务,所以也可以完成) + updateTaskStatus(parentTaskId, BpmTaskStatusEnum.APPROVE.getStatus()); + taskService.complete(parentTaskId); + } + + // 4. 递归处理父任务 + handleParentTaskIfSign(parentTask.getParentTaskId()); + } + + /** + * 审批被委派的任务 + * + * @param reqVO 前端请求参数,包含当前任务ID,审批意见等 + * @param task 当前被审批的任务 + */ + private void approveDelegateTask(BpmTaskApproveReqVO reqVO, Task task) { + // 1. 添加审批意见 + AdminUserRespDTO currentUser = adminUserApi.getUser(WebFrameworkUtils.getLoginUserId()).getCheckedData(); + AdminUserRespDTO ownerUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())).getCheckedData(); // 发起委托的用户 + Assert.notNull(ownerUser, "委派任务找不到原审批人,需要检查数据"); + taskService.addComment(reqVO.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_END.getType(), + BpmCommentTypeEnum.DELEGATE_END.formatComment(currentUser.getNickname(), ownerUser.getNickname(), reqVO.getReason())); + + // 2.1 调用 resolveTask 完成任务。 + // 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()):将 owner 设置为 assignee + taskService.resolveTask(task.getId()); + // 2.2 更新 task 状态 + 原因 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus(), reqVO.getReason()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) { + // 1.1 校验任务存在 + Task task = validateTask(userId, reqVO.getId()); + // 1.2 校验流程实例存在 + ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw exception(PROCESS_INSTANCE_NOT_EXISTS); + } + + // 2.1 更新流程任务为不通过 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason()); + // 2.2 添加流程评论 + taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), + BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); + // 2.3 如果当前任务时被加签的,则加它的根任务也标记成未通过 + // 疑问:为什么要标记未通过呢? + // 回答:例如说 A 任务被向前加签除 B 任务时,B 任务被审批不通过,此时 A 会被取消。而 cloud-ui-admin-vue3 不展示“已取消”的任务,导致展示不出审批不通过的细节。 + if (task.getParentTaskId() != null) { + String rootParentId = getTaskRootParentId(task); + updateTaskStatusAndReason(rootParentId, BpmTaskStatusEnum.REJECT.getStatus(), + BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); + taskService.addComment(rootParentId, task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), + BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); + } + + // 3. 根据不同的 RejectHandler 处理策略 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + // 3.1 情况一:驳回到指定的任务节点 + BpmUserTaskRejectHandlerTypeEnum userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement); + if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerTypeEnum.RETURN_USER_TASK) { + String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement); + Assert.notNull(returnTaskId, "退回的节点不能为空"); + returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId()) + .setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason())); + return; + } + // 3.2 情况二:直接结束,审批不通过 + processInstanceService.updateProcessInstanceReject(instance, reqVO.getReason()); // 标记不通过 + moveTaskToEnd(task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); // 结束流程 + } + + /** + * 更新流程任务的 status 状态 + * + * @param id 任务编号 + * @param status 状态 + */ + private void updateTaskStatus(String id, Integer status) { + taskService.setVariableLocal(id, BpmnVariableConstants.TASK_VARIABLE_STATUS, status); + } + + /** + * 更新流程任务的 status 状态、reason 理由 + * + * @param id 任务编号 + * @param status 状态 + * @param reason 理由(审批通过、审批不通过的理由) + */ + private void updateTaskStatusAndReason(String id, Integer status, String reason) { + updateTaskStatus(id, status); + taskService.setVariableLocal(id, BpmnVariableConstants.TASK_VARIABLE_REASON, reason); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void returnTask(Long userId, BpmTaskReturnReqVO reqVO) { + // 1.1 当前任务 task + Task task = validateTask(userId, reqVO.getId()); + if (task.isSuspended()) { + throw exception(TASK_IS_PENDING); + } + // 1.2 校验源头和目标节点的关系,并返回目标元素 + FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(), + reqVO.getTargetTaskDefinitionKey(), task.getProcessDefinitionId()); + + // 2. 调用 Flowable 框架的退回逻辑 + returnTask(userId, task, targetElement, reqVO); + } + + /** + * 退回流程节点时,校验目标任务节点是否可退回 + * + * @param sourceKey 当前任务节点 Key + * @param targetKey 目标任务节点 key + * @param processDefinitionId 当前流程定义 ID + * @return 目标任务节点元素 + */ + private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) { + // 1.1 获取流程模型信息 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); + // 1.3 获取当前任务节点元素 + FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey); + // 1.3 获取跳转的节点元素 + FlowElement target = BpmnModelUtils.getFlowElementById(bpmnModel, targetKey); + if (target == null) { + throw exception(TASK_TARGET_NODE_NOT_EXISTS); + } + + // 2.2 只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回 + if (!BpmnModelUtils.isSequentialReachable(source, target, null)) { + throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR); + } + return target; + } + + /** + * 执行退回逻辑 + * + * @param userId 用户编号 + * @param currentTask 当前退回的任务 + * @param targetElement 需要退回到的目标任务 + * @param reqVO 前端参数封装 + */ + public void returnTask(Long userId, Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) { + // 1. 获得所有需要回撤的任务 taskDefinitionKey,用于稍后的 moveActivityIdsToSingleActivityId 回撤 + // 1.1 获取所有正常进行的任务节点 Key + List taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list(); + List runTaskKeyList = convertList(taskList, Task::getTaskDefinitionKey); + // 1.2 通过 targetElement 的出口连线,计算在 runTaskKeyList 有哪些 key 需要被撤回 + // 为什么不直接使用 runTaskKeyList 呢?因为可能存在多个审批分支,例如说:A -> B -> C 和 D -> F,而只要 C 撤回到 A,需要排除掉 F + List returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null); + List returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId); + + List runExecutionIds = new ArrayList<>(); + // 2. 给当前要被退回的 task 数组,设置退回意见 + taskList.forEach(task -> { + // 需要排除掉,不需要设置退回意见的任务 + if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) { + return; + } + runExecutionIds.add(task.getExecutionId()); + + // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 + if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 + // 2.1.1 添加评论 + taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN.getType(), + BpmCommentTypeEnum.RETURN.formatComment(reqVO.getReason())); + // 2.1.2 更新 task 状态 + 原因 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason()); + } else { // 情况二:别人的任务,进行 CANCEL 标记 + processTaskCanceled(task.getId()); + } + }); + + // 3. 设置流程变量节点驳回标记:用于驳回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略。导致自动通过 + runtimeService.setVariable(currentTask.getProcessInstanceId(), + String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE); + // 4. 执行驳回 + // 使用 moveExecutionsToSingleActivityId 替换 moveActivityIdsToSingleActivityId 原因: + // 当多实例任务回退的时候有问题。相关 issue: https://github.com/flowable/flowable-engine/issues/3944 + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(currentTask.getProcessInstanceId()) + .moveExecutionsToSingleActivityId(runExecutionIds, reqVO.getTargetTaskDefinitionKey()) + .changeState(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO) { + String taskId = reqVO.getId(); + // 1.1 校验任务 + Task task = validateTask(userId, reqVO.getId()); + if (task.getAssignee().equals(reqVO.getDelegateUserId().toString())) { // 校验当前审批人和被委派人不是同一人 + throw exception(TASK_DELEGATE_FAIL_USER_REPEAT); + } + // 1.2 校验目标用户存在 + AdminUserRespDTO delegateUser = adminUserApi.getUser(reqVO.getDelegateUserId()).getCheckedData(); + if (delegateUser == null) { + throw exception(TASK_DELEGATE_FAIL_USER_NOT_EXISTS); + } + + // 2. 添加委托意见 + AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData(); + taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_START.getType(), + BpmCommentTypeEnum.DELEGATE_START.formatComment(currentUser.getNickname(), delegateUser.getNickname(), reqVO.getReason())); + + // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) + taskService.setOwner(taskId, task.getAssignee()); + // 3.2 执行委派,将任务委派给 delegateUser + taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString()); + // 补充说明:委托不单独设置状态。如果需要,可通过 Task 的 DelegationState 字段,判断是否为 DelegationState.PENDING 委托中 + } + + @Override + public void transferTask(Long userId, BpmTaskTransferReqVO reqVO) { + String taskId = reqVO.getId(); + // 1.1 校验任务 + Task task = validateTask(userId, reqVO.getId()); + if (task.getAssignee().equals(reqVO.getAssigneeUserId().toString())) { // 校验当前审批人和被转派人不是同一人 + throw exception(TASK_TRANSFER_FAIL_USER_REPEAT); + } + // 1.2 校验目标用户存在 + AdminUserRespDTO assigneeUser = adminUserApi.getUser(reqVO.getAssigneeUserId()).getCheckedData(); + if (assigneeUser == null) { + throw exception(TASK_TRANSFER_FAIL_USER_NOT_EXISTS); + } + + // 2. 添加委托意见 + AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData(); + taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.TRANSFER.getType(), + BpmCommentTypeEnum.TRANSFER.formatComment(currentUser.getNickname(), assigneeUser.getNickname(), reqVO.getReason())); + + // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) + taskService.setOwner(taskId, task.getAssignee()); + // 3.2 执行转派(审批人),将任务转派给 assigneeUser + // 委托( delegate)和转派(transfer)的差别,就在这块的调用!!!! + taskService.setAssignee(taskId, reqVO.getAssigneeUserId().toString()); + } + + @Override + public void moveTaskToEnd(String processInstanceId, String reason) { + List taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null); + if (CollUtil.isEmpty(taskList)) { + return; + } + + // 1. 其它未结束的任务,直接取消 + // 疑问:为什么不通过 updateTaskStatusWhenCanceled 监听取消,而是直接提前调用呢? + // 回答:详细见 updateTaskStatusWhenCanceled 的方法,加签的场景 + taskList.forEach(task -> { + Integer otherTaskStatus = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); + if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) { + return; + } + processTaskCanceled(task.getId()); + }); + + // 2. 终止流程 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId()); + List activityIds = CollUtil.newArrayList(convertSet(taskList, Task::getTaskDefinitionKey)); + EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); + Assert.notNull(endEvent, "结束节点不能为空"); + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(processInstanceId) + .moveActivityIdsToSingleActivityId(activityIds, endEvent.getId()) + .changeState(); + + // 3. 特殊:如果跳转到 EndEvent 流程还未结束, 执行 deleteProcessInstance 方法 + // TODO 芋艿:目前发现并行分支情况下,会存在这个情况,后续看看有没更好的方案; + List executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list(); + if (CollUtil.isNotEmpty(executions)) { + log.warn("[moveTaskToEnd][执行跳转到 EndEvent 后, 流程实例未结束,强制执行 deleteProcessInstance 方法]"); + runtimeService.deleteProcessInstance(processInstanceId, reason); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO) { + // 1. 获取和校验任务 + TaskEntityImpl taskEntity = validateTaskCanCreateSign(userId, reqVO); + List userList = adminUserApi.getUserList(reqVO.getUserIds()).getCheckedData(); + if (CollUtil.isEmpty(userList)) { + throw exception(TASK_SIGN_CREATE_USER_NOT_EXIST); + } + + // 2. 处理当前任务 + // 2.1 开启计数功能,主要用于为了让表 ACT_RU_TASK 中的 SUB_TASK_COUNT_ 字段记录下总共有多少子任务,后续可能有用 + taskEntity.setCountEnabled(true); + // 2.2 向前加签,设置 owner,置空 assign。等子任务都完成后,再调用 resolveTask 重新将 owner 设置为 assign + // 原因是:不能和向前加签的子任务一起审批,需要等前面的子任务都完成才能审批 + if (reqVO.getType().equals(BpmTaskSignTypeEnum.BEFORE.getType())) { + taskEntity.setOwner(taskEntity.getAssignee()); + taskEntity.setAssignee(null); + } + // 2.4 记录加签方式,完成任务时需要用到判断 + taskEntity.setScopeType(reqVO.getType()); + // 2.5 保存当前任务修改后的值 + taskService.saveTask(taskEntity); + // 2.6 更新 task 状态为 WAIT,只有在向前加签的时候 + if (reqVO.getType().equals(BpmTaskSignTypeEnum.BEFORE.getType())) { + updateTaskStatus(taskEntity.getId(), BpmTaskStatusEnum.WAIT.getStatus()); + } + + // 3. 创建加签任务 + createSignTaskList(convertList(reqVO.getUserIds(), String::valueOf), taskEntity); + + // 4. 记录加签的评论到 task 任务 + AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData(); + String comment = StrUtil.format(BpmCommentTypeEnum.ADD_SIGN.getComment(), + currentUser.getNickname(), BpmTaskSignTypeEnum.nameOfType(reqVO.getType()), + String.join(",", convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason()); + taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(), BpmCommentTypeEnum.ADD_SIGN.getType(), comment); + } + + /** + * 校验任务是否可以加签,主要校验加签类型是否一致: + *

+ * 1. 如果存在“向前加签”的任务,则不能“向后加签” + * 2. 如果存在“向后加签”的任务,则不能“向前加签” + * + * @param userId 当前用户 ID + * @param reqVO 请求参数,包含任务 ID 和加签类型 + * @return 当前任务 + */ + private TaskEntityImpl validateTaskCanCreateSign(Long userId, BpmTaskSignCreateReqVO reqVO) { + TaskEntityImpl taskEntity = (TaskEntityImpl) validateTask(userId, reqVO.getId()); + // 向前加签和向后加签不能同时存在 + if (taskEntity.getScopeType() != null + && ObjectUtil.notEqual(taskEntity.getScopeType(), reqVO.getType())) { + throw exception(TASK_SIGN_CREATE_TYPE_ERROR, + BpmTaskSignTypeEnum.nameOfType(taskEntity.getScopeType()), BpmTaskSignTypeEnum.nameOfType(reqVO.getType())); + } + + // 同一个 key 的任务,审批人不重复 + List taskList = taskService.createTaskQuery().processInstanceId(taskEntity.getProcessInstanceId()) + .taskDefinitionKey(taskEntity.getTaskDefinitionKey()).list(); + List currentAssigneeList = convertListByFlatMap(taskList, task -> // 需要考虑 owner 的情况,因为向后加签时,它暂时没 assignee 而是 owner + Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner()))); + if (CollUtil.containsAny(currentAssigneeList, reqVO.getUserIds())) { + List userList = adminUserApi.getUserList(CollUtil.intersection(currentAssigneeList, reqVO.getUserIds())).getCheckedData(); + throw exception(TASK_SIGN_CREATE_USER_REPEAT, String.join(",", convertList(userList, AdminUserRespDTO::getNickname))); + } + return taskEntity; + } + + /** + * 创建加签子任务 + * + * @param userIds 被加签的用户 ID + * @param taskEntity 被加签的任务 + */ + private void createSignTaskList(List userIds, TaskEntityImpl taskEntity) { + if (CollUtil.isEmpty(userIds)) { + return; + } + // 创建加签人的新任务,全部基于 taskEntity 为父任务来创建 + for (String addSignId : userIds) { + if (StrUtil.isBlank(addSignId)) { + continue; + } + createSignTask(taskEntity, addSignId); + } + } + + /** + * 创建加签子任务 + * + * @param parentTask 父任务 + * @param assignee 子任务的执行人 + */ + private void createSignTask(TaskEntityImpl parentTask, String assignee) { + // 1. 生成子任务 + TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID()); + BpmTaskConvert.INSTANCE.copyTo(parentTask, task); + + // 2.1 向前加签,设置审批人 + if (BpmTaskSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) { + task.setAssignee(assignee); + // 2.2 向后加签,设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 + } else { + task.setOwner(assignee); + } + // 2.3 保存子任务 + taskService.saveTask(task); + + // 3. 向后前签,设置子任务的状态为 WAIT,因为需要等父任务审批完 + if (BpmTaskSignTypeEnum.AFTER.getType().equals(parentTask.getScopeType())) { + updateTaskStatus(task.getId(), BpmTaskStatusEnum.WAIT.getStatus()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + @SuppressWarnings("DataFlowIssue") + public void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO) { + // 1.1 校验 task 可以被减签 + Task task = validateTaskCanSignDelete(reqVO.getId()); + // 1.2 校验取消人存在 + AdminUserRespDTO cancelUser = null; + if (StrUtil.isNotBlank(task.getAssignee())) { + cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getAssignee())).getCheckedData(); + } + if (cancelUser == null && StrUtil.isNotBlank(task.getOwner())) { + cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())).getCheckedData(); + } + Assert.notNull(cancelUser, "任务中没有所有者和审批人,数据错误"); + + // 2.1 获得子任务列表,包括子任务的子任务 + List childTaskList = getAllChildTaskList(task); + childTaskList.add(task); + // 2.2 更新子任务为已取消 + String cancelReason = StrUtil.format("任务被取消,原因:由于[{}]操作[减签],", cancelUser.getNickname()); + childTaskList.forEach(childTask -> updateTaskStatusAndReason(childTask.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), cancelReason)); + // 2.3 删除任务和所有子任务 + taskService.deleteTasks(convertList(childTaskList, Task::getId)); + + // 3. 记录日志到父任务中。先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录 + AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); + taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(), BpmCommentTypeEnum.SUB_SIGN.getType(), + StrUtil.format(BpmCommentTypeEnum.SUB_SIGN.getComment(), user.getNickname(), cancelUser.getNickname())); + + // 4. 处理当前任务的父任务 + handleParentTaskIfSign(task.getParentTaskId()); + } + + @Override + public void copyTask(Long userId, BpmTaskCopyReqVO reqVO) { + processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getReason(), reqVO.getId()); + } + + /** + * 校验任务是否能被减签 + * + * @param id 任务编号 + * @return 任务信息 + */ + private Task validateTaskCanSignDelete(String id) { + Task task = validateTaskExist(id); + if (task.getParentTaskId() == null) { + throw exception(TASK_SIGN_DELETE_NO_PARENT); + } + Task parentTask = getTask(task.getParentTaskId()); + if (parentTask == null) { + throw exception(TASK_SIGN_DELETE_NO_PARENT); + } + if (BpmTaskSignTypeEnum.of(parentTask.getScopeType()) == null) { + throw exception(TASK_SIGN_DELETE_NO_PARENT); + } + return task; + } + + // ========== Event 事件相关方法 ========== + + @Override + public void processTaskCreated(Task task) { + // 1. 设置为待办中 + Integer status = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); + if (status != null) { + log.error("[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]", task.getId(), status); + return; + } + updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); + + ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (processInstance == null) { + log.error("[processTaskCreated][taskId({}) 没有找到流程实例]", task.getId()); + return; + } + BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService. + getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); + if (processDefinitionInfo == null) { + log.error("[processTaskCreated][processDefinitionId({}) 没有找到流程定义]", processInstance.getProcessDefinitionId()); + return; + } + + // 2. 任务前置通知 + if (ObjUtil.isNotNull(processDefinitionInfo.getTaskBeforeTriggerSetting())){ + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskBeforeTriggerSetting(); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); + } + + // 3. 处理自动通过的情况,例如说:1)无审批人时,是否自动通过、不通过;2)非【人工审核】时,是否自动通过、不通过 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + Integer approveType = BpmnModelUtils.parseApproveType(userTaskElement); + Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(userTaskElement); + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + + /** + * 特殊情况:部分情况下,TransactionSynchronizationManager 注册 afterCommit 监听时,不会被调用,但是 afterCompletion 可以 + * 例如说:第一个 task 就是配置【自动通过】或者【自动拒绝】时 + * 参见 issue 反馈 + */ + @Override + public void afterCompletion(int transactionStatus) { + // 回滚情况,直接返回 + if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) { + return; + } + // 特殊情况:第一个 task 【自动通过】时,第二个任务设置审批人时 transactionStatus 会为 STATUS_UNKNOWN,不知道啥原因 + if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_UNKNOWN) + && getTask(task.getId()) == null) { + return; + } + // 特殊情况一:【人工审核】审批人为空,根据配置是否要自动通过、自动拒绝 + if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.USER.getType())) { + // 如果有审批人、或者拥有人,则说明不满足情况一,不自动通过、不自动拒绝 + if (!ObjectUtil.isAllEmpty(task.getAssignee(), task.getOwner())) { + return; + } + if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.APPROVE.getType())) { + getSelf().approveTask(null, new BpmTaskApproveReqVO() + .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_APPROVE.getReason())); + } else if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.REJECT.getType())) { + getSelf().rejectTask(null, new BpmTaskRejectReqVO() + .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_REJECT.getReason())); + } + // 特殊情况二:【自动审核】审批类型为自动通过、不通过 + } else { + if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType())) { + getSelf().approveTask(null, new BpmTaskApproveReqVO() + .setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_APPROVE.getReason())); + } else if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { + getSelf().rejectTask(null, new BpmTaskRejectReqVO() + .setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_REJECT.getReason())); + } + } + } + + }); + } + + /** + * 重要补充说明:该方法目前主要有两个情况会调用到: + *

+ * 1. 或签场景 + 审批通过:一个或签有多个审批时,如果 A 审批通过,其它或签 B、C 等任务会被 Flowable 自动删除,此时需要通过该方法更新状态为已取消 + * 2. 审批不通过:在 {@link #rejectTask(Long, BpmTaskRejectReqVO)} 不通过时,对于加签的任务,不会被 Flowable 删除,此时需要通过该方法更新状态为已取消 + */ + @Override + public void processTaskCanceled(String taskId) { + Task task = getTask(taskId); + // 1. 可能只是活动,不是任务,所以查询不到 + if (task == null) { + log.error("[updateTaskStatusWhenCanceled][taskId({}) 任务不存在]", taskId); + return; + } + + // 2. 更新 task 状态 + 原因 + Integer status = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); + if (BpmTaskStatusEnum.isEndStatus(status)) { + log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status); + return; + } + updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_SYSTEM.getReason()); + // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 + } + + @Override + @DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人 + public void processTaskAssigned(Task task) { + // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + + /** + * 特殊情况:部分情况下,TransactionSynchronizationManager 注册 afterCommit 监听时,不会被调用,但是 afterCompletion 可以 + * 例如说:第一个 task 就是配置【自动通过】或者【自动拒绝】时 + * 参见 issue 反馈 + */ + @Override + public void afterCompletion(int transactionStatus) { + // 回滚情况,直接返回 + if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) { + return; + } + // 特殊情况:第一个 task 【自动通过】时,第二个任务设置审批人时 transactionStatus 会为 STATUS_UNKNOWN,不知道啥原因 + if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_UNKNOWN) + && getTask(task.getId()) == null) { + return; + } + if (StrUtil.isEmpty(task.getAssignee())) { + log.error("[processTaskAssigned][taskId({}) 没有分配到负责人]", task.getId()); + return; + } + ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (processInstance == null) { + log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId()); + return; + } + + // 自动去重,通过自动审批的方式 TODO @芋艿 驳回的情况得考虑一下;@lesan:驳回后,又自动审批么? + BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId()); + if (processDefinitionInfo == null) { + log.error("[processTaskAssigned][taskId({}) 没有找到流程定义({})]", task.getId(), task.getProcessDefinitionId()); + return; + } + if (processDefinitionInfo.getAutoApprovalType() != null) { + HistoricTaskInstanceQuery sameAssigneeQuery = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(task.getProcessInstanceId()) + .taskAssignee(task.getAssignee()) // 相同审批人 + .taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus()) + .finished(); + if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType()) + && sameAssigneeQuery.count() > 0) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName())); + return; + } + if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) { + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); + if (bpmnModel == null) { + log.error("[processTaskAssigned][taskId({}) 没有找到流程模型({})]", task.getId(), task.getProcessDefinitionId()); + return; + } + List sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingFlows( // 获取所有上一个节点 + BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())), + SequenceFlow::getSourceRef); + if (sameAssigneeQuery.taskDefinitionKeys(sourceTaskIds).count() > 0) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getName())); + return; + } + } + } + + // 获取发起人节点 + BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); + if (bpmnModel == null) { + log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); + return; + } + FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 + // TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 + Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), + String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); + Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), + PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); + if (userTaskElement.getId().equals(START_USER_NODE_ID) + && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 + || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 + && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); + return; + } + // 当不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 + if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID) + && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { + if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { + Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); + + // 情况一:自动跳过 + if (ObjectUtils.equalsAny(assignStartUserHandlerType, + BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason())); + return; + } + // 情况二:转交给部门负责人审批 + if (ObjectUtils.equalsAny(assignStartUserHandlerType, + BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) { + AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData(); + Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId()); + Long deptId = startUser.getDeptIds().stream().findAny().orElse(null); + DeptRespDTO dept = deptId != null ? deptApi.getDept(deptId).getCheckedData() : null; + Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), deptId); + // 找不到部门负责人的情况下,自动审批通过 + // noinspection DataFlowIssue + if (dept.getLeaderUserId() == null) { + getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) + .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason())); + return; + } + // 找得到部门负责人的情况下,修改负责人 + if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) { + getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO() + .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId()) + .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason())); + return; + } + // 如果部门负责人是自己,还是自己审批吧~ + } + } + } + // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 + FlowableUtils.execute(processInstance.getTenantId(), () -> { + AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData(); + messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); + }); + } + + }); + } + + @Override + public void processTaskCompleted(Task task) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (processInstance == null) { + log.error("[processTaskCompleted][taskId({}) 没有找到流程实例]", task.getId()); + return; + } + BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService. + getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); + if (processDefinitionInfo == null) { + log.error("[processTaskCompleted][processDefinitionId({}) 没有找到流程定义]", processInstance.getProcessDefinitionId()); + return; + } + + // 任务后置通知 + if (ObjUtil.isNotNull(processDefinitionInfo.getTaskAfterTriggerSetting())){ + BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskAfterTriggerSetting(); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType) { + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + if (processInstance == null) { + log.error("[processTaskTimeout][processInstanceId({}) 没有找到流程实例]", processInstanceId); + return; + } + List taskList = getRunningTaskListByProcessInstanceId(processInstanceId, true, taskDefineKey); + // TODO 优化:未来需要考虑加签的情况 + if (CollUtil.isEmpty(taskList)) { + log.error("[processTaskTimeout][processInstanceId({}) 定义Key({}) 没有找到任务]", processInstanceId, taskDefineKey); + return; + } + + taskList.forEach(task -> FlowableUtils.execute(task.getTenantId(), () -> { + // 情况一:自动提醒 + if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType())) { + messageService.sendMessageWhenTaskTimeout(new BpmMessageSendWhenTaskTimeoutReqDTO() + .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName()) + .setTaskId(task.getId()).setTaskName(task.getName()).setAssigneeUserId(Long.parseLong(task.getAssignee()))); + return; + } + + // 情况二:自动同意 + if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.APPROVE.getType())) { + approveTask(Long.parseLong(task.getAssignee()), + new BpmTaskApproveReqVO().setId(task.getId()).setReason(BpmReasonEnum.TIMEOUT_APPROVE.getReason())); + return; + } + + // 情况三:自动拒绝 + if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.REJECT.getType())) { + rejectTask(Long.parseLong(task.getAssignee()), + new BpmTaskRejectReqVO().setId(task.getId()).setReason(BpmReasonEnum.REJECT_TASK.getReason())); + } + })); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void processChildProcessTimeout(String processInstanceId, String taskDefineKey) { + List activityInstances = runtimeService.createActivityInstanceQuery() + .processInstanceId(processInstanceId) + .activityId(taskDefineKey).list(); + activityInstances.forEach(activityInstance -> FlowableUtils.execute(activityInstance.getTenantId(), + () -> moveTaskToEnd(activityInstance.getCalledProcessInstanceId(), BpmReasonEnum.TIMEOUT_APPROVE.getReason()))); + } + + @Override + public void triggerTask(String processInstanceId, String taskDefineKey) { + Execution execution = runtimeService.createExecutionQuery() + .processInstanceId(processInstanceId) + .activityId(taskDefineKey) + .singleResult(); + if (execution == null) { + log.error("[triggerTask][processInstanceId({}) activityId({}) 没有找到执行活动]", processInstanceId, taskDefineKey); + return; + } + + // 若存在直接触发接收任务,执行后续节点 + FlowableUtils.execute(execution.getTenantId(), + () -> runtimeService.trigger(execution.getId())); + } + + /** + * 获得自身的代理对象,解决 AOP 生效问题 + * + * @return 自己 + */ + private BpmTaskServiceImpl getSelf() { + return SpringUtil.getBean(getClass()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java new file mode 100644 index 0000000..e458af4 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java @@ -0,0 +1,96 @@ +package com.zt.plat.module.bpm.service.task.listener; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.definition.BpmChildProcessStartUserEmptyTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmChildProcessStartUserTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.delegate.ExecutionListener; +import org.flowable.engine.impl.el.FixedValue; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * BPM 子流程监听器:设置流程的发起人 + * + * @author Lesan + */ +@Component +@Slf4j +public class BpmCallActivityListener implements ExecutionListener { + + public static final String DELEGATE_EXPRESSION = "${bpmCallActivityListener}"; + + @Setter + private FixedValue listenerConfig; + + @Resource + private BpmProcessDefinitionService processDefinitionService; + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public void notify(DelegateExecution execution) { + String expressionText = listenerConfig.getExpressionText(); + Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); + BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject( + expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class); + ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getRootProcessInstanceId()); + + // 1. 当发起人来源为主流程发起人时,并兜底 startUserSetting 为空时 + if (startUserSetting == null + || startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) { + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); + return; + } + + // 2. 当发起人来源为表单时 + if (startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) { + String formFieldValue = MapUtil.getStr(processInstance.getProcessVariables(), startUserSetting.getFormField()); + // 2.1 当表单值为空时 + if (StrUtil.isEmpty(formFieldValue)) { + // 2.1.1 来自主流程发起人 + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) { + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); + return; + } + // 2.1.2 来自子流程管理员 + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) { + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId()); + List managerUserIds = processDefinition.getManagerUserIds(); + FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); + return; + } + // 2.1.3 来自主流程管理员 + if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) { + BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); + List managerUserIds = processDefinition.getManagerUserIds(); + FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); + return; + } + } + // 2.2 使用表单值,并兜底字符串转 Long 失败时使用主流程发起人 + try { + FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); + } catch (Exception e) { + log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]", + DELEGATE_EXPRESSION, formFieldValue); + FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); + } + } + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java new file mode 100644 index 0000000..2102560 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java @@ -0,0 +1,59 @@ +package com.zt.plat.module.bpm.service.task.listener; + +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.delegate.TaskListener; +import org.flowable.engine.impl.el.FixedValue; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.service.delegate.DelegateTask; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Component; + +import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig; + +// TODO @芋艿:可能会想换个包地址 +/** + * BPM 用户任务通用监听器 + * + * @author Lesan + */ +@Component +@Slf4j +@Scope("prototype") +public class BpmUserTaskListener implements TaskListener { + + public static final String DELEGATE_EXPRESSION = "${bpmUserTaskListener}"; + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Setter + private FixedValue listenerConfig; + + @Override + public void notify(DelegateTask delegateTask) { + // 1. 获取所需基础信息 + ProcessInstance processInstance = processInstanceService.getProcessInstance(delegateTask.getProcessInstanceId()); + BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig); + + // 2. 发起请求 + // TODO @芋艿:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去; + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getProcessInstanceId())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getAssignee())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskDefinitionKey") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getTaskDefinitionKey())); + listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskId") + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getId())); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + listenerHandler.getPath(), listenerHandler.getHeader(), listenerHandler.getBody(), false, null); + + // 3. 是否需要后续操作?TODO 芋艿:待定! + } +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java new file mode 100644 index 0000000..02fb2d5 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java @@ -0,0 +1,30 @@ +package com.zt.plat.module.bpm.service.task.trigger; + +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; + +// TODO @芋艿:可能会想换个包地址 +/** + * BPM 触发器接口 + *

+ * 处理不同的动作 + * + * @author jason + */ +public interface BpmTrigger { + + /** + * 对应触发器类型 + * + * @return 触发器类型 + */ + BpmTriggerTypeEnum getType(); + + /** + * 触发器执行 + * + * @param processInstanceId 流程实例编号 + * @param param 触发器参数 + */ + void execute(String processInstanceId, String param); + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java new file mode 100644 index 0000000..46ed559 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java @@ -0,0 +1,73 @@ +package com.zt.plat.module.bpm.service.task.trigger.form; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * BPM 删除流程表单数据触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmFormDeleteTrigger implements BpmTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.FORM_DELETE; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析删除流程表单数据配置 + List settings = JsonUtils.parseObject(param, new TypeReference<>() {}); + if (CollUtil.isEmpty(settings)) { + log.error("[execute][流程({}) 删除流程表单数据触发器配置为空]", processInstanceId); + return; + } + + // 2. 获取流程变量 + Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); + + // 3.1 获取需要删除的表单字段 + Set deleteFields = new HashSet<>(); + settings.forEach(setting -> { + if (CollUtil.isEmpty(setting.getDeleteFields())) { + return; + } + // 配置了条件,判断条件是否满足 + boolean isFieldDeletedNeeded = true; + if (setting.getConditionType() != null) { + String conditionExpression = SimpleModelUtils.buildConditionExpression( + setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups()); + isFieldDeletedNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); + } + if (isFieldDeletedNeeded) { + deleteFields.addAll(setting.getDeleteFields()); + } + }); + + // 3.2 删除流程变量 + if (CollUtil.isNotEmpty(deleteFields)) { + processInstanceService.removeProcessInstanceVariables(processInstanceId, deleteFields); + } + } +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java new file mode 100644 index 0000000..c8427b2 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java @@ -0,0 +1,66 @@ +package com.zt.plat.module.bpm.service.task.trigger.form; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.FormTriggerSetting; +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; +import com.fasterxml.jackson.core.type.TypeReference; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; + +/** + * BPM 更新流程表单触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmFormUpdateTrigger implements BpmTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.FORM_UPDATE; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析更新流程表单配置 + List settings = JsonUtils.parseObject(param, new TypeReference<>() {}); + if (CollUtil.isEmpty(settings)) { + log.error("[execute][流程({}) 更新流程表单触发器配置为空]", processInstanceId); + return; + } + + // 2. 获取流程变量 + Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); + + // 3. 更新流程变量 + for (FormTriggerSetting setting : settings) { + if (CollUtil.isEmpty(setting.getUpdateFormFields())) { + continue; + } + // 配置了条件,判断条件是否满足 + boolean isFormUpdateNeeded = true; + if (setting.getConditionType() != null) { + String conditionExpression = SimpleModelUtils.buildConditionExpression( + setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups()); + isFormUpdateNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); + } + // 更新流程表单 + if (isFormUpdateNeeded) { + processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields()); + } + } + } +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java new file mode 100644 index 0000000..676b260 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java @@ -0,0 +1,14 @@ +package com.zt.plat.module.bpm.service.task.trigger.http; + +import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; +import lombok.extern.slf4j.Slf4j; + +/** + * BPM 发送 HTTP 请求触发器抽象类 + * + * @author jason + */ +@Slf4j +public abstract class BpmAbstractHttpRequestTrigger implements BpmTrigger { + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java new file mode 100644 index 0000000..3fe0606 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java @@ -0,0 +1,51 @@ +package com.zt.plat.module.bpm.service.task.trigger.http; + +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import com.zt.plat.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +/** + * BPM HTTP 回调触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.HTTP_CALLBACK; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析 http 请求配置 + BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, + BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting.class); + if (setting == null) { + log.error("[execute][流程({}) HTTP 回调触发器配置为空]", processInstanceId); + return; + } + + // 2. 发起请求 + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + setting.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam() + .setKey("taskDefineKey") // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 + .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(setting.getCallbackTaskDefineKey())); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), setting.getHeader(), setting.getBody(), false, null); + } + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java new file mode 100644 index 0000000..617bf7a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.service.task.trigger.http; + +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting; +import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import jakarta.annotation.Resource; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +/** + * BPM 发送同步 HTTP 请求触发器 + * + * @author jason + */ +@Component +@Slf4j +public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { + + @Resource + private BpmProcessInstanceService processInstanceService; + + @Override + public BpmTriggerTypeEnum getType() { + return BpmTriggerTypeEnum.HTTP_REQUEST; + } + + @Override + public void execute(String processInstanceId, String param) { + // 1. 解析 http 请求配置 + HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, HttpRequestTriggerSetting.class); + if (setting == null) { + log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId); + return; + } + + // 2. 发起请求 + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, + setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); + } + +} diff --git a/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java b/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java new file mode 100644 index 0000000..f7558a1 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java @@ -0,0 +1,546 @@ +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by FernFlower decompiler) +// + +package liquibase.database.core; + +import java.lang.reflect.Method; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import liquibase.CatalogAndSchema; +import liquibase.GlobalConfiguration; +import liquibase.Scope; +import liquibase.database.AbstractJdbcDatabase; +import liquibase.database.DatabaseConnection; +import liquibase.database.OfflineConnection; +import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.DatabaseException; +import liquibase.exception.UnexpectedLiquibaseException; +import liquibase.exception.ValidationErrors; +import liquibase.executor.ExecutorService; +import liquibase.statement.DatabaseFunction; +import liquibase.statement.SequenceCurrentValueFunction; +import liquibase.statement.SequenceNextValueFunction; +import liquibase.statement.UniqueConstraint; +import liquibase.statement.core.RawCallStatement; +import liquibase.statement.core.RawParameterizedSqlStatement; +import liquibase.structure.DatabaseObject; +import liquibase.structure.core.Catalog; +import liquibase.structure.core.Column; +import liquibase.structure.core.Index; +import liquibase.structure.core.PrimaryKey; +import liquibase.structure.core.Schema; +import liquibase.util.JdbcUtil; +import liquibase.util.StringUtil; +import org.apache.commons.lang3.StringUtils; + +public class DmDatabase extends AbstractJdbcDatabase { + private static final String PROXY_USER_REGEX = ".*(?:thin|oci)\\:(.+)/@.*"; + public static final Pattern PROXY_USER_PATTERN = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*"); + private static final String VERSION_REGEX = "(\\d+)\\.(\\d+)\\..*"; + private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\..*"); + public static final String PRODUCT_NAME = "DM DBMS"; + private static final ResourceBundle coreBundle = ResourceBundle.getBundle("liquibase/i18n/liquibase-core"); + protected final int SHORT_IDENTIFIERS_LENGTH = 30; + protected final int LONG_IDENTIFIERS_LEGNTH = 128; + public static final int ORACLE_12C_MAJOR_VERSION = 12; + public static final int ORACLE_23C_MAJOR_VERSION = 23; + private final Set reservedWords = new HashSet(); + private Set userDefinedTypes; + private Map savedSessionNlsSettings; + private Boolean canAccessDbaRecycleBin; + private Integer databaseMajorVersion; + private Integer databaseMinorVersion; + + public DmDatabase() { + super.unquotedObjectsAreUppercased = true; + super.setCurrentDateTimeFunction("SYSTIMESTAMP"); + this.dateFunctions.add(new DatabaseFunction("SYSDATE")); + this.dateFunctions.add(new DatabaseFunction("SYSTIMESTAMP")); + this.dateFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP")); + super.sequenceNextValueFunction = "%s.nextval"; + super.sequenceCurrentValueFunction = "%s.currval"; + } + + public int getPriority() { + return 1; + } + + private void tryProxySession(String url, Connection con) { + Matcher m = PROXY_USER_PATTERN.matcher(url); + if (m.matches()) { + Properties props = new Properties(); + props.put("PROXY_USER_NAME", m.group(1)); + + try { + Method method = con.getClass().getMethod("openProxySession", Integer.TYPE, Properties.class); + method.setAccessible(true); + method.invoke(con, 1, props); + } catch (Exception e) { + Scope.getCurrentScope().getLog(this.getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage()); + return; + } + + try { + Method method = con.getClass().getMethod("isProxySession"); + method.setAccessible(true); + boolean b = (Boolean)method.invoke(con); + if (!b) { + Scope.getCurrentScope().getLog(this.getClass()).info("Proxy session not established on OracleDatabase: "); + } + } catch (Exception e) { + Scope.getCurrentScope().getLog(this.getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage()); + } + } + + } + + public void setConnection(DatabaseConnection conn) { + this.reservedWords.addAll(Arrays.asList("GROUP", "USER", "SESSION", "PASSWORD", "RESOURCE", "START", "SIZE", "UID", "DESC", "ORDER")); + Connection sqlConn = null; + if (!(conn instanceof OfflineConnection)) { + try { + if (conn instanceof JdbcConnection) { + sqlConn = ((JdbcConnection)conn).getWrappedConnection(); + } + } catch (Exception e) { + throw new UnexpectedLiquibaseException(e); + } + + if (sqlConn != null) { + this.tryProxySession(conn.getURL(), sqlConn); + + try { + this.reservedWords.addAll(Arrays.asList(sqlConn.getMetaData().getSQLKeywords().toUpperCase().split(",\\s*"))); + } catch (SQLException e) { + Scope.getCurrentScope().getLog(this.getClass()).info("Could get sql keywords on OracleDatabase: " + e.getMessage()); + } + + try { + Method method = sqlConn.getClass().getMethod("setRemarksReporting", Boolean.TYPE); + method.setAccessible(true); + method.invoke(sqlConn, true); + } catch (Exception e) { + Scope.getCurrentScope().getLog(this.getClass()).info("Could not set remarks reporting on OracleDatabase: " + e.getMessage()); + } + + CallableStatement statement = null; + + try { + statement = sqlConn.prepareCall("{call DBMS_UTILITY.DB_VERSION(?,?)}"); + statement.registerOutParameter(1, 12); + statement.registerOutParameter(2, 12); + statement.execute(); + String compatibleVersion = statement.getString(2); + if (compatibleVersion != null) { + Matcher majorVersionMatcher = VERSION_PATTERN.matcher(compatibleVersion); + if (majorVersionMatcher.matches()) { + this.databaseMajorVersion = Integer.valueOf(majorVersionMatcher.group(1)); + this.databaseMinorVersion = Integer.valueOf(majorVersionMatcher.group(2)); + } + } + } catch (SQLException e) { + String message = "Cannot read from DBMS_UTILITY.DB_VERSION: " + e.getMessage(); + Scope.getCurrentScope().getLog(this.getClass()).info("Could not set check compatibility mode on OracleDatabase, assuming not running in any sort of compatibility mode: " + message); + } finally { + JdbcUtil.closeStatement(statement); + } + + if (GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue() != null) { + int timeoutValue = (Integer)GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue(); + Scope.getCurrentScope().getLog(this.getClass()).fine("Setting DDL_LOCK_TIMEOUT value to " + timeoutValue); + String sql = "ALTER SESSION SET DDL_LOCK_TIMEOUT=" + timeoutValue; + PreparedStatement ddlLockTimeoutStatement = null; + + try { + ddlLockTimeoutStatement = sqlConn.prepareStatement(sql); + ddlLockTimeoutStatement.execute(); + } catch (SQLException sqle) { + Scope.getCurrentScope().getUI().sendErrorMessage("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + sqle.getMessage(), sqle); + Scope.getCurrentScope().getLog(this.getClass()).warning("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + sqle.getMessage(), sqle); + } finally { + JdbcUtil.closeStatement(ddlLockTimeoutStatement); + } + } + } + } + + super.setConnection(conn); + } + + public String getShortName() { + return "dm"; + } + + protected String getDefaultDatabaseProductName() { + return PRODUCT_NAME; + } + + public int getDatabaseMajorVersion() throws DatabaseException { + return this.databaseMajorVersion == null ? super.getDatabaseMajorVersion() : this.databaseMajorVersion; + } + + public int getDatabaseMinorVersion() throws DatabaseException { + return this.databaseMinorVersion == null ? super.getDatabaseMinorVersion() : this.databaseMinorVersion; + } + + public Integer getDefaultPort() { + return 5236; + } + + public String getJdbcCatalogName(CatalogAndSchema schema) { + return null; + } + + public String getJdbcSchemaName(CatalogAndSchema schema) { + return this.correctObjectName(schema.getCatalogName() == null ? schema.getSchemaName() : schema.getCatalogName(), Schema.class); + } + + protected String getAutoIncrementClause(String generationType, Boolean defaultOnNull) { + if (StringUtil.isEmpty(generationType)) { + return super.getAutoIncrementClause(); + } else { + String autoIncrementClause = "GENERATED %s AS IDENTITY"; + String generationStrategy = generationType; + if (Boolean.TRUE.equals(defaultOnNull) && generationType.toUpperCase().equals("BY DEFAULT")) { + generationStrategy = generationType + " ON NULL"; + } + + return String.format(autoIncrementClause, generationStrategy); + } + } + + public String generatePrimaryKeyName(String tableName) { + return tableName.length() > 27 ? "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27) : "PK_" + tableName.toUpperCase(Locale.US); + } + + public boolean supportsInitiallyDeferrableColumns() { + return true; + } + + public boolean isReservedWord(String objectName) { + return this.reservedWords.contains(objectName.toUpperCase()); + } + + public boolean supportsSequences() { + return true; + } + + public boolean supports(Class object) { + return Schema.class.isAssignableFrom(object) ? false : super.supports(object); + } + + public boolean supportsSchemas() { + return false; + } + + protected String getConnectionCatalogName() throws DatabaseException { + if (this.getConnection() instanceof OfflineConnection) { + return this.getConnection().getCatalog(); + } else if (!(this.getConnection() instanceof JdbcConnection)) { + return this.defaultCatalogName; + } else { + try { + return (String)((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForObject(new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"), String.class); + } catch (Exception e) { + Scope.getCurrentScope().getLog(this.getClass()).info("Error getting default schema", e); + return null; + } + } + } + + public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException { + return "oracle".equalsIgnoreCase(conn.getDatabaseProductName()); + } + + public String getDefaultDriver(String url) { + return url.startsWith("jdbc:dm") ? "dm.jdbc.driver.DmDriver" : null; + } + + public String getDefaultCatalogName() { + String defaultCatalogName = super.getDefaultCatalogName(); + if (Boolean.TRUE.equals(GlobalConfiguration.PRESERVE_SCHEMA_CASE.getCurrentValue())) { + return defaultCatalogName; + } else { + return defaultCatalogName == null ? null : defaultCatalogName.toUpperCase(Locale.US); + } + } + + public String getDateLiteral(String isoDate) { + String normalLiteral = super.getDateLiteral(isoDate); + if (this.isDateOnly(isoDate)) { + return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD')"; + } else if (this.isTimeOnly(isoDate)) { + return "TO_DATE(" + normalLiteral + ", 'HH24:MI:SS')"; + } else if (this.isTimestamp(isoDate)) { + return "TO_TIMESTAMP(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS.FF')"; + } else if (this.isDateTime(isoDate)) { + int seppos = normalLiteral.lastIndexOf(46); + if (seppos != -1) { + normalLiteral = normalLiteral.substring(0, seppos) + "'"; + } + + return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS')"; + } else { + return "UNSUPPORTED:" + isoDate; + } + } + + public boolean isSystemObject(DatabaseObject example) { + if (example == null) { + return false; + } else if (this.isLiquibaseObject(example)) { + return false; + } else { + if (example instanceof Schema) { + if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) { + return true; + } + + if ("SYSTEM".equals(example.getSchema().getCatalogName()) || "SYS".equals(example.getSchema().getCatalogName()) || "CTXSYS".equals(example.getSchema().getCatalogName()) || "XDB".equals(example.getSchema().getCatalogName())) { + return true; + } + } else if (this.isSystemObject(example.getSchema())) { + return true; + } + + if (example instanceof Catalog) { + if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) { + return true; + } + } else if (example.getName() != null) { + if (example.getName().startsWith("BIN$")) { + boolean filteredInOriginalQuery = this.canAccessDbaRecycleBin(); + if (!filteredInOriginalQuery) { + filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName()); + } + + if (!filteredInOriginalQuery) { + return true; + } + + return !(example instanceof PrimaryKey) && !(example instanceof Index) && !(example instanceof UniqueConstraint); + } + + if (example.getName().startsWith("AQ$")) { + return true; + } + + if (example.getName().startsWith("DR$")) { + return true; + } + + if (example.getName().startsWith("SYS_IOT_OVER")) { + return true; + } + + if ((example.getName().startsWith("MDRT_") || example.getName().startsWith("MDRS_")) && example.getName().endsWith("$")) { + return true; + } + + if (example.getName().startsWith("MLOG$_")) { + return true; + } + + if (example.getName().startsWith("RUPD$_")) { + return true; + } + + if (example.getName().startsWith("WM$_")) { + return true; + } + + if ("CREATE$JAVA$LOB$TABLE".equals(example.getName())) { + return true; + } + + if ("JAVA$CLASS$MD5$TABLE".equals(example.getName())) { + return true; + } + + if (example.getName().startsWith("ISEQ$$_")) { + return true; + } + + if (example.getName().startsWith("USLOG$")) { + return true; + } + + if (example.getName().startsWith("SYS_FBA")) { + return true; + } + } + + return super.isSystemObject(example); + } + } + + public boolean supportsTablespaces() { + return true; + } + + public boolean supportsAutoIncrement() { + boolean isAutoIncrementSupported = false; + + try { + if (this.getDatabaseMajorVersion() >= 12) { + isAutoIncrementSupported = true; + } + } catch (DatabaseException var3) { + isAutoIncrementSupported = false; + } + + return isAutoIncrementSupported; + } + + public boolean supportsRestrictForeignKeys() { + return false; + } + + public int getDataTypeMaxParameters(String dataTypeName) { + if ("BINARY_FLOAT".equals(dataTypeName.toUpperCase())) { + return 0; + } else { + return "BINARY_DOUBLE".equals(dataTypeName.toUpperCase()) ? 0 : super.getDataTypeMaxParameters(dataTypeName); + } + } + + public String getSystemTableWhereClause(String tableNameColumn) { + List clauses = new ArrayList(Arrays.asList("BIN$", "AQ$", "DR$", "SYS_IOT_OVER", "MLOG$_", "RUPD$_", "WM$_", "ISEQ$$_", "USLOG$", "SYS_FBA")); + clauses.replaceAll((s) -> tableNameColumn + " NOT LIKE '" + s + "%'"); + return "(" + StringUtil.join(clauses, " AND ") + ")"; + } + + public boolean jdbcCallsCatalogsSchemas() { + return true; + } + + public Set getUserDefinedTypes() { + if (this.userDefinedTypes == null) { + this.userDefinedTypes = new HashSet(); + if (this.getConnection() != null && !(this.getConnection() instanceof OfflineConnection)) { + try { + try { + this.userDefinedTypes.addAll(((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForList(new RawParameterizedSqlStatement("SELECT DISTINCT TYPE_NAME FROM ALL_TYPES"), String.class)); + } catch (DatabaseException var2) { + this.userDefinedTypes.addAll(((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForList(new RawParameterizedSqlStatement("SELECT TYPE_NAME FROM USER_TYPES"), String.class)); + } + } catch (DatabaseException var3) { + } + } + } + + return this.userDefinedTypes; + } + + public String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) { + if (databaseFunction != null && "current_timestamp".equalsIgnoreCase(databaseFunction.toString())) { + return databaseFunction.toString(); + } else if (!(databaseFunction instanceof SequenceNextValueFunction) && !(databaseFunction instanceof SequenceCurrentValueFunction)) { + return super.generateDatabaseFunctionValue(databaseFunction); + } else { + String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction); + return quotedSeq.replaceFirst("\"([^.\"]+)\\.([^.\"]+)\"", "\"$1\".\"$2\""); + } + } + + public ValidationErrors validate() { + ValidationErrors errors = super.validate(); + DatabaseConnection connection = this.getConnection(); + if (connection != null && !(connection instanceof OfflineConnection)) { + if (!this.canAccessDbaRecycleBin()) { + errors.addWarning(this.getDbaRecycleBinWarning()); + } + + return errors; + } else { + Scope.getCurrentScope().getLog(this.getClass()).info("Cannot validate offline database"); + return errors; + } + } + + public String getDbaRecycleBinWarning() { + return "Liquibase needs to access the DBA_RECYCLEBIN table so we can automatically handle the case where constraints are deleted and restored. Since Oracle doesn't properly restore the original table names referenced in the constraint, we use the information from the DBA_RECYCLEBIN to automatically correct this issue.\n\nThe user you used to connect to the database (" + this.getConnection().getConnectionUserName() + ") needs to have \"SELECT ON SYS.DBA_RECYCLEBIN\" permissions set before we can perform this operation. Please run the following SQL to set the appropriate permissions, and try running the command again.\n\n GRANT SELECT ON SYS.DBA_RECYCLEBIN TO " + this.getConnection().getConnectionUserName() + ";"; + } + + public boolean canAccessDbaRecycleBin() { + if (this.canAccessDbaRecycleBin == null) { + DatabaseConnection connection = this.getConnection(); + if (connection == null || connection instanceof OfflineConnection) { + return false; + } + + Statement statement = null; + + try { + statement = ((JdbcConnection)connection).createStatement(); + ResultSet resultSet = statement.executeQuery("select 1 from dba_recyclebin where 0=1"); + resultSet.close(); + this.canAccessDbaRecycleBin = true; + } catch (Exception var7) { + if (var7 instanceof SQLException && var7.getMessage().startsWith("ORA-00942")) { + this.canAccessDbaRecycleBin = false; + } else { + Scope.getCurrentScope().getLog(this.getClass()).warning("Cannot check dba_recyclebin access", var7); + this.canAccessDbaRecycleBin = false; + } + } finally { + JdbcUtil.close((ResultSet)null, statement); + } + } + + return this.canAccessDbaRecycleBin; + } + + public boolean supportsNotNullConstraintNames() { + return true; + } + + public boolean isValidOracleIdentifier(String identifier, Class type) { + if (identifier != null && identifier.length() >= 1) { + if (!identifier.matches("^(i?)[A-Z][A-Z0-9\\$\\_\\#]*$")) { + return false; + } else { + return identifier.length() <= 128; + } + } else { + return false; + } + } + + public int getIdentifierMaximumLength() { + try { + if (this.getDatabaseMajorVersion() < 12) { + return 30; + } else { + return this.getDatabaseMajorVersion() == 12 && this.getDatabaseMinorVersion() <= 1 ? 30 : 128; + } + } catch (DatabaseException ex) { + throw new UnexpectedLiquibaseException("Cannot determine the Oracle database version number", ex); + } + } + + public boolean supportsDatabaseChangeLogHistory() { + return true; + } + + public String correctObjectName(String objectName, Class objectType) { + return objectType.equals(Column.class) && StringUtils.startsWithIgnoreCase(objectName, "int") ? "NUMBER(*, 0)" : super.correctObjectName(objectName, objectType); + } +} diff --git a/zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java b/zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java new file mode 100644 index 0000000..6f57410 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java @@ -0,0 +1,149 @@ +package liquibase.datatype.core; + +import liquibase.change.core.LoadDataChange; +import liquibase.database.Database; +import liquibase.database.core.*; +import liquibase.datatype.DataTypeInfo; +import liquibase.datatype.DatabaseDataType; +import liquibase.datatype.LiquibaseDataType; +import liquibase.exception.UnexpectedLiquibaseException; +import liquibase.statement.DatabaseFunction; +import liquibase.util.StringUtil; + +import java.util.Locale; +import java.util.regex.Pattern; + +@DataTypeInfo(name = "boolean", aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, minParameters = 0, maxParameters = 0, priority = LiquibaseDataType.PRIORITY_DEFAULT) +public class BooleanType extends LiquibaseDataType { + + @Override + public DatabaseDataType toDatabaseDataType(Database database) { + String originalDefinition = StringUtil.trimToEmpty(getRawDefinition()); +// if ((database instanceof Firebird3Database)) { +// return new DatabaseDataType("BOOLEAN"); +// } + + if ((database instanceof AbstractDb2Database) || (database instanceof FirebirdDatabase)) { + return new DatabaseDataType("SMALLINT"); + } else if (database instanceof MSSQLDatabase) { + return new DatabaseDataType(database.escapeDataTypeName("bit")); + } else if (database instanceof MySQLDatabase) { + if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) { + return new DatabaseDataType("BIT", getParameters()); + } + return new DatabaseDataType("BIT", 1); + } else if (database instanceof OracleDatabase) { + return new DatabaseDataType("NUMBER", 1); + } else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase)) { + return new DatabaseDataType("BIT"); + } else if (database instanceof DerbyDatabase) { + if (((DerbyDatabase) database).supportsBooleanDataType()) { + return new DatabaseDataType("BOOLEAN"); + } else { + return new DatabaseDataType("SMALLINT"); + } + } else if (database.getClass().isAssignableFrom(DB2Database.class)) { + if (((DB2Database) database).supportsBooleanDataType()) + return new DatabaseDataType("BOOLEAN"); + else + return new DatabaseDataType("SMALLINT"); + } else if (database instanceof HsqlDatabase) { + return new DatabaseDataType("BOOLEAN"); + } else if (database instanceof PostgresDatabase) { + if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) { + return new DatabaseDataType("BIT", getParameters()); + } + } else if(database instanceof DmDatabase) { + return new DatabaseDataType("bit"); + } + + return super.toDatabaseDataType(database); + } + + @Override + public String objectToSql(Object value, Database database) { + if ((value == null) || "null".equals(value.toString().toLowerCase(Locale.US))) { + return null; + } + + String returnValue; + if (value instanceof String) { + value = ((String) value).replaceAll("'", ""); + if ("true".equals(((String) value).toLowerCase(Locale.US)) || "1".equals(value) || "b'1'".equals(((String) value).toLowerCase(Locale.US)) || "t".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) { + returnValue = this.getTrueBooleanValue(database); + } else if ("false".equals(((String) value).toLowerCase(Locale.US)) || "0".equals(value) || "b'0'".equals( + ((String) value).toLowerCase(Locale.US)) || "f".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) { + returnValue = this.getFalseBooleanValue(database); + } else { + throw new UnexpectedLiquibaseException("Unknown boolean value: " + value); + } + } else if (value instanceof Long) { + if (Long.valueOf(1).equals(value)) { + returnValue = this.getTrueBooleanValue(database); + } else { + returnValue = this.getFalseBooleanValue(database); + } + } else if (value instanceof Number) { + if (value.equals(1) || "1".equals(value.toString()) || "1.0".equals(value.toString())) { + returnValue = this.getTrueBooleanValue(database); + } else { + returnValue = this.getFalseBooleanValue(database); + } + } else if (value instanceof DatabaseFunction) { + return value.toString(); + } else if (value instanceof Boolean) { + if (((Boolean) value)) { + returnValue = this.getTrueBooleanValue(database); + } else { + returnValue = this.getFalseBooleanValue(database); + } + } else { + throw new UnexpectedLiquibaseException("Cannot convert type " + value.getClass() + " to a boolean value"); + } + + return returnValue; + } + + protected boolean isNumericBoolean(Database database) { + if (database instanceof DerbyDatabase) { + return !((DerbyDatabase) database).supportsBooleanDataType(); + } else if (database.getClass().isAssignableFrom(DB2Database.class)) { + return !((DB2Database) database).supportsBooleanDataType(); + } + return (database instanceof Db2zDatabase) || (database instanceof DB2Database) || (database instanceof FirebirdDatabase) || (database instanceof + MSSQLDatabase) || (database instanceof MySQLDatabase) || (database instanceof OracleDatabase) || + (database instanceof SQLiteDatabase) || (database instanceof SybaseASADatabase) || (database instanceof + SybaseDatabase) || (database instanceof DmDatabase); + } + + /** + * The database-specific value to use for "false" "boolean" columns. + */ + public String getFalseBooleanValue(Database database) { + if (isNumericBoolean(database)) { + return "0"; + } + if (database instanceof InformixDatabase) { + return "'f'"; + } + return "FALSE"; + } + + /** + * The database-specific value to use for "true" "boolean" columns. + */ + public String getTrueBooleanValue(Database database) { + if (isNumericBoolean(database)) { + return "1"; + } + if (database instanceof InformixDatabase) { + return "'t'"; + } + return "TRUE"; + } + + @Override + public LoadDataChange.LOAD_DATA_TYPE getLoadTypeName() { + return LoadDataChange.LOAD_DATA_TYPE.BOOLEAN; + } +} diff --git a/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java b/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java new file mode 100644 index 0000000..2ac83d5 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java @@ -0,0 +1,2094 @@ +/* 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.flowable.common.engine.impl; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.ServiceLoader; +import java.util.Set; + +import javax.naming.InitialContext; +import javax.sql.DataSource; + +import org.apache.commons.lang3.StringUtils; +import org.apache.ibatis.builder.xml.XMLConfigBuilder; +import org.apache.ibatis.builder.xml.XMLMapperBuilder; +import org.apache.ibatis.datasource.pooled.PooledDataSource; +import org.apache.ibatis.mapping.Environment; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.session.Configuration; +import org.apache.ibatis.session.SqlSessionFactory; +import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory; +import org.apache.ibatis.transaction.TransactionFactory; +import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; +import org.apache.ibatis.transaction.managed.ManagedTransactionFactory; +import org.apache.ibatis.type.ArrayTypeHandler; +import org.apache.ibatis.type.BigDecimalTypeHandler; +import org.apache.ibatis.type.BlobInputStreamTypeHandler; +import org.apache.ibatis.type.BlobTypeHandler; +import org.apache.ibatis.type.BooleanTypeHandler; +import org.apache.ibatis.type.ByteTypeHandler; +import org.apache.ibatis.type.ClobTypeHandler; +import org.apache.ibatis.type.DateOnlyTypeHandler; +import org.apache.ibatis.type.DateTypeHandler; +import org.apache.ibatis.type.DoubleTypeHandler; +import org.apache.ibatis.type.FloatTypeHandler; +import org.apache.ibatis.type.IntegerTypeHandler; +import org.apache.ibatis.type.JdbcType; +import org.apache.ibatis.type.LongTypeHandler; +import org.apache.ibatis.type.NClobTypeHandler; +import org.apache.ibatis.type.NStringTypeHandler; +import org.apache.ibatis.type.ShortTypeHandler; +import org.apache.ibatis.type.SqlxmlTypeHandler; +import org.apache.ibatis.type.StringTypeHandler; +import org.apache.ibatis.type.TimeOnlyTypeHandler; +import org.apache.ibatis.type.TypeHandlerRegistry; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; +import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.common.engine.api.engine.EngineLifecycleListener; +import org.flowable.common.engine.impl.agenda.AgendaOperationExecutionListener; +import org.flowable.common.engine.impl.agenda.AgendaOperationRunner; +import org.flowable.common.engine.impl.cfg.CommandExecutorImpl; +import org.flowable.common.engine.impl.cfg.IdGenerator; +import org.flowable.common.engine.impl.cfg.TransactionContextFactory; +import org.flowable.common.engine.impl.cfg.standalone.StandaloneMybatisTransactionContextFactory; +import org.flowable.common.engine.impl.db.CommonDbSchemaManager; +import org.flowable.common.engine.impl.db.DbSqlSessionFactory; +import org.flowable.common.engine.impl.db.LogSqlExecutionTimePlugin; +import org.flowable.common.engine.impl.db.MybatisTypeAliasConfigurator; +import org.flowable.common.engine.impl.db.MybatisTypeHandlerConfigurator; +import org.flowable.common.engine.impl.db.SchemaManager; +import org.flowable.common.engine.impl.event.EventDispatchAction; +import org.flowable.common.engine.impl.event.FlowableEventDispatcherImpl; +import org.flowable.common.engine.impl.interceptor.Command; +import org.flowable.common.engine.impl.interceptor.CommandConfig; +import org.flowable.common.engine.impl.interceptor.CommandContextFactory; +import org.flowable.common.engine.impl.interceptor.CommandContextInterceptor; +import org.flowable.common.engine.impl.interceptor.CommandExecutor; +import org.flowable.common.engine.impl.interceptor.CommandInterceptor; +import org.flowable.common.engine.impl.interceptor.CrDbRetryInterceptor; +import org.flowable.common.engine.impl.interceptor.DefaultCommandInvoker; +import org.flowable.common.engine.impl.interceptor.LogInterceptor; +import org.flowable.common.engine.impl.interceptor.SessionFactory; +import org.flowable.common.engine.impl.interceptor.TransactionContextInterceptor; +import org.flowable.common.engine.impl.lock.LockManager; +import org.flowable.common.engine.impl.lock.LockManagerImpl; +import org.flowable.common.engine.impl.logging.LoggingListener; +import org.flowable.common.engine.impl.logging.LoggingSession; +import org.flowable.common.engine.impl.logging.LoggingSessionFactory; +import org.flowable.common.engine.impl.persistence.GenericManagerFactory; +import org.flowable.common.engine.impl.persistence.StrongUuidGenerator; +import org.flowable.common.engine.impl.persistence.cache.EntityCache; +import org.flowable.common.engine.impl.persistence.cache.EntityCacheImpl; +import org.flowable.common.engine.impl.persistence.entity.ByteArrayEntityManager; +import org.flowable.common.engine.impl.persistence.entity.ByteArrayEntityManagerImpl; +import org.flowable.common.engine.impl.persistence.entity.Entity; +import org.flowable.common.engine.impl.persistence.entity.PropertyEntityManager; +import org.flowable.common.engine.impl.persistence.entity.PropertyEntityManagerImpl; +import org.flowable.common.engine.impl.persistence.entity.TableDataManager; +import org.flowable.common.engine.impl.persistence.entity.TableDataManagerImpl; +import org.flowable.common.engine.impl.persistence.entity.data.ByteArrayDataManager; +import org.flowable.common.engine.impl.persistence.entity.data.PropertyDataManager; +import org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisByteArrayDataManager; +import org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisPropertyDataManager; +import org.flowable.common.engine.impl.runtime.Clock; +import org.flowable.common.engine.impl.service.CommonEngineServiceImpl; +import org.flowable.common.engine.impl.util.DefaultClockImpl; +import org.flowable.common.engine.impl.util.IoUtil; +import org.flowable.common.engine.impl.util.ReflectUtil; +import org.flowable.eventregistry.api.EventRegistryEventConsumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; + +public abstract class AbstractEngineConfiguration { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + /** The tenant id indicating 'no tenant' */ + public static final String NO_TENANT_ID = ""; + + /** + * Checks the version of the DB schema against the library when the form engine is being created and throws an exception if the versions don't match. + */ + public static final String DB_SCHEMA_UPDATE_FALSE = "false"; + public static final String DB_SCHEMA_UPDATE_CREATE = "create"; + public static final String DB_SCHEMA_UPDATE_CREATE_DROP = "create-drop"; + + /** + * Creates the schema when the form engine is being created and drops the schema when the form engine is being closed. + */ + public static final String DB_SCHEMA_UPDATE_DROP_CREATE = "drop-create"; + + /** + * Upon building of the process engine, a check is performed and an update of the schema is performed if it is necessary. + */ + public static final String DB_SCHEMA_UPDATE_TRUE = "true"; + + protected boolean forceCloseMybatisConnectionPool = true; + + protected String databaseType; + protected String jdbcDriver = "org.h2.Driver"; + protected String jdbcUrl = "jdbc:h2:tcp://localhost/~/flowable"; + protected String jdbcUsername = "sa"; + protected String jdbcPassword = ""; + protected String dataSourceJndiName; + protected int jdbcMaxActiveConnections = 16; + protected int jdbcMaxIdleConnections = 8; + protected int jdbcMaxCheckoutTime; + protected int jdbcMaxWaitTime; + protected boolean jdbcPingEnabled; + protected String jdbcPingQuery; + protected int jdbcPingConnectionNotUsedFor; + protected int jdbcDefaultTransactionIsolationLevel; + protected DataSource dataSource; + protected SchemaManager commonSchemaManager; + protected SchemaManager schemaManager; + protected Command schemaManagementCmd; + + protected String databaseSchemaUpdate = DB_SCHEMA_UPDATE_FALSE; + + /** + * Whether to use a lock when performing the database schema create or update operations. + */ + protected boolean useLockForDatabaseSchemaUpdate = false; + + protected String xmlEncoding = "UTF-8"; + + // COMMAND EXECUTORS /////////////////////////////////////////////// + + protected CommandExecutor commandExecutor; + protected Collection defaultCommandInterceptors; + protected CommandConfig defaultCommandConfig; + protected CommandConfig schemaCommandConfig; + protected CommandContextFactory commandContextFactory; + protected CommandInterceptor commandInvoker; + + protected AgendaOperationRunner agendaOperationRunner = (commandContext, runnable) -> runnable.run(); + protected Collection agendaOperationExecutionListeners; + + protected List customPreCommandInterceptors; + protected List customPostCommandInterceptors; + protected List commandInterceptors; + + protected Map engineConfigurations = new HashMap<>(); + protected Map serviceConfigurations = new HashMap<>(); + + protected ClassLoader classLoader; + /** + * Either use Class.forName or ClassLoader.loadClass for class loading. See http://forums.activiti.org/content/reflectutilloadclass-and-custom- classloader + */ + protected boolean useClassForNameClassLoading = true; + + protected List engineLifecycleListeners; + + // Event Registry ////////////////////////////////////////////////// + protected Map eventRegistryEventConsumers = new HashMap<>(); + + // MYBATIS SQL SESSION FACTORY ///////////////////////////////////// + + protected boolean isDbHistoryUsed = true; + protected DbSqlSessionFactory dbSqlSessionFactory; + protected SqlSessionFactory sqlSessionFactory; + protected TransactionFactory transactionFactory; + protected TransactionContextFactory transactionContextFactory; + + /** + * If set to true, enables bulk insert (grouping sql inserts together). Default true. + * For some databases (eg DB2+z/OS) needs to be set to false. + */ + protected boolean isBulkInsertEnabled = true; + + /** + * Some databases have a limit of how many parameters one sql insert can have (eg SQL Server, 2000 params (!= insert statements) ). Tweak this parameter in case of exceptions indicating too much + * is being put into one bulk insert, or make it higher if your database can cope with it and there are inserts with a huge amount of data. + *

+ * By default: 100 (55 for mssql server as it has a hard limit of 2000 parameters in a statement) + */ + protected int maxNrOfStatementsInBulkInsert = 100; + + public int DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER = 55; // currently Execution has most params (35). 2000 / 35 = 57. + + protected String mybatisMappingFile; + protected Set> customMybatisMappers; + protected Set customMybatisXMLMappers; + protected List customMybatisInterceptors; + + protected Set dependentEngineMyBatisXmlMappers; + protected List dependentEngineMybatisTypeAliasConfigs; + protected List dependentEngineMybatisTypeHandlerConfigs; + + // SESSION FACTORIES /////////////////////////////////////////////// + protected List customSessionFactories; + protected Map, SessionFactory> sessionFactories; + + protected boolean enableEventDispatcher = true; + protected FlowableEventDispatcher eventDispatcher; + protected List eventListeners; + protected Map> typedEventListeners; + protected List additionalEventDispatchActions; + + protected LoggingListener loggingListener; + + protected boolean transactionsExternallyManaged; + + /** + * Flag that can be set to configure or not a relational database is used. This is useful for custom implementations that do not use relational databases at all. + * + * If true (default), the {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will be used to determine what needs to happen wrt the database schema. + * + * If false, no validation or schema creation will be done. That means that the database schema must have been created 'manually' before but the engine does not validate whether the schema is + * correct. The {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will not be used. + */ + protected boolean usingRelationalDatabase = true; + + /** + * Flag that can be set to configure whether or not a schema is used. This is useful for custom implementations that do not use relational databases at all. + * Setting {@link #usingRelationalDatabase} to true will automatically imply using a schema. + */ + protected boolean usingSchemaMgmt = true; + + /** + * Allows configuring a database table prefix which is used for all runtime operations of the process engine. For example, if you specify a prefix named 'PRE1.', Flowable will query for executions + * in a table named 'PRE1.ACT_RU_EXECUTION_'. + * + *

+ * NOTE: the prefix is not respected by automatic database schema management. If you use {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_CREATE_DROP} or + * {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_TRUE}, Flowable will create the database tables using the default names, regardless of the prefix configured here. + */ + protected String databaseTablePrefix = ""; + + /** + * Escape character for doing wildcard searches. + * + * This will be added at then end of queries that include for example a LIKE clause. For example: SELECT * FROM table WHERE column LIKE '%\%%' ESCAPE '\'; + */ + protected String databaseWildcardEscapeCharacter; + + /** + * database catalog to use + */ + protected String databaseCatalog = ""; + + /** + * In some situations you want to set the schema to use for table checks / generation if the database metadata doesn't return that correctly, see https://jira.codehaus.org/browse/ACT-1220, + * https://jira.codehaus.org/browse/ACT-1062 + */ + protected String databaseSchema; + + /** + * Set to true in case the defined databaseTablePrefix is a schema-name, instead of an actual table name prefix. This is relevant for checking if Flowable-tables exist, the databaseTablePrefix + * will not be used here - since the schema is taken into account already, adding a prefix for the table-check will result in wrong table-names. + */ + protected boolean tablePrefixIsSchema; + + /** + * Set to true if the latest version of a definition should be retrieved, ignoring a possible parent deployment id value + */ + protected boolean alwaysLookupLatestDefinitionVersion; + + /** + * Set to true if by default lookups should fallback to the default tenant (an empty string by default or a defined tenant value) + */ + protected boolean fallbackToDefaultTenant; + + /** + * Default tenant provider that is executed when looking up definitions, in case the global or local fallback to default tenant value is true + */ + protected DefaultTenantProvider defaultTenantProvider = (tenantId, scope, scopeKey) -> NO_TENANT_ID; + + /** + * Enables the MyBatis plugin that logs the execution time of sql statements. + */ + protected boolean enableLogSqlExecutionTime; + + protected Properties databaseTypeMappings = getDefaultDatabaseTypeMappings(); + + /** + * Duration between the checks when acquiring a lock. + */ + protected Duration lockPollRate = Duration.ofSeconds(10); + + /** + * Duration to wait for the DB Schema lock before giving up. + */ + protected Duration schemaLockWaitTime = Duration.ofMinutes(5); + + // DATA MANAGERS ////////////////////////////////////////////////////////////////// + + protected PropertyDataManager propertyDataManager; + protected ByteArrayDataManager byteArrayDataManager; + protected TableDataManager tableDataManager; + + // ENTITY MANAGERS //////////////////////////////////////////////////////////////// + + protected PropertyEntityManager propertyEntityManager; + protected ByteArrayEntityManager byteArrayEntityManager; + + protected List customPreDeployers; + protected List customPostDeployers; + protected List deployers; + + // CONFIGURATORS //////////////////////////////////////////////////////////// + + protected boolean enableConfiguratorServiceLoader = true; // Enabled by default. In certain environments this should be set to false (eg osgi) + protected List configurators; // The injected configurators + protected List allConfigurators; // Including auto-discovered configurators + protected EngineConfigurator idmEngineConfigurator; + protected EngineConfigurator eventRegistryConfigurator; + + public static final String PRODUCT_NAME_POSTGRES = "PostgreSQL"; + public static final String PRODUCT_NAME_CRDB = "CockroachDB"; + + public static final String DATABASE_TYPE_H2 = "h2"; + public static final String DATABASE_TYPE_HSQL = "hsql"; + public static final String DATABASE_TYPE_MYSQL = "mysql"; + public static final String DATABASE_TYPE_ORACLE = "oracle"; + public static final String DATABASE_TYPE_POSTGRES = "postgres"; + public static final String DATABASE_TYPE_MSSQL = "mssql"; + public static final String DATABASE_TYPE_DB2 = "db2"; + public static final String DATABASE_TYPE_COCKROACHDB = "cockroachdb"; + + public static Properties getDefaultDatabaseTypeMappings() { + Properties databaseTypeMappings = new Properties(); + databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2); + databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL); + databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL); + databaseTypeMappings.setProperty("MariaDB", DATABASE_TYPE_MYSQL); + databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE); + databaseTypeMappings.setProperty(PRODUCT_NAME_POSTGRES, DATABASE_TYPE_POSTGRES); + databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL); + databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/LINUXPPC64LE", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2); + databaseTypeMappings.setProperty(PRODUCT_NAME_CRDB, DATABASE_TYPE_COCKROACHDB); + databaseTypeMappings.setProperty("DM DBMS", DATABASE_TYPE_ORACLE);// 加入达梦支持 可以直接使用ORACLE或者MYSQL的 + return databaseTypeMappings; + } + + protected Map beans; + + protected IdGenerator idGenerator; + protected boolean usePrefixId; + + protected Clock clock; + protected ObjectMapper objectMapper; + + // Variables + + public static final int DEFAULT_GENERIC_MAX_LENGTH_STRING = 4000; + public static final int DEFAULT_ORACLE_MAX_LENGTH_STRING = 2000; + + /** + * Define a max length for storing String variable types in the database. Mainly used for the Oracle NVARCHAR2 limit of 2000 characters + */ + protected int maxLengthStringVariableType = -1; + + protected void initEngineConfigurations() { + addEngineConfiguration(getEngineCfgKey(), getEngineScopeType(), this); + } + + // DataSource + // /////////////////////////////////////////////////////////////// + + protected void initDataSource() { + if (dataSource == null) { + if (dataSourceJndiName != null) { + try { + dataSource = (DataSource) new InitialContext().lookup(dataSourceJndiName); + } catch (Exception e) { + throw new FlowableException("couldn't lookup datasource from " + dataSourceJndiName + ": " + e.getMessage(), e); + } + + } else if (jdbcUrl != null) { + if ((jdbcDriver == null) || (jdbcUsername == null)) { + throw new FlowableException("DataSource or JDBC properties have to be specified in a process engine configuration"); + } + + logger.debug("initializing datasource to db: {}", jdbcUrl); + + if (logger.isInfoEnabled()) { + logger.info("Configuring Datasource with following properties (omitted password for security)"); + logger.info("datasource driver : {}", jdbcDriver); + logger.info("datasource url : {}", jdbcUrl); + logger.info("datasource user name : {}", jdbcUsername); + } + + PooledDataSource pooledDataSource = new PooledDataSource(this.getClass().getClassLoader(), jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword); + + if (jdbcMaxActiveConnections > 0) { + pooledDataSource.setPoolMaximumActiveConnections(jdbcMaxActiveConnections); + } + if (jdbcMaxIdleConnections > 0) { + pooledDataSource.setPoolMaximumIdleConnections(jdbcMaxIdleConnections); + } + if (jdbcMaxCheckoutTime > 0) { + pooledDataSource.setPoolMaximumCheckoutTime(jdbcMaxCheckoutTime); + } + if (jdbcMaxWaitTime > 0) { + pooledDataSource.setPoolTimeToWait(jdbcMaxWaitTime); + } + if (jdbcPingEnabled) { + pooledDataSource.setPoolPingEnabled(true); + if (jdbcPingQuery != null) { + pooledDataSource.setPoolPingQuery(jdbcPingQuery); + } + pooledDataSource.setPoolPingConnectionsNotUsedFor(jdbcPingConnectionNotUsedFor); + } + if (jdbcDefaultTransactionIsolationLevel > 0) { + pooledDataSource.setDefaultTransactionIsolationLevel(jdbcDefaultTransactionIsolationLevel); + } + dataSource = pooledDataSource; + } + } + + if (databaseType == null) { + initDatabaseType(); + } + } + + public void initDatabaseType() { + Connection connection = null; + try { + connection = dataSource.getConnection(); + DatabaseMetaData databaseMetaData = connection.getMetaData(); + String databaseProductName = databaseMetaData.getDatabaseProductName(); + logger.debug("database product name: '{}'", databaseProductName); + + // CRDB does not expose the version through the jdbc driver, so we need to fetch it through version(). + if (PRODUCT_NAME_POSTGRES.equalsIgnoreCase(databaseProductName)) { + try (PreparedStatement preparedStatement = connection.prepareStatement("select version() as version;"); + ResultSet resultSet = preparedStatement.executeQuery()) { + String version = null; + if (resultSet.next()) { + version = resultSet.getString("version"); + } + + if (StringUtils.isNotEmpty(version) && version.toLowerCase().startsWith(PRODUCT_NAME_CRDB.toLowerCase())) { + databaseProductName = PRODUCT_NAME_CRDB; + logger.info("CockroachDB version '{}' detected", version); + } + } + } + + databaseType = databaseTypeMappings.getProperty(databaseProductName); + if (databaseType == null) { + throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'"); + } + logger.debug("using database type: {}", databaseType); + + } catch (SQLException e) { + throw new RuntimeException("Exception while initializing Database connection", e); + } finally { + try { + if (connection != null) { + connection.close(); + } + } catch (SQLException e) { + logger.error("Exception while closing the Database connection", e); + } + } + + // Special care for MSSQL, as it has a hard limit of 2000 params per statement (incl bulk statement). + // Especially with executions, with 100 as default, this limit is passed. + if (DATABASE_TYPE_MSSQL.equals(databaseType)) { + maxNrOfStatementsInBulkInsert = DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER; + } + } + + public void initSchemaManager() { + if (this.commonSchemaManager == null) { + this.commonSchemaManager = new CommonDbSchemaManager(); + } + } + + // session factories //////////////////////////////////////////////////////// + + public void addSessionFactory(SessionFactory sessionFactory) { + sessionFactories.put(sessionFactory.getSessionType(), sessionFactory); + } + + public void initCommandContextFactory() { + if (commandContextFactory == null) { + commandContextFactory = new CommandContextFactory(); + } + } + + public void initTransactionContextFactory() { + if (transactionContextFactory == null) { + transactionContextFactory = new StandaloneMybatisTransactionContextFactory(); + } + } + + public void initCommandExecutors() { + initDefaultCommandConfig(); + initSchemaCommandConfig(); + initCommandInvoker(); + initCommandInterceptors(); + initCommandExecutor(); + } + + + public void initDefaultCommandConfig() { + if (defaultCommandConfig == null) { + defaultCommandConfig = new CommandConfig(); + } + } + + public void initSchemaCommandConfig() { + if (schemaCommandConfig == null) { + schemaCommandConfig = new CommandConfig(); + } + } + + public void initCommandInvoker() { + if (commandInvoker == null) { + commandInvoker = new DefaultCommandInvoker(); + } + } + + public void initCommandInterceptors() { + if (commandInterceptors == null) { + commandInterceptors = new ArrayList<>(); + if (customPreCommandInterceptors != null) { + commandInterceptors.addAll(customPreCommandInterceptors); + } + commandInterceptors.addAll(getDefaultCommandInterceptors()); + if (customPostCommandInterceptors != null) { + commandInterceptors.addAll(customPostCommandInterceptors); + } + commandInterceptors.add(commandInvoker); + } + } + + public Collection getDefaultCommandInterceptors() { + if (defaultCommandInterceptors == null) { + List interceptors = new ArrayList<>(); + interceptors.add(new LogInterceptor()); + + if (DATABASE_TYPE_COCKROACHDB.equals(databaseType)) { + interceptors.add(new CrDbRetryInterceptor()); + } + + CommandInterceptor transactionInterceptor = createTransactionInterceptor(); + if (transactionInterceptor != null) { + interceptors.add(transactionInterceptor); + } + + if (commandContextFactory != null) { + String engineCfgKey = getEngineCfgKey(); + CommandContextInterceptor commandContextInterceptor = new CommandContextInterceptor(commandContextFactory, + classLoader, useClassForNameClassLoading, clock, objectMapper); + engineConfigurations.put(engineCfgKey, this); + commandContextInterceptor.setEngineCfgKey(engineCfgKey); + commandContextInterceptor.setEngineConfigurations(engineConfigurations); + interceptors.add(commandContextInterceptor); + } + + if (transactionContextFactory != null) { + interceptors.add(new TransactionContextInterceptor(transactionContextFactory)); + } + + List additionalCommandInterceptors = getAdditionalDefaultCommandInterceptors(); + if (additionalCommandInterceptors != null) { + interceptors.addAll(additionalCommandInterceptors); + } + + defaultCommandInterceptors = interceptors; + } + return defaultCommandInterceptors; + } + + public abstract String getEngineCfgKey(); + + public abstract String getEngineScopeType(); + + public List getAdditionalDefaultCommandInterceptors() { + return null; + } + + public void initCommandExecutor() { + if (commandExecutor == null) { + CommandInterceptor first = initInterceptorChain(commandInterceptors); + commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first); + } + } + + public CommandInterceptor initInterceptorChain(List chain) { + if (chain == null || chain.isEmpty()) { + throw new FlowableException("invalid command interceptor chain configuration: " + chain); + } + for (int i = 0; i < chain.size() - 1; i++) { + chain.get(i).setNext(chain.get(i + 1)); + } + return chain.get(0); + } + + public abstract CommandInterceptor createTransactionInterceptor(); + + + public void initBeans() { + if (beans == null) { + beans = new HashMap<>(); + } + } + + // id generator + // ///////////////////////////////////////////////////////////// + + public void initIdGenerator() { + if (idGenerator == null) { + idGenerator = new StrongUuidGenerator(); + } + } + + public void initObjectMapper() { + if (objectMapper == null) { + objectMapper = new ObjectMapper(); + objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); + } + } + + public void initClock() { + if (clock == null) { + clock = new DefaultClockImpl(); + } + } + + // Data managers /////////////////////////////////////////////////////////// + + public void initDataManagers() { + if (propertyDataManager == null) { + propertyDataManager = new MybatisPropertyDataManager(idGenerator); + } + + if (byteArrayDataManager == null) { + byteArrayDataManager = new MybatisByteArrayDataManager(idGenerator); + } + } + + // Entity managers ////////////////////////////////////////////////////////// + + public void initEntityManagers() { + if (propertyEntityManager == null) { + propertyEntityManager = new PropertyEntityManagerImpl(this, propertyDataManager); + } + + if (byteArrayEntityManager == null) { + byteArrayEntityManager = new ByteArrayEntityManagerImpl(byteArrayDataManager, getEngineCfgKey(), this::getEventDispatcher); + } + + if (tableDataManager == null) { + tableDataManager = new TableDataManagerImpl(this); + } + } + + // services + // ///////////////////////////////////////////////////////////////// + + protected void initService(Object service) { + if (service instanceof CommonEngineServiceImpl) { + ((CommonEngineServiceImpl) service).setCommandExecutor(commandExecutor); + } + } + + // myBatis SqlSessionFactory + // //////////////////////////////////////////////// + + public void initSessionFactories() { + if (sessionFactories == null) { + sessionFactories = new HashMap<>(); + + if (usingRelationalDatabase) { + initDbSqlSessionFactory(); + } + + addSessionFactory(new GenericManagerFactory(EntityCache.class, EntityCacheImpl.class)); + + if (isLoggingSessionEnabled()) { + if (!sessionFactories.containsKey(LoggingSession.class)) { + LoggingSessionFactory loggingSessionFactory = new LoggingSessionFactory(); + loggingSessionFactory.setLoggingListener(loggingListener); + loggingSessionFactory.setObjectMapper(objectMapper); + sessionFactories.put(LoggingSession.class, loggingSessionFactory); + } + } + + commandContextFactory.setSessionFactories(sessionFactories); + + } else { + if (usingRelationalDatabase) { + initDbSqlSessionFactoryEntitySettings(); + } + } + + if (customSessionFactories != null) { + for (SessionFactory sessionFactory : customSessionFactories) { + addSessionFactory(sessionFactory); + } + } + } + + public void initDbSqlSessionFactory() { + if (dbSqlSessionFactory == null) { + dbSqlSessionFactory = createDbSqlSessionFactory(); + } + dbSqlSessionFactory.setDatabaseType(databaseType); + dbSqlSessionFactory.setSqlSessionFactory(sqlSessionFactory); + dbSqlSessionFactory.setDbHistoryUsed(isDbHistoryUsed); + dbSqlSessionFactory.setDatabaseTablePrefix(databaseTablePrefix); + dbSqlSessionFactory.setTablePrefixIsSchema(tablePrefixIsSchema); + dbSqlSessionFactory.setDatabaseCatalog(databaseCatalog); + dbSqlSessionFactory.setDatabaseSchema(databaseSchema); + dbSqlSessionFactory.setMaxNrOfStatementsInBulkInsert(maxNrOfStatementsInBulkInsert); + + initDbSqlSessionFactoryEntitySettings(); + + addSessionFactory(dbSqlSessionFactory); + } + + public DbSqlSessionFactory createDbSqlSessionFactory() { + return new DbSqlSessionFactory(usePrefixId); + } + + protected abstract void initDbSqlSessionFactoryEntitySettings(); + + protected void defaultInitDbSqlSessionFactoryEntitySettings(List> insertOrder, List> deleteOrder) { + if (insertOrder != null) { + for (Class clazz : insertOrder) { + dbSqlSessionFactory.getInsertionOrder().add(clazz); + + if (isBulkInsertEnabled) { + dbSqlSessionFactory.getBulkInserteableEntityClasses().add(clazz); + } + } + } + + if (deleteOrder != null) { + for (Class clazz : deleteOrder) { + dbSqlSessionFactory.getDeletionOrder().add(clazz); + } + } + } + + public void initTransactionFactory() { + if (transactionFactory == null) { + if (transactionsExternallyManaged) { + transactionFactory = new ManagedTransactionFactory(); + Properties properties = new Properties(); + properties.put("closeConnection", "false"); + this.transactionFactory.setProperties(properties); + } else { + transactionFactory = new JdbcTransactionFactory(); + } + } + } + + public void initSqlSessionFactory() { + if (sqlSessionFactory == null) { + InputStream inputStream = null; + try { + inputStream = getMyBatisXmlConfigurationStream(); + + Environment environment = new Environment("default", transactionFactory, dataSource); + Reader reader = new InputStreamReader(inputStream); + Properties properties = new Properties(); + properties.put("prefix", databaseTablePrefix); + + String wildcardEscapeClause = ""; + if ((databaseWildcardEscapeCharacter != null) && (databaseWildcardEscapeCharacter.length() != 0)) { + wildcardEscapeClause = " escape '" + databaseWildcardEscapeCharacter + "'"; + } + properties.put("wildcardEscapeClause", wildcardEscapeClause); + + // set default properties + properties.put("limitBefore", ""); + properties.put("limitAfter", ""); + properties.put("limitBetween", ""); + properties.put("limitBeforeNativeQuery", ""); + properties.put("limitAfterNativeQuery", ""); + properties.put("blobType", "BLOB"); + properties.put("boolValue", "TRUE"); + + if (databaseType != null) { + properties.load(getResourceAsStream(pathToEngineDbProperties())); + } + + Configuration configuration = initMybatisConfiguration(environment, reader, properties); + sqlSessionFactory = new DefaultSqlSessionFactory(configuration); + + } catch (Exception e) { + throw new FlowableException("Error while building ibatis SqlSessionFactory: " + e.getMessage(), e); + } finally { + IoUtil.closeSilently(inputStream); + } + } else { + // This is needed when the SQL Session Factory is created by another engine. + // When custom XML Mappers are registered with this engine they need to be loaded in the configuration as well + applyCustomMybatisCustomizations(sqlSessionFactory.getConfiguration()); + } + } + + public String pathToEngineDbProperties() { + return "org/flowable/common/db/properties/" + databaseType + ".properties"; + } + + public Configuration initMybatisConfiguration(Environment environment, Reader reader, Properties properties) { + XMLConfigBuilder parser = new XMLConfigBuilder(reader, "", properties); + Configuration configuration = parser.getConfiguration(); + + if (databaseType != null) { + configuration.setDatabaseId(databaseType); + } + + configuration.setEnvironment(environment); + + initMybatisTypeHandlers(configuration); + initCustomMybatisInterceptors(configuration); + if (isEnableLogSqlExecutionTime()) { + initMyBatisLogSqlExecutionTimePlugin(configuration); + } + + configuration = parseMybatisConfiguration(parser); + return configuration; + } + + public void initCustomMybatisMappers(Configuration configuration) { + if (getCustomMybatisMappers() != null) { + for (Class clazz : getCustomMybatisMappers()) { + if (!configuration.hasMapper(clazz)) { + configuration.addMapper(clazz); + } + } + } + } + + public void initMybatisTypeHandlers(Configuration configuration) { + // When mapping into Map there is currently a problem with MyBatis. + // It will return objects which are driver specific. + // Therefore we are registering the mappings between Object.class and the specific jdbc type here. + // see https://github.com/mybatis/mybatis-3/issues/2216 for more info + TypeHandlerRegistry handlerRegistry = configuration.getTypeHandlerRegistry(); + + handlerRegistry.register(Object.class, JdbcType.BOOLEAN, new BooleanTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.BIT, new BooleanTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.TINYINT, new ByteTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.SMALLINT, new ShortTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.INTEGER, new IntegerTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.FLOAT, new FloatTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.DOUBLE, new DoubleTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.CHAR, new StringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.CLOB, new ClobTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.VARCHAR, new StringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.LONGVARCHAR, new StringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NVARCHAR, new NStringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NCHAR, new NStringTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NCLOB, new NClobTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.BIGINT, new LongTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.REAL, new BigDecimalTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.DECIMAL, new BigDecimalTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.NUMERIC, new BigDecimalTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.BLOB, new BlobInputStreamTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.LONGVARBINARY, new BlobTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.DATE, new DateOnlyTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.TIME, new TimeOnlyTypeHandler()); + handlerRegistry.register(Object.class, JdbcType.TIMESTAMP, new DateTypeHandler()); + + handlerRegistry.register(Object.class, JdbcType.SQLXML, new SqlxmlTypeHandler()); + } + + public void initCustomMybatisInterceptors(Configuration configuration) { + if (customMybatisInterceptors!=null){ + for (Interceptor interceptor :customMybatisInterceptors){ + configuration.addInterceptor(interceptor); + } + } + } + + public void initMyBatisLogSqlExecutionTimePlugin(Configuration configuration) { + configuration.addInterceptor(new LogSqlExecutionTimePlugin()); + } + + public Configuration parseMybatisConfiguration(XMLConfigBuilder parser) { + Configuration configuration = parser.parse(); + + applyCustomMybatisCustomizations(configuration); + return configuration; + } + + protected void applyCustomMybatisCustomizations(Configuration configuration) { + initCustomMybatisMappers(configuration); + + if (dependentEngineMybatisTypeAliasConfigs != null) { + for (MybatisTypeAliasConfigurator typeAliasConfig : dependentEngineMybatisTypeAliasConfigs) { + typeAliasConfig.configure(configuration.getTypeAliasRegistry()); + } + } + if (dependentEngineMybatisTypeHandlerConfigs != null) { + for (MybatisTypeHandlerConfigurator typeHandlerConfig : dependentEngineMybatisTypeHandlerConfigs) { + typeHandlerConfig.configure(configuration.getTypeHandlerRegistry()); + } + } + + parseDependentEngineMybatisXMLMappers(configuration); + parseCustomMybatisXMLMappers(configuration); + } + + public void parseCustomMybatisXMLMappers(Configuration configuration) { + if (getCustomMybatisXMLMappers() != null) { + for (String resource : getCustomMybatisXMLMappers()) { + parseMybatisXmlMapping(configuration, resource); + } + } + } + + public void parseDependentEngineMybatisXMLMappers(Configuration configuration) { + if (getDependentEngineMyBatisXmlMappers() != null) { + for (String resource : getDependentEngineMyBatisXmlMappers()) { + parseMybatisXmlMapping(configuration, resource); + } + } + } + + protected void parseMybatisXmlMapping(Configuration configuration, String resource) { + // see XMLConfigBuilder.mapperElement() + XMLMapperBuilder mapperParser = new XMLMapperBuilder(getResourceAsStream(resource), configuration, resource, configuration.getSqlFragments()); + mapperParser.parse(); + } + + protected InputStream getResourceAsStream(String resource) { + ClassLoader classLoader = getClassLoader(); + if (classLoader != null) { + return getClassLoader().getResourceAsStream(resource); + } else { + return this.getClass().getClassLoader().getResourceAsStream(resource); + } + } + + public void setMybatisMappingFile(String file) { + this.mybatisMappingFile = file; + } + + public String getMybatisMappingFile() { + return mybatisMappingFile; + } + + public abstract InputStream getMyBatisXmlConfigurationStream(); + + public void initConfigurators() { + + allConfigurators = new ArrayList<>(); + allConfigurators.addAll(getEngineSpecificEngineConfigurators()); + + // Configurators that are explicitly added to the config + if (configurators != null) { + allConfigurators.addAll(configurators); + } + + // Auto discovery through ServiceLoader + if (enableConfiguratorServiceLoader) { + ClassLoader classLoader = getClassLoader(); + if (classLoader == null) { + classLoader = ReflectUtil.getClassLoader(); + } + + ServiceLoader configuratorServiceLoader = ServiceLoader.load(EngineConfigurator.class, classLoader); + int nrOfServiceLoadedConfigurators = 0; + for (EngineConfigurator configurator : configuratorServiceLoader) { + allConfigurators.add(configurator); + nrOfServiceLoadedConfigurators++; + } + + if (nrOfServiceLoadedConfigurators > 0) { + logger.info("Found {} auto-discoverable Process Engine Configurator{}", nrOfServiceLoadedConfigurators, nrOfServiceLoadedConfigurators > 1 ? "s" : ""); + } + + if (!allConfigurators.isEmpty()) { + + // Order them according to the priorities (useful for dependent + // configurator) + allConfigurators.sort(new Comparator() { + + @Override + public int compare(EngineConfigurator configurator1, EngineConfigurator configurator2) { + int priority1 = configurator1.getPriority(); + int priority2 = configurator2.getPriority(); + + if (priority1 < priority2) { + return -1; + } else if (priority1 > priority2) { + return 1; + } + return 0; + } + }); + + // Execute the configurators + logger.info("Found {} Engine Configurators in total:", allConfigurators.size()); + for (EngineConfigurator configurator : allConfigurators) { + logger.info("{} (priority:{})", configurator.getClass(), configurator.getPriority()); + } + + } + + } + } + + public void close() { + if (forceCloseMybatisConnectionPool && dataSource instanceof PooledDataSource) { + /* + * When the datasource is created by a Flowable engine (i.e. it's an instance of PooledDataSource), + * the connection pool needs to be closed when closing the engine. + * Note that calling forceCloseAll() multiple times (as is the case when running with multiple engine) is ok. + */ + ((PooledDataSource) dataSource).forceCloseAll(); + } + } + + protected List getEngineSpecificEngineConfigurators() { + // meant to be overridden if needed + return Collections.emptyList(); + } + + public void configuratorsBeforeInit() { + for (EngineConfigurator configurator : allConfigurators) { + logger.info("Executing beforeInit() of {} (priority:{})", configurator.getClass(), configurator.getPriority()); + configurator.beforeInit(this); + } + } + + public void configuratorsAfterInit() { + for (EngineConfigurator configurator : allConfigurators) { + logger.info("Executing configure() of {} (priority:{})", configurator.getClass(), configurator.getPriority()); + configurator.configure(this); + } + } + + public LockManager getLockManager(String lockName) { + return new LockManagerImpl(commandExecutor, lockName, getLockPollRate(), getEngineCfgKey()); + } + + // getters and setters + // ////////////////////////////////////////////////////// + + public abstract String getEngineName(); + + public ClassLoader getClassLoader() { + return classLoader; + } + + public AbstractEngineConfiguration setClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + return this; + } + + public boolean isUseClassForNameClassLoading() { + return useClassForNameClassLoading; + } + + public AbstractEngineConfiguration setUseClassForNameClassLoading(boolean useClassForNameClassLoading) { + this.useClassForNameClassLoading = useClassForNameClassLoading; + return this; + } + + public void addEngineLifecycleListener(EngineLifecycleListener engineLifecycleListener) { + if (this.engineLifecycleListeners == null) { + this.engineLifecycleListeners = new ArrayList<>(); + } + this.engineLifecycleListeners.add(engineLifecycleListener); + } + + public List getEngineLifecycleListeners() { + return engineLifecycleListeners; + } + + public AbstractEngineConfiguration setEngineLifecycleListeners(List engineLifecycleListeners) { + this.engineLifecycleListeners = engineLifecycleListeners; + return this; + } + + public String getDatabaseType() { + return databaseType; + } + + public AbstractEngineConfiguration setDatabaseType(String databaseType) { + this.databaseType = databaseType; + return this; + } + + public DataSource getDataSource() { + return dataSource; + } + + public AbstractEngineConfiguration setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + return this; + } + + public SchemaManager getSchemaManager() { + return schemaManager; + } + + public AbstractEngineConfiguration setSchemaManager(SchemaManager schemaManager) { + this.schemaManager = schemaManager; + return this; + } + + public SchemaManager getCommonSchemaManager() { + return commonSchemaManager; + } + + public AbstractEngineConfiguration setCommonSchemaManager(SchemaManager commonSchemaManager) { + this.commonSchemaManager = commonSchemaManager; + return this; + } + + public Command getSchemaManagementCmd() { + return schemaManagementCmd; + } + + public AbstractEngineConfiguration setSchemaManagementCmd(Command schemaManagementCmd) { + this.schemaManagementCmd = schemaManagementCmd; + return this; + } + + public String getJdbcDriver() { + return jdbcDriver; + } + + public AbstractEngineConfiguration setJdbcDriver(String jdbcDriver) { + this.jdbcDriver = jdbcDriver; + return this; + } + + public String getJdbcUrl() { + return jdbcUrl; + } + + public AbstractEngineConfiguration setJdbcUrl(String jdbcUrl) { + this.jdbcUrl = jdbcUrl; + return this; + } + + public String getJdbcUsername() { + return jdbcUsername; + } + + public AbstractEngineConfiguration setJdbcUsername(String jdbcUsername) { + this.jdbcUsername = jdbcUsername; + return this; + } + + public String getJdbcPassword() { + return jdbcPassword; + } + + public AbstractEngineConfiguration setJdbcPassword(String jdbcPassword) { + this.jdbcPassword = jdbcPassword; + return this; + } + + public int getJdbcMaxActiveConnections() { + return jdbcMaxActiveConnections; + } + + public AbstractEngineConfiguration setJdbcMaxActiveConnections(int jdbcMaxActiveConnections) { + this.jdbcMaxActiveConnections = jdbcMaxActiveConnections; + return this; + } + + public int getJdbcMaxIdleConnections() { + return jdbcMaxIdleConnections; + } + + public AbstractEngineConfiguration setJdbcMaxIdleConnections(int jdbcMaxIdleConnections) { + this.jdbcMaxIdleConnections = jdbcMaxIdleConnections; + return this; + } + + public int getJdbcMaxCheckoutTime() { + return jdbcMaxCheckoutTime; + } + + public AbstractEngineConfiguration setJdbcMaxCheckoutTime(int jdbcMaxCheckoutTime) { + this.jdbcMaxCheckoutTime = jdbcMaxCheckoutTime; + return this; + } + + public int getJdbcMaxWaitTime() { + return jdbcMaxWaitTime; + } + + public AbstractEngineConfiguration setJdbcMaxWaitTime(int jdbcMaxWaitTime) { + this.jdbcMaxWaitTime = jdbcMaxWaitTime; + return this; + } + + public boolean isJdbcPingEnabled() { + return jdbcPingEnabled; + } + + public AbstractEngineConfiguration setJdbcPingEnabled(boolean jdbcPingEnabled) { + this.jdbcPingEnabled = jdbcPingEnabled; + return this; + } + + public int getJdbcPingConnectionNotUsedFor() { + return jdbcPingConnectionNotUsedFor; + } + + public AbstractEngineConfiguration setJdbcPingConnectionNotUsedFor(int jdbcPingConnectionNotUsedFor) { + this.jdbcPingConnectionNotUsedFor = jdbcPingConnectionNotUsedFor; + return this; + } + + public int getJdbcDefaultTransactionIsolationLevel() { + return jdbcDefaultTransactionIsolationLevel; + } + + public AbstractEngineConfiguration setJdbcDefaultTransactionIsolationLevel(int jdbcDefaultTransactionIsolationLevel) { + this.jdbcDefaultTransactionIsolationLevel = jdbcDefaultTransactionIsolationLevel; + return this; + } + + public String getJdbcPingQuery() { + return jdbcPingQuery; + } + + public AbstractEngineConfiguration setJdbcPingQuery(String jdbcPingQuery) { + this.jdbcPingQuery = jdbcPingQuery; + return this; + } + + public String getDataSourceJndiName() { + return dataSourceJndiName; + } + + public AbstractEngineConfiguration setDataSourceJndiName(String dataSourceJndiName) { + this.dataSourceJndiName = dataSourceJndiName; + return this; + } + + public CommandConfig getSchemaCommandConfig() { + return schemaCommandConfig; + } + + public AbstractEngineConfiguration setSchemaCommandConfig(CommandConfig schemaCommandConfig) { + this.schemaCommandConfig = schemaCommandConfig; + return this; + } + + public boolean isTransactionsExternallyManaged() { + return transactionsExternallyManaged; + } + + public AbstractEngineConfiguration setTransactionsExternallyManaged(boolean transactionsExternallyManaged) { + this.transactionsExternallyManaged = transactionsExternallyManaged; + return this; + } + + public Map getBeans() { + return beans; + } + + public AbstractEngineConfiguration setBeans(Map beans) { + this.beans = beans; + return this; + } + + public IdGenerator getIdGenerator() { + return idGenerator; + } + + public AbstractEngineConfiguration setIdGenerator(IdGenerator idGenerator) { + this.idGenerator = idGenerator; + return this; + } + + public boolean isUsePrefixId() { + return usePrefixId; + } + + public AbstractEngineConfiguration setUsePrefixId(boolean usePrefixId) { + this.usePrefixId = usePrefixId; + return this; + } + + public String getXmlEncoding() { + return xmlEncoding; + } + + public AbstractEngineConfiguration setXmlEncoding(String xmlEncoding) { + this.xmlEncoding = xmlEncoding; + return this; + } + + public CommandConfig getDefaultCommandConfig() { + return defaultCommandConfig; + } + + public AbstractEngineConfiguration setDefaultCommandConfig(CommandConfig defaultCommandConfig) { + this.defaultCommandConfig = defaultCommandConfig; + return this; + } + + public CommandExecutor getCommandExecutor() { + return commandExecutor; + } + + public AbstractEngineConfiguration setCommandExecutor(CommandExecutor commandExecutor) { + this.commandExecutor = commandExecutor; + return this; + } + + public CommandContextFactory getCommandContextFactory() { + return commandContextFactory; + } + + public AbstractEngineConfiguration setCommandContextFactory(CommandContextFactory commandContextFactory) { + this.commandContextFactory = commandContextFactory; + return this; + } + + public CommandInterceptor getCommandInvoker() { + return commandInvoker; + } + + public AbstractEngineConfiguration setCommandInvoker(CommandInterceptor commandInvoker) { + this.commandInvoker = commandInvoker; + return this; + } + + public AgendaOperationRunner getAgendaOperationRunner() { + return agendaOperationRunner; + } + + public AbstractEngineConfiguration setAgendaOperationRunner(AgendaOperationRunner agendaOperationRunner) { + this.agendaOperationRunner = agendaOperationRunner; + return this; + } + + public Collection getAgendaOperationExecutionListeners() { + return agendaOperationExecutionListeners; + } + + public AbstractEngineConfiguration addAgendaOperationExecutionListener(AgendaOperationExecutionListener listener) { + if (this.agendaOperationExecutionListeners == null) { + this.agendaOperationExecutionListeners = new ArrayList<>(); + } + this.agendaOperationExecutionListeners.add(listener); + return this; + } + + public AbstractEngineConfiguration setAgendaOperationExecutionListeners(Collection agendaOperationExecutionListeners) { + this.agendaOperationExecutionListeners = agendaOperationExecutionListeners; + return this; + } + + public List getCustomPreCommandInterceptors() { + return customPreCommandInterceptors; + } + + public AbstractEngineConfiguration addCustomPreCommandInterceptor(CommandInterceptor commandInterceptor) { + if (this.customPreCommandInterceptors == null) { + this.customPreCommandInterceptors = new ArrayList<>(); + } + this.customPreCommandInterceptors.add(commandInterceptor); + return this; + } + + public AbstractEngineConfiguration setCustomPreCommandInterceptors(List customPreCommandInterceptors) { + this.customPreCommandInterceptors = customPreCommandInterceptors; + return this; + } + + public List getCustomPostCommandInterceptors() { + return customPostCommandInterceptors; + } + + public AbstractEngineConfiguration addCustomPostCommandInterceptor(CommandInterceptor commandInterceptor) { + if (this.customPostCommandInterceptors == null) { + this.customPostCommandInterceptors = new ArrayList<>(); + } + this.customPostCommandInterceptors.add(commandInterceptor); + return this; + } + + public AbstractEngineConfiguration setCustomPostCommandInterceptors(List customPostCommandInterceptors) { + this.customPostCommandInterceptors = customPostCommandInterceptors; + return this; + } + + public List getCommandInterceptors() { + return commandInterceptors; + } + + public AbstractEngineConfiguration setCommandInterceptors(List commandInterceptors) { + this.commandInterceptors = commandInterceptors; + return this; + } + + public Map getEngineConfigurations() { + return engineConfigurations; + } + + public AbstractEngineConfiguration setEngineConfigurations(Map engineConfigurations) { + this.engineConfigurations = engineConfigurations; + return this; + } + + public void addEngineConfiguration(String key, String scopeType, AbstractEngineConfiguration engineConfiguration) { + if (engineConfigurations == null) { + engineConfigurations = new HashMap<>(); + } + engineConfigurations.put(key, engineConfiguration); + engineConfigurations.put(scopeType, engineConfiguration); + } + + public Map getServiceConfigurations() { + return serviceConfigurations; + } + + public AbstractEngineConfiguration setServiceConfigurations(Map serviceConfigurations) { + this.serviceConfigurations = serviceConfigurations; + return this; + } + + public void addServiceConfiguration(String key, AbstractServiceConfiguration serviceConfiguration) { + if (serviceConfigurations == null) { + serviceConfigurations = new HashMap<>(); + } + serviceConfigurations.put(key, serviceConfiguration); + } + + public Map getEventRegistryEventConsumers() { + return eventRegistryEventConsumers; + } + + public AbstractEngineConfiguration setEventRegistryEventConsumers(Map eventRegistryEventConsumers) { + this.eventRegistryEventConsumers = eventRegistryEventConsumers; + return this; + } + + public void addEventRegistryEventConsumer(String key, EventRegistryEventConsumer eventRegistryEventConsumer) { + if (eventRegistryEventConsumers == null) { + eventRegistryEventConsumers = new HashMap<>(); + } + eventRegistryEventConsumers.put(key, eventRegistryEventConsumer); + } + + public AbstractEngineConfiguration setDefaultCommandInterceptors(Collection defaultCommandInterceptors) { + this.defaultCommandInterceptors = defaultCommandInterceptors; + return this; + } + + public SqlSessionFactory getSqlSessionFactory() { + return sqlSessionFactory; + } + + public AbstractEngineConfiguration setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { + this.sqlSessionFactory = sqlSessionFactory; + return this; + } + + public boolean isDbHistoryUsed() { + return isDbHistoryUsed; + } + + public AbstractEngineConfiguration setDbHistoryUsed(boolean isDbHistoryUsed) { + this.isDbHistoryUsed = isDbHistoryUsed; + return this; + } + + public DbSqlSessionFactory getDbSqlSessionFactory() { + return dbSqlSessionFactory; + } + + public AbstractEngineConfiguration setDbSqlSessionFactory(DbSqlSessionFactory dbSqlSessionFactory) { + this.dbSqlSessionFactory = dbSqlSessionFactory; + return this; + } + + public TransactionFactory getTransactionFactory() { + return transactionFactory; + } + + public AbstractEngineConfiguration setTransactionFactory(TransactionFactory transactionFactory) { + this.transactionFactory = transactionFactory; + return this; + } + + public TransactionContextFactory getTransactionContextFactory() { + return transactionContextFactory; + } + + public AbstractEngineConfiguration setTransactionContextFactory(TransactionContextFactory transactionContextFactory) { + this.transactionContextFactory = transactionContextFactory; + return this; + } + + public int getMaxNrOfStatementsInBulkInsert() { + return maxNrOfStatementsInBulkInsert; + } + + public AbstractEngineConfiguration setMaxNrOfStatementsInBulkInsert(int maxNrOfStatementsInBulkInsert) { + this.maxNrOfStatementsInBulkInsert = maxNrOfStatementsInBulkInsert; + return this; + } + + public boolean isBulkInsertEnabled() { + return isBulkInsertEnabled; + } + + public AbstractEngineConfiguration setBulkInsertEnabled(boolean isBulkInsertEnabled) { + this.isBulkInsertEnabled = isBulkInsertEnabled; + return this; + } + + public Set> getCustomMybatisMappers() { + return customMybatisMappers; + } + + public AbstractEngineConfiguration setCustomMybatisMappers(Set> customMybatisMappers) { + this.customMybatisMappers = customMybatisMappers; + return this; + } + + public Set getCustomMybatisXMLMappers() { + return customMybatisXMLMappers; + } + + public AbstractEngineConfiguration setCustomMybatisXMLMappers(Set customMybatisXMLMappers) { + this.customMybatisXMLMappers = customMybatisXMLMappers; + return this; + } + + public Set getDependentEngineMyBatisXmlMappers() { + return dependentEngineMyBatisXmlMappers; + } + + public AbstractEngineConfiguration setCustomMybatisInterceptors(List customMybatisInterceptors) { + this.customMybatisInterceptors = customMybatisInterceptors; + return this; + } + + public List getCustomMybatisInterceptors() { + return customMybatisInterceptors; + } + + public AbstractEngineConfiguration setDependentEngineMyBatisXmlMappers(Set dependentEngineMyBatisXmlMappers) { + this.dependentEngineMyBatisXmlMappers = dependentEngineMyBatisXmlMappers; + return this; + } + + public List getDependentEngineMybatisTypeAliasConfigs() { + return dependentEngineMybatisTypeAliasConfigs; + } + + public AbstractEngineConfiguration setDependentEngineMybatisTypeAliasConfigs(List dependentEngineMybatisTypeAliasConfigs) { + this.dependentEngineMybatisTypeAliasConfigs = dependentEngineMybatisTypeAliasConfigs; + return this; + } + + public List getDependentEngineMybatisTypeHandlerConfigs() { + return dependentEngineMybatisTypeHandlerConfigs; + } + + public AbstractEngineConfiguration setDependentEngineMybatisTypeHandlerConfigs(List dependentEngineMybatisTypeHandlerConfigs) { + this.dependentEngineMybatisTypeHandlerConfigs = dependentEngineMybatisTypeHandlerConfigs; + return this; + } + + public List getCustomSessionFactories() { + return customSessionFactories; + } + + public AbstractEngineConfiguration addCustomSessionFactory(SessionFactory sessionFactory) { + if (customSessionFactories == null) { + customSessionFactories = new ArrayList<>(); + } + customSessionFactories.add(sessionFactory); + return this; + } + + public AbstractEngineConfiguration setCustomSessionFactories(List customSessionFactories) { + this.customSessionFactories = customSessionFactories; + return this; + } + + public boolean isUsingRelationalDatabase() { + return usingRelationalDatabase; + } + + public AbstractEngineConfiguration setUsingRelationalDatabase(boolean usingRelationalDatabase) { + this.usingRelationalDatabase = usingRelationalDatabase; + return this; + } + + public boolean isUsingSchemaMgmt() { + return usingSchemaMgmt; + } + + public AbstractEngineConfiguration setUsingSchemaMgmt(boolean usingSchema) { + this.usingSchemaMgmt = usingSchema; + return this; + } + + public String getDatabaseTablePrefix() { + return databaseTablePrefix; + } + + public AbstractEngineConfiguration setDatabaseTablePrefix(String databaseTablePrefix) { + this.databaseTablePrefix = databaseTablePrefix; + return this; + } + + public String getDatabaseWildcardEscapeCharacter() { + return databaseWildcardEscapeCharacter; + } + + public AbstractEngineConfiguration setDatabaseWildcardEscapeCharacter(String databaseWildcardEscapeCharacter) { + this.databaseWildcardEscapeCharacter = databaseWildcardEscapeCharacter; + return this; + } + + public String getDatabaseCatalog() { + return databaseCatalog; + } + + public AbstractEngineConfiguration setDatabaseCatalog(String databaseCatalog) { + this.databaseCatalog = databaseCatalog; + return this; + } + + public String getDatabaseSchema() { + return databaseSchema; + } + + public AbstractEngineConfiguration setDatabaseSchema(String databaseSchema) { + this.databaseSchema = databaseSchema; + return this; + } + + public boolean isTablePrefixIsSchema() { + return tablePrefixIsSchema; + } + + public AbstractEngineConfiguration setTablePrefixIsSchema(boolean tablePrefixIsSchema) { + this.tablePrefixIsSchema = tablePrefixIsSchema; + return this; + } + + public boolean isAlwaysLookupLatestDefinitionVersion() { + return alwaysLookupLatestDefinitionVersion; + } + + public AbstractEngineConfiguration setAlwaysLookupLatestDefinitionVersion(boolean alwaysLookupLatestDefinitionVersion) { + this.alwaysLookupLatestDefinitionVersion = alwaysLookupLatestDefinitionVersion; + return this; + } + + public boolean isFallbackToDefaultTenant() { + return fallbackToDefaultTenant; + } + + public AbstractEngineConfiguration setFallbackToDefaultTenant(boolean fallbackToDefaultTenant) { + this.fallbackToDefaultTenant = fallbackToDefaultTenant; + return this; + } + + public AbstractEngineConfiguration setDefaultTenantValue(String defaultTenantValue) { + this.defaultTenantProvider = (tenantId, scope, scopeKey) -> defaultTenantValue; + return this; + } + + public DefaultTenantProvider getDefaultTenantProvider() { + return defaultTenantProvider; + } + + public AbstractEngineConfiguration setDefaultTenantProvider(DefaultTenantProvider defaultTenantProvider) { + this.defaultTenantProvider = defaultTenantProvider; + return this; + } + + public boolean isEnableLogSqlExecutionTime() { + return enableLogSqlExecutionTime; + } + + public void setEnableLogSqlExecutionTime(boolean enableLogSqlExecutionTime) { + this.enableLogSqlExecutionTime = enableLogSqlExecutionTime; + } + + public Map, SessionFactory> getSessionFactories() { + return sessionFactories; + } + + public AbstractEngineConfiguration setSessionFactories(Map, SessionFactory> sessionFactories) { + this.sessionFactories = sessionFactories; + return this; + } + + public String getDatabaseSchemaUpdate() { + return databaseSchemaUpdate; + } + + public AbstractEngineConfiguration setDatabaseSchemaUpdate(String databaseSchemaUpdate) { + this.databaseSchemaUpdate = databaseSchemaUpdate; + return this; + } + + public boolean isUseLockForDatabaseSchemaUpdate() { + return useLockForDatabaseSchemaUpdate; + } + + public AbstractEngineConfiguration setUseLockForDatabaseSchemaUpdate(boolean useLockForDatabaseSchemaUpdate) { + this.useLockForDatabaseSchemaUpdate = useLockForDatabaseSchemaUpdate; + return this; + } + + public boolean isEnableEventDispatcher() { + return enableEventDispatcher; + } + + public AbstractEngineConfiguration setEnableEventDispatcher(boolean enableEventDispatcher) { + this.enableEventDispatcher = enableEventDispatcher; + return this; + } + + public FlowableEventDispatcher getEventDispatcher() { + return eventDispatcher; + } + + public AbstractEngineConfiguration setEventDispatcher(FlowableEventDispatcher eventDispatcher) { + this.eventDispatcher = eventDispatcher; + return this; + } + + public List getEventListeners() { + return eventListeners; + } + + public AbstractEngineConfiguration setEventListeners(List eventListeners) { + this.eventListeners = eventListeners; + return this; + } + + public Map> getTypedEventListeners() { + return typedEventListeners; + } + + public AbstractEngineConfiguration setTypedEventListeners(Map> typedEventListeners) { + this.typedEventListeners = typedEventListeners; + return this; + } + + public List getAdditionalEventDispatchActions() { + return additionalEventDispatchActions; + } + + public AbstractEngineConfiguration setAdditionalEventDispatchActions(List additionalEventDispatchActions) { + this.additionalEventDispatchActions = additionalEventDispatchActions; + return this; + } + + public void initEventDispatcher() { + if (this.eventDispatcher == null) { + this.eventDispatcher = new FlowableEventDispatcherImpl(); + } + + initAdditionalEventDispatchActions(); + + this.eventDispatcher.setEnabled(enableEventDispatcher); + + initEventListeners(); + initTypedEventListeners(); + } + + protected void initEventListeners() { + if (eventListeners != null) { + for (FlowableEventListener listenerToAdd : eventListeners) { + this.eventDispatcher.addEventListener(listenerToAdd); + } + } + } + + protected void initAdditionalEventDispatchActions() { + if (this.additionalEventDispatchActions == null) { + this.additionalEventDispatchActions = new ArrayList<>(); + } + } + + protected void initTypedEventListeners() { + if (typedEventListeners != null) { + for (Map.Entry> listenersToAdd : typedEventListeners.entrySet()) { + // Extract types from the given string + FlowableEngineEventType[] types = FlowableEngineEventType.getTypesFromString(listenersToAdd.getKey()); + + for (FlowableEventListener listenerToAdd : listenersToAdd.getValue()) { + this.eventDispatcher.addEventListener(listenerToAdd, types); + } + } + } + } + + public boolean isLoggingSessionEnabled() { + return loggingListener != null; + } + + public LoggingListener getLoggingListener() { + return loggingListener; + } + + public void setLoggingListener(LoggingListener loggingListener) { + this.loggingListener = loggingListener; + } + + public Clock getClock() { + return clock; + } + + public AbstractEngineConfiguration setClock(Clock clock) { + this.clock = clock; + return this; + } + + public ObjectMapper getObjectMapper() { + return objectMapper; + } + + public AbstractEngineConfiguration setObjectMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + return this; + } + + public int getMaxLengthString() { + if (maxLengthStringVariableType == -1) { + if ("oracle".equalsIgnoreCase(databaseType)) { + return DEFAULT_ORACLE_MAX_LENGTH_STRING; + } else { + return DEFAULT_GENERIC_MAX_LENGTH_STRING; + } + } else { + return maxLengthStringVariableType; + } + } + + public int getMaxLengthStringVariableType() { + return maxLengthStringVariableType; + } + + public AbstractEngineConfiguration setMaxLengthStringVariableType(int maxLengthStringVariableType) { + this.maxLengthStringVariableType = maxLengthStringVariableType; + return this; + } + + public PropertyDataManager getPropertyDataManager() { + return propertyDataManager; + } + + public Duration getLockPollRate() { + return lockPollRate; + } + + public AbstractEngineConfiguration setLockPollRate(Duration lockPollRate) { + this.lockPollRate = lockPollRate; + return this; + } + + public Duration getSchemaLockWaitTime() { + return schemaLockWaitTime; + } + + public void setSchemaLockWaitTime(Duration schemaLockWaitTime) { + this.schemaLockWaitTime = schemaLockWaitTime; + } + + public AbstractEngineConfiguration setPropertyDataManager(PropertyDataManager propertyDataManager) { + this.propertyDataManager = propertyDataManager; + return this; + } + + public PropertyEntityManager getPropertyEntityManager() { + return propertyEntityManager; + } + + public AbstractEngineConfiguration setPropertyEntityManager(PropertyEntityManager propertyEntityManager) { + this.propertyEntityManager = propertyEntityManager; + return this; + } + + public ByteArrayDataManager getByteArrayDataManager() { + return byteArrayDataManager; + } + + public AbstractEngineConfiguration setByteArrayDataManager(ByteArrayDataManager byteArrayDataManager) { + this.byteArrayDataManager = byteArrayDataManager; + return this; + } + + public ByteArrayEntityManager getByteArrayEntityManager() { + return byteArrayEntityManager; + } + + public AbstractEngineConfiguration setByteArrayEntityManager(ByteArrayEntityManager byteArrayEntityManager) { + this.byteArrayEntityManager = byteArrayEntityManager; + return this; + } + + public TableDataManager getTableDataManager() { + return tableDataManager; + } + + public AbstractEngineConfiguration setTableDataManager(TableDataManager tableDataManager) { + this.tableDataManager = tableDataManager; + return this; + } + + public List getDeployers() { + return deployers; + } + + public AbstractEngineConfiguration setDeployers(List deployers) { + this.deployers = deployers; + return this; + } + + public List getCustomPreDeployers() { + return customPreDeployers; + } + + public AbstractEngineConfiguration setCustomPreDeployers(List customPreDeployers) { + this.customPreDeployers = customPreDeployers; + return this; + } + + public List getCustomPostDeployers() { + return customPostDeployers; + } + + public AbstractEngineConfiguration setCustomPostDeployers(List customPostDeployers) { + this.customPostDeployers = customPostDeployers; + return this; + } + + public boolean isEnableConfiguratorServiceLoader() { + return enableConfiguratorServiceLoader; + } + + public AbstractEngineConfiguration setEnableConfiguratorServiceLoader(boolean enableConfiguratorServiceLoader) { + this.enableConfiguratorServiceLoader = enableConfiguratorServiceLoader; + return this; + } + + public List getConfigurators() { + return configurators; + } + + public AbstractEngineConfiguration addConfigurator(EngineConfigurator configurator) { + if (configurators == null) { + configurators = new ArrayList<>(); + } + configurators.add(configurator); + return this; + } + + /** + * @return All {@link EngineConfigurator} instances. Will only contain values after init of the engine. + * Use the {@link #getConfigurators()} or {@link #addConfigurator(EngineConfigurator)} methods otherwise. + */ + public List getAllConfigurators() { + return allConfigurators; + } + + public AbstractEngineConfiguration setConfigurators(List configurators) { + this.configurators = configurators; + return this; + } + + public EngineConfigurator getIdmEngineConfigurator() { + return idmEngineConfigurator; + } + + public AbstractEngineConfiguration setIdmEngineConfigurator(EngineConfigurator idmEngineConfigurator) { + this.idmEngineConfigurator = idmEngineConfigurator; + return this; + } + + public EngineConfigurator getEventRegistryConfigurator() { + return eventRegistryConfigurator; + } + + public AbstractEngineConfiguration setEventRegistryConfigurator(EngineConfigurator eventRegistryConfigurator) { + this.eventRegistryConfigurator = eventRegistryConfigurator; + return this; + } + + public AbstractEngineConfiguration setForceCloseMybatisConnectionPool(boolean forceCloseMybatisConnectionPool) { + this.forceCloseMybatisConnectionPool = forceCloseMybatisConnectionPool; + return this; + } + + public boolean isForceCloseMybatisConnectionPool() { + return forceCloseMybatisConnectionPool; + } +} diff --git a/zt-module-bpm-server/src/main/resources/META-INF/package-info.md b/zt-module-bpm-server/src/main/resources/META-INF/package-info.md new file mode 100644 index 0000000..1932c7a --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/META-INF/package-info.md @@ -0,0 +1 @@ +防止IDEA将`.`和`/`混为一谈 \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database b/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database new file mode 100644 index 0000000..0ccf224 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database @@ -0,0 +1,20 @@ +liquibase.database.core.CockroachDatabase +liquibase.database.core.DB2Database +liquibase.database.core.Db2zDatabase +liquibase.database.core.DerbyDatabase +#liquibase.database.core.Firebird3Database +liquibase.database.core.FirebirdDatabase +liquibase.database.core.H2Database +liquibase.database.core.HsqlDatabase +liquibase.database.core.InformixDatabase +liquibase.database.core.Ingres9Database +liquibase.database.core.MSSQLDatabase +liquibase.database.core.MariaDBDatabase +liquibase.database.core.MockDatabase +liquibase.database.core.MySQLDatabase +liquibase.database.core.OracleDatabase +liquibase.database.core.PostgresDatabase +liquibase.database.core.SQLiteDatabase +liquibase.database.core.SybaseASADatabase +liquibase.database.core.SybaseDatabase +liquibase.database.core.UnsupportedDatabase diff --git a/zt-module-bpm-server/src/main/resources/application-dev.yaml b/zt-module-bpm-server/src/main/resources/application-dev.yaml new file mode 100644 index 0000000..26fb687 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/application-dev.yaml @@ -0,0 +1,94 @@ +--- #################### 数据库相关配置 #################### +spring: + # 数据源配置项 + autoconfigure: + exclude: + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 5 # 初始连接数 + min-idle: 10 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: jygk-test + password: Zgty@0527 + slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: jygk-test + password: Zgty@0527 + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 172.16.46.63 # 地址 + port: 30379 # 端口 + database: 1 # 数据库索引 +# password: 123456 # 密码,建议生产环境开启 + +--- #################### MQ 消息队列相关配置 #################### + +--- #################### 定时任务相关配置 #################### +xxl: + job: + admin: + addresses: http://172.16.46.63:30082/xxl-job-admin # 调度中心部署跟地址 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +--- #################### ZT相关配置 #################### + + diff --git a/zt-module-bpm-server/src/main/resources/application-local.yaml b/zt-module-bpm-server/src/main/resources/application-local.yaml new file mode 100644 index 0000000..4d12470 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/application-local.yaml @@ -0,0 +1,111 @@ +--- #################### 数据库相关配置 #################### +spring: + # 数据源配置项 + autoconfigure: + exclude: + - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 + datasource: + druid: # Druid 【监控】相关的全局配置 + web-stat-filter: + enabled: true + stat-view-servlet: + enabled: true + allow: # 设置白名单,不填则允许所有访问 + url-pattern: /druid/* + login-username: # 控制台管理用户名和密码 + login-password: + filter: + stat: + enabled: true + log-slow-sql: true # 慢 SQL 记录 + slow-sql-millis: 100 + merge-sql: true + wall: + config: + multi-statement-allow: true + dynamic: # 多数据源配置 + druid: # Druid 【连接池】相关的全局配置 + initial-size: 1 # 初始连接数 + min-idle: 1 # 最小连接池数量 + max-active: 20 # 最大连接池数量 + max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 + time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 + min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 + max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 + validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 + test-while-idle: true + test-on-borrow: false + test-on-return: false + primary: master + datasource: + master: + url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: jygk-test + password: Zgty@0527 + slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 + lazy: true # 开启懒加载,保证启动速度 + url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 + username: jygk-test + password: Zgty@0527 + + # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 + data: + redis: + host: 172.16.46.64 # 地址 + port: 30379 # 端口 + database: 0 # 数据库索引 +# password: 123456 # 密码,建议生产环境开启 + +--- #################### MQ 消息队列相关配置 #################### + +--- #################### 定时任务相关配置 #################### + +xxl: + job: + enabled: false # 是否开启调度中心,默认为 true 开启 + admin: + addresses: http://172.16.46.63:30082/xxl-job-admin # 调度中心部署跟地址 + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项 +lock4j: + acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 + expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 + +--- #################### 监控相关配置 #################### + +# Actuator 监控端点的配置项 +management: + endpoints: + web: + base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator + exposure: + include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 + +# Spring Boot Admin 配置项 +spring: + boot: + admin: + # Spring Boot Admin Client 客户端的相关配置 + client: + instance: + service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] + +# 日志文件配置 +logging: + level: + # 配置自己写的 MyBatis Mapper 打印日志 + com.zt.plat.module.bpm.dal.mysql: debug + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + +--- #################### ZT相关配置 #################### + +# ZT配置项,设置当前项目所有自定义的配置 +cloud: + env: # 多环境的配置项 + tag: ${HOSTNAME} + security: + mock-enable: true + access-log: # 访问日志的配置项 + enable: false \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/resources/application.yaml b/zt-module-bpm-server/src/main/resources/application.yaml new file mode 100644 index 0000000..5d95503 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/application.yaml @@ -0,0 +1,150 @@ +spring: + application: + name: bpm-server + + profiles: + active: ${env.name} + #统一nacos配置,使用 profile 管理 + cloud: + nacos: + server-addr: ${config.server-addr} # Nacos 服务器地址 + username: ${config.username} # Nacos 账号 + password: ${config.password} # Nacos 密码 + discovery: # 【配置中心】配置项 + namespace: ${config.namespace} # 命名空间。这里使用 maven Profile 资源过滤进行动态替换 + group: ${config.group} # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + metadata: + version: 1.0.0 # 服务实例的版本号,可用于灰度发布 + config: # 【注册中心】配置项 + namespace: ${config.namespace} # 命名空间。这里使用 maven Profile 资源过滤进行动态替换 + group: ${config.group} # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP + + main: + allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 + allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务 + + config: + import: + - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置 + - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置 + + # Servlet 配置 + servlet: + # 文件上传相关配置项 + multipart: + max-file-size: 16MB # 单个文件大小 + max-request-size: 32MB # 设置总上传的文件大小 + + # Jackson 配置项 + jackson: + serialization: + write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 + write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 + write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 + fail-on-empty-beans: false # 允许序列化无属性的 Bean + + # Cache 配置项 + cache: + type: REDIS + redis: + time-to-live: 1h # 设置过期时间为 1 小时 + +server: + port: 48083 + +logging: + file: + name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 + +--- #################### 接口文档配置 #################### + +springdoc: + api-docs: + enabled: true # 1. 是否开启 Swagger 接文档的元数据 + path: /v3/api-docs + swagger-ui: + enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面 + path: /swagger-ui + default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 + +knife4j: + enable: false # TODO 芋艿:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 + setting: + language: zh_cn + +# 工作流 Flowable 配置 +flowable: + # 1. false: 默认值,Flowable 启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常 + # 2. true: 启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表 + # 3. create_drop: 启动时自动创建表,关闭时自动删除表 + # 4. drop_create: 启动时,删除旧表,再创建新表 + database-schema-update: false # 设置为 false,可通过 https://github.com/flowable/flowable-sql 初始化 + db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置 + check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程 + history-level: audit # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数 + +# MyBatis Plus 的配置项 +mybatis-plus: + configuration: + map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 + global-config: + db-config: + id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 + # id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库 + # id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库 + # id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解 + logic-delete-value: 1 # 逻辑已删除值(默认为 1) + logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) + banner: false # 关闭控制台的 Banner 打印 + type-aliases-package: ${cloud.info.base-package}.dal.dataobject + encryptor: + password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成 + +mybatis-plus-join: + banner: false # 关闭控制台的 Banner 打印 + +# Spring Data Redis 配置 +spring: + data: + redis: + repositories: + enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度 + +# VO 转换(数据翻译)相关 +easy-trans: + is-enable-global: true # 启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口 + +--- #################### RPC 远程调用相关配置 #################### + +--- #################### MQ 消息队列相关配置 #################### + +--- #################### 定时任务相关配置 #################### + +xxl: + job: + executor: + appname: ${spring.application.name} # 执行器 AppName + logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 + accessToken: default_token # 执行器通讯TOKEN + +--- #################### ZT相关配置 #################### + +cloud: + info: + version: 1.0.0 + base-package: com.zt.plat.module.bpm + web: + admin-ui: + url: http://dashboard.cloud.iocoder.cn # Admin 管理后台 UI 的地址 + xss: + enable: false + exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 + - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 + swagger: + title: 管理后台 + description: 提供管理员管理的所有功能 + version: ${cloud.info.version} + tenant: # 多租户相关配置项 + enable: true + +debug: false diff --git a/zt-module-bpm-server/src/main/resources/logback-spring.xml b/zt-module-bpm-server/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..e6801ea --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/logback-spring.xml @@ -0,0 +1,76 @@ + + + + + + + + + +       + + + ${PATTERN_DEFAULT} + + + + + + + + + + ${PATTERN_DEFAULT} + + + + ${LOG_FILE} + + + ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} + + ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} + + ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} + + ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} + + ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30} + + + + + + 0 + + 256 + + + + + + + + ${PATTERN_DEFAULT} + + + + + + + + + + + + + + + + + + + + + + diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java new file mode 100644 index 0000000..83a42f8 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java @@ -0,0 +1,274 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other.BpmTaskCandidateAssignEmptyStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.ExtensionElement; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockedStatic; +import org.mockito.Spy; +import org.mockito.internal.util.collections.Sets; + +import java.util.*; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomString; +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; +import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +/** + * {@link BpmTaskCandidateInvoker} 的单元测试 + * + * @author ZT + */ +public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest { + + private BpmTaskCandidateInvoker taskCandidateInvoker; + + @Mock + private AdminUserApi adminUserApi; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Spy + private BpmTaskCandidateStrategy userStrategy; + @Mock + private BpmTaskCandidateAssignEmptyStrategy emptyStrategy; + + @Spy + private List strategyList; + + @BeforeEach + public void setUp() { + userStrategy = new BpmTaskCandidateUserStrategy(); // 创建 strategy 实例 + when(emptyStrategy.getStrategy()).thenReturn(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY); + strategyList = List.of(userStrategy, emptyStrategy); // 创建 strategyList + taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi); + } + + /** + * 场景:成功计算到候选人,但是移除了发起人的用户 + */ + @Test + public void testCalculateUsersByTask_some() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + // 准备参数 + String param = "1,2"; + DelegateExecution execution = mock(DelegateExecution.class); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + String processInstanceId = randomString(); + when(execution.getProcessInstanceId()).thenReturn(processInstanceId); + when(execution.getCurrentFlowElement()).thenReturn(userTask); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString()); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM))) + .thenReturn(param); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 移除发起人的用户 + springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class)) + .thenReturn(processInstanceService); + ProcessInstance processInstance = mock(ProcessInstance.class); + when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn("1"); + mockFlowElementExtensionElement(userTask, BpmnModelConstants.USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, + String.valueOf(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())); + + // 调用 + Set results = taskCandidateInvoker.calculateUsersByTask(execution); + // 断言 + assertEquals(asSet(2L), results); + } + } + + /** + * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配 + */ + @Test + public void testCalculateUsersByTask_none() { + try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { + // 准备参数 + String param = "1,2"; + DelegateExecution execution = mock(DelegateExecution.class); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + String processInstanceId = randomString(); + when(execution.getProcessInstanceId()).thenReturn(processInstanceId); + when(execution.getCurrentFlowElement()).thenReturn(userTask); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString()); + when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM))) + .thenReturn(param); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 方法(empty) + when(emptyStrategy.calculateUsersByTask(same(execution), same(param))) + .thenReturn(Sets.newSet(2L)); + // mock 移除发起人的用户 + springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class)) + .thenReturn(processInstanceService); + ProcessInstance processInstance = mock(ProcessInstance.class); + when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn("1"); + + // 调用 + Set results = taskCandidateInvoker.calculateUsersByTask(execution); + // 断言 + assertEquals(asSet(2L), results); + } + } + + /** + * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配 + */ + @Test + public void testCalculateUsersByActivity_some() { + try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { + // 准备参数 + String param = "1,2"; + BpmnModel bpmnModel = mock(BpmnModel.class); + String activityId = randomString(); + Long startUserId = 1L; + String processDefinitionId = randomString(); + Map processVariables = new HashMap<>(); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy()); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask))) + .thenReturn(param); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 移除发起人的用户 + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignStartUserHandlerType(same(userTask))) + .thenReturn(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType()); + + // 调用 + Set results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, + startUserId, processDefinitionId, processVariables); + // 断言 + assertEquals(asSet(2L), results); + } + } + + /** + * 场景:成功计算到候选人,但是移除了发起人的用户 + */ + @Test + public void testCalculateUsersByActivity_none() { + try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { + // 准备参数 + String param = "1,2"; + BpmnModel bpmnModel = mock(BpmnModel.class); + String activityId = randomString(); + Long startUserId = 1L; + String processDefinitionId = randomString(); + Map processVariables = new HashMap<>(); + // mock 方法(DelegateExecution) + UserTask userTask = mock(UserTask.class); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask))) + .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy()); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask))) + .thenReturn(param); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask); + // mock 方法(adminUserApi) + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); + // mock 方法(empty) + when(emptyStrategy.calculateUsersByActivity(same(bpmnModel), eq(activityId), + eq(param), same(startUserId), same(processDefinitionId), same(processVariables))) + .thenReturn(Sets.newSet(2L)); + + // 调用 + Set results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, + startUserId, processDefinitionId, processVariables); + // 断言 + assertEquals(asSet(2L), results); + } + } + + private static void mockFlowElementExtensionElement(FlowElement element, String name, String value) { + if (value == null) { + return; + } + ExtensionElement extensionElement = new ExtensionElement(); + extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); + extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); + extensionElement.setElementText(value); + extensionElement.setName(name); + // mock + Map> extensionElements = element.getExtensionElements(); + if (extensionElements == null) { + extensionElements = new LinkedHashMap<>(); + } + extensionElements.put(name, Collections.singletonList(extensionElement)); + when(element.getExtensionElements()).thenReturn(extensionElements); + } + + @Test + public void testRemoveDisableUsers() { + // 准备参数. 1L 可以找到;2L 是禁用的;3L 找不到 + Set assigneeUserIds = asSet(1L, 2L, 3L); + // mock 方法 + AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) + .setStatus(CommonStatusEnum.ENABLE.getStatus())); + AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) + .setStatus(CommonStatusEnum.DISABLE.getStatus())); + Map userMap = MapUtil.builder(user1.getId(), user1) + .put(user2.getId(), user2).build(); + when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap); + + // 调用 + taskCandidateInvoker.removeDisableUsers(assigneeUserIds); + // 断言 + assertEquals(asSet(1L), assigneeUserIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java new file mode 100644 index 0000000..d7d1151 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java @@ -0,0 +1,107 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.expression; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Collections; +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskAssignLeaderExpression expression; + + @Mock + private AdminUserApi adminUserApi; + @Mock + private DeptApi deptApi; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Test + public void testCalculateUsers_noDept() { + // 准备参数 + DelegateExecution execution = mockDelegateExecution(1L); + // mock 方法(startUser) + AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptIds(Collections.singletonList(10L))); + when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser)); + // mock 方法(getStartUserDept)没有部门 + when(deptApi.getDept(eq(10L))).thenReturn(success(null)); + + // 调用 + Set result = expression.calculateUsers(execution, 1); + // 断言 + assertEquals(0, result.size()); + } + + @Test + public void testCalculateUsers_noParentDept() { + // 准备参数 + DelegateExecution execution = mockDelegateExecution(1L); + // mock 方法(startUser) + AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptIds(Collections.singletonList(10L))); + when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser)); + DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L) + .setLeaderUserId(20L)); + // mock 方法(getDept) + when(deptApi.getDept(eq(10L))).thenReturn(success(startUserDept)); + when(deptApi.getDept(eq(100L))).thenReturn(success(null)); + + // 调用 + Set result = expression.calculateUsers(execution, 2); + // 断言 + assertEquals(asSet(20L), result); + } + + @Test + public void testCalculateUsers_existParentDept() { + // 准备参数 + DelegateExecution execution = mockDelegateExecution(1L); + // mock 方法(startUser) + AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptIds(Collections.singletonList(10L))); + when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser)); + DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L) + .setLeaderUserId(20L)); + when(deptApi.getDept(eq(10L))).thenReturn(success(startUserDept)); + // mock 方法(父 dept) + DeptRespDTO parentDept = randomPojo(DeptRespDTO.class, o -> o.setId(100L).setParentId(1000L) + .setLeaderUserId(200L)); + when(deptApi.getDept(eq(100L))).thenReturn(success(parentDept)); + + // 调用 + Set result = expression.calculateUsers(execution, 2); + // 断言 + assertEquals(asSet(200L), result); + } + + @SuppressWarnings("SameParameterValue") + private DelegateExecution mockDelegateExecution(Long startUserId) { + ExecutionEntityImpl execution = new ExecutionEntityImpl(); + execution.setProcessInstanceId(randomString()); + // mock 返回 startUserId + ExecutionEntityImpl processInstance = new ExecutionEntityImpl(); + processInstance.setStartUserId(String.valueOf(startUserId)); + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))) + .thenReturn(processInstance); + return execution; + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java new file mode 100644 index 0000000..63d4c39 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java @@ -0,0 +1,45 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import org.assertj.core.util.Sets; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.stubbing.Answer; + +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateDeptLeaderMultiStrategy strategy; + + @Mock + private DeptApi deptApi; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "10,20|2"; + // mock 方法 + when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult>) invocationOnMock -> { + Long deptId = invocationOnMock.getArgument(0); + return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1))); + }); + + // 调用 + Set userIds = strategy.calculateUsers(param); + // 断言结果 + assertEquals(Sets.newLinkedHashSet(11L, 1001L, 21L, 2001L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java new file mode 100644 index 0000000..a7bbbf1 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java @@ -0,0 +1,44 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import org.assertj.core.util.Sets; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateDeptLeaderStrategy strategy; + + @Mock + private DeptApi deptApi; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "10,20"; + // mock 方法 + when(deptApi.getDeptList(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList( + randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(10L).setLeaderUserId(11L)), + randomPojo(DeptRespDTO.class, o -> o.setId(20L).setParentId(20L).setLeaderUserId(21L))))); + + // 调用 + Set userIds = strategy.calculateUsers(param); + // 断言结果 + assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java new file mode 100644 index 0000000..443eeb7 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java @@ -0,0 +1,47 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.assertj.core.util.Sets; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateDeptMemberStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateDeptMemberStrategy strategy; + + @Mock + private DeptApi deptApi; + @Mock + private AdminUserApi adminUserApi; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "10,20"; + // mock 方法 + when(adminUserApi.getUserListByDeptIds(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList( + randomPojo(AdminUserRespDTO.class, o -> o.setId(11L)), + randomPojo(AdminUserRespDTO.class, o -> o.setId(21L))))); + + // 调用 + Set userIds = strategy.calculateUsers(param); + // 断言结果 + assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java new file mode 100644 index 0000000..4a34602 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java @@ -0,0 +1,85 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.assertj.core.util.Sets; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.stubbing.Answer; + +import java.util.Collections; +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateStartUserDeptLeaderMultiStrategy strategy; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Mock + private AdminUserApi adminUserApi; + @Mock + private DeptApi deptApi; + + @Test + public void testCalculateUsersByTask() { + // 准备参数 + String param = "2"; + // mock 方法(获得流程发起人) + Long startUserId = 1L; + ProcessInstance processInstance = mock(ProcessInstance.class); + DelegateExecution execution = mock(DelegateExecution.class); + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn(startUserId.toString()); + // mock 方法(获取发起人的 multi 部门负责人) + mockGetStartUserDept(startUserId); + + // 调用 + Set userIds = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds); + } + + @Test + public void testCalculateUsersByActivity() { + // 准备参数 + String param = "2"; + // mock 方法 + Long startUserId = 1L; + mockGetStartUserDept(startUserId); + + // 调用 + Set userIds = strategy.calculateUsersByActivity(null, null, param, + startUserId, null, null); + // 断言 + assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds); + } + + private void mockGetStartUserDept(Long startUserId) { + when(adminUserApi.getUser(eq(startUserId))).thenReturn( + success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptIds(Collections.singletonList(10L))))); + when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult>) invocationOnMock -> { + Long deptId = invocationOnMock.getArgument(0); + return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1))); + }); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java new file mode 100644 index 0000000..f829b9e --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java @@ -0,0 +1,85 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.assertj.core.util.Sets; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.stubbing.Answer; + +import java.util.Collections; +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateStartUserDeptLeaderStrategy strategy; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Mock + private AdminUserApi adminUserApi; + @Mock + private DeptApi deptApi; + + @Test + public void testCalculateUsersByTask() { + // 准备参数 + String param = "2"; + // mock 方法(获得流程发起人) + Long startUserId = 1L; + ProcessInstance processInstance = mock(ProcessInstance.class); + DelegateExecution execution = mock(DelegateExecution.class); + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn(startUserId.toString()); + // mock 方法(获取发起人的部门负责人) + mockGetStartUserDeptLeader(startUserId); + + // 调用 + Set userIds = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(Sets.newLinkedHashSet(1001L), userIds); + } + + @Test + public void testGetStartUserDeptLeader() { + // 准备参数 + String param = "2"; + // mock 方法 + Long startUserId = 1L; + mockGetStartUserDeptLeader(startUserId); + + // 调用 + Set userIds = strategy.calculateUsersByActivity(null, null, param, + startUserId, null, null); + // 断言 + assertEquals(Sets.newLinkedHashSet(1001L), userIds); + } + + private void mockGetStartUserDeptLeader(Long startUserId) { + when(adminUserApi.getUser(eq(startUserId))).thenReturn( + success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptIds(Collections.singletonList(10L))))); + when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult>) invocationOnMock -> { + Long deptId = invocationOnMock.getArgument(0); + return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1))); + }); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java new file mode 100644 index 0000000..2769280 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java @@ -0,0 +1,68 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; + +import cn.hutool.core.map.MapUtil; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import org.assertj.core.util.Sets; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateStartUserSelectStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateStartUserSelectStrategy strategy; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Test + public void testCalculateUsersByTask() { + // 准备参数 + String param = "2"; + // mock 方法(获得流程发起人) + ProcessInstance processInstance = mock(ProcessInstance.class); + DelegateExecution execution = mock(DelegateExecution.class); + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); + when(execution.getCurrentActivityId()).thenReturn("activity_001"); + // mock 方法(FlowableUtils) + Map processVariables = new HashMap<>(); + processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, + MapUtil.of("activity_001", List.of(1L, 2L))); + when(processInstance.getProcessVariables()).thenReturn(processVariables); + + // 调用 + Set userIds = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds); + } + + @Test + public void testCalculateUsersByActivity() { + // 准备参数 + String activityId = "activity_001"; + Map processVariables = new HashMap<>(); + processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, + MapUtil.of("activity_001", List.of(1L, 2L))); + + // 调用 + Set userIds = strategy.calculateUsersByActivity(null, activityId, null, + null, null, processVariables); + // 断言 + assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java new file mode 100644 index 0000000..1bf5725 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java @@ -0,0 +1,88 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; + +import cn.hutool.core.collection.ListUtil; +import com.zt.plat.framework.common.util.collection.SetUtils; +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum; +import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.engine.delegate.DelegateExecution; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockedStatic; + +import java.util.Set; + +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomString; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; + +public class BpmTaskCandidateAssignEmptyStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateAssignEmptyStrategy strategy; + + @Mock + private BpmProcessDefinitionService processDefinitionService; + + @Test + public void testCalculateUsersByTask() { + try (MockedStatic flowableUtilMockedStatic = mockStatic(FlowableUtils.class); + MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { + // 准备参数 + DelegateExecution execution = mock(DelegateExecution.class); + String param = randomString(); + // mock 方法(execution) + String processDefinitionId = randomString(); + when(execution.getProcessDefinitionId()).thenReturn(processDefinitionId); + FlowElement flowElement = mock(FlowElement.class); + when(execution.getCurrentFlowElement()).thenReturn(flowElement); + // mock 方法(parseAssignEmptyHandlerType) + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement))) + .thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType()); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerUserIds(same(flowElement))) + .thenReturn(ListUtil.of(1L, 2L)); + + // 调用 + Set userIds = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(SetUtils.asSet(1L, 2L), userIds); + } + + } + + @Test + public void testCalculateUsersByActivity() { + try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { + // 准备参数 + String processDefinitionId = randomString(); + String activityId = randomString(); + String param = randomString(); + // mock 方法(getFlowElementById) + FlowElement flowElement = mock(FlowElement.class); + BpmnModel bpmnModel = mock(BpmnModel.class); + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))) + .thenReturn(flowElement); + // mock 方法(parseAssignEmptyHandlerType) + bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement))) + .thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType()); + // mock 方法(getProcessDefinitionInfo) + BpmProcessDefinitionInfoDO processDefinition = randomPojo(BpmProcessDefinitionInfoDO.class, + o -> o.setManagerUserIds(ListUtil.of(1L, 2L))); + when(processDefinitionService.getProcessDefinitionInfo(eq(processDefinitionId))).thenReturn(processDefinition); + + // 调用 + Set userIds = strategy.calculateUsersByActivity(bpmnModel, activityId, param, + null, processDefinitionId, null); + // 断言 + assertEquals(SetUtils.asSet(1L, 2L), userIds); + } + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java new file mode 100644 index 0000000..d9ad569 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java @@ -0,0 +1,61 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; +import org.flowable.engine.delegate.DelegateExecution; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.MockedStatic; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; + +@Disabled // TODO 芋艿:临时注释 +public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateExpressionStrategy strategy; + + @Test + public void testCalculateUsersByTask() { + try (MockedStatic flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) { + // 准备参数 + String param = "1,2"; + DelegateExecution execution = mock(DelegateExecution.class); + // mock 方法 + flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(execution), eq(param))) + .thenReturn(asSet(1L, 2L)); + + // 调用 + Set results = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(asSet(1L, 2L), results); + } + } + + @Test + public void testCalculateUsersByActivity() { + try (MockedStatic flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) { + // 准备参数 + String param = "1,2"; + Map processVariables = new HashMap<>(); + // mock 方法 + flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(processVariables), eq(param))) + .thenReturn(asSet(1L, 2L)); + + // 调用 + Set results = strategy.calculateUsersByActivity(null, null, param, + null, null, processVariables); + // 断言 + assertEquals(asSet(1L, 2L), results); + } + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java new file mode 100644 index 0000000..1bb03b5 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java @@ -0,0 +1,44 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import com.zt.plat.module.bpm.service.definition.BpmUserGroupService; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Arrays; +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@Disabled // TODO 芋艿:临时注释 +public class BpmTaskCandidateGroupStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateGroupStrategy strategy; + + @Mock + private BpmUserGroupService userGroupService; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "1,2"; + // mock 方法 + BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(11L, 12L))); + BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(21L, 22L))); + when(userGroupService.getUserGroupList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(userGroup1, userGroup2)); + + // 调用 + Set userIds = strategy.calculateUsersByTask(null, param); + // 断言 + assertEquals(asSet(11L, 12L, 21L, 22L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java new file mode 100644 index 0000000..8560052 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java @@ -0,0 +1,48 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.api.dept.PostApi; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.List; +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@Disabled // TODO 芋艿:临时注释 +public class BpmTaskCandidatePostStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidatePostStrategy strategy; + + @Mock + private PostApi postApi; + @Mock + private AdminUserApi adminUserApi; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "1,2"; + // mock 方法 + List users = convertList(asSet(11L, 22L), + id -> new AdminUserRespDTO().setId(id)); + when(adminUserApi.getUserListByPostIds(eq(asSet(1L, 2L)))).thenReturn(success(users)); + + // 调用 + Set userIds = strategy.calculateUsersByTask(null, param); + // 断言 + assertEquals(asSet(11L, 22L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java new file mode 100644 index 0000000..9af421a --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java @@ -0,0 +1,44 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.system.api.permission.PermissionApi; +import com.zt.plat.module.system.api.permission.RoleApi; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Set; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@Disabled // TODO 芋艿:临时注释 +public class BpmTaskCandidateRoleStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateRoleStrategy strategy; + + @Mock + private RoleApi roleApi; + @Mock + private PermissionApi permissionApi; + + @Test + public void testCalculateUsers() { + // 准备参数 + String param = "1,2"; + // mock 方法 + when(permissionApi.getUserRoleIdListByRoleIds(eq(asSet(1L, 2L)))) + .thenReturn(success(asSet(11L, 22L))); + + // 调用 + Set userIds = strategy.calculateUsersByTask(null, param); + // 断言 + assertEquals(asSet(11L, 22L), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java new file mode 100644 index 0000000..79c08ba --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java @@ -0,0 +1,56 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import org.assertj.core.util.Sets; +import org.flowable.engine.delegate.DelegateExecution; +import org.flowable.engine.runtime.ProcessInstance; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; +import org.mockito.Mock; + +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class BpmTaskCandidateStartUserStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateStartUserStrategy strategy; + + @Mock + private BpmProcessInstanceService processInstanceService; + + @Test + public void testCalculateUsersByTask() { + // 准备参数 + String param = "2"; + // mock 方法(获得流程发起人) + Long startUserId = 1L; + ProcessInstance processInstance = mock(ProcessInstance.class); + DelegateExecution execution = mock(DelegateExecution.class); + when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); + when(processInstance.getStartUserId()).thenReturn(startUserId.toString()); + + // 调用 + Set userIds = strategy.calculateUsersByTask(execution, param); + // 断言 + assertEquals(Sets.newLinkedHashSet(startUserId), userIds); + } + + @Test + public void testCalculateUsersByActivity() { + // 准备参数 + Long startUserId = 1L; + + // 调用 + Set userIds = strategy.calculateUsersByActivity(null, null, null, + startUserId, null, null); + // 断言 + assertEquals(Sets.newLinkedHashSet(startUserId), userIds); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java new file mode 100644 index 0000000..91e19c5 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; + +import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.mockito.InjectMocks; + +import java.util.Set; + +import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; +import static org.junit.jupiter.api.Assertions.assertEquals; + +@Disabled // TODO 芋艿:临时注释 +public class BpmTaskCandidateUserStrategyTest extends BaseMockitoUnitTest { + + @InjectMocks + private BpmTaskCandidateUserStrategy strategy; + + @Test + public void test() { + // 准备参数 + String param = "1,2"; + + // 调用 + Set userIds = strategy.calculateUsersByTask(null, param); + // 断言 + assertEquals(asSet(1L, 2L), userIds); + } + + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java new file mode 100644 index 0000000..621d68e --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java @@ -0,0 +1,136 @@ +package com.zt.plat.module.bpm.service.category; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.test.core.ut.BaseDbUnitTest; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.dal.mysql.category.BpmCategoryMapper; +import com.zt.plat.module.bpm.service.definition.BpmCategoryServiceImpl; +import jakarta.annotation.Resource; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import static com.zt.plat.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; +import static com.zt.plat.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static com.zt.plat.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static com.zt.plat.framework.test.core.util.AssertUtils.assertPojoEquals; +import static com.zt.plat.framework.test.core.util.AssertUtils.assertServiceException; +import static com.zt.plat.framework.test.core.util.RandomUtils.*; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link BpmCategoryServiceImpl} 的单元测试类 + * + * @author ZT + */ +@Import(BpmCategoryServiceImpl.class) +public class BpmCategoryServiceImplTest extends BaseDbUnitTest { + + @Resource + private BpmCategoryServiceImpl categoryService; + + @Resource + private BpmCategoryMapper categoryMapper; + + @Test + public void testCreateCategory_success() { + // 准备参数 + BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null) + .setStatus(randomCommonStatus()); + + // 调用 + Long categoryId = categoryService.createCategory(createReqVO); + // 断言 + assertNotNull(categoryId); + // 校验记录的属性是否正确 + BpmCategoryDO category = categoryMapper.selectById(categoryId); + assertPojoEquals(createReqVO, category, "id"); + } + + @Test + public void testUpdateCategory_success() { + // mock 数据 + BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class); + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 + // 准备参数 + BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> { + o.setId(dbCategory.getId()); // 设置更新的 ID + o.setStatus(randomCommonStatus()); + }); + + // 调用 + categoryService.updateCategory(updateReqVO); + // 校验是否更新正确 + BpmCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的 + assertPojoEquals(updateReqVO, category); + } + + @Test + public void testUpdateCategory_notExists() { + // 准备参数 + BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class); + + // 调用, 并断言异常 + assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS); + } + + @Test + public void testDeleteCategory_success() { + // mock 数据 + BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class); + categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbCategory.getId(); + + // 调用 + categoryService.deleteCategory(id); + // 校验数据不存在了 + assertNull(categoryMapper.selectById(id)); + } + + @Test + public void testDeleteCategory_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS); + } + + @Test + public void testGetCategoryPage() { + // mock 数据 + BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class, o -> { // 等会查询到 + o.setName("芋头"); + o.setCode("xiaodun"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2023, 2, 2)); + }); + categoryMapper.insert(dbCategory); + // 测试 name 不匹配 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName("小盾"))); + // 测试 code 不匹配 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode("tudou"))); + // 测试 status 不匹配 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(buildTime(2024, 2, 2)))); + // 准备参数 + BpmCategoryPageReqVO reqVO = new BpmCategoryPageReqVO(); + reqVO.setName("芋"); + reqVO.setCode("xiao"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); + + // 调用 + PageResult pageResult = categoryService.getCategoryPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbCategory, pageResult.getList().get(0)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java new file mode 100644 index 0000000..57d2f2a --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java @@ -0,0 +1,144 @@ +package com.zt.plat.module.bpm.service.definition; + +import cn.hutool.core.util.RandomUtil; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.test.core.ut.BaseDbUnitTest; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmFormMapper; +import com.zt.plat.module.bpm.service.definition.dto.BpmFormFieldRespDTO; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.zt.plat.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static com.zt.plat.framework.test.core.util.AssertUtils.assertPojoEquals; +import static com.zt.plat.framework.test.core.util.AssertUtils.assertServiceException; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomLongId; +import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.FORM_NOT_EXISTS; +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link BpmFormServiceImpl} 的单元测试类 + * + * @author ZT + */ +@Import(BpmFormServiceImpl.class) +public class BpmFormServiceTest extends BaseDbUnitTest { + + @Resource + private BpmFormServiceImpl formService; + + @Resource + private BpmFormMapper formMapper; + + @Test + public void testCreateForm_success() { + // 准备参数 + BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { + o.setConf("{}"); + o.setFields(randomFields()); + }); + + // 调用 + Long formId = formService.createForm(reqVO); + // 断言 + assertNotNull(formId); + // 校验记录的属性是否正确 + BpmFormDO form = formMapper.selectById(formId); + assertPojoEquals(reqVO, form); + } + + @Test + public void testUpdateForm_success() { + // mock 数据 + BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> { + o.setConf("{}"); + o.setFields(randomFields()); + }); + formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据 + // 准备参数 + BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { + o.setId(dbForm.getId()); // 设置更新的 ID + o.setConf("{'cloud': 'yuanma'}"); + o.setFields(randomFields()); + }); + + // 调用 + formService.updateForm(reqVO); + // 校验是否更新正确 + BpmFormDO form = formMapper.selectById(reqVO.getId()); // 获取最新的 + assertPojoEquals(reqVO, form); + } + + @Test + public void testUpdateForm_notExists() { + // 准备参数 + BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { + o.setConf("{'cloud': 'yuanma'}"); + o.setFields(randomFields()); + }); + + // 调用, 并断言异常 + assertServiceException(() -> formService.updateForm(reqVO), FORM_NOT_EXISTS); + } + + @Test + public void testDeleteForm_success() { + // mock 数据 + BpmFormDO dbForm = randomPojo(BpmFormDO.class); + formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbForm.getId(); + + // 调用 + formService.deleteForm(id); + // 校验数据不存在了 + assertNull(formMapper.selectById(id)); + } + + @Test + public void testDeleteForm_notExists() { + // 准备参数 + Long id = randomLongId(); + + // 调用, 并断言异常 + assertServiceException(() -> formService.deleteForm(id), FORM_NOT_EXISTS); + } + + @Test + public void testGetFormPage() { + // mock 数据 + BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> { // 等会查询到 + o.setName("ZT源码"); + }); + formMapper.insert(dbForm); + // 测试 name 不匹配 + formMapper.insert(cloneIgnoreId(dbForm, o -> o.setName("源码"))); + // 准备参数 + BpmFormPageReqVO reqVO = new BpmFormPageReqVO(); + reqVO.setName("ZT"); + + // 调用 + PageResult pageResult = formService.getFormPage(reqVO); + // 断言 + assertEquals(1, pageResult.getTotal()); + assertEquals(1, pageResult.getList().size()); + assertPojoEquals(dbForm, pageResult.getList().get(0)); + } + + private List randomFields() { + int size = RandomUtil.randomInt(1, 3); + return Stream.iterate(0, i -> i).limit(size) + .map(i -> JsonUtils.toJsonString(randomPojo(BpmFormFieldRespDTO.class))) + .collect(Collectors.toList()); + } + +} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java new file mode 100644 index 0000000..a74e9d3 --- /dev/null +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java @@ -0,0 +1,130 @@ +package com.zt.plat.module.bpm.service.definition; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.test.core.ut.BaseDbUnitTest; +import com.zt.plat.framework.test.core.util.AssertUtils; +import com.zt.plat.framework.test.core.util.RandomUtils; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import com.zt.plat.module.bpm.dal.mysql.definition.BpmUserGroupMapper; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.context.annotation.Import; + +import jakarta.annotation.Resource; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.LocalDateTimeUtils.buildTime; +import static com.zt.plat.framework.common.util.object.ObjectUtils.cloneIgnoreId; +import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS; + +/** + * {@link BpmUserGroupServiceImpl} 的单元测试类 + * + * @author ZT + */ +@Import(BpmUserGroupServiceImpl.class) +public class BpmUserGroupServiceTest extends BaseDbUnitTest { + + @Resource + private BpmUserGroupServiceImpl userGroupService; + + @Resource + private BpmUserGroupMapper userGroupMapper; + + @Test + public void testCreateUserGroup_success() { + // 准备参数 + BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class); + + // 调用 + Long userGroupId = userGroupService.createUserGroup(reqVO); + // 断言 + Assertions.assertNotNull(userGroupId); + // 校验记录的属性是否正确 + BpmUserGroupDO userGroup = userGroupMapper.selectById(userGroupId); + AssertUtils.assertPojoEquals(reqVO, userGroup); + } + + @Test + public void testUpdateUserGroup_success() { + // mock 数据 + BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class); + userGroupMapper.insert(dbUserGroup);// @Sql: 先插入出一条存在的数据 + // 准备参数 + BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class, o -> { + o.setId(dbUserGroup.getId()); // 设置更新的 ID + }); + + // 调用 + userGroupService.updateUserGroup(reqVO); + // 校验是否更新正确 + BpmUserGroupDO userGroup = userGroupMapper.selectById(reqVO.getId()); // 获取最新的 + AssertUtils.assertPojoEquals(reqVO, userGroup); + } + + @Test + public void testUpdateUserGroup_notExists() { + // 准备参数 + BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class); + + // 调用, 并断言异常 + AssertUtils.assertServiceException(() -> userGroupService.updateUserGroup(reqVO), USER_GROUP_NOT_EXISTS); + } + + @Test + public void testDeleteUserGroup_success() { + // mock 数据 + BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class); + userGroupMapper.insert(dbUserGroup);// @Sql: 先插入出一条存在的数据 + // 准备参数 + Long id = dbUserGroup.getId(); + + // 调用 + userGroupService.deleteUserGroup(id); + // 校验数据不存在了 + Assertions.assertNull(userGroupMapper.selectById(id)); + } + + @Test + public void testDeleteUserGroup_notExists() { + // 准备参数 + Long id = RandomUtils.randomLongId(); + + // 调用, 并断言异常 + AssertUtils.assertServiceException(() -> userGroupService.deleteUserGroup(id), USER_GROUP_NOT_EXISTS); + } + + @Test + public void testGetUserGroupPage() { + // mock 数据 + BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class, o -> { // 等会查询到 + o.setName("ZT源码"); + o.setStatus(CommonStatusEnum.ENABLE.getStatus()); + o.setCreateTime(buildTime(2021, 11, 11)); + }); + userGroupMapper.insert(dbUserGroup); + // 测试 name 不匹配 + userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("ZT"))); + // 测试 status 不匹配 + userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); + // 测试 createTime 不匹配 + userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setCreateTime(buildTime(2021, 12, 12)))); + // 准备参数 + BpmUserGroupPageReqVO reqVO = new BpmUserGroupPageReqVO(); + reqVO.setName("源码"); + reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); + reqVO.setCreateTime((new LocalDateTime[]{buildTime(2021, 11, 10),buildTime(2021, 11, 12)})); + + // 调用 + PageResult pageResult = userGroupService.getUserGroupPage(reqVO); + // 断言 + Assertions.assertEquals(1, pageResult.getTotal()); + Assertions.assertEquals(1, pageResult.getList().size()); + AssertUtils.assertPojoEquals(dbUserGroup, pageResult.getList().get(0)); + } + +} diff --git a/zt-module-bpm-server/src/test/resources/application-unit-test.yaml b/zt-module-bpm-server/src/test/resources/application-unit-test.yaml new file mode 100644 index 0000000..87521f0 --- /dev/null +++ b/zt-module-bpm-server/src/test/resources/application-unit-test.yaml @@ -0,0 +1,45 @@ +spring: + main: + lazy-initialization: true # 开启懒加载,加快速度 + banner-mode: off # 单元测试,禁用 Banner + +--- #################### 数据库相关配置 #################### + +spring: + # 数据源配置项 + datasource: + name: ruoyi-vue-pro + url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 + driver-class-name: org.h2.Driver + username: sa + password: + druid: + async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 + initial-size: 1 # 单元测试,配置为 1,提升启动速度 + sql: + init: + schema-locations: classpath:/sql/create_tables.sql + +mybatis-plus: + lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 + type-aliases-package: ${cloud.info.base-package}.dal.dataobject + global-config: + db-config: + id-type: AUTO # H2 主键递增 + +--- #################### 定时任务相关配置 #################### + +--- #################### 配置中心相关配置 #################### + +--- #################### 服务保障相关配置 #################### + +# Lock4j 配置项(单元测试,禁用 Lock4j) + +--- #################### 监控相关配置 #################### + +--- #################### ZT相关配置 #################### + +# ZT配置项,设置当前项目所有自定义的配置 +cloud: + info: + base-package: com.zt.plat.module.bpm diff --git a/zt-module-bpm-server/src/test/resources/logback.xml b/zt-module-bpm-server/src/test/resources/logback.xml new file mode 100644 index 0000000..daf756b --- /dev/null +++ b/zt-module-bpm-server/src/test/resources/logback.xml @@ -0,0 +1,4 @@ + + + + diff --git a/zt-module-bpm-server/src/test/resources/sql/clean.sql b/zt-module-bpm-server/src/test/resources/sql/clean.sql new file mode 100644 index 0000000..d4f93bb --- /dev/null +++ b/zt-module-bpm-server/src/test/resources/sql/clean.sql @@ -0,0 +1,3 @@ +DELETE FROM "bpm_form"; +DELETE FROM "bpm_user_group"; +DELETE FROM "bpm_category"; diff --git a/zt-module-bpm-server/src/test/resources/sql/create_tables.sql b/zt-module-bpm-server/src/test/resources/sql/create_tables.sql new file mode 100644 index 0000000..1034962 --- /dev/null +++ b/zt-module-bpm-server/src/test/resources/sql/create_tables.sql @@ -0,0 +1,43 @@ +CREATE TABLE IF NOT EXISTS "bpm_user_group" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(63) NOT NULL, + "description" varchar(255) NOT NULL, + "status" tinyint NOT NULL, + "user_ids" varchar(255) NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '用户组'; + +CREATE TABLE IF NOT EXISTS "bpm_category" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(63) NOT NULL, + "code" varchar(63) NOT NULL, + "description" varchar(255) NOT NULL, + "status" tinyint NOT NULL, + "sort" int NOT NULL, + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '分类'; + +CREATE TABLE IF NOT EXISTS "bpm_form" ( + "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, + "name" varchar(63) NOT NULL, + "status" tinyint NOT NULL, + "fields" varchar(255) NOT NULL, + "conf" varchar(255) NOT NULL, + "remark" varchar(255), + "creator" varchar(64) DEFAULT '', + "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updater" varchar(64) DEFAULT '', + "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + "deleted" bit NOT NULL DEFAULT FALSE, + PRIMARY KEY ("id") +) COMMENT '动态表单'; From 7cbcd1539973b13f1ec967245e2939c5504cbc95 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Mon, 22 Sep 2025 14:53:45 +0800 Subject: [PATCH 03/35] =?UTF-8?q?1.=20=E7=BB=9F=E4=B8=80=E5=8C=85=E5=90=8D?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/Dockerfile | 6 +++--- .../admin/definition/vo/form/BpmFormPageReqVO.java | 2 +- .../admin/definition/vo/form/BpmFormRespVO.java | 2 +- .../admin/definition/vo/form/BpmFormSaveReqVO.java | 2 +- .../definition/vo/group/BpmUserGroupPageReqVO.java | 2 +- .../admin/definition/vo/group/BpmUserGroupRespVO.java | 4 ++-- .../definition/vo/group/BpmUserGroupSaveReqVO.java | 4 ++-- .../admin/definition/vo/model/BpmModelMetaInfoVO.java | 2 +- .../admin/definition/vo/model/BpmModelRespVO.java | 6 +++--- .../admin/definition/vo/model/BpmModelSaveReqVO.java | 4 ++-- .../vo/model/simple/BpmSimpleModelNodeVO.java | 2 +- .../vo/process/BpmProcessDefinitionRespVO.java | 2 +- .../controller/admin/oa/vo/BpmOALeaveCreateReqVO.java | 2 +- .../controller/admin/oa/vo/BpmOALeavePageReqVO.java | 2 +- .../bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java | 2 +- .../vo/instance/BpmProcessInstanceCopyPageReqVO.java | 2 +- .../task/vo/instance/BpmProcessInstancePageReqVO.java | 2 +- .../task/vo/instance/BpmProcessInstanceRespVO.java | 4 ++-- .../admin/task/vo/task/BpmTaskPageReqVO.java | 2 +- .../controller/admin/task/vo/task/BpmTaskRespVO.java | 4 ++-- .../zt/plat/module/bpm/controller/package-info.java | 4 ++-- .../《芋道 Spring Boot 对象转换 MapStruct 入门》.md | 2 +- .../behavior/BpmSequentialMultiInstanceBehavior.java | 2 +- .../bpm/framework/web/config/BpmWebConfiguration.java | 2 +- .../module/bpm/service/task/BpmTaskServiceImpl.java | 6 +++--- .../src/main/resources/application-dev.yaml | 2 +- .../src/main/resources/application-local.yaml | 6 +++--- .../src/main/resources/application.yaml | 10 +++++----- .../src/main/resources/logback-spring.xml | 4 ++-- .../bpm/service/definition/BpmFormServiceTest.java | 8 ++++---- .../service/definition/BpmUserGroupServiceTest.java | 4 ++-- .../src/test/resources/application-unit-test.yaml | 8 ++++---- 32 files changed, 58 insertions(+), 58 deletions(-) diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile index 9cdca32..230aa5c 100644 --- a/zt-module-bpm-server/Dockerfile +++ b/zt-module-bpm-server/Dockerfile @@ -3,10 +3,10 @@ FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre ## 创建目录,并使用它作为工作目录 -RUN mkdir -p /cloud-module-bpm-server -WORKDIR /cloud-module-bpm-server +RUN mkdir -p /zt-module-bpm-server +WORKDIR /zt-module-bpm-server ## 将后端项目的 Jar 文件,复制到镜像中 -COPY ./target/cloud-module-bpm-server.jar app.jar +COPY ./target/zt-module-bpm-server.jar app.jar ## 设置 TZ 时区 ## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java index 4160512..437f67c 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java @@ -8,7 +8,7 @@ import lombok.Data; @Data public class BpmFormPageReqVO extends PageParam { - @Schema(description = "表单名称", example = "ZT") + @Schema(description = "表单名称", example = "芋道") private String name; } diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java index 1950d7a..42295df 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java @@ -14,7 +14,7 @@ public class BpmFormRespVO { @Schema(description = "表单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; - @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") @NotNull(message = "表单名称不能为空") private String name; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java index 5953485..faa420b 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java @@ -13,7 +13,7 @@ public class BpmFormSaveReqVO { @Schema(description = "表单编号", example = "1024") private Long id; - @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") @NotNull(message = "表单名称不能为空") private String name; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java index 25a0c2f..a146e35 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java @@ -15,7 +15,7 @@ public class BpmUserGroupPageReqVO extends PageParam { @Schema(description = "编号", example = "1024") private Long id; - @Schema(description = "组名", example = "ZT") + @Schema(description = "组名", example = "芋道") private String name; @Schema(description = "状态", example = "1") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java index 7bff95d..b356351 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java @@ -13,10 +13,10 @@ public class BpmUserGroupRespVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; - @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; - @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT源码") + @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") private String description; @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java index ceccee3..ce2c7d9 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java @@ -13,11 +13,11 @@ public class BpmUserGroupSaveReqVO { @Schema(description = "编号", example = "1024") private Long id; - @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") @NotNull(message = "组名不能为空") private String name; - @Schema(description = "描述", example = "ZT源码") + @Schema(description = "描述", example = "芋道源码") private String description; @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index 8c9176f..cabc1fd 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -28,7 +28,7 @@ import java.util.List; @Data public class BpmModelMetaInfoVO { - @Schema(description = "流程图标", example = "https://www.iocoder.cn/cloud.jpg") + @Schema(description = "流程图标", example = "https://www.iocoder.cn/zt.jpg") @URL(message = "流程图标格式不正确") private String icon; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java index 4a79092..12d6ac1 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java @@ -17,13 +17,13 @@ public class BpmModelRespVO extends BpmModelMetaInfoVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_cloud") + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_zt") private String key; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; - @Schema(description = "流程图标", example = "https://www.iocoder.cn/cloud.jpg") + @Schema(description = "流程图标", example = "https://www.iocoder.cn/zt.jpg") private String icon; @Schema(description = "流程分类编号", example = "1") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java index b347b48..279519c 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java @@ -13,11 +13,11 @@ public class BpmModelSaveReqVO extends BpmModelMetaInfoVO { @Schema(description = "编号", example = "1024") private String id; - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_cloud") + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_zt") @NotEmpty(message = "流程标识不能为空") private String key; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") @NotEmpty(message = "流程名称不能为空") private String name; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index cd5177a..8dc222c 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -35,7 +35,7 @@ public class BpmSimpleModelNodeVO { @Schema(description = "模型节点名称", example = "领导审批") private String name; - @Schema(description = "节点展示内容", example = "指定成员: ZT源码") + @Schema(description = "节点展示内容", example = "指定成员: 芋道源码") private String showText; @Schema(description = "子节点") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java index f0d2892..5e65c0f 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -17,7 +17,7 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer version; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java index 00779aa..4078b99 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java @@ -29,7 +29,7 @@ public class BpmOALeaveCreateReqVO { @Schema(description = "请假类型-参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer type; - @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读ZT源码") + @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码") private String reason; @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java index 31c9f1b..f4226a4 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java @@ -19,7 +19,7 @@ public class BpmOALeavePageReqVO extends PageParam { @Schema(description = "请假类型,参见 bpm_oa_type", example = "1") private Integer type; - @Schema(description = "原因,模糊匹配", example = "阅读ZT源码") + @Schema(description = "原因,模糊匹配", example = "阅读芋道源码") private String reason; @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java index ae43fcd..02a5555 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java @@ -15,7 +15,7 @@ public class BpmOALeaveRespVO { @Schema(description = "请假类型,参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer type; - @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读ZT源码") + @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码") private String reason; @Schema(description = "申请时间", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java index 2aff881..b7956e4 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java @@ -13,7 +13,7 @@ import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH @Data public class BpmProcessInstanceCopyPageReqVO extends PageParam { - @Schema(description = "流程名称", example = "ZT") + @Schema(description = "流程名称", example = "芋道") private String processInstanceName; @Schema(description = "创建时间") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java index 130d75b..33875b6 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java @@ -15,7 +15,7 @@ import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH @Data public class BpmProcessInstancePageReqVO extends PageParam { - @Schema(description = "流程名称", example = "ZT") + @Schema(description = "流程名称", example = "芋道") private String name; @Schema(description = "流程定义的标识", example = "2048") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java index 14cc77a..fb88587 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java @@ -18,7 +18,7 @@ public class BpmProcessInstanceRespVO { @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; @Schema(description = "流程摘要") @@ -71,7 +71,7 @@ public class BpmProcessInstanceRespVO { @Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java index 5c1ee94..c7c1721 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java @@ -12,7 +12,7 @@ import java.time.LocalDateTime; @Data public class BpmTaskPageReqVO extends PageParam { - @Schema(description = "流程任务名", example = "ZT") + @Schema(description = "流程任务名", example = "芋道") private String name; @Schema(description = "流程分类", example = "1") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java index fb86f57..109e26f 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -17,7 +17,7 @@ public class BpmTaskRespVO { @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "任务名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @Schema(description = "任务名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @@ -97,7 +97,7 @@ public class BpmTaskRespVO { @Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "流程实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") + @Schema(description = "流程实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") private String name; @Schema(description = "提交时间", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java index b6413c1..52ffdf8 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java @@ -1,6 +1,6 @@ /** * 提供 RESTful API 给前端: - * 1. admin 包:提供给管理后台 cloud-ui-admin 前端项目 - * 2. app 包:提供给用户 APP cloud-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分 + * 1. admin 包:提供给管理后台 zt-ui-admin 前端项目 + * 2. app 包:提供给用户 APP zt-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分 */ package com.zt.plat.module.bpm.controller; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md index 6565cb0..d796f41 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md @@ -1 +1 @@ - + diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java index 44b4398..2f7f76d 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java @@ -85,7 +85,7 @@ public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceB @Override protected void executeOriginalBehavior(DelegateExecution execution, ExecutionEntity multiInstanceRootExecution, int loopCounter) { - // 参见 https://gitee.com/zhijiantianya/cloud-cloud/issues/IC239F + // 参见 https://gitee.com/zhijiantianya/zt-cloud/issues/IC239F super.collectionExpression = null; super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java index e113b8e..fed2cc5 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java @@ -1,7 +1,7 @@ package com.zt.plat.module.bpm.framework.web.config; import com.zt.plat.framework.common.enums.WebFilterOrderEnum; -import com.zt.plat.framework.swagger.config.CloudSwaggerAutoConfiguration; +import com.zt.plat.framework.swagger.config.ZtSwaggerAutoConfiguration; import com.zt.plat.module.bpm.framework.web.core.FlowableWebFilter; import org.springdoc.core.models.GroupedOpenApi; import org.springframework.boot.web.servlet.FilterRegistrationBean; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java index 42ef5d4..1c44b8c 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java @@ -785,7 +785,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); // 2.3 如果当前任务时被加签的,则加它的根任务也标记成未通过 // 疑问:为什么要标记未通过呢? - // 回答:例如说 A 任务被向前加签除 B 任务时,B 任务被审批不通过,此时 A 会被取消。而 cloud-ui-admin-vue3 不展示“已取消”的任务,导致展示不出审批不通过的细节。 + // 回答:例如说 A 任务被向前加签除 B 任务时,B 任务被审批不通过,此时 A 会被取消。而 zt-ui-admin-vue3 不展示“已取消”的任务,导致展示不出审批不通过的细节。 if (task.getParentTaskId() != null) { String rootParentId = getTaskRootParentId(task); updateTaskStatusAndReason(rootParentId, BpmTaskStatusEnum.REJECT.getStatus(), @@ -1233,7 +1233,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { /** * 特殊情况:部分情况下,TransactionSynchronizationManager 注册 afterCommit 监听时,不会被调用,但是 afterCompletion 可以 * 例如说:第一个 task 就是配置【自动通过】或者【自动拒绝】时 - * 参见 issue 反馈 + * 参见 issue 反馈 */ @Override public void afterCompletion(int transactionStatus) { @@ -1308,7 +1308,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { /** * 特殊情况:部分情况下,TransactionSynchronizationManager 注册 afterCommit 监听时,不会被调用,但是 afterCompletion 可以 * 例如说:第一个 task 就是配置【自动通过】或者【自动拒绝】时 - * 参见 issue 反馈 + * 参见 issue 反馈 */ @Override public void afterCompletion(int transactionStatus) { diff --git a/zt-module-bpm-server/src/main/resources/application-dev.yaml b/zt-module-bpm-server/src/main/resources/application-dev.yaml index 26fb687..b6b318c 100644 --- a/zt-module-bpm-server/src/main/resources/application-dev.yaml +++ b/zt-module-bpm-server/src/main/resources/application-dev.yaml @@ -89,6 +89,6 @@ spring: instance: service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] ---- #################### ZT相关配置 #################### +--- #################### 芋道相关配置 #################### diff --git a/zt-module-bpm-server/src/main/resources/application-local.yaml b/zt-module-bpm-server/src/main/resources/application-local.yaml index 4d12470..2bf6df5 100644 --- a/zt-module-bpm-server/src/main/resources/application-local.yaml +++ b/zt-module-bpm-server/src/main/resources/application-local.yaml @@ -99,10 +99,10 @@ logging: com.zt.plat.module.bpm.dal.mysql: debug org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 ---- #################### ZT相关配置 #################### +--- #################### 芋道相关配置 #################### -# ZT配置项,设置当前项目所有自定义的配置 -cloud: +# 芋道配置项,设置当前项目所有自定义的配置 +zt: env: # 多环境的配置项 tag: ${HOSTNAME} security: diff --git a/zt-module-bpm-server/src/main/resources/application.yaml b/zt-module-bpm-server/src/main/resources/application.yaml index 5d95503..07020c5 100644 --- a/zt-module-bpm-server/src/main/resources/application.yaml +++ b/zt-module-bpm-server/src/main/resources/application.yaml @@ -96,7 +96,7 @@ mybatis-plus: logic-delete-value: 1 # 逻辑已删除值(默认为 1) logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) banner: false # 关闭控制台的 Banner 打印 - type-aliases-package: ${cloud.info.base-package}.dal.dataobject + type-aliases-package: ${zt.info.base-package}.dal.dataobject encryptor: password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成 @@ -127,15 +127,15 @@ xxl: logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 accessToken: default_token # 执行器通讯TOKEN ---- #################### ZT相关配置 #################### +--- #################### 芋道相关配置 #################### -cloud: +zt: info: version: 1.0.0 base-package: com.zt.plat.module.bpm web: admin-ui: - url: http://dashboard.cloud.iocoder.cn # Admin 管理后台 UI 的地址 + url: http://dashboard.zt.iocoder.cn # Admin 管理后台 UI 的地址 xss: enable: false exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 @@ -143,7 +143,7 @@ cloud: swagger: title: 管理后台 description: 提供管理员管理的所有功能 - version: ${cloud.info.version} + version: ${zt.info.version} tenant: # 多租户相关配置项 enable: true diff --git a/zt-module-bpm-server/src/main/resources/logback-spring.xml b/zt-module-bpm-server/src/main/resources/logback-spring.xml index e6801ea..0e55141 100644 --- a/zt-module-bpm-server/src/main/resources/logback-spring.xml +++ b/zt-module-bpm-server/src/main/resources/logback-spring.xml @@ -1,8 +1,8 @@ - - + + diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java index 57d2f2a..ac03d9e 100644 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java @@ -67,7 +67,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest { // 准备参数 BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { o.setId(dbForm.getId()); // 设置更新的 ID - o.setConf("{'cloud': 'yuanma'}"); + o.setConf("{'zt': 'yuanma'}"); o.setFields(randomFields()); }); @@ -82,7 +82,7 @@ public class BpmFormServiceTest extends BaseDbUnitTest { public void testUpdateForm_notExists() { // 准备参数 BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { - o.setConf("{'cloud': 'yuanma'}"); + o.setConf("{'zt': 'yuanma'}"); o.setFields(randomFields()); }); @@ -117,14 +117,14 @@ public class BpmFormServiceTest extends BaseDbUnitTest { public void testGetFormPage() { // mock 数据 BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> { // 等会查询到 - o.setName("ZT源码"); + o.setName("芋道源码"); }); formMapper.insert(dbForm); // 测试 name 不匹配 formMapper.insert(cloneIgnoreId(dbForm, o -> o.setName("源码"))); // 准备参数 BpmFormPageReqVO reqVO = new BpmFormPageReqVO(); - reqVO.setName("ZT"); + reqVO.setName("芋道"); // 调用 PageResult pageResult = formService.getFormPage(reqVO); diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java index a74e9d3..88b1d50 100644 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java @@ -102,13 +102,13 @@ public class BpmUserGroupServiceTest extends BaseDbUnitTest { public void testGetUserGroupPage() { // mock 数据 BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class, o -> { // 等会查询到 - o.setName("ZT源码"); + o.setName("芋道源码"); o.setStatus(CommonStatusEnum.ENABLE.getStatus()); o.setCreateTime(buildTime(2021, 11, 11)); }); userGroupMapper.insert(dbUserGroup); // 测试 name 不匹配 - userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("ZT"))); + userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("芋道"))); // 测试 status 不匹配 userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); // 测试 createTime 不匹配 diff --git a/zt-module-bpm-server/src/test/resources/application-unit-test.yaml b/zt-module-bpm-server/src/test/resources/application-unit-test.yaml index 87521f0..a669ee0 100644 --- a/zt-module-bpm-server/src/test/resources/application-unit-test.yaml +++ b/zt-module-bpm-server/src/test/resources/application-unit-test.yaml @@ -22,7 +22,7 @@ spring: mybatis-plus: lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 - type-aliases-package: ${cloud.info.base-package}.dal.dataobject + type-aliases-package: ${zt.info.base-package}.dal.dataobject global-config: db-config: id-type: AUTO # H2 主键递增 @@ -37,9 +37,9 @@ mybatis-plus: --- #################### 监控相关配置 #################### ---- #################### ZT相关配置 #################### +--- #################### 芋道相关配置 #################### -# ZT配置项,设置当前项目所有自定义的配置 -cloud: +# 芋道配置项,设置当前项目所有自定义的配置 +zt: info: base-package: com.zt.plat.module.bpm From 3b8fb98eeb8eb09c13372b02f917d8a311b65209 Mon Sep 17 00:00:00 2001 From: qianshijiang <1965297290@qq.com> Date: Tue, 23 Sep 2025 11:38:29 +0800 Subject: [PATCH 04/35] =?UTF-8?q?=E6=B5=81=E7=A8=8B=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0-=E8=B5=84=E9=87=91=E6=8E=88=E4=BF=A1?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E7=9A=84=E6=B5=81=E7=A8=8B=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E7=9B=91=E5=90=AC=E5=99=A8=E8=BF=9B=E8=A1=8C=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/pom.xml | 5 +++ .../rpc/config/RpcConfiguration.java | 3 +- ...terApprovalProcessLeaveStatusListener.java | 42 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalProcessLeaveStatusListener.java diff --git a/zt-module-bpm-server/pom.xml b/zt-module-bpm-server/pom.xml index 06a5ac4..eac85c0 100644 --- a/zt-module-bpm-server/pom.xml +++ b/zt-module-bpm-server/pom.xml @@ -33,6 +33,11 @@ zt-module-system-api ${revision} + + com.zt.plat + cloud-module-capital-api + ${revision} + diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java index 0be19c0..0ab98bc 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java @@ -1,5 +1,6 @@ package com.zt.plat.module.bpm.framework.rpc.config; +import com.zt.plat.module.capital.api.AmountCreditApplyApi; import com.zt.plat.module.system.api.dept.DeptApi; import com.zt.plat.module.system.api.dept.PostApi; import com.zt.plat.module.system.api.dict.DictDataApi; @@ -12,6 +13,6 @@ import org.springframework.context.annotation.Configuration; @Configuration(value = "bpmRpcConfiguration", proxyBeanMethods = false) @EnableFeignClients(clients = {RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, - PermissionApi.class}) + PermissionApi.class, AmountCreditApplyApi.class}) public class RpcConfiguration { } diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalProcessLeaveStatusListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalProcessLeaveStatusListener.java new file mode 100644 index 0000000..40d911c --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalProcessLeaveStatusListener.java @@ -0,0 +1,42 @@ +package com.zt.plat.module.bpm.service.supply.capital.listener; + +import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; +import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEventListener; +import com.zt.plat.module.bpm.enums.task.BpmProcessInstanceStatusEnum; +import com.zt.plat.module.capital.api.AmountCreditApplyApi; +import com.zt.plat.module.capital.enums.AmountCreditApplyApiStatusEnum; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Component; + +import java.util.List; + +/** + * credit_letter_approval_process - 授信单的状态的监听器实现类 + * + * @author ZT + */ +@Component +public class BpmCreditLetterApprovalProcessLeaveStatusListener extends BpmProcessInstanceStatusEventListener { + + @Resource + private AmountCreditApplyApi amountCreditApplyApi; + + @Override + protected List getProcessDefinitionKey() { + return List.of(amountCreditApplyApi.PROCESS_KEY); + } + + @Override + protected void onEvent(BpmProcessInstanceStatusEvent event) { + // 将流程中的状态转换为业务的状态 + Integer status = event.getStatus(); + String approvalStatus = AmountCreditApplyApiStatusEnum.ACAS_PASS.getCode(); + if (BpmProcessInstanceStatusEnum.APPROVE.getStatus() == status) { + approvalStatus = AmountCreditApplyApiStatusEnum.ACAS_PASS.getCode(); + } else if (BpmProcessInstanceStatusEnum.REJECT.getStatus() == status) { + approvalStatus = AmountCreditApplyApiStatusEnum.ACAS_REJECT.getCode(); + } + amountCreditApplyApi.updateAmountCreditApplyStatus(Long.parseLong(event.getBusinessKey()),approvalStatus); + } + +} From fc24ab91fadd181be22812d923f5008dead80be8 Mon Sep 17 00:00:00 2001 From: qianshijiang <1965297290@qq.com> Date: Tue, 23 Sep 2025 11:40:26 +0800 Subject: [PATCH 05/35] =?UTF-8?q?=E6=B5=81=E7=A8=8B=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0-=E8=B5=84=E9=87=91=E6=8E=88=E4=BF=A1?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E7=9A=84=E6=B5=81=E7=A8=8B=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E7=9B=91=E5=90=AC=E5=99=A8=E8=BF=9B=E8=A1=8C=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...stener.java => BpmCreditLetterApprovalStatusListener.java} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/{BpmCreditLetterApprovalProcessLeaveStatusListener.java => BpmCreditLetterApprovalStatusListener.java} (92%) diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalProcessLeaveStatusListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java similarity index 92% rename from zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalProcessLeaveStatusListener.java rename to zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java index 40d911c..bd0a092 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalProcessLeaveStatusListener.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java @@ -13,10 +13,10 @@ import java.util.List; /** * credit_letter_approval_process - 授信单的状态的监听器实现类 * - * @author ZT + * @author qianshijiang */ @Component -public class BpmCreditLetterApprovalProcessLeaveStatusListener extends BpmProcessInstanceStatusEventListener { +public class BpmCreditLetterApprovalStatusListener extends BpmProcessInstanceStatusEventListener { @Resource private AmountCreditApplyApi amountCreditApplyApi; From 5b3b1e4f66db662619d32cd665e27c1f0bb7f93e Mon Sep 17 00:00:00 2001 From: qianshijiang <1965297290@qq.com> Date: Tue, 23 Sep 2025 11:41:32 +0800 Subject: [PATCH 06/35] =?UTF-8?q?=E6=B5=81=E7=A8=8B=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0-=E8=B5=84=E9=87=91=E6=8E=88=E4=BF=A1?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E7=9A=84=E6=B5=81=E7=A8=8B=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E7=9B=91=E5=90=AC=E5=99=A8=E8=BF=9B=E8=A1=8C=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../capital/listener/BpmCreditLetterApprovalStatusListener.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java index bd0a092..83fed4c 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java @@ -30,7 +30,7 @@ public class BpmCreditLetterApprovalStatusListener extends BpmProcessInstanceSta protected void onEvent(BpmProcessInstanceStatusEvent event) { // 将流程中的状态转换为业务的状态 Integer status = event.getStatus(); - String approvalStatus = AmountCreditApplyApiStatusEnum.ACAS_PASS.getCode(); + String approvalStatus = null; if (BpmProcessInstanceStatusEnum.APPROVE.getStatus() == status) { approvalStatus = AmountCreditApplyApiStatusEnum.ACAS_PASS.getCode(); } else if (BpmProcessInstanceStatusEnum.REJECT.getStatus() == status) { From b4b538365c4b4bef6b9fa25a0ce048c960982ece Mon Sep 17 00:00:00 2001 From: qianshijiang <1965297290@qq.com> Date: Tue, 23 Sep 2025 15:11:09 +0800 Subject: [PATCH 07/35] =?UTF-8?q?=E6=B5=81=E7=A8=8B=E4=BB=BB=E5=8A=A1feign?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/api/task/BpmProcessInstanceApi.java | 12 +++++++ .../api/task/dto/BpmTaskApproveReqDTO.java | 33 +++++++++++++++++++ .../bpm/api/task/dto/BpmTaskRejectReqDTO.java | 18 ++++++++++ .../api/task/BpmProcessInstanceApiImpl.java | 22 +++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java index 061e677..baf8f52 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java @@ -2,6 +2,8 @@ package com.zt.plat.module.bpm.api.task; import com.zt.plat.framework.common.pojo.CommonResult; import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskApproveReqDTO; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskRejectReqDTO; import com.zt.plat.module.bpm.enums.ApiConstants; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -9,6 +11,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @@ -24,4 +27,13 @@ public interface BpmProcessInstanceApi { CommonResult createProcessInstance(@RequestParam("userId") Long userId, @Valid @RequestBody BpmProcessInstanceCreateReqDTO reqDTO); + + @PutMapping(PREFIX + "/approveTask") + @Operation(summary = "通过任务") + CommonResult approveTask(@Valid @RequestBody BpmTaskApproveReqDTO reqVO); + + @PutMapping(PREFIX + "/rejectTask") + @Operation(summary = "不通过任务") + CommonResult rejectTask(@Valid @RequestBody BpmTaskRejectReqDTO reqVO); + } diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java new file mode 100644 index 0000000..705f930 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "RPC 服务 - 通过流程任务的 Request DTO") +@Data +public class BpmTaskApproveReqDTO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "审批意见", example = "不错不错!") + private String reason; + + @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") + private String signPicUrl; + + @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) + private Map variables; + + @Schema(description = "下一个节点审批人", example = "{nodeId:[1, 2]}") + private Map> nextAssignees; // 为什么是 Map,而不是 List 呢?因为下一个节点可能是多个,例如说并行网关的情况 + + // 新增任务变量实例,业务表单 + @Schema(description = "任务变量实例,业务表单", example = "{'formField1': 'value1', 'formField2': 'value2'}") + private Map taskVariables; +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java new file mode 100644 index 0000000..94f73bf --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java @@ -0,0 +1,18 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "RPC 服务 - 不通过流程任务的 Request DTO") +@Data +public class BpmTaskRejectReqDTO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java index 261b750..686d8dd 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java @@ -1,14 +1,21 @@ package com.zt.plat.module.bpm.api.task; import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.util.object.BeanUtils; import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskApproveReqDTO; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskRejectReqDTO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRejectReqVO; import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.bpm.service.task.BpmTaskService; import jakarta.annotation.Resource; import jakarta.validation.Valid; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.getLoginUserId; /** * Flowable 流程实例 Api 实现类 @@ -23,9 +30,24 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { @Resource private BpmProcessInstanceService processInstanceService; + @Resource + private BpmTaskService taskService; + @Override public CommonResult createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) { return success(processInstanceService.createProcessInstance(userId, reqDTO)); } + @Override + public CommonResult approveTask(BpmTaskApproveReqDTO reqVO) { + taskService.approveTask(getLoginUserId(), BeanUtils.toBean(reqVO, BpmTaskApproveReqVO.class)); + return success(true); + } + + @Override + public CommonResult rejectTask(BpmTaskRejectReqDTO reqVO) { + taskService.rejectTask(getLoginUserId(), BeanUtils.toBean(reqVO, BpmTaskRejectReqVO.class)); + return success(true); + } + } From a8780089f1410417238472a9f1d6c84d28e943a1 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Tue, 23 Sep 2025 15:57:36 +0800 Subject: [PATCH 08/35] =?UTF-8?q?1.=20bpm=20=E6=8E=A5=E5=85=A5=20=E4=B8=9A?= =?UTF-8?q?=E5=8A=A1=E5=9B=9E=E8=B0=83=20openfeign?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zt-module-bpm-server/pom.xml b/zt-module-bpm-server/pom.xml index eac85c0..dbaa6e9 100644 --- a/zt-module-bpm-server/pom.xml +++ b/zt-module-bpm-server/pom.xml @@ -35,7 +35,7 @@ com.zt.plat - cloud-module-capital-api + zt-module-capital-api ${revision} From af60a8c020127a1ed2a2bfeaa44e488ffccec309 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Wed, 24 Sep 2025 15:28:40 +0800 Subject: [PATCH 09/35] =?UTF-8?q?1.=20=E5=8D=87=E7=BA=A7=203.0.35=20?= =?UTF-8?q?=E8=A1=A5=E5=85=A8=20bpm=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/api/definition/BpmCategoryApi.java | 55 +++++ .../module/bpm/api/definition/BpmFormApi.java | 50 +++++ .../bpm/api/definition/BpmUserGroupApi.java | 29 +++ .../definition/dto/BpmCategoryPageReqDTO.java | 32 +++ .../definition/dto/BpmCategoryRespDTO.java | 33 +++ .../definition/dto/BpmCategorySaveReqDTO.java | 37 ++++ .../api/definition/dto/BpmFormPageReqDTO.java | 18 ++ .../api/definition/dto/BpmFormRespDTO.java | 33 +++ .../api/definition/dto/BpmFormSaveReqDTO.java | 32 +++ .../definition/dto/BpmUserGroupRespDTO.java | 31 +++ .../bpm/api/task/BpmProcessInstanceApi.java | 3 +- .../plat/module/bpm/api/task/BpmTaskApi.java | 53 +++++ .../dto/BpmProcessInstancePageReqDTO.java | 38 ++++ .../task/dto/BpmProcessInstanceRespDTO.java | 46 ++++ .../bpm/api/task/dto/BpmTaskPageReqDTO.java | 29 +++ .../bpm/api/task/dto/BpmTaskRespDTO.java | 59 ++++++ .../api/definition/BpmCategoryApiImpl.java | 79 +++++++ .../bpm/api/definition/BpmFormApiImpl.java | 76 +++++++ .../api/definition/BpmUserGroupApiImpl.java | 41 ++++ .../module/bpm/api/task/BpmTaskApiImpl.java | 197 ++++++++++++++++++ 20 files changed, 969 insertions(+), 2 deletions(-) create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApi.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApi.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApi.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryPageReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryRespDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategorySaveReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApi.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApiImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApiImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApiImpl.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApi.java new file mode 100644 index 0000000..19f885c --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApi.java @@ -0,0 +1,55 @@ +package com.zt.plat.module.bpm.api.definition; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.api.definition.dto.BpmCategoryPageReqDTO; +import com.zt.plat.module.bpm.api.definition.dto.BpmCategoryRespDTO; +import com.zt.plat.module.bpm.api.definition.dto.BpmCategorySaveReqDTO; +import com.zt.plat.module.bpm.enums.ApiConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@FeignClient(name = ApiConstants.NAME) +@Tag(name = "RPC 服务 - BPM 流程分类") +public interface BpmCategoryApi { + + String PREFIX = ApiConstants.PREFIX + "/category"; + + @PostMapping(PREFIX + "/create") + @Operation(summary = "创建流程分类") + CommonResult createCategory(@Valid @RequestBody BpmCategorySaveReqDTO createReqDTO); + + @PutMapping(PREFIX + "/update") + @Operation(summary = "更新流程分类") + CommonResult updateCategory(@Valid @RequestBody BpmCategorySaveReqDTO updateReqDTO); + + @PutMapping(PREFIX + "/update-sort-batch") + @Operation(summary = "批量更新流程分类的排序") + @Parameter(name = "ids", description = "分类编号列表", required = true, example = "1,2,3") + CommonResult updateCategorySortBatch(@RequestParam("ids") List ids); + + @DeleteMapping(PREFIX + "/delete") + @Operation(summary = "删除流程分类") + @Parameter(name = "id", description = "编号", required = true) + CommonResult deleteCategory(@RequestParam("id") Long id); + + @GetMapping(PREFIX + "/get") + @Operation(summary = "获得流程分类") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + CommonResult getCategory(@RequestParam("id") Long id); + + @PostMapping(PREFIX + "/page") + @Operation(summary = "获得流程分类分页") + CommonResult> getCategoryPage(@Valid @RequestBody BpmCategoryPageReqDTO pageReqDTO); + + @GetMapping(PREFIX + "/simple-list") + @Operation(summary = "获取流程分类的精简信息列表", description = "只包含被开启的分类,主要用于前端的下拉选项") + CommonResult> getCategorySimpleList(); + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApi.java new file mode 100644 index 0000000..b21f94a --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApi.java @@ -0,0 +1,50 @@ +package com.zt.plat.module.bpm.api.definition; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.module.bpm.api.definition.dto.BpmFormPageReqDTO; +import com.zt.plat.module.bpm.api.definition.dto.BpmFormRespDTO; +import com.zt.plat.module.bpm.api.definition.dto.BpmFormSaveReqDTO; +import com.zt.plat.module.bpm.enums.ApiConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@FeignClient(name = ApiConstants.NAME) +@Tag(name = "RPC 服务 - 动态表单") +public interface BpmFormApi { + + String PREFIX = ApiConstants.PREFIX + "/form"; + + @PostMapping(PREFIX + "/create") + @Operation(summary = "创建动态表单") + CommonResult createForm(@Valid @RequestBody BpmFormSaveReqDTO createReqDTO); + + @PutMapping(PREFIX + "/update") + @Operation(summary = "更新动态表单") + CommonResult updateForm(@Valid @RequestBody BpmFormSaveReqDTO updateReqDTO); + + @DeleteMapping(PREFIX + "/delete") + @Operation(summary = "删除动态表单") + @Parameter(name = "id", description = "编号", required = true) + CommonResult deleteForm(@RequestParam("id") Long id); + + @GetMapping(PREFIX + "/get") + @Operation(summary = "获得动态表单") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + CommonResult getForm(@RequestParam("id") Long id); + + @PostMapping(PREFIX + "/page") + @Operation(summary = "获得动态表单分页") + CommonResult> getFormPage(@Valid @RequestBody BpmFormPageReqDTO pageReqDTO); + + @GetMapping(PREFIX + "/simple-list") + @Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框") + CommonResult> getFormSimpleList(); + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApi.java new file mode 100644 index 0000000..dbf25d9 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApi.java @@ -0,0 +1,29 @@ +package com.zt.plat.module.bpm.api.definition; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.module.bpm.api.definition.dto.BpmUserGroupRespDTO; +import com.zt.plat.module.bpm.enums.ApiConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@FeignClient(name = ApiConstants.NAME) +@Tag(name = "RPC 服务 - 用户组") +public interface BpmUserGroupApi { + + String PREFIX = ApiConstants.PREFIX + "/user-group"; + + @GetMapping(PREFIX + "/get") + @Operation(summary = "获得用户组") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + CommonResult getUserGroup(@RequestParam("id") Long id); + + @GetMapping(PREFIX + "/simple-list") + @Operation(summary = "获取用户组精简信息列表", description = "只包含被开启的用户组,主要用于前端的下拉选项") + CommonResult> getUserGroupSimpleList(); + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryPageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryPageReqDTO.java new file mode 100644 index 0000000..df35f7a --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryPageReqDTO.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.api.definition.dto; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.PageParam; +import com.zt.plat.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "RPC 服务 - BPM 流程分类分页 Request DTO") +@Data +public class BpmCategoryPageReqDTO extends PageParam { + + @Schema(description = "分类名", example = "王五") + private String name; + + @Schema(description = "分类标志", example = "OA") + private String code; + + @Schema(description = "分类状态", example = "1") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryRespDTO.java new file mode 100644 index 0000000..8831550 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryRespDTO.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.api.definition.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "RPC 服务 - BPM 流程分类 Response DTO") +@Data +public class BpmCategoryRespDTO { + + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") + private Long id; + + @Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + private String name; + + @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") + private String code; + + @Schema(description = "分类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") + private String description; + + @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED) + private Integer sort; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategorySaveReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategorySaveReqDTO.java new file mode 100644 index 0000000..39c84e5 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategorySaveReqDTO.java @@ -0,0 +1,37 @@ +package com.zt.plat.module.bpm.api.definition.dto; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Schema(description = "RPC 服务 - BPM 流程分类新增/修改 Request DTO") +@Data +public class BpmCategorySaveReqDTO { + + @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") + private Long id; + + @Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") + @NotEmpty(message = "分类名不能为空") + private String name; + + @Schema(description = "分类描述", example = "你猜") + private String description; + + @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") + @NotEmpty(message = "分类标志不能为空") + private String code; + + @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "分类状态不能为空") + @InEnum(CommonStatusEnum.class) + private Integer status; + + @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED) + @NotNull(message = "分类排序不能为空") + private Integer sort; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java new file mode 100644 index 0000000..4ed3bc3 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java @@ -0,0 +1,18 @@ +package com.zt.plat.module.bpm.api.definition.dto; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.ToString; + +@Schema(description = "动态表单分页 Request DTO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class BpmFormPageReqDTO extends PageParam { + + @Schema(description = "表单名称", example = "芋道") + private String name; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java new file mode 100644 index 0000000..a8cb537 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.api.definition.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "RPC 服务 - 动态表单 Response DTO") +@Data +public class BpmFormRespDTO { + + @Schema(description = "表单编号", example = "1024") + private Long id; + + @Schema(description = "表单名", example = "芋艿") + private String name; + + @Schema(description = "表单状态", example = "1") + private Integer status; + + @Schema(description = "表单的配置") + private String conf; + + @Schema(description = "表单项的数组") + private String fields; + + @Schema(description = "备注", example = "我是备注") + private String remark; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java new file mode 100644 index 0000000..787a8cd --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java @@ -0,0 +1,32 @@ +package com.zt.plat.module.bpm.api.definition.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "RPC 服务 - 动态表单新增/修改 Request DTO") +@Data +public class BpmFormSaveReqDTO { + + @Schema(description = "表单编号", example = "1024") + private Long id; + + @Schema(description = "表单名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @NotEmpty(message = "表单名不能为空") + private String name; + + @Schema(description = "表单状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + + @Schema(description = "表单的配置", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "表单的配置不能为空") + private String conf; + + @Schema(description = "表单项的数组", requiredMode = Schema.RequiredMode.REQUIRED) + @NotEmpty(message = "表单项的数组不能为空") + private String fields; + + @Schema(description = "备注", example = "我是备注") + private String remark; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java new file mode 100644 index 0000000..854efa6 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.api.definition.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Set; + +@Schema(description = "RPC 服务 - 用户组 Response DTO") +@Data +public class BpmUserGroupRespDTO { + + @Schema(description = "编号", example = "1024") + private Long id; + + @Schema(description = "组名", example = "芋艿") + private String name; + + @Schema(description = "描述", example = "芋艿") + private String description; + + @Schema(description = "成员用户编号数组", example = "1,2,3") + private Set memberUserIds; + + @Schema(description = "状态", example = "1") + private Integer status; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java index 28d062e..061e677 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java @@ -6,13 +6,12 @@ import com.zt.plat.module.bpm.enums.ApiConstants; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; -import jakarta.validation.Valid; - @FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = @Tag(name = "RPC 服务 - 流程实例") public interface BpmProcessInstanceApi { diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApi.java new file mode 100644 index 0000000..b55ae5c --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApi.java @@ -0,0 +1,53 @@ +package com.zt.plat.module.bpm.api.task; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskPageReqDTO; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskRespDTO; +import com.zt.plat.module.bpm.enums.ApiConstants; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; + +@FeignClient(name = ApiConstants.NAME) +@Tag(name = "RPC 服务 - 流程任务实例") +public interface BpmTaskApi { + + String PREFIX = ApiConstants.PREFIX + "/task"; + + @PostMapping(PREFIX + "/todo-page") + @Operation(summary = "获取 Todo 待办任务分页") + CommonResult> getTaskTodoPage(@Valid @RequestBody BpmTaskPageReqDTO pageReqDTO); + + @PostMapping(PREFIX + "/done-page") + @Operation(summary = "获取 Done 已办任务分页") + CommonResult> getTaskDonePage(@Valid @RequestBody BpmTaskPageReqDTO pageReqDTO); + + @PostMapping(PREFIX + "/manager-page") + @Operation(summary = "获取全部任务的分页", description = "用于【流程任务】菜单") + CommonResult> getTaskManagerPage(@Valid @RequestBody BpmTaskPageReqDTO pageReqDTO); + + @GetMapping(PREFIX + "/list-by-process-instance-id") + @Operation(summary = "获得指定流程实例的任务列表", description = "包括完成的、未完成的") + @Parameter(name = "processInstanceId", description = "流程实例的编号", required = true) + CommonResult> getTaskListByProcessInstanceId( + @RequestParam("processInstanceId") String processInstanceId); + + @GetMapping(PREFIX + "/list-by-return") + @Operation(summary = "获取所有可退回的节点", description = "用于【流程详情】的【退回】按钮") + @Parameter(name = "id", description = "当前任务ID", required = true) + CommonResult> getTaskListByReturn(@RequestParam("id") String id); + + @GetMapping(PREFIX + "/list-by-parent-task-id") + @Operation(summary = "获得指定父级任务的子任务列表") // 目前用于,减签的时候,获得子任务列表 + @Parameter(name = "parentTaskId", description = "父级任务编号", required = true) + CommonResult> getTaskListByParentTaskId(@RequestParam("parentTaskId") String parentTaskId); + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java new file mode 100644 index 0000000..b041098 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java @@ -0,0 +1,38 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "RPC 服务 - 流程实例分页 Request DTO") +@Data +public class BpmProcessInstancePageReqDTO extends PageParam { + + @Schema(description = "流程实例的编号", example = "1024") + private String id; + + @Schema(description = "流程实例的名字", example = "芋艿") + private String name; + + @Schema(description = "流程定义的编号", example = "2048") + private String processDefinitionId; + + @Schema(description = "流程分类", example = "1") + private String category; + + @Schema(description = "流程实例的状态", example = "1") + private Integer status; + + @Schema(description = "流程实例的结果", example = "1") + private Integer result; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java new file mode 100644 index 0000000..1138169 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java @@ -0,0 +1,46 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.Map; + +@Schema(description = "RPC 服务 - 流程实例 Response DTO") +@Data +public class BpmProcessInstanceRespDTO { + + @Schema(description = "流程实例的编号", example = "1024") + private String id; + + @Schema(description = "流程实例的名字", example = "芋艿") + private String name; + + @Schema(description = "流程定义的编号", example = "2048") + private String processDefinitionId; + + @Schema(description = "流程分类", example = "1") + private String category; + + @Schema(description = "流程实例的状态", example = "1") + private Integer status; + + @Schema(description = "流程实例的结果", example = "1") + private Integer result; + + @Schema(description = "提交的表单值", example = "{\"name\": \"芋艿\"}") + private Map formVariables; + + @Schema(description = "业务的唯一标识", example = "1") + private String businessKey; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "结束时间") + private LocalDateTime endTime; + + @Schema(description = "持续时间", example = "1000") + private Long durationInMillis; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java new file mode 100644 index 0000000..fae7055 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java @@ -0,0 +1,29 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import com.zt.plat.framework.common.pojo.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.time.LocalDateTime; + +import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "RPC 服务 - 流程任务分页 Request DTO") +@Data +public class BpmTaskPageReqDTO extends PageParam { + + @Schema(description = "流程任务名", example = "芋艿") + private String name; + + @Schema(description = "流程定义的编号", example = "2048") + private String processDefinitionId; + + @Schema(description = "流程分类", example = "1") + private String category; + + @Schema(description = "创建时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] createTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java new file mode 100644 index 0000000..1ae2e45 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java @@ -0,0 +1,59 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Schema(description = "RPC 服务 - 流程任务 Response DTO") +@Data +public class BpmTaskRespDTO { + + @Schema(description = "任务编号", example = "1024") + private String id; + + @Schema(description = "任务名字", example = "芋艿") + private String name; + + @Schema(description = "接收人的用户编号", example = "1") + private Long assigneeUserId; + + @Schema(description = "创建时间") + private LocalDateTime createTime; + + @Schema(description = "结束时间") + private LocalDateTime endTime; + + @Schema(description = "持续时间", example = "1000") + private Long durationInMillis; + + @Schema(description = "流程实例的编号", example = "1024") + private String processInstanceId; + + @Schema(description = "流程定义的编号", example = "2048") + private String processDefinitionId; + + @Schema(description = "任务状态", example = "1") + private Integer status; + + @Schema(description = "审批建议", example = "不错不错!") + private String reason; + + @Schema(description = "任务定义的标识", example = "Activity_one") + private String taskDefinitionKey; + + @Schema(description = "表单编号", example = "1024") + private Long formId; + + @Schema(description = "表单的配置", example = "[]") + private String formConf; + + @Schema(description = "表单项的数组", example = "[]") + private String formFields; + + @Schema(description = "提交的表单值", example = "{\"name\": \"芋艿\"}") + private Map formVariables; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApiImpl.java new file mode 100644 index 0000000..7f161dd --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApiImpl.java @@ -0,0 +1,79 @@ +package com.zt.plat.module.bpm.api.definition; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.api.definition.dto.BpmCategoryPageReqDTO; +import com.zt.plat.module.bpm.api.definition.dto.BpmCategoryRespDTO; +import com.zt.plat.module.bpm.api.definition.dto.BpmCategorySaveReqDTO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; +import com.zt.plat.module.bpm.service.definition.BpmCategoryService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +/** + * BPM 流程分类 Api 实现类 + * + * @author ZT + */ +@RestController +@Validated +public class BpmCategoryApiImpl implements BpmCategoryApi { + + @Resource + private BpmCategoryService categoryService; + + @Override + public CommonResult createCategory(@Valid BpmCategorySaveReqDTO createReqDTO) { + BpmCategorySaveReqVO createReqVO = BeanUtils.toBean(createReqDTO, BpmCategorySaveReqVO.class); + return success(categoryService.createCategory(createReqVO)); + } + + @Override + public CommonResult updateCategory(@Valid BpmCategorySaveReqDTO updateReqDTO) { + BpmCategorySaveReqVO updateReqVO = BeanUtils.toBean(updateReqDTO, BpmCategorySaveReqVO.class); + categoryService.updateCategory(updateReqVO); + return success(true); + } + + @Override + public CommonResult updateCategorySortBatch(List ids) { + categoryService.updateCategorySortBatch(ids); + return success(true); + } + + @Override + public CommonResult deleteCategory(Long id) { + categoryService.deleteCategory(id); + return success(true); + } + + @Override + public CommonResult getCategory(Long id) { + BpmCategoryDO category = categoryService.getCategory(id); + return success(BeanUtils.toBean(category, BpmCategoryRespDTO.class)); + } + + @Override + public CommonResult> getCategoryPage(@Valid BpmCategoryPageReqDTO pageReqDTO) { + BpmCategoryPageReqVO pageReqVO = BeanUtils.toBean(pageReqDTO, BpmCategoryPageReqVO.class); + PageResult pageResult = categoryService.getCategoryPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, BpmCategoryRespDTO.class)); + } + + @Override + public CommonResult> getCategorySimpleList() { + List list = categoryService.getCategoryListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(BeanUtils.toBean(list, BpmCategoryRespDTO.class)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApiImpl.java new file mode 100644 index 0000000..faceb1e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApiImpl.java @@ -0,0 +1,76 @@ +package com.zt.plat.module.bpm.api.definition; + +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.api.definition.dto.BpmFormPageReqDTO; +import com.zt.plat.module.bpm.api.definition.dto.BpmFormRespDTO; +import com.zt.plat.module.bpm.api.definition.dto.BpmFormSaveReqDTO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +/** + * 动态表单 Api 实现类 + * + * @author ZT + */ +@RestController +@Validated +public class BpmFormApiImpl implements BpmFormApi { + + @Resource + private BpmFormService formService; + + @Override + public CommonResult createForm(@Valid BpmFormSaveReqDTO createReqDTO) { + BpmFormSaveReqVO createReqVO = BeanUtils.toBean(createReqDTO, BpmFormSaveReqVO.class); + return success(formService.createForm(createReqVO)); + } + + @Override + public CommonResult updateForm(@Valid BpmFormSaveReqDTO updateReqDTO) { + BpmFormSaveReqVO updateReqVO = BeanUtils.toBean(updateReqDTO, BpmFormSaveReqVO.class); + formService.updateForm(updateReqVO); + return success(true); + } + + @Override + public CommonResult deleteForm(Long id) { + formService.deleteForm(id); + return success(true); + } + + @Override + public CommonResult getForm(Long id) { + BpmFormDO form = formService.getForm(id); + return success(BeanUtils.toBean(form, BpmFormRespDTO.class)); + } + + @Override + public CommonResult> getFormPage(BpmFormPageReqDTO pageReqDTO) { + BpmFormPageReqVO pageReqVO = BeanUtils.toBean(pageReqDTO, BpmFormPageReqVO.class); + PageResult pageResult = formService.getFormPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, BpmFormRespDTO.class)); + } + + @Override + public CommonResult> getFormSimpleList() { + List list = formService.getFormList(); + // 只返回 id、name 字段 + List dtoList = list.stream() + .map(formDO -> new BpmFormRespDTO().setId(formDO.getId()).setName(formDO.getName())) + .collect(java.util.stream.Collectors.toList()); + return success(dtoList); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApiImpl.java new file mode 100644 index 0000000..502b9d6 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApiImpl.java @@ -0,0 +1,41 @@ +package com.zt.plat.module.bpm.api.definition; + +import com.zt.plat.framework.common.enums.CommonStatusEnum; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.api.definition.dto.BpmUserGroupRespDTO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; +import com.zt.plat.module.bpm.service.definition.BpmUserGroupService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import jakarta.annotation.Resource; +import java.util.List; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; + +/** + * 用户组 Api 实现类 + * + * @author ZT + */ +@RestController +@Validated +public class BpmUserGroupApiImpl implements BpmUserGroupApi { + + @Resource + private BpmUserGroupService userGroupService; + + @Override + public CommonResult getUserGroup(Long id) { + BpmUserGroupDO userGroup = userGroupService.getUserGroup(id); + return success(BeanUtils.toBean(userGroup, BpmUserGroupRespDTO.class)); + } + + @Override + public CommonResult> getUserGroupSimpleList() { + List list = userGroupService.getUserGroupListByStatus(CommonStatusEnum.ENABLE.getStatus()); + return success(BeanUtils.toBean(list, BpmUserGroupRespDTO.class)); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java new file mode 100644 index 0000000..d688f4a --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java @@ -0,0 +1,197 @@ +package com.zt.plat.module.bpm.api.task; + +import cn.hutool.core.collection.CollUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; +import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.number.NumberUtils; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskPageReqDTO; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskRespDTO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskPageReqVO; +import com.zt.plat.module.bpm.convert.task.BpmTaskConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.service.definition.BpmFormService; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.bpm.service.task.BpmTaskService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.RestController; + +import java.util.*; +import java.util.stream.Stream; + +import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; +import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; + +/** + * BPM 任务 API 实现类 + * + * @author ZT + */ +@RestController +@Validated +public class BpmTaskApiImpl implements BpmTaskApi { + + @Resource + private BpmTaskService taskService; + @Resource + private BpmProcessInstanceService processInstanceService; + @Resource + private BpmFormService formService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + @Resource + private AdminUserApi adminUserApi; + @Resource + private DeptApi deptApi; + + @Override + public CommonResult> getTaskTodoPage(@Valid BpmTaskPageReqDTO pageReqDTO) { + // 转换请求参数 + BpmTaskPageReqVO pageReqVO = BeanUtils.toBean(pageReqDTO, BpmTaskPageReqVO.class); + + // 调用 Service 层方法 + PageResult pageResult = taskService.getTaskTodoPage(SecurityFrameworkUtils.getLoginUserId(), pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(new ArrayList<>()); + } + + // 拼接数据 - 参考 Controller 逻辑 + Map processInstanceMap = processInstanceService.getProcessInstanceMap(convertSet(pageResult.getList(), Task::getProcessInstanceId)); + Map userMap = adminUserApi.getUserMap(convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(convertSet(pageResult.getList(), Task::getProcessDefinitionId)); + + // 使用转换器构建结果并转换为 DTO + var voPageResult = BpmTaskConvert.INSTANCE.buildTodoTaskPage(pageResult, processInstanceMap, userMap, processDefinitionInfoMap); + return success(BeanUtils.toBean(voPageResult.getList(), BpmTaskRespDTO.class)); + } + + @Override + public CommonResult> getTaskDonePage(@Valid BpmTaskPageReqDTO pageReqDTO) { + // 转换请求参数 + BpmTaskPageReqVO pageReqVO = BeanUtils.toBean(pageReqDTO, BpmTaskPageReqVO.class); + + // 调用 Service 层方法 + PageResult pageResult = taskService.getTaskDonePage(SecurityFrameworkUtils.getLoginUserId(), pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(new ArrayList<>()); + } + + // 拼接数据 - 参考 Controller 逻辑 + Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); + Map userMap = adminUserApi.getUserMap(convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); + + // 使用转换器构建结果并转换为 DTO + var voPageResult = BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null, processDefinitionInfoMap); + return success(BeanUtils.toBean(voPageResult.getList(), BpmTaskRespDTO.class)); + } + + @Override + public CommonResult> getTaskManagerPage(@Valid BpmTaskPageReqDTO pageReqDTO) { + // 转换请求参数 + BpmTaskPageReqVO pageReqVO = BeanUtils.toBean(pageReqDTO, BpmTaskPageReqVO.class); + + // 调用 Service 层方法 + PageResult pageResult = taskService.getTaskPage(SecurityFrameworkUtils.getLoginUserId(), pageReqVO); + if (CollUtil.isEmpty(pageResult.getList())) { + return success(new ArrayList<>()); + } + + // 拼接数据 - 参考 Controller 逻辑 + Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); + // 获得 User 和 Dept Map + Set userIds = convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())); + userIds.addAll(convertSet(pageResult.getList(), task -> NumberUtils.parseLong(task.getAssignee()))); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); + + // 使用转换器构建结果并转换为 DTO + var voPageResult = BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap, processDefinitionInfoMap); + return success(BeanUtils.toBean(voPageResult.getList(), BpmTaskRespDTO.class)); + } + + @Override + public CommonResult> getTaskListByProcessInstanceId(String processInstanceId) { + // 调用 Service 层方法 + List taskList = taskService.getTaskListByProcessInstanceId(processInstanceId, true); + if (CollUtil.isEmpty(taskList)) { + return success(Collections.emptyList()); + } + + // 拼接数据 - 参考 Controller 逻辑 + Set userIds = convertSetByFlatMap(taskList, task -> Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner()))); + Map userMap = adminUserApi.getUserMap(userIds); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + // 获得 Form Map + Map formMap = formService.getFormMap(convertSet(taskList, task -> { + String formKey = task.getFormKey(); + if (formKey == null || formKey.isBlank()) { + return 0L; + } + try { + return Long.parseLong(formKey); + } catch (NumberFormatException e) { + // 如果 formKey 不是数字(比如是URL),返回0L + return 0L; + } + })); + + // 使用转换器构建结果并转换为 DTO + var voList = BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, formMap, userMap, deptMap); + List result = BeanUtils.toBean(voList, BpmTaskRespDTO.class); + + return success(result); + } + + @Override + public CommonResult> getTaskListByReturn(String id) { + // 调用 Service 层方法 + var userTaskList = taskService.getUserTaskListByReturn(id); + + // 转换返回结果 - 只返回 id 和 name(对应 taskDefinitionKey 和 name) + List result = userTaskList.stream().map(userTask -> { + BpmTaskRespDTO dto = new BpmTaskRespDTO(); + dto.setName(userTask.getName()); + dto.setTaskDefinitionKey(userTask.getId()); + return dto; + }).toList(); + + return success(result); + } + + @Override + public CommonResult> getTaskListByParentTaskId(String parentTaskId) { + // 调用 Service 层方法 + List taskList = taskService.getTaskListByParentTaskId(parentTaskId); + if (CollUtil.isEmpty(taskList)) { + return success(Collections.emptyList()); + } + + // 拼接数据 - 参考 Controller 逻辑 + Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(taskList, user -> Stream.of(NumberUtils.parseLong(user.getAssignee()), NumberUtils.parseLong(user.getOwner())))); + Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); + + // 使用转换器构建结果并转换为 DTO + var voList = BpmTaskConvert.INSTANCE.buildTaskListByParentTaskId(taskList, userMap, deptMap); + List result = BeanUtils.toBean(voList, BpmTaskRespDTO.class); + + return success(result); + } + +} \ No newline at end of file From 285e0ab17d7d9e43b4df7c87ac8ad775e1a89e84 Mon Sep 17 00:00:00 2001 From: qianshijiang <1965297290@qq.com> Date: Tue, 23 Sep 2025 15:11:09 +0800 Subject: [PATCH 10/35] =?UTF-8?q?=E6=B5=81=E7=A8=8B=E4=BB=BB=E5=8A=A1feign?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=B7=BB=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/api/task/BpmProcessInstanceApi.java | 12 +++++++ .../api/task/dto/BpmTaskApproveReqDTO.java | 33 +++++++++++++++++++ .../bpm/api/task/dto/BpmTaskRejectReqDTO.java | 18 ++++++++++ .../api/task/BpmProcessInstanceApiImpl.java | 24 ++++++++++++++ 4 files changed, 87 insertions(+) create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java index 061e677..baf8f52 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java @@ -2,6 +2,8 @@ package com.zt.plat.module.bpm.api.task; import com.zt.plat.framework.common.pojo.CommonResult; import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskApproveReqDTO; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskRejectReqDTO; import com.zt.plat.module.bpm.enums.ApiConstants; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; @@ -9,6 +11,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; @@ -24,4 +27,13 @@ public interface BpmProcessInstanceApi { CommonResult createProcessInstance(@RequestParam("userId") Long userId, @Valid @RequestBody BpmProcessInstanceCreateReqDTO reqDTO); + + @PutMapping(PREFIX + "/approveTask") + @Operation(summary = "通过任务") + CommonResult approveTask(@Valid @RequestBody BpmTaskApproveReqDTO reqVO); + + @PutMapping(PREFIX + "/rejectTask") + @Operation(summary = "不通过任务") + CommonResult rejectTask(@Valid @RequestBody BpmTaskRejectReqDTO reqVO); + } diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java new file mode 100644 index 0000000..705f930 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java @@ -0,0 +1,33 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "RPC 服务 - 通过流程任务的 Request DTO") +@Data +public class BpmTaskApproveReqDTO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "审批意见", example = "不错不错!") + private String reason; + + @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") + private String signPicUrl; + + @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) + private Map variables; + + @Schema(description = "下一个节点审批人", example = "{nodeId:[1, 2]}") + private Map> nextAssignees; // 为什么是 Map,而不是 List 呢?因为下一个节点可能是多个,例如说并行网关的情况 + + // 新增任务变量实例,业务表单 + @Schema(description = "任务变量实例,业务表单", example = "{'formField1': 'value1', 'formField2': 'value2'}") + private Map taskVariables; +} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java new file mode 100644 index 0000000..94f73bf --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java @@ -0,0 +1,18 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "RPC 服务 - 不通过流程任务的 Request DTO") +@Data +public class BpmTaskRejectReqDTO { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java index 52fa15f..9b84efe 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java @@ -1,8 +1,16 @@ package com.zt.plat.module.bpm.api.task; import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.util.object.BeanUtils; import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskApproveReqDTO; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskRejectReqDTO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO; +import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRejectReqVO; import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; +import com.zt.plat.module.bpm.service.task.BpmTaskService; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RestController; @@ -10,6 +18,7 @@ import jakarta.annotation.Resource; import jakarta.validation.Valid; import static com.zt.plat.framework.common.pojo.CommonResult.success; +import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.getLoginUserId; /** * Flowable 流程实例 Api 实现类 @@ -24,9 +33,24 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { @Resource private BpmProcessInstanceService processInstanceService; + @Resource + private BpmTaskService taskService; + @Override public CommonResult createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) { return success(processInstanceService.createProcessInstance(userId, reqDTO)); } + @Override + public CommonResult approveTask(BpmTaskApproveReqDTO reqVO) { + taskService.approveTask(getLoginUserId(), BeanUtils.toBean(reqVO, BpmTaskApproveReqVO.class)); + return success(true); + } + + @Override + public CommonResult rejectTask(BpmTaskRejectReqDTO reqVO) { + taskService.rejectTask(getLoginUserId(), BeanUtils.toBean(reqVO, BpmTaskRejectReqVO.class)); + return success(true); + } + } From e43e5f5caf3763fe3748af3f065fc3d6432de1b2 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 25 Sep 2025 19:24:06 +0800 Subject: [PATCH 11/35] =?UTF-8?q?1.=20=E5=8D=87=E7=BA=A7=203.0.38=20=20?= =?UTF-8?q?=E8=A1=A5=E5=85=A8=E4=B8=9A=E5=8A=A1=E9=99=84=E4=BB=B6=E8=A1=A8?= =?UTF-8?q?=E7=BC=BA=E5=A4=B1=E7=9A=84=20api=20=20=E6=94=AF=E6=8C=81=20api?= =?UTF-8?q?=20=E8=8E=B7=E5=8F=96=E9=99=84=E4=BB=B6=E4=BA=8C=E8=BF=9B?= =?UTF-8?q?=E5=88=B6=E6=95=B0=E6=8D=AE=20=20=E6=96=B0=E5=A2=9E=E4=B8=9A?= =?UTF-8?q?=E5=8A=A1=E9=99=84=E4=BB=B6=E8=A1=A8=E7=8A=B6=E6=80=81=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=20=20=E8=A1=A5=E5=85=A8=E9=83=A8=E5=88=86=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E5=B5=8C=E5=A5=97=E7=BB=93=E6=9E=84=E7=9A=84=20bpm=20?= =?UTF-8?q?api=20=E7=BC=BA=E5=A4=B1=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/api/task/dto/BpmTaskRespDTO.java | 76 ++++++++++++++++- .../bpm/api/task/dto/UserSimpleDTO.java | 25 ++++++ .../module/bpm/api/task/BpmTaskApiImpl.java | 28 ++++--- .../bpm/convert/task/BpmTaskConvert.java | 83 +++++++++++++++++++ 4 files changed, 200 insertions(+), 12 deletions(-) create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java index 1ae2e45..412c098 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java @@ -1,5 +1,6 @@ package com.zt.plat.module.bpm.api.task.dto; +import com.zt.plat.framework.common.core.KeyValue; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -46,14 +47,87 @@ public class BpmTaskRespDTO { @Schema(description = "表单编号", example = "1024") private Long formId; + + @Schema(description = "表单路径", example = "/form/leave") + private String formPath; + + @Schema(description = "表单名字", example = "请假表单") + private String formName; @Schema(description = "表单的配置", example = "[]") private String formConf; @Schema(description = "表单项的数组", example = "[]") - private String formFields; + private List formFields; @Schema(description = "提交的表单值", example = "{\"name\": \"芋艿\"}") private Map formVariables; + @Schema(description = "任务负责人编号", example = "2048") + private Long owner; + + @Schema(description = "负责人的用户信息") + private UserSimpleDTO ownerUser; + + @Schema(description = "任务分配人编号", example = "2048") + private Long assignee; + + @Schema(description = "审核的用户信息") + private UserSimpleDTO assigneeUser; + + @Schema(description = "父任务编号", example = "1024") + private String parentTaskId; + + @Schema(description = "子任务列表(由加签生成)") + private List children; + + @Schema(description = "所属流程实例") + private ProcessInstanceDTO processInstance; + + @Schema(description = "操作按钮设置值") + private Map buttonsSetting; + + @Schema(description = "是否需要签名", example = "false") + private Boolean signEnable; + + @Schema(description = "是否填写审批意见", example = "false") + private Boolean reasonRequire; + + @Schema(description = "节点类型", example = "10") + private Integer nodeType; + + @Data + @Schema(description = "流程实例信息") + public static class ProcessInstanceDTO { + + @Schema(description = "流程实例编号", example = "1024") + private String id; + + @Schema(description = "流程实例名称", example = "芋道") + private String name; + + @Schema(description = "提交时间") + private LocalDateTime createTime; + + @Schema(description = "流程定义的编号", example = "2048") + private String processDefinitionId; + + @Schema(description = "流程摘要", example = "[]") + private List> summary; + + @Schema(description = "发起人的用户信息") + private UserSimpleDTO startUser; + } + + @Data + @Schema(description = "操作按钮设置") + public static class OperationButtonSettingDTO { + + @Schema(description = "显示名称", example = "审批") + private String displayName; + + @Schema(description = "是否启用", example = "true") + private Boolean enable; + } + } \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java new file mode 100644 index 0000000..ddb00d6 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java @@ -0,0 +1,25 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +@Schema(description = "RPC 服务 - 用户精简信息 DTO") +@Data +public class UserSimpleDTO { + + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long id; + + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + private String nickname; + + @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png") + private String avatar; + + @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Long deptId; + + @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部") + private String deptName; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java index d688f4a..4d70c6a 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java @@ -75,12 +75,14 @@ public class BpmTaskApiImpl implements BpmTaskApi { Map userMap = adminUserApi.getUserMap(convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(convertSet(pageResult.getList(), Task::getProcessDefinitionId)); - // 使用转换器构建结果并转换为 DTO + // 使用转换器构建完整的 VO 结果,然后转换为 DTO var voPageResult = BpmTaskConvert.INSTANCE.buildTodoTaskPage(pageResult, processInstanceMap, userMap, processDefinitionInfoMap); - return success(BeanUtils.toBean(voPageResult.getList(), BpmTaskRespDTO.class)); + List result = BpmTaskConvert.INSTANCE.buildTaskRespDTOList(voPageResult.getList()); + + return success(result); } - @Override + @Override public CommonResult> getTaskDonePage(@Valid BpmTaskPageReqDTO pageReqDTO) { // 转换请求参数 BpmTaskPageReqVO pageReqVO = BeanUtils.toBean(pageReqDTO, BpmTaskPageReqVO.class); @@ -96,9 +98,11 @@ public class BpmTaskApiImpl implements BpmTaskApi { Map userMap = adminUserApi.getUserMap(convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); - // 使用转换器构建结果并转换为 DTO + // 使用转换器构建完整的 VO 结果,然后转换为 DTO var voPageResult = BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null, processDefinitionInfoMap); - return success(BeanUtils.toBean(voPageResult.getList(), BpmTaskRespDTO.class)); + List result = BpmTaskConvert.INSTANCE.buildTaskRespDTOList(voPageResult.getList()); + + return success(result); } @Override @@ -121,9 +125,11 @@ public class BpmTaskApiImpl implements BpmTaskApi { Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); - // 使用转换器构建结果并转换为 DTO + // 使用转换器构建完整的 VO 结果,然后转换为 DTO var voPageResult = BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap, processDefinitionInfoMap); - return success(BeanUtils.toBean(voPageResult.getList(), BpmTaskRespDTO.class)); + List result = BpmTaskConvert.INSTANCE.buildTaskRespDTOList(voPageResult.getList()); + + return success(result); } @Override @@ -152,9 +158,9 @@ public class BpmTaskApiImpl implements BpmTaskApi { } })); - // 使用转换器构建结果并转换为 DTO + // 使用转换器构建完整的 VO 结果,然后转换为 DTO var voList = BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, formMap, userMap, deptMap); - List result = BeanUtils.toBean(voList, BpmTaskRespDTO.class); + List result = BpmTaskConvert.INSTANCE.buildTaskRespDTOList(voList); return success(result); } @@ -187,9 +193,9 @@ public class BpmTaskApiImpl implements BpmTaskApi { Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(taskList, user -> Stream.of(NumberUtils.parseLong(user.getAssignee()), NumberUtils.parseLong(user.getOwner())))); Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); - // 使用转换器构建结果并转换为 DTO + // 使用转换器构建完整的 VO 结果,然后转换为 DTO var voList = BpmTaskConvert.INSTANCE.buildTaskListByParentTaskId(taskList, userMap, deptMap); - List result = BeanUtils.toBean(voList, BpmTaskRespDTO.class); + List result = BpmTaskConvert.INSTANCE.buildTaskRespDTOList(voList); return success(result); } diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java index 5926d84..ccba6d7 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java @@ -8,6 +8,8 @@ import com.zt.plat.framework.common.util.collection.CollectionUtils; import com.zt.plat.framework.common.util.date.DateUtils; import com.zt.plat.framework.common.util.number.NumberUtils; import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.api.task.dto.BpmTaskRespDTO; +import com.zt.plat.module.bpm.api.task.dto.UserSimpleDTO; import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; @@ -230,4 +232,85 @@ public interface BpmTaskConvert { childTask.setTenantId(parentTask.getTenantId()); } + /** + * 将 BpmTaskRespVO 转换为 BpmTaskRespDTO,保持完整的嵌套结构 + */ + default List buildTaskRespDTOList(List voList) { + return CollectionUtils.convertList(voList, this::buildTaskRespDTO); + } + + /** + * 将 BpmTaskRespVO 转换为 BpmTaskRespDTO,保持完整的嵌套结构 + */ + default BpmTaskRespDTO buildTaskRespDTO(BpmTaskRespVO vo) { + if (vo == null) { + return null; + } + + BpmTaskRespDTO dto = BeanUtils.toBean(vo, BpmTaskRespDTO.class); + + // 转换用户信息 + if (vo.getAssigneeUser() != null) { + dto.setAssigneeUser(convertToUserSimpleDTO(vo.getAssigneeUser())); + } + if (vo.getOwnerUser() != null) { + dto.setOwnerUser(convertToUserSimpleDTO(vo.getOwnerUser())); + } + + // 转换流程实例信息 + if (vo.getProcessInstance() != null) { + BpmTaskRespDTO.ProcessInstanceDTO processInstanceDTO = new BpmTaskRespDTO.ProcessInstanceDTO(); + processInstanceDTO.setId(vo.getProcessInstance().getId()); + processInstanceDTO.setName(vo.getProcessInstance().getName()); + processInstanceDTO.setCreateTime(vo.getProcessInstance().getCreateTime()); + processInstanceDTO.setProcessDefinitionId(vo.getProcessInstance().getProcessDefinitionId()); + processInstanceDTO.setSummary(vo.getProcessInstance().getSummary()); + + if (vo.getProcessInstance().getStartUser() != null) { + processInstanceDTO.setStartUser(convertToUserSimpleDTO(vo.getProcessInstance().getStartUser())); + } + dto.setProcessInstance(processInstanceDTO); + } + + // 转换操作按钮设置 + if (vo.getButtonsSetting() != null) { + Map buttonsSettingDTO = vo.getButtonsSetting().entrySet() + .stream() + .collect(java.util.stream.Collectors.toMap( + Map.Entry::getKey, + entry -> { + BpmTaskRespDTO.OperationButtonSettingDTO settingDTO = new BpmTaskRespDTO.OperationButtonSettingDTO(); + settingDTO.setDisplayName(entry.getValue().getDisplayName()); + settingDTO.setEnable(entry.getValue().getEnable()); + return settingDTO; + } + )); + dto.setButtonsSetting(buttonsSettingDTO); + } + + // 递归转换子任务 + if (vo.getChildren() != null) { + dto.setChildren(buildTaskRespDTOList(vo.getChildren())); + } + + return dto; + } + + /** + * 将 UserSimpleBaseVO 转换为 UserSimpleDTO,确保所有字段都被正确赋值 + */ + default UserSimpleDTO convertToUserSimpleDTO(UserSimpleBaseVO vo) { + if (vo == null) { + return null; + } + + UserSimpleDTO dto = new UserSimpleDTO(); + dto.setId(vo.getId()); + dto.setNickname(vo.getNickname()); + dto.setAvatar(vo.getAvatar()); + dto.setDeptId(vo.getDeptId()); + dto.setDeptName(vo.getDeptName()); // 确保 deptName 被正确赋值 + return dto; + } + } From 00e445c80de89ff971fb27f7b2f8fab8362348bf Mon Sep 17 00:00:00 2001 From: chenbowen Date: Sun, 28 Sep 2025 00:08:55 +0800 Subject: [PATCH 12/35] =?UTF-8?q?1.=20=E5=8D=87=E7=BA=A7=203.0.40=20?= =?UTF-8?q?=E6=96=B0=E5=A2=9E=20bpm=20api=20=E6=96=B0=E5=A2=9E=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=A1=B5=E9=9D=A2=E5=8C=BA=E5=88=86=E5=86=85=E5=A4=96?= =?UTF-8?q?=E9=83=A8=E7=94=A8=E6=88=B7=E4=BB=A5=E5=8F=8A=20e=20=E5=8A=9E?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E8=AE=A4=E8=AF=81=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/api/task/BpmProcessInstanceApi.java | 42 +++++-- .../api/task/dto/BpmApprovalDetailReqDTO.java | 40 +++++++ .../task/dto/BpmApprovalDetailRespDTO.java | 110 ++++++++++++++++++ .../task/dto/BpmProcessDefinitionRespDTO.java | 55 +++++++++ ...pmProcessInstanceBpmnModelViewRespDTO.java | 41 +++++++ .../dto/BpmProcessInstanceCancelReqDTO.java | 19 +++ .../dto/BpmProcessInstancePageReqDTO.java | 13 +++ .../task/dto/BpmProcessInstanceRespDTO.java | 56 ++++++++- .../api/task/dto/BpmSimpleModelNodeDTO.java | 31 +++++ .../api/task/BpmProcessInstanceApiImpl.java | 110 ++++++++++++++++-- .../task/BpmProcessInstanceDTOConvert.java | 67 +++++++++++ 11 files changed, 564 insertions(+), 20 deletions(-) create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailRespDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessDefinitionRespDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceBpmnModelViewRespDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCancelReqDTO.java create mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmSimpleModelNodeDTO.java create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceDTOConvert.java diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java index baf8f52..89839e8 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java @@ -1,19 +1,16 @@ package com.zt.plat.module.bpm.api.task; import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; -import com.zt.plat.module.bpm.api.task.dto.BpmTaskApproveReqDTO; -import com.zt.plat.module.bpm.api.task.dto.BpmTaskRejectReqDTO; +import com.zt.plat.module.bpm.api.task.dto.*; import com.zt.plat.module.bpm.enums.ApiConstants; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = @Tag(name = "RPC 服务 - 流程实例") @@ -27,6 +24,37 @@ public interface BpmProcessInstanceApi { CommonResult createProcessInstance(@RequestParam("userId") Long userId, @Valid @RequestBody BpmProcessInstanceCreateReqDTO reqDTO); + @GetMapping(PREFIX + "/get") + @Operation(summary = "获得指定流程实例", description = "在【流程详细】界面中,进行调用") + @Parameter(name = "id", description = "流程实例的编号", required = true) + CommonResult getProcessInstance(@RequestParam("id") String id); + + @DeleteMapping(PREFIX + "/cancel-by-start-user") + @Operation(summary = "用户取消流程实例", description = "取消发起的流程") + CommonResult cancelProcessInstanceByStartUser( + @RequestParam("userId") Long userId, + @Valid @RequestBody BpmProcessInstanceCancelReqDTO cancelReqDTO); + + @DeleteMapping(PREFIX + "/cancel-by-admin") + @Operation(summary = "管理员取消流程实例", description = "管理员撤回流程") + CommonResult cancelProcessInstanceByAdmin( + @RequestParam("userId") Long userId, + @Valid @RequestBody BpmProcessInstanceCancelReqDTO cancelReqDTO); + + @PostMapping(PREFIX + "/get-approval-detail") + @Operation(summary = "获得审批详情") + CommonResult getApprovalDetail(@RequestParam("userId") Long userId, + @Valid BpmApprovalDetailReqDTO reqDTO); + + @PostMapping(PREFIX + "/get-next-approval-nodes") + @Operation(summary = "获取下一个执行的流程节点") + CommonResult> getNextApprovalNodes(@RequestParam("userId") Long userId, + @Valid BpmApprovalDetailReqDTO reqDTO); + + @PostMapping(PREFIX + "/get-bpmn-model-view") + @Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用") + @Parameter(name = "id", description = "流程实例的编号", required = true) + CommonResult getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id); @PutMapping(PREFIX + "/approveTask") @Operation(summary = "通过任务") diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java new file mode 100644 index 0000000..4794386 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java @@ -0,0 +1,40 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import cn.hutool.core.util.StrUtil; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.AssertTrue; +import lombok.Data; + +import java.util.Map; + +@Schema(description = "RPC 服务 - 审批详情 Request DTO") +@Data +public class BpmApprovalDetailReqDTO { + + @Schema(description = "流程定义的编号", example = "1024") + private String processDefinitionId; // 使用场景:发起流程时,传流程定义 ID + + @Schema(description = "流程变量") + private Map processVariables; // 使用场景:同 processDefinitionId,用于流程预测 + + @Schema(description = "流程变量") + private String processVariablesStr; // 解决 GET 无法传递对象的问题,最终转换成 processVariables 变量 + + @Schema(description = "流程实例的编号", example = "1024") + private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID + + // TODO @芋艿:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 + @Schema(description = "流程活动编号", example = "StartUserNode") + private String activityId; // 用于获取表单权限。1)发起流程时,传"发起人节点" activityId 可获取发起人的表单权限;2)从抄送列表界面进来时,传抄送的 activityId 可获取抄送人的表单权限; + + @Schema(description = "流程任务编号", example = "95f2f08b-621b-11ef-bf39-00ff4722db8b") + private String taskId; // 用于获取表单权限。1)从待审批/已审批界面进来时,传递 taskId 任务编号,可获取任务节点的变得权限 + + @AssertTrue(message = "流程定义的编号和流程实例的编号不能同时为空") + @JsonIgnore + public boolean isValidProcessParam() { + return StrUtil.isNotEmpty(processDefinitionId) || StrUtil.isNotEmpty(processInstanceId); + } + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailRespDTO.java new file mode 100644 index 0000000..3183561 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailRespDTO.java @@ -0,0 +1,110 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import com.zt.plat.module.bpm.api.task.dto.BpmTaskRespDTO; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + + +@Schema(description = "RPC 服务 - 审批详情 Response DTO") +@Data +public class BpmApprovalDetailRespDTO { + + @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 + + @Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List activityNodes; + + @Schema(description = "表单字段权限") + private Map formFieldsPermission; + + @Schema(description = "待办任务") + private BpmTaskRespDTO todoTask; + + /** + * 所属流程定义信息 + */ + private BpmProcessDefinitionRespDTO processDefinition; + + /** + * 所属流程实例信息 + */ + private BpmProcessInstanceRespDTO processInstance; + + @Schema(description = "活动节点信息") + @Data + public static class ActivityNode { + + @Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartUserNode") + private String id; + + @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "发起人") + private String name; + + @Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer nodeType; // 参见 BpmSimpleModelNodeType 枚举 + + @Schema(description = "节点状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") + private Integer status; // 参见 BpmTaskStatusEnum 枚举 + + @Schema(description = "节点的开始时间") + private LocalDateTime startTime; + @Schema(description = "节点的结束时间") + private LocalDateTime endTime; + + @Schema(description = "审批节点的任务信息") + private List tasks; + + @Schema(description = "候选人策略", example = "35") + private Integer candidateStrategy; // 参见 BpmTaskCandidateStrategyEnum 枚举。主要用于发起时,审批节点、抄送节点自选 + + @Schema(description = "候选人用户 ID 列表", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 candidateUsers + private List candidateUserIds; + + @Schema(description = "候选人用户列表") + private List candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表 + + @Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf") + private String processInstanceId; // 当且仅当,该节点是子流程节点时,才会有值(CallActivity 的 processInstanceId 字段) + + } + + @Schema(description = "活动节点的任务信息") + @Data + public static class ActivityNodeTask { + + @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private String id; + + @Schema(description = "任务所属人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser + private Long owner; + + @Schema(description = "任务所属人", example = "1024") + private UserSimpleDTO ownerUser; + + @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser + private Long assignee; + + @Schema(description = "任务分配人", example = "2048") + private UserSimpleDTO assigneeUser; + + @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; // 参见 BpmTaskStatusEnum 枚举 + + @Schema(description = "审批意见", example = "同意") + private String reason; + + @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") + private String signPicUrl; + + } + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessDefinitionRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessDefinitionRespDTO.java new file mode 100644 index 0000000..81aec42 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessDefinitionRespDTO.java @@ -0,0 +1,55 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; + +@Schema(description = "RPC 服务 - 流程定义 Response DTO") +@Data +public class BpmProcessDefinitionRespDTO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_order") + private String key; + + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "请假流程") + private String name; + + @Schema(description = "流程描述", example = "请假流程描述") + private String description; + + @Schema(description = "流程分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "oa") + private String category; + + @Schema(description = "表单类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer formType; + + @Schema(description = "表单编号", example = "1024") + private Long formId; + + @Schema(description = "表单的配置", requiredMode = Schema.RequiredMode.REQUIRED) + private String formConf; + + @Schema(description = "表单项的数组", requiredMode = Schema.RequiredMode.REQUIRED) + private List formFields; + + @Schema(description = "表单项的 JSON 数组字符串", requiredMode = Schema.RequiredMode.REQUIRED) + private String formFieldsStr; + + @Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED) + private String bpmnXml; + + @Schema(description = "版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer version; + + @Schema(description = "是否挂起", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") + private Integer suspensionState; + + @Schema(description = "部署时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime deploymentTime; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceBpmnModelViewRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceBpmnModelViewRespDTO.java new file mode 100644 index 0000000..9b1f2c3 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceBpmnModelViewRespDTO.java @@ -0,0 +1,41 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; +import java.util.Set; + +@Schema(description = "RPC 服务 - 流程示例的 BPMN 视图 Response DTO") +@Data +public class BpmProcessInstanceBpmnModelViewRespDTO { + + // ========== 基本信息 ========== + + @Schema(description = "流程实例信息", requiredMode = Schema.RequiredMode.REQUIRED) + private BpmProcessInstanceRespDTO processInstance; + + @Schema(description = "任务列表", requiredMode = Schema.RequiredMode.REQUIRED) + private List tasks; + + @Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED) + private String bpmnXml; + + @Schema(description = "SIMPLE 模型") + private BpmSimpleModelNodeDTO simpleModel; + + // ========== 进度信息 ========== + + @Schema(description = "进行中的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set unfinishedTaskActivityIds; // 只包括 UserTask + + @Schema(description = "已经完成的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set finishedTaskActivityIds; // 包括 UserTask、Gateway 等,不包括 SequenceFlow + + @Schema(description = "已经完成的连线节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set finishedSequenceFlowActivityIds; // 只包括 SequenceFlow + + @Schema(description = "已经拒绝的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) + private Set rejectedTaskActivityIds; // 只包括 UserTask + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCancelReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCancelReqDTO.java new file mode 100644 index 0000000..15316d0 --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCancelReqDTO.java @@ -0,0 +1,19 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotEmpty; +import lombok.Data; + +@Schema(description = "RPC 服务 - 流程实例的取消 Request DTO") +@Data +public class BpmProcessInstanceCancelReqDTO { + + @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + @NotEmpty(message = "流程实例的编号不能为空") + private String id; + + @Schema(description = "取消原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "不请假了!") + @NotEmpty(message = "取消原因不能为空") + private String reason; + +} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java index b041098..ce6c38c 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java @@ -22,6 +22,9 @@ public class BpmProcessInstancePageReqDTO extends PageParam { @Schema(description = "流程定义的编号", example = "2048") private String processDefinitionId; + @Schema(description = "流程定义的标识", example = "2048") + private String processDefinitionKey; // 精准匹配 + @Schema(description = "流程分类", example = "1") private String category; @@ -35,4 +38,14 @@ public class BpmProcessInstancePageReqDTO extends PageParam { @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) private LocalDateTime[] createTime; + @Schema(description = "结束时间") + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) + private LocalDateTime[] endTime; + + @Schema(description = "发起用户编号", example = "1024") + private Long startUserId; // 注意,只有在【流程实例】菜单,才使用该参数 + + @Schema(description = "动态表单字段查询 JSON Str", example = "{}") + private String formFieldsParams; // SpringMVC 在 get 请求下,无法方便的定义 Map 类型的参数,所以通过 String 接收后,逻辑里面转换 + } \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java index 1138169..48c779c 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java @@ -1,9 +1,12 @@ package com.zt.plat.module.bpm.api.task.dto; +import com.zt.plat.framework.common.core.KeyValue; +import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import java.time.LocalDateTime; +import java.util.List; import java.util.Map; @Schema(description = "RPC 服务 - 流程实例 Response DTO") @@ -16,23 +19,26 @@ public class BpmProcessInstanceRespDTO { @Schema(description = "流程实例的名字", example = "芋艿") private String name; + @Schema(description = "流程摘要") + private List> summary; // 只有流程表单,才有摘要! + @Schema(description = "流程定义的编号", example = "2048") private String processDefinitionId; @Schema(description = "流程分类", example = "1") private String category; + @Schema(description = "流程分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "请假") + private String categoryName; + @Schema(description = "流程实例的状态", example = "1") private Integer status; @Schema(description = "流程实例的结果", example = "1") private Integer result; - @Schema(description = "提交的表单值", example = "{\"name\": \"芋艿\"}") - private Map formVariables; - - @Schema(description = "业务的唯一标识", example = "1") - private String businessKey; + @Schema(description = "发起时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime startTime; @Schema(description = "创建时间") private LocalDateTime createTime; @@ -43,4 +49,44 @@ public class BpmProcessInstanceRespDTO { @Schema(description = "持续时间", example = "1000") private Long durationInMillis; + @Schema(description = "提交的表单值", example = "{\"name\": \"芋艿\"}") + private Map formVariables; + + @Schema(description = "业务的唯一标识", example = "1") + private String businessKey; + + /** + * 发起流程的用户 + */ + private UserSimpleDTO startUser; + + /** + * 流程定义 + */ + private BpmProcessDefinitionRespDTO processDefinition; + + /** + * 当前审批中的任务 + */ + private List tasks; // 仅在流程实例分页才返回 + + @Schema(description = "流程任务") + @Data + public static class Task { + + @Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") + private String id; + + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + private String name; + + @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser + private Long assignee; + + @Schema(description = "任务分配人", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") + private UserSimpleDTO assigneeUser; + + } + } \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmSimpleModelNodeDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmSimpleModelNodeDTO.java new file mode 100644 index 0000000..a01c60d --- /dev/null +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmSimpleModelNodeDTO.java @@ -0,0 +1,31 @@ +package com.zt.plat.module.bpm.api.task.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +@Schema(description = "RPC 服务 - 简单模型节点 DTO") +@Data +public class BpmSimpleModelNodeDTO { + + @Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartNode") + private String id; + + @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "开始节点") + private String name; + + @Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer type; + + @Schema(description = "节点属性") + private Map attributes; + + @Schema(description = "子节点列表") + private List childNode; + + @Schema(description = "条件节点列表") + private List conditionNodes; + +} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java index 9b84efe..b3d1e79 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java @@ -1,21 +1,34 @@ package com.zt.plat.module.bpm.api.task; +import cn.hutool.core.util.StrUtil; +import com.zt.plat.framework.business.core.util.DeptUtil; import com.zt.plat.framework.common.pojo.CommonResult; +import com.zt.plat.framework.common.util.json.JsonUtils; +import com.zt.plat.framework.common.util.number.NumberUtils; import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; -import com.zt.plat.module.bpm.api.task.dto.BpmTaskApproveReqDTO; -import com.zt.plat.module.bpm.api.task.dto.BpmTaskRejectReqDTO; +import com.zt.plat.module.bpm.api.task.dto.*; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO; import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRejectReqVO; +import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceConvert; +import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceDTOConvert; +import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; import com.zt.plat.module.bpm.service.task.BpmTaskService; +import com.zt.plat.module.system.api.dept.DeptApi; +import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; +import com.zt.plat.module.system.api.user.AdminUserApi; +import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import jakarta.validation.Valid; -import org.springframework.validation.annotation.Validated; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.repository.ProcessDefinition; +import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; +import java.util.List; +import java.util.Map; import static com.zt.plat.framework.common.pojo.CommonResult.success; import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.getLoginUserId; @@ -27,7 +40,7 @@ import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.getLoginUser * @author jason */ @RestController -@Validated +@Valid public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { @Resource @@ -36,11 +49,92 @@ public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { @Resource private BpmTaskService taskService; + @Resource + private BpmProcessDefinitionService processDefinitionService; + + @Resource + private AdminUserApi adminUserApi; + + @Resource + private DeptApi deptApi; + @Override - public CommonResult createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO reqDTO) { + public CommonResult createProcessInstance(Long userId, @Valid @RequestBody BpmProcessInstanceCreateReqDTO reqDTO) { return success(processInstanceService.createProcessInstance(userId, reqDTO)); } + + @Override + public CommonResult getProcessInstance(String id) { + HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(id); + if (processInstance == null) { + return success(null); + } + + // 拼接返回 + ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( + processInstance.getProcessDefinitionId()); + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo( + processInstance.getProcessDefinitionId()); + AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData(); + DeptRespDTO dept = null; + if (startUser != null) { + Long deptId = DeptUtil.getDeptId(startUser); + if (deptId > 0) { + dept = deptApi.getDept(deptId).getCheckedData(); + } + } + BpmProcessInstanceRespVO vo = BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance, + processDefinition, processDefinitionInfo, startUser, dept); + return success(BpmProcessInstanceDTOConvert.INSTANCE.convert(vo)); + } + + @Override + public CommonResult cancelProcessInstanceByStartUser( + Long userId, @Valid @RequestBody BpmProcessInstanceCancelReqDTO cancelReqDTO) { + BpmProcessInstanceCancelReqVO cancelReqVO = BpmProcessInstanceDTOConvert.INSTANCE.convertVO(cancelReqDTO); + processInstanceService.cancelProcessInstanceByStartUser(userId, cancelReqVO); + return success(true); + } + + @Override + public CommonResult cancelProcessInstanceByAdmin( + Long userId, @Valid @RequestBody BpmProcessInstanceCancelReqDTO cancelReqDTO) { + BpmProcessInstanceCancelReqVO cancelReqVO = BpmProcessInstanceDTOConvert.INSTANCE.convertVO(cancelReqDTO); + processInstanceService.cancelProcessInstanceByAdmin(userId, cancelReqVO); + return success(true); + } + + @Override + @SuppressWarnings("unchecked") + public CommonResult getApprovalDetail(Long userId, + @Valid @RequestBody BpmApprovalDetailReqDTO reqDTO) { + BpmApprovalDetailReqVO reqVO = BpmProcessInstanceDTOConvert.INSTANCE.convertVO(reqDTO); + if (StrUtil.isNotEmpty(reqDTO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqDTO.getProcessVariablesStr(), Map.class)); + } + BpmApprovalDetailRespVO respVO = processInstanceService.getApprovalDetail(userId, reqVO); + return success(BpmProcessInstanceDTOConvert.INSTANCE.convert(respVO)); + } + + @Override + @SuppressWarnings("unchecked") + public CommonResult> getNextApprovalNodes(Long userId, + @Valid @RequestBody BpmApprovalDetailReqDTO reqDTO) { + BpmApprovalDetailReqVO reqVO = BpmProcessInstanceDTOConvert.INSTANCE.convertVO(reqDTO); + if (StrUtil.isNotEmpty(reqDTO.getProcessVariablesStr())) { + reqVO.setProcessVariables(JsonUtils.parseObject(reqDTO.getProcessVariablesStr(), Map.class)); + } + List nodes = processInstanceService.getNextApprovalNodes(userId, reqVO); + return success(BpmProcessInstanceDTOConvert.INSTANCE.convertActivityNodes(nodes)); + } + + @Override + public CommonResult getProcessInstanceBpmnModelView(String id) { + BpmProcessInstanceBpmnModelViewRespVO respVO = processInstanceService.getProcessInstanceBpmnModelView(id); + return success(BpmProcessInstanceDTOConvert.INSTANCE.convert(respVO)); + } + @Override public CommonResult approveTask(BpmTaskApproveReqDTO reqVO) { taskService.approveTask(getLoginUserId(), BeanUtils.toBean(reqVO, BpmTaskApproveReqVO.class)); diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceDTOConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceDTOConvert.java new file mode 100644 index 0000000..8d0a6ff --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceDTOConvert.java @@ -0,0 +1,67 @@ +package com.zt.plat.module.bpm.convert.task; + +import com.zt.plat.framework.common.pojo.PageResult; +import com.zt.plat.framework.common.util.object.BeanUtils; +import com.zt.plat.module.bpm.api.task.dto.*; +import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; +import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; +import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * 流程实例 DTO 转换器 + * + * @author ZT + */ +@Mapper +public interface BpmProcessInstanceDTOConvert { + + BpmProcessInstanceDTOConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceDTOConvert.class); + + // VO to DTO + BpmProcessInstancePageReqDTO convert(BpmProcessInstancePageReqVO bean); + BpmProcessInstanceCancelReqDTO convert(BpmProcessInstanceCancelReqVO bean); + BpmApprovalDetailReqDTO convert(BpmApprovalDetailReqVO bean); + + // DTO to VO + BpmProcessInstancePageReqVO convertVO(BpmProcessInstancePageReqDTO bean); + BpmProcessInstanceCancelReqVO convertVO(BpmProcessInstanceCancelReqDTO bean); + BpmApprovalDetailReqVO convertVO(BpmApprovalDetailReqDTO bean); + + PageResult convertPage(PageResult pageResult); + BpmProcessInstanceRespDTO convert(BpmProcessInstanceRespVO bean); + BpmApprovalDetailRespDTO convert(BpmApprovalDetailRespVO bean); + BpmProcessInstanceBpmnModelViewRespDTO convert(BpmProcessInstanceBpmnModelViewRespVO bean); + + List convertActivityNodes(List nodes); + + // 内部类转换 + BpmProcessInstanceRespDTO.Task convert(BpmProcessInstanceRespVO.Task task); + BpmApprovalDetailRespDTO.ActivityNode convert(BpmApprovalDetailRespVO.ActivityNode node); + BpmApprovalDetailRespDTO.ActivityNodeTask convert(BpmApprovalDetailRespVO.ActivityNodeTask task); + + // 用户信息转换 + UserSimpleDTO convertUser(UserSimpleBaseVO user); + BpmProcessDefinitionRespDTO convert(BpmProcessDefinitionRespVO definition); + + @Mapping(target = "childNode", source = "childNode", qualifiedByName = "mapChildNode") + BpmSimpleModelNodeDTO convert(BpmSimpleModelNodeVO simpleModel); + + /** + * 将BpmSimpleModelNodeVO的childNode转换为List + */ + @Named("mapChildNode") + default List mapChildNode(BpmSimpleModelNodeVO childNode) { + if (childNode == null) { + return null; + } + return List.of(convert(childNode)); + } + +} \ No newline at end of file From edb686174dc45138fd72a7ae3d2875b228c054cb Mon Sep 17 00:00:00 2001 From: qianshijiang <1965297290@qq.com> Date: Sun, 28 Sep 2025 11:43:54 +0800 Subject: [PATCH 13/35] =?UTF-8?q?=E6=B5=81=E7=A8=8B=E6=9E=9A=E4=B8=BE?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E8=A1=A5=E5=85=85=E6=96=B9=E6=B3=95=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E5=AF=B9=E5=BA=94=E6=9E=9A=E4=B8=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceStatusEnum.java | 30 +++++++++++++++++++ .../bpm/enums/task/BpmTaskStatusEnum.java | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java index 411e9ad..d3a1fed 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java @@ -4,6 +4,7 @@ import com.zt.plat.framework.common.core.ArrayValuable; import com.zt.plat.framework.common.util.object.ObjectUtils; import lombok.AllArgsConstructor; import lombok.Getter; +import org.apache.commons.lang3.StringUtils; import java.util.Arrays; @@ -47,4 +48,33 @@ public enum BpmProcessInstanceStatusEnum implements ArrayValuable { APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus()); } + /** + * 通过流程的状态返回对应的枚举 + * @param status 流程状态 + * @return + */ + public static BpmProcessInstanceStatusEnum getEnumBystatus(Integer status){ + for (BpmProcessInstanceStatusEnum e : BpmProcessInstanceStatusEnum.values()) { + if (e.getStatus().equals(status)) { + return e; + } + } + return NOT_START; + } + + /** + * 通过枚举描述返回对应的枚举 + * @param desc 描述 + * @return + */ + public static BpmProcessInstanceStatusEnum getEnumByDesc(String desc){ + if (StringUtils.isEmpty(desc)) return NOT_START; + for (BpmProcessInstanceStatusEnum e : BpmProcessInstanceStatusEnum.values()) { + if (desc.equals(e.getDesc())) { + return e; + } + } + return NOT_START; + } + } diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java index 51ca7af..ab335d5 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java @@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjUtil; import com.zt.plat.framework.common.util.object.ObjectUtils; import lombok.AllArgsConstructor; import lombok.Getter; +import org.apache.commons.lang3.StringUtils; /** * 流程任务 Task 的状态枚举 @@ -67,4 +68,33 @@ public enum BpmTaskStatusEnum { return ObjUtil.equal(status, CANCEL.getStatus()); } + + /** + * 通过流程的状态返回对应的枚举 + * @param status 流程状态 + * @return + */ + public static BpmTaskStatusEnum getEnumBystatus(Integer status){ + for (BpmTaskStatusEnum e : BpmTaskStatusEnum.values()) { + if (e.getStatus().equals(status)) { + return e; + } + } + return NOT_START; + } + + /** + * 通过枚举描述返回对应的枚举 + * @param name 描述 + * @return + */ + public static BpmTaskStatusEnum getEnumByName(String name){ + if (StringUtils.isEmpty(name)) return NOT_START; + for (BpmTaskStatusEnum e : BpmTaskStatusEnum.values()) { + if (name.equals(e.getName())) { + return e; + } + } + return NOT_START; + } } From bd6ffa6217344576f6e8e776ffcdf9217d3378fd Mon Sep 17 00:00:00 2001 From: qianshijiang <1965297290@qq.com> Date: Sun, 28 Sep 2025 11:48:25 +0800 Subject: [PATCH 14/35] =?UTF-8?q?=E6=B5=81=E7=A8=8B=E6=9E=9A=E4=B8=BE?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E8=A1=A5=E5=85=85=E6=96=B9=E6=B3=95=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E5=AF=B9=E5=BA=94=E6=9E=9A=E4=B8=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../module/bpm/enums/task/BpmProcessInstanceStatusEnum.java | 2 +- .../com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java index d3a1fed..0e61d22 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java @@ -53,7 +53,7 @@ public enum BpmProcessInstanceStatusEnum implements ArrayValuable { * @param status 流程状态 * @return */ - public static BpmProcessInstanceStatusEnum getEnumBystatus(Integer status){ + public static BpmProcessInstanceStatusEnum getEnumByStatus(Integer status){ for (BpmProcessInstanceStatusEnum e : BpmProcessInstanceStatusEnum.values()) { if (e.getStatus().equals(status)) { return e; diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java index ab335d5..42ac7df 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java @@ -74,7 +74,7 @@ public enum BpmTaskStatusEnum { * @param status 流程状态 * @return */ - public static BpmTaskStatusEnum getEnumBystatus(Integer status){ + public static BpmTaskStatusEnum getEnumByStatus(Integer status){ for (BpmTaskStatusEnum e : BpmTaskStatusEnum.values()) { if (e.getStatus().equals(status)) { return e; From 7ac2e4dcbdcbea8c5dc9e554430b50b072b6e56c Mon Sep 17 00:00:00 2001 From: qianshijiang <1965297290@qq.com> Date: Sun, 28 Sep 2025 11:43:54 +0800 Subject: [PATCH 15/35] =?UTF-8?q?1.=20=E6=B5=81=E7=A8=8B=E6=9E=9A=E4=B8=BE?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E8=A1=A5=E5=85=85=E6=96=B9=E6=B3=95=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E5=AF=B9=E5=BA=94=E6=9E=9A=E4=B8=BE=202.=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E8=8E=B7=E5=BE=97=E6=8C=87=E5=AE=9A=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=9A=84=E5=85=AC=E5=8F=B8=E9=83=A8=E9=97=A8=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=90=BA=E5=B8=A6=20Code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceStatusEnum.java | 30 +++++++++++++++++++ .../bpm/enums/task/BpmTaskStatusEnum.java | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java index 411e9ad..0e61d22 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java @@ -4,6 +4,7 @@ import com.zt.plat.framework.common.core.ArrayValuable; import com.zt.plat.framework.common.util.object.ObjectUtils; import lombok.AllArgsConstructor; import lombok.Getter; +import org.apache.commons.lang3.StringUtils; import java.util.Arrays; @@ -47,4 +48,33 @@ public enum BpmProcessInstanceStatusEnum implements ArrayValuable { APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus()); } + /** + * 通过流程的状态返回对应的枚举 + * @param status 流程状态 + * @return + */ + public static BpmProcessInstanceStatusEnum getEnumByStatus(Integer status){ + for (BpmProcessInstanceStatusEnum e : BpmProcessInstanceStatusEnum.values()) { + if (e.getStatus().equals(status)) { + return e; + } + } + return NOT_START; + } + + /** + * 通过枚举描述返回对应的枚举 + * @param desc 描述 + * @return + */ + public static BpmProcessInstanceStatusEnum getEnumByDesc(String desc){ + if (StringUtils.isEmpty(desc)) return NOT_START; + for (BpmProcessInstanceStatusEnum e : BpmProcessInstanceStatusEnum.values()) { + if (desc.equals(e.getDesc())) { + return e; + } + } + return NOT_START; + } + } diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java index 51ca7af..42ac7df 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java @@ -4,6 +4,7 @@ import cn.hutool.core.util.ObjUtil; import com.zt.plat.framework.common.util.object.ObjectUtils; import lombok.AllArgsConstructor; import lombok.Getter; +import org.apache.commons.lang3.StringUtils; /** * 流程任务 Task 的状态枚举 @@ -67,4 +68,33 @@ public enum BpmTaskStatusEnum { return ObjUtil.equal(status, CANCEL.getStatus()); } + + /** + * 通过流程的状态返回对应的枚举 + * @param status 流程状态 + * @return + */ + public static BpmTaskStatusEnum getEnumByStatus(Integer status){ + for (BpmTaskStatusEnum e : BpmTaskStatusEnum.values()) { + if (e.getStatus().equals(status)) { + return e; + } + } + return NOT_START; + } + + /** + * 通过枚举描述返回对应的枚举 + * @param name 描述 + * @return + */ + public static BpmTaskStatusEnum getEnumByName(String name){ + if (StringUtils.isEmpty(name)) return NOT_START; + for (BpmTaskStatusEnum e : BpmTaskStatusEnum.values()) { + if (name.equals(e.getName())) { + return e; + } + } + return NOT_START; + } } From 10df09b9bb98a749bce77e506d9e6b5af02a29b3 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Fri, 17 Oct 2025 17:40:46 +0800 Subject: [PATCH 16/35] =?UTF-8?q?1.=20=E6=96=B0=E5=A2=9E=E5=A4=96=E9=83=A8?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E7=BC=96=E7=A0=81=E9=83=A8=E9=97=A8=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E5=85=B3=E8=81=94=E7=AE=A1=E7=90=86=202.=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E7=BB=9F=E4=B8=80=E7=9A=84=20api=20=E5=AF=B9=E5=A4=96?= =?UTF-8?q?=E9=97=A8=E6=88=B7=E7=AE=A1=E7=90=86=203.=20=E4=BF=AE=E6=AD=A3?= =?UTF-8?q?=E5=90=84=E4=B8=AA=E6=A8=A1=E5=9D=97=E7=9A=84=20api=20=E5=91=BD?= =?UTF-8?q?=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/src/main/resources/application.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zt-module-bpm-server/src/main/resources/application.yaml b/zt-module-bpm-server/src/main/resources/application.yaml index 07020c5..adfbf58 100644 --- a/zt-module-bpm-server/src/main/resources/application.yaml +++ b/zt-module-bpm-server/src/main/resources/application.yaml @@ -141,8 +141,8 @@ zt: exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 swagger: - title: 管理后台 - description: 提供管理员管理的所有功能 + title: 流程模块 + description: 提供流程模块功能 version: ${zt.info.version} tenant: # 多租户相关配置项 enable: true From 8e43f3e2a3e7c514acf485855ec7caad0112552f Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 6 Nov 2025 00:28:14 +0800 Subject: [PATCH 17/35] =?UTF-8?q?1.=20=E6=96=B0=E5=A2=9E=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=97=A5=E5=BF=97=E6=94=B6=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/Dockerfile | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile index 230aa5c..95f6b15 100644 --- a/zt-module-bpm-server/Dockerfile +++ b/zt-module-bpm-server/Dockerfile @@ -1,6 +1,7 @@ ## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 -FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre +ARG BASE_IMAGE=skywalking-agent-jre:21 +FROM ${BASE_IMAGE} ## 创建目录,并使用它作为工作目录 RUN mkdir -p /zt-module-bpm-server @@ -10,10 +11,15 @@ COPY ./target/zt-module-bpm-server.jar app.jar ## 设置 TZ 时区 ## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 -ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m" +ENV TZ=Asia/Shanghai +ENV JAVA_OPTS="-Xms512m -Xmx512m" +ENV SW_AGENT_HOME=/opt/skywalking/agent +ENV SW_AGENT_NAME=zt-module-bpm-server +ENV SW_AGENT_COLLECTOR_BACKEND_SERVICES=oap:11800 +ENV AGENT_JAVA_OPTS="-javaagent:${SW_AGENT_HOME}/skywalking-agent.jar -Dskywalking.agent.service_name=${SW_AGENT_NAME} -Dskywalking.collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES}" ## 暴露后端项目的 48080 端口 EXPOSE 48083 ## 启动后端项目 -CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar +CMD java ${AGENT_JAVA_OPTS} ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar From 947c867526675b230b15c9c4392b11ee222d09b6 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 6 Nov 2025 00:37:48 +0800 Subject: [PATCH 18/35] =?UTF-8?q?1.=20=E6=96=B0=E5=A2=9E=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=97=A5=E5=BF=97=E6=94=B6=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile index 95f6b15..45f60e9 100644 --- a/zt-module-bpm-server/Dockerfile +++ b/zt-module-bpm-server/Dockerfile @@ -1,6 +1,6 @@ ## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 -ARG BASE_IMAGE=skywalking-agent-jre:21 +ARG BASE_IMAGE=172.16.46.66:10043/base-service/skywalking-agent-jre:21 FROM ${BASE_IMAGE} ## 创建目录,并使用它作为工作目录 From f7af0cad094225522e34f52c994a8a7a56768203 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 6 Nov 2025 00:41:57 +0800 Subject: [PATCH 19/35] =?UTF-8?q?1.=20=E6=96=B0=E5=A2=9E=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=97=A5=E5=BF=97=E6=94=B6=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile index 45f60e9..f573a48 100644 --- a/zt-module-bpm-server/Dockerfile +++ b/zt-module-bpm-server/Dockerfile @@ -1,6 +1,6 @@ ## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 -ARG BASE_IMAGE=172.16.46.66:10043/base-service/skywalking-agent-jre:21 +ARG BASE_IMAGE=172.16.46.66:10043/base-service/skywalking-agent-jre:9.7.0 FROM ${BASE_IMAGE} ## 创建目录,并使用它作为工作目录 From cea6ff4a529892048a800dd116f7fbb91f766472 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 6 Nov 2025 00:49:53 +0800 Subject: [PATCH 20/35] =?UTF-8?q?1.=20=E6=96=B0=E5=A2=9E=E4=B8=9A=E5=8A=A1?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E6=97=A5=E5=BF=97=E6=94=B6=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile index f573a48..eceda19 100644 --- a/zt-module-bpm-server/Dockerfile +++ b/zt-module-bpm-server/Dockerfile @@ -15,7 +15,7 @@ ENV TZ=Asia/Shanghai ENV JAVA_OPTS="-Xms512m -Xmx512m" ENV SW_AGENT_HOME=/opt/skywalking/agent ENV SW_AGENT_NAME=zt-module-bpm-server -ENV SW_AGENT_COLLECTOR_BACKEND_SERVICES=oap:11800 +ENV SW_AGENT_COLLECTOR_BACKEND_SERVICES=172.16.46.63:30201 ENV AGENT_JAVA_OPTS="-javaagent:${SW_AGENT_HOME}/skywalking-agent.jar -Dskywalking.agent.service_name=${SW_AGENT_NAME} -Dskywalking.collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES}" ## 暴露后端项目的 48080 端口 From f5f06f503bc4614c449fc2ead0545110df143177 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 27 Nov 2025 09:58:44 +0800 Subject: [PATCH 21/35] =?UTF-8?q?flowable=20=E8=BE=BE=E6=A2=A6=E8=BF=81?= =?UTF-8?q?=E7=A7=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../config/BpmFlowableConfiguration.java | 41 ++ .../liquibase/database/core/DmDatabase.java | 60 ++- .../datatype/core/DmBooleanType.java | 32 ++ .../services/liquibase.database.Database | 1 + .../liquibase.datatype.LiquibaseDataType | 1 + .../src/main/resources/application-local.yaml | 17 +- .../create/flowable.oracle.create.batch.sql | 41 ++ .../db/drop/flowable.oracle.drop.batch.sql | 4 + .../create/flowable.oracle.create.common.sql | 23 ++ .../db/drop/flowable.oracle.drop.common.sql | 2 + .../create/flowable.oracle.create.engine.sql | 355 ++++++++++++++++++ .../create/flowable.oracle.create.history.sql | 114 ++++++ .../db/drop/flowable.oracle.drop.engine.sql | 148 ++++++++ .../db/drop/flowable.oracle.drop.history.sql | 23 ++ ...wable.oracle.create.entitylink.history.sql | 23 ++ .../flowable.oracle.create.entitylink.sql | 26 ++ ...lowable.oracle.drop.entitylink.history.sql | 4 + .../drop/flowable.oracle.drop.entitylink.sql | 4 + ...owable.oracle.create.eventsubscription.sql | 28 ++ ...flowable.oracle.drop.eventsubscription.sql | 5 + ...ble.oracle.create.identitylink.history.sql | 20 + .../flowable.oracle.create.identitylink.sql | 24 ++ ...wable.oracle.drop.identitylink.history.sql | 6 + .../flowable.oracle.drop.identitylink.sql | 7 + .../flowable.oracle.create.identity.sql | 108 ++++++ .../db/drop/flowable.oracle.drop.identity.sql | 22 ++ .../db/create/flowable.oracle.create.job.sql | 261 +++++++++++++ .../db/drop/flowable.oracle.drop.job.sql | 74 ++++ .../flowable.oracle.create.task.history.sql | 64 ++++ .../db/create/flowable.oracle.create.task.sql | 48 +++ .../flowable.oracle.drop.task.history.sql | 8 + .../db/drop/flowable.oracle.drop.task.sql | 6 + ...lowable.oracle.create.variable.history.sql | 26 ++ .../flowable.oracle.create.variable.sql | 31 ++ .../flowable.oracle.drop.variable.history.sql | 6 + .../db/drop/flowable.oracle.drop.variable.sql | 9 + 36 files changed, 1649 insertions(+), 23 deletions(-) create mode 100644 zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java create mode 100644 zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql create mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java index f0d5b49..159cfce 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java @@ -8,17 +8,25 @@ import com.zt.plat.module.bpm.framework.flowable.core.event.BpmProcessInstanceEv import com.zt.plat.module.system.api.user.AdminUserApi; import org.flowable.common.engine.api.delegate.FlowableFunctionDelegate; import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.engine.ProcessEngineConfiguration; import org.flowable.spring.SpringProcessEngineConfiguration; import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.AsyncListenableTaskExecutor; +import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.List; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; /** * BPM 模块的 Flowable 配置类 @@ -28,6 +36,8 @@ import java.util.List; @Configuration(proxyBeanMethods = false) public class BpmFlowableConfiguration { + private static final Logger log = LoggerFactory.getLogger(BpmFlowableConfiguration.class); + /** * 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean * @@ -69,6 +79,37 @@ public class BpmFlowableConfiguration { }; } + @Bean + public EngineConfigurationConfigurer dmProcessEngineConfigurationConfigurer(DataSource dataSource) { + return configuration -> { + try { + configureDmCompatibility(configuration, dataSource); + } catch (SQLException ex) { + log.warn("Failed to inspect datasource for DM compatibility; Flowable will keep default settings", ex); + } + }; + } + + private void configureDmCompatibility(SpringProcessEngineConfiguration configuration, DataSource dataSource) throws SQLException { + Connection connection = null; + try { + connection = DataSourceUtils.getConnection(dataSource); + DatabaseMetaData metaData = connection.getMetaData(); + String productName = metaData.getDatabaseProductName(); + String jdbcUrl = metaData.getURL(); + boolean dmProduct = productName != null && productName.toLowerCase().contains("dm"); + boolean dmUrl = jdbcUrl != null && jdbcUrl.toLowerCase().startsWith("jdbc:dm"); + if (!dmProduct && !dmUrl) { + return; + } + log.info("Detected DM database (product='{}'); enabling Flowable Oracle compatibility with automatic schema updates", productName); + configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); + configuration.setDatabaseType("oracle"); + } finally { + DataSourceUtils.releaseConnection(connection, dataSource); + } + } + // =========== 审批人相关的 Bean ========== @Bean diff --git a/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java b/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java index f7558a1..9e2c50f 100644 --- a/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java +++ b/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java @@ -8,6 +8,7 @@ package liquibase.database.core; import java.lang.reflect.Method; import java.sql.CallableStatement; import java.sql.Connection; +import java.sql.DatabaseMetaData; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -114,6 +115,7 @@ public class DmDatabase extends AbstractJdbcDatabase { public void setConnection(DatabaseConnection conn) { this.reservedWords.addAll(Arrays.asList("GROUP", "USER", "SESSION", "PASSWORD", "RESOURCE", "START", "SIZE", "UID", "DESC", "ORDER")); Connection sqlConn = null; + boolean dmDatabase = false; if (!(conn instanceof OfflineConnection)) { try { if (conn instanceof JdbcConnection) { @@ -140,26 +142,42 @@ public class DmDatabase extends AbstractJdbcDatabase { Scope.getCurrentScope().getLog(this.getClass()).info("Could not set remarks reporting on OracleDatabase: " + e.getMessage()); } - CallableStatement statement = null; - try { - statement = sqlConn.prepareCall("{call DBMS_UTILITY.DB_VERSION(?,?)}"); - statement.registerOutParameter(1, 12); - statement.registerOutParameter(2, 12); - statement.execute(); - String compatibleVersion = statement.getString(2); - if (compatibleVersion != null) { - Matcher majorVersionMatcher = VERSION_PATTERN.matcher(compatibleVersion); - if (majorVersionMatcher.matches()) { - this.databaseMajorVersion = Integer.valueOf(majorVersionMatcher.group(1)); - this.databaseMinorVersion = Integer.valueOf(majorVersionMatcher.group(2)); + DatabaseMetaData metaData = sqlConn.getMetaData(); + if (metaData != null) { + String productName = metaData.getDatabaseProductName(); + dmDatabase = productName != null && PRODUCT_NAME.equalsIgnoreCase(productName); + if (dmDatabase) { + this.databaseMajorVersion = metaData.getDatabaseMajorVersion(); + this.databaseMinorVersion = metaData.getDatabaseMinorVersion(); } } } catch (SQLException e) { - String message = "Cannot read from DBMS_UTILITY.DB_VERSION: " + e.getMessage(); - Scope.getCurrentScope().getLog(this.getClass()).info("Could not set check compatibility mode on OracleDatabase, assuming not running in any sort of compatibility mode: " + message); - } finally { - JdbcUtil.closeStatement(statement); + Scope.getCurrentScope().getLog(this.getClass()).info("Unable to inspect database metadata for DM version detection: " + e.getMessage()); + } + + if (!dmDatabase) { + CallableStatement statement = null; + + try { + statement = sqlConn.prepareCall("{call DBMS_UTILITY.DB_VERSION(?,?)}"); + statement.registerOutParameter(1, 12); + statement.registerOutParameter(2, 12); + statement.execute(); + String compatibleVersion = statement.getString(2); + if (compatibleVersion != null) { + Matcher majorVersionMatcher = VERSION_PATTERN.matcher(compatibleVersion); + if (majorVersionMatcher.matches()) { + this.databaseMajorVersion = Integer.valueOf(majorVersionMatcher.group(1)); + this.databaseMinorVersion = Integer.valueOf(majorVersionMatcher.group(2)); + } + } + } catch (SQLException e) { + String message = "Cannot read from DBMS_UTILITY.DB_VERSION: " + e.getMessage(); + Scope.getCurrentScope().getLog(this.getClass()).info("Could not set check compatibility mode on OracleDatabase, assuming not running in any sort of compatibility mode: " + message); + } finally { + JdbcUtil.closeStatement(statement); + } } if (GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue() != null) { @@ -266,7 +284,15 @@ public class DmDatabase extends AbstractJdbcDatabase { } public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException { - return "oracle".equalsIgnoreCase(conn.getDatabaseProductName()); + String databaseProductName = conn == null ? null : conn.getDatabaseProductName(); + if (databaseProductName == null) { + return false; + } + if (PRODUCT_NAME.equalsIgnoreCase(databaseProductName)) { + return true; + } + // Flowable 历史上将 DM 映射为 Oracle 元数据,因此这里同样接受 Oracle 以保持兼容 + return "oracle".equalsIgnoreCase(databaseProductName); } public String getDefaultDriver(String url) { diff --git a/zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java b/zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java new file mode 100644 index 0000000..7f66250 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java @@ -0,0 +1,32 @@ +package liquibase.datatype.core; + +import liquibase.database.Database; +import liquibase.database.core.DmDatabase; +import liquibase.datatype.DataTypeInfo; +import liquibase.datatype.DatabaseDataType; + +@DataTypeInfo( + name = "boolean", + aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, + minParameters = 0, + maxParameters = 0, + priority = 2 +) +public class DmBooleanType extends BooleanType { + + @Override + public boolean supports(Database database) { + if (database instanceof DmDatabase) { + return true; + } + return super.supports(database); + } + + @Override + public DatabaseDataType toDatabaseDataType(Database database) { + if (database instanceof DmDatabase) { + return new DatabaseDataType("NUMBER", 1); + } + return super.toDatabaseDataType(database); + } +} diff --git a/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database b/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database index 0ccf224..765e41a 100644 --- a/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database +++ b/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database @@ -13,6 +13,7 @@ liquibase.database.core.MariaDBDatabase liquibase.database.core.MockDatabase liquibase.database.core.MySQLDatabase liquibase.database.core.OracleDatabase +liquibase.database.core.DmDatabase liquibase.database.core.PostgresDatabase liquibase.database.core.SQLiteDatabase liquibase.database.core.SybaseASADatabase diff --git a/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType b/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType new file mode 100644 index 0000000..5be88a3 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType @@ -0,0 +1 @@ +liquibase.datatype.core.DmBooleanType diff --git a/zt-module-bpm-server/src/main/resources/application-local.yaml b/zt-module-bpm-server/src/main/resources/application-local.yaml index 2bf6df5..1a65166 100644 --- a/zt-module-bpm-server/src/main/resources/application-local.yaml +++ b/zt-module-bpm-server/src/main/resources/application-local.yaml @@ -39,14 +39,14 @@ spring: primary: master datasource: master: - url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 - username: jygk-test - password: Zgty@0527 + url: jdbc:dm://172.16.46.247:1050?schema=BPM + username: SYSDBA + password: pgbsci6ddJ6Sqj@e slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 - username: jygk-test - password: Zgty@0527 + url: jdbc:dm://172.16.46.247:1050?schema=BPM + username: SYSDBA + password: pgbsci6ddJ6Sqj@e # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: @@ -56,6 +56,11 @@ spring: database: 0 # 数据库索引 # password: 123456 # 密码,建议生产环境开启 +# Flowable 在 DM 场景下需要识别为 Oracle 并自动升级表结构 +flowable: + database-schema-update: true + database-type: oracle + --- #################### MQ 消息队列相关配置 #################### --- #################### 定时任务相关配置 #################### diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql b/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql new file mode 100644 index 0000000..19dca82 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql @@ -0,0 +1,41 @@ +create table FLW_RU_BATCH ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + TYPE_ VARCHAR2(64) not null, + SEARCH_KEY_ VARCHAR2(255), + SEARCH_KEY2_ VARCHAR2(255), + CREATE_TIME_ TIMESTAMP(6) not null, + COMPLETE_TIME_ TIMESTAMP(6), + STATUS_ VARCHAR2(255), + BATCH_DOC_ID_ VARCHAR2(64), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create table FLW_RU_BATCH_PART ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + BATCH_ID_ VARCHAR2(64), + TYPE_ VARCHAR2(64) not null, + SCOPE_ID_ VARCHAR2(64), + SUB_SCOPE_ID_ VARCHAR2(64), + SCOPE_TYPE_ VARCHAR2(64), + SEARCH_KEY_ VARCHAR2(255), + SEARCH_KEY2_ VARCHAR2(255), + CREATE_TIME_ TIMESTAMP(6) not null, + COMPLETE_TIME_ TIMESTAMP(6), + STATUS_ VARCHAR2(255), + RESULT_DOC_ID_ VARCHAR2(64), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create index FLW_IDX_BATCH_PART on FLW_RU_BATCH_PART(BATCH_ID_); + +alter table FLW_RU_BATCH_PART + add constraint FLW_FK_BATCH_PART_PARENT + foreign key (BATCH_ID_) + references FLW_RU_BATCH (ID_); + +insert into ACT_GE_PROPERTY values ('batch.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql b/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql new file mode 100644 index 0000000..d16ba1c --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql @@ -0,0 +1,4 @@ +drop index FLW_IDX_BATCH_PART; + +drop table FLW_RU_BATCH_PART; +drop table FLW_RU_BATCH; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql b/zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql new file mode 100644 index 0000000..4ef0d2e --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql @@ -0,0 +1,23 @@ +create table ACT_GE_PROPERTY ( + NAME_ VARCHAR2(64), + VALUE_ VARCHAR2(300), + REV_ INTEGER, + primary key (NAME_) +); + +create table ACT_GE_BYTEARRAY ( + ID_ VARCHAR2(64), + REV_ INTEGER, + NAME_ VARCHAR2(255), + DEPLOYMENT_ID_ VARCHAR2(64), + BYTES_ BLOB, + GENERATED_ NUMBER(1) CHECK (GENERATED_ IN (1,0)), + primary key (ID_) +); + +insert into ACT_GE_PROPERTY +values ('common.schema.version', '7.0.1.1', 1); + +insert into ACT_GE_PROPERTY +values ('next.dbid', '1', 1); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql b/zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql new file mode 100644 index 0000000..9019cb9 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql @@ -0,0 +1,2 @@ +drop table ACT_GE_BYTEARRAY; +drop table ACT_GE_PROPERTY; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql b/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql new file mode 100644 index 0000000..d0139b7 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql @@ -0,0 +1,355 @@ +create table ACT_RE_DEPLOYMENT ( + ID_ VARCHAR2(64), + NAME_ VARCHAR2(255), + CATEGORY_ VARCHAR2(255), + KEY_ VARCHAR2(255), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + DEPLOY_TIME_ TIMESTAMP(6), + DERIVED_FROM_ VARCHAR2(64), + DERIVED_FROM_ROOT_ VARCHAR2(64), + PARENT_DEPLOYMENT_ID_ VARCHAR2(255), + ENGINE_VERSION_ VARCHAR2(255), + primary key (ID_) +); + +create table ACT_RE_MODEL ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + NAME_ VARCHAR2(255), + KEY_ VARCHAR2(255), + CATEGORY_ VARCHAR2(255), + CREATE_TIME_ TIMESTAMP(6), + LAST_UPDATE_TIME_ TIMESTAMP(6), + VERSION_ INTEGER, + META_INFO_ VARCHAR2(2000), + DEPLOYMENT_ID_ VARCHAR2(64), + EDITOR_SOURCE_VALUE_ID_ VARCHAR2(64), + EDITOR_SOURCE_EXTRA_VALUE_ID_ VARCHAR2(64), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_EXECUTION ( + ID_ VARCHAR2(64), + REV_ INTEGER, + PROC_INST_ID_ VARCHAR2(64), + BUSINESS_KEY_ VARCHAR2(255), + PARENT_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + SUPER_EXEC_ VARCHAR2(64), + ROOT_PROC_INST_ID_ VARCHAR2(64), + ACT_ID_ VARCHAR2(255), + IS_ACTIVE_ NUMBER(1) CHECK (IS_ACTIVE_ IN (1,0)), + IS_CONCURRENT_ NUMBER(1) CHECK (IS_CONCURRENT_ IN (1,0)), + IS_SCOPE_ NUMBER(1) CHECK (IS_SCOPE_ IN (1,0)), + IS_EVENT_SCOPE_ NUMBER(1) CHECK (IS_EVENT_SCOPE_ IN (1,0)), + IS_MI_ROOT_ NUMBER(1) CHECK (IS_MI_ROOT_ IN (1,0)), + SUSPENSION_STATE_ INTEGER, + CACHED_ENT_STATE_ INTEGER, + TENANT_ID_ VARCHAR2(255) DEFAULT '', + NAME_ VARCHAR2(255), + START_ACT_ID_ VARCHAR2(255), + START_TIME_ TIMESTAMP(6), + START_USER_ID_ VARCHAR2(255), + LOCK_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + IS_COUNT_ENABLED_ NUMBER(1) CHECK (IS_COUNT_ENABLED_ IN (1,0)), + EVT_SUBSCR_COUNT_ INTEGER, + TASK_COUNT_ INTEGER, + JOB_COUNT_ INTEGER, + TIMER_JOB_COUNT_ INTEGER, + SUSP_JOB_COUNT_ INTEGER, + DEADLETTER_JOB_COUNT_ INTEGER, + EXTERNAL_WORKER_JOB_COUNT_ INTEGER, + VAR_COUNT_ INTEGER, + ID_LINK_COUNT_ INTEGER, + CALLBACK_ID_ VARCHAR2(255), + CALLBACK_TYPE_ VARCHAR2(255), + REFERENCE_ID_ VARCHAR2(255), + REFERENCE_TYPE_ VARCHAR2(255), + PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), + BUSINESS_STATUS_ VARCHAR2(255), + primary key (ID_) +); + +create table ACT_RE_PROCDEF ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + NAME_ VARCHAR2(255), + KEY_ VARCHAR2(255) NOT NULL, + VERSION_ INTEGER NOT NULL, + DEPLOYMENT_ID_ VARCHAR2(64), + RESOURCE_NAME_ VARCHAR2(2000), + DGRM_RESOURCE_NAME_ VARCHAR2(4000), + DESCRIPTION_ VARCHAR2(2000), + HAS_START_FORM_KEY_ NUMBER(1) CHECK (HAS_START_FORM_KEY_ IN (1,0)), + HAS_GRAPHICAL_NOTATION_ NUMBER(1) CHECK (HAS_GRAPHICAL_NOTATION_ IN (1,0)), + SUSPENSION_STATE_ INTEGER, + TENANT_ID_ VARCHAR2(255) DEFAULT '', + DERIVED_FROM_ VARCHAR2(64), + DERIVED_FROM_ROOT_ VARCHAR2(64), + DERIVED_VERSION_ INTEGER DEFAULT 0 NOT NULL, + ENGINE_VERSION_ VARCHAR2(255), + primary key (ID_) +); + +create table ACT_EVT_LOG ( + LOG_NR_ NUMBER(19), + TYPE_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + EXECUTION_ID_ VARCHAR2(64), + TASK_ID_ VARCHAR2(64), + TIME_STAMP_ TIMESTAMP(6) not null, + USER_ID_ VARCHAR2(255), + DATA_ BLOB, + LOCK_OWNER_ VARCHAR2(255), + LOCK_TIME_ TIMESTAMP(6) null, + IS_PROCESSED_ NUMBER(3) default 0, + primary key (LOG_NR_) +); + +create sequence act_evt_log_seq; + +create table ACT_PROCDEF_INFO ( + ID_ VARCHAR2(64) not null, + PROC_DEF_ID_ VARCHAR2(64) not null, + REV_ integer, + INFO_JSON_ID_ VARCHAR2(64), + primary key (ID_) +); + +create table ACT_RU_ACTINST ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER default 1, + PROC_DEF_ID_ VARCHAR2(64) not null, + PROC_INST_ID_ VARCHAR2(64) not null, + EXECUTION_ID_ VARCHAR2(64) not null, + ACT_ID_ VARCHAR2(255) not null, + TASK_ID_ VARCHAR2(64), + CALL_PROC_INST_ID_ VARCHAR2(64), + ACT_NAME_ VARCHAR2(255), + ACT_TYPE_ VARCHAR2(255) not null, + ASSIGNEE_ VARCHAR2(255), + START_TIME_ TIMESTAMP(6) not null, + END_TIME_ TIMESTAMP(6), + DURATION_ NUMBER(19,0), + TRANSACTION_ORDER_ INTEGER, + DELETE_REASON_ VARCHAR2(2000), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create index ACT_IDX_EXEC_BUSKEY on ACT_RU_EXECUTION(BUSINESS_KEY_); +create index ACT_IDX_EXEC_ROOT on ACT_RU_EXECUTION(ROOT_PROC_INST_ID_); +create index ACT_IDX_EXEC_REF_ID_ on ACT_RU_EXECUTION(REFERENCE_ID_); +create index ACT_IDX_VARIABLE_TASK_ID on ACT_RU_VARIABLE(TASK_ID_); + +create index ACT_IDX_RU_ACTI_START on ACT_RU_ACTINST(START_TIME_); +create index ACT_IDX_RU_ACTI_END on ACT_RU_ACTINST(END_TIME_); +create index ACT_IDX_RU_ACTI_PROC on ACT_RU_ACTINST(PROC_INST_ID_); +create index ACT_IDX_RU_ACTI_PROC_ACT on ACT_RU_ACTINST(PROC_INST_ID_, ACT_ID_); +create index ACT_IDX_RU_ACTI_EXEC on ACT_RU_ACTINST(EXECUTION_ID_); +create index ACT_IDX_RU_ACTI_EXEC_ACT on ACT_RU_ACTINST(EXECUTION_ID_, ACT_ID_); +create index ACT_IDX_RU_ACTI_TASK on ACT_RU_ACTINST(TASK_ID_); + +create index ACT_IDX_BYTEAR_DEPL on ACT_GE_BYTEARRAY(DEPLOYMENT_ID_); +alter table ACT_GE_BYTEARRAY + add constraint ACT_FK_BYTEARR_DEPL + foreign key (DEPLOYMENT_ID_) + references ACT_RE_DEPLOYMENT (ID_); + +alter table ACT_RE_PROCDEF + add constraint ACT_UNIQ_PROCDEF + unique (KEY_,VERSION_, DERIVED_VERSION_, TENANT_ID_); + +create index ACT_IDX_EXE_PROCINST on ACT_RU_EXECUTION(PROC_INST_ID_); +alter table ACT_RU_EXECUTION + add constraint ACT_FK_EXE_PROCINST + foreign key (PROC_INST_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_EXE_PARENT on ACT_RU_EXECUTION(PARENT_ID_); +alter table ACT_RU_EXECUTION + add constraint ACT_FK_EXE_PARENT + foreign key (PARENT_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_EXE_SUPER on ACT_RU_EXECUTION(SUPER_EXEC_); +alter table ACT_RU_EXECUTION + add constraint ACT_FK_EXE_SUPER + foreign key (SUPER_EXEC_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_EXE_PROCDEF on ACT_RU_EXECUTION(PROC_DEF_ID_); +alter table ACT_RU_EXECUTION + add constraint ACT_FK_EXE_PROCDEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_TSKASS_TASK on ACT_RU_IDENTITYLINK(TASK_ID_); +alter table ACT_RU_IDENTITYLINK + add constraint ACT_FK_TSKASS_TASK + foreign key (TASK_ID_) + references ACT_RU_TASK (ID_); + +create index ACT_IDX_ATHRZ_PROCEDEF on ACT_RU_IDENTITYLINK(PROC_DEF_ID_); +alter table ACT_RU_IDENTITYLINK + add constraint ACT_FK_ATHRZ_PROCEDEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_IDL_PROCINST on ACT_RU_IDENTITYLINK(PROC_INST_ID_); +alter table ACT_RU_IDENTITYLINK + add constraint ACT_FK_IDL_PROCINST + foreign key (PROC_INST_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_TASK_EXEC on ACT_RU_TASK(EXECUTION_ID_); +alter table ACT_RU_TASK + add constraint ACT_FK_TASK_EXE + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_TASK_PROCINST on ACT_RU_TASK(PROC_INST_ID_); +alter table ACT_RU_TASK + add constraint ACT_FK_TASK_PROCINST + foreign key (PROC_INST_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_TASK_PROCDEF on ACT_RU_TASK(PROC_DEF_ID_); +alter table ACT_RU_TASK + add constraint ACT_FK_TASK_PROCDEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_VAR_EXE on ACT_RU_VARIABLE(EXECUTION_ID_); +alter table ACT_RU_VARIABLE + add constraint ACT_FK_VAR_EXE + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_VAR_PROCINST on ACT_RU_VARIABLE(PROC_INST_ID_); +alter table ACT_RU_VARIABLE + add constraint ACT_FK_VAR_PROCINST + foreign key (PROC_INST_ID_) + references ACT_RU_EXECUTION(ID_); + +create index ACT_IDX_JOB_EXECUTION_ID on ACT_RU_JOB(EXECUTION_ID_); +alter table ACT_RU_JOB + add constraint ACT_FK_JOB_EXECUTION + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_JOB_PROC_INST_ID on ACT_RU_JOB(PROCESS_INSTANCE_ID_); +alter table ACT_RU_JOB + add constraint ACT_FK_JOB_PROCESS_INSTANCE + foreign key (PROCESS_INSTANCE_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_JOB_PROC_DEF_ID on ACT_RU_JOB(PROC_DEF_ID_); +alter table ACT_RU_JOB + add constraint ACT_FK_JOB_PROC_DEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_TJOB_EXECUTION_ID on ACT_RU_TIMER_JOB(EXECUTION_ID_); +alter table ACT_RU_TIMER_JOB + add constraint ACT_FK_TJOB_EXECUTION + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_TJOB_PROC_INST_ID on ACT_RU_TIMER_JOB(PROCESS_INSTANCE_ID_); +alter table ACT_RU_TIMER_JOB + add constraint ACT_FK_TJOB_PROCESS_INSTANCE + foreign key (PROCESS_INSTANCE_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_TJOB_PROC_DEF_ID on ACT_RU_TIMER_JOB(PROC_DEF_ID_); +alter table ACT_RU_TIMER_JOB + add constraint ACT_FK_TJOB_PROC_DEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_SJOB_EXECUTION_ID on ACT_RU_SUSPENDED_JOB(EXECUTION_ID_); +alter table ACT_RU_SUSPENDED_JOB + add constraint ACT_FK_SJOB_EXECUTION + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_SJOB_PROC_INST_ID on ACT_RU_SUSPENDED_JOB(PROCESS_INSTANCE_ID_); +alter table ACT_RU_SUSPENDED_JOB + add constraint ACT_FK_SJOB_PROCESS_INSTANCE + foreign key (PROCESS_INSTANCE_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_SJOB_PROC_DEF_ID on ACT_RU_SUSPENDED_JOB(PROC_DEF_ID_); +alter table ACT_RU_SUSPENDED_JOB + add constraint ACT_FK_SJOB_PROC_DEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_DJOB_EXECUTION_ID on ACT_RU_DEADLETTER_JOB(EXECUTION_ID_); +alter table ACT_RU_DEADLETTER_JOB + add constraint ACT_FK_DJOB_EXECUTION + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_DJOB_PROC_INST_ID on ACT_RU_DEADLETTER_JOB(PROCESS_INSTANCE_ID_); +alter table ACT_RU_DEADLETTER_JOB + add constraint ACT_FK_DJOB_PROCESS_INSTANCE + foreign key (PROCESS_INSTANCE_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_DJOB_PROC_DEF_ID on ACT_RU_DEADLETTER_JOB(PROC_DEF_ID_); +alter table ACT_RU_DEADLETTER_JOB + add constraint ACT_FK_DJOB_PROC_DEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +alter table ACT_RU_EVENT_SUBSCR + add constraint ACT_FK_EVENT_EXEC + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION(ID_); + +create index ACT_IDX_MODEL_SOURCE on ACT_RE_MODEL(EDITOR_SOURCE_VALUE_ID_); +alter table ACT_RE_MODEL + add constraint ACT_FK_MODEL_SOURCE + foreign key (EDITOR_SOURCE_VALUE_ID_) + references ACT_GE_BYTEARRAY (ID_); + +create index ACT_IDX_MODEL_SOURCE_EXTRA on ACT_RE_MODEL(EDITOR_SOURCE_EXTRA_VALUE_ID_); +alter table ACT_RE_MODEL + add constraint ACT_FK_MODEL_SOURCE_EXTRA + foreign key (EDITOR_SOURCE_EXTRA_VALUE_ID_) + references ACT_GE_BYTEARRAY (ID_); + +create index ACT_IDX_MODEL_DEPLOYMENT on ACT_RE_MODEL(DEPLOYMENT_ID_); +alter table ACT_RE_MODEL + add constraint ACT_FK_MODEL_DEPLOYMENT + foreign key (DEPLOYMENT_ID_) + references ACT_RE_DEPLOYMENT (ID_); + +create index ACT_IDX_PROCDEF_INFO_JSON on ACT_PROCDEF_INFO(INFO_JSON_ID_); +alter table ACT_PROCDEF_INFO + add constraint ACT_FK_INFO_JSON_BA + foreign key (INFO_JSON_ID_) + references ACT_GE_BYTEARRAY (ID_); + +create index ACT_IDX_PROCDEF_INFO_PROC on ACT_PROCDEF_INFO(PROC_DEF_ID_); +alter table ACT_PROCDEF_INFO + add constraint ACT_FK_INFO_PROCDEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +alter table ACT_PROCDEF_INFO + add constraint ACT_UNIQ_INFO_PROCDEF + unique (PROC_DEF_ID_); + +insert into ACT_GE_PROPERTY +values ('schema.version', '7.0.1.1', 1); + +insert into ACT_GE_PROPERTY +values ('schema.history', 'create(7.0.1.1)', 1); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql new file mode 100644 index 0000000..75782f4 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql @@ -0,0 +1,114 @@ +create table ACT_HI_PROCINST ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER default 1, + PROC_INST_ID_ VARCHAR2(64) not null, + BUSINESS_KEY_ VARCHAR2(255), + PROC_DEF_ID_ VARCHAR2(64) not null, + START_TIME_ TIMESTAMP(6) not null, + END_TIME_ TIMESTAMP(6), + DURATION_ NUMBER(19,0), + START_USER_ID_ VARCHAR2(255), + START_ACT_ID_ VARCHAR2(255), + END_ACT_ID_ VARCHAR2(255), + SUPER_PROCESS_INSTANCE_ID_ VARCHAR2(64), + DELETE_REASON_ VARCHAR2(2000), + TENANT_ID_ VARCHAR2(255) default '', + NAME_ VARCHAR2(255), + CALLBACK_ID_ VARCHAR2(255), + CALLBACK_TYPE_ VARCHAR2(255), + REFERENCE_ID_ VARCHAR2(255), + REFERENCE_TYPE_ VARCHAR2(255), + PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), + BUSINESS_STATUS_ VARCHAR2(255), + primary key (ID_), + unique (PROC_INST_ID_) +); + +create table ACT_HI_ACTINST ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER default 1, + PROC_DEF_ID_ VARCHAR2(64) not null, + PROC_INST_ID_ VARCHAR2(64) not null, + EXECUTION_ID_ VARCHAR2(64) not null, + ACT_ID_ VARCHAR2(255) not null, + TASK_ID_ VARCHAR2(64), + CALL_PROC_INST_ID_ VARCHAR2(64), + ACT_NAME_ VARCHAR2(255), + ACT_TYPE_ VARCHAR2(255) not null, + ASSIGNEE_ VARCHAR2(255), + START_TIME_ TIMESTAMP(6) not null, + END_TIME_ TIMESTAMP(6), + TRANSACTION_ORDER_ INTEGER, + DURATION_ NUMBER(19,0), + DELETE_REASON_ VARCHAR2(2000), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create table ACT_HI_DETAIL ( + ID_ VARCHAR2(64) not null, + TYPE_ VARCHAR2(255) not null, + PROC_INST_ID_ VARCHAR2(64), + EXECUTION_ID_ VARCHAR2(64), + TASK_ID_ VARCHAR2(64), + ACT_INST_ID_ VARCHAR2(64), + NAME_ VARCHAR2(255) not null, + VAR_TYPE_ VARCHAR2(64), + REV_ INTEGER, + TIME_ TIMESTAMP(6) not null, + BYTEARRAY_ID_ VARCHAR2(64), + DOUBLE_ NUMBER(38,10), + LONG_ NUMBER(19,0), + TEXT_ VARCHAR2(2000), + TEXT2_ VARCHAR2(2000), + primary key (ID_) +); + +create table ACT_HI_COMMENT ( + ID_ VARCHAR2(64) not null, + TYPE_ VARCHAR2(255), + TIME_ TIMESTAMP(6) not null, + USER_ID_ VARCHAR2(255), + TASK_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + ACTION_ VARCHAR2(255), + MESSAGE_ VARCHAR2(2000), + FULL_MSG_ BLOB, + primary key (ID_) +); + +create table ACT_HI_ATTACHMENT ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + USER_ID_ VARCHAR2(255), + NAME_ VARCHAR2(255), + DESCRIPTION_ VARCHAR2(2000), + TYPE_ VARCHAR2(255), + TASK_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + URL_ VARCHAR2(2000), + CONTENT_ID_ VARCHAR2(64), + TIME_ TIMESTAMP(6), + primary key (ID_) +); + +create index ACT_IDX_HI_PRO_INST_END on ACT_HI_PROCINST(END_TIME_); +create index ACT_IDX_HI_PRO_I_BUSKEY on ACT_HI_PROCINST(BUSINESS_KEY_); +create index ACT_IDX_HI_PRO_SUPER_PROCINST on ACT_HI_PROCINST(SUPER_PROCESS_INSTANCE_ID_); +create index ACT_IDX_HI_ACT_INST_START on ACT_HI_ACTINST(START_TIME_); +create index ACT_IDX_HI_ACT_INST_END on ACT_HI_ACTINST(END_TIME_); +create index ACT_IDX_HI_DETAIL_PROC_INST on ACT_HI_DETAIL(PROC_INST_ID_); +create index ACT_IDX_HI_DETAIL_ACT_INST on ACT_HI_DETAIL(ACT_INST_ID_); +create index ACT_IDX_HI_DETAIL_TIME on ACT_HI_DETAIL(TIME_); +create index ACT_IDX_HI_DETAIL_NAME on ACT_HI_DETAIL(NAME_); +create index ACT_IDX_HI_DETAIL_TASK_ID on ACT_HI_DETAIL(TASK_ID_); +create index ACT_IDX_HI_PROCVAR_PROC_INST on ACT_HI_VARINST(PROC_INST_ID_); +create index ACT_IDX_HI_PROCVAR_TASK_ID on ACT_HI_VARINST(TASK_ID_); +create index ACT_IDX_HI_PROCVAR_EXE on ACT_HI_VARINST(EXECUTION_ID_); +create index ACT_IDX_HI_IDENT_LNK_TASK on ACT_HI_IDENTITYLINK(TASK_ID_); +create index ACT_IDX_HI_IDENT_LNK_PROCINST on ACT_HI_IDENTITYLINK(PROC_INST_ID_); + +create index ACT_IDX_HI_ACT_INST_PROCINST on ACT_HI_ACTINST(PROC_INST_ID_, ACT_ID_); +create index ACT_IDX_HI_ACT_INST_EXEC on ACT_HI_ACTINST(EXECUTION_ID_, ACT_ID_); +create index ACT_IDX_HI_TASK_INST_PROCINST on ACT_HI_TASKINST(PROC_INST_ID_); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql b/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql new file mode 100644 index 0000000..58537ba --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql @@ -0,0 +1,148 @@ +drop index ACT_IDX_BYTEAR_DEPL; +drop index ACT_IDX_EXE_PROCINST; +drop index ACT_IDX_EXE_PARENT; +drop index ACT_IDX_EXE_SUPER; +drop index ACT_IDX_TSKASS_TASK; +drop index ACT_IDX_TASK_EXEC; +drop index ACT_IDX_TASK_PROCINST; +drop index ACT_IDX_TASK_PROCDEF; +drop index ACT_IDX_VAR_EXE; +drop index ACT_IDX_VAR_PROCINST; +drop index ACT_IDX_JOB_EXECUTION_ID; +drop index ACT_IDX_JOB_PROC_INST_ID; +drop index ACT_IDX_JOB_PROC_DEF_ID; +drop index ACT_IDX_TJOB_EXECUTION_ID; +drop index ACT_IDX_TJOB_PROC_INST_ID; +drop index ACT_IDX_TJOB_PROC_DEF_ID; +drop index ACT_IDX_SJOB_EXECUTION_ID; +drop index ACT_IDX_SJOB_PROC_INST_ID; +drop index ACT_IDX_SJOB_PROC_DEF_ID; +drop index ACT_IDX_DJOB_EXECUTION_ID; +drop index ACT_IDX_DJOB_PROC_INST_ID; +drop index ACT_IDX_DJOB_PROC_DEF_ID; +drop index ACT_IDX_MODEL_SOURCE; +drop index ACT_IDX_MODEL_SOURCE_EXTRA; +drop index ACT_IDX_MODEL_DEPLOYMENT; +drop index ACT_IDX_PROCDEF_INFO_JSON; + +drop index ACT_IDX_EXEC_BUSKEY; +drop index ACT_IDX_VARIABLE_TASK_ID; + +drop index ACT_IDX_RU_ACTI_START; +drop index ACT_IDX_RU_ACTI_END; +drop index ACT_IDX_RU_ACTI_PROC; +drop index ACT_IDX_RU_ACTI_PROC_ACT; +drop index ACT_IDX_RU_ACTI_EXEC; +drop index ACT_IDX_RU_ACTI_EXEC_ACT; + +alter table ACT_GE_BYTEARRAY + drop CONSTRAINT ACT_FK_BYTEARR_DEPL; + +alter table ACT_RU_EXECUTION + drop CONSTRAINT ACT_FK_EXE_PROCINST; + +alter table ACT_RU_EXECUTION + drop CONSTRAINT ACT_FK_EXE_PARENT; + +alter table ACT_RU_EXECUTION + drop CONSTRAINT ACT_FK_EXE_SUPER; + +alter table ACT_RU_EXECUTION + drop CONSTRAINT ACT_FK_EXE_PROCDEF; + +alter table ACT_RU_IDENTITYLINK + drop CONSTRAINT ACT_FK_TSKASS_TASK; + +alter table ACT_RU_IDENTITYLINK + drop CONSTRAINT ACT_FK_IDL_PROCINST; + +alter table ACT_RU_IDENTITYLINK + drop CONSTRAINT ACT_FK_ATHRZ_PROCEDEF; + +alter table ACT_RU_TASK + drop CONSTRAINT ACT_FK_TASK_EXE; + +alter table ACT_RU_TASK + drop CONSTRAINT ACT_FK_TASK_PROCINST; + +alter table ACT_RU_TASK + drop CONSTRAINT ACT_FK_TASK_PROCDEF; + +alter table ACT_RU_VARIABLE + drop CONSTRAINT ACT_FK_VAR_EXE; + +alter table ACT_RU_VARIABLE + drop CONSTRAINT ACT_FK_VAR_PROCINST; + +alter table ACT_RU_JOB + drop CONSTRAINT ACT_FK_JOB_EXECUTION; + +alter table ACT_RU_JOB + drop CONSTRAINT ACT_FK_JOB_PROCESS_INSTANCE; + +alter table ACT_RU_JOB + drop CONSTRAINT ACT_FK_JOB_PROC_DEF; + +alter table ACT_RU_TIMER_JOB + drop CONSTRAINT ACT_FK_TJOB_EXECUTION; + +alter table ACT_RU_TIMER_JOB + drop CONSTRAINT ACT_FK_TJOB_PROCESS_INSTANCE; + +alter table ACT_RU_TIMER_JOB + drop CONSTRAINT ACT_FK_TJOB_PROC_DEF; + +alter table ACT_RU_SUSPENDED_JOB + drop CONSTRAINT ACT_FK_SJOB_EXECUTION; + +alter table ACT_RU_SUSPENDED_JOB + drop CONSTRAINT ACT_FK_SJOB_PROCESS_INSTANCE; + +alter table ACT_RU_SUSPENDED_JOB + drop CONSTRAINT ACT_FK_SJOB_PROC_DEF; + +alter table ACT_RU_DEADLETTER_JOB + drop CONSTRAINT ACT_FK_DJOB_EXECUTION; + +alter table ACT_RU_DEADLETTER_JOB + drop CONSTRAINT ACT_FK_DJOB_PROCESS_INSTANCE; + +alter table ACT_RU_DEADLETTER_JOB + drop CONSTRAINT ACT_FK_DJOB_PROC_DEF; + +alter table ACT_RU_EVENT_SUBSCR + drop CONSTRAINT ACT_FK_EVENT_EXEC; + +alter table ACT_RE_PROCDEF + drop CONSTRAINT ACT_UNIQ_PROCDEF; + +alter table ACT_RE_MODEL + drop CONSTRAINT ACT_FK_MODEL_SOURCE; + +alter table ACT_RE_MODEL + drop CONSTRAINT ACT_FK_MODEL_SOURCE_EXTRA; + +alter table ACT_RE_MODEL + drop CONSTRAINT ACT_FK_MODEL_DEPLOYMENT; + +alter table ACT_PROCDEF_INFO + drop CONSTRAINT ACT_UNIQ_INFO_PROCDEF; + +alter table ACT_PROCDEF_INFO + drop CONSTRAINT ACT_FK_INFO_JSON_BA; + +alter table ACT_PROCDEF_INFO + drop CONSTRAINT ACT_FK_INFO_PROCDEF; + +drop index ACT_IDX_ATHRZ_PROCEDEF; +drop index ACT_IDX_PROCDEF_INFO_PROC; + +drop table ACT_RU_ACTINST; +drop table ACT_RE_DEPLOYMENT; +drop table ACT_RE_MODEL; +drop table ACT_RE_PROCDEF; +drop table ACT_RU_EXECUTION; + +drop sequence act_evt_log_seq; +drop table ACT_EVT_LOG; +drop table ACT_PROCDEF_INFO; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql new file mode 100644 index 0000000..2a31cc4 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql @@ -0,0 +1,23 @@ +drop index ACT_IDX_HI_PRO_INST_END; +drop index ACT_IDX_HI_PRO_I_BUSKEY; +drop index ACT_IDX_HI_ACT_INST_START; +drop index ACT_IDX_HI_ACT_INST_END; +drop index ACT_IDX_HI_DETAIL_PROC_INST; +drop index ACT_IDX_HI_DETAIL_ACT_INST; +drop index ACT_IDX_HI_DETAIL_TIME; +drop index ACT_IDX_HI_DETAIL_NAME; +drop index ACT_IDX_HI_DETAIL_TASK_ID; +drop index ACT_IDX_HI_PROCVAR_PROC_INST; +drop index ACT_IDX_HI_PROCVAR_TASK_ID; +drop index ACT_IDX_HI_PROCVAR_EXE; +drop index ACT_IDX_HI_ACT_INST_PROCINST; +drop index ACT_IDX_HI_IDENT_LNK_TASK; +drop index ACT_IDX_HI_IDENT_LNK_PROCINST; +drop index ACT_IDX_HI_TASK_INST_PROCINST; + +drop table ACT_HI_PROCINST; +drop table ACT_HI_ACTINST; +drop table ACT_HI_DETAIL; +drop table ACT_HI_COMMENT; +drop table ACT_HI_ATTACHMENT; + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql new file mode 100644 index 0000000..55c5dbe --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql @@ -0,0 +1,23 @@ +create table ACT_HI_ENTITYLINK ( + ID_ VARCHAR2(64), + LINK_TYPE_ VARCHAR2(255), + CREATE_TIME_ TIMESTAMP(6), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + PARENT_ELEMENT_ID_ VARCHAR2(255), + REF_SCOPE_ID_ VARCHAR2(255), + REF_SCOPE_TYPE_ VARCHAR2(255), + REF_SCOPE_DEFINITION_ID_ VARCHAR2(255), + ROOT_SCOPE_ID_ VARCHAR2(255), + ROOT_SCOPE_TYPE_ VARCHAR2(255), + HIERARCHY_TYPE_ VARCHAR2(255), + primary key (ID_) +); + +create index ACT_IDX_HI_ENT_LNK_SCOPE on ACT_HI_ENTITYLINK(SCOPE_ID_, SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_HI_ENT_LNK_REF_SCOPE on ACT_HI_ENTITYLINK(REF_SCOPE_ID_, REF_SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_HI_ENT_LNK_ROOT_SCOPE on ACT_HI_ENTITYLINK(ROOT_SCOPE_ID_, ROOT_SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_HI_ENT_LNK_SCOPE_DEF on ACT_HI_ENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_, LINK_TYPE_); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql new file mode 100644 index 0000000..de08451 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql @@ -0,0 +1,26 @@ +create table ACT_RU_ENTITYLINK ( + ID_ VARCHAR2(64), + REV_ INTEGER, + CREATE_TIME_ TIMESTAMP(6), + LINK_TYPE_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + PARENT_ELEMENT_ID_ VARCHAR2(255), + REF_SCOPE_ID_ VARCHAR2(255), + REF_SCOPE_TYPE_ VARCHAR2(255), + REF_SCOPE_DEFINITION_ID_ VARCHAR2(255), + ROOT_SCOPE_ID_ VARCHAR2(255), + ROOT_SCOPE_TYPE_ VARCHAR2(255), + HIERARCHY_TYPE_ VARCHAR2(255), + primary key (ID_) +); + +create index ACT_IDX_ENT_LNK_SCOPE on ACT_RU_ENTITYLINK(SCOPE_ID_, SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_ENT_LNK_REF_SCOPE on ACT_RU_ENTITYLINK(REF_SCOPE_ID_, REF_SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_ENT_LNK_ROOT_SCOPE on ACT_RU_ENTITYLINK(ROOT_SCOPE_ID_, ROOT_SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_ENT_LNK_SCOPE_DEF on ACT_RU_ENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_, LINK_TYPE_); + +insert into ACT_GE_PROPERTY values ('entitylink.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql new file mode 100644 index 0000000..a908877 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql @@ -0,0 +1,4 @@ +drop index ACT_IDX_HI_ENT_LNK_SCOPE; +drop index ACT_IDX_HI_ENT_LNK_SCOPE_DEF; + +drop table ACT_HI_ENTITYLINK; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql new file mode 100644 index 0000000..aedbacd --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql @@ -0,0 +1,4 @@ +drop index ACT_IDX_ENT_LNK_SCOPE; +drop index ACT_IDX_ENT_LNK_SCOPE_DEF; + +drop table ACT_RU_ENTITYLINK; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql b/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql new file mode 100644 index 0000000..eb22164 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql @@ -0,0 +1,28 @@ +create table ACT_RU_EVENT_SUBSCR ( + ID_ VARCHAR2(64) not null, + REV_ integer, + EVENT_TYPE_ VARCHAR2(255) not null, + EVENT_NAME_ VARCHAR2(255), + EXECUTION_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + ACTIVITY_ID_ VARCHAR2(64), + CONFIGURATION_ VARCHAR2(255), + CREATED_ TIMESTAMP(6) not null, + PROC_DEF_ID_ VARCHAR2(64), + SUB_SCOPE_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(64), + SCOPE_DEFINITION_ID_ VARCHAR2(64), + SCOPE_DEFINITION_KEY_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(64), + LOCK_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create index ACT_IDX_EVENT_SUBSCR_CONFIG_ on ACT_RU_EVENT_SUBSCR(CONFIGURATION_); +create index ACT_IDX_EVENT_SUBSCR on ACT_RU_EVENT_SUBSCR(EXECUTION_ID_); +create index ACT_IDX_EVENT_SUBSCR_SCOPEREF_ on ACT_RU_EVENT_SUBSCR(SCOPE_ID_, SCOPE_TYPE_); + +insert into ACT_GE_PROPERTY values ('eventsubscription.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql b/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql new file mode 100644 index 0000000..c85ad74 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql @@ -0,0 +1,5 @@ +drop index ACT_IDX_EVENT_SUBSCR_CONFIG_; +drop index ACT_IDX_EVENT_SUBSCR; +drop index ACT_IDX_EVENT_SUBSCR_SCOPEREF_; + +drop table ACT_RU_EVENT_SUBSCR; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql new file mode 100644 index 0000000..2305f0a --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql @@ -0,0 +1,20 @@ +create table ACT_HI_IDENTITYLINK ( + ID_ VARCHAR2(64), + GROUP_ID_ VARCHAR2(255), + TYPE_ VARCHAR2(255), + USER_ID_ VARCHAR2(255), + TASK_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + PROC_INST_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + primary key (ID_) +); + +create index ACT_IDX_HI_IDENT_LNK_USER on ACT_HI_IDENTITYLINK(USER_ID_); +create index ACT_IDX_HI_IDENT_LNK_SCOPE on ACT_HI_IDENTITYLINK(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_HI_IDENT_LNK_SUB_SCOPE on ACT_HI_IDENTITYLINK(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_HI_IDENT_LNK_SCOPE_DEF on ACT_HI_IDENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql new file mode 100644 index 0000000..e290879 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql @@ -0,0 +1,24 @@ +create table ACT_RU_IDENTITYLINK ( + ID_ VARCHAR2(64), + REV_ INTEGER, + GROUP_ID_ VARCHAR2(255), + TYPE_ VARCHAR2(255), + USER_ID_ VARCHAR2(255), + TASK_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + primary key (ID_) +); + +create index ACT_IDX_IDENT_LNK_USER on ACT_RU_IDENTITYLINK(USER_ID_); +create index ACT_IDX_IDENT_LNK_GROUP on ACT_RU_IDENTITYLINK(GROUP_ID_); +create index ACT_IDX_IDENT_LNK_SCOPE on ACT_RU_IDENTITYLINK(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_IDENT_LNK_SUB_SCOPE on ACT_RU_IDENTITYLINK(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_IDENT_LNK_SCOPE_DEF on ACT_RU_IDENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +insert into ACT_GE_PROPERTY values ('identitylink.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql new file mode 100644 index 0000000..7cff665 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql @@ -0,0 +1,6 @@ +drop index ACT_IDX_HI_IDENT_LNK_USER; +drop index ACT_IDX_HI_IDENT_LNK_SCOPE; +drop index ACT_IDX_HI_IDENT_LNK_SUB_SCOPE; +drop index ACT_IDX_HI_IDENT_LNK_SCOPE_DEF; + +drop table ACT_HI_IDENTITYLINK; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql new file mode 100644 index 0000000..485344a --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql @@ -0,0 +1,7 @@ +drop index ACT_IDX_IDENT_LNK_USER; +drop index ACT_IDX_IDENT_LNK_GROUP; +drop index ACT_IDX_IDENT_LNK_SCOPE; +drop index ACT_IDX_IDENT_LNK_SUB_SCOPE; +drop index ACT_IDX_IDENT_LNK_SCOPE_DEF; + +drop table ACT_RU_IDENTITYLINK; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql b/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql new file mode 100644 index 0000000..562f45e --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql @@ -0,0 +1,108 @@ +create table ACT_ID_PROPERTY ( + NAME_ VARCHAR2(64), + VALUE_ VARCHAR2(300), + REV_ INTEGER, + primary key (NAME_) +); + +insert into ACT_ID_PROPERTY +values ('schema.version', '7.0.1.1', 1); + +create table ACT_ID_BYTEARRAY ( + ID_ VARCHAR2(64), + REV_ INTEGER, + NAME_ VARCHAR2(255), + BYTES_ BLOB, + primary key (ID_) +); + +create table ACT_ID_GROUP ( + ID_ VARCHAR2(64), + REV_ INTEGER, + NAME_ VARCHAR2(255), + TYPE_ VARCHAR2(255), + primary key (ID_) +); + +create table ACT_ID_MEMBERSHIP ( + USER_ID_ VARCHAR2(64), + GROUP_ID_ VARCHAR2(64), + primary key (USER_ID_, GROUP_ID_) +); + +create table ACT_ID_USER ( + ID_ VARCHAR2(64), + REV_ INTEGER, + FIRST_ VARCHAR2(255), + LAST_ VARCHAR2(255), + DISPLAY_NAME_ VARCHAR2(255), + EMAIL_ VARCHAR2(255), + PWD_ VARCHAR2(255), + PICTURE_ID_ VARCHAR2(64), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create table ACT_ID_INFO ( + ID_ VARCHAR2(64), + REV_ INTEGER, + USER_ID_ VARCHAR2(64), + TYPE_ VARCHAR2(64), + KEY_ VARCHAR2(255), + VALUE_ VARCHAR2(255), + PASSWORD_ BLOB, + PARENT_ID_ VARCHAR2(255), + primary key (ID_) +); + +create table ACT_ID_TOKEN ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + TOKEN_VALUE_ VARCHAR2(255), + TOKEN_DATE_ TIMESTAMP(6), + IP_ADDRESS_ VARCHAR2(255), + USER_AGENT_ VARCHAR2(255), + USER_ID_ VARCHAR2(255), + TOKEN_DATA_ VARCHAR2(2000), + primary key (ID_) +); + +create table ACT_ID_PRIV ( + ID_ VARCHAR2(64) not null, + NAME_ VARCHAR2(255) not null, + primary key (ID_) +); + +create table ACT_ID_PRIV_MAPPING ( + ID_ VARCHAR2(64) not null, + PRIV_ID_ VARCHAR2(64) not null, + USER_ID_ VARCHAR2(255), + GROUP_ID_ VARCHAR2(255), + primary key (ID_) +); + +create index ACT_IDX_MEMB_GROUP on ACT_ID_MEMBERSHIP(GROUP_ID_); +alter table ACT_ID_MEMBERSHIP + add constraint ACT_FK_MEMB_GROUP + foreign key (GROUP_ID_) + references ACT_ID_GROUP (ID_); + +create index ACT_IDX_MEMB_USER on ACT_ID_MEMBERSHIP(USER_ID_); +alter table ACT_ID_MEMBERSHIP + add constraint ACT_FK_MEMB_USER + foreign key (USER_ID_) + references ACT_ID_USER (ID_); + +create index ACT_IDX_PRIV_MAPPING on ACT_ID_PRIV_MAPPING(PRIV_ID_); +alter table ACT_ID_PRIV_MAPPING + add constraint ACT_FK_PRIV_MAPPING + foreign key (PRIV_ID_) + references ACT_ID_PRIV (ID_); + +create index ACT_IDX_PRIV_USER on ACT_ID_PRIV_MAPPING(USER_ID_); +create index ACT_IDX_PRIV_GROUP on ACT_ID_PRIV_MAPPING(GROUP_ID_); + +alter table ACT_ID_PRIV + add constraint ACT_UNIQ_PRIV_NAME + unique (NAME_); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql b/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql new file mode 100644 index 0000000..5cac418 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql @@ -0,0 +1,22 @@ +alter table ACT_ID_MEMBERSHIP + drop CONSTRAINT ACT_FK_MEMB_GROUP; + +alter table ACT_ID_MEMBERSHIP + drop CONSTRAINT ACT_FK_MEMB_USER; + +alter table ACT_ID_PRIV_MAPPING + drop CONSTRAINT ACT_FK_PRIV_MAPPING; + +drop index ACT_IDX_MEMB_GROUP; +drop index ACT_IDX_MEMB_USER; +drop index ACT_IDX_PRIV_MAPPING; + +drop table ACT_ID_PROPERTY; +drop table ACT_ID_BYTEARRAY; +drop table ACT_ID_INFO; +drop table ACT_ID_MEMBERSHIP; +drop table ACT_ID_GROUP; +drop table ACT_ID_USER; +drop table ACT_ID_TOKEN; +drop table ACT_ID_PRIV; +drop table ACT_ID_PRIV_MAPPING; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql b/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql new file mode 100644 index 0000000..8b3e79b --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql @@ -0,0 +1,261 @@ +create table ACT_RU_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + TYPE_ VARCHAR2(255) NOT NULL, + LOCK_EXP_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), + EXECUTION_ID_ VARCHAR2(64), + PROCESS_INSTANCE_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + ELEMENT_ID_ VARCHAR2(255), + ELEMENT_NAME_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + CORRELATION_ID_ VARCHAR2(255), + RETRIES_ INTEGER, + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + DUEDATE_ TIMESTAMP(6), + REPEAT_ VARCHAR2(255), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_TIMER_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + TYPE_ VARCHAR2(255) NOT NULL, + LOCK_EXP_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), + EXECUTION_ID_ VARCHAR2(64), + PROCESS_INSTANCE_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + ELEMENT_ID_ VARCHAR2(255), + ELEMENT_NAME_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + CORRELATION_ID_ VARCHAR2(255), + RETRIES_ INTEGER, + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + DUEDATE_ TIMESTAMP(6), + REPEAT_ VARCHAR2(255), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_SUSPENDED_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + TYPE_ VARCHAR2(255) NOT NULL, + EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), + EXECUTION_ID_ VARCHAR2(64), + PROCESS_INSTANCE_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + ELEMENT_ID_ VARCHAR2(255), + ELEMENT_NAME_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + CORRELATION_ID_ VARCHAR2(255), + RETRIES_ INTEGER, + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + DUEDATE_ TIMESTAMP(6), + REPEAT_ VARCHAR2(255), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_DEADLETTER_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + TYPE_ VARCHAR2(255) NOT NULL, + EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), + EXECUTION_ID_ VARCHAR2(64), + PROCESS_INSTANCE_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + ELEMENT_ID_ VARCHAR2(255), + ELEMENT_NAME_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + CORRELATION_ID_ VARCHAR2(255), + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + DUEDATE_ TIMESTAMP(6), + REPEAT_ VARCHAR2(255), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_HISTORY_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + LOCK_EXP_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + RETRIES_ INTEGER, + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + ADV_HANDLER_CFG_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + SCOPE_TYPE_ VARCHAR2(255), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_EXTERNAL_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + TYPE_ VARCHAR2(255) NOT NULL, + LOCK_EXP_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), + EXECUTION_ID_ VARCHAR2(64), + PROCESS_INSTANCE_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + ELEMENT_ID_ VARCHAR2(255), + ELEMENT_NAME_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + CORRELATION_ID_ VARCHAR2(255), + RETRIES_ INTEGER, + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + DUEDATE_ TIMESTAMP(6), + REPEAT_ VARCHAR2(255), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create index ACT_IDX_JOB_EXCEPTION on ACT_RU_JOB(EXCEPTION_STACK_ID_); +create index ACT_IDX_JOB_CUSTOM_VAL_ID on ACT_RU_JOB(CUSTOM_VALUES_ID_); +create index ACT_IDX_JOB_CORRELATION_ID on ACT_RU_JOB(CORRELATION_ID_); + +create index ACT_IDX_TJOB_EXCEPTION on ACT_RU_TIMER_JOB(EXCEPTION_STACK_ID_); +create index ACT_IDX_TJOB_CUSTOM_VAL_ID on ACT_RU_TIMER_JOB(CUSTOM_VALUES_ID_); +create index ACT_IDX_TJOB_CORRELATION_ID on ACT_RU_TIMER_JOB(CORRELATION_ID_); +create index ACT_IDX_TJOB_DUEDATE on ACT_RU_TIMER_JOB(DUEDATE_); + +create index ACT_IDX_SJOB_EXCEPTION on ACT_RU_SUSPENDED_JOB(EXCEPTION_STACK_ID_); +create index ACT_IDX_SJOB_CUSTOM_VAL_ID on ACT_RU_SUSPENDED_JOB(CUSTOM_VALUES_ID_); +create index ACT_IDX_SJOB_CORRELATION_ID on ACT_RU_SUSPENDED_JOB(CORRELATION_ID_); + +create index ACT_IDX_DJOB_EXCEPTION on ACT_RU_DEADLETTER_JOB(EXCEPTION_STACK_ID_); +create index ACT_IDX_DJOB_CUSTOM_VAL_ID on ACT_RU_DEADLETTER_JOB(CUSTOM_VALUES_ID_); +create index ACT_IDX_DJOB_CORRELATION_ID on ACT_RU_DEADLETTER_JOB(CORRELATION_ID_); + +create index ACT_IDX_EJOB_EXCEPTION on ACT_RU_EXTERNAL_JOB(EXCEPTION_STACK_ID_); +create index ACT_IDX_EJOB_CUSTOM_VAL_ID on ACT_RU_EXTERNAL_JOB(CUSTOM_VALUES_ID_); +create index ACT_IDX_EJOB_CORRELATION_ID on ACT_RU_EXTERNAL_JOB(CORRELATION_ID_); + +alter table ACT_RU_JOB + add constraint ACT_FK_JOB_EXCEPTION + foreign key (EXCEPTION_STACK_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_JOB + add constraint ACT_FK_JOB_CUSTOM_VAL + foreign key (CUSTOM_VALUES_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_TIMER_JOB + add constraint ACT_FK_TJOB_EXCEPTION + foreign key (EXCEPTION_STACK_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_TIMER_JOB + add constraint ACT_FK_TJOB_CUSTOM_VAL + foreign key (CUSTOM_VALUES_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_SUSPENDED_JOB + add constraint ACT_FK_SJOB_EXCEPTION + foreign key (EXCEPTION_STACK_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_SUSPENDED_JOB + add constraint ACT_FK_SJOB_CUSTOM_VAL + foreign key (CUSTOM_VALUES_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_DEADLETTER_JOB + add constraint ACT_FK_DJOB_EXCEPTION + foreign key (EXCEPTION_STACK_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_DEADLETTER_JOB + add constraint ACT_FK_DJOB_CUSTOM_VAL + foreign key (CUSTOM_VALUES_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_EXTERNAL_JOB + add constraint ACT_FK_EJOB_EXCEPTION + foreign key (EXCEPTION_STACK_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_EXTERNAL_JOB + add constraint ACT_FK_EJOB_CUSTOM_VAL + foreign key (CUSTOM_VALUES_ID_) + references ACT_GE_BYTEARRAY (ID_); + +create index ACT_IDX_JOB_SCOPE on ACT_RU_JOB(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_JOB_SUB_SCOPE on ACT_RU_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_JOB_SCOPE_DEF on ACT_RU_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +create index ACT_IDX_TJOB_SCOPE on ACT_RU_TIMER_JOB(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_TJOB_SUB_SCOPE on ACT_RU_TIMER_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_TJOB_SCOPE_DEF on ACT_RU_TIMER_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +create index ACT_IDX_SJOB_SCOPE on ACT_RU_SUSPENDED_JOB(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_SJOB_SUB_SCOPE on ACT_RU_SUSPENDED_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_SJOB_SCOPE_DEF on ACT_RU_SUSPENDED_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +create index ACT_IDX_DJOB_SCOPE on ACT_RU_DEADLETTER_JOB(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_DJOB_SUB_SCOPE on ACT_RU_DEADLETTER_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_DJOB_SCOPE_DEF on ACT_RU_DEADLETTER_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +create index ACT_IDX_EJOB_SCOPE on ACT_RU_EXTERNAL_JOB(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_EJOB_SUB_SCOPE on ACT_RU_EXTERNAL_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_EJOB_SCOPE_DEF on ACT_RU_EXTERNAL_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +insert into ACT_GE_PROPERTY values ('job.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql b/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql new file mode 100644 index 0000000..a219e97 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql @@ -0,0 +1,74 @@ +drop index ACT_IDX_JOB_SCOPE; +drop index ACT_IDX_JOB_SUB_SCOPE; +drop index ACT_IDX_JOB_SCOPE_DEF; +drop index ACT_IDX_TJOB_SCOPE; +drop index ACT_IDX_TJOB_SUB_SCOPE; +drop index ACT_IDX_TJOB_SCOPE_DEF; +drop index ACT_IDX_SJOB_SCOPE; +drop index ACT_IDX_SJOB_SUB_SCOPE; +drop index ACT_IDX_SJOB_SCOPE_DEF; +drop index ACT_IDX_DJOB_SCOPE; +drop index ACT_IDX_DJOB_SUB_SCOPE; +drop index ACT_IDX_DJOB_SCOPE_DEF; +drop index ACT_IDX_EJOB_SCOPE; +drop index ACT_IDX_EJOB_SUB_SCOPE; +drop index ACT_IDX_EJOB_SCOPE_DEF; + +drop index ACT_IDX_JOB_EXCEPTION; +drop index ACT_IDX_JOB_CUSTOM_VAL_ID; +drop index ACT_IDX_JOB_CORRELATION_ID; + +drop index ACT_IDX_TJOB_EXCEPTION; +drop index ACT_IDX_TJOB_CUSTOM_VAL_ID; +drop index ACT_IDX_TJOB_CORRELATION_ID; +drop index ACT_IDX_TJOB_DUEDATE; + +drop index ACT_IDX_SJOB_EXCEPTION; +drop index ACT_IDX_SJOB_CUSTOM_VAL_ID; +drop index ACT_IDX_SJOB_CORRELATION_ID; + +drop index ACT_IDX_DJOB_EXCEPTION; +drop index ACT_IDX_DJOB_CUSTOM_VAL_ID; +drop index ACT_IDX_DJOB_CORRELATION_ID; + +drop index ACT_IDX_EJOB_EXCEPTION; +drop index ACT_IDX_EJOB_CUSTOM_VAL_ID; +drop index ACT_IDX_EJOB_CORRELATION_ID; + +alter table ACT_RU_JOB + drop CONSTRAINT ACT_FK_JOB_EXCEPTION; + +alter table ACT_RU_JOB + drop CONSTRAINT ACT_FK_JOB_CUSTOM_VAL; + +alter table ACT_RU_TIMER_JOB + drop CONSTRAINT ACT_FK_TJOB_EXCEPTION; + +alter table ACT_RU_TIMER_JOB + drop CONSTRAINT ACT_FK_TJOB_CUSTOM_VAL; + +alter table ACT_RU_SUSPENDED_JOB + drop CONSTRAINT ACT_FK_SJOB_EXCEPTION; + +alter table ACT_RU_SUSPENDED_JOB + drop CONSTRAINT ACT_FK_SJOB_CUSTOM_VAL; + +alter table ACT_RU_DEADLETTER_JOB + drop CONSTRAINT ACT_FK_DJOB_EXCEPTION; + +alter table ACT_RU_DEADLETTER_JOB + drop CONSTRAINT ACT_FK_DJOB_CUSTOM_VAL; + +alter table ACT_RU_EXTERNAL_JOB + drop CONSTRAINT ACT_FK_DJOB_EXCEPTION; + +alter table ACT_RU_EXTERNAL_JOB + drop CONSTRAINT ACT_FK_DJOB_CUSTOM_VAL; + +drop table ACT_RU_JOB; +drop table ACT_RU_TIMER_JOB; +drop table ACT_RU_SUSPENDED_JOB; +drop table ACT_RU_DEADLETTER_JOB; +drop table ACT_RU_HISTORY_JOB; +drop table ACT_RU_EXTERNAL_JOB; + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql new file mode 100644 index 0000000..1651c0c --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql @@ -0,0 +1,64 @@ +create table ACT_HI_TASKINST ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER default 1, + PROC_DEF_ID_ VARCHAR2(64), + TASK_DEF_ID_ VARCHAR2(64), + TASK_DEF_KEY_ VARCHAR2(255), + PROC_INST_ID_ VARCHAR2(64), + EXECUTION_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), + PARENT_TASK_ID_ VARCHAR2(64), + STATE_ VARCHAR2(255), + NAME_ VARCHAR2(255), + DESCRIPTION_ VARCHAR2(2000), + OWNER_ VARCHAR2(255), + ASSIGNEE_ VARCHAR2(255), + START_TIME_ TIMESTAMP(6) not null, + IN_PROGRESS_TIME_ TIMESTAMP(6), + IN_PROGRESS_STARTED_BY_ VARCHAR2(255), + CLAIM_TIME_ TIMESTAMP(6), + CLAIMED_BY_ VARCHAR2(255), + SUSPENDED_TIME_ TIMESTAMP(6), + SUSPENDED_BY_ VARCHAR2(255), + END_TIME_ TIMESTAMP(6), + COMPLETED_BY_ VARCHAR2(255), + DURATION_ NUMBER(19,0), + DELETE_REASON_ VARCHAR2(2000), + PRIORITY_ INTEGER, + IN_PROGRESS_DUE_DATE_ TIMESTAMP(6), + DUE_DATE_ TIMESTAMP(6), + FORM_KEY_ VARCHAR2(255), + CATEGORY_ VARCHAR2(255), + TENANT_ID_ VARCHAR2(255) default '', + LAST_UPDATED_TIME_ TIMESTAMP(6), + primary key (ID_) +); + +create table ACT_HI_TSK_LOG ( + ID_ NUMBER(19), + TYPE_ VARCHAR2(64), + TASK_ID_ VARCHAR2(64) not null, + TIME_STAMP_ TIMESTAMP(6) not null, + USER_ID_ VARCHAR2(255), + DATA_ VARCHAR2(2000), + EXECUTION_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create sequence act_hi_task_evt_log_seq start with 1 increment by 1; + +create index ACT_IDX_HI_TASK_SCOPE on ACT_HI_TASKINST(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_HI_TASK_SUB_SCOPE on ACT_HI_TASKINST(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_HI_TASK_SCOPE_DEF on ACT_HI_TASKINST(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql new file mode 100644 index 0000000..9430a1c --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql @@ -0,0 +1,48 @@ +create table ACT_RU_TASK ( + ID_ VARCHAR2(64), + REV_ INTEGER, + EXECUTION_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + TASK_DEF_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), + STATE_ VARCHAR2(255), + NAME_ VARCHAR2(255), + PARENT_TASK_ID_ VARCHAR2(64), + DESCRIPTION_ VARCHAR2(2000), + TASK_DEF_KEY_ VARCHAR2(255), + OWNER_ VARCHAR2(255), + ASSIGNEE_ VARCHAR2(255), + DELEGATION_ VARCHAR2(64), + PRIORITY_ INTEGER, + CREATE_TIME_ TIMESTAMP(6), + IN_PROGRESS_TIME_ TIMESTAMP(6), + IN_PROGRESS_STARTED_BY_ VARCHAR2(255), + CLAIM_TIME_ TIMESTAMP(6), + CLAIMED_BY_ VARCHAR2(255), + SUSPENDED_TIME_ TIMESTAMP(6), + SUSPENDED_BY_ VARCHAR2(255), + IN_PROGRESS_DUE_DATE_ TIMESTAMP(6), + DUE_DATE_ TIMESTAMP(6), + CATEGORY_ VARCHAR2(255), + SUSPENSION_STATE_ INTEGER, + TENANT_ID_ VARCHAR2(255) DEFAULT '', + FORM_KEY_ VARCHAR2(255), + IS_COUNT_ENABLED_ NUMBER(1) CHECK (IS_COUNT_ENABLED_ IN (1,0)), + VAR_COUNT_ INTEGER, + ID_LINK_COUNT_ INTEGER, + SUB_TASK_COUNT_ INTEGER, + primary key (ID_) +); + +create index ACT_IDX_TASK_CREATE on ACT_RU_TASK(CREATE_TIME_); +create index ACT_IDX_TASK_SCOPE on ACT_RU_TASK(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_TASK_SUB_SCOPE on ACT_RU_TASK(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_TASK_SCOPE_DEF on ACT_RU_TASK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +insert into ACT_GE_PROPERTY values ('task.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql new file mode 100644 index 0000000..c5ce7bb --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql @@ -0,0 +1,8 @@ +drop index ACT_IDX_HI_TASK_SCOPE; +drop index ACT_IDX_HI_TASK_SUB_SCOPE; +drop index ACT_IDX_HI_TASK_SCOPE_DEF; + +drop sequence act_hi_task_evt_log_seq; + +drop table ACT_HI_TASKINST; +drop table ACT_HI_TSK_LOG; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql new file mode 100644 index 0000000..9ecd1e9 --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql @@ -0,0 +1,6 @@ +drop index ACT_IDX_TASK_CREATE; +drop index ACT_IDX_TASK_SCOPE; +drop index ACT_IDX_TASK_SUB_SCOPE; +drop index ACT_IDX_TASK_SCOPE_DEF; + +drop table ACT_RU_TASK; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql new file mode 100644 index 0000000..3d9b50f --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql @@ -0,0 +1,26 @@ +create table ACT_HI_VARINST ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER default 1, + PROC_INST_ID_ VARCHAR2(64), + EXECUTION_ID_ VARCHAR2(64), + TASK_ID_ VARCHAR2(64), + NAME_ VARCHAR2(255) not null, + VAR_TYPE_ VARCHAR2(100), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + BYTEARRAY_ID_ VARCHAR2(64), + DOUBLE_ NUMBER(38,10), + LONG_ NUMBER(19,0), + TEXT_ VARCHAR2(2000), + TEXT2_ VARCHAR2(2000), + META_INFO_ VARCHAR2(2000), + CREATE_TIME_ TIMESTAMP(6), + LAST_UPDATED_TIME_ TIMESTAMP(6), + primary key (ID_) +); + +create index ACT_IDX_HI_PROCVAR_NAME_TYPE on ACT_HI_VARINST(NAME_, VAR_TYPE_); +create index ACT_IDX_HI_VAR_SCOPE_ID_TYPE on ACT_HI_VARINST(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_HI_VAR_SUB_ID_TYPE on ACT_HI_VARINST(SUB_SCOPE_ID_, SCOPE_TYPE_); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql new file mode 100644 index 0000000..7c02f7f --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql @@ -0,0 +1,31 @@ +create table ACT_RU_VARIABLE ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + TYPE_ VARCHAR2(255) not null, + NAME_ VARCHAR2(255) not null, + EXECUTION_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + TASK_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + BYTEARRAY_ID_ VARCHAR2(64), + DOUBLE_ NUMBER(38,10), + LONG_ NUMBER(19,0), + TEXT_ VARCHAR2(2000), + TEXT2_ VARCHAR2(2000), + META_INFO_ VARCHAR2(2000), + primary key (ID_) +); + +create index ACT_IDX_RU_VAR_SCOPE_ID_TYPE on ACT_RU_VARIABLE(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_RU_VAR_SUB_ID_TYPE on ACT_RU_VARIABLE(SUB_SCOPE_ID_, SCOPE_TYPE_); + +create index ACT_IDX_VAR_BYTEARRAY on ACT_RU_VARIABLE(BYTEARRAY_ID_); +alter table ACT_RU_VARIABLE + add constraint ACT_FK_VAR_BYTEARRAY + foreign key (BYTEARRAY_ID_) + references ACT_GE_BYTEARRAY (ID_); + +insert into ACT_GE_PROPERTY values ('variable.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql new file mode 100644 index 0000000..efcc68d --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql @@ -0,0 +1,6 @@ +drop index ACT_IDX_HI_PROCVAR_NAME_TYPE; +drop index ACT_IDX_HI_VAR_SCOPE_ID_TYPE; +drop index ACT_IDX_HI_VAR_SUB_ID_TYPE; + +drop table ACT_HI_VARINST; + diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql new file mode 100644 index 0000000..0d0a95f --- /dev/null +++ b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql @@ -0,0 +1,9 @@ +drop index ACT_IDX_VAR_BYTEARRAY; +drop index ACT_IDX_RU_VAR_SCOPE_ID_TYPE; +drop index ACT_IDX_RU_VAR_SUB_ID_TYPE; + +alter table ACT_RU_VARIABLE + drop CONSTRAINT ACT_FK_VAR_BYTEARRAY; + +drop table ACT_RU_VARIABLE; + From e909649eac4a244011d94ce2b4ee74c6f776f003 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 27 Nov 2025 11:16:49 +0800 Subject: [PATCH 22/35] =?UTF-8?q?=E7=A6=81=E6=AD=A2=E4=BA=8B=E4=BB=B6?= =?UTF-8?q?=E5=BC=95=E6=93=8E=E9=87=8D=E5=A4=8D=E8=87=AA=E5=8A=A8=E5=BB=BA?= =?UTF-8?q?=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/src/main/resources/application.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/zt-module-bpm-server/src/main/resources/application.yaml b/zt-module-bpm-server/src/main/resources/application.yaml index adfbf58..37f2d24 100644 --- a/zt-module-bpm-server/src/main/resources/application.yaml +++ b/zt-module-bpm-server/src/main/resources/application.yaml @@ -82,6 +82,9 @@ flowable: db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置 check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程 history-level: audit # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数 + eventregistry: + enabled: true # 默认开启事件引擎,这里显式声明,便于阅读 + database-schema-update: false # 禁止事件引擎重复自动建表,防止 FLW_EV_* 表冲突 # MyBatis Plus 的配置项 mybatis-plus: From 224a83f7e747930d0825b61e54a3656f41f233dc Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 27 Nov 2025 13:26:30 +0800 Subject: [PATCH 23/35] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20dm=20jdbc=20?= =?UTF-8?q?=E4=B8=8D=E5=85=BC=E5=AE=B9=20flowable=20=E8=BD=AC=E4=B9=89=20s?= =?UTF-8?q?ql=20=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snapshot/JdbcDatabaseSnapshot.java | 1957 +++++++++++++++++ .../src/main/resources/application.yaml | 3 - 2 files changed, 1957 insertions(+), 3 deletions(-) create mode 100644 zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java diff --git a/zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java b/zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java new file mode 100644 index 0000000..1e0a40e --- /dev/null +++ b/zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java @@ -0,0 +1,1957 @@ +package liquibase.snapshot; + +import liquibase.CatalogAndSchema; +import liquibase.Scope; +import liquibase.database.AbstractJdbcDatabase; +import liquibase.database.Database; +import liquibase.database.DatabaseConnection; +import liquibase.database.LiquibaseTableNamesFactory; +import liquibase.database.core.*; +import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.DatabaseException; +import liquibase.executor.jvm.ColumnMapRowMapper; +import liquibase.executor.jvm.RowMapperNotNullConstraintsResultSetExtractor; +import liquibase.structure.DatabaseObject; +import liquibase.structure.core.Catalog; +import liquibase.structure.core.Schema; +import liquibase.structure.core.Table; +import liquibase.structure.core.View; +import liquibase.util.JdbcUtil; +import liquibase.util.StringUtil; + +import java.sql.*; +import java.util.*; + +public class JdbcDatabaseSnapshot extends DatabaseSnapshot { + + private boolean warnedAboutDbaRecycleBin; + private static final boolean ignoreWarnAboutDbaRecycleBin = Boolean.getBoolean("liquibase.ignoreRecycleBinWarning"); + + private CachingDatabaseMetaData cachingDatabaseMetaData; + + private Map cachedExpressionMap = null; + + private Set userDefinedTypes; + + public JdbcDatabaseSnapshot(DatabaseObject[] examples, Database database, SnapshotControl snapshotControl) throws DatabaseException, InvalidExampleException { + super(examples, database, snapshotControl); + } + + public JdbcDatabaseSnapshot(DatabaseObject[] examples, Database database) throws DatabaseException, InvalidExampleException { + super(examples, database); + } + + public CachingDatabaseMetaData getMetaDataFromCache() throws SQLException { + if (cachingDatabaseMetaData == null) { + DatabaseMetaData databaseMetaData = null; + if (getDatabase().getConnection() != null) { + databaseMetaData = ((JdbcConnection) getDatabase().getConnection()).getUnderlyingConnection().getMetaData(); + } + + cachingDatabaseMetaData = new CachingDatabaseMetaData(this.getDatabase(), databaseMetaData); + } + return cachingDatabaseMetaData; + } + + public class CachingDatabaseMetaData { + private static final String SQL_FILTER_MATCH_ALL = "%"; + private final DatabaseMetaData databaseMetaData; + private final Database database; + + public CachingDatabaseMetaData(Database database, DatabaseMetaData metaData) { + this.databaseMetaData = metaData; + this.database = database; + } + + public java.sql.DatabaseMetaData getDatabaseMetaData() { + return databaseMetaData; + } + + public List getForeignKeys(final String catalogName, final String schemaName, final String tableName, + final String fkName) throws DatabaseException { + ForeignKeysResultSetCache foreignKeysResultSetCache = new ForeignKeysResultSetCache(database, catalogName, schemaName, tableName, fkName); + ResultSetCache importedKeys = getResultSetCache("getImportedKeys"); + importedKeys.setBulkTracking(!(database instanceof MSSQLDatabase)); + + return importedKeys.get(foreignKeysResultSetCache); + } + + public List getIndexInfo(final String catalogName, final String schemaName, final String tableName, final String indexName) throws DatabaseException, SQLException { + + return getResultSetCache("getIndexInfo").get(new ResultSetCache.UnionResultSetExtractor(database) { + + public boolean isBulkFetchMode; + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME"), row.getString("INDEX_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, tableName, indexName); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return getAllCatalogsStringScratchData() != null && database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEM"); + } + + @Override + public List fastFetch() throws SQLException, DatabaseException { + List returnList = new ArrayList<>(); + + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + if (database instanceof OracleDatabase) { + warnAboutDbaRecycleBin(); + + //oracle getIndexInfo is buggy and slow. See Issue 1824548 and http://forums.oracle.com/forums/thread.jspa?messageID=578383򍍏 + String sql = + "SELECT " + + "c.INDEX_NAME, " + + "3 AS TYPE, " + + "c.TABLE_OWNER AS TABLE_SCHEM, " + + "c.TABLE_NAME, " + + "c.COLUMN_NAME, " + + "c.COLUMN_POSITION AS ORDINAL_POSITION, " + + "NULL AS FILTER_CONDITION, " + + "c.INDEX_OWNER, " + + "CASE I.UNIQUENESS WHEN 'UNIQUE' THEN 0 ELSE 1 END AS NON_UNIQUE, " + + "CASE c.DESCEND WHEN 'Y' THEN 'D' WHEN 'DESC' THEN 'D' WHEN 'N' THEN 'A' WHEN 'ASC' THEN 'A' END AS ASC_OR_DESC, " + + "CASE WHEN tablespace_name = (SELECT default_tablespace FROM user_users) " + + "THEN NULL ELSE tablespace_name END AS tablespace_name " + + "FROM ALL_IND_COLUMNS c " + + "JOIN ALL_INDEXES i ON i.owner=c.index_owner AND i.index_name = c.index_name and i.table_owner = c.table_owner " + + "LEFT OUTER JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=c.table_name "; + if (!isBulkFetchMode || getAllCatalogsStringScratchData() == null) { + sql += "WHERE c.TABLE_OWNER = '" + database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class) + "' "; + } else { + sql += "WHERE c.TABLE_OWNER IN ('" + database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class) + "', " + getAllCatalogsStringScratchData() + ")"; + } + sql += "AND i.OWNER = c.TABLE_OWNER " + + "AND d.object_name IS NULL "; + + + if (!isBulkFetchMode && (tableName != null)) { + sql += " AND c.TABLE_NAME='" + tableName + "'"; + } + + if (!isBulkFetchMode && (indexName != null)) { + sql += " AND c.INDEX_NAME='" + indexName + "'"; + } + + sql += " ORDER BY c.INDEX_NAME, ORDINAL_POSITION"; + + returnList.addAll(setIndexExpressions(executeAndExtract(sql, database))); + } else if (database instanceof MSSQLDatabase) { + String tableCat = "original_db_name()"; + + if (9 <= database.getDatabaseMajorVersion()) { + tableCat = "db_name()"; + } + //fetch additional index info + String sql = "SELECT " + + tableCat + " as TABLE_CAT, " + + "object_schema_name(i.object_id) as TABLE_SCHEM, " + + "object_name(i.object_id) as TABLE_NAME, " + + "CASE is_unique WHEN 1 then 0 else 1 end as NON_UNIQUE, " + + "object_name(i.object_id) as INDEX_QUALIFIER, " + + "i.name as INDEX_NAME, " + + "case i.type when 1 then 1 ELSE 3 end as TYPE, " + + "key_ordinal as ORDINAL_POSITION, " + + "COL_NAME(c.object_id,c.column_id) AS COLUMN_NAME, " + + "case is_descending_key when 0 then 'A' else 'D' end as ASC_OR_DESC, " + + "null as CARDINALITY, " + + "null as PAGES, " + + "i.filter_definition as FILTER_CONDITION, " + + "o.type AS INTERNAL_OBJECT_TYPE, " + + "i.*, " + + "c.*, " + + "s.* " + + "FROM sys.indexes i " + + "join sys.index_columns c on i.object_id=c.object_id and i.index_id=c.index_id " + + "join sys.stats s on i.object_id=s.object_id and i.name=s.name " + + "join sys.objects o on i.object_id=o.object_id " + + "WHERE object_schema_name(i.object_id)='" + database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class) + "'"; + + if (!isBulkFetchMode && (tableName != null)) { + sql += " AND object_name(i.object_id)='" + database.escapeStringForDatabase(tableName) + "'"; + } + + if (!isBulkFetchMode && (indexName != null)) { + sql += " AND i.name='" + database.escapeStringForDatabase(indexName) + "'"; + } + + sql += "ORDER BY i.object_id, i.index_id, c.key_ordinal"; + + returnList.addAll(executeAndExtract(sql, database)); + + } else if (database instanceof Db2zDatabase) { + List parameters = new ArrayList<>(3); + String sql = "SELECT i.CREATOR AS TABLE_SCHEM, " + + "i.TBNAME AS TABLE_NAME, " + + "i.NAME AS INDEX_NAME, " + + "3 AS TYPE, " + + "k.COLNAME AS COLUMN_NAME, " + + "k.COLSEQ AS ORDINAL_POSITION, " + + "CASE UNIQUERULE WHEN 'D' then 1 else 0 end as NON_UNIQUE, " + + "k.ORDERING AS ORDER, " + + "i.CREATOR AS INDEX_QUALIFIER " + + "FROM SYSIBM.SYSKEYS k " + + "JOIN SYSIBM.SYSINDEXES i " + + "ON k.IXNAME = i.NAME " + + "AND k.IXCREATOR = i.CREATOR " + + "WHERE i.CREATOR = ?"; + parameters.add(database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class)); + if (!isBulkFetchMode && tableName != null) { + sql += " AND i.TBNAME = ?"; + parameters.add(database.escapeStringForDatabase(tableName)); + } + + if (!isBulkFetchMode && indexName != null) { + sql += " AND i.NAME = ?"; + parameters.add(database.escapeStringForDatabase(indexName)); + } + + sql += "ORDER BY i.NAME, k.COLSEQ"; + + returnList.addAll(executeAndExtract(database, sql, parameters.toArray())); + } else if (!(database instanceof MariaDBDatabase) && database instanceof MySQLDatabase) { + + //mysql 8.0.13 introduced support for indexes on `lower(first_name)` which comes back in an "expression" column + String filterConditionValue = "NULL"; + if (database.getDatabaseMajorVersion() > 8 || (database.getDatabaseMajorVersion() == 8 && ((MySQLDatabase) database).getDatabasePatchVersion() >= 13)) { + filterConditionValue = "EXPRESSION"; + } + + StringBuilder sql = new StringBuilder("SELECT TABLE_CATALOG AS TABLE_CAT, TABLE_SCHEMA AS TABLE_SCHEM,"); + sql.append(" TABLE_NAME, NON_UNIQUE, NULL AS INDEX_QUALIFIER, INDEX_NAME,"); + sql.append(DatabaseMetaData.tableIndexOther); + sql.append(" AS TYPE, SEQ_IN_INDEX AS ORDINAL_POSITION, COLUMN_NAME,"); + sql.append("COLLATION AS ASC_OR_DESC, CARDINALITY, 0 AS PAGES, " + filterConditionValue + " AS FILTER_CONDITION FROM INFORMATION_SCHEMA.STATISTICS WHERE"); + sql.append(" TABLE_SCHEMA = '").append(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)).append("'"); + + if (!isBulkFetchMode && tableName != null) { + sql.append(" AND TABLE_NAME = '").append(database.escapeStringForDatabase(tableName)).append("'"); + } + + if (!isBulkFetchMode && indexName != null) { + sql.append(" AND INDEX_NAME='").append(database.escapeStringForDatabase(indexName)).append("'"); + } + + sql.append("ORDER BY NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX"); + + returnList.addAll(executeAndExtract(sql.toString(), database)); + } else { + /* + * If we do not know in which table to look for the index, things get a little bit ugly. + * First, we get a collection of all tables within the catalogAndSchema, then iterate through + * them until we (hopefully) find the index we are looking for. + */ + List tables = new ArrayList<>(); + if (tableName == null) { + // Build a list of all candidate tables in the catalog/schema that might contain the index + for (CachedRow row : getTables(((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), null)) { + tables.add(row.getString("TABLE_NAME")); + } + } else { + tables.add(tableName); + } + + // Iterate through all the candidate tables and try to find the index. + for (String tableName1 : tables) { + ResultSet rs = databaseMetaData.getIndexInfo( + ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), + tableName1, + false, + true); + List rows = extract(rs, (database instanceof InformixDatabase)); + returnList.addAll(rows); + } + } + + return returnList; + } + + private List setIndexExpressions(List c) throws DatabaseException, SQLException { + Map expressionMap = getCachedExpressionMap(); + c.forEach(row -> { + row.set("FILTER_CONDITION", null); + String key = row.getString("INDEX_OWNER") + "::" + row.getString("INDEX_NAME") + "::" + + row.getInt("ORDINAL_POSITION"); + CachedRow fromMap = expressionMap.get(key); + if (fromMap != null) { + row.set("FILTER_CONDITION", fromMap.get("COLUMN_EXPRESSION")); + } + }); + return c; + } + + private Map getCachedExpressionMap() throws DatabaseException, SQLException { + if (cachedExpressionMap != null) { + return cachedExpressionMap; + } + String expSql = "SELECT e.column_expression, e.index_owner, e.index_name, e.column_position FROM all_ind_expressions e"; + List ec = executeAndExtract(expSql, database); + cachedExpressionMap = new HashMap<>(); + ec.forEach(row -> { + String key = row.getString("INDEX_OWNER") + "::" + row.getString("INDEX_NAME") + "::" + + row.getInt("COLUMN_POSITION"); + cachedExpressionMap.put(key, row); + }); + return cachedExpressionMap; + } + + @Override + public List bulkFetch() throws SQLException, DatabaseException { + this.isBulkFetchMode = true; + return fastFetch(); + } + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + if (database instanceof OracleDatabase || database instanceof MSSQLDatabase) { + return JdbcDatabaseSnapshot.this.getAllCatalogsStringScratchData() != null || (tableName == null && indexName == null) || super.shouldBulkSelect(schemaKey, resultSetCache); + } + return false; + } + }); + } + + + protected void warnAboutDbaRecycleBin() { + if (!ignoreWarnAboutDbaRecycleBin && !warnedAboutDbaRecycleBin && !(((OracleDatabase) database).canAccessDbaRecycleBin())) { + Scope.getCurrentScope().getLog(getClass()).warning(((OracleDatabase) database).getDbaRecycleBinWarning()); + warnedAboutDbaRecycleBin = true; + } + } + + /** + * Return the columns for the given catalog, schema, table, and column. + */ + public List getColumns(final String catalogName, final String schemaName, final String tableName, final String columnName) throws SQLException, DatabaseException { + + if ((database instanceof MSSQLDatabase) && (userDefinedTypes == null)) { + userDefinedTypes = new HashSet<>(); + DatabaseConnection databaseConnection = database.getConnection(); + if (databaseConnection instanceof JdbcConnection) { + Statement stmt = null; + ResultSet resultSet = null; + try { + stmt = ((JdbcConnection) databaseConnection).getUnderlyingConnection().createStatement(); + resultSet = stmt.executeQuery("select name from " + (catalogName == null ? "" : "[" + catalogName + "].") + "sys.types where is_user_defined=1"); + while (resultSet.next()) { + userDefinedTypes.add(resultSet.getString("name").toLowerCase()); + } + } finally { + JdbcUtil.close(resultSet, stmt); + } + } + } + GetColumnResultSetCache getColumnResultSetCache = new GetColumnResultSetCache(database, catalogName, + schemaName, tableName, columnName); + return getResultSetCache("getColumns").get(getColumnResultSetCache); + } + + /** + * Return the NotNullConstraints for the given catalog, schema, table, and column. + */ + public List getNotNullConst(final String catalogName, final String schemaName, + final String tableName) throws DatabaseException { + if (!(database instanceof OracleDatabase)) { + return Collections.emptyList(); + } + GetNotNullConstraintsResultSetCache getNotNullConstraintsResultSetCache = new GetNotNullConstraintsResultSetCache(database, catalogName, + schemaName, tableName); + return getResultSetCache("getNotNullConst").get(getNotNullConstraintsResultSetCache); + } + + private class GetColumnResultSetCache extends ResultSetCache.SingleResultSetExtractor { + final String catalogName; + final String schemaName; + final String tableName; + final String columnName; + + private GetColumnResultSetCache(Database database, String catalogName, String schemaName, String tableName, String columnName) { + super(database); + this.catalogName = catalogName; + this.schemaName = schemaName; + this.tableName = tableName; + this.columnName = columnName; + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME"), row.getString("COLUMN_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, tableName, columnName); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + String catalogs = getAllCatalogsStringScratchData(); + return catalogs != null && schemaKey != null + && catalogs.contains("'" + schemaKey.toUpperCase() + "'") + && database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEM"); + } + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + LiquibaseTableNamesFactory liquibaseTableNamesFactory = Scope.getCurrentScope().getSingleton(LiquibaseTableNamesFactory.class); + List liquibaseTableNames = liquibaseTableNamesFactory.getLiquibaseTableNames(database); + return liquibaseTableNames.stream().noneMatch(tableName::equalsIgnoreCase); + } + + @Override + public List fastFetchQuery() throws SQLException, DatabaseException { + if (database instanceof OracleDatabase) { + return oracleQuery(false); + } else if (database instanceof MSSQLDatabase) { + return mssqlQuery(false); + } + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + try { + List returnList = + extract( + databaseMetaData.getColumns( + ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), + escapeForLike(((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), database), + escapeForLike(tableName, database), + SQL_FILTER_MATCH_ALL) + ); + // + // IF MARIADB OR SQL ANYWHERE + // Query to get actual data types and then map each column to its CachedRow + // + determineActualDataTypes(returnList, tableName); + return returnList; + } catch (SQLException e) { + if (shouldReturnEmptyColumns(e)) { //view with table already dropped. Act like it has no columns. + return new ArrayList<>(); + } else { + throw e; + } + } + } + + @Override + public List bulkFetchQuery() throws SQLException, DatabaseException { + if (database instanceof OracleDatabase) { + return oracleQuery(true); + } else if (database instanceof MSSQLDatabase) { + return mssqlQuery(true); + } + + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + try { + List returnList = + extract(databaseMetaData.getColumns(((AbstractJdbcDatabase) database) + .getJdbcCatalogName(catalogAndSchema), + escapeForLike(((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), database), + SQL_FILTER_MATCH_ALL, SQL_FILTER_MATCH_ALL)); + // + // IF MARIADB OR SQL ANYWHERE + // Query to get actual data types and then map each column to its CachedRow + // + determineActualDataTypes(returnList, null); + return returnList; + } catch (SQLException e) { + if (shouldReturnEmptyColumns(e)) { + return new ArrayList<>(); + } else { + throw e; + } + } + } + + // + // For MariaDB, query for the data type column so that we can correctly + // set the DATETIME(6) type if specified + // + // For SQL Anywhere, query for the scale column so we can correctly + // set the size unit + // + private void determineActualDataTypes(List returnList, String tableName) throws SQLException { + // + // If not MariaDB / SQL Anywhere then just return + // + if (!(database instanceof MariaDBDatabase || database instanceof SybaseASADatabase)) { + return; + } + + if (database instanceof SybaseASADatabase) { + // + // Query for actual data type for column. The actual SYSTABCOL.scale column value is + // not reported by the DatabaseMetadata.getColumns() query for CHAR-limited (in contrast + // to BYTE-limited) columns, and it is needed to capture the kind if limitation. + // The actual SYSTABCOL.column_type is not reported by the DatabaseMetadata.getColumns() + // query as the IS_GENERATEDCOLUMN columns is missing in the result set, and it is needed to + // capture the kind of column (regular or computed). + // + // See https://help.sap.com/docs/SAP_SQL_Anywhere/93079d4ba8e44920ae63ffb4def91f5b/3beaa3956c5f1014883cb0c3e3559cc9.html. + // + String selectStatement = + "SELECT table_name, column_name, scale, column_type FROM SYSTABCOL KEY JOIN SYSTAB KEY JOIN SYSUSER " + + "WHERE user_name = ? AND ? IS NULL OR table_name = ?"; + Connection underlyingConnection = ((JdbcConnection) database.getConnection()).getUnderlyingConnection(); + try (PreparedStatement stmt = underlyingConnection.prepareStatement(selectStatement)) { + stmt.setString(1, schemaName); + stmt.setString(2, tableName); + stmt.setString(3, tableName); + try (ResultSet columnSelectRS = stmt.executeQuery()) { + while (columnSelectRS.next()) { + String selectedTableName = columnSelectRS.getString("table_name"); + String selectedColumnName = columnSelectRS.getString("column_name"); + int selectedScale = columnSelectRS.getInt("scale"); + String selectedColumnType = columnSelectRS.getString("column_type"); + for (CachedRow row : returnList) { + String rowTableName = row.getString("TABLE_NAME"); + String rowColumnName = row.getString("COLUMN_NAME"); + if (rowTableName.equalsIgnoreCase(selectedTableName) && + rowColumnName.equalsIgnoreCase(selectedColumnName)) { + int rowDataType = row.getInt("DATA_TYPE"); + if (rowDataType == Types.VARCHAR || rowDataType == Types.CHAR) { + row.set("scale", selectedScale); + } + row.set("IS_GENERATEDCOLUMN", "C".equals(selectedColumnType) ? "YES" : "NO"); + break; + } + } + } + } + } catch (SQLException sqle) { + throw new RuntimeException(sqle); + // + // Do not stop + // + } + return; + } + + // + // Query for actual data type for column. The actual DATA_TYPE column string is + // not returned by the DatabaseMetadata.getColumns() query, and it is needed + // to capture DATETIME() data types. + // + StringBuilder selectStatement = new StringBuilder( + "SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ?"); + if(tableName != null) { + selectStatement.append(" AND TABLE_NAME = ?"); + } + Connection underlyingConnection = ((JdbcConnection) database.getConnection()).getUnderlyingConnection(); + PreparedStatement statement = underlyingConnection.prepareStatement(selectStatement.toString()); + statement.setString(1, schemaName); + if (tableName != null) { + statement.setString(2, tableName); + } + try { + ResultSet columnSelectRS = statement.executeQuery(selectStatement.toString()); + // + // Iterate the result set from the query and match the rows + // to the rows that were returned by getColumns() in order + // to assign the actual DATA_TYPE string to the appropriate row. + // + while (columnSelectRS.next()) { + String selectedTableName = columnSelectRS.getString("TABLE_NAME"); + String selectedColumnName = columnSelectRS.getString("COLUMN_NAME"); + String actualDataType = columnSelectRS.getString("DATA_TYPE"); + for (CachedRow row : returnList) { + String rowTableName = row.getString("TABLE_NAME"); + String rowColumnName = row.getString("COLUMN_NAME"); + String rowTypeName = row.getString("TYPE_NAME"); + int rowDataType = row.getInt("DATA_TYPE"); + if (rowTableName.equalsIgnoreCase(selectedTableName) && + rowColumnName.equalsIgnoreCase(selectedColumnName) && + rowTypeName.equalsIgnoreCase("datetime") && + rowDataType == Types.OTHER && + !rowTypeName.equalsIgnoreCase(actualDataType)) { + row.set("TYPE_NAME", actualDataType); + row.set("DATA_TYPE", Types.TIMESTAMP); + break; + } + } + } + } catch (SQLException sqle) { + // + // Do not stop + // + } + finally { + JdbcUtil.closeStatement(statement); + } + } + + protected boolean shouldReturnEmptyColumns(SQLException e) { + return e.getMessage().contains("references invalid table"); //view with table already dropped. Act like it has no columns. + } + + protected List oracleQuery(boolean bulk) throws DatabaseException, SQLException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + boolean collectIdentityData = database.getDatabaseMajorVersion() >= OracleDatabase.ORACLE_12C_MAJOR_VERSION; + + String sql = "select NULL AS TABLE_CAT, OWNER AS TABLE_SCHEM, 'NO' as IS_AUTOINCREMENT, cc.COMMENTS AS REMARKS," + + "OWNER, TABLE_NAME, COLUMN_NAME, DATA_TYPE AS DATA_TYPE_NAME, DATA_TYPE_MOD, DATA_TYPE_OWNER, " + + // note: oracle reports DATA_LENGTH=4*CHAR_LENGTH when using VARCHAR( CHAR ), thus BYTEs + "DECODE (c.data_type, 'CHAR', 1, 'VARCHAR2', 12, 'NUMBER', 3, 'LONG', -1, 'DATE', " + "93" + ", 'RAW', -3, 'LONG RAW', -4, 'BLOB', 2004, 'CLOB', 2005, 'BFILE', -13, 'FLOAT', 6, 'TIMESTAMP(6)', 93, 'TIMESTAMP(6) WITH TIME ZONE', -101, 'TIMESTAMP(6) WITH LOCAL TIME ZONE', -102, 'INTERVAL YEAR(2) TO MONTH', -103, 'INTERVAL DAY(2) TO SECOND(6)', -104, 'BINARY_FLOAT', 100, 'BINARY_DOUBLE', 101, 'XMLTYPE', 2009, 1111) AS data_type, " + + "DECODE( CHAR_USED, 'C',CHAR_LENGTH, DATA_LENGTH ) as DATA_LENGTH, " + + "DATA_PRECISION, DATA_SCALE, NULLABLE, COLUMN_ID as ORDINAL_POSITION, DEFAULT_LENGTH, " + + "DATA_DEFAULT, " + + "NUM_BUCKETS, CHARACTER_SET_NAME, " + + "CHAR_COL_DECL_LENGTH, CHAR_LENGTH, " + + "CHAR_USED, VIRTUAL_COLUMN "; + if (collectIdentityData) { + sql += ", DEFAULT_ON_NULL, IDENTITY_COLUMN, ic.GENERATION_TYPE "; + } + sql += "FROM ALL_TAB_COLS c " + + "JOIN ALL_COL_COMMENTS cc USING ( OWNER, TABLE_NAME, COLUMN_NAME ) "; + if (collectIdentityData) { + sql += "LEFT JOIN ALL_TAB_IDENTITY_COLS ic USING (OWNER, TABLE_NAME, COLUMN_NAME ) "; + } + if (!bulk || getAllCatalogsStringScratchData() == null) { + sql += "WHERE OWNER='" + jdbcSchemaName + "' AND hidden_column='NO'"; + } else { + sql += "WHERE OWNER IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ") AND hidden_column='NO'"; + } + + if (!bulk) { + if (tableName != null) { + sql += " AND TABLE_NAME='" + database.escapeStringForDatabase(tableName) + "'"; + } + if (columnName != null) { + sql += " AND COLUMN_NAME='" + database.escapeStringForDatabase(columnName) + "'"; + } + } + sql += " AND " + ((OracleDatabase) database).getSystemTableWhereClause("TABLE_NAME"); + sql += " ORDER BY OWNER, TABLE_NAME, c.COLUMN_ID"; + + return this.executeAndExtract(sql, database); + } + + + protected List mssqlQuery(boolean bulk) throws DatabaseException, SQLException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + String databaseName = StringUtil.trimToNull(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)); + String dbIdParam; + String databasePrefix; + if (databaseName == null) { + databasePrefix = ""; + dbIdParam = ""; + } else { + dbIdParam = ", db_id('" + databaseName + "')"; + databasePrefix = "[" + databaseName + "]."; + } + + String sql = "select " + + "db_name(" + (databaseName == null ? "" : "db_id('" + databaseName + "')") + ") AS TABLE_CAT, " + + "object_schema_name(c.object_id" + dbIdParam + ") AS TABLE_SCHEM, " + + "object_name(c.object_id" + dbIdParam + ") AS TABLE_NAME, " + + "c.name AS COLUMN_NAME, " + + "is_filestream AS IS_FILESTREAM, " + + "is_rowguidcol AS IS_ROWGUIDCOL, " + + "CASE WHEN c.is_identity = 'true' THEN 'YES' ELSE 'NO' END as IS_AUTOINCREMENT, " + + "{REMARKS_COLUMN_PLACEHOLDER}" + + "t.name AS TYPE_NAME, " + + "dc.name as COLUMN_DEF_NAME, " + + "dc.definition as COLUMN_DEF, " + + // data type mapping from https://msdn.microsoft.com/en-us/library/ms378878(v=sql.110).aspx + "CASE t.name " + + "WHEN 'bigint' THEN " + java.sql.Types.BIGINT + " " + + "WHEN 'binary' THEN " + java.sql.Types.BINARY + " " + + "WHEN 'bit' THEN " + java.sql.Types.BIT + " " + + "WHEN 'char' THEN " + java.sql.Types.CHAR + " " + + "WHEN 'date' THEN " + java.sql.Types.DATE + " " + + "WHEN 'datetime' THEN " + java.sql.Types.TIMESTAMP + " " + + "WHEN 'datetime2' THEN " + java.sql.Types.TIMESTAMP + " " + + "WHEN 'datetimeoffset' THEN -155 " + + "WHEN 'decimal' THEN " + java.sql.Types.DECIMAL + " " + + "WHEN 'float' THEN " + java.sql.Types.DOUBLE + " " + + "WHEN 'image' THEN " + java.sql.Types.LONGVARBINARY + " " + + "WHEN 'int' THEN " + java.sql.Types.INTEGER + " " + + "WHEN 'money' THEN " + java.sql.Types.DECIMAL + " " + + "WHEN 'nchar' THEN " + java.sql.Types.NCHAR + " " + + "WHEN 'ntext' THEN " + java.sql.Types.LONGNVARCHAR + " " + + "WHEN 'numeric' THEN " + java.sql.Types.NUMERIC + " " + + "WHEN 'nvarchar' THEN " + java.sql.Types.NVARCHAR + " " + + "WHEN 'real' THEN " + Types.REAL + " " + + "WHEN 'smalldatetime' THEN " + java.sql.Types.TIMESTAMP + " " + + "WHEN 'smallint' THEN " + java.sql.Types.SMALLINT + " " + + "WHEN 'smallmoney' THEN " + java.sql.Types.DECIMAL + " " + + "WHEN 'text' THEN " + java.sql.Types.LONGVARCHAR + " " + + "WHEN 'time' THEN " + java.sql.Types.TIME + " " + + "WHEN 'timestamp' THEN " + java.sql.Types.BINARY + " " + + "WHEN 'tinyint' THEN " + java.sql.Types.TINYINT + " " + + "WHEN 'udt' THEN " + java.sql.Types.VARBINARY + " " + + "WHEN 'uniqueidentifier' THEN " + java.sql.Types.CHAR + " " + + "WHEN 'varbinary' THEN " + java.sql.Types.VARBINARY + " " + + "WHEN 'varbinary(max)' THEN " + java.sql.Types.VARBINARY + " " + + "WHEN 'varchar' THEN " + java.sql.Types.VARCHAR + " " + + "WHEN 'varchar(max)' THEN " + java.sql.Types.VARCHAR + " " + + "WHEN 'xml' THEN " + java.sql.Types.LONGVARCHAR + " " + + "WHEN 'LONGNVARCHAR' THEN " + java.sql.Types.SQLXML + " " + + "ELSE " + Types.OTHER + " END AS DATA_TYPE, " + + "CASE WHEN c.is_nullable = 'true' THEN 1 ELSE 0 END AS NULLABLE, " + + "10 as NUM_PREC_RADIX, " + + "c.column_id as ORDINAL_POSITION, " + + "c.scale as DECIMAL_DIGITS, " + + "c.max_length as COLUMN_SIZE, " + + "c.precision as DATA_PRECISION, " + + "c.is_computed as IS_COMPUTED " + + "FROM " + databasePrefix + "sys.columns c " + + "inner join " + databasePrefix + "sys.types t on c.user_type_id=t.user_type_id " + + "{REMARKS_JOIN_PLACEHOLDER}" + + "left outer join " + databasePrefix + "sys.default_constraints dc on dc.parent_column_id = c.column_id AND dc.parent_object_id=c.object_id AND type_desc='DEFAULT_CONSTRAINT' " + + "WHERE object_schema_name(c.object_id" + dbIdParam + ")='" + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema) + "'"; + + + if (!bulk) { + if (tableName != null) { + sql += " and object_name(c.object_id" + dbIdParam + ")='" + database.escapeStringForDatabase(tableName) + "'"; + } + if (columnName != null) { + sql += " and c.name='" + database.escapeStringForDatabase(columnName) + "'"; + } + } + sql += "order by object_schema_name(c.object_id" + dbIdParam + "), object_name(c.object_id" + dbIdParam + "), c.column_id"; + + + // sys.extended_properties is added to Azure on V12: https://feedback.azure.com/forums/217321-sql-database/suggestions/6549815-add-sys-extended-properties-for-meta-data-support + if ((!((MSSQLDatabase) database).isAzureDb()) // Either NOT AzureDB (=SQL Server 2008 or higher) + || (database.getDatabaseMajorVersion() >= 12)) { // or at least AzureDB v12 + // SQL Server 2005 or later + // https://technet.microsoft.com/en-us/library/ms177541.aspx + sql = sql.replace("{REMARKS_COLUMN_PLACEHOLDER}", "CAST([ep].[value] AS [nvarchar](MAX)) AS [REMARKS], "); + sql = sql.replace("{REMARKS_JOIN_PLACEHOLDER}", "left outer join " + databasePrefix + "[sys].[extended_properties] AS [ep] ON [ep].[class] = 1 " + + "AND [ep].[major_id] = c.object_id " + + "AND [ep].[minor_id] = column_id " + + "AND [ep].[name] = 'MS_Description' "); + } else { + sql = sql.replace("{REMARKS_COLUMN_PLACEHOLDER}", ""); + sql = sql.replace("{REMARKS_JOIN_PLACEHOLDER}", ""); + } + + List rows = this.executeAndExtract(sql, database); + + for (CachedRow row : rows) { + String typeName = row.getString("TYPE_NAME"); + if ("nvarchar".equals(typeName) || "nchar".equals(typeName)) { + Integer size = row.getInt("COLUMN_SIZE"); + if (size > 0) { + row.set("COLUMN_SIZE", size / 2); + } + } else if ((row.getInt("DATA_PRECISION") != null) && (row.getInt("DATA_PRECISION") > 0)) { + row.set("COLUMN_SIZE", row.getInt("DATA_PRECISION")); + } + } + + return rows; + } + + @Override + protected List extract(ResultSet resultSet, boolean informixIndexTrimHint) throws SQLException { + List rows = super.extract(resultSet, informixIndexTrimHint); + if ((database instanceof MSSQLDatabase) && !userDefinedTypes.isEmpty()) { //UDT types in MSSQL don't take parameters + for (CachedRow row : rows) { + String dataType = (String) row.get("TYPE_NAME"); + if (userDefinedTypes.contains(dataType.toLowerCase())) { + row.set("COLUMN_SIZE", null); + row.set("DECIMAL_DIGITS ", null); + } + } + } + return rows; + } + } + + private class ForeignKeysResultSetCache extends ResultSetCache.UnionResultSetExtractor { + final String catalogName; + final String schemaName; + final String tableName; + final String fkName; + + private ForeignKeysResultSetCache(Database database, String catalogName, String schemaName, String tableName, String fkName) { + super(database); + this.catalogName = catalogName; + this.schemaName = schemaName; + this.tableName = tableName; + this.fkName = fkName; + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("FKTABLE_CAT"), row.getString("FKTABLE_SCHEM"), database, row.getString("FKTABLE_NAME"), row.getString("FK_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, tableName, fkName); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("FKTABLE_SCHEM"); + } + + @Override + public List fastFetch() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + String jdbcCatalogName = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + + if (database instanceof DB2Database) { + if (database.getDatabaseProductName().startsWith("DB2 UDB for AS/400")) { + return executeAndExtract(getDB2ForAs400Sql(jdbcSchemaName, tableName), database); + } + return querytDB2Luw(jdbcSchemaName, tableName); + } else if (database instanceof Db2zDatabase) { + return queryDb2Zos(catalogAndSchema, tableName); + } else { + List tables = new ArrayList<>(); + if (tableName == null) { + for (CachedRow row : getTables(jdbcCatalogName, jdbcSchemaName, null)) { + tables.add(row.getString("TABLE_NAME")); + } + } else { + tables.add(tableName); + } + + List returnList = new ArrayList<>(); + for (String foundTable : tables) { + if (database instanceof OracleDatabase) { + throw new RuntimeException("Should have bulk selected"); + } else { + returnList.addAll(extract(databaseMetaData.getImportedKeys(jdbcCatalogName, jdbcSchemaName, foundTable))); + } + } + + return returnList; + } + } + + @Override + public List bulkFetch() throws SQLException, DatabaseException { + if (database instanceof OracleDatabase) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + String sql = getOracleSql(jdbcSchemaName); + return executeAndExtract(sql, database); + } else if (database instanceof DB2Database) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + if (database.getDatabaseProductName().startsWith("DB2 UDB for AS/400")) { + return executeAndExtract(getDB2ForAs400Sql(jdbcSchemaName, null), database); + } + return querytDB2Luw(jdbcSchemaName, null); + } else if (database instanceof Db2zDatabase) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + return queryDb2Zos(catalogAndSchema, null); + } else if (database instanceof MSSQLDatabase) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + String sql = getMSSQLSql(jdbcSchemaName, tableName); + return executeAndExtract(sql, database); + } else { + throw new RuntimeException("Cannot bulk select"); + } + } + + protected String getOracleSql(String jdbcSchemaName) { + String sql = "SELECT /*+rule*/" + + " NULL AS pktable_cat, " + + " p.owner as pktable_schem, " + + " p.table_name as pktable_name, " + + " pc.column_name as pkcolumn_name, " + + " NULL as fktable_cat, " + + " f.owner as fktable_schem, " + + " f.table_name as fktable_name, " + + " fc.column_name as fkcolumn_name, " + + " fc.position as key_seq, " + + " NULL as update_rule, " + + " decode (f.delete_rule, 'CASCADE', 0, 'SET NULL', 2, 1) as delete_rule, " + + " f.constraint_name as fk_name, " + + " p.constraint_name as pk_name, " + + " decode(f.deferrable, 'DEFERRABLE', 5, 'NOT DEFERRABLE', 7, 'DEFERRED', 6) deferrability, " + + " f.validated as fk_validate " + + "FROM " + + "all_cons_columns pc " + + "INNER JOIN all_constraints p " + + "ON pc.owner = p.owner " + + "AND pc.constraint_name = p.constraint_name " + + "INNER JOIN all_constraints f " + + "ON pc.owner = f.r_owner " + + "AND pc.constraint_name = f.r_constraint_name " + + "INNER JOIN all_cons_columns fc " + + "ON fc.owner = f.owner " + + "AND fc.constraint_name = f.constraint_name " + + "AND fc.position = pc.position "; + if (getAllCatalogsStringScratchData() == null) { + sql += "WHERE f.owner = '" + jdbcSchemaName + "' "; + } else { + sql += "WHERE f.owner IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ") "; + } + sql += "AND p.constraint_type in ('P', 'U') " + + "AND f.constraint_type = 'R' " + + "AND p.table_name NOT LIKE 'BIN$%' " + + "ORDER BY fktable_schem, fktable_name, key_seq"; + return sql; + } + + protected String getMSSQLSql(String jdbcSchemaName, String tableName) { + //comes from select object_definition(object_id('sp_fkeys')) + return "select " + + "convert(sysname,db_name()) AS PKTABLE_CAT, " + + "convert(sysname,schema_name(o1.schema_id)) AS PKTABLE_SCHEM, " + + "convert(sysname,o1.name) AS PKTABLE_NAME, " + + "convert(sysname,c1.name) AS PKCOLUMN_NAME, " + + "convert(sysname,db_name()) AS FKTABLE_CAT, " + + "convert(sysname,schema_name(o2.schema_id)) AS FKTABLE_SCHEM, " + + "convert(sysname,o2.name) AS FKTABLE_NAME, " + + "convert(sysname,c2.name) AS FKCOLUMN_NAME, " + + "isnull(convert(smallint,k.constraint_column_id), convert(smallint,0)) AS KEY_SEQ, " + + "convert(smallint, case ObjectProperty(f.object_id, 'CnstIsUpdateCascade') when 1 then 0 else 1 end) AS UPDATE_RULE, " + + "convert(smallint, case ObjectProperty(f.object_id, 'CnstIsDeleteCascade') when 1 then 0 else 1 end) AS DELETE_RULE, " + + "convert(sysname,object_name(f.object_id)) AS FK_NAME, " + + "convert(sysname,i.name) AS PK_NAME, " + + "convert(smallint, 7) AS DEFERRABILITY " + + "from " + + "sys.objects o1, " + + "sys.objects o2, " + + "sys.columns c1, " + + "sys.columns c2, " + + "sys.foreign_keys f inner join " + + "sys.foreign_key_columns k on (k.constraint_object_id = f.object_id) inner join " + + "sys.indexes i on (f.referenced_object_id = i.object_id and f.key_index_id = i.index_id) " + + "where " + + "o1.object_id = f.referenced_object_id and " + + "o2.object_id = f.parent_object_id and " + + "c1.object_id = f.referenced_object_id and " + + "c2.object_id = f.parent_object_id and " + + "c1.column_id = k.referenced_column_id and " + + "c2.column_id = k.parent_column_id and " + + "((object_schema_name(o1.object_id)='" + jdbcSchemaName + "'" + + " and convert(sysname,schema_name(o2.schema_id))='" + jdbcSchemaName + "' and " + + "convert(sysname,o2.name)='" + tableName + "' ) or ( convert(sysname,schema_name" + + "(o2.schema_id))='" + jdbcSchemaName + "' and convert(sysname,o2.name)='" + tableName + + "' )) order by 5, 6, 7, 9, 8"; + } + + private List querytDB2Luw(String jdbcSchemaName, String tableName) throws DatabaseException, SQLException { + List parameters = new ArrayList<>(2); + StringBuilder sql = new StringBuilder ("SELECT " + + " pk_col.tabschema AS pktable_cat, " + + " pk_col.tabname as pktable_name, " + + " pk_col.colname as pkcolumn_name, " + + " fk_col.tabschema as fktable_cat, " + + " fk_col.tabname as fktable_name, " + + " fk_col.colname as fkcolumn_name, " + + " fk_col.colseq as key_seq, " + + " decode (ref.updaterule, 'A', 3, 'R', 1, 1) as update_rule, " + + " decode (ref.deleterule, 'A', 3, 'C', 0, 'N', 2, 'R', 1, 1) as delete_rule, " + + " ref.constname as fk_name, " + + " ref.refkeyname as pk_name, " + + " 7 as deferrability " + + "FROM " + + "syscat.references ref " + + "join syscat.keycoluse fk_col on ref.constname=fk_col.constname and ref.tabschema=fk_col.tabschema and ref.tabname=fk_col.tabname " + + "join syscat.keycoluse pk_col on ref.refkeyname=pk_col.constname and ref.reftabschema=pk_col.tabschema and ref.reftabname=pk_col.tabname and pk_col.colseq=fk_col.colseq " + + "WHERE ref.tabschema = ? "); + parameters.add(jdbcSchemaName); + if (tableName != null) { + sql.append("and fk_col.tabname = ? "); + parameters.add(tableName); + } + sql.append("ORDER BY fk_col.colseq"); + return executeAndExtract(database, sql.toString(), parameters.toArray()); + } + + private String getDB2ForAs400Sql(String jdbcSchemaName, String tableName) { + return "SELECT " + + "pktable_cat, " + + "pktable_name, " + + "pkcolumn_name, " + + "fktable_cat, " + + "fktable_name, " + + "fkcolumn_name, " + + "key_seq, " + + "update_rule, " + + "delete_rule, " + + "fk_name, " + + "pk_name, " + + "deferrability " + + "FROM " + + "sysibm.SQLFORKEYS " + + "WHERE " + + "FKTABLE_SCHEM = '" + jdbcSchemaName + "' " + + "AND FKTABLE_NAME = '" + tableName + "'"; + } + + protected List queryDb2Zos(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { + + List parameters = new ArrayList<>(2); + StringBuilder sql = new StringBuilder("SELECT " + + " ref.REFTBCREATOR AS pktable_cat, " + + " ref.REFTBNAME as pktable_name, " + + " pk_col.colname as pkcolumn_name, " + + " ref.CREATOR as fktable_cat, " + + " ref.TBNAME as fktable_name, " + + " fk_col.colname as fkcolumn_name, " + + " fk_col.colseq as key_seq, " + + " decode (ref.deleterule, 'A', 3, 'C', 0, 'N', 2, 'R', 1, 1) as delete_rule, " + + " ref.relname as fk_name, " + + " pk_col.colname as pk_name, " + + " 7 as deferrability " + + "FROM " + + "SYSIBM.SYSRELS ref " + + "join SYSIBM.SYSFOREIGNKEYS fk_col " + + "on ref.relname = fk_col.RELNAME " + + "and ref.CREATOR = fk_col.CREATOR " + + "and ref.TBNAME = fk_col.TBNAME " + + "join SYSIBM.SYSKEYCOLUSE pk_col " + + "on ref.REFTBCREATOR = pk_col.TBCREATOR " + + "and ref.REFTBNAME = pk_col.TBNAME " + + "and pk_col.colseq=fk_col.colseq " + + "WHERE ref.CREATOR = ? "); + parameters.add(((AbstractJdbcDatabase) CachingDatabaseMetaData.this.database).getJdbcSchemaName(catalogAndSchema)); + if (tableName != null) { + sql.append("AND ref.TBNAME = ? "); + parameters.add(tableName); + } + sql.append("ORDER BY fk_col.colseq"); + + return executeAndExtract(CachingDatabaseMetaData.this.database, sql.toString(), parameters.toArray()); + } + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + if (database instanceof AbstractDb2Database || database instanceof MSSQLDatabase) { + return super.shouldBulkSelect(schemaKey, resultSetCache); //can bulk and fast fetch + } else { + return database instanceof OracleDatabase; //oracle is slow, always bulk select while you are at it. Other databases need to go through all tables. + } + } + } + + private class GetNotNullConstraintsResultSetCache extends ResultSetCache.SingleResultSetExtractor { + final String catalogName; + final String schemaName; + final String tableName; + + private GetNotNullConstraintsResultSetCache(Database database, String catalogName, String schemaName, String tableName) { + super(database); + this.catalogName = catalogName; + this.schemaName = schemaName; + this.tableName = tableName; + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEMA"), + database, row.getString("TABLE_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, tableName); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEMA"); + } + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + LiquibaseTableNamesFactory liquibaseTableNamesFactory = Scope.getCurrentScope().getSingleton(LiquibaseTableNamesFactory.class); + List liquibaseTableNames = liquibaseTableNamesFactory.getLiquibaseTableNames(database); + return liquibaseTableNames.stream().noneMatch(tableName::equalsIgnoreCase); + } + + @Override + public List fastFetchQuery() throws SQLException, DatabaseException { + if (database instanceof OracleDatabase) { + return oracleQuery(false); + } + return Collections.emptyList(); + } + + @Override + public List bulkFetchQuery() throws SQLException, DatabaseException { + if (database instanceof OracleDatabase) { + return oracleQuery(true); + } + return Collections.emptyList(); + } + + private List oracleQuery(boolean bulk) throws DatabaseException, SQLException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + String jdbcTableName = database.escapeStringForDatabase(tableName); + String sqlToSelectNotNullConstraints = "SELECT NULL AS TABLE_CAT, atc.OWNER AS TABLE_SCHEMA, atc.OWNER, atc.TABLE_NAME, " + + "atc.COLUMN_NAME, NULLABLE, ac.VALIDATED as VALIDATED, ac.SEARCH_CONDITION, ac.CONSTRAINT_NAME " + + "FROM ALL_TAB_COLS atc " + + "JOIN all_cons_columns acc ON atc.OWNER = acc.OWNER AND atc.TABLE_NAME = acc.TABLE_NAME AND atc.COLUMN_NAME = acc.COLUMN_NAME " + + "JOIN all_constraints ac ON atc.OWNER = ac.OWNER AND atc.TABLE_NAME = ac.TABLE_NAME AND acc.CONSTRAINT_NAME = ac.CONSTRAINT_NAME "; + + if (!bulk || getAllCatalogsStringScratchData() == null) { + sqlToSelectNotNullConstraints += " WHERE atc.OWNER='" + jdbcSchemaName + "' AND atc.hidden_column='NO' AND ac.CONSTRAINT_TYPE='C' and ac.search_condition is not null "; + } else { + sqlToSelectNotNullConstraints += " WHERE atc.OWNER IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ") " + + " AND atc.hidden_column='NO' AND ac.CONSTRAINT_TYPE='C' and ac.search_condition is not null "; + } + + sqlToSelectNotNullConstraints += (!bulk && tableName != null && !tableName.isEmpty()) ? " AND atc.TABLE_NAME='" + jdbcTableName + "'" : ""; + + return this.executeAndExtract(sqlToSelectNotNullConstraints, database); + } + + @Override + protected List extract(ResultSet resultSet, boolean informixIndexTrimHint) throws SQLException { + List cachedRowList = new ArrayList<>(); + if (!(database instanceof OracleDatabase)) { + return cachedRowList; + } + + resultSet.setFetchSize(database.getFetchSize()); + + try { + List result = (List) new RowMapperNotNullConstraintsResultSetExtractor(new ColumnMapRowMapper(database.isCaseSensitive()) { + @Override + protected Object getColumnValue(ResultSet rs, int index) throws SQLException { + Object value = super.getColumnValue(rs, index); + if (!(value instanceof String)) { + return value; + } + return value.toString().trim(); + } + }).extractData(resultSet); + + for (Map row : result) { + cachedRowList.add(new CachedRow(row)); + } + } finally { + JdbcUtil.closeResultSet(resultSet); + } + return cachedRowList; + + } + } + + public List getTables(final String catalogName, final String schemaName, final String table) throws DatabaseException { + return getResultSetCache("getTables").get(new ResultSetCache.SingleResultSetExtractor(database) { + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + return table == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, table); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEM"); + } + + @Override + public List fastFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + if (database instanceof OracleDatabase) { + return queryOracle(catalogAndSchema, table); + } else if (database instanceof MSSQLDatabase) { + return queryMssql(catalogAndSchema, table); + } else if (database instanceof Db2zDatabase) { + return queryDb2Zos(catalogAndSchema, table); + } else if (database instanceof PostgresDatabase) { + return queryPostgres(catalogAndSchema, table); + } + + String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), ((table == null) ? + SQL_FILTER_MATCH_ALL : escapeForLike(table, database)), new String[]{"TABLE"})); + } + + @Override + public List bulkFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + if (database instanceof OracleDatabase) { + return queryOracle(catalogAndSchema, null); + } else if (database instanceof MSSQLDatabase) { + return queryMssql(catalogAndSchema, null); + } else if (database instanceof Db2zDatabase) { + return queryDb2Zos(catalogAndSchema, null); + } else if (database instanceof PostgresDatabase) { + return queryPostgres(catalogAndSchema, table); + } + + String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), SQL_FILTER_MATCH_ALL, new String[]{"TABLE"})); + } + + private List queryMssql(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { + String ownerName = database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class); + + String databaseName = StringUtil.trimToNull(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)); + String dbIdParam; + String databasePrefix; + if (databaseName == null) { + databasePrefix = ""; + dbIdParam = ""; + } else { + dbIdParam = ", db_id('" + databaseName + "')"; + databasePrefix = "[" + databaseName + "]."; + } + + + //From select object_definition(object_id('sp_tables')) + String sql = "select " + + "db_name(" + (databaseName == null ? "" : "db_id('" + databaseName + "')") + ") AS TABLE_CAT, " + + "convert(sysname,object_schema_name(o.object_id" + dbIdParam + ")) AS TABLE_SCHEM, " + + "convert(sysname,o.name) AS TABLE_NAME, " + + "'TABLE' AS TABLE_TYPE, " + + "CAST(ep.value as varchar(max)) as REMARKS " + + "from " + databasePrefix + "sys.all_objects o " + + "left outer join sys.extended_properties ep on ep.name='MS_Description' and major_id=o.object_id and minor_id=0 " + + "where " + + "o.type in ('U') " + + "and has_perms_by_name(" + (databaseName == null ? "" : "quotename('" + databaseName + "') + '.' + ") + "quotename(object_schema_name(o.object_id" + dbIdParam + ")) + '.' + quotename(o.name), 'object', 'select') = 1 " + + "and charindex(substring(o.type,1,1),'U') <> 0 " + + "and object_schema_name(o.object_id" + dbIdParam + ")='" + database.escapeStringForDatabase(ownerName) + "'"; + if (tableName != null) { + sql += " AND o.name='" + database.escapeStringForDatabase(tableName) + "' "; + } + sql += "order by 4, 1, 2, 3"; + + return executeAndExtract(sql, database); + } + + private List queryOracle(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { + String ownerName = database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class); + + String sql = "SELECT null as TABLE_CAT, a.OWNER as TABLE_SCHEM, a.TABLE_NAME as TABLE_NAME, " + + "a.TEMPORARY as TEMPORARY, a.DURATION as DURATION, 'TABLE' as TABLE_TYPE, " + + "c.COMMENTS as REMARKS, A.tablespace_name as tablespace_name, CASE WHEN A.tablespace_name = " + + "(SELECT DEFAULT_TABLESPACE FROM USER_USERS) THEN 'true' ELSE null END as default_tablespace " + + "from ALL_TABLES a " + + "join ALL_TAB_COMMENTS c on a.TABLE_NAME=c.table_name and a.owner=c.owner " + + "left outer join ALL_QUEUE_TABLES q ON a.TABLE_NAME = q.QUEUE_TABLE and a.OWNER = q.OWNER " + + "WHERE q.QUEUE_TABLE is null "; + String allCatalogsString = getAllCatalogsStringScratchData(); + if (tableName != null || allCatalogsString == null) { + sql += "AND a.OWNER='" + ownerName + "'"; + } else { + sql += "AND a.OWNER IN ('" + ownerName + "', " + allCatalogsString + ")"; + } + if (tableName != null) { + sql += " AND a.TABLE_NAME='" + tableName + "'"; + } + + return executeAndExtract(sql, database); + } + + private List queryDb2Zos(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { + String ownerName = database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class); + + String sql = "SELECT CREATOR AS TABLE_SCHEM, " + + "NAME AS TABLE_NAME, " + + "'TABLE' AS TABLE_TYPE, " + + "REMARKS " + + "FROM SYSIBM.SYSTABLES " + + "WHERE TYPE = 'T'"; + List parameters = new ArrayList<>(2); + if (ownerName != null) { + sql += " AND CREATOR = ?"; + parameters.add(ownerName); + } + if (tableName != null) { + sql += " AND NAME = ?"; + parameters.add(tableName); + } + + return executeAndExtract(database, sql, parameters.toArray()); + } + + private List queryPostgres(CatalogAndSchema catalogAndSchema, String tableName) throws SQLException { + String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), ((tableName == null) ? + SQL_FILTER_MATCH_ALL : escapeForLike(tableName, database)), new String[]{"TABLE", "PARTITIONED TABLE"})); + + } + }); + } + + public List getViews(final String catalogName, final String schemaName, String viewName) throws DatabaseException { + final String view; + if (database instanceof DB2Database) { + view = database.correctObjectName(viewName, View.class); + } else { + view = viewName; + } + return getResultSetCache("getViews").get(new ResultSetCache.SingleResultSetExtractor(database) { + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + return view == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME")); + } + + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, view); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEM"); + } + + + @Override + public List fastFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + if (database instanceof OracleDatabase) { + return queryOracle(catalogAndSchema, view); + } else if (database instanceof MSSQLDatabase) { + return queryMssql(catalogAndSchema, view); + } + + String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), ((view == null) ? SQL_FILTER_MATCH_ALL + : escapeForLike(view, database)), new String[]{"VIEW"})); + } + + @Override + public List bulkFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + if (database instanceof OracleDatabase) { + return queryOracle(catalogAndSchema, null); + } else if (database instanceof MSSQLDatabase) { + return queryMssql(catalogAndSchema, null); + } + + String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), SQL_FILTER_MATCH_ALL, new String[]{"VIEW"})); + } + + private List queryMssql(CatalogAndSchema catalogAndSchema, String viewName) throws DatabaseException, SQLException { + String ownerName = database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class); + String databaseName = StringUtil.trimToNull(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)); + String dbIdParam = ""; + String databasePrefix = ""; + boolean haveDatabaseName = databaseName != null; + + if (haveDatabaseName) { + dbIdParam = ", db_id('" + databaseName + "')"; + databasePrefix = "[" + databaseName + "]."; + } + String tableCatParam = haveDatabaseName ? "db_id('" + databaseName + "')" : ""; + String permsParam = haveDatabaseName ? "quotename('" + databaseName + "') + '.' + " : ""; + + String sql = "select " + + "db_name(" + tableCatParam + ") AS TABLE_CAT, " + + "convert(sysname,object_schema_name(o.object_id" + dbIdParam + ")) AS TABLE_SCHEM, " + + "convert(sysname,o.name) AS TABLE_NAME, " + + "'VIEW' AS TABLE_TYPE, " + + "CAST(ep.value as varchar(max)) as REMARKS " + + "from " + databasePrefix + "sys.all_objects o " + + "left join sys.extended_properties ep on ep.name='MS_Description' and major_id=o.object_id and minor_id=0 " + + "where " + + "o.type in ('V') " + + "and has_perms_by_name(" + permsParam + "quotename(object_schema_name(o.object_id" + dbIdParam + ")) + '.' + quotename(o.name), 'object', 'select') = 1 " + + "and charindex(substring(o.type,1,1),'V') <> 0 " + + "and object_schema_name(o.object_id" + dbIdParam + ")='" + database.escapeStringForDatabase(ownerName) + "'"; + if (viewName != null) { + sql += " AND o.name='" + database.escapeStringForDatabase(viewName) + "' "; + } + sql += "order by 4, 1, 2, 3"; + + return executeAndExtract(sql, database); + } + + + private List queryOracle(CatalogAndSchema catalogAndSchema, String viewName) throws DatabaseException, SQLException { + String ownerName = database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class); + + String sql = "SELECT null as TABLE_CAT, a.OWNER as TABLE_SCHEM, a.VIEW_NAME as TABLE_NAME, 'TABLE' as TABLE_TYPE, c.COMMENTS as REMARKS, TEXT as OBJECT_BODY"; + if (database.getDatabaseMajorVersion() > 10) { + sql += ", EDITIONING_VIEW"; + } + sql += " from ALL_VIEWS a " + + "join ALL_TAB_COMMENTS c on a.VIEW_NAME=c.table_name and a.owner=c.owner "; + if (viewName != null || getAllCatalogsStringScratchData() == null) { + sql += "WHERE a.OWNER='" + ownerName + "'"; + } else { + sql += "WHERE a.OWNER IN ('" + ownerName + "', " + getAllCatalogsStringScratchData() + ")"; + } + if (viewName != null) { + sql += " AND a.VIEW_NAME='" + database.correctObjectName(viewName, View.class) + "'"; + } + sql += " AND a.VIEW_NAME not in (select mv.name from all_registered_mviews mv where mv.owner=a.owner)"; + + return executeAndExtract(sql, database); + } + }); + } + + public List getPrimaryKeys(final String catalogName, final String schemaName, final String table) throws DatabaseException { + return getResultSetCache("getPrimaryKeys").get(new ResultSetCache.SingleResultSetExtractor(database) { + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, table); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEM"); + } + + @Override + public List fastFetchQuery() throws SQLException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + try { + List foundPks = new ArrayList<>(); + if (table == null) { + List tables = CachingDatabaseMetaData.this.getTables(catalogName, schemaName, null); + for (CachedRow table : tables) { + List pkInfo = getPkInfo(catalogAndSchema, table.getString("TABLE_NAME")); + if (pkInfo != null) { + foundPks.addAll(pkInfo); + } + } + return foundPks; + } else { + List pkInfo = getPkInfo(catalogAndSchema, table); + if (pkInfo != null) { + foundPks.addAll(pkInfo); + } + } + return foundPks; + } catch (DatabaseException e) { + throw new SQLException(e); + } + } + + private List getPkInfo(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { + List pkInfo; + if (database instanceof MSSQLDatabase) { + String sql = mssqlSql(catalogAndSchema, tableName); + pkInfo = executeAndExtract(sql, database); + } else { + if (database instanceof Db2zDatabase) { + String sql = "SELECT 'NULL' AS TABLE_CAT," + + " SYSTAB.TBCREATOR AS TABLE_SCHEM, " + + "SYSTAB.TBNAME AS TABLE_NAME, " + + "COLUSE.COLNAME AS COLUMN_NAME, " + + "COLUSE.COLSEQ AS KEY_SEQ, " + + "SYSTAB.CONSTNAME AS PK_NAME " + + "FROM SYSIBM.SYSTABCONST SYSTAB " + + "JOIN SYSIBM.SYSKEYCOLUSE COLUSE " + + "ON SYSTAB.TBCREATOR = COLUSE.TBCREATOR " + + "WHERE SYSTAB.TYPE = 'P' " + + "AND SYSTAB.TBNAME = ? " + + "AND SYSTAB.TBCREATOR = ? " + + "AND SYSTAB.TBNAME=COLUSE.TBNAME " + + "AND SYSTAB.CONSTNAME=COLUSE.CONSTNAME " + + "ORDER BY COLUSE.COLNAME"; + try { + return executeAndExtract(database, sql, table, ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema)); + } catch (DatabaseException e) { + throw new SQLException(e); + } + } else if (database instanceof OracleDatabase) { + warnAboutDbaRecycleBin(); + + String sql = "SELECT NULL AS table_cat, c.owner AS table_schem, c.table_name, c.column_name as COLUMN_NAME, c.position AS key_seq, c.constraint_name AS pk_name, k.VALIDATED as VALIDATED " + + "FROM all_cons_columns c, all_constraints k " + + "LEFT JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=k.table_name " + + "WHERE k.constraint_type = 'P' " + + "AND d.object_name IS NULL " + + "AND k.table_name = '" + table + "' " + + "AND k.owner = '" + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema) + "' " + + "AND k.constraint_name = c.constraint_name " + + "AND k.table_name = c.table_name " + + "AND k.owner = c.owner " + + "ORDER BY column_name"; + try { + return executeAndExtract(sql, database); + } catch (DatabaseException e) { + throw new SQLException(e); + } + } else if (database instanceof CockroachDatabase) { + // This is the same as the query generated by PGJDBC's getPrimaryKeys method, except it + // also adds an `asc_or_desc` column to the result. + String sql = "SELECT " + + " result.table_cat, " + + " result.table_schem, " + + " result.table_name, " + + " result.column_name, " + + " result.key_seq, " + + " result.pk_name, " + + " CASE result.indoption[result.key_seq - 1] & 1 " + + " WHEN 1 THEN 'D' " + + " ELSE 'A' " + + " END AS asc_or_desc " + + "FROM " + + " (" + + " SELECT " + + " NULL AS table_cat, " + + " n.nspname AS table_schem, " + + " ct.relname AS table_name, " + + " a.attname AS column_name, " + + " (information_schema._pg_expandarray(i.indkey)).n " + + " AS key_seq, " + + " ci.relname AS pk_name, " + + " information_schema._pg_expandarray(i.indkey) AS keys, " + + " i.indoption, " + + " a.attnum AS a_attnum " + + " FROM " + + " pg_catalog.pg_class AS ct " + + " JOIN pg_catalog.pg_attribute AS a ON (ct.oid = a.attrelid) " + + " JOIN pg_catalog.pg_namespace AS n ON " + + " (ct.relnamespace = n.oid) " + + " JOIN pg_catalog.pg_index AS i ON (a.attrelid = i.indrelid) " + + " JOIN pg_catalog.pg_class AS ci ON (ci.oid = i.indexrelid) " + + " WHERE " + + " true " + + " AND n.nspname = '" + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema) + "' " + + " AND ct.relname = '" + table + "' " + + " AND i.indisprimary" + + " ) " + + " AS result " + + "WHERE " + + " result.a_attnum = (result.keys).x " + + "ORDER BY " + + " result.table_name, result.pk_name, result.key_seq"; + + try { + return executeAndExtract(sql, database); + } catch (DatabaseException e) { + throw new SQLException(e); + } + } else { + return extract( + databaseMetaData.getPrimaryKeys( + ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), + table + ) + ); + } + } + return pkInfo; + } + + private String mssqlSql(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException { + String sql; + sql = + "SELECT " + + "DB_NAME() AS [TABLE_CAT], " + + "[s].[name] AS [TABLE_SCHEM], " + + "[t].[name] AS [TABLE_NAME], " + + "[c].[name] AS [COLUMN_NAME], " + + "CASE [ic].[is_descending_key] WHEN 0 THEN N'A' WHEN 1 THEN N'D' END AS [ASC_OR_DESC], " + + "[ic].[key_ordinal] AS [KEY_SEQ], " + + "[kc].[name] AS [PK_NAME] " + + "FROM [sys].[schemas] AS [s] " + + "INNER JOIN [sys].[tables] AS [t] " + + "ON [t].[schema_id] = [s].[schema_id] " + + "INNER JOIN [sys].[key_constraints] AS [kc] " + + "ON [kc].[parent_object_id] = [t].[object_id] " + + "INNER JOIN [sys].[indexes] AS [i] " + + "ON [i].[object_id] = [kc].[parent_object_id] " + + "AND [i].[index_id] = [kc].[unique_index_id] " + + "INNER JOIN [sys].[index_columns] AS [ic] " + + "ON [ic].[object_id] = [i].[object_id] " + + "AND [ic].[index_id] = [i].[index_id] " + + "INNER JOIN [sys].[columns] AS [c] " + + "ON [c].[object_id] = [ic].[object_id] " + + "AND [c].[column_id] = [ic].[column_id] " + + "WHERE [s].[name] = N'" + database.escapeStringForDatabase(catalogAndSchema.getSchemaName()) + "' " + // The schema name was corrected in the customized CatalogAndSchema + (tableName == null ? "" : "AND [t].[name] = N'" + database.escapeStringForDatabase(database.correctObjectName(tableName, Table.class)) + "' ") + + "AND [kc].[type] = 'PK' " + + "AND [ic].[key_ordinal] > 0 " + + "ORDER BY " + + "[ic].[key_ordinal]"; + return sql; + } + + @Override + public List bulkFetchQuery() throws SQLException { + if (database instanceof OracleDatabase) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + warnAboutDbaRecycleBin(); + try { + String sql = "SELECT NULL AS table_cat, c.owner AS table_schem, c.table_name, c.column_name, c.position AS key_seq,c.constraint_name AS pk_name, k.VALIDATED as VALIDATED FROM " + + "all_cons_columns c, " + + "all_constraints k " + + "LEFT JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=k.table_name " + + "WHERE k.constraint_type = 'P' " + + "AND d.object_name IS NULL "; + if (getAllCatalogsStringScratchData() == null) { + sql += "AND k.owner='" + catalogAndSchema.getCatalogName() + "' "; + } else { + sql += "AND k.owner IN ('" + catalogAndSchema.getCatalogName() + "', " + getAllCatalogsStringScratchData() + ")"; + } + sql += "AND k.constraint_name = c.constraint_name " + + "AND k.table_name = c.table_name " + + "AND k.owner = c.owner " + + "ORDER BY column_name"; + return executeAndExtract(sql, database); + } catch (DatabaseException e) { + throw new SQLException(e); + } + } else if (database instanceof MSSQLDatabase) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + try { + return executeAndExtract(mssqlSql(catalogAndSchema, null), database); + } catch (DatabaseException e) { + throw new SQLException(e); + } + } + return null; + } + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + if ((database instanceof OracleDatabase) || (database instanceof MSSQLDatabase)) { + return table == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); + } else { + return false; + } + } + }); + } + + public List getUniqueConstraints(final String catalogName, final String schemaName, final String tableName) throws DatabaseException { + return getResultSetCache("getUniqueConstraints").get(new ResultSetCache.SingleResultSetExtractor(database) { + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + return tableName == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("CONSTRAINT_SCHEM"); + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(catalogName, schemaName, database, row.getString("TABLE_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, tableName); + } + + @Override + public List fastFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + return queryDb(catalogAndSchema, tableName); + } + + @Override + public List bulkFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + return queryDb(catalogAndSchema, null); + } + + private List queryDb(CatalogAndSchema catalogAndSchema, String tableName) throws SQLException, DatabaseException { + + String jdbcCatalogName = catalogAndSchema.getCatalogName(); + String jdbcSchemaName = catalogAndSchema.getSchemaName(); + + Database database = getDatabase(); + List parameters = new ArrayList<>(3); + String sql = null; + if (database instanceof Ingres9Database) { + sql = "select CONSTRAINT_NAME, TABLE_NAME from iiconstraints where schema_name ='" + + schemaName + "' and constraint_type='U'"; + if (tableName != null) { + sql += " and table_name='" + tableName + "'"; + } + } else if ((database instanceof MySQLDatabase) || (database instanceof HsqlDatabase) || (database + instanceof MariaDBDatabase)) { + sql = "select CONSTRAINT_NAME, TABLE_NAME " + + "from " + database.getSystemSchema() + ".table_constraints " + + "where constraint_schema='" + jdbcCatalogName + "' " + + "and constraint_type='UNIQUE'"; + if (tableName != null) { + sql += " and table_name='" + tableName + "'"; + } + } else if (database instanceof PostgresDatabase) { + sql = "select CONSTRAINT_NAME, TABLE_NAME " + + "from " + database.getSystemSchema() + ".table_constraints " + + "where constraint_catalog='" + jdbcCatalogName + "' " + + "and constraint_schema='" + jdbcSchemaName + "' " + + "and constraint_type='UNIQUE'"; + if (tableName != null) { + sql += " and table_name='" + tableName + "'"; + } + } else if (database.getClass().getName().contains("MaxDB")) { //have to check classname as this is currently an extension + sql = "select distinct tablename AS TABLE_NAME, constraintname AS CONSTRAINT_NAME from CONSTRAINTCOLUMNS WHERE CONSTRAINTTYPE = 'UNIQUE_CONST'"; + if (tableName != null) { + sql += " and tablename='" + tableName + "'"; + } + } else if (database instanceof MSSQLDatabase) { + sql = + "SELECT " + + "[TC].[CONSTRAINT_NAME], " + + "[TC].[TABLE_NAME], " + + "[TC].[CONSTRAINT_CATALOG] AS INDEX_CATALOG, " + + "[TC].[CONSTRAINT_SCHEMA] AS INDEX_SCHEMA, " + + "[IDX].[TYPE_DESC], " + + "[IDX].[name] AS INDEX_NAME " + + "FROM [INFORMATION_SCHEMA].[TABLE_CONSTRAINTS] AS [TC] " + + "JOIN sys.indexes AS IDX ON IDX.name=[TC].[CONSTRAINT_NAME] AND object_schema_name(object_id)=[TC].[CONSTRAINT_SCHEMA] " + + "WHERE [TC].[CONSTRAINT_TYPE] = 'UNIQUE' " + + "AND [TC].[CONSTRAINT_CATALOG] = N'" + database.escapeStringForDatabase(jdbcCatalogName) + "' " + + "AND [TC].[CONSTRAINT_SCHEMA] = N'" + database.escapeStringForDatabase(jdbcSchemaName) + "'"; + if (tableName != null) { + sql += " AND [TC].[TABLE_NAME] = N'" + database.escapeStringForDatabase(database.correctObjectName(tableName, Table.class)) + "'"; + } + } else if (database instanceof OracleDatabase) { + warnAboutDbaRecycleBin(); + + sql = "select uc.owner AS CONSTRAINT_SCHEM, uc.constraint_name, uc.table_name,uc.status,uc.deferrable,uc.deferred,ui.tablespace_name, ui.index_name, ui.owner as INDEX_CATALOG, uc.VALIDATED as VALIDATED, ac.COLUMN_NAME as COLUMN_NAME " + + "from all_constraints uc " + + "join all_indexes ui on uc.index_name = ui.index_name and uc.owner=ui.table_owner and uc.table_name=ui.table_name " + + "LEFT OUTER JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=ui.table_name " + + "LEFT JOIN all_cons_columns ac ON ac.OWNER = uc.OWNER AND ac.TABLE_NAME = uc.TABLE_NAME AND ac.CONSTRAINT_NAME = uc.CONSTRAINT_NAME " + + "where uc.constraint_type='U' "; + if (tableName != null || getAllCatalogsStringScratchData() == null) { + sql += "and uc.owner = '" + jdbcSchemaName + "'"; + } else { + sql += "and uc.owner IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ")"; + } + sql += "AND d.object_name IS NULL "; + + if (tableName != null) { + sql += " and uc.table_name = '" + tableName + "'"; + } + } else if (database instanceof DB2Database) { + // if we are on DB2 AS400 iSeries + if (database.getDatabaseProductName().startsWith("DB2 UDB for AS/400")) { + sql = "select constraint_name as constraint_name, table_name as table_name from QSYS2.TABLE_CONSTRAINTS where table_schema='" + jdbcSchemaName + "' and constraint_type='UNIQUE'"; + if (tableName != null) { + sql += " and table_name = '" + tableName + "'"; + } + // DB2 z/OS + } + // here we are on DB2 UDB + else { + sql = "select distinct k.constname as constraint_name, t.tabname as TABLE_NAME " + + "from syscat.keycoluse k " + + "inner join syscat.tabconst t " + + "on k.constname = t.constname " + + "where t.tabschema = ? " + + "and t.type = 'U'"; + parameters.add(jdbcSchemaName); + if (tableName != null) { + sql += " and t.tabname = ?"; + parameters.add(tableName); + } + } + } else if (database instanceof Db2zDatabase) { + sql = "select k.constname as constraint_name, t.tbname as TABLE_NAME" + + " from SYSIBM.SYSKEYCOLUSE k" + + " inner join SYSIBM.SYSTABCONST t" + + " on k.constname = t.constname" + + " and k.TBCREATOR = t.TBCREATOR" + + " and k.TBNAME = t.TBNAME" + + " where t.TBCREATOR = ?" + + " and t.TYPE = 'U'"; + parameters.add(jdbcSchemaName); + if (tableName != null) { + sql += " and t.TBNAME = ?"; + parameters.add(tableName); + } + } else if (database instanceof FirebirdDatabase) { + sql = "SELECT TRIM(RDB$INDICES.RDB$INDEX_NAME) AS CONSTRAINT_NAME, " + + "TRIM(RDB$INDICES.RDB$RELATION_NAME) AS TABLE_NAME " + + "FROM RDB$INDICES " + + "LEFT JOIN RDB$RELATION_CONSTRAINTS " + + "ON RDB$RELATION_CONSTRAINTS.RDB$INDEX_NAME = RDB$INDICES.RDB$INDEX_NAME " + + "WHERE RDB$INDICES.RDB$UNIQUE_FLAG IS NOT NULL " + + "AND (" + + "RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_TYPE IS NULL " + + "OR TRIM(RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_TYPE)='UNIQUE') " + + "AND NOT(RDB$INDICES.RDB$INDEX_NAME LIKE 'RDB$%')"; + if (tableName != null) { + sql += " AND TRIM(RDB$INDICES.RDB$RELATION_NAME)='" + tableName + "'"; + } + } else if (database instanceof DerbyDatabase) { + sql = "select c.constraintname as CONSTRAINT_NAME, tablename AS TABLE_NAME " + + "from sys.systables t, sys.sysconstraints c, sys.sysschemas s " + + "where s.schemaname='" + jdbcCatalogName + "' " + + "and t.tableid = c.tableid " + + "and t.schemaid=s.schemaid " + + "and c.type = 'U'"; + if (tableName != null) { + sql += " AND t.tablename = '" + tableName + "'"; + } + } else if (database instanceof InformixDatabase) { + sql = "select unique sysindexes.idxname as CONSTRAINT_NAME, sysindexes.idxtype, systables.tabname as TABLE_NAME " + + "from sysindexes, systables " + + "left outer join sysconstraints on sysconstraints.tabid = systables.tabid and sysconstraints.constrtype = 'P' " + + "where sysindexes.tabid = systables.tabid and sysindexes.idxtype = 'U' " + + "and sysconstraints.idxname != sysindexes.idxname " + + "and sysconstraints.tabid = sysindexes.tabid"; + if (tableName != null) { + sql += " and systables.tabname = '" + database.correctObjectName(tableName, Table.class) + "'"; + } + } else if (database instanceof SybaseDatabase) { + sql = "select idx.name as CONSTRAINT_NAME, tbl.name as TABLE_NAME " + + "from sysindexes idx " + + "inner join sysobjects tbl on tbl.id = idx.id " + + "where idx.indid between 1 and 254 " + + "and (idx.status & 2) = 2 " + + "and tbl.type = 'U'"; + if (tableName != null) { + sql += " and tbl.name = '" + database.correctObjectName(tableName, Table.class) + "'"; + } + } else if (database instanceof SybaseASADatabase) { + sql = "select sysconstraint.constraint_name, sysconstraint.constraint_type, systable.table_name " + + "from sysconstraint, systable " + + "where sysconstraint.table_object_id = systable.object_id " + + "and sysconstraint.constraint_type = 'U'"; + if (tableName != null) { + sql += " and systable.table_name = '" + tableName + "'"; + } + } else { + if (database instanceof H2Database) { + try { + if (database.getDatabaseMajorVersion() >= 2) { + sql = "select CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME " + + "from " + database.getSystemSchema() + ".table_constraints " + + "where constraint_schema='" + jdbcSchemaName + "' " + + "and constraint_catalog='" + jdbcCatalogName + "' " + + "and constraint_type='UNIQUE'"; + if (tableName != null) { + sql += " and table_name='" + tableName + "'"; + } + } + } catch (DatabaseException e) { + Scope.getCurrentScope().getLog(getClass()).fine("Cannot determine h2 version, using default unique constraint query"); + } + } + if (sql == null) { + + sql = "select CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME " + + "from " + database.getSystemSchema() + ".constraints " + + "where constraint_schema='" + jdbcSchemaName + "' " + + "and constraint_catalog='" + jdbcCatalogName + "' " + + "and constraint_type='UNIQUE'"; + if (tableName != null) { + sql += " and table_name='" + tableName + "'"; + } + } + } + + return executeAndExtract(database, database instanceof InformixDatabase, sql, parameters.toArray()); + } + }); + } + } + + private String getAllCatalogsStringScratchData() { + return (String) getScratchData(ALL_CATALOGS_STRING_SCRATCH_KEY); + } + + private String escapeForLike(String string, Database database) { + if (string == null) { + return null; + } + + if (database instanceof SQLiteDatabase || database instanceof DmDatabase) { + //sqlite jdbc's queries does not support escaped patterns. + // DM 也不支持转义的匹配方式,需要兼容 + return string; + } + + return string + .replace("%", "\\%") + .replace("_", "\\_"); + } +} diff --git a/zt-module-bpm-server/src/main/resources/application.yaml b/zt-module-bpm-server/src/main/resources/application.yaml index 37f2d24..adfbf58 100644 --- a/zt-module-bpm-server/src/main/resources/application.yaml +++ b/zt-module-bpm-server/src/main/resources/application.yaml @@ -82,9 +82,6 @@ flowable: db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置 check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程 history-level: audit # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数 - eventregistry: - enabled: true # 默认开启事件引擎,这里显式声明,便于阅读 - database-schema-update: false # 禁止事件引擎重复自动建表,防止 FLW_EV_* 表冲突 # MyBatis Plus 的配置项 mybatis-plus: From ae451f9062bae479ae0f02608ef62cc55d21451e Mon Sep 17 00:00:00 2001 From: chenbowen Date: Thu, 27 Nov 2025 16:01:05 +0800 Subject: [PATCH 24/35] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20flowable=20=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E9=80=9A=E8=BF=87=20dm=20=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E9=A9=B1=E5=8A=A8=E6=AD=A3=E5=B8=B8=E8=8E=B7=E5=8F=96=20schema?= =?UTF-8?q?=20=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../engine/impl/db/DbSqlSessionFactory.java | 354 ++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java diff --git a/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java b/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java new file mode 100644 index 0000000..ed2f0c9 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java @@ -0,0 +1,354 @@ +/* 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.flowable.common.engine.impl.db; + +import org.apache.ibatis.session.SqlSessionFactory; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.impl.context.Context; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.common.engine.impl.interceptor.Session; +import org.flowable.common.engine.impl.interceptor.SessionFactory; +import org.flowable.common.engine.impl.persistence.cache.EntityCache; +import org.flowable.common.engine.impl.persistence.entity.Entity; + +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author Tom Baeyens + * @author Joram Barrez + */ +public class DbSqlSessionFactory implements SessionFactory { + + protected Map> databaseSpecificStatements = new HashMap<>(); + + protected String databaseType; + protected String databaseTablePrefix = ""; + protected boolean tablePrefixIsSchema; + + protected String databaseCatalog; + protected String databaseSchema; + protected SqlSessionFactory sqlSessionFactory; + protected Map statementMappings; + + protected Map, String> insertStatements = new ConcurrentHashMap<>(); + protected Map, String> updateStatements = new ConcurrentHashMap<>(); + protected Map, String> deleteStatements = new ConcurrentHashMap<>(); + protected Map, String> selectStatements = new ConcurrentHashMap<>(); + + protected List> insertionOrder = new ArrayList<>(); + protected List> deletionOrder = new ArrayList<>(); + + protected boolean isDbHistoryUsed = true; + + protected Set> bulkInserteableEntityClasses = new HashSet<>(); + protected Map, String> bulkInsertStatements = new ConcurrentHashMap<>(); + + protected int maxNrOfStatementsInBulkInsert = 100; + + protected Map> logicalNameToClassMapping = new ConcurrentHashMap<>(); + + protected boolean usePrefixId; + + public DbSqlSessionFactory(boolean usePrefixId) { + this.usePrefixId = usePrefixId; + } + + @Override + public Class getSessionType() { + return DbSqlSession.class; + } + + @Override + public Session openSession(CommandContext commandContext) { + DbSqlSession dbSqlSession = createDbSqlSession(); + // 当前系统适配 dm,如果存在 schema 为空的情况,从 connection 获取 + try { + if (getDatabaseSchema() == null || getDatabaseSchema().length() == 0){ + setDatabaseSchema(dbSqlSession.getSqlSession().getConnection().getSchema()); + } + dbSqlSession.getSqlSession().getConnection().getSchema(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + + if (getDatabaseSchema() != null && getDatabaseSchema().length() > 0) { + try { + dbSqlSession.getSqlSession().getConnection().setSchema(getDatabaseSchema()); + } catch (SQLException e) { + throw new FlowableException("Could not set database schema on connection", e); + } + } + if (getDatabaseCatalog() != null && getDatabaseCatalog().length() > 0) { + try { + dbSqlSession.getSqlSession().getConnection().setCatalog(getDatabaseCatalog()); + } catch (SQLException e) { + throw new FlowableException("Could not set database catalog on connection", e); + } + } + if (dbSqlSession.getSqlSession().getConnection() == null) { + throw new FlowableException("Invalid dbSqlSession: no active connection found"); + } + return dbSqlSession; + } + + protected DbSqlSession createDbSqlSession() { + return new DbSqlSession(this, Context.getCommandContext().getSession(EntityCache.class)); + } + + // insert, update and delete statements + // ///////////////////////////////////// + + public String getInsertStatement(Entity object) { + return getStatement(object.getClass(), insertStatements, "insert"); + } + + public String getInsertStatement(Class clazz) { + return getStatement(clazz, insertStatements, "insert"); + } + + public String getUpdateStatement(Entity object) { + return getStatement(object.getClass(), updateStatements, "update"); + } + + public String getDeleteStatement(Class entityClass) { + return getStatement(entityClass, deleteStatements, "delete"); + } + + public String getSelectStatement(Class entityClass) { + return getStatement(entityClass, selectStatements, "select"); + } + + protected String getStatement(Class entityClass, Map, String> cachedStatements, String prefix) { + String statement = cachedStatements.get(entityClass); + if (statement != null) { + return statement; + } + statement = prefix + entityClass.getSimpleName(); + if (statement.endsWith("Impl")) { + statement = statement.substring(0, statement.length() - 10); // removing 'entityImpl' + } else { + statement = statement.substring(0, statement.length() - 6); // removing 'entity' + } + cachedStatements.put(entityClass, statement); + return statement; + } + + // db specific mappings + // ///////////////////////////////////////////////////// + + protected void addDatabaseSpecificStatement(String databaseType, String activitiStatement, String ibatisStatement) { + Map specificStatements = databaseSpecificStatements.get(databaseType); + if (specificStatements == null) { + specificStatements = new HashMap<>(); + databaseSpecificStatements.put(databaseType, specificStatements); + } + specificStatements.put(activitiStatement, ibatisStatement); + } + + public String mapStatement(String statement) { + if (statementMappings == null) { + return statement; + } + String mappedStatement = statementMappings.get(statement); + return (mappedStatement != null ? mappedStatement : statement); + } + + // customized getters and setters + // /////////////////////////////////////////// + + public void setDatabaseType(String databaseType) { + this.databaseType = databaseType; + this.statementMappings = databaseSpecificStatements.get(databaseType); + } + + public boolean isMysql() { + return "mysql".equals(getDatabaseType()); + } + + public boolean isOracle() { + return "oracle".equals(getDatabaseType()); + } + + public Boolean isBulkInsertable(Class entityClass) { + return bulkInserteableEntityClasses != null && bulkInserteableEntityClasses.contains(entityClass); + } + + @SuppressWarnings("rawtypes") + public String getBulkInsertStatement(Class clazz) { + return getStatement(clazz, bulkInsertStatements, "bulkInsert"); + } + + public Set> getBulkInserteableEntityClasses() { + return bulkInserteableEntityClasses; + } + + public void setBulkInserteableEntityClasses(Set> bulkInserteableEntityClasses) { + this.bulkInserteableEntityClasses = bulkInserteableEntityClasses; + } + + public int getMaxNrOfStatementsInBulkInsert() { + return maxNrOfStatementsInBulkInsert; + } + + public void setMaxNrOfStatementsInBulkInsert(int maxNrOfStatementsInBulkInsert) { + this.maxNrOfStatementsInBulkInsert = maxNrOfStatementsInBulkInsert; + } + + public Map, String> getBulkInsertStatements() { + return bulkInsertStatements; + } + + public void setBulkInsertStatements(Map, String> bulkInsertStatements) { + this.bulkInsertStatements = bulkInsertStatements; + } + + // getters and setters ////////////////////////////////////////////////////// + + public SqlSessionFactory getSqlSessionFactory() { + return sqlSessionFactory; + } + + public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { + this.sqlSessionFactory = sqlSessionFactory; + } + + public String getDatabaseType() { + return databaseType; + } + + public Map> getDatabaseSpecificStatements() { + return databaseSpecificStatements; + } + + public void setDatabaseSpecificStatements(Map> databaseSpecificStatements) { + this.databaseSpecificStatements = databaseSpecificStatements; + } + + public Map getStatementMappings() { + return statementMappings; + } + + public void setStatementMappings(Map statementMappings) { + this.statementMappings = statementMappings; + } + + public Map, String> getInsertStatements() { + return insertStatements; + } + + public void setInsertStatements(Map, String> insertStatements) { + this.insertStatements = insertStatements; + } + + public Map, String> getUpdateStatements() { + return updateStatements; + } + + public void setUpdateStatements(Map, String> updateStatements) { + this.updateStatements = updateStatements; + } + + public Map, String> getDeleteStatements() { + return deleteStatements; + } + + public void setDeleteStatements(Map, String> deleteStatements) { + this.deleteStatements = deleteStatements; + } + + public Map, String> getSelectStatements() { + return selectStatements; + } + + public void setSelectStatements(Map, String> selectStatements) { + this.selectStatements = selectStatements; + } + + public boolean isDbHistoryUsed() { + return isDbHistoryUsed; + } + + public void setDbHistoryUsed(boolean isDbHistoryUsed) { + this.isDbHistoryUsed = isDbHistoryUsed; + } + + public void setDatabaseTablePrefix(String databaseTablePrefix) { + this.databaseTablePrefix = databaseTablePrefix; + } + + public String getDatabaseTablePrefix() { + return databaseTablePrefix; + } + + public String getDatabaseCatalog() { + return databaseCatalog; + } + + public void setDatabaseCatalog(String databaseCatalog) { + this.databaseCatalog = databaseCatalog; + } + + public String getDatabaseSchema() { + return databaseSchema; + } + + public void setDatabaseSchema(String databaseSchema) { + this.databaseSchema = databaseSchema; + } + + public void setTablePrefixIsSchema(boolean tablePrefixIsSchema) { + this.tablePrefixIsSchema = tablePrefixIsSchema; + } + + public boolean isTablePrefixIsSchema() { + return tablePrefixIsSchema; + } + + public List> getInsertionOrder() { + return insertionOrder; + } + + public void setInsertionOrder(List> insertionOrder) { + this.insertionOrder = insertionOrder; + } + + public List> getDeletionOrder() { + return deletionOrder; + } + + public void setDeletionOrder(List> deletionOrder) { + this.deletionOrder = deletionOrder; + } + public void addLogicalEntityClassMapping(String logicalName, Class entityClass) { + logicalNameToClassMapping.put(logicalName, entityClass); + } + + public Map> getLogicalNameToClassMapping() { + return logicalNameToClassMapping; + } + + public void setLogicalNameToClassMapping(Map> logicalNameToClassMapping) { + this.logicalNameToClassMapping = logicalNameToClassMapping; + } + + public boolean isUsePrefixId() { + return usePrefixId; + } + + public void setUsePrefixId(boolean usePrefixId) { + this.usePrefixId = usePrefixId; + } +} From 6dace51774cfcd1dec0a6fd4b59a499e1f66d9a5 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Fri, 28 Nov 2025 18:12:00 +0800 Subject: [PATCH 25/35] =?UTF-8?q?1.=20=E5=89=94=E9=99=A4=E6=8E=89=E5=A4=9A?= =?UTF-8?q?=E4=BD=99=E7=9A=84=E6=96=87=E6=A1=A3=E4=BB=A5=E5=8F=8A=E6=A0=87?= =?UTF-8?q?=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bpm/api/definition/dto/BpmFormPageReqDTO.java | 2 +- .../module/bpm/api/definition/dto/BpmFormRespDTO.java | 2 +- .../bpm/api/definition/dto/BpmFormSaveReqDTO.java | 2 +- .../bpm/api/definition/dto/BpmUserGroupRespDTO.java | 4 ++-- .../module/bpm/api/task/BpmProcessInstanceApi.java | 2 +- .../bpm/api/task/dto/BpmApprovalDetailReqDTO.java | 2 +- .../bpm/api/task/dto/BpmProcessInstancePageReqDTO.java | 2 +- .../bpm/api/task/dto/BpmProcessInstanceRespDTO.java | 6 +++--- .../module/bpm/api/task/dto/BpmTaskPageReqDTO.java | 2 +- .../plat/module/bpm/api/task/dto/BpmTaskRespDTO.java | 6 +++--- .../zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java | 2 +- zt-module-bpm-server/pom.xml | 2 +- .../controller/admin/base/user/UserSimpleBaseVO.java | 2 +- .../admin/definition/BpmModelController.java | 2 +- .../admin/definition/vo/form/BpmFormPageReqVO.java | 2 +- .../admin/definition/vo/form/BpmFormRespVO.java | 2 +- .../admin/definition/vo/form/BpmFormSaveReqVO.java | 2 +- .../definition/vo/group/BpmUserGroupPageReqVO.java | 2 +- .../admin/definition/vo/group/BpmUserGroupRespVO.java | 4 ++-- .../definition/vo/group/BpmUserGroupSaveReqVO.java | 4 ++-- .../admin/definition/vo/model/BpmModelRespVO.java | 2 +- .../admin/definition/vo/model/BpmModelSaveReqVO.java | 2 +- .../vo/model/simple/BpmSimpleModelNodeVO.java | 2 +- .../vo/process/BpmProcessDefinitionRespVO.java | 2 +- .../controller/admin/oa/vo/BpmOALeaveCreateReqVO.java | 2 +- .../controller/admin/oa/vo/BpmOALeavePageReqVO.java | 2 +- .../bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java | 2 +- .../admin/task/vo/instance/BpmApprovalDetailReqVO.java | 2 +- .../vo/instance/BpmProcessInstanceCopyPageReqVO.java | 2 +- .../task/vo/instance/BpmProcessInstancePageReqVO.java | 2 +- .../task/vo/instance/BpmProcessInstanceRespVO.java | 4 ++-- .../admin/task/vo/task/BpmTaskPageReqVO.java | 2 +- .../controller/admin/task/vo/task/BpmTaskRespVO.java | 4 ++-- .../《芋道 Spring Boot 对象转换 MapStruct 入门》.md | 1 - .../framework/flowable/core/util/BpmnModelUtils.java | 2 +- .../framework/flowable/core/util/SimpleModelUtils.java | 10 +++++----- .../security/config/SecurityConfiguration.java | 2 +- .../bpm/service/definition/BpmFormServiceImpl.java | 2 +- .../module/bpm/service/message/BpmMessageService.java | 2 +- .../service/task/BpmProcessInstanceServiceImpl.java | 6 +++--- .../module/bpm/service/task/BpmTaskServiceImpl.java | 6 +++--- .../bpm/service/task/listener/BpmUserTaskListener.java | 6 +++--- .../module/bpm/service/task/trigger/BpmTrigger.java | 2 +- .../src/main/resources/application-dev.yaml | 2 +- .../src/main/resources/application-local.yaml | 6 +++--- .../src/main/resources/application.yaml | 4 ++-- .../other/BpmTaskCandidateExpressionStrategyTest.java | 2 +- .../user/BpmTaskCandidateGroupStrategyTest.java | 2 +- .../user/BpmTaskCandidatePostStrategyTest.java | 2 +- .../user/BpmTaskCandidateRoleStrategyTest.java | 2 +- .../user/BpmTaskCandidateUserStrategyTest.java | 2 +- .../bpm/service/definition/BpmFormServiceTest.java | 4 ++-- .../service/definition/BpmUserGroupServiceTest.java | 4 ++-- .../src/test/resources/application-unit-test.yaml | 4 ++-- 54 files changed, 78 insertions(+), 79 deletions(-) delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java index 4ed3bc3..d75ff78 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java @@ -12,7 +12,7 @@ import lombok.ToString; @ToString(callSuper = true) public class BpmFormPageReqDTO extends PageParam { - @Schema(description = "表单名称", example = "芋道") + @Schema(description = "表单名称", example = "ZT") private String name; } \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java index a8cb537..e33b513 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java @@ -12,7 +12,7 @@ public class BpmFormRespDTO { @Schema(description = "表单编号", example = "1024") private Long id; - @Schema(description = "表单名", example = "芋艿") + @Schema(description = "表单名", example = "ZT") private String name; @Schema(description = "表单状态", example = "1") diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java index 787a8cd..a2b4ab3 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java @@ -11,7 +11,7 @@ public class BpmFormSaveReqDTO { @Schema(description = "表单编号", example = "1024") private Long id; - @Schema(description = "表单名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @Schema(description = "表单名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") @NotEmpty(message = "表单名不能为空") private String name; diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java index 854efa6..f8acdeb 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java @@ -13,10 +13,10 @@ public class BpmUserGroupRespDTO { @Schema(description = "编号", example = "1024") private Long id; - @Schema(description = "组名", example = "芋艿") + @Schema(description = "组名", example = "ZT") private String name; - @Schema(description = "描述", example = "芋艿") + @Schema(description = "描述", example = "ZT") private String description; @Schema(description = "成员用户编号数组", example = "1,2,3") diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java index 89839e8..5a2d905 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; -@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = +@FeignClient(name = ApiConstants.NAME) // TODO ZT:fallbackFactory = @Tag(name = "RPC 服务 - 流程实例") public interface BpmProcessInstanceApi { diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java index 4794386..2dc63a6 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java @@ -24,7 +24,7 @@ public class BpmApprovalDetailReqDTO { @Schema(description = "流程实例的编号", example = "1024") private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID - // TODO @芋艿:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 + // TODO @ZT:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 @Schema(description = "流程活动编号", example = "StartUserNode") private String activityId; // 用于获取表单权限。1)发起流程时,传"发起人节点" activityId 可获取发起人的表单权限;2)从抄送列表界面进来时,传抄送的 activityId 可获取抄送人的表单权限; diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java index ce6c38c..f23c182 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java @@ -16,7 +16,7 @@ public class BpmProcessInstancePageReqDTO extends PageParam { @Schema(description = "流程实例的编号", example = "1024") private String id; - @Schema(description = "流程实例的名字", example = "芋艿") + @Schema(description = "流程实例的名字", example = "ZT") private String name; @Schema(description = "流程定义的编号", example = "2048") diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java index 48c779c..eabc038 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java @@ -16,7 +16,7 @@ public class BpmProcessInstanceRespDTO { @Schema(description = "流程实例的编号", example = "1024") private String id; - @Schema(description = "流程实例的名字", example = "芋艿") + @Schema(description = "流程实例的名字", example = "ZT") private String name; @Schema(description = "流程摘要") @@ -49,7 +49,7 @@ public class BpmProcessInstanceRespDTO { @Schema(description = "持续时间", example = "1000") private Long durationInMillis; - @Schema(description = "提交的表单值", example = "{\"name\": \"芋艿\"}") + @Schema(description = "提交的表单值", example = "{\"name\": \"ZT\"}") private Map formVariables; @Schema(description = "业务的唯一标识", example = "1") @@ -77,7 +77,7 @@ public class BpmProcessInstanceRespDTO { @Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java index fae7055..6ed4a8c 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java @@ -13,7 +13,7 @@ import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH @Data public class BpmTaskPageReqDTO extends PageParam { - @Schema(description = "流程任务名", example = "芋艿") + @Schema(description = "流程任务名", example = "ZT") private String name; @Schema(description = "流程定义的编号", example = "2048") diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java index 412c098..17c5a44 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java @@ -15,7 +15,7 @@ public class BpmTaskRespDTO { @Schema(description = "任务编号", example = "1024") private String id; - @Schema(description = "任务名字", example = "芋艿") + @Schema(description = "任务名字", example = "ZT") private String name; @Schema(description = "接收人的用户编号", example = "1") @@ -60,7 +60,7 @@ public class BpmTaskRespDTO { @Schema(description = "表单项的数组", example = "[]") private List formFields; - @Schema(description = "提交的表单值", example = "{\"name\": \"芋艿\"}") + @Schema(description = "提交的表单值", example = "{\"name\": \"ZT\"}") private Map formVariables; @Schema(description = "任务负责人编号", example = "2048") @@ -103,7 +103,7 @@ public class BpmTaskRespDTO { @Schema(description = "流程实例编号", example = "1024") private String id; - @Schema(description = "流程实例名称", example = "芋道") + @Schema(description = "流程实例名称", example = "ZT") private String name; @Schema(description = "提交时间") diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java index ddb00d6..295d43b 100644 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java +++ b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java @@ -10,7 +10,7 @@ public class UserSimpleDTO { @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long id; - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String nickname; @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png") diff --git a/zt-module-bpm-server/pom.xml b/zt-module-bpm-server/pom.xml index 06a5ac4..4b151c3 100644 --- a/zt-module-bpm-server/pom.xml +++ b/zt-module-bpm-server/pom.xml @@ -79,7 +79,7 @@ spring-cloud-starter-alibaba-nacos-config - + diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java index 6fcbb96..68b9b8a 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java @@ -9,7 +9,7 @@ public class UserSimpleBaseVO { @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long id; - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String nickname; @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png") private String avatar; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java index f787066..8a408e8 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java @@ -60,7 +60,7 @@ public class BpmModelController { @GetMapping("/list") @Operation(summary = "获得模型分页") - @Parameter(name = "name", description = "模型名称", example = "芋艿") + @Parameter(name = "name", description = "模型名称", example = "ZT") public CommonResult> getModelList(@RequestParam(value = "name", required = false) String name) { List list = modelService.getModelList(name); if (CollUtil.isEmpty(list)) { diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java index 437f67c..4160512 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java @@ -8,7 +8,7 @@ import lombok.Data; @Data public class BpmFormPageReqVO extends PageParam { - @Schema(description = "表单名称", example = "芋道") + @Schema(description = "表单名称", example = "ZT") private String name; } diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java index 42295df..1950d7a 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java @@ -14,7 +14,7 @@ public class BpmFormRespVO { @Schema(description = "表单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; - @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") @NotNull(message = "表单名称不能为空") private String name; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java index faa420b..5953485 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java @@ -13,7 +13,7 @@ public class BpmFormSaveReqVO { @Schema(description = "表单编号", example = "1024") private Long id; - @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") @NotNull(message = "表单名称不能为空") private String name; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java index a146e35..25a0c2f 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java @@ -15,7 +15,7 @@ public class BpmUserGroupPageReqVO extends PageParam { @Schema(description = "编号", example = "1024") private Long id; - @Schema(description = "组名", example = "芋道") + @Schema(description = "组名", example = "ZT") private String name; @Schema(description = "状态", example = "1") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java index b356351..7bff95d 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java @@ -13,10 +13,10 @@ public class BpmUserGroupRespVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; - @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; - @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT源码") private String description; @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java index ce2c7d9..ceccee3 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java @@ -13,11 +13,11 @@ public class BpmUserGroupSaveReqVO { @Schema(description = "编号", example = "1024") private Long id; - @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") @NotNull(message = "组名不能为空") private String name; - @Schema(description = "描述", example = "芋道源码") + @Schema(description = "描述", example = "ZT源码") private String description; @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java index 12d6ac1..e0b32f3 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java @@ -20,7 +20,7 @@ public class BpmModelRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_zt") private String key; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "流程图标", example = "https://www.iocoder.cn/zt.jpg") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java index 279519c..905e616 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java @@ -17,7 +17,7 @@ public class BpmModelSaveReqVO extends BpmModelMetaInfoVO { @NotEmpty(message = "流程标识不能为空") private String key; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") @NotEmpty(message = "流程名称不能为空") private String name; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 8dc222c..cd5177a 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -35,7 +35,7 @@ public class BpmSimpleModelNodeVO { @Schema(description = "模型节点名称", example = "领导审批") private String name; - @Schema(description = "节点展示内容", example = "指定成员: 芋道源码") + @Schema(description = "节点展示内容", example = "指定成员: ZT源码") private String showText; @Schema(description = "子节点") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java index 5e65c0f..f0d2892 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -17,7 +17,7 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer version; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java index 4078b99..00779aa 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java @@ -29,7 +29,7 @@ public class BpmOALeaveCreateReqVO { @Schema(description = "请假类型-参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer type; - @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码") + @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读ZT源码") private String reason; @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java index f4226a4..31c9f1b 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java @@ -19,7 +19,7 @@ public class BpmOALeavePageReqVO extends PageParam { @Schema(description = "请假类型,参见 bpm_oa_type", example = "1") private Integer type; - @Schema(description = "原因,模糊匹配", example = "阅读芋道源码") + @Schema(description = "原因,模糊匹配", example = "阅读ZT源码") private String reason; @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java index 02a5555..ae43fcd 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java @@ -15,7 +15,7 @@ public class BpmOALeaveRespVO { @Schema(description = "请假类型,参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer type; - @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码") + @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读ZT源码") private String reason; @Schema(description = "申请时间", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java index 3f37d7a..44c775b 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java @@ -24,7 +24,7 @@ public class BpmApprovalDetailReqVO { @Schema(description = "流程实例的编号", example = "1024") private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID - // TODO @芋艿:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 + // TODO @ZT:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 @Schema(description = "流程活动编号", example = "StartUserNode") private String activityId; // 用于获取表单权限。1)发起流程时,传“发起人节点” activityId 可获取发起人的表单权限;2)从抄送列表界面进来时,传抄送的 activityId 可获取抄送人的表单权限; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java index b7956e4..2aff881 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java @@ -13,7 +13,7 @@ import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH @Data public class BpmProcessInstanceCopyPageReqVO extends PageParam { - @Schema(description = "流程名称", example = "芋道") + @Schema(description = "流程名称", example = "ZT") private String processInstanceName; @Schema(description = "创建时间") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java index 33875b6..130d75b 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java @@ -15,7 +15,7 @@ import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH @Data public class BpmProcessInstancePageReqVO extends PageParam { - @Schema(description = "流程名称", example = "芋道") + @Schema(description = "流程名称", example = "ZT") private String name; @Schema(description = "流程定义的标识", example = "2048") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java index fb88587..14cc77a 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java @@ -18,7 +18,7 @@ public class BpmProcessInstanceRespVO { @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "流程摘要") @@ -71,7 +71,7 @@ public class BpmProcessInstanceRespVO { @Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java index c7c1721..5c1ee94 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java @@ -12,7 +12,7 @@ import java.time.LocalDateTime; @Data public class BpmTaskPageReqVO extends PageParam { - @Schema(description = "流程任务名", example = "芋道") + @Schema(description = "流程任务名", example = "ZT") private String name; @Schema(description = "流程分类", example = "1") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java index 109e26f..fb86f57 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -17,7 +17,7 @@ public class BpmTaskRespVO { @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "任务名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "任务名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @@ -97,7 +97,7 @@ public class BpmTaskRespVO { @Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "流程实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "流程实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "提交时间", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md deleted file mode 100644 index d796f41..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md +++ /dev/null @@ -1 +0,0 @@ - diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 1be0cd5..b53a819 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -123,7 +123,7 @@ public class BpmnModelUtils { public static Integer parseCandidateStrategy(FlowElement userTask) { Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue( BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); - // TODO @芋艿 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限 + // TODO @ZT 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限 if (candidateStrategy == null) { ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); candidateStrategy = element != null ? NumberUtils.parseInt(element.getElementText()) : null; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index 3f2408d..cb03e01 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -208,7 +208,7 @@ public class SimpleModelUtils { BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType()); BpmSimpleModelNodeVO childNode = node.getChildNode(); List conditionNodes = node.getConditionNodes(); - // TODO @芋艿 路由分支没有conditionNodes 这里注释会影响吗?@jason:一起帮忙瞅瞅! + // TODO @ZT 路由分支没有conditionNodes 这里注释会影响吗?@jason:一起帮忙瞅瞅! // Assert.notEmpty(conditionNodes, "分支节点的条件节点不能为空"); // 分支终点节点 ID String branchEndNodeId = null; @@ -277,8 +277,8 @@ public class SimpleModelUtils { String conditionExpression) { Assert.notEmpty(sourceId, "sourceId 不能为空"); Assert.notEmpty(targetId, "targetId 不能为空"); - // TODO @jason:如果 sequenceFlowId 不存在的时候,是不是要生成一个默认的 sequenceFlowId? @芋艿: 貌似不需要,Flowable 会默认生成;TODO @jason:建议还是搞一个,主要是后续好排查问题。 - // TODO @jason:如果 name 不存在的时候,是不是要生成一个默认的 name? @芋艿: 不需要生成默认的吧? 这个会在流程图展示的, 一般用户填写的。不好生成默认的吧;TODO @jason:建议还是搞一个,主要是后续好排查问题。 + // TODO @jason:如果 sequenceFlowId 不存在的时候,是不是要生成一个默认的 sequenceFlowId? @ZT: 貌似不需要,Flowable 会默认生成;TODO @jason:建议还是搞一个,主要是后续好排查问题。 + // TODO @jason:如果 name 不存在的时候,是不是要生成一个默认的 name? @ZT: 不需要生成默认的吧? 这个会在流程图展示的, 一般用户填写的。不好生成默认的吧;TODO @jason:建议还是搞一个,主要是后续好排查问题。 SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId); if (StrUtil.isNotEmpty(sequenceFlowId)) { sequenceFlow.setId(sequenceFlowId); @@ -341,7 +341,7 @@ public class SimpleModelUtils { EndEvent endEvent = new EndEvent(); endEvent.setId(node.getId()); endEvent.setName(node.getName()); - // TODO @芋艿 + jason:要不要加一个终止定义? + // TODO @ZT + jason:要不要加一个终止定义? return endEvent; } @@ -369,7 +369,7 @@ public class SimpleModelUtils { // 添加操作按钮配置属性元素 addButtonsSetting(node.getButtonsSetting(), userTask); // 使用自动通过策略 - // TODO @芋艿 复用了SKIP, 是否需要新加一个策略;TODO @芋艿:【回复】是不是应该类似飞书,搞个草稿状态。待定;还有一种策略,不标记自动通过,而是首次发起后,第一个节点,自动通过; + // TODO @ZT 复用了SKIP, 是否需要新加一个策略;TODO @ZT:【回复】是不是应该类似飞书,搞个草稿状态。待定;还有一种策略,不标记自动通过,而是首次发起后,第一个节点,自动通过; addAssignStartUserHandlerType(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType(), userTask); return userTask; } diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java index eb6e099..5e59e23 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java @@ -19,7 +19,7 @@ public class SecurityConfiguration { @Override public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { - // TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案 + // TODO ZT:这个每个项目都需要重复配置,得捉摸有没通用的方案 // Swagger 接口文档 registry.requestMatchers("/v3/api-docs/**").permitAll() .requestMatchers("/webjars/**").permitAll() diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java index 73511ad..768239d 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java @@ -94,7 +94,7 @@ public class BpmFormServiceImpl implements BpmFormService { * @param fields field 数组 */ private void validateFields(List fields) { - if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验 + if (true) { // TODO ZT:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验 return; } Map fieldMap = new HashMap<>(); // key 是 vModel,value 是 label diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java index 1698829..ccbd904 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java @@ -9,7 +9,7 @@ import jakarta.validation.Valid; /** * BPM 消息 Service 接口 * - * TODO 芋艿:未来支持消息的可配置;不同的流程,在什么场景下,需要发送什么消息,消息的内容是什么; + * TODO ZT:未来支持消息的可配置;不同的流程,在什么场景下,需要发送什么消息,消息的内容是什么; * * @author ZT */ diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index fc01ac7..d675a7e 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -495,7 +495,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService for (HistoricActivityInstance activity : taskActivities) { HistoricTaskInstance task = taskMap.get(activity.getTaskId()); // 特殊情况:子流程节点 ChildProcess 仅存在于 activity 中,并且没有自身的 task,需要跳过执行 - // TODO @芋艿:后续看看怎么优化! + // TODO @ZT:后续看看怎么优化! if (task == null) { continue; } @@ -535,7 +535,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, List activities) { - // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance + // TODO @ZT:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); // 情况一:BPMN 设计器 @@ -558,7 +558,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, BpmSimpleModelNodeVO node, Set runActivityIds) { - // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance + // TODO @ZT:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 if (runActivityIds.contains(node.getId())) { return null; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java index 1c44b8c..416444b 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java @@ -1008,7 +1008,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { .changeState(); // 3. 特殊:如果跳转到 EndEvent 流程还未结束, 执行 deleteProcessInstance 方法 - // TODO 芋艿:目前发现并行分支情况下,会存在这个情况,后续看看有没更好的方案; + // TODO ZT:目前发现并行分支情况下,会存在这个情况,后续看看有没更好的方案; List executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list(); if (CollUtil.isNotEmpty(executions)) { log.warn("[moveTaskToEnd][执行跳转到 EndEvent 后, 流程实例未结束,强制执行 deleteProcessInstance 方法]"); @@ -1331,7 +1331,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { return; } - // 自动去重,通过自动审批的方式 TODO @芋艿 驳回的情况得考虑一下;@lesan:驳回后,又自动审批么? + // 自动去重,通过自动审批的方式 TODO @ZT 驳回的情况得考虑一下;@lesan:驳回后,又自动审批么? BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId()); if (processDefinitionInfo == null) { log.error("[processTaskAssigned][taskId({}) 没有找到流程定义({})]", task.getId(), task.getProcessDefinitionId()); @@ -1374,7 +1374,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 - // TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 + // TODO ZT:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java index 2102560..ec0f164 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java @@ -16,7 +16,7 @@ import org.springframework.stereotype.Component; import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig; -// TODO @芋艿:可能会想换个包地址 +// TODO @ZT:可能会想换个包地址 /** * BPM 用户任务通用监听器 * @@ -42,7 +42,7 @@ public class BpmUserTaskListener implements TaskListener { BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig); // 2. 发起请求 - // TODO @芋艿:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去; + // TODO @ZT:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去; listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId") .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getProcessInstanceId())); listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee") @@ -54,6 +54,6 @@ public class BpmUserTaskListener implements TaskListener { BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, listenerHandler.getPath(), listenerHandler.getHeader(), listenerHandler.getBody(), false, null); - // 3. 是否需要后续操作?TODO 芋艿:待定! + // 3. 是否需要后续操作?TODO ZT:待定! } } \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java index 02fb2d5..8aee717 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java @@ -2,7 +2,7 @@ package com.zt.plat.module.bpm.service.task.trigger; import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; -// TODO @芋艿:可能会想换个包地址 +// TODO @ZT:可能会想换个包地址 /** * BPM 触发器接口 *

diff --git a/zt-module-bpm-server/src/main/resources/application-dev.yaml b/zt-module-bpm-server/src/main/resources/application-dev.yaml index b6b318c..26fb687 100644 --- a/zt-module-bpm-server/src/main/resources/application-dev.yaml +++ b/zt-module-bpm-server/src/main/resources/application-dev.yaml @@ -89,6 +89,6 @@ spring: instance: service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] ---- #################### 芋道相关配置 #################### +--- #################### ZT相关配置 #################### diff --git a/zt-module-bpm-server/src/main/resources/application-local.yaml b/zt-module-bpm-server/src/main/resources/application-local.yaml index 1a65166..ae73a0b 100644 --- a/zt-module-bpm-server/src/main/resources/application-local.yaml +++ b/zt-module-bpm-server/src/main/resources/application-local.yaml @@ -102,11 +102,11 @@ logging: level: # 配置自己写的 MyBatis Mapper 打印日志 com.zt.plat.module.bpm.dal.mysql: debug - org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO ZT:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 ---- #################### 芋道相关配置 #################### +--- #################### ZT相关配置 #################### -# 芋道配置项,设置当前项目所有自定义的配置 +# ZT配置项,设置当前项目所有自定义的配置 zt: env: # 多环境的配置项 tag: ${HOSTNAME} diff --git a/zt-module-bpm-server/src/main/resources/application.yaml b/zt-module-bpm-server/src/main/resources/application.yaml index adfbf58..bf3f40d 100644 --- a/zt-module-bpm-server/src/main/resources/application.yaml +++ b/zt-module-bpm-server/src/main/resources/application.yaml @@ -68,7 +68,7 @@ springdoc: default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 knife4j: - enable: false # TODO 芋艿:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 + enable: false # TODO ZT:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 setting: language: zh_cn @@ -127,7 +127,7 @@ xxl: logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 accessToken: default_token # 执行器通讯TOKEN ---- #################### 芋道相关配置 #################### +--- #################### ZT相关配置 #################### zt: info: diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java index d9ad569..dc50daa 100644 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java @@ -17,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; -@Disabled // TODO 芋艿:临时注释 +@Disabled // TODO ZT:临时注释 public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest { @InjectMocks diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java index 1bb03b5..4caed36 100644 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java @@ -17,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -@Disabled // TODO 芋艿:临时注释 +@Disabled // TODO ZT:临时注释 public class BpmTaskCandidateGroupStrategyTest extends BaseMockitoUnitTest { @InjectMocks diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java index 8560052..503a984 100644 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java @@ -19,7 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -@Disabled // TODO 芋艿:临时注释 +@Disabled // TODO ZT:临时注释 public class BpmTaskCandidatePostStrategyTest extends BaseMockitoUnitTest { @InjectMocks diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java index 9af421a..0250e74 100644 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java @@ -16,7 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -@Disabled // TODO 芋艿:临时注释 +@Disabled // TODO ZT:临时注释 public class BpmTaskCandidateRoleStrategyTest extends BaseMockitoUnitTest { @InjectMocks diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java index 91e19c5..557e2d5 100644 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java @@ -10,7 +10,7 @@ import java.util.Set; import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; import static org.junit.jupiter.api.Assertions.assertEquals; -@Disabled // TODO 芋艿:临时注释 +@Disabled // TODO ZT:临时注释 public class BpmTaskCandidateUserStrategyTest extends BaseMockitoUnitTest { @InjectMocks diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java index ac03d9e..2bb6fba 100644 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java @@ -117,14 +117,14 @@ public class BpmFormServiceTest extends BaseDbUnitTest { public void testGetFormPage() { // mock 数据 BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> { // 等会查询到 - o.setName("芋道源码"); + o.setName("ZT源码"); }); formMapper.insert(dbForm); // 测试 name 不匹配 formMapper.insert(cloneIgnoreId(dbForm, o -> o.setName("源码"))); // 准备参数 BpmFormPageReqVO reqVO = new BpmFormPageReqVO(); - reqVO.setName("芋道"); + reqVO.setName("ZT"); // 调用 PageResult pageResult = formService.getFormPage(reqVO); diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java index 88b1d50..a74e9d3 100644 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java +++ b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java @@ -102,13 +102,13 @@ public class BpmUserGroupServiceTest extends BaseDbUnitTest { public void testGetUserGroupPage() { // mock 数据 BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class, o -> { // 等会查询到 - o.setName("芋道源码"); + o.setName("ZT源码"); o.setStatus(CommonStatusEnum.ENABLE.getStatus()); o.setCreateTime(buildTime(2021, 11, 11)); }); userGroupMapper.insert(dbUserGroup); // 测试 name 不匹配 - userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("芋道"))); + userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("ZT"))); // 测试 status 不匹配 userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); // 测试 createTime 不匹配 diff --git a/zt-module-bpm-server/src/test/resources/application-unit-test.yaml b/zt-module-bpm-server/src/test/resources/application-unit-test.yaml index a669ee0..e92e3da 100644 --- a/zt-module-bpm-server/src/test/resources/application-unit-test.yaml +++ b/zt-module-bpm-server/src/test/resources/application-unit-test.yaml @@ -37,9 +37,9 @@ mybatis-plus: --- #################### 监控相关配置 #################### ---- #################### 芋道相关配置 #################### +--- #################### ZT相关配置 #################### -# 芋道配置项,设置当前项目所有自定义的配置 +# ZT配置项,设置当前项目所有自定义的配置 zt: info: base-package: com.zt.plat.module.bpm From bd602cb3cd654c0938b4608bdcb18cfa5d8c4ffd Mon Sep 17 00:00:00 2001 From: chenbowen Date: Wed, 3 Dec 2025 17:59:56 +0800 Subject: [PATCH 26/35] =?UTF-8?q?=E6=8F=90=E5=8D=87=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E7=9A=84=20xmx=20=E8=87=B3=201024mb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile index 230aa5c..868eec5 100644 --- a/zt-module-bpm-server/Dockerfile +++ b/zt-module-bpm-server/Dockerfile @@ -10,7 +10,7 @@ COPY ./target/zt-module-bpm-server.jar app.jar ## 设置 TZ 时区 ## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 -ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m" +ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx1024m" ## 暴露后端项目的 48080 端口 EXPOSE 48083 From 0740da8559c5f3bba3db16441342f52d0d95a9d3 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Wed, 17 Dec 2025 20:30:33 +0800 Subject: [PATCH 27/35] =?UTF-8?q?1.=20=E8=B0=83=E6=95=B4=E6=96=B0=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E7=9A=84=E5=91=BD=E5=90=8D=E7=A9=BA=E9=97=B4=E5=92=8C?= =?UTF-8?q?=20harbo=20=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile index 868eec5..b8f089e 100644 --- a/zt-module-bpm-server/Dockerfile +++ b/zt-module-bpm-server/Dockerfile @@ -1,6 +1,6 @@ ## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 -FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre +FROM 172.17.19.16:10043/zt-cloud-base-service/eclipse-temurin:21-jre ## 创建目录,并使用它作为工作目录 RUN mkdir -p /zt-module-bpm-server From 92f60e7132f199f6dcbb28c48647947cb286cc31 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Sat, 27 Dec 2025 12:48:56 +0800 Subject: [PATCH 28/35] =?UTF-8?q?1.=20=E4=B8=B4=E6=97=B6=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E9=9B=86=E7=BE=A4=E9=85=8D=E5=90=88=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm-server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile index 868eec5..b8f089e 100644 --- a/zt-module-bpm-server/Dockerfile +++ b/zt-module-bpm-server/Dockerfile @@ -1,6 +1,6 @@ ## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 -FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre +FROM 172.17.19.16:10043/zt-cloud-base-service/eclipse-temurin:21-jre ## 创建目录,并使用它作为工作目录 RUN mkdir -p /zt-module-bpm-server From 30f182828ad7ec2819a65cd31c358800ebdfce55 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Sat, 27 Dec 2025 13:12:22 +0800 Subject: [PATCH 29/35] =?UTF-8?q?Revert=20"1.=20=E4=B8=B4=E6=97=B6?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E9=9B=86=E7=BE=A4=E9=85=8D=E5=90=88=E6=B5=8B?= =?UTF-8?q?=E8=AF=95"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 3c3ac230080104fffbcfe32f53b3dde9fe12991e. --- zt-module-bpm-server/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile index b8f089e..868eec5 100644 --- a/zt-module-bpm-server/Dockerfile +++ b/zt-module-bpm-server/Dockerfile @@ -1,6 +1,6 @@ ## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 -FROM 172.17.19.16:10043/zt-cloud-base-service/eclipse-temurin:21-jre +FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre ## 创建目录,并使用它作为工作目录 RUN mkdir -p /zt-module-bpm-server From 0eff938f9e57fd202ea23fa8db4a8b6f6d8c200c Mon Sep 17 00:00:00 2001 From: ranke <213539@qq.com> Date: Tue, 30 Dec 2025 18:07:58 +0800 Subject: [PATCH 30/35] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E7=AE=A1=E6=8E=A7=E6=8C=89=E9=92=AE=E6=98=AF?= =?UTF-8?q?=E5=90=A6=E5=8F=AF=E7=94=A8=E9=85=8D=E7=BD=AE=E5=8A=9F=E8=83=BD?= =?UTF-8?q?.=20http://172.16.46.63:31560/index.php=3Fm=3Dtask&f=3Dview&tas?= =?UTF-8?q?kID=3D551?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../definition/vo/model/BpmModelMetaInfoVO.java | 4 ++++ .../admin/definition/vo/model/BpmModelRespVO.java | 1 + .../definition/vo/model/BpmModelSaveReqVO.java | 1 + .../bpm/convert/task/BpmProcessInstanceConvert.java | 13 ++++++++++--- .../definition/BpmProcessDefinitionInfoDO.java | 7 +++++++ 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index cabc1fd..dcc06fc 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -56,6 +56,10 @@ public class BpmModelMetaInfoVO { @NotNull(message = "是否可见不能为空") private Boolean visible; + @Schema(description = "是否允许重新发起", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否允许重新发起不能为空") + private Boolean restart; + @Schema(description = "可发起用户编号数组", example = "[1,2,3]") private List startUserIds; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java index e0b32f3..63ef57d 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java @@ -5,6 +5,7 @@ import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import lombok.Data; import java.time.LocalDateTime; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java index 905e616..6d57341 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java @@ -4,6 +4,7 @@ import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSim import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; @Schema(description = "管理后台 - 流程模型的保存 Request VO") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java index 791e694..87652f8 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java @@ -87,9 +87,16 @@ public interface BpmProcessInstanceConvert { }); } } - // 摘要 - respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(respVO.getProcessDefinitionId()), - pageResult.getList().get(i).getProcessVariables())); + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionInfoMap.get(respVO.getProcessDefinitionId()); + if (processDefinitionInfo != null) { + // 摘要 + respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfo, + pageResult.getList().get(i).getProcessVariables())); + // 是否可见 + respVO.getProcessDefinition().setVisible(processDefinitionInfo.getVisible()); + // 是否可以重新发起流程 + respVO.getProcessDefinition().setRestart(processDefinitionInfo.getRestart()); + } // 表单 respVO.setFormVariables(pageResult.getList().get(i).getProcessVariables()); } diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java index 0ebef4b..349f404 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -129,6 +129,13 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { * 目的:如果 false 不可见,则不展示在“发起流程”的列表里 */ private Boolean visible; + + /** + * 是否允许重新发起 + * + * 目的:如果 false 则不可以重新发起流程 + */ + private Boolean restart; /** * 排序值 */ From ea9474895c9da547483d8fae04b98d0620f19b67 Mon Sep 17 00:00:00 2001 From: ranke <213539@qq.com> Date: Wed, 31 Dec 2025 17:06:51 +0800 Subject: [PATCH 31/35] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A0=B9=E6=8D=AE?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=E5=AE=9E=E4=BE=8BID=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=8A=84=E9=80=81=E8=AE=B0=E5=BD=95=E7=9A=84=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?,=E7=94=A8=E4=BA=8E=E5=BE=85=E5=8A=9E=E8=AF=A6=E6=83=85?= =?UTF-8?q?=E4=B8=AD=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../task/BpmProcessInstanceController.java | 35 ++++++++ .../vo/instance/BpmProcessInstanceCopyVO.java | 88 +++++++++++++++++++ .../task/BpmProcessInstanceCopyMapper.java | 6 ++ .../task/BpmProcessInstanceCopyService.java | 9 ++ .../BpmProcessInstanceCopyServiceImpl.java | 5 ++ .../src/main/resources/logback-spring.xml | 4 + 6 files changed, 147 insertions(+) create mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 53ea6fa..d55f27a 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -1,5 +1,6 @@ package com.zt.plat.module.bpm.controller.admin.task; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import com.zt.plat.framework.business.core.util.DeptUtil; @@ -11,8 +12,10 @@ import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceConvert; import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; import com.zt.plat.module.bpm.service.definition.BpmCategoryService; import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; import com.zt.plat.module.bpm.service.task.BpmTaskService; import com.zt.plat.module.system.api.dept.DeptApi; @@ -24,6 +27,8 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.list.SetUniqueList; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.task.api.Task; @@ -31,6 +36,8 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -53,6 +60,8 @@ public class BpmProcessInstanceController { private BpmProcessDefinitionService processDefinitionService; @Resource private BpmCategoryService categoryService; + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; @Resource private AdminUserApi adminUserApi; @@ -181,6 +190,32 @@ public class BpmProcessInstanceController { return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } + @GetMapping("/copy-list-by-process-instance-id") + @Operation(summary = "根据流程实例编号获取抄送列表") + @Parameter(name = "id", description = "流程实例的编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + public CommonResult> getCopyListByProcessInstanceId(@RequestParam("processInstanceId") String processInstanceId) { + List copyDOList = processInstanceCopyService.getByProcessInstanceId(processInstanceId); + if (CollectionUtils.isEmpty(copyDOList)) { + return success(new ArrayList<>(0)); + } + List copyVOList = new ArrayList<>(copyDOList.size()); + SetUniqueList userIdList = SetUniqueList.setUniqueList(new ArrayList<>()); + for (BpmProcessInstanceCopyDO copyDO : copyDOList) { + BpmProcessInstanceCopyVO copyVO = new BpmProcessInstanceCopyVO(); + BeanUtil.copyProperties(copyDO, copyVO); + copyVOList.add(copyVO); + userIdList.add(copyDO.getStartUserId()); + userIdList.add(copyDO.getUserId()); + } + Map userMap = adminUserApi.getUserMap(userIdList); + for (BpmProcessInstanceCopyVO copyVO : copyVOList) { + copyVO.setStartUserName(userMap.get(copyVO.getStartUserId()).getNickname()); + copyVO.setUserName(userMap.get(copyVO.getUserId()).getNickname()); + } + return success(copyVOList); + } + @GetMapping("/get-next-approval-nodes") @Operation(summary = "获取下一个执行的流程节点") @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java new file mode 100644 index 0000000..0012c19 --- /dev/null +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java @@ -0,0 +1,88 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 流程抄送 VO + * + * @author kr + * @since 2025-12-31 + */ +@Data +@NoArgsConstructor +public class BpmProcessInstanceCopyVO extends BaseDO { + + /** + * 编号 + */ + private Long id; + + /** + * 发起人 Id + */ + @Schema(description ="发起人 Id") + private Long startUserId; + + /** + * 发起人 姓名 + */ + @Schema(description ="发起人 姓名") + private String startUserName; + /** + * 流程名 + */ + @Schema(description ="流程名") + private String processInstanceName; + /** + * 流程实例的编号 + */ + @Schema(description ="流程实例的编号") + private String processInstanceId; + /** + * 流程实例的流程定义编号 + */ + @Schema(description ="流程实例的流程定义编号") + private String processDefinitionId; + /** + * 流程分类 + */ + @Schema(description ="流程分类") + private String category; + /** + * 流程活动的编号 + */ + @Schema(description ="流程活动的编号") + private String activityId; + /** + * 流程活动的名字 + */ + @Schema(description ="流程活动的名字") + private String activityName; + /** + * 流程活动的编号 + */ + @Schema(description ="流程活动的编号") + private String taskId; + + /** + * 用户编号(被抄送的用户编号) + */ + @Schema(description ="用户编号(被抄送的用户编号)") + private Long userId; + + /** + * 用户姓名(被抄送的用户姓名) + */ + @Schema(description ="用户姓名(被抄送的用户姓名)") + private String userName; + + /** + * 抄送意见 + */ + @Schema(description ="抄送意见") + private String reason; + +} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java index 61a73c3..b35d217 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java @@ -7,6 +7,8 @@ import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstan import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + @Mapper public interface BpmProcessInstanceCopyMapper extends BaseMapperX { @@ -22,4 +24,8 @@ public interface BpmProcessInstanceCopyMapper extends BaseMapperX getByProcessInstanceId(String processInstanceId) { + return selectList(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId); + } + } diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java index e5f06b6..76350bc 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java @@ -7,6 +7,7 @@ import jakarta.validation.constraints.NotEmpty; import org.flowable.bpmn.model.FlowNode; import java.util.Collection; +import java.util.List; /** * 流程抄送 Service 接口 @@ -57,4 +58,12 @@ public interface BpmProcessInstanceCopyService { */ void deleteProcessInstanceCopy(String processInstanceId); + /** + * 获得流程的抄送列表 + * + * @param processInstanceId 流程实例 ID + * @return 抄送流程列表 + */ + List getByProcessInstanceId(String processInstanceId); + } diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java index dd842b9..5fec394 100644 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java +++ b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java @@ -93,4 +93,9 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy processInstanceCopyMapper.deleteByProcessInstanceId(processInstanceId); } + @Override + public List getByProcessInstanceId(String processInstanceId) { + return processInstanceCopyMapper.getByProcessInstanceId(processInstanceId); + } + } diff --git a/zt-module-bpm-server/src/main/resources/logback-spring.xml b/zt-module-bpm-server/src/main/resources/logback-spring.xml index 0e55141..51a9a48 100644 --- a/zt-module-bpm-server/src/main/resources/logback-spring.xml +++ b/zt-module-bpm-server/src/main/resources/logback-spring.xml @@ -73,4 +73,8 @@ + + + + From 1e404a9ef29a9f29f7c4f4b3ea8fdd287edaea37 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Mon, 12 Jan 2026 15:03:02 +0800 Subject: [PATCH 32/35] =?UTF-8?q?1.=20=E7=A7=BB=E9=99=A4=E5=90=88=E5=B9=B6?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5=E7=9A=84=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 3 +- zt-module-bpm-api/pom.xml | 47 - .../bpm/api/definition/BpmCategoryApi.java | 55 - .../module/bpm/api/definition/BpmFormApi.java | 50 - .../bpm/api/definition/BpmUserGroupApi.java | 29 - .../definition/dto/BpmCategoryPageReqDTO.java | 32 - .../definition/dto/BpmCategoryRespDTO.java | 33 - .../definition/dto/BpmCategorySaveReqDTO.java | 37 - .../api/definition/dto/BpmFormPageReqDTO.java | 18 - .../api/definition/dto/BpmFormRespDTO.java | 33 - .../api/definition/dto/BpmFormSaveReqDTO.java | 32 - .../definition/dto/BpmUserGroupRespDTO.java | 31 - .../event/BpmProcessInstanceStatusEvent.java | 41 - ...BpmProcessInstanceStatusEventListener.java | 34 - .../zt/plat/module/bpm/api/package-info.java | 4 - .../bpm/api/task/BpmProcessInstanceApi.java | 67 - .../plat/module/bpm/api/task/BpmTaskApi.java | 53 - .../api/task/dto/BpmApprovalDetailReqDTO.java | 40 - .../task/dto/BpmApprovalDetailRespDTO.java | 110 - .../task/dto/BpmProcessDefinitionRespDTO.java | 55 - ...pmProcessInstanceBpmnModelViewRespDTO.java | 41 - .../dto/BpmProcessInstanceCancelReqDTO.java | 19 - .../dto/BpmProcessInstanceCreateReqDTO.java | 35 - .../dto/BpmProcessInstancePageReqDTO.java | 51 - .../task/dto/BpmProcessInstanceRespDTO.java | 92 - .../api/task/dto/BpmSimpleModelNodeDTO.java | 31 - .../api/task/dto/BpmTaskApproveReqDTO.java | 33 - .../bpm/api/task/dto/BpmTaskPageReqDTO.java | 29 - .../bpm/api/task/dto/BpmTaskRejectReqDTO.java | 18 - .../bpm/api/task/dto/BpmTaskRespDTO.java | 133 -- .../bpm/api/task/dto/UserSimpleDTO.java | 25 - .../plat/module/bpm/enums/ApiConstants.java | 23 - .../module/bpm/enums/DictTypeConstants.java | 10 - .../module/bpm/enums/ErrorCodeConstants.java | 87 - .../definition/BpmAutoApproveTypeEnum.java | 32 - .../definition/BpmBoundaryEventTypeEnum.java | 27 - ...ildProcessMultiInstanceSourceTypeEnum.java | 37 - ...BpmChildProcessStartUserEmptyTypeEnum.java | 36 - .../BpmChildProcessStartUserTypeEnum.java | 35 - .../definition/BpmDelayTimerTypeEnum.java | 31 - .../definition/BpmFieldPermissionEnum.java | 33 - .../BpmHttpRequestParamTypeEnum.java | 31 - .../definition/BpmModelFormTypeEnum.java | 32 - .../enums/definition/BpmModelTypeEnum.java | 31 - .../BpmProcessListenerTypeEnum.java | 21 - .../BpmProcessListenerValueTypeEnum.java | 22 - .../BpmSimpleModeConditionTypeEnum.java | 36 - .../BpmSimpleModelNodeTypeEnum.java | 70 - .../enums/definition/BpmTriggerTypeEnum.java | 46 - .../BpmUserTaskApproveMethodEnum.java | 47 - .../BpmUserTaskApproveTypeEnum.java | 31 - ...BpmUserTaskAssignEmptyHandlerTypeEnum.java | 33 - ...serTaskAssignStartUserHandlerTypeEnum.java | 31 - .../BpmUserTaskRejectHandlerTypeEnum.java | 35 - .../BpmUserTaskTimeoutHandlerTypeEnum.java | 32 - .../bpm/enums/message/BpmMessageEnum.java | 27 - .../bpm/enums/task/BpmCommentTypeEnum.java | 46 - .../task/BpmProcessInstanceStatusEnum.java | 80 - .../module/bpm/enums/task/BpmReasonEnum.java | 52 - .../bpm/enums/task/BpmTaskSignTypeEnum.java | 47 - .../bpm/enums/task/BpmTaskStatusEnum.java | 100 - zt-module-bpm-server/Dockerfile | 19 - zt-module-bpm-server/pom.xml | 147 -- .../druid/pool/DruidPooledStatement.java | 778 ------- .../plat/module/bpm/BpmServerApplication.java | 30 - .../api/definition/BpmCategoryApiImpl.java | 79 - .../bpm/api/definition/BpmFormApiImpl.java | 76 - .../api/definition/BpmUserGroupApiImpl.java | 41 - .../zt/plat/module/bpm/api/package-info.java | 4 - .../api/task/BpmProcessInstanceApiImpl.java | 151 -- .../module/bpm/api/task/BpmTaskApiImpl.java | 203 -- .../admin/base/dept/DeptSimpleBaseVO.java | 15 - .../controller/admin/base/package-info.java | 4 - .../admin/base/user/UserSimpleBaseVO.java | 22 - .../definition/BpmCategoryController.java | 95 - .../admin/definition/BpmFormController.java | 83 - .../admin/definition/BpmModelController.java | 200 -- .../BpmProcessDefinitionController.java | 133 -- .../BpmProcessExpressionController.java | 73 - .../BpmProcessListenerController.java | 73 - .../definition/BpmUserGroupController.java | 83 - .../vo/category/BpmCategoryPageReqVO.java | 32 - .../vo/category/BpmCategoryRespVO.java | 33 - .../vo/category/BpmCategorySaveReqVO.java | 37 - .../BpmProcessExpressionPageReqVO.java | 33 - .../BpmProcessExpressionRespVO.java | 30 - .../BpmProcessExpressionSaveReqVO.java | 27 - .../definition/vo/form/BpmFormFieldVO.java | 24 - .../definition/vo/form/BpmFormPageReqVO.java | 14 - .../definition/vo/form/BpmFormRespVO.java | 39 - .../definition/vo/form/BpmFormSaveReqVO.java | 35 - .../vo/group/BpmUserGroupPageReqVO.java | 28 - .../vo/group/BpmUserGroupRespVO.java | 31 - .../vo/group/BpmUserGroupSaveReqVO.java | 31 - .../listener/BpmProcessListenerPageReqVO.java | 30 - .../vo/listener/BpmProcessListenerRespVO.java | 36 - .../listener/BpmProcessListenerSaveReqVO.java | 39 - .../vo/model/BpmModeUpdateBpmnReqVO.java | 19 - .../vo/model/BpmModelMetaInfoVO.java | 184 -- .../definition/vo/model/BpmModelRespVO.java | 58 - .../vo/model/BpmModelSaveReqVO.java | 35 - .../vo/model/BpmModelUpdateStateReqVO.java | 19 - .../vo/model/simple/BpmSimpleModelNodeVO.java | 526 ----- .../simple/BpmSimpleModelUpdateReqVO.java | 23 - .../BpmProcessDefinitionPageReqVO.java | 14 - .../process/BpmProcessDefinitionRespVO.java | 71 - .../admin/oa/BpmOALeaveController.http | 12 - .../admin/oa/BpmOALeaveController.java | 62 - .../bpm/controller/admin/oa/package-info.java | 5 - .../admin/oa/vo/BpmOALeaveCreateReqVO.java | 43 - .../admin/oa/vo/BpmOALeavePageReqVO.java | 29 - .../admin/oa/vo/BpmOALeaveRespVO.java | 36 - .../task/BpmProcessInstanceController.http | 16 - .../task/BpmProcessInstanceController.java | 237 -- .../BpmProcessInstanceCopyController.java | 89 - .../admin/task/BpmTaskController.java | 252 -- .../task/vo/activity/BpmActivityRespVO.java | 25 - .../vo/cc/BpmProcessInstanceCopyRespVO.java | 48 - .../vo/instance/BpmApprovalDetailReqVO.java | 40 - .../vo/instance/BpmApprovalDetailRespVO.java | 112 - ...BpmProcessInstanceBpmnModelViewRespVO.java | 43 - .../BpmProcessInstanceCancelReqVO.java | 19 - .../BpmProcessInstanceCopyPageReqVO.java | 23 - .../vo/instance/BpmProcessInstanceCopyVO.java | 88 - .../BpmProcessInstanceCreateReqVO.java | 24 - .../instance/BpmProcessInstancePageReqVO.java | 45 - .../vo/instance/BpmProcessInstanceRespVO.java | 86 - .../task/vo/task/BpmTaskApproveReqVO.java | 33 - .../admin/task/vo/task/BpmTaskCopyReqVO.java | 23 - .../task/vo/task/BpmTaskDelegateReqVO.java | 24 - .../admin/task/vo/task/BpmTaskPageReqVO.java | 28 - .../task/vo/task/BpmTaskRejectReqVO.java | 18 - .../admin/task/vo/task/BpmTaskRespVO.java | 130 -- .../task/vo/task/BpmTaskReturnReqVO.java | 23 - .../task/vo/task/BpmTaskSignCreateReqVO.java | 29 - .../task/vo/task/BpmTaskSignDeleteReqVO.java | 19 - .../task/vo/task/BpmTaskTransferReqVO.java | 24 - .../bpm/controller/app/package-info.java | 4 - .../module/bpm/controller/package-info.java | 6 - .../convert/definition/BpmModelConvert.java | 132 -- .../BpmProcessDefinitionConvert.java | 99 - .../convert/message/BpmMessageConvert.java | 21 - .../plat/module/bpm/convert/package-info.java | 6 - .../task/BpmProcessInstanceConvert.java | 305 --- .../task/BpmProcessInstanceDTOConvert.java | 67 - .../bpm/convert/task/BpmTaskConvert.java | 316 --- .../dataobject/definition/BpmCategoryDO.java | 54 - .../dal/dataobject/definition/BpmFormDO.java | 57 - .../BpmProcessDefinitionInfoDO.java | 226 -- .../definition/BpmProcessExpressionDO.java | 45 - .../definition/BpmProcessListenerDO.java | 74 - .../dataobject/definition/BpmUserGroupDO.java | 52 - .../bpm/dal/dataobject/oa/BpmOALeaveDO.java | 78 - .../task/BpmProcessInstanceCopyDO.java | 98 - .../dal/mysql/category/BpmCategoryMapper.java | 46 - .../dal/mysql/definition/BpmFormMapper.java | 25 - .../BpmProcessDefinitionInfoMapper.java | 27 - .../BpmProcessExpressionMapper.java | 26 - .../definition/BpmProcessListenerMapper.java | 27 - .../mysql/definition/BpmUserGroupMapper.java | 32 - .../bpm/dal/mysql/oa/BpmOALeaveMapper.java | 29 - .../task/BpmProcessInstanceCopyMapper.java | 31 - .../bpm/dal/redis/BpmProcessIdRedisDAO.java | 62 - .../bpm/dal/redis/RedisKeyConstants.java | 15 - .../config/BpmFlowableConfiguration.java | 136 -- .../behavior/BpmActivityBehaviorFactory.java | 44 - .../BpmParallelMultiInstanceBehavior.java | 91 - .../BpmSequentialMultiInstanceBehavior.java | 95 - .../behavior/BpmUserTaskActivityBehavior.java | 86 - .../candidate/BpmTaskCandidateInvoker.java | 207 -- .../candidate/BpmTaskCandidateStrategy.java | 85 - .../BpmTaskAssignLeaderExpression.java | 79 - .../BpmTaskAssignStartUserExpression.java | 36 - ...actBpmTaskCandidateDeptLeaderStrategy.java | 95 - ...askCandidateApproveUserSelectStrategy.java | 78 - ...mTaskCandidateDeptLeaderMultiStrategy.java | 45 - .../BpmTaskCandidateDeptLeaderStrategy.java | 45 - .../BpmTaskCandidateDeptMemberStrategy.java | 48 - ...idateStartUserDeptLeaderMultiStrategy.java | 70 - ...kCandidateStartUserDeptLeaderStrategy.java | 71 - ...mTaskCandidateStartUserSelectStrategy.java | 73 - ...pmTaskCandidateFormDeptLeaderStrategy.java | 56 - .../BpmTaskCandidateFormUserStrategy.java | 47 - .../BpmTaskCandidateAssignEmptyStrategy.java | 73 - .../BpmTaskCandidateExpressionStrategy.java | 58 - .../user/BpmTaskCandidateGroupStrategy.java | 46 - .../user/BpmTaskCandidatePostStrategy.java | 48 - .../user/BpmTaskCandidateRoleStrategy.java | 43 - .../BpmTaskCandidateStartUserStrategy.java | 57 - .../user/BpmTaskCandidateUserStrategy.java | 39 - ...riableConvertByTypeExpressionFunction.java | 32 - .../enums/BpmTaskCandidateStrategyEnum.java | 59 - .../core/enums/BpmnModelConstants.java | 146 -- .../core/enums/BpmnVariableConstants.java | 99 - .../BpmProcessInstanceEventPublisher.java | 24 - .../core/listener/BpmCopyTaskDelegate.java | 47 - .../BpmProcessInstanceEventListener.java | 54 - .../core/listener/BpmTaskEventListener.java | 125 - .../core/listener/BpmTriggerTaskDelegate.java | 55 - .../DemoDelegateClassExecutionListener.java | 21 - ...moDelegateExpressionExecutionListener.java | 23 - ...DemoSpringExpressionExecutionListener.java | 21 - .../task/DemoDelegateClassTaskListener.java | 20 - .../DemoDelegateExpressionTaskListener.java | 22 - .../DemoSpringExpressionTaskListener.java | 20 - .../core/util/BpmHttpRequestUtils.java | 158 -- .../flowable/core/util/BpmnModelUtils.java | 1025 --------- .../flowable/core/util/FlowableUtils.java | 362 --- .../flowable/core/util/SimpleModelUtils.java | 1007 -------- .../module/bpm/framework/package-info.java | 6 - .../rpc/config/RpcConfiguration.java | 18 - .../bpm/framework/rpc/package-info.java | 4 - .../config/SecurityConfiguration.java | 40 - .../framework/security/core/package-info.java | 4 - .../web/config/BpmWebConfiguration.java | 28 - .../framework/web/core/FlowableWebFilter.java | 36 - .../bpm/framework/web/package-info.java | 4 - .../com/zt/plat/module/bpm/package-info.java | 12 - .../definition/BpmCategoryService.java | 92 - .../definition/BpmCategoryServiceImpl.java | 130 -- .../service/definition/BpmFormService.java | 85 - .../definition/BpmFormServiceImpl.java | 114 - .../service/definition/BpmModelService.java | 134 -- .../definition/BpmModelServiceImpl.java | 436 ---- .../BpmProcessDefinitionService.java | 181 -- .../BpmProcessDefinitionServiceImpl.java | 248 -- .../BpmProcessExpressionService.java | 54 - .../BpmProcessExpressionServiceImpl.java | 70 - .../definition/BpmProcessListenerService.java | 54 - .../BpmProcessListenerServiceImpl.java | 102 - .../definition/BpmUserGroupService.java | 82 - .../definition/BpmUserGroupServiceImpl.java | 107 - .../definition/dto/BpmFormFieldRespDTO.java | 25 - .../dto/BpmModelMetaInfoRespDTO.java | 46 - .../dto/BpmProcessDefinitionCreateReqDTO.java | 81 - .../service/message/BpmMessageService.java | 46 - .../message/BpmMessageServiceImpl.java | 79 - ...eSendWhenProcessInstanceApproveReqDTO.java | 26 - ...geSendWhenProcessInstanceRejectReqDTO.java | 32 - .../BpmMessageSendWhenTaskCreatedReqDTO.java | 45 - .../BpmMessageSendWhenTaskTimeoutReqDTO.java | 41 - .../bpm/service/oa/BpmOALeaveService.java | 52 - .../bpm/service/oa/BpmOALeaveServiceImpl.java | 89 - .../oa/listener/BpmOALeaveStatusListener.java | 33 - ...BpmCreditLetterApprovalStatusListener.java | 42 - .../task/BpmProcessInstanceCopyService.java | 69 - .../BpmProcessInstanceCopyServiceImpl.java | 101 - .../task/BpmProcessInstanceService.java | 191 -- .../task/BpmProcessInstanceServiceImpl.java | 964 -------- .../bpm/service/task/BpmTaskService.java | 316 --- .../bpm/service/task/BpmTaskServiceImpl.java | 1535 ------------- .../listener/BpmCallActivityListener.java | 96 - .../task/listener/BpmUserTaskListener.java | 59 - .../bpm/service/task/trigger/BpmTrigger.java | 30 - .../trigger/form/BpmFormDeleteTrigger.java | 73 - .../trigger/form/BpmFormUpdateTrigger.java | 66 - .../http/BpmAbstractHttpRequestTrigger.java | 14 - .../trigger/http/BpmHttpCallbackTrigger.java | 50 - .../http/BpmSyncHttpRequestTrigger.java | 45 - .../liquibase/database/core/DmDatabase.java | 572 ----- .../liquibase/datatype/core/BooleanType.java | 148 -- .../datatype/core/DmBooleanType.java | 32 - .../snapshot/JdbcDatabaseSnapshot.java | 1957 ---------------- .../impl/AbstractEngineConfiguration.java | 2038 ----------------- .../engine/impl/db/DbSqlSessionFactory.java | 354 --- .../main/resources/META-INF/package-info.md | 1 - .../services/liquibase.database.Database | 21 - .../liquibase.datatype.LiquibaseDataType | 1 - .../src/main/resources/application-dev.yaml | 94 - .../src/main/resources/application-local.yaml | 116 - .../src/main/resources/application.yaml | 150 -- .../src/main/resources/logback-spring.xml | 80 - .../create/flowable.oracle.create.batch.sql | 41 - .../db/drop/flowable.oracle.drop.batch.sql | 4 - .../create/flowable.oracle.create.common.sql | 23 - .../db/drop/flowable.oracle.drop.common.sql | 2 - .../create/flowable.oracle.create.engine.sql | 355 --- .../create/flowable.oracle.create.history.sql | 114 - .../db/drop/flowable.oracle.drop.engine.sql | 148 -- .../db/drop/flowable.oracle.drop.history.sql | 23 - ...wable.oracle.create.entitylink.history.sql | 23 - .../flowable.oracle.create.entitylink.sql | 26 - ...lowable.oracle.drop.entitylink.history.sql | 4 - .../drop/flowable.oracle.drop.entitylink.sql | 4 - ...owable.oracle.create.eventsubscription.sql | 28 - ...flowable.oracle.drop.eventsubscription.sql | 5 - ...ble.oracle.create.identitylink.history.sql | 20 - .../flowable.oracle.create.identitylink.sql | 24 - ...wable.oracle.drop.identitylink.history.sql | 6 - .../flowable.oracle.drop.identitylink.sql | 7 - .../flowable.oracle.create.identity.sql | 108 - .../db/drop/flowable.oracle.drop.identity.sql | 22 - .../db/create/flowable.oracle.create.job.sql | 261 --- .../db/drop/flowable.oracle.drop.job.sql | 74 - .../flowable.oracle.create.task.history.sql | 64 - .../db/create/flowable.oracle.create.task.sql | 48 - .../flowable.oracle.drop.task.history.sql | 8 - .../db/drop/flowable.oracle.drop.task.sql | 6 - ...lowable.oracle.create.variable.history.sql | 26 - .../flowable.oracle.create.variable.sql | 31 - .../flowable.oracle.drop.variable.history.sql | 6 - .../db/drop/flowable.oracle.drop.variable.sql | 9 - .../BpmTaskCandidateInvokerTest.java | 274 --- .../BpmTaskAssignLeaderExpressionTest.java | 107 - ...kCandidateDeptLeaderMultiStrategyTest.java | 45 - ...pmTaskCandidateDeptLeaderStrategyTest.java | 44 - ...pmTaskCandidateDeptMemberStrategyTest.java | 47 - ...eStartUserDeptLeaderMultiStrategyTest.java | 85 - ...didateStartUserDeptLeaderStrategyTest.java | 85 - ...kCandidateStartUserSelectStrategyTest.java | 68 - ...mTaskCandidateAssignEmptyStrategyTest.java | 88 - ...pmTaskCandidateExpressionStrategyTest.java | 61 - .../BpmTaskCandidateGroupStrategyTest.java | 44 - .../BpmTaskCandidatePostStrategyTest.java | 48 - .../BpmTaskCandidateRoleStrategyTest.java | 44 - ...BpmTaskCandidateStartUserStrategyTest.java | 56 - .../BpmTaskCandidateUserStrategyTest.java | 31 - .../category/BpmCategoryServiceImplTest.java | 136 -- .../definition/BpmFormServiceTest.java | 144 -- .../definition/BpmUserGroupServiceTest.java | 129 -- .../test/resources/application-unit-test.yaml | 45 - .../src/test/resources/logback.xml | 4 - .../src/test/resources/sql/clean.sql | 3 - .../src/test/resources/sql/create_tables.sql | 43 - 324 files changed, 1 insertion(+), 30086 deletions(-) delete mode 100644 zt-module-bpm-api/pom.xml delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApi.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApi.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApi.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryPageReqDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryRespDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategorySaveReqDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApi.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailRespDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessDefinitionRespDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceBpmnModelViewRespDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCancelReqDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmSimpleModelNodeDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java delete mode 100644 zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java delete mode 100644 zt-module-bpm-server/Dockerfile delete mode 100644 zt-module-bpm-server/pom.xml delete mode 100644 zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApiImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApiImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApiImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceDTOConvert.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java delete mode 100644 zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java delete mode 100644 zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java delete mode 100644 zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java delete mode 100644 zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java delete mode 100644 zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java delete mode 100644 zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java delete mode 100644 zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java delete mode 100644 zt-module-bpm-server/src/main/resources/META-INF/package-info.md delete mode 100644 zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database delete mode 100644 zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType delete mode 100644 zt-module-bpm-server/src/main/resources/application-dev.yaml delete mode 100644 zt-module-bpm-server/src/main/resources/application-local.yaml delete mode 100644 zt-module-bpm-server/src/main/resources/application.yaml delete mode 100644 zt-module-bpm-server/src/main/resources/logback-spring.xml delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql delete mode 100644 zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java delete mode 100644 zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java delete mode 100644 zt-module-bpm-server/src/test/resources/application-unit-test.yaml delete mode 100644 zt-module-bpm-server/src/test/resources/logback.xml delete mode 100644 zt-module-bpm-server/src/test/resources/sql/clean.sql delete mode 100644 zt-module-bpm-server/src/test/resources/sql/create_tables.sql diff --git a/pom.xml b/pom.xml index ac296a6..997e3e6 100644 --- a/pom.xml +++ b/pom.xml @@ -12,8 +12,7 @@ ${project.artifactId} - 芋道项目基础脚手架 - https://github.com/YunaiV/ruoyi-vue-pro + 项目基础脚手架 3.0.46 diff --git a/zt-module-bpm-api/pom.xml b/zt-module-bpm-api/pom.xml deleted file mode 100644 index 2aa75a5..0000000 --- a/zt-module-bpm-api/pom.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - com.zt.plat - zt-module-bpm - ${revision} - - 4.0.0 - zt-module-bpm-api - jar - - ${project.artifactId} - - bpm 模块 API,暴露给其它模块调用 - - - - - com.zt.plat - zt-common - - - - - org.springdoc - springdoc-openapi-starter-webmvc-api - provided - - - - - org.springframework.boot - spring-boot-starter-validation - true - - - - - org.springframework.cloud - spring-cloud-starter-openfeign - true - - - - diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApi.java deleted file mode 100644 index 19f885c..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApi.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.zt.plat.module.bpm.api.definition; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.api.definition.dto.BpmCategoryPageReqDTO; -import com.zt.plat.module.bpm.api.definition.dto.BpmCategoryRespDTO; -import com.zt.plat.module.bpm.api.definition.dto.BpmCategorySaveReqDTO; -import com.zt.plat.module.bpm.enums.ApiConstants; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@FeignClient(name = ApiConstants.NAME) -@Tag(name = "RPC 服务 - BPM 流程分类") -public interface BpmCategoryApi { - - String PREFIX = ApiConstants.PREFIX + "/category"; - - @PostMapping(PREFIX + "/create") - @Operation(summary = "创建流程分类") - CommonResult createCategory(@Valid @RequestBody BpmCategorySaveReqDTO createReqDTO); - - @PutMapping(PREFIX + "/update") - @Operation(summary = "更新流程分类") - CommonResult updateCategory(@Valid @RequestBody BpmCategorySaveReqDTO updateReqDTO); - - @PutMapping(PREFIX + "/update-sort-batch") - @Operation(summary = "批量更新流程分类的排序") - @Parameter(name = "ids", description = "分类编号列表", required = true, example = "1,2,3") - CommonResult updateCategorySortBatch(@RequestParam("ids") List ids); - - @DeleteMapping(PREFIX + "/delete") - @Operation(summary = "删除流程分类") - @Parameter(name = "id", description = "编号", required = true) - CommonResult deleteCategory(@RequestParam("id") Long id); - - @GetMapping(PREFIX + "/get") - @Operation(summary = "获得流程分类") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - CommonResult getCategory(@RequestParam("id") Long id); - - @PostMapping(PREFIX + "/page") - @Operation(summary = "获得流程分类分页") - CommonResult> getCategoryPage(@Valid @RequestBody BpmCategoryPageReqDTO pageReqDTO); - - @GetMapping(PREFIX + "/simple-list") - @Operation(summary = "获取流程分类的精简信息列表", description = "只包含被开启的分类,主要用于前端的下拉选项") - CommonResult> getCategorySimpleList(); - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApi.java deleted file mode 100644 index b21f94a..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApi.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.zt.plat.module.bpm.api.definition; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.api.definition.dto.BpmFormPageReqDTO; -import com.zt.plat.module.bpm.api.definition.dto.BpmFormRespDTO; -import com.zt.plat.module.bpm.api.definition.dto.BpmFormSaveReqDTO; -import com.zt.plat.module.bpm.enums.ApiConstants; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@FeignClient(name = ApiConstants.NAME) -@Tag(name = "RPC 服务 - 动态表单") -public interface BpmFormApi { - - String PREFIX = ApiConstants.PREFIX + "/form"; - - @PostMapping(PREFIX + "/create") - @Operation(summary = "创建动态表单") - CommonResult createForm(@Valid @RequestBody BpmFormSaveReqDTO createReqDTO); - - @PutMapping(PREFIX + "/update") - @Operation(summary = "更新动态表单") - CommonResult updateForm(@Valid @RequestBody BpmFormSaveReqDTO updateReqDTO); - - @DeleteMapping(PREFIX + "/delete") - @Operation(summary = "删除动态表单") - @Parameter(name = "id", description = "编号", required = true) - CommonResult deleteForm(@RequestParam("id") Long id); - - @GetMapping(PREFIX + "/get") - @Operation(summary = "获得动态表单") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - CommonResult getForm(@RequestParam("id") Long id); - - @PostMapping(PREFIX + "/page") - @Operation(summary = "获得动态表单分页") - CommonResult> getFormPage(@Valid @RequestBody BpmFormPageReqDTO pageReqDTO); - - @GetMapping(PREFIX + "/simple-list") - @Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框") - CommonResult> getFormSimpleList(); - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApi.java deleted file mode 100644 index dbf25d9..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApi.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.zt.plat.module.bpm.api.definition; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.module.bpm.api.definition.dto.BpmUserGroupRespDTO; -import com.zt.plat.module.bpm.enums.ApiConstants; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@FeignClient(name = ApiConstants.NAME) -@Tag(name = "RPC 服务 - 用户组") -public interface BpmUserGroupApi { - - String PREFIX = ApiConstants.PREFIX + "/user-group"; - - @GetMapping(PREFIX + "/get") - @Operation(summary = "获得用户组") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - CommonResult getUserGroup(@RequestParam("id") Long id); - - @GetMapping(PREFIX + "/simple-list") - @Operation(summary = "获取用户组精简信息列表", description = "只包含被开启的用户组,主要用于前端的下拉选项") - CommonResult> getUserGroupSimpleList(); - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryPageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryPageReqDTO.java deleted file mode 100644 index df35f7a..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryPageReqDTO.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.zt.plat.module.bpm.api.definition.dto; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.pojo.PageParam; -import com.zt.plat.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "RPC 服务 - BPM 流程分类分页 Request DTO") -@Data -public class BpmCategoryPageReqDTO extends PageParam { - - @Schema(description = "分类名", example = "王五") - private String name; - - @Schema(description = "分类标志", example = "OA") - private String code; - - @Schema(description = "分类状态", example = "1") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryRespDTO.java deleted file mode 100644 index 8831550..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategoryRespDTO.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.zt.plat.module.bpm.api.definition.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "RPC 服务 - BPM 流程分类 Response DTO") -@Data -public class BpmCategoryRespDTO { - - @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") - private Long id; - - @Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") - private String name; - - @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") - private String code; - - @Schema(description = "分类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") - private String description; - - @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED) - private Integer sort; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategorySaveReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategorySaveReqDTO.java deleted file mode 100644 index 39c84e5..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmCategorySaveReqDTO.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.zt.plat.module.bpm.api.definition.dto; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "RPC 服务 - BPM 流程分类新增/修改 Request DTO") -@Data -public class BpmCategorySaveReqDTO { - - @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") - private Long id; - - @Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") - @NotEmpty(message = "分类名不能为空") - private String name; - - @Schema(description = "分类描述", example = "你猜") - private String description; - - @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") - @NotEmpty(message = "分类标志不能为空") - private String code; - - @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "分类状态不能为空") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "分类排序不能为空") - private Integer sort; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java deleted file mode 100644 index d75ff78..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.zt.plat.module.bpm.api.definition.dto; - -import com.zt.plat.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -@Schema(description = "动态表单分页 Request DTO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class BpmFormPageReqDTO extends PageParam { - - @Schema(description = "表单名称", example = "ZT") - private String name; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java deleted file mode 100644 index e33b513..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.zt.plat.module.bpm.api.definition.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "RPC 服务 - 动态表单 Response DTO") -@Data -public class BpmFormRespDTO { - - @Schema(description = "表单编号", example = "1024") - private Long id; - - @Schema(description = "表单名", example = "ZT") - private String name; - - @Schema(description = "表单状态", example = "1") - private Integer status; - - @Schema(description = "表单的配置") - private String conf; - - @Schema(description = "表单项的数组") - private String fields; - - @Schema(description = "备注", example = "我是备注") - private String remark; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java deleted file mode 100644 index a2b4ab3..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.zt.plat.module.bpm.api.definition.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -@Schema(description = "RPC 服务 - 动态表单新增/修改 Request DTO") -@Data -public class BpmFormSaveReqDTO { - - @Schema(description = "表单编号", example = "1024") - private Long id; - - @Schema(description = "表单名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - @NotEmpty(message = "表单名不能为空") - private String name; - - @Schema(description = "表单状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "表单的配置", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "表单的配置不能为空") - private String conf; - - @Schema(description = "表单项的数组", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "表单项的数组不能为空") - private String fields; - - @Schema(description = "备注", example = "我是备注") - private String remark; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java deleted file mode 100644 index f8acdeb..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.zt.plat.module.bpm.api.definition.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.Set; - -@Schema(description = "RPC 服务 - 用户组 Response DTO") -@Data -public class BpmUserGroupRespDTO { - - @Schema(description = "编号", example = "1024") - private Long id; - - @Schema(description = "组名", example = "ZT") - private String name; - - @Schema(description = "描述", example = "ZT") - private String description; - - @Schema(description = "成员用户编号数组", example = "1,2,3") - private Set memberUserIds; - - @Schema(description = "状态", example = "1") - private Integer status; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java deleted file mode 100644 index 4a4316c..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEvent.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.zt.plat.module.bpm.api.event; - -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import org.springframework.context.ApplicationEvent; - -/** - * 流程实例的状态(结果)发生变化的 Event - * - * @author ZT - */ -@SuppressWarnings("ALL") -@Data -public class BpmProcessInstanceStatusEvent extends ApplicationEvent { - - /** - * 流程实例的编号 - */ - @NotNull(message = "流程实例的编号不能为空") - private String id; - /** - * 流程实例的 key - */ - @NotNull(message = "流程实例的 key 不能为空") - private String processDefinitionKey; - /** - * 流程实例的结果 - */ - @NotNull(message = "流程实例的状态不能为空") - private Integer status; - /** - * 流程实例对应的业务标识 - * 例如说,请假 - */ - private String businessKey; - - public BpmProcessInstanceStatusEvent(Object source) { - super(source); - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java deleted file mode 100644 index 553096a..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/event/BpmProcessInstanceStatusEventListener.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.zt.plat.module.bpm.api.event; - -import org.springframework.context.ApplicationListener; - -import java.util.List; - -/** - * {@link BpmProcessInstanceStatusEvent} 的监听器 - * - * @author ZT - */ -public abstract class BpmProcessInstanceStatusEventListener - implements ApplicationListener { - - @Override - public final void onApplicationEvent(BpmProcessInstanceStatusEvent event) { - if (getProcessDefinitionKey().contains(event.getProcessDefinitionKey())){ - onEvent(event); - } - } - - /** - * @return 返回监听的流程定义 Key - */ - protected abstract List getProcessDefinitionKey(); - - /** - * 处理事件 - * - * @param event 事件 - */ - protected abstract void onEvent(BpmProcessInstanceStatusEvent event); - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java deleted file mode 100644 index 10d2e79..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * bpm API 包,定义暴露给其它模块的 API - */ -package com.zt.plat.module.bpm.api; diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java deleted file mode 100644 index 5a2d905..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.zt.plat.module.bpm.api.task; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.module.bpm.api.task.dto.*; -import com.zt.plat.module.bpm.enums.ApiConstants; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@FeignClient(name = ApiConstants.NAME) // TODO ZT:fallbackFactory = -@Tag(name = "RPC 服务 - 流程实例") -public interface BpmProcessInstanceApi { - - String PREFIX = ApiConstants.PREFIX + "/process-instance"; - - @PostMapping(PREFIX + "/create") - @Operation(summary = "创建流程实例(提供给内部),返回实例编号") - @Parameter(name = "userId", description = "用户编号", required = true, example = "1") - CommonResult createProcessInstance(@RequestParam("userId") Long userId, - @Valid @RequestBody BpmProcessInstanceCreateReqDTO reqDTO); - - @GetMapping(PREFIX + "/get") - @Operation(summary = "获得指定流程实例", description = "在【流程详细】界面中,进行调用") - @Parameter(name = "id", description = "流程实例的编号", required = true) - CommonResult getProcessInstance(@RequestParam("id") String id); - - @DeleteMapping(PREFIX + "/cancel-by-start-user") - @Operation(summary = "用户取消流程实例", description = "取消发起的流程") - CommonResult cancelProcessInstanceByStartUser( - @RequestParam("userId") Long userId, - @Valid @RequestBody BpmProcessInstanceCancelReqDTO cancelReqDTO); - - @DeleteMapping(PREFIX + "/cancel-by-admin") - @Operation(summary = "管理员取消流程实例", description = "管理员撤回流程") - CommonResult cancelProcessInstanceByAdmin( - @RequestParam("userId") Long userId, - @Valid @RequestBody BpmProcessInstanceCancelReqDTO cancelReqDTO); - - @PostMapping(PREFIX + "/get-approval-detail") - @Operation(summary = "获得审批详情") - CommonResult getApprovalDetail(@RequestParam("userId") Long userId, - @Valid BpmApprovalDetailReqDTO reqDTO); - - @PostMapping(PREFIX + "/get-next-approval-nodes") - @Operation(summary = "获取下一个执行的流程节点") - CommonResult> getNextApprovalNodes(@RequestParam("userId") Long userId, - @Valid BpmApprovalDetailReqDTO reqDTO); - - @PostMapping(PREFIX + "/get-bpmn-model-view") - @Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用") - @Parameter(name = "id", description = "流程实例的编号", required = true) - CommonResult getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id); - - @PutMapping(PREFIX + "/approveTask") - @Operation(summary = "通过任务") - CommonResult approveTask(@Valid @RequestBody BpmTaskApproveReqDTO reqVO); - - @PutMapping(PREFIX + "/rejectTask") - @Operation(summary = "不通过任务") - CommonResult rejectTask(@Valid @RequestBody BpmTaskRejectReqDTO reqVO); - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApi.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApi.java deleted file mode 100644 index b55ae5c..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApi.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.zt.plat.module.bpm.api.task; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.module.bpm.api.task.dto.BpmTaskPageReqDTO; -import com.zt.plat.module.bpm.api.task.dto.BpmTaskRespDTO; -import com.zt.plat.module.bpm.enums.ApiConstants; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.validation.Valid; -import org.springframework.cloud.openfeign.FeignClient; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestParam; - -import java.util.List; - -@FeignClient(name = ApiConstants.NAME) -@Tag(name = "RPC 服务 - 流程任务实例") -public interface BpmTaskApi { - - String PREFIX = ApiConstants.PREFIX + "/task"; - - @PostMapping(PREFIX + "/todo-page") - @Operation(summary = "获取 Todo 待办任务分页") - CommonResult> getTaskTodoPage(@Valid @RequestBody BpmTaskPageReqDTO pageReqDTO); - - @PostMapping(PREFIX + "/done-page") - @Operation(summary = "获取 Done 已办任务分页") - CommonResult> getTaskDonePage(@Valid @RequestBody BpmTaskPageReqDTO pageReqDTO); - - @PostMapping(PREFIX + "/manager-page") - @Operation(summary = "获取全部任务的分页", description = "用于【流程任务】菜单") - CommonResult> getTaskManagerPage(@Valid @RequestBody BpmTaskPageReqDTO pageReqDTO); - - @GetMapping(PREFIX + "/list-by-process-instance-id") - @Operation(summary = "获得指定流程实例的任务列表", description = "包括完成的、未完成的") - @Parameter(name = "processInstanceId", description = "流程实例的编号", required = true) - CommonResult> getTaskListByProcessInstanceId( - @RequestParam("processInstanceId") String processInstanceId); - - @GetMapping(PREFIX + "/list-by-return") - @Operation(summary = "获取所有可退回的节点", description = "用于【流程详情】的【退回】按钮") - @Parameter(name = "id", description = "当前任务ID", required = true) - CommonResult> getTaskListByReturn(@RequestParam("id") String id); - - @GetMapping(PREFIX + "/list-by-parent-task-id") - @Operation(summary = "获得指定父级任务的子任务列表") // 目前用于,减签的时候,获得子任务列表 - @Parameter(name = "parentTaskId", description = "父级任务编号", required = true) - CommonResult> getTaskListByParentTaskId(@RequestParam("parentTaskId") String parentTaskId); - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java deleted file mode 100644 index 2dc63a6..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import cn.hutool.core.util.StrUtil; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.AssertTrue; -import lombok.Data; - -import java.util.Map; - -@Schema(description = "RPC 服务 - 审批详情 Request DTO") -@Data -public class BpmApprovalDetailReqDTO { - - @Schema(description = "流程定义的编号", example = "1024") - private String processDefinitionId; // 使用场景:发起流程时,传流程定义 ID - - @Schema(description = "流程变量") - private Map processVariables; // 使用场景:同 processDefinitionId,用于流程预测 - - @Schema(description = "流程变量") - private String processVariablesStr; // 解决 GET 无法传递对象的问题,最终转换成 processVariables 变量 - - @Schema(description = "流程实例的编号", example = "1024") - private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID - - // TODO @ZT:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 - @Schema(description = "流程活动编号", example = "StartUserNode") - private String activityId; // 用于获取表单权限。1)发起流程时,传"发起人节点" activityId 可获取发起人的表单权限;2)从抄送列表界面进来时,传抄送的 activityId 可获取抄送人的表单权限; - - @Schema(description = "流程任务编号", example = "95f2f08b-621b-11ef-bf39-00ff4722db8b") - private String taskId; // 用于获取表单权限。1)从待审批/已审批界面进来时,传递 taskId 任务编号,可获取任务节点的变得权限 - - @AssertTrue(message = "流程定义的编号和流程实例的编号不能同时为空") - @JsonIgnore - public boolean isValidProcessParam() { - return StrUtil.isNotEmpty(processDefinitionId) || StrUtil.isNotEmpty(processInstanceId); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailRespDTO.java deleted file mode 100644 index 3183561..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailRespDTO.java +++ /dev/null @@ -1,110 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import com.zt.plat.module.bpm.api.task.dto.BpmTaskRespDTO; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - - -@Schema(description = "RPC 服务 - 审批详情 Response DTO") -@Data -public class BpmApprovalDetailRespDTO { - - @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 - - @Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED) - private List activityNodes; - - @Schema(description = "表单字段权限") - private Map formFieldsPermission; - - @Schema(description = "待办任务") - private BpmTaskRespDTO todoTask; - - /** - * 所属流程定义信息 - */ - private BpmProcessDefinitionRespDTO processDefinition; - - /** - * 所属流程实例信息 - */ - private BpmProcessInstanceRespDTO processInstance; - - @Schema(description = "活动节点信息") - @Data - public static class ActivityNode { - - @Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartUserNode") - private String id; - - @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "发起人") - private String name; - - @Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer nodeType; // 参见 BpmSimpleModelNodeType 枚举 - - @Schema(description = "节点状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - private Integer status; // 参见 BpmTaskStatusEnum 枚举 - - @Schema(description = "节点的开始时间") - private LocalDateTime startTime; - @Schema(description = "节点的结束时间") - private LocalDateTime endTime; - - @Schema(description = "审批节点的任务信息") - private List tasks; - - @Schema(description = "候选人策略", example = "35") - private Integer candidateStrategy; // 参见 BpmTaskCandidateStrategyEnum 枚举。主要用于发起时,审批节点、抄送节点自选 - - @Schema(description = "候选人用户 ID 列表", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818") - @JsonIgnore // 不返回,只是方便后续读取,赋值给 candidateUsers - private List candidateUserIds; - - @Schema(description = "候选人用户列表") - private List candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表 - - @Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf") - private String processInstanceId; // 当且仅当,该节点是子流程节点时,才会有值(CallActivity 的 processInstanceId 字段) - - } - - @Schema(description = "活动节点的任务信息") - @Data - public static class ActivityNodeTask { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private String id; - - @Schema(description = "任务所属人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818") - @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser - private Long owner; - - @Schema(description = "任务所属人", example = "1024") - private UserSimpleDTO ownerUser; - - @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") - @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser - private Long assignee; - - @Schema(description = "任务分配人", example = "2048") - private UserSimpleDTO assigneeUser; - - @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; // 参见 BpmTaskStatusEnum 枚举 - - @Schema(description = "审批意见", example = "同意") - private String reason; - - @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") - private String signPicUrl; - - } - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessDefinitionRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessDefinitionRespDTO.java deleted file mode 100644 index 81aec42..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessDefinitionRespDTO.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "RPC 服务 - 流程定义 Response DTO") -@Data -public class BpmProcessDefinitionRespDTO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String id; - - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_order") - private String key; - - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "请假流程") - private String name; - - @Schema(description = "流程描述", example = "请假流程描述") - private String description; - - @Schema(description = "流程分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "oa") - private String category; - - @Schema(description = "表单类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer formType; - - @Schema(description = "表单编号", example = "1024") - private Long formId; - - @Schema(description = "表单的配置", requiredMode = Schema.RequiredMode.REQUIRED) - private String formConf; - - @Schema(description = "表单项的数组", requiredMode = Schema.RequiredMode.REQUIRED) - private List formFields; - - @Schema(description = "表单项的 JSON 数组字符串", requiredMode = Schema.RequiredMode.REQUIRED) - private String formFieldsStr; - - @Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED) - private String bpmnXml; - - @Schema(description = "版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer version; - - @Schema(description = "是否挂起", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - private Integer suspensionState; - - @Schema(description = "部署时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime deploymentTime; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceBpmnModelViewRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceBpmnModelViewRespDTO.java deleted file mode 100644 index 9b1f2c3..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceBpmnModelViewRespDTO.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; -import java.util.Set; - -@Schema(description = "RPC 服务 - 流程示例的 BPMN 视图 Response DTO") -@Data -public class BpmProcessInstanceBpmnModelViewRespDTO { - - // ========== 基本信息 ========== - - @Schema(description = "流程实例信息", requiredMode = Schema.RequiredMode.REQUIRED) - private BpmProcessInstanceRespDTO processInstance; - - @Schema(description = "任务列表", requiredMode = Schema.RequiredMode.REQUIRED) - private List tasks; - - @Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED) - private String bpmnXml; - - @Schema(description = "SIMPLE 模型") - private BpmSimpleModelNodeDTO simpleModel; - - // ========== 进度信息 ========== - - @Schema(description = "进行中的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) - private Set unfinishedTaskActivityIds; // 只包括 UserTask - - @Schema(description = "已经完成的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) - private Set finishedTaskActivityIds; // 包括 UserTask、Gateway 等,不包括 SequenceFlow - - @Schema(description = "已经完成的连线节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) - private Set finishedSequenceFlowActivityIds; // 只包括 SequenceFlow - - @Schema(description = "已经拒绝的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) - private Set rejectedTaskActivityIds; // 只包括 UserTask - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCancelReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCancelReqDTO.java deleted file mode 100644 index 15316d0..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCancelReqDTO.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -@Schema(description = "RPC 服务 - 流程实例的取消 Request DTO") -@Data -public class BpmProcessInstanceCancelReqDTO { - - @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "流程实例的编号不能为空") - private String id; - - @Schema(description = "取消原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "不请假了!") - @NotEmpty(message = "取消原因不能为空") - private String reason; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java deleted file mode 100644 index 24b08a4..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceCreateReqDTO.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.List; -import java.util.Map; - -@Schema(description = "RPC 服务 - 流程实例的创建 Request DTO") -@Data -public class BpmProcessInstanceCreateReqDTO { - - @Schema(description = "流程定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "leave") - @NotEmpty(message = "流程定义的标识不能为空") - private String processDefinitionKey; - - @Schema(description = "变量实例", requiredMode = Schema.RequiredMode.REQUIRED) - private Map variables; - - @Schema(description = "业务的唯一标识", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "业务的唯一标识不能为空") - private String businessKey; // 例如说,请假申请的编号。通过它,可以查询到对应的实例 - - /** - * 发起人自选审批人 Map - * - * key:taskKey 任务编码 - * value:审批人的数组 - * 例如:{ taskKey1 :[1, 2] },则表示 taskKey1 这个任务,提前设定了,由 userId 为 1,2 的用户进行审批 - */ - @Schema(description = "发起人自选审批人 Map") - private Map> startUserSelectAssignees; - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java deleted file mode 100644 index f23c182..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java +++ /dev/null @@ -1,51 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import com.zt.plat.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "RPC 服务 - 流程实例分页 Request DTO") -@Data -public class BpmProcessInstancePageReqDTO extends PageParam { - - @Schema(description = "流程实例的编号", example = "1024") - private String id; - - @Schema(description = "流程实例的名字", example = "ZT") - private String name; - - @Schema(description = "流程定义的编号", example = "2048") - private String processDefinitionId; - - @Schema(description = "流程定义的标识", example = "2048") - private String processDefinitionKey; // 精准匹配 - - @Schema(description = "流程分类", example = "1") - private String category; - - @Schema(description = "流程实例的状态", example = "1") - private Integer status; - - @Schema(description = "流程实例的结果", example = "1") - private Integer result; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - - @Schema(description = "结束时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] endTime; - - @Schema(description = "发起用户编号", example = "1024") - private Long startUserId; // 注意,只有在【流程实例】菜单,才使用该参数 - - @Schema(description = "动态表单字段查询 JSON Str", example = "{}") - private String formFieldsParams; // SpringMVC 在 get 请求下,无法方便的定义 Map 类型的参数,所以通过 String 接收后,逻辑里面转换 - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java deleted file mode 100644 index eabc038..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import com.zt.plat.framework.common.core.KeyValue; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -@Schema(description = "RPC 服务 - 流程实例 Response DTO") -@Data -public class BpmProcessInstanceRespDTO { - - @Schema(description = "流程实例的编号", example = "1024") - private String id; - - @Schema(description = "流程实例的名字", example = "ZT") - private String name; - - @Schema(description = "流程摘要") - private List> summary; // 只有流程表单,才有摘要! - - @Schema(description = "流程定义的编号", example = "2048") - private String processDefinitionId; - - @Schema(description = "流程分类", example = "1") - private String category; - - @Schema(description = "流程分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "请假") - private String categoryName; - - @Schema(description = "流程实例的状态", example = "1") - private Integer status; - - @Schema(description = "流程实例的结果", example = "1") - private Integer result; - - @Schema(description = "发起时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime startTime; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - - @Schema(description = "结束时间") - private LocalDateTime endTime; - - @Schema(description = "持续时间", example = "1000") - private Long durationInMillis; - - @Schema(description = "提交的表单值", example = "{\"name\": \"ZT\"}") - private Map formVariables; - - @Schema(description = "业务的唯一标识", example = "1") - private String businessKey; - - /** - * 发起流程的用户 - */ - private UserSimpleDTO startUser; - - /** - * 流程定义 - */ - private BpmProcessDefinitionRespDTO processDefinition; - - /** - * 当前审批中的任务 - */ - private List tasks; // 仅在流程实例分页才返回 - - @Schema(description = "流程任务") - @Data - public static class Task { - - @Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String id; - - @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - private String name; - - @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") - @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser - private Long assignee; - - @Schema(description = "任务分配人", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") - private UserSimpleDTO assigneeUser; - - } - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmSimpleModelNodeDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmSimpleModelNodeDTO.java deleted file mode 100644 index a01c60d..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmSimpleModelNodeDTO.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; -import java.util.Map; - -@Schema(description = "RPC 服务 - 简单模型节点 DTO") -@Data -public class BpmSimpleModelNodeDTO { - - @Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartNode") - private String id; - - @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "开始节点") - private String name; - - @Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer type; - - @Schema(description = "节点属性") - private Map attributes; - - @Schema(description = "子节点列表") - private List childNode; - - @Schema(description = "条件节点列表") - private List conditionNodes; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java deleted file mode 100644 index 705f930..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskApproveReqDTO.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.List; -import java.util.Map; - -@Schema(description = "RPC 服务 - 通过流程任务的 Request DTO") -@Data -public class BpmTaskApproveReqDTO { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "任务编号不能为空") - private String id; - - @Schema(description = "审批意见", example = "不错不错!") - private String reason; - - @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") - private String signPicUrl; - - @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) - private Map variables; - - @Schema(description = "下一个节点审批人", example = "{nodeId:[1, 2]}") - private Map> nextAssignees; // 为什么是 Map,而不是 List 呢?因为下一个节点可能是多个,例如说并行网关的情况 - - // 新增任务变量实例,业务表单 - @Schema(description = "任务变量实例,业务表单", example = "{'formField1': 'value1', 'formField2': 'value2'}") - private Map taskVariables; -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java deleted file mode 100644 index 6ed4a8c..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import com.zt.plat.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "RPC 服务 - 流程任务分页 Request DTO") -@Data -public class BpmTaskPageReqDTO extends PageParam { - - @Schema(description = "流程任务名", example = "ZT") - private String name; - - @Schema(description = "流程定义的编号", example = "2048") - private String processDefinitionId; - - @Schema(description = "流程分类", example = "1") - private String category; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java deleted file mode 100644 index 94f73bf..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRejectReqDTO.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -@Schema(description = "RPC 服务 - 不通过流程任务的 Request DTO") -@Data -public class BpmTaskRejectReqDTO { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "任务编号不能为空") - private String id; - - @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!") - private String reason; - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java deleted file mode 100644 index 17c5a44..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import com.zt.plat.framework.common.core.KeyValue; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -@Schema(description = "RPC 服务 - 流程任务 Response DTO") -@Data -public class BpmTaskRespDTO { - - @Schema(description = "任务编号", example = "1024") - private String id; - - @Schema(description = "任务名字", example = "ZT") - private String name; - - @Schema(description = "接收人的用户编号", example = "1") - private Long assigneeUserId; - - @Schema(description = "创建时间") - private LocalDateTime createTime; - - @Schema(description = "结束时间") - private LocalDateTime endTime; - - @Schema(description = "持续时间", example = "1000") - private Long durationInMillis; - - @Schema(description = "流程实例的编号", example = "1024") - private String processInstanceId; - - @Schema(description = "流程定义的编号", example = "2048") - private String processDefinitionId; - - @Schema(description = "任务状态", example = "1") - private Integer status; - - @Schema(description = "审批建议", example = "不错不错!") - private String reason; - - @Schema(description = "任务定义的标识", example = "Activity_one") - private String taskDefinitionKey; - - @Schema(description = "表单编号", example = "1024") - private Long formId; - - @Schema(description = "表单路径", example = "/form/leave") - private String formPath; - - @Schema(description = "表单名字", example = "请假表单") - private String formName; - - @Schema(description = "表单的配置", example = "[]") - private String formConf; - - @Schema(description = "表单项的数组", example = "[]") - private List formFields; - - @Schema(description = "提交的表单值", example = "{\"name\": \"ZT\"}") - private Map formVariables; - - @Schema(description = "任务负责人编号", example = "2048") - private Long owner; - - @Schema(description = "负责人的用户信息") - private UserSimpleDTO ownerUser; - - @Schema(description = "任务分配人编号", example = "2048") - private Long assignee; - - @Schema(description = "审核的用户信息") - private UserSimpleDTO assigneeUser; - - @Schema(description = "父任务编号", example = "1024") - private String parentTaskId; - - @Schema(description = "子任务列表(由加签生成)") - private List children; - - @Schema(description = "所属流程实例") - private ProcessInstanceDTO processInstance; - - @Schema(description = "操作按钮设置值") - private Map buttonsSetting; - - @Schema(description = "是否需要签名", example = "false") - private Boolean signEnable; - - @Schema(description = "是否填写审批意见", example = "false") - private Boolean reasonRequire; - - @Schema(description = "节点类型", example = "10") - private Integer nodeType; - - @Data - @Schema(description = "流程实例信息") - public static class ProcessInstanceDTO { - - @Schema(description = "流程实例编号", example = "1024") - private String id; - - @Schema(description = "流程实例名称", example = "ZT") - private String name; - - @Schema(description = "提交时间") - private LocalDateTime createTime; - - @Schema(description = "流程定义的编号", example = "2048") - private String processDefinitionId; - - @Schema(description = "流程摘要", example = "[]") - private List> summary; - - @Schema(description = "发起人的用户信息") - private UserSimpleDTO startUser; - } - - @Data - @Schema(description = "操作按钮设置") - public static class OperationButtonSettingDTO { - - @Schema(description = "显示名称", example = "审批") - private String displayName; - - @Schema(description = "是否启用", example = "true") - private Boolean enable; - } - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java deleted file mode 100644 index 295d43b..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.zt.plat.module.bpm.api.task.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "RPC 服务 - 用户精简信息 DTO") -@Data -public class UserSimpleDTO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - private String nickname; - - @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png") - private String avatar; - - @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long deptId; - - @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部") - private String deptName; - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java deleted file mode 100644 index 3c4bac9..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ApiConstants.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.zt.plat.module.bpm.enums; - -import com.zt.plat.framework.common.enums.RpcConstants; - -/** - * API 相关的枚举 - * - * @author ZT - */ -public class ApiConstants { - - /** - * 服务名 - * - * 注意,需要保证和 spring.application.name 保持一致 - */ - public static final String NAME = "bpm-server"; - - public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/bpm"; - - public static final String VERSION = "1.0.0"; - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java deleted file mode 100644 index 13dcf72..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/DictTypeConstants.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.zt.plat.module.bpm.enums; - -/** - * BPM 字典类型的枚举类 - * - * @author ZT - */ -public interface DictTypeConstants { - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java deleted file mode 100644 index b9db1ed..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/ErrorCodeConstants.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.zt.plat.module.bpm.enums; - -import com.zt.plat.framework.common.exception.ErrorCode; - -/** - * Bpm 错误码枚举类 - *

- * bpm 系统,使用 1-009-000-000 段 - */ -public interface ErrorCodeConstants { - - // ========== 通用流程处理 模块 1-009-000-000 ========== - - // ========== OA 流程模块 1-009-001-000 ========== - ErrorCode OA_LEAVE_NOT_EXISTS = new ErrorCode(1_009_001_001, "请假申请不存在"); - - // ========== 流程模型 1-009-002-000 ========== - ErrorCode MODEL_KEY_EXISTS = new ErrorCode(1_009_002_000, "已经存在流程标识为【{}】的流程"); - ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_009_002_001, "流程模型不存在"); - ErrorCode MODEL_KEY_VALID = new ErrorCode(1_009_002_002, "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!"); - ErrorCode MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG = new ErrorCode(1_009_002_003, "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"); - ErrorCode MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG = new ErrorCode(1_009_002_004, "部署流程失败," + - "原因:用户任务({})未配置审批人,请点击【流程设计】按钮,选择该它的【任务(审批人)】进行配置"); - ErrorCode MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS = new ErrorCode(1_009_002_005, "部署流程失败,原因:BPMN 流程图中,没有开始事件"); - ErrorCode MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS = new ErrorCode(1_009_002_006, "部署流程失败,原因:BPMN 流程图中,用户任务({})的名字不存在"); - ErrorCode MODEL_UPDATE_FAIL_NOT_MANAGER = new ErrorCode(1_009_002_007, "操作流程失败,原因:你不是该流程({})的管理员"); - ErrorCode MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR = new ErrorCode(1_009_002_008, "部署流程失败,原因:首个任务({})的审批人不能是【审批人自选】"); - - // ========== 流程定义 1-009-003-000 ========== - ErrorCode PROCESS_DEFINITION_KEY_NOT_MATCH = new ErrorCode(1_009_003_000, "流程定义的标识期望是({}),当前是({}),请修改 BPMN 流程图"); - ErrorCode PROCESS_DEFINITION_NAME_NOT_MATCH = new ErrorCode(1_009_003_001, "流程定义的名字期望是({}),当前是({}),请修改 BPMN 流程图"); - ErrorCode PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_009_003_002, "流程定义不存在"); - ErrorCode PROCESS_DEFINITION_IS_SUSPENDED = new ErrorCode(1_009_003_003, "流程定义处于挂起状态"); - - // ========== 流程实例 1-009-004-000 ========== - ErrorCode PROCESS_INSTANCE_NOT_EXISTS = new ErrorCode(1_009_004_000, "流程实例不存在"); - ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS = new ErrorCode(1_009_004_001, "流程取消失败,流程不处于运行中"); - ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF = new ErrorCode(1_009_004_002, "流程取消失败,该流程不是你发起的"); - ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_003, "任务({})的候选人未配置"); - ErrorCode PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS = new ErrorCode(1_009_004_004, "任务({})的候选人({})不存在"); - ErrorCode PROCESS_INSTANCE_START_USER_CAN_START = new ErrorCode(1_009_004_005, "发起流程失败,你没有权限发起该流程"); - ErrorCode PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_005, "流程取消失败,该流程不允许取消"); - ErrorCode PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR = new ErrorCode(1_009_004_006, "流程 Http 触发器请求调用失败"); - ErrorCode PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG = new ErrorCode(1_009_004_007, "下一个任务({})的审批人未配置"); - ErrorCode PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW = new ErrorCode(1_009_004_008, "子流程取消失败,子流程不允许取消"); - - // ========== 流程任务 1-009-005-000 ========== - ErrorCode TASK_OPERATE_FAIL_ASSIGN_NOT_SELF = new ErrorCode(1_009_005_001, "操作失败,原因:该任务的审批人不是你"); - ErrorCode TASK_NOT_EXISTS = new ErrorCode(1_009_005_002, "流程任务不存在"); - ErrorCode TASK_IS_PENDING = new ErrorCode(1_009_005_003, "当前任务处于挂起状态,不能操作"); - ErrorCode TASK_TARGET_NODE_NOT_EXISTS = new ErrorCode(1_009_005_004, " 目标节点不存在"); - ErrorCode TASK_RETURN_FAIL_SOURCE_TARGET_ERROR = new ErrorCode(1_009_005_006, "退回任务失败,目标节点是在并行网关上或非同一路线上,不可跳转"); - ErrorCode TASK_DELEGATE_FAIL_USER_REPEAT = new ErrorCode(1_009_005_007, "任务委派失败,委派人和当前审批人为同一人"); - ErrorCode TASK_DELEGATE_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_008, "任务委派失败,被委派人不存在"); - ErrorCode TASK_SIGN_CREATE_USER_NOT_EXIST = new ErrorCode(1_009_005_009, "任务加签:选择的用户不存在"); - ErrorCode TASK_SIGN_CREATE_TYPE_ERROR = new ErrorCode(1_009_005_010, "任务加签:当前任务已经{},不能{}"); - ErrorCode TASK_SIGN_CREATE_USER_REPEAT = new ErrorCode(1_009_005_011, "任务加签失败,加签人与现有审批人[{}]重复"); - ErrorCode TASK_SIGN_DELETE_NO_PARENT = new ErrorCode(1_009_005_012, "任务减签失败,被减签的任务必须是通过加签生成的任务"); - ErrorCode TASK_TRANSFER_FAIL_USER_REPEAT = new ErrorCode(1_009_005_013, "任务转办失败,转办人和当前审批人为同一人"); - ErrorCode TASK_TRANSFER_FAIL_USER_NOT_EXISTS = new ErrorCode(1_009_005_014, "任务转办失败,转办人不存在"); - ErrorCode TASK_CREATE_FAIL_NO_CANDIDATE_USER = new ErrorCode(1_009_006_003, "操作失败,原因:找不到任务的审批人!"); - ErrorCode TASK_SIGNATURE_NOT_EXISTS = new ErrorCode(1_009_005_015, "签名不能为空!"); - ErrorCode TASK_REASON_REQUIRE = new ErrorCode(1_009_005_016, "审批意见不能为空!"); - - // ========== 动态表单模块 1-009-010-000 ========== - ErrorCode FORM_NOT_EXISTS = new ErrorCode(1_009_010_000, "动态表单不存在"); - ErrorCode FORM_FIELD_REPEAT = new ErrorCode(1_009_010_001, "表单项({}) 和 ({}) 使用了相同的字段名({})"); - - // ========== 用户组模块 1-009-011-000 ========== - ErrorCode USER_GROUP_NOT_EXISTS = new ErrorCode(1_009_011_000, "用户分组不存在"); - ErrorCode USER_GROUP_IS_DISABLE = new ErrorCode(1_009_011_001, "名字为【{}】的用户分组已被禁用"); - - // ========== 用户组模块 1-009-012-000 ========== - ErrorCode CATEGORY_NOT_EXISTS = new ErrorCode(1_009_012_000, "流程分类不存在"); - ErrorCode CATEGORY_NAME_DUPLICATE = new ErrorCode(1_009_012_001, "流程分类名字【{}】重复"); - ErrorCode CATEGORY_CODE_DUPLICATE = new ErrorCode(1_009_012_002, "流程分类编码【{}】重复"); - - // ========== BPM 流程监听器 1-009-013-000 ========== - ErrorCode PROCESS_LISTENER_NOT_EXISTS = new ErrorCode(1_009_013_000, "流程监听器不存在"); - ErrorCode PROCESS_LISTENER_CLASS_NOT_FOUND = new ErrorCode(1_009_013_001, "流程监听器类({})不存在"); - ErrorCode PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR = new ErrorCode(1_009_013_002, "流程监听器类({})没有实现接口({})"); - ErrorCode PROCESS_LISTENER_EXPRESSION_INVALID = new ErrorCode(1_009_013_003, "流程监听器表达式({})不合法"); - - // ========== BPM 流程表达式 1-009-014-000 ========== - ErrorCode PROCESS_EXPRESSION_NOT_EXISTS = new ErrorCode(1_009_014_000, "流程表达式不存在"); - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java deleted file mode 100644 index 2611d38..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmAutoApproveTypeEnum.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM 自动去重的类型的枚举 - * - * @author Lesan - */ -@Getter -@AllArgsConstructor -public enum BpmAutoApproveTypeEnum implements ArrayValuable { - - NONE(0, "不自动通过"), - APPROVE_ALL(1, "仅审批一次,后续重复的审批节点均自动通过"), - APPROVE_SEQUENT(2, "仅针对连续审批的节点自动通过"); - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmAutoApproveTypeEnum::getType).toArray(Integer[]::new); - - private final Integer type; - private final String name; - - @Override - public Integer[] array() { - return ARRAYS; - } - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java deleted file mode 100644 index d168f66..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmBoundaryEventTypeEnum.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * BPM 边界事件 (boundary event) 自定义类型枚举 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmBoundaryEventTypeEnum { - - USER_TASK_TIMEOUT(1, "用户任务超时"), - DELAY_TIMER_TIMEOUT(2, "延迟器超时"), - CHILD_PROCESS_TIMEOUT(3, "子流程超时"); - - private final Integer type; - private final String name; - - public static BpmBoundaryEventTypeEnum typeOf(Integer type) { - return ArrayUtil.firstMatch(eventType -> eventType.getType().equals(type), values()); - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java deleted file mode 100644 index 68574cb..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessMultiInstanceSourceTypeEnum.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM 子流程多实例来源类型枚举 - * - * @author Lesan - */ -@Getter -@AllArgsConstructor -public enum BpmChildProcessMultiInstanceSourceTypeEnum implements ArrayValuable { - - FIXED_QUANTITY(1, "固定数量"), - NUMBER_FORM(2, "数字表单"), - MULTIPLE_FORM(3, "多选表单"); - - private final Integer type; - private final String name; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessMultiInstanceSourceTypeEnum::getType).toArray(Integer[]::new); - - public static BpmChildProcessMultiInstanceSourceTypeEnum typeOf(Integer type) { - return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); - } - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java deleted file mode 100644 index 5b13462..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserEmptyTypeEnum.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM 当子流程发起人为空时类型枚举 - * - * @author Lesan - */ -@Getter -@AllArgsConstructor -public enum BpmChildProcessStartUserEmptyTypeEnum implements ArrayValuable { - - MAIN_PROCESS_START_USER(1, "同主流程发起人"), - CHILD_PROCESS_ADMIN(2, "子流程管理员"), - MAIN_PROCESS_ADMIN(3, "主流程管理员"); - - private final Integer type; - private final String name; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserEmptyTypeEnum::getType).toArray(Integer[]::new); - - public static BpmChildProcessStartUserEmptyTypeEnum typeOf(Integer type) { - return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); - } - - @Override - public Integer[] array() { - return ARRAYS; - } -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java deleted file mode 100644 index 8470a72..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmChildProcessStartUserTypeEnum.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM 子流程发起人类型枚举 - * - * @author Lesan - */ -@Getter -@AllArgsConstructor -public enum BpmChildProcessStartUserTypeEnum implements ArrayValuable { - - MAIN_PROCESS_START_USER(1, "同主流程发起人"), - FROM_FORM(2, "表单"); - - private final Integer type; - private final String name; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmChildProcessStartUserTypeEnum::getType).toArray(Integer[]::new); - - public static BpmChildProcessStartUserTypeEnum typeOf(Integer type) { - return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); - } - - @Override - public Integer[] array() { - return ARRAYS; - } -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java deleted file mode 100644 index aabaef0..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmDelayTimerTypeEnum.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM 延迟器类型枚举 - * - * @author Lesan - */ -@Getter -@AllArgsConstructor -public enum BpmDelayTimerTypeEnum implements ArrayValuable { - - FIXED_TIME_DURATION(1, "固定时长"), - FIXED_DATE_TIME(2, "固定日期"); - - private final Integer type; - private final String name; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmDelayTimerTypeEnum::getType).toArray(Integer[]::new); - - @Override - public Integer[] array() { - return ARRAYS; - } - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java deleted file mode 100644 index 8571f90..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmFieldPermissionEnum.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * BPM 表单权限的枚举 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmFieldPermissionEnum { - - READ(1, "只读"), - WRITE(2, "可编辑"), - NONE(3, "隐藏"); - - /** - * 权限 - */ - private final Integer permission; - /** - * 名字 - */ - private final String name; - - public static BpmFieldPermissionEnum valueOf(Integer permission) { - return ArrayUtil.firstMatch(item -> item.getPermission().equals(permission), values()); - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java deleted file mode 100644 index dd1fc50..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmHttpRequestParamTypeEnum.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM HTTP 请求参数设置类型。用于 Simple 设计器任务监听器和触发器配置。 - * - * @author Lesan - */ -@Getter -@AllArgsConstructor -public enum BpmHttpRequestParamTypeEnum implements ArrayValuable { - - FIXED_VALUE(1, "固定值"), - FROM_FORM(2, "表单"); - - private final Integer type; - private final String name; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmHttpRequestParamTypeEnum::getType).toArray(Integer[]::new); - - @Override - public Integer[] array() { - return ARRAYS; - } - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java deleted file mode 100644 index f28b1ed..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelFormTypeEnum.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM 模型的表单类型的枚举 - * - * @author ZT - */ -@Getter -@AllArgsConstructor -public enum BpmModelFormTypeEnum implements ArrayValuable { - - NORMAL(10, "流程表单"), // 对应 BpmFormDO - CUSTOM(20, "业务表单") // 业务自己定义的表单,自己进行数据的存储 - ; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmModelFormTypeEnum::getType).toArray(Integer[]::new); - - private final Integer type; - private final String name; - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java deleted file mode 100644 index 259b365..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmModelTypeEnum.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM 模型的类型的枚举 - * - * @author ZT - */ -@Getter -@AllArgsConstructor -public enum BpmModelTypeEnum implements ArrayValuable { - - BPMN(10, "BPMN 设计器"), // https://bpmn.io/toolkit/bpmn-js/ - SIMPLE(20, "SIMPLE 设计器"); // 参考钉钉、飞书工作流的设计器 - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmModelTypeEnum::getType).toArray(Integer[]::new); - - private final Integer type; - private final String name; - - @Override - public Integer[] array() { - return ARRAYS; - } - -} \ No newline at end of file diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java deleted file mode 100644 index b1ecfe4..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerTypeEnum.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * BPM 流程监听器的类型 - * - * @author ZT - */ -@Getter -@AllArgsConstructor -public enum BpmProcessListenerTypeEnum { - - EXECUTION("execution", "执行监听器"), - TASK("task", "任务执行器"); - - private final String type; - private final String name; - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java deleted file mode 100644 index 7ff961b..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmProcessListenerValueTypeEnum.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * BPM 流程监听器的值类型 - * - * @author ZT - */ -@Getter -@AllArgsConstructor -public enum BpmProcessListenerValueTypeEnum { - - CLASS("class", "Java 类"), - DELEGATE_EXPRESSION("delegateExpression", "代理表达式"), - EXPRESSION("expression", "表达式"); - - private final String type; - private final String name; - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java deleted file mode 100644 index b8c8ee2..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModeConditionTypeEnum.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 仿钉钉的流程器设计器条件节点的条件类型 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmSimpleModeConditionTypeEnum implements ArrayValuable { - - EXPRESSION(1, "条件表达式"), - RULE(2, "条件规则"); - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmSimpleModeConditionTypeEnum::getType).toArray(Integer[]::new); - - private final Integer type; - - private final String name; - - public static BpmSimpleModeConditionTypeEnum valueOf(Integer type) { - return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values()); - } - - @Override - public Integer[] array() { - return ARRAYS; - } -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java deleted file mode 100644 index 6590a4c..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmSimpleModelNodeTypeEnum.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; -import java.util.Objects; - -/** - * 仿钉钉的流程器设计器的模型节点类型 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmSimpleModelNodeTypeEnum implements ArrayValuable { - - // 0 ~ 1 开始和结束 - START_NODE(0, "开始", "startEvent"), - END_NODE(1, "结束", "endEvent"), - - // 10 ~ 49 各种节点 - START_USER_NODE(10, "发起人", "userTask"), // 发起人节点。前端的开始节点,Id 固定 - APPROVE_NODE(11, "审批人", "userTask"), - COPY_NODE(12, "抄送人", "serviceTask"), - TRANSACTOR_NODE(13, "办理人", "userTask"), - - DELAY_TIMER_NODE(14, "延迟器", "receiveTask"), - TRIGGER_NODE(15, "触发器", "serviceTask"), - - CHILD_PROCESS(20, "子流程", "callActivity"), - - // 50 ~ 条件分支 - CONDITION_NODE(50, "条件", "sequenceFlow"), // 用于构建流转条件的表达式 - CONDITION_BRANCH_NODE(51, "条件分支", "exclusiveGateway"), - PARALLEL_BRANCH_NODE(52, "并行分支", "parallelGateway"), - INCLUSIVE_BRANCH_NODE(53, "包容分支", "inclusiveGateway"), - ROUTER_BRANCH_NODE(54, "路由分支", "exclusiveGateway") - ; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmSimpleModelNodeTypeEnum::getType).toArray(Integer[]::new); - - private final Integer type; - private final String name; - private final String bpmnType; - - /** - * 判断是否为分支节点 - * - * @param type 节点类型 - */ - public static boolean isBranchNode(Integer type) { - return Objects.equals(CONDITION_BRANCH_NODE.getType(), type) - || Objects.equals(PARALLEL_BRANCH_NODE.getType(), type) - || Objects.equals(INCLUSIVE_BRANCH_NODE.getType(), type) - || Objects.equals(ROUTER_BRANCH_NODE.getType(), type); - } - - public static BpmSimpleModelNodeTypeEnum valueOf(Integer type) { - return ArrayUtil.firstMatch(nodeType -> nodeType.getType().equals(type), values()); - } - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java deleted file mode 100644 index 72c3c39..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmTriggerTypeEnum.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM Simple 触发器类型枚举 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmTriggerTypeEnum implements ArrayValuable { - - HTTP_REQUEST(1, "发起 HTTP 请求"), // BPM => 业务,流程继续执行,无需等待业务 - HTTP_CALLBACK(2, "接收 HTTP 回调"), // BPM => 业务 => BPM,流程卡主,等待业务回调 - - FORM_UPDATE(10, "更新流程表单数据"), - FORM_DELETE(11, "删除流程表单数据"), - ; - - /** - * 触发器执行动作类型 - */ - private final Integer type; - - /** - * 触发器执行动作描述 - */ - private final String desc; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTriggerTypeEnum::getType).toArray(Integer[]::new); - - @Override - public Integer[] array() { - return ARRAYS; - } - - public static BpmTriggerTypeEnum typeOf(Integer type) { - return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); - } -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java deleted file mode 100644 index 065035d..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveMethodEnum.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM 多人审批方式的枚举 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmUserTaskApproveMethodEnum implements ArrayValuable { - - RANDOM(1, "随机挑选一人审批", null), - RATIO(2, "多人会签(按通过比例)", "${ nrOfCompletedInstances/nrOfInstances >= %s}"), // 会签(按通过比例) - ANY(3, "多人或签(一人通过或拒绝)", "${ nrOfCompletedInstances > 0 }"), // 或签(通过只需一人,拒绝只需一人) - SEQUENTIAL(4, "依次审批", "${ nrOfCompletedInstances >= nrOfInstances }"); // 依次审批 - - /** - * 审批方式 - */ - private final Integer method; - /** - * 名字 - */ - private final String name; - /** - * 完成表达式 - */ - private final String completionCondition; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskApproveMethodEnum::getMethod).toArray(Integer[]::new); - - public static BpmUserTaskApproveMethodEnum valueOf(Integer method) { - return ArrayUtil.firstMatch(item -> item.getMethod().equals(method), values()); - } - - @Override - public Integer[] array() { - return ARRAYS; - } -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java deleted file mode 100644 index df40e18..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskApproveTypeEnum.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 用户任务的审批类型枚举 - * - * @author ZT - */ -@Getter -@AllArgsConstructor -public enum BpmUserTaskApproveTypeEnum implements ArrayValuable { - - USER(1), // 人工审批 - AUTO_APPROVE(2), // 自动通过 - AUTO_REJECT(3); // 自动拒绝 - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskApproveTypeEnum::getType).toArray(Integer[]::new); - - private final Integer type; - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java deleted file mode 100644 index 237ef8e..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignEmptyHandlerTypeEnum.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; - -/** - * BPM 用户任务的审批人为空时,处理类型枚举 - * - * @author ZT - */ -@RequiredArgsConstructor -@Getter -public enum BpmUserTaskAssignEmptyHandlerTypeEnum implements ArrayValuable { - - APPROVE(1), // 自动通过 - REJECT(2), // 自动拒绝 - ASSIGN_USER(3), // 指定人员审批 - ASSIGN_ADMIN(4), // 转交给流程管理员 - ; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskAssignEmptyHandlerTypeEnum::getType).toArray(Integer[]::new); - - private final Integer type; - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java deleted file mode 100644 index ffed389..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskAssignStartUserHandlerTypeEnum.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.Arrays; - -/** - * BPM 用户任务的审批人与发起人相同时,处理类型枚举 - * - * @author ZT - */ -@RequiredArgsConstructor -@Getter -public enum BpmUserTaskAssignStartUserHandlerTypeEnum implements ArrayValuable { - - START_USER_AUDIT(1), // 由发起人对自己审批 - SKIP(2), // 自动跳过【参考飞书】:1)如果当前节点还有其他审批人,则交由其他审批人进行审批;2)如果当前节点没有其他审批人,则该节点自动通过 - TRANSFER_DEPT_LEADER(3); // 转交给部门负责人审批【参考飞书】:若部门负责人为空,则自动通过 - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskAssignStartUserHandlerTypeEnum::getType).toArray(Integer[]::new); - - private final Integer type; - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java deleted file mode 100644 index f251971..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskRejectHandlerTypeEnum.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import cn.hutool.core.util.ArrayUtil; -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM 用户任务拒绝处理类型枚举 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmUserTaskRejectHandlerTypeEnum implements ArrayValuable { - - FINISH_PROCESS_INSTANCE(1, "终止流程"), - RETURN_USER_TASK(2, "驳回到指定任务节点"); - - private final Integer type; - private final String name; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskRejectHandlerTypeEnum::getType).toArray(Integer[]::new); - - public static BpmUserTaskRejectHandlerTypeEnum typeOf(Integer type) { - return ArrayUtil.firstMatch(item -> item.getType().equals(type), values()); - } - - @Override - public Integer[] array() { - return ARRAYS; - } -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java deleted file mode 100644 index 8f9460e..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/definition/BpmUserTaskTimeoutHandlerTypeEnum.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.zt.plat.module.bpm.enums.definition; - -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * 用户任务超时处理类型枚举 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmUserTaskTimeoutHandlerTypeEnum implements ArrayValuable { - - REMINDER(1,"自动提醒"), - APPROVE(2, "自动同意"), - REJECT(3, "自动拒绝"); - - private final Integer type; - private final String name; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmUserTaskTimeoutHandlerTypeEnum::getType).toArray(Integer[]::new); - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java deleted file mode 100644 index d8db9ae..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/message/BpmMessageEnum.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.zt.plat.module.bpm.enums.message; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * Bpm 消息的枚举 - * - * @author ZT - */ -@AllArgsConstructor -@Getter -public enum BpmMessageEnum { - - PROCESS_INSTANCE_APPROVE("bpm_process_instance_approve"), // 流程任务被审批通过时,发送给申请人 - PROCESS_INSTANCE_REJECT("bpm_process_instance_reject"), // 流程任务被审批不通过时,发送给申请人 - TASK_ASSIGNED("bpm_task_assigned"), // 任务被分配时,发送给审批人 - TASK_TIMEOUT("bpm_task_timeout"); // 任务审批超时时,发送给审批人 - - /** - * 短信模板的标识 - * - * 关联 SmsTemplateDO 的 code 属性 - */ - private final String smsTemplateCode; - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java deleted file mode 100644 index 51a5ffa..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmCommentTypeEnum.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.zt.plat.module.bpm.enums.task; - -import cn.hutool.core.util.StrUtil; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 流程任务的 Comment 评论类型枚举 - * - * @author kehaiyou - */ -@Getter -@AllArgsConstructor -public enum BpmCommentTypeEnum { - - APPROVE("1", "审批通过", "审批通过,原因是:{}"), - REJECT("2", "不通过", "审批不通过:原因是:{}"), - CANCEL("3", "已取消", "系统自动取消,原因是:{}"), - RETURN("4", "退回", "任务被退回,原因是:{}"), - DELEGATE_START("5", "委派发起", "[{}]将任务委派给[{}],委派理由为:{}"), - DELEGATE_END("6", "委派完成", "[{}]完成委派任务,任务重新回到[{}]手中,审批建议为:{}"), - TRANSFER("7", "转派", "[{}]将任务转派给[{}],转派理由为:{}"), - ADD_SIGN("8", "加签", "[{}]{}给了[{}],理由为:{}"), - SUB_SIGN("9", "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"), - ; - - /** - * 操作类型 - * - * 由于 BPM Comment 类型为 String,所以这里就不使用 Integer - */ - private final String type; - /** - * 操作名字 - */ - private final String name; - /** - * 操作描述 - */ - private final String comment; - - public String formatComment(Object... params) { - return StrUtil.format(comment, params); - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java deleted file mode 100644 index 0e61d22..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmProcessInstanceStatusEnum.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.zt.plat.module.bpm.enums.task; - -import com.zt.plat.framework.common.core.ArrayValuable; -import com.zt.plat.framework.common.util.object.ObjectUtils; -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.apache.commons.lang3.StringUtils; - -import java.util.Arrays; - -/** - * 流程实例 ProcessInstance 的状态 - * - * @author ZT - */ -@Getter -@AllArgsConstructor -public enum BpmProcessInstanceStatusEnum implements ArrayValuable { - - NOT_START(-1, "未开始"), - RUNNING(1, "审批中"), - APPROVE(2, "审批通过"), - REJECT(3, "审批不通过"), - CANCEL(4, "已取消"); - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmProcessInstanceStatusEnum::getStatus).toArray(Integer[]::new); - - /** - * 状态 - */ - private final Integer status; - /** - * 描述 - */ - private final String desc; - - @Override - public Integer[] array() { - return ARRAYS; - } - - public static boolean isRejectStatus(Integer status) { - return REJECT.getStatus().equals(status); - } - - public static boolean isProcessEndStatus(Integer status) { - return ObjectUtils.equalsAny(status, - APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus()); - } - - /** - * 通过流程的状态返回对应的枚举 - * @param status 流程状态 - * @return - */ - public static BpmProcessInstanceStatusEnum getEnumByStatus(Integer status){ - for (BpmProcessInstanceStatusEnum e : BpmProcessInstanceStatusEnum.values()) { - if (e.getStatus().equals(status)) { - return e; - } - } - return NOT_START; - } - - /** - * 通过枚举描述返回对应的枚举 - * @param desc 描述 - * @return - */ - public static BpmProcessInstanceStatusEnum getEnumByDesc(String desc){ - if (StringUtils.isEmpty(desc)) return NOT_START; - for (BpmProcessInstanceStatusEnum e : BpmProcessInstanceStatusEnum.values()) { - if (desc.equals(e.getDesc())) { - return e; - } - } - return NOT_START; - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java deleted file mode 100644 index 8bf768f..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmReasonEnum.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.zt.plat.module.bpm.enums.task; - -import cn.hutool.core.util.StrUtil; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 流程实例/任务的的处理原因枚举 - * - * @author ZT - */ -@Getter -@AllArgsConstructor -public enum BpmReasonEnum { - - // ========== 流程实例的独有原因 ========== - - REJECT_TASK("审批不通过任务,原因:{}"), // 场景:用户审批不通过任务。修改文案时,需要注意 isRejectReason 方法 - CANCEL_PROCESS_INSTANCE_BY_START_USER("用户主动取消流程,原因:{}"), // 场景:用户主动取消流程 - CANCEL_PROCESS_INSTANCE_BY_ADMIN("管理员【{}】取消流程,原因:{}"), // 场景:管理员取消流程 - CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS("子流程自动取消,原因:主流程已取消"), - - // ========== 流程任务的独有原因 ========== - - CANCEL_BY_SYSTEM("系统自动取消"), // 场景:非常多,比如说:1)多任务审批已经满足条件,无需审批该任务;2)流程实例被取消,无需审批该任务;等等 - TIMEOUT_APPROVE("审批超时,系统自动通过"), - TIMEOUT_REJECT("审批超时,系统自动不通过"), - ASSIGN_START_USER_APPROVE("审批人与提交人为同一人时,自动通过"), - ASSIGN_START_USER_APPROVE_WHEN_SKIP("审批人与提交人为同一人时,自动通过"), - ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE("发起人节点首次自动通过"), // 目前仅“子流程”使用 - ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND("审批人与提交人为同一人时,找不到部门负责人,自动通过"), - ASSIGN_START_USER_TRANSFER_DEPT_LEADER("审批人与提交人为同一人时,转交给部门负责人审批"), - ASSIGN_EMPTY_APPROVE("审批人为空,自动通过"), - ASSIGN_EMPTY_REJECT("审批人为空,自动不通过"), - APPROVE_TYPE_AUTO_APPROVE("非人工审核,自动通过"), - APPROVE_TYPE_AUTO_REJECT("非人工审核,自动不通过"), - CANCEL_BY_PROCESS_CLEAN("进程清理自动取消"), - ; - - private final String reason; - - /** - * 格式化理由 - * - * @param args 参数 - * @return 理由 - */ - public String format(Object... args) { - return StrUtil.format(reason, args); - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java deleted file mode 100644 index 40aa2b1..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskSignTypeEnum.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.zt.plat.module.bpm.enums.task; - -import cn.hutool.core.util.ArrayUtil; -import lombok.AllArgsConstructor; -import lombok.Getter; - -/** - * 流程任务的加签类型枚举 - * - * @author kehaiyou - */ -@Getter -@AllArgsConstructor -public enum BpmTaskSignTypeEnum { - - /** - * 向前加签,需要前置任务审批完成,才回到原审批人 - */ - BEFORE("before", "向前加签"), - /** - * 向后加签,需要后置任务全部审批完,才会通过原审批人节点 - */ - AFTER("after", "向后加签"); - - /** - * 类型 - */ - private final String type; - /** - * 名字 - */ - private final String name; - - public static String nameOfType(String type) { - for (BpmTaskSignTypeEnum value : values()) { - if (value.type.equals(type)) { - return value.name; - } - } - return null; - } - - public static BpmTaskSignTypeEnum of(String type) { - return ArrayUtil.firstMatch(value -> value.getType().equals(type), values()); - } - -} diff --git a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java b/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java deleted file mode 100644 index 42ac7df..0000000 --- a/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/enums/task/BpmTaskStatusEnum.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.zt.plat.module.bpm.enums.task; - -import cn.hutool.core.util.ObjUtil; -import com.zt.plat.framework.common.util.object.ObjectUtils; -import lombok.AllArgsConstructor; -import lombok.Getter; -import org.apache.commons.lang3.StringUtils; - -/** - * 流程任务 Task 的状态枚举 - * - * @author jason - */ -@Getter -@AllArgsConstructor -public enum BpmTaskStatusEnum { - - NOT_START(-1, "未开始"), - RUNNING(1, "审批中"), - APPROVE(2, "审批通过"), - REJECT(3, "审批不通过"), - CANCEL(4, "已取消"), - - RETURN(5, "已退回"), - - /** - * 使用场景: - * 1. 任务被向后【加签】时,它在审批通过后,会变成 APPROVING 这个状态,然后等到【加签】出来的任务都被审批后,才会变成 APPROVE 审批通过 - */ - APPROVING(7, "审批通过中"), - /** - * 使用场景: - * 1. 任务被向前【加签】时,它会变成 WAIT 状态,需要等待【加签】出来的任务被审批后,它才能继续变为 RUNNING 继续审批 - * 2. 任务被向后【加签】时,【加签】出来的任务处于 WAIT 状态,它们需要等待该任务被审批后,它们才能继续变为 RUNNING 继续审批 - */ - WAIT(0, "待审批"); - - /** - * 状态 - *

- * 如果新增时,注意 {@link #isEndStatus(Integer)} 是否需要变更 - */ - private final Integer status; - /** - * 名字 - */ - private final String name; - - public static boolean isRejectStatus(Integer status) { - return REJECT.getStatus().equals(status); - } - - /** - * 判断该状态是否已经处于 End 最终状态 - *

- * 主要用于一些状态更新的逻辑,如果已经是最终状态,就不再进行更新 - * - * @param status 状态 - * @return 是否 - */ - public static boolean isEndStatus(Integer status) { - return ObjectUtils.equalsAny(status, - APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus(), - RETURN.getStatus(), APPROVING.getStatus()); - } - - public static boolean isCancelStatus(Integer status) { - return ObjUtil.equal(status, CANCEL.getStatus()); - } - - - /** - * 通过流程的状态返回对应的枚举 - * @param status 流程状态 - * @return - */ - public static BpmTaskStatusEnum getEnumByStatus(Integer status){ - for (BpmTaskStatusEnum e : BpmTaskStatusEnum.values()) { - if (e.getStatus().equals(status)) { - return e; - } - } - return NOT_START; - } - - /** - * 通过枚举描述返回对应的枚举 - * @param name 描述 - * @return - */ - public static BpmTaskStatusEnum getEnumByName(String name){ - if (StringUtils.isEmpty(name)) return NOT_START; - for (BpmTaskStatusEnum e : BpmTaskStatusEnum.values()) { - if (name.equals(e.getName())) { - return e; - } - } - return NOT_START; - } -} diff --git a/zt-module-bpm-server/Dockerfile b/zt-module-bpm-server/Dockerfile deleted file mode 100644 index b8f089e..0000000 --- a/zt-module-bpm-server/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 - -FROM 172.17.19.16:10043/zt-cloud-base-service/eclipse-temurin:21-jre - -## 创建目录,并使用它作为工作目录 -RUN mkdir -p /zt-module-bpm-server -WORKDIR /zt-module-bpm-server -## 将后端项目的 Jar 文件,复制到镜像中 -COPY ./target/zt-module-bpm-server.jar app.jar - -## 设置 TZ 时区 -## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 -ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx1024m" - -## 暴露后端项目的 48080 端口 -EXPOSE 48083 - -## 启动后端项目 -CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar diff --git a/zt-module-bpm-server/pom.xml b/zt-module-bpm-server/pom.xml deleted file mode 100644 index a897263..0000000 --- a/zt-module-bpm-server/pom.xml +++ /dev/null @@ -1,147 +0,0 @@ - - - - com.zt.plat - zt-module-bpm - ${revision} - - 4.0.0 - zt-module-bpm-server - - ${project.artifactId} - - bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能,基于 Flowable 6 版本实现。 - 例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等 - - - - - com.zt.plat - zt-spring-boot-starter-env - - - - - com.zt.plat - zt-module-bpm-api - ${revision} - - - com.zt.plat - zt-module-system-api - ${revision} - - - com.zt.plat - zt-module-capital-api - ${revision} - - - - - com.zt.plat - zt-spring-boot-starter-biz-data-permission - - - com.zt.plat - zt-spring-boot-starter-biz-tenant - - - - - com.zt.plat - zt-spring-boot-starter-security - - - - - com.zt.plat - zt-spring-boot-starter-mybatis - - - - com.zt.plat - zt-spring-boot-starter-redis - - - - - com.zt.plat - zt-spring-boot-starter-rpc - - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-discovery - - - - - com.alibaba.cloud - spring-cloud-starter-alibaba-nacos-config - - - - - - - - - - - com.zt.plat - zt-spring-boot-starter-test - - - - - com.zt.plat - zt-spring-boot-starter-monitor - - - - - com.zt.plat - zt-spring-boot-starter-excel - - - - - org.flowable - flowable-spring-boot-starter-process - - - org.flowable - flowable-spring-boot-starter-actuator - - - com.zt.plat - zt-spring-boot-starter-biz-business - ${revision} - compile - - - - - - ${project.artifactId} - - - - org.springframework.boot - spring-boot-maven-plugin - ${spring.boot.version} - - - - repackage - - - - - - - diff --git a/zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java b/zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java deleted file mode 100644 index fe4cc83..0000000 --- a/zt-module-bpm-server/src/main/java/com/alibaba/druid/pool/DruidPooledStatement.java +++ /dev/null @@ -1,778 +0,0 @@ -// -// Source code recreated from a .class file by IntelliJ IDEA -// (powered by FernFlower decompiler) -// - -package com.alibaba.druid.pool; - -import com.alibaba.cloud.commons.lang.StringUtils; -import com.alibaba.druid.VERSION; -import com.alibaba.druid.support.logging.Log; -import com.alibaba.druid.support.logging.LogFactory; -import com.alibaba.druid.util.JdbcUtils; -import com.alibaba.druid.util.MySqlUtils; - -import java.net.SocketTimeoutException; -import java.sql.*; -import java.util.ArrayList; -import java.util.List; - -public class DruidPooledStatement extends PoolableWrapper implements Statement { - private static final Log LOG = LogFactory.getLog(DruidPooledStatement.class); - private final Statement stmt; - protected DruidPooledConnection conn; - protected List resultSetTrace; - protected boolean closed; - protected int fetchRowPeak = -1; - protected int exceptionCount; - - public DruidPooledStatement(DruidPooledConnection conn, Statement stmt) { - super(stmt); - this.conn = conn; - this.stmt = stmt; - } - - protected void addResultSetTrace(ResultSet resultSet) { - if (this.resultSetTrace == null) { - this.resultSetTrace = new ArrayList(1); - } else if (this.resultSetTrace.size() > 0) { - int lastIndex = this.resultSetTrace.size() - 1; - ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(lastIndex); - - try { - if (lastResultSet.isClosed()) { - this.resultSetTrace.set(lastIndex, resultSet); - return; - } - } catch (SQLException var5) { - } - } - - this.resultSetTrace.add(resultSet); - } - - protected void recordFetchRowCount(int fetchRowCount) { - if (this.fetchRowPeak < fetchRowCount) { - this.fetchRowPeak = fetchRowCount; - } - - } - - public int getFetchRowPeak() { - return this.fetchRowPeak; - } - - protected SQLException checkException(Throwable error) throws SQLException { - String sql = null; - if (this instanceof DruidPooledPreparedStatement) { - sql = ((DruidPooledPreparedStatement)this).getSql(); - } - - this.handleSocketTimeout(error); - ++this.exceptionCount; - return this.conn.handleException(error, sql); - } - - protected SQLException checkException(Throwable error, String sql) throws SQLException { - this.handleSocketTimeout(error); - ++this.exceptionCount; - return this.conn.handleException(error, sql); - } - - protected void handleSocketTimeout(Throwable error) throws SQLException { - if (this.conn != null && this.conn.transactionInfo == null && this.conn.holder != null) { - DruidDataSource dataSource = null; - DruidConnectionHolder holder = this.conn.holder; - if (holder.dataSource instanceof DruidDataSource) { - dataSource = (DruidDataSource)holder.dataSource; - } - - if (dataSource != null) { - if (dataSource.killWhenSocketReadTimeout) { - SQLException sqlException = null; - if (error instanceof SQLException) { - sqlException = (SQLException)error; - } - - if (sqlException != null) { - Throwable cause = error.getCause(); - boolean socketReadTimeout = cause instanceof SocketTimeoutException && "Read timed out".equals(cause.getMessage()); - if (socketReadTimeout) { - if (JdbcUtils.isMysqlDbType(dataSource.dbTypeName)) { - String killQuery = MySqlUtils.buildKillQuerySql(this.conn.getConnection(), (SQLException)error); - if (killQuery != null) { - DruidPooledConnection killQueryConn = null; - Statement killQueryStmt = null; - - try { - killQueryConn = dataSource.getConnection(1000L); - if (killQueryConn != null) { - killQueryStmt = killQueryConn.createStatement(); - killQueryStmt.execute(killQuery); - if (LOG.isDebugEnabled()) { - LOG.debug(killQuery + " success."); - } - - return; - } - } catch (Exception ex) { - LOG.warn(killQuery + " error.", ex); - return; - } finally { - JdbcUtils.close(killQueryStmt); - JdbcUtils.close(killQueryConn); - } - - } - } - } - } - } - } - } - } - - public DruidPooledConnection getPoolableConnection() { - return this.conn; - } - - public Statement getStatement() { - return this.stmt; - } - - protected void checkOpen() throws SQLException { - if (this.closed) { - Throwable disableError = null; - if (this.conn != null) { - disableError = this.conn.getDisableError(); - } - - if (disableError != null) { - throw new SQLException("statement is closed", disableError); - } else { - throw new SQLException("statement is closed"); - } - } - } - - protected void clearResultSet() { - if (this.resultSetTrace != null) { - for(ResultSet rs : this.resultSetTrace) { - try { - if (!rs.isClosed()) { - rs.close(); - } - } catch (SQLException ex) { - LOG.error("clearResultSet error", ex); - } - } - - this.resultSetTrace.clear(); - } - } - - public void incrementExecuteCount() { - DruidPooledConnection conn = this.getPoolableConnection(); - if (conn != null) { - DruidConnectionHolder holder = conn.getConnectionHolder(); - if (holder != null) { - DruidAbstractDataSource dataSource = holder.getDataSource(); - if (dataSource != null) { - dataSource.incrementExecuteCount(); - } - } - } - } - - public void incrementExecuteBatchCount() { - DruidPooledConnection conn = this.getPoolableConnection(); - if (conn != null) { - DruidConnectionHolder holder = conn.getConnectionHolder(); - if (holder != null) { - if (holder.getDataSource() != null) { - DruidAbstractDataSource dataSource = holder.getDataSource(); - if (dataSource != null) { - dataSource.incrementExecuteBatchCount(); - } - } - } - } - } - - public void incrementExecuteUpdateCount() { - DruidPooledConnection conn = this.getPoolableConnection(); - if (conn != null) { - DruidConnectionHolder holder = conn.getConnectionHolder(); - if (holder != null) { - DruidAbstractDataSource dataSource = holder.getDataSource(); - if (dataSource != null) { - dataSource.incrementExecuteUpdateCount(); - } - } - } - } - - public void incrementExecuteQueryCount() { - DruidPooledConnection conn = this.conn; - if (conn != null) { - DruidConnectionHolder holder = conn.holder; - if (holder != null) { - DruidAbstractDataSource dataSource = holder.dataSource; - if (dataSource != null) { - ++dataSource.executeQueryCount; - } - } - } - } - - protected void transactionRecord(String sql) throws SQLException { - this.conn.transactionRecord(sql); - } - - public final ResultSet executeQuery(String sql) throws SQLException { - this.checkOpen(); - this.incrementExecuteQueryCount(); - this.transactionRecord(sql); - this.conn.beforeExecute(); - - ResultSet var3; - try { - ResultSet rs = this.stmt.executeQuery(sql); - if (rs != null) { - DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs); - this.addResultSetTrace(poolableResultSet); - DruidPooledResultSet var4 = poolableResultSet; - return var4; - } - - var3 = rs; - } catch (Throwable t) { - this.errorCheck(t); - throw this.checkException(t, sql); - } finally { - this.conn.afterExecute(); - } - - return var3; - } - - public final int executeUpdate(String sql) throws SQLException { - this.checkOpen(); - this.incrementExecuteUpdateCount(); - this.transactionRecord(sql); - this.conn.beforeExecute(); - - int var2; - try { - var2 = this.stmt.executeUpdate(sql); - } catch (Throwable t) { - this.errorCheck(t); - throw this.checkException(t, sql); - } finally { - this.conn.afterExecute(); - } - - return var2; - } - - protected final void errorCheck(Throwable t) { - String errorClassName = t.getClass().getName(); - if (errorClassName.endsWith(".CommunicationsException") && this.conn.holder != null && this.conn.holder.dataSource.testWhileIdle) { - DruidConnectionHolder holder = this.conn.holder; - DruidAbstractDataSource dataSource = holder.dataSource; - long currentTimeMillis = System.currentTimeMillis(); - long lastActiveTimeMillis = holder.lastActiveTimeMillis; - if (lastActiveTimeMillis < holder.lastKeepTimeMillis) { - lastActiveTimeMillis = holder.lastKeepTimeMillis; - } - - long idleMillis = currentTimeMillis - lastActiveTimeMillis; - long lastValidIdleMillis = currentTimeMillis - holder.lastActiveTimeMillis; - String errorMsg = "CommunicationsException, druid version " + VERSION.getVersionNumber() + ", jdbcUrl : " + dataSource.jdbcUrl + ", testWhileIdle " + dataSource.testWhileIdle + ", idle millis " + idleMillis + ", minIdle " + dataSource.minIdle + ", poolingCount " + dataSource.getPoolingCount() + ", timeBetweenEvictionRunsMillis " + dataSource.timeBetweenEvictionRunsMillis + ", lastValidIdleMillis " + lastValidIdleMillis + ", driver " + dataSource.driver.getClass().getName(); - if (dataSource.exceptionSorter != null) { - errorMsg = errorMsg + ", exceptionSorter " + dataSource.exceptionSorter.getClass().getName(); - } - - LOG.error(errorMsg); - } - - } - - public final int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { - this.checkOpen(); - this.incrementExecuteUpdateCount(); - this.transactionRecord(sql); - this.conn.beforeExecute(); - - int var3; - try { - var3 = this.stmt.executeUpdate(sql, autoGeneratedKeys); - } catch (Throwable t) { - this.errorCheck(t); - throw this.checkException(t, sql); - } finally { - this.conn.afterExecute(); - } - - return var3; - } - - public final int executeUpdate(String sql, int[] columnIndexes) throws SQLException { - this.checkOpen(); - this.incrementExecuteUpdateCount(); - this.transactionRecord(sql); - this.conn.beforeExecute(); - - int var3; - try { - var3 = this.stmt.executeUpdate(sql, columnIndexes); - } catch (Throwable t) { - this.errorCheck(t); - throw this.checkException(t, sql); - } finally { - this.conn.afterExecute(); - } - - return var3; - } - - public final int executeUpdate(String sql, String[] columnNames) throws SQLException { - this.checkOpen(); - this.incrementExecuteUpdateCount(); - this.transactionRecord(sql); - this.conn.beforeExecute(); - - int var3; - try { - var3 = this.stmt.executeUpdate(sql, columnNames); - } catch (Throwable t) { - this.errorCheck(t); - throw this.checkException(t, sql); - } finally { - this.conn.afterExecute(); - } - - return var3; - } - - public final boolean execute(String sql, int autoGeneratedKeys) throws SQLException { - this.checkOpen(); - this.incrementExecuteCount(); - this.transactionRecord(sql); - this.conn.beforeExecute(); - - boolean var3; - try { - var3 = this.stmt.execute(sql, autoGeneratedKeys); - } catch (Throwable t) { - this.errorCheck(t); - throw this.checkException(t, sql); - } finally { - this.conn.afterExecute(); - } - - return var3; - } - - public final boolean execute(String sql, int[] columnIndexes) throws SQLException { - this.checkOpen(); - this.incrementExecuteCount(); - this.transactionRecord(sql); - this.conn.beforeExecute(); - - boolean var3; - try { - var3 = this.stmt.execute(sql, columnIndexes); - } catch (Throwable t) { - this.errorCheck(t); - throw this.checkException(t, sql); - } finally { - this.conn.afterExecute(); - } - - return var3; - } - - public final boolean execute(String sql, String[] columnNames) throws SQLException { - this.checkOpen(); - this.incrementExecuteCount(); - this.transactionRecord(sql); - this.conn.beforeExecute(); - - boolean var3; - try { - var3 = this.stmt.execute(sql, columnNames); - } catch (Throwable t) { - this.errorCheck(t); - throw this.checkException(t, sql); - } finally { - this.conn.afterExecute(); - } - - return var3; - } - - - public int getMaxFieldSize() throws SQLException { - this.checkOpen(); - - try { - return this.stmt.getMaxFieldSize(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public void close() throws SQLException { - if (!this.closed) { - this.clearResultSet(); - if (this.stmt != null) { - this.stmt.close(); - } - - this.closed = true; - DruidConnectionHolder connHolder = this.conn.getConnectionHolder(); - if (connHolder != null) { - connHolder.removeTrace(this); - } - - } - } - - public void setMaxFieldSize(int max) throws SQLException { - this.checkOpen(); - - try { - this.stmt.setMaxFieldSize(max); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final int getMaxRows() throws SQLException { - this.checkOpen(); - - try { - return this.stmt.getMaxRows(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public void setMaxRows(int max) throws SQLException { - this.checkOpen(); - - try { - this.stmt.setMaxRows(max); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final void setEscapeProcessing(boolean enable) throws SQLException { - this.checkOpen(); - - try { - this.stmt.setEscapeProcessing(enable); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final int getQueryTimeout() throws SQLException { - this.checkOpen(); - - try { - return this.stmt.getQueryTimeout(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public void setQueryTimeout(int seconds) throws SQLException { - this.checkOpen(); - - try { - this.stmt.setQueryTimeout(seconds); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final void cancel() throws SQLException { - this.checkOpen(); - - try { - this.stmt.cancel(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final SQLWarning getWarnings() throws SQLException { - this.checkOpen(); - - try { - return this.stmt.getWarnings(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final void clearWarnings() throws SQLException { - this.checkOpen(); - - try { - this.stmt.clearWarnings(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final void setCursorName(String name) throws SQLException { - this.checkOpen(); - - try { - this.stmt.setCursorName(name); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - @Override - public final boolean execute(String sql) throws SQLException { - checkOpen(); - - incrementExecuteCount(); - transactionRecord(sql); - - try { - if (StringUtils.isNotEmpty(sql)){ - sql = sql.replace("TRUE", "1"); - sql = sql.replace("FALSE", "0"); - } - return stmt.execute(sql); - } catch (Throwable t) { - errorCheck(t); - throw checkException(t, sql); - } - } - - public final ResultSet getResultSet() throws SQLException { - this.checkOpen(); - - try { - ResultSet rs = this.stmt.getResultSet(); - if (rs == null) { - return null; - } else { - DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs); - this.addResultSetTrace(poolableResultSet); - return poolableResultSet; - } - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final int getUpdateCount() throws SQLException { - this.checkOpen(); - - try { - return this.stmt.getUpdateCount(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final boolean getMoreResults() throws SQLException { - this.checkOpen(); - - try { - boolean moreResults = this.stmt.getMoreResults(); - if (this.resultSetTrace != null && this.resultSetTrace.size() > 0) { - ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(this.resultSetTrace.size() - 1); - if (lastResultSet instanceof DruidPooledResultSet) { - DruidPooledResultSet pooledResultSet = (DruidPooledResultSet)lastResultSet; - pooledResultSet.closed = true; - } - } - - return moreResults; - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public void setFetchDirection(int direction) throws SQLException { - this.checkOpen(); - - try { - this.stmt.setFetchDirection(direction); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final int getFetchDirection() throws SQLException { - this.checkOpen(); - - try { - return this.stmt.getFetchDirection(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public void setFetchSize(int rows) throws SQLException { - this.checkOpen(); - - try { - this.stmt.setFetchSize(rows); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final int getFetchSize() throws SQLException { - this.checkOpen(); - - try { - return this.stmt.getFetchSize(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final int getResultSetConcurrency() throws SQLException { - this.checkOpen(); - - try { - return this.stmt.getResultSetConcurrency(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final int getResultSetType() throws SQLException { - this.checkOpen(); - - try { - return this.stmt.getResultSetType(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final void addBatch(String sql) throws SQLException { - this.checkOpen(); - this.transactionRecord(sql); - - try { - this.stmt.addBatch(sql); - } catch (Throwable t) { - throw this.checkException(t, sql); - } - } - - public final void clearBatch() throws SQLException { - if (!this.closed) { - try { - this.stmt.clearBatch(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - } - - public int[] executeBatch() throws SQLException { - this.checkOpen(); - this.incrementExecuteBatchCount(); - this.conn.beforeExecute(); - - int[] var1; - try { - var1 = this.stmt.executeBatch(); - } catch (Throwable t) { - this.errorCheck(t); - throw this.checkException(t); - } finally { - this.conn.afterExecute(); - } - - return var1; - } - - public final Connection getConnection() throws SQLException { - this.checkOpen(); - return this.conn; - } - - public final boolean getMoreResults(int current) throws SQLException { - this.checkOpen(); - - try { - boolean results = this.stmt.getMoreResults(current); - if (this.resultSetTrace != null && this.resultSetTrace.size() > 0) { - ResultSet lastResultSet = (ResultSet)this.resultSetTrace.get(this.resultSetTrace.size() - 1); - if (lastResultSet instanceof DruidPooledResultSet) { - DruidPooledResultSet pooledResultSet = (DruidPooledResultSet)lastResultSet; - pooledResultSet.closed = true; - } - } - - return results; - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final ResultSet getGeneratedKeys() throws SQLException { - this.checkOpen(); - - try { - ResultSet rs = this.stmt.getGeneratedKeys(); - DruidPooledResultSet poolableResultSet = new DruidPooledResultSet(this, rs); - this.addResultSetTrace(poolableResultSet); - return poolableResultSet; - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final int getResultSetHoldability() throws SQLException { - this.checkOpen(); - - try { - return this.stmt.getResultSetHoldability(); - } catch (Throwable t) { - throw this.checkException(t); - } - } - - public final boolean isClosed() throws SQLException { - return this.closed; - } - - public final void setPoolable(boolean poolable) throws SQLException { - if (!poolable) { - throw new SQLException("not support"); - } - } - - public final boolean isPoolable() throws SQLException { - return false; - } - - public String toString() { - return this.stmt.toString(); - } - - public void closeOnCompletion() throws SQLException { - this.stmt.closeOnCompletion(); - } - - public boolean isCloseOnCompletion() throws SQLException { - return this.stmt.isCloseOnCompletion(); - } -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java deleted file mode 100644 index f9ce039..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/BpmServerApplication.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.zt.plat.module.bpm; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -/** - * 项目的启动类 - * - * 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 - * 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 - * 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 - * - * @author ZT - */ -@SpringBootApplication -public class BpmServerApplication { - - public static void main(String[] args) { - // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 - // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 - // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 - - SpringApplication.run(BpmServerApplication.class, args); - - // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 - // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 - // 如果你碰到启动的问题,请认真阅读 http://172.16.46.63:30888/quick-start/ 文章 - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApiImpl.java deleted file mode 100644 index 7f161dd..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmCategoryApiImpl.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.zt.plat.module.bpm.api.definition; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.api.definition.dto.BpmCategoryPageReqDTO; -import com.zt.plat.module.bpm.api.definition.dto.BpmCategoryRespDTO; -import com.zt.plat.module.bpm.api.definition.dto.BpmCategorySaveReqDTO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import com.zt.plat.module.bpm.service.definition.BpmCategoryService; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RestController; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.List; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; - -/** - * BPM 流程分类 Api 实现类 - * - * @author ZT - */ -@RestController -@Validated -public class BpmCategoryApiImpl implements BpmCategoryApi { - - @Resource - private BpmCategoryService categoryService; - - @Override - public CommonResult createCategory(@Valid BpmCategorySaveReqDTO createReqDTO) { - BpmCategorySaveReqVO createReqVO = BeanUtils.toBean(createReqDTO, BpmCategorySaveReqVO.class); - return success(categoryService.createCategory(createReqVO)); - } - - @Override - public CommonResult updateCategory(@Valid BpmCategorySaveReqDTO updateReqDTO) { - BpmCategorySaveReqVO updateReqVO = BeanUtils.toBean(updateReqDTO, BpmCategorySaveReqVO.class); - categoryService.updateCategory(updateReqVO); - return success(true); - } - - @Override - public CommonResult updateCategorySortBatch(List ids) { - categoryService.updateCategorySortBatch(ids); - return success(true); - } - - @Override - public CommonResult deleteCategory(Long id) { - categoryService.deleteCategory(id); - return success(true); - } - - @Override - public CommonResult getCategory(Long id) { - BpmCategoryDO category = categoryService.getCategory(id); - return success(BeanUtils.toBean(category, BpmCategoryRespDTO.class)); - } - - @Override - public CommonResult> getCategoryPage(@Valid BpmCategoryPageReqDTO pageReqDTO) { - BpmCategoryPageReqVO pageReqVO = BeanUtils.toBean(pageReqDTO, BpmCategoryPageReqVO.class); - PageResult pageResult = categoryService.getCategoryPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, BpmCategoryRespDTO.class)); - } - - @Override - public CommonResult> getCategorySimpleList() { - List list = categoryService.getCategoryListByStatus(CommonStatusEnum.ENABLE.getStatus()); - return success(BeanUtils.toBean(list, BpmCategoryRespDTO.class)); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApiImpl.java deleted file mode 100644 index faceb1e..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmFormApiImpl.java +++ /dev/null @@ -1,76 +0,0 @@ -package com.zt.plat.module.bpm.api.definition; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.api.definition.dto.BpmFormPageReqDTO; -import com.zt.plat.module.bpm.api.definition.dto.BpmFormRespDTO; -import com.zt.plat.module.bpm.api.definition.dto.BpmFormSaveReqDTO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.service.definition.BpmFormService; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RestController; - -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import java.util.List; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; - -/** - * 动态表单 Api 实现类 - * - * @author ZT - */ -@RestController -@Validated -public class BpmFormApiImpl implements BpmFormApi { - - @Resource - private BpmFormService formService; - - @Override - public CommonResult createForm(@Valid BpmFormSaveReqDTO createReqDTO) { - BpmFormSaveReqVO createReqVO = BeanUtils.toBean(createReqDTO, BpmFormSaveReqVO.class); - return success(formService.createForm(createReqVO)); - } - - @Override - public CommonResult updateForm(@Valid BpmFormSaveReqDTO updateReqDTO) { - BpmFormSaveReqVO updateReqVO = BeanUtils.toBean(updateReqDTO, BpmFormSaveReqVO.class); - formService.updateForm(updateReqVO); - return success(true); - } - - @Override - public CommonResult deleteForm(Long id) { - formService.deleteForm(id); - return success(true); - } - - @Override - public CommonResult getForm(Long id) { - BpmFormDO form = formService.getForm(id); - return success(BeanUtils.toBean(form, BpmFormRespDTO.class)); - } - - @Override - public CommonResult> getFormPage(BpmFormPageReqDTO pageReqDTO) { - BpmFormPageReqVO pageReqVO = BeanUtils.toBean(pageReqDTO, BpmFormPageReqVO.class); - PageResult pageResult = formService.getFormPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, BpmFormRespDTO.class)); - } - - @Override - public CommonResult> getFormSimpleList() { - List list = formService.getFormList(); - // 只返回 id、name 字段 - List dtoList = list.stream() - .map(formDO -> new BpmFormRespDTO().setId(formDO.getId()).setName(formDO.getName())) - .collect(java.util.stream.Collectors.toList()); - return success(dtoList); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApiImpl.java deleted file mode 100644 index 502b9d6..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/definition/BpmUserGroupApiImpl.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.zt.plat.module.bpm.api.definition; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.api.definition.dto.BpmUserGroupRespDTO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; -import com.zt.plat.module.bpm.service.definition.BpmUserGroupService; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RestController; - -import jakarta.annotation.Resource; -import java.util.List; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; - -/** - * 用户组 Api 实现类 - * - * @author ZT - */ -@RestController -@Validated -public class BpmUserGroupApiImpl implements BpmUserGroupApi { - - @Resource - private BpmUserGroupService userGroupService; - - @Override - public CommonResult getUserGroup(Long id) { - BpmUserGroupDO userGroup = userGroupService.getUserGroup(id); - return success(BeanUtils.toBean(userGroup, BpmUserGroupRespDTO.class)); - } - - @Override - public CommonResult> getUserGroupSimpleList() { - List list = userGroupService.getUserGroupListByStatus(CommonStatusEnum.ENABLE.getStatus()); - return success(BeanUtils.toBean(list, BpmUserGroupRespDTO.class)); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java deleted file mode 100644 index 7c3b4c4..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * bpm API 实现类,定义暴露给其它模块的 API - */ -package com.zt.plat.module.bpm.api; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java deleted file mode 100644 index e0cb9bc..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApiImpl.java +++ /dev/null @@ -1,151 +0,0 @@ -package com.zt.plat.module.bpm.api.task; - -import cn.hutool.core.util.StrUtil; -import com.zt.plat.framework.business.core.util.DeptUtil; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.api.task.dto.*; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskApproveReqVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRejectReqVO; -import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceConvert; -import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceDTOConvert; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.bpm.service.task.BpmTaskService; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.repository.ProcessDefinition; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RestController; - -import java.util.List; -import java.util.Map; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.getLoginUserId; - -/** - * Flowable 流程实例 Api 实现类 - * - * @author ZT - * @author jason - */ -@RestController -@Validated -public class BpmProcessInstanceApiImpl implements BpmProcessInstanceApi { - - @Resource - private BpmProcessInstanceService processInstanceService; - - @Resource - private BpmTaskService taskService; - - @Resource - private BpmProcessDefinitionService processDefinitionService; - - @Resource - private AdminUserApi adminUserApi; - - @Resource - private DeptApi deptApi; - - @Override - public CommonResult createProcessInstance(Long userId, @Valid @RequestBody BpmProcessInstanceCreateReqDTO reqDTO) { - return success(processInstanceService.createProcessInstance(userId, reqDTO)); - } - - - @Override - public CommonResult getProcessInstance(String id) { - HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(id); - if (processInstance == null) { - return success(null); - } - - // 拼接返回 - ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( - processInstance.getProcessDefinitionId()); - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo( - processInstance.getProcessDefinitionId()); - AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData(); - DeptRespDTO dept = null; - if (startUser != null) { - Long deptId = DeptUtil.getDeptId(startUser); - if (deptId > 0) { - dept = deptApi.getDept(deptId).getCheckedData(); - } - } - BpmProcessInstanceRespVO vo = BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance, - processDefinition, processDefinitionInfo, startUser, dept); - return success(BpmProcessInstanceDTOConvert.INSTANCE.convert(vo)); - } - - @Override - public CommonResult cancelProcessInstanceByStartUser( - Long userId, @Valid @RequestBody BpmProcessInstanceCancelReqDTO cancelReqDTO) { - BpmProcessInstanceCancelReqVO cancelReqVO = BpmProcessInstanceDTOConvert.INSTANCE.convertVO(cancelReqDTO); - processInstanceService.cancelProcessInstanceByStartUser(userId, cancelReqVO); - return success(true); - } - - @Override - public CommonResult cancelProcessInstanceByAdmin( - Long userId, @Valid @RequestBody BpmProcessInstanceCancelReqDTO cancelReqDTO) { - BpmProcessInstanceCancelReqVO cancelReqVO = BpmProcessInstanceDTOConvert.INSTANCE.convertVO(cancelReqDTO); - processInstanceService.cancelProcessInstanceByAdmin(userId, cancelReqVO); - return success(true); - } - - @Override - @SuppressWarnings("unchecked") - public CommonResult getApprovalDetail(Long userId, - @Valid @RequestBody BpmApprovalDetailReqDTO reqDTO) { - BpmApprovalDetailReqVO reqVO = BpmProcessInstanceDTOConvert.INSTANCE.convertVO(reqDTO); - if (StrUtil.isNotEmpty(reqDTO.getProcessVariablesStr())) { - reqVO.setProcessVariables(JsonUtils.parseObject(reqDTO.getProcessVariablesStr(), Map.class)); - } - BpmApprovalDetailRespVO respVO = processInstanceService.getApprovalDetail(userId, reqVO); - return success(BpmProcessInstanceDTOConvert.INSTANCE.convert(respVO)); - } - - @Override - @SuppressWarnings("unchecked") - public CommonResult> getNextApprovalNodes(Long userId, - @Valid @RequestBody BpmApprovalDetailReqDTO reqDTO) { - BpmApprovalDetailReqVO reqVO = BpmProcessInstanceDTOConvert.INSTANCE.convertVO(reqDTO); - if (StrUtil.isNotEmpty(reqDTO.getProcessVariablesStr())) { - reqVO.setProcessVariables(JsonUtils.parseObject(reqDTO.getProcessVariablesStr(), Map.class)); - } - List nodes = processInstanceService.getNextApprovalNodes(userId, reqVO); - return success(BpmProcessInstanceDTOConvert.INSTANCE.convertActivityNodes(nodes)); - } - - @Override - public CommonResult getProcessInstanceBpmnModelView(String id) { - BpmProcessInstanceBpmnModelViewRespVO respVO = processInstanceService.getProcessInstanceBpmnModelView(id); - return success(BpmProcessInstanceDTOConvert.INSTANCE.convert(respVO)); - } - - @Override - public CommonResult approveTask(BpmTaskApproveReqDTO reqVO) { - taskService.approveTask(getLoginUserId(), BeanUtils.toBean(reqVO, BpmTaskApproveReqVO.class)); - return success(true); - } - - @Override - public CommonResult rejectTask(BpmTaskRejectReqDTO reqVO) { - taskService.rejectTask(getLoginUserId(), BeanUtils.toBean(reqVO, BpmTaskRejectReqVO.class)); - return success(true); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java deleted file mode 100644 index 4d70c6a..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/api/task/BpmTaskApiImpl.java +++ /dev/null @@ -1,203 +0,0 @@ -package com.zt.plat.module.bpm.api.task; - -import cn.hutool.core.collection.CollUtil; -import com.zt.plat.framework.business.core.util.DeptUtil; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils; -import com.zt.plat.module.bpm.api.task.dto.BpmTaskPageReqDTO; -import com.zt.plat.module.bpm.api.task.dto.BpmTaskRespDTO; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskPageReqVO; -import com.zt.plat.module.bpm.convert.task.BpmTaskConvert; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.service.definition.BpmFormService; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.bpm.service.task.BpmTaskService; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.task.api.Task; -import org.flowable.task.api.history.HistoricTaskInstance; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.RestController; - -import java.util.*; -import java.util.stream.Stream; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; - -/** - * BPM 任务 API 实现类 - * - * @author ZT - */ -@RestController -@Validated -public class BpmTaskApiImpl implements BpmTaskApi { - - @Resource - private BpmTaskService taskService; - @Resource - private BpmProcessInstanceService processInstanceService; - @Resource - private BpmFormService formService; - @Resource - private BpmProcessDefinitionService processDefinitionService; - @Resource - private AdminUserApi adminUserApi; - @Resource - private DeptApi deptApi; - - @Override - public CommonResult> getTaskTodoPage(@Valid BpmTaskPageReqDTO pageReqDTO) { - // 转换请求参数 - BpmTaskPageReqVO pageReqVO = BeanUtils.toBean(pageReqDTO, BpmTaskPageReqVO.class); - - // 调用 Service 层方法 - PageResult pageResult = taskService.getTaskTodoPage(SecurityFrameworkUtils.getLoginUserId(), pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(new ArrayList<>()); - } - - // 拼接数据 - 参考 Controller 逻辑 - Map processInstanceMap = processInstanceService.getProcessInstanceMap(convertSet(pageResult.getList(), Task::getProcessInstanceId)); - Map userMap = adminUserApi.getUserMap(convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); - Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(convertSet(pageResult.getList(), Task::getProcessDefinitionId)); - - // 使用转换器构建完整的 VO 结果,然后转换为 DTO - var voPageResult = BpmTaskConvert.INSTANCE.buildTodoTaskPage(pageResult, processInstanceMap, userMap, processDefinitionInfoMap); - List result = BpmTaskConvert.INSTANCE.buildTaskRespDTOList(voPageResult.getList()); - - return success(result); - } - - @Override - public CommonResult> getTaskDonePage(@Valid BpmTaskPageReqDTO pageReqDTO) { - // 转换请求参数 - BpmTaskPageReqVO pageReqVO = BeanUtils.toBean(pageReqDTO, BpmTaskPageReqVO.class); - - // 调用 Service 层方法 - PageResult pageResult = taskService.getTaskDonePage(SecurityFrameworkUtils.getLoginUserId(), pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(new ArrayList<>()); - } - - // 拼接数据 - 参考 Controller 逻辑 - Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); - Map userMap = adminUserApi.getUserMap(convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); - Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); - - // 使用转换器构建完整的 VO 结果,然后转换为 DTO - var voPageResult = BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null, processDefinitionInfoMap); - List result = BpmTaskConvert.INSTANCE.buildTaskRespDTOList(voPageResult.getList()); - - return success(result); - } - - @Override - public CommonResult> getTaskManagerPage(@Valid BpmTaskPageReqDTO pageReqDTO) { - // 转换请求参数 - BpmTaskPageReqVO pageReqVO = BeanUtils.toBean(pageReqDTO, BpmTaskPageReqVO.class); - - // 调用 Service 层方法 - PageResult pageResult = taskService.getTaskPage(SecurityFrameworkUtils.getLoginUserId(), pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(new ArrayList<>()); - } - - // 拼接数据 - 参考 Controller 逻辑 - Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap(convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); - // 获得 User 和 Dept Map - Set userIds = convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())); - userIds.addAll(convertSet(pageResult.getList(), task -> NumberUtils.parseLong(task.getAssignee()))); - Map userMap = adminUserApi.getUserMap(userIds); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); - Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap(convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); - - // 使用转换器构建完整的 VO 结果,然后转换为 DTO - var voPageResult = BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap, processDefinitionInfoMap); - List result = BpmTaskConvert.INSTANCE.buildTaskRespDTOList(voPageResult.getList()); - - return success(result); - } - - @Override - public CommonResult> getTaskListByProcessInstanceId(String processInstanceId) { - // 调用 Service 层方法 - List taskList = taskService.getTaskListByProcessInstanceId(processInstanceId, true); - if (CollUtil.isEmpty(taskList)) { - return success(Collections.emptyList()); - } - - // 拼接数据 - 参考 Controller 逻辑 - Set userIds = convertSetByFlatMap(taskList, task -> Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner()))); - Map userMap = adminUserApi.getUserMap(userIds); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); - // 获得 Form Map - Map formMap = formService.getFormMap(convertSet(taskList, task -> { - String formKey = task.getFormKey(); - if (formKey == null || formKey.isBlank()) { - return 0L; - } - try { - return Long.parseLong(formKey); - } catch (NumberFormatException e) { - // 如果 formKey 不是数字(比如是URL),返回0L - return 0L; - } - })); - - // 使用转换器构建完整的 VO 结果,然后转换为 DTO - var voList = BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, formMap, userMap, deptMap); - List result = BpmTaskConvert.INSTANCE.buildTaskRespDTOList(voList); - - return success(result); - } - - @Override - public CommonResult> getTaskListByReturn(String id) { - // 调用 Service 层方法 - var userTaskList = taskService.getUserTaskListByReturn(id); - - // 转换返回结果 - 只返回 id 和 name(对应 taskDefinitionKey 和 name) - List result = userTaskList.stream().map(userTask -> { - BpmTaskRespDTO dto = new BpmTaskRespDTO(); - dto.setName(userTask.getName()); - dto.setTaskDefinitionKey(userTask.getId()); - return dto; - }).toList(); - - return success(result); - } - - @Override - public CommonResult> getTaskListByParentTaskId(String parentTaskId) { - // 调用 Service 层方法 - List taskList = taskService.getTaskListByParentTaskId(parentTaskId); - if (CollUtil.isEmpty(taskList)) { - return success(Collections.emptyList()); - } - - // 拼接数据 - 参考 Controller 逻辑 - Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(taskList, user -> Stream.of(NumberUtils.parseLong(user.getAssignee()), NumberUtils.parseLong(user.getOwner())))); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); - - // 使用转换器构建完整的 VO 结果,然后转换为 DTO - var voList = BpmTaskConvert.INSTANCE.buildTaskListByParentTaskId(taskList, userMap, deptMap); - List result = BpmTaskConvert.INSTANCE.buildTaskRespDTOList(voList); - - return success(result); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java deleted file mode 100644 index b0c466b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/dept/DeptSimpleBaseVO.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.base.dept; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "部门精简信息 VO") -@Data -public class DeptSimpleBaseVO { - - @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术部") - private String name; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java deleted file mode 100644 index 141c17f..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 基础包,放一些通用的 VO 类 - */ -package com.zt.plat.module.bpm.controller.admin.base; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java deleted file mode 100644 index 68b9b8a..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.base.user; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "用户精简信息 VO") -@Data -public class UserSimpleBaseVO { - - @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long id; - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - private String nickname; - @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png") - private String avatar; - - @Schema(description = "部门编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Long deptId; - @Schema(description = "部门名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "研发部") - private String deptName; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java deleted file mode 100644 index b318b4b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmCategoryController.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryRespVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import com.zt.plat.module.bpm.service.definition.BpmCategoryService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.Comparator; -import java.util.List; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - BPM 流程分类") -@RestController -@RequestMapping("/bpm/category") -@Validated -public class BpmCategoryController { - - @Resource - private BpmCategoryService categoryService; - - @PostMapping("/create") - @Operation(summary = "创建流程分类") - @PreAuthorize("@ss.hasPermission('bpm:category:create')") - public CommonResult createCategory(@Valid @RequestBody BpmCategorySaveReqVO createReqVO) { - return success(categoryService.createCategory(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新流程分类") - @PreAuthorize("@ss.hasPermission('bpm:category:update')") - public CommonResult updateCategory(@Valid @RequestBody BpmCategorySaveReqVO updateReqVO) { - categoryService.updateCategory(updateReqVO); - return success(true); - } - - @PutMapping("/update-sort-batch") - @Operation(summary = "批量更新流程分类的排序") - @Parameter(name = "ids", description = "分类编号列表", required = true, example = "1,2,3") - @PreAuthorize("@ss.hasPermission('bpm:category:update')") - public CommonResult updateCategorySortBatch(@RequestParam("ids") List ids) { - categoryService.updateCategorySortBatch(ids); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除流程分类") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('bpm:category:delete')") - public CommonResult deleteCategory(@RequestParam("id") Long id) { - categoryService.deleteCategory(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得流程分类") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('bpm:category:query')") - public CommonResult getCategory(@RequestParam("id") Long id) { - BpmCategoryDO category = categoryService.getCategory(id); - return success(BeanUtils.toBean(category, BpmCategoryRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得流程分类分页") - @PreAuthorize("@ss.hasPermission('bpm:category:query')") - public CommonResult> getCategoryPage(@Valid BpmCategoryPageReqVO pageReqVO) { - PageResult pageResult = categoryService.getCategoryPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, BpmCategoryRespVO.class)); - } - - @GetMapping("/simple-list") - @Operation(summary = "获取流程分类的精简信息列表", description = "只包含被开启的分类,主要用于前端的下拉选项") - public CommonResult> getCategorySimpleList() { - List list = categoryService.getCategoryListByStatus(CommonStatusEnum.ENABLE.getStatus()); - list.sort(Comparator.comparingInt(BpmCategoryDO::getSort)); - return success(convertList(list, category -> new BpmCategoryRespVO().setId(category.getId()) - .setName(category.getName()).setCode(category.getCode()))); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java deleted file mode 100644 index fa47346..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmFormController.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormRespVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.service.definition.BpmFormService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - 动态表单") -@RestController -@RequestMapping("/bpm/form") -@Validated -public class BpmFormController { - - @Resource - private BpmFormService formService; - - @PostMapping("/create") - @Operation(summary = "创建动态表单") - @PreAuthorize("@ss.hasPermission('bpm:form:create')") - public CommonResult createForm(@Valid @RequestBody BpmFormSaveReqVO createReqVO) { - return success(formService.createForm(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新动态表单") - @PreAuthorize("@ss.hasPermission('bpm:form:update')") - public CommonResult updateForm(@Valid @RequestBody BpmFormSaveReqVO updateReqVO) { - formService.updateForm(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除动态表单") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('bpm:form:delete')") - public CommonResult deleteForm(@RequestParam("id") Long id) { - formService.deleteForm(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得动态表单") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('bpm:form:query')") - public CommonResult getForm(@RequestParam("id") Long id) { - BpmFormDO form = formService.getForm(id); - return success(BeanUtils.toBean(form, BpmFormRespVO.class)); - } - - @GetMapping({"/list-all-simple", "/simple-list"}) - @Operation(summary = "获得动态表单的精简列表", description = "用于表单下拉框") - public CommonResult> getFormSimpleList() { - List list = formService.getFormList(); - return success(convertList(list, formDO -> // 只返回 id、name 字段 - new BpmFormRespVO().setId(formDO.getId()).setName(formDO.getName()))); - } - - @GetMapping("/page") - @Operation(summary = "获得动态表单分页") - @PreAuthorize("@ss.hasPermission('bpm:form:query')") - public CommonResult> getFormPage(@Valid BpmFormPageReqVO pageVO) { - PageResult pageResult = formService.getFormPage(pageVO); - return success(BeanUtils.toBean(pageResult, BpmFormRespVO.class)); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java deleted file mode 100644 index 8a408e8..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java +++ /dev/null @@ -1,200 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition; - -import cn.hutool.core.collection.CollUtil; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.*; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO; -import com.zt.plat.module.bpm.convert.definition.BpmModelConvert; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.service.definition.BpmCategoryService; -import com.zt.plat.module.bpm.service.definition.BpmFormService; -import com.zt.plat.module.bpm.service.definition.BpmModelService; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.flowable.engine.repository.Deployment; -import org.flowable.engine.repository.Model; -import org.flowable.engine.repository.ProcessDefinition; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Stream; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; -import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - 流程模型") -@RestController -@RequestMapping("/bpm/model") -@Validated -public class BpmModelController { - - @Resource - private BpmModelService modelService; - @Resource - private BpmFormService formService; - @Resource - private BpmCategoryService categoryService; - @Resource - private BpmProcessDefinitionService processDefinitionService; - - @Resource - private AdminUserApi adminUserApi; - @Resource - private DeptApi deptApi; - - @GetMapping("/list") - @Operation(summary = "获得模型分页") - @Parameter(name = "name", description = "模型名称", example = "ZT") - public CommonResult> getModelList(@RequestParam(value = "name", required = false) String name) { - List list = modelService.getModelList(name); - if (CollUtil.isEmpty(list)) { - return success(Collections.emptyList()); - } - - // 获得 Form 表单 - Set formIds = convertSet(list, model -> { - BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); - return metaInfo != null ? metaInfo.getFormId() : null; - }); - Map formMap = formService.getFormMap(formIds); - // 获得 Category Map - Map categoryMap = categoryService.getCategoryMap( - convertSet(list, Model::getCategory)); - // 获得 Deployment Map - Map deploymentMap = processDefinitionService.getDeploymentMap( - convertSet(list, Model::getDeploymentId)); - // 获得 ProcessDefinition Map - List processDefinitions = processDefinitionService.getProcessDefinitionListByDeploymentIds( - deploymentMap.keySet()); - Map processDefinitionMap = convertMap(processDefinitions, ProcessDefinition::getDeploymentId); - // 获得 User Map、Dept Map - Set userIds = convertSetByFlatMap(list, model -> { - BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); - return metaInfo != null ? metaInfo.getStartUserIds().stream() : Stream.empty(); - }); - Map userMap = adminUserApi.getUserMap(userIds); - Set deptIds = convertSetByFlatMap(list, model -> { - BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); - return metaInfo != null && metaInfo.getStartDeptIds() != null ? metaInfo.getStartDeptIds().stream() : Stream.empty(); - }); - Map deptMap = deptApi.getDeptMap(deptIds); - return success(BpmModelConvert.INSTANCE.buildModelList(list, - formMap, categoryMap, deploymentMap, processDefinitionMap, userMap, deptMap)); - } - - @GetMapping("/get") - @Operation(summary = "获得模型") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('bpm:model:query')") - public CommonResult getModel(@RequestParam("id") String id) { - Model model = modelService.getModel(id); - if (model == null) { - return null; - } - byte[] bpmnBytes = modelService.getModelBpmnXML(id); - BpmSimpleModelNodeVO simpleModel = modelService.getSimpleModel(id); - return success(BpmModelConvert.INSTANCE.buildModel(model, bpmnBytes, simpleModel)); - } - - @PostMapping("/create") - @Operation(summary = "新建模型") - @PreAuthorize("@ss.hasPermission('bpm:model:create')") - public CommonResult createModel(@Valid @RequestBody BpmModelSaveReqVO createRetVO) { - return success(modelService.createModel(createRetVO)); - } - - @PutMapping("/update") - @Operation(summary = "修改模型") - @PreAuthorize("@ss.hasPermission('bpm:model:update')") - public CommonResult updateModel(@Valid @RequestBody BpmModelSaveReqVO modelVO) { - modelService.updateModel(getLoginUserId(), modelVO); - return success(true); - } - - @PutMapping("/update-sort-batch") - @Operation(summary = "批量修改模型排序") - @Parameter(name = "ids", description = "编号数组", required = true, example = "1,2,3") - public CommonResult updateModelSortBatch(@RequestParam("ids") List ids) { - modelService.updateModelSortBatch(getLoginUserId(), ids); - return success(true); - } - - @PostMapping("/deploy") - @Operation(summary = "部署模型") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('bpm:model:deploy')") - public CommonResult deployModel(@RequestParam("id") String id) { - modelService.deployModel(getLoginUserId(), id); - return success(true); - } - - @PutMapping("/update-state") - @Operation(summary = "修改模型的状态", description = "实际更新的部署的流程定义的状态") - @PreAuthorize("@ss.hasPermission('bpm:model:update')") - public CommonResult updateModelState(@Valid @RequestBody BpmModelUpdateStateReqVO reqVO) { - modelService.updateModelState(getLoginUserId(), reqVO.getId(), reqVO.getState()); - return success(true); - } - - @Deprecated - @PutMapping("/update-bpmn") - @Operation(summary = "修改模型的 BPMN") - @PreAuthorize("@ss.hasPermission('bpm:model:update')") - public CommonResult updateModelBpmn(@Valid @RequestBody BpmModeUpdateBpmnReqVO reqVO) { - modelService.updateModelBpmnXml(reqVO.getId(), reqVO.getBpmnXml()); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除模型") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('bpm:model:delete')") - public CommonResult deleteModel(@RequestParam("id") String id) { - modelService.deleteModel(getLoginUserId(), id); - return success(true); - } - - @DeleteMapping("/clean") - @Operation(summary = "清理模型") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('bpm:model:clean')") - public CommonResult cleanModel(@RequestParam("id") String id) { - modelService.cleanModel(getLoginUserId(), id); - return success(true); - } - - // ========== 仿钉钉/飞书的精简模型 ========= - - @GetMapping("/simple/get") - @Operation(summary = "获得仿钉钉流程设计模型") - @Parameter(name = "modelId", description = "流程模型编号", required = true, example = "a2c5eee0-eb6c-11ee-abf4-0c37967c420a") - public CommonResult getSimpleModel(@RequestParam("id") String modelId){ - return success(modelService.getSimpleModel(modelId)); - } - - @Deprecated - @PostMapping("/simple/update") - @Operation(summary = "保存仿钉钉流程设计模型") - @PreAuthorize("@ss.hasPermission('bpm:model:update')") - public CommonResult updateSimpleModel(@Valid @RequestBody BpmSimpleModelUpdateReqVO reqVO) { - modelService.updateSimpleModel(getLoginUserId(), reqVO); - return success(Boolean.TRUE); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java deleted file mode 100644 index e5ee29a..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessDefinitionController.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition; - -import cn.hutool.core.collection.CollUtil; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; -import com.zt.plat.module.bpm.convert.definition.BpmProcessDefinitionConvert; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.service.definition.BpmCategoryService; -import com.zt.plat.module.bpm.service.definition.BpmFormService; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.common.engine.impl.db.SuspensionState; -import org.flowable.engine.repository.Deployment; -import org.flowable.engine.repository.ProcessDefinition; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; -import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - 流程定义") -@RestController -@RequestMapping("/bpm/process-definition") -@Validated -public class BpmProcessDefinitionController { - - @Resource - private BpmProcessDefinitionService processDefinitionService; - @Resource - private BpmFormService formService; - @Resource - private BpmCategoryService categoryService; - - @GetMapping("/page") - @Operation(summary = "获得流程定义分页") - @PreAuthorize("@ss.hasPermission('bpm:process-definition:query')") - public CommonResult> getProcessDefinitionPage( - BpmProcessDefinitionPageReqVO pageReqVO) { - PageResult pageResult = processDefinitionService.getProcessDefinitionPage(pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(PageResult.empty(pageResult.getTotal())); - } - - // 获得 Category Map - Map categoryMap = categoryService.getCategoryMap( - convertSet(pageResult.getList(), ProcessDefinition::getCategory)); - // 获得 Deployment Map - Map deploymentMap = processDefinitionService.getDeploymentMap( - convertSet(pageResult.getList(), ProcessDefinition::getDeploymentId)); - // 获得 BpmProcessDefinitionInfoDO Map - Map processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap( - convertSet(pageResult.getList(), ProcessDefinition::getId)); - // 获得 Form Map - Map formMap = formService.getFormMap( - convertSet(processDefinitionMap.values(), BpmProcessDefinitionInfoDO::getFormId)); - return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionPage( - pageResult, deploymentMap, processDefinitionMap, formMap, categoryMap)); - } - - @GetMapping ("/list") - @Operation(summary = "获得流程定义列表") - @Parameter(name = "suspensionState", description = "挂起状态", required = true, example = "1") // 参见 Flowable SuspensionState 枚举 - public CommonResult> getProcessDefinitionList( - @RequestParam("suspensionState") Integer suspensionState) { - // 1.1 获得开启的流程定义 - List list = processDefinitionService.getProcessDefinitionListBySuspensionState(suspensionState); - if (CollUtil.isEmpty(list)) { - return success(Collections.emptyList()); - } - // 1.2 移除不可见的流程定义 - Map processDefinitionMap = processDefinitionService.getProcessDefinitionInfoMap( - convertSet(list, ProcessDefinition::getId)); - Long userId = getLoginUserId(); - list.removeIf(processDefinition -> { - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionMap.get(processDefinition.getId()); - return processDefinitionInfo == null // 不存在 - || Boolean.FALSE.equals(processDefinitionInfo.getVisible()) // visible 不可见 - || !processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId); // 无权限发起 - }); - - // 2. 拼接 VO 返回 - return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinitionList( - list, null, processDefinitionMap, null, null)); - } - - @GetMapping("/simple-list") - @Operation(summary = "获得流程定义精简列表", description = "只包含未挂起的流程,主要用于前端的下拉选项") - public CommonResult> getSimpleProcessDefinitionList() { - // 只查询未挂起的流程 - List list = processDefinitionService.getProcessDefinitionListBySuspensionState( - SuspensionState.ACTIVE.getStateCode()); - // 拼接 VO 返回,只返回 id、name、key - return success(convertList(list, definition -> new BpmProcessDefinitionRespVO() - .setId(definition.getId()).setName(definition.getName()).setKey(definition.getKey()))); - } - - @GetMapping ("/get") - @Operation(summary = "获得流程定义") - @Parameter(name = "id", description = "流程编号", required = true, example = "1024") - @Parameter(name = "key", description = "流程定义标识", required = true, example = "1024") - public CommonResult getProcessDefinition( - @RequestParam(value = "id", required = false) String id, - @RequestParam(value = "key", required = false) String key) { - ProcessDefinition processDefinition = id != null ? processDefinitionService.getProcessDefinition(id) - : processDefinitionService.getActiveProcessDefinition(key); - if (processDefinition == null) { - return success(null); - } - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo(processDefinition.getId()); - BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId()); - return success(BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition( - processDefinition, null, processDefinitionInfo, null, null, bpmnModel)); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java deleted file mode 100644 index 08fcb42..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessExpressionController.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionRespVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; -import com.zt.plat.module.bpm.service.definition.BpmProcessExpressionService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - BPM 流程表达式") -@RestController -@RequestMapping("/bpm/process-expression") -@Validated -public class BpmProcessExpressionController { - - @Resource - private BpmProcessExpressionService processExpressionService; - - @PostMapping("/create") - @Operation(summary = "创建流程表达式") - @PreAuthorize("@ss.hasPermission('bpm:process-expression:create')") - public CommonResult createProcessExpression(@Valid @RequestBody BpmProcessExpressionSaveReqVO createReqVO) { - return success(processExpressionService.createProcessExpression(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新流程表达式") - @PreAuthorize("@ss.hasPermission('bpm:process-expression:update')") - public CommonResult updateProcessExpression(@Valid @RequestBody BpmProcessExpressionSaveReqVO updateReqVO) { - processExpressionService.updateProcessExpression(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除流程表达式") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('bpm:process-expression:delete')") - public CommonResult deleteProcessExpression(@RequestParam("id") Long id) { - processExpressionService.deleteProcessExpression(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得流程表达式") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('bpm:process-expression:query')") - public CommonResult getProcessExpression(@RequestParam("id") Long id) { - BpmProcessExpressionDO processExpression = processExpressionService.getProcessExpression(id); - return success(BeanUtils.toBean(processExpression, BpmProcessExpressionRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得流程表达式分页") - @PreAuthorize("@ss.hasPermission('bpm:process-expression:query')") - public CommonResult> getProcessExpressionPage( - @Valid BpmProcessExpressionPageReqVO pageReqVO) { - PageResult pageResult = processExpressionService.getProcessExpressionPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, BpmProcessExpressionRespVO.class)); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java deleted file mode 100644 index 3077a91..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmProcessListenerController.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerRespVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; -import com.zt.plat.module.bpm.service.definition.BpmProcessListenerService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; - -@Tag(name = "管理后台 - BPM 流程监听器") -@RestController -@RequestMapping("/bpm/process-listener") -@Validated -public class BpmProcessListenerController { - - @Resource - private BpmProcessListenerService processListenerService; - - @PostMapping("/create") - @Operation(summary = "创建流程监听器") - @PreAuthorize("@ss.hasPermission('bpm:process-listener:create')") - public CommonResult createProcessListener(@Valid @RequestBody BpmProcessListenerSaveReqVO createReqVO) { - return success(processListenerService.createProcessListener(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新流程监听器") - @PreAuthorize("@ss.hasPermission('bpm:process-listener:update')") - public CommonResult updateProcessListener(@Valid @RequestBody BpmProcessListenerSaveReqVO updateReqVO) { - processListenerService.updateProcessListener(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除流程监听器") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('bpm:process-listener:delete')") - public CommonResult deleteProcessListener(@RequestParam("id") Long id) { - processListenerService.deleteProcessListener(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得流程监听器") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('bpm:process-listener:query')") - public CommonResult getProcessListener(@RequestParam("id") Long id) { - BpmProcessListenerDO processListener = processListenerService.getProcessListener(id); - return success(BeanUtils.toBean(processListener, BpmProcessListenerRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得流程监听器分页") - @PreAuthorize("@ss.hasPermission('bpm:process-listener:query')") - public CommonResult> getProcessListenerPage( - @Valid BpmProcessListenerPageReqVO pageReqVO) { - PageResult pageResult = processListenerService.getProcessListenerPage(pageReqVO); - return success(BeanUtils.toBean(pageResult, BpmProcessListenerRespVO.class)); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java deleted file mode 100644 index 226e5da..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmUserGroupController.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupRespVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; -import com.zt.plat.module.bpm.service.definition.BpmUserGroupService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; - -@Tag(name = "管理后台 - 用户组") -@RestController -@RequestMapping("/bpm/user-group") -@Validated -public class BpmUserGroupController { - - @Resource - private BpmUserGroupService userGroupService; - - @PostMapping("/create") - @Operation(summary = "创建用户组") - @PreAuthorize("@ss.hasPermission('bpm:user-group:create')") - public CommonResult createUserGroup(@Valid @RequestBody BpmUserGroupSaveReqVO createReqVO) { - return success(userGroupService.createUserGroup(createReqVO)); - } - - @PutMapping("/update") - @Operation(summary = "更新用户组") - @PreAuthorize("@ss.hasPermission('bpm:user-group:update')") - public CommonResult updateUserGroup(@Valid @RequestBody BpmUserGroupSaveReqVO updateReqVO) { - userGroupService.updateUserGroup(updateReqVO); - return success(true); - } - - @DeleteMapping("/delete") - @Operation(summary = "删除用户组") - @Parameter(name = "id", description = "编号", required = true) - @PreAuthorize("@ss.hasPermission('bpm:user-group:delete')") - public CommonResult deleteUserGroup(@RequestParam("id") Long id) { - userGroupService.deleteUserGroup(id); - return success(true); - } - - @GetMapping("/get") - @Operation(summary = "获得用户组") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - @PreAuthorize("@ss.hasPermission('bpm:user-group:query')") - public CommonResult getUserGroup(@RequestParam("id") Long id) { - BpmUserGroupDO userGroup = userGroupService.getUserGroup(id); - return success(BeanUtils.toBean(userGroup, BpmUserGroupRespVO.class)); - } - - @GetMapping("/page") - @Operation(summary = "获得用户组分页") - @PreAuthorize("@ss.hasPermission('bpm:user-group:query')") - public CommonResult> getUserGroupPage(@Valid BpmUserGroupPageReqVO pageVO) { - PageResult pageResult = userGroupService.getUserGroupPage(pageVO); - return success(BeanUtils.toBean(pageResult, BpmUserGroupRespVO.class)); - } - - @GetMapping("/simple-list") - @Operation(summary = "获取用户组精简信息列表", description = "只包含被开启的用户组,主要用于前端的下拉选项") - public CommonResult> getUserGroupSimpleList() { - List list = userGroupService.getUserGroupListByStatus(CommonStatusEnum.ENABLE.getStatus()); - return success(convertList(list, group -> new BpmUserGroupRespVO().setId(group.getId()).setName(group.getName()))); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java deleted file mode 100644 index 5de14e2..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryPageReqVO.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.category; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.pojo.PageParam; -import com.zt.plat.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - BPM 流程分类分页 Request VO") -@Data -public class BpmCategoryPageReqVO extends PageParam { - - @Schema(description = "分类名", example = "王五") - private String name; - - @Schema(description = "分类标志", example = "OA") - private String code; - - @Schema(description = "分类状态", example = "1") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java deleted file mode 100644 index 5f3db13..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategoryRespVO.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.category; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - BPM 流程分类 Response VO") -@Data -public class BpmCategoryRespVO { - - @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") - private Long id; - - @Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") - private String name; - - @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") - private String code; - - @Schema(description = "分类描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "你猜") - private String description; - - @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED) - private Integer sort; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java deleted file mode 100644 index 0203453..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/category/BpmCategorySaveReqVO.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.category; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - BPM 流程分类新增/修改 Request VO") -@Data -public class BpmCategorySaveReqVO { - - @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3167") - private Long id; - - @Schema(description = "分类名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") - @NotEmpty(message = "分类名不能为空") - private String name; - - @Schema(description = "分类描述", example = "你猜") - private String description; - - @Schema(description = "分类标志", requiredMode = Schema.RequiredMode.REQUIRED, example = "OA") - @NotEmpty(message = "分类标志不能为空") - private String code; - - @Schema(description = "分类状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "分类状态不能为空") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "分类排序", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "分类排序不能为空") - private Integer sort; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java deleted file mode 100644 index 1d3bbed..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionPageReqVO.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.expression; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.pojo.PageParam; -import com.zt.plat.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - BPM 流程表达式分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class BpmProcessExpressionPageReqVO extends PageParam { - - @Schema(description = "表达式名字", example = "李四") - private String name; - - @Schema(description = "表达式状态", example = "1") - @InEnum(CommonStatusEnum.class) - private Integer status; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java deleted file mode 100644 index 2bb959b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionRespVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.expression; - -import com.alibaba.excel.annotation.ExcelProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - BPM 流程表达式 Response VO") -@Data -public class BpmProcessExpressionRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3870") - @ExcelProperty("编号") - private Long id; - - @Schema(description = "表达式名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") - @ExcelProperty("表达式名字") - private String name; - - @Schema(description = "表达式状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "表达式", requiredMode = Schema.RequiredMode.REQUIRED) - private String expression; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java deleted file mode 100644 index a5771d1..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/expression/BpmProcessExpressionSaveReqVO.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.expression; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - BPM 流程表达式新增/修改 Request VO") -@Data -public class BpmProcessExpressionSaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3870") - private Long id; - - @Schema(description = "表达式名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四") - @NotEmpty(message = "表达式名字不能为空") - private String name; - - @Schema(description = "表达式状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "表达式状态不能为空") - private Integer status; - - @Schema(description = "表达式", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "表达式不能为空") - private String expression; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java deleted file mode 100644 index 53af985..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormFieldVO.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.form; - -import lombok.Data; - -/** - * 流程表单字段 VO - */ -@Data -public class BpmFormFieldVO { - - /** - * 字段类型 - */ - private String type; - /** - * 字段标识 - */ - private String field; - /** - * 字段标题 - */ - private String title; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java deleted file mode 100644 index 4160512..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.form; - -import com.zt.plat.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 动态表单分页 Request VO") -@Data -public class BpmFormPageReqVO extends PageParam { - - @Schema(description = "表单名称", example = "ZT") - private String name; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java deleted file mode 100644 index 1950d7a..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.form; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - 动态表单 Response VO") -@Data -public class BpmFormRespVO { - - @Schema(description = "表单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - @NotNull(message = "表单名称不能为空") - private String name; - - @Schema(description = "表单的配置-JSON 字符串", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "表单的配置不能为空") - private String conf; - - @Schema(description = "表单项的数组-JSON 字符串的数组", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "表单项的数组不能为空") - private List fields; - - @Schema(description = "表单状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "表单状态不能为空") - private Integer status; // 参见 CommonStatusEnum 枚举 - - @Schema(description = "备注", example = "我是备注") - private String remark; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java deleted file mode 100644 index 5953485..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.form; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -import java.util.List; - -@Schema(description = "管理后台 - 动态表单创建/更新 Request VO") -@Data -public class BpmFormSaveReqVO { - - @Schema(description = "表单编号", example = "1024") - private Long id; - - @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - @NotNull(message = "表单名称不能为空") - private String name; - - @Schema(description = "表单的配置-JSON 字符串", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "表单的配置不能为空") - private String conf; - - @Schema(description = "表单项的数组-JSON 字符串的数组", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "表单项的数组不能为空") - private List fields; - - @Schema(description = "表单状态-参见 CommonStatusEnum 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "表单状态不能为空") - private Integer status; - - @Schema(description = "备注", example = "我是备注") - private String remark; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java deleted file mode 100644 index 25a0c2f..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.group; - -import com.zt.plat.framework.common.pojo.PageParam; -import com.zt.plat.framework.common.util.date.DateUtils; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 用户组分页 Request VO") -@Data -public class BpmUserGroupPageReqVO extends PageParam { - - @Schema(description = "编号", example = "1024") - private Long id; - - @Schema(description = "组名", example = "ZT") - private String name; - - @Schema(description = "状态", example = "1") - private Integer status; - - @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "创建时间") - private LocalDateTime[] createTime; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java deleted file mode 100644 index 7bff95d..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.group; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.Set; - -@Schema(description = "管理后台 - 用户组 Response VO") -@Data -public class BpmUserGroupRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - private String name; - - @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT源码") - private String description; - - @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") - private Set userIds; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java deleted file mode 100644 index ceccee3..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.group; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -import java.util.Set; - -@Schema(description = "管理后台 - 用户组创建/修改 Request VO") -@Data -public class BpmUserGroupSaveReqVO { - - @Schema(description = "编号", example = "1024") - private Long id; - - @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - @NotNull(message = "组名不能为空") - private String name; - - @Schema(description = "描述", example = "ZT源码") - private String description; - - @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") - @NotNull(message = "成员编号数组不能为空") - private Set userIds; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - private Integer status; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java deleted file mode 100644 index 13b8a73..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerPageReqVO.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.listener; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.pojo.PageParam; -import com.zt.plat.framework.common.validation.InEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.EqualsAndHashCode; -import lombok.ToString; - -@Schema(description = "管理后台 - BPM 流程监听器分页 Request VO") -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -public class BpmProcessListenerPageReqVO extends PageParam { - - @Schema(description = "监听器名字", example = "赵六") - private String name; - - @Schema(description = "监听器类型", example = "execution") - private String type; - - @Schema(description = "监听事件", example = "start") - private String event; - - @Schema(description = "状态", example = "1") - @InEnum(CommonStatusEnum.class) - private Integer status; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java deleted file mode 100644 index 7bfdde8..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerRespVO.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.listener; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - BPM 流程监听器 Response VO") -@Data -public class BpmProcessListenerRespVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13089") - private Long id; - - @Schema(description = "监听器名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - private String name; - - @Schema(description = "监听器类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "execution") - private String type; - - @Schema(description = "监听器状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; - - @Schema(description = "监听事件", requiredMode = Schema.RequiredMode.REQUIRED, example = "start") - private String event; - - @Schema(description = "监听器值类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "class") - private String valueType; - - @Schema(description = "监听器值", requiredMode = Schema.RequiredMode.REQUIRED) - private String value; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java deleted file mode 100644 index 908caa9..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/listener/BpmProcessListenerSaveReqVO.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.listener; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - BPM 流程监听器新增/修改 Request VO") -@Data -public class BpmProcessListenerSaveReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13089") - private Long id; - - @Schema(description = "监听器名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") - @NotEmpty(message = "监听器名字不能为空") - private String name; - - @Schema(description = "监听器类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "execution") - @NotEmpty(message = "监听器类型不能为空") - private String type; - - @Schema(description = "监听器状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "监听器状态不能为空") - private Integer status; - - @Schema(description = "监听事件", requiredMode = Schema.RequiredMode.REQUIRED, example = "start") - @NotEmpty(message = "监听事件不能为空") - private String event; - - @Schema(description = "监听器值类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "class") - @NotEmpty(message = "监听器值类型不能为空") - private String valueType; - - @Schema(description = "监听器值", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "监听器值不能为空") - private String value; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java deleted file mode 100644 index 03e0e5f..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModeUpdateBpmnReqVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.model; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -@Schema(description = "管理后台 - 流程模型的更新 BPMN XML Request VO") -@Data -public class BpmModeUpdateBpmnReqVO { - - @Schema(description = "流程编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "流程编号不能为空") - private String id; - - @Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED) - @NotEmpty(message = "BPMN XML 不能为空") - private String bpmnXml; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java deleted file mode 100644 index dcc06fc..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ /dev/null @@ -1,184 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.model; - -import com.zt.plat.framework.common.core.KeyValue; -import com.zt.plat.framework.common.validation.InEnum; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.enums.definition.BpmAutoApproveTypeEnum; -import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; -import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import org.hibernate.validator.constraints.URL; - -import java.util.List; - -/** - * BPM 流程 MetaInfo Response DTO - * 主要用于 { Model#setMetaInfo(String)} 的存储 - * - * 最终,它的字段和 - * {@link com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} - * 是一致的 - * - * @author ZT - */ -@Data -public class BpmModelMetaInfoVO { - - @Schema(description = "流程图标", example = "https://www.iocoder.cn/zt.jpg") - @URL(message = "流程图标格式不正确") - private String icon; - - @Schema(description = "流程描述", example = "我是描述") - private String description; - - @Schema(description = "流程类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @InEnum(BpmModelTypeEnum.class) - @NotNull(message = "流程类型不能为空") - private Integer type; - - @Schema(description = "表单类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - @InEnum(BpmModelFormTypeEnum.class) - @NotNull(message = "表单类型不能为空") - private Integer formType; - @Schema(description = "表单编号", example = "1024") - private Long formId; // formType 为 NORMAL 使用,必须非空 - - @Schema(description = "自定义表单的提交路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/create") - private String formCustomCreatePath; // 表单类型为 CUSTOM 时,必须非空 - @Schema(description = "自定义表单的查看路径,使用 Vue 的路由地址", example = "/bpm/oa/leave/view") - private String formCustomViewPath; // 表单类型为 CUSTOM 时,必须非空 - - @Schema(description = "是否可见", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否可见不能为空") - private Boolean visible; - - @Schema(description = "是否允许重新发起", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") - @NotNull(message = "是否允许重新发起不能为空") - private Boolean restart; - - @Schema(description = "可发起用户编号数组", example = "[1,2,3]") - private List startUserIds; - - @Schema(description = "可发起部门编号数组", example = "[2,4,6]") - private List startDeptIds; - - @Schema(description = "可管理用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[2,4,6]") - @NotEmpty(message = "可管理用户编号数组不能为空") - private List managerUserIds; - - @Schema(description = "排序", example = "1") - private Long sort; // 创建时,后端自动生成 - - @Schema(description = "允许撤销审批中的申请", example = "true") - private Boolean allowCancelRunningProcess; - - @Schema(description = "流程 ID 规则", example = "{}") - private ProcessIdRule processIdRule; - - @Schema(description = "自动去重类型", example = "1") - @InEnum(BpmAutoApproveTypeEnum.class) - private Integer autoApprovalType; - - @Schema(description = "标题设置", example = "{}") - private TitleSetting titleSetting; - - @Schema(description = "摘要设置", example = "{}") - private SummarySetting summarySetting; - - @Schema(description = "流程前置通知设置", example = "{}") - private HttpRequestSetting processBeforeTriggerSetting; - - @Schema(description = "流程后置通知设置", example = "{}") - private HttpRequestSetting processAfterTriggerSetting; - - @Schema(description = "任务前置通知设置", example = "{}") - private HttpRequestSetting taskBeforeTriggerSetting; - - @Schema(description = "任务后置通知设置", example = "{}") - private HttpRequestSetting taskAfterTriggerSetting; - - @Schema(description = "流程 ID 规则") - @Data - @Valid - public static class ProcessIdRule { - - @Schema(description = "是否启用", example = "false") - @NotNull(message = "是否启用不能为空") - private Boolean enable; - - @Schema(description = "前缀", example = "XX") - private String prefix; - - @Schema(description = "中缀", example = "20250120") - private String infix; // 精确到日、精确到时、精确到分、精确到秒 - - @Schema(description = "后缀", example = "YY") - private String postfix; - - @Schema(description = "序列长度", example = "5") - @NotNull(message = "序列长度不能为空") - private Integer length; - - } - - @Schema(description = "标题设置") - @Data - @Valid - public static class TitleSetting { - - @Schema(description = "是否自定义", example = "false") - @NotNull(message = "是否自定义不能为空") - private Boolean enable; - - @Schema(description = "标题", example = "流程标题") - private String title; - - } - - @Schema(description = "摘要设置") - @Data - @Valid - public static class SummarySetting { - - @Schema(description = "是否自定义", example = "false") - @NotNull(message = "是否自定义不能为空") - private Boolean enable; - - @Schema(description = "摘要字段数组", example = "[]") - private List summary; - - } - - @Schema(description = "http 请求通知设置", example = "{}") - @Data - public static class HttpRequestSetting { - - @Schema(description = "请求路径", example = "http://127.0.0.1") - @NotEmpty(message = "请求 URL 不能为空") - @URL(message = "请求 URL 格式不正确") - private String url; - - @Schema(description = "请求头参数设置", example = "[]") - @Valid - private List header; - - @Schema(description = "请求头参数设置", example = "[]") - @Valid - private List body; - - /** - * 请求返回处理设置,用于修改流程表单值 - *

- * key:表示要修改的流程表单字段名(name) - * value:接口返回的字段名 - */ - @Schema(description = "请求返回处理设置", example = "[]") - private List> response; - - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java deleted file mode 100644 index 63ef57d..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.model; - -import com.zt.plat.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO; -import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - 流程模型 Response VO") -@Data -public class BpmModelRespVO extends BpmModelMetaInfoVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String id; - - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_zt") - private String key; - - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - private String name; - - @Schema(description = "流程图标", example = "https://www.iocoder.cn/zt.jpg") - private String icon; - - @Schema(description = "流程分类编号", example = "1") - private String category; - @Schema(description = "流程分类名字", example = "请假") - private String categoryName; - - @Schema(description = "表单名字", example = "请假表单") - private String formName; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - - @Schema(description = "可发起的用户数组") - private List startUsers; - - @Schema(description = "可发起的部门数组") - private List startDepts; - - @Schema(description = "BPMN XML") - private String bpmnXml; - - @Schema(description = "仿钉钉流程设计模型对象") - private BpmSimpleModelNodeVO simpleModel; - - /** - * 最新部署的流程定义 - */ - private BpmProcessDefinitionRespVO processDefinition; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java deleted file mode 100644 index 6d57341..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.model; - -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - 流程模型的保存 Request VO") -@Data -public class BpmModelSaveReqVO extends BpmModelMetaInfoVO { - - @Schema(description = "编号", example = "1024") - private String id; - - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_zt") - @NotEmpty(message = "流程标识不能为空") - private String key; - - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - @NotEmpty(message = "流程名称不能为空") - private String name; - - @Schema(description = "流程分类", example = "1") - private String category; - - @Schema(description = "BPMN XML") - private String bpmnXml; - - @Schema(description = "仿钉钉流程设计模型对象") - @Valid - private BpmSimpleModelNodeVO simpleModel; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java deleted file mode 100644 index cd7fea1..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelUpdateStateReqVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.model; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - 流程模型更新状态 Request VO") -@Data -public class BpmModelUpdateStateReqVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotNull(message = "编号不能为空") - private String id; - - @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "状态不能为空") - private Integer state; // 参见 Flowable SuspensionState 枚举 - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java deleted file mode 100644 index f7e7928..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ /dev/null @@ -1,526 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.zt.plat.framework.common.core.KeyValue; -import com.zt.plat.framework.common.validation.InEnum; -import com.zt.plat.module.bpm.enums.definition.*; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import org.flowable.bpmn.model.IOParameter; -import org.hibernate.validator.constraints.URL; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -@Schema(description = "管理后台 - 仿钉钉流程设计模型节点 VO") -@Data -@JsonInclude(JsonInclude.Include.NON_NULL) -public class BpmSimpleModelNodeVO { - - @Schema(description = "模型节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartEvent_1") - @NotEmpty(message = "模型节点编号不能为空") - private String id; - - @Schema(description = "模型节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "模型节点类型不能为空") - @InEnum(BpmSimpleModelNodeTypeEnum.class) - private Integer type; - - @Schema(description = "模型节点名称", example = "领导审批") - private String name; - - @Schema(description = "节点展示内容", example = "指定成员: ZT源码") - private String showText; - - @Schema(description = "子节点") - private BpmSimpleModelNodeVO childNode; // 补充说明:在该模型下,子节点有且仅有一个,不会有多个 - - @Schema(description = "候选人策略", example = "30") - @InEnum(BpmTaskCandidateStrategyEnum.class) - private Integer candidateStrategy; // 用于审批,抄送节点 - - @Schema(description = "候选人参数") - private String candidateParam; // 用于审批,抄送节点 - - @Schema(description = "审批节点类型", example = "1") - @InEnum(BpmUserTaskApproveTypeEnum.class) - private Integer approveType; // 用于审批节点 - - @Schema(description = "多人审批方式", example = "1") - @InEnum(BpmUserTaskApproveMethodEnum.class) - private Integer approveMethod; // 用于审批节点 - - @Schema(description = "通过比例", example = "100") - private Integer approveRatio; // 通过比例,当多人审批方式为:多人会签(按通过比例) 需要设置 - - @Schema(description = "表单权限", example = "[]") - private List> fieldsPermission; - - @Schema(description = "操作按钮设置", example = "[]") - private List buttonsSetting; // 用于审批节点 - - @Schema(description = "是否需要签名", example = "false") - private Boolean signEnable; - - @Schema(description = "是否填写审批意见", example = "false") - private Boolean reasonRequire; - - /** - * 审批节点拒绝处理 - */ - private RejectHandler rejectHandler; - - /** - * 审批节点超时处理 - */ - private TimeoutHandler timeoutHandler; - - @Schema(description = "审批节点的审批人与发起人相同时,对应的处理类型", example = "1") - @InEnum(BpmUserTaskAssignStartUserHandlerTypeEnum.class) - private Integer assignStartUserHandlerType; - - /** - * 空处理策略 - */ - private AssignEmptyHandler assignEmptyHandler; - - /** - * 创建任务监听器 - */ - private ListenerHandler taskCreateListener; - /** - * 指派任务监听器 - */ - private ListenerHandler taskAssignListener; - /** - * 完成任务监听器 - */ - private ListenerHandler taskCompleteListener; - - @Schema(description = "延迟器设置", example = "{}") - private DelaySetting delaySetting; - - @Schema(description = "条件节点") - private List conditionNodes; // 补充说明:有且仅有条件、并行、包容分支会使用 - - /** - * 条件节点设置 - */ - private ConditionSetting conditionSetting; // 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE - - @Schema(description = "路由分支组", example = "[]") - private List routerGroups; - - @Schema(description = "路由分支默认分支 ID", example = "Flow_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true - @JsonIgnore - private String routerDefaultFlowId; // 仅用于路由分支节点 BpmSimpleModelNodeType.ROUTER_BRANCH_NODE - - /** - * 触发器节点设置 - */ - private TriggerSetting triggerSetting; - - @Schema(description = "附加节点 Id", example = "UserTask_xxx", hidden = true) // 由后端生成(不从前端传递),所以 hidden = true - @JsonIgnore - private String attachNodeId; // 目前用于触发器节点(HTTP 回调)。需要 UserTask 和 ReceiveTask(附加节点) 来完成 - - /** - * 子流程设置 - */ - private ChildProcessSetting childProcessSetting; - - @Schema(description = "任务监听器") - @Valid - @Data - public static class ListenerHandler { - - @Schema(description = "是否开启任务监听器", example = "false") - @NotNull(message = "是否开启任务监听器不能为空") - private Boolean enable; - - @Schema(description = "请求路径", example = "http://xxxxx") - private String path; - - @Schema(description = "请求头", example = "[]") - private List header; - - @Schema(description = "请求体", example = "[]") - private List body; - - } - - @Schema(description = "HTTP 请求参数设置") - @Data - public static class HttpRequestParam { - - @Schema(description = "值类型", example = "1") - @InEnum(BpmHttpRequestParamTypeEnum.class) - @NotNull(message = "值类型不能为空") - private Integer type; - - @Schema(description = "键", example = "xxx") - @NotEmpty(message = "键不能为空") - private String key; - - @Schema(description = "值", example = "xxx") - @NotEmpty(message = "值不能为空") - private String value; - - } - - @Schema(description = "审批节点拒绝处理策略") - @Data - public static class RejectHandler { - - @Schema(description = "拒绝处理类型", example = "1") - @InEnum(BpmUserTaskRejectHandlerTypeEnum.class) - private Integer type; - - @Schema(description = "任务拒绝后驳回的节点 Id", example = "Activity_1") - private String returnNodeId; - } - - @Schema(description = "审批节点超时处理策略") - @Valid - @Data - public static class TimeoutHandler { - - @Schema(description = "是否开启超时处理", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - @NotNull(message = "是否开启超时处理不能为空") - private Boolean enable; - - @Schema(description = "任务超时未处理的行为", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "任务超时未处理的行为不能为空") - @InEnum(BpmUserTaskTimeoutHandlerTypeEnum.class) - private Integer type; - - @Schema(description = "超时时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "PT6H") - @NotEmpty(message = "超时时间不能为空") - private String timeDuration; - - @Schema(description = "最大提醒次数", example = "1") - private Integer maxRemindCount; - } - - @Schema(description = "空处理策略") - @Data - @Valid - public static class AssignEmptyHandler { - - @Schema(description = "空处理类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "空处理类型不能为空") - @InEnum(BpmUserTaskAssignEmptyHandlerTypeEnum.class) - private Integer type; - - @Schema(description = "指定人员审批的用户编号数组", example = "1") - private List userIds; - } - - @Schema(description = "操作按钮设置") - @Data - @Valid - public static class OperationButtonSetting { - - // TODO @jason:是不是按钮的标识?id 会和数据库的 id 自增有点模糊,key 标识会更合理一点点哈。 - @Schema(description = "按钮 Id", example = "1") - private Integer id; - - @Schema(description = "显示名称", example = "审批") - private String displayName; - - @Schema(description = "是否启用", example = "true") - private Boolean enable; - } - - @Schema(description = "条件设置") - @Data - @Valid - // 仅用于条件节点 BpmSimpleModelNodeTypeEnum.CONDITION_NODE - public static class ConditionSetting { - - @Schema(description = "条件类型", example = "1") - @InEnum(BpmSimpleModeConditionTypeEnum.class) - private Integer conditionType; - - @Schema(description = "条件表达式", example = "${day>3}") - private String conditionExpression; - - @Schema(description = "是否默认条件", example = "true") - private Boolean defaultFlow; - - /** - * 条件组 - */ - private ConditionGroups conditionGroups; - } - - @Schema(description = "条件组") - @Data - @Valid - public static class ConditionGroups { - - @Schema(description = "条件组下的条件关系是否为与关系", example = "true") - @NotNull(message = "条件关系不能为空") - private Boolean and; - - @Schema(description = "条件组下的条件", example = "[]") - @NotEmpty(message = "条件不能为空") - private List conditions; - } - - @Schema(description = "条件") - @Data - @Valid - public static class Condition { - - @Schema(description = "条件下的规则关系是否为与关系", example = "true") - @NotNull(message = "规则关系不能为空") - private Boolean and; - - @Schema(description = "条件下的规则", example = "[]") - @NotEmpty(message = "规则不能为空") - private List rules; - } - - @Schema(description = "条件规则") - @Data - @Valid - public static class ConditionRule { - - @Schema(description = "运行符号", example = "==") - @NotEmpty(message = "运行符号不能为空") - private String opCode; - - @Schema(description = "运算符左边的值,例如某个流程变量", example = "startUserId") - @NotEmpty(message = "运算符左边的值不能为空") - private String leftSide; - - @Schema(description = "运算符右边的值", example = "1") - @NotEmpty(message = "运算符右边的值不能为空") - private String rightSide; - } - - @Schema(description = "延迟器") - @Data - @Valid - public static class DelaySetting { - - @Schema(description = "延迟时间类型", example = "1") - @NotNull(message = "延迟时间类型不能为空") - @InEnum(BpmDelayTimerTypeEnum.class) - private Integer delayType; - - @Schema(description = "延迟时间表达式", example = "PT1H,2025-01-01T00:00:00") - @NotEmpty(message = "延迟时间表达式不能为空") - private String delayTime; - } - - @Schema(description = "路由分支") - @Data - @Valid - public static class RouterSetting { - - @Schema(description = "节点 Id", example = "Activity_xxx") // 跳转到该节点 - @NotEmpty(message = "节点 Id 不能为空") - private String nodeId; - - @Schema(description = "条件类型", example = "1") - @InEnum(BpmSimpleModeConditionTypeEnum.class) - @NotNull(message = "条件类型不能为空") - private Integer conditionType; - - @Schema(description = "条件表达式", example = "${day>3}") - private String conditionExpression; - - @Schema(description = "条件组", example = "{}") - private ConditionGroups conditionGroups; - } - - @Schema(description = "触发器节点配置") - @Data - @Valid - public static class TriggerSetting { - - @Schema(description = "触发器类型", example = "1") - @InEnum(BpmTriggerTypeEnum.class) - @NotNull(message = "触发器类型不能为空") - private Integer type; - - /** - * http 请求触发器设置 - */ - @Valid - private HttpRequestTriggerSetting httpRequestSetting; - - /** - * 流程表单触发器设置 - */ - private List formSettings; - - @Schema(description = "http 请求触发器设置", example = "{}") - @Data - public static class HttpRequestTriggerSetting { - - @Schema(description = "请求路径", example = "http://127.0.0.1") - @NotEmpty(message = "请求 URL 不能为空") - @URL(message = "请求 URL 格式不正确") - private String url; - - @Schema(description = "请求头参数设置", example = "[]") - @Valid - private List header; - - @Schema(description = "请求头参数设置", example = "[]") - @Valid - private List body; - - /** - * 请求返回处理设置,用于修改流程表单值 - *

- * key:表示要修改的流程表单字段名(name) - * value:接口返回的字段名 - */ - @Schema(description = "请求返回处理设置", example = "[]") - private List> response; - - /** - * Http 回调请求,需要指定回调任务 Key,用于回调执行 - */ - @Schema(description = "回调任务 Key", example = "xxx", hidden = true) - private String callbackTaskDefineKey; - - } - - @Schema(description = "流程表单触发器设置", example = "{}") - @Data - public static class FormTriggerSetting { - - @Schema(description = "条件类型", example = "1") - @InEnum(BpmSimpleModeConditionTypeEnum.class) - private Integer conditionType; - - @Schema(description = "条件表达式", example = "${day>3}") - private String conditionExpression; - - @Schema(description = "条件组", example = "{}") - private ConditionGroups conditionGroups; - - @Schema(description = "修改的表单字段", example = "{}") - private Map updateFormFields; - - @Schema(description = "删除表单字段", example = "[]") - private Set deleteFields; - } - } - - @Schema(description = "子流程节点配置") - @Data - @Valid - public static class ChildProcessSetting { - - @Schema(description = "被调用流程", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") - @NotEmpty(message = "被调用流程不能为空") - private String calledProcessDefinitionKey; - - @Schema(description = "被调用流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "xxx") - @NotEmpty(message = "被调用流程名称不能为空") - private String calledProcessDefinitionName; - - @Schema(description = "是否异步", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - @NotNull(message = "是否异步不能为空") - private Boolean async; - - @Schema(description = "输入参数(主->子)", example = "[]") - private List inVariables; - - @Schema(description = "输出参数(子->主)", example = "[]") - private List outVariables; - - @Schema(description = "是否自动跳过子流程发起节点", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - @NotNull(message = "是否自动跳过子流程发起节点不能为空") - private Boolean skipStartUserNode; - - @Schema(description = "子流程发起人配置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") - @NotNull(message = "子流程发起人配置不能为空") - private StartUserSetting startUserSetting; - - @Schema(description = "超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") - private TimeoutSetting timeoutSetting; - - @Schema(description = "多实例设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "{}") - private MultiInstanceSetting multiInstanceSetting; - - @Schema(description = "子流程发起人配置") - @Data - @Valid - public static class StartUserSetting { - - @Schema(description = "子流程发起人类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "子流程发起人类型") - @InEnum(BpmChildProcessStartUserTypeEnum.class) - private Integer type; - - @Schema(description = "表单", example = "xxx") - private String formField; - - @Schema(description = "当子流程发起人为空时类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "当子流程发起人为空时类型不能为空") - @InEnum(BpmChildProcessStartUserEmptyTypeEnum.class) - private Integer emptyType; - - } - - @Schema(description = "超时设置") - @Data - @Valid - public static class TimeoutSetting { - - @Schema(description = "是否开启超时设置", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - @NotNull(message = "是否开启超时设置不能为空") - private Boolean enable; - - @Schema(description = "时间类型", example = "1") - @InEnum(BpmDelayTimerTypeEnum.class) - private Integer type; - - @Schema(description = "时间表达式", example = "PT1H,2025-01-01T00:00:00") - private String timeExpression; - - } - - @Schema(description = "多实例设置") - @Data - @Valid - public static class MultiInstanceSetting { - - @Schema(description = "是否开启多实例", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - @NotNull(message = "是否开启多实例不能为空") - private Boolean enable; - - @Schema(description = "是否串行", requiredMode = Schema.RequiredMode.REQUIRED, example = "false") - @NotNull(message = "是否串行不能为空") - private Boolean sequential; - - @Schema(description = "完成比例", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") - @NotNull(message = "完成比例不能为空") - private Integer approveRatio; - - @Schema(description = "多实例来源类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "多实例来源类型不能为空") - @InEnum(BpmChildProcessMultiInstanceSourceTypeEnum.class) - private Integer sourceType; - - @Schema(description = "多实例来源", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "多实例来源不能为空") - private String source; - - } - - } -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java deleted file mode 100644 index 43981ba..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelUpdateReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.Valid; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -// TODO @jason:需要考虑,如果某个节点的配置不正确,需要有提示;具体怎么实现,可以讨论下; -@Schema(description = "管理后台 - 仿钉钉流程设计模型的新增/修改 Request VO") -@Data -public class BpmSimpleModelUpdateReqVO { - - @Schema(description = "流程模型编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotEmpty(message = "流程模型编号不能为空") - private String id; // 对应 Flowable act_re_model 表 ID_ 字段 - - @Schema(description = "仿钉钉流程设计模型对象", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "仿钉钉流程设计模型对象不能为空") - @Valid - private BpmSimpleModelNodeVO simpleModel; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java deleted file mode 100644 index 8c61e28..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionPageReqVO.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.process; - -import com.zt.plat.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -@Schema(description = "管理后台 - 流程定义分页 Request VO") -@Data -public class BpmProcessDefinitionPageReqVO extends PageParam { - - @Schema(description = "标识-精准匹配", example = "process1641042089407") - private String key; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java deleted file mode 100644 index f0d2892..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.definition.vo.process; - -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - 流程定义 Response VO") -@Data -public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { - - @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String id; - - @Schema(description = "版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer version; - - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - private String name; - - @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao") - private String key; - - @Schema(description = "流程分类", example = "1") - private String category; - @Schema(description = "流程分类名字", example = "请假") - private String categoryName; - - @Schema(description = "流程模型的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") - private Integer modelType; // 参见 BpmModelTypeEnum 枚举类 - - @Schema(description = "流程模型的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") - private String modelId; - - @Schema(description = "表单的配置-JSON 字符串。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) - private String formConf; - @Schema(description = "表单项的数组-JSON 字符串的数组。在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时,必须非空", requiredMode = Schema.RequiredMode.REQUIRED) - private List formFields; - @Schema(description = "表单名字", example = "请假表单") - private String formName; - - @Schema(description = "中断状态-参见 SuspensionState 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer suspensionState; // 参见 SuspensionState 枚举 - - @Schema(description = "部署时间") - private LocalDateTime deploymentTime; // 需要从对应的 Deployment 读取,非必须返回 - - @Schema(description = "BPMN XML") - private String bpmnXml; // 需要从对应的 BpmnModel 读取,非必须返回 - - @Schema(description = "SIMPLE 设计器模型数据 json 格式") - private String simpleModel; // 非必须返回 - - @Schema(description = "流程定义排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long sort; - - @Schema(description = "BPMN UserTask 用户任务") - @Data - public static class UserTask { - - @Schema(description = "任务标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "sudo") - private String id; - - @Schema(description = "任务名", requiredMode = Schema.RequiredMode.REQUIRED, example = "王五") - private String name; - - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http deleted file mode 100644 index 96bbf96..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.http +++ /dev/null @@ -1,12 +0,0 @@ -### 请求 /bpm/oa/leave/create 接口 => 成功 -POST {{baseUrl}}/bpm/oa/leave/create -Content-Type: application/json -tenant-id: 1 -Authorization: Bearer {{token}} - -{ - "startTime": "2022-03-01", - "endTime": "2022-03-05", - "type": 1, - "reason": "我要请假啦啦啦!" -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java deleted file mode 100644 index 51d5094..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/BpmOALeaveController.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.oa; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO; -import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; -import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveRespVO; -import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; -import com.zt.plat.module.bpm.service.oa.BpmOALeaveService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -/** - * OA 请假申请 Controller,用于演示自己存储数据,接入工作流的例子 - * - * @author jason - * @author ZT - */ -@Tag(name = "管理后台 - OA 请假申请") -@RestController -@RequestMapping("/bpm/oa/leave") -@Validated -public class BpmOALeaveController { - - @Resource - private BpmOALeaveService leaveService; - - @PostMapping("/create") - @PreAuthorize("@ss.hasPermission('bpm:oa-leave:create')") - @Operation(summary = "创建请求申请") - public CommonResult createLeave(@Valid @RequestBody BpmOALeaveCreateReqVO createReqVO) { - return success(leaveService.createLeave(getLoginUserId(), createReqVO)); - } - - @GetMapping("/get") - @PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')") - @Operation(summary = "获得请假申请") - @Parameter(name = "id", description = "编号", required = true, example = "1024") - public CommonResult getLeave(@RequestParam("id") Long id) { - BpmOALeaveDO leave = leaveService.getLeave(id); - return success(BeanUtils.toBean(leave, BpmOALeaveRespVO.class)); - } - - @GetMapping("/page") - @PreAuthorize("@ss.hasPermission('bpm:oa-leave:query')") - @Operation(summary = "获得请假申请分页") - public CommonResult> getLeavePage(@Valid BpmOALeavePageReqVO pageVO) { - PageResult pageResult = leaveService.getLeavePage(getLoginUserId(), pageVO); - return success(BeanUtils.toBean(pageResult, BpmOALeaveRespVO.class)); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java deleted file mode 100644 index 5f65c5b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * OA 示例,用于演示外部业务接入 BPM 工作流的示例 - * 一般的接入方式,只需要调用 接口,后续 Admin 用户在管理后台的【待办事务】进行审批 - */ -package com.zt.plat.module.bpm.controller.admin.oa; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java deleted file mode 100644 index 00779aa..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.oa.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.NotNull; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 请假申请创建 Request VO") -@Data -public class BpmOALeaveCreateReqVO { - - @Schema(description = "请假的开始时间", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "开始时间不能为空") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime startTime; - - @Schema(description = "请假的结束时间", requiredMode = Schema.RequiredMode.REQUIRED) - @NotNull(message = "结束时间不能为空") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime endTime; - - @Schema(description = "请假类型-参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer type; - - @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读ZT源码") - private String reason; - - @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") - private Map> startUserSelectAssignees; - - @AssertTrue(message = "结束时间,需要在开始时间之后") - public boolean isEndTimeValid() { - return !getEndTime().isBefore(getStartTime()); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java deleted file mode 100644 index 31c9f1b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.oa.vo; - -import com.zt.plat.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 请假申请分页 Request VO") -@Data -public class BpmOALeavePageReqVO extends PageParam { - - @Schema(description = "状态", example = "1") - private Integer status; // 参见 BpmProcessInstanceResultEnum 枚举 - - @Schema(description = "请假类型,参见 bpm_oa_type", example = "1") - private Integer type; - - @Schema(description = "原因,模糊匹配", example = "阅读ZT源码") - private String reason; - - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - @Schema(description = "申请时间") - private LocalDateTime[] createTime; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java deleted file mode 100644 index ae43fcd..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.oa.vo; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 请假申请 Response VO") -@Data -public class BpmOALeaveRespVO { - - @Schema(description = "请假表单主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "请假类型,参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer type; - - @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读ZT源码") - private String reason; - - @Schema(description = "申请时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - - @Schema(description = "请假的开始时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime startTime; - - @Schema(description = "请假的结束时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime endTime; - - @Schema(description = "流程编号") - private String processInstanceId; - - @Schema(description = "审批结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http deleted file mode 100644 index c690827..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.http +++ /dev/null @@ -1,16 +0,0 @@ -### 请求 /bpm/process-instance/get-bpmn 接口 => 成功 -GET {{baseUrl}}/bpm/process-instance/get-bpmn-model-view?id=1d5fb5a6-85f8-11ef-b717-7e93075f94e3 -Content-Type: application/json -tenant-id: 1 -Authorization: Bearer {{token}} - -### 请求 /bpm/process-instance/get-bpmn 接口 => 失败 -#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=1d5fb5a6-85f8-11ef-b717-7e93075f94e3 -#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=3ee5c5ba-904a-11ef-a76e-b2ed5d6ef911 -#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=f630dfa2-8f92-11ef-947c-ba5e239a6eb4 -#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=9de8bdbf-9133-11ef-ae97-eaf49df1f932 -#GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processInstanceId=dd2188eb-9394-11ef-a039-7a9ac3d9eb6b -GET {{baseUrl}}/bpm/process-instance/get-approval-detail?processDefinitionId=test-auto:1:c70a799a-9394-11ef-a039-7a9ac3d9eb6b -Content-Type: application/json -tenant-id: 1 -Authorization: Bearer {{token}} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java deleted file mode 100644 index d55f27a..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ /dev/null @@ -1,237 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task; - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import com.zt.plat.framework.business.core.util.DeptUtil; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; -import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceConvert; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; -import com.zt.plat.module.bpm.service.definition.BpmCategoryService; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.bpm.service.task.BpmTaskService; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.collections4.list.SetUniqueList; -import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.repository.ProcessDefinition; -import org.flowable.task.api.Task; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; -import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - 流程实例") // 流程实例,通过流程定义创建的一次“申请” -@RestController -@RequestMapping("/bpm/process-instance") -@Validated -public class BpmProcessInstanceController { - - @Resource - private BpmProcessInstanceService processInstanceService; - @Resource - private BpmTaskService taskService; - @Resource - private BpmProcessDefinitionService processDefinitionService; - @Resource - private BpmCategoryService categoryService; - @Resource - private BpmProcessInstanceCopyService processInstanceCopyService; - - @Resource - private AdminUserApi adminUserApi; - @Resource - private DeptApi deptApi; - - @GetMapping("/my-page") - @Operation(summary = "获得我的实例分页列表", description = "在【我的流程】菜单中,进行调用") - @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - public CommonResult> getProcessInstanceMyPage( - @Valid BpmProcessInstancePageReqVO pageReqVO) { - PageResult pageResult = processInstanceService.getProcessInstancePage( - getLoginUserId(), pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(PageResult.empty(pageResult.getTotal())); - } - - // 拼接返回 - Map> taskMap = taskService.getTaskMapByProcessInstanceIds( - convertList(pageResult.getList(), HistoricProcessInstance::getId)); - Map processDefinitionMap = processDefinitionService.getProcessDefinitionMap( - convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); - Map categoryMap = categoryService.getCategoryMap( - convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); - Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( - convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); - Set userIds = convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId())); - userIds.addAll(convertSetByFlatMap(taskMap.values(), - tasks -> tasks.stream().map(Task::getAssignee).filter(StrUtil::isNotBlank).map(Long::parseLong))); - Map userMap = adminUserApi.getUserMap(userIds); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); - return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, - processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap)); - } - - @GetMapping("/manager-page") - @Operation(summary = "获得管理流程实例的分页列表", description = "在【流程实例】菜单中,进行调用") - @PreAuthorize("@ss.hasPermission('bpm:process-instance:manager-query')") - public CommonResult> getProcessInstanceManagerPage( - @Valid BpmProcessInstancePageReqVO pageReqVO) { - PageResult pageResult = processInstanceService.getProcessInstancePage( - null, pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(PageResult.empty(pageResult.getTotal())); - } - - // 拼接返回 - Map> taskMap = taskService.getTaskMapByProcessInstanceIds( - convertList(pageResult.getList(), HistoricProcessInstance::getId)); - Map processDefinitionMap = processDefinitionService.getProcessDefinitionMap( - convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); - Map categoryMap = categoryService.getCategoryMap( - convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); - // 发起人信息 - Map userMap = adminUserApi.getUserMap( - convertSet(pageResult.getList(), processInstance -> NumberUtils.parseLong(processInstance.getStartUserId()))); - Map deptMap = deptApi.getDeptMap( - convertSet(userMap.values(), DeptUtil::getDeptId)); - Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( - convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); - return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, - processDefinitionMap, categoryMap, taskMap, userMap, deptMap, processDefinitionInfoMap)); - } - - @PostMapping("/create") - @Operation(summary = "新建流程实例") - @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - public CommonResult createProcessInstance(@Valid @RequestBody BpmProcessInstanceCreateReqVO createReqVO) { - return success(processInstanceService.createProcessInstance(getLoginUserId(), createReqVO)); - } - - @GetMapping("/get") - @Operation(summary = "获得指定流程实例", description = "在【流程详细】界面中,进行调用") - @Parameter(name = "id", description = "流程实例的编号", required = true) - @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - public CommonResult getProcessInstance(@RequestParam("id") String id) { - HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(id); - if (processInstance == null) { - return success(null); - } - - // 拼接返回 - ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( - processInstance.getProcessDefinitionId()); - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo( - processInstance.getProcessDefinitionId()); - AdminUserRespDTO startUser = adminUserApi.getUser(NumberUtils.parseLong(processInstance.getStartUserId())).getCheckedData(); - DeptRespDTO dept = null; - if (startUser != null) { - Long deptId = DeptUtil.getDeptId(startUser); - if (deptId > 0) { - dept = deptApi.getDept(deptId).getCheckedData(); - } - } - return success(BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance, - processDefinition, processDefinitionInfo, startUser, dept)); - } - - @DeleteMapping("/cancel-by-start-user") - @Operation(summary = "用户取消流程实例", description = "取消发起的流程") - @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel')") - public CommonResult cancelProcessInstanceByStartUser( - @Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) { - processInstanceService.cancelProcessInstanceByStartUser(getLoginUserId(), cancelReqVO); - return success(true); - } - - @DeleteMapping("/cancel-by-admin") - @Operation(summary = "管理员取消流程实例", description = "管理员撤回流程") - @PreAuthorize("@ss.hasPermission('bpm:process-instance:cancel-by-admin')") - public CommonResult cancelProcessInstanceByManager( - @Valid @RequestBody BpmProcessInstanceCancelReqVO cancelReqVO) { - processInstanceService.cancelProcessInstanceByAdmin(getLoginUserId(), cancelReqVO); - return success(true); - } - - @GetMapping("/get-approval-detail") - @Operation(summary = "获得审批详情") - @Parameter(name = "id", description = "流程实例的编号", required = true) - @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - @SuppressWarnings("unchecked") - public CommonResult getApprovalDetail(@Valid BpmApprovalDetailReqVO reqVO) { - if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { - reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); - } - return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); - } - - @GetMapping("/copy-list-by-process-instance-id") - @Operation(summary = "根据流程实例编号获取抄送列表") - @Parameter(name = "id", description = "流程实例的编号", required = true) - @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - public CommonResult> getCopyListByProcessInstanceId(@RequestParam("processInstanceId") String processInstanceId) { - List copyDOList = processInstanceCopyService.getByProcessInstanceId(processInstanceId); - if (CollectionUtils.isEmpty(copyDOList)) { - return success(new ArrayList<>(0)); - } - List copyVOList = new ArrayList<>(copyDOList.size()); - SetUniqueList userIdList = SetUniqueList.setUniqueList(new ArrayList<>()); - for (BpmProcessInstanceCopyDO copyDO : copyDOList) { - BpmProcessInstanceCopyVO copyVO = new BpmProcessInstanceCopyVO(); - BeanUtil.copyProperties(copyDO, copyVO); - copyVOList.add(copyVO); - userIdList.add(copyDO.getStartUserId()); - userIdList.add(copyDO.getUserId()); - } - Map userMap = adminUserApi.getUserMap(userIdList); - for (BpmProcessInstanceCopyVO copyVO : copyVOList) { - copyVO.setStartUserName(userMap.get(copyVO.getStartUserId()).getNickname()); - copyVO.setUserName(userMap.get(copyVO.getUserId()).getNickname()); - } - return success(copyVOList); - } - - @GetMapping("/get-next-approval-nodes") - @Operation(summary = "获取下一个执行的流程节点") - @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") - @SuppressWarnings("unchecked") - public CommonResult> getNextApprovalNodes(@Valid BpmApprovalDetailReqVO reqVO) { - if (StrUtil.isNotEmpty(reqVO.getProcessVariablesStr())) { - reqVO.setProcessVariables(JsonUtils.parseObject(reqVO.getProcessVariablesStr(), Map.class)); - } - return success(processInstanceService.getNextApprovalNodes(getLoginUserId(), reqVO)); - } - - @GetMapping("/get-bpmn-model-view") - @Operation(summary = "获取流程实例的 BPMN 模型视图", description = "在【流程详细】界面中,进行调用") - @Parameter(name = "id", description = "流程实例的编号", required = true) - public CommonResult getProcessInstanceBpmnModelView(@RequestParam(value = "id") String id) { - return success(processInstanceService.getProcessInstanceBpmnModelView(id)); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java deleted file mode 100644 index bd644ca..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceCopyController.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task; - -import cn.hutool.core.collection.CollUtil; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.collection.MapUtils; -import com.zt.plat.framework.common.util.date.DateUtils; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.cc.BpmProcessInstanceCopyRespVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.flowable.engine.history.HistoricProcessInstance; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import java.util.Map; -import java.util.stream.Stream; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; -import static com.zt.plat.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; - -@Tag(name = "管理后台 - 流程实例抄送") -@RestController -@RequestMapping("/bpm/process-instance/copy") -@Validated -public class BpmProcessInstanceCopyController { - - @Resource - private BpmProcessInstanceCopyService processInstanceCopyService; - @Resource - private BpmProcessInstanceService processInstanceService; - @Resource - private BpmProcessDefinitionService processDefinitionService; - - @Resource - private AdminUserApi adminUserApi; - - @GetMapping("/page") - @Operation(summary = "获得抄送流程分页列表") - @PreAuthorize("@ss.hasPermission('bpm:process-instance-cc:query')") - public CommonResult> getProcessInstanceCopyPage( - @Valid BpmProcessInstanceCopyPageReqVO pageReqVO) { - PageResult pageResult = processInstanceCopyService.getProcessInstanceCopyPage( - getLoginUserId(), pageReqVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(new PageResult<>(pageResult.getTotal())); - } - - // 拼接返回 - Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap( - convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessInstanceId)); - Map userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(), - copy -> Stream.of(copy.getStartUserId(), Long.parseLong(copy.getCreator())))); - Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( - convertSet(pageResult.getList(), BpmProcessInstanceCopyDO::getProcessDefinitionId)); - return success(convertPage(pageResult, copy -> { - BpmProcessInstanceCopyRespVO copyVO = BeanUtils.toBean(copy, BpmProcessInstanceCopyRespVO.class); - MapUtils.findAndThen(userMap, Long.valueOf(copy.getCreator()), - user -> copyVO.setStartUser(BeanUtils.toBean(user, UserSimpleBaseVO.class))); - MapUtils.findAndThen(userMap, copy.getStartUserId(), - user -> copyVO.setCreateUser(BeanUtils.toBean(user, UserSimpleBaseVO.class))); - MapUtils.findAndThen(processInstanceMap, copyVO.getProcessInstanceId(), - processInstance -> { - copyVO.setSummary(FlowableUtils.getSummary( - processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()), - processInstance.getProcessVariables())); - copyVO.setProcessInstanceStartTime(DateUtils.of(processInstance.getStartTime())); - }); - return copyVO; - })); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java deleted file mode 100644 index 54db1af..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmTaskController.java +++ /dev/null @@ -1,252 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import com.zt.plat.framework.business.core.util.DeptUtil; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.*; -import com.zt.plat.module.bpm.convert.task.BpmTaskConvert; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.service.definition.BpmFormService; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.bpm.service.task.BpmTaskService; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import org.flowable.bpmn.model.UserTask; -import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.task.api.Task; -import org.flowable.task.api.history.HistoricTaskInstance; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.stream.Stream; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; -import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.getLoginUserId; - -/** - * @author chenbowen - */ -@Tag(name = "管理后台 - 流程任务实例") -@RestController -@RequestMapping("/bpm/task") -@Validated -public class BpmTaskController { - - @Resource - private BpmTaskService taskService; - @Resource - private BpmProcessInstanceService processInstanceService; - @Resource - private BpmFormService formService; - @Resource - private BpmProcessDefinitionService processDefinitionService; - - @Resource - private AdminUserApi adminUserApi; - @Resource - private DeptApi deptApi; - - @GetMapping("todo-page") - @Operation(summary = "获取 Todo 待办任务分页") - @PreAuthorize("@ss.hasPermission('bpm:task:query')") - public CommonResult> getTaskTodoPage(@Valid BpmTaskPageReqVO pageVO) { - PageResult pageResult = taskService.getTaskTodoPage(getLoginUserId(), pageVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(PageResult.empty()); - } - - // 拼接数据 - Map processInstanceMap = processInstanceService.getProcessInstanceMap( - convertSet(pageResult.getList(), Task::getProcessInstanceId)); - Map userMap = adminUserApi.getUserMap( - convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); - Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( - convertSet(pageResult.getList(), Task::getProcessDefinitionId)); - return success(BpmTaskConvert.INSTANCE.buildTodoTaskPage(pageResult, processInstanceMap, userMap, processDefinitionInfoMap)); - } - - @GetMapping("done-page") - @Operation(summary = "获取 Done 已办任务分页") - @PreAuthorize("@ss.hasPermission('bpm:task:query')") - public CommonResult> getTaskDonePage(@Valid BpmTaskPageReqVO pageVO) { - PageResult pageResult = taskService.getTaskDonePage(getLoginUserId(), pageVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(PageResult.empty()); - } - - // 拼接数据 - Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap( - convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); - Map userMap = adminUserApi.getUserMap( - convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId()))); - Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( - convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); - return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null, processDefinitionInfoMap)); - } - - @GetMapping("manager-page") - @Operation(summary = "获取全部任务的分页", description = "用于【流程任务】菜单") - @PreAuthorize("@ss.hasPermission('bpm:task:mananger-query')") - public CommonResult> getTaskManagerPage(@Valid BpmTaskPageReqVO pageVO) { - PageResult pageResult = taskService.getTaskPage(getLoginUserId(), pageVO); - if (CollUtil.isEmpty(pageResult.getList())) { - return success(PageResult.empty()); - } - - // 拼接数据 - Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap( - convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); - // 获得 User 和 Dept Map - Set userIds = convertSet(processInstanceMap.values(), instance -> Long.valueOf(instance.getStartUserId())); - userIds.addAll(convertSet(pageResult.getList(), task -> NumberUtils.parseLong(task.getAssignee()))); - Map userMap = adminUserApi.getUserMap(userIds); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); - Map processDefinitionInfoMap = processDefinitionService.getProcessDefinitionInfoMap( - convertSet(pageResult.getList(), HistoricTaskInstance::getProcessDefinitionId)); - return success(BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap, processDefinitionInfoMap)); - } - - @GetMapping("/list-by-process-instance-id") - @Operation(summary = "获得指定流程实例的任务列表", description = "包括完成的、未完成的") - @Parameter(name = "processInstanceId", description = "流程实例的编号", required = true) - @PreAuthorize("@ss.hasPermission('bpm:task:query')") - public CommonResult> getTaskListByProcessInstanceId( - @RequestParam("processInstanceId") String processInstanceId) { - List taskList = taskService.getTaskListByProcessInstanceId(processInstanceId, true); - if (CollUtil.isEmpty(taskList)) { - return success(Collections.emptyList()); - } - - // 拼接数据 - Set userIds = convertSetByFlatMap(taskList, task -> - Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner()))); - Map userMap = adminUserApi.getUserMap(userIds); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); - // 获得 Form Map - Map formMap = formService.getFormMap( - convertSet(taskList, task -> { - String formKey = task.getFormKey(); - if (StrUtil.isBlank(formKey)) { - return 0L; - } - try { - return Long.parseLong(formKey); - } catch (NumberFormatException e) { - // 如果 formKey 不是数字(比如是URL),返回0L - return 0L; - } - })); - return success(BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, - formMap, userMap, deptMap)); - } - - @PutMapping("/approve") - @Operation(summary = "通过任务") - @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult approveTask(@Valid @RequestBody BpmTaskApproveReqVO reqVO) { - taskService.approveTask(getLoginUserId(), reqVO); - return success(true); - } - - @PutMapping("/reject") - @Operation(summary = "不通过任务") - @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult rejectTask(@Valid @RequestBody BpmTaskRejectReqVO reqVO) { - taskService.rejectTask(getLoginUserId(), reqVO); - return success(true); - } - - @GetMapping("/list-by-return") - @Operation(summary = "获取所有可退回的节点", description = "用于【流程详情】的【退回】按钮") - @Parameter(name = "taskId", description = "当前任务ID", required = true) - @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult> getTaskListByReturn(@RequestParam("id") String id) { - List userTaskList = taskService.getUserTaskListByReturn(id); - return success(convertList(userTaskList, userTask -> // 只返回 id 和 name - new BpmTaskRespVO().setName(userTask.getName()).setTaskDefinitionKey(userTask.getId()))); - } - - @PutMapping("/return") - @Operation(summary = "退回任务", description = "用于【流程详情】的【退回】按钮") - @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult returnTask(@Valid @RequestBody BpmTaskReturnReqVO reqVO) { - taskService.returnTask(getLoginUserId(), reqVO); - return success(true); - } - - @PutMapping("/delegate") - @Operation(summary = "委派任务", description = "用于【流程详情】的【委派】按钮") - @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult delegateTask(@Valid @RequestBody BpmTaskDelegateReqVO reqVO) { - taskService.delegateTask(getLoginUserId(), reqVO); - return success(true); - } - - @PutMapping("/transfer") - @Operation(summary = "转派任务", description = "用于【流程详情】的【转派】按钮") - @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult transferTask(@Valid @RequestBody BpmTaskTransferReqVO reqVO) { - taskService.transferTask(getLoginUserId(), reqVO); - return success(true); - } - - @PutMapping("/create-sign") - @Operation(summary = "加签", description = "before 前加签,after 后加签") - @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult createSignTask(@Valid @RequestBody BpmTaskSignCreateReqVO reqVO) { - taskService.createSignTask(getLoginUserId(), reqVO); - return success(true); - } - - @DeleteMapping("/delete-sign") - @Operation(summary = "减签") - @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult deleteSignTask(@Valid @RequestBody BpmTaskSignDeleteReqVO reqVO) { - taskService.deleteSignTask(getLoginUserId(), reqVO); - return success(true); - } - - @PutMapping("/copy") - @Operation(summary = "抄送任务") - @PreAuthorize("@ss.hasPermission('bpm:task:update')") - public CommonResult copyTask(@Valid @RequestBody BpmTaskCopyReqVO reqVO) { - taskService.copyTask(getLoginUserId(), reqVO); - return success(true); - } - - @GetMapping("/list-by-parent-task-id") - @Operation(summary = "获得指定父级任务的子任务列表") // 目前用于,减签的时候,获得子任务列表 - @Parameter(name = "parentTaskId", description = "父级任务编号", required = true) - @PreAuthorize("@ss.hasPermission('bpm:task:query')") - public CommonResult> getTaskListByParentTaskId(@RequestParam("parentTaskId") String parentTaskId) { - List taskList = taskService.getTaskListByParentTaskId(parentTaskId); - if (CollUtil.isEmpty(taskList)) { - return success(Collections.emptyList()); - } - // 拼接数据 - Map userMap = adminUserApi.getUserMap(convertSetByFlatMap(taskList, - user -> Stream.of(NumberUtils.parseLong(user.getAssignee()), NumberUtils.parseLong(user.getOwner())))); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); - return success(BpmTaskConvert.INSTANCE.buildTaskListByParentTaskId(taskList, userMap, deptMap)); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java deleted file mode 100644 index 444aeae..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/activity/BpmActivityRespVO.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.activity; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 流程活动的 Response VO") -@Data -public class BpmActivityRespVO { - - @Schema(description = "流程活动的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String key; - @Schema(description = "流程活动的类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartEvent") - private String type; - - @Schema(description = "流程活动的开始时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime startTime; - @Schema(description = "流程活动的结束时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime endTime; - - @Schema(description = "关联的流程任务的编号", example = "2048") - private String taskId; // 关联的流程任务,只有 UserTask 等类型才有 - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java deleted file mode 100644 index b968f9a..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/cc/BpmProcessInstanceCopyRespVO.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.cc; - -import com.zt.plat.framework.common.core.KeyValue; -import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; - -@Schema(description = "管理后台 - 流程实例抄送的分页 Item Response VO") -@Data -public class BpmProcessInstanceCopyRespVO { - - @Schema(description = "抄送主键", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private Long id; - - @Schema(description = "发起人", requiredMode = Schema.RequiredMode.REQUIRED) - private UserSimpleBaseVO startUser; - - @Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "A233") - private String processInstanceId; - @Schema(description = "流程实例的名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "测试") - private String processInstanceName; - @Schema(description = "流程实例的发起时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime processInstanceStartTime; - - @Schema(description = "流程活动的编号", requiredMode = Schema.RequiredMode.REQUIRED) - private String activityId; - @Schema(description = "流程活动的名字", requiredMode = Schema.RequiredMode.REQUIRED) - private String activityName; - - @Schema(description = "流程活动的编号") - private String taskId; - - @Schema(description = "抄送人意见") - private String reason; - - @Schema(description = "创建人", requiredMode = Schema.RequiredMode.REQUIRED) - private UserSimpleBaseVO createUser; - - @Schema(description = "抄送时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - - @Schema(description = "流程摘要", example = "[]") - private List> summary; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java deleted file mode 100644 index 44c775b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.instance; - -import cn.hutool.core.util.StrUtil; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.AssertTrue; -import lombok.Data; - -import java.util.Map; - -@Schema(description = "管理后台 - 审批详情 Request VO") -@Data -public class BpmApprovalDetailReqVO { - - @Schema(description = "流程定义的编号", example = "1024") - private String processDefinitionId; // 使用场景:发起流程时,传流程定义 ID - - @Schema(description = "流程变量") - private Map processVariables; // 使用场景:同 processDefinitionId,用于流程预测 - - @Schema(description = "流程变量") - private String processVariablesStr; // 解决 GET 无法传递对象的问题,最终转换成 processVariables 变量 - - @Schema(description = "流程实例的编号", example = "1024") - private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID - - // TODO @ZT:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 - @Schema(description = "流程活动编号", example = "StartUserNode") - private String activityId; // 用于获取表单权限。1)发起流程时,传“发起人节点” activityId 可获取发起人的表单权限;2)从抄送列表界面进来时,传抄送的 activityId 可获取抄送人的表单权限; - - @Schema(description = "流程任务编号", example = "95f2f08b-621b-11ef-bf39-00ff4722db8b") - private String taskId; // 用于获取表单权限。1)从待审批/已审批界面进来时,传递 taskId 任务编号,可获取任务节点的变得权限 - - @AssertTrue(message = "流程定义的编号和流程实例的编号不能同时为空") - @JsonIgnore - public boolean isValidProcessParam() { - return StrUtil.isNotEmpty(processDefinitionId) || StrUtil.isNotEmpty(processInstanceId); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java deleted file mode 100644 index 4e444f4..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java +++ /dev/null @@ -1,112 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.instance; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - - -@Schema(description = "管理后台 - 审批详情 Response VO") -@Data -public class BpmApprovalDetailRespVO { - - @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 - - @Schema(description = "活动节点列表", requiredMode = Schema.RequiredMode.REQUIRED) - private List activityNodes; - - @Schema(description = "表单字段权限") - private Map formFieldsPermission; - - @Schema(description = "待办任务") - private BpmTaskRespVO todoTask; - - /** - * 所属流程定义信息 - */ - private BpmProcessDefinitionRespVO processDefinition; - - /** - * 所属流程实例信息 - */ - private BpmProcessInstanceRespVO processInstance; - - @Schema(description = "活动节点信息") - @Data - public static class ActivityNode { - - @Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "StartUserNode") - private String id; - - @Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "发起人") - private String name; - - @Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer nodeType; // 参见 BpmSimpleModelNodeType 枚举 - - @Schema(description = "节点状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") - private Integer status; // 参见 BpmTaskStatusEnum 枚举 - - @Schema(description = "节点的开始时间") - private LocalDateTime startTime; - @Schema(description = "节点的结束时间") - private LocalDateTime endTime; - - @Schema(description = "审批节点的任务信息") - private List tasks; - - @Schema(description = "候选人策略", example = "35") - private Integer candidateStrategy; // 参见 BpmTaskCandidateStrategyEnum 枚举。主要用于发起时,审批节点、抄送节点自选 - - @Schema(description = "候选人用户 ID 列表", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818") - @JsonIgnore // 不返回,只是方便后续读取,赋值给 candidateUsers - private List candidateUserIds; - - @Schema(description = "候选人用户列表") - private List candidateUsers; // 只包含未生成 ApprovalTaskInfo 的用户列表 - - @Schema(description = "流程编号", example = "8761d8e0-0922-11f0-bd37-00ff1db677bf") - private String processInstanceId; // 当且仅当,该节点是子流程节点时,才会有值(CallActivity 的 processInstanceId 字段) - - } - - @Schema(description = "活动节点的任务信息") - @Data - public static class ActivityNodeTask { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private String id; - - @Schema(description = "任务所属人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "1818") - @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser - private Long owner; - - @Schema(description = "任务所属人", example = "1024") - private UserSimpleBaseVO ownerUser; - - @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") - @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser - private Long assignee; - - @Schema(description = "任务分配人", example = "2048") - private UserSimpleBaseVO assigneeUser; - - @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; // 参见 BpmTaskStatusEnum 枚举 - - @Schema(description = "审批意见", example = "同意") - private String reason; - - @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") - private String signPicUrl; - - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java deleted file mode 100644 index bf520a0..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceBpmnModelViewRespVO.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.instance; - -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.util.List; -import java.util.Set; - -@Schema(description = "管理后台 - 流程示例的 BPMN 视图 Response VO") -@Data -public class BpmProcessInstanceBpmnModelViewRespVO { - - // ========== 基本信息 ========== - - @Schema(description = "流程实例信息", requiredMode = Schema.RequiredMode.REQUIRED) - private BpmProcessInstanceRespVO processInstance; - - @Schema(description = "任务列表", requiredMode = Schema.RequiredMode.REQUIRED) - private List tasks; - - @Schema(description = "BPMN XML", requiredMode = Schema.RequiredMode.REQUIRED) - private String bpmnXml; - - @Schema(description = "SIMPLE 模型") - private BpmSimpleModelNodeVO simpleModel; - - // ========== 进度信息 ========== - - @Schema(description = "进行中的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) - private Set unfinishedTaskActivityIds; // 只包括 UserTask - - @Schema(description = "已经完成的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) - private Set finishedTaskActivityIds; // 包括 UserTask、Gateway 等,不包括 SequenceFlow - - @Schema(description = "已经完成的连线节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) - private Set finishedSequenceFlowActivityIds; // 只包括 SequenceFlow - - @Schema(description = "已经拒绝的活动节点编号集合", requiredMode = Schema.RequiredMode.REQUIRED) - private Set rejectedTaskActivityIds; // 只包括 UserTask - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java deleted file mode 100644 index b9d19e2..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCancelReqVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.instance; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -@Schema(description = "管理后台 - 流程实例的取消 Request VO") -@Data -public class BpmProcessInstanceCancelReqVO { - - @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "流程实例的编号不能为空") - private String id; - - @Schema(description = "取消原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "不请假了!") - @NotEmpty(message = "取消原因不能为空") - private String reason; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java deleted file mode 100644 index 2aff881..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.instance; - -import com.zt.plat.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 流程实例抄送的分页 Request VO") -@Data -public class BpmProcessInstanceCopyPageReqVO extends PageParam { - - @Schema(description = "流程名称", example = "ZT") - private String processInstanceName; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java deleted file mode 100644 index 0012c19..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.instance; - -import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * 流程抄送 VO - * - * @author kr - * @since 2025-12-31 - */ -@Data -@NoArgsConstructor -public class BpmProcessInstanceCopyVO extends BaseDO { - - /** - * 编号 - */ - private Long id; - - /** - * 发起人 Id - */ - @Schema(description ="发起人 Id") - private Long startUserId; - - /** - * 发起人 姓名 - */ - @Schema(description ="发起人 姓名") - private String startUserName; - /** - * 流程名 - */ - @Schema(description ="流程名") - private String processInstanceName; - /** - * 流程实例的编号 - */ - @Schema(description ="流程实例的编号") - private String processInstanceId; - /** - * 流程实例的流程定义编号 - */ - @Schema(description ="流程实例的流程定义编号") - private String processDefinitionId; - /** - * 流程分类 - */ - @Schema(description ="流程分类") - private String category; - /** - * 流程活动的编号 - */ - @Schema(description ="流程活动的编号") - private String activityId; - /** - * 流程活动的名字 - */ - @Schema(description ="流程活动的名字") - private String activityName; - /** - * 流程活动的编号 - */ - @Schema(description ="流程活动的编号") - private String taskId; - - /** - * 用户编号(被抄送的用户编号) - */ - @Schema(description ="用户编号(被抄送的用户编号)") - private Long userId; - - /** - * 用户姓名(被抄送的用户姓名) - */ - @Schema(description ="用户姓名(被抄送的用户姓名)") - private String userName; - - /** - * 抄送意见 - */ - @Schema(description ="抄送意见") - private String reason; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java deleted file mode 100644 index bb46b1f..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCreateReqVO.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.instance; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.List; -import java.util.Map; - -@Schema(description = "管理后台 - 流程实例的创建 Request VO") -@Data -public class BpmProcessInstanceCreateReqVO { - - @Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "流程定义编号不能为空") - private String processDefinitionId; - - @Schema(description = "变量实例(动态表单)") - private Map variables; - - @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") - private Map> startUserSelectAssignees; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java deleted file mode 100644 index 130d75b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.instance; - -import com.zt.plat.framework.common.pojo.PageParam; -import com.zt.plat.framework.common.validation.InEnum; -import com.zt.plat.module.bpm.enums.task.BpmProcessInstanceStatusEnum; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; - -@Schema(description = "管理后台 - 流程实例分页 Request VO") -@Data -public class BpmProcessInstancePageReqVO extends PageParam { - - @Schema(description = "流程名称", example = "ZT") - private String name; - - @Schema(description = "流程定义的标识", example = "2048") - private String processDefinitionKey; // 精准匹配 - - @Schema(description = "流程实例的状态", example = "1") - @InEnum(BpmProcessInstanceStatusEnum.class) - private Integer status; - - @Schema(description = "流程分类", example = "1") - private String category; - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - - @Schema(description = "结束时间") - @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] endTime; - - @Schema(description = "发起用户编号", example = "1024") - private Long startUserId; // 注意,只有在【流程实例】菜单,才使用该参数 - - @Schema(description = "动态表单字段查询 JSON Str", example = "{}") - private String formFieldsParams; // SpringMVC 在 get 请求下,无法方便的定义 Map 类型的参数,所以通过 String 接收后,逻辑里面转换 - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java deleted file mode 100644 index ae1abaf..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.instance; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.zt.plat.framework.common.core.KeyValue; -import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -@Schema(description = "管理后台 - 流程实例的 Response VO") -@Data -public class BpmProcessInstanceRespVO { - - @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String id; - - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - private String name; - - @Schema(description = "流程摘要") - private List> summary; // 只有流程表单,才有摘要! - - @Schema(description = "流程分类", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private String category; - @Schema(description = "流程分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "请假") - private String categoryName; - - @Schema(description = "流程实例的状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 - - @Schema(description = "发起时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime startTime; - - @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime endTime; - - @Schema(description = "持续时间", example = "1000") - private Long durationInMillis; - - @Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED) - private Map formVariables; - - @Schema(description = "业务的唯一标识-例如说,请假申请的编号", example = "1") - private String businessKey; - - /** - * 发起流程的用户 - */ - private UserSimpleBaseVO startUser; - - @Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") - private String processDefinitionId; - /** - * 流程定义 - */ - private BpmProcessDefinitionRespVO processDefinition; - - /** - * 当前审批中的任务 - */ - private List tasks; // 仅在流程实例分页才返回 - - @Schema(description = "流程任务") - @Data - public static class Task { - - @Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String id; - - @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - private String name; - - @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") - @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser - private Long assignee; - - @Schema(description = "任务分配人", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") - private UserSimpleBaseVO assigneeUser; - - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java deleted file mode 100644 index 7d56754..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.task; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.List; -import java.util.Map; - -@Schema(description = "管理后台 - 通过流程任务的 Request VO") -@Data -public class BpmTaskApproveReqVO { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "任务编号不能为空") - private String id; - - @Schema(description = "审批意见", example = "不错不错!") - private String reason; - - @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png") - private String signPicUrl; - - @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED) - private Map variables; - - @Schema(description = "下一个节点审批人", example = "{nodeId:[1, 2]}") - private Map> nextAssignees; // 为什么是 Map,而不是 List 呢?因为下一个节点可能是多个,例如说并行网关的情况 - - // 新增任务变量实例,业务表单 - @Schema(description = "任务变量实例,业务表单", example = "{'formField1': 'value1', 'formField2': 'value2'}") - private Map taskVariables; -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java deleted file mode 100644 index 1775be0..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskCopyReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.task; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.Collection; - -@Schema(description = "管理后台 - 抄送流程任务的 Request VO") -@Data -public class BpmTaskCopyReqVO { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "任务编号不能为空") - private String id; - - @Schema(description = "抄送的用户编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]") - @NotEmpty(message = "抄送用户不能为空") - private Collection copyUserIds; - - @Schema(description = "抄送意见", example = "帮忙看看!") - private String reason; -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java deleted file mode 100644 index 21aa42a..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskDelegateReqVO.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.task; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - 委派流程任务的 Request VO") -@Data -public class BpmTaskDelegateReqVO { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "任务编号不能为空") - private String id; - - @Schema(description = "被委派人 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotNull(message = "被委派人 ID 不能为空") - private Long delegateUserId; - - @Schema(description = "委派原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "做不了决定,需要你先帮忙瞅瞅") - @NotEmpty(message = "委派原因不能为空") - private String reason; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java deleted file mode 100644 index 5c1ee94..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.task; - -import com.zt.plat.framework.common.pojo.PageParam; -import com.zt.plat.framework.common.util.date.DateUtils; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import org.springframework.format.annotation.DateTimeFormat; - -import java.time.LocalDateTime; - -@Schema(description = "管理后台 - 流程任务的的分页 Request VO") // 待办、已办,都使用该分页 -@Data -public class BpmTaskPageReqVO extends PageParam { - - @Schema(description = "流程任务名", example = "ZT") - private String name; - - @Schema(description = "流程分类", example = "1") - private String category; - - @Schema(description = "流程定义的标识", example = "2048") - private String processDefinitionKey; // 精准匹配 - - @Schema(description = "创建时间") - @DateTimeFormat(pattern = DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) - private LocalDateTime[] createTime; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java deleted file mode 100644 index 8482f48..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.task; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -@Schema(description = "管理后台 - 不通过流程任务的 Request VO") -@Data -public class BpmTaskRejectReqVO { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "任务编号不能为空") - private String id; - - @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!") - private String reason; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java deleted file mode 100644 index 0cafdb4..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.task; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.zt.plat.framework.common.core.KeyValue; -import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Map; - -@Schema(description = "管理后台 - 流程任务 Response VO") -@Data -public class BpmTaskRespVO { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String id; - - @Schema(description = "任务名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - private String name; - - @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - - @Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime endTime; - - @Schema(description = "持续时间", example = "1000") - private Long durationInMillis; - - @Schema(description = "任务状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private Integer status; // 参见 BpmTaskStatusEnum 枚举 - - @Schema(description = "审批理由", requiredMode = Schema.RequiredMode.REQUIRED, example = "2") - private String reason; - - @Schema(description = "任务负责人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") - @JsonIgnore // 不返回,只是方便后续读取,赋值给 ownerUser - private Long owner; - /** - * 负责人的用户信息 - */ - private UserSimpleBaseVO ownerUser; - - @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") - @JsonIgnore // 不返回,只是方便后续读取,赋值给 assigneeUser - private Long assignee; - /** - * 审核的用户信息 - */ - private UserSimpleBaseVO assigneeUser; - - @Schema(description = "任务定义的标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "Activity_one") - private String taskDefinitionKey; - - @Schema(description = "所属流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "8888") - private String processInstanceId; - /** - * 所属流程实例 - */ - private ProcessInstance processInstance; - - @Schema(description = "父任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String parentTaskId; - @Schema(description = "子任务列表(由加签生成)", requiredMode = Schema.RequiredMode.REQUIRED, example = "childrenTask") - private List children; // 由加签生成,包含多层子任务 - - @Schema(description = "表单编号", example = "1024") - private Long formId; - @Schema(description = "表单路由", example = "1024") - private String formPath; - @Schema(description = "表单名字", example = "请假表单") - private String formName; - @Schema(description = "表单的配置,JSON 字符串") - private String formConf; - @Schema(description = "表单项的数组") - private List formFields; - @Schema(description = "提交的表单值", requiredMode = Schema.RequiredMode.REQUIRED) - private Map formVariables; - @Schema(description = "操作按钮设置值") - private Map buttonsSetting; - - @Schema(description = "是否需要签名", example = "false") - private Boolean signEnable; - - @Schema(description = "是否填写审批意见", example = "false") - private Boolean reasonRequire; - - @Schema(description = "节点类型", example = "10") - private Integer nodeType; // 参见 BpmSimpleModelNodeTypeEnum 枚举。 - - @Data - @Schema(description = "流程实例") - public static class ProcessInstance { - - @Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - private String id; - - @Schema(description = "流程实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") - private String name; - - @Schema(description = "提交时间", requiredMode = Schema.RequiredMode.REQUIRED) - private LocalDateTime createTime; - - @Schema(description = "流程定义的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") - private String processDefinitionId; - - @Schema(description = "流程摘要", example = "[]") - private List> summary; // 只有流程表单,才有摘要! - - /** - * 发起人的用户信息 - */ - private UserSimpleBaseVO startUser; - - } - - @Data - @Schema(description = "操作按钮设置") - public static class OperationButtonSetting { - - @Schema(description = "显示名称", example = "审批") - private String displayName; - - @Schema(description = "是否启用", example = "true") - private Boolean enable; - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java deleted file mode 100644 index 22982ab..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskReturnReqVO.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.task; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -@Schema(description = "管理后台 - 退回流程任务的 Request VO") -@Data -public class BpmTaskReturnReqVO { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "任务编号不能为空") - private String id; - - @Schema(description = "退回到的任务 Key", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotEmpty(message = "退回到的任务 Key 不能为空") - private String targetTaskDefinitionKey; - - @Schema(description = "退回意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "我就是想驳回") - @NotEmpty(message = "退回意见不能为空") - private String reason; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java deleted file mode 100644 index 0acf4bd..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignCreateReqVO.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.task; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -import java.util.Set; - -@Schema(description = "管理后台 - 加签任务的创建(加签) Request VO") -@Data -public class BpmTaskSignCreateReqVO { - - @Schema(description = "需要加签的任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotEmpty(message = "任务编号不能为空") - private String id; - - @Schema(description = "加签的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "888") - @NotEmpty(message = "加签用户不能为空") - private Set userIds; - - @Schema(description = "加签类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "before") - @NotEmpty(message = "加签类型不能为空") - private String type; // 参见 BpmTaskSignTypeEnum 枚举 - - @Schema(description = "加签原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "需要加签") - @NotEmpty(message = "加签原因不能为空") - private String reason; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java deleted file mode 100644 index a848485..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskSignDeleteReqVO.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.task; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import lombok.Data; - -@Schema(description = "管理后台 - 加签任务的删除(减签) Request VO") -@Data -public class BpmTaskSignDeleteReqVO { - - @Schema(description = "被减签的任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") - @NotEmpty(message = "任务编号不能为空") - private String id; - - @Schema(description = "加签原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "需要减签") - @NotEmpty(message = "加签原因不能为空") - private String reason; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java deleted file mode 100644 index e55bb73..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskTransferReqVO.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.zt.plat.module.bpm.controller.admin.task.vo.task; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -@Schema(description = "管理后台 - 流程任务的转办 Request VO") -@Data -public class BpmTaskTransferReqVO { - - @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") - @NotEmpty(message = "任务编号不能为空") - private String id; - - @Schema(description = "新审批人的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048") - @NotNull(message = "新审批人的用户编号不能为空") - private Long assigneeUserId; - - @Schema(description = "转办原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "做不了决定,需要你先帮忙瞅瞅") - @NotEmpty(message = "转办原因不能为空") - private String reason; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java deleted file mode 100644 index 0513848..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/app/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位 - */ -package com.zt.plat.module.bpm.controller.app; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java deleted file mode 100644 index 52ffdf8..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 提供 RESTful API 给前端: - * 1. admin 包:提供给管理后台 zt-ui-admin 前端项目 - * 2. app 包:提供给用户 APP zt-ui-app 前端项目,它的 Controller 和 VO 都要添加 App 前缀,用于和管理后台进行区分 - */ -package com.zt.plat.module.bpm.controller; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java deleted file mode 100644 index 262a598..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmModelConvert.java +++ /dev/null @@ -1,132 +0,0 @@ -package com.zt.plat.module.bpm.convert.definition; - -import cn.hutool.core.util.ArrayUtil; -import com.zt.plat.framework.common.util.date.DateUtils; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.base.dept.DeptSimpleBaseVO; -import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelRespVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import org.flowable.common.engine.impl.db.SuspensionState; -import org.flowable.engine.repository.Deployment; -import org.flowable.engine.repository.Model; -import org.flowable.engine.repository.ProcessDefinition; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Map; - -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; - -/** - * 流程模型 Convert - * - * @author yunlongn - */ -@Mapper -public interface BpmModelConvert { - - BpmModelConvert INSTANCE = Mappers.getMapper(BpmModelConvert.class); - - default List buildModelList(List list, - Map formMap, - Map categoryMap, - Map deploymentMap, - Map processDefinitionMap, - Map userMap, - Map deptMap) { - List result = convertList(list, model -> { - BpmModelMetaInfoVO metaInfo = parseMetaInfo(model); - BpmFormDO form = metaInfo != null ? formMap.get(metaInfo.getFormId()) : null; - BpmCategoryDO category = categoryMap.get(model.getCategory()); - Deployment deployment = model.getDeploymentId() != null ? deploymentMap.get(model.getDeploymentId()) : null; - ProcessDefinition processDefinition = model.getDeploymentId() != null ? - processDefinitionMap.get(model.getDeploymentId()) : null; - List startUsers = metaInfo != null ? convertList(metaInfo.getStartUserIds(), userMap::get) : null; - List startDepts = metaInfo != null ? convertList(metaInfo.getStartDeptIds(), deptMap::get) : null; - return buildModel0(model, metaInfo, form, category, deployment, processDefinition, startUsers, startDepts); - }); - // 排序 - result.sort(Comparator.comparing(BpmModelMetaInfoVO::getSort)); - return result; - } - - default BpmModelRespVO buildModel(Model model, byte[] bpmnBytes, BpmSimpleModelNodeVO simpleModel) { - BpmModelMetaInfoVO metaInfo = parseMetaInfo(model); - BpmModelRespVO modelVO = buildModel0(model, metaInfo, null, null, null, null, null, null); - if (ArrayUtil.isNotEmpty(bpmnBytes)) { - modelVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnBytes)); - } - modelVO.setSimpleModel(simpleModel); - return modelVO; - } - - default BpmModelRespVO buildModel0(Model model, - BpmModelMetaInfoVO metaInfo, BpmFormDO form, BpmCategoryDO category, - Deployment deployment, ProcessDefinition processDefinition, - List startUsers, List startDepts) { - BpmModelRespVO modelRespVO = new BpmModelRespVO().setId(model.getId()).setName(model.getName()) - .setKey(model.getKey()).setCategory(model.getCategory()) - .setCreateTime(DateUtils.of(model.getCreateTime())); - // Form - BeanUtils.copyProperties(metaInfo, modelRespVO); - if (form != null) { - modelRespVO.setFormName(form.getName()); - } - // Category - if (category != null) { - modelRespVO.setCategoryName(category.getName()); - } - // ProcessDefinition - if (processDefinition != null) { - modelRespVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)); - modelRespVO.getProcessDefinition().setSuspensionState(processDefinition.isSuspended() ? - SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); - if (deployment != null) { - modelRespVO.getProcessDefinition().setDeploymentTime(DateUtils.of(deployment.getDeploymentTime())); - } - } - // User、Dept - modelRespVO.setStartUsers(BeanUtils.toBean(startUsers, UserSimpleBaseVO.class)) - .setStartDepts(BeanUtils.toBean(startDepts, DeptSimpleBaseVO.class)); - return modelRespVO; - } - - default void copyToModel(Model model, BpmModelSaveReqVO reqVO) { - model.setName(reqVO.getName()); - model.setKey(reqVO.getKey()); - model.setCategory(reqVO.getCategory()); - model.setMetaInfo(JsonUtils.toJsonString(BeanUtils.toBean(reqVO, BpmModelMetaInfoVO.class))); - } - - default BpmModelMetaInfoVO parseMetaInfo(Model model) { - BpmModelMetaInfoVO vo = JsonUtils.parseObject(model.getMetaInfo(), BpmModelMetaInfoVO.class); - if (vo == null) { - return null; - } - if (vo.getManagerUserIds() == null) { - vo.setManagerUserIds(Collections.emptyList()); - } - if (vo.getStartUserIds() == null) { - vo.setStartUserIds(Collections.emptyList()); - } - // 如果为空,兜底处理,使用 createTime 创建时间 - if (vo.getSort() == null) { - vo.setSort(model.getCreateTime().getTime()); - } - return vo; - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java deleted file mode 100644 index 86b256c..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/definition/BpmProcessDefinitionConvert.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.zt.plat.module.bpm.convert.definition; - -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.map.MapUtil; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.collection.CollectionUtils; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.common.engine.impl.db.SuspensionState; -import org.flowable.engine.repository.Deployment; -import org.flowable.engine.repository.ProcessDefinition; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.factory.Mappers; - -import java.util.Comparator; -import java.util.List; -import java.util.Map; - -/** - * Bpm 流程定义的 Convert - * - * @author yunlong.li - */ -@Mapper -public interface BpmProcessDefinitionConvert { - - BpmProcessDefinitionConvert INSTANCE = Mappers.getMapper(BpmProcessDefinitionConvert.class); - - default PageResult buildProcessDefinitionPage(PageResult page, - Map deploymentMap, - Map processDefinitionInfoMap, - Map formMap, - Map categoryMap) { - List list = buildProcessDefinitionList(page.getList(), deploymentMap, processDefinitionInfoMap, formMap, categoryMap); - return new PageResult<>(list, page.getTotal()); - } - - default List buildProcessDefinitionList(List list, - Map deploymentMap, - Map processDefinitionInfoMap, - Map formMap, - Map categoryMap) { - List result = CollectionUtils.convertList(list, definition -> { - Deployment deployment = MapUtil.get(deploymentMap, definition.getDeploymentId(), Deployment.class); - BpmProcessDefinitionInfoDO processDefinitionInfo = MapUtil.get(processDefinitionInfoMap, definition.getId(), BpmProcessDefinitionInfoDO.class); - BpmFormDO form = null; - if (processDefinitionInfo != null) { - form = MapUtil.get(formMap, processDefinitionInfo.getFormId(), BpmFormDO.class); - } - BpmCategoryDO category = MapUtil.get(categoryMap, definition.getCategory(), BpmCategoryDO.class); - return buildProcessDefinition(definition, deployment, processDefinitionInfo, form, category, null); - }); - // 排序 - result.sort(Comparator.comparing(BpmProcessDefinitionRespVO::getSort)); - return result; - } - - default BpmProcessDefinitionRespVO buildProcessDefinition(ProcessDefinition definition, - Deployment deployment, - BpmProcessDefinitionInfoDO processDefinitionInfo, - BpmFormDO form, - BpmCategoryDO category, - BpmnModel bpmnModel) { - BpmProcessDefinitionRespVO respVO = BeanUtils.toBean(definition, BpmProcessDefinitionRespVO.class); - respVO.setSuspensionState(definition.isSuspended() ? SuspensionState.SUSPENDED.getStateCode() : SuspensionState.ACTIVE.getStateCode()); - // Deployment - if (deployment != null) { - respVO.setDeploymentTime(LocalDateTimeUtil.of(deployment.getDeploymentTime())); - } - // BpmProcessDefinitionInfoDO - if (processDefinitionInfo != null) { - copyTo(processDefinitionInfo, respVO); - // Form - if (form != null) { - respVO.setFormName(form.getName()); - } - } - // Category - if (category != null) { - respVO.setCategoryName(category.getName()); - } - // BpmnModel - if (bpmnModel != null) { - respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel)); - } - return respVO; - } - - @Mapping(source = "from.id", target = "to.id", ignore = true) - void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java deleted file mode 100644 index 41de133..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/message/BpmMessageConvert.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.zt.plat.module.bpm.convert.message; - -import com.zt.plat.module.system.api.sms.dto.send.SmsSendSingleToUserReqDTO; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.factory.Mappers; - -import java.util.Map; - -@Mapper -public interface BpmMessageConvert { - - BpmMessageConvert INSTANCE = Mappers.getMapper(BpmMessageConvert.class); - - @Mapping(target = "mobile", ignore = true) - @Mapping(source = "userId", target = "userId") - @Mapping(source = "templateCode", target = "templateCode") - @Mapping(source = "templateParams", target = "templateParams") - SmsSendSingleToUserReqDTO convert(Long userId, String templateCode, Map templateParams); - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java deleted file mode 100644 index b72f713..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 提供 POJO 类的实体转换 - * - * 目前使用 MapStruct 框架 - */ -package com.zt.plat.module.bpm.convert; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java deleted file mode 100644 index 87652f8..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java +++ /dev/null @@ -1,305 +0,0 @@ -package com.zt.plat.module.bpm.convert.task; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import com.zt.plat.framework.business.core.util.DeptUtil; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.collection.MapUtils; -import com.zt.plat.framework.common.util.collection.SetUtils; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; -import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceBpmnModelViewRespVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceRespVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; -import com.zt.plat.module.bpm.convert.definition.BpmProcessDefinitionConvert; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.repository.ProcessDefinition; -import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.task.api.Task; -import org.flowable.task.api.history.HistoricTaskInstance; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.factory.Mappers; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; - -/** - * 流程实例 Convert - * - * @author ZT - */ -@Mapper -public interface BpmProcessInstanceConvert { - - BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class); - - default PageResult buildProcessInstancePage(PageResult pageResult, - Map processDefinitionMap, - Map categoryMap, - Map> taskMap, - Map userMap, - Map deptMap, - Map processDefinitionInfoMap) { - PageResult vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstanceRespVO.class); - for (int i = 0; i < pageResult.getList().size(); i++) { - BpmProcessInstanceRespVO respVO = vpPageResult.getList().get(i); - respVO.setStatus(FlowableUtils.getProcessInstanceStatus(pageResult.getList().get(i))); - MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(), - processDefinition -> respVO.setCategory(processDefinition.getCategory()) - .setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class))); - MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName())); - respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceRespVO.Task.class)); - // user - if (userMap != null) { - AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(pageResult.getList().get(i).getStartUserId())); - if (startUser != null) { - respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); - MapUtils.findAndThen(deptMap, DeptUtil.getDeptId(startUser), dept -> respVO.getStartUser().setDeptName(dept.getName())); - } - if (CollUtil.isNotEmpty(respVO.getTasks())) { - respVO.getTasks().forEach(task -> { - AdminUserRespDTO assigneeUser = userMap.get(task.getAssignee()); - if (assigneeUser!= null) { - task.setAssigneeUser(BeanUtils.toBean(assigneeUser, UserSimpleBaseVO.class)); - MapUtils.findAndThen(deptMap, DeptUtil.getDeptId(assigneeUser), dept -> task.getAssigneeUser().setDeptName(dept.getName())); - } - }); - } - } - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionInfoMap.get(respVO.getProcessDefinitionId()); - if (processDefinitionInfo != null) { - // 摘要 - respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfo, - pageResult.getList().get(i).getProcessVariables())); - // 是否可见 - respVO.getProcessDefinition().setVisible(processDefinitionInfo.getVisible()); - // 是否可以重新发起流程 - respVO.getProcessDefinition().setRestart(processDefinitionInfo.getRestart()); - } - // 表单 - respVO.setFormVariables(pageResult.getList().get(i).getProcessVariables()); - } - return vpPageResult; - } - - default BpmProcessInstanceRespVO buildProcessInstance(HistoricProcessInstance processInstance, - ProcessDefinition processDefinition, - BpmProcessDefinitionInfoDO processDefinitionInfo, - AdminUserRespDTO startUser, - DeptRespDTO dept) { - BpmProcessInstanceRespVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class); - respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance)) - .setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance)); - // definition - respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionRespVO.class)); - copyTo(processDefinitionInfo, respVO.getProcessDefinition()); - // user - if (startUser != null) { - respVO.setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); - if (dept != null) { - respVO.getStartUser().setDeptName(dept.getName()); - } - } - return respVO; - } - - @Mapping(source = "from.id", target = "to.id", ignore = true) - void copyTo(BpmProcessDefinitionInfoDO from, @MappingTarget BpmProcessDefinitionRespVO to); - - default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) { - return new BpmProcessInstanceStatusEvent(source).setId(instance.getId()).setStatus(status) - .setProcessDefinitionKey(instance.getProcessDefinitionKey()).setBusinessKey(instance.getBusinessKey()); - } - - default BpmMessageSendWhenProcessInstanceApproveReqDTO buildProcessInstanceApproveMessage(ProcessInstance instance) { - return new BpmMessageSendWhenProcessInstanceApproveReqDTO() - .setStartUserId(NumberUtils.parseLong(instance.getStartUserId())) - .setProcessInstanceId(instance.getId()) - .setProcessInstanceName(instance.getName()); - } - - default BpmMessageSendWhenProcessInstanceRejectReqDTO buildProcessInstanceRejectMessage(ProcessInstance instance, String reason) { - return new BpmMessageSendWhenProcessInstanceRejectReqDTO() - .setProcessInstanceName(instance.getName()) - .setProcessInstanceId(instance.getId()) - .setReason(reason) - .setStartUserId(NumberUtils.parseLong(instance.getStartUserId())); - } - - default BpmProcessInstanceBpmnModelViewRespVO buildProcessInstanceBpmnModelView(HistoricProcessInstance processInstance, - List taskInstances, - BpmnModel bpmnModel, - BpmSimpleModelNodeVO simpleModel, - Set unfinishedTaskActivityIds, - Set finishedTaskActivityIds, - Set finishedSequenceFlowActivityIds, - Set rejectTaskActivityIds, - Map userMap, - Map deptMap) { - BpmProcessInstanceBpmnModelViewRespVO respVO = new BpmProcessInstanceBpmnModelViewRespVO(); - // 基本信息 - respVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmProcessInstanceRespVO.class, o -> o - .setStatus(FlowableUtils.getProcessInstanceStatus(processInstance))) - .setStartUser(buildUser(processInstance.getStartUserId(), userMap, deptMap))); - respVO.setTasks(convertList(taskInstances, task -> BeanUtils.toBean(task, BpmTaskRespVO.class) - .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)) - .setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap)) - .setOwnerUser(buildUser(task.getOwner(), userMap, deptMap)))); - respVO.setBpmnXml(BpmnModelUtils.getBpmnXml(bpmnModel)); - respVO.setSimpleModel(simpleModel); - // 进度信息 - respVO.setUnfinishedTaskActivityIds(unfinishedTaskActivityIds) - .setFinishedTaskActivityIds(finishedTaskActivityIds) - .setFinishedSequenceFlowActivityIds(finishedSequenceFlowActivityIds) - .setRejectedTaskActivityIds(rejectTaskActivityIds); - return respVO; - } - - default UserSimpleBaseVO buildUser(String userIdStr, - Map userMap, - Map deptMap) { - if (StrUtil.isEmpty(userIdStr)) { - return null; - } - Long userId = NumberUtils.parseLong(userIdStr); - return buildUser(userId, userMap, deptMap); - } - - default UserSimpleBaseVO buildUser(Long userId, - Map userMap, - Map deptMap) { - if (userId == null) { - return null; - } - AdminUserRespDTO user = userMap.get(userId); - if (user == null) { - return null; - } - UserSimpleBaseVO userVO = BeanUtils.toBean(user, UserSimpleBaseVO.class); - Long deptId = DeptUtil.getDeptId(user); - DeptRespDTO dept = deptId != null ? deptMap.get(deptId) : null; - if (dept != null) { - userVO.setDeptName(dept.getName()); - } - return userVO; - } - - default BpmApprovalDetailRespVO.ActivityNodeTask buildApprovalTaskInfo(HistoricTaskInstance task) { - if (task == null) { - return null; - } - return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class) - .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)) - .setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task)); - } - - default Set parseUserIds(HistoricProcessInstance processInstance, - List activityNodes, - BpmTaskRespVO todoTask) { - Set userIds = new HashSet<>(); - if (processInstance != null) { - userIds.add(NumberUtils.parseLong(processInstance.getStartUserId())); - } - for (BpmApprovalDetailRespVO.ActivityNode activityNode : activityNodes) { - CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getAssignee)); - CollUtil.addAll(userIds, convertSet(activityNode.getTasks(), BpmApprovalDetailRespVO.ActivityNodeTask::getOwner)); - CollUtil.addAll(userIds, activityNode.getCandidateUserIds()); - } - if (todoTask != null) { - CollUtil.addIfAbsent(userIds, todoTask.getAssignee()); - CollUtil.addIfAbsent(userIds, todoTask.getOwner()); - if (CollUtil.isNotEmpty(todoTask.getChildren())) { - CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getAssignee)); - CollUtil.addAll(userIds, convertSet(todoTask.getChildren(), BpmTaskRespVO::getOwner)); - } - } - return userIds; - } - - default Set parseUserIds02(HistoricProcessInstance processInstance, - List tasks) { - Set userIds = SetUtils.asSet(Long.valueOf(processInstance.getStartUserId())); - tasks.forEach(task -> { - CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getAssignee()))); - CollUtil.addIfAbsent(userIds, NumberUtils.parseLong((task.getOwner()))); - }); - return userIds; - } - - default BpmApprovalDetailRespVO buildApprovalDetail(BpmnModel bpmnModel, - ProcessDefinition processDefinition, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance processInstance, - Integer processInstanceStatus, - List activityNodes, - BpmTaskRespVO todoTask, - Map formFieldsPermission, - Map userMap, - Map deptMap) { - // 1.1 流程实例 - BpmProcessInstanceRespVO processInstanceResp = null; - if (processInstance != null) { - AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); - Long deptId = DeptUtil.getDeptId(startUser); - DeptRespDTO dept = deptMap.get(deptId); - processInstanceResp = buildProcessInstance(processInstance, null, null, startUser, dept); - } - - // 1.2 流程定义 - BpmProcessDefinitionRespVO definitionResp = BpmProcessDefinitionConvert.INSTANCE.buildProcessDefinition( - processDefinition, null, processDefinitionInfo, null, null, bpmnModel); - - // 1.3 流程节点 - activityNodes.forEach(approveNode -> { - if (approveNode.getTasks() != null) { - approveNode.getTasks().forEach(task -> { - task.setAssigneeUser(buildUser(task.getAssignee(), userMap, deptMap)); - task.setOwnerUser(buildUser(task.getOwner(), userMap, deptMap)); - }); - } - approveNode.setCandidateUsers(convertList(approveNode.getCandidateUserIds(), userId -> buildUser(userId, userMap, deptMap))); - }); - - // 1.4 待办任务 - if (todoTask != null) { - todoTask.setAssigneeUser(buildUser(todoTask.getAssignee(), userMap, deptMap)); - todoTask.setOwnerUser(buildUser(todoTask.getOwner(), userMap, deptMap)); - if (CollUtil.isNotEmpty(todoTask.getChildren())) { - todoTask.getChildren().forEach(childTask -> { - childTask.setAssigneeUser(buildUser(childTask.getAssignee(), userMap, deptMap)); - childTask.setOwnerUser(buildUser(childTask.getOwner(), userMap, deptMap)); - }); - } - } - - // 2. 拼接起来 - return new BpmApprovalDetailRespVO().setStatus(processInstanceStatus) - .setProcessDefinition(definitionResp) - .setProcessInstance(processInstanceResp) - .setFormFieldsPermission(formFieldsPermission) - .setTodoTask(todoTask) - .setActivityNodes(activityNodes); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceDTOConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceDTOConvert.java deleted file mode 100644 index 8d0a6ff..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceDTOConvert.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.zt.plat.module.bpm.convert.task; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.api.task.dto.*; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; -import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Named; -import org.mapstruct.factory.Mappers; - -import java.util.List; - -/** - * 流程实例 DTO 转换器 - * - * @author ZT - */ -@Mapper -public interface BpmProcessInstanceDTOConvert { - - BpmProcessInstanceDTOConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceDTOConvert.class); - - // VO to DTO - BpmProcessInstancePageReqDTO convert(BpmProcessInstancePageReqVO bean); - BpmProcessInstanceCancelReqDTO convert(BpmProcessInstanceCancelReqVO bean); - BpmApprovalDetailReqDTO convert(BpmApprovalDetailReqVO bean); - - // DTO to VO - BpmProcessInstancePageReqVO convertVO(BpmProcessInstancePageReqDTO bean); - BpmProcessInstanceCancelReqVO convertVO(BpmProcessInstanceCancelReqDTO bean); - BpmApprovalDetailReqVO convertVO(BpmApprovalDetailReqDTO bean); - - PageResult convertPage(PageResult pageResult); - BpmProcessInstanceRespDTO convert(BpmProcessInstanceRespVO bean); - BpmApprovalDetailRespDTO convert(BpmApprovalDetailRespVO bean); - BpmProcessInstanceBpmnModelViewRespDTO convert(BpmProcessInstanceBpmnModelViewRespVO bean); - - List convertActivityNodes(List nodes); - - // 内部类转换 - BpmProcessInstanceRespDTO.Task convert(BpmProcessInstanceRespVO.Task task); - BpmApprovalDetailRespDTO.ActivityNode convert(BpmApprovalDetailRespVO.ActivityNode node); - BpmApprovalDetailRespDTO.ActivityNodeTask convert(BpmApprovalDetailRespVO.ActivityNodeTask task); - - // 用户信息转换 - UserSimpleDTO convertUser(UserSimpleBaseVO user); - BpmProcessDefinitionRespDTO convert(BpmProcessDefinitionRespVO definition); - - @Mapping(target = "childNode", source = "childNode", qualifiedByName = "mapChildNode") - BpmSimpleModelNodeDTO convert(BpmSimpleModelNodeVO simpleModel); - - /** - * 将BpmSimpleModelNodeVO的childNode转换为List - */ - @Named("mapChildNode") - default List mapChildNode(BpmSimpleModelNodeVO childNode) { - if (childNode == null) { - return null; - } - return List.of(convert(childNode)); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java deleted file mode 100644 index ccba6d7..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmTaskConvert.java +++ /dev/null @@ -1,316 +0,0 @@ -package com.zt.plat.module.bpm.convert.task; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.map.MapUtil; -import com.zt.plat.framework.business.core.util.DeptUtil; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.collection.CollectionUtils; -import com.zt.plat.framework.common.util.date.DateUtils; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.api.task.dto.BpmTaskRespDTO; -import com.zt.plat.module.bpm.api.task.dto.UserSimpleDTO; -import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.task.api.Task; -import org.flowable.task.api.history.HistoricTaskInstance; -import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -import java.util.Date; -import java.util.List; -import java.util.Map; - -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; -import static com.zt.plat.framework.common.util.collection.MapUtils.findAndThen; - -/** - * Bpm 任务 Convert - * - * @author ZT - */ -@Mapper -public interface BpmTaskConvert { - - BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class); - - default PageResult buildTodoTaskPage(PageResult pageResult, - Map processInstanceMap, - Map userMap, - Map processDefinitionInfoMap) { - return BeanUtils.toBean(pageResult, BpmTaskRespVO.class, taskVO -> { - ProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId()); - if (processInstance == null) { - return; - } - taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class)); - AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); - taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); - taskVO.getProcessInstance().setCreateTime(DateUtils.of(processInstance.getStartTime())); - // 摘要 - taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()), - processInstance.getProcessVariables())); - }); - } - - default PageResult buildTaskPage(PageResult pageResult, - Map processInstanceMap, - Map userMap, - Map deptMap, - Map processDefinitionInfoMap) { - List taskVOList = CollectionUtils.convertList(pageResult.getList(), task -> { - BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); - taskVO.setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task)); - // 用户信息 - AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee())); - if (assignUser != null) { - taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class)); - findAndThen(deptMap, DeptUtil.getDeptId(assignUser), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName())); - } - // 流程实例 - HistoricProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId()); - if (processInstance != null) { - AdminUserRespDTO startUser = userMap.get(NumberUtils.parseLong(processInstance.getStartUserId())); - taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskRespVO.ProcessInstance.class)); - taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, UserSimpleBaseVO.class)); - // 摘要 - taskVO.getProcessInstance().setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(processInstance.getProcessDefinitionId()), - processInstance.getProcessVariables())); - } - return taskVO; - }); - return new PageResult<>(taskVOList, pageResult.getTotal()); - } - - default List buildTaskListByProcessInstanceId(List taskList, - Map formMap, - Map userMap, - Map deptMap) { - return CollectionUtils.convertList(taskList, task -> { - // 特殊:已取消的任务,不返回 - BpmTaskRespVO taskVO = BeanUtils.toBean(task, BpmTaskRespVO.class); - Integer taskStatus = FlowableUtils.getTaskStatus(task); - if (BpmTaskStatusEnum.isCancelStatus(taskStatus)) { - return null; - } - taskVO.setStatus(taskStatus).setReason(FlowableUtils.getTaskReason(task)); - // 表单信息 - BpmFormDO form = null; - try { - Long formId = NumberUtils.parseLong(task.getFormKey()); - form = MapUtil.get(formMap, formId, BpmFormDO.class); - } catch (NumberFormatException e) { - // 如果 formKey 不是数字(比如是URL),设置 formPath - taskVO.setFormPath(task.getFormKey()); - taskVO.setFormVariables(FlowableUtils.getTaskFormVariable(task)); - } - if (form != null) { - taskVO.setFormId(form.getId()).setFormName(form.getName()).setFormConf(form.getConf()) - .setFormFields(form.getFields()).setFormVariables(FlowableUtils.getTaskFormVariable(task)); - } - // 用户信息 - buildTaskAssignee(taskVO, task.getAssignee(), userMap, deptMap); - buildTaskOwner(taskVO, task.getOwner(), userMap, deptMap); - return taskVO; - }); - } - - default List buildTaskListByParentTaskId(List taskList, - Map userMap, - Map deptMap) { - return convertList(taskList, task -> BeanUtils.toBean(task, BpmTaskRespVO.class, taskVO -> { - AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(task.getAssignee())); - if (assignUser != null) { - taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class)); - DeptRespDTO dept = deptMap.get(DeptUtil.getDeptId(assignUser)); - if (dept != null) { - taskVO.getAssigneeUser().setDeptName(dept.getName()); - } - } - AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(task.getOwner())); - if (ownerUser != null) { - taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class)); - findAndThen(deptMap, DeptUtil.getDeptId(ownerUser), dept -> taskVO.getOwnerUser().setDeptName(dept.getName())); - } - })); - } - - default BpmTaskRespVO buildTodoTask(Task todoTask, List childrenTasks, - Map buttonsSetting, - BpmFormDO form) { - BpmTaskRespVO bpmTaskRespVO = BeanUtils.toBean(todoTask, BpmTaskRespVO.class) - .setStatus(FlowableUtils.getTaskStatus(todoTask)).setReason(FlowableUtils.getTaskReason(todoTask)) - .setButtonsSetting(buttonsSetting) - .setChildren(convertList(childrenTasks, childTask -> BeanUtils.toBean(childTask, BpmTaskRespVO.class) - .setStatus(FlowableUtils.getTaskStatus(childTask)))); - if (form != null) { - bpmTaskRespVO.setFormId(form.getId()).setFormName(form.getName()) - .setFormConf(form.getConf()).setFormFields(form.getFields()); - }else{ - // 任务级别的业务表单 - bpmTaskRespVO.setFormPath(todoTask.getFormKey()); - } - return bpmTaskRespVO; - } - - default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser, - Task task) { - BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO(); - reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId()) - .setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId()) - .setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName()) - .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())); - return reqDTO; - } - - default void buildTaskOwner(BpmTaskRespVO task, String taskOwner, - Map userMap, - Map deptMap) { - AdminUserRespDTO ownerUser = userMap.get(NumberUtils.parseLong(taskOwner)); - if (ownerUser != null) { - task.setOwnerUser(BeanUtils.toBean(ownerUser, UserSimpleBaseVO.class)); - findAndThen(deptMap, DeptUtil.getDeptId(ownerUser), dept -> task.getOwnerUser().setDeptName(dept.getName())); - } - } - - default void buildTaskChildren(BpmTaskRespVO task, Map> childrenTaskMap, - Map userMap, Map deptMap) { - List childTasks = childrenTaskMap.get(task.getId()); - if (CollUtil.isNotEmpty(childTasks)) { - task.setChildren( - convertList(childTasks, childTask -> { - BpmTaskRespVO childTaskVO = BeanUtils.toBean(childTask, BpmTaskRespVO.class); - childTaskVO.setStatus(FlowableUtils.getTaskStatus(childTask)); - buildTaskOwner(childTaskVO, childTask.getOwner(), userMap, deptMap); - buildTaskAssignee(childTaskVO, childTask.getAssignee(), userMap, deptMap); - return childTaskVO; - }) - ); - } - } - - default void buildTaskAssignee(BpmTaskRespVO task, String taskAssignee, - Map userMap, - Map deptMap) { - AdminUserRespDTO assignUser = userMap.get(NumberUtils.parseLong(taskAssignee)); - if (assignUser != null) { - task.setAssigneeUser(BeanUtils.toBean(assignUser, UserSimpleBaseVO.class)); - findAndThen(deptMap, DeptUtil.getDeptId(assignUser), dept -> task.getAssigneeUser().setDeptName(dept.getName())); - } - } - - /** - * 将父任务的属性,拷贝到子任务(加签任务) - *

- * 为什么不使用 mapstruct 映射?因为 TaskEntityImpl 还有很多其他属性,这里我们只设置我们需要的。 - * 使用 mapstruct 会将里面嵌套的各个属性值都设置进去,会出现意想不到的问题。 - * - * @param parentTask 父任务 - * @param childTask 加签任务 - */ - default void copyTo(TaskEntityImpl parentTask, TaskEntityImpl childTask) { - childTask.setName(parentTask.getName()); - childTask.setDescription(parentTask.getDescription()); - childTask.setCategory(parentTask.getCategory()); - childTask.setParentTaskId(parentTask.getId()); - childTask.setProcessDefinitionId(parentTask.getProcessDefinitionId()); - childTask.setProcessInstanceId(parentTask.getProcessInstanceId()); - childTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey()); - childTask.setTaskDefinitionId(parentTask.getTaskDefinitionId()); - childTask.setPriority(parentTask.getPriority()); - childTask.setCreateTime(new Date()); - childTask.setTenantId(parentTask.getTenantId()); - } - - /** - * 将 BpmTaskRespVO 转换为 BpmTaskRespDTO,保持完整的嵌套结构 - */ - default List buildTaskRespDTOList(List voList) { - return CollectionUtils.convertList(voList, this::buildTaskRespDTO); - } - - /** - * 将 BpmTaskRespVO 转换为 BpmTaskRespDTO,保持完整的嵌套结构 - */ - default BpmTaskRespDTO buildTaskRespDTO(BpmTaskRespVO vo) { - if (vo == null) { - return null; - } - - BpmTaskRespDTO dto = BeanUtils.toBean(vo, BpmTaskRespDTO.class); - - // 转换用户信息 - if (vo.getAssigneeUser() != null) { - dto.setAssigneeUser(convertToUserSimpleDTO(vo.getAssigneeUser())); - } - if (vo.getOwnerUser() != null) { - dto.setOwnerUser(convertToUserSimpleDTO(vo.getOwnerUser())); - } - - // 转换流程实例信息 - if (vo.getProcessInstance() != null) { - BpmTaskRespDTO.ProcessInstanceDTO processInstanceDTO = new BpmTaskRespDTO.ProcessInstanceDTO(); - processInstanceDTO.setId(vo.getProcessInstance().getId()); - processInstanceDTO.setName(vo.getProcessInstance().getName()); - processInstanceDTO.setCreateTime(vo.getProcessInstance().getCreateTime()); - processInstanceDTO.setProcessDefinitionId(vo.getProcessInstance().getProcessDefinitionId()); - processInstanceDTO.setSummary(vo.getProcessInstance().getSummary()); - - if (vo.getProcessInstance().getStartUser() != null) { - processInstanceDTO.setStartUser(convertToUserSimpleDTO(vo.getProcessInstance().getStartUser())); - } - dto.setProcessInstance(processInstanceDTO); - } - - // 转换操作按钮设置 - if (vo.getButtonsSetting() != null) { - Map buttonsSettingDTO = vo.getButtonsSetting().entrySet() - .stream() - .collect(java.util.stream.Collectors.toMap( - Map.Entry::getKey, - entry -> { - BpmTaskRespDTO.OperationButtonSettingDTO settingDTO = new BpmTaskRespDTO.OperationButtonSettingDTO(); - settingDTO.setDisplayName(entry.getValue().getDisplayName()); - settingDTO.setEnable(entry.getValue().getEnable()); - return settingDTO; - } - )); - dto.setButtonsSetting(buttonsSettingDTO); - } - - // 递归转换子任务 - if (vo.getChildren() != null) { - dto.setChildren(buildTaskRespDTOList(vo.getChildren())); - } - - return dto; - } - - /** - * 将 UserSimpleBaseVO 转换为 UserSimpleDTO,确保所有字段都被正确赋值 - */ - default UserSimpleDTO convertToUserSimpleDTO(UserSimpleBaseVO vo) { - if (vo == null) { - return null; - } - - UserSimpleDTO dto = new UserSimpleDTO(); - dto.setId(vo.getId()); - dto.setNickname(vo.getNickname()); - dto.setAvatar(vo.getAvatar()); - dto.setDeptId(vo.getDeptId()); - dto.setDeptName(vo.getDeptName()); // 确保 deptName 被正确赋值 - return dto; - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java deleted file mode 100644 index a8251da..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmCategoryDO.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.zt.plat.module.bpm.dal.dataobject.definition; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * BPM 流程分类 DO - * - * @author ZT - */ -@TableName("bpm_category") -@KeySequence("bpm_category_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class BpmCategoryDO extends BaseDO { - - /** - * 分类编号 - */ - @TableId(type = IdType.ASSIGN_ID) - private Long id; - /** - * 分类名 - */ - private String name; - /** - * 分类标志 - */ - private String code; - /** - * 分类描述 - */ - private String description; - /** - * 分类状态 - * - * 枚举 {@link com.zt.plat.framework.common.enums.CommonStatusEnum} - */ - private Integer status; - /** - * 分类排序 - */ - private Integer sort; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java deleted file mode 100644 index 22c69b3..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmFormDO.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.zt.plat.module.bpm.dal.dataobject.definition; - -import com.baomidou.mybatisplus.annotation.*; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.List; - -/** - * BPM 工作流的表单定义 - * 用于工作流的申请表单,需要动态配置的场景 - * - * @author ZT - */ -@TableName(value = "bpm_form", autoResultMap = true) -@KeySequence("bpm_form_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class BpmFormDO extends BaseDO { - - /** - * 编号 - */ - @TableId(type = IdType.ASSIGN_ID) - private Long id; - /** - * 表单名 - */ - private String name; - /** - * 状态 - */ - private Integer status; - /** - * 表单的配置 - */ - private String conf; - /** - * 表单项的数组 - * - * 目前直接将 https://github.com/JakHuang/form-generator 生成的 JSON 串,直接保存 - * 定义:https://github.com/JakHuang/form-generator/issues/46 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List fields; - /** - * 备注 - */ - private String remark; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java deleted file mode 100644 index 43a879b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java +++ /dev/null @@ -1,226 +0,0 @@ -package com.zt.plat.module.bpm.dal.dataobject.definition; - -import com.baomidou.mybatisplus.annotation.*; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; -import com.zt.plat.framework.mybatis.core.type.LongListTypeHandler; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; -import com.zt.plat.module.bpm.enums.definition.BpmAutoApproveTypeEnum; -import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; -import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.flowable.engine.repository.Model; -import org.flowable.engine.repository.ProcessDefinition; - -import java.util.List; - -/** - * BPM 流程定义的拓信息 - * 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表 - * - * @author ZT - */ -@TableName(value = "bpm_process_definition_info", autoResultMap = true) -@KeySequence("bpm_process_definition_info_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class BpmProcessDefinitionInfoDO extends BaseDO { - - /** - * 编号 - */ - @TableId(type = IdType.ASSIGN_ID) - private Long id; - /** - * 流程定义的编号 - * - * 关联 {@link ProcessDefinition#getId()} 属性 - */ - private String processDefinitionId; - /** - * 流程模型的编号 - * - * 关联 {@link Model#getId()} 属性 - */ - private String modelId; - /** - * 流程模型的类型 - * - * 枚举 {@link BpmModelTypeEnum} - */ - private Integer modelType; - - /** - * 流程分类的编码 - * - * 关联 {@link BpmCategoryDO#getCode()} - * - * 为什么要存储?原因是,{@link ProcessDefinition#getCategory()} 无法设置 - */ - private String category; - /** - * 图标 - */ - private String icon; - /** - * 描述 - */ - private String description; - - /** - * 表单类型 - * - * 枚举 {@link BpmModelFormTypeEnum} - */ - private Integer formType; - /** - * 动态表单编号 - * - * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 - * - * 关联 {@link BpmFormDO#getId()} - */ - private Long formId; - /** - * 表单的配置 - * - * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 - * - * 冗余 {@link BpmFormDO#getConf()} - */ - private String formConf; - /** - * 表单项的数组 - * - * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 - * - * 冗余 {@link BpmFormDO#getFields()} - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private List formFields; - /** - * 自定义表单的提交路径,使用 Vue 的路由地址 - * - * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 - */ - private String formCustomCreatePath; - /** - * 自定义表单的查看路径,使用 Vue 的路由地址 - * - * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 - */ - private String formCustomViewPath; - - /** - * SIMPLE 设计器模型数据 json 格式 - * - * 目的:当使用仿钉钉设计器时。流程模型发布的时候,需要保存流程模型设计器的快照数据。 - */ - private String simpleModel; - /** - * 是否可见 - * - * 目的:如果 false 不可见,则不展示在“发起流程”的列表里 - */ - private Boolean visible; - - /** - * 是否允许重新发起 - * - * 目的:如果 false 则不可以重新发起流程 - */ - private Boolean restart; - /** - * 排序值 - */ - private Long sort; - - /** - * 可发起用户编号数组 - * - * 关联 {@link AdminUserRespDTO#getId()} 字段的数组 - * - * 如果为空,则表示“全部可以发起”! - * - * 它和 {@link #visible} 的区别在于: - * 1. {@link #visible} 只是决定是否可见。即使不可见,还是可以发起 - * 2. startUserIds 决定某个用户是否可以发起。如果该用户不可发起,则他也是不可见的 - */ - @TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤 - private List startUserIds; - - /** - * 可发起部门编号数组 - * - * 关联 {@link AdminUserRespDTO#getDeptId()} 字段的数组 - */ - @TableField(typeHandler = LongListTypeHandler.class) - private List startDeptIds; - - /** - * 可管理用户编号数组 - * - * 关联 {@link AdminUserRespDTO#getId()} 字段的数组 - */ - @TableField(typeHandler = LongListTypeHandler.class) // 为了可以使用 find_in_set 进行过滤 - private List managerUserIds; - - /** - * 是否允许撤销审批中的申请 - */ - private Boolean allowCancelRunningProcess; - - /** - * 流程 ID 规则 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private BpmModelMetaInfoVO.ProcessIdRule processIdRule; - - /** - * 自动去重类型 - * - * 枚举 {@link BpmAutoApproveTypeEnum} - */ - private Integer autoApprovalType; - - /** - * 标题设置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private BpmModelMetaInfoVO.TitleSetting titleSetting; - /** - * 摘要设置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private BpmModelMetaInfoVO.SummarySetting summarySetting; - - /** - * 流程前置通知设置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private BpmModelMetaInfoVO.HttpRequestSetting processBeforeTriggerSetting; - /** - * 流程后置通知设置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private BpmModelMetaInfoVO.HttpRequestSetting processAfterTriggerSetting; - - /** - * 任务前置通知设置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private BpmModelMetaInfoVO.HttpRequestSetting taskBeforeTriggerSetting; - - /** - * 任务后置通知设置 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private BpmModelMetaInfoVO.HttpRequestSetting taskAfterTriggerSetting; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java deleted file mode 100644 index 441f3cb..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessExpressionDO.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.zt.plat.module.bpm.dal.dataobject.definition; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; -import lombok.*; - -/** - * BPM 流程表达式 DO - * - * @author ZT - */ -@TableName("bpm_process_expression") -@KeySequence("bpm_process_expression_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@EqualsAndHashCode(callSuper = true) -@ToString(callSuper = true) -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class BpmProcessExpressionDO extends BaseDO { - - /** - * 编号 - */ - @TableId(type = IdType.ASSIGN_ID) - private Long id; - /** - * 表达式名字 - */ - private String name; - /** - * 表达式状态 - * - * 枚举 {@link TODO common_status 对应的类} - */ - private Integer status; - /** - * 表达式 - */ - private String expression; - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java deleted file mode 100644 index 9c0f05c..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessListenerDO.java +++ /dev/null @@ -1,74 +0,0 @@ -package com.zt.plat.module.bpm.dal.dataobject.definition; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; -import com.zt.plat.module.bpm.enums.definition.BpmProcessListenerTypeEnum; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -/** - * BPM 流程监听器 DO - * - * 目的:本质上它是流程监听器的模版,用于 BPMN 在设计时,直接选择这些模版 - * - * @author ZT - */ -@TableName(value = "bpm_process_listener") -@KeySequence("bpm_process_listener_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class BpmProcessListenerDO extends BaseDO { - - /** - * 主键 ID,自增 - */ - @TableId(type = IdType.ASSIGN_ID) - private Long id; - /** - * 监听器名字 - */ - private String name; - /** - * 状态 - * - * 枚举 {@link com.zt.plat.framework.common.enums.CommonStatusEnum} - */ - private Integer status; - /** - * 监听类型 - * - * 枚举 {@link BpmProcessListenerTypeEnum} - * - * 1. execution:ExecutionListener 执行监听器 - * 2. task:TaskListener 任务监听器 - */ - private String type; - /** - * 监听事件 - * - * execution 时:start、end - * task 时:create 创建、assignment 指派、complete 完成、delete 删除、update 更新、timeout 超时 - */ - private String event; - - /** - * 值类型 - * - * 1. class:Java 类,ExecutionListener 需要 {@link org.flowable.engine.delegate.JavaDelegate},TaskListener 需要 {@link org.flowable.engine.delegate.TaskListener} - * 2. delegateExpression:委托表达式,在 class 的基础上,需要注册到 Spring 容器里,后续表达式通过 Spring Bean 名称即可 - * 3. expression:表达式,一个普通类的普通方法,将这个普通类注册到 Spring 容器中,然后表达式中还可以执行这个类中的方法 - */ - private String valueType; - /** - * 值 - */ - private String value; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java deleted file mode 100644 index 5b5692f..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmUserGroupDO.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.zt.plat.module.bpm.dal.dataobject.definition; - -import com.baomidou.mybatisplus.annotation.*; -import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.util.Set; - -/** - * BPM 用户组 - * - * @author ZT - */ -@TableName(value = "bpm_user_group", autoResultMap = true) -@KeySequence("bpm_user_group_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class BpmUserGroupDO extends BaseDO { - - /** - * 编号,自增 - */ - @TableId(type = IdType.ASSIGN_ID) - private Long id; - /** - * 组名 - */ - private String name; - /** - * 描述 - */ - private String description; - /** - * 状态 - * - * 枚举 {@link CommonStatusEnum} - */ - private Integer status; - /** - * 成员用户编号数组 - */ - @TableField(typeHandler = JacksonTypeHandler.class) - private Set userIds; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java deleted file mode 100644 index fa17735..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/oa/BpmOALeaveDO.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.zt.plat.module.bpm.dal.dataobject.oa; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; -import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -/** - * OA 请假申请 DO - * - * {@link #day} 请假天数,目前先简单做。一般是分成请假上午和下午,可以是 1 整天,可以是 0.5 半天 - * - * @author jason - * @author ZT - */ -@TableName("bpm_oa_leave") -@KeySequence("bpm_oa_leave_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class BpmOALeaveDO extends BaseDO { - - /** - * 请假表单主键 - */ - @TableId(type = IdType.ASSIGN_ID) - private Long id; - /** - * 申请人的用户编号 - * - * 关联 AdminUserDO 的 id 属性 - */ - private Long userId; - /** - * 请假类型 - */ - private String type; - /** - * 原因 - */ - private String reason; - /** - * 开始时间 - */ - private LocalDateTime startTime; - /** - * 结束时间 - */ - private LocalDateTime endTime; - /** - * 请假天数 - */ - private Long day; - /** - * 审批结果 - * - * 枚举 {@link BpmTaskStatusEnum} - * 考虑到简单,所以直接复用了 BpmProcessInstanceStatusEnum 枚举,也可以自己定义一个枚举哈 - */ - private Integer status; - - /** - * 对应的流程编号 - * - * 关联 ProcessInstance 的 id 属性 - */ - private String processInstanceId; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java deleted file mode 100644 index 779d11d..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/task/BpmProcessInstanceCopyDO.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.zt.plat.module.bpm.dal.dataobject.task; - -import com.baomidou.mybatisplus.annotation.IdType; -import com.baomidou.mybatisplus.annotation.KeySequence; -import com.baomidou.mybatisplus.annotation.TableId; -import com.baomidou.mybatisplus.annotation.TableName; -import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; -import org.flowable.bpmn.model.FlowNode; -import org.flowable.task.api.history.HistoricTaskInstance; - -/** - * 流程抄送 DO - * - * @author kyle - * @since 2024-01-22 - */ -@TableName(value = "bpm_process_instance_copy", autoResultMap = true) -@KeySequence("bpm_process_instance_copy_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。 -@Data -@Builder -@NoArgsConstructor -@AllArgsConstructor -public class BpmProcessInstanceCopyDO extends BaseDO { - - /** - * 编号 - */ - @TableId(type = IdType.ASSIGN_ID) - private Long id; - - /** - * 发起人 Id - * - * 冗余 ProcessInstance 的 startUserId 字段 - */ - private Long startUserId; - /** - * 流程名 - * - * 冗余 ProcessInstance 的 name 字段 - */ - private String processInstanceName; - /** - * 流程实例的编号 - * - * 关联 ProcessInstance 的 id 属性 - */ - private String processInstanceId; - /** - * 流程实例的流程定义编号 - * - * 关联 ProcessInstance 的 processDefinitionId 属性 - */ - private String processDefinitionId; - /** - * 流程分类 - * - * 冗余 ProcessInstance 的 category 字段 - */ - private String category; - /** - * 流程活动的编号 - *

- * - * 冗余 {@link FlowNode#getId()},对应 BPMN XML 节点编号 - * 原因:用于查询抄送节点的表单字段权限。因为仿钉钉/飞书的抄送节点 (ServiceTask),没有 taskId,只有 activityId - */ - private String activityId; - /** - * 流程活动的名字 - * - * 冗余 {@link FlowNode#getName()} - */ - private String activityName; - /** - * 流程活动的编号 - * - * 关联 {@link HistoricTaskInstance#getId()} - */ - private String taskId; - - /** - * 用户编号(被抄送的用户编号) - * - * 关联 system_users 的 id 属性 - */ - private Long userId; - - /** - * 抄送意见 - */ - private String reason; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java deleted file mode 100644 index 5c36ae4..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/category/BpmCategoryMapper.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.zt.plat.module.bpm.dal.mysql.category; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; -import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -/** - * BPM 流程分类 Mapper - * - * @author ZT - */ -@Mapper -public interface BpmCategoryMapper extends BaseMapperX { - - default PageResult selectPage(BpmCategoryPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(BpmCategoryDO::getName, reqVO.getName()) - .likeIfPresent(BpmCategoryDO::getCode, reqVO.getCode()) - .eqIfPresent(BpmCategoryDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(BpmCategoryDO::getCreateTime, reqVO.getCreateTime()) - .orderByAsc(BpmCategoryDO::getSort)); - } - - default BpmCategoryDO selectByName(String name) { - return selectOne(BpmCategoryDO::getName, name); - } - - default BpmCategoryDO selectByCode(String code) { - return selectOne(BpmCategoryDO::getCode, code); - } - - default List selectListByCode(Collection codes) { - return selectList(BpmCategoryDO::getCode, codes); - } - - default List selectListByStatus(Integer status) { - return selectList(BpmCategoryDO::getStatus, status); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java deleted file mode 100644 index db73b65..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmFormMapper.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.zt.plat.module.bpm.dal.mysql.definition; - - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; -import com.zt.plat.framework.mybatis.core.query.QueryWrapperX; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 动态表单 Mapper - * - * @author 风里雾里 - */ -@Mapper -public interface BpmFormMapper extends BaseMapperX { - - default PageResult selectPage(BpmFormPageReqVO reqVO) { - return selectPage(reqVO, new QueryWrapperX() - .likeIfPresent("name", reqVO.getName()) - .orderByDesc("id")); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java deleted file mode 100644 index 5d5be48..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessDefinitionInfoMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.zt.plat.module.bpm.dal.mysql.definition; - -import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; -import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.Collection; -import java.util.List; - -@Mapper -public interface BpmProcessDefinitionInfoMapper extends BaseMapperX { - - default List selectListByProcessDefinitionIds(Collection processDefinitionIds) { - return selectList(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionIds); - } - - default BpmProcessDefinitionInfoDO selectByProcessDefinitionId(String processDefinitionId) { - return selectOne(BpmProcessDefinitionInfoDO::getProcessDefinitionId, processDefinitionId); - } - - default void updateByModelId(String modelId, BpmProcessDefinitionInfoDO updateObj) { - update(updateObj, - new LambdaQueryWrapperX().eq(BpmProcessDefinitionInfoDO::getModelId, modelId)); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java deleted file mode 100644 index 4551fd3..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessExpressionMapper.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.zt.plat.module.bpm.dal.mysql.definition; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; -import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * BPM 流程表达式 Mapper - * - * @author ZT - */ -@Mapper -public interface BpmProcessExpressionMapper extends BaseMapperX { - - default PageResult selectPage(BpmProcessExpressionPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(BpmProcessExpressionDO::getName, reqVO.getName()) - .eqIfPresent(BpmProcessExpressionDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(BpmProcessExpressionDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(BpmProcessExpressionDO::getId)); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java deleted file mode 100644 index 6f6c89e..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmProcessListenerMapper.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.zt.plat.module.bpm.dal.mysql.definition; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; -import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * BPM 流程监听器 Mapper - * - * @author ZT - */ -@Mapper -public interface BpmProcessListenerMapper extends BaseMapperX { - - default PageResult selectPage(BpmProcessListenerPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(BpmProcessListenerDO::getName, reqVO.getName()) - .eqIfPresent(BpmProcessListenerDO::getType, reqVO.getType()) - .eqIfPresent(BpmProcessListenerDO::getEvent, reqVO.getEvent()) - .eqIfPresent(BpmProcessListenerDO::getStatus, reqVO.getStatus()) - .orderByDesc(BpmProcessListenerDO::getId)); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java deleted file mode 100644 index 4c52531..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/definition/BpmUserGroupMapper.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.zt.plat.module.bpm.dal.mysql.definition; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; -import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -/** - * 用户组 Mapper - * - * @author ZT - */ -@Mapper -public interface BpmUserGroupMapper extends BaseMapperX { - - default PageResult selectPage(BpmUserGroupPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .likeIfPresent(BpmUserGroupDO::getName, reqVO.getName()) - .eqIfPresent(BpmUserGroupDO::getStatus, reqVO.getStatus()) - .betweenIfPresent(BpmUserGroupDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(BpmUserGroupDO::getId)); - } - - default List selectListByStatus(Integer status) { - return selectList(BpmUserGroupDO::getStatus, status); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java deleted file mode 100644 index 7e0f439..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/oa/BpmOALeaveMapper.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.zt.plat.module.bpm.dal.mysql.oa; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; -import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; -import org.apache.ibatis.annotations.Mapper; - -/** - * 请假申请 Mapper - * - * @author jason - * @author ZT - */ -@Mapper -public interface BpmOALeaveMapper extends BaseMapperX { - - default PageResult selectPage(Long userId, BpmOALeavePageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(BpmOALeaveDO::getUserId, userId) - .eqIfPresent(BpmOALeaveDO::getStatus, reqVO.getStatus()) - .eqIfPresent(BpmOALeaveDO::getType, reqVO.getType()) - .likeIfPresent(BpmOALeaveDO::getReason, reqVO.getReason()) - .betweenIfPresent(BpmOALeaveDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(BpmOALeaveDO::getId)); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java deleted file mode 100644 index b35d217..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.zt.plat.module.bpm.dal.mysql.task; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.mybatis.core.mapper.BaseMapperX; -import com.zt.plat.framework.mybatis.core.query.LambdaQueryWrapperX; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; -import org.apache.ibatis.annotations.Mapper; - -import java.util.List; - -@Mapper -public interface BpmProcessInstanceCopyMapper extends BaseMapperX { - - default PageResult selectPage(Long loginUserId, BpmProcessInstanceCopyPageReqVO reqVO) { - return selectPage(reqVO, new LambdaQueryWrapperX() - .eqIfPresent(BpmProcessInstanceCopyDO::getUserId, loginUserId) - .likeIfPresent(BpmProcessInstanceCopyDO::getProcessInstanceName, reqVO.getProcessInstanceName()) - .betweenIfPresent(BpmProcessInstanceCopyDO::getCreateTime, reqVO.getCreateTime()) - .orderByDesc(BpmProcessInstanceCopyDO::getId)); - } - - default void deleteByProcessInstanceId(String processInstanceId) { - delete(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId); - } - - default List getByProcessInstanceId(String processInstanceId) { - return selectList(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java deleted file mode 100644 index 77e3a30..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/BpmProcessIdRedisDAO.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.zt.plat.module.bpm.dal.redis; - -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; -import jakarta.annotation.Resource; -import org.springframework.data.redis.core.StringRedisTemplate; -import org.springframework.stereotype.Repository; - -import java.time.Duration; -import java.time.LocalDateTime; - -import static cn.hutool.core.date.DatePattern.PURE_DATETIME_PATTERN; -import static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN; - -/** - * BPM 流程 Id 编码的 Redis DAO - * - * @author Lesan - */ -@Repository -public class BpmProcessIdRedisDAO { - - @Resource - private StringRedisTemplate stringRedisTemplate; - - /** - * 生成序号,使用定义的 processIdRule 规则生成 - * - * @param processIdRule 规则 - * @return 序号 - */ - public String generate(BpmModelMetaInfoVO.ProcessIdRule processIdRule) { - // 生成日期前缀 - String infix = ""; - switch (processIdRule.getInfix()) { - case "DAY": - infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN); - break; - case "HOUR": - infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + "HH"); - break; - case "MINUTE": - infix = DateUtil.format(LocalDateTime.now(), PURE_DATE_PATTERN + "HHmm"); - break; - case "SECOND": - infix = DateUtil.format(LocalDateTime.now(), PURE_DATETIME_PATTERN); - break; - } - - // 生成序号 - String noPrefix = processIdRule.getPrefix() + infix + processIdRule.getPostfix(); - String key = RedisKeyConstants.BPM_PROCESS_ID + noPrefix; - Long no = stringRedisTemplate.opsForValue().increment(key); - if (StrUtil.isNotEmpty(infix)) { - // 特殊:没有前缀,则不能过期,不能每次都是从 0 开始 - stringRedisTemplate.expire(key, Duration.ofDays(1L)); - } - return noPrefix + String.format("%0" + processIdRule.getLength() + "d", no); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java deleted file mode 100644 index 9146bc9..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/redis/RedisKeyConstants.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.zt.plat.module.bpm.dal.redis; - -/** - * BPM Redis Key 枚举类 - * - * @author ZT - */ -public interface RedisKeyConstants { - - /** - * 流程 ID 的缓存 - */ - String BPM_PROCESS_ID = "bpm:process_id:"; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java deleted file mode 100644 index 159cfce..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.config; - -import cn.hutool.core.collection.ListUtil; -import com.zt.plat.module.bpm.framework.flowable.core.behavior.BpmActivityBehaviorFactory; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; -import com.zt.plat.module.system.api.user.AdminUserApi; -import org.flowable.common.engine.api.delegate.FlowableFunctionDelegate; -import org.flowable.common.engine.api.delegate.event.FlowableEventListener; -import org.flowable.engine.ProcessEngineConfiguration; -import org.flowable.spring.SpringProcessEngineConfiguration; -import org.flowable.spring.boot.EngineConfigurationConfigurer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.ObjectProvider; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.task.AsyncListenableTaskExecutor; -import org.springframework.jdbc.datasource.DataSourceUtils; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; - -import java.util.List; -import javax.sql.DataSource; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.SQLException; - -/** - * BPM 模块的 Flowable 配置类 - * - * @author jason - */ -@Configuration(proxyBeanMethods = false) -public class BpmFlowableConfiguration { - - private static final Logger log = LoggerFactory.getLogger(BpmFlowableConfiguration.class); - - /** - * 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean - * - * 如果不创建,会导致项目启动时,Flowable 报错的问题 - */ - @Bean(name = "applicationTaskExecutor") - @ConditionalOnMissingBean(name = "applicationTaskExecutor") - public AsyncListenableTaskExecutor taskExecutor() { - ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); - executor.setCorePoolSize(8); - executor.setMaxPoolSize(8); - executor.setQueueCapacity(100); - executor.setThreadNamePrefix("flowable-task-Executor-"); - executor.setAwaitTerminationSeconds(30); - executor.setWaitForTasksToCompleteOnShutdown(true); - executor.setAllowCoreThreadTimeOut(true); - executor.initialize(); - return executor; - } - - /** - * BPM 模块的 ProcessEngineConfigurationConfigurer 实现类: - * - * 1. 设置各种监听器 - * 2. 设置自定义的 ActivityBehaviorFactory 实现 - */ - @Bean - public EngineConfigurationConfigurer bpmProcessEngineConfigurationConfigurer( - ObjectProvider listeners, - ObjectProvider customFlowableFunctionDelegates, - BpmActivityBehaviorFactory bpmActivityBehaviorFactory) { - return configuration -> { - // 注册监听器,例如说 BpmActivityEventListener - configuration.setEventListeners(ListUtil.toList(listeners.iterator())); - // 设置 ActivityBehaviorFactory 实现类,用于流程任务的审核人的自定义 - configuration.setActivityBehaviorFactory(bpmActivityBehaviorFactory); - // 设置自定义的函数 - configuration.setCustomFlowableFunctionDelegates(ListUtil.toList(customFlowableFunctionDelegates.stream().iterator())); - }; - } - - @Bean - public EngineConfigurationConfigurer dmProcessEngineConfigurationConfigurer(DataSource dataSource) { - return configuration -> { - try { - configureDmCompatibility(configuration, dataSource); - } catch (SQLException ex) { - log.warn("Failed to inspect datasource for DM compatibility; Flowable will keep default settings", ex); - } - }; - } - - private void configureDmCompatibility(SpringProcessEngineConfiguration configuration, DataSource dataSource) throws SQLException { - Connection connection = null; - try { - connection = DataSourceUtils.getConnection(dataSource); - DatabaseMetaData metaData = connection.getMetaData(); - String productName = metaData.getDatabaseProductName(); - String jdbcUrl = metaData.getURL(); - boolean dmProduct = productName != null && productName.toLowerCase().contains("dm"); - boolean dmUrl = jdbcUrl != null && jdbcUrl.toLowerCase().startsWith("jdbc:dm"); - if (!dmProduct && !dmUrl) { - return; - } - log.info("Detected DM database (product='{}'); enabling Flowable Oracle compatibility with automatic schema updates", productName); - configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); - configuration.setDatabaseType("oracle"); - } finally { - DataSourceUtils.releaseConnection(connection, dataSource); - } - } - - // =========== 审批人相关的 Bean ========== - - @Bean - public BpmActivityBehaviorFactory bpmActivityBehaviorFactory(BpmTaskCandidateInvoker bpmTaskCandidateInvoker) { - BpmActivityBehaviorFactory bpmActivityBehaviorFactory = new BpmActivityBehaviorFactory(); - bpmActivityBehaviorFactory.setTaskCandidateInvoker(bpmTaskCandidateInvoker); - return bpmActivityBehaviorFactory; - } - - @Bean - @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") // adminUserApi 可以注入成功 - public BpmTaskCandidateInvoker bpmTaskCandidateInvoker(List strategyList, - AdminUserApi adminUserApi) { - return new BpmTaskCandidateInvoker(strategyList, adminUserApi); - } - - // =========== 自己拓展的 Bean ========== - - @Bean - public BpmProcessInstanceEventPublisher processInstanceEventPublisher(ApplicationEventPublisher publisher) { - return new BpmProcessInstanceEventPublisher(publisher); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java deleted file mode 100644 index ec9cd8f..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmActivityBehaviorFactory.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.behavior; - -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import lombok.Setter; -import org.flowable.bpmn.model.Activity; -import org.flowable.bpmn.model.UserTask; -import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; -import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; -import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; -import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; -import org.flowable.engine.impl.bpmn.parser.factory.DefaultActivityBehaviorFactory; - -/** - * 自定义的 ActivityBehaviorFactory 实现类,目的如下: - * 1. 自定义 {@link #createUserTaskActivityBehavior(UserTask)}:实现自定义的流程任务的 assignee 负责人的分配 - * - * @author ZT - */ -@Setter -public class BpmActivityBehaviorFactory extends DefaultActivityBehaviorFactory { - - private BpmTaskCandidateInvoker taskCandidateInvoker; - - @Override - public UserTaskActivityBehavior createUserTaskActivityBehavior(UserTask userTask) { - return new BpmUserTaskActivityBehavior(userTask) - .setTaskCandidateInvoker(taskCandidateInvoker); - } - - @Override - public ParallelMultiInstanceBehavior createParallelMultiInstanceBehavior(Activity activity, - AbstractBpmnActivityBehavior behavior) { - return new BpmParallelMultiInstanceBehavior(activity, behavior) - .setTaskCandidateInvoker(taskCandidateInvoker); - } - - @Override - public SequentialMultiInstanceBehavior createSequentialMultiInstanceBehavior(Activity activity, - AbstractBpmnActivityBehavior behavior) { - return new BpmSequentialMultiInstanceBehavior(activity, behavior) - .setTaskCandidateInvoker(taskCandidateInvoker); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java deleted file mode 100644 index 2df2ce7..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmParallelMultiInstanceBehavior.java +++ /dev/null @@ -1,91 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.behavior; - -import cn.hutool.core.collection.CollUtil; -import com.zt.plat.framework.common.util.collection.SetUtils; -import com.zt.plat.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import lombok.Setter; -import org.flowable.bpmn.model.Activity; -import org.flowable.bpmn.model.CallActivity; -import org.flowable.bpmn.model.FlowElement; -import org.flowable.bpmn.model.UserTask; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; -import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; - -import java.util.List; -import java.util.Set; - -/** - * 自定义的【并行】的【多个】流程任务的 assignee 负责人的分配 - * 第一步,基于分配规则,计算出分配任务的【多个】候选人们。 - * 第二步,将【多个】任务候选人们,设置到 DelegateExecution 的 collectionVariable 变量中,以便 BpmUserTaskActivityBehavior 使用它 - * - * @author kemengkai - * @since 2022-04-21 16:57 - */ -@Setter -public class BpmParallelMultiInstanceBehavior extends ParallelMultiInstanceBehavior { - - private BpmTaskCandidateInvoker taskCandidateInvoker; - - public BpmParallelMultiInstanceBehavior(Activity activity, - AbstractBpmnActivityBehavior innerActivityBehavior) { - super(activity, innerActivityBehavior); - } - - /** - * 重写该方法,主要实现两个功能: - * 1. 忽略原有的 collectionVariable、collectionElementVariable 表达式,而是采用自己定义的 - * 2. 获得任务的处理人,并设置到 collectionVariable 中,用于 BpmUserTaskActivityBehavior 从中可以获取任务的处理人 - * - * 注意,多个任务实例,每个任务实例对应一个处理人,所以返回的数量就是任务处理人的数量 - * - * @param execution 执行任务 - * @return 数量 - */ - @Override - protected int resolveNrOfInstances(DelegateExecution execution) { - // 情况一:UserTask 节点 - if (execution.getCurrentFlowElement() instanceof UserTask) { - // 第一步,设置 collectionVariable 和 CollectionVariable - // 从 execution.getVariable() 读取所有任务处理人的 key - super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 - super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); - // 从 execution.getVariable() 读取当前所有任务处理的人的 key - super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); - - // 第二步,获取任务的所有处理人 - @SuppressWarnings("unchecked") - Set assigneeUserIds = (Set) execution.getVariable(super.collectionVariable, Set.class); - if (assigneeUserIds == null) { - assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution); - if (CollUtil.isEmpty(assigneeUserIds)) { - // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过! - // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务 - // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时 - assigneeUserIds = SetUtils.asSet((Long) null); - } - execution.setVariableLocal(super.collectionVariable, assigneeUserIds); - } - return assigneeUserIds.size(); - } - - // 情况二:CallActivity 节点 - if (execution.getCurrentFlowElement() instanceof CallActivity) { - FlowElement flowElement = execution.getCurrentFlowElement(); - Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) { - return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); - } - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { - return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); - } - } - - return super.resolveNrOfInstances(execution); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java deleted file mode 100644 index 2f7f76d..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmSequentialMultiInstanceBehavior.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.behavior; - -import cn.hutool.core.collection.CollUtil; -import com.zt.plat.framework.common.util.collection.SetUtils; -import com.zt.plat.module.bpm.enums.definition.BpmChildProcessMultiInstanceSourceTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import lombok.Setter; -import org.flowable.bpmn.model.Activity; -import org.flowable.bpmn.model.CallActivity; -import org.flowable.bpmn.model.FlowElement; -import org.flowable.bpmn.model.UserTask; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.impl.bpmn.behavior.AbstractBpmnActivityBehavior; -import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; -import org.flowable.engine.impl.persistence.entity.ExecutionEntity; - -import java.util.List; -import java.util.Set; - -/** - * 自定义的【串行】的【多个】流程任务的 assignee 负责人的分配 - * - * 本质上,实现和 {@link BpmParallelMultiInstanceBehavior} 一样,只是继承的类不一样 - * - * @author ZT - */ -@Setter -public class BpmSequentialMultiInstanceBehavior extends SequentialMultiInstanceBehavior { - - private BpmTaskCandidateInvoker taskCandidateInvoker; - - public BpmSequentialMultiInstanceBehavior(Activity activity, AbstractBpmnActivityBehavior innerActivityBehavior) { - super(activity, innerActivityBehavior); - } - - /** - * 逻辑和 {@link BpmParallelMultiInstanceBehavior#resolveNrOfInstances(DelegateExecution)} 类似 - * - * 差异的点:是在【第二步】的时候,需要返回 LinkedHashSet 集合!因为它需要有序! - */ - @Override - protected int resolveNrOfInstances(DelegateExecution execution) { - // 情况一:UserTask 节点 - if (execution.getCurrentFlowElement() instanceof UserTask) { - // 第一步,设置 collectionVariable 和 CollectionVariable - // 从 execution.getVariable() 读取所有任务处理人的 key - super.collectionExpression = null; // collectionExpression 和 collectionVariable 是互斥的 - super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); - // 从 execution.getVariable() 读取当前所有任务处理的人的 key - super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); - - // 第二步,获取任务的所有处理人 - // 不使用 execution.getVariable 原因:目前依次审批任务回退后 collectionVariable 变量没有清理, 如果重新进入该任务不会重新分配审批人 - @SuppressWarnings("unchecked") - Set assigneeUserIds = (Set) execution.getVariableLocal(super.collectionVariable, Set.class); - if (assigneeUserIds == null) { - assigneeUserIds = taskCandidateInvoker.calculateUsersByTask(execution); - if (CollUtil.isEmpty(assigneeUserIds)) { - // 特殊:如果没有处理人的情况下,至少有一个 null 空元素,避免自动通过! - // 这样,保证在 BpmUserTaskActivityBehavior 至少创建出一个 Task 任务 - // 用途:1)审批人为空时;2)审批类型为自动通过、自动拒绝时 - assigneeUserIds = SetUtils.asSet((Long) null); - } - execution.setVariableLocal(super.collectionVariable, assigneeUserIds); - } - return assigneeUserIds.size(); - } - - // 情况二:CallActivity 节点 - if (execution.getCurrentFlowElement() instanceof CallActivity) { - FlowElement flowElement = execution.getCurrentFlowElement(); - Integer sourceType = BpmnModelUtils.parseMultiInstanceSourceType(flowElement); - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType())) { - return execution.getVariable(super.collectionExpression.getExpressionText(), Integer.class); - } - if (sourceType.equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { - return execution.getVariable(super.collectionExpression.getExpressionText(), List.class).size(); - } - } - - return super.resolveNrOfInstances(execution); - } - - @Override - protected void executeOriginalBehavior(DelegateExecution execution, ExecutionEntity multiInstanceRootExecution, int loopCounter) { - // 参见 https://gitee.com/zhijiantianya/zt-cloud/issues/IC239F - super.collectionExpression = null; - super.collectionVariable = FlowableUtils.formatExecutionCollectionVariable(execution.getCurrentActivityId()); - super.collectionElementVariable = FlowableUtils.formatExecutionCollectionElementVariable(execution.getCurrentActivityId()); - super.executeOriginalBehavior(execution, multiInstanceRootExecution, loopCounter); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java deleted file mode 100644 index 8d29c54..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/behavior/BpmUserTaskActivityBehavior.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.behavior; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.RandomUtil; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.model.UserTask; -import org.flowable.common.engine.impl.el.ExpressionManager; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.impl.bpmn.behavior.UserTaskActivityBehavior; -import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; -import org.flowable.engine.impl.persistence.entity.ProcessDefinitionEntity; -import org.flowable.engine.impl.util.CommandContextUtil; -import org.flowable.engine.impl.util.TaskHelper; -import org.flowable.engine.interceptor.CreateUserTaskBeforeContext; -import org.flowable.task.service.TaskService; -import org.flowable.task.service.impl.persistence.entity.TaskEntity; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; -import java.util.Set; - -/** - * 自定义的【单个】流程任务的 assignee 负责人的分配 - * 第一步,基于分配规则,计算出分配任务的【单个】候选人。如果找不到,则直接报业务异常,不继续执行后续的流程; - * 第二步,随机选择一个候选人,则选择作为 assignee 负责人。 - * - * @author ZT - */ -@Slf4j -public class BpmUserTaskActivityBehavior extends UserTaskActivityBehavior { - - @Setter - private BpmTaskCandidateInvoker taskCandidateInvoker; - - public BpmUserTaskActivityBehavior(UserTask userTask) { - super(userTask); - } - - @Override - @Transactional(rollbackFor = Exception.class) - protected void handleAssignments(TaskService taskService, String assignee, String owner, - List candidateUsers, List candidateGroups, TaskEntity task, ExpressionManager expressionManager, - DelegateExecution execution, ProcessEngineConfigurationImpl processEngineConfiguration) { - // 第一步,获得任务的候选用户 - Long assigneeUserId = calculateTaskCandidateUsers(execution); - // 第二步,设置作为负责人 - if (assigneeUserId != null) { - TaskHelper.changeTaskAssignee(task, String.valueOf(assigneeUserId)); - } - } - - private Long calculateTaskCandidateUsers(DelegateExecution execution) { - // 情况一,如果是多实例的任务,例如说会签、或签等情况,则从 Variable 中获取。 - // 顺序审批可见 BpmSequentialMultiInstanceBehavior,并发审批可见 BpmSequentialMultiInstanceBehavior - if (super.multiInstanceActivityBehavior != null) { - return execution.getVariable(super.multiInstanceActivityBehavior.getCollectionElementVariable(), Long.class); - } - - // 情况二,如果非多实例的任务,则计算任务处理人 - // 第一步,先计算可处理该任务的处理人们 - Set candidateUserIds = taskCandidateInvoker.calculateUsersByTask(execution); - if (CollUtil.isEmpty(candidateUserIds)) { - return null; - } - // 第二步,后随机选择一个任务的处理人 - // 疑问:为什么一定要选择一个任务处理人? - // 解答:项目对 bpm 的任务是责任到人,所以每个任务有且仅有一个处理人。 - // 如果希望一个任务可以同时被多个人处理,可以考虑使用 BpmParallelMultiInstanceBehavior 实现的会签 or 或签。 - int index = RandomUtil.randomInt(candidateUserIds.size()); - return CollUtil.get(candidateUserIds, index); - } - - @Override - protected void handleCategory(CreateUserTaskBeforeContext beforeContext, ExpressionManager expressionManager, - TaskEntity task, DelegateExecution execution) { - ProcessDefinitionEntity processDefinitionEntity = CommandContextUtil.getProcessDefinitionEntityManager().findById(execution.getProcessDefinitionId()); - if (processDefinitionEntity == null) { - log.warn("[handleCategory][任务编号({}) 找不到流程定义({})]", task.getId(), execution.getProcessDefinitionId()); - return; - } - task.setCategory(processDefinitionEntity.getCategory()); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java deleted file mode 100644 index cdfee97..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvoker.java +++ /dev/null @@ -1,207 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.spring.SpringUtil; -import com.google.common.annotations.VisibleForTesting; -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.util.object.ObjectUtils; -import com.zt.plat.framework.datapermission.core.annotation.DataPermission; -import com.zt.plat.module.bpm.enums.definition.BpmUserTaskApproveTypeEnum; -import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.CallActivity; -import org.flowable.bpmn.model.FlowElement; -import org.flowable.bpmn.model.UserTask; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; - -import java.util.*; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG; - -/** - * {@link BpmTaskCandidateStrategy} 的调用者,用于调用对应的策略,实现任务的候选人的计算 - * - * @author ZT - */ -@Slf4j -public class BpmTaskCandidateInvoker { - - private final Map strategyMap = new HashMap<>(); - - private final AdminUserApi adminUserApi; - - public BpmTaskCandidateInvoker(List strategyList, - AdminUserApi adminUserApi) { - strategyList.forEach(strategy -> { - BpmTaskCandidateStrategy oldStrategy = strategyMap.put(strategy.getStrategy(), strategy); - Assert.isNull(oldStrategy, "策略(%s) 重复", strategy.getStrategy()); - }); - this.adminUserApi = adminUserApi; - } - - /** - * 校验流程模型的任务分配规则全部都配置了 - * 目的:如果有规则未配置,会导致流程任务找不到负责人,进而流程无法进行下去! - * - * @param bpmnBytes BPMN XML - */ - public void validateBpmnConfig(byte[] bpmnBytes) { - BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); - assert bpmnModel != null; - List userTaskList = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); - // 遍历所有的 UserTask,校验审批人配置 - userTaskList.forEach(userTask -> { - // 1.1 非人工审批,无需校验审批人配置 - Integer approveType = BpmnModelUtils.parseApproveType(userTask); - if (ObjectUtils.equalsAny(approveType, - BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), - BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { - return; - } - // 1.2 非空校验 - Integer strategy = BpmnModelUtils.parseCandidateStrategy(userTask); - String param = BpmnModelUtils.parseCandidateParam(userTask); - if (strategy == null) { - throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName()); - } - BpmTaskCandidateStrategy candidateStrategy = getCandidateStrategy(strategy); - if (candidateStrategy.isParamRequired() && StrUtil.isBlank(param)) { - throw exception(MODEL_DEPLOY_FAIL_TASK_CANDIDATE_NOT_CONFIG, userTask.getName()); - } - // 2. 具体策略校验 - getCandidateStrategy(strategy).validateParam(param); - }); - } - - /** - * 计算任务的候选人 - * - * @param execution 执行任务 - * @return 用户编号集合 - */ - @DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人 - public Set calculateUsersByTask(DelegateExecution execution) { - // 注意:解决极端情况下,Flowable 异步调用,导致租户 id 丢失的情况 - // 例如说,SIMPLE 延迟器在 trigger 的时候!!! - return FlowableUtils.execute(execution.getTenantId(), () -> { - // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 - FlowElement flowElement = execution.getCurrentFlowElement(); - Integer approveType = BpmnModelUtils.parseApproveType(flowElement); - if (ObjectUtils.equalsAny(approveType, - BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), - BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { - return new HashSet<>(); - } - - // 1.1 计算任务的候选人 - Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); - String param = BpmnModelUtils.parseCandidateParam(flowElement); - Set userIds = getCandidateStrategy(strategy).calculateUsersByTask(execution, param); - // 1.2 移除被禁用的用户 - removeDisableUsers(userIds); - - // 2. 候选人为空时,根据“审批人为空”的配置补充 - if (CollUtil.isEmpty(userIds)) { - userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy()) - .calculateUsersByTask(execution, param); - // ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!! - } - - // 3. 移除发起人的用户 - ProcessInstance processInstance = SpringUtil.getBean(BpmProcessInstanceService.class) - .getProcessInstance(execution.getProcessInstanceId()); - Assert.notNull(processInstance, "流程实例({}) 不存在", execution.getProcessInstanceId()); - removeStartUserIfSkip(userIds, flowElement, Long.valueOf(processInstance.getStartUserId())); - return userIds; - }); - } - - public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, - Long startUserId, String processDefinitionId, Map processVariables) { - // 如果是 CallActivity 子流程,不进行计算候选人 - FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); - if (flowElement instanceof CallActivity) { - return new HashSet<>(); - } - // 审批类型非人工审核时,不进行计算候选人。原因是:后续会自动通过、不通过 - Integer approveType = BpmnModelUtils.parseApproveType(flowElement); - if (ObjectUtils.equalsAny(approveType, - BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType(), - BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { - return new HashSet<>(); - } - - // 1.1 计算任务的候选人 - Integer strategy = BpmnModelUtils.parseCandidateStrategy(flowElement); - String param = BpmnModelUtils.parseCandidateParam(flowElement); - Set userIds = getCandidateStrategy(strategy).calculateUsersByActivity(bpmnModel, activityId, param, - startUserId, processDefinitionId, processVariables); - // 1.2 移除被禁用的用户 - removeDisableUsers(userIds); - - // 2. 候选人为空时,根据“审批人为空”的配置补充 - if (CollUtil.isEmpty(userIds)) { - userIds = getCandidateStrategy(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY.getStrategy()) - .calculateUsersByActivity(bpmnModel, activityId, param, startUserId, processDefinitionId, processVariables); - // ASSIGN_EMPTY 策略,不需要移除被禁用的用户。原因是,再移除,可能会出现更没审批人了!!! - } - - // 3. 移除发起人的用户 - removeStartUserIfSkip(userIds, flowElement, startUserId); - return userIds; - } - - @VisibleForTesting - void removeDisableUsers(Set assigneeUserIds) { - if (CollUtil.isEmpty(assigneeUserIds)) { - return; - } - Map userMap = adminUserApi.getUserMap(assigneeUserIds); - assigneeUserIds.removeIf(id -> { - AdminUserRespDTO user = userMap.get(id); - return user == null || CommonStatusEnum.isDisable(user.getStatus()); - }); - } - - /** - * 如果“审批人与发起人相同时”,配置了 SKIP 跳过,则移除发起人 - * - * 注意:如果只有一个候选人,则不处理,避免无法审批 - * - * @param assigneeUserIds 当前分配的候选人 - * @param flowElement 当前节点 - * @param startUserId 发起人 - */ - @VisibleForTesting - void removeStartUserIfSkip(Set assigneeUserIds, FlowElement flowElement, Long startUserId) { - if (CollUtil.size(assigneeUserIds) <= 1) { - return; - } - Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(flowElement); - if (ObjectUtil.notEqual(assignStartUserHandlerType, BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { - return; - } - assigneeUserIds.remove(startUserId); - } - - private BpmTaskCandidateStrategy getCandidateStrategy(Integer strategy) { - BpmTaskCandidateStrategyEnum strategyEnum = BpmTaskCandidateStrategyEnum.valueOf(strategy); - Assert.notNull(strategyEnum, "策略(%s) 不存在", strategy); - BpmTaskCandidateStrategy strategyObj = strategyMap.get(strategyEnum); - Assert.notNull(strategyObj, "策略(%s) 不存在", strategy); - return strategyObj; - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java deleted file mode 100644 index 2d916be..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateStrategy.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate; - -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.engine.delegate.DelegateExecution; - -import java.util.Map; -import java.util.Set; - -/** - * BPM 任务的候选人的策略接口 - *

- * 例如说:分配审批人 - * - * @author ZT - */ -public interface BpmTaskCandidateStrategy { - - /** - * 对应策略 - * - * @return 策略 - */ - BpmTaskCandidateStrategyEnum getStrategy(); - - /** - * 校验参数 - * - * @param param 参数 - */ - void validateParam(String param); - - /** - * 是否一定要输入参数 - * - * @return 是否 - */ - default boolean isParamRequired() { - return true; - } - - /** - * 基于候选人参数,获得任务的候选用户们 - * - * 注意:实现 calculateUsers 系列方法时,有两种选择: - * 1. 只重写 calculateUsers 默认方法 - * 2. 都重写 calculateUsersByTask 和 calculateUsersByActivity 两个方法 - * - * @param param 执行任务 - * @return 用户编号集合 - */ - default Set calculateUsers(String param) { - throw new UnsupportedOperationException("该分配方法未实现,请检查!"); - } - - /** - * 基于【执行任务】,获得任务的候选用户们 - * - * @param execution 执行任务 - * @return 用户编号集合 - */ - default Set calculateUsersByTask(DelegateExecution execution, String param) { - return calculateUsers(param); - } - - /** - * 基于【流程活动】,获得任务的候选用户们 - *

- * 目的:用于获取未执行节点的候选用户们 - * - * @param bpmnModel 流程图 - * @param activityId 活动 ID (对应 Bpmn XML id) - * @param param 节点的参数 - * @param startUserId 流程发起人编号 - * @param processDefinitionId 流程定义编号 - * @param processVariables 流程变量 - * @return 用户编号集合 - */ - @SuppressWarnings("unused") - default Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, - Long startUserId, String processDefinitionId, Map processVariables) { - return calculateUsers(param); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java deleted file mode 100644 index fd34457..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpression.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.expression; - -import com.zt.plat.framework.business.core.util.DeptUtil; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import jakarta.annotation.Resource; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.stereotype.Component; -import org.springframework.util.Assert; - -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; -import static java.util.Collections.emptySet; - -/** - * 分配给发起人的 Leader 审批的 Expression 流程表达式 - * 目前 Leader 的定义是,发起人所在部门的 Leader - * - * @author ZT - */ -@Component -public class BpmTaskAssignLeaderExpression { - - @Resource - private AdminUserApi adminUserApi; - @Resource - private DeptApi deptApi; - - @Resource - private BpmProcessInstanceService processInstanceService; - - /** - * 计算审批的候选人 - * - * @param execution 流程执行实体 - * @param level 指定级别 - * @return 指定级别的领导 - */ - public Set calculateUsers(DelegateExecution execution, int level) { - Assert.isTrue(level > 0, "level 必须大于 0"); - // 获得发起人 - ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); - Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); - // 获得对应 leve 的部门 - DeptRespDTO dept = null; - for (int i = 0; i < level; i++) { - // 获得 level 对应的部门 - if (dept == null) { - dept = getStartUserDept(startUserId); - if (dept == null) { // 找不到发起人的部门,所以无法使用该规则 - return emptySet(); - } - } else { - DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()).getCheckedData(); - if (parentDept == null) { // 找不到父级部门,所以只好结束寻找。原因是:例如说,级别比较高的人,所在部门层级比较少 - break; - } - dept = parentDept; - } - } - return dept.getLeaderUserId() != null ? asSet(dept.getLeaderUserId()) : emptySet(); - } - - private DeptRespDTO getStartUserDept(Long startUserId) { - AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData(); - Long deptId = DeptUtil.getDeptId(startUser); - if (deptId == 0L) { // 找不到部门,所以无法使用该规则 - return null; - } - return deptApi.getDept(deptId).getCheckedData(); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java deleted file mode 100644 index bee41e7..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignStartUserExpression.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.expression; - -import com.zt.plat.framework.common.util.collection.SetUtils; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import jakarta.annotation.Resource; -import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.stereotype.Component; - -import java.util.Set; - -/** - * 分配给发起人审批的 Expression 流程表达式 - * - * @author ZT - */ -@Component -public class BpmTaskAssignStartUserExpression { - - @Resource - private BpmProcessInstanceService processInstanceService; - - /** - * 计算审批的候选人 - * - * @param execution 流程执行实体 - * @return 发起人 - */ - public Set calculateUsers(ExecutionEntityImpl execution) { - ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); - Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); - return SetUtils.asSet(startUserId); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java deleted file mode 100644 index 9c0a760..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/AbstractBpmTaskCandidateDeptLeaderStrategy.java +++ /dev/null @@ -1,95 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import com.zt.plat.framework.business.core.util.DeptUtil; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import jakarta.annotation.Resource; - -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Set; - -/** - * 部门的负责人 {@link BpmTaskCandidateStrategy} 抽象类 - * - * @author jason - */ -public abstract class AbstractBpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy { - - @Resource - protected DeptApi deptApi; - @Resource - protected AdminUserApi adminUserApi; - - /** - * 获得指定层级的部门负责人,只有第 level 的负责人 - * - * @param dept 指定部门 - * @param level 第几级 - * @return 部门负责人的编号 - */ - protected Long getAssignLevelDeptLeaderId(DeptRespDTO dept, Integer level) { - Assert.isTrue(level > 0, "level 必须大于 0"); - if (dept == null) { - return null; - } - DeptRespDTO currentDept = dept; - for (int i = 1; i < level; i++) { - DeptRespDTO parentDept = deptApi.getDept(currentDept.getParentId()).getCheckedData(); - if (parentDept == null) { // 找不到父级部门,到了最高级。返回最高级的部门负责人 - break; - } - currentDept = parentDept; - } - return currentDept.getLeaderUserId(); - } - - /** - * 获得连续层级的部门负责人,包含 [1, level] 的负责人 - * - * @param deptIds 指定部门编号数组 - * @param level 最大层级 - * @return 连续部门负责人 Id - */ - protected Set getMultiLevelDeptLeaderIds(List deptIds, Integer level) { - Assert.isTrue(level > 0, "level 必须大于 0"); - if (CollUtil.isEmpty(deptIds)) { - return new HashSet<>(); - } - Set deptLeaderIds = new LinkedHashSet<>(); // 保证有序 - for (Long deptId : deptIds) { - DeptRespDTO dept = deptApi.getDept(deptId).getCheckedData(); - for (int i = 0; i < level; i++) { - if (dept.getLeaderUserId() != null) { - deptLeaderIds.add(dept.getLeaderUserId()); - } - DeptRespDTO parentDept = deptApi.getDept(dept.getParentId()).getCheckedData(); - if (parentDept == null) { // 找不到父级部门. 已经到了最高层级了 - break; - } - dept = parentDept; - } - } - return deptLeaderIds; - } - - /** - * 获取发起人的部门 - * - * @param startUserId 发起人 Id - */ - protected DeptRespDTO getStartUserDept(Long startUserId) { - AdminUserRespDTO startUser = adminUserApi.getUser(startUserId).getCheckedData(); - if (CollUtil.isEmpty(startUser.getDeptIds())) { // 找不到部门 - return null; - } - return deptApi.getDept(DeptUtil.getDeptId(startUser)).getCheckedData(); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java deleted file mode 100644 index e56d6b9..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateApproveUserSelectStrategy.java +++ /dev/null @@ -1,78 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import com.google.common.collect.Sets; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import jakarta.annotation.Resource; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; - -/** - * 审批人自选 {@link BpmTaskCandidateUserStrategy} 实现类 - * 审批人在审批时选择下一个节点的审批人 - * - * @author smallNorthLee - */ -@Component -public class BpmTaskCandidateApproveUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { - - @Resource - @Lazy // 延迟加载,避免循环依赖 - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT; - } - - @Override - public void validateParam(String param) {} - - @Override - public boolean isParamRequired() { - return false; - } - - @Override - public LinkedHashSet calculateUsersByTask(DelegateExecution execution, String param) { - ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); - Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId()); - Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance); - Assert.notNull(approveUserSelectAssignees, "流程实例({}) 的下一个执行节点审批人不能为空", - execution.getProcessInstanceId()); - if (approveUserSelectAssignees == null) { - return Sets.newLinkedHashSet(); - } - // 获得审批人 - List assignees = approveUserSelectAssignees.get(execution.getCurrentActivityId()); - return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); - } - - @Override - public LinkedHashSet calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, - Long startUserId, String processDefinitionId, Map processVariables) { - if (processVariables == null) { - return Sets.newLinkedHashSet(); - } - // 流程预测时会使用,允许审批人为空,如果为空前端会弹出提示选择下一个节点审批人,避免流程无法进行,审批时会真正校验节点是否配置审批人 - Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processVariables); - if (approveUserSelectAssignees == null) { - return Sets.newLinkedHashSet(); - } - // 获得审批人 - List assignees = approveUserSelectAssignees.get(activityId); - return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java deleted file mode 100644 index f851e66..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategy.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import cn.hutool.core.lang.Assert; -import com.zt.plat.framework.common.util.string.StrUtils; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.Set; - -/** - * 连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author jason - */ -@Component -public class BpmTaskCandidateDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.MULTI_DEPT_LEADER_MULTI; - } - - @Override - public void validateParam(String param) { - // 参数格式: | 分隔:1)左边为部门(多个部门用 , 分隔)。2)右边为部门层级 - String[] params = param.split("\\|"); - Assert.isTrue(params.length == 2, "参数格式不匹配"); - List deptIds = StrUtils.splitToLong(params[0], ","); - int level = Integer.parseInt(params[1]); - // 校验部门存在 - deptApi.validateDeptList(deptIds).checkError(); - Assert.isTrue(level > 0, "部门层级必须大于 0"); - } - - @Override - public Set calculateUsers(String param) { - String[] params = param.split("\\|"); - List deptIds = StrUtils.splitToLong(params[0], ","); - int level = Integer.parseInt(params[1]); - return super.getMultiLevelDeptLeaderIds(deptIds, level); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java deleted file mode 100644 index ad1c93b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategy.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import com.zt.plat.framework.common.util.string.StrUtils; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; - -/** - * 部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author kyle - */ -@Component -public class BpmTaskCandidateDeptLeaderStrategy implements BpmTaskCandidateStrategy { - - @Resource - private DeptApi deptApi; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.DEPT_LEADER; - } - - @Override - public void validateParam(String param) { - Set deptIds = StrUtils.splitToLongSet(param); - deptApi.validateDeptList(deptIds).checkError(); - } - - @Override - public Set calculateUsers(String param) { - Set deptIds = StrUtils.splitToLongSet(param); - List depts = deptApi.getDeptList(deptIds).getCheckedData(); - return convertSet(depts, DeptRespDTO::getLeaderUserId); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java deleted file mode 100644 index aa2c3f0..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategy.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import com.zt.plat.framework.common.util.string.StrUtils; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; - -/** - * 部门的成员 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author kyle - */ -@Component -public class BpmTaskCandidateDeptMemberStrategy implements BpmTaskCandidateStrategy { - - @Resource - private DeptApi deptApi; - @Resource - private AdminUserApi adminUserApi; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.DEPT_MEMBER; - } - - @Override - public void validateParam(String param) { - Set deptIds = StrUtils.splitToLongSet(param); - deptApi.validateDeptList(deptIds).checkError(); - } - - @Override - public Set calculateUsers(String param) { - Set deptIds = StrUtils.splitToLongSet(param); - List users = adminUserApi.getUserListByDeptIds(deptIds).getCheckedData(); - return convertSet(users, AdminUserRespDTO::getId); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java deleted file mode 100644 index 04e4963..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategy.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import cn.hutool.core.lang.Assert; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import jakarta.annotation.Resource; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import static cn.hutool.core.collection.ListUtil.toList; - -/** - * 发起人连续多级部门的负责人 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author jason - */ -@Component -public class BpmTaskCandidateStartUserDeptLeaderMultiStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { - - @Resource - @Lazy - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER_MULTI; - } - - @Override - public void validateParam(String param) { - int level = Integer.parseInt(param); // 参数是部门的层级 - Assert.isTrue(level > 0, "部门的层级必须大于 0"); - } - - @Override - public Set calculateUsersByTask(DelegateExecution execution, String param) { - int level = Integer.parseInt(param); // 参数是部门的层级 - // 获得流程发起人 - ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); - Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); - // 获取发起人的 multi 部门负责人 - DeptRespDTO dept = super.getStartUserDept(startUserId); - if (dept == null) { - return new HashSet<>(); - } - return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level); - } - - @Override - public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, - Long startUserId, String processDefinitionId, Map processVariables) { - int level = Integer.parseInt(param); // 参数是部门的层级 - DeptRespDTO dept = super.getStartUserDept(startUserId); - if (dept == null) { - return new HashSet<>(); - } - return super.getMultiLevelDeptLeaderIds(toList(dept.getId()), level); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java deleted file mode 100644 index 9670fa9..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategy.java +++ /dev/null @@ -1,71 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import cn.hutool.core.lang.Assert; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import jakarta.annotation.Resource; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; - -/** - * 发起人的部门负责人, 可以是上级部门负责人 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author jason - */ -@Component -public class BpmTaskCandidateStartUserDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { - - @Resource - @Lazy // 避免循环依赖 - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.START_USER_DEPT_LEADER; - } - - @Override - public void validateParam(String param) { - // 参数是部门的层级 - Assert.isTrue(Integer.parseInt(param) > 0, "部门的层级必须大于 0"); - } - - @Override - public Set calculateUsersByTask(DelegateExecution execution, String param) { - // 获得流程发起人 - ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); - Long startUserId = NumberUtils.parseLong(processInstance.getStartUserId()); - // 获取发起人的部门负责人 - return getStartUserDeptLeader(startUserId, param); - } - - @Override - public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, - Long startUserId, String processDefinitionId, Map processVariables) { - // 获取发起人的部门负责人 - return getStartUserDeptLeader(startUserId, param); - } - - private Set getStartUserDeptLeader(Long startUserId, String param) { - int level = Integer.parseInt(param); // 参数是部门的层级 - DeptRespDTO dept = super.getStartUserDept(startUserId); - if (dept == null) { - return new HashSet<>(); - } - Long deptLeaderId = super.getAssignLevelDeptLeaderId(dept, level); - return deptLeaderId != null ? asSet(deptLeaderId) : new HashSet<>(); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java deleted file mode 100644 index 6aab05c..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategy.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import com.google.common.collect.Sets; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import jakarta.annotation.Resource; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; - -/** - * 发起人自选 {@link BpmTaskCandidateUserStrategy} 实现类 - * - * @author ZT - */ -@Component -public class BpmTaskCandidateStartUserSelectStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { - - @Resource - @Lazy // 延迟加载,避免循环依赖 - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.START_USER_SELECT; - } - - @Override - public void validateParam(String param) {} - - @Override - public boolean isParamRequired() { - return false; - } - - @Override - public LinkedHashSet calculateUsersByTask(DelegateExecution execution, String param) { - ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); - Assert.notNull(processInstance, "流程实例({})不能为空", execution.getProcessInstanceId()); - Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance); - Assert.notNull(startUserSelectAssignees, "流程实例({}) 的发起人自选审批人不能为空", - execution.getProcessInstanceId()); - // 获得审批人 - List assignees = startUserSelectAssignees.get(execution.getCurrentActivityId()); - return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); - } - - @Override - public LinkedHashSet calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, - Long startUserId, String processDefinitionId, Map processVariables) { - if (processVariables == null) { - return Sets.newLinkedHashSet(); - } - Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processVariables); - if (startUserSelectAssignees == null) { - return Sets.newLinkedHashSet(); - } - // 获得审批人 - List assignees = startUserSelectAssignees.get(activityId); - return CollUtil.isNotEmpty(assignees) ? new LinkedHashSet<>(assignees) : Sets.newLinkedHashSet(); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java deleted file mode 100644 index 119b59d..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormDeptLeaderStrategy.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.form; - -import cn.hutool.core.convert.Convert; -import cn.hutool.core.lang.Assert; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept.AbstractBpmTaskCandidateDeptLeaderStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.engine.delegate.DelegateExecution; -import org.springframework.stereotype.Component; - -import java.util.Map; -import java.util.Set; - -/** - * 表单内部门负责人 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author jason - */ -@Component -public class BpmTaskCandidateFormDeptLeaderStrategy extends AbstractBpmTaskCandidateDeptLeaderStrategy { - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.FORM_DEPT_LEADER; - } - - @Override - public void validateParam(String param) { - // 参数格式: | 分隔:1)左边为表单内部门字段。2)右边为部门层级 - String[] params = param.split("\\|"); - Assert.isTrue(params.length == 2, "参数格式不匹配"); - Assert.notEmpty(param, "表单内部门字段不能为空"); - int level = Integer.parseInt(params[1]); - Assert.isTrue(level > 0, "部门层级必须大于 0"); - } - - @Override - public Set calculateUsersByTask(DelegateExecution execution, String param) { - String[] params = param.split("\\|"); - Object result = execution.getVariable(params[0]); - int level = Integer.parseInt(params[1]); - return super.getMultiLevelDeptLeaderIds(Convert.toList(Long.class, result), level); - } - - @Override - public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, - String param, Long startUserId, String processDefinitionId, - Map processVariables) { - String[] params = param.split("\\|"); - Object result = processVariables == null ? null : processVariables.get(params[0]); - int level = Integer.parseInt(params[1]); - return super.getMultiLevelDeptLeaderIds(Convert.toList(Long.class, result), level); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java deleted file mode 100644 index 435717b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/form/BpmTaskCandidateFormUserStrategy.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.form; - -import cn.hutool.core.lang.Assert; -import com.zt.plat.framework.common.util.collection.CollectionUtils; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.engine.delegate.DelegateExecution; -import org.springframework.stereotype.Component; - -import java.util.Map; -import java.util.Set; - -/** - * 表单内用户字段 {@link BpmTaskCandidateUserStrategy} 实现类 - * - * @author jason - */ -@Component -public class BpmTaskCandidateFormUserStrategy implements BpmTaskCandidateStrategy { - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.FORM_USER; - } - - @Override - public void validateParam(String param) { - Assert.notEmpty(param, "表单内用户字段不能为空"); - } - - @Override - public Set calculateUsersByTask(DelegateExecution execution, String param) { - Object result = execution.getVariable(param); - return CollectionUtils.toLinkedHashSet(Long.class, result); - } - - @Override - public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, - String param, Long startUserId, String processDefinitionId, - Map processVariables) { - Object result = processVariables == null ? null : processVariables.get(param); - return CollectionUtils.toLinkedHashSet(Long.class, result); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java deleted file mode 100644 index 8c360bc..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategy.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; - -import cn.hutool.core.lang.Assert; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import jakarta.annotation.Resource; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.FlowElement; -import org.flowable.engine.delegate.DelegateExecution; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import java.util.HashSet; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * 审批人为空 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author kyle - */ -@Component -public class BpmTaskCandidateAssignEmptyStrategy implements BpmTaskCandidateStrategy { - - @Resource - @Lazy // 延迟加载,避免循环依赖 - private BpmProcessDefinitionService processDefinitionService; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY; - } - - @Override - public void validateParam(String param) { - } - - @Override - public Set calculateUsersByTask(DelegateExecution execution, String param) { - return getCandidateUsers(execution.getProcessDefinitionId(), execution.getCurrentFlowElement()); - } - - @Override - public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, - Long startUserId, String processDefinitionId, Map processVariables) { - FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); - return getCandidateUsers(processDefinitionId, flowElement); - } - - private Set getCandidateUsers(String processDefinitionId, FlowElement flowElement) { - // 情况一:指定人员审批 - Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(flowElement); - if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType())) { - return new HashSet<>(BpmnModelUtils.parseAssignEmptyHandlerUserIds(flowElement)); - } - - // 情况二:流程管理员 - if (Objects.equals(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType())) { - BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processDefinitionId); - Assert.notNull(processDefinition, "流程定义({})不存在", processDefinitionId); - return new HashSet<>(processDefinition.getManagerUserIds()); - } - - // 都不满足,还是返回空 - return new HashSet<>(); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java deleted file mode 100644 index aeae390..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategy.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; - -import com.google.common.collect.Sets; -import com.zt.plat.framework.common.util.collection.CollectionUtils; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.common.engine.api.FlowableException; -import org.flowable.engine.delegate.DelegateExecution; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * 流程表达式 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author ZT - */ -@Component -@Slf4j -public class BpmTaskCandidateExpressionStrategy implements BpmTaskCandidateStrategy { - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.EXPRESSION; - } - - @Override - public void validateParam(String param) { - // do nothing 因为它基本做不了校验 - } - - @Override - public Set calculateUsersByTask(DelegateExecution execution, String param) { - Object result = FlowableUtils.getExpressionValue(execution, param); - return CollectionUtils.toLinkedHashSet(Long.class, result); - } - - @Override - public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, - Long startUserId, String processDefinitionId, Map processVariables) { - Map variables = processVariables == null ? new HashMap<>() : processVariables; - try { - Object result = FlowableUtils.getExpressionValue(variables, param); - return CollectionUtils.toLinkedHashSet(Long.class, result); - } catch (FlowableException ex) { - // 预测未运行的节点时候,表达式如果包含 execution 或者不存在的流程变量会抛异常, - log.warn("[calculateUsersByActivity][表达式({}) 变量({}) 解析报错", param, variables, ex); - // 不能预测候选人,返回空列表, 避免流程无法进行 - return Sets.newHashSet(); - } - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java deleted file mode 100644 index 5bc1232..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategy.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; - -import com.zt.plat.framework.common.util.string.StrUtils; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.service.definition.BpmUserGroupService; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Component; - -import java.util.Collection; -import java.util.List; -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSetByFlatMap; - -/** - * 用户组 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author kyle - */ -@Component -public class BpmTaskCandidateGroupStrategy implements BpmTaskCandidateStrategy { - - @Resource - private BpmUserGroupService userGroupService; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.USER_GROUP; - } - - @Override - public void validateParam(String param) { - Set groupIds = StrUtils.splitToLongSet(param); - userGroupService.validUserGroups(groupIds); - } - - @Override - public Set calculateUsers(String param) { - Set groupIds = StrUtils.splitToLongSet(param); - List groups = userGroupService.getUserGroupList(groupIds); - return convertSetByFlatMap(groups, BpmUserGroupDO::getUserIds, Collection::stream); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java deleted file mode 100644 index 74edf8a..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategy.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; - -import com.zt.plat.framework.common.util.string.StrUtils; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.system.api.dept.PostApi; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertSet; - -/** - * 岗位 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author kyle - */ -@Component -public class BpmTaskCandidatePostStrategy implements BpmTaskCandidateStrategy { - - @Resource - private PostApi postApi; - @Resource - private AdminUserApi adminUserApi; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.POST; - } - - @Override - public void validateParam(String param) { - Set postIds = StrUtils.splitToLongSet(param); - postApi.validPostList(postIds); - } - - @Override - public Set calculateUsers(String param) { - Set postIds = StrUtils.splitToLongSet(param); - List users = adminUserApi.getUserListByPostIds(postIds).getCheckedData(); - return convertSet(users, AdminUserRespDTO::getId); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java deleted file mode 100644 index f8223d9..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategy.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; - -import com.zt.plat.framework.common.util.string.StrUtils; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.system.api.permission.PermissionApi; -import com.zt.plat.module.system.api.permission.RoleApi; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Component; - -import java.util.Set; - -/** - * 角色 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author kyle - */ -@Component -public class BpmTaskCandidateRoleStrategy implements BpmTaskCandidateStrategy { - - @Resource - private RoleApi roleApi; - @Resource - private PermissionApi permissionApi; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.ROLE; - } - - @Override - public void validateParam(String param) { - Set roleIds = StrUtils.splitToLongSet(param); - roleApi.validRoleList(roleIds); - } - - @Override - public Set calculateUsers(String param) { - Set roleIds = StrUtils.splitToLongSet(param); - return permissionApi.getUserRoleIdListByRoleIds(roleIds).getCheckedData(); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java deleted file mode 100644 index ed527fa..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategy.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; - -import com.zt.plat.framework.common.util.collection.SetUtils; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import jakarta.annotation.Resource; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import java.util.Map; -import java.util.Set; - -/** - * 发起人自己 {@link BpmTaskCandidateUserStrategy} 实现类 - *

- * 适合场景:用于需要发起人信息复核等场景 - * - * @author jason - */ -@Component -public class BpmTaskCandidateStartUserStrategy implements BpmTaskCandidateStrategy { - - @Resource - @Lazy // 延迟加载,避免循环依赖 - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.START_USER; - } - - @Override - public void validateParam(String param) { - } - - @Override - public boolean isParamRequired() { - return false; - } - - @Override - public Set calculateUsersByTask(DelegateExecution execution, String param) { - ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getProcessInstanceId()); - return SetUtils.asSet(Long.valueOf(processInstance.getStartUserId())); - } - - @Override - public Set calculateUsersByActivity(BpmnModel bpmnModel, String activityId, String param, - Long startUserId, String processDefinitionId, Map processVariables) { - return SetUtils.asSet(startUserId); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java deleted file mode 100644 index f6a64ee..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategy.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; - -import cn.hutool.core.text.StrPool; -import com.zt.plat.framework.common.util.string.StrUtils; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.system.api.user.AdminUserApi; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Component; - -import java.util.LinkedHashSet; - -/** - * 用户 {@link BpmTaskCandidateStrategy} 实现类 - * - * @author kyle - */ -@Component -public class BpmTaskCandidateUserStrategy implements BpmTaskCandidateStrategy { - - @Resource - private AdminUserApi adminUserApi; - - @Override - public BpmTaskCandidateStrategyEnum getStrategy() { - return BpmTaskCandidateStrategyEnum.USER; - } - - @Override - public void validateParam(String param) { - adminUserApi.validateUserList(StrUtils.splitToLongSet(param)).checkError(); - } - - @Override - public LinkedHashSet calculateUsers(String param) { - return new LinkedHashSet<>(StrUtils.splitToLong(param, StrPool.COMMA)); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java deleted file mode 100644 index c5db881..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/el/VariableConvertByTypeExpressionFunction.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.el; - -import org.flowable.common.engine.api.variable.VariableContainer; -import org.flowable.common.engine.impl.el.function.AbstractFlowableVariableExpressionFunction; -import org.springframework.stereotype.Component; - -/** - * 根据流程变量 variable 的类型,转换参数的值 - * - * 目前用于 ConditionNodeConvert 的 buildConditionExpression 方法中 - * - * @author jason - */ -@Component -public class VariableConvertByTypeExpressionFunction extends AbstractFlowableVariableExpressionFunction { - - public VariableConvertByTypeExpressionFunction() { - super("convertByType"); - } - - public static Object convertByType(VariableContainer variableContainer, String variableName, Object parmaValue) { - Object variable = variableContainer.getVariable(variableName); - if (variable != null && parmaValue != null) { - // 如果值不是字符串类型,流程变量的类型是字符串,把值转成字符串 - if (!(parmaValue instanceof String) && variable instanceof String ) { - return parmaValue.toString(); - } - } - return parmaValue; - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java deleted file mode 100644 index 9a8b399..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.enums; - -import cn.hutool.core.util.ArrayUtil; -import com.zt.plat.framework.common.core.ArrayValuable; -import lombok.AllArgsConstructor; -import lombok.Getter; - -import java.util.Arrays; - -/** - * BPM 任务的候选人策略枚举 - * - * 例如说:分配给指定人审批 - * - * @author ZT - */ -@Getter -@AllArgsConstructor -public enum BpmTaskCandidateStrategyEnum implements ArrayValuable { - - ROLE(10, "角色"), - DEPT_MEMBER(20, "部门的成员"), // 包括负责人 - DEPT_LEADER(21, "部门的负责人"), - MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"), - POST(22, "岗位"), - USER(30, "用户"), - APPROVE_USER_SELECT(34, "审批人自身"), // 当前审批人,可在审批时,选择下一个节点的审批人 - START_USER_SELECT(35, "发起人自选"), // 申请人自己,可在提交申请时,选择此节点的审批人 - START_USER(36, "发起人自己"), // 申请人自己, 一般紧挨开始节点,常用于发起人信息审核场景 - START_USER_DEPT_LEADER(37, "发起人部门负责人"), - START_USER_DEPT_LEADER_MULTI(38, "发起人连续多级部门的负责人"), - USER_GROUP(40, "用户组"), - FORM_USER(50, "表单内用户字段"), - FORM_DEPT_LEADER(51, "表单内部门负责人"), - EXPRESSION(60, "流程表达式"), // 表达式 ExpressionManager - ASSIGN_EMPTY(1, "审批人为空"), - ; - - public static final Integer[] ARRAYS = Arrays.stream(values()).map(BpmTaskCandidateStrategyEnum::getStrategy).toArray(Integer[]::new); - - /** - * 类型 - */ - private final Integer strategy; - /** - * 描述 - */ - private final String description; - - public static BpmTaskCandidateStrategyEnum valueOf(Integer strategy) { - return ArrayUtil.firstMatch(o -> o.getStrategy().equals(strategy), values()); - } - - @Override - public Integer[] array() { - return ARRAYS; - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java deleted file mode 100644 index de18134..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnModelConstants.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.enums; - -import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; - -/** - * BPMN XML 常量信息 - * - * @author ZT - */ -public interface BpmnModelConstants { - - String BPMN_FILE_SUFFIX = ".bpmn"; - - /** - * BPMN 中的命名空间 - */ - String NAMESPACE = "http://flowable.org/bpmn"; - - /** - * BPMN UserTask 的扩展属性,用于标记候选人策略 - */ - String USER_TASK_CANDIDATE_STRATEGY = "candidateStrategy"; - /** - * BPMN UserTask 的扩展属性,用于标记候选人参数 - */ - String USER_TASK_CANDIDATE_PARAM = "candidateParam"; - - /** - * BPMN ExtensionElement 的扩展属性,用于标记边界事件类型 - */ - String BOUNDARY_EVENT_TYPE = "boundaryEventType"; - - /** - * BPMN ExtensionElement 的扩展属性,用于标记用户任务超时执行动作 - */ - String USER_TASK_TIMEOUT_HANDLER_TYPE = "timeoutHandlerType"; - - /** - * BPMN ExtensionElement 的扩展属性,用于标记用户任务的审批人与发起人相同时,对应的处理类型 - */ - String USER_TASK_ASSIGN_START_USER_HANDLER_TYPE = "assignStartUserHandlerType"; - - /** - * BPMN ExtensionElement 的扩展属性,用于标记用户任务的空处理类型 - */ - String USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE = "assignEmptyHandlerType"; - /** - * BPMN ExtensionElement 的扩展属性,用于标记用户任务的空处理的指定用户编号数组 - */ - String USER_TASK_ASSIGN_USER_IDS = "assignEmptyUserIds"; - - /** - * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝处理类型 - */ - String USER_TASK_REJECT_HANDLER_TYPE = "rejectHandlerType"; - /** - * BPMN ExtensionElement 的扩展属性,用于标记用户任务拒绝后的退回的任务 Id - */ - String USER_TASK_REJECT_RETURN_TASK_ID = "rejectReturnTaskId"; - - /** - * BPMN UserTask 的扩展属性,用于标记用户任务的审批类型 - */ - String USER_TASK_APPROVE_TYPE = "approveType"; - - /** - * BPMN UserTask 的扩展属性,用于标记用户任务的审批方式 - */ - String USER_TASK_APPROVE_METHOD = "approveMethod"; - - /** - * BPMN Child Process 的扩展属性,用于标记多实例来源类型 - */ - String CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE = "childProcessMultiInstanceSourceType"; - - /** - * BPMN ExtensionElement 流程表单字段权限元素, 用于标记字段权限 - */ - String FORM_FIELD_PERMISSION_ELEMENT = "fieldsPermission"; - - /** - * BPMN ExtensionElement Attribute, 用于标记表单字段 - */ - String FORM_FIELD_PERMISSION_ELEMENT_FIELD_ATTRIBUTE = "field"; - /** - * BPMN ExtensionElement Attribute, 用于标记表单权限 - */ - String FORM_FIELD_PERMISSION_ELEMENT_PERMISSION_ATTRIBUTE = "permission"; - - /** - * BPMN ExtensionElement 操作按钮设置元素, 用于审批节点操作按钮设置 - */ - String BUTTON_SETTING_ELEMENT = "buttonsSetting"; - - /** - * BPMN ExtensionElement Attribute, 用于标记按钮编号 - */ - String BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE = "id"; - - /** - * BPMN ExtensionElement Attribute, 用于标记按钮显示名称 - */ - String BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE = "displayName"; - - /** - * BPMN ExtensionElement Attribute, 用于标记按钮是否启用 - */ - String BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE = "enable"; - - /** - * BPMN ExtensionElement 的扩展属性,用于标记触发器的类型 - */ - String TRIGGER_TYPE = "triggerType"; - /** - * BPMN ExtensionElement 的扩展属性,用于标记触发器参数 - */ - String TRIGGER_PARAM = "triggerParam"; - - /** - * BPMN Start Event Node Id - */ - String START_EVENT_NODE_ID = "StartEvent"; - - /** - * 发起人节点 ID - */ - String START_USER_NODE_ID = "StartUserNode"; - - /** - * 是否需要签名 - */ - String SIGN_ENABLE = "signEnable"; - - /** - * 审批意见是否必填 - */ - String REASON_REQUIRE = "reasonRequire"; - - /** - * 节点类型 - * - * 目前只有 {@link BpmModelTypeEnum#SIMPLE} 的 UserTask 节点会设置该属性,用于区分是审批节点、还是办理节点 - */ - String NODE_TYPE = "nodeType"; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java deleted file mode 100644 index 0e6a697..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/enums/BpmnVariableConstants.java +++ /dev/null @@ -1,99 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.enums; - -import org.flowable.engine.runtime.ProcessInstance; - -/** - * BPM Variable 通用常量 - * - * @author ZT - */ -public class BpmnVariableConstants { - - /** - * 流程实例的变量 - 状态 - * - * @see ProcessInstance#getProcessVariables() - */ - public static final String PROCESS_INSTANCE_VARIABLE_STATUS = "PROCESS_STATUS"; - /** - * 流程实例的变量 - 理由 - * - * 例如说:审批不通过的理由(目前审核通过暂时不会记录) - * - * @see ProcessInstance#getProcessVariables() - */ - public static final String PROCESS_INSTANCE_VARIABLE_REASON = "PROCESS_REASON"; - /** - * 流程实例的变量 - 发起用户选择的审批人 Map - * - * @see ProcessInstance#getProcessVariables() - * @see BpmTaskCandidateStrategyEnum#START_USER_SELECT - */ - public static final String PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES = "PROCESS_START_USER_SELECT_ASSIGNEES"; - /** - * 流程实例的变量 - 审批人选择的审批人 Map - * - * @see ProcessInstance#getProcessVariables() - * @see BpmTaskCandidateStrategyEnum#APPROVE_USER_SELECT - */ - public static final String PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES = "PROCESS_APPROVE_USER_SELECT_ASSIGNEES"; - /** - * 流程实例的变量 - 发起用户 ID - * - * @see ProcessInstance#getProcessVariables() - */ - public static final String PROCESS_INSTANCE_VARIABLE_START_USER_ID = "PROCESS_START_USER_ID"; - /** - * 流程实例的变量 - 用于判断流程实例变量节点是否驳回. 格式 RETURN_FLAG_{节点 id} - * - * 目的是:驳回到发起节点时,因为审批人与发起人相同,所以被自动通过。但是,此时还是希望不要自动通过 - * - * @see ProcessInstance#getProcessVariables() - */ - public static final String PROCESS_INSTANCE_VARIABLE_RETURN_FLAG = "RETURN_FLAG_%s"; - /** - * 流程实例的变量 - 是否跳过表达式 - * - * @see ProcessInstance#getProcessVariables() - * @see Flowable/Activiti之SkipExpression 完成自动审批 - */ - public static final String PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; - - /** - * 流程实例的变量 - 用于判断流程是否需要跳过发起人节点 - * - * @see ProcessInstance#getProcessVariables() - */ - public static final String PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE = "PROCESS_SKIP_START_USER_NODE"; - - /** - * 流程实例的变量 - 流程开始时间 - * - * 【非存储变量】用于部分需要 format 的场景,例如说:流程实例的自定义标题 - */ - public static final String PROCESS_START_TIME = "PROCESS_START_TIME"; - /** - * 流程实例的变量 - 流程定义名称 - */ - public static final String PROCESS_DEFINITION_NAME = "PROCESS_DEFINITION_NAME"; - - /** - * 任务的变量 - 状态 - * - * @see org.flowable.task.api.Task#getTaskLocalVariables() - */ - public static final String TASK_VARIABLE_STATUS = "TASK_STATUS"; - /** - * 任务的变量 - 理由 - * - * 例如说:审批通过、不通过的理由 - * - * @see org.flowable.task.api.Task#getTaskLocalVariables() - */ - public static final String TASK_VARIABLE_REASON = "TASK_REASON"; - /** - * 任务变量 - 签名图片 URL - */ - public static final String TASK_SIGN_PIC_URL = "TASK_SIGN_PIC_URL"; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java deleted file mode 100644 index 5ec3654..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/event/BpmProcessInstanceEventPublisher.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.event; - -import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; -import jakarta.validation.Valid; -import lombok.AllArgsConstructor; -import org.springframework.context.ApplicationEventPublisher; -import org.springframework.validation.annotation.Validated; - -/** - * {@link BpmProcessInstanceStatusEvent} 的生产者 - * - * @author ZT - */ -@AllArgsConstructor -@Validated -public class BpmProcessInstanceEventPublisher { - - private final ApplicationEventPublisher publisher; - - public void sendProcessInstanceResultEvent(@Valid BpmProcessInstanceStatusEvent event) { - publisher.publishEvent(event); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java deleted file mode 100644 index a821f05..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmCopyTaskDelegate.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.listener; - -import cn.hutool.core.collection.CollUtil; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; -import jakarta.annotation.Resource; -import org.flowable.bpmn.model.FlowElement; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.delegate.JavaDelegate; -import org.springframework.stereotype.Component; - -import java.util.Set; - -import static com.zt.plat.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate.BEAN_NAME; - -/** - * 处理抄送用户的 {@link JavaDelegate} 的实现类 - *

- * 目前只有仿钉钉/飞书模式的【抄送节点】使用 - * - * @author jason - */ -@Component(BEAN_NAME) -public class BpmCopyTaskDelegate implements JavaDelegate { - - public static final String BEAN_NAME = "bpmCopyTaskDelegate"; - - @Resource - private BpmTaskCandidateInvoker taskCandidateInvoker; - - @Resource - private BpmProcessInstanceCopyService processInstanceCopyService; - - @Override - public void execute(DelegateExecution execution) { - // 1. 获得抄送人 - Set userIds = taskCandidateInvoker.calculateUsersByTask(execution); - if (CollUtil.isEmpty(userIds)) { - return; - } - // 2. 执行抄送 - FlowElement currentFlowElement = execution.getCurrentFlowElement(); - processInstanceCopyService.createProcessInstanceCopy(userIds, null, execution.getProcessInstanceId(), - currentFlowElement.getId(), currentFlowElement.getName(), null); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java deleted file mode 100644 index cc56501..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmProcessInstanceEventListener.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.listener; - -import com.google.common.collect.ImmutableSet; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import jakarta.annotation.Resource; -import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; -import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; -import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; -import org.flowable.engine.delegate.event.FlowableCancelledEvent; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import java.util.Set; - -/** - * 监听 {@link ProcessInstance} 的状态变更,更新其对应的 status 状态 - * - * @author jason - */ -@Component -public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener { - - public static final Set PROCESS_INSTANCE_EVENTS = ImmutableSet.builder() - .add(FlowableEngineEventType.PROCESS_CREATED) - .add(FlowableEngineEventType.PROCESS_COMPLETED) - .add(FlowableEngineEventType.PROCESS_CANCELLED) - .build(); - - @Resource - @Lazy // 延迟加载,避免循环依赖 - private BpmProcessInstanceService processInstanceService; - - public BpmProcessInstanceEventListener(){ - super(PROCESS_INSTANCE_EVENTS); - } - - @Override - protected void processCreated(FlowableEngineEntityEvent event) { - processInstanceService.processProcessInstanceCreated((ProcessInstance)event.getEntity()); - } - - @Override - protected void processCompleted(FlowableEngineEntityEvent event) { - processInstanceService.processProcessInstanceCompleted((ProcessInstance)event.getEntity()); - } - - @Override // 特殊情况:当跳转到 EndEvent 流程实例未结束, 会执行 deleteProcessInstance 方法 - protected void processCancelled(FlowableCancelledEvent event) { - ProcessInstance processInstance = processInstanceService.getProcessInstance(event.getProcessInstanceId()); - processInstanceService.processProcessInstanceCompleted(processInstance); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java deleted file mode 100644 index b26585b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java +++ /dev/null @@ -1,125 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.listener; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import com.google.common.collect.ImmutableSet; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.module.bpm.enums.definition.BpmBoundaryEventTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.service.definition.BpmModelService; -import com.zt.plat.module.bpm.service.task.BpmTaskService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -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.FlowableEngineEntityEvent; -import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; -import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; -import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent; -import org.flowable.engine.history.HistoricActivityInstance; -import org.flowable.job.api.Job; -import org.flowable.task.api.Task; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.Set; - -/** - * 监听 {@link Task} 的开始与完成 - * - * @author jason - */ -@Component -@Slf4j -public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { - - @Resource - @Lazy // 延迟加载,避免循环依赖 - private BpmModelService modelService; - @Resource - @Lazy // 解决循环依赖 - private BpmTaskService taskService; - - public static final Set TASK_EVENTS = ImmutableSet.builder() - .add(FlowableEngineEventType.TASK_CREATED) - .add(FlowableEngineEventType.TASK_ASSIGNED) - .add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,这里仅处理任务后置通知。 - .add(FlowableEngineEventType.ACTIVITY_CANCELLED) - .add(FlowableEngineEventType.TIMER_FIRED) // 监听审批超时 - .build(); - - public BpmTaskEventListener() { - super(TASK_EVENTS); - } - - @Override - protected void taskCreated(FlowableEngineEntityEvent event) { - taskService.processTaskCreated((Task) event.getEntity()); - } - - @Override - protected void taskAssigned(FlowableEngineEntityEvent event) { - taskService.processTaskAssigned((Task) event.getEntity()); - } - - @Override - protected void taskCompleted(FlowableEngineEntityEvent event) { - taskService.processTaskCompleted((Task) event.getEntity()); - } - - @Override - protected void activityCancelled(FlowableActivityCancelledEvent event) { - List activityList = taskService.getHistoricActivityListByExecutionId(event.getExecutionId()); - if (CollUtil.isEmpty(activityList)) { - log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId()); - return; - } - // 遍历处理 - activityList.forEach(activity -> { - if (StrUtil.isEmpty(activity.getTaskId())) { - return; - } - taskService.processTaskCanceled(activity.getTaskId()); - }); - } - - @Override - @SuppressWarnings("PatternVariableCanBeUsed") - protected void timerFired(FlowableEngineEntityEvent event) { - // 1.1 只处理 BoundaryEvent 边界计时时间 - String processDefinitionId = event.getProcessDefinitionId(); - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); - Job entity = (Job) event.getEntity(); - FlowElement element = BpmnModelUtils.getFlowElementById(bpmnModel, entity.getElementId()); - if (!(element instanceof BoundaryEvent)) { - return; - } - // 1.2 判断是否为超时处理 - BoundaryEvent boundaryEvent = (BoundaryEvent) element; - String boundaryEventType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, - BpmnModelConstants.BOUNDARY_EVENT_TYPE); - BpmBoundaryEventTypeEnum bpmTimerBoundaryEventType = BpmBoundaryEventTypeEnum.typeOf(NumberUtils.parseInt(boundaryEventType)); - - // 2. 处理超时 - if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT)) { - // 2.1 用户任务超时处理 - String timeoutHandlerType = BpmnModelUtils.parseBoundaryEventExtensionElement(boundaryEvent, - BpmnModelConstants.USER_TASK_TIMEOUT_HANDLER_TYPE); - String taskKey = boundaryEvent.getAttachedToRefId(); - taskService.processTaskTimeout(event.getProcessInstanceId(), taskKey, NumberUtils.parseInt(timeoutHandlerType)); - // 2.2 延迟器超时处理 - } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT)) { - String taskKey = boundaryEvent.getAttachedToRefId(); - taskService.triggerTask(event.getProcessInstanceId(), taskKey); - // 2.3 子流程超时处理 - } else if (ObjectUtil.equal(bpmTimerBoundaryEventType, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT)) { - String taskKey = boundaryEvent.getAttachedToRefId(); - taskService.processChildProcessTimeout(event.getProcessInstanceId(), taskKey); - } - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java deleted file mode 100644 index f88ce20..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/BpmTriggerTaskDelegate.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.listener; - -import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; -import jakarta.annotation.PostConstruct; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.model.FlowElement; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.delegate.JavaDelegate; -import org.springframework.stereotype.Component; - -import java.util.EnumMap; -import java.util.List; - -import static com.zt.plat.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate.BEAN_NAME; - - -/** - * 处理触发器任务 {@link JavaDelegate} 的实现类 - *

- * 目前只有 Simple 设计器【触发器节点】使用 - * - * @author jason - */ -@Component(BEAN_NAME) -@Slf4j -public class BpmTriggerTaskDelegate implements JavaDelegate { - - public static final String BEAN_NAME = "bpmTriggerTaskDelegate"; - - @Resource - private List triggers; - - private final EnumMap triggerMap = new EnumMap<>(BpmTriggerTypeEnum.class); - - @PostConstruct - private void init() { - triggers.forEach(trigger -> triggerMap.put(trigger.getType(), trigger)); - } - - @Override - public void execute(DelegateExecution execution) { - FlowElement flowElement = execution.getCurrentFlowElement(); - BpmTriggerTypeEnum bpmTriggerType = BpmnModelUtils.parserTriggerType(flowElement); - BpmTrigger bpmTrigger = triggerMap.get(bpmTriggerType); - if (bpmTrigger == null) { - log.error("[execute][FlowElement({}), {} 找不到匹配的触发器]", execution.getCurrentActivityId(), flowElement); - return; - } - bpmTrigger.execute(execution.getProcessInstanceId(), BpmnModelUtils.parserTriggerParam(flowElement)); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java deleted file mode 100644 index 84763fe..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateClassExecutionListener.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.exection; - -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.delegate.JavaDelegate; - -/** - * 类型为 class 的 ExecutionListener 监听器示例 - * - * @author ZT - */ -@Slf4j -public class DemoDelegateClassExecutionListener implements JavaDelegate { - - @Override - public void execute(DelegateExecution execution) { - log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(), - execution.getCurrentFlowableListener().getFieldExtensions()); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java deleted file mode 100644 index 64dba65..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoDelegateExpressionExecutionListener.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.exection; - -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.delegate.JavaDelegate; -import org.springframework.stereotype.Component; - -/** - * 类型为 delegateExpression 的 ExecutionListener 监听器示例 - * - * 和 {@link DemoDelegateClassExecutionListener} 的差异是,需要注册到 Spring 中 - */ -@Component -@Slf4j -public class DemoDelegateExpressionExecutionListener implements JavaDelegate { - - @Override - public void execute(DelegateExecution execution) { - log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(), - execution.getCurrentFlowableListener().getFieldExtensions()); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java deleted file mode 100644 index c6e5b99..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/exection/DemoSpringExpressionExecutionListener.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.exection; - -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.delegate.DelegateExecution; -import org.springframework.stereotype.Component; - -/** - * 类型为 expression 的 ExecutionListener 监听器示例 - * - * 和 {@link DemoDelegateClassExecutionListener} 的差异是,需要注册到 Spring 中,但不用实现 {@link org.flowable.engine.delegate.JavaDelegate} 接口 - */ -@Component -@Slf4j -public class DemoSpringExpressionExecutionListener { - - public void execute(DelegateExecution execution) { - log.info("[execute][execution({}) 被调用!变量有:{}]", execution.getId(), - execution.getCurrentFlowableListener().getFieldExtensions()); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java deleted file mode 100644 index 6b4e94e..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateClassTaskListener.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.task; - -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.delegate.TaskListener; -import org.flowable.task.service.delegate.DelegateTask; - -/** - * 类型为 class 的 TaskListener 监听器示例 - * - * @author ZT - */ -@Slf4j -public class DemoDelegateClassTaskListener implements TaskListener { - - @Override - public void notify(DelegateTask delegateTask) { - log.info("[execute][task({}) 被调用]", delegateTask.getId()); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java deleted file mode 100644 index 41fa05b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoDelegateExpressionTaskListener.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.task; - -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.delegate.TaskListener; -import org.flowable.task.service.delegate.DelegateTask; -import org.springframework.stereotype.Component; - -/** - * 类型为 delegateExpression 的 TaskListener 监听器示例 - * - * @author ZT - */ -@Component -@Slf4j -public class DemoDelegateExpressionTaskListener implements TaskListener { - - @Override - public void notify(DelegateTask delegateTask) { - log.info("[execute][task({}) 被调用]", delegateTask.getId()); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java deleted file mode 100644 index b9a4ccf..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/listener/demo/task/DemoSpringExpressionTaskListener.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.listener.demo.task; - -import lombok.extern.slf4j.Slf4j; -import org.flowable.task.service.delegate.DelegateTask; -import org.springframework.stereotype.Component; - -/** - * 类型为 expression 的 TaskListener 监听器示例 - * - * @author ZT - */ -@Slf4j -@Component -public class DemoSpringExpressionTaskListener { - - public void notify(DelegateTask delegateTask) { - log.info("[execute][task({}) 被调用]", delegateTask.getId()); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java deleted file mode 100644 index 583e804..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java +++ /dev/null @@ -1,158 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.util; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import com.fasterxml.jackson.core.type.TypeReference; -import com.zt.plat.framework.common.core.KeyValue; -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.framework.common.util.spring.SpringUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.util.LinkedMultiValueMap; -import org.springframework.util.MultiValueMap; -import org.springframework.web.client.RestClientException; -import org.springframework.web.client.RestTemplate; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.framework.web.core.util.WebFrameworkUtils.HEADER_TENANT_ID; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR; - -/** - * 工作流发起 HTTP 请求工具类 - * - * @author ZT - */ -@Slf4j -public class BpmHttpRequestUtils { - - public static void executeBpmHttpRequest(ProcessInstance processInstance, - String url, - List headerParams, - List bodyParams, - Boolean handleResponse, - List> response) { - RestTemplate restTemplate = SpringUtils.getBean(RestTemplate.class); - BpmProcessInstanceService processInstanceService = SpringUtils.getBean(BpmProcessInstanceService.class); - - // 1.1 设置请求头 - MultiValueMap headers = buildHttpHeaders(processInstance, headerParams); - // 1.2 设置请求体 - MultiValueMap body = buildHttpBody(processInstance, bodyParams); - - // 2. 发起请求 - ResponseEntity responseEntity = sendHttpRequest(url, headers, body, restTemplate); - - // 3. 处理返回 - if (Boolean.FALSE.equals(handleResponse)) { - return; - } - // 3.1 判断是否需要解析返回值 - if (responseEntity == null - || StrUtil.isEmpty(responseEntity.getBody()) - || !responseEntity.getStatusCode().is2xxSuccessful() - || CollUtil.isEmpty(response)) { - return; - } - // 3.2 解析返回值, 返回值必须符合 CommonResult 规范。 - CommonResult> respResult = JsonUtils.parseObjectQuietly(responseEntity.getBody(), - new TypeReference<>() {}); - if (respResult == null || !respResult.isSuccess()) { - return; - } - // 3.3 获取需要更新的流程变量 - Map updateVariables = getNeedUpdatedVariablesFromResponse(respResult.getData(), response); - // 3.4 更新流程变量 - if (CollUtil.isNotEmpty(updateVariables)) { - processInstanceService.updateProcessInstanceVariables(processInstance.getId(), updateVariables); - } - } - - public static ResponseEntity sendHttpRequest(String url, - MultiValueMap headers, - MultiValueMap body, - RestTemplate restTemplate) { - HttpEntity> requestEntity = new HttpEntity<>(body, headers); - ResponseEntity responseEntity; - try { - responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class); - log.info("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},响应结果:{}]", headers, body, responseEntity); - } catch (RestClientException e) { - log.error("[sendHttpRequest][HTTP 触发器,请求头:{},请求体:{},请求出错:{}]", headers, body, e.getMessage()); - throw exception(PROCESS_INSTANCE_HTTP_TRIGGER_CALL_ERROR); - } - return responseEntity; - } - - public static MultiValueMap buildHttpHeaders(ProcessInstance processInstance, - List headerSettings) { - Map processVariables = processInstance.getProcessVariables(); - MultiValueMap headers = new LinkedMultiValueMap<>(); - headers.add(HEADER_TENANT_ID, processInstance.getTenantId()); - addHttpRequestParam(headers, headerSettings, processVariables); - return headers; - } - - public static MultiValueMap buildHttpBody(ProcessInstance processInstance, - List bodySettings) { - Map processVariables = processInstance.getProcessVariables(); - MultiValueMap body = new LinkedMultiValueMap<>(); - addHttpRequestParam(body, bodySettings, processVariables); - body.add("processInstanceId", processInstance.getId()); - return body; - } - - /** - * 从请求返回值获取需要更新的流程变量 - * - * @param result 请求返回结果 - * @param responseSettings 返回设置 - * @return 需要更新的流程变量 - */ - public static Map getNeedUpdatedVariablesFromResponse(Map result, - List> responseSettings) { - Map updateVariables = new HashMap<>(); - if (CollUtil.isEmpty(result)) { - return updateVariables; - } - responseSettings.forEach(responseSetting -> { - if (StrUtil.isNotEmpty(responseSetting.getKey()) && result.containsKey(responseSetting.getValue())) { - updateVariables.put(responseSetting.getKey(), result.get(responseSetting.getValue())); - } - }); - return updateVariables; - } - - /** - * 添加 HTTP 请求参数。请求头或者请求体 - * - * @param params HTTP 请求参数 - * @param paramSettings HTTP 请求参数设置 - * @param processVariables 流程变量 - */ - public static void addHttpRequestParam(MultiValueMap params, - List paramSettings, - Map processVariables) { - if (CollUtil.isEmpty(paramSettings)) { - return; - } - paramSettings.forEach(item -> { - if (item.getType().equals(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType())) { - params.add(item.getKey(), item.getValue()); - } else if (item.getType().equals(BpmHttpRequestParamTypeEnum.FROM_FORM.getType())) { - params.add(item.getKey(), processVariables.get(item.getValue()).toString()); - } - }); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java deleted file mode 100644 index d9bf32e..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ /dev/null @@ -1,1025 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.util; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.convert.Convert; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.StrUtil; -import com.google.common.collect.Maps; -import com.zt.plat.framework.common.util.collection.CollectionUtils; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.framework.common.util.string.StrUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; -import com.zt.plat.module.bpm.enums.definition.*; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; -import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.converter.BpmnXMLConverter; -import org.flowable.bpmn.model.*; -import org.flowable.bpmn.model.Process; -import org.flowable.common.engine.api.FlowableException; -import org.flowable.common.engine.impl.util.io.BytesStreamSource; -import org.flowable.engine.impl.el.FixedValue; - -import java.util.*; - -import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; -import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; -import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX; - -/** - * BPMN Model 操作工具类。目前分成三部分: - * - * 1. BPMN 修改 + 解析元素相关的方法 - * 2. BPMN 简单查找相关的方法 - * 3. BPMN 复杂遍历相关的方法 - * 4. BPMN 流程预测相关的方法 - * - * @author ZT - */ -@Slf4j -public class BpmnModelUtils { - - // ========== BPMN 修改 + 解析元素相关的方法 ========== - - public static void addExtensionElement(FlowElement element, String name, String value) { - if (value == null) { - return; - } - ExtensionElement extensionElement = new ExtensionElement(); - extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); - extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); - extensionElement.setElementText(value); - extensionElement.setName(name); - element.addExtensionElement(extensionElement); - } - - public static void addExtensionElement(FlowElement element, String name, Integer value) { - if (value == null) { - return; - } - addExtensionElement(element, name, String.valueOf(value)); - } - - public static void addExtensionElementJson(FlowElement element, String name, Object value) { - if (value == null) { - return; - } - addExtensionElement(element, name, JsonUtils.toJsonString(value)); - } - - public static void addExtensionElement(FlowElement element, String name, Map attributes) { - if (attributes == null) { - return; - } - ExtensionElement extensionElement = new ExtensionElement(); - extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); - extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); - extensionElement.setName(name); - attributes.forEach((key, value) -> { - ExtensionAttribute extensionAttribute = new ExtensionAttribute(key, value); - extensionElement.addAttribute(extensionAttribute); - }); - element.addExtensionElement(extensionElement); - } - - /** - * 解析扩展元素 - * - * @param flowElement 节点 - * @param elementName 元素名称 - * @return 扩展元素 - */ - public static String parseExtensionElement(FlowElement flowElement, String elementName) { - if (flowElement == null) { - return null; - } - ExtensionElement element = CollUtil.getFirst(flowElement.getExtensionElements().get(elementName)); - return element != null ? element.getElementText() : null; - } - - /** - * 给节点添加候选人元素 - * - * @param candidateStrategy 候选人策略 - * @param candidateParam 候选人参数,允许空 - * @param flowElement 节点 - */ - public static void addCandidateElements(Integer candidateStrategy, String candidateParam, FlowElement flowElement) { - addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY, - candidateStrategy == null ? null : candidateStrategy.toString()); - addExtensionElement(flowElement, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM, candidateParam); - } - - /** - * 解析候选人策略 - * - * @param userTask 任务节点 - * @return 候选人策略 - */ - public static Integer parseCandidateStrategy(FlowElement userTask) { - Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue( - BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); - // TODO @ZT 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限 - if (candidateStrategy == null) { - ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); - candidateStrategy = element != null ? NumberUtils.parseInt(element.getElementText()) : null; - } - return candidateStrategy; - } - - /** - * 解析候选人参数 - * - * @param userTask 任务节点 - * @return 候选人参数 - */ - public static String parseCandidateParam(FlowElement userTask) { - String candidateParam = userTask.getAttributeValue( - BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_PARAM); - if (candidateParam == null) { - ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM)); - candidateParam = element != null ? element.getElementText() : null; - } - return candidateParam; - } - - /** - * 解析审批类型 - * - * @see BpmUserTaskApproveTypeEnum - * @param userTask 任务节点 - * @return 审批类型 - */ - public static Integer parseApproveType(FlowElement userTask) { - return NumberUtils.parseInt(parseExtensionElement(userTask, BpmnModelConstants.USER_TASK_APPROVE_TYPE)); - } - - /** - * 解析子流程多实例来源类型 - * - * @see BpmChildProcessMultiInstanceSourceTypeEnum - * @param element 任务节点 - * @return 多实例来源类型 - */ - public static Integer parseMultiInstanceSourceType(FlowElement element) { - return NumberUtils.parseInt(parseExtensionElement(element, BpmnModelConstants.CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE)); - } - - /** - * 添加任务拒绝处理元素 - * - * @param rejectHandler 任务拒绝处理 - * @param userTask 任务节点 - */ - public static void addTaskRejectElements(BpmSimpleModelNodeVO.RejectHandler rejectHandler, UserTask userTask) { - if (rejectHandler == null) { - return; - } - addExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE, StrUtil.toStringOrNull(rejectHandler.getType())); - addExtensionElement(userTask, USER_TASK_REJECT_RETURN_TASK_ID, rejectHandler.getReturnNodeId()); - } - - /** - * 解析任务拒绝处理类型 - * - * @param userTask 任务节点 - * @return 任务拒绝处理类型 - */ - public static BpmUserTaskRejectHandlerTypeEnum parseRejectHandlerType(FlowElement userTask) { - Integer rejectHandlerType = NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_REJECT_HANDLER_TYPE)); - return BpmUserTaskRejectHandlerTypeEnum.typeOf(rejectHandlerType); - } - - /** - * 解析任务拒绝返回任务节点 ID - * - * @param flowElement 任务节点 - * @return 任务拒绝返回任务节点 ID - */ - public static String parseReturnTaskId(FlowElement flowElement) { - return parseExtensionElement(flowElement, USER_TASK_REJECT_RETURN_TASK_ID); - } - - /** - * 给节点添加用户任务的审批人与发起人相同时,处理类型枚举 - * - * @see BpmUserTaskAssignStartUserHandlerTypeEnum - * @param assignStartUserHandlerType 发起人处理类型 - * @param userTask 任务节点 - */ - public static void addAssignStartUserHandlerType(Integer assignStartUserHandlerType, UserTask userTask) { - if (assignStartUserHandlerType == null) { - return; - } - addExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, assignStartUserHandlerType.toString()); - } - - /** - * 给节点添加用户任务的审批人为空时,处理类型枚举 - * - * @see BpmUserTaskAssignEmptyHandlerTypeEnum - * @param emptyHandler 空处理 - * @param userTask 任务节点 - */ - public static void addAssignEmptyHandlerType(BpmSimpleModelNodeVO.AssignEmptyHandler emptyHandler, UserTask userTask) { - if (emptyHandler == null) { - return; - } - addExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE, StrUtil.toStringOrNull(emptyHandler.getType())); - addExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS, StrUtil.join(",", emptyHandler.getUserIds())); - } - - /** - * 解析用户任务的审批人与发起人相同时,处理类型枚举 - * - * @param userTask 任务节点 - * @return 处理类型枚举 - */ - public static Integer parseAssignStartUserHandlerType(FlowElement userTask) { - return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_START_USER_HANDLER_TYPE)); - } - - /** - * 解析用户任务的审批人为空时,处理类型枚举 - * - * @param userTask 任务节点 - * @return 处理类型枚举 - */ - public static Integer parseAssignEmptyHandlerType(FlowElement userTask) { - return NumberUtils.parseInt(parseExtensionElement(userTask, USER_TASK_ASSIGN_EMPTY_HANDLER_TYPE)); - } - - /** - * 解析用户任务的审批人为空时,处理用户 ID 数组 - * - * @param userTask 任务节点 - * @return 处理用户 ID 数组 - */ - public static List parseAssignEmptyHandlerUserIds(FlowElement userTask) { - return StrUtils.splitToLong(parseExtensionElement(userTask, USER_TASK_ASSIGN_USER_IDS), ","); - } - - /** - * 给节点添加表单字段权限元素 - * - * @param fieldsPermissions 表单字段权限 - * @param flowElement 节点 - */ - public static void addFormFieldsPermission(List> fieldsPermissions, FlowElement flowElement) { - if (CollUtil.isNotEmpty(fieldsPermissions)) { - fieldsPermissions.forEach(item -> addExtensionElement(flowElement, FORM_FIELD_PERMISSION_ELEMENT, item)); - } - } - - /** - * 解析表单字段权限 - * - * @param bpmnModel bpmnModel 对象 - * @param flowElementId 元素 ID - * @return 表单字段权限 - */ - public static Map parseFormFieldsPermission(BpmnModel bpmnModel, String flowElementId) { - if (bpmnModel == null || StrUtil.isEmpty(flowElementId)) { - return null; - } - FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); - if (flowElement == null) { - return null; - } - List extensionElements = flowElement.getExtensionElements().get(FORM_FIELD_PERMISSION_ELEMENT); - if (CollUtil.isEmpty(extensionElements)) { - return null; - } - Map fieldsPermission = MapUtil.newHashMap(); - extensionElements.forEach(element -> { - String field = element.getAttributeValue(null, FORM_FIELD_PERMISSION_ELEMENT_FIELD_ATTRIBUTE); - String permission = element.getAttributeValue(null, FORM_FIELD_PERMISSION_ELEMENT_PERMISSION_ATTRIBUTE); - if (StrUtil.isNotEmpty(field) && StrUtil.isNotEmpty(permission)) { - fieldsPermission.put(field, permission); - } - }); - return fieldsPermission; - } - - /** - * 给节点添加操作按钮设置元素 - */ - public static void addButtonsSetting(List buttonsSetting, UserTask userTask) { - if (CollUtil.isNotEmpty(buttonsSetting)) { - List> list = CollectionUtils.convertList(buttonsSetting, item -> { - Map settingMap = Maps.newHashMapWithExpectedSize(3); - settingMap.put(BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE, String.valueOf(item.getId())); - settingMap.put(BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE, item.getDisplayName()); - settingMap.put(BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE, String.valueOf(item.getEnable())); - return settingMap; - }); - list.forEach(item -> addExtensionElement(userTask, BUTTON_SETTING_ELEMENT, item)); - } - } - - /** - * 解析操作按钮设置 - * - * @param bpmnModel bpmnModel 对象 - * @param flowElementId 元素 ID - * @return 操作按钮设置 - */ - public static Map parseButtonsSetting(BpmnModel bpmnModel, String flowElementId) { - FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); - if (flowElement == null) { - return null; - } - List extensionElements = flowElement.getExtensionElements().get(BUTTON_SETTING_ELEMENT); - if (CollUtil.isEmpty(extensionElements)) { - return null; - } - Map buttonSettings = Maps.newHashMapWithExpectedSize(extensionElements.size()); - extensionElements.forEach(element -> { - String id = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_ID_ATTRIBUTE); - String displayName = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_DISPLAY_NAME_ATTRIBUTE); - String enable = element.getAttributeValue(null, BUTTON_SETTING_ELEMENT_ENABLE_ATTRIBUTE); - if (StrUtil.isNotEmpty(id)) { - BpmTaskRespVO.OperationButtonSetting setting = new BpmTaskRespVO.OperationButtonSetting(); - buttonSettings.put(Integer.valueOf(id), setting.setDisplayName(displayName).setEnable(Boolean.parseBoolean(enable))); - } - }); - return buttonSettings; - } - - /** - * 解析边界事件扩展元素 - * - * @param boundaryEvent 边界事件 - * @param customElement 元素 - * @return 扩展元素 - */ - public static String parseBoundaryEventExtensionElement(BoundaryEvent boundaryEvent, String customElement) { - if (boundaryEvent == null) { - return null; - } - ExtensionElement extensionElement = CollUtil.getFirst(boundaryEvent.getExtensionElements().get(customElement)); - return Optional.ofNullable(extensionElement).map(ExtensionElement::getElementText).orElse(null); - } - - public static void addSignEnable(Boolean signEnable, FlowElement userTask) { - addExtensionElement(userTask, SIGN_ENABLE, - ObjUtil.isNotNull(signEnable) ? signEnable.toString() : Boolean.FALSE.toString()); - } - - public static Boolean parseSignEnable(BpmnModel bpmnModel, String flowElementId) { - FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); - if (flowElement == null) { - return false; - } - List extensionElements = flowElement.getExtensionElements().get(SIGN_ENABLE); - if (CollUtil.isEmpty(extensionElements)) { - return false; - } - return Convert.toBool(extensionElements.get(0).getElementText(), false); - } - - public static void addReasonRequire(Boolean reasonRequire, FlowElement userTask) { - addExtensionElement(userTask, REASON_REQUIRE, - ObjUtil.isNotNull(reasonRequire) ? reasonRequire.toString() : Boolean.FALSE.toString()); - } - - public static Boolean parseReasonRequire(BpmnModel bpmnModel, String flowElementId) { - FlowElement flowElement = getFlowElementById(bpmnModel, flowElementId); - if (flowElement == null) { - return false; - } - List extensionElements = flowElement.getExtensionElements().get(REASON_REQUIRE); - if (CollUtil.isEmpty(extensionElements)) { - return false; - } - return Convert.toBool(extensionElements.get(0).getElementText(), false); - } - - public static void addListenerConfig(FlowableListener flowableListener, BpmSimpleModelNodeVO.ListenerHandler handler) { - FieldExtension fieldExtension = new FieldExtension(); - fieldExtension.setFieldName("listenerConfig"); - fieldExtension.setStringValue(JsonUtils.toJsonString(handler)); - flowableListener.getFieldExtensions().add(fieldExtension); - } - - public static BpmSimpleModelNodeVO.ListenerHandler parseListenerConfig(FixedValue fixedValue) { - String expressionText = fixedValue.getExpressionText(); - Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); - return JsonUtils.parseObject(expressionText, BpmSimpleModelNodeVO.ListenerHandler.class); - } - - public static BpmTriggerTypeEnum parserTriggerType(FlowElement flowElement) { - Integer triggerType = NumberUtils.parseInt(parseExtensionElement(flowElement, TRIGGER_TYPE)); - return BpmTriggerTypeEnum.typeOf(triggerType); - } - - public static String parserTriggerParam(FlowElement flowElement) { - return parseExtensionElement(flowElement, TRIGGER_PARAM); - } - - /** - * 给节点添加节点类型 - * - * @param nodeType 节点类型 - * @param flowElement 节点 - */ - public static void addNodeType(Integer nodeType, FlowElement flowElement) { - addExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE, nodeType); - } - - /** - * 解析节点类型 - * - * @param flowElement 节点 - * @return 节点类型 - */ - public static Integer parseNodeType(FlowElement flowElement) { - return NumberUtils.parseInt(parseExtensionElement(flowElement, BpmnModelConstants.NODE_TYPE)); - } - - // ========== BPM 简单查找相关的方法 ========== - - /** - * 根据节点,获取入口连线 - * - * @param source 起始节点 - * @return 入口连线列表 - */ - public static List getElementIncomingFlows(FlowElement source) { - if (source instanceof FlowNode) { - return ((FlowNode) source).getIncomingFlows(); - } - return new ArrayList<>(); - } - - /** - * 根据节点,获取出口连线 - * - * @param source 起始节点 - * @return 出口连线列表 - */ - public static List getElementOutgoingFlows(FlowElement source) { - if (source instanceof FlowNode) { - return ((FlowNode) source).getOutgoingFlows(); - } - return new ArrayList<>(); - } - - /** - * 获取流程元素信息 - * - * @param model bpmnModel 对象 - * @param flowElementId 元素 ID - * @return 元素信息 - */ - public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { - Process process = model.getMainProcess(); - return process.getFlowElement(flowElementId); - } - - /** - * 获得 BPMN 流程中,指定的元素们 - * - * @param model 模型 - * @param clazz 指定元素。例如说,{@link UserTask}、{@link Gateway} 等等 - * @return 元素们 - */ - @SuppressWarnings("unchecked") - public static List getBpmnModelElements(BpmnModel model, Class clazz) { - List result = new ArrayList<>(); - model.getProcesses().forEach(process -> process.getFlowElements().forEach(flowElement -> { - if (flowElement.getClass().isAssignableFrom(clazz)) { - result.add((T) flowElement); - } - })); - return result; - } - - public static StartEvent getStartEvent(BpmnModel model) { - Process process = model.getMainProcess(); - // 从 initialFlowElement 找 - FlowElement startElement = process.getInitialFlowElement(); - if (startElement instanceof StartEvent) { - return (StartEvent) startElement; - } - // 从 flowElementList 找 - return (StartEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof StartEvent); - } - - public static EndEvent getEndEvent(BpmnModel model) { - Process process = model.getMainProcess(); - // 从 flowElementList 找 endEvent - return (EndEvent) CollUtil.findOne(process.getFlowElements(), flowElement -> flowElement instanceof EndEvent); - } - - public static BpmnModel getBpmnModel(byte[] bpmnBytes) { - if (ArrayUtil.isEmpty(bpmnBytes)) { - return null; - } - BpmnXMLConverter converter = new BpmnXMLConverter(); - // 补充说明:由于在 Flowable 中自定义了属性,所以 validateSchema 传递 false - return converter.convertToBpmnModel(new BytesStreamSource(bpmnBytes), false, false); - } - - public static String getBpmnXml(BpmnModel model) { - if (model == null) { - return null; - } - BpmnXMLConverter converter = new BpmnXMLConverter(); - return StrUtil.utf8Str(converter.convertToXML(model)); - } - - public static String getBpmnXml(byte[] bpmnBytes) { - if (ArrayUtil.isEmpty(bpmnBytes)) { - return null; - } - return StrUtil.utf8Str(bpmnBytes); - } - - // ========== BPMN 复杂遍历相关的方法 ========== - - /** - * 找到 source 节点之前的所有用户任务节点 - * - * @param source 起始节点 - * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 - * @param userTaskList 已找到的用户任务节点 - * @return 用户任务节点 数组 - */ - public static List getPreviousUserTaskList(FlowElement source, Set hasSequenceFlow, List userTaskList) { - userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; - hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; - // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 - if (source instanceof StartEvent && source.getSubProcess() != null) { - userTaskList = getPreviousUserTaskList(source.getSubProcess(), hasSequenceFlow, userTaskList); - } - - // 根据类型,获取入口连线 - List sequenceFlows = getElementIncomingFlows(source); - if (sequenceFlows == null) { - return userTaskList; - } - // 循环找到目标元素 - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (hasSequenceFlow.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - hasSequenceFlow.add(sequenceFlow.getId()); - // 类型为用户节点,则新增父级节点 - if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { - userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); - } - // 类型为子流程,则添加子流程开始节点出口处相连的节点 - if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { - // 获取子流程用户任务节点 - List childUserTaskList = findChildProcessUserTaskList((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); - // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 - if (CollUtil.isNotEmpty(childUserTaskList)) { - userTaskList.addAll(childUserTaskList); - } - } - // 继续迭代 - userTaskList = getPreviousUserTaskList(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); - } - return userTaskList; - } - - /** - * 迭代获取子流程用户任务节点 - * - * @param source 起始节点 - * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 - * @param userTaskList 需要撤回的用户任务列表 - * @return 用户任务节点 - */ - public static List findChildProcessUserTaskList(FlowElement source, Set hasSequenceFlow, List userTaskList) { - hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; - userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; - - // 根据类型,获取出口连线 - List sequenceFlows = getElementOutgoingFlows(source); - if (sequenceFlows == null) { - return userTaskList; - } - // 循环找到目标元素 - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (hasSequenceFlow.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - hasSequenceFlow.add(sequenceFlow.getId()); - // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 - if (sequenceFlow.getTargetFlowElement() instanceof UserTask) { - userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); - continue; - } - // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 - if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { - List childUserTaskList = findChildProcessUserTaskList((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); - // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 - if (CollUtil.isNotEmpty(childUserTaskList)) { - userTaskList.addAll(childUserTaskList); - continue; - } - } - // 继续迭代 - userTaskList = findChildProcessUserTaskList(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); - } - return userTaskList; - } - - /** - * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 - * 不存在直接退回到子流程中的情况,但存在从子流程出去到父流程情况 - * - * @param source 起始节点 - * @param target 目标节点 - * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 - * @return 结果 - */ - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - public static boolean isSequentialReachable(FlowElement source, FlowElement target, Set visitedElements) { - visitedElements = visitedElements == null ? new HashSet<>() : visitedElements; - // 不能是开始事件和子流程 - if (source instanceof StartEvent && isInEventSubprocess(source)) { - return false; - } - - // 根据类型,获取入口连线 - List sequenceFlows = getElementIncomingFlows(source); - if (CollUtil.isEmpty(sequenceFlows)) { - return true; - } - // 循环找到目标元素 - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (visitedElements.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - visitedElements.add(sequenceFlow.getId()); - // 这条线路存在目标节点,这条线路完成,进入下个线路 - FlowElement sourceFlowElement = sequenceFlow.getSourceFlowElement(); - if (target.getId().equals(sourceFlowElement.getId())) { - continue; - } - // 如果目标节点为并行网关,则不继续 - if (sourceFlowElement instanceof ParallelGateway) { - return false; - } - // 否则就继续迭代 - if (!isSequentialReachable(sourceFlowElement, target, visitedElements)) { - return false; - } - } - return true; - } - - /** - * 判断当前节点是否属于不同的子流程 - * - * @param flowElement 被判断的节点 - * @return true 表示属于子流程 - */ - private static boolean isInEventSubprocess(FlowElement flowElement) { - FlowElementsContainer flowElementsContainer = flowElement.getParentContainer(); - while (flowElementsContainer != null) { - if (flowElementsContainer instanceof EventSubProcess) { - return true; - } - - if (flowElementsContainer instanceof FlowElement) { - flowElementsContainer = ((FlowElement) flowElementsContainer).getParentContainer(); - } else { - flowElementsContainer = null; - } - } - return false; - } - - /** - * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 - * - * @param source 起始节点 - * @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点 - * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 - * @param userTaskList 需要撤回的用户任务列表 - * @return 子级任务节点列表 - */ - public static List iteratorFindChildUserTasks(FlowElement source, List runTaskKeyList, - Set hasSequenceFlow, List userTaskList) { - hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; - userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; - // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 - if (source instanceof StartEvent && source.getSubProcess() != null) { - userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); - } - - // 根据类型,获取出口连线 - List sequenceFlows = getElementOutgoingFlows(source); - if (sequenceFlows == null) { - return userTaskList; - } - // 循环找到目标元素 - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (hasSequenceFlow.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - hasSequenceFlow.add(sequenceFlow.getId()); - // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 - if (sequenceFlow.getTargetFlowElement() instanceof UserTask && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) { - userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); - continue; - } - // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 - if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { - List childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null); - // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 - if (CollUtil.isNotEmpty(childUserTaskList)) { - userTaskList.addAll(childUserTaskList); - continue; - } - } - // 继续迭代 - userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); - } - return userTaskList; - } - - // ========== BPMN 流程预测相关的方法 ========== - - /** - * 流程预测,返回 StartEvent、UserTask、ServiceTask、EndEvent 节点元素,最终是 List 串行结果 - * - * @param bpmnModel BPMN 图 - * @param variables 变量 - * @return 节点元素数组 - */ - public static List simulateProcess(BpmnModel bpmnModel, Map variables) { - List resultElements = new ArrayList<>(); - Set visitElements = new HashSet<>(); - - // 从 StartEvent 开始遍历 - StartEvent startEvent = getStartEvent(bpmnModel); - simulateNextFlowElements(startEvent, variables, resultElements, visitElements); - - // 将 EndEvent 放在末尾。原因是,DFS 遍历,可能 EndEvent 在 resultElements 中 - List endEvents = CollUtil.removeWithAddIf(resultElements, - flowElement -> flowElement instanceof EndEvent); - resultElements.addAll(endEvents); - return resultElements; - } - - @SuppressWarnings("PatternVariableCanBeUsed") - private static void simulateNextFlowElements(FlowElement currentElement, Map variables, - List resultElements, Set visitElements) { - // 如果为空,或者已经遍历过,则直接结束 - if (currentElement == null) { - return; - } - if (visitElements.contains(currentElement)) { - return; - } - visitElements.add(currentElement); - - // 情况:StartEvent/EndEvent/UserTask/ServiceTask - if (currentElement instanceof StartEvent - || currentElement instanceof EndEvent - || currentElement instanceof UserTask - || currentElement instanceof ServiceTask) { - // 添加元素 - FlowNode flowNode = (FlowNode) currentElement; - resultElements.add(flowNode); - // 遍历子节点 - flowNode.getOutgoingFlows().forEach( - nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); - return; - } - - // 情况:ExclusiveGateway 排它,只有一个满足条件的。如果没有,就走默认的 - if (currentElement instanceof ExclusiveGateway) { - // 查找满足条件的 SequenceFlow 路径 - SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway((Gateway) currentElement, variables); - // 遍历满足条件的 SequenceFlow 路径 - if (matchSequenceFlow != null) { - simulateNextFlowElements(matchSequenceFlow.getTargetFlowElement(), variables, resultElements, visitElements); - } - } - // 情况:InclusiveGateway 包容,多个满足条件的。如果没有,就走默认的 - else if (currentElement instanceof InclusiveGateway) { - // 查找满足条件的 SequenceFlow 路径 - Collection matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway((Gateway) currentElement, variables); - // 遍历满足条件的 SequenceFlow 路径 - matchSequenceFlows.forEach( - flow -> simulateNextFlowElements(flow.getTargetFlowElement(), variables, resultElements, visitElements)); - } - // 情况:ParallelGateway 并行,都满足,都走 - else if (currentElement instanceof ParallelGateway) { - Gateway gateway = (Gateway) currentElement; - // 遍历子节点 - gateway.getOutgoingFlows().forEach( - nextElement -> simulateNextFlowElements(nextElement.getTargetFlowElement(), variables, resultElements, visitElements)); - } - } - - /** - * 根据当前节点,获取下一个节点 - * - * @param currentElement 当前节点 - * @param bpmnModel BPMN模型 - * @param variables 流程变量 - */ - @SuppressWarnings("PatternVariableCanBeUsed") - public static List getNextFlowNodes(FlowElement currentElement, BpmnModel bpmnModel, - Map variables){ - List nextFlowNodes = new ArrayList<>(); // 下一个执行的流程节点集合 - FlowNode currentNode = (FlowNode) currentElement; // 当前执行节点的基本属性 - List outgoingFlows = currentNode.getOutgoingFlows(); // 当前节点的关联节点 - if (CollUtil.isEmpty(outgoingFlows)) { - log.warn("[getNextFlowNodes][当前节点({}) 的 outgoingFlows 为空]", currentNode.getId()); - return nextFlowNodes; - } - - // 遍历每个出口流 - for (SequenceFlow outgoingFlow : outgoingFlows) { - // 获取目标节点的基本属性 - FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef()); - if (targetElement == null) { - continue; - } - // 如果是结束节点,直接返回 - if (targetElement instanceof EndEvent) { - break; - } - // 情况一:处理不同类型的网关 - if (targetElement instanceof Gateway) { - Gateway gateway = (Gateway) targetElement; - if (gateway instanceof ExclusiveGateway) { - handleExclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); - } else if (gateway instanceof InclusiveGateway) { - handleInclusiveGateway(gateway, bpmnModel, variables, nextFlowNodes); - } else if (gateway instanceof ParallelGateway) { - handleParallelGateway(gateway, bpmnModel, variables, nextFlowNodes); - } - } else { - // 情况二:如果不是网关,直接添加到下一个节点列表 - nextFlowNodes.add((FlowNode) targetElement); - } - } - return nextFlowNodes; - } - - /** - * 处理排它网关 - * - * @param gateway 排他网关 - * @param bpmnModel BPMN模型 - * @param variables 流程变量 - * @param nextFlowNodes 下一个执行的流程节点集合 - */ - private static void handleExclusiveGateway(Gateway gateway, BpmnModel bpmnModel, - Map variables, List nextFlowNodes) { - // 查找满足条件的 SequenceFlow 路径 - SequenceFlow matchSequenceFlow = findMatchSequenceFlowByExclusiveGateway(gateway, variables); - // 遍历满足条件的 SequenceFlow 路径 - if (matchSequenceFlow != null) { - FlowElement targetElement = bpmnModel.getFlowElement(matchSequenceFlow.getTargetRef()); - if (targetElement instanceof FlowNode) { - nextFlowNodes.add((FlowNode) targetElement); - } - } - } - - /** - * 处理排它网关(Exclusive Gateway),选择符合条件的路径 - * - * @param gateway 排他网关 - * @param variables 流程变量 - * @return 符合条件的路径 - */ - private static SequenceFlow findMatchSequenceFlowByExclusiveGateway(Gateway gateway, Map variables) { - SequenceFlow matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && (evalConditionExpress(variables, flow.getConditionExpression()))); - if (matchSequenceFlow == null) { - matchSequenceFlow = CollUtil.findOne(gateway.getOutgoingFlows(), - flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); - // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 - if (matchSequenceFlow == null && gateway.getOutgoingFlows().size() == 1) { - matchSequenceFlow = gateway.getOutgoingFlows().get(0); - } - } - return matchSequenceFlow; - } - - /** - * 处理包容网关 - * - * @param gateway 排他网关 - * @param bpmnModel BPMN模型 - * @param variables 流程变量 - * @param nextFlowNodes 下一个执行的流程节点集合 - */ - private static void handleInclusiveGateway(Gateway gateway, BpmnModel bpmnModel, - Map variables, List nextFlowNodes) { - // 查找满足条件的 SequenceFlow 路径集合 - Collection matchSequenceFlows = findMatchSequenceFlowsByInclusiveGateway(gateway, variables); - // 遍历满足条件的 SequenceFlow 路径,获取目标节点 - matchSequenceFlows.forEach(flow -> { - FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); - if (targetElement instanceof FlowNode) { - nextFlowNodes.add((FlowNode) targetElement); - } - }); - } - - /** - * 处理排它网关(Inclusive Gateway),选择符合条件的路径 - * - * @param gateway 排他网关 - * @param variables 流程变量 - * @return 符合条件的路径 - */ - private static Collection findMatchSequenceFlowsByInclusiveGateway(Gateway gateway, Map variables) { - // 查找满足条件的 SequenceFlow 路径 - Collection matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.notEqual(gateway.getDefaultFlow(), flow.getId()) - && evalConditionExpress(variables, flow.getConditionExpression())); - if (CollUtil.isEmpty(matchSequenceFlows)) { - matchSequenceFlows = CollUtil.filterNew(gateway.getOutgoingFlows(), - flow -> ObjUtil.equal(gateway.getDefaultFlow(), flow.getId())); - // 特殊:没有默认的情况下,并且只有 1 个条件,则认为它是默认的 - if (CollUtil.isEmpty(matchSequenceFlows) && gateway.getOutgoingFlows().size() == 1) { - matchSequenceFlows = gateway.getOutgoingFlows(); - } - } - return matchSequenceFlows; - } - - - /** - * 处理并行网关 - * - * @param gateway 排他网关 - * @param bpmnModel BPMN模型 - * @param variables 流程变量 - * @param nextFlowNodes 下一个执行的流程节点集合 - */ - private static void handleParallelGateway(Gateway gateway, BpmnModel bpmnModel, - Map variables, List nextFlowNodes) { - // 并行网关,遍历所有出口路径,获取目标节点 - gateway.getOutgoingFlows().forEach(flow -> { - FlowElement targetElement = bpmnModel.getFlowElement(flow.getTargetRef()); - if (targetElement instanceof FlowNode) { - nextFlowNodes.add((FlowNode) targetElement); - } - }); - } - - /** - * 计算条件表达式是否为 true 满足条件 - * - * @param variables 流程实例 - * @param expression 条件表达式 - * @return 是否满足条件 - */ - public static boolean evalConditionExpress(Map variables, String expression) { - if (expression == null) { - return Boolean.FALSE; - } - // 如果 variables 为空,则创建一个的原因?可能 expression 的计算,不依赖于 variables - if (variables == null) { - variables = new HashMap<>(); - } - - // 执行计算 - try { - Object result = FlowableUtils.getExpressionValue(variables, expression); - return Boolean.TRUE.equals(result); - } catch (FlowableException ex) { - // 为什么使用 info 日志?原因是,expression 如果从 variables 取不到值,会报错。实际这种情况下,可以忽略 - log.info("[evalConditionExpress][条件表达式({}) 变量({}) 解析报错]", expression, variables, ex); - return Boolean.FALSE; - } - } - - @SuppressWarnings("PatternVariableCanBeUsed") - public static boolean isSequentialUserTask(FlowElement flowElement) { - if (!(flowElement instanceof UserTask)) { - return false; - } - UserTask userTask = (UserTask) flowElement; - MultiInstanceLoopCharacteristics loopCharacteristics = userTask.getLoopCharacteristics(); - return loopCharacteristics != null && loopCharacteristics.isSequential(); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java deleted file mode 100644 index 7d54ce5..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/FlowableUtils.java +++ /dev/null @@ -1,362 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.util; - -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.extra.spring.SpringUtil; -import com.zt.plat.framework.common.core.KeyValue; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.framework.tenant.core.context.TenantContextHolder; -import com.zt.plat.framework.tenant.core.util.TenantUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormFieldVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; -import lombok.SneakyThrows; -import org.flowable.common.engine.api.delegate.Expression; -import org.flowable.common.engine.api.variable.VariableContainer; -import org.flowable.common.engine.impl.el.ExpressionManager; -import org.flowable.common.engine.impl.identity.Authentication; -import org.flowable.common.engine.impl.variable.MapDelegateVariableContainer; -import org.flowable.engine.ManagementService; -import org.flowable.engine.ProcessEngineConfiguration; -import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl; -import org.flowable.engine.impl.util.CommandContextUtil; -import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.task.api.TaskInfo; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.stream.Collectors; - -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; - -/** - * Flowable 相关的工具方法 - * - * @author ZT - */ -public class FlowableUtils { - - // ========== User 相关的工具方法 ========== - - public static void setAuthenticatedUserId(Long userId) { - Authentication.setAuthenticatedUserId(String.valueOf(userId)); - } - - public static void clearAuthenticatedUserId() { - Authentication.setAuthenticatedUserId(null); - } - - public static V executeAuthenticatedUserId(Long userId, Callable callable) { - setAuthenticatedUserId(userId); - try { - return callable.call(); - } catch (Exception e) { - throw new RuntimeException(e); - } finally { - clearAuthenticatedUserId(); - } - } - - public static String getTenantId() { - Long tenantId = TenantContextHolder.getTenantId(); - return tenantId != null ? String.valueOf(tenantId) : ProcessEngineConfiguration.NO_TENANT_ID; - } - - public static void execute(String tenantIdStr, Runnable runnable) { - if (ObjectUtil.isEmpty(tenantIdStr) - || Objects.equals(tenantIdStr, ProcessEngineConfiguration.NO_TENANT_ID)) { - runnable.run(); - } else { - Long tenantId = Long.valueOf(tenantIdStr); - TenantUtils.execute(tenantId, runnable); - } - } - - @SneakyThrows - public static V execute(String tenantIdStr, Callable callable) { - if (ObjectUtil.isEmpty(tenantIdStr) - || Objects.equals(tenantIdStr, ProcessEngineConfiguration.NO_TENANT_ID)) { - return callable.call(); - } else { - Long tenantId = Long.valueOf(tenantIdStr); - return TenantUtils.execute(tenantId, callable); - } - } - - // ========== Execution 相关的工具方法 ========== - - /** - * 格式化多实例(并签、或签)的 collectionVariable 变量(多实例对应的多审批人列表) - * - * @param activityId 活动编号 - * @return collectionVariable 变量 - */ - public static String formatExecutionCollectionVariable(String activityId) { - return activityId + "_assignees"; - } - - /** - * 格式化多实例(并签、或签)的 collectionElementVariable 变量(当前实例对应的一个审批人) - * - * @param activityId 活动编号 - * @return collectionElementVariable 变量 - */ - public static String formatExecutionCollectionElementVariable(String activityId) { - return activityId + "_assignee"; - } - - // ========== ProcessInstance 相关的工具方法 ========== - - public static Integer getProcessInstanceStatus(ProcessInstance processInstance) { - return getProcessInstanceStatus(processInstance.getProcessVariables()); - } - - public static Integer getProcessInstanceStatus(HistoricProcessInstance processInstance) { - return getProcessInstanceStatus(processInstance.getProcessVariables()); - } - - /** - * 获得流程实例的状态 - * - * @param processVariables 流程实例的 variables - * @return 状态 - */ - private static Integer getProcessInstanceStatus(Map processVariables) { - return (Integer) processVariables.get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); - } - - /** - * 获得流程实例的审批原因 - * - * @param processInstance 流程实例 - * @return 审批原因 - */ - public static String getProcessInstanceReason(HistoricProcessInstance processInstance) { - return (String) processInstance.getProcessVariables().get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON); - } - - /** - * 获得流程实例的表单 - * - * @param processInstance 流程实例 - * @return 表单 - */ - public static Map getProcessInstanceFormVariable(ProcessInstance processInstance) { - Map processVariables = new HashMap<>(processInstance.getProcessVariables()); - return filterProcessInstanceFormVariable(processVariables); - } - - /** - * 获得流程实例的表单 - * - * @param processInstance 流程实例 - * @return 表单 - */ - public static Map getProcessInstanceFormVariable(HistoricProcessInstance processInstance) { - Map processVariables = new HashMap<>(processInstance.getProcessVariables()); - return filterProcessInstanceFormVariable(processVariables); - } - - /** - * 过滤流程实例的表单 - * - * 为什么要过滤?目前使用 processVariables 存储所有流程实例的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示 - * - * @param processVariables 流程实例的 variables - * @return 过滤后的表单 - */ - public static Map filterProcessInstanceFormVariable(Map processVariables) { - processVariables.remove(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); - return processVariables; - } - - /** - * 获得流程实例的发起用户选择的审批人 Map - * - * @param processInstance 流程实例 - * @return 发起用户选择的审批人 Map - */ - public static Map> getStartUserSelectAssignees(ProcessInstance processInstance) { - return processInstance != null ? getStartUserSelectAssignees(processInstance.getProcessVariables()) : null; - } - - /** - * 获得流程实例的发起用户选择的审批人 Map - * - * @param processVariables 流程变量 - * @return 发起用户选择的审批人 Map - */ - @SuppressWarnings("unchecked") - public static Map> getStartUserSelectAssignees(Map processVariables) { - if (processVariables == null) { - return new HashMap<>(); - } - return (Map>) processVariables.get( - BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES); - } - - /** - * 获得流程实例的审批用户选择的下一个节点的审批人 Map - * - * @param processInstance 流程实例 - * @return 审批用户选择的下一个节点的审批人Map - */ - public static Map> getApproveUserSelectAssignees(ProcessInstance processInstance) { - return processInstance != null ? getApproveUserSelectAssignees(processInstance.getProcessVariables()) : null; - } - - /** - * 获得流程实例的审批用户选择的下一个节点的审批人 Map - * - * @param processVariables 流程变量 - * @return 审批用户选择的下一个节点的审批人Map Map - */ - @SuppressWarnings("unchecked") - public static Map> getApproveUserSelectAssignees(Map processVariables) { - if (processVariables == null) { - return new HashMap<>(); - } - return (Map>) processVariables.get( - BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); - } - - /** - * 获得流程实例的摘要 - * - * 仅有 {@link BpmModelFormTypeEnum#getType()} 表单,才有摘要。 - * 原因是,只有它才有表单项的配置,从而可以根据配置,展示摘要。 - * - * @param processDefinitionInfo 流程定义 - * @param processVariables 流程实例的 variables - * @return 摘要 - */ - public static List> getSummary(BpmProcessDefinitionInfoDO processDefinitionInfo, - Map processVariables) { - // 只有流程表单才会显示摘要! - if (ObjectUtil.isNull(processDefinitionInfo) - || !BpmModelFormTypeEnum.NORMAL.getType().equals(processDefinitionInfo.getFormType())) { - return null; - } - - // 解析表单配置 - Map formFieldsMap = new HashMap<>(); - processDefinitionInfo.getFormFields().forEach(formFieldStr -> { - BpmFormFieldVO formField = JsonUtils.parseObject(formFieldStr, BpmFormFieldVO.class); - if (formField != null) { - formFieldsMap.put(formField.getField(), formField); - } - }); - - // 情况一:当自定义了摘要 - if (ObjectUtil.isNotNull(processDefinitionInfo.getSummarySetting()) - && Boolean.TRUE.equals(processDefinitionInfo.getSummarySetting().getEnable())) { - return convertList(processDefinitionInfo.getSummarySetting().getSummary(), item -> { - BpmFormFieldVO formField = formFieldsMap.get(item); - if (formField != null) { - return new KeyValue(formField.getTitle(), - processVariables.getOrDefault(item, "").toString()); - } - return null; - }); - } - - // 情况二:默认摘要展示前三个表单字段 - return formFieldsMap.entrySet().stream() - .limit(3) - .map(entry -> new KeyValue<>(entry.getValue().getTitle(), - MapUtil.getStr(processVariables, entry.getValue().getField(), ""))) - .collect(Collectors.toList()); - } - - // ========== Task 相关的工具方法 ========== - - /** - * 获得任务的状态 - * - * @param task 任务 - * @return 状态 - */ - public static Integer getTaskStatus(TaskInfo task) { - return (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); - } - - /** - * 获得任务的审批原因 - * - * @param task 任务 - * @return 审批原因 - */ - public static String getTaskReason(TaskInfo task) { - return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_REASON); - } - - /** - * 获得任务的签名图片 URL - * - * @param task 任务 - * @return 签名图片 URL - */ - public static String getTaskSignPicUrl(TaskInfo task) { - return (String) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_SIGN_PIC_URL); - } - - /** - * 获得任务的表单 - * - * @param task 任务 - * @return 表单 - */ - public static Map getTaskFormVariable(TaskInfo task) { - Map formVariables = new HashMap<>(task.getTaskLocalVariables()); - filterTaskFormVariable(formVariables); - return formVariables; - } - - /** - * 过滤任务的表单 - * - * 为什么要过滤?目前使用 taskLocalVariables 存储所有任务的拓展字段,需要过滤掉一部分的系统字段,从而实现表单的展示 - * - * @param taskLocalVariables 任务的 taskLocalVariables - * @return 过滤后的表单 - */ - public static Map filterTaskFormVariable(Map taskLocalVariables) { - taskLocalVariables.remove(BpmnVariableConstants.TASK_VARIABLE_STATUS); - taskLocalVariables.remove(BpmnVariableConstants.TASK_VARIABLE_REASON); - return taskLocalVariables; - } - - // ========== Expression 相关的工具方法 ========== - - private static Object getExpressionValue(VariableContainer variableContainer, String expressionString, - ProcessEngineConfigurationImpl processEngineConfiguration) { - assert processEngineConfiguration != null; - ExpressionManager expressionManager = processEngineConfiguration.getExpressionManager(); - assert expressionManager != null; - Expression expression = expressionManager.createExpression(expressionString); - return expression.getValue(variableContainer); - } - - public static Object getExpressionValue(VariableContainer variableContainer, String expressionString) { - ProcessEngineConfigurationImpl processEngineConfiguration = CommandContextUtil.getProcessEngineConfiguration(); - if (processEngineConfiguration != null) { - return getExpressionValue(variableContainer, expressionString, processEngineConfiguration); - } - // 如果 ProcessEngineConfigurationImpl 获取不到,则需要通过 ManagementService 来获取 - ManagementService managementService = SpringUtil.getBean(ManagementService.class); - assert managementService != null; - return managementService.executeCommand(context -> - getExpressionValue(variableContainer, expressionString, CommandContextUtil.getProcessEngineConfiguration())); - } - - public static Object getExpressionValue(Map variable, String expressionString) { - VariableContainer variableContainer = new MapDelegateVariableContainer(variable, VariableContainer.empty()); - return getExpressionValue(variableContainer, expressionString); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java deleted file mode 100644 index 562310a..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ /dev/null @@ -1,1007 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.util; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.*; -import com.zt.plat.framework.common.util.collection.CollectionUtils; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.ConditionGroups; -import com.zt.plat.module.bpm.enums.definition.*; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; -import com.zt.plat.module.bpm.framework.flowable.core.listener.BpmCopyTaskDelegate; -import com.zt.plat.module.bpm.framework.flowable.core.listener.BpmTriggerTaskDelegate; -import com.zt.plat.module.bpm.service.task.listener.BpmCallActivityListener; -import com.zt.plat.module.bpm.service.task.listener.BpmUserTaskListener; -import org.flowable.bpmn.BpmnAutoLayout; -import org.flowable.bpmn.constants.BpmnXMLConstants; -import org.flowable.bpmn.model.*; -import org.flowable.bpmn.model.Process; -import org.flowable.engine.delegate.ExecutionListener; -import org.flowable.engine.delegate.TaskListener; - -import java.util.*; - -import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.*; -import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; -import static java.util.Arrays.asList; - -/** - * 仿钉钉/飞书的模型相关的工具方法 - *

- * 1. 核心的逻辑实现,可见 {@link #buildBpmnModel(String, String, BpmSimpleModelNodeVO)} 方法 - * 2. 所有的 BpmSimpleModelNodeVO 转换成 BPMN FlowNode 元素,可见 {@link NodeConvert} 实现类 - * - * @author jason - */ -public class SimpleModelUtils { - - private static final Map NODE_CONVERTS = MapUtil.newHashMap(); - - static { - List converts = asList(new StartNodeConvert(), new EndNodeConvert(), - new StartUserNodeConvert(), new ApproveNodeConvert(), new CopyNodeConvert(), new TransactorNodeConvert(), - new DelayTimerNodeConvert(), new TriggerNodeConvert(), - new ConditionBranchNodeConvert(), new ParallelBranchNodeConvert(), new InclusiveBranchNodeConvert(), new RouteBranchNodeConvert(), - new ChildProcessConvert()); - converts.forEach(convert -> NODE_CONVERTS.put(convert.getType(), convert)); - } - - /** - * 仿钉钉流程设计模型数据结构(json)转换成 Bpmn Model - *

- * 整体逻辑如下: - * 1. 创建:BpmnModel、Process 对象 - * 2. 转换:将 BpmSimpleModelNodeVO 转换成 BPMN FlowNode 元素 - * 3. 连接:构建并添加节点之间的连线 Sequence Flow - * - * @param processId 流程标识 - * @param processName 流程名称 - * @param simpleModelNode 仿钉钉流程设计模型数据结构 - * @return Bpmn Model - */ - public static BpmnModel buildBpmnModel(String processId, String processName, BpmSimpleModelNodeVO simpleModelNode) { - // 1. 创建 BpmnModel - BpmnModel bpmnModel = new BpmnModel(); - bpmnModel.setTargetNamespace(BpmnXMLConstants.BPMN2_NAMESPACE); // 设置命名空间。不加这个,解析 Message 会报 NPE 异常 - // 创建 Process 对象 - Process process = new Process(); - process.setId(processId); - process.setName(processName); - process.setExecutable(Boolean.TRUE); - bpmnModel.addProcess(process); - - // 2.1 创建 StartNode 节点 - // 原因是:目前前端的第一个节点是“发起人节点”,所以这里构建一个 StartNode,用于创建 Bpmn 的 StartEvent 节点 - BpmSimpleModelNodeVO startNode = buildStartNode(); - startNode.setChildNode(simpleModelNode); - // 2.2 将前端传递的 simpleModelNode 数据结构(json),转换成从 BPMN FlowNode 元素,并添加到 Main Process 中 - traverseNodeToBuildFlowNode(startNode, process); - - // 3. 构建并添加节点之间的连线 Sequence Flow - EndEvent endEvent = getEndEvent(bpmnModel); - traverseNodeToBuildSequenceFlow(process, startNode, endEvent.getId()); - - // 4. 自动布局 - new BpmnAutoLayout(bpmnModel).execute(); - return bpmnModel; - } - - private static BpmSimpleModelNodeVO buildStartNode() { - return new BpmSimpleModelNodeVO().setId(START_EVENT_NODE_ID) - .setName(BpmSimpleModelNodeTypeEnum.START_NODE.getName()) - .setType(BpmSimpleModelNodeTypeEnum.START_NODE.getType()); - } - - /** - * 遍历节点,构建 FlowNode 元素 - * - * @param node SIMPLE 节点 - * @param process BPMN 流程 - */ - private static void traverseNodeToBuildFlowNode(BpmSimpleModelNodeVO node, Process process) { - // 1. 判断是否有效节点 - if (!isValidNode(node)) { - return; - } - BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType()); - Assert.notNull(nodeType, "模型节点类型({})不支持", node.getType()); - - // 2. 处理当前节点 - NodeConvert nodeConvert = NODE_CONVERTS.get(nodeType); - Assert.notNull(nodeConvert, "模型节点类型的转换器({})不存在", node.getType()); - List flowElements = nodeConvert.convertList(node); - flowElements.forEach(process::addFlowElement); - - // 3.1 情况一:如果当前是分支节点,并且存在条件节点,则处理每个条件的子节点 - if (BpmSimpleModelNodeTypeEnum.isBranchNode(node.getType()) - && CollUtil.isNotEmpty(node.getConditionNodes())) { - // 注意:这里的 item.getChildNode() 处理的是每个条件的子节点,不是处理条件 - node.getConditionNodes().forEach(item -> traverseNodeToBuildFlowNode(item.getChildNode(), process)); - } - - // 3.2 情况二:如果有“子”节点,则递归处理子节点 - traverseNodeToBuildFlowNode(node.getChildNode(), process); - } - - /** - * 遍历节点,构建 SequenceFlow 元素 - * - * @param process Bpmn 流程 - * @param node 当前节点 - * @param targetNodeId 目标节点 ID - */ - private static void traverseNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { - // 1.1 无效节点返回 - if (!isValidNode(node)) { - return; - } - // 1.2 END_NODE 直接返回 - BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType()); - Assert.notNull(nodeType, "模型节点类型不支持"); - if (nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { - return; - } - - // 2.1 情况一:普通节点 - if (!BpmSimpleModelNodeTypeEnum.isBranchNode(node.getType())) { - traverseNormalNodeToBuildSequenceFlow(process, node, targetNodeId); - } else { - // 2.2 情况二:分支节点 - traverseBranchNodeToBuildSequenceFlow(process, node, targetNodeId); - } - } - - /** - * 遍历普通(非条件)节点,构建 SequenceFlow 元素 - * - * @param process Bpmn 流程 - * @param node 当前节点 - * @param targetNodeId 目标节点 ID - */ - private static void traverseNormalNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { - BpmSimpleModelNodeVO childNode = node.getChildNode(); - boolean isChildNodeValid = isValidNode(childNode); - // 情况一:有“子”节点,则建立连线 - // 情况二:没有“子节点”,则直接跟 targetNodeId 建立连线。例如说,结束节点、条件分支(分支节点的孩子节点或聚合节点)的最后一个节点 - String finalTargetNodeId = isChildNodeValid ? childNode.getId() : targetNodeId; - - // 如果没有附加节点:则直接建立连线 - if (StrUtil.isEmpty(node.getAttachNodeId())) { - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), finalTargetNodeId); - process.addFlowElement(sequenceFlow); - } else { - // 如果有附加节点:需要先建立和附加节点的连线,再建立附加节点和目标节点的连线。例如说,触发器节点(HTTP 回调) - List sequenceFlows = buildAttachNodeSequenceFlow(node.getId(), node.getAttachNodeId(), finalTargetNodeId); - sequenceFlows.forEach(process::addFlowElement); - } - - // 因为有子节点,递归调用后续子节点 - if (isChildNodeValid) { - traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId); - } - } - - /** - * 构建有附加节点的连线 - * - * @param nodeId 当前节点 ID - * @param attachNodeId 附属节点 ID - * @param targetNodeId 目标节点 ID - */ - private static List buildAttachNodeSequenceFlow(String nodeId, String attachNodeId, String targetNodeId) { - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(nodeId, attachNodeId, null, null, null); - SequenceFlow attachSequenceFlow = buildBpmnSequenceFlow(attachNodeId, targetNodeId, null, null, null); - return CollUtil.newArrayList(sequenceFlow, attachSequenceFlow); - } - - /** - * 遍历条件节点,构建 SequenceFlow 元素 - * - * @param process Bpmn 流程 - * @param node 当前节点 - * @param targetNodeId 目标节点 ID - */ - private static void traverseBranchNodeToBuildSequenceFlow(Process process, BpmSimpleModelNodeVO node, String targetNodeId) { - BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType()); - BpmSimpleModelNodeVO childNode = node.getChildNode(); - List conditionNodes = node.getConditionNodes(); - // TODO @ZT 路由分支没有conditionNodes 这里注释会影响吗?@jason:一起帮忙瞅瞅! -// Assert.notEmpty(conditionNodes, "分支节点的条件节点不能为空"); - // 分支终点节点 ID - String branchEndNodeId = null; - if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE - || nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) { // 条件分支或路由分支 - // 分两种情况 1. 分支节点有孩子节点为孩子节点 Id 2. 分支节点孩子为无效节点时 (分支嵌套且为分支最后一个节点) 为分支终点节点 ID - branchEndNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; - } else if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE - || nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { // 并行分支或包容分支 - // 分支节点:分支终点节点 Id 为程序创建的网关集合节点。目前不会从前端传入。 - branchEndNodeId = buildGatewayJoinId(node.getId()); - } - Assert.notEmpty(branchEndNodeId, "分支终点节点 Id 不能为空"); - - // 3. 遍历分支节点 - if (nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) { - // 路由分支遍历 - for (BpmSimpleModelNodeVO.RouterSetting router : node.getRouterGroups()) { - SequenceFlow sequenceFlow = RouteBranchNodeConvert.buildSequenceFlow(node.getId(), router); - process.addFlowElement(sequenceFlow); - } - } else { - // 下面的注释,以如下情况举例子。分支 1:A->B->C->D->E,分支 2:A->D->E。其中,A 为分支节点, D 为 A 孩子节点 - for (BpmSimpleModelNodeVO item : conditionNodes) { - Assert.isTrue(Objects.equals(item.getType(), BpmSimpleModelNodeTypeEnum.CONDITION_NODE.getType()), - "条件节点类型({})不符合", item.getType()); - BpmSimpleModelNodeVO conditionChildNode = item.getChildNode(); - // 3.1 分支有后续节点。即分支 1: A->B->C->D 的情况 - if (isValidNode(conditionChildNode)) { - // 3.1.1 建立与后续的节点的连线。例如说,建立 A->B 的连线 - SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), conditionChildNode.getId(), item); - process.addFlowElement(sequenceFlow); - // 3.1.2 递归调用后续节点连线。例如说,建立 B->C->D 的连线 - traverseNodeToBuildSequenceFlow(process, conditionChildNode, branchEndNodeId); - } else { - // 3.2 分支没有后续节点。例如说,建立 A->D 的连线 - SequenceFlow sequenceFlow = ConditionNodeConvert.buildSequenceFlow(node.getId(), branchEndNodeId, item); - process.addFlowElement(sequenceFlow); - } - } - } - - // 4.1 如果是并行分支、包容分支,由于是程序创建的聚合网关,需要手工创建聚合网关和下一个节点的连线 - if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE - || nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { - String nextNodeId = isValidNode(childNode) ? childNode.getId() : targetNodeId; - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(branchEndNodeId, nextNodeId); - process.addFlowElement(sequenceFlow); - // 4.2 如果是路由分支,需要连接后续节点为默认路由 - } else if (nodeType == BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE) { - SequenceFlow sequenceFlow = buildBpmnSequenceFlow(node.getId(), branchEndNodeId, node.getRouterDefaultFlowId(), - null, null); - process.addFlowElement(sequenceFlow); - } - - // 5. 递归调用后续节点 继续递归。例如说,建立 D->E 的连线 - traverseNodeToBuildSequenceFlow(process, childNode, targetNodeId); - } - - private static SequenceFlow buildBpmnSequenceFlow(String sourceId, String targetId) { - return buildBpmnSequenceFlow(sourceId, targetId, null, null, null); - } - - private static SequenceFlow buildBpmnSequenceFlow(String sourceId, String targetId, - String sequenceFlowId, String sequenceFlowName, - String conditionExpression) { - Assert.notEmpty(sourceId, "sourceId 不能为空"); - Assert.notEmpty(targetId, "targetId 不能为空"); - // TODO @jason:如果 sequenceFlowId 不存在的时候,是不是要生成一个默认的 sequenceFlowId? @ZT: 貌似不需要,Flowable 会默认生成;TODO @jason:建议还是搞一个,主要是后续好排查问题。 - // TODO @jason:如果 name 不存在的时候,是不是要生成一个默认的 name? @ZT: 不需要生成默认的吧? 这个会在流程图展示的, 一般用户填写的。不好生成默认的吧;TODO @jason:建议还是搞一个,主要是后续好排查问题。 - SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId); - if (StrUtil.isNotEmpty(sequenceFlowId)) { - sequenceFlow.setId(sequenceFlowId); - } - if (StrUtil.isNotEmpty(sequenceFlowName)) { - sequenceFlow.setName(sequenceFlowName); - } - if (StrUtil.isNotEmpty(conditionExpression)) { - sequenceFlow.setConditionExpression(conditionExpression); - } - return sequenceFlow; - } - - public static boolean isValidNode(BpmSimpleModelNodeVO node) { - return node != null && node.getId() != null; - } - - public static boolean isSequentialApproveNode(BpmSimpleModelNodeVO node) { - return BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType().equals(node.getType()) - && BpmUserTaskApproveMethodEnum.SEQUENTIAL.getMethod().equals(node.getApproveMethod()); - } - - // ========== 各种 convert 节点的方法: BpmSimpleModelNodeVO => BPMN FlowElement ========== - - private interface NodeConvert { - - default List convertList(BpmSimpleModelNodeVO node) { - return Collections.singletonList(convert(node)); - } - - default FlowElement convert(BpmSimpleModelNodeVO node) { - throw new UnsupportedOperationException("请实现该方法"); - } - - BpmSimpleModelNodeTypeEnum getType(); - - } - - private static class StartNodeConvert implements NodeConvert { - - @Override - public StartEvent convert(BpmSimpleModelNodeVO node) { - StartEvent startEvent = new StartEvent(); - startEvent.setId(node.getId()); - startEvent.setName(node.getName()); - return startEvent; - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.START_NODE; - } - - } - - private static class EndNodeConvert implements NodeConvert { - - @Override - public EndEvent convert(BpmSimpleModelNodeVO node) { - EndEvent endEvent = new EndEvent(); - endEvent.setId(node.getId()); - endEvent.setName(node.getName()); - // TODO @ZT + jason:要不要加一个终止定义? - return endEvent; - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.END_NODE; - } - - } - - private static class StartUserNodeConvert implements NodeConvert { - - @Override - public UserTask convert(BpmSimpleModelNodeVO node) { - UserTask userTask = new UserTask(); - userTask.setId(node.getId()); - userTask.setName(node.getName()); - - // 人工审批 - addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, BpmUserTaskApproveTypeEnum.USER.getType()); - // 候选人策略为发起人自己 - addCandidateElements(BpmTaskCandidateStrategyEnum.START_USER.getStrategy(), null, userTask); - // 添加表单字段权限属性元素 - addFormFieldsPermission(node.getFieldsPermission(), userTask); - // 添加操作按钮配置属性元素 - addButtonsSetting(node.getButtonsSetting(), userTask); - // 使用自动通过策略 - // TODO @ZT 复用了SKIP, 是否需要新加一个策略;TODO @ZT:【回复】是不是应该类似飞书,搞个草稿状态。待定;还有一种策略,不标记自动通过,而是首次发起后,第一个节点,自动通过; - addAssignStartUserHandlerType(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType(), userTask); - return userTask; - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.START_USER_NODE; - } - - } - - private static class ApproveNodeConvert implements NodeConvert { - - @Override - public List convertList(BpmSimpleModelNodeVO node) { - List flowElements = new ArrayList<>(2); - // 1. 构建用户任务 - UserTask userTask = buildBpmnUserTask(node); - flowElements.add(userTask); - - // 2. 添加用户任务的 Timer Boundary Event, 用于任务的审批超时处理 - if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) { - BoundaryEvent boundaryEvent = buildUserTaskTimeoutBoundaryEvent(userTask, node.getTimeoutHandler()); - flowElements.add(boundaryEvent); - } - return flowElements; - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.APPROVE_NODE; - } - - /** - * 添加 UserTask 用户的审批超时 BoundaryEvent 事件 - * - * @param userTask 审批任务 - * @param timeoutHandler 超时处理器 - * @return BoundaryEvent 超时事件 - */ - private BoundaryEvent buildUserTaskTimeoutBoundaryEvent(UserTask userTask, - BpmSimpleModelNodeVO.TimeoutHandler timeoutHandler) { - // 1. 创建 Timeout Boundary Event - String timeCycle = null; - if (Objects.equals(BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType(), timeoutHandler.getType()) && - timeoutHandler.getMaxRemindCount() != null && timeoutHandler.getMaxRemindCount() > 1) { - timeCycle = String.format("R%d/%s", - timeoutHandler.getMaxRemindCount(), timeoutHandler.getTimeDuration()); - } - BoundaryEvent boundaryEvent = buildTimeoutBoundaryEvent(userTask, BpmBoundaryEventTypeEnum.USER_TASK_TIMEOUT.getType(), - timeoutHandler.getTimeDuration(), timeCycle, null); - - // 2 添加超时执行动作元素 - addExtensionElement(boundaryEvent, USER_TASK_TIMEOUT_HANDLER_TYPE, timeoutHandler.getType()); - return boundaryEvent; - } - - private UserTask buildBpmnUserTask(BpmSimpleModelNodeVO node) { - UserTask userTask = new UserTask(); - userTask.setId(node.getId()); - userTask.setName(node.getName()); - - // 如果不是审批人节点,则直接返回 - addExtensionElement(userTask, USER_TASK_APPROVE_TYPE, node.getApproveType()); - if (ObjectUtil.notEqual(node.getApproveType(), BpmUserTaskApproveTypeEnum.USER.getType())) { - return userTask; - } - - // 添加候选人元素 - addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), userTask); - // 添加表单字段权限属性元素 - addFormFieldsPermission(node.getFieldsPermission(), userTask); - // 添加操作按钮配置属性元素 - addButtonsSetting(node.getButtonsSetting(), userTask); - // 处理多实例(审批方式) - processMultiInstanceLoopCharacteristics(node.getApproveMethod(), node.getApproveRatio(), userTask); - // 添加任务被拒绝的处理元素 - addTaskRejectElements(node.getRejectHandler(), userTask); - // 添加用户任务的审批人与发起人相同时的处理元素 - addAssignStartUserHandlerType(node.getAssignStartUserHandlerType(), userTask); - // 添加用户任务的空处理元素 - addAssignEmptyHandlerType(node.getAssignEmptyHandler(), userTask); - // 设置审批任务的截止时间 - if (node.getTimeoutHandler() != null && node.getTimeoutHandler().getEnable()) { - userTask.setDueDate(node.getTimeoutHandler().getTimeDuration()); - } - // 设置监听器 - addUserTaskListener(node, userTask); - // 添加是否需要签名 - addSignEnable(node.getSignEnable(), userTask); - // 审批意见 - addReasonRequire(node.getReasonRequire(), userTask); - // 节点类型 - addNodeType(node.getType(), userTask); - return userTask; - } - - private void addUserTaskListener(BpmSimpleModelNodeVO node, UserTask userTask) { - List flowableListeners = new ArrayList<>(3); - if (node.getTaskCreateListener() != null - && Boolean.TRUE.equals(node.getTaskCreateListener().getEnable())) { - FlowableListener flowableListener = new FlowableListener(); - flowableListener.setEvent(TaskListener.EVENTNAME_CREATE); - flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); - addListenerConfig(flowableListener, node.getTaskCreateListener()); - flowableListeners.add(flowableListener); - } - if (node.getTaskAssignListener() != null - && Boolean.TRUE.equals(node.getTaskAssignListener().getEnable())) { - FlowableListener flowableListener = new FlowableListener(); - flowableListener.setEvent(TaskListener.EVENTNAME_ASSIGNMENT); - flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); - addListenerConfig(flowableListener, node.getTaskAssignListener()); - flowableListeners.add(flowableListener); - } - if (node.getTaskCompleteListener() != null - && Boolean.TRUE.equals(node.getTaskCompleteListener().getEnable())) { - FlowableListener flowableListener = new FlowableListener(); - flowableListener.setEvent(TaskListener.EVENTNAME_COMPLETE); - flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation(BpmUserTaskListener.DELEGATE_EXPRESSION); - addListenerConfig(flowableListener, node.getTaskCompleteListener()); - flowableListeners.add(flowableListener); - } - if (CollUtil.isNotEmpty(flowableListeners)) { - userTask.setTaskListeners(flowableListeners); - } - } - - private void processMultiInstanceLoopCharacteristics(Integer approveMethod, Integer approveRatio, UserTask userTask) { - BpmUserTaskApproveMethodEnum approveMethodEnum = BpmUserTaskApproveMethodEnum.valueOf(approveMethod); - Assert.notNull(approveMethodEnum, "审批方式({})不能为空", approveMethodEnum); - // 添加审批方式的扩展属性 - addExtensionElement(userTask, USER_TASK_APPROVE_METHOD, approveMethod); - if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RANDOM) { - // 随机审批,不需要设置多实例属性 - return; - } - - // 处理多实例审批方式 - MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); - // 设置 collectionVariable。本系统用不到,仅仅为了 Flowable 校验不报错 - multiInstanceCharacteristics.setInputDataItem("${coll_userList}"); - if (approveMethodEnum == BpmUserTaskApproveMethodEnum.ANY) { - multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition()); - multiInstanceCharacteristics.setSequential(false); - } else if (approveMethodEnum == BpmUserTaskApproveMethodEnum.SEQUENTIAL) { - multiInstanceCharacteristics.setCompletionCondition(approveMethodEnum.getCompletionCondition()); - multiInstanceCharacteristics.setSequential(true); - multiInstanceCharacteristics.setLoopCardinality("1"); - } else if (approveMethodEnum == BpmUserTaskApproveMethodEnum.RATIO) { - Assert.notNull(approveRatio, "通过比例不能为空"); - multiInstanceCharacteristics.setCompletionCondition( - String.format(approveMethodEnum.getCompletionCondition(), String.format("%.2f", approveRatio / 100D))); - multiInstanceCharacteristics.setSequential(false); - } - userTask.setLoopCharacteristics(multiInstanceCharacteristics); - } - - } - - private static class TransactorNodeConvert extends ApproveNodeConvert { - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE; - } - - } - - private static class CopyNodeConvert implements NodeConvert { - - @Override - public ServiceTask convert(BpmSimpleModelNodeVO node) { - ServiceTask serviceTask = new ServiceTask(); - serviceTask.setId(node.getId()); - serviceTask.setName(node.getName()); - serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - serviceTask.setImplementation("${" + BpmCopyTaskDelegate.BEAN_NAME + "}"); - - // 添加抄送候选人元素 - addCandidateElements(node.getCandidateStrategy(), node.getCandidateParam(), serviceTask); - // 添加表单字段权限属性元素 - addFormFieldsPermission(node.getFieldsPermission(), serviceTask); - return serviceTask; - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.COPY_NODE; - } - - } - - private static class ConditionBranchNodeConvert implements NodeConvert { - - @Override - public ExclusiveGateway convert(BpmSimpleModelNodeVO node) { - ExclusiveGateway exclusiveGateway = new ExclusiveGateway(); - exclusiveGateway.setId(node.getId()); - // TODO @jason:setName - - // 设置默认的序列流(条件) - BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(), - item -> BooleanUtil.isTrue(item.getConditionSetting().getDefaultFlow())); - Assert.notNull(defaultSeqFlow, "条件分支节点({})的默认序列流不能为空", node.getId()); - exclusiveGateway.setDefaultFlow(defaultSeqFlow.getId()); - return exclusiveGateway; - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE; - } - - } - - private static class ParallelBranchNodeConvert implements NodeConvert { - - @Override - public List convertList(BpmSimpleModelNodeVO node) { - ParallelGateway parallelGateway = new ParallelGateway(); - parallelGateway.setId(node.getId()); - // TODO @jason:setName - - // 并行聚合网关由程序创建,前端不需要传入 - ParallelGateway joinParallelGateway = new ParallelGateway(); - joinParallelGateway.setId(buildGatewayJoinId(node.getId())); - // TODO @jason:setName - return CollUtil.newArrayList(parallelGateway, joinParallelGateway); - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE; - } - - } - - private static class InclusiveBranchNodeConvert implements NodeConvert { - - @Override - public List convertList(BpmSimpleModelNodeVO node) { - InclusiveGateway inclusiveGateway = new InclusiveGateway(); - inclusiveGateway.setId(node.getId()); - // 设置默认的序列流(条件) - BpmSimpleModelNodeVO defaultSeqFlow = CollUtil.findOne(node.getConditionNodes(), - item -> BooleanUtil.isTrue(item.getConditionSetting().getDefaultFlow())); - Assert.notNull(defaultSeqFlow, "包容分支节点({})的默认序列流不能为空", node.getId()); - inclusiveGateway.setDefaultFlow(defaultSeqFlow.getId()); - // TODO @jason:setName - - // 并行聚合网关由程序创建,前端不需要传入 - InclusiveGateway joinInclusiveGateway = new InclusiveGateway(); - joinInclusiveGateway.setId(buildGatewayJoinId(node.getId())); - // TODO @jason:setName - return CollUtil.newArrayList(inclusiveGateway, joinInclusiveGateway); - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE; - } - - } - - public static class ConditionNodeConvert implements NodeConvert { - - @Override - public List convertList(BpmSimpleModelNodeVO node) { - // 原因是:正常情况下,它不会被调用到 - throw new UnsupportedOperationException("条件节点不支持转换"); - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.CONDITION_NODE; - } - - public static SequenceFlow buildSequenceFlow(String sourceId, String targetId, - BpmSimpleModelNodeVO node) { - String conditionExpression = buildConditionExpression(node.getConditionSetting()); - return buildBpmnSequenceFlow(sourceId, targetId, node.getId(), node.getName(), conditionExpression); - } - } - - /** - * 构造条件表达式 - */ - public static String buildConditionExpression(BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { - // 并行网关不需要设置条件 - if (conditionSetting == null) { - return null; - } - return buildConditionExpression(conditionSetting.getConditionType(), conditionSetting.getConditionExpression(), - conditionSetting.getConditionGroups()); - } - - public static String buildConditionExpression(BpmSimpleModelNodeVO.RouterSetting routerSetting) { - return buildConditionExpression(routerSetting.getConditionType(), routerSetting.getConditionExpression(), - routerSetting.getConditionGroups()); - } - - public static String buildConditionExpression(Integer conditionType, String conditionExpression, ConditionGroups conditionGroups) { - BpmSimpleModeConditionTypeEnum conditionTypeEnum = BpmSimpleModeConditionTypeEnum.valueOf(conditionType); - if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.EXPRESSION) { - return conditionExpression; - } - if (conditionTypeEnum == BpmSimpleModeConditionTypeEnum.RULE) { - if (conditionGroups == null || CollUtil.isEmpty(conditionGroups.getConditions())) { - return null; - } - List strConditionGroups = CollectionUtils.convertList(conditionGroups.getConditions(), item -> { - if (CollUtil.isEmpty(item.getRules())) { - return ""; - } - // 构造规则表达式 - List list = CollectionUtils.convertList(item.getRules(), (rule) -> { - String rightSide = NumberUtil.isNumber(rule.getRightSide()) ? rule.getRightSide() - : "\"" + rule.getRightSide() + "\""; // 如果非数值类型加引号 - return String.format(" %s %s var:convertByType(%s,%s)", rule.getLeftSide(), rule.getOpCode(), rule.getLeftSide(), rightSide); - }); - // 构造条件组的表达式 - Boolean and = item.getAnd(); - return "(" + CollUtil.join(list, and ? " && " : " || ") + ")"; - }); - return String.format("${%s}", CollUtil.join(strConditionGroups, conditionGroups.getAnd() ? " && " : " || ")); - } - return null; - } - - public static class DelayTimerNodeConvert implements NodeConvert { - - @Override - public List convertList(BpmSimpleModelNodeVO node) { - List flowElements = new ArrayList<>(2); - // 1. 构建接收任务,通过接收任务可卡住节点 - ReceiveTask receiveTask = new ReceiveTask(); - receiveTask.setId(node.getId()); - receiveTask.setName(node.getName()); - flowElements.add(receiveTask); - - // 2. 添加接收任务的 Timer Boundary Event - if (node.getDelaySetting() != null) { - BoundaryEvent boundaryEvent = null; - if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { - boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), - node.getDelaySetting().getDelayTime(), null, null); - } else if (node.getDelaySetting().getDelayType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { - boundaryEvent = buildTimeoutBoundaryEvent(receiveTask, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), - null, null, node.getDelaySetting().getDelayTime()); - } else { - throw new UnsupportedOperationException("不支持的延迟类型:" + node.getDelaySetting()); - } - flowElements.add(boundaryEvent); - } - return flowElements; - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.DELAY_TIMER_NODE; - } - } - - public static class TriggerNodeConvert implements NodeConvert { - - @Override - public List convertList(BpmSimpleModelNodeVO node) { - Assert.notNull(node.getTriggerSetting(), "触发器节点设置不能为空"); - List flowElements = new ArrayList<>(2); - // HTTP 回调请求。需要附加一个 ReceiveTask、发起请求后、等待回调执行 - if (BpmTriggerTypeEnum.HTTP_CALLBACK.getType().equals(node.getTriggerSetting().getType())) { - Assert.notNull(node.getTriggerSetting().getHttpRequestSetting(), "触发器 HTTP 回调请求设置不能为空"); - ReceiveTask receiveTask = new ReceiveTask(); - receiveTask.setId("Activity_" + IdUtil.fastUUID()); - receiveTask.setName("HTTP 回调"); - node.setAttachNodeId(receiveTask.getId()); - flowElements.add(receiveTask); - // 重要:设置 callbackTaskDefineKey,用于 HTTP 回调 - node.getTriggerSetting().getHttpRequestSetting().setCallbackTaskDefineKey(receiveTask.getId()); - } - - // 触发器使用 ServiceTask 来实现 - ServiceTask serviceTask = new ServiceTask(); - serviceTask.setId(node.getId()); - serviceTask.setName(node.getName()); - serviceTask.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - serviceTask.setImplementation("${" + BpmTriggerTaskDelegate.BEAN_NAME + "}"); - addExtensionElement(serviceTask, TRIGGER_TYPE, node.getTriggerSetting().getType()); - if (node.getTriggerSetting().getHttpRequestSetting() != null) { - addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getHttpRequestSetting()); - } - if (node.getTriggerSetting().getFormSettings() != null) { - addExtensionElementJson(serviceTask, TRIGGER_PARAM, node.getTriggerSetting().getFormSettings()); - } - flowElements.add(serviceTask); - return flowElements; - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.TRIGGER_NODE; - } - } - - public static class RouteBranchNodeConvert implements NodeConvert { - - @Override - public ExclusiveGateway convert(BpmSimpleModelNodeVO node) { - ExclusiveGateway exclusiveGateway = new ExclusiveGateway(); - exclusiveGateway.setId(node.getId()); - - // 设置默认的序列流(条件) - node.setRouterDefaultFlowId("Flow_" + IdUtil.fastUUID()); - exclusiveGateway.setDefaultFlow(node.getRouterDefaultFlowId()); - return exclusiveGateway; - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.ROUTER_BRANCH_NODE; - } - - public static SequenceFlow buildSequenceFlow(String nodeId, BpmSimpleModelNodeVO.RouterSetting router) { - String conditionExpression = SimpleModelUtils.buildConditionExpression(router); - return buildBpmnSequenceFlow(nodeId, router.getNodeId(), null, null, conditionExpression); - } - - } - - private static class ChildProcessConvert implements NodeConvert { - - @Override - public List convertList(BpmSimpleModelNodeVO node) { - List flowElements = new ArrayList<>(2); - BpmSimpleModelNodeVO.ChildProcessSetting childProcessSetting = node.getChildProcessSetting(); - List inVariables = childProcessSetting.getInVariables() == null ? - new ArrayList<>() : new ArrayList<>(childProcessSetting.getInVariables()); - CallActivity callActivity = new CallActivity(); - callActivity.setId(node.getId()); - callActivity.setName(node.getName()); - callActivity.setCalledElementType("key"); - // 1. 是否异步 - if (node.getChildProcessSetting().getAsync()) { - callActivity.setAsynchronous(true); - } - - // 2. 调用的子流程 - callActivity.setCalledElement(childProcessSetting.getCalledProcessDefinitionKey()); - callActivity.setProcessInstanceName(childProcessSetting.getCalledProcessDefinitionName()); - - // 3. 是否自动跳过子流程发起节点 - IOParameter ioParameter = new IOParameter(); - ioParameter.setSourceExpression(childProcessSetting.getSkipStartUserNode().toString()); - ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE); - inVariables.add(ioParameter); - - // 4. 【默认需要传递的一些变量】流程状态 - ioParameter = new IOParameter(); - ioParameter.setSource(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); - ioParameter.setTarget(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); - inVariables.add(ioParameter); - - // 5. 主→子变量传递、子->主变量传递 - callActivity.setInParameters(inVariables); - if (ArrayUtil.isNotEmpty(childProcessSetting.getOutVariables()) && ObjUtil.notEqual(childProcessSetting.getAsync(), Boolean.TRUE)) { - callActivity.setOutParameters(childProcessSetting.getOutVariables()); - } - - // 6. 子流程发起人配置 - List executionListeners = new ArrayList<>(); - FlowableListener flowableListener = new FlowableListener(); - flowableListener.setEvent(ExecutionListener.EVENTNAME_START); - flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION); - flowableListener.setImplementation(BpmCallActivityListener.DELEGATE_EXPRESSION); - FieldExtension fieldExtension = new FieldExtension(); - fieldExtension.setFieldName("listenerConfig"); - fieldExtension.setStringValue(JsonUtils.toJsonString(childProcessSetting.getStartUserSetting())); - flowableListener.getFieldExtensions().add(fieldExtension); - executionListeners.add(flowableListener); - callActivity.setExecutionListeners(executionListeners); - - // 7. 超时设置 - if (childProcessSetting.getTimeoutSetting() != null && Boolean.TRUE.equals(childProcessSetting.getTimeoutSetting().getEnable())) { - BoundaryEvent boundaryEvent = null; - if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_DATE_TIME.getType())) { - boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.DELAY_TIMER_TIMEOUT.getType(), - childProcessSetting.getTimeoutSetting().getTimeExpression(), null, null); - } else if (childProcessSetting.getTimeoutSetting().getType().equals(BpmDelayTimerTypeEnum.FIXED_TIME_DURATION.getType())) { - boundaryEvent = buildTimeoutBoundaryEvent(callActivity, BpmBoundaryEventTypeEnum.CHILD_PROCESS_TIMEOUT.getType(), - null, null, childProcessSetting.getTimeoutSetting().getTimeExpression()); - } - flowElements.add(boundaryEvent); - } - - // 8. 多实例 - if (childProcessSetting.getMultiInstanceSetting() != null && Boolean.TRUE.equals(childProcessSetting.getMultiInstanceSetting().getEnable())) { - MultiInstanceLoopCharacteristics multiInstanceCharacteristics = new MultiInstanceLoopCharacteristics(); - multiInstanceCharacteristics.setSequential(childProcessSetting.getMultiInstanceSetting().getSequential()); - if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.FIXED_QUANTITY.getType())) { - multiInstanceCharacteristics.setLoopCardinality(childProcessSetting.getMultiInstanceSetting().getSource()); - } - if (childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.NUMBER_FORM.getType()) || - childProcessSetting.getMultiInstanceSetting().getSourceType().equals(BpmChildProcessMultiInstanceSourceTypeEnum.MULTIPLE_FORM.getType())) { - multiInstanceCharacteristics.setInputDataItem(childProcessSetting.getMultiInstanceSetting().getSource()); - } - multiInstanceCharacteristics.setCompletionCondition(String.format(BpmUserTaskApproveMethodEnum.RATIO.getCompletionCondition(), - String.format("%.2f", childProcessSetting.getMultiInstanceSetting().getApproveRatio() / 100D))); - callActivity.setLoopCharacteristics(multiInstanceCharacteristics); - addExtensionElement(callActivity, CHILD_PROCESS_MULTI_INSTANCE_SOURCE_TYPE, childProcessSetting.getMultiInstanceSetting().getSourceType()); - } - - // 添加节点类型 - addNodeType(node.getType(), callActivity); - flowElements.add(callActivity); - return flowElements; - } - - @Override - public BpmSimpleModelNodeTypeEnum getType() { - return BpmSimpleModelNodeTypeEnum.CHILD_PROCESS; - } - - } - - private static String buildGatewayJoinId(String id) { - return id + "_join"; - } - - private static BoundaryEvent buildTimeoutBoundaryEvent(Activity attachedToRef, Integer type, - String timeDuration, String timeCycle, String timeDate) { - // 1.1 定时器边界事件 - BoundaryEvent boundaryEvent = new BoundaryEvent(); - boundaryEvent.setId("Event-" + IdUtil.fastUUID()); - boundaryEvent.setCancelActivity(false); // 设置关联的任务为不会被中断 - boundaryEvent.setAttachedToRef(attachedToRef); - // 1.2 定义超时时间表达式 - TimerEventDefinition eventDefinition = new TimerEventDefinition(); - if (ObjUtil.isNotNull(timeDuration)) { - eventDefinition.setTimeDuration(timeDuration); - } - if (ObjUtil.isNotNull(timeDuration)) { - eventDefinition.setTimeCycle(timeCycle); - } - if (ObjUtil.isNotNull(timeDate)) { - eventDefinition.setTimeDate(timeDate); - } - boundaryEvent.addEventDefinition(eventDefinition); - - // 2. 添加定时器边界事件类型 - addExtensionElement(boundaryEvent, BOUNDARY_EVENT_TYPE, type); - return boundaryEvent; - } - - // ========== SIMPLE 流程预测相关的方法 ========== - - public static List simulateProcess(BpmSimpleModelNodeVO rootNode, Map variables) { - List resultNodes = new ArrayList<>(); - - // 从头开始遍历 - simulateNextNode(rootNode, variables, resultNodes); - return resultNodes; - } - - private static void simulateNextNode(BpmSimpleModelNodeVO currentNode, Map variables, - List resultNodes) { - // 如果不合法(包括为空),则直接结束 - if (!isValidNode(currentNode)) { - return; - } - BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(currentNode.getType()); - Assert.notNull(nodeType, "模型节点类型不支持"); - - // 情况:START_NODE/START_USER_NODE/APPROVE_NODE/COPY_NODE/END_NODE/TRANSACTOR_NODE - if (nodeType == BpmSimpleModelNodeTypeEnum.START_NODE - || nodeType == BpmSimpleModelNodeTypeEnum.START_USER_NODE - || nodeType == BpmSimpleModelNodeTypeEnum.APPROVE_NODE - || nodeType == BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE - || nodeType == BpmSimpleModelNodeTypeEnum.COPY_NODE - || nodeType == BpmSimpleModelNodeTypeEnum.CHILD_PROCESS - || nodeType == BpmSimpleModelNodeTypeEnum.END_NODE) { - // 添加元素 - resultNodes.add(currentNode); - } - - // 情况:CONDITION_BRANCH_NODE 排它,只有一个满足条件的。如果没有,就走默认的 - if (nodeType == BpmSimpleModelNodeTypeEnum.CONDITION_BRANCH_NODE) { - // 查找满足条件的 BpmSimpleModelNodeVO 节点 - BpmSimpleModelNodeVO matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) - && evalConditionExpress(variables, conditionNode.getConditionSetting())); - if (matchConditionNode == null) { - matchConditionNode = CollUtil.findOne(currentNode.getConditionNodes(), - conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); - } - Assert.notNull(matchConditionNode, "找不到条件节点({})", currentNode); - // 遍历满足条件的 BpmSimpleModelNodeVO 节点 - simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes); - } - - // 情况:INCLUSIVE_BRANCH_NODE 包容,多个满足条件的。如果没有,就走默认的 - if (nodeType == BpmSimpleModelNodeTypeEnum.INCLUSIVE_BRANCH_NODE) { - // 查找满足条件的 BpmSimpleModelNodeVO 节点 - Collection matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), - conditionNode -> !BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow()) - && evalConditionExpress(variables, conditionNode.getConditionSetting())); - if (CollUtil.isEmpty(matchConditionNodes)) { - matchConditionNodes = CollUtil.filterNew(currentNode.getConditionNodes(), - conditionNode -> BooleanUtil.isTrue(conditionNode.getConditionSetting().getDefaultFlow())); - } - Assert.isTrue(!matchConditionNodes.isEmpty(), "找不到条件节点({})", currentNode); - // 遍历满足条件的 BpmSimpleModelNodeVO 节点 - matchConditionNodes.forEach(matchConditionNode -> - simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes)); - } - - // 情况:PARALLEL_BRANCH_NODE 并行,都满足,都走 - if (nodeType == BpmSimpleModelNodeTypeEnum.PARALLEL_BRANCH_NODE) { - // 遍历所有 BpmSimpleModelNodeVO 节点 - currentNode.getConditionNodes().forEach(matchConditionNode -> - simulateNextNode(matchConditionNode.getChildNode(), variables, resultNodes)); - } - - // 遍历子节点 - simulateNextNode(currentNode.getChildNode(), variables, resultNodes); - } - - public static boolean evalConditionExpress(Map variables, BpmSimpleModelNodeVO.ConditionSetting conditionSetting) { - return BpmnModelUtils.evalConditionExpress(variables, buildConditionExpression(conditionSetting)); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java deleted file mode 100644 index 73f8350..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/package-info.java +++ /dev/null @@ -1,6 +0,0 @@ -/** - * 属于 bpm 模块的 framework 封装 - * - * @author ZT - */ -package com.zt.plat.module.bpm.framework; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java deleted file mode 100644 index 0ab98bc..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.zt.plat.module.bpm.framework.rpc.config; - -import com.zt.plat.module.capital.api.AmountCreditApplyApi; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.PostApi; -import com.zt.plat.module.system.api.dict.DictDataApi; -import com.zt.plat.module.system.api.permission.PermissionApi; -import com.zt.plat.module.system.api.permission.RoleApi; -import com.zt.plat.module.system.api.sms.SmsSendApi; -import com.zt.plat.module.system.api.user.AdminUserApi; -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.context.annotation.Configuration; - -@Configuration(value = "bpmRpcConfiguration", proxyBeanMethods = false) -@EnableFeignClients(clients = {RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, - PermissionApi.class, AmountCreditApplyApi.class}) -public class RpcConfiguration { -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java deleted file mode 100644 index 4a733c4..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位 - */ -package com.zt.plat.module.bpm.framework.rpc; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java deleted file mode 100644 index 5e59e23..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.zt.plat.module.bpm.framework.security.config; - -import com.zt.plat.framework.security.config.AuthorizeRequestsCustomizer; -import com.zt.plat.module.bpm.enums.ApiConstants; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.AuthorizeHttpRequestsConfigurer; - -/** - * Bpm 模块的 Security 配置 - */ -@Configuration(proxyBeanMethods = false, value = "bpmSecurityConfiguration") -public class SecurityConfiguration { - - @Bean("bpmAuthorizeRequestsCustomizer") - public AuthorizeRequestsCustomizer authorizeRequestsCustomizer() { - return new AuthorizeRequestsCustomizer() { - - @Override - public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { - // TODO ZT:这个每个项目都需要重复配置,得捉摸有没通用的方案 - // Swagger 接口文档 - registry.requestMatchers("/v3/api-docs/**").permitAll() - .requestMatchers("/webjars/**").permitAll() - .requestMatchers("/swagger-ui").permitAll() - .requestMatchers("/swagger-ui/**").permitAll(); - // Druid 监控 - registry.requestMatchers("/druid/**").permitAll(); - // Spring Boot Actuator 的安全配置 - registry.requestMatchers("/actuator").permitAll() - .requestMatchers("/actuator/**").permitAll(); - // RPC 服务的安全配置 - registry.requestMatchers(ApiConstants.PREFIX + "/**").permitAll(); - } - - }; - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java deleted file mode 100644 index a637358..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/core/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * 占位 - */ -package com.zt.plat.module.bpm.framework.security.core; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java deleted file mode 100644 index ad4c2c0..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/config/BpmWebConfiguration.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.zt.plat.module.bpm.framework.web.config; - -import com.zt.plat.framework.common.enums.WebFilterOrderEnum; -import com.zt.plat.module.bpm.framework.web.core.FlowableWebFilter; -import org.springframework.boot.web.servlet.FilterRegistrationBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -/** - * bpm 模块的 web 组件的 Configuration - * - * @author ZT - */ -@Configuration(proxyBeanMethods = false) -public class BpmWebConfiguration { - - /** - * 配置 Flowable Web 过滤器 - */ - @Bean - public FilterRegistrationBean flowableWebFilter() { - FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); - registrationBean.setFilter(new FlowableWebFilter()); - registrationBean.setOrder(WebFilterOrderEnum.FLOWABLE_FILTER); - return registrationBean; - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java deleted file mode 100644 index 4699de5..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/core/FlowableWebFilter.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.zt.plat.module.bpm.framework.web.core; - -import com.zt.plat.framework.security.core.util.SecurityFrameworkUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import jakarta.servlet.FilterChain; -import jakarta.servlet.ServletException; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.springframework.web.filter.OncePerRequestFilter; - -import java.io.IOException; - -/** - * Flowable Web 过滤器,将 userId 设置到 {@link org.flowable.common.engine.impl.identity.Authentication} 中 - * - * @author jason - */ -public class FlowableWebFilter extends OncePerRequestFilter { - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) - throws ServletException, IOException { - try { - // 设置工作流的用户 - Long userId = SecurityFrameworkUtils.getLoginUserId(); - if (userId != null) { - FlowableUtils.setAuthenticatedUserId(userId); - } - // 过滤 - chain.doFilter(request, response); - } finally { - // 清理 - FlowableUtils.clearAuthenticatedUserId(); - } - } -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java deleted file mode 100644 index 62601dc..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/web/package-info.java +++ /dev/null @@ -1,4 +0,0 @@ -/** - * bpm 模块的 web 配置 - */ -package com.zt.plat.module.bpm.framework.web; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java deleted file mode 100644 index 4039eb8..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/package-info.java +++ /dev/null @@ -1,12 +0,0 @@ -/** - * bpm 包下,业务流程管理(Business Process Management),我们放工作流的功能,基于 Flowable 6 版本实现。 - * 例如说:流程定义、表单配置、审核中心(我的申请、我的待办、我的已办)等等 - * - * bpm 解释:https://baike.baidu.com/item/BPM/1933 - * - * 1. Controller URL:以 /bpm/ 开头,避免和其它 Module 冲突 - * 2. DataObject 表名:以 bpm_ 开头,方便在数据库中区分 - * - * 注意,由于 Bpm 模块下,容易和其它模块重名,所以类名都加载 Bpm 的前缀~ - */ -package com.zt.plat.module.bpm; diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java deleted file mode 100644 index 6e24f44..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryService.java +++ /dev/null @@ -1,92 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import jakarta.validation.Valid; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; - -/** - * BPM 流程分类 Service 接口 - * - * @author ZT - */ -public interface BpmCategoryService { - - /** - * 创建流程分类 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createCategory(@Valid BpmCategorySaveReqVO createReqVO); - - /** - * 更新流程分类 - * - * @param updateReqVO 更新信息 - */ - void updateCategory(@Valid BpmCategorySaveReqVO updateReqVO); - - /** - * 删除流程分类 - * - * @param id 编号 - */ - void deleteCategory(Long id); - - /** - * 获得流程分类 - * - * @param id 编号 - * @return BPM 流程分类 - */ - BpmCategoryDO getCategory(Long id); - - /** - * 获得流程分类分页 - * - * @param pageReqVO 分页查询 - * @return 流程分类分页 - */ - PageResult getCategoryPage(BpmCategoryPageReqVO pageReqVO); - - /** - * 获得流程分类 Map,基于指定编码 - * - * @param codes 编号数组 - * @return 流程分类 Map - */ - default Map getCategoryMap(Collection codes) { - return convertMap(getCategoryListByCode(codes), BpmCategoryDO::getCode); - } - - /** - * 获得流程分类列表,基于指定编码 - * - * @return 流程分类列表 - */ - List getCategoryListByCode(Collection codes); - - /** - * 获得流程分类列表,基于指定状态 - * - * @param status 状态 - * @return 流程分类列表 - */ - List getCategoryListByStatus(Integer status); - - /** - * 批量更新流程分类的排序:每个分类的 sort 值,从 0 开始递增 - * - * @param ids 分类编号列表 - */ - void updateCategorySortBatch(List ids); - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java deleted file mode 100644 index d111015..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmCategoryServiceImpl.java +++ /dev/null @@ -1,130 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ObjUtil; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import com.zt.plat.module.bpm.dal.mysql.category.BpmCategoryMapper; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.IntStream; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; - -/** - * BPM 流程分类 Service 实现类 - * - * @author ZT - */ -@Service -@Validated -public class BpmCategoryServiceImpl implements BpmCategoryService { - - @Resource - private BpmCategoryMapper bpmCategoryMapper; - - @Override - public Long createCategory(BpmCategorySaveReqVO createReqVO) { - // 校验唯一 - validateCategoryNameUnique(createReqVO); - validateCategoryCodeUnique(createReqVO); - // 插入 - BpmCategoryDO category = BeanUtils.toBean(createReqVO, BpmCategoryDO.class); - bpmCategoryMapper.insert(category); - return category.getId(); - } - - @Override - public void updateCategory(BpmCategorySaveReqVO updateReqVO) { - // 校验存在 - validateCategoryExists(updateReqVO.getId()); - validateCategoryNameUnique(updateReqVO); - validateCategoryCodeUnique(updateReqVO); - // 更新 - BpmCategoryDO updateObj = BeanUtils.toBean(updateReqVO, BpmCategoryDO.class); - bpmCategoryMapper.updateById(updateObj); - } - - private void validateCategoryNameUnique(BpmCategorySaveReqVO updateReqVO) { - BpmCategoryDO category = bpmCategoryMapper.selectByName(updateReqVO.getName()); - if (category == null - || ObjUtil.equal(category.getId(), updateReqVO.getId())) { - return; - } - throw exception(CATEGORY_NAME_DUPLICATE, updateReqVO.getName()); - } - - private void validateCategoryCodeUnique(BpmCategorySaveReqVO updateReqVO) { - BpmCategoryDO category = bpmCategoryMapper.selectByCode(updateReqVO.getCode()); - if (category == null - || ObjUtil.equal(category.getId(), updateReqVO.getId())) { - return; - } - throw exception(CATEGORY_CODE_DUPLICATE, updateReqVO.getCode()); - } - - @Override - public void deleteCategory(Long id) { - // 校验存在 - validateCategoryExists(id); - // 删除 - bpmCategoryMapper.deleteById(id); - } - - private void validateCategoryExists(Long id) { - if (bpmCategoryMapper.selectById(id) == null) { - throw exception(CATEGORY_NOT_EXISTS); - } - } - - @Override - public BpmCategoryDO getCategory(Long id) { - return bpmCategoryMapper.selectById(id); - } - - @Override - public PageResult getCategoryPage(BpmCategoryPageReqVO pageReqVO) { - return bpmCategoryMapper.selectPage(pageReqVO); - } - - @Override - public List getCategoryListByCode(Collection codes) { - if (CollUtil.isEmpty(codes)) { - return Collections.emptyList(); - } - return bpmCategoryMapper.selectListByCode(codes); - } - - @Override - public List getCategoryListByStatus(Integer status) { - return bpmCategoryMapper.selectListByStatus(status); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateCategorySortBatch(List ids) { - // 校验分类都存在 - List categories = bpmCategoryMapper.selectByIds(ids); - if (categories.size() != ids.size()) { - throw exception(CATEGORY_NOT_EXISTS); - } - - // 批量更新排序 - List updateList = IntStream.range(0, ids.size()) - .mapToObj(index -> new BpmCategoryDO().setId(ids.get(index)).setSort(index)) - .collect(Collectors.toList()); - bpmCategoryMapper.updateBatch(updateList); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java deleted file mode 100644 index 6a5a8c0..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormService.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.collection.CollectionUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import jakarta.validation.Valid; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - - -/** - * 动态表单 Service 接口 - * - * @author @风里雾里 - */ -public interface BpmFormService { - - /** - * 创建动态表单 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createForm(@Valid BpmFormSaveReqVO createReqVO); - - /** - * 更新动态表单 - * - * @param updateReqVO 更新信息 - */ - void updateForm(@Valid BpmFormSaveReqVO updateReqVO); - - /** - * 删除动态表单 - * - * @param id 编号 - */ - void deleteForm(Long id); - - /** - * 获得动态表单 - * - * @param id 编号 - * @return 动态表单 - */ - BpmFormDO getForm(Long id); - - /** - * 获得动态表单列表 - * - * @return 动态表单列表 - */ - List getFormList(); - - /** - * 获得动态表单列表 - * - * @param ids 编号 - * @return 动态表单列表 - */ - List getFormList(Collection ids); - - /** - * 获得动态表单 Map - * - * @param ids 编号 - * @return 动态表单 Map - */ - default Map getFormMap(Collection ids) { - return CollectionUtils.convertMap(this.getFormList(ids), BpmFormDO::getId); - } - - /** - * 获得动态表单分页 - * - * @param pageReqVO 分页查询 - * @return 动态表单分页 - */ - PageResult getFormPage(BpmFormPageReqVO pageReqVO); - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java deleted file mode 100644 index 768239d..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java +++ /dev/null @@ -1,114 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Assert; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.dal.mysql.definition.BpmFormMapper; -import com.zt.plat.module.bpm.enums.ErrorCodeConstants; -import com.zt.plat.module.bpm.service.definition.dto.BpmFormFieldRespDTO; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.*; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; - -/** - * 动态表单 Service 实现类 - * - * @author 风里雾里 - */ -@Service -@Validated -public class BpmFormServiceImpl implements BpmFormService { - - @Resource - private BpmFormMapper formMapper; - - @Override - public Long createForm(BpmFormSaveReqVO createReqVO) { - this.validateFields(createReqVO.getFields()); - // 插入 - BpmFormDO form = BeanUtils.toBean(createReqVO, BpmFormDO.class); - formMapper.insert(form); - // 返回 - return form.getId(); - } - - @Override - public void updateForm(BpmFormSaveReqVO updateReqVO) { - validateFields(updateReqVO.getFields()); - // 校验存在 - validateFormExists(updateReqVO.getId()); - // 更新 - BpmFormDO updateObj = BeanUtils.toBean(updateReqVO, BpmFormDO.class); - formMapper.updateById(updateObj); - } - - @Override - public void deleteForm(Long id) { - // 校验存在 - this.validateFormExists(id); - // 删除 - formMapper.deleteById(id); - } - - private void validateFormExists(Long id) { - if (formMapper.selectById(id) == null) { - throw exception(ErrorCodeConstants.FORM_NOT_EXISTS); - } - } - - @Override - public BpmFormDO getForm(Long id) { - return formMapper.selectById(id); - } - - @Override - public List getFormList() { - return formMapper.selectList(); - } - - @Override - public List getFormList(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return Collections.emptyList(); - } - return formMapper.selectBatchIds(ids); - } - - @Override - public PageResult getFormPage(BpmFormPageReqVO pageReqVO) { - return formMapper.selectPage(pageReqVO); - } - - /** - * 校验 Field,避免 field 重复 - * - * @param fields field 数组 - */ - private void validateFields(List fields) { - if (true) { // TODO ZT:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验 - return; - } - Map fieldMap = new HashMap<>(); // key 是 vModel,value 是 label - for (String field : fields) { - BpmFormFieldRespDTO fieldDTO = JsonUtils.parseObject(field, BpmFormFieldRespDTO.class); - Assert.notNull(fieldDTO); - String oldLabel = fieldMap.put(fieldDTO.getVModel(), fieldDTO.getLabel()); - // 如果不存在,则直接返回 - if (oldLabel == null) { - continue; - } - // 如果存在,则报错 - throw exception(ErrorCodeConstants.FORM_FIELD_REPEAT, oldLabel, fieldDTO.getLabel(), fieldDTO.getVModel()); - } - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java deleted file mode 100644 index a9f8b1d..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelService.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO; -import jakarta.validation.Valid; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.engine.repository.Model; - -import java.util.List; - -/** - * 流程模型接口 - * - * @author yunlongn - */ -public interface BpmModelService { - - /** - * 获得流程模型列表 - * - * @param name 模型名称 - * @return 流程模型列表 - */ - List getModelList(String name); - - /** - * 创建流程模型 - * - * @param modelVO 创建信息 - * @return 创建的流程模型的编号 - */ - String createModel(@Valid BpmModelSaveReqVO modelVO); - - /** - * 获得流程模块 - * - * @param id 编号 - * @return 流程模型 - */ - Model getModel(String id); - - /** - * 获得流程模型的 BPMN XML - * - * @param id 编号 - * @return BPMN XML - */ - byte[] getModelBpmnXML(String id); - - /** - * 修改流程模型的 BPMN XML - * - * @param id 编号 - * @param bpmnXml BPMN XML - */ - void updateModelBpmnXml(String id, String bpmnXml); - - /** - * 修改流程模型 - * - * @param userId 用户编号 - * @param updateReqVO 更新信息 - */ - void updateModel(Long userId, @Valid BpmModelSaveReqVO updateReqVO); - - /** - * 批量更新模型排序 - * - * @param userId 用户编号 - * @param ids 编号列表 - */ - void updateModelSortBatch(Long userId, List ids); - - /** - * 将流程模型,部署成一个流程定义 - * - * @param userId 用户编号 - * @param id 编号 - */ - void deployModel(Long userId, String id); - - /** - * 删除模型 - * - * @param userId 用户编号 - * @param id 编号 - */ - void deleteModel(Long userId, String id); - - /** - * 清理模型,包括流程实例 - * - * @param userId 用户编号 - * @param id 编号 - */ - void cleanModel(Long userId, String id); - - /** - * 修改模型的状态,实际更新的部署的流程定义的状态 - * - * @param userId 用户编号 - * @param id 编号 - * @param state 状态 - */ - void updateModelState(Long userId, String id, Integer state); - - /** - * 获得流程定义编号对应的 BPMN Model - * - * @param processDefinitionId 流程定义编号 - * @return BPMN Model - */ - BpmnModel getBpmnModelByDefinitionId(String processDefinitionId); - - // ========== 仿钉钉/飞书的精简模型 ========= - - /** - * 获取仿钉钉流程设计模型结构 - * - * @param modelId 流程模型编号 - * @return 仿钉钉流程设计模型结构 - */ - BpmSimpleModelNodeVO getSimpleModel(String modelId); - - /** - * 更新仿钉钉流程设计模型 - * - * @param userId 用户编号 - * @param reqVO 请求信息 - */ - void updateSimpleModel(Long userId, @Valid BpmSimpleModelUpdateReqVO reqVO); - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java deleted file mode 100644 index d65084c..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmModelServiceImpl.java +++ /dev/null @@ -1,436 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.StrUtil; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.framework.common.util.validation.ValidationUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelSaveReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelUpdateReqVO; -import com.zt.plat.module.bpm.convert.definition.BpmModelConvert; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; -import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; -import com.zt.plat.module.bpm.enums.task.BpmReasonEnum; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.StartEvent; -import org.flowable.bpmn.model.UserTask; -import org.flowable.common.engine.impl.db.SuspensionState; -import org.flowable.engine.HistoryService; -import org.flowable.engine.RepositoryService; -import org.flowable.engine.RuntimeService; -import org.flowable.engine.TaskService; -import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.repository.Model; -import org.flowable.engine.repository.ModelQuery; -import org.flowable.engine.repository.ProcessDefinition; -import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.task.api.Task; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import java.util.List; -import java.util.Map; -import java.util.Objects; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; -import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseCandidateStrategy; - -/** - * 流程模型实现:主要进行 Flowable {@link Model} 的维护 - * - * @author yunlongn - * @author ZT - * @author jason - */ -@Service -@Validated -@Slf4j -public class BpmModelServiceImpl implements BpmModelService { - - @Resource - private RepositoryService repositoryService; - @Resource - private BpmProcessDefinitionService processDefinitionService; - @Resource - private BpmFormService bpmFormService; - - @Resource - private BpmTaskCandidateInvoker taskCandidateInvoker; - - @Resource - private HistoryService historyService; - @Resource - private RuntimeService runtimeService; - @Resource - private TaskService taskService; - @Resource - private BpmProcessInstanceCopyService processInstanceCopyService; - - @Override - public List getModelList(String name) { - ModelQuery modelQuery = repositoryService.createModelQuery(); - if (StrUtil.isNotEmpty(name)) { - modelQuery.modelNameLike("%" + name + "%"); - } - modelQuery.modelTenantId(FlowableUtils.getTenantId()); - return modelQuery.list(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public String createModel(@Valid BpmModelSaveReqVO createReqVO) { - if (!ValidationUtils.isXmlNCName(createReqVO.getKey())) { - throw exception(MODEL_KEY_VALID); - } - // 1. 校验流程标识已经存在 - Model keyModel = getModelByKey(createReqVO.getKey()); - if (keyModel != null) { - throw exception(MODEL_KEY_EXISTS, createReqVO.getKey()); - } - - // 2. 创建 Model 对象 - createReqVO.setSort(System.currentTimeMillis()); // 使用当前时间,作为排序 - Model model = repositoryService.newModel(); - BpmModelConvert.INSTANCE.copyToModel(model, createReqVO); - model.setTenantId(FlowableUtils.getTenantId()); - - // 3. 保存模型 - saveModel(model, createReqVO); - return model.getId(); - } - - @Override - @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 - public void updateModel(Long userId, BpmModelSaveReqVO updateReqVO) { - // 1. 校验流程模型存在 - Model model = validateModelManager(updateReqVO.getId(), userId); - - // 2. 填充 Model 信息 - BpmModelConvert.INSTANCE.copyToModel(model, updateReqVO); - - // 3. 保存模型 - saveModel(model, updateReqVO); - } - - /** - * 保存模型的基本信息、流程图 - * - * @param model 模型 - * @param saveReqVO 保存信息 - */ - private void saveModel(Model model, BpmModelSaveReqVO saveReqVO) { - // 1. 保存模型的基础信息 - repositoryService.saveModel(model); - - // 2. 保存流程图 - if (ObjUtil.equals(BpmModelTypeEnum.BPMN.getType(), saveReqVO.getType()) - && StrUtil.isNotEmpty(saveReqVO.getBpmnXml())) { - updateModelBpmnXml(model.getId(), saveReqVO.getBpmnXml()); - } else if (ObjUtil.equals(BpmModelTypeEnum.SIMPLE.getType(), saveReqVO.getType()) - && saveReqVO.getSimpleModel() != null) { - // JSON 转换成 bpmnModel - BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(), - saveReqVO.getSimpleModel()); - // 保存 Bpmn XML - updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel)); - // 保存 JSON 数据 - updateModelSimpleJson(model.getId(), saveReqVO.getSimpleModel()); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void updateModelSortBatch(Long userId, List ids) { - // 1.1 校验流程模型存在 - List models = repositoryService.createModelQuery() - .modelTenantId(FlowableUtils.getTenantId()).list(); - models.removeIf(model -> !ids.contains(model.getId())); - if (ids.size() != models.size()) { - throw exception(MODEL_NOT_EXISTS); - } - Map modelMap = convertMap(models, Model::getId); - // 1.2 校验是否为管理员 - ids.forEach(id -> validateModelManager(id, userId)); - - // 保存排序 - long sort = System.currentTimeMillis(); // 使用时间戳 - i 作为排序 - for (int i = ids.size() - 1; i > 0; i--) { - Model model = modelMap.get(ids.get(i)); - // 更新模型 - BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model).setSort(sort); - model.setMetaInfo(JsonUtils.toJsonString(metaInfo)); - repositoryService.saveModel(model); - // 更新排序 - processDefinitionService.updateProcessDefinitionSortByModelId(model.getId(), sort); - sort--; - } - } - - private Model validateModelExists(String id) { - Model model = repositoryService.getModel(id); - if (model == null) { - throw exception(MODEL_NOT_EXISTS); - } - return model; - } - - /** - * 校验是否有流程模型的管理权限 - * - * @param id 流程模型编号 - * @param userId 用户编号 - * @return 流程模型 - */ - private Model validateModelManager(String id, Long userId) { - Model model = validateModelExists(id); - BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); - if (metaInfo == null || !CollUtil.contains(metaInfo.getManagerUserIds(), userId)) { - throw exception(MODEL_UPDATE_FAIL_NOT_MANAGER, model.getName()); - } - return model; - } - - @Override - @Transactional(rollbackFor = Exception.class) // 因为进行多个操作,所以开启事务 - public void deployModel(Long userId, String id) { - // 1.1 校验流程模型存在 - Model model = validateModelManager(id, userId); - BpmModelMetaInfoVO metaInfo = BpmModelConvert.INSTANCE.parseMetaInfo(model); - // 1.2 校验流程图 - byte[] bpmnBytes = getModelBpmnXML(model.getId()); - validateBpmnXml(bpmnBytes, metaInfo.getType()); - // 1.3 校验表单已配 - BpmFormDO form = validateFormConfig(metaInfo); - // 1.4 校验任务分配规则已配置 - taskCandidateInvoker.validateBpmnConfig(bpmnBytes); - // 1.5 获取仿钉钉流程设计器模型数据 - String simpleJson = getModelSimpleJson(model.getId()); - - // 2.1 创建流程定义 - String definitionId = processDefinitionService.createProcessDefinition(model, metaInfo, bpmnBytes, simpleJson, - form); - - // 2.2 将老的流程定义进行挂起。也就是说,只有最新部署的流程定义,才可以发起任务。 - updateProcessDefinitionSuspended(model.getDeploymentId()); - - // 2.3 更新 model 的 deploymentId,进行关联 - ProcessDefinition definition = processDefinitionService.getProcessDefinition(definitionId); - model.setDeploymentId(definition.getDeploymentId()); - repositoryService.saveModel(model); - } - - private void validateBpmnXml(byte[] bpmnBytes, Integer type) { - BpmnModel bpmnModel = BpmnModelUtils.getBpmnModel(bpmnBytes); - if (bpmnModel == null) { - throw exception(MODEL_NOT_EXISTS); - } - // 1. 没有 StartEvent - StartEvent startEvent = BpmnModelUtils.getStartEvent(bpmnModel); - if (startEvent == null) { - throw exception(MODEL_DEPLOY_FAIL_BPMN_START_EVENT_NOT_EXISTS); - } - // 2. 校验 UserTask 的 name 都配置了 - List userTasks = BpmnModelUtils.getBpmnModelElements(bpmnModel, UserTask.class); - userTasks.forEach(userTask -> { - if (StrUtil.isEmpty(userTask.getName())) { - throw exception(MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS, userTask.getId()); - } - }); - // 3. 校验第一个用户任务节点的规则类型是否为“审批人自选”,BPMN 设计器,校验第一个用户任务节点,SIMPLE 设计器,第一个节点固定为发起人所以校验第二个用户任务节点 - UserTask firUserTask = CollUtil.get(userTasks, BpmModelTypeEnum.BPMN.getType().equals(type) ? 0 : 1); - if (firUserTask == null) { - return; - } - Integer candidateStrategy = parseCandidateStrategy(firUserTask); - if (Objects.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { - throw exception(MODEL_DEPLOY_FAIL_FIRST_USER_TASK_CANDIDATE_STRATEGY_ERROR, firUserTask.getName()); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void deleteModel(Long userId, String id) { - // 校验流程模型存在 - Model model = validateModelManager(id, userId); - - // 执行删除 - repositoryService.deleteModel(id); - // 禁用流程定义 - updateProcessDefinitionSuspended(model.getDeploymentId()); - } - - @Override - public void cleanModel(Long userId, String id) { - // 1. 校验流程模型存在 - Model model = validateModelManager(id, userId); - - // 2. 清理所有流程数据 - // 2.1 先取消所有正在运行的流程 - List processInstances = runtimeService.createProcessInstanceQuery() - .processDefinitionKey(model.getKey()).list(); - processInstances.forEach(processInstance -> { - runtimeService.deleteProcessInstance(processInstance.getId(), - BpmReasonEnum.CANCEL_BY_SYSTEM.getReason()); - historyService.deleteHistoricProcessInstance(processInstance.getId()); - processInstanceCopyService.deleteProcessInstanceCopy(processInstance.getId()); - }); - // 2.2 再从历史中删除所有相关的流程数据 - List historicProcessInstances = historyService.createHistoricProcessInstanceQuery() - .processDefinitionKey(model.getKey()).list(); - historicProcessInstances.forEach(historicProcessInstance -> { - historyService.deleteHistoricProcessInstance(historicProcessInstance.getId()); - processInstanceCopyService.deleteProcessInstanceCopy(historicProcessInstance.getId()); - }); - // 2.3 清理所有 Task - List tasks = taskService.createTaskQuery() - .processDefinitionKey(model.getKey()).list(); - tasks.forEach(task -> taskService.deleteTask(task.getId(),BpmReasonEnum.CANCEL_BY_PROCESS_CLEAN.getReason())); - } - - @Override - public void updateModelState(Long userId, String id, Integer state) { - // 1.1 校验流程模型存在 - Model model = validateModelManager(id, userId); - // 1.2 校验流程定义存在 - ProcessDefinition definition = processDefinitionService - .getProcessDefinitionByDeploymentId(model.getDeploymentId()); - if (definition == null) { - throw exception(PROCESS_DEFINITION_NOT_EXISTS); - } - - // 2. 更新状态 - processDefinitionService.updateProcessDefinitionState(definition.getId(), state); - } - - @Override - public BpmnModel getBpmnModelByDefinitionId(String processDefinitionId) { - return repositoryService.getBpmnModel(processDefinitionId); - } - - @Override - public BpmSimpleModelNodeVO getSimpleModel(String modelId) { - Model model = validateModelExists(modelId); - // 通过 ACT_RE_MODEL 表 EDITOR_SOURCE_EXTRA_VALUE_ID_ ,获取仿钉钉快搭模型的 JSON 数据 - String json = getModelSimpleJson(model.getId()); - return JsonUtils.parseObject(json, BpmSimpleModelNodeVO.class); - } - - @Override - public void updateSimpleModel(Long userId, BpmSimpleModelUpdateReqVO reqVO) { - // 1. 校验流程模型存在 - Model model = validateModelManager(reqVO.getId(), userId); - - // 2.1 JSON 转换成 bpmnModel - BpmnModel bpmnModel = SimpleModelUtils.buildBpmnModel(model.getKey(), model.getName(), reqVO.getSimpleModel()); - // 2.2 保存 Bpmn XML - updateModelBpmnXml(model.getId(), BpmnModelUtils.getBpmnXml(bpmnModel)); - // 2.3 保存 JSON 数据 - updateModelSimpleJson(model.getId(), reqVO.getSimpleModel()); - } - - /** - * 校验流程表单已配置 - * - * @param metaInfo 流程模型元数据 - * @return 表单配置 - */ - private BpmFormDO validateFormConfig(BpmModelMetaInfoVO metaInfo) { - if (metaInfo == null || metaInfo.getFormType() == null) { - throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); - } - // 校验表单存在 - if (Objects.equals(metaInfo.getFormType(), BpmModelFormTypeEnum.NORMAL.getType())) { - if (metaInfo.getFormId() == null) { - throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); - } - BpmFormDO form = bpmFormService.getForm(metaInfo.getFormId()); - if (form == null) { - throw exception(FORM_NOT_EXISTS); - } - return form; - } else { - if (StrUtil.isEmpty(metaInfo.getFormCustomCreatePath()) - || StrUtil.isEmpty(metaInfo.getFormCustomViewPath())) { - throw exception(MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG); - } - return null; - } - } - - @Override - public void updateModelBpmnXml(String id, String bpmnXml) { - if (StrUtil.isEmpty(bpmnXml)) { - return; - } - repositoryService.addModelEditorSource(id, StrUtil.utf8Bytes(bpmnXml)); - } - - @SuppressWarnings("JavaExistingMethodCanBeUsed") - private String getModelSimpleJson(String id) { - byte[] bytes = repositoryService.getModelEditorSourceExtra(id); - if (ArrayUtil.isEmpty(bytes)) { - return null; - } - return StrUtil.utf8Str(bytes); - } - - private void updateModelSimpleJson(String id, BpmSimpleModelNodeVO node) { - if (node == null) { - return; - } - byte[] bytes = JsonUtils.toJsonByte(node); - repositoryService.addModelEditorSourceExtra(id, bytes); - } - - /** - * 挂起 deploymentId 对应的流程定义 - *

- * 注意:这里一个 deploymentId 只关联一个流程定义 - * - * @param deploymentId 流程发布Id - */ - private void updateProcessDefinitionSuspended(String deploymentId) { - if (StrUtil.isEmpty(deploymentId)) { - return; - } - ProcessDefinition oldDefinition = processDefinitionService.getProcessDefinitionByDeploymentId(deploymentId); - if (oldDefinition == null) { - return; - } - processDefinitionService.updateProcessDefinitionState(oldDefinition.getId(), - SuspensionState.SUSPENDED.getStateCode()); - } - - private Model getModelByKey(String key) { - return repositoryService.createModelQuery() - .modelTenantId(FlowableUtils.getTenantId()) - .modelKey(key).singleResult(); - } - - @Override - public Model getModel(String id) { - return repositoryService.getModel(id); - } - - @Override - public byte[] getModelBpmnXML(String id) { - return repositoryService.getModelEditorSource(id); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java deleted file mode 100644 index f819b48..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionService.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.engine.repository.Deployment; -import org.flowable.engine.repository.Model; -import org.flowable.engine.repository.ProcessDefinition; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; - -/** - * 流程定义接口 - * - * @author yunlong.li - * @author ZJQ - * @author ZT - */ -public interface BpmProcessDefinitionService { - - /** - * 获得流程定义分页 - * - * @param pageReqVO 分页入参 - * @return 流程定义 Page - */ - PageResult getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageReqVO); - - /** - * 获得流程定义列表 - * - * @param suspensionState 中断状态 - * @return 流程定义列表 - */ - List getProcessDefinitionListBySuspensionState(Integer suspensionState); - - /** - * 基于流程模型,创建流程定义 - * - * @param model 流程模型 - * @param modelMetaInfo 流程模型元信息 - * @param bpmnBytes BPMN XML 字节数组 - * @param simpleJson SIMPLE Model JSON - * @param form 表单 - * @return 流程编号 - */ - String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo, - byte[] bpmnBytes, String simpleJson, BpmFormDO form); - - /** - * 更新流程定义状态 - * - * @param id 流程定义的编号 - * @param state 状态 - */ - void updateProcessDefinitionState(String id, Integer state); - - /** - * 更新模型编号 - * - * @param modelId 流程定义编号 - * @param sort 排序 - */ - void updateProcessDefinitionSortByModelId(String modelId, Long sort); - - /** - * 获得流程定义对应的 BPMN - * - * @param id 流程定义编号 - * @return BPMN - */ - BpmnModel getProcessDefinitionBpmnModel(String id); - - /** - * 获得流程定义的信息 - * - * @param id 流程定义编号 - * @return 流程定义信息 - */ - BpmProcessDefinitionInfoDO getProcessDefinitionInfo(String id); - - /** - * 获得流程定义的信息 List - * - * @param ids 流程定义编号数组 - * @return 流程额定义信息数组 - */ - List getProcessDefinitionInfoList(Collection ids); - - default Map getProcessDefinitionInfoMap(Set ids) { - return convertMap(getProcessDefinitionInfoList(ids), BpmProcessDefinitionInfoDO::getProcessDefinitionId); - } - - /** - * 获得流程定义编号对应的 ProcessDefinition - * - * @param id 流程定义编号 - * @return 流程定义 - */ - ProcessDefinition getProcessDefinition(String id); - - /** - * 获得 ids 对应的 ProcessDefinition 数组 - * - * @param ids 编号的数组 - * @return 流程定义的数组 - */ - List getProcessDefinitionList(Set ids); - - default Map getProcessDefinitionMap(Set ids) { - return convertMap(getProcessDefinitionList(ids), ProcessDefinition::getId); - } - - /** - * 获得 deploymentId 对应的 ProcessDefinition - * - * @param deploymentId 部署编号 - * @return 流程定义 - */ - ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId); - - /** - * 获得 deploymentIds 对应的 ProcessDefinition 数组 - * - * @param deploymentIds 部署编号的数组 - * @return 流程定义的数组 - */ - List getProcessDefinitionListByDeploymentIds(Set deploymentIds); - - /** - * 获得流程定义标识对应的激活的流程定义 - * - * @param key 流程定义的标识 - * @return 流程定义 - */ - ProcessDefinition getActiveProcessDefinition(String key); - - /** - * 判断用户是否可以使用该流程定义,进行流程的发起 - * - * @param processDefinition 流程定义 - * @param userId 用户编号 - * @return 是否可以发起流程 - */ - boolean canUserStartProcessDefinition(BpmProcessDefinitionInfoDO processDefinition, Long userId); - - /** - * 获得 ids 对应的 Deployment Map - * - * @param ids 部署编号的数组 - * @return 流程部署 Map - */ - default Map getDeploymentMap(Set ids) { - return convertMap(getDeploymentList(ids), Deployment::getId); - } - - /** - * 获得 ids 对应的 Deployment 数组 - * - * @param ids 部署编号的数组 - * @return 流程部署的数组 - */ - List getDeploymentList(Set ids); - - /** - * 获得 id 对应的 Deployment - * - * @param id 部署编号 - * @return 流程部署 - */ - Deployment getDeployment(String id); - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java deleted file mode 100644 index f2f1d6e..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessDefinitionServiceImpl.java +++ /dev/null @@ -1,248 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.util.StrUtil; -import com.zt.plat.framework.business.core.util.DeptUtil; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.framework.common.util.object.PageUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionPageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.dal.mysql.definition.BpmProcessDefinitionInfoMapper; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.common.engine.impl.db.SuspensionState; -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.flowable.engine.repository.ProcessDefinitionQuery; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.*; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.addIfNotNull; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; -import static java.util.Collections.emptyList; - -/** - * 流程定义实现 - * 主要进行 Flowable {@link ProcessDefinition} 和 {@link Deployment} 的维护 - * - * @author yunlongn - * @author ZJQ - * @author ZT - */ -@Service -@Validated -@Slf4j -public class BpmProcessDefinitionServiceImpl implements BpmProcessDefinitionService { - - @Resource - private RepositoryService repositoryService; - - @Resource - private BpmProcessDefinitionInfoMapper processDefinitionMapper; - - @Resource - private AdminUserApi adminUserApi; - - @Override - public ProcessDefinition getProcessDefinition(String id) { - return repositoryService.getProcessDefinition(id); - } - - @Override - public List getProcessDefinitionList(Set ids) { - return repositoryService.createProcessDefinitionQuery().processDefinitionIds(ids).list(); - } - - @Override - public ProcessDefinition getProcessDefinitionByDeploymentId(String deploymentId) { - if (StrUtil.isEmpty(deploymentId)) { - return null; - } - return repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).singleResult(); - } - - @Override - public List getProcessDefinitionListByDeploymentIds(Set deploymentIds) { - if (CollUtil.isEmpty(deploymentIds)) { - return emptyList(); - } - return repositoryService.createProcessDefinitionQuery().deploymentIds(deploymentIds).list(); - } - - @Override - public ProcessDefinition getActiveProcessDefinition(String key) { - return repositoryService.createProcessDefinitionQuery() - .processDefinitionTenantId(FlowableUtils.getTenantId()) - .processDefinitionKey(key).active().singleResult(); - } - - @Override - public boolean canUserStartProcessDefinition(BpmProcessDefinitionInfoDO processDefinition, Long userId) { - if (processDefinition == null) { - return false; - } - - // 校验用户是否在允许发起的用户列表中 - if (CollUtil.isNotEmpty(processDefinition.getStartUserIds())) { - return processDefinition.getStartUserIds().contains(userId); - } - - // 校验用户是否在允许发起的部门列表中 - if (CollUtil.isNotEmpty(processDefinition.getStartDeptIds())) { - AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); - return user != null - && DeptUtil.getDeptId(user) > 0L - && processDefinition.getStartDeptIds().contains(DeptUtil.getDeptId(user)); - } - - // 都为空,则所有人都可以发起 - return true; - } - - @Override - public List getDeploymentList(Set ids) { - if (CollUtil.isEmpty(ids)) { - return emptyList(); - } - List list = new ArrayList<>(ids.size()); - for (String id : ids) { - addIfNotNull(list, getDeployment(id)); - } - return list; - } - - @Override - public Deployment getDeployment(String id) { - if (StrUtil.isEmpty(id)) { - return null; - } - return repositoryService.createDeploymentQuery().deploymentId(id).singleResult(); - } - - @Override - public String createProcessDefinition(Model model, BpmModelMetaInfoVO modelMetaInfo, - byte[] bpmnBytes, String simpleJson, BpmFormDO form) { - // 创建 Deployment 部署 - Deployment deploy = repositoryService.createDeployment() - .key(model.getKey()).name(model.getName()).category(model.getCategory()) - .addBytes(model.getKey() + BpmnModelConstants.BPMN_FILE_SUFFIX, bpmnBytes) - .tenantId(FlowableUtils.getTenantId()) - .disableSchemaValidation() // 禁用 XML Schema 验证,因为有自定义的属性 - .deploy(); - - // 设置 ProcessDefinition 的 category 分类 - ProcessDefinition definition = repositoryService.createProcessDefinitionQuery() - .deploymentId(deploy.getId()).singleResult(); - repositoryService.setProcessDefinitionCategory(definition.getId(), model.getCategory()); - // 注意 1,ProcessDefinition 的 key 和 name 是通过 BPMN 中的 的 id 和 name 决定 - // 注意 2,目前该项目的设计上,需要保证 Model、Deployment、ProcessDefinition 使用相同的 key,保证关联性。 - // 否则,会导致 ProcessDefinition 的分页无法查询到。 - if (!Objects.equals(definition.getKey(), model.getKey())) { - throw exception(PROCESS_DEFINITION_KEY_NOT_MATCH, model.getKey(), definition.getKey()); - } - if (!Objects.equals(definition.getName(), model.getName())) { - throw exception(PROCESS_DEFINITION_NAME_NOT_MATCH, model.getName(), definition.getName()); - } - - // 插入拓展表 - BpmProcessDefinitionInfoDO definitionDO = BeanUtils.toBean(modelMetaInfo, BpmProcessDefinitionInfoDO.class) - .setModelId(model.getId()).setCategory(model.getCategory()).setProcessDefinitionId(definition.getId()) - .setModelType(modelMetaInfo.getType()).setSimpleModel(simpleJson); - if (form != null) { - definitionDO.setFormFields(form.getFields()).setFormConf(form.getConf()); - } - processDefinitionMapper.insert(definitionDO); - return definition.getId(); - } - - @Override - public void updateProcessDefinitionState(String id, Integer state) { - ProcessDefinition processDefinition = repositoryService.getProcessDefinition(id); - if (processDefinition == null) { - throw exception(PROCESS_DEFINITION_NOT_EXISTS); - } - - // 激活 - if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), state)) { - if (processDefinition.isSuspended()) { - repositoryService.activateProcessDefinitionById(id, false, null); - } - return; - } - // 挂起 - if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), state)) { - // suspendProcessInstances = false,进行中的任务,不进行挂起。 - // 原因:只要新的流程不允许发起即可,老流程继续可以执行。 - if (!processDefinition.isSuspended()) { - repositoryService.suspendProcessDefinitionById(id, false, null); - } - return; - } - log.error("[updateProcessDefinitionState][流程定义({}) 修改未知状态({})]", id, state); - } - - @Override - public void updateProcessDefinitionSortByModelId(String modelId, Long sort) { - processDefinitionMapper.updateByModelId(modelId, new BpmProcessDefinitionInfoDO().setSort(sort)); - } - - @Override - public BpmnModel getProcessDefinitionBpmnModel(String id) { - return repositoryService.getBpmnModel(id); - } - - @Override - public BpmProcessDefinitionInfoDO getProcessDefinitionInfo(String id) { - return processDefinitionMapper.selectByProcessDefinitionId(id); - } - - @Override - public List getProcessDefinitionInfoList(Collection ids) { - return processDefinitionMapper.selectListByProcessDefinitionIds(ids); - } - - @Override - public PageResult getProcessDefinitionPage(BpmProcessDefinitionPageReqVO pageVO) { - ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery(); - query.processDefinitionTenantId(FlowableUtils.getTenantId()); - if (StrUtil.isNotBlank(pageVO.getKey())) { - query.processDefinitionKey(pageVO.getKey()); - } - // 执行查询 - long count = query.count(); - if (count == 0) { - return PageResult.empty(count); - } - List list = query.orderByProcessDefinitionVersion().desc() - .listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); - return new PageResult<>(list, count); - } - - @Override - public List getProcessDefinitionListBySuspensionState(Integer suspensionState) { - // 拼接查询条件 - ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery(); - if (Objects.equals(SuspensionState.SUSPENDED.getStateCode(), suspensionState)) { - query.suspended(); - } else if (Objects.equals(SuspensionState.ACTIVE.getStateCode(), suspensionState)) { - query.active(); - } - // 执行查询 - query.processDefinitionTenantId(FlowableUtils.getTenantId()); - return query.list(); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java deleted file mode 100644 index 448300c..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionService.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; -import jakarta.validation.Valid; - -/** - * BPM 流程表达式 Service 接口 - * - * @author ZT - */ -public interface BpmProcessExpressionService { - - /** - * 创建流程表达式 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createProcessExpression(@Valid BpmProcessExpressionSaveReqVO createReqVO); - - /** - * 更新流程表达式 - * - * @param updateReqVO 更新信息 - */ - void updateProcessExpression(@Valid BpmProcessExpressionSaveReqVO updateReqVO); - - /** - * 删除流程表达式 - * - * @param id 编号 - */ - void deleteProcessExpression(Long id); - - /** - * 获得流程表达式 - * - * @param id 编号 - * @return 流程表达式 - */ - BpmProcessExpressionDO getProcessExpression(Long id); - - /** - * 获得流程表达式分页 - * - * @param pageReqVO 分页查询 - * @return 流程表达式分页 - */ - PageResult getProcessExpressionPage(BpmProcessExpressionPageReqVO pageReqVO); - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java deleted file mode 100644 index e367aff..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessExpressionServiceImpl.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.expression.BpmProcessExpressionSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessExpressionDO; -import com.zt.plat.module.bpm.dal.mysql.definition.BpmProcessExpressionMapper; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.PROCESS_EXPRESSION_NOT_EXISTS; - -/** - * BPM 流程表达式 Service 实现类 - * - * @author ZT - */ -@Service -@Validated -public class BpmProcessExpressionServiceImpl implements BpmProcessExpressionService { - - @Resource - private BpmProcessExpressionMapper processExpressionMapper; - - @Override - public Long createProcessExpression(BpmProcessExpressionSaveReqVO createReqVO) { - // 插入 - BpmProcessExpressionDO processExpression = BeanUtils.toBean(createReqVO, BpmProcessExpressionDO.class); - processExpressionMapper.insert(processExpression); - // 返回 - return processExpression.getId(); - } - - @Override - public void updateProcessExpression(BpmProcessExpressionSaveReqVO updateReqVO) { - // 校验存在 - validateProcessExpressionExists(updateReqVO.getId()); - // 更新 - BpmProcessExpressionDO updateObj = BeanUtils.toBean(updateReqVO, BpmProcessExpressionDO.class); - processExpressionMapper.updateById(updateObj); - } - - @Override - public void deleteProcessExpression(Long id) { - // 校验存在 - validateProcessExpressionExists(id); - // 删除 - processExpressionMapper.deleteById(id); - } - - private void validateProcessExpressionExists(Long id) { - if (processExpressionMapper.selectById(id) == null) { - throw exception(PROCESS_EXPRESSION_NOT_EXISTS); - } - } - - @Override - public BpmProcessExpressionDO getProcessExpression(Long id) { - return processExpressionMapper.selectById(id); - } - - @Override - public PageResult getProcessExpressionPage(BpmProcessExpressionPageReqVO pageReqVO) { - return processExpressionMapper.selectPage(pageReqVO); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java deleted file mode 100644 index 9f111a7..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerService.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; -import jakarta.validation.Valid; - -/** - * BPM 流程监听器 Service 接口 - * - * @author ZT - */ -public interface BpmProcessListenerService { - - /** - * 创建流程监听器 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createProcessListener(@Valid BpmProcessListenerSaveReqVO createReqVO); - - /** - * 更新流程监听器 - * - * @param updateReqVO 更新信息 - */ - void updateProcessListener(@Valid BpmProcessListenerSaveReqVO updateReqVO); - - /** - * 删除流程监听器 - * - * @param id 编号 - */ - void deleteProcessListener(Long id); - - /** - * 获得流程监听器 - * - * @param id 编号 - * @return 流程监听器 - */ - BpmProcessListenerDO getProcessListener(Long id); - - /** - * 获得流程监听器分页 - * - * @param pageReqVO 分页查询 - * @return 流程监听器分页 - */ - PageResult getProcessListenerPage(BpmProcessListenerPageReqVO pageReqVO); - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java deleted file mode 100644 index 916949b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmProcessListenerServiceImpl.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import cn.hutool.core.util.StrUtil; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.listener.BpmProcessListenerSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessListenerDO; -import com.zt.plat.module.bpm.dal.mysql.definition.BpmProcessListenerMapper; -import com.zt.plat.module.bpm.enums.definition.BpmProcessListenerTypeEnum; -import com.zt.plat.module.bpm.enums.definition.BpmProcessListenerValueTypeEnum; -import jakarta.annotation.Resource; -import org.flowable.engine.delegate.JavaDelegate; -import org.flowable.engine.delegate.TaskListener; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; - -/** - * BPM 流程监听器 Service 实现类 - * - * @author ZT - */ -@Service -@Validated -public class BpmProcessListenerServiceImpl implements BpmProcessListenerService { - - @Resource - private BpmProcessListenerMapper processListenerMapper; - - @Override - public Long createProcessListener(BpmProcessListenerSaveReqVO createReqVO) { - // 校验 - validateCreateProcessListenerValue(createReqVO); - // 插入 - BpmProcessListenerDO processListener = BeanUtils.toBean(createReqVO, BpmProcessListenerDO.class); - processListenerMapper.insert(processListener); - return processListener.getId(); - } - - @Override - public void updateProcessListener(BpmProcessListenerSaveReqVO updateReqVO) { - // 校验存在 - validateProcessListenerExists(updateReqVO.getId()); - validateCreateProcessListenerValue(updateReqVO); - // 更新 - BpmProcessListenerDO updateObj = BeanUtils.toBean(updateReqVO, BpmProcessListenerDO.class); - processListenerMapper.updateById(updateObj); - } - - private void validateCreateProcessListenerValue(BpmProcessListenerSaveReqVO createReqVO) { - // class 类型 - if (createReqVO.getValueType().equals(BpmProcessListenerValueTypeEnum.CLASS.getType())) { - try { - Class clazz = Class.forName(createReqVO.getValue()); - if (createReqVO.getType().equals(BpmProcessListenerTypeEnum.EXECUTION.getType()) - && !JavaDelegate.class.isAssignableFrom(clazz)) { - throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(), - JavaDelegate.class.getName()); - } else if (createReqVO.getType().equals(BpmProcessListenerTypeEnum.TASK.getType()) - && !TaskListener.class.isAssignableFrom(clazz)) { - throw exception(PROCESS_LISTENER_CLASS_IMPLEMENTS_ERROR, createReqVO.getValue(), - TaskListener.class.getName()); - } - } catch (ClassNotFoundException e) { - throw exception(PROCESS_LISTENER_CLASS_NOT_FOUND, createReqVO.getValue()); - } - return; - } - // 表达式 - if (!StrUtil.startWith(createReqVO.getValue(), "${") || !StrUtil.endWith(createReqVO.getValue(), "}")) { - throw exception(PROCESS_LISTENER_EXPRESSION_INVALID, createReqVO.getValue()); - } - } - - @Override - public void deleteProcessListener(Long id) { - // 校验存在 - validateProcessListenerExists(id); - // 删除 - processListenerMapper.deleteById(id); - } - - private void validateProcessListenerExists(Long id) { - if (processListenerMapper.selectById(id) == null) { - throw exception(PROCESS_LISTENER_NOT_EXISTS); - } - } - - @Override - public BpmProcessListenerDO getProcessListener(Long id) { - return processListenerMapper.selectById(id); - } - - @Override - public PageResult getProcessListenerPage(BpmProcessListenerPageReqVO pageReqVO) { - return processListenerMapper.selectPage(pageReqVO); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java deleted file mode 100644 index 40bfea3..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupService.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; -import jakarta.validation.Valid; - -import java.util.Collection; -import java.util.List; - -/** - * 用户组 Service 接口 - * - * @author ZT - */ -public interface BpmUserGroupService { - - /** - * 创建用户组 - * - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createUserGroup(@Valid BpmUserGroupSaveReqVO createReqVO); - - /** - * 更新用户组 - * - * @param updateReqVO 更新信息 - */ - void updateUserGroup(@Valid BpmUserGroupSaveReqVO updateReqVO); - - /** - * 删除用户组 - * - * @param id 编号 - */ - void deleteUserGroup(Long id); - - /** - * 获得用户组 - * - * @param id 编号 - * @return 用户组 - */ - BpmUserGroupDO getUserGroup(Long id); - - /** - * 获得用户组列表 - * - * @param ids 编号 - * @return 用户组列表 - */ - List getUserGroupList(Collection ids); - - /** - * 获得指定状态的用户组列表 - * - * @param status 状态 - * @return 用户组列表 - */ - List getUserGroupListByStatus(Integer status); - - /** - * 获得用户组分页 - * - * @param pageReqVO 分页查询 - * @return 用户组分页 - */ - PageResult getUserGroupPage(BpmUserGroupPageReqVO pageReqVO); - - /** - * 校验用户组们是否有效。如下情况,视为无效: - * 1. 用户组编号不存在 - * 2. 用户组被禁用 - * - * @param ids 用户组编号数组 - */ - void validUserGroups(Collection ids); - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java deleted file mode 100644 index f0e6ddb..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceImpl.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import cn.hutool.core.collection.CollUtil; -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; -import com.zt.plat.module.bpm.dal.mysql.definition.BpmUserGroupMapper; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.USER_GROUP_IS_DISABLE; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS; - -/** - * 用户组 Service 实现类 - * - * @author ZT - */ -@Service -@Validated -public class BpmUserGroupServiceImpl implements BpmUserGroupService { - - @Resource - private BpmUserGroupMapper userGroupMapper; - - @Override - public Long createUserGroup(BpmUserGroupSaveReqVO createReqVO) { - BpmUserGroupDO userGroup = BeanUtils.toBean(createReqVO, BpmUserGroupDO.class); - userGroupMapper.insert(userGroup); - return userGroup.getId(); - } - - @Override - public void updateUserGroup(BpmUserGroupSaveReqVO updateReqVO) { - // 校验存在 - validateUserGroupExists(updateReqVO.getId()); - // 更新 - BpmUserGroupDO updateObj = BeanUtils.toBean(updateReqVO, BpmUserGroupDO.class); - userGroupMapper.updateById(updateObj); - } - - @Override - public void deleteUserGroup(Long id) { - // 校验存在 - this.validateUserGroupExists(id); - // 删除 - userGroupMapper.deleteById(id); - } - - private void validateUserGroupExists(Long id) { - if (userGroupMapper.selectById(id) == null) { - throw exception(USER_GROUP_NOT_EXISTS); - } - } - - @Override - public BpmUserGroupDO getUserGroup(Long id) { - return userGroupMapper.selectById(id); - } - - @Override - public List getUserGroupList(Collection ids) { - return userGroupMapper.selectBatchIds(ids); - } - - - @Override - public List getUserGroupListByStatus(Integer status) { - return userGroupMapper.selectListByStatus(status); - } - - @Override - public PageResult getUserGroupPage(BpmUserGroupPageReqVO pageReqVO) { - return userGroupMapper.selectPage(pageReqVO); - } - - @Override - public void validUserGroups(Collection ids) { - if (CollUtil.isEmpty(ids)) { - return; - } - // 获得用户组信息 - List userGroups = userGroupMapper.selectBatchIds(ids); - Map userGroupMap = convertMap(userGroups, BpmUserGroupDO::getId); - // 校验 - ids.forEach(id -> { - BpmUserGroupDO userGroup = userGroupMap.get(id); - if (userGroup == null) { - throw exception(USER_GROUP_NOT_EXISTS); - } - if (!CommonStatusEnum.ENABLE.getStatus().equals(userGroup.getStatus())) { - throw exception(USER_GROUP_IS_DISABLE, userGroup.getName()); - } - }); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java deleted file mode 100644 index f99ab3a..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmFormFieldRespDTO.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.zt.plat.module.bpm.service.definition.dto; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Data; - -/** - * Bpm 表单的 Field 表单项 Response DTO - * 字段的定义,可见 https://github.com/JakHuang/form-generator/issues/46 文档 - * - * @author ZT - */ -@Data -public class BpmFormFieldRespDTO { - - /** - * 表单标题 - */ - private String label; - /** - * 表单字段的属性名,可自定义 - */ - @JsonProperty(value = "vModel") - private String vModel; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java deleted file mode 100644 index 69767d9..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmModelMetaInfoRespDTO.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.zt.plat.module.bpm.service.definition.dto; - -import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; -import lombok.Data; - -/** - * BPM 流程 MetaInfo Response DTO - * 主要用于 { Model#setMetaInfo(String)} 的存储 - * - * 最终,它的字段和 {@link com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO} 是一致的 - * - * @author ZT - */ -@Data -public class BpmModelMetaInfoRespDTO { - - /** - * 流程图标 - */ - private String icon; - /** - * 流程描述 - */ - private String description; - - /** - * 表单类型 - */ - private Integer formType; - /** - * 表单编号 - * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 - */ - private Long formId; - /** - * 自定义表单的提交路径,使用 Vue 的路由地址 - * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 - */ - private String formCustomCreatePath; - /** - * 自定义表单的查看路径,使用 Vue 的路由地址 - * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 - */ - private String formCustomViewPath; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java deleted file mode 100644 index 2132a21..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/dto/BpmProcessDefinitionCreateReqDTO.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.zt.plat.module.bpm.service.definition.dto; - -import com.zt.plat.module.bpm.enums.definition.BpmModelFormTypeEnum; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -import java.util.List; - -/** - * 流程定义创建 Request DTO - */ -@Data -public class BpmProcessDefinitionCreateReqDTO { - - // ========== 模型相关 ========== - - /** - * 流程模型的编号 - */ - @NotEmpty(message = "流程模型编号不能为空") - private String modelId; - /** - * 流程标识 - */ - @NotEmpty(message = "流程标识不能为空") - private String key; - /** - * 流程名称 - */ - @NotEmpty(message = "流程名称不能为空") - private String name; - /** - * 流程描述 - */ - private String description; - /** - * 流程分类 - */ - @NotEmpty(message = "流程分类不能为空") - private String category; - /** - * BPMN XML - */ - @NotEmpty(message = "BPMN XML 不能为空") - private byte[] bpmnBytes; - - // ========== 表单相关 ========== - - /** - * 表单类型 - */ - @NotNull(message = "表单类型不能为空") - private Integer formType; - /** - * 动态表单编号 - * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 - */ - private Long formId; - /** - * 表单的配置 - * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 - */ - private String formConf; - /** - * 表单项的数组 - * 在表单类型为 {@link BpmModelFormTypeEnum#NORMAL} 时 - */ - private List formFields; - /** - * 自定义表单的提交路径,使用 Vue 的路由地址 - * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 - */ - private String formCustomCreatePath; - /** - * 自定义表单的查看路径,使用 Vue 的路由地址 - * 在表单类型为 {@link BpmModelFormTypeEnum#CUSTOM} 时 - */ - private String formCustomViewPath; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java deleted file mode 100644 index ccbd904..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.zt.plat.module.bpm.service.message; - -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO; -import jakarta.validation.Valid; - -/** - * BPM 消息 Service 接口 - * - * TODO ZT:未来支持消息的可配置;不同的流程,在什么场景下,需要发送什么消息,消息的内容是什么; - * - * @author ZT - */ -public interface BpmMessageService { - - /** - * 发送流程实例被通过的消息 - * - * @param reqDTO 发送信息 - */ - void sendMessageWhenProcessInstanceApprove(@Valid BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO); - - /** - * 发送流程实例被不通过的消息 - * - * @param reqDTO 发送信息 - */ - void sendMessageWhenProcessInstanceReject(@Valid BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO); - - /** - * 发送任务被分配的消息 - * - * @param reqDTO 发送信息 - */ - void sendMessageWhenTaskAssigned(@Valid BpmMessageSendWhenTaskCreatedReqDTO reqDTO); - - /** - * 发送任务审批超时的消息 - * - * @param reqDTO 发送信息 - */ - void sendMessageWhenTaskTimeout(@Valid BpmMessageSendWhenTaskTimeoutReqDTO reqDTO); - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java deleted file mode 100644 index 9650cb7..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageServiceImpl.java +++ /dev/null @@ -1,79 +0,0 @@ -package com.zt.plat.module.bpm.service.message; - -import com.zt.plat.framework.web.config.WebProperties; -import com.zt.plat.module.bpm.convert.message.BpmMessageConvert; -import com.zt.plat.module.bpm.enums.message.BpmMessageEnum; -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskCreatedReqDTO; -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO; -import com.zt.plat.module.system.api.sms.SmsSendApi; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.HashMap; -import java.util.Map; - -/** - * BPM 消息 Service 实现类 - * - * @author ZT - */ -@Service -@Validated -@Slf4j -public class BpmMessageServiceImpl implements BpmMessageService { - - @Resource - private SmsSendApi smsSendApi; - - @Resource - private WebProperties webProperties; - - @Override - public void sendMessageWhenProcessInstanceApprove(BpmMessageSendWhenProcessInstanceApproveReqDTO reqDTO) { - Map templateParams = new HashMap<>(); - templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); - templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); - smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(), - BpmMessageEnum.PROCESS_INSTANCE_APPROVE.getSmsTemplateCode(), templateParams)).checkError(); - } - - @Override - public void sendMessageWhenProcessInstanceReject(BpmMessageSendWhenProcessInstanceRejectReqDTO reqDTO) { - Map templateParams = new HashMap<>(); - templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); - templateParams.put("reason", reqDTO.getReason()); - templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); - smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getStartUserId(), - BpmMessageEnum.PROCESS_INSTANCE_REJECT.getSmsTemplateCode(), templateParams)).checkError(); - } - - @Override - public void sendMessageWhenTaskAssigned(BpmMessageSendWhenTaskCreatedReqDTO reqDTO) { - Map templateParams = new HashMap<>(); - templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); - templateParams.put("taskName", reqDTO.getTaskName()); - templateParams.put("startUserNickname", reqDTO.getStartUserNickname()); - templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); - smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(), - BpmMessageEnum.TASK_ASSIGNED.getSmsTemplateCode(), templateParams)).checkError(); - } - - @Override - public void sendMessageWhenTaskTimeout(BpmMessageSendWhenTaskTimeoutReqDTO reqDTO) { - Map templateParams = new HashMap<>(); - templateParams.put("processInstanceName", reqDTO.getProcessInstanceName()); - templateParams.put("taskName", reqDTO.getTaskName()); - templateParams.put("detailUrl", getProcessInstanceDetailUrl(reqDTO.getProcessInstanceId())); - smsSendApi.sendSingleSmsToAdmin(BpmMessageConvert.INSTANCE.convert(reqDTO.getAssigneeUserId(), - BpmMessageEnum.TASK_TIMEOUT.getSmsTemplateCode(), templateParams)).checkError(); - } - - private String getProcessInstanceDetailUrl(String taskId) { - return webProperties.getAdminUi().getUrl() + "/bpm/process-instance/detail?id=" + taskId; - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java deleted file mode 100644 index d06cbf4..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.zt.plat.module.bpm.service.message.dto; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -/** - * BPM 发送流程实例被通过 Request DTO - */ -@Data -public class BpmMessageSendWhenProcessInstanceApproveReqDTO { - - /** - * 流程实例的编号 - */ - @NotEmpty(message = "流程实例的编号不能为空") - private String processInstanceId; - /** - * 流程实例的名字 - */ - @NotEmpty(message = "流程实例的名字不能为空") - private String processInstanceName; - @NotNull(message = "发起人的用户编号") - private Long startUserId; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java deleted file mode 100644 index cf4bd29..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.zt.plat.module.bpm.service.message.dto; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -/** - * BPM 发送流程实例被不通过 Request DTO - */ -@Data -public class BpmMessageSendWhenProcessInstanceRejectReqDTO { - - /** - * 流程实例的编号 - */ - @NotEmpty(message = "流程实例的编号不能为空") - private String processInstanceId; - /** - * 流程实例的名字 - */ - @NotEmpty(message = "流程实例的名字不能为空") - private String processInstanceName; - @NotNull(message = "发起人的用户编号") - private Long startUserId; - - /** - * 不通过理由 - */ - @NotEmpty(message = "不通过理由不能为空") - private String reason; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java deleted file mode 100644 index 03adf51..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskCreatedReqDTO.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.zt.plat.module.bpm.service.message.dto; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -/** - * BPM 发送任务被分配 Request DTO - */ -@Data -public class BpmMessageSendWhenTaskCreatedReqDTO { - - /** - * 流程实例的编号 - */ - @NotEmpty(message = "流程实例的编号不能为空") - private String processInstanceId; - /** - * 流程实例的名字 - */ - @NotEmpty(message = "流程实例的名字不能为空") - private String processInstanceName; - @NotNull(message = "发起人的用户编号") - private Long startUserId; - @NotEmpty(message = "发起人的昵称") - private String startUserNickname; - - /** - * 流程任务的编号 - */ - @NotEmpty(message = "流程任务的编号不能为空") - private String taskId; - /** - * 流程任务的名字 - */ - @NotEmpty(message = "流程任务的名字不能为空") - private String taskName; - - /** - * 审批人的用户编号 - */ - @NotNull(message = "审批人的用户编号不能为空") - private Long assigneeUserId; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java deleted file mode 100644 index f9a9067..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/dto/BpmMessageSendWhenTaskTimeoutReqDTO.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.zt.plat.module.bpm.service.message.dto; - -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; -import lombok.Data; - -/** - * BPM 发送任务审批超时 Request DTO - */ -@Data -public class BpmMessageSendWhenTaskTimeoutReqDTO { - - /** - * 流程实例的编号 - */ - @NotEmpty(message = "流程实例的编号不能为空") - private String processInstanceId; - /** - * 流程实例的名字 - */ - @NotEmpty(message = "流程实例的名字不能为空") - private String processInstanceName; - - /** - * 流程任务的编号 - */ - @NotEmpty(message = "流程任务的编号不能为空") - private String taskId; - /** - * 流程任务的名字 - */ - @NotEmpty(message = "流程任务的名字不能为空") - private String taskName; - - /** - * 审批人的用户编号 - */ - @NotNull(message = "审批人的用户编号不能为空") - private Long assigneeUserId; - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java deleted file mode 100644 index bf19ddb..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveService.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.zt.plat.module.bpm.service.oa; - - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO; -import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; -import jakarta.validation.Valid; - -/** - * 请假申请 Service 接口 - * - * @author jason - * @author ZT - */ -public interface BpmOALeaveService { - - /** - * 创建请假申请 - * - * @param userId 用户编号 - * @param createReqVO 创建信息 - * @return 编号 - */ - Long createLeave(Long userId, @Valid BpmOALeaveCreateReqVO createReqVO); - - /** - * 更新请假申请的状态 - * - * @param id 编号 - * @param status 结果 - */ - void updateLeaveStatus(Long id, Integer status); - - /** - * 获得请假申请 - * - * @param id 编号 - * @return 请假申请 - */ - BpmOALeaveDO getLeave(Long id); - - /** - * 获得请假申请分页 - * - * @param userId 用户编号 - * @param pageReqVO 分页查询 - * @return 请假申请分页 - */ - PageResult getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO); - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java deleted file mode 100644 index fec6e27..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/BpmOALeaveServiceImpl.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.zt.plat.module.bpm.service.oa; - -import cn.hutool.core.date.LocalDateTimeUtil; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.object.BeanUtils; -import com.zt.plat.module.bpm.api.task.BpmProcessInstanceApi; -import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; -import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeaveCreateReqVO; -import com.zt.plat.module.bpm.controller.admin.oa.vo.BpmOALeavePageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.oa.BpmOALeaveDO; -import com.zt.plat.module.bpm.dal.mysql.oa.BpmOALeaveMapper; -import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import java.util.HashMap; -import java.util.Map; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.OA_LEAVE_NOT_EXISTS; - -/** - * OA 请假申请 Service 实现类 - * - * @author jason - * @author ZT - */ -@Service -@Validated -public class BpmOALeaveServiceImpl implements BpmOALeaveService { - - /** - * OA 请假对应的流程定义 KEY - */ - public static final String PROCESS_KEY = "oa_leave"; - - @Resource - private BpmOALeaveMapper leaveMapper; - - @Resource - private BpmProcessInstanceApi processInstanceApi; - - @Override - @Transactional(rollbackFor = Exception.class) - public Long createLeave(Long userId, BpmOALeaveCreateReqVO createReqVO) { - // 插入 OA 请假单 - long day = LocalDateTimeUtil.between(createReqVO.getStartTime(), createReqVO.getEndTime()).toDays(); - BpmOALeaveDO leave = BeanUtils.toBean(createReqVO, BpmOALeaveDO.class) - .setUserId(userId).setDay(day).setStatus(BpmTaskStatusEnum.RUNNING.getStatus()); - leaveMapper.insert(leave); - - // 发起 BPM 流程 - Map processInstanceVariables = new HashMap<>(); - processInstanceVariables.put("day", day); - String processInstanceId = processInstanceApi.createProcessInstance(userId, - new BpmProcessInstanceCreateReqDTO().setProcessDefinitionKey(PROCESS_KEY) - .setVariables(processInstanceVariables).setBusinessKey(String.valueOf(leave.getId())) - .setStartUserSelectAssignees(createReqVO.getStartUserSelectAssignees())).getCheckedData(); - - // 将工作流的编号,更新到 OA 请假单中 - leaveMapper.updateById(new BpmOALeaveDO().setId(leave.getId()).setProcessInstanceId(processInstanceId)); - return leave.getId(); - } - - @Override - public void updateLeaveStatus(Long id, Integer status) { - validateLeaveExists(id); - leaveMapper.updateById(new BpmOALeaveDO().setId(id).setStatus(status)); - } - - private void validateLeaveExists(Long id) { - if (leaveMapper.selectById(id) == null) { - throw exception(OA_LEAVE_NOT_EXISTS); - } - } - - @Override - public BpmOALeaveDO getLeave(Long id) { - return leaveMapper.selectById(id); - } - - @Override - public PageResult getLeavePage(Long userId, BpmOALeavePageReqVO pageReqVO) { - return leaveMapper.selectPage(userId, pageReqVO); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java deleted file mode 100644 index 122a6ff..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/oa/listener/BpmOALeaveStatusListener.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.zt.plat.module.bpm.service.oa.listener; - -import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; -import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEventListener; -import com.zt.plat.module.bpm.service.oa.BpmOALeaveService; -import com.zt.plat.module.bpm.service.oa.BpmOALeaveServiceImpl; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Component; - -import java.util.List; - -/** - * OA 请假单的结果的监听器实现类 - * - * @author ZT - */ -@Component -public class BpmOALeaveStatusListener extends BpmProcessInstanceStatusEventListener { - - @Resource - private BpmOALeaveService leaveService; - - @Override - protected List getProcessDefinitionKey() { - return List.of(BpmOALeaveServiceImpl.PROCESS_KEY); - } - - @Override - protected void onEvent(BpmProcessInstanceStatusEvent event) { - leaveService.updateLeaveStatus(Long.parseLong(event.getBusinessKey()), event.getStatus()); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java deleted file mode 100644 index 83fed4c..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java +++ /dev/null @@ -1,42 +0,0 @@ -package com.zt.plat.module.bpm.service.supply.capital.listener; - -import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; -import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEventListener; -import com.zt.plat.module.bpm.enums.task.BpmProcessInstanceStatusEnum; -import com.zt.plat.module.capital.api.AmountCreditApplyApi; -import com.zt.plat.module.capital.enums.AmountCreditApplyApiStatusEnum; -import jakarta.annotation.Resource; -import org.springframework.stereotype.Component; - -import java.util.List; - -/** - * credit_letter_approval_process - 授信单的状态的监听器实现类 - * - * @author qianshijiang - */ -@Component -public class BpmCreditLetterApprovalStatusListener extends BpmProcessInstanceStatusEventListener { - - @Resource - private AmountCreditApplyApi amountCreditApplyApi; - - @Override - protected List getProcessDefinitionKey() { - return List.of(amountCreditApplyApi.PROCESS_KEY); - } - - @Override - protected void onEvent(BpmProcessInstanceStatusEvent event) { - // 将流程中的状态转换为业务的状态 - Integer status = event.getStatus(); - String approvalStatus = null; - if (BpmProcessInstanceStatusEnum.APPROVE.getStatus() == status) { - approvalStatus = AmountCreditApplyApiStatusEnum.ACAS_PASS.getCode(); - } else if (BpmProcessInstanceStatusEnum.REJECT.getStatus() == status) { - approvalStatus = AmountCreditApplyApiStatusEnum.ACAS_REJECT.getCode(); - } - amountCreditApplyApi.updateAmountCreditApplyStatus(Long.parseLong(event.getBusinessKey()),approvalStatus); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java deleted file mode 100644 index 76350bc..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.zt.plat.module.bpm.service.task; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; -import jakarta.validation.constraints.NotEmpty; -import org.flowable.bpmn.model.FlowNode; - -import java.util.Collection; -import java.util.List; - -/** - * 流程抄送 Service 接口 - * - * 现在是在审批的时候进行流程抄送 - */ -public interface BpmProcessInstanceCopyService { - - /** - * 【管理员】流程实例的抄送 - * - * @param userIds 抄送的用户编号 - * @param reason 抄送意见 - * @param taskId 流程任务编号 - */ - void createProcessInstanceCopy(Collection userIds, String reason, String taskId); - - /** - * 【自动抄送】流程实例的抄送 - * - * @param userIds 抄送的用户编号 - * @param reason 抄送意见 - * @param processInstanceId 流程编号 - * @param activityId 流程活动编号(对应 {@link FlowNode#getId()}) - * @param activityName 任务编号(对应 {@link FlowNode#getName()}) - * @param taskId 任务编号,允许空 - */ - void createProcessInstanceCopy(Collection userIds, String reason, - @NotEmpty(message = "流程实例编号不能为空") String processInstanceId, - @NotEmpty(message = "流程活动编号不能为空") String activityId, - @NotEmpty(message = "流程活动名字不能为空") String activityName, - String taskId); - - /** - * 获得抄送的流程的分页 - * - * @param userId 当前登录用户 - * @param pageReqVO 分页请求 - * @return 抄送的分页结果 - */ - PageResult getProcessInstanceCopyPage(Long userId, - BpmProcessInstanceCopyPageReqVO pageReqVO); - - /** - * 删除抄送流程 - * - * @param processInstanceId 流程实例 ID - */ - void deleteProcessInstanceCopy(String processInstanceId); - - /** - * 获得流程的抄送列表 - * - * @param processInstanceId 流程实例 ID - * @return 抄送流程列表 - */ - List getByProcessInstanceId(String processInstanceId); - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java deleted file mode 100644 index 5fec394..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.zt.plat.module.bpm.service.task; - -import cn.hutool.core.util.ObjectUtil; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCopyPageReqVO; -import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; -import com.zt.plat.module.bpm.dal.mysql.task.BpmProcessInstanceCopyMapper; -import com.zt.plat.module.bpm.enums.ErrorCodeConstants; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.repository.ProcessDefinition; -import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.task.api.Task; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.validation.annotation.Validated; - -import java.util.Collection; -import java.util.List; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; - -/** - * 流程抄送 Service 实现类 - * - * @author kyle - */ -@Service -@Validated -@Slf4j -public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopyService { - - @Resource - private BpmProcessInstanceCopyMapper processInstanceCopyMapper; - - @Resource - @Lazy // 延迟加载,避免循环依赖 - private BpmTaskService taskService; - - @Resource - @Lazy // 延迟加载,避免循环依赖 - private BpmProcessInstanceService processInstanceService; - @Resource - @Lazy // 延迟加载,避免循环依赖 - private BpmProcessDefinitionService processDefinitionService; - - @Override - public void createProcessInstanceCopy(Collection userIds, String reason, String taskId) { - Task task = taskService.getTask(taskId); - if (ObjectUtil.isNull(task)) { - throw exception(ErrorCodeConstants.TASK_NOT_EXISTS); - } - // 执行抄送 - createProcessInstanceCopy(userIds, reason, - task.getProcessInstanceId(), task.getTaskDefinitionKey(), task.getId(), task.getName()); - } - - @Override - public void createProcessInstanceCopy(Collection userIds, String reason, String processInstanceId, - String activityId, String activityName, String taskId) { - // 1.1 校验流程实例存在 - ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); - if (processInstance == null) { - throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); - } - // 1.2 校验流程定义存在 - ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( - processInstance.getProcessDefinitionId()); - if (processDefinition == null) { - throw exception(ErrorCodeConstants.PROCESS_DEFINITION_NOT_EXISTS); - } - - // 2. 创建抄送流程 - List copyList = convertList(userIds, userId -> new BpmProcessInstanceCopyDO() - .setUserId(userId).setReason(reason).setStartUserId(Long.valueOf(processInstance.getStartUserId())) - .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName()) - .setCategory(processDefinition.getCategory()).setTaskId(taskId) - .setActivityId(activityId).setActivityName(activityName) - .setProcessDefinitionId(processInstance.getProcessDefinitionId())); - processInstanceCopyMapper.insertBatch(copyList); - } - - @Override - public PageResult getProcessInstanceCopyPage(Long userId, - BpmProcessInstanceCopyPageReqVO pageReqVO) { - return processInstanceCopyMapper.selectPage(userId, pageReqVO); - } - - @Override - public void deleteProcessInstanceCopy(String processInstanceId) { - processInstanceCopyMapper.deleteByProcessInstanceId(processInstanceId); - } - - @Override - public List getByProcessInstanceId(String processInstanceId) { - return processInstanceCopyMapper.getByProcessInstanceId(processInstanceId); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java deleted file mode 100644 index 98af5ee..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceService.java +++ /dev/null @@ -1,191 +0,0 @@ -package com.zt.plat.module.bpm.service.task; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; -import jakarta.validation.Valid; -import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.runtime.ProcessInstance; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertMap; - -/** - * 流程实例 Service 接口 - * - * @author ZT - */ -public interface BpmProcessInstanceService { - - // ========== Query 查询相关方法 ========== - - /** - * 获得流程实例 - * - * @param id 流程实例的编号 - * @return 流程实例 - */ - ProcessInstance getProcessInstance(String id); - - /** - * 获得流程实例列表 - * - * @param ids 流程实例的编号集合 - * @return 流程实例列表 - */ - List getProcessInstances(Set ids); - - /** - * 获得流程实例 Map - * - * @param ids 流程实例的编号集合 - * @return 流程实例列表 Map - */ - default Map getProcessInstanceMap(Set ids) { - return convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId); - } - - /** - * 获得历史的流程实例 - * - * @param id 流程实例的编号 - * @return 历史的流程实例 - */ - HistoricProcessInstance getHistoricProcessInstance(String id); - - /** - * 获得历史的流程实例列表 - * - * @param ids 流程实例的编号集合 - * @return 历史的流程实例列表 - */ - List getHistoricProcessInstances(Set ids); - - /** - * 获得历史的流程实例 Map - * - * @param ids 流程实例的编号集合 - * @return 历史的流程实例列表 Map - */ - default Map getHistoricProcessInstanceMap(Set ids) { - return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId); - } - - /** - * 获得流程实例的分页 - * - * @param userId 用户编号 - * @param pageReqVO 分页请求 - * @return 流程实例的分页 - */ - PageResult getProcessInstancePage(Long userId, - @Valid BpmProcessInstancePageReqVO pageReqVO); - - /** - * 获取审批详情。 - *

- * 可以是准备发起的流程、进行中的流程、已经结束的流程 - * - * @param loginUserId 登录人的用户编号 - * @param reqVO 请求信息 - * @return 流程实例的进度 - */ - BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); - - /** - * 获取下一个执行节点信息 - * - * @param loginUserId 登录人的用户编号 - * @param reqVO 请求信息 - * @return 下一个执行节点信息 - */ - List getNextApprovalNodes(Long loginUserId, @Valid BpmApprovalDetailReqVO reqVO); - - /** - * 获取流程实例的 BPMN 模型视图 - * - * @param id 流程实例的编号 - * @return BPMN 模型视图 - */ - BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id); - - // ========== Update 写入相关方法 ========== - - /** - * 创建流程实例(提供给前端) - * - * @param userId 用户编号 - * @param createReqVO 创建信息 - * @return 实例的编号 - */ - String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO); - - /** - * 创建流程实例(提供给内部) - * - * @param userId 用户编号 - * @param createReqDTO 创建信息 - * @return 实例的编号 - */ - String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO); - - /** - * 发起人取消流程实例 - * - * @param userId 用户编号 - * @param cancelReqVO 取消信息 - */ - void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO); - - /** - * 管理员取消流程实例 - * - * @param userId 用户编号 - * @param cancelReqVO 取消信息 - */ - void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO); - - /** - * 更新 ProcessInstance 为不通过 - * - * @param processInstance 流程实例 - * @param reason 理由。例如说,审批不通过时,需要传递该值 - */ - void updateProcessInstanceReject(ProcessInstance processInstance, String reason); - - /** - * 更新 ProcessInstance 的变量 - * - * @param id 流程编号 - * @param variables 流程变量 - */ - void updateProcessInstanceVariables(String id, Map variables); - - /** - * 删除 ProcessInstance 的变量 - * - * @param id 流程编号 - * @param variableNames 流程变量名 - */ - void removeProcessInstanceVariables(String id, Collection variableNames); - - // ========== Event 事件相关方法 ========== - - /** - * 处理 ProcessInstance 完成事件,例如说:审批通过、不通过、取消 - * - * @param instance 流程任务 - */ - void processProcessInstanceCompleted(ProcessInstance instance); - - /** - * 处理 ProcessInstance 开始事件,例如说:流程前置通知 - * - * @param instance 流程任务 - */ - void processProcessInstanceCreated(ProcessInstance instance); -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java deleted file mode 100644 index d675a7e..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ /dev/null @@ -1,964 +0,0 @@ -package com.zt.plat.module.bpm.service.task; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.collection.ListUtil; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ObjUtil; -import cn.hutool.core.util.ObjectUtil; -import cn.hutool.core.util.StrUtil; -import com.zt.plat.framework.business.core.util.DeptUtil; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.date.DateUtils; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.framework.common.util.object.ObjectUtils; -import com.zt.plat.framework.common.util.object.PageUtils; -import com.zt.plat.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; -import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNodeTask; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.BpmTaskRespVO; -import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceConvert; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.dal.redis.BpmProcessIdRedisDAO; -import com.zt.plat.module.bpm.enums.ErrorCodeConstants; -import com.zt.plat.module.bpm.enums.definition.BpmModelTypeEnum; -import com.zt.plat.module.bpm.enums.definition.BpmSimpleModelNodeTypeEnum; -import com.zt.plat.module.bpm.enums.task.BpmProcessInstanceStatusEnum; -import com.zt.plat.module.bpm.enums.task.BpmReasonEnum; -import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.BpmTaskCandidateInvoker; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; -import com.zt.plat.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import com.zt.plat.module.bpm.service.message.BpmMessageService; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.constants.BpmnXMLConstants; -import org.flowable.bpmn.model.*; -import org.flowable.engine.HistoryService; -import org.flowable.engine.RuntimeService; -import org.flowable.engine.history.HistoricActivityInstance; -import org.flowable.engine.history.HistoricProcessInstance; -import org.flowable.engine.history.HistoricProcessInstanceQuery; -import org.flowable.engine.repository.ProcessDefinition; -import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.engine.runtime.ProcessInstanceBuilder; -import org.flowable.task.api.Task; -import org.flowable.task.api.history.HistoricTaskInstance; -import org.springframework.context.annotation.Lazy; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.validation.annotation.Validated; - -import java.util.*; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; -import static com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmApprovalDetailRespVO.ActivityNode; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; -import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; -import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseNodeType; -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; -import static org.flowable.bpmn.constants.BpmnXMLConstants.*; - -/** - * 流程实例 Service 实现类 - *

- * ProcessDefinition & ProcessInstance & Execution & Task 的关系: - * 1. - *

- * HistoricProcessInstance & ProcessInstance 的关系: - * 1. - *

- * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 - * - * @author ZT - */ -@Service -@Validated -@Slf4j -public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { - - @Resource - private RuntimeService runtimeService; - @Resource - private HistoryService historyService; - - @Resource - private BpmProcessDefinitionService processDefinitionService; - @Resource - @Lazy // 避免循环依赖 - private BpmTaskService taskService; - @Resource - private BpmMessageService messageService; - - @Resource - private AdminUserApi adminUserApi; - @Resource - private DeptApi deptApi; - - @Resource - private BpmProcessInstanceEventPublisher processInstanceEventPublisher; - - @Resource - private BpmTaskCandidateInvoker taskCandidateInvoker; - - @Resource - private BpmProcessIdRedisDAO processIdRedisDAO; - - // ========== Query 查询相关方法 ========== - - @Override - public ProcessInstance getProcessInstance(String id) { - return runtimeService.createProcessInstanceQuery() - .includeProcessVariables() - .processInstanceId(id) - .singleResult(); - } - - @Override - public List getProcessInstances(Set ids) { - return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables().list(); - } - - @Override - public HistoricProcessInstance getHistoricProcessInstance(String id) { - return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables() - .singleResult(); - } - - @Override - public List getHistoricProcessInstances(Set ids) { - return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).includeProcessVariables() - .list(); - } - - private Map getFormFieldsPermission(BpmnModel bpmnModel, - String activityId, String taskId) { - // 1. 获取流程活动编号。流程活动 Id 为空事,从流程任务中获取流程活动 Id - if (StrUtil.isEmpty(activityId) && StrUtil.isNotEmpty(taskId)) { - activityId = Optional.ofNullable(taskService.getHistoricTask(taskId)) - .map(HistoricTaskInstance::getTaskDefinitionKey).orElse(null); - } - if (StrUtil.isEmpty(activityId)) { - return null; - } - - // 2. 从 BpmnModel 中解析表单字段权限 - return BpmnModelUtils.parseFormFieldsPermission(bpmnModel, activityId); - } - - @Override - public BpmApprovalDetailRespVO getApprovalDetail(Long loginUserId, BpmApprovalDetailReqVO reqVO) { - // 1.1 从 reqVO 中,读取公共变量 - Long startUserId = loginUserId; // 流程发起人 - HistoricProcessInstance historicProcessInstance = null; // 流程实例 - Integer processInstanceStatus = BpmProcessInstanceStatusEnum.NOT_START.getStatus(); // 流程状态 - Map processVariables = new HashMap<>(); // 流程变量 - // 1.2 如果是流程已发起的场景,则使用流程实例的数据 - if (reqVO.getProcessInstanceId() != null) { - historicProcessInstance = getHistoricProcessInstance(reqVO.getProcessInstanceId()); - if (historicProcessInstance == null) { - throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); - } - startUserId = Long.valueOf(historicProcessInstance.getStartUserId()); - processInstanceStatus = FlowableUtils.getProcessInstanceStatus(historicProcessInstance); - // 合并 DB 和前端传递的流量变量,以前端的为主 - if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) { - processVariables.putAll(historicProcessInstance.getProcessVariables()); - } - } - if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { - processVariables.putAll(reqVO.getProcessVariables()); - } - // 1.3 读取其它相关数据 - ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( - historicProcessInstance != null ? historicProcessInstance.getProcessDefinitionId() - : reqVO.getProcessDefinitionId()); - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService - .getProcessDefinitionInfo(processDefinition.getId()); - BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processDefinition.getId()); - - // 2.1 已结束 + 进行中的活动节点 - List endActivityNodes = null; // 已结束的审批信息 - List runActivityNodes = null; // 进行中的审批信息 - List activities = null; // 流程实例列表 - if (reqVO.getProcessInstanceId() != null) { - activities = taskService.getActivityListByProcessInstanceId(reqVO.getProcessInstanceId()); - List tasks = taskService.getTaskListByProcessInstanceId(reqVO.getProcessInstanceId(), - true); - endActivityNodes = getEndActivityNodeList(startUserId, bpmnModel, processDefinitionInfo, - historicProcessInstance, processInstanceStatus, activities, tasks); - runActivityNodes = getRunApproveNodeList(startUserId, bpmnModel, processDefinition, processVariables, - activities, tasks); - } - - // 2.2 流程已经结束,直接 return,无需预测 - if (BpmProcessInstanceStatusEnum.isProcessEndStatus(processInstanceStatus)) { - return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, - historicProcessInstance, - processInstanceStatus, endActivityNodes, runActivityNodes, null, null); - } - - // 3.1 计算当前登录用户的待办任务 - BpmTaskRespVO todoTask = taskService.getTodoTask(loginUserId, reqVO.getTaskId(), reqVO.getProcessInstanceId()); - // 3.2 预测未运行节点的审批信息 - List simulateActivityNodes = getSimulateApproveNodeList(startUserId, bpmnModel, - processDefinitionInfo, - processVariables, activities); - // 3.3 如果是发起动作,activityId 为开始节点,不校验审批人自选节点 - if (ObjUtil.equals(reqVO.getActivityId(), BpmnModelConstants.START_USER_NODE_ID)) { - simulateActivityNodes.removeIf(node -> - BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy().equals(node.getCandidateStrategy())); - } - - // 4. 拼接最终数据 - return buildApprovalDetail(reqVO, bpmnModel, processDefinition, processDefinitionInfo, historicProcessInstance, - processInstanceStatus, endActivityNodes, runActivityNodes, simulateActivityNodes, todoTask); - } - - @Override - public List getNextApprovalNodes(Long loginUserId, BpmApprovalDetailReqVO reqVO) { - // 1.1 校验任务存在,且是当前用户的 - Task task = taskService.validateTask(loginUserId, reqVO.getTaskId()); - // 1.2 校验流程实例存在 - ProcessInstance instance = getProcessInstance(task.getProcessInstanceId()); - if (instance == null) { - throw exception(PROCESS_INSTANCE_NOT_EXISTS); - } - HistoricProcessInstance historicProcessInstance = getHistoricProcessInstance(task.getProcessInstanceId()); - if (historicProcessInstance == null) { - throw exception(ErrorCodeConstants.PROCESS_INSTANCE_NOT_EXISTS); - } - // 1.3 校验BpmnModel - BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(task.getProcessDefinitionId()); - if (bpmnModel == null) { - return null; - } - - // 2. 设置流程变量 - Map processVariables = new HashMap<>(); - // 2.1 获取历史中流程变量 - if (CollUtil.isNotEmpty(historicProcessInstance.getProcessVariables())) { - processVariables.putAll(historicProcessInstance.getProcessVariables()); - } - // 2.2 合并前端传递的流程变量,以前端为准 - if (CollUtil.isNotEmpty(reqVO.getProcessVariables())) { - processVariables.putAll(reqVO.getProcessVariables()); - } - - // 3. 获取下一个将要执行的节点集合 - FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey()); - List nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables); - List nextActivityNodes = convertList(nextFlowNodes, node -> new ActivityNode().setId(node.getId()) - .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) - .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) - .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) - .setCandidateUserIds(getTaskCandidateUserList(bpmnModel, node.getId(), - loginUserId, historicProcessInstance.getProcessDefinitionId(), processVariables))); - if (CollUtil.isNotEmpty(nextActivityNodes)) { - return nextActivityNodes; - } - - // 4. 拼接基础信息 - Map userMap = adminUserApi.getUserMap( - convertSetByFlatMap(nextActivityNodes, ActivityNode::getCandidateUserIds, Collection::stream)); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); - nextActivityNodes.forEach(node -> node.setCandidateUsers(convertList(node.getCandidateUserIds(), userId -> { - AdminUserRespDTO user = userMap.get(userId); - if (user != null) { - return BpmProcessInstanceConvert.INSTANCE.buildUser(userId, userMap, deptMap); - } - return null; - }))); - return nextActivityNodes; - } - - @Override - @SuppressWarnings("unchecked") - public PageResult getProcessInstancePage(Long userId, - BpmProcessInstancePageReqVO pageReqVO) { - // 1. 构建查询条件 - HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() - .includeProcessVariables() - .processInstanceTenantId(FlowableUtils.getTenantId()) - .orderByProcessInstanceStartTime().desc(); - if (userId != null) { // 【我的流程】菜单时,需要传递该字段 - processInstanceQuery.startedBy(String.valueOf(userId)); - } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 - processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); - } - if (StrUtil.isNotEmpty(pageReqVO.getName())) { - processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); - } - if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionKey())) { - processInstanceQuery.processDefinitionKey(pageReqVO.getProcessDefinitionKey()); - } - if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { - processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); - } - if (pageReqVO.getStatus() != null) { - processInstanceQuery.variableValueEquals(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, - pageReqVO.getStatus()); - } - if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { - processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); - processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); - } - if (ArrayUtil.isNotEmpty(pageReqVO.getEndTime())) { - processInstanceQuery.finishedAfter(DateUtils.of(pageReqVO.getEndTime()[0])); - processInstanceQuery.finishedBefore(DateUtils.of(pageReqVO.getEndTime()[1])); - } - // 表单字段查询 - Map formFieldsParams = JsonUtils.parseObject(pageReqVO.getFormFieldsParams(), Map.class); - if (CollUtil.isNotEmpty(formFieldsParams)) { - formFieldsParams.forEach((key, value) -> { - if (StrUtil.isEmpty(String.valueOf(value))) { - return; - } - // TODO @lesan:应支持多种类型的查询方式,目前只有字符串全等 - processInstanceQuery.variableValueEquals(key, value); - }); - } - - // 2.1 查询数量 - long processInstanceCount = processInstanceQuery.count(); - if (processInstanceCount == 0) { - return PageResult.empty(processInstanceCount); - } - // 2.2 查询列表 - List processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), - pageReqVO.getPageSize()); - return new PageResult<>(processInstanceList, processInstanceCount); - } - - /** - * 拼接审批详情的最终数据 - *

- * 主要是,拼接审批人的用户信息、部门信息 - */ - private BpmApprovalDetailRespVO buildApprovalDetail(BpmApprovalDetailReqVO reqVO, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance processInstance, - Integer processInstanceStatus, - List endApprovalNodeInfos, - List runningApprovalNodeInfos, - List simulateApprovalNodeInfos, - BpmTaskRespVO todoTask) { - // 1. 获取所有需要读取用户信息的 userIds - List approveNodes = newArrayList( - asList(endApprovalNodeInfos, runningApprovalNodeInfos, simulateApprovalNodeInfos)); - Set userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds(processInstance, approveNodes, todoTask); - Map userMap = adminUserApi.getUserMap(userIds); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); - - // 2. 表单权限 - String taskId = reqVO.getTaskId() == null && todoTask != null ? todoTask.getId() : reqVO.getTaskId(); - Map formFieldsPermission = getFormFieldsPermission(bpmnModel, reqVO.getActivityId(), taskId); - - // 3. 拼接数据 - return BpmProcessInstanceConvert.INSTANCE.buildApprovalDetail(bpmnModel, processDefinition, - processDefinitionInfo, processInstance, - processInstanceStatus, approveNodes, todoTask, formFieldsPermission, userMap, deptMap); - } - - /** - * 获得【已结束】的活动节点们 - */ - private List getEndActivityNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - HistoricProcessInstance historicProcessInstance, Integer processInstanceStatus, - List activities, List tasks) { - // 遍历 tasks 列表,只处理已结束的 UserTask - // 为什么不通过 activities 呢?因为,加签场景下,它只存在于 tasks,没有 activities,导致如果遍历 activities 的话,它无法成为一个节点 - List endTasks = filterList(tasks, task -> task.getEndTime() != null); - List approvalNodes = convertList(endTasks, task -> { - FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); - ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName()) - .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey()) - ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType() - : ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”的识别 - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) - .setStatus(FlowableUtils.getTaskStatus(task)) - .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) - .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime())) - .setTasks(singletonList(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task))); - // 如果是取消状态,则跳过 - if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) { - return null; - } - return activityNode; - }); - - // 遍历 activities,只处理已结束的 StartEvent、EndEvent - List endActivities = filterList(activities, activity -> activity.getEndTime() != null - && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_EVENT_START, ELEMENT_CALL_ACTIVITY, ELEMENT_EVENT_END))); - endActivities.forEach(activity -> { - // StartEvent:只处理 BPMN 的场景。因为,SIMPLE 情况下,已经有 START_USER_NODE 节点 - if (ELEMENT_EVENT_START.equals(activity.getActivityType()) - && BpmModelTypeEnum.BPMN.getType().equals(processDefinitionInfo.getModelType())) { - ActivityNodeTask startTask = new ActivityNodeTask().setId(BpmnModelConstants.START_USER_NODE_ID) - .setAssignee(startUserId).setStatus(BpmTaskStatusEnum.APPROVE.getStatus()); - ActivityNode startNode = new ActivityNode().setId(startTask.getId()) - .setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName()) - .setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()) - .setStatus(startTask.getStatus()).setTasks(ListUtil.of(startTask)) - .setStartTime(DateUtils.of(activity.getStartTime())) - .setEndTime(DateUtils.of(activity.getEndTime())); - approvalNodes.add(0, startNode); - return; - } - // EndEvent - if (ELEMENT_EVENT_END.equals(activity.getActivityType())) { - if (BpmProcessInstanceStatusEnum.isRejectStatus(processInstanceStatus)) { - // 拒绝情况下,不需要展示 EndEvent 结束节点。原因是:前端已经展示 x 效果,无需重复展示 - return; - } - ActivityNode endNode = new ActivityNode().setId(activity.getId()) - .setName(BpmSimpleModelNodeTypeEnum.END_NODE.getName()) - .setNodeType(BpmSimpleModelNodeTypeEnum.END_NODE.getType()).setStatus(processInstanceStatus) - .setStartTime(DateUtils.of(activity.getStartTime())) - .setEndTime(DateUtils.of(activity.getEndTime())); - String reason = FlowableUtils.getProcessInstanceReason(historicProcessInstance); - if (StrUtil.isNotEmpty(reason)) { - endNode.setTasks(singletonList(new ActivityNodeTask().setId(endNode.getId()) - .setStatus(endNode.getStatus()).setReason(reason))); - } - approvalNodes.add(endNode); - } - // CallActivity - if (ELEMENT_CALL_ACTIVITY.equals(activity.getActivityType())) { - ActivityNode callActivity = new ActivityNode().setId(activity.getId()) - .setName(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getName()) - .setNodeType(BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType()).setStatus(processInstanceStatus) - .setStartTime(DateUtils.of(activity.getStartTime())) - .setEndTime(DateUtils.of(activity.getEndTime())) - .setProcessInstanceId(activity.getProcessInstanceId()); - approvalNodes.add(callActivity); - } - }); - - // 按照时间排序 - approvalNodes.sort(Comparator.comparing(ActivityNode::getStartTime)); - return approvalNodes; - } - - /** - * 获得【进行中】的活动节点们 - */ - private List getRunApproveNodeList(Long startUserId, - BpmnModel bpmnModel, - ProcessDefinition processDefinition, - Map processVariables, - List activities, - List tasks) { - // 构建运行中的任务、子流程,基于 activityId 分组 - List runActivities = filterList(activities, activity -> activity.getEndTime() == null - && (StrUtil.equalsAny(activity.getActivityType(), ELEMENT_TASK_USER, ELEMENT_CALL_ACTIVITY))); - Map> runningTaskMap = convertMultiMap(runActivities, - HistoricActivityInstance::getActivityId); - - // 按照 activityId 分组,构建 ApprovalNodeInfo 节点 - Map taskMap = convertMap(tasks, HistoricTaskInstance::getId); - return convertList(runningTaskMap.entrySet(), entry -> { - String activityId = entry.getKey(); - List taskActivities = entry.getValue(); - // 构建活动节点 - FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, activityId); - HistoricActivityInstance firstActivity = CollUtil.getFirst(taskActivities); // 取第一个任务,会签/或签的任务,开始时间相同 - ActivityNode activityNode = new ActivityNode().setId(firstActivity.getActivityId()) - .setName(firstActivity.getActivityName()) - .setNodeType(ObjUtil.defaultIfNull(parseNodeType(flowNode), // 目的:解决“办理节点”和"子流程"的识别 - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())) - .setStatus(BpmTaskStatusEnum.RUNNING.getStatus()) - .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode)) - .setStartTime(DateUtils.of(CollUtil.getFirst(taskActivities).getStartTime())) - .setTasks(new ArrayList<>()); - // 处理每个任务的 tasks 属性 - for (HistoricActivityInstance activity : taskActivities) { - HistoricTaskInstance task = taskMap.get(activity.getTaskId()); - // 特殊情况:子流程节点 ChildProcess 仅存在于 activity 中,并且没有自身的 task,需要跳过执行 - // TODO @ZT:后续看看怎么优化! - if (task == null) { - continue; - } - activityNode.getTasks().add(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task)); - // 加签子任务,需要过滤掉已经完成的加签子任务 - List childrenTasks = filterList( - taskService.getAllChildrenTaskListByParentTaskId(activity.getTaskId(), tasks), - childTask -> childTask.getEndTime() == null); - if (CollUtil.isNotEmpty(childrenTasks)) { - activityNode.getTasks().addAll( - convertList(childrenTasks, BpmProcessInstanceConvert.INSTANCE::buildApprovalTaskInfo)); - } - } - // 处理每个任务的 candidateUsers 属性:如果是依次审批,需要预测它的后续审批人。因为 Task 是审批完一个,创建一个新的 Task - if (BpmnModelUtils.isSequentialUserTask(flowNode)) { - List candidateUserIds = getTaskCandidateUserList(bpmnModel, flowNode.getId(), - startUserId, processDefinition.getId(), processVariables); - // 截取当前审批人位置后面的候选人,不包含当前审批人 - ActivityNodeTask approvalTaskInfo = CollUtil.getFirst(activityNode.getTasks()); - Assert.notNull(approvalTaskInfo, "任务不能为空"); - int index = CollUtil.indexOf(candidateUserIds, - userId -> ObjectUtils.equalsAny(userId, approvalTaskInfo.getOwner(), - approvalTaskInfo.getAssignee())); // 委派或者向前加签情况,需要先比较 owner - activityNode.setCandidateUserIds(CollUtil.sub(candidateUserIds, index + 1, candidateUserIds.size())); - } - if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(activityNode.getNodeType())) { - activityNode.setProcessInstanceId(firstActivity.getProcessInstanceId()); - } - return activityNode; - }); - } - - /** - * 获得【预测(未来)】的活动节点们 - */ - private List getSimulateApproveNodeList(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, - Map processVariables, - List activities) { - // TODO @ZT:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance - // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 - Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); - // 情况一:BPMN 设计器 - if (Objects.equals(BpmModelTypeEnum.BPMN.getType(), processDefinitionInfo.getModelType())) { - List flowElements = BpmnModelUtils.simulateProcess(bpmnModel, processVariables); - return convertList(flowElements, flowElement -> buildNotRunApproveNodeForBpmn(startUserId, bpmnModel, - processDefinitionInfo, processVariables, flowElement, runActivityIds)); - } - // 情况二:SIMPLE 设计器 - if (Objects.equals(BpmModelTypeEnum.SIMPLE.getType(), processDefinitionInfo.getModelType())) { - BpmSimpleModelNodeVO simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), - BpmSimpleModelNodeVO.class); - List simpleNodes = SimpleModelUtils.simulateProcess(simpleModel, processVariables); - return convertList(simpleNodes, simpleNode -> buildNotRunApproveNodeForSimple(startUserId, bpmnModel, - processDefinitionInfo, processVariables, simpleNode, runActivityIds)); - } - throw new IllegalArgumentException("未知设计器类型:" + processDefinitionInfo.getModelType()); - } - - private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - BpmSimpleModelNodeVO node, Set runActivityIds) { - // TODO @ZT:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance - // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 - if (runActivityIds.contains(node.getId())) { - return null; - } - - ActivityNode activityNode = new ActivityNode().setId(node.getId()).setName(node.getName()) - .setNodeType(node.getType()).setCandidateStrategy(node.getCandidateStrategy()) - .setStatus(BpmTaskStatusEnum.NOT_START.getStatus()); - - // 1. 开始节点/审批节点 - if (ObjectUtils.equalsAny(node.getType(), - BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType(), - BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType(), - BpmSimpleModelNodeTypeEnum.TRANSACTOR_NODE.getType())) { - List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), - startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); - activityNode.setCandidateUserIds(candidateUserIds); - return activityNode; - } - - // 2. 结束节点 - if (BpmSimpleModelNodeTypeEnum.END_NODE.getType().equals(node.getType())) { - return activityNode; - } - - // 3. 抄送节点 - if (CollUtil.isEmpty(runActivityIds) && // 流程发起时:需要展示抄送节点,用于选择抄送人 - BpmSimpleModelNodeTypeEnum.COPY_NODE.getType().equals(node.getType())) { - List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), - startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); - activityNode.setCandidateUserIds(candidateUserIds); - return activityNode; - } - - // 4. 子流程节点 - if (BpmSimpleModelNodeTypeEnum.CHILD_PROCESS.getType().equals(node.getType())) { - return activityNode; - } - return null; - } - - private ActivityNode buildNotRunApproveNodeForBpmn(Long startUserId, BpmnModel bpmnModel, - BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, - FlowElement node, Set runActivityIds) { - if (runActivityIds.contains(node.getId())) { - return null; - } - ActivityNode activityNode = new ActivityNode().setId(node.getId()) - .setStatus(BpmTaskStatusEnum.NOT_START.getStatus()); - - // 1. 开始节点 - if (node instanceof StartEvent) { - return activityNode.setName(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getName()) - .setNodeType(BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()); - } - - // 2. 审批节点 - if (node instanceof UserTask) { - List candidateUserIds = getTaskCandidateUserList(bpmnModel, node.getId(), - startUserId, processDefinitionInfo.getProcessDefinitionId(), processVariables); - return activityNode.setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType()) - .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(node)) - .setCandidateUserIds(candidateUserIds); - } - - // 3. 结束节点 - if (node instanceof EndEvent) { - return activityNode.setName(BpmSimpleModelNodeTypeEnum.END_NODE.getName()) - .setNodeType(BpmSimpleModelNodeTypeEnum.END_NODE.getType()); - } - return null; - } - - private List getTaskCandidateUserList(BpmnModel bpmnModel, String activityId, - Long startUserId, String processDefinitionId, Map processVariables) { - Set userIds = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, - startUserId, processDefinitionId, processVariables); - return new ArrayList<>(userIds); - } - - @Override - public BpmProcessInstanceBpmnModelViewRespVO getProcessInstanceBpmnModelView(String id) { - // 1.1 获得流程实例 - HistoricProcessInstance processInstance = getHistoricProcessInstance(id); - if (processInstance == null) { - return null; - } - // 1.2 获得流程定义 - BpmnModel bpmnModel = processDefinitionService - .getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()); - if (bpmnModel == null) { - return null; - } - BpmSimpleModelNodeVO simpleModel = null; - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo( - processInstance.getProcessDefinitionId()); - if (processDefinitionInfo != null - && BpmModelTypeEnum.SIMPLE.getType().equals(processDefinitionInfo.getModelType())) { - simpleModel = JsonUtils.parseObject(processDefinitionInfo.getSimpleModel(), BpmSimpleModelNodeVO.class); - } - // 1.3 获得流程实例对应的活动实例列表 + 任务列表 - List activities = taskService.getActivityListByProcessInstanceId(id); - List tasks = taskService.getTaskListByProcessInstanceId(id, true); - - // 2.1 拼接进度信息 - Set unfinishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, - activityInstance -> activityInstance.getEndTime() == null); - Set finishedTaskActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, - activityInstance -> activityInstance.getEndTime() != null - && ObjectUtil.notEqual(activityInstance.getActivityType(), - BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); - Set finishedSequenceFlowActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId, - activityInstance -> activityInstance.getEndTime() != null - && ObjectUtil.equals(activityInstance.getActivityType(), - BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW)); - // 特殊:会签情况下,会有部分已完成(审批)、部分未完成(待审批),此时需要 finishedTaskActivityIds 移除掉 - finishedTaskActivityIds.removeAll(unfinishedTaskActivityIds); - // 特殊:如果流程实例被拒绝,则需要计算是哪个活动节点。 - // 注意,只取最后一个。因为会存在多次拒绝的情况,拒绝驳回到指定节点 - Set rejectTaskActivityIds = CollUtil.newHashSet(); - if (BpmProcessInstanceStatusEnum.isRejectStatus(FlowableUtils.getProcessInstanceStatus(processInstance))) { - tasks.stream() - .filter(task -> BpmTaskStatusEnum.isRejectStatus(FlowableUtils.getTaskStatus(task))) - .max(Comparator.comparing(HistoricTaskInstance::getEndTime)) - .ifPresent(reject -> rejectTaskActivityIds.add(reject.getTaskDefinitionKey())); - finishedTaskActivityIds.removeAll(rejectTaskActivityIds); - } - - // 2.2 拼接基础信息 - Set userIds = BpmProcessInstanceConvert.INSTANCE.parseUserIds02(processInstance, tasks); - Map userMap = adminUserApi.getUserMap(userIds); - Map deptMap = deptApi.getDeptMap(convertSet(userMap.values(), DeptUtil::getDeptId)); - return BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceBpmnModelView(processInstance, tasks, bpmnModel, - simpleModel, - unfinishedTaskActivityIds, finishedTaskActivityIds, finishedSequenceFlowActivityIds, - rejectTaskActivityIds, - userMap, deptMap); - } - - // ========== Update 写入相关方法 ========== - - @Override - @Transactional(rollbackFor = Exception.class) - public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { - // 获得流程定义 - ProcessDefinition definition = processDefinitionService - .getProcessDefinition(createReqVO.getProcessDefinitionId()); - // 发起流程 - return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, - createReqVO.getStartUserSelectAssignees()); - } - - @Override - public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { - return FlowableUtils.executeAuthenticatedUserId(userId, () -> { - // 获得流程定义 - ProcessDefinition definition = processDefinitionService - .getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); - // 发起流程 - return createProcessInstance0(userId, definition, createReqDTO.getVariables(), - createReqDTO.getBusinessKey(), - createReqDTO.getStartUserSelectAssignees()); - }); - } - - private String createProcessInstance0(Long userId, ProcessDefinition definition, - Map variables, String businessKey, - Map> startUserSelectAssignees) { - // 1.1 校验流程定义 - if (definition == null) { - throw exception(PROCESS_DEFINITION_NOT_EXISTS); - } - if (definition.isSuspended()) { - throw exception(PROCESS_DEFINITION_IS_SUSPENDED); - } - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService - .getProcessDefinitionInfo(definition.getId()); - if (processDefinitionInfo == null) { - throw exception(PROCESS_DEFINITION_NOT_EXISTS); - } - // 1.2 校验是否能够发起 - if (!processDefinitionService.canUserStartProcessDefinition(processDefinitionInfo, userId)) { - throw exception(PROCESS_INSTANCE_START_USER_CAN_START); - } - // 1.3 校验发起人自选审批人 - validateStartUserSelectAssignees(userId, definition, startUserSelectAssignees, variables); - - // 2. 创建流程实例 - if (variables == null) { - variables = new HashMap<>(); - } - FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, userId); // 设置流程变量,发起人 ID - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 - BpmProcessInstanceStatusEnum.RUNNING.getStatus()); - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_SKIP_EXPRESSION_ENABLED, true); // 跳过表达式需要添加此变量为 true,不影响没配置 skipExpression 的节点 - if (CollUtil.isNotEmpty(startUserSelectAssignees)) { - // 设置流程变量,发起人自选审批人 - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, - startUserSelectAssignees); - } - - // 3. 创建流程 - ProcessInstanceBuilder processInstanceBuilder = runtimeService.createProcessInstanceBuilder() - .processDefinitionId(definition.getId()) - .businessKey(businessKey) - .variables(variables); - // 3.1 创建流程 ID - BpmModelMetaInfoVO.ProcessIdRule processIdRule = processDefinitionInfo.getProcessIdRule(); - if (processIdRule != null && Boolean.TRUE.equals(processIdRule.getEnable())) { - processInstanceBuilder.predefineProcessInstanceId(processIdRedisDAO.generate(processIdRule)); - } - // 3.2 流程名称 - BpmModelMetaInfoVO.TitleSetting titleSetting = processDefinitionInfo.getTitleSetting(); - if (titleSetting != null && Boolean.TRUE.equals(titleSetting.getEnable())) { - AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); - Map cloneVariables = new HashMap<>(variables); - cloneVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_ID, user.getNickname()); - cloneVariables.put(BpmnVariableConstants.PROCESS_START_TIME, DateUtil.now()); - cloneVariables.put(BpmnVariableConstants.PROCESS_DEFINITION_NAME, definition.getName().trim()); - processInstanceBuilder.name(StrUtil.format(titleSetting.getTitle(), cloneVariables)); - } else { - processInstanceBuilder.name(definition.getName().trim()); - } - // 3.3 发起流程实例 - ProcessInstance instance = processInstanceBuilder.start(); - return instance.getId(); - } - - private void validateStartUserSelectAssignees(Long userId, ProcessDefinition definition, - Map> startUserSelectAssignees, - Map variables) { - // 1. 获取预测的节点信息 - BpmApprovalDetailRespVO detailRespVO = getApprovalDetail(userId, new BpmApprovalDetailReqVO() - .setProcessDefinitionId(definition.getId()) - .setProcessVariables(variables)); - List activityNodes = detailRespVO.getActivityNodes(); - if (CollUtil.isEmpty(activityNodes)) { - return; - } - - // 2.1 移除掉不是发起人自选审批人节点 - activityNodes.removeIf(task -> - ObjectUtil.notEqual(BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy(), task.getCandidateStrategy())); - // 2.2 流程发起时要先获取当前流程的预测走向节点,发起时只校验预测的节点发起人自选审批人的审批人和抄送人是否都配置了 - activityNodes.forEach(task -> { - List assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(task.getId()) : null; - if (CollUtil.isEmpty(assignees)) { - throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, task.getName()); - } - Map userMap = adminUserApi.getUserMap(assignees); - assignees.forEach(assignee -> { - if (userMap.get(assignee) == null) { - throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, task.getName(), assignee); - } - }); - }); - } - - @Override - public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { - // 1.1 校验流程实例存在 - ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); - if (instance == null) { - throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); - } - // 1.2 只能取消自己的 - if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { - throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); - } - // 1.3 校验允许撤销审批中的申请 - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService - .getProcessDefinitionInfo(instance.getProcessDefinitionId()); - Assert.notNull(processDefinitionInfo, "流程定义({})不存在", processDefinitionInfo); - if (processDefinitionInfo.getAllowCancelRunningProcess() != null // 防止未配置 AllowCancelRunningProcess , 默认为可取消 - && Boolean.FALSE.equals(processDefinitionInfo.getAllowCancelRunningProcess())) { - throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_ALLOW); - } - // 1.4 子流程不允许取消 - if (StrUtil.isNotBlank(instance.getSuperExecutionId())) { - throw exception(PROCESS_INSTANCE_CANCEL_CHILD_FAIL_NOT_ALLOW); - } - - // 2. 取消流程 - updateProcessInstanceCancel(cancelReqVO.getId(), - BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); - } - - @Override - public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { - // 1.1 校验流程实例存在 - ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); - if (instance == null) { - throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); - } - - // 2. 取消流程 - AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); - updateProcessInstanceCancel(cancelReqVO.getId(), - BpmReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); - } - - private void updateProcessInstanceCancel(String id, String reason) { - // 1. 更新流程实例 status - runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, - BpmProcessInstanceStatusEnum.CANCEL.getStatus()); - runtimeService.setVariable(id, BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON, reason); - - // 2. 取消所有子流程 - List childProcessInstances = runtimeService.createProcessInstanceQuery() - .superProcessInstanceId(id).list(); - childProcessInstances.forEach(processInstance -> updateProcessInstanceCancel( - processInstance.getProcessInstanceId(), BpmReasonEnum.CANCEL_CHILD_PROCESS_INSTANCE_BY_MAIN_PROCESS.getReason())); - - // 3. 结束流程 - taskService.moveTaskToEnd(id, reason); - } - - @Override - public void updateProcessInstanceReject(ProcessInstance processInstance, String reason) { - runtimeService.setVariable(processInstance.getProcessInstanceId(), - BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, - BpmProcessInstanceStatusEnum.REJECT.getStatus()); - runtimeService.setVariable(processInstance.getProcessInstanceId(), - BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON, - BpmReasonEnum.REJECT_TASK.format(reason)); - } - - @Override - public void updateProcessInstanceVariables(String id, Map variables) { - runtimeService.setVariables(id, variables); - } - - @Override - public void removeProcessInstanceVariables(String id, Collection variableNames) { - runtimeService.removeVariables(id, variableNames); - } - - // ========== Event 事件相关方法 ========== - - @Override - public void processProcessInstanceCompleted(ProcessInstance instance) { - // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 - FlowableUtils.execute(instance.getTenantId(), () -> { - // 1.1 获取当前状态 - Integer status = (Integer) instance.getProcessVariables() - .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS); - String reason = (String) instance.getProcessVariables() - .get(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_REASON); - // 1.2 当流程状态还是审批状态中,说明审批通过了,则变更下它的状态 - // 为什么这么处理?因为流程完成,并且完成了,说明审批通过了 - if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { - status = BpmProcessInstanceStatusEnum.APPROVE.getStatus(); - runtimeService.setVariable(instance.getId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_STATUS, - status); - } - - // 2. 发送对应的消息通知 - if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { - messageService.sendMessageWhenProcessInstanceApprove( - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); - } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) { - messageService.sendMessageWhenProcessInstanceReject( - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(instance, reason)); - } - - // 3. 发送流程实例的状态事件 - processInstanceEventPublisher.sendProcessInstanceResultEvent( - BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, instance, status)); - - // 4. 流程后置通知 - if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. - getProcessDefinitionInfo(instance.getProcessDefinitionId()); - if (ObjUtil.isNotNull(processDefinitionInfo) && - ObjUtil.isNotNull(processDefinitionInfo.getProcessAfterTriggerSetting())) { - BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessAfterTriggerSetting(); - - BpmHttpRequestUtils.executeBpmHttpRequest(instance, - setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); - } - } - }); - } - - @Override - public void processProcessInstanceCreated(ProcessInstance instance) { - // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 - FlowableUtils.execute(instance.getTenantId(), () -> { - // 流程前置通知 - BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionService. - getProcessDefinitionInfo(instance.getProcessDefinitionId()); - if (ObjUtil.isNull(processDefinitionInfo) || - ObjUtil.isNull(processDefinitionInfo.getProcessBeforeTriggerSetting())) { - return; - } - BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getProcessBeforeTriggerSetting(); - BpmHttpRequestUtils.executeBpmHttpRequest(instance, - setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); - }); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java deleted file mode 100644 index 8211bfe..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskService.java +++ /dev/null @@ -1,316 +0,0 @@ -package com.zt.plat.module.bpm.service.task; - -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.collection.CollectionUtils; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.*; -import com.zt.plat.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum; -import jakarta.validation.Valid; -import org.flowable.bpmn.model.UserTask; -import org.flowable.engine.history.HistoricActivityInstance; -import org.flowable.task.api.Task; -import org.flowable.task.api.TaskInfo; -import org.flowable.task.api.history.HistoricTaskInstance; - -import java.util.Collection; -import java.util.List; -import java.util.Map; - -/** - * 流程任务实例 Service 接口 - * - * @author jason - * @author ZT - */ -public interface BpmTaskService { - - // ========== Query 查询相关方法 ========== - - /** - * 获得待办的流程任务分页 - * - * @param userId 用户编号 - * @param pageReqVO 分页请求 - * @return 流程任务分页 - */ - PageResult getTaskTodoPage(Long userId, BpmTaskPageReqVO pageReqVO); - - /** - * 获得用户(待办)的任务: - * 1. 根据 taskId 查询待办任务 - * 2. 如果任务不存在(或者已审核),获取指定流程下,首个需要处理任务 - * - * @param userId 用户编号 - * @param taskId 任务编号 - * @param processInstanceId 流程实例编号 - * @return 待办任务 - */ - BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId); - - /** - * 获得已办的流程任务分页 - * - * @param userId 用户编号 - * @param pageReqVO 分页请求 - * @return 流程任务分页 - */ - PageResult getTaskDonePage(Long userId, BpmTaskPageReqVO pageReqVO); - - /** - * 获得全部的流程任务分页 - * - * @param userId 用户编号 - * @param pageReqVO 分页请求 - * @return 流程任务分页 - */ - PageResult getTaskPage(Long userId, BpmTaskPageReqVO pageReqVO); - - /** - * 获得流程任务 Map - * - * @param processInstanceIds 流程实例的编号数组 - * @return 流程任务 Map - */ - default Map> getTaskMapByProcessInstanceIds(List processInstanceIds) { - return CollectionUtils.convertMultiMap(getTasksByProcessInstanceIds(processInstanceIds), - Task::getProcessInstanceId); - } - - /** - * 获得流程任务列表 - * - * @param processInstanceIds 流程实例的编号数组 - * @return 流程任务列表 - */ - List getTasksByProcessInstanceIds(List processInstanceIds); - - /** - * 获得指定流程实例的流程任务列表,包括所有状态的 - * - * @param processInstanceId 流程实例的编号 - * @param asc 是否升序 - * @return 流程任务列表 - */ - List getTaskListByProcessInstanceId(String processInstanceId, Boolean asc); - - /** - * 校验任务是否存在,并且是否是分配给自己的任务 - * - * @param userId 用户 id - * @param taskId task id - */ - Task validateTask(Long userId, String taskId); - - /** - * 获取任务 - * - * @param id 任务编号 - * @return 任务 - */ - Task getTask(String id); - - /** - * 获取历史任务 - * - * @param id 任务编号 - * @return 历史任务 - */ - HistoricTaskInstance getHistoricTask(String id); - - /** - * 获取历史任务列表 - * - * @param taskIds 任务编号集合 - * @return 历史任务列表 - */ - List getHistoricTasks(Collection taskIds); - - /** - * 根据条件查询正在进行中的任务 - * - * @param processInstanceId 流程实例编号,不允许为空 - * @param assigned 是否分配了审批人,允许空 - * @param taskDefineKey 任务定义 Key,允许空 - */ - List getRunningTaskListByProcessInstanceId(String processInstanceId, - Boolean assigned, - String taskDefineKey); - - /** - * 获取当前任务的可退回的 UserTask 集合 - * - * @param id 当前的任务 ID - * @return 可以退回的节点列表 - */ - List getUserTaskListByReturn(String id); - - /** - * 获取指定任务的子任务列表(多层) - * - * @param parentTaskId 父任务 ID - * @param tasks 任务列表 - * @return 子任务列表 - */ - List getAllChildrenTaskListByParentTaskId(String parentTaskId, List tasks); - - /** - * 获取指定任务的子任务列表 - * - * @param parentTaskId 父任务ID - * @return 子任务列表 - */ - List getTaskListByParentTaskId(String parentTaskId); - - /** - * 获得指定流程实例的活动实例列表 - * - * @param processInstanceId 流程实例的编号 - * @return 活动实例列表 - */ - List getActivityListByProcessInstanceId(String processInstanceId); - - /** - * 获得执行编号对应的活动实例 - * - * @param executionId 执行编号 - * @return 活动实例 - */ - List getHistoricActivityListByExecutionId(String executionId); - - // ========== Update 写入相关方法 ========== - - /** - * 通过任务 - * - * @param userId 用户编号 - * @param reqVO 通过请求 - */ - void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO); - - /** - * 不通过任务 - * - * @param userId 用户编号 - * @param reqVO 不通过请求 - */ - void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO); - - /** - * 将流程任务分配给指定用户 - * - * @param userId 用户编号 - * @param reqVO 分配请求 - */ - void transferTask(Long userId, BpmTaskTransferReqVO reqVO); - - /** - * 将指定流程实例的、进行中的流程任务,移动到结束节点 - * - * @param processInstanceId 流程编号 - * @param reason 原因 - */ - void moveTaskToEnd(String processInstanceId, String reason); - - /** - * 将任务退回到指定的 targetDefinitionKey 位置 - * - * @param userId 用户编号 - * @param reqVO 退回的任务key和当前所在的任务ID - */ - void returnTask(Long userId, BpmTaskReturnReqVO reqVO); - - /** - * 将指定任务委派给其他人处理,等接收人处理后再回到原审批人手中审批 - * - * @param userId 用户编号 - * @param reqVO 被委派人和被委派的任务编号理由参数 - */ - void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO); - - /** - * 任务加签 - * - * @param userId 被加签的用户和任务 ID,加签类型 - * @param reqVO 当前用户 ID - */ - void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO); - - /** - * 任务减签 - * - * @param userId 当前用户ID - * @param reqVO 被减签的任务 ID,理由 - */ - void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO); - - /** - * 抄送任务 - * - * @param userId 用户编号 - * @param reqVO 通过请求 - */ - void copyTask(Long userId, @Valid BpmTaskCopyReqVO reqVO); - - // ========== Event 事件相关方法 ========== - - /** - * 处理 Task 创建事件,目前是 - *

- * 1. 更新它的状态为审批中 - * 2. 处理自动通过的情况,例如说:1)无审批人时,是否自动通过、不通过;2)非【人工审核】时,是否自动通过、不通过 - *

- * 注意:它的触发时机,晚于 {@link #processTaskAssigned(Task)} 之后 - * - * @param task 任务实体 - */ - void processTaskCreated(Task task); - - /** - * 处理 Task 取消事件,目前是更新它的状态为已取消 - * - * @param taskId 任务的编号 - */ - void processTaskCanceled(String taskId); - - /** - * 处理 Task 设置审批人事件,目前是发送审批消息 - * - * @param task 任务实体 - */ - void processTaskAssigned(Task task); - - /** - * 处理 Task 完成事件,目前是发送任务后置通知 - * - * @param task 任务实体 - */ - void processTaskCompleted(Task task); - - /** - * 处理 Task 审批超时事件,可能会处理多个当前审批中的任务 - * - * @param processInstanceId 流程示例编号 - * @param taskDefineKey 任务 Key - * @param handlerType 处理类型,参见 {@link BpmUserTaskTimeoutHandlerTypeEnum} - */ - void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType); - - /** - * 处理 ChildProcess 子流程的审批超时事件 - * - * @param processInstanceId 流程示例编号 - * @param taskDefineKey 任务 Key - */ - void processChildProcessTimeout(String processInstanceId, String taskDefineKey); - - /** - * 触发流程任务 (ReceiveTask) 的执行 - *

- * 1. Simple 模型 HTTP 回调请求触发器节点的回调,触发流程继续执行 - * 2. Simple 模型延迟器节点,到时触发流程继续执行 - * - * @param processInstanceId 流程示例编号 - * @param taskDefineKey 任务 Key - */ - void triggerTask(String processInstanceId, String taskDefineKey); - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java deleted file mode 100644 index 416444b..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java +++ /dev/null @@ -1,1535 +0,0 @@ -package com.zt.plat.module.bpm.service.task; - -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.convert.Convert; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.*; -import cn.hutool.extra.spring.SpringUtil; -import cn.hutool.json.JSONUtil; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.date.DateUtils; -import com.zt.plat.framework.common.util.number.NumberUtils; -import com.zt.plat.framework.common.util.object.ObjectUtils; -import com.zt.plat.framework.common.util.object.PageUtils; -import com.zt.plat.framework.datapermission.core.annotation.DataPermission; -import com.zt.plat.framework.web.core.util.WebFrameworkUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO; -import com.zt.plat.module.bpm.controller.admin.task.vo.task.*; -import com.zt.plat.module.bpm.convert.task.BpmTaskConvert; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.enums.definition.*; -import com.zt.plat.module.bpm.enums.task.BpmCommentTypeEnum; -import com.zt.plat.module.bpm.enums.task.BpmReasonEnum; -import com.zt.plat.module.bpm.enums.task.BpmTaskSignTypeEnum; -import com.zt.plat.module.bpm.enums.task.BpmTaskStatusEnum; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.bpm.service.definition.BpmFormService; -import com.zt.plat.module.bpm.service.definition.BpmModelService; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import com.zt.plat.module.bpm.service.message.BpmMessageService; -import com.zt.plat.module.bpm.service.message.dto.BpmMessageSendWhenTaskTimeoutReqDTO; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import jakarta.annotation.Resource; -import jakarta.validation.Valid; -import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.model.*; -import org.flowable.engine.HistoryService; -import org.flowable.engine.ManagementService; -import org.flowable.engine.RuntimeService; -import org.flowable.engine.TaskService; -import org.flowable.engine.history.HistoricActivityInstance; -import org.flowable.engine.runtime.ActivityInstance; -import org.flowable.engine.runtime.Execution; -import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.task.api.DelegationState; -import org.flowable.task.api.Task; -import org.flowable.task.api.TaskInfo; -import org.flowable.task.api.TaskQuery; -import org.flowable.task.api.history.HistoricTaskInstance; -import org.flowable.task.api.history.HistoricTaskInstanceQuery; -import org.flowable.task.service.impl.persistence.entity.TaskEntity; -import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.transaction.support.TransactionSynchronization; -import org.springframework.transaction.support.TransactionSynchronizationManager; - -import java.util.*; -import java.util.stream.Stream; - -import static com.zt.plat.framework.common.exception.util.ServiceExceptionUtil.exception; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.*; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.*; -import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants.START_USER_NODE_ID; -import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG; -import static com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE; -import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.*; - -/** - * 流程任务实例 Service 实现类 - * - * @author ZT - * @author jason - */ -@Slf4j -@Service -public class BpmTaskServiceImpl implements BpmTaskService { - - @Resource - private TaskService taskService; - @Resource - private HistoryService historyService; - @Resource - private RuntimeService runtimeService; - @Resource - private ManagementService managementService; - - @Resource - private BpmProcessInstanceService processInstanceService; - @Resource - private BpmProcessDefinitionService bpmProcessDefinitionService; - @Resource - private BpmProcessInstanceCopyService processInstanceCopyService; - @Resource - private BpmModelService modelService; - @Resource - private BpmMessageService messageService; - @Resource - private BpmFormService formService; - - @Resource - private AdminUserApi adminUserApi; - @Resource - private DeptApi deptApi; - - // ========== Query 查询相关方法 ========== - - @Override - public PageResult getTaskTodoPage(Long userId, BpmTaskPageReqVO pageVO) { - TaskQuery taskQuery = taskService.createTaskQuery() - .taskAssignee(String.valueOf(userId)) // 分配给自己 - .active() - .includeProcessVariables() - .orderByTaskCreateTime().desc(); // 创建时间倒序 - if (StrUtil.isNotBlank(pageVO.getName())) { - taskQuery.taskNameLike("%" + pageVO.getName() + "%"); - } - if (StrUtil.isNotEmpty(pageVO.getCategory())) { - taskQuery.taskCategory(pageVO.getCategory()); - } - if (StrUtil.isNotEmpty(pageVO.getProcessDefinitionKey())) { - taskQuery.processDefinitionKey(pageVO.getProcessDefinitionKey()); - } - if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { - taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); - taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); - } - long count = taskQuery.count(); - if (count == 0) { - return PageResult.empty(); - } - List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); - return new PageResult<>(tasks, count); - } - - @Override - public BpmTaskRespVO getTodoTask(Long userId, String taskId, String processInstanceId) { - // 1.1 获取指定的用户待办任务 - Task todoTask = getMyTodoTask(userId, taskId); - // 1.2 获取不到,则获取该流程实例下,第一个用户的待办任务 - if (todoTask == null) { - todoTask = getMyFirstTodoTask(userId, processInstanceId); - } - if (todoTask == null) { - return null; - } - - // 2. 查询该任务的子任务 - List childrenTasks = getAllChildrenTaskListByParentTaskId(todoTask.getId(), CollUtil.newArrayList(todoTask)); - - // 3. 转换返回 - BpmnModel bpmnModel = bpmProcessDefinitionService.getProcessDefinitionBpmnModel(todoTask.getProcessDefinitionId()); - Map buttonsSetting = BpmnModelUtils.parseButtonsSetting( - bpmnModel, todoTask.getTaskDefinitionKey()); - Boolean signEnable = parseSignEnable(bpmnModel, todoTask.getTaskDefinitionKey()); - Boolean reasonRequire = parseReasonRequire(bpmnModel, todoTask.getTaskDefinitionKey()); - Integer nodeType = parseNodeType(BpmnModelUtils.getFlowElementById(bpmnModel, todoTask.getTaskDefinitionKey())); - - // 4. 任务表单 - BpmFormDO taskForm = null; - if (StrUtil.isNotBlank(todoTask.getFormKey())) { - try { - Long formId = Long.parseLong(todoTask.getFormKey()); - taskForm = formService.getForm(formId); - } catch (NumberFormatException e) { - // 如果 formKey 不是数字(比如是URL),则不处理表单 - taskForm = null; - } - } - - return BpmTaskConvert.INSTANCE.buildTodoTask(todoTask, childrenTasks, buttonsSetting, taskForm) - .setNodeType(nodeType).setSignEnable(signEnable).setReasonRequire(reasonRequire); - } - - /** - * 获得用户指定 taskId 任务编号的“待办”(未审批、且可审核)的任务 - * - * @param userId 用户编号 - * @param taskId 任务编号 - * @return 任务 - */ - private Task getMyTodoTask(Long userId, String taskId) { - if (StrUtil.isEmpty(taskId)) { - return null; - } - Task task = getTask(taskId); - if (task == null) { - return null; - } - if (!isAssignUserTask(userId, task) && !isAddSignUserTask(userId, task)) { - return null; - } - return task; - } - - /** - * 获得用户指定 processInstanceId 流程编号下的首个“待办”(未审批、且可审核)的任务 - * - * @param userId 用户编号 - * @param processInstanceId 流程编号 - * @return 任务 - */ - private Task getMyFirstTodoTask(Long userId, String processInstanceId) { - if (processInstanceId == null) { - return null; - } - // 1. 查询所有任务 - List tasks = taskService.createTaskQuery() - .active() - .processInstanceId(processInstanceId) - .includeTaskLocalVariables() - .includeProcessVariables() - .orderByTaskCreateTime().asc() // 按创建时间升序 - .list(); - - // 2. 查询我的首个任务 - return CollUtil.findOne(tasks, task -> { - return isAssignUserTask(userId, task) // 当前用户为审批人 - || isAddSignUserTask(userId, task); // 当前用户为加签人(为了减签) - }); - } - - @Override - public PageResult getTaskDonePage(Long userId, BpmTaskPageReqVO pageVO) { - HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery() - .finished() // 已完成 - .taskAssignee(String.valueOf(userId)) // 分配给自己 - .includeTaskLocalVariables() - .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序 - if (StrUtil.isNotBlank(pageVO.getName())) { - taskQuery.taskNameLike("%" + pageVO.getName() + "%"); - } - if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { - taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); - taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); - } - // 执行查询 - long count = taskQuery.count(); - if (count == 0) { - return PageResult.empty(); - } - List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); - - // 特殊:强制移除自动完成的“发起人”节点 - // 补充说明:由于 taskQuery 无法方面的过滤,所以暂时通过内存过滤 - tasks.removeIf(task -> task.getTaskDefinitionKey().equals(START_USER_NODE_ID)); - return new PageResult<>(tasks, count); - } - - @Override - public PageResult getTaskPage(Long userId, BpmTaskPageReqVO pageVO) { - HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery() - .includeTaskLocalVariables() - .taskTenantId(FlowableUtils.getTenantId()) - .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序 - if (StrUtil.isNotBlank(pageVO.getName())) { - taskQuery.taskNameLike("%" + pageVO.getName() + "%"); - } - if (StrUtil.isNotEmpty(pageVO.getCategory())) { - taskQuery.taskCategory(pageVO.getCategory()); - } - if (ArrayUtil.isNotEmpty(pageVO.getCreateTime())) { - taskQuery.taskCreatedAfter(DateUtils.of(pageVO.getCreateTime()[0])); - taskQuery.taskCreatedBefore(DateUtils.of(pageVO.getCreateTime()[1])); - } - // 执行查询 - long count = taskQuery.count(); - if (count == 0) { - return PageResult.empty(); - } - List tasks = taskQuery.listPage(PageUtils.getStart(pageVO), pageVO.getPageSize()); - return new PageResult<>(tasks, count); - } - - @Override - public List getTasksByProcessInstanceIds(List processInstanceIds) { - if (CollUtil.isEmpty(processInstanceIds)) { - return Collections.emptyList(); - } - return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list(); - } - - @Override - public List getTaskListByProcessInstanceId(String processInstanceId, Boolean asc) { - HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery() - .includeTaskLocalVariables() - .processInstanceId(processInstanceId); - if (Boolean.TRUE.equals(asc)) { - query.orderByHistoricTaskInstanceStartTime().asc(); - } else { - query.orderByHistoricTaskInstanceStartTime().desc(); - } - return query.list(); - } - - @Override - public Task validateTask(Long userId, String taskId) { - Task task = validateTaskExist(taskId); - // 为什么判断 assignee 非空的情况下? - // 例如说:在审批人为空时,我们会有“自动审批通过”的策略,此时 userId 为 null,允许通过 - if (StrUtil.isNotBlank(task.getAssignee()) - && ObjectUtil.notEqual(userId, NumberUtils.parseLong(task.getAssignee()))) { - throw exception(TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); - } - return task; - } - - private Task validateTaskExist(String id) { - Task task = getTask(id); - if (task == null) { - throw exception(TASK_NOT_EXISTS); - } - return task; - } - - @Override - public Task getTask(String id) { - return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult(); - } - - @Override - public HistoricTaskInstance getHistoricTask(String id) { - return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult(); - } - - @Override - public List getHistoricTasks(Collection taskIds) { - return historyService.createHistoricTaskInstanceQuery().taskIds(taskIds).includeTaskLocalVariables().list(); - } - - @Override - public List getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String defineKey) { - Assert.notNull(processInstanceId, "processInstanceId 不能为空"); - TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId).active() - .includeTaskLocalVariables(); - if (BooleanUtil.isTrue(assigned)) { - taskQuery.taskAssigned(); - } else if (BooleanUtil.isFalse(assigned)) { - taskQuery.taskUnassigned(); - } - if (StrUtil.isNotEmpty(defineKey)) { - taskQuery.taskDefinitionKey(defineKey); - } - return taskQuery.list(); - } - - @Override - public List getUserTaskListByReturn(String id) { - // 1.1 校验当前任务 task 存在 - Task task = validateTaskExist(id); - // 1.2 根据流程定义获取流程模型信息 - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); - FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); - if (source == null) { - throw exception(TASK_NOT_EXISTS); - } - - // 2.1 查询该任务的前置任务节点的 key 集合 - List previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null); - if (CollUtil.isEmpty(previousUserList)) { - return Collections.emptyList(); - } - // 2.2 过滤:只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回 - previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null)); - return previousUserList; - } - - @Override - public List getAllChildrenTaskListByParentTaskId(String parentTaskId, List tasks) { - if (CollUtil.isEmpty(tasks)) { - return Collections.emptyList(); - } - Map> parentTaskMap = convertMultiMap( - filterList(tasks, task -> StrUtil.isNotEmpty(task.getParentTaskId())), TaskInfo::getParentTaskId); - if (CollUtil.isEmpty(parentTaskMap)) { - return Collections.emptyList(); - } - - List result = new ArrayList<>(); - // 1. 递归获取子级 - Stack stack = new Stack<>(); - stack.push(parentTaskId); - // 2. 递归遍历 - for (int i = 0; i < Short.MAX_VALUE; i++) { - if (stack.isEmpty()) { - break; - } - // 2.1 获取子任务们 - String taskId = stack.pop(); - List childTaskList = filterList(tasks, task -> StrUtil.equals(task.getParentTaskId(), taskId)); - // 2.2 如果非空,则添加到 stack 进一步递归 - if (CollUtil.isNotEmpty(childTaskList)) { - stack.addAll(convertList(childTaskList, TaskInfo::getId)); - result.addAll(childTaskList); - } - } - return result; - } - - /** - * 获得所有子任务列表 - * - * @param parentTask 父任务 - * @return 所有子任务列表 - */ - private List getAllChildTaskList(Task parentTask) { - List result = new ArrayList<>(); - // 1. 递归获取子级 - Stack stack = new Stack<>(); - stack.push(parentTask); - // 2. 递归遍历 - for (int i = 0; i < Short.MAX_VALUE; i++) { - if (stack.isEmpty()) { - break; - } - // 2.1 获取子任务们 - Task task = stack.pop(); - List childTaskList = getTaskListByParentTaskId(task.getId()); - // 2.2 如果非空,则添加到 stack 进一步递归 - if (CollUtil.isNotEmpty(childTaskList)) { - stack.addAll(childTaskList); - result.addAll(childTaskList); - } - } - return result; - } - - @Override - public List getTaskListByParentTaskId(String parentTaskId) { - String tableName = managementService.getTableName(TaskEntity.class); - // taskService.createTaskQuery() 没有 parentId 参数,所以写 sql 查询 - String sql = "select ID_,NAME_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}"; - return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list(); - } - - /** - * 获取子任务个数 - * - * @param parentTaskId 父任务 ID - * @return 剩余子任务个数 - */ - private Long getTaskCountByParentTaskId(String parentTaskId) { - String tableName = managementService.getTableName(TaskEntity.class); - String sql = "SELECT COUNT(1) from " + tableName + " WHERE PARENT_TASK_ID_=#{parentTaskId}"; - return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count(); - } - - /** - * 获得任务根任务的父任务编号 - * - * @param task 任务 - * @return 根任务的父任务编号 - */ - private String getTaskRootParentId(Task task) { - if (task == null || task.getParentTaskId() == null) { - return null; - } - for (int i = 0; i < Short.MAX_VALUE; i++) { - Task parentTask = getTask(task.getParentTaskId()); - if (parentTask == null) { - return null; - } - if (parentTask.getParentTaskId() == null) { - return parentTask.getId(); - } - task = parentTask; - } - throw new IllegalArgumentException(String.format("Task(%s) 层级过深,无法获取父节点编号", task.getId())); - } - - @Override - public List getActivityListByProcessInstanceId(String processInstanceId) { - return historyService.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId) - .orderByHistoricActivityInstanceStartTime().asc().list(); - } - - @Override - public List getHistoricActivityListByExecutionId(String executionId) { - return historyService.createHistoricActivityInstanceQuery().executionId(executionId).list(); - } - - /** - * 判断指定用户,是否是当前任务的审批人 - * - * @param userId 用户编号 - * @param task 任务 - * @return 是否 - */ - private boolean isAssignUserTask(Long userId, Task task) { - Long assignee = NumberUtil.parseLong(task.getAssignee(), null); - return ObjectUtil.equals(userId, assignee); - } - - /** - * 判断指定用户,是否是当前任务的拥有人 - * - * @param userId 用户编号 - * @param task 任务 - * @return 是否 - */ - private boolean isOwnerUserTask(Long userId, Task task) { - Long assignee = NumberUtil.parseLong(task.getOwner(), null); - return ObjectUtil.equal(userId, assignee); - } - - /** - * 判断指定用户,是否是当前任务的加签人 - * - * @param userId 用户 Id - * @param task 任务 - * @return 是否 - */ - private boolean isAddSignUserTask(Long userId, Task task) { - return (isAssignUserTask(userId, task) || isOwnerUserTask(userId, task)) - && BpmTaskSignTypeEnum.of(task.getScopeType()) != null; - } - - // ========== Update 写入相关方法 ========== - - @Override - @Transactional(rollbackFor = Exception.class) - public void approveTask(Long userId, @Valid BpmTaskApproveReqVO reqVO) { - // 1.1 校验任务存在 - Task task = validateTask(userId, reqVO.getId()); - // 1.2 校验流程实例存在 - ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); - if (instance == null) { - throw exception(PROCESS_INSTANCE_NOT_EXISTS); - } - // 1.3 校验签名 - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); - Boolean signEnable = parseSignEnable(bpmnModel, task.getTaskDefinitionKey()); - if (signEnable && StrUtil.isEmpty(reqVO.getSignPicUrl())) { - throw exception(TASK_SIGNATURE_NOT_EXISTS); - } - // 1.4 校验审批意见 - Boolean reasonRequire = parseReasonRequire(bpmnModel, task.getTaskDefinitionKey()); - if (reasonRequire && StrUtil.isEmpty(reqVO.getReason())) { - throw exception(TASK_REASON_REQUIRE); - } - - // 情况一:被委派的任务,不调用 complete 去完成任务 - if (DelegationState.PENDING.equals(task.getDelegationState())) { - approveDelegateTask(reqVO, task); - return; - } - - // 情况二:审批有【后】加签的任务 - if (BpmTaskSignTypeEnum.AFTER.getType().equals(task.getScopeType())) { - approveAfterSignTask(task, reqVO); - return; - } - - // 情况三:审批普通的任务。大多数情况下,都是这样 - // 2.1 更新 task 状态、原因、签字 - updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), reqVO.getReason()); - if (signEnable) { - taskService.setVariableLocal(task.getId(), BpmnVariableConstants.TASK_SIGN_PIC_URL, reqVO.getSignPicUrl()); - } - // 2.2 添加评论 - taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), - BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason())); - - // 3. 设置流程变量。如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时 variables 一定会为空 - // 场景一:A 节点发起,B 节点表单无可编辑字段,审批通过时,C 节点需要流程变量获取下一个执行节点,但因为 B 节点无可编辑的字段,variables 为空,流程可能出现问题。 - // 场景二:A 节点发起,B 节点只有某一个字段可编辑(比如 day),但 C 节点需要多个节点。 - // (比如 work + day 变量,在发起时填写,因为 B 节点只有 day 的编辑权限,在审批后,variables 会缺少 work 的值) - Map processVariables = new HashMap<>(); - if (CollUtil.isNotEmpty(instance.getProcessVariables())) { // 获取历史中流程变量 - processVariables.putAll(instance.getProcessVariables()); - } - if (CollUtil.isNotEmpty(reqVO.getVariables())) { // 合并前端传递的流程变量,以前端为准 - processVariables.putAll(reqVO.getVariables()); - } - - // 如果任务变量不为空,设置任务级别的变量实例信息 - if (CollUtil.isNotEmpty(reqVO.getTaskVariables())) { - Map taskVariables = new HashMap<>(); - taskVariables.put("taskVariables", JSONUtil.toJsonStr(reqVO.getTaskVariables())); - taskService.setVariablesLocal(task.getId(), taskVariables); - } - - // 4. 校验并处理 APPROVE_USER_SELECT 当前审批人,选择下一节点审批人的逻辑 - Map variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables, - bpmnModel, reqVO.getNextAssignees(), instance); - runtimeService.setVariables(task.getProcessInstanceId(), variables); - - // 5. 调用 BPM complete 去完成任务 - taskService.complete(task.getId(), variables, true); - - // 【加签专属】处理加签任务 - handleParentTaskIfSign(task.getParentTaskId()); - } - - /** - * 校验选择的下一个节点的审批人,是否合法 - * - * 1. 是否有漏选:没有选择审批人 - * 2. 是否有多选:非下一个节点 - * - * @param taskDefinitionKey 当前任务节点标识 - * @param variables 流程变量 - * @param bpmnModel 流程模型 - * @param nextAssignees 下一个节点审批人集合(参数) - * @param processInstance 流程实例 - */ - @SuppressWarnings("unchecked") - private Map validateAndSetNextAssignees(String taskDefinitionKey, Map variables, BpmnModel bpmnModel, - Map> nextAssignees, ProcessInstance processInstance) { - // simple 设计器第一个节点默认为发起人节点,不校验是否存在审批人 - if (Objects.equals(taskDefinitionKey, START_USER_NODE_ID)) { - return variables; - } - // 1. 获取下一个将要执行的节点集合 - FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey); - List nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables); - - // 2. 校验选择的下一个节点的审批人,是否合法 - for (FlowNode nextFlowNode : nextFlowNodes) { - Integer candidateStrategy = parseCandidateStrategy(nextFlowNode); - // 2.1 情况一:如果节点中的审批人策略为 发起人自选 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) { - // 特殊:如果当前节点已经存在审批人,则不允许覆盖 - Map> startUserSelectAssignees = FlowableUtils.getStartUserSelectAssignees(processInstance.getProcessVariables()); - if (startUserSelectAssignees != null && CollUtil.isNotEmpty(startUserSelectAssignees.get(nextFlowNode.getId()))) { - continue; - } - // 如果节点存在,但未配置审批人 - List assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; - if (CollUtil.isEmpty(assignees)) { - throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); - } - - // 设置 PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES - if (startUserSelectAssignees == null) { - startUserSelectAssignees = new HashMap<>(); - } - startUserSelectAssignees.put(nextFlowNode.getId(), assignees); - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); - continue; - } - - // 2.2 情况二:如果节点中的审批人策略为 审批人,在审批时选择下一个节点的审批人,并且该节点的审批人为空 - if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) { - // 如果节点存在,但未配置审批人 - Map> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables()); - List assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null; - if (CollUtil.isEmpty(assignees)) { - throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName()); - } - - // 设置 PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES - if (approveUserSelectAssignees == null) { - approveUserSelectAssignees = new HashMap<>(); - } - approveUserSelectAssignees.put(nextFlowNode.getId(), assignees); - Map> existingApproveUserSelectAssignees = (Map>) variables.get( - BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES); - if (CollUtil.isNotEmpty(existingApproveUserSelectAssignees)) { - approveUserSelectAssignees.putAll(existingApproveUserSelectAssignees); - } - variables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_APPROVE_USER_SELECT_ASSIGNEES, approveUserSelectAssignees); - } - } - return variables; - } - - /** - * 审批通过存在“后加签”的任务。 - *

- * 注意:该任务不能马上完成,需要一个中间状态(APPROVING),并激活剩余所有子任务(PROCESS)为可审批处理 - * 如果马上完成,则会触发下一个任务,甚至如果没有下一个任务则流程实例就直接结束了! - * - * @param task 当前任务 - * @param reqVO 前端请求参数 - */ - private void approveAfterSignTask(Task task, BpmTaskApproveReqVO reqVO) { - // 更新父 task 状态 + 原因 - updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVING.getStatus(), reqVO.getReason()); - - // 2. 激活子任务 - List childrenTaskList = getTaskListByParentTaskId(task.getId()); - for (Task childrenTask : childrenTaskList) { - taskService.resolveTask(childrenTask.getId()); - // 更新子 task 状态 - updateTaskStatus(childrenTask.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); - } - } - - /** - * 如果父任务是有前后【加签】的任务,如果它【加签】出来的子任务都被处理,需要处理父任务: - *

- * 1. 如果是【向前】加签,则需要重新激活父任务,让它可以被审批 - * 2. 如果是【向后】加签,则需要完成父任务,让它完成审批 - * - * @param parentTaskId 父任务编号 - */ - private void handleParentTaskIfSign(String parentTaskId) { - if (StrUtil.isBlank(parentTaskId)) { - return; - } - // 1.1 判断是否还有子任务。如果没有,就不处理 - Long childrenTaskCount = getTaskCountByParentTaskId(parentTaskId); - if (childrenTaskCount > 0) { - return; - } - // 1.2 只处理加签的父任务 - Task parentTask = validateTaskExist(parentTaskId); - String scopeType = parentTask.getScopeType(); - if (BpmTaskSignTypeEnum.of(scopeType) == null) { - return; - } - - // 2. 子任务已处理完成,清空 scopeType 字段,修改 parentTask 信息,方便后续可以继续向前后向后加签 - TaskEntityImpl parentTaskImpl = (TaskEntityImpl) parentTask; - parentTaskImpl.setScopeType(null); - taskService.saveTask(parentTaskImpl); - - // 3.1 情况一:处理向【向前】加签 - if (BpmTaskSignTypeEnum.BEFORE.getType().equals(scopeType)) { - // 3.1.1 owner 重新赋值给父任务的 assignee,这样它就可以被审批 - taskService.resolveTask(parentTaskId); - // 3.1.2 更新流程任务 status - updateTaskStatus(parentTaskId, BpmTaskStatusEnum.RUNNING.getStatus()); - // 3.2 情况二:处理向【向后】加签 - } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(scopeType)) { - // 只有 parentTask 处于 APPROVING 的情况下,才可以继续 complete 完成 - // 否则,一个未审批的 parentTask 任务,在加签出来的任务都被减签的情况下,就直接完成审批,这样会存在问题 - Integer status = (Integer) parentTask.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); - if (ObjectUtil.notEqual(status, BpmTaskStatusEnum.APPROVING.getStatus())) { - return; - } - // 3.2.2 完成自己(因为它已经没有子任务,所以也可以完成) - updateTaskStatus(parentTaskId, BpmTaskStatusEnum.APPROVE.getStatus()); - taskService.complete(parentTaskId); - } - - // 4. 递归处理父任务 - handleParentTaskIfSign(parentTask.getParentTaskId()); - } - - /** - * 审批被委派的任务 - * - * @param reqVO 前端请求参数,包含当前任务ID,审批意见等 - * @param task 当前被审批的任务 - */ - private void approveDelegateTask(BpmTaskApproveReqVO reqVO, Task task) { - // 1. 添加审批意见 - AdminUserRespDTO currentUser = adminUserApi.getUser(WebFrameworkUtils.getLoginUserId()).getCheckedData(); - AdminUserRespDTO ownerUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())).getCheckedData(); // 发起委托的用户 - Assert.notNull(ownerUser, "委派任务找不到原审批人,需要检查数据"); - taskService.addComment(reqVO.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_END.getType(), - BpmCommentTypeEnum.DELEGATE_END.formatComment(currentUser.getNickname(), ownerUser.getNickname(), reqVO.getReason())); - - // 2.1 调用 resolveTask 完成任务。 - // 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()):将 owner 设置为 assignee - taskService.resolveTask(task.getId()); - // 2.2 更新 task 状态 + 原因 - updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus(), reqVO.getReason()); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void rejectTask(Long userId, @Valid BpmTaskRejectReqVO reqVO) { - // 1.1 校验任务存在 - Task task = validateTask(userId, reqVO.getId()); - // 1.2 校验流程实例存在 - ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); - if (instance == null) { - throw exception(PROCESS_INSTANCE_NOT_EXISTS); - } - - // 2.1 更新流程任务为不通过 - updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason()); - // 2.2 添加流程评论 - taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), - BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); - // 2.3 如果当前任务时被加签的,则加它的根任务也标记成未通过 - // 疑问:为什么要标记未通过呢? - // 回答:例如说 A 任务被向前加签除 B 任务时,B 任务被审批不通过,此时 A 会被取消。而 zt-ui-admin-vue3 不展示“已取消”的任务,导致展示不出审批不通过的细节。 - if (task.getParentTaskId() != null) { - String rootParentId = getTaskRootParentId(task); - updateTaskStatusAndReason(rootParentId, BpmTaskStatusEnum.REJECT.getStatus(), - BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); - taskService.addComment(rootParentId, task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), - BpmCommentTypeEnum.REJECT.formatComment("加签任务不通过")); - } - - // 3. 根据不同的 RejectHandler 处理策略 - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); - FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); - // 3.1 情况一:驳回到指定的任务节点 - BpmUserTaskRejectHandlerTypeEnum userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(userTaskElement); - if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerTypeEnum.RETURN_USER_TASK) { - String returnTaskId = BpmnModelUtils.parseReturnTaskId(userTaskElement); - Assert.notNull(returnTaskId, "退回的节点不能为空"); - returnTask(userId, new BpmTaskReturnReqVO().setId(task.getId()) - .setTargetTaskDefinitionKey(returnTaskId).setReason(reqVO.getReason())); - return; - } - // 3.2 情况二:直接结束,审批不通过 - processInstanceService.updateProcessInstanceReject(instance, reqVO.getReason()); // 标记不通过 - moveTaskToEnd(task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); // 结束流程 - } - - /** - * 更新流程任务的 status 状态 - * - * @param id 任务编号 - * @param status 状态 - */ - private void updateTaskStatus(String id, Integer status) { - taskService.setVariableLocal(id, BpmnVariableConstants.TASK_VARIABLE_STATUS, status); - } - - /** - * 更新流程任务的 status 状态、reason 理由 - * - * @param id 任务编号 - * @param status 状态 - * @param reason 理由(审批通过、审批不通过的理由) - */ - private void updateTaskStatusAndReason(String id, Integer status, String reason) { - updateTaskStatus(id, status); - taskService.setVariableLocal(id, BpmnVariableConstants.TASK_VARIABLE_REASON, reason); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void returnTask(Long userId, BpmTaskReturnReqVO reqVO) { - // 1.1 当前任务 task - Task task = validateTask(userId, reqVO.getId()); - if (task.isSuspended()) { - throw exception(TASK_IS_PENDING); - } - // 1.2 校验源头和目标节点的关系,并返回目标元素 - FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(), - reqVO.getTargetTaskDefinitionKey(), task.getProcessDefinitionId()); - - // 2. 调用 Flowable 框架的退回逻辑 - returnTask(userId, task, targetElement, reqVO); - } - - /** - * 退回流程节点时,校验目标任务节点是否可退回 - * - * @param sourceKey 当前任务节点 Key - * @param targetKey 目标任务节点 key - * @param processDefinitionId 当前流程定义 ID - * @return 目标任务节点元素 - */ - private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) { - // 1.1 获取流程模型信息 - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processDefinitionId); - // 1.3 获取当前任务节点元素 - FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey); - // 1.3 获取跳转的节点元素 - FlowElement target = BpmnModelUtils.getFlowElementById(bpmnModel, targetKey); - if (target == null) { - throw exception(TASK_TARGET_NODE_NOT_EXISTS); - } - - // 2.2 只有串行可到达的节点,才可以退回。类似非串行、子流程无法退回 - if (!BpmnModelUtils.isSequentialReachable(source, target, null)) { - throw exception(TASK_RETURN_FAIL_SOURCE_TARGET_ERROR); - } - return target; - } - - /** - * 执行退回逻辑 - * - * @param userId 用户编号 - * @param currentTask 当前退回的任务 - * @param targetElement 需要退回到的目标任务 - * @param reqVO 前端参数封装 - */ - public void returnTask(Long userId, Task currentTask, FlowElement targetElement, BpmTaskReturnReqVO reqVO) { - // 1. 获得所有需要回撤的任务 taskDefinitionKey,用于稍后的 moveActivityIdsToSingleActivityId 回撤 - // 1.1 获取所有正常进行的任务节点 Key - List taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list(); - List runTaskKeyList = convertList(taskList, Task::getTaskDefinitionKey); - // 1.2 通过 targetElement 的出口连线,计算在 runTaskKeyList 有哪些 key 需要被撤回 - // 为什么不直接使用 runTaskKeyList 呢?因为可能存在多个审批分支,例如说:A -> B -> C 和 D -> F,而只要 C 撤回到 A,需要排除掉 F - List returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null); - List returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId); - - List runExecutionIds = new ArrayList<>(); - // 2. 给当前要被退回的 task 数组,设置退回意见 - taskList.forEach(task -> { - // 需要排除掉,不需要设置退回意见的任务 - if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) { - return; - } - runExecutionIds.add(task.getExecutionId()); - - // 判断是否分配给自己任务,因为会签任务,一个节点会有多个任务 - if (isAssignUserTask(userId, task)) { // 情况一:自己的任务,进行 RETURN 标记 - // 2.1.1 添加评论 - taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN.getType(), - BpmCommentTypeEnum.RETURN.formatComment(reqVO.getReason())); - // 2.1.2 更新 task 状态 + 原因 - updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), reqVO.getReason()); - } else { // 情况二:别人的任务,进行 CANCEL 标记 - processTaskCanceled(task.getId()); - } - }); - - // 3. 设置流程变量节点驳回标记:用于驳回到节点,不执行 BpmUserTaskAssignStartUserHandlerTypeEnum 策略。导致自动通过 - runtimeService.setVariable(currentTask.getProcessInstanceId(), - String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE); - // 4. 执行驳回 - // 使用 moveExecutionsToSingleActivityId 替换 moveActivityIdsToSingleActivityId 原因: - // 当多实例任务回退的时候有问题。相关 issue: https://github.com/flowable/flowable-engine/issues/3944 - runtimeService.createChangeActivityStateBuilder() - .processInstanceId(currentTask.getProcessInstanceId()) - .moveExecutionsToSingleActivityId(runExecutionIds, reqVO.getTargetTaskDefinitionKey()) - .changeState(); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void delegateTask(Long userId, BpmTaskDelegateReqVO reqVO) { - String taskId = reqVO.getId(); - // 1.1 校验任务 - Task task = validateTask(userId, reqVO.getId()); - if (task.getAssignee().equals(reqVO.getDelegateUserId().toString())) { // 校验当前审批人和被委派人不是同一人 - throw exception(TASK_DELEGATE_FAIL_USER_REPEAT); - } - // 1.2 校验目标用户存在 - AdminUserRespDTO delegateUser = adminUserApi.getUser(reqVO.getDelegateUserId()).getCheckedData(); - if (delegateUser == null) { - throw exception(TASK_DELEGATE_FAIL_USER_NOT_EXISTS); - } - - // 2. 添加委托意见 - AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData(); - taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_START.getType(), - BpmCommentTypeEnum.DELEGATE_START.formatComment(currentUser.getNickname(), delegateUser.getNickname(), reqVO.getReason())); - - // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) - taskService.setOwner(taskId, task.getAssignee()); - // 3.2 执行委派,将任务委派给 delegateUser - taskService.delegateTask(taskId, reqVO.getDelegateUserId().toString()); - // 补充说明:委托不单独设置状态。如果需要,可通过 Task 的 DelegationState 字段,判断是否为 DelegationState.PENDING 委托中 - } - - @Override - public void transferTask(Long userId, BpmTaskTransferReqVO reqVO) { - String taskId = reqVO.getId(); - // 1.1 校验任务 - Task task = validateTask(userId, reqVO.getId()); - if (task.getAssignee().equals(reqVO.getAssigneeUserId().toString())) { // 校验当前审批人和被转派人不是同一人 - throw exception(TASK_TRANSFER_FAIL_USER_REPEAT); - } - // 1.2 校验目标用户存在 - AdminUserRespDTO assigneeUser = adminUserApi.getUser(reqVO.getAssigneeUserId()).getCheckedData(); - if (assigneeUser == null) { - throw exception(TASK_TRANSFER_FAIL_USER_NOT_EXISTS); - } - - // 2. 添加委托意见 - AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData(); - taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.TRANSFER.getType(), - BpmCommentTypeEnum.TRANSFER.formatComment(currentUser.getNickname(), assigneeUser.getNickname(), reqVO.getReason())); - - // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) - taskService.setOwner(taskId, task.getAssignee()); - // 3.2 执行转派(审批人),将任务转派给 assigneeUser - // 委托( delegate)和转派(transfer)的差别,就在这块的调用!!!! - taskService.setAssignee(taskId, reqVO.getAssigneeUserId().toString()); - } - - @Override - public void moveTaskToEnd(String processInstanceId, String reason) { - List taskList = getRunningTaskListByProcessInstanceId(processInstanceId, null, null); - if (CollUtil.isEmpty(taskList)) { - return; - } - - // 1. 其它未结束的任务,直接取消 - // 疑问:为什么不通过 updateTaskStatusWhenCanceled 监听取消,而是直接提前调用呢? - // 回答:详细见 updateTaskStatusWhenCanceled 的方法,加签的场景 - taskList.forEach(task -> { - Integer otherTaskStatus = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); - if (BpmTaskStatusEnum.isEndStatus(otherTaskStatus)) { - return; - } - processTaskCanceled(task.getId()); - }); - - // 2. 终止流程 - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(taskList.get(0).getProcessDefinitionId()); - List activityIds = CollUtil.newArrayList(convertSet(taskList, Task::getTaskDefinitionKey)); - EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); - Assert.notNull(endEvent, "结束节点不能为空"); - runtimeService.createChangeActivityStateBuilder() - .processInstanceId(processInstanceId) - .moveActivityIdsToSingleActivityId(activityIds, endEvent.getId()) - .changeState(); - - // 3. 特殊:如果跳转到 EndEvent 流程还未结束, 执行 deleteProcessInstance 方法 - // TODO ZT:目前发现并行分支情况下,会存在这个情况,后续看看有没更好的方案; - List executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list(); - if (CollUtil.isNotEmpty(executions)) { - log.warn("[moveTaskToEnd][执行跳转到 EndEvent 后, 流程实例未结束,强制执行 deleteProcessInstance 方法]"); - runtimeService.deleteProcessInstance(processInstanceId, reason); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void createSignTask(Long userId, BpmTaskSignCreateReqVO reqVO) { - // 1. 获取和校验任务 - TaskEntityImpl taskEntity = validateTaskCanCreateSign(userId, reqVO); - List userList = adminUserApi.getUserList(reqVO.getUserIds()).getCheckedData(); - if (CollUtil.isEmpty(userList)) { - throw exception(TASK_SIGN_CREATE_USER_NOT_EXIST); - } - - // 2. 处理当前任务 - // 2.1 开启计数功能,主要用于为了让表 ACT_RU_TASK 中的 SUB_TASK_COUNT_ 字段记录下总共有多少子任务,后续可能有用 - taskEntity.setCountEnabled(true); - // 2.2 向前加签,设置 owner,置空 assign。等子任务都完成后,再调用 resolveTask 重新将 owner 设置为 assign - // 原因是:不能和向前加签的子任务一起审批,需要等前面的子任务都完成才能审批 - if (reqVO.getType().equals(BpmTaskSignTypeEnum.BEFORE.getType())) { - taskEntity.setOwner(taskEntity.getAssignee()); - taskEntity.setAssignee(null); - } - // 2.4 记录加签方式,完成任务时需要用到判断 - taskEntity.setScopeType(reqVO.getType()); - // 2.5 保存当前任务修改后的值 - taskService.saveTask(taskEntity); - // 2.6 更新 task 状态为 WAIT,只有在向前加签的时候 - if (reqVO.getType().equals(BpmTaskSignTypeEnum.BEFORE.getType())) { - updateTaskStatus(taskEntity.getId(), BpmTaskStatusEnum.WAIT.getStatus()); - } - - // 3. 创建加签任务 - createSignTaskList(convertList(reqVO.getUserIds(), String::valueOf), taskEntity); - - // 4. 记录加签的评论到 task 任务 - AdminUserRespDTO currentUser = adminUserApi.getUser(userId).getCheckedData(); - String comment = StrUtil.format(BpmCommentTypeEnum.ADD_SIGN.getComment(), - currentUser.getNickname(), BpmTaskSignTypeEnum.nameOfType(reqVO.getType()), - String.join(",", convertList(userList, AdminUserRespDTO::getNickname)), reqVO.getReason()); - taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(), BpmCommentTypeEnum.ADD_SIGN.getType(), comment); - } - - /** - * 校验任务是否可以加签,主要校验加签类型是否一致: - *

- * 1. 如果存在“向前加签”的任务,则不能“向后加签” - * 2. 如果存在“向后加签”的任务,则不能“向前加签” - * - * @param userId 当前用户 ID - * @param reqVO 请求参数,包含任务 ID 和加签类型 - * @return 当前任务 - */ - private TaskEntityImpl validateTaskCanCreateSign(Long userId, BpmTaskSignCreateReqVO reqVO) { - TaskEntityImpl taskEntity = (TaskEntityImpl) validateTask(userId, reqVO.getId()); - // 向前加签和向后加签不能同时存在 - if (taskEntity.getScopeType() != null - && ObjectUtil.notEqual(taskEntity.getScopeType(), reqVO.getType())) { - throw exception(TASK_SIGN_CREATE_TYPE_ERROR, - BpmTaskSignTypeEnum.nameOfType(taskEntity.getScopeType()), BpmTaskSignTypeEnum.nameOfType(reqVO.getType())); - } - - // 同一个 key 的任务,审批人不重复 - List taskList = taskService.createTaskQuery().processInstanceId(taskEntity.getProcessInstanceId()) - .taskDefinitionKey(taskEntity.getTaskDefinitionKey()).list(); - List currentAssigneeList = convertListByFlatMap(taskList, task -> // 需要考虑 owner 的情况,因为向后加签时,它暂时没 assignee 而是 owner - Stream.of(NumberUtils.parseLong(task.getAssignee()), NumberUtils.parseLong(task.getOwner()))); - if (CollUtil.containsAny(currentAssigneeList, reqVO.getUserIds())) { - List userList = adminUserApi.getUserList(CollUtil.intersection(currentAssigneeList, reqVO.getUserIds())).getCheckedData(); - throw exception(TASK_SIGN_CREATE_USER_REPEAT, String.join(",", convertList(userList, AdminUserRespDTO::getNickname))); - } - return taskEntity; - } - - /** - * 创建加签子任务 - * - * @param userIds 被加签的用户 ID - * @param taskEntity 被加签的任务 - */ - private void createSignTaskList(List userIds, TaskEntityImpl taskEntity) { - if (CollUtil.isEmpty(userIds)) { - return; - } - // 创建加签人的新任务,全部基于 taskEntity 为父任务来创建 - for (String addSignId : userIds) { - if (StrUtil.isBlank(addSignId)) { - continue; - } - createSignTask(taskEntity, addSignId); - } - } - - /** - * 创建加签子任务 - * - * @param parentTask 父任务 - * @param assignee 子任务的执行人 - */ - private void createSignTask(TaskEntityImpl parentTask, String assignee) { - // 1. 生成子任务 - TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID()); - BpmTaskConvert.INSTANCE.copyTo(parentTask, task); - - // 2.1 向前加签,设置审批人 - if (BpmTaskSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) { - task.setAssignee(assignee); - // 2.2 向后加签,设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 - } else { - task.setOwner(assignee); - } - // 2.3 保存子任务 - taskService.saveTask(task); - - // 3. 向后前签,设置子任务的状态为 WAIT,因为需要等父任务审批完 - if (BpmTaskSignTypeEnum.AFTER.getType().equals(parentTask.getScopeType())) { - updateTaskStatus(task.getId(), BpmTaskStatusEnum.WAIT.getStatus()); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - @SuppressWarnings("DataFlowIssue") - public void deleteSignTask(Long userId, BpmTaskSignDeleteReqVO reqVO) { - // 1.1 校验 task 可以被减签 - Task task = validateTaskCanSignDelete(reqVO.getId()); - // 1.2 校验取消人存在 - AdminUserRespDTO cancelUser = null; - if (StrUtil.isNotBlank(task.getAssignee())) { - cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getAssignee())).getCheckedData(); - } - if (cancelUser == null && StrUtil.isNotBlank(task.getOwner())) { - cancelUser = adminUserApi.getUser(NumberUtils.parseLong(task.getOwner())).getCheckedData(); - } - Assert.notNull(cancelUser, "任务中没有所有者和审批人,数据错误"); - - // 2.1 获得子任务列表,包括子任务的子任务 - List childTaskList = getAllChildTaskList(task); - childTaskList.add(task); - // 2.2 更新子任务为已取消 - String cancelReason = StrUtil.format("任务被取消,原因:由于[{}]操作[减签],", cancelUser.getNickname()); - childTaskList.forEach(childTask -> updateTaskStatusAndReason(childTask.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), cancelReason)); - // 2.3 删除任务和所有子任务 - taskService.deleteTasks(convertList(childTaskList, Task::getId)); - - // 3. 记录日志到父任务中。先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录 - AdminUserRespDTO user = adminUserApi.getUser(userId).getCheckedData(); - taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(), BpmCommentTypeEnum.SUB_SIGN.getType(), - StrUtil.format(BpmCommentTypeEnum.SUB_SIGN.getComment(), user.getNickname(), cancelUser.getNickname())); - - // 4. 处理当前任务的父任务 - handleParentTaskIfSign(task.getParentTaskId()); - } - - @Override - public void copyTask(Long userId, BpmTaskCopyReqVO reqVO) { - processInstanceCopyService.createProcessInstanceCopy(reqVO.getCopyUserIds(), reqVO.getReason(), reqVO.getId()); - } - - /** - * 校验任务是否能被减签 - * - * @param id 任务编号 - * @return 任务信息 - */ - private Task validateTaskCanSignDelete(String id) { - Task task = validateTaskExist(id); - if (task.getParentTaskId() == null) { - throw exception(TASK_SIGN_DELETE_NO_PARENT); - } - Task parentTask = getTask(task.getParentTaskId()); - if (parentTask == null) { - throw exception(TASK_SIGN_DELETE_NO_PARENT); - } - if (BpmTaskSignTypeEnum.of(parentTask.getScopeType()) == null) { - throw exception(TASK_SIGN_DELETE_NO_PARENT); - } - return task; - } - - // ========== Event 事件相关方法 ========== - - @Override - public void processTaskCreated(Task task) { - // 1. 设置为待办中 - Integer status = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); - if (status != null) { - log.error("[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]", task.getId(), status); - return; - } - updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); - - ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); - if (processInstance == null) { - log.error("[processTaskCreated][taskId({}) 没有找到流程实例]", task.getId()); - return; - } - BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService. - getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); - if (processDefinitionInfo == null) { - log.error("[processTaskCreated][processDefinitionId({}) 没有找到流程定义]", processInstance.getProcessDefinitionId()); - return; - } - - // 2. 任务前置通知 - if (ObjUtil.isNotNull(processDefinitionInfo.getTaskBeforeTriggerSetting())){ - BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskBeforeTriggerSetting(); - BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, - setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); - } - - // 3. 处理自动通过的情况,例如说:1)无审批人时,是否自动通过、不通过;2)非【人工审核】时,是否自动通过、不通过 - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); - FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); - Integer approveType = BpmnModelUtils.parseApproveType(userTaskElement); - Integer assignEmptyHandlerType = BpmnModelUtils.parseAssignEmptyHandlerType(userTaskElement); - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - /** - * 特殊情况:部分情况下,TransactionSynchronizationManager 注册 afterCommit 监听时,不会被调用,但是 afterCompletion 可以 - * 例如说:第一个 task 就是配置【自动通过】或者【自动拒绝】时 - * 参见 issue 反馈 - */ - @Override - public void afterCompletion(int transactionStatus) { - // 回滚情况,直接返回 - if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) { - return; - } - // 特殊情况:第一个 task 【自动通过】时,第二个任务设置审批人时 transactionStatus 会为 STATUS_UNKNOWN,不知道啥原因 - if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_UNKNOWN) - && getTask(task.getId()) == null) { - return; - } - // 特殊情况一:【人工审核】审批人为空,根据配置是否要自动通过、自动拒绝 - if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.USER.getType())) { - // 如果有审批人、或者拥有人,则说明不满足情况一,不自动通过、不自动拒绝 - if (!ObjectUtil.isAllEmpty(task.getAssignee(), task.getOwner())) { - return; - } - if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.APPROVE.getType())) { - getSelf().approveTask(null, new BpmTaskApproveReqVO() - .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_APPROVE.getReason())); - } else if (ObjectUtil.equal(assignEmptyHandlerType, BpmUserTaskAssignEmptyHandlerTypeEnum.REJECT.getType())) { - getSelf().rejectTask(null, new BpmTaskRejectReqVO() - .setId(task.getId()).setReason(BpmReasonEnum.ASSIGN_EMPTY_REJECT.getReason())); - } - // 特殊情况二:【自动审核】审批类型为自动通过、不通过 - } else { - if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.AUTO_APPROVE.getType())) { - getSelf().approveTask(null, new BpmTaskApproveReqVO() - .setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_APPROVE.getReason())); - } else if (ObjectUtil.equal(approveType, BpmUserTaskApproveTypeEnum.AUTO_REJECT.getType())) { - getSelf().rejectTask(null, new BpmTaskRejectReqVO() - .setId(task.getId()).setReason(BpmReasonEnum.APPROVE_TYPE_AUTO_REJECT.getReason())); - } - } - } - - }); - } - - /** - * 重要补充说明:该方法目前主要有两个情况会调用到: - *

- * 1. 或签场景 + 审批通过:一个或签有多个审批时,如果 A 审批通过,其它或签 B、C 等任务会被 Flowable 自动删除,此时需要通过该方法更新状态为已取消 - * 2. 审批不通过:在 {@link #rejectTask(Long, BpmTaskRejectReqVO)} 不通过时,对于加签的任务,不会被 Flowable 删除,此时需要通过该方法更新状态为已取消 - */ - @Override - public void processTaskCanceled(String taskId) { - Task task = getTask(taskId); - // 1. 可能只是活动,不是任务,所以查询不到 - if (task == null) { - log.error("[updateTaskStatusWhenCanceled][taskId({}) 任务不存在]", taskId); - return; - } - - // 2. 更新 task 状态 + 原因 - Integer status = (Integer) task.getTaskLocalVariables().get(BpmnVariableConstants.TASK_VARIABLE_STATUS); - if (BpmTaskStatusEnum.isEndStatus(status)) { - log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status); - return; - } - updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmReasonEnum.CANCEL_BY_SYSTEM.getReason()); - // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 - } - - @Override - @DataPermission(enable = false) // 忽略数据权限,避免因为过滤,导致找不到候选人 - public void processTaskAssigned(Task task) { - // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。 - TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { - - /** - * 特殊情况:部分情况下,TransactionSynchronizationManager 注册 afterCommit 监听时,不会被调用,但是 afterCompletion 可以 - * 例如说:第一个 task 就是配置【自动通过】或者【自动拒绝】时 - * 参见 issue 反馈 - */ - @Override - public void afterCompletion(int transactionStatus) { - // 回滚情况,直接返回 - if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_ROLLED_BACK)) { - return; - } - // 特殊情况:第一个 task 【自动通过】时,第二个任务设置审批人时 transactionStatus 会为 STATUS_UNKNOWN,不知道啥原因 - if (ObjectUtil.equal(transactionStatus, TransactionSynchronization.STATUS_UNKNOWN) - && getTask(task.getId()) == null) { - return; - } - if (StrUtil.isEmpty(task.getAssignee())) { - log.error("[processTaskAssigned][taskId({}) 没有分配到负责人]", task.getId()); - return; - } - ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); - if (processInstance == null) { - log.error("[processTaskAssigned][taskId({}) 没有找到流程实例]", task.getId()); - return; - } - - // 自动去重,通过自动审批的方式 TODO @ZT 驳回的情况得考虑一下;@lesan:驳回后,又自动审批么? - BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId()); - if (processDefinitionInfo == null) { - log.error("[processTaskAssigned][taskId({}) 没有找到流程定义({})]", task.getId(), task.getProcessDefinitionId()); - return; - } - if (processDefinitionInfo.getAutoApprovalType() != null) { - HistoricTaskInstanceQuery sameAssigneeQuery = historyService.createHistoricTaskInstanceQuery() - .processInstanceId(task.getProcessInstanceId()) - .taskAssignee(task.getAssignee()) // 相同审批人 - .taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus()) - .finished(); - if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType()) - && sameAssigneeQuery.count() > 0) { - getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) - .setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName())); - return; - } - if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) { - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); - if (bpmnModel == null) { - log.error("[processTaskAssigned][taskId({}) 没有找到流程模型({})]", task.getId(), task.getProcessDefinitionId()); - return; - } - List sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingFlows( // 获取所有上一个节点 - BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())), - SequenceFlow::getSourceRef); - if (sameAssigneeQuery.taskDefinitionKeys(sourceTaskIds).count() > 0) { - getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) - .setReason(BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getName())); - return; - } - } - } - - // 获取发起人节点 - BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId()); - if (bpmnModel == null) { - log.error("[processTaskAssigned][taskId({}) 没有找到流程模型]", task.getId()); - return; - } - FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); - // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 - // TODO ZT:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 - Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), - String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); - Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), - PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class)); - if (userTaskElement.getId().equals(START_USER_NODE_ID) - && (skipStartUserNodeFlag == null // 目的:一般是“主流程”,发起人节点,自动通过审核 - || Boolean.TRUE.equals(skipStartUserNodeFlag)) // 目的:一般是“子流程”,发起人节点,按配置自动通过审核 - && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { - getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) - .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason())); - return; - } - // 当不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum 策略进行处理 - if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID) - && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) { - if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) { - Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement); - - // 情况一:自动跳过 - if (ObjectUtils.equalsAny(assignStartUserHandlerType, - BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) { - getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) - .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason())); - return; - } - // 情况二:转交给部门负责人审批 - if (ObjectUtils.equalsAny(assignStartUserHandlerType, - BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) { - AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData(); - Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId()); - Long deptId = startUser.getDeptIds().stream().findAny().orElse(null); - DeptRespDTO dept = deptId != null ? deptApi.getDept(deptId).getCheckedData() : null; - Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), deptId); - // 找不到部门负责人的情况下,自动审批通过 - // noinspection DataFlowIssue - if (dept.getLeaderUserId() == null) { - getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId()) - .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason())); - return; - } - // 找得到部门负责人的情况下,修改负责人 - if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) { - getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO() - .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId()) - .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason())); - return; - } - // 如果部门负责人是自己,还是自己审批吧~ - } - } - } - // 注意:需要基于 instance 设置租户编号,避免 Flowable 内部异步时,丢失租户编号 - FlowableUtils.execute(processInstance.getTenantId(), () -> { - AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId())).getCheckedData(); - messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); - }); - } - - }); - } - - @Override - public void processTaskCompleted(Task task) { - ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); - if (processInstance == null) { - log.error("[processTaskCompleted][taskId({}) 没有找到流程实例]", task.getId()); - return; - } - BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService. - getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); - if (processDefinitionInfo == null) { - log.error("[processTaskCompleted][processDefinitionId({}) 没有找到流程定义]", processInstance.getProcessDefinitionId()); - return; - } - - // 任务后置通知 - if (ObjUtil.isNotNull(processDefinitionInfo.getTaskAfterTriggerSetting())){ - BpmModelMetaInfoVO.HttpRequestSetting setting = processDefinitionInfo.getTaskAfterTriggerSetting(); - BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, - setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); - } - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void processTaskTimeout(String processInstanceId, String taskDefineKey, Integer handlerType) { - ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); - if (processInstance == null) { - log.error("[processTaskTimeout][processInstanceId({}) 没有找到流程实例]", processInstanceId); - return; - } - List taskList = getRunningTaskListByProcessInstanceId(processInstanceId, true, taskDefineKey); - // TODO 优化:未来需要考虑加签的情况 - if (CollUtil.isEmpty(taskList)) { - log.error("[processTaskTimeout][processInstanceId({}) 定义Key({}) 没有找到任务]", processInstanceId, taskDefineKey); - return; - } - - taskList.forEach(task -> FlowableUtils.execute(task.getTenantId(), () -> { - // 情况一:自动提醒 - if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.REMINDER.getType())) { - messageService.sendMessageWhenTaskTimeout(new BpmMessageSendWhenTaskTimeoutReqDTO() - .setProcessInstanceId(processInstanceId).setProcessInstanceName(processInstance.getName()) - .setTaskId(task.getId()).setTaskName(task.getName()).setAssigneeUserId(Long.parseLong(task.getAssignee()))); - return; - } - - // 情况二:自动同意 - if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.APPROVE.getType())) { - approveTask(Long.parseLong(task.getAssignee()), - new BpmTaskApproveReqVO().setId(task.getId()).setReason(BpmReasonEnum.TIMEOUT_APPROVE.getReason())); - return; - } - - // 情况三:自动拒绝 - if (Objects.equals(handlerType, BpmUserTaskTimeoutHandlerTypeEnum.REJECT.getType())) { - rejectTask(Long.parseLong(task.getAssignee()), - new BpmTaskRejectReqVO().setId(task.getId()).setReason(BpmReasonEnum.REJECT_TASK.getReason())); - } - })); - } - - @Override - @Transactional(rollbackFor = Exception.class) - public void processChildProcessTimeout(String processInstanceId, String taskDefineKey) { - List activityInstances = runtimeService.createActivityInstanceQuery() - .processInstanceId(processInstanceId) - .activityId(taskDefineKey).list(); - activityInstances.forEach(activityInstance -> FlowableUtils.execute(activityInstance.getTenantId(), - () -> moveTaskToEnd(activityInstance.getCalledProcessInstanceId(), BpmReasonEnum.TIMEOUT_APPROVE.getReason()))); - } - - @Override - public void triggerTask(String processInstanceId, String taskDefineKey) { - Execution execution = runtimeService.createExecutionQuery() - .processInstanceId(processInstanceId) - .activityId(taskDefineKey) - .singleResult(); - if (execution == null) { - log.error("[triggerTask][processInstanceId({}) activityId({}) 没有找到执行活动]", processInstanceId, taskDefineKey); - return; - } - - // 若存在直接触发接收任务,执行后续节点 - FlowableUtils.execute(execution.getTenantId(), - () -> runtimeService.trigger(execution.getId())); - } - - /** - * 获得自身的代理对象,解决 AOP 生效问题 - * - * @return 自己 - */ - private BpmTaskServiceImpl getSelf() { - return SpringUtil.getBean(getClass()); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java deleted file mode 100644 index e458af4..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmCallActivityListener.java +++ /dev/null @@ -1,96 +0,0 @@ -package com.zt.plat.module.bpm.service.task.listener; - -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -import cn.hutool.core.util.StrUtil; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.enums.definition.BpmChildProcessStartUserEmptyTypeEnum; -import com.zt.plat.module.bpm.enums.definition.BpmChildProcessStartUserTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import jakarta.annotation.Resource; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.delegate.ExecutionListener; -import org.flowable.engine.impl.el.FixedValue; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.stereotype.Component; - -import java.util.List; - -/** - * BPM 子流程监听器:设置流程的发起人 - * - * @author Lesan - */ -@Component -@Slf4j -public class BpmCallActivityListener implements ExecutionListener { - - public static final String DELEGATE_EXPRESSION = "${bpmCallActivityListener}"; - - @Setter - private FixedValue listenerConfig; - - @Resource - private BpmProcessDefinitionService processDefinitionService; - - @Resource - private BpmProcessInstanceService processInstanceService; - - @Override - public void notify(DelegateExecution execution) { - String expressionText = listenerConfig.getExpressionText(); - Assert.notNull(expressionText, "监听器扩展字段({})不能为空", expressionText); - BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting startUserSetting = JsonUtils.parseObject( - expressionText, BpmSimpleModelNodeVO.ChildProcessSetting.StartUserSetting.class); - ProcessInstance processInstance = processInstanceService.getProcessInstance(execution.getRootProcessInstanceId()); - - // 1. 当发起人来源为主流程发起人时,并兜底 startUserSetting 为空时 - if (startUserSetting == null - || startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.MAIN_PROCESS_START_USER.getType())) { - FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); - return; - } - - // 2. 当发起人来源为表单时 - if (startUserSetting.getType().equals(BpmChildProcessStartUserTypeEnum.FROM_FORM.getType())) { - String formFieldValue = MapUtil.getStr(processInstance.getProcessVariables(), startUserSetting.getFormField()); - // 2.1 当表单值为空时 - if (StrUtil.isEmpty(formFieldValue)) { - // 2.1.1 来自主流程发起人 - if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_START_USER.getType())) { - FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); - return; - } - // 2.1.2 来自子流程管理员 - if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.CHILD_PROCESS_ADMIN.getType())) { - BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(execution.getProcessDefinitionId()); - List managerUserIds = processDefinition.getManagerUserIds(); - FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); - return; - } - // 2.1.3 来自主流程管理员 - if (startUserSetting.getEmptyType().equals(BpmChildProcessStartUserEmptyTypeEnum.MAIN_PROCESS_ADMIN.getType())) { - BpmProcessDefinitionInfoDO processDefinition = processDefinitionService.getProcessDefinitionInfo(processInstance.getProcessDefinitionId()); - List managerUserIds = processDefinition.getManagerUserIds(); - FlowableUtils.setAuthenticatedUserId(managerUserIds.get(0)); - return; - } - } - // 2.2 使用表单值,并兜底字符串转 Long 失败时使用主流程发起人 - try { - FlowableUtils.setAuthenticatedUserId(Long.parseLong(formFieldValue)); - } catch (Exception e) { - log.error("[notify][监听器:{},子流程监听器设置流程的发起人字符串转 Long 失败,字符串:{}]", - DELEGATE_EXPRESSION, formFieldValue); - FlowableUtils.setAuthenticatedUserId(Long.parseLong(processInstance.getStartUserId())); - } - } - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java deleted file mode 100644 index ec0f164..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.zt.plat.module.bpm.service.task.listener; - -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import jakarta.annotation.Resource; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.delegate.TaskListener; -import org.flowable.engine.impl.el.FixedValue; -import org.flowable.engine.runtime.ProcessInstance; -import org.flowable.task.service.delegate.DelegateTask; -import org.springframework.context.annotation.Scope; -import org.springframework.stereotype.Component; - -import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig; - -// TODO @ZT:可能会想换个包地址 -/** - * BPM 用户任务通用监听器 - * - * @author Lesan - */ -@Component -@Slf4j -@Scope("prototype") -public class BpmUserTaskListener implements TaskListener { - - public static final String DELEGATE_EXPRESSION = "${bpmUserTaskListener}"; - - @Resource - private BpmProcessInstanceService processInstanceService; - - @Setter - private FixedValue listenerConfig; - - @Override - public void notify(DelegateTask delegateTask) { - // 1. 获取所需基础信息 - ProcessInstance processInstance = processInstanceService.getProcessInstance(delegateTask.getProcessInstanceId()); - BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig); - - // 2. 发起请求 - // TODO @ZT:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去; - listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getProcessInstanceId())); - listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getAssignee())); - listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskDefinitionKey") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getTaskDefinitionKey())); - listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("taskId") - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getId())); - BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, - listenerHandler.getPath(), listenerHandler.getHeader(), listenerHandler.getBody(), false, null); - - // 3. 是否需要后续操作?TODO ZT:待定! - } -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java deleted file mode 100644 index 8aee717..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.zt.plat.module.bpm.service.task.trigger; - -import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; - -// TODO @ZT:可能会想换个包地址 -/** - * BPM 触发器接口 - *

- * 处理不同的动作 - * - * @author jason - */ -public interface BpmTrigger { - - /** - * 对应触发器类型 - * - * @return 触发器类型 - */ - BpmTriggerTypeEnum getType(); - - /** - * 触发器执行 - * - * @param processInstanceId 流程实例编号 - * @param param 触发器参数 - */ - void execute(String processInstanceId, String param); - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java deleted file mode 100644 index 60d3073..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormDeleteTrigger.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.zt.plat.module.bpm.service.task.trigger.form; - -import cn.hutool.core.collection.CollUtil; -import com.fasterxml.jackson.core.type.TypeReference; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * BPM 删除流程表单数据触发器 - * - * @author jason - */ -@Component -@Slf4j -public class BpmFormDeleteTrigger implements BpmTrigger { - - @Resource - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTriggerTypeEnum getType() { - return BpmTriggerTypeEnum.FORM_DELETE; - } - - @Override - public void execute(String processInstanceId, String param) { - // 1. 解析删除流程表单数据配置 - List settings = JsonUtils.parseObject(param, new TypeReference<>() {}); - if (CollUtil.isEmpty(settings)) { - log.error("[execute][流程({}) 删除流程表单数据触发器配置为空]", processInstanceId); - return; - } - - // 2. 获取流程变量 - Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); - - // 3.1 获取需要删除的表单字段 - Set deleteFields = new HashSet<>(); - settings.forEach(setting -> { - if (CollUtil.isEmpty(setting.getDeleteFields())) { - return; - } - // 配置了条件,判断条件是否满足 - boolean isFieldDeletedNeeded = true; - if (setting.getConditionType() != null) { - String conditionExpression = SimpleModelUtils.buildConditionExpression( - setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups()); - isFieldDeletedNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); - } - if (isFieldDeletedNeeded) { - deleteFields.addAll(setting.getDeleteFields()); - } - }); - - // 3.2 删除流程变量 - if (CollUtil.isNotEmpty(deleteFields)) { - processInstanceService.removeProcessInstanceVariables(processInstanceId, deleteFields); - } - } -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java deleted file mode 100644 index 79a48d9..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/form/BpmFormUpdateTrigger.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.zt.plat.module.bpm.service.task.trigger.form; - -import cn.hutool.core.collection.CollUtil; -import com.fasterxml.jackson.core.type.TypeReference; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.FormTriggerSetting; -import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.SimpleModelUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Component; - -import java.util.List; -import java.util.Map; - -/** - * BPM 更新流程表单触发器 - * - * @author jason - */ -@Component -@Slf4j -public class BpmFormUpdateTrigger implements BpmTrigger { - - @Resource - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTriggerTypeEnum getType() { - return BpmTriggerTypeEnum.FORM_UPDATE; - } - - @Override - public void execute(String processInstanceId, String param) { - // 1. 解析更新流程表单配置 - List settings = JsonUtils.parseObject(param, new TypeReference<>() {}); - if (CollUtil.isEmpty(settings)) { - log.error("[execute][流程({}) 更新流程表单触发器配置为空]", processInstanceId); - return; - } - - // 2. 获取流程变量 - Map processVariables = processInstanceService.getProcessInstance(processInstanceId).getProcessVariables(); - - // 3. 更新流程变量 - for (FormTriggerSetting setting : settings) { - if (CollUtil.isEmpty(setting.getUpdateFormFields())) { - continue; - } - // 配置了条件,判断条件是否满足 - boolean isFormUpdateNeeded = true; - if (setting.getConditionType() != null) { - String conditionExpression = SimpleModelUtils.buildConditionExpression( - setting.getConditionType(), setting.getConditionExpression(), setting.getConditionGroups()); - isFormUpdateNeeded = BpmnModelUtils.evalConditionExpress(processVariables, conditionExpression); - } - // 更新流程表单 - if (isFormUpdateNeeded) { - processInstanceService.updateProcessInstanceVariables(processInstanceId, setting.getUpdateFormFields()); - } - } - } -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java deleted file mode 100644 index 676b260..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmAbstractHttpRequestTrigger.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.zt.plat.module.bpm.service.task.trigger.http; - -import com.zt.plat.module.bpm.service.task.trigger.BpmTrigger; -import lombok.extern.slf4j.Slf4j; - -/** - * BPM 发送 HTTP 请求触发器抽象类 - * - * @author jason - */ -@Slf4j -public abstract class BpmAbstractHttpRequestTrigger implements BpmTrigger { - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java deleted file mode 100644 index 76292d8..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmHttpCallbackTrigger.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.zt.plat.module.bpm.service.task.trigger.http; - -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; -import com.zt.plat.module.bpm.enums.definition.BpmHttpRequestParamTypeEnum; -import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.stereotype.Component; - -/** - * BPM HTTP 回调触发器 - * - * @author jason - */ -@Component -@Slf4j -public class BpmHttpCallbackTrigger extends BpmAbstractHttpRequestTrigger { - - @Resource - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTriggerTypeEnum getType() { - return BpmTriggerTypeEnum.HTTP_CALLBACK; - } - - @Override - public void execute(String processInstanceId, String param) { - // 1. 解析 http 请求配置 - BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, - BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting.class); - if (setting == null) { - log.error("[execute][流程({}) HTTP 回调触发器配置为空]", processInstanceId); - return; - } - - // 2. 发起请求 - ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); - setting.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam() - .setKey("taskDefineKey") // 重要:回调请求 taskDefineKey 需要传给被调用方,用于回调执行 - .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(setting.getCallbackTaskDefineKey())); - BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, - setting.getUrl(), setting.getHeader(), setting.getBody(), false, null); - } - -} diff --git a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java b/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java deleted file mode 100644 index f612660..0000000 --- a/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/http/BpmSyncHttpRequestTrigger.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.zt.plat.module.bpm.service.task.trigger.http; - -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO.TriggerSetting.HttpRequestTriggerSetting; -import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import jakarta.annotation.Resource; -import lombok.extern.slf4j.Slf4j; -import org.flowable.engine.runtime.ProcessInstance; -import org.springframework.stereotype.Component; - -/** - * BPM 发送同步 HTTP 请求触发器 - * - * @author jason - */ -@Component -@Slf4j -public class BpmSyncHttpRequestTrigger extends BpmAbstractHttpRequestTrigger { - - @Resource - private BpmProcessInstanceService processInstanceService; - - @Override - public BpmTriggerTypeEnum getType() { - return BpmTriggerTypeEnum.HTTP_REQUEST; - } - - @Override - public void execute(String processInstanceId, String param) { - // 1. 解析 http 请求配置 - HttpRequestTriggerSetting setting = JsonUtils.parseObject(param, HttpRequestTriggerSetting.class); - if (setting == null) { - log.error("[execute][流程({}) HTTP 触发器请求配置为空]", processInstanceId); - return; - } - - // 2. 发起请求 - ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); - BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, - setting.getUrl(), setting.getHeader(), setting.getBody(), true, setting.getResponse()); - } - -} diff --git a/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java b/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java deleted file mode 100644 index 9e2c50f..0000000 --- a/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java +++ /dev/null @@ -1,572 +0,0 @@ -// -// Source code recreated from a .class file by IntelliJ IDEA -// (powered by FernFlower decompiler) -// - -package liquibase.database.core; - -import java.lang.reflect.Method; -import java.sql.CallableStatement; -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Properties; -import java.util.ResourceBundle; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import liquibase.CatalogAndSchema; -import liquibase.GlobalConfiguration; -import liquibase.Scope; -import liquibase.database.AbstractJdbcDatabase; -import liquibase.database.DatabaseConnection; -import liquibase.database.OfflineConnection; -import liquibase.database.jvm.JdbcConnection; -import liquibase.exception.DatabaseException; -import liquibase.exception.UnexpectedLiquibaseException; -import liquibase.exception.ValidationErrors; -import liquibase.executor.ExecutorService; -import liquibase.statement.DatabaseFunction; -import liquibase.statement.SequenceCurrentValueFunction; -import liquibase.statement.SequenceNextValueFunction; -import liquibase.statement.UniqueConstraint; -import liquibase.statement.core.RawCallStatement; -import liquibase.statement.core.RawParameterizedSqlStatement; -import liquibase.structure.DatabaseObject; -import liquibase.structure.core.Catalog; -import liquibase.structure.core.Column; -import liquibase.structure.core.Index; -import liquibase.structure.core.PrimaryKey; -import liquibase.structure.core.Schema; -import liquibase.util.JdbcUtil; -import liquibase.util.StringUtil; -import org.apache.commons.lang3.StringUtils; - -public class DmDatabase extends AbstractJdbcDatabase { - private static final String PROXY_USER_REGEX = ".*(?:thin|oci)\\:(.+)/@.*"; - public static final Pattern PROXY_USER_PATTERN = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*"); - private static final String VERSION_REGEX = "(\\d+)\\.(\\d+)\\..*"; - private static final Pattern VERSION_PATTERN = Pattern.compile("(\\d+)\\.(\\d+)\\..*"); - public static final String PRODUCT_NAME = "DM DBMS"; - private static final ResourceBundle coreBundle = ResourceBundle.getBundle("liquibase/i18n/liquibase-core"); - protected final int SHORT_IDENTIFIERS_LENGTH = 30; - protected final int LONG_IDENTIFIERS_LEGNTH = 128; - public static final int ORACLE_12C_MAJOR_VERSION = 12; - public static final int ORACLE_23C_MAJOR_VERSION = 23; - private final Set reservedWords = new HashSet(); - private Set userDefinedTypes; - private Map savedSessionNlsSettings; - private Boolean canAccessDbaRecycleBin; - private Integer databaseMajorVersion; - private Integer databaseMinorVersion; - - public DmDatabase() { - super.unquotedObjectsAreUppercased = true; - super.setCurrentDateTimeFunction("SYSTIMESTAMP"); - this.dateFunctions.add(new DatabaseFunction("SYSDATE")); - this.dateFunctions.add(new DatabaseFunction("SYSTIMESTAMP")); - this.dateFunctions.add(new DatabaseFunction("CURRENT_TIMESTAMP")); - super.sequenceNextValueFunction = "%s.nextval"; - super.sequenceCurrentValueFunction = "%s.currval"; - } - - public int getPriority() { - return 1; - } - - private void tryProxySession(String url, Connection con) { - Matcher m = PROXY_USER_PATTERN.matcher(url); - if (m.matches()) { - Properties props = new Properties(); - props.put("PROXY_USER_NAME", m.group(1)); - - try { - Method method = con.getClass().getMethod("openProxySession", Integer.TYPE, Properties.class); - method.setAccessible(true); - method.invoke(con, 1, props); - } catch (Exception e) { - Scope.getCurrentScope().getLog(this.getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage()); - return; - } - - try { - Method method = con.getClass().getMethod("isProxySession"); - method.setAccessible(true); - boolean b = (Boolean)method.invoke(con); - if (!b) { - Scope.getCurrentScope().getLog(this.getClass()).info("Proxy session not established on OracleDatabase: "); - } - } catch (Exception e) { - Scope.getCurrentScope().getLog(this.getClass()).info("Could not open proxy session on OracleDatabase: " + e.getCause().getMessage()); - } - } - - } - - public void setConnection(DatabaseConnection conn) { - this.reservedWords.addAll(Arrays.asList("GROUP", "USER", "SESSION", "PASSWORD", "RESOURCE", "START", "SIZE", "UID", "DESC", "ORDER")); - Connection sqlConn = null; - boolean dmDatabase = false; - if (!(conn instanceof OfflineConnection)) { - try { - if (conn instanceof JdbcConnection) { - sqlConn = ((JdbcConnection)conn).getWrappedConnection(); - } - } catch (Exception e) { - throw new UnexpectedLiquibaseException(e); - } - - if (sqlConn != null) { - this.tryProxySession(conn.getURL(), sqlConn); - - try { - this.reservedWords.addAll(Arrays.asList(sqlConn.getMetaData().getSQLKeywords().toUpperCase().split(",\\s*"))); - } catch (SQLException e) { - Scope.getCurrentScope().getLog(this.getClass()).info("Could get sql keywords on OracleDatabase: " + e.getMessage()); - } - - try { - Method method = sqlConn.getClass().getMethod("setRemarksReporting", Boolean.TYPE); - method.setAccessible(true); - method.invoke(sqlConn, true); - } catch (Exception e) { - Scope.getCurrentScope().getLog(this.getClass()).info("Could not set remarks reporting on OracleDatabase: " + e.getMessage()); - } - - try { - DatabaseMetaData metaData = sqlConn.getMetaData(); - if (metaData != null) { - String productName = metaData.getDatabaseProductName(); - dmDatabase = productName != null && PRODUCT_NAME.equalsIgnoreCase(productName); - if (dmDatabase) { - this.databaseMajorVersion = metaData.getDatabaseMajorVersion(); - this.databaseMinorVersion = metaData.getDatabaseMinorVersion(); - } - } - } catch (SQLException e) { - Scope.getCurrentScope().getLog(this.getClass()).info("Unable to inspect database metadata for DM version detection: " + e.getMessage()); - } - - if (!dmDatabase) { - CallableStatement statement = null; - - try { - statement = sqlConn.prepareCall("{call DBMS_UTILITY.DB_VERSION(?,?)}"); - statement.registerOutParameter(1, 12); - statement.registerOutParameter(2, 12); - statement.execute(); - String compatibleVersion = statement.getString(2); - if (compatibleVersion != null) { - Matcher majorVersionMatcher = VERSION_PATTERN.matcher(compatibleVersion); - if (majorVersionMatcher.matches()) { - this.databaseMajorVersion = Integer.valueOf(majorVersionMatcher.group(1)); - this.databaseMinorVersion = Integer.valueOf(majorVersionMatcher.group(2)); - } - } - } catch (SQLException e) { - String message = "Cannot read from DBMS_UTILITY.DB_VERSION: " + e.getMessage(); - Scope.getCurrentScope().getLog(this.getClass()).info("Could not set check compatibility mode on OracleDatabase, assuming not running in any sort of compatibility mode: " + message); - } finally { - JdbcUtil.closeStatement(statement); - } - } - - if (GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue() != null) { - int timeoutValue = (Integer)GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue(); - Scope.getCurrentScope().getLog(this.getClass()).fine("Setting DDL_LOCK_TIMEOUT value to " + timeoutValue); - String sql = "ALTER SESSION SET DDL_LOCK_TIMEOUT=" + timeoutValue; - PreparedStatement ddlLockTimeoutStatement = null; - - try { - ddlLockTimeoutStatement = sqlConn.prepareStatement(sql); - ddlLockTimeoutStatement.execute(); - } catch (SQLException sqle) { - Scope.getCurrentScope().getUI().sendErrorMessage("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + sqle.getMessage(), sqle); - Scope.getCurrentScope().getLog(this.getClass()).warning("Unable to set the DDL_LOCK_TIMEOUT_VALUE: " + sqle.getMessage(), sqle); - } finally { - JdbcUtil.closeStatement(ddlLockTimeoutStatement); - } - } - } - } - - super.setConnection(conn); - } - - public String getShortName() { - return "dm"; - } - - protected String getDefaultDatabaseProductName() { - return PRODUCT_NAME; - } - - public int getDatabaseMajorVersion() throws DatabaseException { - return this.databaseMajorVersion == null ? super.getDatabaseMajorVersion() : this.databaseMajorVersion; - } - - public int getDatabaseMinorVersion() throws DatabaseException { - return this.databaseMinorVersion == null ? super.getDatabaseMinorVersion() : this.databaseMinorVersion; - } - - public Integer getDefaultPort() { - return 5236; - } - - public String getJdbcCatalogName(CatalogAndSchema schema) { - return null; - } - - public String getJdbcSchemaName(CatalogAndSchema schema) { - return this.correctObjectName(schema.getCatalogName() == null ? schema.getSchemaName() : schema.getCatalogName(), Schema.class); - } - - protected String getAutoIncrementClause(String generationType, Boolean defaultOnNull) { - if (StringUtil.isEmpty(generationType)) { - return super.getAutoIncrementClause(); - } else { - String autoIncrementClause = "GENERATED %s AS IDENTITY"; - String generationStrategy = generationType; - if (Boolean.TRUE.equals(defaultOnNull) && generationType.toUpperCase().equals("BY DEFAULT")) { - generationStrategy = generationType + " ON NULL"; - } - - return String.format(autoIncrementClause, generationStrategy); - } - } - - public String generatePrimaryKeyName(String tableName) { - return tableName.length() > 27 ? "PK_" + tableName.toUpperCase(Locale.US).substring(0, 27) : "PK_" + tableName.toUpperCase(Locale.US); - } - - public boolean supportsInitiallyDeferrableColumns() { - return true; - } - - public boolean isReservedWord(String objectName) { - return this.reservedWords.contains(objectName.toUpperCase()); - } - - public boolean supportsSequences() { - return true; - } - - public boolean supports(Class object) { - return Schema.class.isAssignableFrom(object) ? false : super.supports(object); - } - - public boolean supportsSchemas() { - return false; - } - - protected String getConnectionCatalogName() throws DatabaseException { - if (this.getConnection() instanceof OfflineConnection) { - return this.getConnection().getCatalog(); - } else if (!(this.getConnection() instanceof JdbcConnection)) { - return this.defaultCatalogName; - } else { - try { - return (String)((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForObject(new RawCallStatement("select sys_context( 'userenv', 'current_schema' ) from dual"), String.class); - } catch (Exception e) { - Scope.getCurrentScope().getLog(this.getClass()).info("Error getting default schema", e); - return null; - } - } - } - - public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException { - String databaseProductName = conn == null ? null : conn.getDatabaseProductName(); - if (databaseProductName == null) { - return false; - } - if (PRODUCT_NAME.equalsIgnoreCase(databaseProductName)) { - return true; - } - // Flowable 历史上将 DM 映射为 Oracle 元数据,因此这里同样接受 Oracle 以保持兼容 - return "oracle".equalsIgnoreCase(databaseProductName); - } - - public String getDefaultDriver(String url) { - return url.startsWith("jdbc:dm") ? "dm.jdbc.driver.DmDriver" : null; - } - - public String getDefaultCatalogName() { - String defaultCatalogName = super.getDefaultCatalogName(); - if (Boolean.TRUE.equals(GlobalConfiguration.PRESERVE_SCHEMA_CASE.getCurrentValue())) { - return defaultCatalogName; - } else { - return defaultCatalogName == null ? null : defaultCatalogName.toUpperCase(Locale.US); - } - } - - public String getDateLiteral(String isoDate) { - String normalLiteral = super.getDateLiteral(isoDate); - if (this.isDateOnly(isoDate)) { - return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD')"; - } else if (this.isTimeOnly(isoDate)) { - return "TO_DATE(" + normalLiteral + ", 'HH24:MI:SS')"; - } else if (this.isTimestamp(isoDate)) { - return "TO_TIMESTAMP(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS.FF')"; - } else if (this.isDateTime(isoDate)) { - int seppos = normalLiteral.lastIndexOf(46); - if (seppos != -1) { - normalLiteral = normalLiteral.substring(0, seppos) + "'"; - } - - return "TO_DATE(" + normalLiteral + ", 'YYYY-MM-DD HH24:MI:SS')"; - } else { - return "UNSUPPORTED:" + isoDate; - } - } - - public boolean isSystemObject(DatabaseObject example) { - if (example == null) { - return false; - } else if (this.isLiquibaseObject(example)) { - return false; - } else { - if (example instanceof Schema) { - if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) { - return true; - } - - if ("SYSTEM".equals(example.getSchema().getCatalogName()) || "SYS".equals(example.getSchema().getCatalogName()) || "CTXSYS".equals(example.getSchema().getCatalogName()) || "XDB".equals(example.getSchema().getCatalogName())) { - return true; - } - } else if (this.isSystemObject(example.getSchema())) { - return true; - } - - if (example instanceof Catalog) { - if ("SYSTEM".equals(example.getName()) || "SYS".equals(example.getName()) || "CTXSYS".equals(example.getName()) || "XDB".equals(example.getName())) { - return true; - } - } else if (example.getName() != null) { - if (example.getName().startsWith("BIN$")) { - boolean filteredInOriginalQuery = this.canAccessDbaRecycleBin(); - if (!filteredInOriginalQuery) { - filteredInOriginalQuery = StringUtil.trimToEmpty(example.getSchema().getName()).equalsIgnoreCase(this.getConnection().getConnectionUserName()); - } - - if (!filteredInOriginalQuery) { - return true; - } - - return !(example instanceof PrimaryKey) && !(example instanceof Index) && !(example instanceof UniqueConstraint); - } - - if (example.getName().startsWith("AQ$")) { - return true; - } - - if (example.getName().startsWith("DR$")) { - return true; - } - - if (example.getName().startsWith("SYS_IOT_OVER")) { - return true; - } - - if ((example.getName().startsWith("MDRT_") || example.getName().startsWith("MDRS_")) && example.getName().endsWith("$")) { - return true; - } - - if (example.getName().startsWith("MLOG$_")) { - return true; - } - - if (example.getName().startsWith("RUPD$_")) { - return true; - } - - if (example.getName().startsWith("WM$_")) { - return true; - } - - if ("CREATE$JAVA$LOB$TABLE".equals(example.getName())) { - return true; - } - - if ("JAVA$CLASS$MD5$TABLE".equals(example.getName())) { - return true; - } - - if (example.getName().startsWith("ISEQ$$_")) { - return true; - } - - if (example.getName().startsWith("USLOG$")) { - return true; - } - - if (example.getName().startsWith("SYS_FBA")) { - return true; - } - } - - return super.isSystemObject(example); - } - } - - public boolean supportsTablespaces() { - return true; - } - - public boolean supportsAutoIncrement() { - boolean isAutoIncrementSupported = false; - - try { - if (this.getDatabaseMajorVersion() >= 12) { - isAutoIncrementSupported = true; - } - } catch (DatabaseException var3) { - isAutoIncrementSupported = false; - } - - return isAutoIncrementSupported; - } - - public boolean supportsRestrictForeignKeys() { - return false; - } - - public int getDataTypeMaxParameters(String dataTypeName) { - if ("BINARY_FLOAT".equals(dataTypeName.toUpperCase())) { - return 0; - } else { - return "BINARY_DOUBLE".equals(dataTypeName.toUpperCase()) ? 0 : super.getDataTypeMaxParameters(dataTypeName); - } - } - - public String getSystemTableWhereClause(String tableNameColumn) { - List clauses = new ArrayList(Arrays.asList("BIN$", "AQ$", "DR$", "SYS_IOT_OVER", "MLOG$_", "RUPD$_", "WM$_", "ISEQ$$_", "USLOG$", "SYS_FBA")); - clauses.replaceAll((s) -> tableNameColumn + " NOT LIKE '" + s + "%'"); - return "(" + StringUtil.join(clauses, " AND ") + ")"; - } - - public boolean jdbcCallsCatalogsSchemas() { - return true; - } - - public Set getUserDefinedTypes() { - if (this.userDefinedTypes == null) { - this.userDefinedTypes = new HashSet(); - if (this.getConnection() != null && !(this.getConnection() instanceof OfflineConnection)) { - try { - try { - this.userDefinedTypes.addAll(((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForList(new RawParameterizedSqlStatement("SELECT DISTINCT TYPE_NAME FROM ALL_TYPES"), String.class)); - } catch (DatabaseException var2) { - this.userDefinedTypes.addAll(((ExecutorService)Scope.getCurrentScope().getSingleton(ExecutorService.class)).getExecutor("jdbc", this).queryForList(new RawParameterizedSqlStatement("SELECT TYPE_NAME FROM USER_TYPES"), String.class)); - } - } catch (DatabaseException var3) { - } - } - } - - return this.userDefinedTypes; - } - - public String generateDatabaseFunctionValue(DatabaseFunction databaseFunction) { - if (databaseFunction != null && "current_timestamp".equalsIgnoreCase(databaseFunction.toString())) { - return databaseFunction.toString(); - } else if (!(databaseFunction instanceof SequenceNextValueFunction) && !(databaseFunction instanceof SequenceCurrentValueFunction)) { - return super.generateDatabaseFunctionValue(databaseFunction); - } else { - String quotedSeq = super.generateDatabaseFunctionValue(databaseFunction); - return quotedSeq.replaceFirst("\"([^.\"]+)\\.([^.\"]+)\"", "\"$1\".\"$2\""); - } - } - - public ValidationErrors validate() { - ValidationErrors errors = super.validate(); - DatabaseConnection connection = this.getConnection(); - if (connection != null && !(connection instanceof OfflineConnection)) { - if (!this.canAccessDbaRecycleBin()) { - errors.addWarning(this.getDbaRecycleBinWarning()); - } - - return errors; - } else { - Scope.getCurrentScope().getLog(this.getClass()).info("Cannot validate offline database"); - return errors; - } - } - - public String getDbaRecycleBinWarning() { - return "Liquibase needs to access the DBA_RECYCLEBIN table so we can automatically handle the case where constraints are deleted and restored. Since Oracle doesn't properly restore the original table names referenced in the constraint, we use the information from the DBA_RECYCLEBIN to automatically correct this issue.\n\nThe user you used to connect to the database (" + this.getConnection().getConnectionUserName() + ") needs to have \"SELECT ON SYS.DBA_RECYCLEBIN\" permissions set before we can perform this operation. Please run the following SQL to set the appropriate permissions, and try running the command again.\n\n GRANT SELECT ON SYS.DBA_RECYCLEBIN TO " + this.getConnection().getConnectionUserName() + ";"; - } - - public boolean canAccessDbaRecycleBin() { - if (this.canAccessDbaRecycleBin == null) { - DatabaseConnection connection = this.getConnection(); - if (connection == null || connection instanceof OfflineConnection) { - return false; - } - - Statement statement = null; - - try { - statement = ((JdbcConnection)connection).createStatement(); - ResultSet resultSet = statement.executeQuery("select 1 from dba_recyclebin where 0=1"); - resultSet.close(); - this.canAccessDbaRecycleBin = true; - } catch (Exception var7) { - if (var7 instanceof SQLException && var7.getMessage().startsWith("ORA-00942")) { - this.canAccessDbaRecycleBin = false; - } else { - Scope.getCurrentScope().getLog(this.getClass()).warning("Cannot check dba_recyclebin access", var7); - this.canAccessDbaRecycleBin = false; - } - } finally { - JdbcUtil.close((ResultSet)null, statement); - } - } - - return this.canAccessDbaRecycleBin; - } - - public boolean supportsNotNullConstraintNames() { - return true; - } - - public boolean isValidOracleIdentifier(String identifier, Class type) { - if (identifier != null && identifier.length() >= 1) { - if (!identifier.matches("^(i?)[A-Z][A-Z0-9\\$\\_\\#]*$")) { - return false; - } else { - return identifier.length() <= 128; - } - } else { - return false; - } - } - - public int getIdentifierMaximumLength() { - try { - if (this.getDatabaseMajorVersion() < 12) { - return 30; - } else { - return this.getDatabaseMajorVersion() == 12 && this.getDatabaseMinorVersion() <= 1 ? 30 : 128; - } - } catch (DatabaseException ex) { - throw new UnexpectedLiquibaseException("Cannot determine the Oracle database version number", ex); - } - } - - public boolean supportsDatabaseChangeLogHistory() { - return true; - } - - public String correctObjectName(String objectName, Class objectType) { - return objectType.equals(Column.class) && StringUtils.startsWithIgnoreCase(objectName, "int") ? "NUMBER(*, 0)" : super.correctObjectName(objectName, objectType); - } -} diff --git a/zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java b/zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java deleted file mode 100644 index b2c55f0..0000000 --- a/zt-module-bpm-server/src/main/java/liquibase/datatype/core/BooleanType.java +++ /dev/null @@ -1,148 +0,0 @@ -package liquibase.datatype.core; - -import liquibase.change.core.LoadDataChange; -import liquibase.database.Database; -import liquibase.database.core.*; -import liquibase.datatype.DataTypeInfo; -import liquibase.datatype.DatabaseDataType; -import liquibase.datatype.LiquibaseDataType; -import liquibase.exception.UnexpectedLiquibaseException; -import liquibase.statement.DatabaseFunction; -import liquibase.util.StringUtil; - -import java.util.Locale; - -@DataTypeInfo(name = "boolean", aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, minParameters = 0, maxParameters = 0, priority = LiquibaseDataType.PRIORITY_DEFAULT) -public class BooleanType extends LiquibaseDataType { - - @Override - public DatabaseDataType toDatabaseDataType(Database database) { - String originalDefinition = StringUtil.trimToEmpty(getRawDefinition()); -// if ((database instanceof Firebird3Database)) { -// return new DatabaseDataType("BOOLEAN"); -// } - - if ((database instanceof AbstractDb2Database) || (database instanceof FirebirdDatabase)) { - return new DatabaseDataType("SMALLINT"); - } else if (database instanceof MSSQLDatabase) { - return new DatabaseDataType(database.escapeDataTypeName("bit")); - } else if (database instanceof MySQLDatabase) { - if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) { - return new DatabaseDataType("BIT", getParameters()); - } - return new DatabaseDataType("BIT", 1); - } else if (database instanceof OracleDatabase) { - return new DatabaseDataType("NUMBER", 1); - } else if ((database instanceof SybaseASADatabase) || (database instanceof SybaseDatabase)) { - return new DatabaseDataType("BIT"); - } else if (database instanceof DerbyDatabase) { - if (((DerbyDatabase) database).supportsBooleanDataType()) { - return new DatabaseDataType("BOOLEAN"); - } else { - return new DatabaseDataType("SMALLINT"); - } - } else if (database.getClass().isAssignableFrom(DB2Database.class)) { - if (((DB2Database) database).supportsBooleanDataType()) - return new DatabaseDataType("BOOLEAN"); - else - return new DatabaseDataType("SMALLINT"); - } else if (database instanceof HsqlDatabase) { - return new DatabaseDataType("BOOLEAN"); - } else if (database instanceof PostgresDatabase) { - if (originalDefinition.toLowerCase(Locale.US).startsWith("bit")) { - return new DatabaseDataType("BIT", getParameters()); - } - } else if(database instanceof DmDatabase) { - return new DatabaseDataType("bit"); - } - - return super.toDatabaseDataType(database); - } - - @Override - public String objectToSql(Object value, Database database) { - if ((value == null) || "null".equals(value.toString().toLowerCase(Locale.US))) { - return null; - } - - String returnValue; - if (value instanceof String) { - value = ((String) value).replaceAll("'", ""); - if ("true".equals(((String) value).toLowerCase(Locale.US)) || "1".equals(value) || "b'1'".equals(((String) value).toLowerCase(Locale.US)) || "t".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getTrueBooleanValue(database).toLowerCase(Locale.US))) { - returnValue = this.getTrueBooleanValue(database); - } else if ("false".equals(((String) value).toLowerCase(Locale.US)) || "0".equals(value) || "b'0'".equals( - ((String) value).toLowerCase(Locale.US)) || "f".equals(((String) value).toLowerCase(Locale.US)) || ((String) value).toLowerCase(Locale.US).equals(this.getFalseBooleanValue(database).toLowerCase(Locale.US))) { - returnValue = this.getFalseBooleanValue(database); - } else { - throw new UnexpectedLiquibaseException("Unknown boolean value: " + value); - } - } else if (value instanceof Long) { - if (Long.valueOf(1).equals(value)) { - returnValue = this.getTrueBooleanValue(database); - } else { - returnValue = this.getFalseBooleanValue(database); - } - } else if (value instanceof Number) { - if (value.equals(1) || "1".equals(value.toString()) || "1.0".equals(value.toString())) { - returnValue = this.getTrueBooleanValue(database); - } else { - returnValue = this.getFalseBooleanValue(database); - } - } else if (value instanceof DatabaseFunction) { - return value.toString(); - } else if (value instanceof Boolean) { - if (((Boolean) value)) { - returnValue = this.getTrueBooleanValue(database); - } else { - returnValue = this.getFalseBooleanValue(database); - } - } else { - throw new UnexpectedLiquibaseException("Cannot convert type " + value.getClass() + " to a boolean value"); - } - - return returnValue; - } - - protected boolean isNumericBoolean(Database database) { - if (database instanceof DerbyDatabase) { - return !((DerbyDatabase) database).supportsBooleanDataType(); - } else if (database.getClass().isAssignableFrom(DB2Database.class)) { - return !((DB2Database) database).supportsBooleanDataType(); - } - return (database instanceof Db2zDatabase) || (database instanceof DB2Database) || (database instanceof FirebirdDatabase) || (database instanceof - MSSQLDatabase) || (database instanceof MySQLDatabase) || (database instanceof OracleDatabase) || - (database instanceof SQLiteDatabase) || (database instanceof SybaseASADatabase) || (database instanceof - SybaseDatabase) || (database instanceof DmDatabase); - } - - /** - * The database-specific value to use for "false" "boolean" columns. - */ - public String getFalseBooleanValue(Database database) { - if (isNumericBoolean(database)) { - return "0"; - } - if (database instanceof InformixDatabase) { - return "'f'"; - } - return "FALSE"; - } - - /** - * The database-specific value to use for "true" "boolean" columns. - */ - public String getTrueBooleanValue(Database database) { - if (isNumericBoolean(database)) { - return "1"; - } - if (database instanceof InformixDatabase) { - return "'t'"; - } - return "TRUE"; - } - - @Override - public LoadDataChange.LOAD_DATA_TYPE getLoadTypeName() { - return LoadDataChange.LOAD_DATA_TYPE.BOOLEAN; - } -} diff --git a/zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java b/zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java deleted file mode 100644 index 7f66250..0000000 --- a/zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java +++ /dev/null @@ -1,32 +0,0 @@ -package liquibase.datatype.core; - -import liquibase.database.Database; -import liquibase.database.core.DmDatabase; -import liquibase.datatype.DataTypeInfo; -import liquibase.datatype.DatabaseDataType; - -@DataTypeInfo( - name = "boolean", - aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, - minParameters = 0, - maxParameters = 0, - priority = 2 -) -public class DmBooleanType extends BooleanType { - - @Override - public boolean supports(Database database) { - if (database instanceof DmDatabase) { - return true; - } - return super.supports(database); - } - - @Override - public DatabaseDataType toDatabaseDataType(Database database) { - if (database instanceof DmDatabase) { - return new DatabaseDataType("NUMBER", 1); - } - return super.toDatabaseDataType(database); - } -} diff --git a/zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java b/zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java deleted file mode 100644 index 1e0a40e..0000000 --- a/zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java +++ /dev/null @@ -1,1957 +0,0 @@ -package liquibase.snapshot; - -import liquibase.CatalogAndSchema; -import liquibase.Scope; -import liquibase.database.AbstractJdbcDatabase; -import liquibase.database.Database; -import liquibase.database.DatabaseConnection; -import liquibase.database.LiquibaseTableNamesFactory; -import liquibase.database.core.*; -import liquibase.database.jvm.JdbcConnection; -import liquibase.exception.DatabaseException; -import liquibase.executor.jvm.ColumnMapRowMapper; -import liquibase.executor.jvm.RowMapperNotNullConstraintsResultSetExtractor; -import liquibase.structure.DatabaseObject; -import liquibase.structure.core.Catalog; -import liquibase.structure.core.Schema; -import liquibase.structure.core.Table; -import liquibase.structure.core.View; -import liquibase.util.JdbcUtil; -import liquibase.util.StringUtil; - -import java.sql.*; -import java.util.*; - -public class JdbcDatabaseSnapshot extends DatabaseSnapshot { - - private boolean warnedAboutDbaRecycleBin; - private static final boolean ignoreWarnAboutDbaRecycleBin = Boolean.getBoolean("liquibase.ignoreRecycleBinWarning"); - - private CachingDatabaseMetaData cachingDatabaseMetaData; - - private Map cachedExpressionMap = null; - - private Set userDefinedTypes; - - public JdbcDatabaseSnapshot(DatabaseObject[] examples, Database database, SnapshotControl snapshotControl) throws DatabaseException, InvalidExampleException { - super(examples, database, snapshotControl); - } - - public JdbcDatabaseSnapshot(DatabaseObject[] examples, Database database) throws DatabaseException, InvalidExampleException { - super(examples, database); - } - - public CachingDatabaseMetaData getMetaDataFromCache() throws SQLException { - if (cachingDatabaseMetaData == null) { - DatabaseMetaData databaseMetaData = null; - if (getDatabase().getConnection() != null) { - databaseMetaData = ((JdbcConnection) getDatabase().getConnection()).getUnderlyingConnection().getMetaData(); - } - - cachingDatabaseMetaData = new CachingDatabaseMetaData(this.getDatabase(), databaseMetaData); - } - return cachingDatabaseMetaData; - } - - public class CachingDatabaseMetaData { - private static final String SQL_FILTER_MATCH_ALL = "%"; - private final DatabaseMetaData databaseMetaData; - private final Database database; - - public CachingDatabaseMetaData(Database database, DatabaseMetaData metaData) { - this.databaseMetaData = metaData; - this.database = database; - } - - public java.sql.DatabaseMetaData getDatabaseMetaData() { - return databaseMetaData; - } - - public List getForeignKeys(final String catalogName, final String schemaName, final String tableName, - final String fkName) throws DatabaseException { - ForeignKeysResultSetCache foreignKeysResultSetCache = new ForeignKeysResultSetCache(database, catalogName, schemaName, tableName, fkName); - ResultSetCache importedKeys = getResultSetCache("getImportedKeys"); - importedKeys.setBulkTracking(!(database instanceof MSSQLDatabase)); - - return importedKeys.get(foreignKeysResultSetCache); - } - - public List getIndexInfo(final String catalogName, final String schemaName, final String tableName, final String indexName) throws DatabaseException, SQLException { - - return getResultSetCache("getIndexInfo").get(new ResultSetCache.UnionResultSetExtractor(database) { - - public boolean isBulkFetchMode; - - @Override - public ResultSetCache.RowData rowKeyParameters(CachedRow row) { - return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME"), row.getString("INDEX_NAME")); - } - - @Override - public ResultSetCache.RowData wantedKeyParameters() { - return new ResultSetCache.RowData(catalogName, schemaName, database, tableName, indexName); - } - - @Override - public boolean bulkContainsSchema(String schemaKey) { - return getAllCatalogsStringScratchData() != null && database instanceof OracleDatabase; - } - - @Override - public String getSchemaKey(CachedRow row) { - return row.getString("TABLE_SCHEM"); - } - - @Override - public List fastFetch() throws SQLException, DatabaseException { - List returnList = new ArrayList<>(); - - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - if (database instanceof OracleDatabase) { - warnAboutDbaRecycleBin(); - - //oracle getIndexInfo is buggy and slow. See Issue 1824548 and http://forums.oracle.com/forums/thread.jspa?messageID=578383򍍏 - String sql = - "SELECT " + - "c.INDEX_NAME, " + - "3 AS TYPE, " + - "c.TABLE_OWNER AS TABLE_SCHEM, " + - "c.TABLE_NAME, " + - "c.COLUMN_NAME, " + - "c.COLUMN_POSITION AS ORDINAL_POSITION, " + - "NULL AS FILTER_CONDITION, " + - "c.INDEX_OWNER, " + - "CASE I.UNIQUENESS WHEN 'UNIQUE' THEN 0 ELSE 1 END AS NON_UNIQUE, " + - "CASE c.DESCEND WHEN 'Y' THEN 'D' WHEN 'DESC' THEN 'D' WHEN 'N' THEN 'A' WHEN 'ASC' THEN 'A' END AS ASC_OR_DESC, " + - "CASE WHEN tablespace_name = (SELECT default_tablespace FROM user_users) " + - "THEN NULL ELSE tablespace_name END AS tablespace_name " + - "FROM ALL_IND_COLUMNS c " + - "JOIN ALL_INDEXES i ON i.owner=c.index_owner AND i.index_name = c.index_name and i.table_owner = c.table_owner " + - "LEFT OUTER JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=c.table_name "; - if (!isBulkFetchMode || getAllCatalogsStringScratchData() == null) { - sql += "WHERE c.TABLE_OWNER = '" + database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class) + "' "; - } else { - sql += "WHERE c.TABLE_OWNER IN ('" + database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class) + "', " + getAllCatalogsStringScratchData() + ")"; - } - sql += "AND i.OWNER = c.TABLE_OWNER " + - "AND d.object_name IS NULL "; - - - if (!isBulkFetchMode && (tableName != null)) { - sql += " AND c.TABLE_NAME='" + tableName + "'"; - } - - if (!isBulkFetchMode && (indexName != null)) { - sql += " AND c.INDEX_NAME='" + indexName + "'"; - } - - sql += " ORDER BY c.INDEX_NAME, ORDINAL_POSITION"; - - returnList.addAll(setIndexExpressions(executeAndExtract(sql, database))); - } else if (database instanceof MSSQLDatabase) { - String tableCat = "original_db_name()"; - - if (9 <= database.getDatabaseMajorVersion()) { - tableCat = "db_name()"; - } - //fetch additional index info - String sql = "SELECT " + - tableCat + " as TABLE_CAT, " + - "object_schema_name(i.object_id) as TABLE_SCHEM, " + - "object_name(i.object_id) as TABLE_NAME, " + - "CASE is_unique WHEN 1 then 0 else 1 end as NON_UNIQUE, " + - "object_name(i.object_id) as INDEX_QUALIFIER, " + - "i.name as INDEX_NAME, " + - "case i.type when 1 then 1 ELSE 3 end as TYPE, " + - "key_ordinal as ORDINAL_POSITION, " + - "COL_NAME(c.object_id,c.column_id) AS COLUMN_NAME, " + - "case is_descending_key when 0 then 'A' else 'D' end as ASC_OR_DESC, " + - "null as CARDINALITY, " + - "null as PAGES, " + - "i.filter_definition as FILTER_CONDITION, " + - "o.type AS INTERNAL_OBJECT_TYPE, " + - "i.*, " + - "c.*, " + - "s.* " + - "FROM sys.indexes i " + - "join sys.index_columns c on i.object_id=c.object_id and i.index_id=c.index_id " + - "join sys.stats s on i.object_id=s.object_id and i.name=s.name " + - "join sys.objects o on i.object_id=o.object_id " + - "WHERE object_schema_name(i.object_id)='" + database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class) + "'"; - - if (!isBulkFetchMode && (tableName != null)) { - sql += " AND object_name(i.object_id)='" + database.escapeStringForDatabase(tableName) + "'"; - } - - if (!isBulkFetchMode && (indexName != null)) { - sql += " AND i.name='" + database.escapeStringForDatabase(indexName) + "'"; - } - - sql += "ORDER BY i.object_id, i.index_id, c.key_ordinal"; - - returnList.addAll(executeAndExtract(sql, database)); - - } else if (database instanceof Db2zDatabase) { - List parameters = new ArrayList<>(3); - String sql = "SELECT i.CREATOR AS TABLE_SCHEM, " + - "i.TBNAME AS TABLE_NAME, " + - "i.NAME AS INDEX_NAME, " + - "3 AS TYPE, " + - "k.COLNAME AS COLUMN_NAME, " + - "k.COLSEQ AS ORDINAL_POSITION, " + - "CASE UNIQUERULE WHEN 'D' then 1 else 0 end as NON_UNIQUE, " + - "k.ORDERING AS ORDER, " + - "i.CREATOR AS INDEX_QUALIFIER " + - "FROM SYSIBM.SYSKEYS k " + - "JOIN SYSIBM.SYSINDEXES i " + - "ON k.IXNAME = i.NAME " + - "AND k.IXCREATOR = i.CREATOR " + - "WHERE i.CREATOR = ?"; - parameters.add(database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class)); - if (!isBulkFetchMode && tableName != null) { - sql += " AND i.TBNAME = ?"; - parameters.add(database.escapeStringForDatabase(tableName)); - } - - if (!isBulkFetchMode && indexName != null) { - sql += " AND i.NAME = ?"; - parameters.add(database.escapeStringForDatabase(indexName)); - } - - sql += "ORDER BY i.NAME, k.COLSEQ"; - - returnList.addAll(executeAndExtract(database, sql, parameters.toArray())); - } else if (!(database instanceof MariaDBDatabase) && database instanceof MySQLDatabase) { - - //mysql 8.0.13 introduced support for indexes on `lower(first_name)` which comes back in an "expression" column - String filterConditionValue = "NULL"; - if (database.getDatabaseMajorVersion() > 8 || (database.getDatabaseMajorVersion() == 8 && ((MySQLDatabase) database).getDatabasePatchVersion() >= 13)) { - filterConditionValue = "EXPRESSION"; - } - - StringBuilder sql = new StringBuilder("SELECT TABLE_CATALOG AS TABLE_CAT, TABLE_SCHEMA AS TABLE_SCHEM,"); - sql.append(" TABLE_NAME, NON_UNIQUE, NULL AS INDEX_QUALIFIER, INDEX_NAME,"); - sql.append(DatabaseMetaData.tableIndexOther); - sql.append(" AS TYPE, SEQ_IN_INDEX AS ORDINAL_POSITION, COLUMN_NAME,"); - sql.append("COLLATION AS ASC_OR_DESC, CARDINALITY, 0 AS PAGES, " + filterConditionValue + " AS FILTER_CONDITION FROM INFORMATION_SCHEMA.STATISTICS WHERE"); - sql.append(" TABLE_SCHEMA = '").append(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)).append("'"); - - if (!isBulkFetchMode && tableName != null) { - sql.append(" AND TABLE_NAME = '").append(database.escapeStringForDatabase(tableName)).append("'"); - } - - if (!isBulkFetchMode && indexName != null) { - sql.append(" AND INDEX_NAME='").append(database.escapeStringForDatabase(indexName)).append("'"); - } - - sql.append("ORDER BY NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX"); - - returnList.addAll(executeAndExtract(sql.toString(), database)); - } else { - /* - * If we do not know in which table to look for the index, things get a little bit ugly. - * First, we get a collection of all tables within the catalogAndSchema, then iterate through - * them until we (hopefully) find the index we are looking for. - */ - List tables = new ArrayList<>(); - if (tableName == null) { - // Build a list of all candidate tables in the catalog/schema that might contain the index - for (CachedRow row : getTables(((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), null)) { - tables.add(row.getString("TABLE_NAME")); - } - } else { - tables.add(tableName); - } - - // Iterate through all the candidate tables and try to find the index. - for (String tableName1 : tables) { - ResultSet rs = databaseMetaData.getIndexInfo( - ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), - ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), - tableName1, - false, - true); - List rows = extract(rs, (database instanceof InformixDatabase)); - returnList.addAll(rows); - } - } - - return returnList; - } - - private List setIndexExpressions(List c) throws DatabaseException, SQLException { - Map expressionMap = getCachedExpressionMap(); - c.forEach(row -> { - row.set("FILTER_CONDITION", null); - String key = row.getString("INDEX_OWNER") + "::" + row.getString("INDEX_NAME") + "::" + - row.getInt("ORDINAL_POSITION"); - CachedRow fromMap = expressionMap.get(key); - if (fromMap != null) { - row.set("FILTER_CONDITION", fromMap.get("COLUMN_EXPRESSION")); - } - }); - return c; - } - - private Map getCachedExpressionMap() throws DatabaseException, SQLException { - if (cachedExpressionMap != null) { - return cachedExpressionMap; - } - String expSql = "SELECT e.column_expression, e.index_owner, e.index_name, e.column_position FROM all_ind_expressions e"; - List ec = executeAndExtract(expSql, database); - cachedExpressionMap = new HashMap<>(); - ec.forEach(row -> { - String key = row.getString("INDEX_OWNER") + "::" + row.getString("INDEX_NAME") + "::" + - row.getInt("COLUMN_POSITION"); - cachedExpressionMap.put(key, row); - }); - return cachedExpressionMap; - } - - @Override - public List bulkFetch() throws SQLException, DatabaseException { - this.isBulkFetchMode = true; - return fastFetch(); - } - - @Override - protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { - if (database instanceof OracleDatabase || database instanceof MSSQLDatabase) { - return JdbcDatabaseSnapshot.this.getAllCatalogsStringScratchData() != null || (tableName == null && indexName == null) || super.shouldBulkSelect(schemaKey, resultSetCache); - } - return false; - } - }); - } - - - protected void warnAboutDbaRecycleBin() { - if (!ignoreWarnAboutDbaRecycleBin && !warnedAboutDbaRecycleBin && !(((OracleDatabase) database).canAccessDbaRecycleBin())) { - Scope.getCurrentScope().getLog(getClass()).warning(((OracleDatabase) database).getDbaRecycleBinWarning()); - warnedAboutDbaRecycleBin = true; - } - } - - /** - * Return the columns for the given catalog, schema, table, and column. - */ - public List getColumns(final String catalogName, final String schemaName, final String tableName, final String columnName) throws SQLException, DatabaseException { - - if ((database instanceof MSSQLDatabase) && (userDefinedTypes == null)) { - userDefinedTypes = new HashSet<>(); - DatabaseConnection databaseConnection = database.getConnection(); - if (databaseConnection instanceof JdbcConnection) { - Statement stmt = null; - ResultSet resultSet = null; - try { - stmt = ((JdbcConnection) databaseConnection).getUnderlyingConnection().createStatement(); - resultSet = stmt.executeQuery("select name from " + (catalogName == null ? "" : "[" + catalogName + "].") + "sys.types where is_user_defined=1"); - while (resultSet.next()) { - userDefinedTypes.add(resultSet.getString("name").toLowerCase()); - } - } finally { - JdbcUtil.close(resultSet, stmt); - } - } - } - GetColumnResultSetCache getColumnResultSetCache = new GetColumnResultSetCache(database, catalogName, - schemaName, tableName, columnName); - return getResultSetCache("getColumns").get(getColumnResultSetCache); - } - - /** - * Return the NotNullConstraints for the given catalog, schema, table, and column. - */ - public List getNotNullConst(final String catalogName, final String schemaName, - final String tableName) throws DatabaseException { - if (!(database instanceof OracleDatabase)) { - return Collections.emptyList(); - } - GetNotNullConstraintsResultSetCache getNotNullConstraintsResultSetCache = new GetNotNullConstraintsResultSetCache(database, catalogName, - schemaName, tableName); - return getResultSetCache("getNotNullConst").get(getNotNullConstraintsResultSetCache); - } - - private class GetColumnResultSetCache extends ResultSetCache.SingleResultSetExtractor { - final String catalogName; - final String schemaName; - final String tableName; - final String columnName; - - private GetColumnResultSetCache(Database database, String catalogName, String schemaName, String tableName, String columnName) { - super(database); - this.catalogName = catalogName; - this.schemaName = schemaName; - this.tableName = tableName; - this.columnName = columnName; - } - - @Override - public ResultSetCache.RowData rowKeyParameters(CachedRow row) { - return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME"), row.getString("COLUMN_NAME")); - } - - @Override - public ResultSetCache.RowData wantedKeyParameters() { - return new ResultSetCache.RowData(catalogName, schemaName, database, tableName, columnName); - } - - @Override - public boolean bulkContainsSchema(String schemaKey) { - String catalogs = getAllCatalogsStringScratchData(); - return catalogs != null && schemaKey != null - && catalogs.contains("'" + schemaKey.toUpperCase() + "'") - && database instanceof OracleDatabase; - } - - @Override - public String getSchemaKey(CachedRow row) { - return row.getString("TABLE_SCHEM"); - } - - @Override - protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { - LiquibaseTableNamesFactory liquibaseTableNamesFactory = Scope.getCurrentScope().getSingleton(LiquibaseTableNamesFactory.class); - List liquibaseTableNames = liquibaseTableNamesFactory.getLiquibaseTableNames(database); - return liquibaseTableNames.stream().noneMatch(tableName::equalsIgnoreCase); - } - - @Override - public List fastFetchQuery() throws SQLException, DatabaseException { - if (database instanceof OracleDatabase) { - return oracleQuery(false); - } else if (database instanceof MSSQLDatabase) { - return mssqlQuery(false); - } - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - try { - List returnList = - extract( - databaseMetaData.getColumns( - ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), - escapeForLike(((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), database), - escapeForLike(tableName, database), - SQL_FILTER_MATCH_ALL) - ); - // - // IF MARIADB OR SQL ANYWHERE - // Query to get actual data types and then map each column to its CachedRow - // - determineActualDataTypes(returnList, tableName); - return returnList; - } catch (SQLException e) { - if (shouldReturnEmptyColumns(e)) { //view with table already dropped. Act like it has no columns. - return new ArrayList<>(); - } else { - throw e; - } - } - } - - @Override - public List bulkFetchQuery() throws SQLException, DatabaseException { - if (database instanceof OracleDatabase) { - return oracleQuery(true); - } else if (database instanceof MSSQLDatabase) { - return mssqlQuery(true); - } - - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - try { - List returnList = - extract(databaseMetaData.getColumns(((AbstractJdbcDatabase) database) - .getJdbcCatalogName(catalogAndSchema), - escapeForLike(((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), database), - SQL_FILTER_MATCH_ALL, SQL_FILTER_MATCH_ALL)); - // - // IF MARIADB OR SQL ANYWHERE - // Query to get actual data types and then map each column to its CachedRow - // - determineActualDataTypes(returnList, null); - return returnList; - } catch (SQLException e) { - if (shouldReturnEmptyColumns(e)) { - return new ArrayList<>(); - } else { - throw e; - } - } - } - - // - // For MariaDB, query for the data type column so that we can correctly - // set the DATETIME(6) type if specified - // - // For SQL Anywhere, query for the scale column so we can correctly - // set the size unit - // - private void determineActualDataTypes(List returnList, String tableName) throws SQLException { - // - // If not MariaDB / SQL Anywhere then just return - // - if (!(database instanceof MariaDBDatabase || database instanceof SybaseASADatabase)) { - return; - } - - if (database instanceof SybaseASADatabase) { - // - // Query for actual data type for column. The actual SYSTABCOL.scale column value is - // not reported by the DatabaseMetadata.getColumns() query for CHAR-limited (in contrast - // to BYTE-limited) columns, and it is needed to capture the kind if limitation. - // The actual SYSTABCOL.column_type is not reported by the DatabaseMetadata.getColumns() - // query as the IS_GENERATEDCOLUMN columns is missing in the result set, and it is needed to - // capture the kind of column (regular or computed). - // - // See https://help.sap.com/docs/SAP_SQL_Anywhere/93079d4ba8e44920ae63ffb4def91f5b/3beaa3956c5f1014883cb0c3e3559cc9.html. - // - String selectStatement = - "SELECT table_name, column_name, scale, column_type FROM SYSTABCOL KEY JOIN SYSTAB KEY JOIN SYSUSER " + - "WHERE user_name = ? AND ? IS NULL OR table_name = ?"; - Connection underlyingConnection = ((JdbcConnection) database.getConnection()).getUnderlyingConnection(); - try (PreparedStatement stmt = underlyingConnection.prepareStatement(selectStatement)) { - stmt.setString(1, schemaName); - stmt.setString(2, tableName); - stmt.setString(3, tableName); - try (ResultSet columnSelectRS = stmt.executeQuery()) { - while (columnSelectRS.next()) { - String selectedTableName = columnSelectRS.getString("table_name"); - String selectedColumnName = columnSelectRS.getString("column_name"); - int selectedScale = columnSelectRS.getInt("scale"); - String selectedColumnType = columnSelectRS.getString("column_type"); - for (CachedRow row : returnList) { - String rowTableName = row.getString("TABLE_NAME"); - String rowColumnName = row.getString("COLUMN_NAME"); - if (rowTableName.equalsIgnoreCase(selectedTableName) && - rowColumnName.equalsIgnoreCase(selectedColumnName)) { - int rowDataType = row.getInt("DATA_TYPE"); - if (rowDataType == Types.VARCHAR || rowDataType == Types.CHAR) { - row.set("scale", selectedScale); - } - row.set("IS_GENERATEDCOLUMN", "C".equals(selectedColumnType) ? "YES" : "NO"); - break; - } - } - } - } - } catch (SQLException sqle) { - throw new RuntimeException(sqle); - // - // Do not stop - // - } - return; - } - - // - // Query for actual data type for column. The actual DATA_TYPE column string is - // not returned by the DatabaseMetadata.getColumns() query, and it is needed - // to capture DATETIME() data types. - // - StringBuilder selectStatement = new StringBuilder( - "SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ?"); - if(tableName != null) { - selectStatement.append(" AND TABLE_NAME = ?"); - } - Connection underlyingConnection = ((JdbcConnection) database.getConnection()).getUnderlyingConnection(); - PreparedStatement statement = underlyingConnection.prepareStatement(selectStatement.toString()); - statement.setString(1, schemaName); - if (tableName != null) { - statement.setString(2, tableName); - } - try { - ResultSet columnSelectRS = statement.executeQuery(selectStatement.toString()); - // - // Iterate the result set from the query and match the rows - // to the rows that were returned by getColumns() in order - // to assign the actual DATA_TYPE string to the appropriate row. - // - while (columnSelectRS.next()) { - String selectedTableName = columnSelectRS.getString("TABLE_NAME"); - String selectedColumnName = columnSelectRS.getString("COLUMN_NAME"); - String actualDataType = columnSelectRS.getString("DATA_TYPE"); - for (CachedRow row : returnList) { - String rowTableName = row.getString("TABLE_NAME"); - String rowColumnName = row.getString("COLUMN_NAME"); - String rowTypeName = row.getString("TYPE_NAME"); - int rowDataType = row.getInt("DATA_TYPE"); - if (rowTableName.equalsIgnoreCase(selectedTableName) && - rowColumnName.equalsIgnoreCase(selectedColumnName) && - rowTypeName.equalsIgnoreCase("datetime") && - rowDataType == Types.OTHER && - !rowTypeName.equalsIgnoreCase(actualDataType)) { - row.set("TYPE_NAME", actualDataType); - row.set("DATA_TYPE", Types.TIMESTAMP); - break; - } - } - } - } catch (SQLException sqle) { - // - // Do not stop - // - } - finally { - JdbcUtil.closeStatement(statement); - } - } - - protected boolean shouldReturnEmptyColumns(SQLException e) { - return e.getMessage().contains("references invalid table"); //view with table already dropped. Act like it has no columns. - } - - protected List oracleQuery(boolean bulk) throws DatabaseException, SQLException { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - boolean collectIdentityData = database.getDatabaseMajorVersion() >= OracleDatabase.ORACLE_12C_MAJOR_VERSION; - - String sql = "select NULL AS TABLE_CAT, OWNER AS TABLE_SCHEM, 'NO' as IS_AUTOINCREMENT, cc.COMMENTS AS REMARKS," + - "OWNER, TABLE_NAME, COLUMN_NAME, DATA_TYPE AS DATA_TYPE_NAME, DATA_TYPE_MOD, DATA_TYPE_OWNER, " + - // note: oracle reports DATA_LENGTH=4*CHAR_LENGTH when using VARCHAR( CHAR ), thus BYTEs - "DECODE (c.data_type, 'CHAR', 1, 'VARCHAR2', 12, 'NUMBER', 3, 'LONG', -1, 'DATE', " + "93" + ", 'RAW', -3, 'LONG RAW', -4, 'BLOB', 2004, 'CLOB', 2005, 'BFILE', -13, 'FLOAT', 6, 'TIMESTAMP(6)', 93, 'TIMESTAMP(6) WITH TIME ZONE', -101, 'TIMESTAMP(6) WITH LOCAL TIME ZONE', -102, 'INTERVAL YEAR(2) TO MONTH', -103, 'INTERVAL DAY(2) TO SECOND(6)', -104, 'BINARY_FLOAT', 100, 'BINARY_DOUBLE', 101, 'XMLTYPE', 2009, 1111) AS data_type, " + - "DECODE( CHAR_USED, 'C',CHAR_LENGTH, DATA_LENGTH ) as DATA_LENGTH, " + - "DATA_PRECISION, DATA_SCALE, NULLABLE, COLUMN_ID as ORDINAL_POSITION, DEFAULT_LENGTH, " + - "DATA_DEFAULT, " + - "NUM_BUCKETS, CHARACTER_SET_NAME, " + - "CHAR_COL_DECL_LENGTH, CHAR_LENGTH, " + - "CHAR_USED, VIRTUAL_COLUMN "; - if (collectIdentityData) { - sql += ", DEFAULT_ON_NULL, IDENTITY_COLUMN, ic.GENERATION_TYPE "; - } - sql += "FROM ALL_TAB_COLS c " + - "JOIN ALL_COL_COMMENTS cc USING ( OWNER, TABLE_NAME, COLUMN_NAME ) "; - if (collectIdentityData) { - sql += "LEFT JOIN ALL_TAB_IDENTITY_COLS ic USING (OWNER, TABLE_NAME, COLUMN_NAME ) "; - } - if (!bulk || getAllCatalogsStringScratchData() == null) { - sql += "WHERE OWNER='" + jdbcSchemaName + "' AND hidden_column='NO'"; - } else { - sql += "WHERE OWNER IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ") AND hidden_column='NO'"; - } - - if (!bulk) { - if (tableName != null) { - sql += " AND TABLE_NAME='" + database.escapeStringForDatabase(tableName) + "'"; - } - if (columnName != null) { - sql += " AND COLUMN_NAME='" + database.escapeStringForDatabase(columnName) + "'"; - } - } - sql += " AND " + ((OracleDatabase) database).getSystemTableWhereClause("TABLE_NAME"); - sql += " ORDER BY OWNER, TABLE_NAME, c.COLUMN_ID"; - - return this.executeAndExtract(sql, database); - } - - - protected List mssqlQuery(boolean bulk) throws DatabaseException, SQLException { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - String databaseName = StringUtil.trimToNull(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)); - String dbIdParam; - String databasePrefix; - if (databaseName == null) { - databasePrefix = ""; - dbIdParam = ""; - } else { - dbIdParam = ", db_id('" + databaseName + "')"; - databasePrefix = "[" + databaseName + "]."; - } - - String sql = "select " + - "db_name(" + (databaseName == null ? "" : "db_id('" + databaseName + "')") + ") AS TABLE_CAT, " + - "object_schema_name(c.object_id" + dbIdParam + ") AS TABLE_SCHEM, " + - "object_name(c.object_id" + dbIdParam + ") AS TABLE_NAME, " + - "c.name AS COLUMN_NAME, " + - "is_filestream AS IS_FILESTREAM, " + - "is_rowguidcol AS IS_ROWGUIDCOL, " + - "CASE WHEN c.is_identity = 'true' THEN 'YES' ELSE 'NO' END as IS_AUTOINCREMENT, " + - "{REMARKS_COLUMN_PLACEHOLDER}" + - "t.name AS TYPE_NAME, " + - "dc.name as COLUMN_DEF_NAME, " + - "dc.definition as COLUMN_DEF, " + - // data type mapping from https://msdn.microsoft.com/en-us/library/ms378878(v=sql.110).aspx - "CASE t.name " + - "WHEN 'bigint' THEN " + java.sql.Types.BIGINT + " " + - "WHEN 'binary' THEN " + java.sql.Types.BINARY + " " + - "WHEN 'bit' THEN " + java.sql.Types.BIT + " " + - "WHEN 'char' THEN " + java.sql.Types.CHAR + " " + - "WHEN 'date' THEN " + java.sql.Types.DATE + " " + - "WHEN 'datetime' THEN " + java.sql.Types.TIMESTAMP + " " + - "WHEN 'datetime2' THEN " + java.sql.Types.TIMESTAMP + " " + - "WHEN 'datetimeoffset' THEN -155 " + - "WHEN 'decimal' THEN " + java.sql.Types.DECIMAL + " " + - "WHEN 'float' THEN " + java.sql.Types.DOUBLE + " " + - "WHEN 'image' THEN " + java.sql.Types.LONGVARBINARY + " " + - "WHEN 'int' THEN " + java.sql.Types.INTEGER + " " + - "WHEN 'money' THEN " + java.sql.Types.DECIMAL + " " + - "WHEN 'nchar' THEN " + java.sql.Types.NCHAR + " " + - "WHEN 'ntext' THEN " + java.sql.Types.LONGNVARCHAR + " " + - "WHEN 'numeric' THEN " + java.sql.Types.NUMERIC + " " + - "WHEN 'nvarchar' THEN " + java.sql.Types.NVARCHAR + " " + - "WHEN 'real' THEN " + Types.REAL + " " + - "WHEN 'smalldatetime' THEN " + java.sql.Types.TIMESTAMP + " " + - "WHEN 'smallint' THEN " + java.sql.Types.SMALLINT + " " + - "WHEN 'smallmoney' THEN " + java.sql.Types.DECIMAL + " " + - "WHEN 'text' THEN " + java.sql.Types.LONGVARCHAR + " " + - "WHEN 'time' THEN " + java.sql.Types.TIME + " " + - "WHEN 'timestamp' THEN " + java.sql.Types.BINARY + " " + - "WHEN 'tinyint' THEN " + java.sql.Types.TINYINT + " " + - "WHEN 'udt' THEN " + java.sql.Types.VARBINARY + " " + - "WHEN 'uniqueidentifier' THEN " + java.sql.Types.CHAR + " " + - "WHEN 'varbinary' THEN " + java.sql.Types.VARBINARY + " " + - "WHEN 'varbinary(max)' THEN " + java.sql.Types.VARBINARY + " " + - "WHEN 'varchar' THEN " + java.sql.Types.VARCHAR + " " + - "WHEN 'varchar(max)' THEN " + java.sql.Types.VARCHAR + " " + - "WHEN 'xml' THEN " + java.sql.Types.LONGVARCHAR + " " + - "WHEN 'LONGNVARCHAR' THEN " + java.sql.Types.SQLXML + " " + - "ELSE " + Types.OTHER + " END AS DATA_TYPE, " + - "CASE WHEN c.is_nullable = 'true' THEN 1 ELSE 0 END AS NULLABLE, " + - "10 as NUM_PREC_RADIX, " + - "c.column_id as ORDINAL_POSITION, " + - "c.scale as DECIMAL_DIGITS, " + - "c.max_length as COLUMN_SIZE, " + - "c.precision as DATA_PRECISION, " + - "c.is_computed as IS_COMPUTED " + - "FROM " + databasePrefix + "sys.columns c " + - "inner join " + databasePrefix + "sys.types t on c.user_type_id=t.user_type_id " + - "{REMARKS_JOIN_PLACEHOLDER}" + - "left outer join " + databasePrefix + "sys.default_constraints dc on dc.parent_column_id = c.column_id AND dc.parent_object_id=c.object_id AND type_desc='DEFAULT_CONSTRAINT' " + - "WHERE object_schema_name(c.object_id" + dbIdParam + ")='" + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema) + "'"; - - - if (!bulk) { - if (tableName != null) { - sql += " and object_name(c.object_id" + dbIdParam + ")='" + database.escapeStringForDatabase(tableName) + "'"; - } - if (columnName != null) { - sql += " and c.name='" + database.escapeStringForDatabase(columnName) + "'"; - } - } - sql += "order by object_schema_name(c.object_id" + dbIdParam + "), object_name(c.object_id" + dbIdParam + "), c.column_id"; - - - // sys.extended_properties is added to Azure on V12: https://feedback.azure.com/forums/217321-sql-database/suggestions/6549815-add-sys-extended-properties-for-meta-data-support - if ((!((MSSQLDatabase) database).isAzureDb()) // Either NOT AzureDB (=SQL Server 2008 or higher) - || (database.getDatabaseMajorVersion() >= 12)) { // or at least AzureDB v12 - // SQL Server 2005 or later - // https://technet.microsoft.com/en-us/library/ms177541.aspx - sql = sql.replace("{REMARKS_COLUMN_PLACEHOLDER}", "CAST([ep].[value] AS [nvarchar](MAX)) AS [REMARKS], "); - sql = sql.replace("{REMARKS_JOIN_PLACEHOLDER}", "left outer join " + databasePrefix + "[sys].[extended_properties] AS [ep] ON [ep].[class] = 1 " + - "AND [ep].[major_id] = c.object_id " + - "AND [ep].[minor_id] = column_id " + - "AND [ep].[name] = 'MS_Description' "); - } else { - sql = sql.replace("{REMARKS_COLUMN_PLACEHOLDER}", ""); - sql = sql.replace("{REMARKS_JOIN_PLACEHOLDER}", ""); - } - - List rows = this.executeAndExtract(sql, database); - - for (CachedRow row : rows) { - String typeName = row.getString("TYPE_NAME"); - if ("nvarchar".equals(typeName) || "nchar".equals(typeName)) { - Integer size = row.getInt("COLUMN_SIZE"); - if (size > 0) { - row.set("COLUMN_SIZE", size / 2); - } - } else if ((row.getInt("DATA_PRECISION") != null) && (row.getInt("DATA_PRECISION") > 0)) { - row.set("COLUMN_SIZE", row.getInt("DATA_PRECISION")); - } - } - - return rows; - } - - @Override - protected List extract(ResultSet resultSet, boolean informixIndexTrimHint) throws SQLException { - List rows = super.extract(resultSet, informixIndexTrimHint); - if ((database instanceof MSSQLDatabase) && !userDefinedTypes.isEmpty()) { //UDT types in MSSQL don't take parameters - for (CachedRow row : rows) { - String dataType = (String) row.get("TYPE_NAME"); - if (userDefinedTypes.contains(dataType.toLowerCase())) { - row.set("COLUMN_SIZE", null); - row.set("DECIMAL_DIGITS ", null); - } - } - } - return rows; - } - } - - private class ForeignKeysResultSetCache extends ResultSetCache.UnionResultSetExtractor { - final String catalogName; - final String schemaName; - final String tableName; - final String fkName; - - private ForeignKeysResultSetCache(Database database, String catalogName, String schemaName, String tableName, String fkName) { - super(database); - this.catalogName = catalogName; - this.schemaName = schemaName; - this.tableName = tableName; - this.fkName = fkName; - } - - @Override - public ResultSetCache.RowData rowKeyParameters(CachedRow row) { - return new ResultSetCache.RowData(row.getString("FKTABLE_CAT"), row.getString("FKTABLE_SCHEM"), database, row.getString("FKTABLE_NAME"), row.getString("FK_NAME")); - } - - @Override - public ResultSetCache.RowData wantedKeyParameters() { - return new ResultSetCache.RowData(catalogName, schemaName, database, tableName, fkName); - } - - @Override - public boolean bulkContainsSchema(String schemaKey) { - return database instanceof OracleDatabase; - } - - @Override - public String getSchemaKey(CachedRow row) { - return row.getString("FKTABLE_SCHEM"); - } - - @Override - public List fastFetch() throws SQLException, DatabaseException { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - String jdbcCatalogName = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); - String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - - if (database instanceof DB2Database) { - if (database.getDatabaseProductName().startsWith("DB2 UDB for AS/400")) { - return executeAndExtract(getDB2ForAs400Sql(jdbcSchemaName, tableName), database); - } - return querytDB2Luw(jdbcSchemaName, tableName); - } else if (database instanceof Db2zDatabase) { - return queryDb2Zos(catalogAndSchema, tableName); - } else { - List tables = new ArrayList<>(); - if (tableName == null) { - for (CachedRow row : getTables(jdbcCatalogName, jdbcSchemaName, null)) { - tables.add(row.getString("TABLE_NAME")); - } - } else { - tables.add(tableName); - } - - List returnList = new ArrayList<>(); - for (String foundTable : tables) { - if (database instanceof OracleDatabase) { - throw new RuntimeException("Should have bulk selected"); - } else { - returnList.addAll(extract(databaseMetaData.getImportedKeys(jdbcCatalogName, jdbcSchemaName, foundTable))); - } - } - - return returnList; - } - } - - @Override - public List bulkFetch() throws SQLException, DatabaseException { - if (database instanceof OracleDatabase) { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - String sql = getOracleSql(jdbcSchemaName); - return executeAndExtract(sql, database); - } else if (database instanceof DB2Database) { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - if (database.getDatabaseProductName().startsWith("DB2 UDB for AS/400")) { - return executeAndExtract(getDB2ForAs400Sql(jdbcSchemaName, null), database); - } - return querytDB2Luw(jdbcSchemaName, null); - } else if (database instanceof Db2zDatabase) { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - return queryDb2Zos(catalogAndSchema, null); - } else if (database instanceof MSSQLDatabase) { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - String sql = getMSSQLSql(jdbcSchemaName, tableName); - return executeAndExtract(sql, database); - } else { - throw new RuntimeException("Cannot bulk select"); - } - } - - protected String getOracleSql(String jdbcSchemaName) { - String sql = "SELECT /*+rule*/" + - " NULL AS pktable_cat, " + - " p.owner as pktable_schem, " + - " p.table_name as pktable_name, " + - " pc.column_name as pkcolumn_name, " + - " NULL as fktable_cat, " + - " f.owner as fktable_schem, " + - " f.table_name as fktable_name, " + - " fc.column_name as fkcolumn_name, " + - " fc.position as key_seq, " + - " NULL as update_rule, " + - " decode (f.delete_rule, 'CASCADE', 0, 'SET NULL', 2, 1) as delete_rule, " + - " f.constraint_name as fk_name, " + - " p.constraint_name as pk_name, " + - " decode(f.deferrable, 'DEFERRABLE', 5, 'NOT DEFERRABLE', 7, 'DEFERRED', 6) deferrability, " + - " f.validated as fk_validate " + - "FROM " + - "all_cons_columns pc " + - "INNER JOIN all_constraints p " + - "ON pc.owner = p.owner " + - "AND pc.constraint_name = p.constraint_name " + - "INNER JOIN all_constraints f " + - "ON pc.owner = f.r_owner " + - "AND pc.constraint_name = f.r_constraint_name " + - "INNER JOIN all_cons_columns fc " + - "ON fc.owner = f.owner " + - "AND fc.constraint_name = f.constraint_name " + - "AND fc.position = pc.position "; - if (getAllCatalogsStringScratchData() == null) { - sql += "WHERE f.owner = '" + jdbcSchemaName + "' "; - } else { - sql += "WHERE f.owner IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ") "; - } - sql += "AND p.constraint_type in ('P', 'U') " + - "AND f.constraint_type = 'R' " + - "AND p.table_name NOT LIKE 'BIN$%' " + - "ORDER BY fktable_schem, fktable_name, key_seq"; - return sql; - } - - protected String getMSSQLSql(String jdbcSchemaName, String tableName) { - //comes from select object_definition(object_id('sp_fkeys')) - return "select " + - "convert(sysname,db_name()) AS PKTABLE_CAT, " + - "convert(sysname,schema_name(o1.schema_id)) AS PKTABLE_SCHEM, " + - "convert(sysname,o1.name) AS PKTABLE_NAME, " + - "convert(sysname,c1.name) AS PKCOLUMN_NAME, " + - "convert(sysname,db_name()) AS FKTABLE_CAT, " + - "convert(sysname,schema_name(o2.schema_id)) AS FKTABLE_SCHEM, " + - "convert(sysname,o2.name) AS FKTABLE_NAME, " + - "convert(sysname,c2.name) AS FKCOLUMN_NAME, " + - "isnull(convert(smallint,k.constraint_column_id), convert(smallint,0)) AS KEY_SEQ, " + - "convert(smallint, case ObjectProperty(f.object_id, 'CnstIsUpdateCascade') when 1 then 0 else 1 end) AS UPDATE_RULE, " + - "convert(smallint, case ObjectProperty(f.object_id, 'CnstIsDeleteCascade') when 1 then 0 else 1 end) AS DELETE_RULE, " + - "convert(sysname,object_name(f.object_id)) AS FK_NAME, " + - "convert(sysname,i.name) AS PK_NAME, " + - "convert(smallint, 7) AS DEFERRABILITY " + - "from " + - "sys.objects o1, " + - "sys.objects o2, " + - "sys.columns c1, " + - "sys.columns c2, " + - "sys.foreign_keys f inner join " + - "sys.foreign_key_columns k on (k.constraint_object_id = f.object_id) inner join " + - "sys.indexes i on (f.referenced_object_id = i.object_id and f.key_index_id = i.index_id) " + - "where " + - "o1.object_id = f.referenced_object_id and " + - "o2.object_id = f.parent_object_id and " + - "c1.object_id = f.referenced_object_id and " + - "c2.object_id = f.parent_object_id and " + - "c1.column_id = k.referenced_column_id and " + - "c2.column_id = k.parent_column_id and " + - "((object_schema_name(o1.object_id)='" + jdbcSchemaName + "'" + - " and convert(sysname,schema_name(o2.schema_id))='" + jdbcSchemaName + "' and " + - "convert(sysname,o2.name)='" + tableName + "' ) or ( convert(sysname,schema_name" + - "(o2.schema_id))='" + jdbcSchemaName + "' and convert(sysname,o2.name)='" + tableName + - "' )) order by 5, 6, 7, 9, 8"; - } - - private List querytDB2Luw(String jdbcSchemaName, String tableName) throws DatabaseException, SQLException { - List parameters = new ArrayList<>(2); - StringBuilder sql = new StringBuilder ("SELECT " + - " pk_col.tabschema AS pktable_cat, " + - " pk_col.tabname as pktable_name, " + - " pk_col.colname as pkcolumn_name, " + - " fk_col.tabschema as fktable_cat, " + - " fk_col.tabname as fktable_name, " + - " fk_col.colname as fkcolumn_name, " + - " fk_col.colseq as key_seq, " + - " decode (ref.updaterule, 'A', 3, 'R', 1, 1) as update_rule, " + - " decode (ref.deleterule, 'A', 3, 'C', 0, 'N', 2, 'R', 1, 1) as delete_rule, " + - " ref.constname as fk_name, " + - " ref.refkeyname as pk_name, " + - " 7 as deferrability " + - "FROM " + - "syscat.references ref " + - "join syscat.keycoluse fk_col on ref.constname=fk_col.constname and ref.tabschema=fk_col.tabschema and ref.tabname=fk_col.tabname " + - "join syscat.keycoluse pk_col on ref.refkeyname=pk_col.constname and ref.reftabschema=pk_col.tabschema and ref.reftabname=pk_col.tabname and pk_col.colseq=fk_col.colseq " + - "WHERE ref.tabschema = ? "); - parameters.add(jdbcSchemaName); - if (tableName != null) { - sql.append("and fk_col.tabname = ? "); - parameters.add(tableName); - } - sql.append("ORDER BY fk_col.colseq"); - return executeAndExtract(database, sql.toString(), parameters.toArray()); - } - - private String getDB2ForAs400Sql(String jdbcSchemaName, String tableName) { - return "SELECT " + - "pktable_cat, " + - "pktable_name, " + - "pkcolumn_name, " + - "fktable_cat, " + - "fktable_name, " + - "fkcolumn_name, " + - "key_seq, " + - "update_rule, " + - "delete_rule, " + - "fk_name, " + - "pk_name, " + - "deferrability " + - "FROM " + - "sysibm.SQLFORKEYS " + - "WHERE " + - "FKTABLE_SCHEM = '" + jdbcSchemaName + "' " + - "AND FKTABLE_NAME = '" + tableName + "'"; - } - - protected List queryDb2Zos(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { - - List parameters = new ArrayList<>(2); - StringBuilder sql = new StringBuilder("SELECT " + - " ref.REFTBCREATOR AS pktable_cat, " + - " ref.REFTBNAME as pktable_name, " + - " pk_col.colname as pkcolumn_name, " + - " ref.CREATOR as fktable_cat, " + - " ref.TBNAME as fktable_name, " + - " fk_col.colname as fkcolumn_name, " + - " fk_col.colseq as key_seq, " + - " decode (ref.deleterule, 'A', 3, 'C', 0, 'N', 2, 'R', 1, 1) as delete_rule, " + - " ref.relname as fk_name, " + - " pk_col.colname as pk_name, " + - " 7 as deferrability " + - "FROM " + - "SYSIBM.SYSRELS ref " + - "join SYSIBM.SYSFOREIGNKEYS fk_col " + - "on ref.relname = fk_col.RELNAME " + - "and ref.CREATOR = fk_col.CREATOR " + - "and ref.TBNAME = fk_col.TBNAME " + - "join SYSIBM.SYSKEYCOLUSE pk_col " + - "on ref.REFTBCREATOR = pk_col.TBCREATOR " + - "and ref.REFTBNAME = pk_col.TBNAME " + - "and pk_col.colseq=fk_col.colseq " + - "WHERE ref.CREATOR = ? "); - parameters.add(((AbstractJdbcDatabase) CachingDatabaseMetaData.this.database).getJdbcSchemaName(catalogAndSchema)); - if (tableName != null) { - sql.append("AND ref.TBNAME = ? "); - parameters.add(tableName); - } - sql.append("ORDER BY fk_col.colseq"); - - return executeAndExtract(CachingDatabaseMetaData.this.database, sql.toString(), parameters.toArray()); - } - - @Override - protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { - if (database instanceof AbstractDb2Database || database instanceof MSSQLDatabase) { - return super.shouldBulkSelect(schemaKey, resultSetCache); //can bulk and fast fetch - } else { - return database instanceof OracleDatabase; //oracle is slow, always bulk select while you are at it. Other databases need to go through all tables. - } - } - } - - private class GetNotNullConstraintsResultSetCache extends ResultSetCache.SingleResultSetExtractor { - final String catalogName; - final String schemaName; - final String tableName; - - private GetNotNullConstraintsResultSetCache(Database database, String catalogName, String schemaName, String tableName) { - super(database); - this.catalogName = catalogName; - this.schemaName = schemaName; - this.tableName = tableName; - } - - @Override - public ResultSetCache.RowData rowKeyParameters(CachedRow row) { - return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEMA"), - database, row.getString("TABLE_NAME")); - } - - @Override - public ResultSetCache.RowData wantedKeyParameters() { - return new ResultSetCache.RowData(catalogName, schemaName, database, tableName); - } - - @Override - public boolean bulkContainsSchema(String schemaKey) { - return database instanceof OracleDatabase; - } - - @Override - public String getSchemaKey(CachedRow row) { - return row.getString("TABLE_SCHEMA"); - } - - @Override - protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { - LiquibaseTableNamesFactory liquibaseTableNamesFactory = Scope.getCurrentScope().getSingleton(LiquibaseTableNamesFactory.class); - List liquibaseTableNames = liquibaseTableNamesFactory.getLiquibaseTableNames(database); - return liquibaseTableNames.stream().noneMatch(tableName::equalsIgnoreCase); - } - - @Override - public List fastFetchQuery() throws SQLException, DatabaseException { - if (database instanceof OracleDatabase) { - return oracleQuery(false); - } - return Collections.emptyList(); - } - - @Override - public List bulkFetchQuery() throws SQLException, DatabaseException { - if (database instanceof OracleDatabase) { - return oracleQuery(true); - } - return Collections.emptyList(); - } - - private List oracleQuery(boolean bulk) throws DatabaseException, SQLException { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - String jdbcTableName = database.escapeStringForDatabase(tableName); - String sqlToSelectNotNullConstraints = "SELECT NULL AS TABLE_CAT, atc.OWNER AS TABLE_SCHEMA, atc.OWNER, atc.TABLE_NAME, " + - "atc.COLUMN_NAME, NULLABLE, ac.VALIDATED as VALIDATED, ac.SEARCH_CONDITION, ac.CONSTRAINT_NAME " + - "FROM ALL_TAB_COLS atc " + - "JOIN all_cons_columns acc ON atc.OWNER = acc.OWNER AND atc.TABLE_NAME = acc.TABLE_NAME AND atc.COLUMN_NAME = acc.COLUMN_NAME " + - "JOIN all_constraints ac ON atc.OWNER = ac.OWNER AND atc.TABLE_NAME = ac.TABLE_NAME AND acc.CONSTRAINT_NAME = ac.CONSTRAINT_NAME "; - - if (!bulk || getAllCatalogsStringScratchData() == null) { - sqlToSelectNotNullConstraints += " WHERE atc.OWNER='" + jdbcSchemaName + "' AND atc.hidden_column='NO' AND ac.CONSTRAINT_TYPE='C' and ac.search_condition is not null "; - } else { - sqlToSelectNotNullConstraints += " WHERE atc.OWNER IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ") " - + " AND atc.hidden_column='NO' AND ac.CONSTRAINT_TYPE='C' and ac.search_condition is not null "; - } - - sqlToSelectNotNullConstraints += (!bulk && tableName != null && !tableName.isEmpty()) ? " AND atc.TABLE_NAME='" + jdbcTableName + "'" : ""; - - return this.executeAndExtract(sqlToSelectNotNullConstraints, database); - } - - @Override - protected List extract(ResultSet resultSet, boolean informixIndexTrimHint) throws SQLException { - List cachedRowList = new ArrayList<>(); - if (!(database instanceof OracleDatabase)) { - return cachedRowList; - } - - resultSet.setFetchSize(database.getFetchSize()); - - try { - List result = (List) new RowMapperNotNullConstraintsResultSetExtractor(new ColumnMapRowMapper(database.isCaseSensitive()) { - @Override - protected Object getColumnValue(ResultSet rs, int index) throws SQLException { - Object value = super.getColumnValue(rs, index); - if (!(value instanceof String)) { - return value; - } - return value.toString().trim(); - } - }).extractData(resultSet); - - for (Map row : result) { - cachedRowList.add(new CachedRow(row)); - } - } finally { - JdbcUtil.closeResultSet(resultSet); - } - return cachedRowList; - - } - } - - public List getTables(final String catalogName, final String schemaName, final String table) throws DatabaseException { - return getResultSetCache("getTables").get(new ResultSetCache.SingleResultSetExtractor(database) { - - @Override - protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { - return table == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); - } - - @Override - public ResultSetCache.RowData rowKeyParameters(CachedRow row) { - return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME")); - } - - @Override - public ResultSetCache.RowData wantedKeyParameters() { - return new ResultSetCache.RowData(catalogName, schemaName, database, table); - } - - @Override - public boolean bulkContainsSchema(String schemaKey) { - return database instanceof OracleDatabase; - } - - @Override - public String getSchemaKey(CachedRow row) { - return row.getString("TABLE_SCHEM"); - } - - @Override - public List fastFetchQuery() throws SQLException, DatabaseException { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - if (database instanceof OracleDatabase) { - return queryOracle(catalogAndSchema, table); - } else if (database instanceof MSSQLDatabase) { - return queryMssql(catalogAndSchema, table); - } else if (database instanceof Db2zDatabase) { - return queryDb2Zos(catalogAndSchema, table); - } else if (database instanceof PostgresDatabase) { - return queryPostgres(catalogAndSchema, table); - } - - String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); - String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), ((table == null) ? - SQL_FILTER_MATCH_ALL : escapeForLike(table, database)), new String[]{"TABLE"})); - } - - @Override - public List bulkFetchQuery() throws SQLException, DatabaseException { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - if (database instanceof OracleDatabase) { - return queryOracle(catalogAndSchema, null); - } else if (database instanceof MSSQLDatabase) { - return queryMssql(catalogAndSchema, null); - } else if (database instanceof Db2zDatabase) { - return queryDb2Zos(catalogAndSchema, null); - } else if (database instanceof PostgresDatabase) { - return queryPostgres(catalogAndSchema, table); - } - - String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); - String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), SQL_FILTER_MATCH_ALL, new String[]{"TABLE"})); - } - - private List queryMssql(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { - String ownerName = database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class); - - String databaseName = StringUtil.trimToNull(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)); - String dbIdParam; - String databasePrefix; - if (databaseName == null) { - databasePrefix = ""; - dbIdParam = ""; - } else { - dbIdParam = ", db_id('" + databaseName + "')"; - databasePrefix = "[" + databaseName + "]."; - } - - - //From select object_definition(object_id('sp_tables')) - String sql = "select " + - "db_name(" + (databaseName == null ? "" : "db_id('" + databaseName + "')") + ") AS TABLE_CAT, " + - "convert(sysname,object_schema_name(o.object_id" + dbIdParam + ")) AS TABLE_SCHEM, " + - "convert(sysname,o.name) AS TABLE_NAME, " + - "'TABLE' AS TABLE_TYPE, " + - "CAST(ep.value as varchar(max)) as REMARKS " + - "from " + databasePrefix + "sys.all_objects o " + - "left outer join sys.extended_properties ep on ep.name='MS_Description' and major_id=o.object_id and minor_id=0 " + - "where " + - "o.type in ('U') " + - "and has_perms_by_name(" + (databaseName == null ? "" : "quotename('" + databaseName + "') + '.' + ") + "quotename(object_schema_name(o.object_id" + dbIdParam + ")) + '.' + quotename(o.name), 'object', 'select') = 1 " + - "and charindex(substring(o.type,1,1),'U') <> 0 " + - "and object_schema_name(o.object_id" + dbIdParam + ")='" + database.escapeStringForDatabase(ownerName) + "'"; - if (tableName != null) { - sql += " AND o.name='" + database.escapeStringForDatabase(tableName) + "' "; - } - sql += "order by 4, 1, 2, 3"; - - return executeAndExtract(sql, database); - } - - private List queryOracle(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { - String ownerName = database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class); - - String sql = "SELECT null as TABLE_CAT, a.OWNER as TABLE_SCHEM, a.TABLE_NAME as TABLE_NAME, " + - "a.TEMPORARY as TEMPORARY, a.DURATION as DURATION, 'TABLE' as TABLE_TYPE, " + - "c.COMMENTS as REMARKS, A.tablespace_name as tablespace_name, CASE WHEN A.tablespace_name = " + - "(SELECT DEFAULT_TABLESPACE FROM USER_USERS) THEN 'true' ELSE null END as default_tablespace " + - "from ALL_TABLES a " + - "join ALL_TAB_COMMENTS c on a.TABLE_NAME=c.table_name and a.owner=c.owner " + - "left outer join ALL_QUEUE_TABLES q ON a.TABLE_NAME = q.QUEUE_TABLE and a.OWNER = q.OWNER " + - "WHERE q.QUEUE_TABLE is null "; - String allCatalogsString = getAllCatalogsStringScratchData(); - if (tableName != null || allCatalogsString == null) { - sql += "AND a.OWNER='" + ownerName + "'"; - } else { - sql += "AND a.OWNER IN ('" + ownerName + "', " + allCatalogsString + ")"; - } - if (tableName != null) { - sql += " AND a.TABLE_NAME='" + tableName + "'"; - } - - return executeAndExtract(sql, database); - } - - private List queryDb2Zos(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { - String ownerName = database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class); - - String sql = "SELECT CREATOR AS TABLE_SCHEM, " + - "NAME AS TABLE_NAME, " + - "'TABLE' AS TABLE_TYPE, " + - "REMARKS " + - "FROM SYSIBM.SYSTABLES " + - "WHERE TYPE = 'T'"; - List parameters = new ArrayList<>(2); - if (ownerName != null) { - sql += " AND CREATOR = ?"; - parameters.add(ownerName); - } - if (tableName != null) { - sql += " AND NAME = ?"; - parameters.add(tableName); - } - - return executeAndExtract(database, sql, parameters.toArray()); - } - - private List queryPostgres(CatalogAndSchema catalogAndSchema, String tableName) throws SQLException { - String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); - String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), ((tableName == null) ? - SQL_FILTER_MATCH_ALL : escapeForLike(tableName, database)), new String[]{"TABLE", "PARTITIONED TABLE"})); - - } - }); - } - - public List getViews(final String catalogName, final String schemaName, String viewName) throws DatabaseException { - final String view; - if (database instanceof DB2Database) { - view = database.correctObjectName(viewName, View.class); - } else { - view = viewName; - } - return getResultSetCache("getViews").get(new ResultSetCache.SingleResultSetExtractor(database) { - - @Override - protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { - return view == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); - } - - @Override - public ResultSetCache.RowData rowKeyParameters(CachedRow row) { - return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME")); - } - - - @Override - public ResultSetCache.RowData wantedKeyParameters() { - return new ResultSetCache.RowData(catalogName, schemaName, database, view); - } - - @Override - public boolean bulkContainsSchema(String schemaKey) { - return database instanceof OracleDatabase; - } - - @Override - public String getSchemaKey(CachedRow row) { - return row.getString("TABLE_SCHEM"); - } - - - @Override - public List fastFetchQuery() throws SQLException, DatabaseException { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - if (database instanceof OracleDatabase) { - return queryOracle(catalogAndSchema, view); - } else if (database instanceof MSSQLDatabase) { - return queryMssql(catalogAndSchema, view); - } - - String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); - String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), ((view == null) ? SQL_FILTER_MATCH_ALL - : escapeForLike(view, database)), new String[]{"VIEW"})); - } - - @Override - public List bulkFetchQuery() throws SQLException, DatabaseException { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - if (database instanceof OracleDatabase) { - return queryOracle(catalogAndSchema, null); - } else if (database instanceof MSSQLDatabase) { - return queryMssql(catalogAndSchema, null); - } - - String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); - String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), SQL_FILTER_MATCH_ALL, new String[]{"VIEW"})); - } - - private List queryMssql(CatalogAndSchema catalogAndSchema, String viewName) throws DatabaseException, SQLException { - String ownerName = database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class); - String databaseName = StringUtil.trimToNull(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)); - String dbIdParam = ""; - String databasePrefix = ""; - boolean haveDatabaseName = databaseName != null; - - if (haveDatabaseName) { - dbIdParam = ", db_id('" + databaseName + "')"; - databasePrefix = "[" + databaseName + "]."; - } - String tableCatParam = haveDatabaseName ? "db_id('" + databaseName + "')" : ""; - String permsParam = haveDatabaseName ? "quotename('" + databaseName + "') + '.' + " : ""; - - String sql = "select " + - "db_name(" + tableCatParam + ") AS TABLE_CAT, " + - "convert(sysname,object_schema_name(o.object_id" + dbIdParam + ")) AS TABLE_SCHEM, " + - "convert(sysname,o.name) AS TABLE_NAME, " + - "'VIEW' AS TABLE_TYPE, " + - "CAST(ep.value as varchar(max)) as REMARKS " + - "from " + databasePrefix + "sys.all_objects o " + - "left join sys.extended_properties ep on ep.name='MS_Description' and major_id=o.object_id and minor_id=0 " + - "where " + - "o.type in ('V') " + - "and has_perms_by_name(" + permsParam + "quotename(object_schema_name(o.object_id" + dbIdParam + ")) + '.' + quotename(o.name), 'object', 'select') = 1 " + - "and charindex(substring(o.type,1,1),'V') <> 0 " + - "and object_schema_name(o.object_id" + dbIdParam + ")='" + database.escapeStringForDatabase(ownerName) + "'"; - if (viewName != null) { - sql += " AND o.name='" + database.escapeStringForDatabase(viewName) + "' "; - } - sql += "order by 4, 1, 2, 3"; - - return executeAndExtract(sql, database); - } - - - private List queryOracle(CatalogAndSchema catalogAndSchema, String viewName) throws DatabaseException, SQLException { - String ownerName = database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class); - - String sql = "SELECT null as TABLE_CAT, a.OWNER as TABLE_SCHEM, a.VIEW_NAME as TABLE_NAME, 'TABLE' as TABLE_TYPE, c.COMMENTS as REMARKS, TEXT as OBJECT_BODY"; - if (database.getDatabaseMajorVersion() > 10) { - sql += ", EDITIONING_VIEW"; - } - sql += " from ALL_VIEWS a " + - "join ALL_TAB_COMMENTS c on a.VIEW_NAME=c.table_name and a.owner=c.owner "; - if (viewName != null || getAllCatalogsStringScratchData() == null) { - sql += "WHERE a.OWNER='" + ownerName + "'"; - } else { - sql += "WHERE a.OWNER IN ('" + ownerName + "', " + getAllCatalogsStringScratchData() + ")"; - } - if (viewName != null) { - sql += " AND a.VIEW_NAME='" + database.correctObjectName(viewName, View.class) + "'"; - } - sql += " AND a.VIEW_NAME not in (select mv.name from all_registered_mviews mv where mv.owner=a.owner)"; - - return executeAndExtract(sql, database); - } - }); - } - - public List getPrimaryKeys(final String catalogName, final String schemaName, final String table) throws DatabaseException { - return getResultSetCache("getPrimaryKeys").get(new ResultSetCache.SingleResultSetExtractor(database) { - - @Override - public ResultSetCache.RowData rowKeyParameters(CachedRow row) { - return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME")); - } - - @Override - public ResultSetCache.RowData wantedKeyParameters() { - return new ResultSetCache.RowData(catalogName, schemaName, database, table); - } - - @Override - public boolean bulkContainsSchema(String schemaKey) { - return database instanceof OracleDatabase; - } - - - @Override - public String getSchemaKey(CachedRow row) { - return row.getString("TABLE_SCHEM"); - } - - @Override - public List fastFetchQuery() throws SQLException { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - try { - List foundPks = new ArrayList<>(); - if (table == null) { - List tables = CachingDatabaseMetaData.this.getTables(catalogName, schemaName, null); - for (CachedRow table : tables) { - List pkInfo = getPkInfo(catalogAndSchema, table.getString("TABLE_NAME")); - if (pkInfo != null) { - foundPks.addAll(pkInfo); - } - } - return foundPks; - } else { - List pkInfo = getPkInfo(catalogAndSchema, table); - if (pkInfo != null) { - foundPks.addAll(pkInfo); - } - } - return foundPks; - } catch (DatabaseException e) { - throw new SQLException(e); - } - } - - private List getPkInfo(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { - List pkInfo; - if (database instanceof MSSQLDatabase) { - String sql = mssqlSql(catalogAndSchema, tableName); - pkInfo = executeAndExtract(sql, database); - } else { - if (database instanceof Db2zDatabase) { - String sql = "SELECT 'NULL' AS TABLE_CAT," + - " SYSTAB.TBCREATOR AS TABLE_SCHEM, " + - "SYSTAB.TBNAME AS TABLE_NAME, " + - "COLUSE.COLNAME AS COLUMN_NAME, " + - "COLUSE.COLSEQ AS KEY_SEQ, " + - "SYSTAB.CONSTNAME AS PK_NAME " + - "FROM SYSIBM.SYSTABCONST SYSTAB " + - "JOIN SYSIBM.SYSKEYCOLUSE COLUSE " + - "ON SYSTAB.TBCREATOR = COLUSE.TBCREATOR " + - "WHERE SYSTAB.TYPE = 'P' " + - "AND SYSTAB.TBNAME = ? " + - "AND SYSTAB.TBCREATOR = ? " + - "AND SYSTAB.TBNAME=COLUSE.TBNAME " + - "AND SYSTAB.CONSTNAME=COLUSE.CONSTNAME " + - "ORDER BY COLUSE.COLNAME"; - try { - return executeAndExtract(database, sql, table, ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema)); - } catch (DatabaseException e) { - throw new SQLException(e); - } - } else if (database instanceof OracleDatabase) { - warnAboutDbaRecycleBin(); - - String sql = "SELECT NULL AS table_cat, c.owner AS table_schem, c.table_name, c.column_name as COLUMN_NAME, c.position AS key_seq, c.constraint_name AS pk_name, k.VALIDATED as VALIDATED " + - "FROM all_cons_columns c, all_constraints k " + - "LEFT JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=k.table_name " + - "WHERE k.constraint_type = 'P' " + - "AND d.object_name IS NULL " + - "AND k.table_name = '" + table + "' " + - "AND k.owner = '" + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema) + "' " + - "AND k.constraint_name = c.constraint_name " + - "AND k.table_name = c.table_name " + - "AND k.owner = c.owner " + - "ORDER BY column_name"; - try { - return executeAndExtract(sql, database); - } catch (DatabaseException e) { - throw new SQLException(e); - } - } else if (database instanceof CockroachDatabase) { - // This is the same as the query generated by PGJDBC's getPrimaryKeys method, except it - // also adds an `asc_or_desc` column to the result. - String sql = "SELECT " + - " result.table_cat, " + - " result.table_schem, " + - " result.table_name, " + - " result.column_name, " + - " result.key_seq, " + - " result.pk_name, " + - " CASE result.indoption[result.key_seq - 1] & 1 " + - " WHEN 1 THEN 'D' " + - " ELSE 'A' " + - " END AS asc_or_desc " + - "FROM " + - " (" + - " SELECT " + - " NULL AS table_cat, " + - " n.nspname AS table_schem, " + - " ct.relname AS table_name, " + - " a.attname AS column_name, " + - " (information_schema._pg_expandarray(i.indkey)).n " + - " AS key_seq, " + - " ci.relname AS pk_name, " + - " information_schema._pg_expandarray(i.indkey) AS keys, " + - " i.indoption, " + - " a.attnum AS a_attnum " + - " FROM " + - " pg_catalog.pg_class AS ct " + - " JOIN pg_catalog.pg_attribute AS a ON (ct.oid = a.attrelid) " + - " JOIN pg_catalog.pg_namespace AS n ON " + - " (ct.relnamespace = n.oid) " + - " JOIN pg_catalog.pg_index AS i ON (a.attrelid = i.indrelid) " + - " JOIN pg_catalog.pg_class AS ci ON (ci.oid = i.indexrelid) " + - " WHERE " + - " true " + - " AND n.nspname = '" + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema) + "' " + - " AND ct.relname = '" + table + "' " + - " AND i.indisprimary" + - " ) " + - " AS result " + - "WHERE " + - " result.a_attnum = (result.keys).x " + - "ORDER BY " + - " result.table_name, result.pk_name, result.key_seq"; - - try { - return executeAndExtract(sql, database); - } catch (DatabaseException e) { - throw new SQLException(e); - } - } else { - return extract( - databaseMetaData.getPrimaryKeys( - ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), - ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), - table - ) - ); - } - } - return pkInfo; - } - - private String mssqlSql(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException { - String sql; - sql = - "SELECT " + - "DB_NAME() AS [TABLE_CAT], " + - "[s].[name] AS [TABLE_SCHEM], " + - "[t].[name] AS [TABLE_NAME], " + - "[c].[name] AS [COLUMN_NAME], " + - "CASE [ic].[is_descending_key] WHEN 0 THEN N'A' WHEN 1 THEN N'D' END AS [ASC_OR_DESC], " + - "[ic].[key_ordinal] AS [KEY_SEQ], " + - "[kc].[name] AS [PK_NAME] " + - "FROM [sys].[schemas] AS [s] " + - "INNER JOIN [sys].[tables] AS [t] " + - "ON [t].[schema_id] = [s].[schema_id] " + - "INNER JOIN [sys].[key_constraints] AS [kc] " + - "ON [kc].[parent_object_id] = [t].[object_id] " + - "INNER JOIN [sys].[indexes] AS [i] " + - "ON [i].[object_id] = [kc].[parent_object_id] " + - "AND [i].[index_id] = [kc].[unique_index_id] " + - "INNER JOIN [sys].[index_columns] AS [ic] " + - "ON [ic].[object_id] = [i].[object_id] " + - "AND [ic].[index_id] = [i].[index_id] " + - "INNER JOIN [sys].[columns] AS [c] " + - "ON [c].[object_id] = [ic].[object_id] " + - "AND [c].[column_id] = [ic].[column_id] " + - "WHERE [s].[name] = N'" + database.escapeStringForDatabase(catalogAndSchema.getSchemaName()) + "' " + // The schema name was corrected in the customized CatalogAndSchema - (tableName == null ? "" : "AND [t].[name] = N'" + database.escapeStringForDatabase(database.correctObjectName(tableName, Table.class)) + "' ") + - "AND [kc].[type] = 'PK' " + - "AND [ic].[key_ordinal] > 0 " + - "ORDER BY " + - "[ic].[key_ordinal]"; - return sql; - } - - @Override - public List bulkFetchQuery() throws SQLException { - if (database instanceof OracleDatabase) { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - warnAboutDbaRecycleBin(); - try { - String sql = "SELECT NULL AS table_cat, c.owner AS table_schem, c.table_name, c.column_name, c.position AS key_seq,c.constraint_name AS pk_name, k.VALIDATED as VALIDATED FROM " + - "all_cons_columns c, " + - "all_constraints k " + - "LEFT JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=k.table_name " + - "WHERE k.constraint_type = 'P' " + - "AND d.object_name IS NULL "; - if (getAllCatalogsStringScratchData() == null) { - sql += "AND k.owner='" + catalogAndSchema.getCatalogName() + "' "; - } else { - sql += "AND k.owner IN ('" + catalogAndSchema.getCatalogName() + "', " + getAllCatalogsStringScratchData() + ")"; - } - sql += "AND k.constraint_name = c.constraint_name " + - "AND k.table_name = c.table_name " + - "AND k.owner = c.owner " + - "ORDER BY column_name"; - return executeAndExtract(sql, database); - } catch (DatabaseException e) { - throw new SQLException(e); - } - } else if (database instanceof MSSQLDatabase) { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - try { - return executeAndExtract(mssqlSql(catalogAndSchema, null), database); - } catch (DatabaseException e) { - throw new SQLException(e); - } - } - return null; - } - - @Override - protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { - if ((database instanceof OracleDatabase) || (database instanceof MSSQLDatabase)) { - return table == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); - } else { - return false; - } - } - }); - } - - public List getUniqueConstraints(final String catalogName, final String schemaName, final String tableName) throws DatabaseException { - return getResultSetCache("getUniqueConstraints").get(new ResultSetCache.SingleResultSetExtractor(database) { - - @Override - protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { - return tableName == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); - } - - @Override - public boolean bulkContainsSchema(String schemaKey) { - return database instanceof OracleDatabase; - } - - @Override - public String getSchemaKey(CachedRow row) { - return row.getString("CONSTRAINT_SCHEM"); - } - - @Override - public ResultSetCache.RowData rowKeyParameters(CachedRow row) { - return new ResultSetCache.RowData(catalogName, schemaName, database, row.getString("TABLE_NAME")); - } - - @Override - public ResultSetCache.RowData wantedKeyParameters() { - return new ResultSetCache.RowData(catalogName, schemaName, database, tableName); - } - - @Override - public List fastFetchQuery() throws SQLException, DatabaseException { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - return queryDb(catalogAndSchema, tableName); - } - - @Override - public List bulkFetchQuery() throws SQLException, DatabaseException { - CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); - - return queryDb(catalogAndSchema, null); - } - - private List queryDb(CatalogAndSchema catalogAndSchema, String tableName) throws SQLException, DatabaseException { - - String jdbcCatalogName = catalogAndSchema.getCatalogName(); - String jdbcSchemaName = catalogAndSchema.getSchemaName(); - - Database database = getDatabase(); - List parameters = new ArrayList<>(3); - String sql = null; - if (database instanceof Ingres9Database) { - sql = "select CONSTRAINT_NAME, TABLE_NAME from iiconstraints where schema_name ='" - + schemaName + "' and constraint_type='U'"; - if (tableName != null) { - sql += " and table_name='" + tableName + "'"; - } - } else if ((database instanceof MySQLDatabase) || (database instanceof HsqlDatabase) || (database - instanceof MariaDBDatabase)) { - sql = "select CONSTRAINT_NAME, TABLE_NAME " - + "from " + database.getSystemSchema() + ".table_constraints " - + "where constraint_schema='" + jdbcCatalogName + "' " - + "and constraint_type='UNIQUE'"; - if (tableName != null) { - sql += " and table_name='" + tableName + "'"; - } - } else if (database instanceof PostgresDatabase) { - sql = "select CONSTRAINT_NAME, TABLE_NAME " - + "from " + database.getSystemSchema() + ".table_constraints " - + "where constraint_catalog='" + jdbcCatalogName + "' " - + "and constraint_schema='" + jdbcSchemaName + "' " - + "and constraint_type='UNIQUE'"; - if (tableName != null) { - sql += " and table_name='" + tableName + "'"; - } - } else if (database.getClass().getName().contains("MaxDB")) { //have to check classname as this is currently an extension - sql = "select distinct tablename AS TABLE_NAME, constraintname AS CONSTRAINT_NAME from CONSTRAINTCOLUMNS WHERE CONSTRAINTTYPE = 'UNIQUE_CONST'"; - if (tableName != null) { - sql += " and tablename='" + tableName + "'"; - } - } else if (database instanceof MSSQLDatabase) { - sql = - "SELECT " + - "[TC].[CONSTRAINT_NAME], " + - "[TC].[TABLE_NAME], " + - "[TC].[CONSTRAINT_CATALOG] AS INDEX_CATALOG, " + - "[TC].[CONSTRAINT_SCHEMA] AS INDEX_SCHEMA, " + - "[IDX].[TYPE_DESC], " + - "[IDX].[name] AS INDEX_NAME " + - "FROM [INFORMATION_SCHEMA].[TABLE_CONSTRAINTS] AS [TC] " + - "JOIN sys.indexes AS IDX ON IDX.name=[TC].[CONSTRAINT_NAME] AND object_schema_name(object_id)=[TC].[CONSTRAINT_SCHEMA] " + - "WHERE [TC].[CONSTRAINT_TYPE] = 'UNIQUE' " + - "AND [TC].[CONSTRAINT_CATALOG] = N'" + database.escapeStringForDatabase(jdbcCatalogName) + "' " + - "AND [TC].[CONSTRAINT_SCHEMA] = N'" + database.escapeStringForDatabase(jdbcSchemaName) + "'"; - if (tableName != null) { - sql += " AND [TC].[TABLE_NAME] = N'" + database.escapeStringForDatabase(database.correctObjectName(tableName, Table.class)) + "'"; - } - } else if (database instanceof OracleDatabase) { - warnAboutDbaRecycleBin(); - - sql = "select uc.owner AS CONSTRAINT_SCHEM, uc.constraint_name, uc.table_name,uc.status,uc.deferrable,uc.deferred,ui.tablespace_name, ui.index_name, ui.owner as INDEX_CATALOG, uc.VALIDATED as VALIDATED, ac.COLUMN_NAME as COLUMN_NAME " + - "from all_constraints uc " + - "join all_indexes ui on uc.index_name = ui.index_name and uc.owner=ui.table_owner and uc.table_name=ui.table_name " + - "LEFT OUTER JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=ui.table_name " + - "LEFT JOIN all_cons_columns ac ON ac.OWNER = uc.OWNER AND ac.TABLE_NAME = uc.TABLE_NAME AND ac.CONSTRAINT_NAME = uc.CONSTRAINT_NAME " + - "where uc.constraint_type='U' "; - if (tableName != null || getAllCatalogsStringScratchData() == null) { - sql += "and uc.owner = '" + jdbcSchemaName + "'"; - } else { - sql += "and uc.owner IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ")"; - } - sql += "AND d.object_name IS NULL "; - - if (tableName != null) { - sql += " and uc.table_name = '" + tableName + "'"; - } - } else if (database instanceof DB2Database) { - // if we are on DB2 AS400 iSeries - if (database.getDatabaseProductName().startsWith("DB2 UDB for AS/400")) { - sql = "select constraint_name as constraint_name, table_name as table_name from QSYS2.TABLE_CONSTRAINTS where table_schema='" + jdbcSchemaName + "' and constraint_type='UNIQUE'"; - if (tableName != null) { - sql += " and table_name = '" + tableName + "'"; - } - // DB2 z/OS - } - // here we are on DB2 UDB - else { - sql = "select distinct k.constname as constraint_name, t.tabname as TABLE_NAME " - + "from syscat.keycoluse k " - + "inner join syscat.tabconst t " - + "on k.constname = t.constname " - + "where t.tabschema = ? " - + "and t.type = 'U'"; - parameters.add(jdbcSchemaName); - if (tableName != null) { - sql += " and t.tabname = ?"; - parameters.add(tableName); - } - } - } else if (database instanceof Db2zDatabase) { - sql = "select k.constname as constraint_name, t.tbname as TABLE_NAME" - + " from SYSIBM.SYSKEYCOLUSE k" - + " inner join SYSIBM.SYSTABCONST t" - + " on k.constname = t.constname" - + " and k.TBCREATOR = t.TBCREATOR" - + " and k.TBNAME = t.TBNAME" - + " where t.TBCREATOR = ?" - + " and t.TYPE = 'U'"; - parameters.add(jdbcSchemaName); - if (tableName != null) { - sql += " and t.TBNAME = ?"; - parameters.add(tableName); - } - } else if (database instanceof FirebirdDatabase) { - sql = "SELECT TRIM(RDB$INDICES.RDB$INDEX_NAME) AS CONSTRAINT_NAME, " + - "TRIM(RDB$INDICES.RDB$RELATION_NAME) AS TABLE_NAME " + - "FROM RDB$INDICES " - + "LEFT JOIN RDB$RELATION_CONSTRAINTS " - + "ON RDB$RELATION_CONSTRAINTS.RDB$INDEX_NAME = RDB$INDICES.RDB$INDEX_NAME " - + "WHERE RDB$INDICES.RDB$UNIQUE_FLAG IS NOT NULL " - + "AND (" - + "RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_TYPE IS NULL " - + "OR TRIM(RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_TYPE)='UNIQUE') " - + "AND NOT(RDB$INDICES.RDB$INDEX_NAME LIKE 'RDB$%')"; - if (tableName != null) { - sql += " AND TRIM(RDB$INDICES.RDB$RELATION_NAME)='" + tableName + "'"; - } - } else if (database instanceof DerbyDatabase) { - sql = "select c.constraintname as CONSTRAINT_NAME, tablename AS TABLE_NAME " - + "from sys.systables t, sys.sysconstraints c, sys.sysschemas s " - + "where s.schemaname='" + jdbcCatalogName + "' " - + "and t.tableid = c.tableid " - + "and t.schemaid=s.schemaid " - + "and c.type = 'U'"; - if (tableName != null) { - sql += " AND t.tablename = '" + tableName + "'"; - } - } else if (database instanceof InformixDatabase) { - sql = "select unique sysindexes.idxname as CONSTRAINT_NAME, sysindexes.idxtype, systables.tabname as TABLE_NAME " - + "from sysindexes, systables " - + "left outer join sysconstraints on sysconstraints.tabid = systables.tabid and sysconstraints.constrtype = 'P' " - + "where sysindexes.tabid = systables.tabid and sysindexes.idxtype = 'U' " - + "and sysconstraints.idxname != sysindexes.idxname " - + "and sysconstraints.tabid = sysindexes.tabid"; - if (tableName != null) { - sql += " and systables.tabname = '" + database.correctObjectName(tableName, Table.class) + "'"; - } - } else if (database instanceof SybaseDatabase) { - sql = "select idx.name as CONSTRAINT_NAME, tbl.name as TABLE_NAME " - + "from sysindexes idx " - + "inner join sysobjects tbl on tbl.id = idx.id " - + "where idx.indid between 1 and 254 " - + "and (idx.status & 2) = 2 " - + "and tbl.type = 'U'"; - if (tableName != null) { - sql += " and tbl.name = '" + database.correctObjectName(tableName, Table.class) + "'"; - } - } else if (database instanceof SybaseASADatabase) { - sql = "select sysconstraint.constraint_name, sysconstraint.constraint_type, systable.table_name " + - "from sysconstraint, systable " + - "where sysconstraint.table_object_id = systable.object_id " + - "and sysconstraint.constraint_type = 'U'"; - if (tableName != null) { - sql += " and systable.table_name = '" + tableName + "'"; - } - } else { - if (database instanceof H2Database) { - try { - if (database.getDatabaseMajorVersion() >= 2) { - sql = "select CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME " - + "from " + database.getSystemSchema() + ".table_constraints " - + "where constraint_schema='" + jdbcSchemaName + "' " - + "and constraint_catalog='" + jdbcCatalogName + "' " - + "and constraint_type='UNIQUE'"; - if (tableName != null) { - sql += " and table_name='" + tableName + "'"; - } - } - } catch (DatabaseException e) { - Scope.getCurrentScope().getLog(getClass()).fine("Cannot determine h2 version, using default unique constraint query"); - } - } - if (sql == null) { - - sql = "select CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME " - + "from " + database.getSystemSchema() + ".constraints " - + "where constraint_schema='" + jdbcSchemaName + "' " - + "and constraint_catalog='" + jdbcCatalogName + "' " - + "and constraint_type='UNIQUE'"; - if (tableName != null) { - sql += " and table_name='" + tableName + "'"; - } - } - } - - return executeAndExtract(database, database instanceof InformixDatabase, sql, parameters.toArray()); - } - }); - } - } - - private String getAllCatalogsStringScratchData() { - return (String) getScratchData(ALL_CATALOGS_STRING_SCRATCH_KEY); - } - - private String escapeForLike(String string, Database database) { - if (string == null) { - return null; - } - - if (database instanceof SQLiteDatabase || database instanceof DmDatabase) { - //sqlite jdbc's queries does not support escaped patterns. - // DM 也不支持转义的匹配方式,需要兼容 - return string; - } - - return string - .replace("%", "\\%") - .replace("_", "\\_"); - } -} diff --git a/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java b/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java deleted file mode 100644 index 790e65e..0000000 --- a/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/AbstractEngineConfiguration.java +++ /dev/null @@ -1,2038 +0,0 @@ -/* 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.flowable.common.engine.impl; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import org.apache.commons.lang3.StringUtils; -import org.apache.ibatis.builder.xml.XMLConfigBuilder; -import org.apache.ibatis.builder.xml.XMLMapperBuilder; -import org.apache.ibatis.datasource.pooled.PooledDataSource; -import org.apache.ibatis.mapping.Environment; -import org.apache.ibatis.plugin.Interceptor; -import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.session.SqlSessionFactory; -import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory; -import org.apache.ibatis.transaction.TransactionFactory; -import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; -import org.apache.ibatis.transaction.managed.ManagedTransactionFactory; -import org.apache.ibatis.type.*; -import org.flowable.common.engine.api.FlowableException; -import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; -import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; -import org.flowable.common.engine.api.delegate.event.FlowableEventListener; -import org.flowable.common.engine.api.engine.EngineLifecycleListener; -import org.flowable.common.engine.impl.agenda.AgendaOperationExecutionListener; -import org.flowable.common.engine.impl.agenda.AgendaOperationRunner; -import org.flowable.common.engine.impl.cfg.CommandExecutorImpl; -import org.flowable.common.engine.impl.cfg.IdGenerator; -import org.flowable.common.engine.impl.cfg.TransactionContextFactory; -import org.flowable.common.engine.impl.cfg.standalone.StandaloneMybatisTransactionContextFactory; -import org.flowable.common.engine.impl.db.*; -import org.flowable.common.engine.impl.event.EventDispatchAction; -import org.flowable.common.engine.impl.event.FlowableEventDispatcherImpl; -import org.flowable.common.engine.impl.interceptor.*; -import org.flowable.common.engine.impl.lock.LockManager; -import org.flowable.common.engine.impl.lock.LockManagerImpl; -import org.flowable.common.engine.impl.logging.LoggingListener; -import org.flowable.common.engine.impl.logging.LoggingSession; -import org.flowable.common.engine.impl.logging.LoggingSessionFactory; -import org.flowable.common.engine.impl.persistence.GenericManagerFactory; -import org.flowable.common.engine.impl.persistence.StrongUuidGenerator; -import org.flowable.common.engine.impl.persistence.cache.EntityCache; -import org.flowable.common.engine.impl.persistence.cache.EntityCacheImpl; -import org.flowable.common.engine.impl.persistence.entity.*; -import org.flowable.common.engine.impl.persistence.entity.data.ByteArrayDataManager; -import org.flowable.common.engine.impl.persistence.entity.data.PropertyDataManager; -import org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisByteArrayDataManager; -import org.flowable.common.engine.impl.persistence.entity.data.impl.MybatisPropertyDataManager; -import org.flowable.common.engine.impl.runtime.Clock; -import org.flowable.common.engine.impl.service.CommonEngineServiceImpl; -import org.flowable.common.engine.impl.util.DefaultClockImpl; -import org.flowable.common.engine.impl.util.IoUtil; -import org.flowable.common.engine.impl.util.ReflectUtil; -import org.flowable.eventregistry.api.EventRegistryEventConsumer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.naming.InitialContext; -import javax.sql.DataSource; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.sql.*; -import java.time.Duration; -import java.util.*; - -public abstract class AbstractEngineConfiguration { - - protected final Logger logger = LoggerFactory.getLogger(getClass()); - - /** The tenant id indicating 'no tenant' */ - public static final String NO_TENANT_ID = ""; - - /** - * Checks the version of the DB schema against the library when the form engine is being created and throws an exception if the versions don't match. - */ - public static final String DB_SCHEMA_UPDATE_FALSE = "false"; - public static final String DB_SCHEMA_UPDATE_CREATE = "create"; - public static final String DB_SCHEMA_UPDATE_CREATE_DROP = "create-drop"; - - /** - * Creates the schema when the form engine is being created and drops the schema when the form engine is being closed. - */ - public static final String DB_SCHEMA_UPDATE_DROP_CREATE = "drop-create"; - - /** - * Upon building of the process engine, a check is performed and an update of the schema is performed if it is necessary. - */ - public static final String DB_SCHEMA_UPDATE_TRUE = "true"; - - protected boolean forceCloseMybatisConnectionPool = true; - - protected String databaseType; - protected String jdbcDriver = "org.h2.Driver"; - protected String jdbcUrl = "jdbc:h2:tcp://localhost/~/flowable"; - protected String jdbcUsername = "sa"; - protected String jdbcPassword = ""; - protected String dataSourceJndiName; - protected int jdbcMaxActiveConnections = 16; - protected int jdbcMaxIdleConnections = 8; - protected int jdbcMaxCheckoutTime; - protected int jdbcMaxWaitTime; - protected boolean jdbcPingEnabled; - protected String jdbcPingQuery; - protected int jdbcPingConnectionNotUsedFor; - protected int jdbcDefaultTransactionIsolationLevel; - protected DataSource dataSource; - protected SchemaManager commonSchemaManager; - protected SchemaManager schemaManager; - protected Command schemaManagementCmd; - - protected String databaseSchemaUpdate = DB_SCHEMA_UPDATE_FALSE; - - /** - * Whether to use a lock when performing the database schema create or update operations. - */ - protected boolean useLockForDatabaseSchemaUpdate = false; - - protected String xmlEncoding = "UTF-8"; - - // COMMAND EXECUTORS /////////////////////////////////////////////// - - protected CommandExecutor commandExecutor; - protected Collection defaultCommandInterceptors; - protected CommandConfig defaultCommandConfig; - protected CommandConfig schemaCommandConfig; - protected CommandContextFactory commandContextFactory; - protected CommandInterceptor commandInvoker; - - protected AgendaOperationRunner agendaOperationRunner = (commandContext, runnable) -> runnable.run(); - protected Collection agendaOperationExecutionListeners; - - protected List customPreCommandInterceptors; - protected List customPostCommandInterceptors; - protected List commandInterceptors; - - protected Map engineConfigurations = new HashMap<>(); - protected Map serviceConfigurations = new HashMap<>(); - - protected ClassLoader classLoader; - /** - * Either use Class.forName or ClassLoader.loadClass for class loading. See http://forums.activiti.org/content/reflectutilloadclass-and-custom- classloader - */ - protected boolean useClassForNameClassLoading = true; - - protected List engineLifecycleListeners; - - // Event Registry ////////////////////////////////////////////////// - protected Map eventRegistryEventConsumers = new HashMap<>(); - - // MYBATIS SQL SESSION FACTORY ///////////////////////////////////// - - protected boolean isDbHistoryUsed = true; - protected DbSqlSessionFactory dbSqlSessionFactory; - protected SqlSessionFactory sqlSessionFactory; - protected TransactionFactory transactionFactory; - protected TransactionContextFactory transactionContextFactory; - - /** - * If set to true, enables bulk insert (grouping sql inserts together). Default true. - * For some databases (eg DB2+z/OS) needs to be set to false. - */ - protected boolean isBulkInsertEnabled = true; - - /** - * Some databases have a limit of how many parameters one sql insert can have (eg SQL Server, 2000 params (!= insert statements) ). Tweak this parameter in case of exceptions indicating too much - * is being put into one bulk insert, or make it higher if your database can cope with it and there are inserts with a huge amount of data. - *

- * By default: 100 (55 for mssql server as it has a hard limit of 2000 parameters in a statement) - */ - protected int maxNrOfStatementsInBulkInsert = 100; - - public int DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER = 55; // currently Execution has most params (35). 2000 / 35 = 57. - - protected String mybatisMappingFile; - protected Set> customMybatisMappers; - protected Set customMybatisXMLMappers; - protected List customMybatisInterceptors; - - protected Set dependentEngineMyBatisXmlMappers; - protected List dependentEngineMybatisTypeAliasConfigs; - protected List dependentEngineMybatisTypeHandlerConfigs; - - // SESSION FACTORIES /////////////////////////////////////////////// - protected List customSessionFactories; - protected Map, SessionFactory> sessionFactories; - - protected boolean enableEventDispatcher = true; - protected FlowableEventDispatcher eventDispatcher; - protected List eventListeners; - protected Map> typedEventListeners; - protected List additionalEventDispatchActions; - - protected LoggingListener loggingListener; - - protected boolean transactionsExternallyManaged; - - /** - * Flag that can be set to configure or not a relational database is used. This is useful for custom implementations that do not use relational databases at all. - * - * If true (default), the {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will be used to determine what needs to happen wrt the database schema. - * - * If false, no validation or schema creation will be done. That means that the database schema must have been created 'manually' before but the engine does not validate whether the schema is - * correct. The {@link AbstractEngineConfiguration#getDatabaseSchemaUpdate()} value will not be used. - */ - protected boolean usingRelationalDatabase = true; - - /** - * Flag that can be set to configure whether or not a schema is used. This is useful for custom implementations that do not use relational databases at all. - * Setting {@link #usingRelationalDatabase} to true will automatically imply using a schema. - */ - protected boolean usingSchemaMgmt = true; - - /** - * Allows configuring a database table prefix which is used for all runtime operations of the process engine. For example, if you specify a prefix named 'PRE1.', Flowable will query for executions - * in a table named 'PRE1.ACT_RU_EXECUTION_'. - * - *

- * NOTE: the prefix is not respected by automatic database schema management. If you use {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_CREATE_DROP} or - * {@link AbstractEngineConfiguration#DB_SCHEMA_UPDATE_TRUE}, Flowable will create the database tables using the default names, regardless of the prefix configured here. - */ - protected String databaseTablePrefix = ""; - - /** - * Escape character for doing wildcard searches. - * - * This will be added at then end of queries that include for example a LIKE clause. For example: SELECT * FROM table WHERE column LIKE '%\%%' ESCAPE '\'; - */ - protected String databaseWildcardEscapeCharacter; - - /** - * database catalog to use - */ - protected String databaseCatalog = ""; - - /** - * In some situations you want to set the schema to use for table checks / generation if the database metadata doesn't return that correctly, see https://jira.codehaus.org/browse/ACT-1220, - * https://jira.codehaus.org/browse/ACT-1062 - */ - protected String databaseSchema; - - /** - * Set to true in case the defined databaseTablePrefix is a schema-name, instead of an actual table name prefix. This is relevant for checking if Flowable-tables exist, the databaseTablePrefix - * will not be used here - since the schema is taken into account already, adding a prefix for the table-check will result in wrong table-names. - */ - protected boolean tablePrefixIsSchema; - - /** - * Set to true if the latest version of a definition should be retrieved, ignoring a possible parent deployment id value - */ - protected boolean alwaysLookupLatestDefinitionVersion; - - /** - * Set to true if by default lookups should fallback to the default tenant (an empty string by default or a defined tenant value) - */ - protected boolean fallbackToDefaultTenant; - - /** - * Default tenant provider that is executed when looking up definitions, in case the global or local fallback to default tenant value is true - */ - protected DefaultTenantProvider defaultTenantProvider = (tenantId, scope, scopeKey) -> NO_TENANT_ID; - - /** - * Enables the MyBatis plugin that logs the execution time of sql statements. - */ - protected boolean enableLogSqlExecutionTime; - - protected Properties databaseTypeMappings = getDefaultDatabaseTypeMappings(); - - /** - * Duration between the checks when acquiring a lock. - */ - protected Duration lockPollRate = Duration.ofSeconds(10); - - /** - * Duration to wait for the DB Schema lock before giving up. - */ - protected Duration schemaLockWaitTime = Duration.ofMinutes(5); - - // DATA MANAGERS ////////////////////////////////////////////////////////////////// - - protected PropertyDataManager propertyDataManager; - protected ByteArrayDataManager byteArrayDataManager; - protected TableDataManager tableDataManager; - - // ENTITY MANAGERS //////////////////////////////////////////////////////////////// - - protected PropertyEntityManager propertyEntityManager; - protected ByteArrayEntityManager byteArrayEntityManager; - - protected List customPreDeployers; - protected List customPostDeployers; - protected List deployers; - - // CONFIGURATORS //////////////////////////////////////////////////////////// - - protected boolean enableConfiguratorServiceLoader = true; // Enabled by default. In certain environments this should be set to false (eg osgi) - protected List configurators; // The injected configurators - protected List allConfigurators; // Including auto-discovered configurators - protected EngineConfigurator idmEngineConfigurator; - protected EngineConfigurator eventRegistryConfigurator; - - public static final String PRODUCT_NAME_POSTGRES = "PostgreSQL"; - public static final String PRODUCT_NAME_CRDB = "CockroachDB"; - - public static final String DATABASE_TYPE_H2 = "h2"; - public static final String DATABASE_TYPE_HSQL = "hsql"; - public static final String DATABASE_TYPE_MYSQL = "mysql"; - public static final String DATABASE_TYPE_ORACLE = "oracle"; - public static final String DATABASE_TYPE_POSTGRES = "postgres"; - public static final String DATABASE_TYPE_MSSQL = "mssql"; - public static final String DATABASE_TYPE_DB2 = "db2"; - public static final String DATABASE_TYPE_COCKROACHDB = "cockroachdb"; - - public static Properties getDefaultDatabaseTypeMappings() { - Properties databaseTypeMappings = new Properties(); - databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2); - databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL); - databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL); - databaseTypeMappings.setProperty("MariaDB", DATABASE_TYPE_MYSQL); - databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE); - databaseTypeMappings.setProperty(PRODUCT_NAME_POSTGRES, DATABASE_TYPE_POSTGRES); - databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL); - databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/LINUXPPC64LE", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2); - databaseTypeMappings.setProperty(PRODUCT_NAME_CRDB, DATABASE_TYPE_COCKROACHDB); - databaseTypeMappings.setProperty("DM DBMS", DATABASE_TYPE_ORACLE);// 加入达梦支持 可以直接使用ORACLE或者MYSQL的 - return databaseTypeMappings; - } - - protected Map beans; - - protected IdGenerator idGenerator; - protected boolean usePrefixId; - - protected Clock clock; - protected ObjectMapper objectMapper; - - // Variables - - public static final int DEFAULT_GENERIC_MAX_LENGTH_STRING = 4000; - public static final int DEFAULT_ORACLE_MAX_LENGTH_STRING = 2000; - - /** - * Define a max length for storing String variable types in the database. Mainly used for the Oracle NVARCHAR2 limit of 2000 characters - */ - protected int maxLengthStringVariableType = -1; - - protected void initEngineConfigurations() { - addEngineConfiguration(getEngineCfgKey(), getEngineScopeType(), this); - } - - // DataSource - // /////////////////////////////////////////////////////////////// - - protected void initDataSource() { - if (dataSource == null) { - if (dataSourceJndiName != null) { - try { - dataSource = (DataSource) new InitialContext().lookup(dataSourceJndiName); - } catch (Exception e) { - throw new FlowableException("couldn't lookup datasource from " + dataSourceJndiName + ": " + e.getMessage(), e); - } - - } else if (jdbcUrl != null) { - if ((jdbcDriver == null) || (jdbcUsername == null)) { - throw new FlowableException("DataSource or JDBC properties have to be specified in a process engine configuration"); - } - - logger.debug("initializing datasource to db: {}", jdbcUrl); - - if (logger.isInfoEnabled()) { - logger.info("Configuring Datasource with following properties (omitted password for security)"); - logger.info("datasource driver : {}", jdbcDriver); - logger.info("datasource url : {}", jdbcUrl); - logger.info("datasource user name : {}", jdbcUsername); - } - - PooledDataSource pooledDataSource = new PooledDataSource(this.getClass().getClassLoader(), jdbcDriver, jdbcUrl, jdbcUsername, jdbcPassword); - - if (jdbcMaxActiveConnections > 0) { - pooledDataSource.setPoolMaximumActiveConnections(jdbcMaxActiveConnections); - } - if (jdbcMaxIdleConnections > 0) { - pooledDataSource.setPoolMaximumIdleConnections(jdbcMaxIdleConnections); - } - if (jdbcMaxCheckoutTime > 0) { - pooledDataSource.setPoolMaximumCheckoutTime(jdbcMaxCheckoutTime); - } - if (jdbcMaxWaitTime > 0) { - pooledDataSource.setPoolTimeToWait(jdbcMaxWaitTime); - } - if (jdbcPingEnabled) { - pooledDataSource.setPoolPingEnabled(true); - if (jdbcPingQuery != null) { - pooledDataSource.setPoolPingQuery(jdbcPingQuery); - } - pooledDataSource.setPoolPingConnectionsNotUsedFor(jdbcPingConnectionNotUsedFor); - } - if (jdbcDefaultTransactionIsolationLevel > 0) { - pooledDataSource.setDefaultTransactionIsolationLevel(jdbcDefaultTransactionIsolationLevel); - } - dataSource = pooledDataSource; - } - } - - if (databaseType == null) { - initDatabaseType(); - } - } - - public void initDatabaseType() { - Connection connection = null; - try { - connection = dataSource.getConnection(); - DatabaseMetaData databaseMetaData = connection.getMetaData(); - String databaseProductName = databaseMetaData.getDatabaseProductName(); - logger.debug("database product name: '{}'", databaseProductName); - - // CRDB does not expose the version through the jdbc driver, so we need to fetch it through version(). - if (PRODUCT_NAME_POSTGRES.equalsIgnoreCase(databaseProductName)) { - try (PreparedStatement preparedStatement = connection.prepareStatement("select version() as version;"); - ResultSet resultSet = preparedStatement.executeQuery()) { - String version = null; - if (resultSet.next()) { - version = resultSet.getString("version"); - } - - if (StringUtils.isNotEmpty(version) && version.toLowerCase().startsWith(PRODUCT_NAME_CRDB.toLowerCase())) { - databaseProductName = PRODUCT_NAME_CRDB; - logger.info("CockroachDB version '{}' detected", version); - } - } - } - - databaseType = databaseTypeMappings.getProperty(databaseProductName); - if (databaseType == null) { - throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'"); - } - logger.debug("using database type: {}", databaseType); - - } catch (SQLException e) { - throw new RuntimeException("Exception while initializing Database connection", e); - } finally { - try { - if (connection != null) { - connection.close(); - } - } catch (SQLException e) { - logger.error("Exception while closing the Database connection", e); - } - } - - // Special care for MSSQL, as it has a hard limit of 2000 params per statement (incl bulk statement). - // Especially with executions, with 100 as default, this limit is passed. - if (DATABASE_TYPE_MSSQL.equals(databaseType)) { - maxNrOfStatementsInBulkInsert = DEFAULT_MAX_NR_OF_STATEMENTS_BULK_INSERT_SQL_SERVER; - } - } - - public void initSchemaManager() { - if (this.commonSchemaManager == null) { - this.commonSchemaManager = new CommonDbSchemaManager(); - } - } - - // session factories //////////////////////////////////////////////////////// - - public void addSessionFactory(SessionFactory sessionFactory) { - sessionFactories.put(sessionFactory.getSessionType(), sessionFactory); - } - - public void initCommandContextFactory() { - if (commandContextFactory == null) { - commandContextFactory = new CommandContextFactory(); - } - } - - public void initTransactionContextFactory() { - if (transactionContextFactory == null) { - transactionContextFactory = new StandaloneMybatisTransactionContextFactory(); - } - } - - public void initCommandExecutors() { - initDefaultCommandConfig(); - initSchemaCommandConfig(); - initCommandInvoker(); - initCommandInterceptors(); - initCommandExecutor(); - } - - - public void initDefaultCommandConfig() { - if (defaultCommandConfig == null) { - defaultCommandConfig = new CommandConfig(); - } - } - - public void initSchemaCommandConfig() { - if (schemaCommandConfig == null) { - schemaCommandConfig = new CommandConfig(); - } - } - - public void initCommandInvoker() { - if (commandInvoker == null) { - commandInvoker = new DefaultCommandInvoker(); - } - } - - public void initCommandInterceptors() { - if (commandInterceptors == null) { - commandInterceptors = new ArrayList<>(); - if (customPreCommandInterceptors != null) { - commandInterceptors.addAll(customPreCommandInterceptors); - } - commandInterceptors.addAll(getDefaultCommandInterceptors()); - if (customPostCommandInterceptors != null) { - commandInterceptors.addAll(customPostCommandInterceptors); - } - commandInterceptors.add(commandInvoker); - } - } - - public Collection getDefaultCommandInterceptors() { - if (defaultCommandInterceptors == null) { - List interceptors = new ArrayList<>(); - interceptors.add(new LogInterceptor()); - - if (DATABASE_TYPE_COCKROACHDB.equals(databaseType)) { - interceptors.add(new CrDbRetryInterceptor()); - } - - CommandInterceptor transactionInterceptor = createTransactionInterceptor(); - if (transactionInterceptor != null) { - interceptors.add(transactionInterceptor); - } - - if (commandContextFactory != null) { - String engineCfgKey = getEngineCfgKey(); - CommandContextInterceptor commandContextInterceptor = new CommandContextInterceptor(commandContextFactory, - classLoader, useClassForNameClassLoading, clock, objectMapper); - engineConfigurations.put(engineCfgKey, this); - commandContextInterceptor.setEngineCfgKey(engineCfgKey); - commandContextInterceptor.setEngineConfigurations(engineConfigurations); - interceptors.add(commandContextInterceptor); - } - - if (transactionContextFactory != null) { - interceptors.add(new TransactionContextInterceptor(transactionContextFactory)); - } - - List additionalCommandInterceptors = getAdditionalDefaultCommandInterceptors(); - if (additionalCommandInterceptors != null) { - interceptors.addAll(additionalCommandInterceptors); - } - - defaultCommandInterceptors = interceptors; - } - return defaultCommandInterceptors; - } - - public abstract String getEngineCfgKey(); - - public abstract String getEngineScopeType(); - - public List getAdditionalDefaultCommandInterceptors() { - return null; - } - - public void initCommandExecutor() { - if (commandExecutor == null) { - CommandInterceptor first = initInterceptorChain(commandInterceptors); - commandExecutor = new CommandExecutorImpl(getDefaultCommandConfig(), first); - } - } - - public CommandInterceptor initInterceptorChain(List chain) { - if (chain == null || chain.isEmpty()) { - throw new FlowableException("invalid command interceptor chain configuration: " + chain); - } - for (int i = 0; i < chain.size() - 1; i++) { - chain.get(i).setNext(chain.get(i + 1)); - } - return chain.get(0); - } - - public abstract CommandInterceptor createTransactionInterceptor(); - - - public void initBeans() { - if (beans == null) { - beans = new HashMap<>(); - } - } - - // id generator - // ///////////////////////////////////////////////////////////// - - public void initIdGenerator() { - if (idGenerator == null) { - idGenerator = new StrongUuidGenerator(); - } - } - - public void initObjectMapper() { - if (objectMapper == null) { - objectMapper = new ObjectMapper(); - objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - } - } - - public void initClock() { - if (clock == null) { - clock = new DefaultClockImpl(); - } - } - - // Data managers /////////////////////////////////////////////////////////// - - public void initDataManagers() { - if (propertyDataManager == null) { - propertyDataManager = new MybatisPropertyDataManager(idGenerator); - } - - if (byteArrayDataManager == null) { - byteArrayDataManager = new MybatisByteArrayDataManager(idGenerator); - } - } - - // Entity managers ////////////////////////////////////////////////////////// - - public void initEntityManagers() { - if (propertyEntityManager == null) { - propertyEntityManager = new PropertyEntityManagerImpl(this, propertyDataManager); - } - - if (byteArrayEntityManager == null) { - byteArrayEntityManager = new ByteArrayEntityManagerImpl(byteArrayDataManager, getEngineCfgKey(), this::getEventDispatcher); - } - - if (tableDataManager == null) { - tableDataManager = new TableDataManagerImpl(this); - } - } - - // services - // ///////////////////////////////////////////////////////////////// - - protected void initService(Object service) { - if (service instanceof CommonEngineServiceImpl) { - ((CommonEngineServiceImpl) service).setCommandExecutor(commandExecutor); - } - } - - // myBatis SqlSessionFactory - // //////////////////////////////////////////////// - - public void initSessionFactories() { - if (sessionFactories == null) { - sessionFactories = new HashMap<>(); - - if (usingRelationalDatabase) { - initDbSqlSessionFactory(); - } - - addSessionFactory(new GenericManagerFactory(EntityCache.class, EntityCacheImpl.class)); - - if (isLoggingSessionEnabled()) { - if (!sessionFactories.containsKey(LoggingSession.class)) { - LoggingSessionFactory loggingSessionFactory = new LoggingSessionFactory(); - loggingSessionFactory.setLoggingListener(loggingListener); - loggingSessionFactory.setObjectMapper(objectMapper); - sessionFactories.put(LoggingSession.class, loggingSessionFactory); - } - } - - commandContextFactory.setSessionFactories(sessionFactories); - - } else { - if (usingRelationalDatabase) { - initDbSqlSessionFactoryEntitySettings(); - } - } - - if (customSessionFactories != null) { - for (SessionFactory sessionFactory : customSessionFactories) { - addSessionFactory(sessionFactory); - } - } - } - - public void initDbSqlSessionFactory() { - if (dbSqlSessionFactory == null) { - dbSqlSessionFactory = createDbSqlSessionFactory(); - } - dbSqlSessionFactory.setDatabaseType(databaseType); - dbSqlSessionFactory.setSqlSessionFactory(sqlSessionFactory); - dbSqlSessionFactory.setDbHistoryUsed(isDbHistoryUsed); - dbSqlSessionFactory.setDatabaseTablePrefix(databaseTablePrefix); - dbSqlSessionFactory.setTablePrefixIsSchema(tablePrefixIsSchema); - dbSqlSessionFactory.setDatabaseCatalog(databaseCatalog); - dbSqlSessionFactory.setDatabaseSchema(databaseSchema); - dbSqlSessionFactory.setMaxNrOfStatementsInBulkInsert(maxNrOfStatementsInBulkInsert); - - initDbSqlSessionFactoryEntitySettings(); - - addSessionFactory(dbSqlSessionFactory); - } - - public DbSqlSessionFactory createDbSqlSessionFactory() { - return new DbSqlSessionFactory(usePrefixId); - } - - protected abstract void initDbSqlSessionFactoryEntitySettings(); - - protected void defaultInitDbSqlSessionFactoryEntitySettings(List> insertOrder, List> deleteOrder) { - if (insertOrder != null) { - for (Class clazz : insertOrder) { - dbSqlSessionFactory.getInsertionOrder().add(clazz); - - if (isBulkInsertEnabled) { - dbSqlSessionFactory.getBulkInserteableEntityClasses().add(clazz); - } - } - } - - if (deleteOrder != null) { - for (Class clazz : deleteOrder) { - dbSqlSessionFactory.getDeletionOrder().add(clazz); - } - } - } - - public void initTransactionFactory() { - if (transactionFactory == null) { - if (transactionsExternallyManaged) { - transactionFactory = new ManagedTransactionFactory(); - Properties properties = new Properties(); - properties.put("closeConnection", "false"); - this.transactionFactory.setProperties(properties); - } else { - transactionFactory = new JdbcTransactionFactory(); - } - } - } - - public void initSqlSessionFactory() { - if (sqlSessionFactory == null) { - InputStream inputStream = null; - try { - inputStream = getMyBatisXmlConfigurationStream(); - - Environment environment = new Environment("default", transactionFactory, dataSource); - Reader reader = new InputStreamReader(inputStream); - Properties properties = new Properties(); - properties.put("prefix", databaseTablePrefix); - - String wildcardEscapeClause = ""; - if ((databaseWildcardEscapeCharacter != null) && (databaseWildcardEscapeCharacter.length() != 0)) { - wildcardEscapeClause = " escape '" + databaseWildcardEscapeCharacter + "'"; - } - properties.put("wildcardEscapeClause", wildcardEscapeClause); - - // set default properties - properties.put("limitBefore", ""); - properties.put("limitAfter", ""); - properties.put("limitBetween", ""); - properties.put("limitBeforeNativeQuery", ""); - properties.put("limitAfterNativeQuery", ""); - properties.put("blobType", "BLOB"); - properties.put("boolValue", "TRUE"); - - if (databaseType != null) { - properties.load(getResourceAsStream(pathToEngineDbProperties())); - } - - Configuration configuration = initMybatisConfiguration(environment, reader, properties); - sqlSessionFactory = new DefaultSqlSessionFactory(configuration); - - } catch (Exception e) { - throw new FlowableException("Error while building ibatis SqlSessionFactory: " + e.getMessage(), e); - } finally { - IoUtil.closeSilently(inputStream); - } - } else { - // This is needed when the SQL Session Factory is created by another engine. - // When custom XML Mappers are registered with this engine they need to be loaded in the configuration as well - applyCustomMybatisCustomizations(sqlSessionFactory.getConfiguration()); - } - } - - public String pathToEngineDbProperties() { - return "org/flowable/common/db/properties/" + databaseType + ".properties"; - } - - public Configuration initMybatisConfiguration(Environment environment, Reader reader, Properties properties) { - XMLConfigBuilder parser = new XMLConfigBuilder(reader, "", properties); - Configuration configuration = parser.getConfiguration(); - - if (databaseType != null) { - configuration.setDatabaseId(databaseType); - } - - configuration.setEnvironment(environment); - - initMybatisTypeHandlers(configuration); - initCustomMybatisInterceptors(configuration); - if (isEnableLogSqlExecutionTime()) { - initMyBatisLogSqlExecutionTimePlugin(configuration); - } - - configuration = parseMybatisConfiguration(parser); - return configuration; - } - - public void initCustomMybatisMappers(Configuration configuration) { - if (getCustomMybatisMappers() != null) { - for (Class clazz : getCustomMybatisMappers()) { - if (!configuration.hasMapper(clazz)) { - configuration.addMapper(clazz); - } - } - } - } - - public void initMybatisTypeHandlers(Configuration configuration) { - // When mapping into Map there is currently a problem with MyBatis. - // It will return objects which are driver specific. - // Therefore we are registering the mappings between Object.class and the specific jdbc type here. - // see https://github.com/mybatis/mybatis-3/issues/2216 for more info - TypeHandlerRegistry handlerRegistry = configuration.getTypeHandlerRegistry(); - - handlerRegistry.register(Object.class, JdbcType.BOOLEAN, new BooleanTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.BIT, new BooleanTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.TINYINT, new ByteTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.SMALLINT, new ShortTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.INTEGER, new IntegerTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.FLOAT, new FloatTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.DOUBLE, new DoubleTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.CHAR, new StringTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.CLOB, new ClobTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.VARCHAR, new StringTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.LONGVARCHAR, new StringTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.NVARCHAR, new NStringTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.NCHAR, new NStringTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.NCLOB, new NClobTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.BIGINT, new LongTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.REAL, new BigDecimalTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.DECIMAL, new BigDecimalTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.NUMERIC, new BigDecimalTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.BLOB, new BlobInputStreamTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.LONGVARBINARY, new BlobTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.DATE, new DateOnlyTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.TIME, new TimeOnlyTypeHandler()); - handlerRegistry.register(Object.class, JdbcType.TIMESTAMP, new DateTypeHandler()); - - handlerRegistry.register(Object.class, JdbcType.SQLXML, new SqlxmlTypeHandler()); - } - - public void initCustomMybatisInterceptors(Configuration configuration) { - if (customMybatisInterceptors!=null){ - for (Interceptor interceptor :customMybatisInterceptors){ - configuration.addInterceptor(interceptor); - } - } - } - - public void initMyBatisLogSqlExecutionTimePlugin(Configuration configuration) { - configuration.addInterceptor(new LogSqlExecutionTimePlugin()); - } - - public Configuration parseMybatisConfiguration(XMLConfigBuilder parser) { - Configuration configuration = parser.parse(); - - applyCustomMybatisCustomizations(configuration); - return configuration; - } - - protected void applyCustomMybatisCustomizations(Configuration configuration) { - initCustomMybatisMappers(configuration); - - if (dependentEngineMybatisTypeAliasConfigs != null) { - for (MybatisTypeAliasConfigurator typeAliasConfig : dependentEngineMybatisTypeAliasConfigs) { - typeAliasConfig.configure(configuration.getTypeAliasRegistry()); - } - } - if (dependentEngineMybatisTypeHandlerConfigs != null) { - for (MybatisTypeHandlerConfigurator typeHandlerConfig : dependentEngineMybatisTypeHandlerConfigs) { - typeHandlerConfig.configure(configuration.getTypeHandlerRegistry()); - } - } - - parseDependentEngineMybatisXMLMappers(configuration); - parseCustomMybatisXMLMappers(configuration); - } - - public void parseCustomMybatisXMLMappers(Configuration configuration) { - if (getCustomMybatisXMLMappers() != null) { - for (String resource : getCustomMybatisXMLMappers()) { - parseMybatisXmlMapping(configuration, resource); - } - } - } - - public void parseDependentEngineMybatisXMLMappers(Configuration configuration) { - if (getDependentEngineMyBatisXmlMappers() != null) { - for (String resource : getDependentEngineMyBatisXmlMappers()) { - parseMybatisXmlMapping(configuration, resource); - } - } - } - - protected void parseMybatisXmlMapping(Configuration configuration, String resource) { - // see XMLConfigBuilder.mapperElement() - XMLMapperBuilder mapperParser = new XMLMapperBuilder(getResourceAsStream(resource), configuration, resource, configuration.getSqlFragments()); - mapperParser.parse(); - } - - protected InputStream getResourceAsStream(String resource) { - ClassLoader classLoader = getClassLoader(); - if (classLoader != null) { - return getClassLoader().getResourceAsStream(resource); - } else { - return this.getClass().getClassLoader().getResourceAsStream(resource); - } - } - - public void setMybatisMappingFile(String file) { - this.mybatisMappingFile = file; - } - - public String getMybatisMappingFile() { - return mybatisMappingFile; - } - - public abstract InputStream getMyBatisXmlConfigurationStream(); - - public void initConfigurators() { - - allConfigurators = new ArrayList<>(); - allConfigurators.addAll(getEngineSpecificEngineConfigurators()); - - // Configurators that are explicitly added to the config - if (configurators != null) { - allConfigurators.addAll(configurators); - } - - // Auto discovery through ServiceLoader - if (enableConfiguratorServiceLoader) { - ClassLoader classLoader = getClassLoader(); - if (classLoader == null) { - classLoader = ReflectUtil.getClassLoader(); - } - - ServiceLoader configuratorServiceLoader = ServiceLoader.load(EngineConfigurator.class, classLoader); - int nrOfServiceLoadedConfigurators = 0; - for (EngineConfigurator configurator : configuratorServiceLoader) { - allConfigurators.add(configurator); - nrOfServiceLoadedConfigurators++; - } - - if (nrOfServiceLoadedConfigurators > 0) { - logger.info("Found {} auto-discoverable Process Engine Configurator{}", nrOfServiceLoadedConfigurators, nrOfServiceLoadedConfigurators > 1 ? "s" : ""); - } - - if (!allConfigurators.isEmpty()) { - - // Order them according to the priorities (useful for dependent - // configurator) - allConfigurators.sort(new Comparator() { - - @Override - public int compare(EngineConfigurator configurator1, EngineConfigurator configurator2) { - int priority1 = configurator1.getPriority(); - int priority2 = configurator2.getPriority(); - - if (priority1 < priority2) { - return -1; - } else if (priority1 > priority2) { - return 1; - } - return 0; - } - }); - - // Execute the configurators - logger.info("Found {} Engine Configurators in total:", allConfigurators.size()); - for (EngineConfigurator configurator : allConfigurators) { - logger.info("{} (priority:{})", configurator.getClass(), configurator.getPriority()); - } - - } - - } - } - - public void close() { - if (forceCloseMybatisConnectionPool && dataSource instanceof PooledDataSource) { - /* - * When the datasource is created by a Flowable engine (i.e. it's an instance of PooledDataSource), - * the connection pool needs to be closed when closing the engine. - * Note that calling forceCloseAll() multiple times (as is the case when running with multiple engine) is ok. - */ - ((PooledDataSource) dataSource).forceCloseAll(); - } - } - - protected List getEngineSpecificEngineConfigurators() { - // meant to be overridden if needed - return Collections.emptyList(); - } - - public void configuratorsBeforeInit() { - for (EngineConfigurator configurator : allConfigurators) { - logger.info("Executing beforeInit() of {} (priority:{})", configurator.getClass(), configurator.getPriority()); - configurator.beforeInit(this); - } - } - - public void configuratorsAfterInit() { - for (EngineConfigurator configurator : allConfigurators) { - logger.info("Executing configure() of {} (priority:{})", configurator.getClass(), configurator.getPriority()); - configurator.configure(this); - } - } - - public LockManager getLockManager(String lockName) { - return new LockManagerImpl(commandExecutor, lockName, getLockPollRate(), getEngineCfgKey()); - } - - // getters and setters - // ////////////////////////////////////////////////////// - - public abstract String getEngineName(); - - public ClassLoader getClassLoader() { - return classLoader; - } - - public AbstractEngineConfiguration setClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader; - return this; - } - - public boolean isUseClassForNameClassLoading() { - return useClassForNameClassLoading; - } - - public AbstractEngineConfiguration setUseClassForNameClassLoading(boolean useClassForNameClassLoading) { - this.useClassForNameClassLoading = useClassForNameClassLoading; - return this; - } - - public void addEngineLifecycleListener(EngineLifecycleListener engineLifecycleListener) { - if (this.engineLifecycleListeners == null) { - this.engineLifecycleListeners = new ArrayList<>(); - } - this.engineLifecycleListeners.add(engineLifecycleListener); - } - - public List getEngineLifecycleListeners() { - return engineLifecycleListeners; - } - - public AbstractEngineConfiguration setEngineLifecycleListeners(List engineLifecycleListeners) { - this.engineLifecycleListeners = engineLifecycleListeners; - return this; - } - - public String getDatabaseType() { - return databaseType; - } - - public AbstractEngineConfiguration setDatabaseType(String databaseType) { - this.databaseType = databaseType; - return this; - } - - public DataSource getDataSource() { - return dataSource; - } - - public AbstractEngineConfiguration setDataSource(DataSource dataSource) { - this.dataSource = dataSource; - return this; - } - - public SchemaManager getSchemaManager() { - return schemaManager; - } - - public AbstractEngineConfiguration setSchemaManager(SchemaManager schemaManager) { - this.schemaManager = schemaManager; - return this; - } - - public SchemaManager getCommonSchemaManager() { - return commonSchemaManager; - } - - public AbstractEngineConfiguration setCommonSchemaManager(SchemaManager commonSchemaManager) { - this.commonSchemaManager = commonSchemaManager; - return this; - } - - public Command getSchemaManagementCmd() { - return schemaManagementCmd; - } - - public AbstractEngineConfiguration setSchemaManagementCmd(Command schemaManagementCmd) { - this.schemaManagementCmd = schemaManagementCmd; - return this; - } - - public String getJdbcDriver() { - return jdbcDriver; - } - - public AbstractEngineConfiguration setJdbcDriver(String jdbcDriver) { - this.jdbcDriver = jdbcDriver; - return this; - } - - public String getJdbcUrl() { - return jdbcUrl; - } - - public AbstractEngineConfiguration setJdbcUrl(String jdbcUrl) { - this.jdbcUrl = jdbcUrl; - return this; - } - - public String getJdbcUsername() { - return jdbcUsername; - } - - public AbstractEngineConfiguration setJdbcUsername(String jdbcUsername) { - this.jdbcUsername = jdbcUsername; - return this; - } - - public String getJdbcPassword() { - return jdbcPassword; - } - - public AbstractEngineConfiguration setJdbcPassword(String jdbcPassword) { - this.jdbcPassword = jdbcPassword; - return this; - } - - public int getJdbcMaxActiveConnections() { - return jdbcMaxActiveConnections; - } - - public AbstractEngineConfiguration setJdbcMaxActiveConnections(int jdbcMaxActiveConnections) { - this.jdbcMaxActiveConnections = jdbcMaxActiveConnections; - return this; - } - - public int getJdbcMaxIdleConnections() { - return jdbcMaxIdleConnections; - } - - public AbstractEngineConfiguration setJdbcMaxIdleConnections(int jdbcMaxIdleConnections) { - this.jdbcMaxIdleConnections = jdbcMaxIdleConnections; - return this; - } - - public int getJdbcMaxCheckoutTime() { - return jdbcMaxCheckoutTime; - } - - public AbstractEngineConfiguration setJdbcMaxCheckoutTime(int jdbcMaxCheckoutTime) { - this.jdbcMaxCheckoutTime = jdbcMaxCheckoutTime; - return this; - } - - public int getJdbcMaxWaitTime() { - return jdbcMaxWaitTime; - } - - public AbstractEngineConfiguration setJdbcMaxWaitTime(int jdbcMaxWaitTime) { - this.jdbcMaxWaitTime = jdbcMaxWaitTime; - return this; - } - - public boolean isJdbcPingEnabled() { - return jdbcPingEnabled; - } - - public AbstractEngineConfiguration setJdbcPingEnabled(boolean jdbcPingEnabled) { - this.jdbcPingEnabled = jdbcPingEnabled; - return this; - } - - public int getJdbcPingConnectionNotUsedFor() { - return jdbcPingConnectionNotUsedFor; - } - - public AbstractEngineConfiguration setJdbcPingConnectionNotUsedFor(int jdbcPingConnectionNotUsedFor) { - this.jdbcPingConnectionNotUsedFor = jdbcPingConnectionNotUsedFor; - return this; - } - - public int getJdbcDefaultTransactionIsolationLevel() { - return jdbcDefaultTransactionIsolationLevel; - } - - public AbstractEngineConfiguration setJdbcDefaultTransactionIsolationLevel(int jdbcDefaultTransactionIsolationLevel) { - this.jdbcDefaultTransactionIsolationLevel = jdbcDefaultTransactionIsolationLevel; - return this; - } - - public String getJdbcPingQuery() { - return jdbcPingQuery; - } - - public AbstractEngineConfiguration setJdbcPingQuery(String jdbcPingQuery) { - this.jdbcPingQuery = jdbcPingQuery; - return this; - } - - public String getDataSourceJndiName() { - return dataSourceJndiName; - } - - public AbstractEngineConfiguration setDataSourceJndiName(String dataSourceJndiName) { - this.dataSourceJndiName = dataSourceJndiName; - return this; - } - - public CommandConfig getSchemaCommandConfig() { - return schemaCommandConfig; - } - - public AbstractEngineConfiguration setSchemaCommandConfig(CommandConfig schemaCommandConfig) { - this.schemaCommandConfig = schemaCommandConfig; - return this; - } - - public boolean isTransactionsExternallyManaged() { - return transactionsExternallyManaged; - } - - public AbstractEngineConfiguration setTransactionsExternallyManaged(boolean transactionsExternallyManaged) { - this.transactionsExternallyManaged = transactionsExternallyManaged; - return this; - } - - public Map getBeans() { - return beans; - } - - public AbstractEngineConfiguration setBeans(Map beans) { - this.beans = beans; - return this; - } - - public IdGenerator getIdGenerator() { - return idGenerator; - } - - public AbstractEngineConfiguration setIdGenerator(IdGenerator idGenerator) { - this.idGenerator = idGenerator; - return this; - } - - public boolean isUsePrefixId() { - return usePrefixId; - } - - public AbstractEngineConfiguration setUsePrefixId(boolean usePrefixId) { - this.usePrefixId = usePrefixId; - return this; - } - - public String getXmlEncoding() { - return xmlEncoding; - } - - public AbstractEngineConfiguration setXmlEncoding(String xmlEncoding) { - this.xmlEncoding = xmlEncoding; - return this; - } - - public CommandConfig getDefaultCommandConfig() { - return defaultCommandConfig; - } - - public AbstractEngineConfiguration setDefaultCommandConfig(CommandConfig defaultCommandConfig) { - this.defaultCommandConfig = defaultCommandConfig; - return this; - } - - public CommandExecutor getCommandExecutor() { - return commandExecutor; - } - - public AbstractEngineConfiguration setCommandExecutor(CommandExecutor commandExecutor) { - this.commandExecutor = commandExecutor; - return this; - } - - public CommandContextFactory getCommandContextFactory() { - return commandContextFactory; - } - - public AbstractEngineConfiguration setCommandContextFactory(CommandContextFactory commandContextFactory) { - this.commandContextFactory = commandContextFactory; - return this; - } - - public CommandInterceptor getCommandInvoker() { - return commandInvoker; - } - - public AbstractEngineConfiguration setCommandInvoker(CommandInterceptor commandInvoker) { - this.commandInvoker = commandInvoker; - return this; - } - - public AgendaOperationRunner getAgendaOperationRunner() { - return agendaOperationRunner; - } - - public AbstractEngineConfiguration setAgendaOperationRunner(AgendaOperationRunner agendaOperationRunner) { - this.agendaOperationRunner = agendaOperationRunner; - return this; - } - - public Collection getAgendaOperationExecutionListeners() { - return agendaOperationExecutionListeners; - } - - public AbstractEngineConfiguration addAgendaOperationExecutionListener(AgendaOperationExecutionListener listener) { - if (this.agendaOperationExecutionListeners == null) { - this.agendaOperationExecutionListeners = new ArrayList<>(); - } - this.agendaOperationExecutionListeners.add(listener); - return this; - } - - public AbstractEngineConfiguration setAgendaOperationExecutionListeners(Collection agendaOperationExecutionListeners) { - this.agendaOperationExecutionListeners = agendaOperationExecutionListeners; - return this; - } - - public List getCustomPreCommandInterceptors() { - return customPreCommandInterceptors; - } - - public AbstractEngineConfiguration addCustomPreCommandInterceptor(CommandInterceptor commandInterceptor) { - if (this.customPreCommandInterceptors == null) { - this.customPreCommandInterceptors = new ArrayList<>(); - } - this.customPreCommandInterceptors.add(commandInterceptor); - return this; - } - - public AbstractEngineConfiguration setCustomPreCommandInterceptors(List customPreCommandInterceptors) { - this.customPreCommandInterceptors = customPreCommandInterceptors; - return this; - } - - public List getCustomPostCommandInterceptors() { - return customPostCommandInterceptors; - } - - public AbstractEngineConfiguration addCustomPostCommandInterceptor(CommandInterceptor commandInterceptor) { - if (this.customPostCommandInterceptors == null) { - this.customPostCommandInterceptors = new ArrayList<>(); - } - this.customPostCommandInterceptors.add(commandInterceptor); - return this; - } - - public AbstractEngineConfiguration setCustomPostCommandInterceptors(List customPostCommandInterceptors) { - this.customPostCommandInterceptors = customPostCommandInterceptors; - return this; - } - - public List getCommandInterceptors() { - return commandInterceptors; - } - - public AbstractEngineConfiguration setCommandInterceptors(List commandInterceptors) { - this.commandInterceptors = commandInterceptors; - return this; - } - - public Map getEngineConfigurations() { - return engineConfigurations; - } - - public AbstractEngineConfiguration setEngineConfigurations(Map engineConfigurations) { - this.engineConfigurations = engineConfigurations; - return this; - } - - public void addEngineConfiguration(String key, String scopeType, AbstractEngineConfiguration engineConfiguration) { - if (engineConfigurations == null) { - engineConfigurations = new HashMap<>(); - } - engineConfigurations.put(key, engineConfiguration); - engineConfigurations.put(scopeType, engineConfiguration); - } - - public Map getServiceConfigurations() { - return serviceConfigurations; - } - - public AbstractEngineConfiguration setServiceConfigurations(Map serviceConfigurations) { - this.serviceConfigurations = serviceConfigurations; - return this; - } - - public void addServiceConfiguration(String key, AbstractServiceConfiguration serviceConfiguration) { - if (serviceConfigurations == null) { - serviceConfigurations = new HashMap<>(); - } - serviceConfigurations.put(key, serviceConfiguration); - } - - public Map getEventRegistryEventConsumers() { - return eventRegistryEventConsumers; - } - - public AbstractEngineConfiguration setEventRegistryEventConsumers(Map eventRegistryEventConsumers) { - this.eventRegistryEventConsumers = eventRegistryEventConsumers; - return this; - } - - public void addEventRegistryEventConsumer(String key, EventRegistryEventConsumer eventRegistryEventConsumer) { - if (eventRegistryEventConsumers == null) { - eventRegistryEventConsumers = new HashMap<>(); - } - eventRegistryEventConsumers.put(key, eventRegistryEventConsumer); - } - - public AbstractEngineConfiguration setDefaultCommandInterceptors(Collection defaultCommandInterceptors) { - this.defaultCommandInterceptors = defaultCommandInterceptors; - return this; - } - - public SqlSessionFactory getSqlSessionFactory() { - return sqlSessionFactory; - } - - public AbstractEngineConfiguration setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { - this.sqlSessionFactory = sqlSessionFactory; - return this; - } - - public boolean isDbHistoryUsed() { - return isDbHistoryUsed; - } - - public AbstractEngineConfiguration setDbHistoryUsed(boolean isDbHistoryUsed) { - this.isDbHistoryUsed = isDbHistoryUsed; - return this; - } - - public DbSqlSessionFactory getDbSqlSessionFactory() { - return dbSqlSessionFactory; - } - - public AbstractEngineConfiguration setDbSqlSessionFactory(DbSqlSessionFactory dbSqlSessionFactory) { - this.dbSqlSessionFactory = dbSqlSessionFactory; - return this; - } - - public TransactionFactory getTransactionFactory() { - return transactionFactory; - } - - public AbstractEngineConfiguration setTransactionFactory(TransactionFactory transactionFactory) { - this.transactionFactory = transactionFactory; - return this; - } - - public TransactionContextFactory getTransactionContextFactory() { - return transactionContextFactory; - } - - public AbstractEngineConfiguration setTransactionContextFactory(TransactionContextFactory transactionContextFactory) { - this.transactionContextFactory = transactionContextFactory; - return this; - } - - public int getMaxNrOfStatementsInBulkInsert() { - return maxNrOfStatementsInBulkInsert; - } - - public AbstractEngineConfiguration setMaxNrOfStatementsInBulkInsert(int maxNrOfStatementsInBulkInsert) { - this.maxNrOfStatementsInBulkInsert = maxNrOfStatementsInBulkInsert; - return this; - } - - public boolean isBulkInsertEnabled() { - return isBulkInsertEnabled; - } - - public AbstractEngineConfiguration setBulkInsertEnabled(boolean isBulkInsertEnabled) { - this.isBulkInsertEnabled = isBulkInsertEnabled; - return this; - } - - public Set> getCustomMybatisMappers() { - return customMybatisMappers; - } - - public AbstractEngineConfiguration setCustomMybatisMappers(Set> customMybatisMappers) { - this.customMybatisMappers = customMybatisMappers; - return this; - } - - public Set getCustomMybatisXMLMappers() { - return customMybatisXMLMappers; - } - - public AbstractEngineConfiguration setCustomMybatisXMLMappers(Set customMybatisXMLMappers) { - this.customMybatisXMLMappers = customMybatisXMLMappers; - return this; - } - - public Set getDependentEngineMyBatisXmlMappers() { - return dependentEngineMyBatisXmlMappers; - } - - public AbstractEngineConfiguration setCustomMybatisInterceptors(List customMybatisInterceptors) { - this.customMybatisInterceptors = customMybatisInterceptors; - return this; - } - - public List getCustomMybatisInterceptors() { - return customMybatisInterceptors; - } - - public AbstractEngineConfiguration setDependentEngineMyBatisXmlMappers(Set dependentEngineMyBatisXmlMappers) { - this.dependentEngineMyBatisXmlMappers = dependentEngineMyBatisXmlMappers; - return this; - } - - public List getDependentEngineMybatisTypeAliasConfigs() { - return dependentEngineMybatisTypeAliasConfigs; - } - - public AbstractEngineConfiguration setDependentEngineMybatisTypeAliasConfigs(List dependentEngineMybatisTypeAliasConfigs) { - this.dependentEngineMybatisTypeAliasConfigs = dependentEngineMybatisTypeAliasConfigs; - return this; - } - - public List getDependentEngineMybatisTypeHandlerConfigs() { - return dependentEngineMybatisTypeHandlerConfigs; - } - - public AbstractEngineConfiguration setDependentEngineMybatisTypeHandlerConfigs(List dependentEngineMybatisTypeHandlerConfigs) { - this.dependentEngineMybatisTypeHandlerConfigs = dependentEngineMybatisTypeHandlerConfigs; - return this; - } - - public List getCustomSessionFactories() { - return customSessionFactories; - } - - public AbstractEngineConfiguration addCustomSessionFactory(SessionFactory sessionFactory) { - if (customSessionFactories == null) { - customSessionFactories = new ArrayList<>(); - } - customSessionFactories.add(sessionFactory); - return this; - } - - public AbstractEngineConfiguration setCustomSessionFactories(List customSessionFactories) { - this.customSessionFactories = customSessionFactories; - return this; - } - - public boolean isUsingRelationalDatabase() { - return usingRelationalDatabase; - } - - public AbstractEngineConfiguration setUsingRelationalDatabase(boolean usingRelationalDatabase) { - this.usingRelationalDatabase = usingRelationalDatabase; - return this; - } - - public boolean isUsingSchemaMgmt() { - return usingSchemaMgmt; - } - - public AbstractEngineConfiguration setUsingSchemaMgmt(boolean usingSchema) { - this.usingSchemaMgmt = usingSchema; - return this; - } - - public String getDatabaseTablePrefix() { - return databaseTablePrefix; - } - - public AbstractEngineConfiguration setDatabaseTablePrefix(String databaseTablePrefix) { - this.databaseTablePrefix = databaseTablePrefix; - return this; - } - - public String getDatabaseWildcardEscapeCharacter() { - return databaseWildcardEscapeCharacter; - } - - public AbstractEngineConfiguration setDatabaseWildcardEscapeCharacter(String databaseWildcardEscapeCharacter) { - this.databaseWildcardEscapeCharacter = databaseWildcardEscapeCharacter; - return this; - } - - public String getDatabaseCatalog() { - return databaseCatalog; - } - - public AbstractEngineConfiguration setDatabaseCatalog(String databaseCatalog) { - this.databaseCatalog = databaseCatalog; - return this; - } - - public String getDatabaseSchema() { - return databaseSchema; - } - - public AbstractEngineConfiguration setDatabaseSchema(String databaseSchema) { - this.databaseSchema = databaseSchema; - return this; - } - - public boolean isTablePrefixIsSchema() { - return tablePrefixIsSchema; - } - - public AbstractEngineConfiguration setTablePrefixIsSchema(boolean tablePrefixIsSchema) { - this.tablePrefixIsSchema = tablePrefixIsSchema; - return this; - } - - public boolean isAlwaysLookupLatestDefinitionVersion() { - return alwaysLookupLatestDefinitionVersion; - } - - public AbstractEngineConfiguration setAlwaysLookupLatestDefinitionVersion(boolean alwaysLookupLatestDefinitionVersion) { - this.alwaysLookupLatestDefinitionVersion = alwaysLookupLatestDefinitionVersion; - return this; - } - - public boolean isFallbackToDefaultTenant() { - return fallbackToDefaultTenant; - } - - public AbstractEngineConfiguration setFallbackToDefaultTenant(boolean fallbackToDefaultTenant) { - this.fallbackToDefaultTenant = fallbackToDefaultTenant; - return this; - } - - public AbstractEngineConfiguration setDefaultTenantValue(String defaultTenantValue) { - this.defaultTenantProvider = (tenantId, scope, scopeKey) -> defaultTenantValue; - return this; - } - - public DefaultTenantProvider getDefaultTenantProvider() { - return defaultTenantProvider; - } - - public AbstractEngineConfiguration setDefaultTenantProvider(DefaultTenantProvider defaultTenantProvider) { - this.defaultTenantProvider = defaultTenantProvider; - return this; - } - - public boolean isEnableLogSqlExecutionTime() { - return enableLogSqlExecutionTime; - } - - public void setEnableLogSqlExecutionTime(boolean enableLogSqlExecutionTime) { - this.enableLogSqlExecutionTime = enableLogSqlExecutionTime; - } - - public Map, SessionFactory> getSessionFactories() { - return sessionFactories; - } - - public AbstractEngineConfiguration setSessionFactories(Map, SessionFactory> sessionFactories) { - this.sessionFactories = sessionFactories; - return this; - } - - public String getDatabaseSchemaUpdate() { - return databaseSchemaUpdate; - } - - public AbstractEngineConfiguration setDatabaseSchemaUpdate(String databaseSchemaUpdate) { - this.databaseSchemaUpdate = databaseSchemaUpdate; - return this; - } - - public boolean isUseLockForDatabaseSchemaUpdate() { - return useLockForDatabaseSchemaUpdate; - } - - public AbstractEngineConfiguration setUseLockForDatabaseSchemaUpdate(boolean useLockForDatabaseSchemaUpdate) { - this.useLockForDatabaseSchemaUpdate = useLockForDatabaseSchemaUpdate; - return this; - } - - public boolean isEnableEventDispatcher() { - return enableEventDispatcher; - } - - public AbstractEngineConfiguration setEnableEventDispatcher(boolean enableEventDispatcher) { - this.enableEventDispatcher = enableEventDispatcher; - return this; - } - - public FlowableEventDispatcher getEventDispatcher() { - return eventDispatcher; - } - - public AbstractEngineConfiguration setEventDispatcher(FlowableEventDispatcher eventDispatcher) { - this.eventDispatcher = eventDispatcher; - return this; - } - - public List getEventListeners() { - return eventListeners; - } - - public AbstractEngineConfiguration setEventListeners(List eventListeners) { - this.eventListeners = eventListeners; - return this; - } - - public Map> getTypedEventListeners() { - return typedEventListeners; - } - - public AbstractEngineConfiguration setTypedEventListeners(Map> typedEventListeners) { - this.typedEventListeners = typedEventListeners; - return this; - } - - public List getAdditionalEventDispatchActions() { - return additionalEventDispatchActions; - } - - public AbstractEngineConfiguration setAdditionalEventDispatchActions(List additionalEventDispatchActions) { - this.additionalEventDispatchActions = additionalEventDispatchActions; - return this; - } - - public void initEventDispatcher() { - if (this.eventDispatcher == null) { - this.eventDispatcher = new FlowableEventDispatcherImpl(); - } - - initAdditionalEventDispatchActions(); - - this.eventDispatcher.setEnabled(enableEventDispatcher); - - initEventListeners(); - initTypedEventListeners(); - } - - protected void initEventListeners() { - if (eventListeners != null) { - for (FlowableEventListener listenerToAdd : eventListeners) { - this.eventDispatcher.addEventListener(listenerToAdd); - } - } - } - - protected void initAdditionalEventDispatchActions() { - if (this.additionalEventDispatchActions == null) { - this.additionalEventDispatchActions = new ArrayList<>(); - } - } - - protected void initTypedEventListeners() { - if (typedEventListeners != null) { - for (Map.Entry> listenersToAdd : typedEventListeners.entrySet()) { - // Extract types from the given string - FlowableEngineEventType[] types = FlowableEngineEventType.getTypesFromString(listenersToAdd.getKey()); - - for (FlowableEventListener listenerToAdd : listenersToAdd.getValue()) { - this.eventDispatcher.addEventListener(listenerToAdd, types); - } - } - } - } - - public boolean isLoggingSessionEnabled() { - return loggingListener != null; - } - - public LoggingListener getLoggingListener() { - return loggingListener; - } - - public void setLoggingListener(LoggingListener loggingListener) { - this.loggingListener = loggingListener; - } - - public Clock getClock() { - return clock; - } - - public AbstractEngineConfiguration setClock(Clock clock) { - this.clock = clock; - return this; - } - - public ObjectMapper getObjectMapper() { - return objectMapper; - } - - public AbstractEngineConfiguration setObjectMapper(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - return this; - } - - public int getMaxLengthString() { - if (maxLengthStringVariableType == -1) { - if ("oracle".equalsIgnoreCase(databaseType)) { - return DEFAULT_ORACLE_MAX_LENGTH_STRING; - } else { - return DEFAULT_GENERIC_MAX_LENGTH_STRING; - } - } else { - return maxLengthStringVariableType; - } - } - - public int getMaxLengthStringVariableType() { - return maxLengthStringVariableType; - } - - public AbstractEngineConfiguration setMaxLengthStringVariableType(int maxLengthStringVariableType) { - this.maxLengthStringVariableType = maxLengthStringVariableType; - return this; - } - - public PropertyDataManager getPropertyDataManager() { - return propertyDataManager; - } - - public Duration getLockPollRate() { - return lockPollRate; - } - - public AbstractEngineConfiguration setLockPollRate(Duration lockPollRate) { - this.lockPollRate = lockPollRate; - return this; - } - - public Duration getSchemaLockWaitTime() { - return schemaLockWaitTime; - } - - public void setSchemaLockWaitTime(Duration schemaLockWaitTime) { - this.schemaLockWaitTime = schemaLockWaitTime; - } - - public AbstractEngineConfiguration setPropertyDataManager(PropertyDataManager propertyDataManager) { - this.propertyDataManager = propertyDataManager; - return this; - } - - public PropertyEntityManager getPropertyEntityManager() { - return propertyEntityManager; - } - - public AbstractEngineConfiguration setPropertyEntityManager(PropertyEntityManager propertyEntityManager) { - this.propertyEntityManager = propertyEntityManager; - return this; - } - - public ByteArrayDataManager getByteArrayDataManager() { - return byteArrayDataManager; - } - - public AbstractEngineConfiguration setByteArrayDataManager(ByteArrayDataManager byteArrayDataManager) { - this.byteArrayDataManager = byteArrayDataManager; - return this; - } - - public ByteArrayEntityManager getByteArrayEntityManager() { - return byteArrayEntityManager; - } - - public AbstractEngineConfiguration setByteArrayEntityManager(ByteArrayEntityManager byteArrayEntityManager) { - this.byteArrayEntityManager = byteArrayEntityManager; - return this; - } - - public TableDataManager getTableDataManager() { - return tableDataManager; - } - - public AbstractEngineConfiguration setTableDataManager(TableDataManager tableDataManager) { - this.tableDataManager = tableDataManager; - return this; - } - - public List getDeployers() { - return deployers; - } - - public AbstractEngineConfiguration setDeployers(List deployers) { - this.deployers = deployers; - return this; - } - - public List getCustomPreDeployers() { - return customPreDeployers; - } - - public AbstractEngineConfiguration setCustomPreDeployers(List customPreDeployers) { - this.customPreDeployers = customPreDeployers; - return this; - } - - public List getCustomPostDeployers() { - return customPostDeployers; - } - - public AbstractEngineConfiguration setCustomPostDeployers(List customPostDeployers) { - this.customPostDeployers = customPostDeployers; - return this; - } - - public boolean isEnableConfiguratorServiceLoader() { - return enableConfiguratorServiceLoader; - } - - public AbstractEngineConfiguration setEnableConfiguratorServiceLoader(boolean enableConfiguratorServiceLoader) { - this.enableConfiguratorServiceLoader = enableConfiguratorServiceLoader; - return this; - } - - public List getConfigurators() { - return configurators; - } - - public AbstractEngineConfiguration addConfigurator(EngineConfigurator configurator) { - if (configurators == null) { - configurators = new ArrayList<>(); - } - configurators.add(configurator); - return this; - } - - /** - * @return All {@link EngineConfigurator} instances. Will only contain values after init of the engine. - * Use the {@link #getConfigurators()} or {@link #addConfigurator(EngineConfigurator)} methods otherwise. - */ - public List getAllConfigurators() { - return allConfigurators; - } - - public AbstractEngineConfiguration setConfigurators(List configurators) { - this.configurators = configurators; - return this; - } - - public EngineConfigurator getIdmEngineConfigurator() { - return idmEngineConfigurator; - } - - public AbstractEngineConfiguration setIdmEngineConfigurator(EngineConfigurator idmEngineConfigurator) { - this.idmEngineConfigurator = idmEngineConfigurator; - return this; - } - - public EngineConfigurator getEventRegistryConfigurator() { - return eventRegistryConfigurator; - } - - public AbstractEngineConfiguration setEventRegistryConfigurator(EngineConfigurator eventRegistryConfigurator) { - this.eventRegistryConfigurator = eventRegistryConfigurator; - return this; - } - - public AbstractEngineConfiguration setForceCloseMybatisConnectionPool(boolean forceCloseMybatisConnectionPool) { - this.forceCloseMybatisConnectionPool = forceCloseMybatisConnectionPool; - return this; - } - - public boolean isForceCloseMybatisConnectionPool() { - return forceCloseMybatisConnectionPool; - } -} diff --git a/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java b/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java deleted file mode 100644 index ed2f0c9..0000000 --- a/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java +++ /dev/null @@ -1,354 +0,0 @@ -/* 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.flowable.common.engine.impl.db; - -import org.apache.ibatis.session.SqlSessionFactory; -import org.flowable.common.engine.api.FlowableException; -import org.flowable.common.engine.impl.context.Context; -import org.flowable.common.engine.impl.interceptor.CommandContext; -import org.flowable.common.engine.impl.interceptor.Session; -import org.flowable.common.engine.impl.interceptor.SessionFactory; -import org.flowable.common.engine.impl.persistence.cache.EntityCache; -import org.flowable.common.engine.impl.persistence.entity.Entity; - -import java.sql.SQLException; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * @author Tom Baeyens - * @author Joram Barrez - */ -public class DbSqlSessionFactory implements SessionFactory { - - protected Map> databaseSpecificStatements = new HashMap<>(); - - protected String databaseType; - protected String databaseTablePrefix = ""; - protected boolean tablePrefixIsSchema; - - protected String databaseCatalog; - protected String databaseSchema; - protected SqlSessionFactory sqlSessionFactory; - protected Map statementMappings; - - protected Map, String> insertStatements = new ConcurrentHashMap<>(); - protected Map, String> updateStatements = new ConcurrentHashMap<>(); - protected Map, String> deleteStatements = new ConcurrentHashMap<>(); - protected Map, String> selectStatements = new ConcurrentHashMap<>(); - - protected List> insertionOrder = new ArrayList<>(); - protected List> deletionOrder = new ArrayList<>(); - - protected boolean isDbHistoryUsed = true; - - protected Set> bulkInserteableEntityClasses = new HashSet<>(); - protected Map, String> bulkInsertStatements = new ConcurrentHashMap<>(); - - protected int maxNrOfStatementsInBulkInsert = 100; - - protected Map> logicalNameToClassMapping = new ConcurrentHashMap<>(); - - protected boolean usePrefixId; - - public DbSqlSessionFactory(boolean usePrefixId) { - this.usePrefixId = usePrefixId; - } - - @Override - public Class getSessionType() { - return DbSqlSession.class; - } - - @Override - public Session openSession(CommandContext commandContext) { - DbSqlSession dbSqlSession = createDbSqlSession(); - // 当前系统适配 dm,如果存在 schema 为空的情况,从 connection 获取 - try { - if (getDatabaseSchema() == null || getDatabaseSchema().length() == 0){ - setDatabaseSchema(dbSqlSession.getSqlSession().getConnection().getSchema()); - } - dbSqlSession.getSqlSession().getConnection().getSchema(); - } catch (SQLException e) { - throw new RuntimeException(e); - } - - if (getDatabaseSchema() != null && getDatabaseSchema().length() > 0) { - try { - dbSqlSession.getSqlSession().getConnection().setSchema(getDatabaseSchema()); - } catch (SQLException e) { - throw new FlowableException("Could not set database schema on connection", e); - } - } - if (getDatabaseCatalog() != null && getDatabaseCatalog().length() > 0) { - try { - dbSqlSession.getSqlSession().getConnection().setCatalog(getDatabaseCatalog()); - } catch (SQLException e) { - throw new FlowableException("Could not set database catalog on connection", e); - } - } - if (dbSqlSession.getSqlSession().getConnection() == null) { - throw new FlowableException("Invalid dbSqlSession: no active connection found"); - } - return dbSqlSession; - } - - protected DbSqlSession createDbSqlSession() { - return new DbSqlSession(this, Context.getCommandContext().getSession(EntityCache.class)); - } - - // insert, update and delete statements - // ///////////////////////////////////// - - public String getInsertStatement(Entity object) { - return getStatement(object.getClass(), insertStatements, "insert"); - } - - public String getInsertStatement(Class clazz) { - return getStatement(clazz, insertStatements, "insert"); - } - - public String getUpdateStatement(Entity object) { - return getStatement(object.getClass(), updateStatements, "update"); - } - - public String getDeleteStatement(Class entityClass) { - return getStatement(entityClass, deleteStatements, "delete"); - } - - public String getSelectStatement(Class entityClass) { - return getStatement(entityClass, selectStatements, "select"); - } - - protected String getStatement(Class entityClass, Map, String> cachedStatements, String prefix) { - String statement = cachedStatements.get(entityClass); - if (statement != null) { - return statement; - } - statement = prefix + entityClass.getSimpleName(); - if (statement.endsWith("Impl")) { - statement = statement.substring(0, statement.length() - 10); // removing 'entityImpl' - } else { - statement = statement.substring(0, statement.length() - 6); // removing 'entity' - } - cachedStatements.put(entityClass, statement); - return statement; - } - - // db specific mappings - // ///////////////////////////////////////////////////// - - protected void addDatabaseSpecificStatement(String databaseType, String activitiStatement, String ibatisStatement) { - Map specificStatements = databaseSpecificStatements.get(databaseType); - if (specificStatements == null) { - specificStatements = new HashMap<>(); - databaseSpecificStatements.put(databaseType, specificStatements); - } - specificStatements.put(activitiStatement, ibatisStatement); - } - - public String mapStatement(String statement) { - if (statementMappings == null) { - return statement; - } - String mappedStatement = statementMappings.get(statement); - return (mappedStatement != null ? mappedStatement : statement); - } - - // customized getters and setters - // /////////////////////////////////////////// - - public void setDatabaseType(String databaseType) { - this.databaseType = databaseType; - this.statementMappings = databaseSpecificStatements.get(databaseType); - } - - public boolean isMysql() { - return "mysql".equals(getDatabaseType()); - } - - public boolean isOracle() { - return "oracle".equals(getDatabaseType()); - } - - public Boolean isBulkInsertable(Class entityClass) { - return bulkInserteableEntityClasses != null && bulkInserteableEntityClasses.contains(entityClass); - } - - @SuppressWarnings("rawtypes") - public String getBulkInsertStatement(Class clazz) { - return getStatement(clazz, bulkInsertStatements, "bulkInsert"); - } - - public Set> getBulkInserteableEntityClasses() { - return bulkInserteableEntityClasses; - } - - public void setBulkInserteableEntityClasses(Set> bulkInserteableEntityClasses) { - this.bulkInserteableEntityClasses = bulkInserteableEntityClasses; - } - - public int getMaxNrOfStatementsInBulkInsert() { - return maxNrOfStatementsInBulkInsert; - } - - public void setMaxNrOfStatementsInBulkInsert(int maxNrOfStatementsInBulkInsert) { - this.maxNrOfStatementsInBulkInsert = maxNrOfStatementsInBulkInsert; - } - - public Map, String> getBulkInsertStatements() { - return bulkInsertStatements; - } - - public void setBulkInsertStatements(Map, String> bulkInsertStatements) { - this.bulkInsertStatements = bulkInsertStatements; - } - - // getters and setters ////////////////////////////////////////////////////// - - public SqlSessionFactory getSqlSessionFactory() { - return sqlSessionFactory; - } - - public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { - this.sqlSessionFactory = sqlSessionFactory; - } - - public String getDatabaseType() { - return databaseType; - } - - public Map> getDatabaseSpecificStatements() { - return databaseSpecificStatements; - } - - public void setDatabaseSpecificStatements(Map> databaseSpecificStatements) { - this.databaseSpecificStatements = databaseSpecificStatements; - } - - public Map getStatementMappings() { - return statementMappings; - } - - public void setStatementMappings(Map statementMappings) { - this.statementMappings = statementMappings; - } - - public Map, String> getInsertStatements() { - return insertStatements; - } - - public void setInsertStatements(Map, String> insertStatements) { - this.insertStatements = insertStatements; - } - - public Map, String> getUpdateStatements() { - return updateStatements; - } - - public void setUpdateStatements(Map, String> updateStatements) { - this.updateStatements = updateStatements; - } - - public Map, String> getDeleteStatements() { - return deleteStatements; - } - - public void setDeleteStatements(Map, String> deleteStatements) { - this.deleteStatements = deleteStatements; - } - - public Map, String> getSelectStatements() { - return selectStatements; - } - - public void setSelectStatements(Map, String> selectStatements) { - this.selectStatements = selectStatements; - } - - public boolean isDbHistoryUsed() { - return isDbHistoryUsed; - } - - public void setDbHistoryUsed(boolean isDbHistoryUsed) { - this.isDbHistoryUsed = isDbHistoryUsed; - } - - public void setDatabaseTablePrefix(String databaseTablePrefix) { - this.databaseTablePrefix = databaseTablePrefix; - } - - public String getDatabaseTablePrefix() { - return databaseTablePrefix; - } - - public String getDatabaseCatalog() { - return databaseCatalog; - } - - public void setDatabaseCatalog(String databaseCatalog) { - this.databaseCatalog = databaseCatalog; - } - - public String getDatabaseSchema() { - return databaseSchema; - } - - public void setDatabaseSchema(String databaseSchema) { - this.databaseSchema = databaseSchema; - } - - public void setTablePrefixIsSchema(boolean tablePrefixIsSchema) { - this.tablePrefixIsSchema = tablePrefixIsSchema; - } - - public boolean isTablePrefixIsSchema() { - return tablePrefixIsSchema; - } - - public List> getInsertionOrder() { - return insertionOrder; - } - - public void setInsertionOrder(List> insertionOrder) { - this.insertionOrder = insertionOrder; - } - - public List> getDeletionOrder() { - return deletionOrder; - } - - public void setDeletionOrder(List> deletionOrder) { - this.deletionOrder = deletionOrder; - } - public void addLogicalEntityClassMapping(String logicalName, Class entityClass) { - logicalNameToClassMapping.put(logicalName, entityClass); - } - - public Map> getLogicalNameToClassMapping() { - return logicalNameToClassMapping; - } - - public void setLogicalNameToClassMapping(Map> logicalNameToClassMapping) { - this.logicalNameToClassMapping = logicalNameToClassMapping; - } - - public boolean isUsePrefixId() { - return usePrefixId; - } - - public void setUsePrefixId(boolean usePrefixId) { - this.usePrefixId = usePrefixId; - } -} diff --git a/zt-module-bpm-server/src/main/resources/META-INF/package-info.md b/zt-module-bpm-server/src/main/resources/META-INF/package-info.md deleted file mode 100644 index 1932c7a..0000000 --- a/zt-module-bpm-server/src/main/resources/META-INF/package-info.md +++ /dev/null @@ -1 +0,0 @@ -防止IDEA将`.`和`/`混为一谈 \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database b/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database deleted file mode 100644 index 765e41a..0000000 --- a/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database +++ /dev/null @@ -1,21 +0,0 @@ -liquibase.database.core.CockroachDatabase -liquibase.database.core.DB2Database -liquibase.database.core.Db2zDatabase -liquibase.database.core.DerbyDatabase -#liquibase.database.core.Firebird3Database -liquibase.database.core.FirebirdDatabase -liquibase.database.core.H2Database -liquibase.database.core.HsqlDatabase -liquibase.database.core.InformixDatabase -liquibase.database.core.Ingres9Database -liquibase.database.core.MSSQLDatabase -liquibase.database.core.MariaDBDatabase -liquibase.database.core.MockDatabase -liquibase.database.core.MySQLDatabase -liquibase.database.core.OracleDatabase -liquibase.database.core.DmDatabase -liquibase.database.core.PostgresDatabase -liquibase.database.core.SQLiteDatabase -liquibase.database.core.SybaseASADatabase -liquibase.database.core.SybaseDatabase -liquibase.database.core.UnsupportedDatabase diff --git a/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType b/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType deleted file mode 100644 index 5be88a3..0000000 --- a/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType +++ /dev/null @@ -1 +0,0 @@ -liquibase.datatype.core.DmBooleanType diff --git a/zt-module-bpm-server/src/main/resources/application-dev.yaml b/zt-module-bpm-server/src/main/resources/application-dev.yaml deleted file mode 100644 index 26fb687..0000000 --- a/zt-module-bpm-server/src/main/resources/application-dev.yaml +++ /dev/null @@ -1,94 +0,0 @@ ---- #################### 数据库相关配置 #################### -spring: - # 数据源配置项 - autoconfigure: - exclude: - datasource: - druid: # Druid 【监控】相关的全局配置 - web-stat-filter: - enabled: true - stat-view-servlet: - enabled: true - allow: # 设置白名单,不填则允许所有访问 - url-pattern: /druid/* - login-username: # 控制台管理用户名和密码 - login-password: - filter: - stat: - enabled: true - log-slow-sql: true # 慢 SQL 记录 - slow-sql-millis: 100 - merge-sql: true - wall: - config: - multi-statement-allow: true - dynamic: # 多数据源配置 - druid: # Druid 【连接池】相关的全局配置 - initial-size: 5 # 初始连接数 - min-idle: 10 # 最小连接池数量 - max-active: 20 # 最大连接池数量 - max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 - time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 - min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 - max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 - validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 - test-while-idle: true - test-on-borrow: false - test-on-return: false - primary: master - datasource: - master: - url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 - username: jygk-test - password: Zgty@0527 - slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 - lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 - username: jygk-test - password: Zgty@0527 - - # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 - data: - redis: - host: 172.16.46.63 # 地址 - port: 30379 # 端口 - database: 1 # 数据库索引 -# password: 123456 # 密码,建议生产环境开启 - ---- #################### MQ 消息队列相关配置 #################### - ---- #################### 定时任务相关配置 #################### -xxl: - job: - admin: - addresses: http://172.16.46.63:30082/xxl-job-admin # 调度中心部署跟地址 - ---- #################### 服务保障相关配置 #################### - -# Lock4j 配置项 -lock4j: - acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 - expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 - ---- #################### 监控相关配置 #################### - -# Actuator 监控端点的配置项 -management: - endpoints: - web: - base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator - exposure: - include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 - -# Spring Boot Admin 配置项 -spring: - boot: - admin: - # Spring Boot Admin Client 客户端的相关配置 - client: - instance: - service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] - ---- #################### ZT相关配置 #################### - - diff --git a/zt-module-bpm-server/src/main/resources/application-local.yaml b/zt-module-bpm-server/src/main/resources/application-local.yaml deleted file mode 100644 index ae73a0b..0000000 --- a/zt-module-bpm-server/src/main/resources/application-local.yaml +++ /dev/null @@ -1,116 +0,0 @@ ---- #################### 数据库相关配置 #################### -spring: - # 数据源配置项 - autoconfigure: - exclude: - - de.codecentric.boot.admin.client.config.SpringBootAdminClientAutoConfiguration # 禁用 Spring Boot Admin 的 Client 的自动配置 - datasource: - druid: # Druid 【监控】相关的全局配置 - web-stat-filter: - enabled: true - stat-view-servlet: - enabled: true - allow: # 设置白名单,不填则允许所有访问 - url-pattern: /druid/* - login-username: # 控制台管理用户名和密码 - login-password: - filter: - stat: - enabled: true - log-slow-sql: true # 慢 SQL 记录 - slow-sql-millis: 100 - merge-sql: true - wall: - config: - multi-statement-allow: true - dynamic: # 多数据源配置 - druid: # Druid 【连接池】相关的全局配置 - initial-size: 1 # 初始连接数 - min-idle: 1 # 最小连接池数量 - max-active: 20 # 最大连接池数量 - max-wait: 600000 # 配置获取连接等待超时的时间,单位:毫秒 - time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位:毫秒 - min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位:毫秒 - max-evictable-idle-time-millis: 900000 # 配置一个连接在池中最大生存的时间,单位:毫秒 - validation-query: SELECT 1 FROM DUAL # 配置检测连接是否有效 - test-while-idle: true - test-on-borrow: false - test-on-return: false - primary: master - datasource: - master: - url: jdbc:dm://172.16.46.247:1050?schema=BPM - username: SYSDBA - password: pgbsci6ddJ6Sqj@e - slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 - lazy: true # 开启懒加载,保证启动速度 - url: jdbc:dm://172.16.46.247:1050?schema=BPM - username: SYSDBA - password: pgbsci6ddJ6Sqj@e - - # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 - data: - redis: - host: 172.16.46.64 # 地址 - port: 30379 # 端口 - database: 0 # 数据库索引 -# password: 123456 # 密码,建议生产环境开启 - -# Flowable 在 DM 场景下需要识别为 Oracle 并自动升级表结构 -flowable: - database-schema-update: true - database-type: oracle - ---- #################### MQ 消息队列相关配置 #################### - ---- #################### 定时任务相关配置 #################### - -xxl: - job: - enabled: false # 是否开启调度中心,默认为 true 开启 - admin: - addresses: http://172.16.46.63:30082/xxl-job-admin # 调度中心部署跟地址 - ---- #################### 服务保障相关配置 #################### - -# Lock4j 配置项 -lock4j: - acquire-timeout: 3000 # 获取分布式锁超时时间,默认为 3000 毫秒 - expire: 30000 # 分布式锁的超时时间,默认为 30 毫秒 - ---- #################### 监控相关配置 #################### - -# Actuator 监控端点的配置项 -management: - endpoints: - web: - base-path: /actuator # Actuator 提供的 API 接口的根目录。默认为 /actuator - exposure: - include: '*' # 需要开放的端点。默认值只打开 health 和 info 两个端点。通过设置 * ,可以开放所有端点。 - -# Spring Boot Admin 配置项 -spring: - boot: - admin: - # Spring Boot Admin Client 客户端的相关配置 - client: - instance: - service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] - -# 日志文件配置 -logging: - level: - # 配置自己写的 MyBatis Mapper 打印日志 - com.zt.plat.module.bpm.dal.mysql: debug - org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO ZT:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 - ---- #################### ZT相关配置 #################### - -# ZT配置项,设置当前项目所有自定义的配置 -zt: - env: # 多环境的配置项 - tag: ${HOSTNAME} - security: - mock-enable: true - access-log: # 访问日志的配置项 - enable: false \ No newline at end of file diff --git a/zt-module-bpm-server/src/main/resources/application.yaml b/zt-module-bpm-server/src/main/resources/application.yaml deleted file mode 100644 index bf3f40d..0000000 --- a/zt-module-bpm-server/src/main/resources/application.yaml +++ /dev/null @@ -1,150 +0,0 @@ -spring: - application: - name: bpm-server - - profiles: - active: ${env.name} - #统一nacos配置,使用 profile 管理 - cloud: - nacos: - server-addr: ${config.server-addr} # Nacos 服务器地址 - username: ${config.username} # Nacos 账号 - password: ${config.password} # Nacos 密码 - discovery: # 【配置中心】配置项 - namespace: ${config.namespace} # 命名空间。这里使用 maven Profile 资源过滤进行动态替换 - group: ${config.group} # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP - metadata: - version: 1.0.0 # 服务实例的版本号,可用于灰度发布 - config: # 【注册中心】配置项 - namespace: ${config.namespace} # 命名空间。这里使用 maven Profile 资源过滤进行动态替换 - group: ${config.group} # 使用的 Nacos 配置分组,默认为 DEFAULT_GROUP - - main: - allow-circular-references: true # 允许循环依赖,因为项目是三层架构,无法避免这个情况。 - allow-bean-definition-overriding: true # 允许 Bean 覆盖,例如说 Feign 等会存在重复定义的服务 - - config: - import: - - optional:classpath:application-${spring.profiles.active}.yaml # 加载【本地】配置 - - optional:nacos:${spring.application.name}-${spring.profiles.active}.yaml # 加载【Nacos】的配置 - - # Servlet 配置 - servlet: - # 文件上传相关配置项 - multipart: - max-file-size: 16MB # 单个文件大小 - max-request-size: 32MB # 设置总上传的文件大小 - - # Jackson 配置项 - jackson: - serialization: - write-dates-as-timestamps: true # 设置 LocalDateTime 的格式,使用时间戳 - write-date-timestamps-as-nanoseconds: false # 设置不使用 nanoseconds 的格式。例如说 1611460870.401,而是直接 1611460870401 - write-durations-as-timestamps: true # 设置 Duration 的格式,使用时间戳 - fail-on-empty-beans: false # 允许序列化无属性的 Bean - - # Cache 配置项 - cache: - type: REDIS - redis: - time-to-live: 1h # 设置过期时间为 1 小时 - -server: - port: 48083 - -logging: - file: - name: ${user.home}/logs/${spring.application.name}.log # 日志文件名,全路径 - ---- #################### 接口文档配置 #################### - -springdoc: - api-docs: - enabled: true # 1. 是否开启 Swagger 接文档的元数据 - path: /v3/api-docs - swagger-ui: - enabled: true # 2.1 是否开启 Swagger 文档的官方 UI 界面 - path: /swagger-ui - default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 - -knife4j: - enable: false # TODO ZT:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 - setting: - language: zh_cn - -# 工作流 Flowable 配置 -flowable: - # 1. false: 默认值,Flowable 启动时,对比数据库表中保存的版本,如果不匹配。将抛出异常 - # 2. true: 启动时会对数据库中所有表进行更新操作,如果表存在,不做处理,反之,自动创建表 - # 3. create_drop: 启动时自动创建表,关闭时自动删除表 - # 4. drop_create: 启动时,删除旧表,再创建新表 - database-schema-update: false # 设置为 false,可通过 https://github.com/flowable/flowable-sql 初始化 - db-history-used: true # flowable6 默认 true 生成信息表,无需手动设置 - check-process-definitions: false # 设置为 false,禁用 /resources/processes 自动部署 BPMN XML 流程 - history-level: audit # full:保存历史数据的最高级别,可保存全部流程相关细节,包括流程流转各节点参数 - -# MyBatis Plus 的配置项 -mybatis-plus: - configuration: - map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。 - global-config: - db-config: - id-type: NONE # “智能”模式,基于 IdTypeEnvironmentPostProcessor + 数据源的类型,自动适配成 AUTO、INPUT 模式。 - # id-type: AUTO # 自增 ID,适合 MySQL 等直接自增的数据库 - # id-type: INPUT # 用户输入 ID,适合 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库 - # id-type: ASSIGN_ID # 分配 ID,默认使用雪花算法。注意,Oracle、PostgreSQL、Kingbase、DB2、H2 数据库时,需要去除实体类上的 @KeySequence 注解 - logic-delete-value: 1 # 逻辑已删除值(默认为 1) - logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) - banner: false # 关闭控制台的 Banner 打印 - type-aliases-package: ${zt.info.base-package}.dal.dataobject - encryptor: - password: XDV71a+xqStEA3WH # 加解密的秘钥,可使用 https://www.imaegoo.com/2020/aes-key-generator/ 网站生成 - -mybatis-plus-join: - banner: false # 关闭控制台的 Banner 打印 - -# Spring Data Redis 配置 -spring: - data: - redis: - repositories: - enabled: false # 项目未使用到 Spring Data Redis 的 Repository,所以直接禁用,保证启动速度 - -# VO 转换(数据翻译)相关 -easy-trans: - is-enable-global: true # 启用全局翻译(拦截所有 SpringMVC ResponseBody 进行自动翻译 )。如果对于性能要求很高可关闭此配置,或通过 @IgnoreTrans 忽略某个接口 - ---- #################### RPC 远程调用相关配置 #################### - ---- #################### MQ 消息队列相关配置 #################### - ---- #################### 定时任务相关配置 #################### - -xxl: - job: - executor: - appname: ${spring.application.name} # 执行器 AppName - logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 - accessToken: default_token # 执行器通讯TOKEN - ---- #################### ZT相关配置 #################### - -zt: - info: - version: 1.0.0 - base-package: com.zt.plat.module.bpm - web: - admin-ui: - url: http://dashboard.zt.iocoder.cn # Admin 管理后台 UI 的地址 - xss: - enable: false - exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 - - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 - swagger: - title: 流程模块 - description: 提供流程模块功能 - version: ${zt.info.version} - tenant: # 多租户相关配置项 - enable: true - -debug: false diff --git a/zt-module-bpm-server/src/main/resources/logback-spring.xml b/zt-module-bpm-server/src/main/resources/logback-spring.xml deleted file mode 100644 index 51a9a48..0000000 --- a/zt-module-bpm-server/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - -       - - - ${PATTERN_DEFAULT} - - - - - - - - - - ${PATTERN_DEFAULT} - - - - ${LOG_FILE} - - - ${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz} - - ${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false} - - ${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB} - - ${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-0} - - ${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30} - - - - - - 0 - - 256 - - - - - - - - ${PATTERN_DEFAULT} - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql b/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql deleted file mode 100644 index 19dca82..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql +++ /dev/null @@ -1,41 +0,0 @@ -create table FLW_RU_BATCH ( - ID_ VARCHAR2(64) not null, - REV_ INTEGER, - TYPE_ VARCHAR2(64) not null, - SEARCH_KEY_ VARCHAR2(255), - SEARCH_KEY2_ VARCHAR2(255), - CREATE_TIME_ TIMESTAMP(6) not null, - COMPLETE_TIME_ TIMESTAMP(6), - STATUS_ VARCHAR2(255), - BATCH_DOC_ID_ VARCHAR2(64), - TENANT_ID_ VARCHAR2(255) default '', - primary key (ID_) -); - -create table FLW_RU_BATCH_PART ( - ID_ VARCHAR2(64) not null, - REV_ INTEGER, - BATCH_ID_ VARCHAR2(64), - TYPE_ VARCHAR2(64) not null, - SCOPE_ID_ VARCHAR2(64), - SUB_SCOPE_ID_ VARCHAR2(64), - SCOPE_TYPE_ VARCHAR2(64), - SEARCH_KEY_ VARCHAR2(255), - SEARCH_KEY2_ VARCHAR2(255), - CREATE_TIME_ TIMESTAMP(6) not null, - COMPLETE_TIME_ TIMESTAMP(6), - STATUS_ VARCHAR2(255), - RESULT_DOC_ID_ VARCHAR2(64), - TENANT_ID_ VARCHAR2(255) default '', - primary key (ID_) -); - -create index FLW_IDX_BATCH_PART on FLW_RU_BATCH_PART(BATCH_ID_); - -alter table FLW_RU_BATCH_PART - add constraint FLW_FK_BATCH_PART_PARENT - foreign key (BATCH_ID_) - references FLW_RU_BATCH (ID_); - -insert into ACT_GE_PROPERTY values ('batch.schema.version', '7.0.1.1', 1); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql b/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql deleted file mode 100644 index d16ba1c..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql +++ /dev/null @@ -1,4 +0,0 @@ -drop index FLW_IDX_BATCH_PART; - -drop table FLW_RU_BATCH_PART; -drop table FLW_RU_BATCH; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql b/zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql deleted file mode 100644 index 4ef0d2e..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql +++ /dev/null @@ -1,23 +0,0 @@ -create table ACT_GE_PROPERTY ( - NAME_ VARCHAR2(64), - VALUE_ VARCHAR2(300), - REV_ INTEGER, - primary key (NAME_) -); - -create table ACT_GE_BYTEARRAY ( - ID_ VARCHAR2(64), - REV_ INTEGER, - NAME_ VARCHAR2(255), - DEPLOYMENT_ID_ VARCHAR2(64), - BYTES_ BLOB, - GENERATED_ NUMBER(1) CHECK (GENERATED_ IN (1,0)), - primary key (ID_) -); - -insert into ACT_GE_PROPERTY -values ('common.schema.version', '7.0.1.1', 1); - -insert into ACT_GE_PROPERTY -values ('next.dbid', '1', 1); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql b/zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql deleted file mode 100644 index 9019cb9..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql +++ /dev/null @@ -1,2 +0,0 @@ -drop table ACT_GE_BYTEARRAY; -drop table ACT_GE_PROPERTY; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql b/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql deleted file mode 100644 index d0139b7..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql +++ /dev/null @@ -1,355 +0,0 @@ -create table ACT_RE_DEPLOYMENT ( - ID_ VARCHAR2(64), - NAME_ VARCHAR2(255), - CATEGORY_ VARCHAR2(255), - KEY_ VARCHAR2(255), - TENANT_ID_ VARCHAR2(255) DEFAULT '', - DEPLOY_TIME_ TIMESTAMP(6), - DERIVED_FROM_ VARCHAR2(64), - DERIVED_FROM_ROOT_ VARCHAR2(64), - PARENT_DEPLOYMENT_ID_ VARCHAR2(255), - ENGINE_VERSION_ VARCHAR2(255), - primary key (ID_) -); - -create table ACT_RE_MODEL ( - ID_ VARCHAR2(64) not null, - REV_ INTEGER, - NAME_ VARCHAR2(255), - KEY_ VARCHAR2(255), - CATEGORY_ VARCHAR2(255), - CREATE_TIME_ TIMESTAMP(6), - LAST_UPDATE_TIME_ TIMESTAMP(6), - VERSION_ INTEGER, - META_INFO_ VARCHAR2(2000), - DEPLOYMENT_ID_ VARCHAR2(64), - EDITOR_SOURCE_VALUE_ID_ VARCHAR2(64), - EDITOR_SOURCE_EXTRA_VALUE_ID_ VARCHAR2(64), - TENANT_ID_ VARCHAR2(255) DEFAULT '', - primary key (ID_) -); - -create table ACT_RU_EXECUTION ( - ID_ VARCHAR2(64), - REV_ INTEGER, - PROC_INST_ID_ VARCHAR2(64), - BUSINESS_KEY_ VARCHAR2(255), - PARENT_ID_ VARCHAR2(64), - PROC_DEF_ID_ VARCHAR2(64), - SUPER_EXEC_ VARCHAR2(64), - ROOT_PROC_INST_ID_ VARCHAR2(64), - ACT_ID_ VARCHAR2(255), - IS_ACTIVE_ NUMBER(1) CHECK (IS_ACTIVE_ IN (1,0)), - IS_CONCURRENT_ NUMBER(1) CHECK (IS_CONCURRENT_ IN (1,0)), - IS_SCOPE_ NUMBER(1) CHECK (IS_SCOPE_ IN (1,0)), - IS_EVENT_SCOPE_ NUMBER(1) CHECK (IS_EVENT_SCOPE_ IN (1,0)), - IS_MI_ROOT_ NUMBER(1) CHECK (IS_MI_ROOT_ IN (1,0)), - SUSPENSION_STATE_ INTEGER, - CACHED_ENT_STATE_ INTEGER, - TENANT_ID_ VARCHAR2(255) DEFAULT '', - NAME_ VARCHAR2(255), - START_ACT_ID_ VARCHAR2(255), - START_TIME_ TIMESTAMP(6), - START_USER_ID_ VARCHAR2(255), - LOCK_TIME_ TIMESTAMP(6), - LOCK_OWNER_ VARCHAR2(255), - IS_COUNT_ENABLED_ NUMBER(1) CHECK (IS_COUNT_ENABLED_ IN (1,0)), - EVT_SUBSCR_COUNT_ INTEGER, - TASK_COUNT_ INTEGER, - JOB_COUNT_ INTEGER, - TIMER_JOB_COUNT_ INTEGER, - SUSP_JOB_COUNT_ INTEGER, - DEADLETTER_JOB_COUNT_ INTEGER, - EXTERNAL_WORKER_JOB_COUNT_ INTEGER, - VAR_COUNT_ INTEGER, - ID_LINK_COUNT_ INTEGER, - CALLBACK_ID_ VARCHAR2(255), - CALLBACK_TYPE_ VARCHAR2(255), - REFERENCE_ID_ VARCHAR2(255), - REFERENCE_TYPE_ VARCHAR2(255), - PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), - BUSINESS_STATUS_ VARCHAR2(255), - primary key (ID_) -); - -create table ACT_RE_PROCDEF ( - ID_ VARCHAR2(64) NOT NULL, - REV_ INTEGER, - CATEGORY_ VARCHAR2(255), - NAME_ VARCHAR2(255), - KEY_ VARCHAR2(255) NOT NULL, - VERSION_ INTEGER NOT NULL, - DEPLOYMENT_ID_ VARCHAR2(64), - RESOURCE_NAME_ VARCHAR2(2000), - DGRM_RESOURCE_NAME_ VARCHAR2(4000), - DESCRIPTION_ VARCHAR2(2000), - HAS_START_FORM_KEY_ NUMBER(1) CHECK (HAS_START_FORM_KEY_ IN (1,0)), - HAS_GRAPHICAL_NOTATION_ NUMBER(1) CHECK (HAS_GRAPHICAL_NOTATION_ IN (1,0)), - SUSPENSION_STATE_ INTEGER, - TENANT_ID_ VARCHAR2(255) DEFAULT '', - DERIVED_FROM_ VARCHAR2(64), - DERIVED_FROM_ROOT_ VARCHAR2(64), - DERIVED_VERSION_ INTEGER DEFAULT 0 NOT NULL, - ENGINE_VERSION_ VARCHAR2(255), - primary key (ID_) -); - -create table ACT_EVT_LOG ( - LOG_NR_ NUMBER(19), - TYPE_ VARCHAR2(64), - PROC_DEF_ID_ VARCHAR2(64), - PROC_INST_ID_ VARCHAR2(64), - EXECUTION_ID_ VARCHAR2(64), - TASK_ID_ VARCHAR2(64), - TIME_STAMP_ TIMESTAMP(6) not null, - USER_ID_ VARCHAR2(255), - DATA_ BLOB, - LOCK_OWNER_ VARCHAR2(255), - LOCK_TIME_ TIMESTAMP(6) null, - IS_PROCESSED_ NUMBER(3) default 0, - primary key (LOG_NR_) -); - -create sequence act_evt_log_seq; - -create table ACT_PROCDEF_INFO ( - ID_ VARCHAR2(64) not null, - PROC_DEF_ID_ VARCHAR2(64) not null, - REV_ integer, - INFO_JSON_ID_ VARCHAR2(64), - primary key (ID_) -); - -create table ACT_RU_ACTINST ( - ID_ VARCHAR2(64) not null, - REV_ INTEGER default 1, - PROC_DEF_ID_ VARCHAR2(64) not null, - PROC_INST_ID_ VARCHAR2(64) not null, - EXECUTION_ID_ VARCHAR2(64) not null, - ACT_ID_ VARCHAR2(255) not null, - TASK_ID_ VARCHAR2(64), - CALL_PROC_INST_ID_ VARCHAR2(64), - ACT_NAME_ VARCHAR2(255), - ACT_TYPE_ VARCHAR2(255) not null, - ASSIGNEE_ VARCHAR2(255), - START_TIME_ TIMESTAMP(6) not null, - END_TIME_ TIMESTAMP(6), - DURATION_ NUMBER(19,0), - TRANSACTION_ORDER_ INTEGER, - DELETE_REASON_ VARCHAR2(2000), - TENANT_ID_ VARCHAR2(255) default '', - primary key (ID_) -); - -create index ACT_IDX_EXEC_BUSKEY on ACT_RU_EXECUTION(BUSINESS_KEY_); -create index ACT_IDX_EXEC_ROOT on ACT_RU_EXECUTION(ROOT_PROC_INST_ID_); -create index ACT_IDX_EXEC_REF_ID_ on ACT_RU_EXECUTION(REFERENCE_ID_); -create index ACT_IDX_VARIABLE_TASK_ID on ACT_RU_VARIABLE(TASK_ID_); - -create index ACT_IDX_RU_ACTI_START on ACT_RU_ACTINST(START_TIME_); -create index ACT_IDX_RU_ACTI_END on ACT_RU_ACTINST(END_TIME_); -create index ACT_IDX_RU_ACTI_PROC on ACT_RU_ACTINST(PROC_INST_ID_); -create index ACT_IDX_RU_ACTI_PROC_ACT on ACT_RU_ACTINST(PROC_INST_ID_, ACT_ID_); -create index ACT_IDX_RU_ACTI_EXEC on ACT_RU_ACTINST(EXECUTION_ID_); -create index ACT_IDX_RU_ACTI_EXEC_ACT on ACT_RU_ACTINST(EXECUTION_ID_, ACT_ID_); -create index ACT_IDX_RU_ACTI_TASK on ACT_RU_ACTINST(TASK_ID_); - -create index ACT_IDX_BYTEAR_DEPL on ACT_GE_BYTEARRAY(DEPLOYMENT_ID_); -alter table ACT_GE_BYTEARRAY - add constraint ACT_FK_BYTEARR_DEPL - foreign key (DEPLOYMENT_ID_) - references ACT_RE_DEPLOYMENT (ID_); - -alter table ACT_RE_PROCDEF - add constraint ACT_UNIQ_PROCDEF - unique (KEY_,VERSION_, DERIVED_VERSION_, TENANT_ID_); - -create index ACT_IDX_EXE_PROCINST on ACT_RU_EXECUTION(PROC_INST_ID_); -alter table ACT_RU_EXECUTION - add constraint ACT_FK_EXE_PROCINST - foreign key (PROC_INST_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_EXE_PARENT on ACT_RU_EXECUTION(PARENT_ID_); -alter table ACT_RU_EXECUTION - add constraint ACT_FK_EXE_PARENT - foreign key (PARENT_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_EXE_SUPER on ACT_RU_EXECUTION(SUPER_EXEC_); -alter table ACT_RU_EXECUTION - add constraint ACT_FK_EXE_SUPER - foreign key (SUPER_EXEC_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_EXE_PROCDEF on ACT_RU_EXECUTION(PROC_DEF_ID_); -alter table ACT_RU_EXECUTION - add constraint ACT_FK_EXE_PROCDEF - foreign key (PROC_DEF_ID_) - references ACT_RE_PROCDEF (ID_); - -create index ACT_IDX_TSKASS_TASK on ACT_RU_IDENTITYLINK(TASK_ID_); -alter table ACT_RU_IDENTITYLINK - add constraint ACT_FK_TSKASS_TASK - foreign key (TASK_ID_) - references ACT_RU_TASK (ID_); - -create index ACT_IDX_ATHRZ_PROCEDEF on ACT_RU_IDENTITYLINK(PROC_DEF_ID_); -alter table ACT_RU_IDENTITYLINK - add constraint ACT_FK_ATHRZ_PROCEDEF - foreign key (PROC_DEF_ID_) - references ACT_RE_PROCDEF (ID_); - -create index ACT_IDX_IDL_PROCINST on ACT_RU_IDENTITYLINK(PROC_INST_ID_); -alter table ACT_RU_IDENTITYLINK - add constraint ACT_FK_IDL_PROCINST - foreign key (PROC_INST_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_TASK_EXEC on ACT_RU_TASK(EXECUTION_ID_); -alter table ACT_RU_TASK - add constraint ACT_FK_TASK_EXE - foreign key (EXECUTION_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_TASK_PROCINST on ACT_RU_TASK(PROC_INST_ID_); -alter table ACT_RU_TASK - add constraint ACT_FK_TASK_PROCINST - foreign key (PROC_INST_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_TASK_PROCDEF on ACT_RU_TASK(PROC_DEF_ID_); -alter table ACT_RU_TASK - add constraint ACT_FK_TASK_PROCDEF - foreign key (PROC_DEF_ID_) - references ACT_RE_PROCDEF (ID_); - -create index ACT_IDX_VAR_EXE on ACT_RU_VARIABLE(EXECUTION_ID_); -alter table ACT_RU_VARIABLE - add constraint ACT_FK_VAR_EXE - foreign key (EXECUTION_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_VAR_PROCINST on ACT_RU_VARIABLE(PROC_INST_ID_); -alter table ACT_RU_VARIABLE - add constraint ACT_FK_VAR_PROCINST - foreign key (PROC_INST_ID_) - references ACT_RU_EXECUTION(ID_); - -create index ACT_IDX_JOB_EXECUTION_ID on ACT_RU_JOB(EXECUTION_ID_); -alter table ACT_RU_JOB - add constraint ACT_FK_JOB_EXECUTION - foreign key (EXECUTION_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_JOB_PROC_INST_ID on ACT_RU_JOB(PROCESS_INSTANCE_ID_); -alter table ACT_RU_JOB - add constraint ACT_FK_JOB_PROCESS_INSTANCE - foreign key (PROCESS_INSTANCE_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_JOB_PROC_DEF_ID on ACT_RU_JOB(PROC_DEF_ID_); -alter table ACT_RU_JOB - add constraint ACT_FK_JOB_PROC_DEF - foreign key (PROC_DEF_ID_) - references ACT_RE_PROCDEF (ID_); - -create index ACT_IDX_TJOB_EXECUTION_ID on ACT_RU_TIMER_JOB(EXECUTION_ID_); -alter table ACT_RU_TIMER_JOB - add constraint ACT_FK_TJOB_EXECUTION - foreign key (EXECUTION_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_TJOB_PROC_INST_ID on ACT_RU_TIMER_JOB(PROCESS_INSTANCE_ID_); -alter table ACT_RU_TIMER_JOB - add constraint ACT_FK_TJOB_PROCESS_INSTANCE - foreign key (PROCESS_INSTANCE_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_TJOB_PROC_DEF_ID on ACT_RU_TIMER_JOB(PROC_DEF_ID_); -alter table ACT_RU_TIMER_JOB - add constraint ACT_FK_TJOB_PROC_DEF - foreign key (PROC_DEF_ID_) - references ACT_RE_PROCDEF (ID_); - -create index ACT_IDX_SJOB_EXECUTION_ID on ACT_RU_SUSPENDED_JOB(EXECUTION_ID_); -alter table ACT_RU_SUSPENDED_JOB - add constraint ACT_FK_SJOB_EXECUTION - foreign key (EXECUTION_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_SJOB_PROC_INST_ID on ACT_RU_SUSPENDED_JOB(PROCESS_INSTANCE_ID_); -alter table ACT_RU_SUSPENDED_JOB - add constraint ACT_FK_SJOB_PROCESS_INSTANCE - foreign key (PROCESS_INSTANCE_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_SJOB_PROC_DEF_ID on ACT_RU_SUSPENDED_JOB(PROC_DEF_ID_); -alter table ACT_RU_SUSPENDED_JOB - add constraint ACT_FK_SJOB_PROC_DEF - foreign key (PROC_DEF_ID_) - references ACT_RE_PROCDEF (ID_); - -create index ACT_IDX_DJOB_EXECUTION_ID on ACT_RU_DEADLETTER_JOB(EXECUTION_ID_); -alter table ACT_RU_DEADLETTER_JOB - add constraint ACT_FK_DJOB_EXECUTION - foreign key (EXECUTION_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_DJOB_PROC_INST_ID on ACT_RU_DEADLETTER_JOB(PROCESS_INSTANCE_ID_); -alter table ACT_RU_DEADLETTER_JOB - add constraint ACT_FK_DJOB_PROCESS_INSTANCE - foreign key (PROCESS_INSTANCE_ID_) - references ACT_RU_EXECUTION (ID_); - -create index ACT_IDX_DJOB_PROC_DEF_ID on ACT_RU_DEADLETTER_JOB(PROC_DEF_ID_); -alter table ACT_RU_DEADLETTER_JOB - add constraint ACT_FK_DJOB_PROC_DEF - foreign key (PROC_DEF_ID_) - references ACT_RE_PROCDEF (ID_); - -alter table ACT_RU_EVENT_SUBSCR - add constraint ACT_FK_EVENT_EXEC - foreign key (EXECUTION_ID_) - references ACT_RU_EXECUTION(ID_); - -create index ACT_IDX_MODEL_SOURCE on ACT_RE_MODEL(EDITOR_SOURCE_VALUE_ID_); -alter table ACT_RE_MODEL - add constraint ACT_FK_MODEL_SOURCE - foreign key (EDITOR_SOURCE_VALUE_ID_) - references ACT_GE_BYTEARRAY (ID_); - -create index ACT_IDX_MODEL_SOURCE_EXTRA on ACT_RE_MODEL(EDITOR_SOURCE_EXTRA_VALUE_ID_); -alter table ACT_RE_MODEL - add constraint ACT_FK_MODEL_SOURCE_EXTRA - foreign key (EDITOR_SOURCE_EXTRA_VALUE_ID_) - references ACT_GE_BYTEARRAY (ID_); - -create index ACT_IDX_MODEL_DEPLOYMENT on ACT_RE_MODEL(DEPLOYMENT_ID_); -alter table ACT_RE_MODEL - add constraint ACT_FK_MODEL_DEPLOYMENT - foreign key (DEPLOYMENT_ID_) - references ACT_RE_DEPLOYMENT (ID_); - -create index ACT_IDX_PROCDEF_INFO_JSON on ACT_PROCDEF_INFO(INFO_JSON_ID_); -alter table ACT_PROCDEF_INFO - add constraint ACT_FK_INFO_JSON_BA - foreign key (INFO_JSON_ID_) - references ACT_GE_BYTEARRAY (ID_); - -create index ACT_IDX_PROCDEF_INFO_PROC on ACT_PROCDEF_INFO(PROC_DEF_ID_); -alter table ACT_PROCDEF_INFO - add constraint ACT_FK_INFO_PROCDEF - foreign key (PROC_DEF_ID_) - references ACT_RE_PROCDEF (ID_); - -alter table ACT_PROCDEF_INFO - add constraint ACT_UNIQ_INFO_PROCDEF - unique (PROC_DEF_ID_); - -insert into ACT_GE_PROPERTY -values ('schema.version', '7.0.1.1', 1); - -insert into ACT_GE_PROPERTY -values ('schema.history', 'create(7.0.1.1)', 1); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql deleted file mode 100644 index 75782f4..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql +++ /dev/null @@ -1,114 +0,0 @@ -create table ACT_HI_PROCINST ( - ID_ VARCHAR2(64) not null, - REV_ INTEGER default 1, - PROC_INST_ID_ VARCHAR2(64) not null, - BUSINESS_KEY_ VARCHAR2(255), - PROC_DEF_ID_ VARCHAR2(64) not null, - START_TIME_ TIMESTAMP(6) not null, - END_TIME_ TIMESTAMP(6), - DURATION_ NUMBER(19,0), - START_USER_ID_ VARCHAR2(255), - START_ACT_ID_ VARCHAR2(255), - END_ACT_ID_ VARCHAR2(255), - SUPER_PROCESS_INSTANCE_ID_ VARCHAR2(64), - DELETE_REASON_ VARCHAR2(2000), - TENANT_ID_ VARCHAR2(255) default '', - NAME_ VARCHAR2(255), - CALLBACK_ID_ VARCHAR2(255), - CALLBACK_TYPE_ VARCHAR2(255), - REFERENCE_ID_ VARCHAR2(255), - REFERENCE_TYPE_ VARCHAR2(255), - PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), - BUSINESS_STATUS_ VARCHAR2(255), - primary key (ID_), - unique (PROC_INST_ID_) -); - -create table ACT_HI_ACTINST ( - ID_ VARCHAR2(64) not null, - REV_ INTEGER default 1, - PROC_DEF_ID_ VARCHAR2(64) not null, - PROC_INST_ID_ VARCHAR2(64) not null, - EXECUTION_ID_ VARCHAR2(64) not null, - ACT_ID_ VARCHAR2(255) not null, - TASK_ID_ VARCHAR2(64), - CALL_PROC_INST_ID_ VARCHAR2(64), - ACT_NAME_ VARCHAR2(255), - ACT_TYPE_ VARCHAR2(255) not null, - ASSIGNEE_ VARCHAR2(255), - START_TIME_ TIMESTAMP(6) not null, - END_TIME_ TIMESTAMP(6), - TRANSACTION_ORDER_ INTEGER, - DURATION_ NUMBER(19,0), - DELETE_REASON_ VARCHAR2(2000), - TENANT_ID_ VARCHAR2(255) default '', - primary key (ID_) -); - -create table ACT_HI_DETAIL ( - ID_ VARCHAR2(64) not null, - TYPE_ VARCHAR2(255) not null, - PROC_INST_ID_ VARCHAR2(64), - EXECUTION_ID_ VARCHAR2(64), - TASK_ID_ VARCHAR2(64), - ACT_INST_ID_ VARCHAR2(64), - NAME_ VARCHAR2(255) not null, - VAR_TYPE_ VARCHAR2(64), - REV_ INTEGER, - TIME_ TIMESTAMP(6) not null, - BYTEARRAY_ID_ VARCHAR2(64), - DOUBLE_ NUMBER(38,10), - LONG_ NUMBER(19,0), - TEXT_ VARCHAR2(2000), - TEXT2_ VARCHAR2(2000), - primary key (ID_) -); - -create table ACT_HI_COMMENT ( - ID_ VARCHAR2(64) not null, - TYPE_ VARCHAR2(255), - TIME_ TIMESTAMP(6) not null, - USER_ID_ VARCHAR2(255), - TASK_ID_ VARCHAR2(64), - PROC_INST_ID_ VARCHAR2(64), - ACTION_ VARCHAR2(255), - MESSAGE_ VARCHAR2(2000), - FULL_MSG_ BLOB, - primary key (ID_) -); - -create table ACT_HI_ATTACHMENT ( - ID_ VARCHAR2(64) not null, - REV_ INTEGER, - USER_ID_ VARCHAR2(255), - NAME_ VARCHAR2(255), - DESCRIPTION_ VARCHAR2(2000), - TYPE_ VARCHAR2(255), - TASK_ID_ VARCHAR2(64), - PROC_INST_ID_ VARCHAR2(64), - URL_ VARCHAR2(2000), - CONTENT_ID_ VARCHAR2(64), - TIME_ TIMESTAMP(6), - primary key (ID_) -); - -create index ACT_IDX_HI_PRO_INST_END on ACT_HI_PROCINST(END_TIME_); -create index ACT_IDX_HI_PRO_I_BUSKEY on ACT_HI_PROCINST(BUSINESS_KEY_); -create index ACT_IDX_HI_PRO_SUPER_PROCINST on ACT_HI_PROCINST(SUPER_PROCESS_INSTANCE_ID_); -create index ACT_IDX_HI_ACT_INST_START on ACT_HI_ACTINST(START_TIME_); -create index ACT_IDX_HI_ACT_INST_END on ACT_HI_ACTINST(END_TIME_); -create index ACT_IDX_HI_DETAIL_PROC_INST on ACT_HI_DETAIL(PROC_INST_ID_); -create index ACT_IDX_HI_DETAIL_ACT_INST on ACT_HI_DETAIL(ACT_INST_ID_); -create index ACT_IDX_HI_DETAIL_TIME on ACT_HI_DETAIL(TIME_); -create index ACT_IDX_HI_DETAIL_NAME on ACT_HI_DETAIL(NAME_); -create index ACT_IDX_HI_DETAIL_TASK_ID on ACT_HI_DETAIL(TASK_ID_); -create index ACT_IDX_HI_PROCVAR_PROC_INST on ACT_HI_VARINST(PROC_INST_ID_); -create index ACT_IDX_HI_PROCVAR_TASK_ID on ACT_HI_VARINST(TASK_ID_); -create index ACT_IDX_HI_PROCVAR_EXE on ACT_HI_VARINST(EXECUTION_ID_); -create index ACT_IDX_HI_IDENT_LNK_TASK on ACT_HI_IDENTITYLINK(TASK_ID_); -create index ACT_IDX_HI_IDENT_LNK_PROCINST on ACT_HI_IDENTITYLINK(PROC_INST_ID_); - -create index ACT_IDX_HI_ACT_INST_PROCINST on ACT_HI_ACTINST(PROC_INST_ID_, ACT_ID_); -create index ACT_IDX_HI_ACT_INST_EXEC on ACT_HI_ACTINST(EXECUTION_ID_, ACT_ID_); -create index ACT_IDX_HI_TASK_INST_PROCINST on ACT_HI_TASKINST(PROC_INST_ID_); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql b/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql deleted file mode 100644 index 58537ba..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql +++ /dev/null @@ -1,148 +0,0 @@ -drop index ACT_IDX_BYTEAR_DEPL; -drop index ACT_IDX_EXE_PROCINST; -drop index ACT_IDX_EXE_PARENT; -drop index ACT_IDX_EXE_SUPER; -drop index ACT_IDX_TSKASS_TASK; -drop index ACT_IDX_TASK_EXEC; -drop index ACT_IDX_TASK_PROCINST; -drop index ACT_IDX_TASK_PROCDEF; -drop index ACT_IDX_VAR_EXE; -drop index ACT_IDX_VAR_PROCINST; -drop index ACT_IDX_JOB_EXECUTION_ID; -drop index ACT_IDX_JOB_PROC_INST_ID; -drop index ACT_IDX_JOB_PROC_DEF_ID; -drop index ACT_IDX_TJOB_EXECUTION_ID; -drop index ACT_IDX_TJOB_PROC_INST_ID; -drop index ACT_IDX_TJOB_PROC_DEF_ID; -drop index ACT_IDX_SJOB_EXECUTION_ID; -drop index ACT_IDX_SJOB_PROC_INST_ID; -drop index ACT_IDX_SJOB_PROC_DEF_ID; -drop index ACT_IDX_DJOB_EXECUTION_ID; -drop index ACT_IDX_DJOB_PROC_INST_ID; -drop index ACT_IDX_DJOB_PROC_DEF_ID; -drop index ACT_IDX_MODEL_SOURCE; -drop index ACT_IDX_MODEL_SOURCE_EXTRA; -drop index ACT_IDX_MODEL_DEPLOYMENT; -drop index ACT_IDX_PROCDEF_INFO_JSON; - -drop index ACT_IDX_EXEC_BUSKEY; -drop index ACT_IDX_VARIABLE_TASK_ID; - -drop index ACT_IDX_RU_ACTI_START; -drop index ACT_IDX_RU_ACTI_END; -drop index ACT_IDX_RU_ACTI_PROC; -drop index ACT_IDX_RU_ACTI_PROC_ACT; -drop index ACT_IDX_RU_ACTI_EXEC; -drop index ACT_IDX_RU_ACTI_EXEC_ACT; - -alter table ACT_GE_BYTEARRAY - drop CONSTRAINT ACT_FK_BYTEARR_DEPL; - -alter table ACT_RU_EXECUTION - drop CONSTRAINT ACT_FK_EXE_PROCINST; - -alter table ACT_RU_EXECUTION - drop CONSTRAINT ACT_FK_EXE_PARENT; - -alter table ACT_RU_EXECUTION - drop CONSTRAINT ACT_FK_EXE_SUPER; - -alter table ACT_RU_EXECUTION - drop CONSTRAINT ACT_FK_EXE_PROCDEF; - -alter table ACT_RU_IDENTITYLINK - drop CONSTRAINT ACT_FK_TSKASS_TASK; - -alter table ACT_RU_IDENTITYLINK - drop CONSTRAINT ACT_FK_IDL_PROCINST; - -alter table ACT_RU_IDENTITYLINK - drop CONSTRAINT ACT_FK_ATHRZ_PROCEDEF; - -alter table ACT_RU_TASK - drop CONSTRAINT ACT_FK_TASK_EXE; - -alter table ACT_RU_TASK - drop CONSTRAINT ACT_FK_TASK_PROCINST; - -alter table ACT_RU_TASK - drop CONSTRAINT ACT_FK_TASK_PROCDEF; - -alter table ACT_RU_VARIABLE - drop CONSTRAINT ACT_FK_VAR_EXE; - -alter table ACT_RU_VARIABLE - drop CONSTRAINT ACT_FK_VAR_PROCINST; - -alter table ACT_RU_JOB - drop CONSTRAINT ACT_FK_JOB_EXECUTION; - -alter table ACT_RU_JOB - drop CONSTRAINT ACT_FK_JOB_PROCESS_INSTANCE; - -alter table ACT_RU_JOB - drop CONSTRAINT ACT_FK_JOB_PROC_DEF; - -alter table ACT_RU_TIMER_JOB - drop CONSTRAINT ACT_FK_TJOB_EXECUTION; - -alter table ACT_RU_TIMER_JOB - drop CONSTRAINT ACT_FK_TJOB_PROCESS_INSTANCE; - -alter table ACT_RU_TIMER_JOB - drop CONSTRAINT ACT_FK_TJOB_PROC_DEF; - -alter table ACT_RU_SUSPENDED_JOB - drop CONSTRAINT ACT_FK_SJOB_EXECUTION; - -alter table ACT_RU_SUSPENDED_JOB - drop CONSTRAINT ACT_FK_SJOB_PROCESS_INSTANCE; - -alter table ACT_RU_SUSPENDED_JOB - drop CONSTRAINT ACT_FK_SJOB_PROC_DEF; - -alter table ACT_RU_DEADLETTER_JOB - drop CONSTRAINT ACT_FK_DJOB_EXECUTION; - -alter table ACT_RU_DEADLETTER_JOB - drop CONSTRAINT ACT_FK_DJOB_PROCESS_INSTANCE; - -alter table ACT_RU_DEADLETTER_JOB - drop CONSTRAINT ACT_FK_DJOB_PROC_DEF; - -alter table ACT_RU_EVENT_SUBSCR - drop CONSTRAINT ACT_FK_EVENT_EXEC; - -alter table ACT_RE_PROCDEF - drop CONSTRAINT ACT_UNIQ_PROCDEF; - -alter table ACT_RE_MODEL - drop CONSTRAINT ACT_FK_MODEL_SOURCE; - -alter table ACT_RE_MODEL - drop CONSTRAINT ACT_FK_MODEL_SOURCE_EXTRA; - -alter table ACT_RE_MODEL - drop CONSTRAINT ACT_FK_MODEL_DEPLOYMENT; - -alter table ACT_PROCDEF_INFO - drop CONSTRAINT ACT_UNIQ_INFO_PROCDEF; - -alter table ACT_PROCDEF_INFO - drop CONSTRAINT ACT_FK_INFO_JSON_BA; - -alter table ACT_PROCDEF_INFO - drop CONSTRAINT ACT_FK_INFO_PROCDEF; - -drop index ACT_IDX_ATHRZ_PROCEDEF; -drop index ACT_IDX_PROCDEF_INFO_PROC; - -drop table ACT_RU_ACTINST; -drop table ACT_RE_DEPLOYMENT; -drop table ACT_RE_MODEL; -drop table ACT_RE_PROCDEF; -drop table ACT_RU_EXECUTION; - -drop sequence act_evt_log_seq; -drop table ACT_EVT_LOG; -drop table ACT_PROCDEF_INFO; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql deleted file mode 100644 index 2a31cc4..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql +++ /dev/null @@ -1,23 +0,0 @@ -drop index ACT_IDX_HI_PRO_INST_END; -drop index ACT_IDX_HI_PRO_I_BUSKEY; -drop index ACT_IDX_HI_ACT_INST_START; -drop index ACT_IDX_HI_ACT_INST_END; -drop index ACT_IDX_HI_DETAIL_PROC_INST; -drop index ACT_IDX_HI_DETAIL_ACT_INST; -drop index ACT_IDX_HI_DETAIL_TIME; -drop index ACT_IDX_HI_DETAIL_NAME; -drop index ACT_IDX_HI_DETAIL_TASK_ID; -drop index ACT_IDX_HI_PROCVAR_PROC_INST; -drop index ACT_IDX_HI_PROCVAR_TASK_ID; -drop index ACT_IDX_HI_PROCVAR_EXE; -drop index ACT_IDX_HI_ACT_INST_PROCINST; -drop index ACT_IDX_HI_IDENT_LNK_TASK; -drop index ACT_IDX_HI_IDENT_LNK_PROCINST; -drop index ACT_IDX_HI_TASK_INST_PROCINST; - -drop table ACT_HI_PROCINST; -drop table ACT_HI_ACTINST; -drop table ACT_HI_DETAIL; -drop table ACT_HI_COMMENT; -drop table ACT_HI_ATTACHMENT; - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql deleted file mode 100644 index 55c5dbe..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql +++ /dev/null @@ -1,23 +0,0 @@ -create table ACT_HI_ENTITYLINK ( - ID_ VARCHAR2(64), - LINK_TYPE_ VARCHAR2(255), - CREATE_TIME_ TIMESTAMP(6), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - PARENT_ELEMENT_ID_ VARCHAR2(255), - REF_SCOPE_ID_ VARCHAR2(255), - REF_SCOPE_TYPE_ VARCHAR2(255), - REF_SCOPE_DEFINITION_ID_ VARCHAR2(255), - ROOT_SCOPE_ID_ VARCHAR2(255), - ROOT_SCOPE_TYPE_ VARCHAR2(255), - HIERARCHY_TYPE_ VARCHAR2(255), - primary key (ID_) -); - -create index ACT_IDX_HI_ENT_LNK_SCOPE on ACT_HI_ENTITYLINK(SCOPE_ID_, SCOPE_TYPE_, LINK_TYPE_); -create index ACT_IDX_HI_ENT_LNK_REF_SCOPE on ACT_HI_ENTITYLINK(REF_SCOPE_ID_, REF_SCOPE_TYPE_, LINK_TYPE_); -create index ACT_IDX_HI_ENT_LNK_ROOT_SCOPE on ACT_HI_ENTITYLINK(ROOT_SCOPE_ID_, ROOT_SCOPE_TYPE_, LINK_TYPE_); -create index ACT_IDX_HI_ENT_LNK_SCOPE_DEF on ACT_HI_ENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_, LINK_TYPE_); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql deleted file mode 100644 index de08451..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql +++ /dev/null @@ -1,26 +0,0 @@ -create table ACT_RU_ENTITYLINK ( - ID_ VARCHAR2(64), - REV_ INTEGER, - CREATE_TIME_ TIMESTAMP(6), - LINK_TYPE_ VARCHAR2(255), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - PARENT_ELEMENT_ID_ VARCHAR2(255), - REF_SCOPE_ID_ VARCHAR2(255), - REF_SCOPE_TYPE_ VARCHAR2(255), - REF_SCOPE_DEFINITION_ID_ VARCHAR2(255), - ROOT_SCOPE_ID_ VARCHAR2(255), - ROOT_SCOPE_TYPE_ VARCHAR2(255), - HIERARCHY_TYPE_ VARCHAR2(255), - primary key (ID_) -); - -create index ACT_IDX_ENT_LNK_SCOPE on ACT_RU_ENTITYLINK(SCOPE_ID_, SCOPE_TYPE_, LINK_TYPE_); -create index ACT_IDX_ENT_LNK_REF_SCOPE on ACT_RU_ENTITYLINK(REF_SCOPE_ID_, REF_SCOPE_TYPE_, LINK_TYPE_); -create index ACT_IDX_ENT_LNK_ROOT_SCOPE on ACT_RU_ENTITYLINK(ROOT_SCOPE_ID_, ROOT_SCOPE_TYPE_, LINK_TYPE_); -create index ACT_IDX_ENT_LNK_SCOPE_DEF on ACT_RU_ENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_, LINK_TYPE_); - -insert into ACT_GE_PROPERTY values ('entitylink.schema.version', '7.0.1.1', 1); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql deleted file mode 100644 index a908877..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql +++ /dev/null @@ -1,4 +0,0 @@ -drop index ACT_IDX_HI_ENT_LNK_SCOPE; -drop index ACT_IDX_HI_ENT_LNK_SCOPE_DEF; - -drop table ACT_HI_ENTITYLINK; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql b/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql deleted file mode 100644 index aedbacd..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql +++ /dev/null @@ -1,4 +0,0 @@ -drop index ACT_IDX_ENT_LNK_SCOPE; -drop index ACT_IDX_ENT_LNK_SCOPE_DEF; - -drop table ACT_RU_ENTITYLINK; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql b/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql deleted file mode 100644 index eb22164..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql +++ /dev/null @@ -1,28 +0,0 @@ -create table ACT_RU_EVENT_SUBSCR ( - ID_ VARCHAR2(64) not null, - REV_ integer, - EVENT_TYPE_ VARCHAR2(255) not null, - EVENT_NAME_ VARCHAR2(255), - EXECUTION_ID_ VARCHAR2(64), - PROC_INST_ID_ VARCHAR2(64), - ACTIVITY_ID_ VARCHAR2(64), - CONFIGURATION_ VARCHAR2(255), - CREATED_ TIMESTAMP(6) not null, - PROC_DEF_ID_ VARCHAR2(64), - SUB_SCOPE_ID_ VARCHAR2(64), - SCOPE_ID_ VARCHAR2(64), - SCOPE_DEFINITION_ID_ VARCHAR2(64), - SCOPE_DEFINITION_KEY_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(64), - LOCK_TIME_ TIMESTAMP(6), - LOCK_OWNER_ VARCHAR2(255), - TENANT_ID_ VARCHAR2(255) DEFAULT '', - primary key (ID_) -); - -create index ACT_IDX_EVENT_SUBSCR_CONFIG_ on ACT_RU_EVENT_SUBSCR(CONFIGURATION_); -create index ACT_IDX_EVENT_SUBSCR on ACT_RU_EVENT_SUBSCR(EXECUTION_ID_); -create index ACT_IDX_EVENT_SUBSCR_SCOPEREF_ on ACT_RU_EVENT_SUBSCR(SCOPE_ID_, SCOPE_TYPE_); - -insert into ACT_GE_PROPERTY values ('eventsubscription.schema.version', '7.0.1.1', 1); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql b/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql deleted file mode 100644 index c85ad74..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql +++ /dev/null @@ -1,5 +0,0 @@ -drop index ACT_IDX_EVENT_SUBSCR_CONFIG_; -drop index ACT_IDX_EVENT_SUBSCR; -drop index ACT_IDX_EVENT_SUBSCR_SCOPEREF_; - -drop table ACT_RU_EVENT_SUBSCR; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql deleted file mode 100644 index 2305f0a..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql +++ /dev/null @@ -1,20 +0,0 @@ -create table ACT_HI_IDENTITYLINK ( - ID_ VARCHAR2(64), - GROUP_ID_ VARCHAR2(255), - TYPE_ VARCHAR2(255), - USER_ID_ VARCHAR2(255), - TASK_ID_ VARCHAR2(64), - CREATE_TIME_ TIMESTAMP(6), - PROC_INST_ID_ VARCHAR2(64), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - primary key (ID_) -); - -create index ACT_IDX_HI_IDENT_LNK_USER on ACT_HI_IDENTITYLINK(USER_ID_); -create index ACT_IDX_HI_IDENT_LNK_SCOPE on ACT_HI_IDENTITYLINK(SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_HI_IDENT_LNK_SUB_SCOPE on ACT_HI_IDENTITYLINK(SUB_SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_HI_IDENT_LNK_SCOPE_DEF on ACT_HI_IDENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql deleted file mode 100644 index e290879..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql +++ /dev/null @@ -1,24 +0,0 @@ -create table ACT_RU_IDENTITYLINK ( - ID_ VARCHAR2(64), - REV_ INTEGER, - GROUP_ID_ VARCHAR2(255), - TYPE_ VARCHAR2(255), - USER_ID_ VARCHAR2(255), - TASK_ID_ VARCHAR2(64), - PROC_INST_ID_ VARCHAR2(64), - PROC_DEF_ID_ VARCHAR2(64), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - primary key (ID_) -); - -create index ACT_IDX_IDENT_LNK_USER on ACT_RU_IDENTITYLINK(USER_ID_); -create index ACT_IDX_IDENT_LNK_GROUP on ACT_RU_IDENTITYLINK(GROUP_ID_); -create index ACT_IDX_IDENT_LNK_SCOPE on ACT_RU_IDENTITYLINK(SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_IDENT_LNK_SUB_SCOPE on ACT_RU_IDENTITYLINK(SUB_SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_IDENT_LNK_SCOPE_DEF on ACT_RU_IDENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); - -insert into ACT_GE_PROPERTY values ('identitylink.schema.version', '7.0.1.1', 1); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql deleted file mode 100644 index 7cff665..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql +++ /dev/null @@ -1,6 +0,0 @@ -drop index ACT_IDX_HI_IDENT_LNK_USER; -drop index ACT_IDX_HI_IDENT_LNK_SCOPE; -drop index ACT_IDX_HI_IDENT_LNK_SUB_SCOPE; -drop index ACT_IDX_HI_IDENT_LNK_SCOPE_DEF; - -drop table ACT_HI_IDENTITYLINK; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql b/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql deleted file mode 100644 index 485344a..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql +++ /dev/null @@ -1,7 +0,0 @@ -drop index ACT_IDX_IDENT_LNK_USER; -drop index ACT_IDX_IDENT_LNK_GROUP; -drop index ACT_IDX_IDENT_LNK_SCOPE; -drop index ACT_IDX_IDENT_LNK_SUB_SCOPE; -drop index ACT_IDX_IDENT_LNK_SCOPE_DEF; - -drop table ACT_RU_IDENTITYLINK; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql b/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql deleted file mode 100644 index 562f45e..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql +++ /dev/null @@ -1,108 +0,0 @@ -create table ACT_ID_PROPERTY ( - NAME_ VARCHAR2(64), - VALUE_ VARCHAR2(300), - REV_ INTEGER, - primary key (NAME_) -); - -insert into ACT_ID_PROPERTY -values ('schema.version', '7.0.1.1', 1); - -create table ACT_ID_BYTEARRAY ( - ID_ VARCHAR2(64), - REV_ INTEGER, - NAME_ VARCHAR2(255), - BYTES_ BLOB, - primary key (ID_) -); - -create table ACT_ID_GROUP ( - ID_ VARCHAR2(64), - REV_ INTEGER, - NAME_ VARCHAR2(255), - TYPE_ VARCHAR2(255), - primary key (ID_) -); - -create table ACT_ID_MEMBERSHIP ( - USER_ID_ VARCHAR2(64), - GROUP_ID_ VARCHAR2(64), - primary key (USER_ID_, GROUP_ID_) -); - -create table ACT_ID_USER ( - ID_ VARCHAR2(64), - REV_ INTEGER, - FIRST_ VARCHAR2(255), - LAST_ VARCHAR2(255), - DISPLAY_NAME_ VARCHAR2(255), - EMAIL_ VARCHAR2(255), - PWD_ VARCHAR2(255), - PICTURE_ID_ VARCHAR2(64), - TENANT_ID_ VARCHAR2(255) default '', - primary key (ID_) -); - -create table ACT_ID_INFO ( - ID_ VARCHAR2(64), - REV_ INTEGER, - USER_ID_ VARCHAR2(64), - TYPE_ VARCHAR2(64), - KEY_ VARCHAR2(255), - VALUE_ VARCHAR2(255), - PASSWORD_ BLOB, - PARENT_ID_ VARCHAR2(255), - primary key (ID_) -); - -create table ACT_ID_TOKEN ( - ID_ VARCHAR2(64) not null, - REV_ INTEGER, - TOKEN_VALUE_ VARCHAR2(255), - TOKEN_DATE_ TIMESTAMP(6), - IP_ADDRESS_ VARCHAR2(255), - USER_AGENT_ VARCHAR2(255), - USER_ID_ VARCHAR2(255), - TOKEN_DATA_ VARCHAR2(2000), - primary key (ID_) -); - -create table ACT_ID_PRIV ( - ID_ VARCHAR2(64) not null, - NAME_ VARCHAR2(255) not null, - primary key (ID_) -); - -create table ACT_ID_PRIV_MAPPING ( - ID_ VARCHAR2(64) not null, - PRIV_ID_ VARCHAR2(64) not null, - USER_ID_ VARCHAR2(255), - GROUP_ID_ VARCHAR2(255), - primary key (ID_) -); - -create index ACT_IDX_MEMB_GROUP on ACT_ID_MEMBERSHIP(GROUP_ID_); -alter table ACT_ID_MEMBERSHIP - add constraint ACT_FK_MEMB_GROUP - foreign key (GROUP_ID_) - references ACT_ID_GROUP (ID_); - -create index ACT_IDX_MEMB_USER on ACT_ID_MEMBERSHIP(USER_ID_); -alter table ACT_ID_MEMBERSHIP - add constraint ACT_FK_MEMB_USER - foreign key (USER_ID_) - references ACT_ID_USER (ID_); - -create index ACT_IDX_PRIV_MAPPING on ACT_ID_PRIV_MAPPING(PRIV_ID_); -alter table ACT_ID_PRIV_MAPPING - add constraint ACT_FK_PRIV_MAPPING - foreign key (PRIV_ID_) - references ACT_ID_PRIV (ID_); - -create index ACT_IDX_PRIV_USER on ACT_ID_PRIV_MAPPING(USER_ID_); -create index ACT_IDX_PRIV_GROUP on ACT_ID_PRIV_MAPPING(GROUP_ID_); - -alter table ACT_ID_PRIV - add constraint ACT_UNIQ_PRIV_NAME - unique (NAME_); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql b/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql deleted file mode 100644 index 5cac418..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql +++ /dev/null @@ -1,22 +0,0 @@ -alter table ACT_ID_MEMBERSHIP - drop CONSTRAINT ACT_FK_MEMB_GROUP; - -alter table ACT_ID_MEMBERSHIP - drop CONSTRAINT ACT_FK_MEMB_USER; - -alter table ACT_ID_PRIV_MAPPING - drop CONSTRAINT ACT_FK_PRIV_MAPPING; - -drop index ACT_IDX_MEMB_GROUP; -drop index ACT_IDX_MEMB_USER; -drop index ACT_IDX_PRIV_MAPPING; - -drop table ACT_ID_PROPERTY; -drop table ACT_ID_BYTEARRAY; -drop table ACT_ID_INFO; -drop table ACT_ID_MEMBERSHIP; -drop table ACT_ID_GROUP; -drop table ACT_ID_USER; -drop table ACT_ID_TOKEN; -drop table ACT_ID_PRIV; -drop table ACT_ID_PRIV_MAPPING; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql b/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql deleted file mode 100644 index 8b3e79b..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql +++ /dev/null @@ -1,261 +0,0 @@ -create table ACT_RU_JOB ( - ID_ VARCHAR2(64) NOT NULL, - REV_ INTEGER, - CATEGORY_ VARCHAR2(255), - TYPE_ VARCHAR2(255) NOT NULL, - LOCK_EXP_TIME_ TIMESTAMP(6), - LOCK_OWNER_ VARCHAR2(255), - EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), - EXECUTION_ID_ VARCHAR2(64), - PROCESS_INSTANCE_ID_ VARCHAR2(64), - PROC_DEF_ID_ VARCHAR2(64), - ELEMENT_ID_ VARCHAR2(255), - ELEMENT_NAME_ VARCHAR2(255), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - CORRELATION_ID_ VARCHAR2(255), - RETRIES_ INTEGER, - EXCEPTION_STACK_ID_ VARCHAR2(64), - EXCEPTION_MSG_ VARCHAR2(2000), - DUEDATE_ TIMESTAMP(6), - REPEAT_ VARCHAR2(255), - HANDLER_TYPE_ VARCHAR2(255), - HANDLER_CFG_ VARCHAR2(2000), - CUSTOM_VALUES_ID_ VARCHAR2(64), - CREATE_TIME_ TIMESTAMP(6), - TENANT_ID_ VARCHAR2(255) DEFAULT '', - primary key (ID_) -); - -create table ACT_RU_TIMER_JOB ( - ID_ VARCHAR2(64) NOT NULL, - REV_ INTEGER, - CATEGORY_ VARCHAR2(255), - TYPE_ VARCHAR2(255) NOT NULL, - LOCK_EXP_TIME_ TIMESTAMP(6), - LOCK_OWNER_ VARCHAR2(255), - EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), - EXECUTION_ID_ VARCHAR2(64), - PROCESS_INSTANCE_ID_ VARCHAR2(64), - PROC_DEF_ID_ VARCHAR2(64), - ELEMENT_ID_ VARCHAR2(255), - ELEMENT_NAME_ VARCHAR2(255), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - CORRELATION_ID_ VARCHAR2(255), - RETRIES_ INTEGER, - EXCEPTION_STACK_ID_ VARCHAR2(64), - EXCEPTION_MSG_ VARCHAR2(2000), - DUEDATE_ TIMESTAMP(6), - REPEAT_ VARCHAR2(255), - HANDLER_TYPE_ VARCHAR2(255), - HANDLER_CFG_ VARCHAR2(2000), - CUSTOM_VALUES_ID_ VARCHAR2(64), - CREATE_TIME_ TIMESTAMP(6), - TENANT_ID_ VARCHAR2(255) DEFAULT '', - primary key (ID_) -); - -create table ACT_RU_SUSPENDED_JOB ( - ID_ VARCHAR2(64) NOT NULL, - REV_ INTEGER, - CATEGORY_ VARCHAR2(255), - TYPE_ VARCHAR2(255) NOT NULL, - EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), - EXECUTION_ID_ VARCHAR2(64), - PROCESS_INSTANCE_ID_ VARCHAR2(64), - PROC_DEF_ID_ VARCHAR2(64), - ELEMENT_ID_ VARCHAR2(255), - ELEMENT_NAME_ VARCHAR2(255), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - CORRELATION_ID_ VARCHAR2(255), - RETRIES_ INTEGER, - EXCEPTION_STACK_ID_ VARCHAR2(64), - EXCEPTION_MSG_ VARCHAR2(2000), - DUEDATE_ TIMESTAMP(6), - REPEAT_ VARCHAR2(255), - HANDLER_TYPE_ VARCHAR2(255), - HANDLER_CFG_ VARCHAR2(2000), - CUSTOM_VALUES_ID_ VARCHAR2(64), - CREATE_TIME_ TIMESTAMP(6), - TENANT_ID_ VARCHAR2(255) DEFAULT '', - primary key (ID_) -); - -create table ACT_RU_DEADLETTER_JOB ( - ID_ VARCHAR2(64) NOT NULL, - REV_ INTEGER, - CATEGORY_ VARCHAR2(255), - TYPE_ VARCHAR2(255) NOT NULL, - EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), - EXECUTION_ID_ VARCHAR2(64), - PROCESS_INSTANCE_ID_ VARCHAR2(64), - PROC_DEF_ID_ VARCHAR2(64), - ELEMENT_ID_ VARCHAR2(255), - ELEMENT_NAME_ VARCHAR2(255), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - CORRELATION_ID_ VARCHAR2(255), - EXCEPTION_STACK_ID_ VARCHAR2(64), - EXCEPTION_MSG_ VARCHAR2(2000), - DUEDATE_ TIMESTAMP(6), - REPEAT_ VARCHAR2(255), - HANDLER_TYPE_ VARCHAR2(255), - HANDLER_CFG_ VARCHAR2(2000), - CUSTOM_VALUES_ID_ VARCHAR2(64), - CREATE_TIME_ TIMESTAMP(6), - TENANT_ID_ VARCHAR2(255) DEFAULT '', - primary key (ID_) -); - -create table ACT_RU_HISTORY_JOB ( - ID_ VARCHAR2(64) NOT NULL, - REV_ INTEGER, - LOCK_EXP_TIME_ TIMESTAMP(6), - LOCK_OWNER_ VARCHAR2(255), - RETRIES_ INTEGER, - EXCEPTION_STACK_ID_ VARCHAR2(64), - EXCEPTION_MSG_ VARCHAR2(2000), - HANDLER_TYPE_ VARCHAR2(255), - HANDLER_CFG_ VARCHAR2(2000), - CUSTOM_VALUES_ID_ VARCHAR2(64), - ADV_HANDLER_CFG_ID_ VARCHAR2(64), - CREATE_TIME_ TIMESTAMP(6), - SCOPE_TYPE_ VARCHAR2(255), - TENANT_ID_ VARCHAR2(255) DEFAULT '', - primary key (ID_) -); - -create table ACT_RU_EXTERNAL_JOB ( - ID_ VARCHAR2(64) NOT NULL, - REV_ INTEGER, - CATEGORY_ VARCHAR2(255), - TYPE_ VARCHAR2(255) NOT NULL, - LOCK_EXP_TIME_ TIMESTAMP(6), - LOCK_OWNER_ VARCHAR2(255), - EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), - EXECUTION_ID_ VARCHAR2(64), - PROCESS_INSTANCE_ID_ VARCHAR2(64), - PROC_DEF_ID_ VARCHAR2(64), - ELEMENT_ID_ VARCHAR2(255), - ELEMENT_NAME_ VARCHAR2(255), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - CORRELATION_ID_ VARCHAR2(255), - RETRIES_ INTEGER, - EXCEPTION_STACK_ID_ VARCHAR2(64), - EXCEPTION_MSG_ VARCHAR2(2000), - DUEDATE_ TIMESTAMP(6), - REPEAT_ VARCHAR2(255), - HANDLER_TYPE_ VARCHAR2(255), - HANDLER_CFG_ VARCHAR2(2000), - CUSTOM_VALUES_ID_ VARCHAR2(64), - CREATE_TIME_ TIMESTAMP(6), - TENANT_ID_ VARCHAR2(255) DEFAULT '', - primary key (ID_) -); - -create index ACT_IDX_JOB_EXCEPTION on ACT_RU_JOB(EXCEPTION_STACK_ID_); -create index ACT_IDX_JOB_CUSTOM_VAL_ID on ACT_RU_JOB(CUSTOM_VALUES_ID_); -create index ACT_IDX_JOB_CORRELATION_ID on ACT_RU_JOB(CORRELATION_ID_); - -create index ACT_IDX_TJOB_EXCEPTION on ACT_RU_TIMER_JOB(EXCEPTION_STACK_ID_); -create index ACT_IDX_TJOB_CUSTOM_VAL_ID on ACT_RU_TIMER_JOB(CUSTOM_VALUES_ID_); -create index ACT_IDX_TJOB_CORRELATION_ID on ACT_RU_TIMER_JOB(CORRELATION_ID_); -create index ACT_IDX_TJOB_DUEDATE on ACT_RU_TIMER_JOB(DUEDATE_); - -create index ACT_IDX_SJOB_EXCEPTION on ACT_RU_SUSPENDED_JOB(EXCEPTION_STACK_ID_); -create index ACT_IDX_SJOB_CUSTOM_VAL_ID on ACT_RU_SUSPENDED_JOB(CUSTOM_VALUES_ID_); -create index ACT_IDX_SJOB_CORRELATION_ID on ACT_RU_SUSPENDED_JOB(CORRELATION_ID_); - -create index ACT_IDX_DJOB_EXCEPTION on ACT_RU_DEADLETTER_JOB(EXCEPTION_STACK_ID_); -create index ACT_IDX_DJOB_CUSTOM_VAL_ID on ACT_RU_DEADLETTER_JOB(CUSTOM_VALUES_ID_); -create index ACT_IDX_DJOB_CORRELATION_ID on ACT_RU_DEADLETTER_JOB(CORRELATION_ID_); - -create index ACT_IDX_EJOB_EXCEPTION on ACT_RU_EXTERNAL_JOB(EXCEPTION_STACK_ID_); -create index ACT_IDX_EJOB_CUSTOM_VAL_ID on ACT_RU_EXTERNAL_JOB(CUSTOM_VALUES_ID_); -create index ACT_IDX_EJOB_CORRELATION_ID on ACT_RU_EXTERNAL_JOB(CORRELATION_ID_); - -alter table ACT_RU_JOB - add constraint ACT_FK_JOB_EXCEPTION - foreign key (EXCEPTION_STACK_ID_) - references ACT_GE_BYTEARRAY (ID_); - -alter table ACT_RU_JOB - add constraint ACT_FK_JOB_CUSTOM_VAL - foreign key (CUSTOM_VALUES_ID_) - references ACT_GE_BYTEARRAY (ID_); - -alter table ACT_RU_TIMER_JOB - add constraint ACT_FK_TJOB_EXCEPTION - foreign key (EXCEPTION_STACK_ID_) - references ACT_GE_BYTEARRAY (ID_); - -alter table ACT_RU_TIMER_JOB - add constraint ACT_FK_TJOB_CUSTOM_VAL - foreign key (CUSTOM_VALUES_ID_) - references ACT_GE_BYTEARRAY (ID_); - -alter table ACT_RU_SUSPENDED_JOB - add constraint ACT_FK_SJOB_EXCEPTION - foreign key (EXCEPTION_STACK_ID_) - references ACT_GE_BYTEARRAY (ID_); - -alter table ACT_RU_SUSPENDED_JOB - add constraint ACT_FK_SJOB_CUSTOM_VAL - foreign key (CUSTOM_VALUES_ID_) - references ACT_GE_BYTEARRAY (ID_); - -alter table ACT_RU_DEADLETTER_JOB - add constraint ACT_FK_DJOB_EXCEPTION - foreign key (EXCEPTION_STACK_ID_) - references ACT_GE_BYTEARRAY (ID_); - -alter table ACT_RU_DEADLETTER_JOB - add constraint ACT_FK_DJOB_CUSTOM_VAL - foreign key (CUSTOM_VALUES_ID_) - references ACT_GE_BYTEARRAY (ID_); - -alter table ACT_RU_EXTERNAL_JOB - add constraint ACT_FK_EJOB_EXCEPTION - foreign key (EXCEPTION_STACK_ID_) - references ACT_GE_BYTEARRAY (ID_); - -alter table ACT_RU_EXTERNAL_JOB - add constraint ACT_FK_EJOB_CUSTOM_VAL - foreign key (CUSTOM_VALUES_ID_) - references ACT_GE_BYTEARRAY (ID_); - -create index ACT_IDX_JOB_SCOPE on ACT_RU_JOB(SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_JOB_SUB_SCOPE on ACT_RU_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_JOB_SCOPE_DEF on ACT_RU_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); - -create index ACT_IDX_TJOB_SCOPE on ACT_RU_TIMER_JOB(SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_TJOB_SUB_SCOPE on ACT_RU_TIMER_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_TJOB_SCOPE_DEF on ACT_RU_TIMER_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); - -create index ACT_IDX_SJOB_SCOPE on ACT_RU_SUSPENDED_JOB(SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_SJOB_SUB_SCOPE on ACT_RU_SUSPENDED_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_SJOB_SCOPE_DEF on ACT_RU_SUSPENDED_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); - -create index ACT_IDX_DJOB_SCOPE on ACT_RU_DEADLETTER_JOB(SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_DJOB_SUB_SCOPE on ACT_RU_DEADLETTER_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_DJOB_SCOPE_DEF on ACT_RU_DEADLETTER_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); - -create index ACT_IDX_EJOB_SCOPE on ACT_RU_EXTERNAL_JOB(SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_EJOB_SUB_SCOPE on ACT_RU_EXTERNAL_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_EJOB_SCOPE_DEF on ACT_RU_EXTERNAL_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); - -insert into ACT_GE_PROPERTY values ('job.schema.version', '7.0.1.1', 1); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql b/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql deleted file mode 100644 index a219e97..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql +++ /dev/null @@ -1,74 +0,0 @@ -drop index ACT_IDX_JOB_SCOPE; -drop index ACT_IDX_JOB_SUB_SCOPE; -drop index ACT_IDX_JOB_SCOPE_DEF; -drop index ACT_IDX_TJOB_SCOPE; -drop index ACT_IDX_TJOB_SUB_SCOPE; -drop index ACT_IDX_TJOB_SCOPE_DEF; -drop index ACT_IDX_SJOB_SCOPE; -drop index ACT_IDX_SJOB_SUB_SCOPE; -drop index ACT_IDX_SJOB_SCOPE_DEF; -drop index ACT_IDX_DJOB_SCOPE; -drop index ACT_IDX_DJOB_SUB_SCOPE; -drop index ACT_IDX_DJOB_SCOPE_DEF; -drop index ACT_IDX_EJOB_SCOPE; -drop index ACT_IDX_EJOB_SUB_SCOPE; -drop index ACT_IDX_EJOB_SCOPE_DEF; - -drop index ACT_IDX_JOB_EXCEPTION; -drop index ACT_IDX_JOB_CUSTOM_VAL_ID; -drop index ACT_IDX_JOB_CORRELATION_ID; - -drop index ACT_IDX_TJOB_EXCEPTION; -drop index ACT_IDX_TJOB_CUSTOM_VAL_ID; -drop index ACT_IDX_TJOB_CORRELATION_ID; -drop index ACT_IDX_TJOB_DUEDATE; - -drop index ACT_IDX_SJOB_EXCEPTION; -drop index ACT_IDX_SJOB_CUSTOM_VAL_ID; -drop index ACT_IDX_SJOB_CORRELATION_ID; - -drop index ACT_IDX_DJOB_EXCEPTION; -drop index ACT_IDX_DJOB_CUSTOM_VAL_ID; -drop index ACT_IDX_DJOB_CORRELATION_ID; - -drop index ACT_IDX_EJOB_EXCEPTION; -drop index ACT_IDX_EJOB_CUSTOM_VAL_ID; -drop index ACT_IDX_EJOB_CORRELATION_ID; - -alter table ACT_RU_JOB - drop CONSTRAINT ACT_FK_JOB_EXCEPTION; - -alter table ACT_RU_JOB - drop CONSTRAINT ACT_FK_JOB_CUSTOM_VAL; - -alter table ACT_RU_TIMER_JOB - drop CONSTRAINT ACT_FK_TJOB_EXCEPTION; - -alter table ACT_RU_TIMER_JOB - drop CONSTRAINT ACT_FK_TJOB_CUSTOM_VAL; - -alter table ACT_RU_SUSPENDED_JOB - drop CONSTRAINT ACT_FK_SJOB_EXCEPTION; - -alter table ACT_RU_SUSPENDED_JOB - drop CONSTRAINT ACT_FK_SJOB_CUSTOM_VAL; - -alter table ACT_RU_DEADLETTER_JOB - drop CONSTRAINT ACT_FK_DJOB_EXCEPTION; - -alter table ACT_RU_DEADLETTER_JOB - drop CONSTRAINT ACT_FK_DJOB_CUSTOM_VAL; - -alter table ACT_RU_EXTERNAL_JOB - drop CONSTRAINT ACT_FK_DJOB_EXCEPTION; - -alter table ACT_RU_EXTERNAL_JOB - drop CONSTRAINT ACT_FK_DJOB_CUSTOM_VAL; - -drop table ACT_RU_JOB; -drop table ACT_RU_TIMER_JOB; -drop table ACT_RU_SUSPENDED_JOB; -drop table ACT_RU_DEADLETTER_JOB; -drop table ACT_RU_HISTORY_JOB; -drop table ACT_RU_EXTERNAL_JOB; - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql deleted file mode 100644 index 1651c0c..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql +++ /dev/null @@ -1,64 +0,0 @@ -create table ACT_HI_TASKINST ( - ID_ VARCHAR2(64) not null, - REV_ INTEGER default 1, - PROC_DEF_ID_ VARCHAR2(64), - TASK_DEF_ID_ VARCHAR2(64), - TASK_DEF_KEY_ VARCHAR2(255), - PROC_INST_ID_ VARCHAR2(64), - EXECUTION_ID_ VARCHAR2(64), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), - PARENT_TASK_ID_ VARCHAR2(64), - STATE_ VARCHAR2(255), - NAME_ VARCHAR2(255), - DESCRIPTION_ VARCHAR2(2000), - OWNER_ VARCHAR2(255), - ASSIGNEE_ VARCHAR2(255), - START_TIME_ TIMESTAMP(6) not null, - IN_PROGRESS_TIME_ TIMESTAMP(6), - IN_PROGRESS_STARTED_BY_ VARCHAR2(255), - CLAIM_TIME_ TIMESTAMP(6), - CLAIMED_BY_ VARCHAR2(255), - SUSPENDED_TIME_ TIMESTAMP(6), - SUSPENDED_BY_ VARCHAR2(255), - END_TIME_ TIMESTAMP(6), - COMPLETED_BY_ VARCHAR2(255), - DURATION_ NUMBER(19,0), - DELETE_REASON_ VARCHAR2(2000), - PRIORITY_ INTEGER, - IN_PROGRESS_DUE_DATE_ TIMESTAMP(6), - DUE_DATE_ TIMESTAMP(6), - FORM_KEY_ VARCHAR2(255), - CATEGORY_ VARCHAR2(255), - TENANT_ID_ VARCHAR2(255) default '', - LAST_UPDATED_TIME_ TIMESTAMP(6), - primary key (ID_) -); - -create table ACT_HI_TSK_LOG ( - ID_ NUMBER(19), - TYPE_ VARCHAR2(64), - TASK_ID_ VARCHAR2(64) not null, - TIME_STAMP_ TIMESTAMP(6) not null, - USER_ID_ VARCHAR2(255), - DATA_ VARCHAR2(2000), - EXECUTION_ID_ VARCHAR2(64), - PROC_INST_ID_ VARCHAR2(64), - PROC_DEF_ID_ VARCHAR2(64), - SCOPE_ID_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - TENANT_ID_ VARCHAR2(255) default '', - primary key (ID_) -); - -create sequence act_hi_task_evt_log_seq start with 1 increment by 1; - -create index ACT_IDX_HI_TASK_SCOPE on ACT_HI_TASKINST(SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_HI_TASK_SUB_SCOPE on ACT_HI_TASKINST(SUB_SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_HI_TASK_SCOPE_DEF on ACT_HI_TASKINST(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql deleted file mode 100644 index 9430a1c..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql +++ /dev/null @@ -1,48 +0,0 @@ -create table ACT_RU_TASK ( - ID_ VARCHAR2(64), - REV_ INTEGER, - EXECUTION_ID_ VARCHAR2(64), - PROC_INST_ID_ VARCHAR2(64), - PROC_DEF_ID_ VARCHAR2(64), - TASK_DEF_ID_ VARCHAR2(64), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - SCOPE_DEFINITION_ID_ VARCHAR2(255), - PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), - STATE_ VARCHAR2(255), - NAME_ VARCHAR2(255), - PARENT_TASK_ID_ VARCHAR2(64), - DESCRIPTION_ VARCHAR2(2000), - TASK_DEF_KEY_ VARCHAR2(255), - OWNER_ VARCHAR2(255), - ASSIGNEE_ VARCHAR2(255), - DELEGATION_ VARCHAR2(64), - PRIORITY_ INTEGER, - CREATE_TIME_ TIMESTAMP(6), - IN_PROGRESS_TIME_ TIMESTAMP(6), - IN_PROGRESS_STARTED_BY_ VARCHAR2(255), - CLAIM_TIME_ TIMESTAMP(6), - CLAIMED_BY_ VARCHAR2(255), - SUSPENDED_TIME_ TIMESTAMP(6), - SUSPENDED_BY_ VARCHAR2(255), - IN_PROGRESS_DUE_DATE_ TIMESTAMP(6), - DUE_DATE_ TIMESTAMP(6), - CATEGORY_ VARCHAR2(255), - SUSPENSION_STATE_ INTEGER, - TENANT_ID_ VARCHAR2(255) DEFAULT '', - FORM_KEY_ VARCHAR2(255), - IS_COUNT_ENABLED_ NUMBER(1) CHECK (IS_COUNT_ENABLED_ IN (1,0)), - VAR_COUNT_ INTEGER, - ID_LINK_COUNT_ INTEGER, - SUB_TASK_COUNT_ INTEGER, - primary key (ID_) -); - -create index ACT_IDX_TASK_CREATE on ACT_RU_TASK(CREATE_TIME_); -create index ACT_IDX_TASK_SCOPE on ACT_RU_TASK(SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_TASK_SUB_SCOPE on ACT_RU_TASK(SUB_SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_TASK_SCOPE_DEF on ACT_RU_TASK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); - -insert into ACT_GE_PROPERTY values ('task.schema.version', '7.0.1.1', 1); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql deleted file mode 100644 index c5ce7bb..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql +++ /dev/null @@ -1,8 +0,0 @@ -drop index ACT_IDX_HI_TASK_SCOPE; -drop index ACT_IDX_HI_TASK_SUB_SCOPE; -drop index ACT_IDX_HI_TASK_SCOPE_DEF; - -drop sequence act_hi_task_evt_log_seq; - -drop table ACT_HI_TASKINST; -drop table ACT_HI_TSK_LOG; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql b/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql deleted file mode 100644 index 9ecd1e9..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql +++ /dev/null @@ -1,6 +0,0 @@ -drop index ACT_IDX_TASK_CREATE; -drop index ACT_IDX_TASK_SCOPE; -drop index ACT_IDX_TASK_SUB_SCOPE; -drop index ACT_IDX_TASK_SCOPE_DEF; - -drop table ACT_RU_TASK; diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql deleted file mode 100644 index 3d9b50f..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql +++ /dev/null @@ -1,26 +0,0 @@ -create table ACT_HI_VARINST ( - ID_ VARCHAR2(64) not null, - REV_ INTEGER default 1, - PROC_INST_ID_ VARCHAR2(64), - EXECUTION_ID_ VARCHAR2(64), - TASK_ID_ VARCHAR2(64), - NAME_ VARCHAR2(255) not null, - VAR_TYPE_ VARCHAR2(100), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - BYTEARRAY_ID_ VARCHAR2(64), - DOUBLE_ NUMBER(38,10), - LONG_ NUMBER(19,0), - TEXT_ VARCHAR2(2000), - TEXT2_ VARCHAR2(2000), - META_INFO_ VARCHAR2(2000), - CREATE_TIME_ TIMESTAMP(6), - LAST_UPDATED_TIME_ TIMESTAMP(6), - primary key (ID_) -); - -create index ACT_IDX_HI_PROCVAR_NAME_TYPE on ACT_HI_VARINST(NAME_, VAR_TYPE_); -create index ACT_IDX_HI_VAR_SCOPE_ID_TYPE on ACT_HI_VARINST(SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_HI_VAR_SUB_ID_TYPE on ACT_HI_VARINST(SUB_SCOPE_ID_, SCOPE_TYPE_); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql deleted file mode 100644 index 7c02f7f..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql +++ /dev/null @@ -1,31 +0,0 @@ -create table ACT_RU_VARIABLE ( - ID_ VARCHAR2(64) not null, - REV_ INTEGER, - TYPE_ VARCHAR2(255) not null, - NAME_ VARCHAR2(255) not null, - EXECUTION_ID_ VARCHAR2(64), - PROC_INST_ID_ VARCHAR2(64), - TASK_ID_ VARCHAR2(64), - SCOPE_ID_ VARCHAR2(255), - SUB_SCOPE_ID_ VARCHAR2(255), - SCOPE_TYPE_ VARCHAR2(255), - BYTEARRAY_ID_ VARCHAR2(64), - DOUBLE_ NUMBER(38,10), - LONG_ NUMBER(19,0), - TEXT_ VARCHAR2(2000), - TEXT2_ VARCHAR2(2000), - META_INFO_ VARCHAR2(2000), - primary key (ID_) -); - -create index ACT_IDX_RU_VAR_SCOPE_ID_TYPE on ACT_RU_VARIABLE(SCOPE_ID_, SCOPE_TYPE_); -create index ACT_IDX_RU_VAR_SUB_ID_TYPE on ACT_RU_VARIABLE(SUB_SCOPE_ID_, SCOPE_TYPE_); - -create index ACT_IDX_VAR_BYTEARRAY on ACT_RU_VARIABLE(BYTEARRAY_ID_); -alter table ACT_RU_VARIABLE - add constraint ACT_FK_VAR_BYTEARRAY - foreign key (BYTEARRAY_ID_) - references ACT_GE_BYTEARRAY (ID_); - -insert into ACT_GE_PROPERTY values ('variable.schema.version', '7.0.1.1', 1); - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql deleted file mode 100644 index efcc68d..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql +++ /dev/null @@ -1,6 +0,0 @@ -drop index ACT_IDX_HI_PROCVAR_NAME_TYPE; -drop index ACT_IDX_HI_VAR_SCOPE_ID_TYPE; -drop index ACT_IDX_HI_VAR_SUB_ID_TYPE; - -drop table ACT_HI_VARINST; - diff --git a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql b/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql deleted file mode 100644 index 0d0a95f..0000000 --- a/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql +++ /dev/null @@ -1,9 +0,0 @@ -drop index ACT_IDX_VAR_BYTEARRAY; -drop index ACT_IDX_RU_VAR_SCOPE_ID_TYPE; -drop index ACT_IDX_RU_VAR_SUB_ID_TYPE; - -alter table ACT_RU_VARIABLE - drop CONSTRAINT ACT_FK_VAR_BYTEARRAY; - -drop table ACT_RU_VARIABLE; - diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java deleted file mode 100644 index 83a42f8..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java +++ /dev/null @@ -1,274 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate; - -import cn.hutool.core.map.MapUtil; -import cn.hutool.extra.spring.SpringUtil; -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignStartUserHandlerTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other.BpmTaskCandidateAssignEmptyStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user.BpmTaskCandidateUserStrategy; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnModelConstants; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.ExtensionElement; -import org.flowable.bpmn.model.FlowElement; -import org.flowable.bpmn.model.UserTask; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.Spy; -import org.mockito.internal.util.collections.Sets; - -import java.util.*; - -import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomString; -import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_NAMESPACE; -import static org.flowable.bpmn.constants.BpmnXMLConstants.FLOWABLE_EXTENSIONS_PREFIX; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -/** - * {@link BpmTaskCandidateInvoker} 的单元测试 - * - * @author ZT - */ -public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest { - - private BpmTaskCandidateInvoker taskCandidateInvoker; - - @Mock - private AdminUserApi adminUserApi; - - @Mock - private BpmProcessInstanceService processInstanceService; - - @Spy - private BpmTaskCandidateStrategy userStrategy; - @Mock - private BpmTaskCandidateAssignEmptyStrategy emptyStrategy; - - @Spy - private List strategyList; - - @BeforeEach - public void setUp() { - userStrategy = new BpmTaskCandidateUserStrategy(); // 创建 strategy 实例 - when(emptyStrategy.getStrategy()).thenReturn(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY); - strategyList = List.of(userStrategy, emptyStrategy); // 创建 strategyList - taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi); - } - - /** - * 场景:成功计算到候选人,但是移除了发起人的用户 - */ - @Test - public void testCalculateUsersByTask_some() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - // 准备参数 - String param = "1,2"; - DelegateExecution execution = mock(DelegateExecution.class); - // mock 方法(DelegateExecution) - UserTask userTask = mock(UserTask.class); - String processInstanceId = randomString(); - when(execution.getProcessInstanceId()).thenReturn(processInstanceId); - when(execution.getCurrentFlowElement()).thenReturn(userTask); - when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY))) - .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString()); - when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM))) - .thenReturn(param); - // mock 方法(adminUserApi) - AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - Map userMap = MapUtil.builder(user1.getId(), user1) - .put(user2.getId(), user2).build(); - when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); - // mock 移除发起人的用户 - springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class)) - .thenReturn(processInstanceService); - ProcessInstance processInstance = mock(ProcessInstance.class); - when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance); - when(processInstance.getStartUserId()).thenReturn("1"); - mockFlowElementExtensionElement(userTask, BpmnModelConstants.USER_TASK_ASSIGN_START_USER_HANDLER_TYPE, - String.valueOf(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())); - - // 调用 - Set results = taskCandidateInvoker.calculateUsersByTask(execution); - // 断言 - assertEquals(asSet(2L), results); - } - } - - /** - * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配 - */ - @Test - public void testCalculateUsersByTask_none() { - try (MockedStatic springUtilMockedStatic = mockStatic(SpringUtil.class)) { - // 准备参数 - String param = "1,2"; - DelegateExecution execution = mock(DelegateExecution.class); - // mock 方法(DelegateExecution) - UserTask userTask = mock(UserTask.class); - String processInstanceId = randomString(); - when(execution.getProcessInstanceId()).thenReturn(processInstanceId); - when(execution.getCurrentFlowElement()).thenReturn(userTask); - when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY))) - .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy().toString()); - when(userTask.getAttributeValue(eq(BpmnModelConstants.NAMESPACE), eq(BpmnModelConstants.USER_TASK_CANDIDATE_PARAM))) - .thenReturn(param); - // mock 方法(adminUserApi) - AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) - .setStatus(CommonStatusEnum.DISABLE.getStatus())); - AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) - .setStatus(CommonStatusEnum.DISABLE.getStatus())); - Map userMap = MapUtil.builder(user1.getId(), user1) - .put(user2.getId(), user2).build(); - when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); - // mock 方法(empty) - when(emptyStrategy.calculateUsersByTask(same(execution), same(param))) - .thenReturn(Sets.newSet(2L)); - // mock 移除发起人的用户 - springUtilMockedStatic.when(() -> SpringUtil.getBean(BpmProcessInstanceService.class)) - .thenReturn(processInstanceService); - ProcessInstance processInstance = mock(ProcessInstance.class); - when(processInstanceService.getProcessInstance(eq(processInstanceId))).thenReturn(processInstance); - when(processInstance.getStartUserId()).thenReturn("1"); - - // 调用 - Set results = taskCandidateInvoker.calculateUsersByTask(execution); - // 断言 - assertEquals(asSet(2L), results); - } - } - - /** - * 场景:没有计算到候选人,但是被禁用移除,最终通过 empty 进行分配 - */ - @Test - public void testCalculateUsersByActivity_some() { - try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { - // 准备参数 - String param = "1,2"; - BpmnModel bpmnModel = mock(BpmnModel.class); - String activityId = randomString(); - Long startUserId = 1L; - String processDefinitionId = randomString(); - Map processVariables = new HashMap<>(); - // mock 方法(DelegateExecution) - UserTask userTask = mock(UserTask.class); - bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask))) - .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy()); - bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask))) - .thenReturn(param); - bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask); - // mock 方法(adminUserApi) - AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - Map userMap = MapUtil.builder(user1.getId(), user1) - .put(user2.getId(), user2).build(); - when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); - // mock 移除发起人的用户 - bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignStartUserHandlerType(same(userTask))) - .thenReturn(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType()); - - // 调用 - Set results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, - startUserId, processDefinitionId, processVariables); - // 断言 - assertEquals(asSet(2L), results); - } - } - - /** - * 场景:成功计算到候选人,但是移除了发起人的用户 - */ - @Test - public void testCalculateUsersByActivity_none() { - try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { - // 准备参数 - String param = "1,2"; - BpmnModel bpmnModel = mock(BpmnModel.class); - String activityId = randomString(); - Long startUserId = 1L; - String processDefinitionId = randomString(); - Map processVariables = new HashMap<>(); - // mock 方法(DelegateExecution) - UserTask userTask = mock(UserTask.class); - bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateStrategy(same(userTask))) - .thenReturn(BpmTaskCandidateStrategyEnum.USER.getStrategy()); - bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseCandidateParam(same(userTask))) - .thenReturn(param); - bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))).thenReturn(userTask); - // mock 方法(adminUserApi) - AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) - .setStatus(CommonStatusEnum.DISABLE.getStatus())); - AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) - .setStatus(CommonStatusEnum.DISABLE.getStatus())); - Map userMap = MapUtil.builder(user1.getId(), user1) - .put(user2.getId(), user2).build(); - when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap); - // mock 方法(empty) - when(emptyStrategy.calculateUsersByActivity(same(bpmnModel), eq(activityId), - eq(param), same(startUserId), same(processDefinitionId), same(processVariables))) - .thenReturn(Sets.newSet(2L)); - - // 调用 - Set results = taskCandidateInvoker.calculateUsersByActivity(bpmnModel, activityId, - startUserId, processDefinitionId, processVariables); - // 断言 - assertEquals(asSet(2L), results); - } - } - - private static void mockFlowElementExtensionElement(FlowElement element, String name, String value) { - if (value == null) { - return; - } - ExtensionElement extensionElement = new ExtensionElement(); - extensionElement.setNamespace(FLOWABLE_EXTENSIONS_NAMESPACE); - extensionElement.setNamespacePrefix(FLOWABLE_EXTENSIONS_PREFIX); - extensionElement.setElementText(value); - extensionElement.setName(name); - // mock - Map> extensionElements = element.getExtensionElements(); - if (extensionElements == null) { - extensionElements = new LinkedHashMap<>(); - } - extensionElements.put(name, Collections.singletonList(extensionElement)); - when(element.getExtensionElements()).thenReturn(extensionElements); - } - - @Test - public void testRemoveDisableUsers() { - // 准备参数. 1L 可以找到;2L 是禁用的;3L 找不到 - Set assigneeUserIds = asSet(1L, 2L, 3L); - // mock 方法 - AdminUserRespDTO user1 = randomPojo(AdminUserRespDTO.class, o -> o.setId(1L) - .setStatus(CommonStatusEnum.ENABLE.getStatus())); - AdminUserRespDTO user2 = randomPojo(AdminUserRespDTO.class, o -> o.setId(2L) - .setStatus(CommonStatusEnum.DISABLE.getStatus())); - Map userMap = MapUtil.builder(user1.getId(), user1) - .put(user2.getId(), user2).build(); - when(adminUserApi.getUserMap(eq(assigneeUserIds))).thenReturn(userMap); - - // 调用 - taskCandidateInvoker.removeDisableUsers(assigneeUserIds); - // 断言 - assertEquals(asSet(1L), assigneeUserIds); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java deleted file mode 100644 index d7d1151..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/expression/BpmTaskAssignLeaderExpressionTest.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.expression; - -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.impl.persistence.entity.ExecutionEntityImpl; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.Collections; -import java.util.Set; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -public class BpmTaskAssignLeaderExpressionTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskAssignLeaderExpression expression; - - @Mock - private AdminUserApi adminUserApi; - @Mock - private DeptApi deptApi; - - @Mock - private BpmProcessInstanceService processInstanceService; - - @Test - public void testCalculateUsers_noDept() { - // 准备参数 - DelegateExecution execution = mockDelegateExecution(1L); - // mock 方法(startUser) - AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptIds(Collections.singletonList(10L))); - when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser)); - // mock 方法(getStartUserDept)没有部门 - when(deptApi.getDept(eq(10L))).thenReturn(success(null)); - - // 调用 - Set result = expression.calculateUsers(execution, 1); - // 断言 - assertEquals(0, result.size()); - } - - @Test - public void testCalculateUsers_noParentDept() { - // 准备参数 - DelegateExecution execution = mockDelegateExecution(1L); - // mock 方法(startUser) - AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptIds(Collections.singletonList(10L))); - when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser)); - DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L) - .setLeaderUserId(20L)); - // mock 方法(getDept) - when(deptApi.getDept(eq(10L))).thenReturn(success(startUserDept)); - when(deptApi.getDept(eq(100L))).thenReturn(success(null)); - - // 调用 - Set result = expression.calculateUsers(execution, 2); - // 断言 - assertEquals(asSet(20L), result); - } - - @Test - public void testCalculateUsers_existParentDept() { - // 准备参数 - DelegateExecution execution = mockDelegateExecution(1L); - // mock 方法(startUser) - AdminUserRespDTO startUser = randomPojo(AdminUserRespDTO.class, o -> o.setDeptIds(Collections.singletonList(10L))); - when(adminUserApi.getUser(eq(1L))).thenReturn(success(startUser)); - DeptRespDTO startUserDept = randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(100L) - .setLeaderUserId(20L)); - when(deptApi.getDept(eq(10L))).thenReturn(success(startUserDept)); - // mock 方法(父 dept) - DeptRespDTO parentDept = randomPojo(DeptRespDTO.class, o -> o.setId(100L).setParentId(1000L) - .setLeaderUserId(200L)); - when(deptApi.getDept(eq(100L))).thenReturn(success(parentDept)); - - // 调用 - Set result = expression.calculateUsers(execution, 2); - // 断言 - assertEquals(asSet(200L), result); - } - - @SuppressWarnings("SameParameterValue") - private DelegateExecution mockDelegateExecution(Long startUserId) { - ExecutionEntityImpl execution = new ExecutionEntityImpl(); - execution.setProcessInstanceId(randomString()); - // mock 返回 startUserId - ExecutionEntityImpl processInstance = new ExecutionEntityImpl(); - processInstance.setStartUserId(String.valueOf(startUserId)); - when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))) - .thenReturn(processInstance); - return execution; - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java deleted file mode 100644 index 63d4c39..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderMultiStrategyTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import org.assertj.core.util.Sets; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.stubbing.Answer; - -import java.util.Set; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - -public class BpmTaskCandidateDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateDeptLeaderMultiStrategy strategy; - - @Mock - private DeptApi deptApi; - - @Test - public void testCalculateUsers() { - // 准备参数 - String param = "10,20|2"; - // mock 方法 - when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult>) invocationOnMock -> { - Long deptId = invocationOnMock.getArgument(0); - return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1))); - }); - - // 调用 - Set userIds = strategy.calculateUsers(param); - // 断言结果 - assertEquals(Sets.newLinkedHashSet(11L, 1001L, 21L, 2001L), userIds); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java deleted file mode 100644 index a7bbbf1..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptLeaderStrategyTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import com.zt.plat.framework.common.util.collection.SetUtils; -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import org.assertj.core.util.Sets; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.Set; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; -import static java.util.Arrays.asList; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -public class BpmTaskCandidateDeptLeaderStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateDeptLeaderStrategy strategy; - - @Mock - private DeptApi deptApi; - - @Test - public void testCalculateUsers() { - // 准备参数 - String param = "10,20"; - // mock 方法 - when(deptApi.getDeptList(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList( - randomPojo(DeptRespDTO.class, o -> o.setId(10L).setParentId(10L).setLeaderUserId(11L)), - randomPojo(DeptRespDTO.class, o -> o.setId(20L).setParentId(20L).setLeaderUserId(21L))))); - - // 调用 - Set userIds = strategy.calculateUsers(param); - // 断言结果 - assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java deleted file mode 100644 index 443eeb7..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateDeptMemberStrategyTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import com.zt.plat.framework.common.util.collection.SetUtils; -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import org.assertj.core.util.Sets; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.Set; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; -import static java.util.Arrays.asList; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -public class BpmTaskCandidateDeptMemberStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateDeptMemberStrategy strategy; - - @Mock - private DeptApi deptApi; - @Mock - private AdminUserApi adminUserApi; - - @Test - public void testCalculateUsers() { - // 准备参数 - String param = "10,20"; - // mock 方法 - when(adminUserApi.getUserListByDeptIds(eq(SetUtils.asSet(10L, 20L)))).thenReturn(success(asList( - randomPojo(AdminUserRespDTO.class, o -> o.setId(11L)), - randomPojo(AdminUserRespDTO.class, o -> o.setId(21L))))); - - // 调用 - Set userIds = strategy.calculateUsers(param); - // 断言结果 - assertEquals(Sets.newLinkedHashSet(11L, 21L), userIds); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java deleted file mode 100644 index 4a34602..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import org.assertj.core.util.Sets; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.stubbing.Answer; - -import java.util.Collections; -import java.util.Set; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class BpmTaskCandidateStartUserDeptLeaderMultiStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateStartUserDeptLeaderMultiStrategy strategy; - - @Mock - private BpmProcessInstanceService processInstanceService; - - @Mock - private AdminUserApi adminUserApi; - @Mock - private DeptApi deptApi; - - @Test - public void testCalculateUsersByTask() { - // 准备参数 - String param = "2"; - // mock 方法(获得流程发起人) - Long startUserId = 1L; - ProcessInstance processInstance = mock(ProcessInstance.class); - DelegateExecution execution = mock(DelegateExecution.class); - when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); - when(processInstance.getStartUserId()).thenReturn(startUserId.toString()); - // mock 方法(获取发起人的 multi 部门负责人) - mockGetStartUserDept(startUserId); - - // 调用 - Set userIds = strategy.calculateUsersByTask(execution, param); - // 断言 - assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds); - } - - @Test - public void testCalculateUsersByActivity() { - // 准备参数 - String param = "2"; - // mock 方法 - Long startUserId = 1L; - mockGetStartUserDept(startUserId); - - // 调用 - Set userIds = strategy.calculateUsersByActivity(null, null, param, - startUserId, null, null); - // 断言 - assertEquals(Sets.newLinkedHashSet(11L, 1001L), userIds); - } - - private void mockGetStartUserDept(Long startUserId) { - when(adminUserApi.getUser(eq(startUserId))).thenReturn( - success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptIds(Collections.singletonList(10L))))); - when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult>) invocationOnMock -> { - Long deptId = invocationOnMock.getArgument(0); - return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1))); - }); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java deleted file mode 100644 index f829b9e..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserDeptLeaderStrategyTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import com.zt.plat.framework.common.pojo.CommonResult; -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import com.zt.plat.module.system.api.dept.DeptApi; -import com.zt.plat.module.system.api.dept.dto.DeptRespDTO; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import org.assertj.core.util.Sets; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.stubbing.Answer; - -import java.util.Collections; -import java.util.Set; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class BpmTaskCandidateStartUserDeptLeaderStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateStartUserDeptLeaderStrategy strategy; - - @Mock - private BpmProcessInstanceService processInstanceService; - - @Mock - private AdminUserApi adminUserApi; - @Mock - private DeptApi deptApi; - - @Test - public void testCalculateUsersByTask() { - // 准备参数 - String param = "2"; - // mock 方法(获得流程发起人) - Long startUserId = 1L; - ProcessInstance processInstance = mock(ProcessInstance.class); - DelegateExecution execution = mock(DelegateExecution.class); - when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); - when(processInstance.getStartUserId()).thenReturn(startUserId.toString()); - // mock 方法(获取发起人的部门负责人) - mockGetStartUserDeptLeader(startUserId); - - // 调用 - Set userIds = strategy.calculateUsersByTask(execution, param); - // 断言 - assertEquals(Sets.newLinkedHashSet(1001L), userIds); - } - - @Test - public void testGetStartUserDeptLeader() { - // 准备参数 - String param = "2"; - // mock 方法 - Long startUserId = 1L; - mockGetStartUserDeptLeader(startUserId); - - // 调用 - Set userIds = strategy.calculateUsersByActivity(null, null, param, - startUserId, null, null); - // 断言 - assertEquals(Sets.newLinkedHashSet(1001L), userIds); - } - - private void mockGetStartUserDeptLeader(Long startUserId) { - when(adminUserApi.getUser(eq(startUserId))).thenReturn( - success(randomPojo(AdminUserRespDTO.class, o -> o.setId(startUserId).setDeptIds(Collections.singletonList(10L))))); - when(deptApi.getDept(any())).thenAnswer((Answer< CommonResult>) invocationOnMock -> { - Long deptId = invocationOnMock.getArgument(0); - return success(randomPojo(DeptRespDTO.class, o -> o.setId(deptId).setParentId(deptId * 100).setLeaderUserId(deptId + 1))); - }); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java deleted file mode 100644 index 2769280..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.dept; - -import cn.hutool.core.map.MapUtil; -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.bpm.framework.flowable.core.enums.BpmnVariableConstants; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import org.assertj.core.util.Sets; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class BpmTaskCandidateStartUserSelectStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateStartUserSelectStrategy strategy; - - @Mock - private BpmProcessInstanceService processInstanceService; - - @Test - public void testCalculateUsersByTask() { - // 准备参数 - String param = "2"; - // mock 方法(获得流程发起人) - ProcessInstance processInstance = mock(ProcessInstance.class); - DelegateExecution execution = mock(DelegateExecution.class); - when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); - when(execution.getCurrentActivityId()).thenReturn("activity_001"); - // mock 方法(FlowableUtils) - Map processVariables = new HashMap<>(); - processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, - MapUtil.of("activity_001", List.of(1L, 2L))); - when(processInstance.getProcessVariables()).thenReturn(processVariables); - - // 调用 - Set userIds = strategy.calculateUsersByTask(execution, param); - // 断言 - assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds); - } - - @Test - public void testCalculateUsersByActivity() { - // 准备参数 - String activityId = "activity_001"; - Map processVariables = new HashMap<>(); - processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, - MapUtil.of("activity_001", List.of(1L, 2L))); - - // 调用 - Set userIds = strategy.calculateUsersByActivity(null, activityId, null, - null, null, processVariables); - // 断言 - assertEquals(Sets.newLinkedHashSet(1L, 2L), userIds); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java deleted file mode 100644 index 1bf5725..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateAssignEmptyStrategyTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; - -import cn.hutool.core.collection.ListUtil; -import com.zt.plat.framework.common.util.collection.SetUtils; -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; -import com.zt.plat.module.bpm.enums.definition.BpmUserTaskAssignEmptyHandlerTypeEnum; -import com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.FlowElement; -import org.flowable.engine.delegate.DelegateExecution; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockedStatic; - -import java.util.Set; - -import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomString; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.Mockito.*; - -public class BpmTaskCandidateAssignEmptyStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateAssignEmptyStrategy strategy; - - @Mock - private BpmProcessDefinitionService processDefinitionService; - - @Test - public void testCalculateUsersByTask() { - try (MockedStatic flowableUtilMockedStatic = mockStatic(FlowableUtils.class); - MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { - // 准备参数 - DelegateExecution execution = mock(DelegateExecution.class); - String param = randomString(); - // mock 方法(execution) - String processDefinitionId = randomString(); - when(execution.getProcessDefinitionId()).thenReturn(processDefinitionId); - FlowElement flowElement = mock(FlowElement.class); - when(execution.getCurrentFlowElement()).thenReturn(flowElement); - // mock 方法(parseAssignEmptyHandlerType) - bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement))) - .thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_USER.getType()); - bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerUserIds(same(flowElement))) - .thenReturn(ListUtil.of(1L, 2L)); - - // 调用 - Set userIds = strategy.calculateUsersByTask(execution, param); - // 断言 - assertEquals(SetUtils.asSet(1L, 2L), userIds); - } - - } - - @Test - public void testCalculateUsersByActivity() { - try (MockedStatic bpmnModelUtilsMockedStatic = mockStatic(BpmnModelUtils.class)) { - // 准备参数 - String processDefinitionId = randomString(); - String activityId = randomString(); - String param = randomString(); - // mock 方法(getFlowElementById) - FlowElement flowElement = mock(FlowElement.class); - BpmnModel bpmnModel = mock(BpmnModel.class); - bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.getFlowElementById(same(bpmnModel), eq(activityId))) - .thenReturn(flowElement); - // mock 方法(parseAssignEmptyHandlerType) - bpmnModelUtilsMockedStatic.when(() -> BpmnModelUtils.parseAssignEmptyHandlerType(same(flowElement))) - .thenReturn(BpmUserTaskAssignEmptyHandlerTypeEnum.ASSIGN_ADMIN.getType()); - // mock 方法(getProcessDefinitionInfo) - BpmProcessDefinitionInfoDO processDefinition = randomPojo(BpmProcessDefinitionInfoDO.class, - o -> o.setManagerUserIds(ListUtil.of(1L, 2L))); - when(processDefinitionService.getProcessDefinitionInfo(eq(processDefinitionId))).thenReturn(processDefinition); - - // 调用 - Set userIds = strategy.calculateUsersByActivity(bpmnModel, activityId, param, - null, processDefinitionId, null); - // 断言 - assertEquals(SetUtils.asSet(1L, 2L), userIds); - } - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java deleted file mode 100644 index dc50daa..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.other; - -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.bpm.framework.flowable.core.util.FlowableUtils; -import org.flowable.engine.delegate.DelegateExecution; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.MockedStatic; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.*; - -@Disabled // TODO ZT:临时注释 -public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateExpressionStrategy strategy; - - @Test - public void testCalculateUsersByTask() { - try (MockedStatic flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) { - // 准备参数 - String param = "1,2"; - DelegateExecution execution = mock(DelegateExecution.class); - // mock 方法 - flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(execution), eq(param))) - .thenReturn(asSet(1L, 2L)); - - // 调用 - Set results = strategy.calculateUsersByTask(execution, param); - // 断言 - assertEquals(asSet(1L, 2L), results); - } - } - - @Test - public void testCalculateUsersByActivity() { - try (MockedStatic flowableUtilMockedStatic = mockStatic(FlowableUtils.class)) { - // 准备参数 - String param = "1,2"; - Map processVariables = new HashMap<>(); - // mock 方法 - flowableUtilMockedStatic.when(() -> FlowableUtils.getExpressionValue(same(processVariables), eq(param))) - .thenReturn(asSet(1L, 2L)); - - // 调用 - Set results = strategy.calculateUsersByActivity(null, null, param, - null, null, processVariables); - // 断言 - assertEquals(asSet(1L, 2L), results); - } - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java deleted file mode 100644 index 4caed36..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; - -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; -import com.zt.plat.module.bpm.service.definition.BpmUserGroupService; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.Arrays; -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -@Disabled // TODO ZT:临时注释 -public class BpmTaskCandidateGroupStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateGroupStrategy strategy; - - @Mock - private BpmUserGroupService userGroupService; - - @Test - public void testCalculateUsers() { - // 准备参数 - String param = "1,2"; - // mock 方法 - BpmUserGroupDO userGroup1 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(11L, 12L))); - BpmUserGroupDO userGroup2 = randomPojo(BpmUserGroupDO.class, o -> o.setUserIds(asSet(21L, 22L))); - when(userGroupService.getUserGroupList(eq(asSet(1L, 2L)))).thenReturn(Arrays.asList(userGroup1, userGroup2)); - - // 调用 - Set userIds = strategy.calculateUsersByTask(null, param); - // 断言 - assertEquals(asSet(11L, 12L, 21L, 22L), userIds); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java deleted file mode 100644 index 503a984..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; - -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.system.api.dept.PostApi; -import com.zt.plat.module.system.api.user.AdminUserApi; -import com.zt.plat.module.system.api.user.dto.AdminUserRespDTO; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.List; -import java.util.Set; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.CollectionUtils.convertList; -import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -@Disabled // TODO ZT:临时注释 -public class BpmTaskCandidatePostStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidatePostStrategy strategy; - - @Mock - private PostApi postApi; - @Mock - private AdminUserApi adminUserApi; - - @Test - public void testCalculateUsers() { - // 准备参数 - String param = "1,2"; - // mock 方法 - List users = convertList(asSet(11L, 22L), - id -> new AdminUserRespDTO().setId(id)); - when(adminUserApi.getUserListByPostIds(eq(asSet(1L, 2L)))).thenReturn(success(users)); - - // 调用 - Set userIds = strategy.calculateUsersByTask(null, param); - // 断言 - assertEquals(asSet(11L, 22L), userIds); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java deleted file mode 100644 index 0250e74..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; - -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.system.api.permission.PermissionApi; -import com.zt.plat.module.system.api.permission.RoleApi; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.Set; - -import static com.zt.plat.framework.common.pojo.CommonResult.success; -import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; - -@Disabled // TODO ZT:临时注释 -public class BpmTaskCandidateRoleStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateRoleStrategy strategy; - - @Mock - private RoleApi roleApi; - @Mock - private PermissionApi permissionApi; - - @Test - public void testCalculateUsers() { - // 准备参数 - String param = "1,2"; - // mock 方法 - when(permissionApi.getUserRoleIdListByRoleIds(eq(asSet(1L, 2L)))) - .thenReturn(success(asSet(11L, 22L))); - - // 调用 - Set userIds = strategy.calculateUsersByTask(null, param); - // 断言 - assertEquals(asSet(11L, 22L), userIds); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java deleted file mode 100644 index 79c08ba..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateStartUserStrategyTest.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; - -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; -import org.assertj.core.util.Sets; -import org.flowable.engine.delegate.DelegateExecution; -import org.flowable.engine.runtime.ProcessInstance; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; - -import java.util.Set; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class BpmTaskCandidateStartUserStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateStartUserStrategy strategy; - - @Mock - private BpmProcessInstanceService processInstanceService; - - @Test - public void testCalculateUsersByTask() { - // 准备参数 - String param = "2"; - // mock 方法(获得流程发起人) - Long startUserId = 1L; - ProcessInstance processInstance = mock(ProcessInstance.class); - DelegateExecution execution = mock(DelegateExecution.class); - when(processInstanceService.getProcessInstance(eq(execution.getProcessInstanceId()))).thenReturn(processInstance); - when(processInstance.getStartUserId()).thenReturn(startUserId.toString()); - - // 调用 - Set userIds = strategy.calculateUsersByTask(execution, param); - // 断言 - assertEquals(Sets.newLinkedHashSet(startUserId), userIds); - } - - @Test - public void testCalculateUsersByActivity() { - // 准备参数 - Long startUserId = 1L; - - // 调用 - Set userIds = strategy.calculateUsersByActivity(null, null, null, - startUserId, null, null); - // 断言 - assertEquals(Sets.newLinkedHashSet(startUserId), userIds); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java deleted file mode 100644 index 557e2d5..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.zt.plat.module.bpm.framework.flowable.core.candidate.strategy.user; - -import com.zt.plat.framework.test.core.ut.BaseMockitoUnitTest; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; - -import java.util.Set; - -import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; -import static org.junit.jupiter.api.Assertions.assertEquals; - -@Disabled // TODO ZT:临时注释 -public class BpmTaskCandidateUserStrategyTest extends BaseMockitoUnitTest { - - @InjectMocks - private BpmTaskCandidateUserStrategy strategy; - - @Test - public void test() { - // 准备参数 - String param = "1,2"; - - // 调用 - Set userIds = strategy.calculateUsersByTask(null, param); - // 断言 - assertEquals(asSet(1L, 2L), userIds); - } - - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java deleted file mode 100644 index 621d68e..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/category/BpmCategoryServiceImplTest.java +++ /dev/null @@ -1,136 +0,0 @@ -package com.zt.plat.module.bpm.service.category; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.test.core.ut.BaseDbUnitTest; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategoryPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.category.BpmCategorySaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; -import com.zt.plat.module.bpm.dal.mysql.category.BpmCategoryMapper; -import com.zt.plat.module.bpm.service.definition.BpmCategoryServiceImpl; -import jakarta.annotation.Resource; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import static com.zt.plat.framework.common.util.date.LocalDateTimeUtils.buildBetweenTime; -import static com.zt.plat.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static com.zt.plat.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static com.zt.plat.framework.test.core.util.AssertUtils.assertPojoEquals; -import static com.zt.plat.framework.test.core.util.AssertUtils.assertServiceException; -import static com.zt.plat.framework.test.core.util.RandomUtils.*; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.CATEGORY_NOT_EXISTS; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link BpmCategoryServiceImpl} 的单元测试类 - * - * @author ZT - */ -@Import(BpmCategoryServiceImpl.class) -public class BpmCategoryServiceImplTest extends BaseDbUnitTest { - - @Resource - private BpmCategoryServiceImpl categoryService; - - @Resource - private BpmCategoryMapper categoryMapper; - - @Test - public void testCreateCategory_success() { - // 准备参数 - BpmCategorySaveReqVO createReqVO = randomPojo(BpmCategorySaveReqVO.class).setId(null) - .setStatus(randomCommonStatus()); - - // 调用 - Long categoryId = categoryService.createCategory(createReqVO); - // 断言 - assertNotNull(categoryId); - // 校验记录的属性是否正确 - BpmCategoryDO category = categoryMapper.selectById(categoryId); - assertPojoEquals(createReqVO, category, "id"); - } - - @Test - public void testUpdateCategory_success() { - // mock 数据 - BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class); - categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 - // 准备参数 - BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class, o -> { - o.setId(dbCategory.getId()); // 设置更新的 ID - o.setStatus(randomCommonStatus()); - }); - - // 调用 - categoryService.updateCategory(updateReqVO); - // 校验是否更新正确 - BpmCategoryDO category = categoryMapper.selectById(updateReqVO.getId()); // 获取最新的 - assertPojoEquals(updateReqVO, category); - } - - @Test - public void testUpdateCategory_notExists() { - // 准备参数 - BpmCategorySaveReqVO updateReqVO = randomPojo(BpmCategorySaveReqVO.class); - - // 调用, 并断言异常 - assertServiceException(() -> categoryService.updateCategory(updateReqVO), CATEGORY_NOT_EXISTS); - } - - @Test - public void testDeleteCategory_success() { - // mock 数据 - BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class); - categoryMapper.insert(dbCategory);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbCategory.getId(); - - // 调用 - categoryService.deleteCategory(id); - // 校验数据不存在了 - assertNull(categoryMapper.selectById(id)); - } - - @Test - public void testDeleteCategory_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> categoryService.deleteCategory(id), CATEGORY_NOT_EXISTS); - } - - @Test - public void testGetCategoryPage() { - // mock 数据 - BpmCategoryDO dbCategory = randomPojo(BpmCategoryDO.class, o -> { // 等会查询到 - o.setName("芋头"); - o.setCode("xiaodun"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2023, 2, 2)); - }); - categoryMapper.insert(dbCategory); - // 测试 name 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setName("小盾"))); - // 测试 code 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCode("tudou"))); - // 测试 status 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - categoryMapper.insert(cloneIgnoreId(dbCategory, o -> o.setCreateTime(buildTime(2024, 2, 2)))); - // 准备参数 - BpmCategoryPageReqVO reqVO = new BpmCategoryPageReqVO(); - reqVO.setName("芋"); - reqVO.setCode("xiao"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime(buildBetweenTime(2023, 2, 1, 2023, 2, 28)); - - // 调用 - PageResult pageResult = categoryService.getCategoryPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbCategory, pageResult.getList().get(0)); - } - -} \ No newline at end of file diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java deleted file mode 100644 index 8742afe..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java +++ /dev/null @@ -1,144 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import cn.hutool.core.util.RandomUtil; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.common.util.json.JsonUtils; -import com.zt.plat.framework.test.core.ut.BaseDbUnitTest; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.form.BpmFormSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmFormDO; -import com.zt.plat.module.bpm.dal.mysql.definition.BpmFormMapper; -import com.zt.plat.module.bpm.service.definition.dto.BpmFormFieldRespDTO; -import jakarta.annotation.Resource; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import static com.zt.plat.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static com.zt.plat.framework.test.core.util.AssertUtils.assertPojoEquals; -import static com.zt.plat.framework.test.core.util.AssertUtils.assertServiceException; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomLongId; -import static com.zt.plat.framework.test.core.util.RandomUtils.randomPojo; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.FORM_NOT_EXISTS; -import static org.junit.jupiter.api.Assertions.*; - -/** - * {@link BpmFormServiceImpl} 的单元测试类 - * - * @author ZT - */ -@Import(BpmFormServiceImpl.class) -public class BpmFormServiceTest extends BaseDbUnitTest { - - @Resource - private BpmFormServiceImpl formService; - - @Resource - private BpmFormMapper formMapper; - - @Test - public void testCreateForm_success() { - // 准备参数 - BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { - o.setConf("{}"); - o.setFields(randomFields()); - }); - - // 调用 - Long formId = formService.createForm(reqVO); - // 断言 - assertNotNull(formId); - // 校验记录的属性是否正确 - BpmFormDO form = formMapper.selectById(formId); - assertPojoEquals(reqVO, form); - } - - @Test - public void testUpdateForm_success() { - // mock 数据 - BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> { - o.setConf("{}"); - o.setFields(randomFields()); - }); - formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据 - // 准备参数 - BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { - o.setId(dbForm.getId()); // 设置更新的 ID - o.setConf("{'zt': 'yuanma'}"); - o.setFields(randomFields()); - }); - - // 调用 - formService.updateForm(reqVO); - // 校验是否更新正确 - BpmFormDO form = formMapper.selectById(reqVO.getId()); // 获取最新的 - assertPojoEquals(reqVO, form); - } - - @Test - public void testUpdateForm_notExists() { - // 准备参数 - BpmFormSaveReqVO reqVO = randomPojo(BpmFormSaveReqVO.class, o -> { - o.setConf("{'zt': 'yuanma'}"); - o.setFields(randomFields()); - }); - - // 调用, 并断言异常 - assertServiceException(() -> formService.updateForm(reqVO), FORM_NOT_EXISTS); - } - - @Test - public void testDeleteForm_success() { - // mock 数据 - BpmFormDO dbForm = randomPojo(BpmFormDO.class); - formMapper.insert(dbForm);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbForm.getId(); - - // 调用 - formService.deleteForm(id); - // 校验数据不存在了 - assertNull(formMapper.selectById(id)); - } - - @Test - public void testDeleteForm_notExists() { - // 准备参数 - Long id = randomLongId(); - - // 调用, 并断言异常 - assertServiceException(() -> formService.deleteForm(id), FORM_NOT_EXISTS); - } - - @Test - public void testGetFormPage() { - // mock 数据 - BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> { // 等会查询到 - o.setName("ZT源码"); - }); - formMapper.insert(dbForm); - // 测试 name 不匹配 - formMapper.insert(cloneIgnoreId(dbForm, o -> o.setName("源码"))); - // 准备参数 - BpmFormPageReqVO reqVO = new BpmFormPageReqVO(); - reqVO.setName("ZT"); - - // 调用 - PageResult pageResult = formService.getFormPage(reqVO); - // 断言 - assertEquals(1, pageResult.getTotal()); - assertEquals(1, pageResult.getList().size()); - assertPojoEquals(dbForm, pageResult.getList().get(0)); - } - - private List randomFields() { - int size = RandomUtil.randomInt(1, 3); - return Stream.iterate(0, i -> i).limit(size) - .map(i -> JsonUtils.toJsonString(randomPojo(BpmFormFieldRespDTO.class))) - .collect(Collectors.toList()); - } - -} diff --git a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java b/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java deleted file mode 100644 index b2b04bc..0000000 --- a/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java +++ /dev/null @@ -1,129 +0,0 @@ -package com.zt.plat.module.bpm.service.definition; - -import com.zt.plat.framework.common.enums.CommonStatusEnum; -import com.zt.plat.framework.common.pojo.PageResult; -import com.zt.plat.framework.test.core.ut.BaseDbUnitTest; -import com.zt.plat.framework.test.core.util.AssertUtils; -import com.zt.plat.framework.test.core.util.RandomUtils; -import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupPageReqVO; -import com.zt.plat.module.bpm.controller.admin.definition.vo.group.BpmUserGroupSaveReqVO; -import com.zt.plat.module.bpm.dal.dataobject.definition.BpmUserGroupDO; -import com.zt.plat.module.bpm.dal.mysql.definition.BpmUserGroupMapper; -import jakarta.annotation.Resource; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.springframework.context.annotation.Import; - -import java.time.LocalDateTime; - -import static com.zt.plat.framework.common.util.date.LocalDateTimeUtils.buildTime; -import static com.zt.plat.framework.common.util.object.ObjectUtils.cloneIgnoreId; -import static com.zt.plat.module.bpm.enums.ErrorCodeConstants.USER_GROUP_NOT_EXISTS; - -/** - * {@link BpmUserGroupServiceImpl} 的单元测试类 - * - * @author ZT - */ -@Import(BpmUserGroupServiceImpl.class) -public class BpmUserGroupServiceTest extends BaseDbUnitTest { - - @Resource - private BpmUserGroupServiceImpl userGroupService; - - @Resource - private BpmUserGroupMapper userGroupMapper; - - @Test - public void testCreateUserGroup_success() { - // 准备参数 - BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class); - - // 调用 - Long userGroupId = userGroupService.createUserGroup(reqVO); - // 断言 - Assertions.assertNotNull(userGroupId); - // 校验记录的属性是否正确 - BpmUserGroupDO userGroup = userGroupMapper.selectById(userGroupId); - AssertUtils.assertPojoEquals(reqVO, userGroup); - } - - @Test - public void testUpdateUserGroup_success() { - // mock 数据 - BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class); - userGroupMapper.insert(dbUserGroup);// @Sql: 先插入出一条存在的数据 - // 准备参数 - BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class, o -> { - o.setId(dbUserGroup.getId()); // 设置更新的 ID - }); - - // 调用 - userGroupService.updateUserGroup(reqVO); - // 校验是否更新正确 - BpmUserGroupDO userGroup = userGroupMapper.selectById(reqVO.getId()); // 获取最新的 - AssertUtils.assertPojoEquals(reqVO, userGroup); - } - - @Test - public void testUpdateUserGroup_notExists() { - // 准备参数 - BpmUserGroupSaveReqVO reqVO = RandomUtils.randomPojo(BpmUserGroupSaveReqVO.class); - - // 调用, 并断言异常 - AssertUtils.assertServiceException(() -> userGroupService.updateUserGroup(reqVO), USER_GROUP_NOT_EXISTS); - } - - @Test - public void testDeleteUserGroup_success() { - // mock 数据 - BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class); - userGroupMapper.insert(dbUserGroup);// @Sql: 先插入出一条存在的数据 - // 准备参数 - Long id = dbUserGroup.getId(); - - // 调用 - userGroupService.deleteUserGroup(id); - // 校验数据不存在了 - Assertions.assertNull(userGroupMapper.selectById(id)); - } - - @Test - public void testDeleteUserGroup_notExists() { - // 准备参数 - Long id = RandomUtils.randomLongId(); - - // 调用, 并断言异常 - AssertUtils.assertServiceException(() -> userGroupService.deleteUserGroup(id), USER_GROUP_NOT_EXISTS); - } - - @Test - public void testGetUserGroupPage() { - // mock 数据 - BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class, o -> { // 等会查询到 - o.setName("ZT源码"); - o.setStatus(CommonStatusEnum.ENABLE.getStatus()); - o.setCreateTime(buildTime(2021, 11, 11)); - }); - userGroupMapper.insert(dbUserGroup); - // 测试 name 不匹配 - userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("ZT"))); - // 测试 status 不匹配 - userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); - // 测试 createTime 不匹配 - userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setCreateTime(buildTime(2021, 12, 12)))); - // 准备参数 - BpmUserGroupPageReqVO reqVO = new BpmUserGroupPageReqVO(); - reqVO.setName("源码"); - reqVO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - reqVO.setCreateTime((new LocalDateTime[]{buildTime(2021, 11, 10),buildTime(2021, 11, 12)})); - - // 调用 - PageResult pageResult = userGroupService.getUserGroupPage(reqVO); - // 断言 - Assertions.assertEquals(1, pageResult.getTotal()); - Assertions.assertEquals(1, pageResult.getList().size()); - AssertUtils.assertPojoEquals(dbUserGroup, pageResult.getList().get(0)); - } - -} diff --git a/zt-module-bpm-server/src/test/resources/application-unit-test.yaml b/zt-module-bpm-server/src/test/resources/application-unit-test.yaml deleted file mode 100644 index e92e3da..0000000 --- a/zt-module-bpm-server/src/test/resources/application-unit-test.yaml +++ /dev/null @@ -1,45 +0,0 @@ -spring: - main: - lazy-initialization: true # 开启懒加载,加快速度 - banner-mode: off # 单元测试,禁用 Banner - ---- #################### 数据库相关配置 #################### - -spring: - # 数据源配置项 - datasource: - name: ruoyi-vue-pro - url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写 - driver-class-name: org.h2.Driver - username: sa - password: - druid: - async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度 - initial-size: 1 # 单元测试,配置为 1,提升启动速度 - sql: - init: - schema-locations: classpath:/sql/create_tables.sql - -mybatis-plus: - lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 - type-aliases-package: ${zt.info.base-package}.dal.dataobject - global-config: - db-config: - id-type: AUTO # H2 主键递增 - ---- #################### 定时任务相关配置 #################### - ---- #################### 配置中心相关配置 #################### - ---- #################### 服务保障相关配置 #################### - -# Lock4j 配置项(单元测试,禁用 Lock4j) - ---- #################### 监控相关配置 #################### - ---- #################### ZT相关配置 #################### - -# ZT配置项,设置当前项目所有自定义的配置 -zt: - info: - base-package: com.zt.plat.module.bpm diff --git a/zt-module-bpm-server/src/test/resources/logback.xml b/zt-module-bpm-server/src/test/resources/logback.xml deleted file mode 100644 index daf756b..0000000 --- a/zt-module-bpm-server/src/test/resources/logback.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/zt-module-bpm-server/src/test/resources/sql/clean.sql b/zt-module-bpm-server/src/test/resources/sql/clean.sql deleted file mode 100644 index d4f93bb..0000000 --- a/zt-module-bpm-server/src/test/resources/sql/clean.sql +++ /dev/null @@ -1,3 +0,0 @@ -DELETE FROM "bpm_form"; -DELETE FROM "bpm_user_group"; -DELETE FROM "bpm_category"; diff --git a/zt-module-bpm-server/src/test/resources/sql/create_tables.sql b/zt-module-bpm-server/src/test/resources/sql/create_tables.sql deleted file mode 100644 index 1034962..0000000 --- a/zt-module-bpm-server/src/test/resources/sql/create_tables.sql +++ /dev/null @@ -1,43 +0,0 @@ -CREATE TABLE IF NOT EXISTS "bpm_user_group" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(63) NOT NULL, - "description" varchar(255) NOT NULL, - "status" tinyint NOT NULL, - "user_ids" varchar(255) NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '用户组'; - -CREATE TABLE IF NOT EXISTS "bpm_category" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(63) NOT NULL, - "code" varchar(63) NOT NULL, - "description" varchar(255) NOT NULL, - "status" tinyint NOT NULL, - "sort" int NOT NULL, - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '分类'; - -CREATE TABLE IF NOT EXISTS "bpm_form" ( - "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, - "name" varchar(63) NOT NULL, - "status" tinyint NOT NULL, - "fields" varchar(255) NOT NULL, - "conf" varchar(255) NOT NULL, - "remark" varchar(255), - "creator" varchar(64) DEFAULT '', - "create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "updater" varchar(64) DEFAULT '', - "update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - "deleted" bit NOT NULL DEFAULT FALSE, - PRIMARY KEY ("id") -) COMMENT '动态表单'; From 9ad0b03b6b0b73ff5c504c4cd09b69486220dccb Mon Sep 17 00:00:00 2001 From: chenbowen Date: Wed, 3 Dec 2025 20:32:06 +0800 Subject: [PATCH 33/35] =?UTF-8?q?1.=20=E6=8F=90=E5=87=BA=E7=A7=81=E6=9C=8D?= =?UTF-8?q?=20skywalking=20=E7=9A=84=E4=BE=9D=E8=B5=96=EF=BC=8C=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20ZStack=20=E8=87=AA=E5=B8=A6=E9=93=BE=E8=B7=AF?= =?UTF-8?q?=E8=BF=BD=E8=B8=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm/zt-module-bpm-server/Dockerfile | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/zt-module-bpm/zt-module-bpm-server/Dockerfile b/zt-module-bpm/zt-module-bpm-server/Dockerfile index eceda19..230aa5c 100644 --- a/zt-module-bpm/zt-module-bpm-server/Dockerfile +++ b/zt-module-bpm/zt-module-bpm-server/Dockerfile @@ -1,7 +1,6 @@ ## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 -ARG BASE_IMAGE=172.16.46.66:10043/base-service/skywalking-agent-jre:9.7.0 -FROM ${BASE_IMAGE} +FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre ## 创建目录,并使用它作为工作目录 RUN mkdir -p /zt-module-bpm-server @@ -11,15 +10,10 @@ COPY ./target/zt-module-bpm-server.jar app.jar ## 设置 TZ 时区 ## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 -ENV TZ=Asia/Shanghai -ENV JAVA_OPTS="-Xms512m -Xmx512m" -ENV SW_AGENT_HOME=/opt/skywalking/agent -ENV SW_AGENT_NAME=zt-module-bpm-server -ENV SW_AGENT_COLLECTOR_BACKEND_SERVICES=172.16.46.63:30201 -ENV AGENT_JAVA_OPTS="-javaagent:${SW_AGENT_HOME}/skywalking-agent.jar -Dskywalking.agent.service_name=${SW_AGENT_NAME} -Dskywalking.collector.backend_service=${SW_AGENT_COLLECTOR_BACKEND_SERVICES}" +ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m" ## 暴露后端项目的 48080 端口 EXPOSE 48083 ## 启动后端项目 -CMD java ${AGENT_JAVA_OPTS} ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar +CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar From a693c2d91ef75d77bd2b7acb1f7476b24d939d07 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Mon, 12 Jan 2026 16:38:14 +0800 Subject: [PATCH 34/35] =?UTF-8?q?1.=20bpm=20=E6=A8=A1=E5=9D=97=E6=95=B4?= =?UTF-8?q?=E5=90=88=E5=90=88=E5=B9=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/definition/dto/BpmFormPageReqDTO.java | 2 +- .../api/definition/dto/BpmFormRespDTO.java | 2 +- .../api/definition/dto/BpmFormSaveReqDTO.java | 2 +- .../definition/dto/BpmUserGroupRespDTO.java | 4 +- .../bpm/api/task/BpmProcessInstanceApi.java | 2 +- .../api/task/dto/BpmApprovalDetailReqDTO.java | 2 +- .../dto/BpmProcessInstancePageReqDTO.java | 2 +- .../task/dto/BpmProcessInstanceRespDTO.java | 6 +- .../bpm/api/task/dto/BpmTaskPageReqDTO.java | 2 +- .../bpm/api/task/dto/BpmTaskRespDTO.java | 6 +- .../bpm/api/task/dto/UserSimpleDTO.java | 2 +- zt-module-bpm/zt-module-bpm-server/Dockerfile | 4 +- zt-module-bpm/zt-module-bpm-server/pom.xml | 15 +--- .../admin/base/user/UserSimpleBaseVO.java | 2 +- .../admin/definition/BpmModelController.java | 2 +- .../definition/vo/form/BpmFormPageReqVO.java | 2 +- .../definition/vo/form/BpmFormRespVO.java | 2 +- .../definition/vo/form/BpmFormSaveReqVO.java | 2 +- .../vo/group/BpmUserGroupPageReqVO.java | 2 +- .../vo/group/BpmUserGroupRespVO.java | 4 +- .../vo/group/BpmUserGroupSaveReqVO.java | 4 +- .../vo/model/BpmModelMetaInfoVO.java | 4 + .../definition/vo/model/BpmModelRespVO.java | 3 +- .../vo/model/BpmModelSaveReqVO.java | 3 +- .../vo/model/simple/BpmSimpleModelNodeVO.java | 2 +- .../process/BpmProcessDefinitionRespVO.java | 2 +- .../admin/oa/vo/BpmOALeaveCreateReqVO.java | 2 +- .../admin/oa/vo/BpmOALeavePageReqVO.java | 2 +- .../admin/oa/vo/BpmOALeaveRespVO.java | 2 +- .../task/BpmProcessInstanceController.java | 35 ++++++++ .../vo/instance/BpmApprovalDetailReqVO.java | 2 +- .../BpmProcessInstanceCopyPageReqVO.java | 2 +- .../instance/BpmProcessInstancePageReqVO.java | 2 +- .../vo/instance/BpmProcessInstanceRespVO.java | 4 +- .../admin/task/vo/task/BpmTaskPageReqVO.java | 2 +- .../admin/task/vo/task/BpmTaskRespVO.java | 4 +- .../task/BpmProcessInstanceConvert.java | 13 ++- .../BpmProcessDefinitionInfoDO.java | 7 ++ .../task/BpmProcessInstanceCopyMapper.java | 6 ++ .../config/BpmFlowableConfiguration.java | 41 +++++++++ .../flowable/core/util/BpmnModelUtils.java | 2 +- .../flowable/core/util/SimpleModelUtils.java | 10 +-- .../rpc/config/RpcConfiguration.java | 9 +- .../config/SecurityConfiguration.java | 2 +- .../definition/BpmFormServiceImpl.java | 2 +- .../service/message/BpmMessageService.java | 2 +- ...BpmCreditLetterApprovalStatusListener.java | 2 +- .../task/BpmProcessInstanceCopyService.java | 9 ++ .../BpmProcessInstanceCopyServiceImpl.java | 5 ++ .../task/BpmProcessInstanceServiceImpl.java | 6 +- .../bpm/service/task/BpmTaskServiceImpl.java | 6 +- .../task/listener/BpmUserTaskListener.java | 6 +- .../bpm/service/task/trigger/BpmTrigger.java | 2 +- .../liquibase/database/core/DmDatabase.java | 90 ++++++++++++++----- .../services/liquibase.database.Database | 1 + .../src/main/resources/application-dev.yaml | 2 +- .../src/main/resources/application-local.yaml | 23 +++-- .../src/main/resources/application.yaml | 8 +- .../src/main/resources/logback-spring.xml | 37 ++------ ...pmTaskCandidateExpressionStrategyTest.java | 2 +- .../BpmTaskCandidateGroupStrategyTest.java | 2 +- .../BpmTaskCandidatePostStrategyTest.java | 2 +- .../BpmTaskCandidateRoleStrategyTest.java | 2 +- .../BpmTaskCandidateUserStrategyTest.java | 2 +- .../definition/BpmFormServiceTest.java | 4 +- .../definition/BpmUserGroupServiceTest.java | 4 +- .../test/resources/application-unit-test.yaml | 4 +- 67 files changed, 289 insertions(+), 166 deletions(-) diff --git a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java index 4ed3bc3..d75ff78 100644 --- a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java +++ b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormPageReqDTO.java @@ -12,7 +12,7 @@ import lombok.ToString; @ToString(callSuper = true) public class BpmFormPageReqDTO extends PageParam { - @Schema(description = "表单名称", example = "芋道") + @Schema(description = "表单名称", example = "ZT") private String name; } \ No newline at end of file diff --git a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java index a8cb537..e33b513 100644 --- a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java +++ b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormRespDTO.java @@ -12,7 +12,7 @@ public class BpmFormRespDTO { @Schema(description = "表单编号", example = "1024") private Long id; - @Schema(description = "表单名", example = "芋艿") + @Schema(description = "表单名", example = "ZT") private String name; @Schema(description = "表单状态", example = "1") diff --git a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java index 787a8cd..a2b4ab3 100644 --- a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java +++ b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmFormSaveReqDTO.java @@ -11,7 +11,7 @@ public class BpmFormSaveReqDTO { @Schema(description = "表单编号", example = "1024") private Long id; - @Schema(description = "表单名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @Schema(description = "表单名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") @NotEmpty(message = "表单名不能为空") private String name; diff --git a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java index 854efa6..f8acdeb 100644 --- a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java +++ b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/definition/dto/BpmUserGroupRespDTO.java @@ -13,10 +13,10 @@ public class BpmUserGroupRespDTO { @Schema(description = "编号", example = "1024") private Long id; - @Schema(description = "组名", example = "芋艿") + @Schema(description = "组名", example = "ZT") private String name; - @Schema(description = "描述", example = "芋艿") + @Schema(description = "描述", example = "ZT") private String description; @Schema(description = "成员用户编号数组", example = "1,2,3") diff --git a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java index 89839e8..5a2d905 100644 --- a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java +++ b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/BpmProcessInstanceApi.java @@ -12,7 +12,7 @@ import org.springframework.web.bind.annotation.*; import java.util.List; -@FeignClient(name = ApiConstants.NAME) // TODO 芋艿:fallbackFactory = +@FeignClient(name = ApiConstants.NAME) // TODO ZT:fallbackFactory = @Tag(name = "RPC 服务 - 流程实例") public interface BpmProcessInstanceApi { diff --git a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java index 4794386..2dc63a6 100644 --- a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java +++ b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmApprovalDetailReqDTO.java @@ -24,7 +24,7 @@ public class BpmApprovalDetailReqDTO { @Schema(description = "流程实例的编号", example = "1024") private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID - // TODO @芋艿:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 + // TODO @ZT:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 @Schema(description = "流程活动编号", example = "StartUserNode") private String activityId; // 用于获取表单权限。1)发起流程时,传"发起人节点" activityId 可获取发起人的表单权限;2)从抄送列表界面进来时,传抄送的 activityId 可获取抄送人的表单权限; diff --git a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java index ce6c38c..f23c182 100644 --- a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java +++ b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstancePageReqDTO.java @@ -16,7 +16,7 @@ public class BpmProcessInstancePageReqDTO extends PageParam { @Schema(description = "流程实例的编号", example = "1024") private String id; - @Schema(description = "流程实例的名字", example = "芋艿") + @Schema(description = "流程实例的名字", example = "ZT") private String name; @Schema(description = "流程定义的编号", example = "2048") diff --git a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java index 48c779c..eabc038 100644 --- a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java +++ b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmProcessInstanceRespDTO.java @@ -16,7 +16,7 @@ public class BpmProcessInstanceRespDTO { @Schema(description = "流程实例的编号", example = "1024") private String id; - @Schema(description = "流程实例的名字", example = "芋艿") + @Schema(description = "流程实例的名字", example = "ZT") private String name; @Schema(description = "流程摘要") @@ -49,7 +49,7 @@ public class BpmProcessInstanceRespDTO { @Schema(description = "持续时间", example = "1000") private Long durationInMillis; - @Schema(description = "提交的表单值", example = "{\"name\": \"芋艿\"}") + @Schema(description = "提交的表单值", example = "{\"name\": \"ZT\"}") private Map formVariables; @Schema(description = "业务的唯一标识", example = "1") @@ -77,7 +77,7 @@ public class BpmProcessInstanceRespDTO { @Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") diff --git a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java index fae7055..6ed4a8c 100644 --- a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java +++ b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskPageReqDTO.java @@ -13,7 +13,7 @@ import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH @Data public class BpmTaskPageReqDTO extends PageParam { - @Schema(description = "流程任务名", example = "芋艿") + @Schema(description = "流程任务名", example = "ZT") private String name; @Schema(description = "流程定义的编号", example = "2048") diff --git a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java index 412c098..17c5a44 100644 --- a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java +++ b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/BpmTaskRespDTO.java @@ -15,7 +15,7 @@ public class BpmTaskRespDTO { @Schema(description = "任务编号", example = "1024") private String id; - @Schema(description = "任务名字", example = "芋艿") + @Schema(description = "任务名字", example = "ZT") private String name; @Schema(description = "接收人的用户编号", example = "1") @@ -60,7 +60,7 @@ public class BpmTaskRespDTO { @Schema(description = "表单项的数组", example = "[]") private List formFields; - @Schema(description = "提交的表单值", example = "{\"name\": \"芋艿\"}") + @Schema(description = "提交的表单值", example = "{\"name\": \"ZT\"}") private Map formVariables; @Schema(description = "任务负责人编号", example = "2048") @@ -103,7 +103,7 @@ public class BpmTaskRespDTO { @Schema(description = "流程实例编号", example = "1024") private String id; - @Schema(description = "流程实例名称", example = "芋道") + @Schema(description = "流程实例名称", example = "ZT") private String name; @Schema(description = "提交时间") diff --git a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java index ddb00d6..295d43b 100644 --- a/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java +++ b/zt-module-bpm/zt-module-bpm-api/src/main/java/com/zt/plat/module/bpm/api/task/dto/UserSimpleDTO.java @@ -10,7 +10,7 @@ public class UserSimpleDTO { @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long id; - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String nickname; @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png") diff --git a/zt-module-bpm/zt-module-bpm-server/Dockerfile b/zt-module-bpm/zt-module-bpm-server/Dockerfile index 230aa5c..b8f089e 100644 --- a/zt-module-bpm/zt-module-bpm-server/Dockerfile +++ b/zt-module-bpm/zt-module-bpm-server/Dockerfile @@ -1,6 +1,6 @@ ## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性 -FROM 172.16.46.66:10043/base-service/eclipse-temurin:21-jre +FROM 172.17.19.16:10043/zt-cloud-base-service/eclipse-temurin:21-jre ## 创建目录,并使用它作为工作目录 RUN mkdir -p /zt-module-bpm-server @@ -10,7 +10,7 @@ COPY ./target/zt-module-bpm-server.jar app.jar ## 设置 TZ 时区 ## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖 -ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m" +ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx1024m" ## 暴露后端项目的 48080 端口 EXPOSE 48083 diff --git a/zt-module-bpm/zt-module-bpm-server/pom.xml b/zt-module-bpm/zt-module-bpm-server/pom.xml index 21b79b2..a897263 100644 --- a/zt-module-bpm/zt-module-bpm-server/pom.xml +++ b/zt-module-bpm/zt-module-bpm-server/pom.xml @@ -36,20 +36,9 @@ com.zt.plat zt-module-capital-api - ${business.capital.version} - - - com.zt.plat - zt-module-product-api - ${business.product.version} + ${revision} - - - com.zt.plat - zt-module-qms-api - ${business.qms.version} - com.zt.plat @@ -95,7 +84,7 @@ spring-cloud-starter-alibaba-nacos-config - + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java index 6fcbb96..68b9b8a 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/base/user/UserSimpleBaseVO.java @@ -9,7 +9,7 @@ public class UserSimpleBaseVO { @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Long id; - @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋艿") + @Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String nickname; @Schema(description = "用户头像", example = "https://www.iocoder.cn/1.png") private String avatar; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java index f787066..8a408e8 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/BpmModelController.java @@ -60,7 +60,7 @@ public class BpmModelController { @GetMapping("/list") @Operation(summary = "获得模型分页") - @Parameter(name = "name", description = "模型名称", example = "芋艿") + @Parameter(name = "name", description = "模型名称", example = "ZT") public CommonResult> getModelList(@RequestParam(value = "name", required = false) String name) { List list = modelService.getModelList(name); if (CollUtil.isEmpty(list)) { diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java index 437f67c..4160512 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormPageReqVO.java @@ -8,7 +8,7 @@ import lombok.Data; @Data public class BpmFormPageReqVO extends PageParam { - @Schema(description = "表单名称", example = "芋道") + @Schema(description = "表单名称", example = "ZT") private String name; } diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java index 42295df..1950d7a 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormRespVO.java @@ -14,7 +14,7 @@ public class BpmFormRespVO { @Schema(description = "表单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; - @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") @NotNull(message = "表单名称不能为空") private String name; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java index faa420b..5953485 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/form/BpmFormSaveReqVO.java @@ -13,7 +13,7 @@ public class BpmFormSaveReqVO { @Schema(description = "表单编号", example = "1024") private Long id; - @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "表单名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") @NotNull(message = "表单名称不能为空") private String name; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java index a146e35..25a0c2f 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupPageReqVO.java @@ -15,7 +15,7 @@ public class BpmUserGroupPageReqVO extends PageParam { @Schema(description = "编号", example = "1024") private Long id; - @Schema(description = "组名", example = "芋道") + @Schema(description = "组名", example = "ZT") private String name; @Schema(description = "状态", example = "1") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java index b356351..7bff95d 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupRespVO.java @@ -13,10 +13,10 @@ public class BpmUserGroupRespVO { @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private Long id; - @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; - @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道源码") + @Schema(description = "描述", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT源码") private String description; @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java index ce2c7d9..ceccee3 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/group/BpmUserGroupSaveReqVO.java @@ -13,11 +13,11 @@ public class BpmUserGroupSaveReqVO { @Schema(description = "编号", example = "1024") private Long id; - @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "组名", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") @NotNull(message = "组名不能为空") private String name; - @Schema(description = "描述", example = "芋道源码") + @Schema(description = "描述", example = "ZT源码") private String description; @Schema(description = "成员编号数组", requiredMode = Schema.RequiredMode.REQUIRED, example = "1,2,3") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java index cabc1fd..dcc06fc 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelMetaInfoVO.java @@ -56,6 +56,10 @@ public class BpmModelMetaInfoVO { @NotNull(message = "是否可见不能为空") private Boolean visible; + @Schema(description = "是否允许重新发起", requiredMode = Schema.RequiredMode.REQUIRED, example = "true") + @NotNull(message = "是否允许重新发起不能为空") + private Boolean restart; + @Schema(description = "可发起用户编号数组", example = "[1,2,3]") private List startUserIds; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java index 12d6ac1..63ef57d 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelRespVO.java @@ -5,6 +5,7 @@ import com.zt.plat.module.bpm.controller.admin.base.user.UserSimpleBaseVO; import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO; import com.zt.plat.module.bpm.controller.admin.definition.vo.process.BpmProcessDefinitionRespVO; import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotNull; import lombok.Data; import java.time.LocalDateTime; @@ -20,7 +21,7 @@ public class BpmModelRespVO extends BpmModelMetaInfoVO { @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "process_zt") private String key; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "流程图标", example = "https://www.iocoder.cn/zt.jpg") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java index 279519c..6d57341 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/BpmModelSaveReqVO.java @@ -4,6 +4,7 @@ import com.zt.plat.module.bpm.controller.admin.definition.vo.model.simple.BpmSim import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; import lombok.Data; @Schema(description = "管理后台 - 流程模型的保存 Request VO") @@ -17,7 +18,7 @@ public class BpmModelSaveReqVO extends BpmModelMetaInfoVO { @NotEmpty(message = "流程标识不能为空") private String key; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") @NotEmpty(message = "流程名称不能为空") private String name; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java index 235e062..f7e7928 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/model/simple/BpmSimpleModelNodeVO.java @@ -35,7 +35,7 @@ public class BpmSimpleModelNodeVO { @Schema(description = "模型节点名称", example = "领导审批") private String name; - @Schema(description = "节点展示内容", example = "指定成员: 芋道源码") + @Schema(description = "节点展示内容", example = "指定成员: ZT源码") private String showText; @Schema(description = "子节点") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java index 5e65c0f..f0d2892 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/definition/vo/process/BpmProcessDefinitionRespVO.java @@ -17,7 +17,7 @@ public class BpmProcessDefinitionRespVO extends BpmModelMetaInfoVO { @Schema(description = "版本", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer version; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "流程标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "youdao") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java index 4078b99..00779aa 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveCreateReqVO.java @@ -29,7 +29,7 @@ public class BpmOALeaveCreateReqVO { @Schema(description = "请假类型-参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer type; - @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码") + @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读ZT源码") private String reason; @Schema(description = "发起人自选审批人 Map", example = "{taskKey1: [1, 2]}") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java index f4226a4..31c9f1b 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeavePageReqVO.java @@ -19,7 +19,7 @@ public class BpmOALeavePageReqVO extends PageParam { @Schema(description = "请假类型,参见 bpm_oa_type", example = "1") private Integer type; - @Schema(description = "原因,模糊匹配", example = "阅读芋道源码") + @Schema(description = "原因,模糊匹配", example = "阅读ZT源码") private String reason; @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java index 02a5555..ae43fcd 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/oa/vo/BpmOALeaveRespVO.java @@ -15,7 +15,7 @@ public class BpmOALeaveRespVO { @Schema(description = "请假类型,参见 bpm_oa_type 枚举", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") private Integer type; - @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读芋道源码") + @Schema(description = "原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "阅读ZT源码") private String reason; @Schema(description = "申请时间", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java index 53ea6fa..d55f27a 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/BpmProcessInstanceController.java @@ -1,5 +1,6 @@ package com.zt.plat.module.bpm.controller.admin.task; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import com.zt.plat.framework.business.core.util.DeptUtil; @@ -11,8 +12,10 @@ import com.zt.plat.module.bpm.controller.admin.task.vo.instance.*; import com.zt.plat.module.bpm.convert.task.BpmProcessInstanceConvert; import com.zt.plat.module.bpm.dal.dataobject.definition.BpmCategoryDO; import com.zt.plat.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; +import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; import com.zt.plat.module.bpm.service.definition.BpmCategoryService; import com.zt.plat.module.bpm.service.definition.BpmProcessDefinitionService; +import com.zt.plat.module.bpm.service.task.BpmProcessInstanceCopyService; import com.zt.plat.module.bpm.service.task.BpmProcessInstanceService; import com.zt.plat.module.bpm.service.task.BpmTaskService; import com.zt.plat.module.system.api.dept.DeptApi; @@ -24,6 +27,8 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import jakarta.validation.Valid; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.collections4.list.SetUniqueList; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.task.api.Task; @@ -31,6 +36,8 @@ import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -53,6 +60,8 @@ public class BpmProcessInstanceController { private BpmProcessDefinitionService processDefinitionService; @Resource private BpmCategoryService categoryService; + @Resource + private BpmProcessInstanceCopyService processInstanceCopyService; @Resource private AdminUserApi adminUserApi; @@ -181,6 +190,32 @@ public class BpmProcessInstanceController { return success(processInstanceService.getApprovalDetail(getLoginUserId(), reqVO)); } + @GetMapping("/copy-list-by-process-instance-id") + @Operation(summary = "根据流程实例编号获取抄送列表") + @Parameter(name = "id", description = "流程实例的编号", required = true) + @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") + public CommonResult> getCopyListByProcessInstanceId(@RequestParam("processInstanceId") String processInstanceId) { + List copyDOList = processInstanceCopyService.getByProcessInstanceId(processInstanceId); + if (CollectionUtils.isEmpty(copyDOList)) { + return success(new ArrayList<>(0)); + } + List copyVOList = new ArrayList<>(copyDOList.size()); + SetUniqueList userIdList = SetUniqueList.setUniqueList(new ArrayList<>()); + for (BpmProcessInstanceCopyDO copyDO : copyDOList) { + BpmProcessInstanceCopyVO copyVO = new BpmProcessInstanceCopyVO(); + BeanUtil.copyProperties(copyDO, copyVO); + copyVOList.add(copyVO); + userIdList.add(copyDO.getStartUserId()); + userIdList.add(copyDO.getUserId()); + } + Map userMap = adminUserApi.getUserMap(userIdList); + for (BpmProcessInstanceCopyVO copyVO : copyVOList) { + copyVO.setStartUserName(userMap.get(copyVO.getStartUserId()).getNickname()); + copyVO.setUserName(userMap.get(copyVO.getUserId()).getNickname()); + } + return success(copyVOList); + } + @GetMapping("/get-next-approval-nodes") @Operation(summary = "获取下一个执行的流程节点") @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java index 3f37d7a..44c775b 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailReqVO.java @@ -24,7 +24,7 @@ public class BpmApprovalDetailReqVO { @Schema(description = "流程实例的编号", example = "1024") private String processInstanceId; // 使用场景:流程已发起时候传流程实例 ID - // TODO @芋艿:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 + // TODO @ZT:如果未来 BPMN 增加流程图,它没有发起人节点,会有问题。 @Schema(description = "流程活动编号", example = "StartUserNode") private String activityId; // 用于获取表单权限。1)发起流程时,传“发起人节点” activityId 可获取发起人的表单权限;2)从抄送列表界面进来时,传抄送的 activityId 可获取抄送人的表单权限; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java index b7956e4..2aff881 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyPageReqVO.java @@ -13,7 +13,7 @@ import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH @Data public class BpmProcessInstanceCopyPageReqVO extends PageParam { - @Schema(description = "流程名称", example = "芋道") + @Schema(description = "流程名称", example = "ZT") private String processInstanceName; @Schema(description = "创建时间") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java index 33875b6..130d75b 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstancePageReqVO.java @@ -15,7 +15,7 @@ import static com.zt.plat.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH @Data public class BpmProcessInstancePageReqVO extends PageParam { - @Schema(description = "流程名称", example = "芋道") + @Schema(description = "流程名称", example = "ZT") private String name; @Schema(description = "流程定义的标识", example = "2048") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java index a56e268..ae1abaf 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceRespVO.java @@ -18,7 +18,7 @@ public class BpmProcessInstanceRespVO { @Schema(description = "流程实例的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "流程名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "流程摘要") @@ -71,7 +71,7 @@ public class BpmProcessInstanceRespVO { @Schema(description = "流程任务的编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "任务名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "任务分配人编号", requiredMode = Schema.RequiredMode.NOT_REQUIRED, example = "2048") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java index c7c1721..5c1ee94 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskPageReqVO.java @@ -12,7 +12,7 @@ import java.time.LocalDateTime; @Data public class BpmTaskPageReqVO extends PageParam { - @Schema(description = "流程任务名", example = "芋道") + @Schema(description = "流程任务名", example = "ZT") private String name; @Schema(description = "流程分类", example = "1") diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java index e5318c5..0cafdb4 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/task/BpmTaskRespVO.java @@ -17,7 +17,7 @@ public class BpmTaskRespVO { @Schema(description = "任务编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "任务名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "任务名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @@ -97,7 +97,7 @@ public class BpmTaskRespVO { @Schema(description = "流程实例编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") private String id; - @Schema(description = "流程实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") + @Schema(description = "流程实例名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "ZT") private String name; @Schema(description = "提交时间", requiredMode = Schema.RequiredMode.REQUIRED) diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java index 791e694..87652f8 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/convert/task/BpmProcessInstanceConvert.java @@ -87,9 +87,16 @@ public interface BpmProcessInstanceConvert { }); } } - // 摘要 - respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfoMap.get(respVO.getProcessDefinitionId()), - pageResult.getList().get(i).getProcessVariables())); + BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionInfoMap.get(respVO.getProcessDefinitionId()); + if (processDefinitionInfo != null) { + // 摘要 + respVO.setSummary(FlowableUtils.getSummary(processDefinitionInfo, + pageResult.getList().get(i).getProcessVariables())); + // 是否可见 + respVO.getProcessDefinition().setVisible(processDefinitionInfo.getVisible()); + // 是否可以重新发起流程 + respVO.getProcessDefinition().setRestart(processDefinitionInfo.getRestart()); + } // 表单 respVO.setFormVariables(pageResult.getList().get(i).getProcessVariables()); } diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java index b65c192..43a879b 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/dataobject/definition/BpmProcessDefinitionInfoDO.java @@ -129,6 +129,13 @@ public class BpmProcessDefinitionInfoDO extends BaseDO { * 目的:如果 false 不可见,则不展示在“发起流程”的列表里 */ private Boolean visible; + + /** + * 是否允许重新发起 + * + * 目的:如果 false 则不可以重新发起流程 + */ + private Boolean restart; /** * 排序值 */ diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java index 61a73c3..b35d217 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/dal/mysql/task/BpmProcessInstanceCopyMapper.java @@ -7,6 +7,8 @@ import com.zt.plat.module.bpm.controller.admin.task.vo.instance.BpmProcessInstan import com.zt.plat.module.bpm.dal.dataobject.task.BpmProcessInstanceCopyDO; import org.apache.ibatis.annotations.Mapper; +import java.util.List; + @Mapper public interface BpmProcessInstanceCopyMapper extends BaseMapperX { @@ -22,4 +24,8 @@ public interface BpmProcessInstanceCopyMapper extends BaseMapperX getByProcessInstanceId(String processInstanceId) { + return selectList(BpmProcessInstanceCopyDO::getProcessInstanceId, processInstanceId); + } + } diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java index f0d5b49..159cfce 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/config/BpmFlowableConfiguration.java @@ -8,17 +8,25 @@ import com.zt.plat.module.bpm.framework.flowable.core.event.BpmProcessInstanceEv import com.zt.plat.module.system.api.user.AdminUserApi; import org.flowable.common.engine.api.delegate.FlowableFunctionDelegate; import org.flowable.common.engine.api.delegate.event.FlowableEventListener; +import org.flowable.engine.ProcessEngineConfiguration; import org.flowable.spring.SpringProcessEngineConfiguration; import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.task.AsyncListenableTaskExecutor; +import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.List; +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; /** * BPM 模块的 Flowable 配置类 @@ -28,6 +36,8 @@ import java.util.List; @Configuration(proxyBeanMethods = false) public class BpmFlowableConfiguration { + private static final Logger log = LoggerFactory.getLogger(BpmFlowableConfiguration.class); + /** * 参考 {@link org.flowable.spring.boot.FlowableJobConfiguration} 类,创建对应的 AsyncListenableTaskExecutor Bean * @@ -69,6 +79,37 @@ public class BpmFlowableConfiguration { }; } + @Bean + public EngineConfigurationConfigurer dmProcessEngineConfigurationConfigurer(DataSource dataSource) { + return configuration -> { + try { + configureDmCompatibility(configuration, dataSource); + } catch (SQLException ex) { + log.warn("Failed to inspect datasource for DM compatibility; Flowable will keep default settings", ex); + } + }; + } + + private void configureDmCompatibility(SpringProcessEngineConfiguration configuration, DataSource dataSource) throws SQLException { + Connection connection = null; + try { + connection = DataSourceUtils.getConnection(dataSource); + DatabaseMetaData metaData = connection.getMetaData(); + String productName = metaData.getDatabaseProductName(); + String jdbcUrl = metaData.getURL(); + boolean dmProduct = productName != null && productName.toLowerCase().contains("dm"); + boolean dmUrl = jdbcUrl != null && jdbcUrl.toLowerCase().startsWith("jdbc:dm"); + if (!dmProduct && !dmUrl) { + return; + } + log.info("Detected DM database (product='{}'); enabling Flowable Oracle compatibility with automatic schema updates", productName); + configuration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE); + configuration.setDatabaseType("oracle"); + } finally { + DataSourceUtils.releaseConnection(connection, dataSource); + } + } + // =========== 审批人相关的 Bean ========== @Bean diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java index 932afa7..d9bf32e 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/BpmnModelUtils.java @@ -123,7 +123,7 @@ public class BpmnModelUtils { public static Integer parseCandidateStrategy(FlowElement userTask) { Integer candidateStrategy = NumberUtils.parseInt(userTask.getAttributeValue( BpmnModelConstants.NAMESPACE, BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); - // TODO @芋艿 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限 + // TODO @ZT 尝试从 ExtensionElement 取. 后续相关扩展是否都可以 存 extensionElement。 如表单权限。 按钮权限 if (candidateStrategy == null) { ExtensionElement element = CollUtil.getFirst(userTask.getExtensionElements().get(BpmnModelConstants.USER_TASK_CANDIDATE_STRATEGY)); candidateStrategy = element != null ? NumberUtils.parseInt(element.getElementText()) : null; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java index e7b5533..562310a 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/flowable/core/util/SimpleModelUtils.java @@ -208,7 +208,7 @@ public class SimpleModelUtils { BpmSimpleModelNodeTypeEnum nodeType = BpmSimpleModelNodeTypeEnum.valueOf(node.getType()); BpmSimpleModelNodeVO childNode = node.getChildNode(); List conditionNodes = node.getConditionNodes(); - // TODO @芋艿 路由分支没有conditionNodes 这里注释会影响吗?@jason:一起帮忙瞅瞅! + // TODO @ZT 路由分支没有conditionNodes 这里注释会影响吗?@jason:一起帮忙瞅瞅! // Assert.notEmpty(conditionNodes, "分支节点的条件节点不能为空"); // 分支终点节点 ID String branchEndNodeId = null; @@ -277,8 +277,8 @@ public class SimpleModelUtils { String conditionExpression) { Assert.notEmpty(sourceId, "sourceId 不能为空"); Assert.notEmpty(targetId, "targetId 不能为空"); - // TODO @jason:如果 sequenceFlowId 不存在的时候,是不是要生成一个默认的 sequenceFlowId? @芋艿: 貌似不需要,Flowable 会默认生成;TODO @jason:建议还是搞一个,主要是后续好排查问题。 - // TODO @jason:如果 name 不存在的时候,是不是要生成一个默认的 name? @芋艿: 不需要生成默认的吧? 这个会在流程图展示的, 一般用户填写的。不好生成默认的吧;TODO @jason:建议还是搞一个,主要是后续好排查问题。 + // TODO @jason:如果 sequenceFlowId 不存在的时候,是不是要生成一个默认的 sequenceFlowId? @ZT: 貌似不需要,Flowable 会默认生成;TODO @jason:建议还是搞一个,主要是后续好排查问题。 + // TODO @jason:如果 name 不存在的时候,是不是要生成一个默认的 name? @ZT: 不需要生成默认的吧? 这个会在流程图展示的, 一般用户填写的。不好生成默认的吧;TODO @jason:建议还是搞一个,主要是后续好排查问题。 SequenceFlow sequenceFlow = new SequenceFlow(sourceId, targetId); if (StrUtil.isNotEmpty(sequenceFlowId)) { sequenceFlow.setId(sequenceFlowId); @@ -341,7 +341,7 @@ public class SimpleModelUtils { EndEvent endEvent = new EndEvent(); endEvent.setId(node.getId()); endEvent.setName(node.getName()); - // TODO @芋艿 + jason:要不要加一个终止定义? + // TODO @ZT + jason:要不要加一个终止定义? return endEvent; } @@ -369,7 +369,7 @@ public class SimpleModelUtils { // 添加操作按钮配置属性元素 addButtonsSetting(node.getButtonsSetting(), userTask); // 使用自动通过策略 - // TODO @芋艿 复用了SKIP, 是否需要新加一个策略;TODO @芋艿:【回复】是不是应该类似飞书,搞个草稿状态。待定;还有一种策略,不标记自动通过,而是首次发起后,第一个节点,自动通过; + // TODO @ZT 复用了SKIP, 是否需要新加一个策略;TODO @ZT:【回复】是不是应该类似飞书,搞个草稿状态。待定;还有一种策略,不标记自动通过,而是首次发起后,第一个节点,自动通过; addAssignStartUserHandlerType(BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType(), userTask); return userTask; } diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java index 30b7068..0ab98bc 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java @@ -1,11 +1,6 @@ package com.zt.plat.module.bpm.framework.rpc.config; -import com.zt.plat.module.capital.api.splyAmountRequest.AmountRequestApi; -import com.zt.plat.module.capital.api.splyAmtCrdtAppl.AmountCreditApplyApi; -import com.zt.plat.module.product.api.MesProcessRoutApi; -import com.zt.plat.module.product.api.plan.MesCompanyPlanApi; -import com.zt.plat.module.product.api.plan.MesFactoryPlanApi; -import com.zt.plat.module.qms.api.task.QmsApi; +import com.zt.plat.module.capital.api.AmountCreditApplyApi; import com.zt.plat.module.system.api.dept.DeptApi; import com.zt.plat.module.system.api.dept.PostApi; import com.zt.plat.module.system.api.dict.DictDataApi; @@ -18,6 +13,6 @@ import org.springframework.context.annotation.Configuration; @Configuration(value = "bpmRpcConfiguration", proxyBeanMethods = false) @EnableFeignClients(clients = {RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, - PermissionApi.class, AmountCreditApplyApi.class, MesProcessRoutApi.class, MesFactoryPlanApi.class, MesCompanyPlanApi.class, AmountRequestApi.class, QmsApi.class}) + PermissionApi.class, AmountCreditApplyApi.class}) public class RpcConfiguration { } diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java index eb6e099..5e59e23 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/security/config/SecurityConfiguration.java @@ -19,7 +19,7 @@ public class SecurityConfiguration { @Override public void customize(AuthorizeHttpRequestsConfigurer.AuthorizationManagerRequestMatcherRegistry registry) { - // TODO 芋艿:这个每个项目都需要重复配置,得捉摸有没通用的方案 + // TODO ZT:这个每个项目都需要重复配置,得捉摸有没通用的方案 // Swagger 接口文档 registry.requestMatchers("/v3/api-docs/**").permitAll() .requestMatchers("/webjars/**").permitAll() diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java index 73511ad..768239d 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceImpl.java @@ -94,7 +94,7 @@ public class BpmFormServiceImpl implements BpmFormService { * @param fields field 数组 */ private void validateFields(List fields) { - if (true) { // TODO 芋艿:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验 + if (true) { // TODO ZT:兼容 Vue3 工作流:因为采用了新的表单设计器,所以暂时不校验 return; } Map fieldMap = new HashMap<>(); // key 是 vModel,value 是 label diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java index 1698829..ccbd904 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/message/BpmMessageService.java @@ -9,7 +9,7 @@ import jakarta.validation.Valid; /** * BPM 消息 Service 接口 * - * TODO 芋艿:未来支持消息的可配置;不同的流程,在什么场景下,需要发送什么消息,消息的内容是什么; + * TODO ZT:未来支持消息的可配置;不同的流程,在什么场景下,需要发送什么消息,消息的内容是什么; * * @author ZT */ diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java index 099b709..83fed4c 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java @@ -3,7 +3,7 @@ package com.zt.plat.module.bpm.service.supply.capital.listener; import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEventListener; import com.zt.plat.module.bpm.enums.task.BpmProcessInstanceStatusEnum; -import com.zt.plat.module.capital.api.splyAmtCrdtAppl.AmountCreditApplyApi; +import com.zt.plat.module.capital.api.AmountCreditApplyApi; import com.zt.plat.module.capital.enums.AmountCreditApplyApiStatusEnum; import jakarta.annotation.Resource; import org.springframework.stereotype.Component; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java index e5f06b6..76350bc 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyService.java @@ -7,6 +7,7 @@ import jakarta.validation.constraints.NotEmpty; import org.flowable.bpmn.model.FlowNode; import java.util.Collection; +import java.util.List; /** * 流程抄送 Service 接口 @@ -57,4 +58,12 @@ public interface BpmProcessInstanceCopyService { */ void deleteProcessInstanceCopy(String processInstanceId); + /** + * 获得流程的抄送列表 + * + * @param processInstanceId 流程实例 ID + * @return 抄送流程列表 + */ + List getByProcessInstanceId(String processInstanceId); + } diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java index dd842b9..5fec394 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceCopyServiceImpl.java @@ -93,4 +93,9 @@ public class BpmProcessInstanceCopyServiceImpl implements BpmProcessInstanceCopy processInstanceCopyMapper.deleteByProcessInstanceId(processInstanceId); } + @Override + public List getByProcessInstanceId(String processInstanceId) { + return processInstanceCopyMapper.getByProcessInstanceId(processInstanceId); + } + } diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java index fc01ac7..d675a7e 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmProcessInstanceServiceImpl.java @@ -495,7 +495,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService for (HistoricActivityInstance activity : taskActivities) { HistoricTaskInstance task = taskMap.get(activity.getTaskId()); // 特殊情况:子流程节点 ChildProcess 仅存在于 activity 中,并且没有自身的 task,需要跳过执行 - // TODO @芋艿:后续看看怎么优化! + // TODO @ZT:后续看看怎么优化! if (task == null) { continue; } @@ -535,7 +535,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, List activities) { - // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance + // TODO @ZT:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 Set runActivityIds = convertSet(activities, HistoricActivityInstance::getActivityId); // 情况一:BPMN 设计器 @@ -558,7 +558,7 @@ public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService private ActivityNode buildNotRunApproveNodeForSimple(Long startUserId, BpmnModel bpmnModel, BpmProcessDefinitionInfoDO processDefinitionInfo, Map processVariables, BpmSimpleModelNodeVO node, Set runActivityIds) { - // TODO @芋艿:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance + // TODO @ZT:【可优化】在驳回场景下,未来的预测准确性不高。原因是,驳回后,HistoricActivityInstance // 包括了历史的操作,不是只有 startEvent 到当前节点的记录 if (runActivityIds.contains(node.getId())) { return null; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java index 1c44b8c..416444b 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/BpmTaskServiceImpl.java @@ -1008,7 +1008,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { .changeState(); // 3. 特殊:如果跳转到 EndEvent 流程还未结束, 执行 deleteProcessInstance 方法 - // TODO 芋艿:目前发现并行分支情况下,会存在这个情况,后续看看有没更好的方案; + // TODO ZT:目前发现并行分支情况下,会存在这个情况,后续看看有没更好的方案; List executions = runtimeService.createExecutionQuery().processInstanceId(processInstanceId).list(); if (CollUtil.isNotEmpty(executions)) { log.warn("[moveTaskToEnd][执行跳转到 EndEvent 后, 流程实例未结束,强制执行 deleteProcessInstance 方法]"); @@ -1331,7 +1331,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { return; } - // 自动去重,通过自动审批的方式 TODO @芋艿 驳回的情况得考虑一下;@lesan:驳回后,又自动审批么? + // 自动去重,通过自动审批的方式 TODO @ZT 驳回的情况得考虑一下;@lesan:驳回后,又自动审批么? BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId()); if (processDefinitionInfo == null) { log.error("[processTaskAssigned][taskId({}) 没有找到流程定义({})]", task.getId(), task.getProcessDefinitionId()); @@ -1374,7 +1374,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { } FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); // 判断是否为退回或者驳回:如果是退回或者驳回不走这个策略 - // TODO 芋艿:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 + // TODO ZT:【优化】未来有没更好的判断方式?!另外,还要考虑清理机制。就是说,下次处理了之后,就移除这个标识 Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(), String.format(PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class); Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(), diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java index 2102560..ec0f164 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/listener/BpmUserTaskListener.java @@ -16,7 +16,7 @@ import org.springframework.stereotype.Component; import static com.zt.plat.module.bpm.framework.flowable.core.util.BpmnModelUtils.parseListenerConfig; -// TODO @芋艿:可能会想换个包地址 +// TODO @ZT:可能会想换个包地址 /** * BPM 用户任务通用监听器 * @@ -42,7 +42,7 @@ public class BpmUserTaskListener implements TaskListener { BpmSimpleModelNodeVO.ListenerHandler listenerHandler = parseListenerConfig(listenerConfig); // 2. 发起请求 - // TODO @芋艿:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去; + // TODO @ZT:哪些默认参数,后续再调研下;感觉可以搞个 task 字段,把整个 delegateTask 放进去; listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("processInstanceId") .setType(BpmHttpRequestParamTypeEnum.FIXED_VALUE.getType()).setValue(delegateTask.getProcessInstanceId())); listenerHandler.getBody().add(new BpmSimpleModelNodeVO.HttpRequestParam().setKey("assignee") @@ -54,6 +54,6 @@ public class BpmUserTaskListener implements TaskListener { BpmHttpRequestUtils.executeBpmHttpRequest(processInstance, listenerHandler.getPath(), listenerHandler.getHeader(), listenerHandler.getBody(), false, null); - // 3. 是否需要后续操作?TODO 芋艿:待定! + // 3. 是否需要后续操作?TODO ZT:待定! } } \ No newline at end of file diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java index 02fb2d5..8aee717 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/task/trigger/BpmTrigger.java @@ -2,7 +2,7 @@ package com.zt.plat.module.bpm.service.task.trigger; import com.zt.plat.module.bpm.enums.definition.BpmTriggerTypeEnum; -// TODO @芋艿:可能会想换个包地址 +// TODO @ZT:可能会想换个包地址 /** * BPM 触发器接口 *

diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java index e7dc5bf..9e2c50f 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/database/core/DmDatabase.java @@ -5,6 +5,25 @@ package liquibase.database.core; +import java.lang.reflect.Method; +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import liquibase.CatalogAndSchema; import liquibase.GlobalConfiguration; import liquibase.Scope; @@ -23,17 +42,15 @@ import liquibase.statement.UniqueConstraint; import liquibase.statement.core.RawCallStatement; import liquibase.statement.core.RawParameterizedSqlStatement; import liquibase.structure.DatabaseObject; -import liquibase.structure.core.*; +import liquibase.structure.core.Catalog; +import liquibase.structure.core.Column; +import liquibase.structure.core.Index; +import liquibase.structure.core.PrimaryKey; +import liquibase.structure.core.Schema; import liquibase.util.JdbcUtil; import liquibase.util.StringUtil; import org.apache.commons.lang3.StringUtils; -import java.lang.reflect.Method; -import java.sql.*; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - public class DmDatabase extends AbstractJdbcDatabase { private static final String PROXY_USER_REGEX = ".*(?:thin|oci)\\:(.+)/@.*"; public static final Pattern PROXY_USER_PATTERN = Pattern.compile(".*(?:thin|oci)\\:(.+)/@.*"); @@ -98,6 +115,7 @@ public class DmDatabase extends AbstractJdbcDatabase { public void setConnection(DatabaseConnection conn) { this.reservedWords.addAll(Arrays.asList("GROUP", "USER", "SESSION", "PASSWORD", "RESOURCE", "START", "SIZE", "UID", "DESC", "ORDER")); Connection sqlConn = null; + boolean dmDatabase = false; if (!(conn instanceof OfflineConnection)) { try { if (conn instanceof JdbcConnection) { @@ -124,26 +142,42 @@ public class DmDatabase extends AbstractJdbcDatabase { Scope.getCurrentScope().getLog(this.getClass()).info("Could not set remarks reporting on OracleDatabase: " + e.getMessage()); } - CallableStatement statement = null; - try { - statement = sqlConn.prepareCall("{call DBMS_UTILITY.DB_VERSION(?,?)}"); - statement.registerOutParameter(1, 12); - statement.registerOutParameter(2, 12); - statement.execute(); - String compatibleVersion = statement.getString(2); - if (compatibleVersion != null) { - Matcher majorVersionMatcher = VERSION_PATTERN.matcher(compatibleVersion); - if (majorVersionMatcher.matches()) { - this.databaseMajorVersion = Integer.valueOf(majorVersionMatcher.group(1)); - this.databaseMinorVersion = Integer.valueOf(majorVersionMatcher.group(2)); + DatabaseMetaData metaData = sqlConn.getMetaData(); + if (metaData != null) { + String productName = metaData.getDatabaseProductName(); + dmDatabase = productName != null && PRODUCT_NAME.equalsIgnoreCase(productName); + if (dmDatabase) { + this.databaseMajorVersion = metaData.getDatabaseMajorVersion(); + this.databaseMinorVersion = metaData.getDatabaseMinorVersion(); } } } catch (SQLException e) { - String message = "Cannot read from DBMS_UTILITY.DB_VERSION: " + e.getMessage(); - Scope.getCurrentScope().getLog(this.getClass()).info("Could not set check compatibility mode on OracleDatabase, assuming not running in any sort of compatibility mode: " + message); - } finally { - JdbcUtil.closeStatement(statement); + Scope.getCurrentScope().getLog(this.getClass()).info("Unable to inspect database metadata for DM version detection: " + e.getMessage()); + } + + if (!dmDatabase) { + CallableStatement statement = null; + + try { + statement = sqlConn.prepareCall("{call DBMS_UTILITY.DB_VERSION(?,?)}"); + statement.registerOutParameter(1, 12); + statement.registerOutParameter(2, 12); + statement.execute(); + String compatibleVersion = statement.getString(2); + if (compatibleVersion != null) { + Matcher majorVersionMatcher = VERSION_PATTERN.matcher(compatibleVersion); + if (majorVersionMatcher.matches()) { + this.databaseMajorVersion = Integer.valueOf(majorVersionMatcher.group(1)); + this.databaseMinorVersion = Integer.valueOf(majorVersionMatcher.group(2)); + } + } + } catch (SQLException e) { + String message = "Cannot read from DBMS_UTILITY.DB_VERSION: " + e.getMessage(); + Scope.getCurrentScope().getLog(this.getClass()).info("Could not set check compatibility mode on OracleDatabase, assuming not running in any sort of compatibility mode: " + message); + } finally { + JdbcUtil.closeStatement(statement); + } } if (GlobalConfiguration.DDL_LOCK_TIMEOUT.getCurrentValue() != null) { @@ -250,7 +284,15 @@ public class DmDatabase extends AbstractJdbcDatabase { } public boolean isCorrectDatabaseImplementation(DatabaseConnection conn) throws DatabaseException { - return "oracle".equalsIgnoreCase(conn.getDatabaseProductName()); + String databaseProductName = conn == null ? null : conn.getDatabaseProductName(); + if (databaseProductName == null) { + return false; + } + if (PRODUCT_NAME.equalsIgnoreCase(databaseProductName)) { + return true; + } + // Flowable 历史上将 DM 映射为 Oracle 元数据,因此这里同样接受 Oracle 以保持兼容 + return "oracle".equalsIgnoreCase(databaseProductName); } public String getDefaultDriver(String url) { diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database b/zt-module-bpm/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database index 0ccf224..765e41a 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.database.Database @@ -13,6 +13,7 @@ liquibase.database.core.MariaDBDatabase liquibase.database.core.MockDatabase liquibase.database.core.MySQLDatabase liquibase.database.core.OracleDatabase +liquibase.database.core.DmDatabase liquibase.database.core.PostgresDatabase liquibase.database.core.SQLiteDatabase liquibase.database.core.SybaseASADatabase diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/application-dev.yaml b/zt-module-bpm/zt-module-bpm-server/src/main/resources/application-dev.yaml index b6b318c..26fb687 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/resources/application-dev.yaml +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/application-dev.yaml @@ -89,6 +89,6 @@ spring: instance: service-host-type: IP # 注册实例时,优先使用 IP [IP, HOST_NAME, CANONICAL_HOST_NAME] ---- #################### 芋道相关配置 #################### +--- #################### ZT相关配置 #################### diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/application-local.yaml b/zt-module-bpm/zt-module-bpm-server/src/main/resources/application-local.yaml index 2bf6df5..ae73a0b 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/resources/application-local.yaml +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/application-local.yaml @@ -39,14 +39,14 @@ spring: primary: master datasource: master: - url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 - username: jygk-test - password: Zgty@0527 + url: jdbc:dm://172.16.46.247:1050?schema=BPM + username: SYSDBA + password: pgbsci6ddJ6Sqj@e slave: # 模拟从库,可根据自己需要修改 # 模拟从库,可根据自己需要修改 lazy: true # 开启懒加载,保证启动速度 - url: jdbc:mysql://172.16.46.247:4787/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true&rewriteBatchedStatements=true # MySQL Connector/J 8.X 连接的示例 - username: jygk-test - password: Zgty@0527 + url: jdbc:dm://172.16.46.247:1050?schema=BPM + username: SYSDBA + password: pgbsci6ddJ6Sqj@e # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: @@ -56,6 +56,11 @@ spring: database: 0 # 数据库索引 # password: 123456 # 密码,建议生产环境开启 +# Flowable 在 DM 场景下需要识别为 Oracle 并自动升级表结构 +flowable: + database-schema-update: true + database-type: oracle + --- #################### MQ 消息队列相关配置 #################### --- #################### 定时任务相关配置 #################### @@ -97,11 +102,11 @@ logging: level: # 配置自己写的 MyBatis Mapper 打印日志 com.zt.plat.module.bpm.dal.mysql: debug - org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 + org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO ZT:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 ---- #################### 芋道相关配置 #################### +--- #################### ZT相关配置 #################### -# 芋道配置项,设置当前项目所有自定义的配置 +# ZT配置项,设置当前项目所有自定义的配置 zt: env: # 多环境的配置项 tag: ${HOSTNAME} diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/application.yaml b/zt-module-bpm/zt-module-bpm-server/src/main/resources/application.yaml index 09a74df..bf3f40d 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/resources/application.yaml +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/application.yaml @@ -68,7 +68,7 @@ springdoc: default-flat-param-object: true # 参见 https://doc.xiaominfo.com/docs/faq/v4/knife4j-parameterobject-flat-param 文档 knife4j: - enable: false # TODO 芋艿:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 + enable: false # TODO ZT:需要关闭增强,具体原因见:https://github.com/xiaoymin/knife4j/issues/874 setting: language: zh_cn @@ -127,7 +127,7 @@ xxl: logpath: ${user.home}/logs/xxl-job/${spring.application.name} # 执行器运行日志文件存储磁盘路径 accessToken: default_token # 执行器通讯TOKEN ---- #################### 芋道相关配置 #################### +--- #################### ZT相关配置 #################### zt: info: @@ -141,8 +141,8 @@ zt: exclude-urls: # 如下 url,仅仅是为了演示,去掉配置也没关系 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 swagger: - title: Bpm 模块 - description: 提供 Bpm 管理的所有功能 + title: 流程模块 + description: 提供流程模块功能 version: ${zt.info.version} tenant: # 多租户相关配置项 enable: true diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/logback-spring.xml b/zt-module-bpm/zt-module-bpm-server/src/main/resources/logback-spring.xml index 590fafc..51a9a48 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/resources/logback-spring.xml +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/logback-spring.xml @@ -5,10 +5,6 @@ - - - -       @@ -60,46 +56,25 @@ - - - ${LOG_DIR}-error.log - - ERROR - ACCEPT - DENY - - - ${LOG_DIR}-error.%d{yyyy-MM-dd}.log - 30 - - - %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n - - - - - - + + - - - - - - - + + + + diff --git a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java index d9ad569..dc50daa 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java +++ b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/other/BpmTaskCandidateExpressionStrategyTest.java @@ -17,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; -@Disabled // TODO 芋艿:临时注释 +@Disabled // TODO ZT:临时注释 public class BpmTaskCandidateExpressionStrategyTest extends BaseMockitoUnitTest { @InjectMocks diff --git a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java index 1bb03b5..4caed36 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java +++ b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateGroupStrategyTest.java @@ -17,7 +17,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -@Disabled // TODO 芋艿:临时注释 +@Disabled // TODO ZT:临时注释 public class BpmTaskCandidateGroupStrategyTest extends BaseMockitoUnitTest { @InjectMocks diff --git a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java index 8560052..503a984 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java +++ b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidatePostStrategyTest.java @@ -19,7 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -@Disabled // TODO 芋艿:临时注释 +@Disabled // TODO ZT:临时注释 public class BpmTaskCandidatePostStrategyTest extends BaseMockitoUnitTest { @InjectMocks diff --git a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java index 9af421a..0250e74 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java +++ b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateRoleStrategyTest.java @@ -16,7 +16,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -@Disabled // TODO 芋艿:临时注释 +@Disabled // TODO ZT:临时注释 public class BpmTaskCandidateRoleStrategyTest extends BaseMockitoUnitTest { @InjectMocks diff --git a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java index 91e19c5..557e2d5 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java +++ b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/framework/flowable/core/candidate/strategy/user/BpmTaskCandidateUserStrategyTest.java @@ -10,7 +10,7 @@ import java.util.Set; import static com.zt.plat.framework.common.util.collection.SetUtils.asSet; import static org.junit.jupiter.api.Assertions.assertEquals; -@Disabled // TODO 芋艿:临时注释 +@Disabled // TODO ZT:临时注释 public class BpmTaskCandidateUserStrategyTest extends BaseMockitoUnitTest { @InjectMocks diff --git a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java index 02160d9..8742afe 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java +++ b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmFormServiceTest.java @@ -117,14 +117,14 @@ public class BpmFormServiceTest extends BaseDbUnitTest { public void testGetFormPage() { // mock 数据 BpmFormDO dbForm = randomPojo(BpmFormDO.class, o -> { // 等会查询到 - o.setName("芋道源码"); + o.setName("ZT源码"); }); formMapper.insert(dbForm); // 测试 name 不匹配 formMapper.insert(cloneIgnoreId(dbForm, o -> o.setName("源码"))); // 准备参数 BpmFormPageReqVO reqVO = new BpmFormPageReqVO(); - reqVO.setName("芋道"); + reqVO.setName("ZT"); // 调用 PageResult pageResult = formService.getFormPage(reqVO); diff --git a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java index 34a5205..b2b04bc 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java +++ b/zt-module-bpm/zt-module-bpm-server/src/test/java/com/zt/plat/module/bpm/service/definition/BpmUserGroupServiceTest.java @@ -101,13 +101,13 @@ public class BpmUserGroupServiceTest extends BaseDbUnitTest { public void testGetUserGroupPage() { // mock 数据 BpmUserGroupDO dbUserGroup = RandomUtils.randomPojo(BpmUserGroupDO.class, o -> { // 等会查询到 - o.setName("芋道源码"); + o.setName("ZT源码"); o.setStatus(CommonStatusEnum.ENABLE.getStatus()); o.setCreateTime(buildTime(2021, 11, 11)); }); userGroupMapper.insert(dbUserGroup); // 测试 name 不匹配 - userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("芋道"))); + userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setName("ZT"))); // 测试 status 不匹配 userGroupMapper.insert(cloneIgnoreId(dbUserGroup, o -> o.setStatus(CommonStatusEnum.DISABLE.getStatus()))); // 测试 createTime 不匹配 diff --git a/zt-module-bpm/zt-module-bpm-server/src/test/resources/application-unit-test.yaml b/zt-module-bpm/zt-module-bpm-server/src/test/resources/application-unit-test.yaml index a669ee0..e92e3da 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/test/resources/application-unit-test.yaml +++ b/zt-module-bpm/zt-module-bpm-server/src/test/resources/application-unit-test.yaml @@ -37,9 +37,9 @@ mybatis-plus: --- #################### 监控相关配置 #################### ---- #################### 芋道相关配置 #################### +--- #################### ZT相关配置 #################### -# 芋道配置项,设置当前项目所有自定义的配置 +# ZT配置项,设置当前项目所有自定义的配置 zt: info: base-package: com.zt.plat.module.bpm From 234ab78ba946cb684095be0026d1cb9dad60fb42 Mon Sep 17 00:00:00 2001 From: chenbowen Date: Mon, 12 Jan 2026 17:11:30 +0800 Subject: [PATCH 35/35] =?UTF-8?q?1.=20=E5=90=88=E5=B9=B6=20=E8=BE=BE?= =?UTF-8?q?=E6=A2=A6=E6=95=B0=E6=8D=AE=E5=BA=93=E5=85=BC=E5=AE=B9=E6=94=B9?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zt-module-bpm/zt-module-bpm-server/pom.xml | 13 +- .../vo/instance/BpmProcessInstanceCopyVO.java | 88 + .../rpc/config/RpcConfiguration.java | 9 +- ...BpmCreditLetterApprovalStatusListener.java | 2 +- .../datatype/core/DmBooleanType.java | 32 + .../snapshot/JdbcDatabaseSnapshot.java | 1957 +++++++++++++++++ .../engine/impl/db/DbSqlSessionFactory.java | 354 +++ .../liquibase.datatype.LiquibaseDataType | 1 + .../create/flowable.oracle.create.batch.sql | 41 + .../db/drop/flowable.oracle.drop.batch.sql | 4 + .../create/flowable.oracle.create.common.sql | 23 + .../db/drop/flowable.oracle.drop.common.sql | 2 + .../create/flowable.oracle.create.engine.sql | 355 +++ .../create/flowable.oracle.create.history.sql | 114 + .../db/drop/flowable.oracle.drop.engine.sql | 148 ++ .../db/drop/flowable.oracle.drop.history.sql | 23 + ...wable.oracle.create.entitylink.history.sql | 23 + .../flowable.oracle.create.entitylink.sql | 26 + ...lowable.oracle.drop.entitylink.history.sql | 4 + .../drop/flowable.oracle.drop.entitylink.sql | 4 + ...owable.oracle.create.eventsubscription.sql | 28 + ...flowable.oracle.drop.eventsubscription.sql | 5 + ...ble.oracle.create.identitylink.history.sql | 20 + .../flowable.oracle.create.identitylink.sql | 24 + ...wable.oracle.drop.identitylink.history.sql | 6 + .../flowable.oracle.drop.identitylink.sql | 7 + .../flowable.oracle.create.identity.sql | 108 + .../db/drop/flowable.oracle.drop.identity.sql | 22 + .../db/create/flowable.oracle.create.job.sql | 261 +++ .../db/drop/flowable.oracle.drop.job.sql | 74 + .../flowable.oracle.create.task.history.sql | 64 + .../db/create/flowable.oracle.create.task.sql | 48 + .../flowable.oracle.drop.task.history.sql | 8 + .../db/drop/flowable.oracle.drop.task.sql | 6 + ...lowable.oracle.create.variable.history.sql | 26 + .../flowable.oracle.create.variable.sql | 31 + .../flowable.oracle.drop.variable.history.sql | 6 + .../db/drop/flowable.oracle.drop.variable.sql | 9 + 38 files changed, 3972 insertions(+), 4 deletions(-) create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql create mode 100644 zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql diff --git a/zt-module-bpm/zt-module-bpm-server/pom.xml b/zt-module-bpm/zt-module-bpm-server/pom.xml index a897263..72239fb 100644 --- a/zt-module-bpm/zt-module-bpm-server/pom.xml +++ b/zt-module-bpm/zt-module-bpm-server/pom.xml @@ -36,9 +36,20 @@ com.zt.plat zt-module-capital-api - ${revision} + ${business.capital.version} + + + com.zt.plat + zt-module-product-api + ${business.product.version} + + + com.zt.plat + zt-module-qms-api + ${business.qms.version} + com.zt.plat diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java new file mode 100644 index 0000000..0012c19 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/controller/admin/task/vo/instance/BpmProcessInstanceCopyVO.java @@ -0,0 +1,88 @@ +package com.zt.plat.module.bpm.controller.admin.task.vo.instance; + +import com.zt.plat.framework.mybatis.core.dataobject.BaseDO; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 流程抄送 VO + * + * @author kr + * @since 2025-12-31 + */ +@Data +@NoArgsConstructor +public class BpmProcessInstanceCopyVO extends BaseDO { + + /** + * 编号 + */ + private Long id; + + /** + * 发起人 Id + */ + @Schema(description ="发起人 Id") + private Long startUserId; + + /** + * 发起人 姓名 + */ + @Schema(description ="发起人 姓名") + private String startUserName; + /** + * 流程名 + */ + @Schema(description ="流程名") + private String processInstanceName; + /** + * 流程实例的编号 + */ + @Schema(description ="流程实例的编号") + private String processInstanceId; + /** + * 流程实例的流程定义编号 + */ + @Schema(description ="流程实例的流程定义编号") + private String processDefinitionId; + /** + * 流程分类 + */ + @Schema(description ="流程分类") + private String category; + /** + * 流程活动的编号 + */ + @Schema(description ="流程活动的编号") + private String activityId; + /** + * 流程活动的名字 + */ + @Schema(description ="流程活动的名字") + private String activityName; + /** + * 流程活动的编号 + */ + @Schema(description ="流程活动的编号") + private String taskId; + + /** + * 用户编号(被抄送的用户编号) + */ + @Schema(description ="用户编号(被抄送的用户编号)") + private Long userId; + + /** + * 用户姓名(被抄送的用户姓名) + */ + @Schema(description ="用户姓名(被抄送的用户姓名)") + private String userName; + + /** + * 抄送意见 + */ + @Schema(description ="抄送意见") + private String reason; + +} diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java index 0ab98bc..30b7068 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/framework/rpc/config/RpcConfiguration.java @@ -1,6 +1,11 @@ package com.zt.plat.module.bpm.framework.rpc.config; -import com.zt.plat.module.capital.api.AmountCreditApplyApi; +import com.zt.plat.module.capital.api.splyAmountRequest.AmountRequestApi; +import com.zt.plat.module.capital.api.splyAmtCrdtAppl.AmountCreditApplyApi; +import com.zt.plat.module.product.api.MesProcessRoutApi; +import com.zt.plat.module.product.api.plan.MesCompanyPlanApi; +import com.zt.plat.module.product.api.plan.MesFactoryPlanApi; +import com.zt.plat.module.qms.api.task.QmsApi; import com.zt.plat.module.system.api.dept.DeptApi; import com.zt.plat.module.system.api.dept.PostApi; import com.zt.plat.module.system.api.dict.DictDataApi; @@ -13,6 +18,6 @@ import org.springframework.context.annotation.Configuration; @Configuration(value = "bpmRpcConfiguration", proxyBeanMethods = false) @EnableFeignClients(clients = {RoleApi.class, DeptApi.class, PostApi.class, AdminUserApi.class, SmsSendApi.class, DictDataApi.class, - PermissionApi.class, AmountCreditApplyApi.class}) + PermissionApi.class, AmountCreditApplyApi.class, MesProcessRoutApi.class, MesFactoryPlanApi.class, MesCompanyPlanApi.class, AmountRequestApi.class, QmsApi.class}) public class RpcConfiguration { } diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java index 83fed4c..099b709 100644 --- a/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/com/zt/plat/module/bpm/service/supply/capital/listener/BpmCreditLetterApprovalStatusListener.java @@ -3,7 +3,7 @@ package com.zt.plat.module.bpm.service.supply.capital.listener; import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEvent; import com.zt.plat.module.bpm.api.event.BpmProcessInstanceStatusEventListener; import com.zt.plat.module.bpm.enums.task.BpmProcessInstanceStatusEnum; -import com.zt.plat.module.capital.api.AmountCreditApplyApi; +import com.zt.plat.module.capital.api.splyAmtCrdtAppl.AmountCreditApplyApi; import com.zt.plat.module.capital.enums.AmountCreditApplyApiStatusEnum; import jakarta.annotation.Resource; import org.springframework.stereotype.Component; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java new file mode 100644 index 0000000..7f66250 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/datatype/core/DmBooleanType.java @@ -0,0 +1,32 @@ +package liquibase.datatype.core; + +import liquibase.database.Database; +import liquibase.database.core.DmDatabase; +import liquibase.datatype.DataTypeInfo; +import liquibase.datatype.DatabaseDataType; + +@DataTypeInfo( + name = "boolean", + aliases = {"java.sql.Types.BOOLEAN", "java.lang.Boolean", "bit", "bool"}, + minParameters = 0, + maxParameters = 0, + priority = 2 +) +public class DmBooleanType extends BooleanType { + + @Override + public boolean supports(Database database) { + if (database instanceof DmDatabase) { + return true; + } + return super.supports(database); + } + + @Override + public DatabaseDataType toDatabaseDataType(Database database) { + if (database instanceof DmDatabase) { + return new DatabaseDataType("NUMBER", 1); + } + return super.toDatabaseDataType(database); + } +} diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java new file mode 100644 index 0000000..1e0a40e --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java @@ -0,0 +1,1957 @@ +package liquibase.snapshot; + +import liquibase.CatalogAndSchema; +import liquibase.Scope; +import liquibase.database.AbstractJdbcDatabase; +import liquibase.database.Database; +import liquibase.database.DatabaseConnection; +import liquibase.database.LiquibaseTableNamesFactory; +import liquibase.database.core.*; +import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.DatabaseException; +import liquibase.executor.jvm.ColumnMapRowMapper; +import liquibase.executor.jvm.RowMapperNotNullConstraintsResultSetExtractor; +import liquibase.structure.DatabaseObject; +import liquibase.structure.core.Catalog; +import liquibase.structure.core.Schema; +import liquibase.structure.core.Table; +import liquibase.structure.core.View; +import liquibase.util.JdbcUtil; +import liquibase.util.StringUtil; + +import java.sql.*; +import java.util.*; + +public class JdbcDatabaseSnapshot extends DatabaseSnapshot { + + private boolean warnedAboutDbaRecycleBin; + private static final boolean ignoreWarnAboutDbaRecycleBin = Boolean.getBoolean("liquibase.ignoreRecycleBinWarning"); + + private CachingDatabaseMetaData cachingDatabaseMetaData; + + private Map cachedExpressionMap = null; + + private Set userDefinedTypes; + + public JdbcDatabaseSnapshot(DatabaseObject[] examples, Database database, SnapshotControl snapshotControl) throws DatabaseException, InvalidExampleException { + super(examples, database, snapshotControl); + } + + public JdbcDatabaseSnapshot(DatabaseObject[] examples, Database database) throws DatabaseException, InvalidExampleException { + super(examples, database); + } + + public CachingDatabaseMetaData getMetaDataFromCache() throws SQLException { + if (cachingDatabaseMetaData == null) { + DatabaseMetaData databaseMetaData = null; + if (getDatabase().getConnection() != null) { + databaseMetaData = ((JdbcConnection) getDatabase().getConnection()).getUnderlyingConnection().getMetaData(); + } + + cachingDatabaseMetaData = new CachingDatabaseMetaData(this.getDatabase(), databaseMetaData); + } + return cachingDatabaseMetaData; + } + + public class CachingDatabaseMetaData { + private static final String SQL_FILTER_MATCH_ALL = "%"; + private final DatabaseMetaData databaseMetaData; + private final Database database; + + public CachingDatabaseMetaData(Database database, DatabaseMetaData metaData) { + this.databaseMetaData = metaData; + this.database = database; + } + + public java.sql.DatabaseMetaData getDatabaseMetaData() { + return databaseMetaData; + } + + public List getForeignKeys(final String catalogName, final String schemaName, final String tableName, + final String fkName) throws DatabaseException { + ForeignKeysResultSetCache foreignKeysResultSetCache = new ForeignKeysResultSetCache(database, catalogName, schemaName, tableName, fkName); + ResultSetCache importedKeys = getResultSetCache("getImportedKeys"); + importedKeys.setBulkTracking(!(database instanceof MSSQLDatabase)); + + return importedKeys.get(foreignKeysResultSetCache); + } + + public List getIndexInfo(final String catalogName, final String schemaName, final String tableName, final String indexName) throws DatabaseException, SQLException { + + return getResultSetCache("getIndexInfo").get(new ResultSetCache.UnionResultSetExtractor(database) { + + public boolean isBulkFetchMode; + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME"), row.getString("INDEX_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, tableName, indexName); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return getAllCatalogsStringScratchData() != null && database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEM"); + } + + @Override + public List fastFetch() throws SQLException, DatabaseException { + List returnList = new ArrayList<>(); + + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + if (database instanceof OracleDatabase) { + warnAboutDbaRecycleBin(); + + //oracle getIndexInfo is buggy and slow. See Issue 1824548 and http://forums.oracle.com/forums/thread.jspa?messageID=578383򍍏 + String sql = + "SELECT " + + "c.INDEX_NAME, " + + "3 AS TYPE, " + + "c.TABLE_OWNER AS TABLE_SCHEM, " + + "c.TABLE_NAME, " + + "c.COLUMN_NAME, " + + "c.COLUMN_POSITION AS ORDINAL_POSITION, " + + "NULL AS FILTER_CONDITION, " + + "c.INDEX_OWNER, " + + "CASE I.UNIQUENESS WHEN 'UNIQUE' THEN 0 ELSE 1 END AS NON_UNIQUE, " + + "CASE c.DESCEND WHEN 'Y' THEN 'D' WHEN 'DESC' THEN 'D' WHEN 'N' THEN 'A' WHEN 'ASC' THEN 'A' END AS ASC_OR_DESC, " + + "CASE WHEN tablespace_name = (SELECT default_tablespace FROM user_users) " + + "THEN NULL ELSE tablespace_name END AS tablespace_name " + + "FROM ALL_IND_COLUMNS c " + + "JOIN ALL_INDEXES i ON i.owner=c.index_owner AND i.index_name = c.index_name and i.table_owner = c.table_owner " + + "LEFT OUTER JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=c.table_name "; + if (!isBulkFetchMode || getAllCatalogsStringScratchData() == null) { + sql += "WHERE c.TABLE_OWNER = '" + database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class) + "' "; + } else { + sql += "WHERE c.TABLE_OWNER IN ('" + database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class) + "', " + getAllCatalogsStringScratchData() + ")"; + } + sql += "AND i.OWNER = c.TABLE_OWNER " + + "AND d.object_name IS NULL "; + + + if (!isBulkFetchMode && (tableName != null)) { + sql += " AND c.TABLE_NAME='" + tableName + "'"; + } + + if (!isBulkFetchMode && (indexName != null)) { + sql += " AND c.INDEX_NAME='" + indexName + "'"; + } + + sql += " ORDER BY c.INDEX_NAME, ORDINAL_POSITION"; + + returnList.addAll(setIndexExpressions(executeAndExtract(sql, database))); + } else if (database instanceof MSSQLDatabase) { + String tableCat = "original_db_name()"; + + if (9 <= database.getDatabaseMajorVersion()) { + tableCat = "db_name()"; + } + //fetch additional index info + String sql = "SELECT " + + tableCat + " as TABLE_CAT, " + + "object_schema_name(i.object_id) as TABLE_SCHEM, " + + "object_name(i.object_id) as TABLE_NAME, " + + "CASE is_unique WHEN 1 then 0 else 1 end as NON_UNIQUE, " + + "object_name(i.object_id) as INDEX_QUALIFIER, " + + "i.name as INDEX_NAME, " + + "case i.type when 1 then 1 ELSE 3 end as TYPE, " + + "key_ordinal as ORDINAL_POSITION, " + + "COL_NAME(c.object_id,c.column_id) AS COLUMN_NAME, " + + "case is_descending_key when 0 then 'A' else 'D' end as ASC_OR_DESC, " + + "null as CARDINALITY, " + + "null as PAGES, " + + "i.filter_definition as FILTER_CONDITION, " + + "o.type AS INTERNAL_OBJECT_TYPE, " + + "i.*, " + + "c.*, " + + "s.* " + + "FROM sys.indexes i " + + "join sys.index_columns c on i.object_id=c.object_id and i.index_id=c.index_id " + + "join sys.stats s on i.object_id=s.object_id and i.name=s.name " + + "join sys.objects o on i.object_id=o.object_id " + + "WHERE object_schema_name(i.object_id)='" + database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class) + "'"; + + if (!isBulkFetchMode && (tableName != null)) { + sql += " AND object_name(i.object_id)='" + database.escapeStringForDatabase(tableName) + "'"; + } + + if (!isBulkFetchMode && (indexName != null)) { + sql += " AND i.name='" + database.escapeStringForDatabase(indexName) + "'"; + } + + sql += "ORDER BY i.object_id, i.index_id, c.key_ordinal"; + + returnList.addAll(executeAndExtract(sql, database)); + + } else if (database instanceof Db2zDatabase) { + List parameters = new ArrayList<>(3); + String sql = "SELECT i.CREATOR AS TABLE_SCHEM, " + + "i.TBNAME AS TABLE_NAME, " + + "i.NAME AS INDEX_NAME, " + + "3 AS TYPE, " + + "k.COLNAME AS COLUMN_NAME, " + + "k.COLSEQ AS ORDINAL_POSITION, " + + "CASE UNIQUERULE WHEN 'D' then 1 else 0 end as NON_UNIQUE, " + + "k.ORDERING AS ORDER, " + + "i.CREATOR AS INDEX_QUALIFIER " + + "FROM SYSIBM.SYSKEYS k " + + "JOIN SYSIBM.SYSINDEXES i " + + "ON k.IXNAME = i.NAME " + + "AND k.IXCREATOR = i.CREATOR " + + "WHERE i.CREATOR = ?"; + parameters.add(database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class)); + if (!isBulkFetchMode && tableName != null) { + sql += " AND i.TBNAME = ?"; + parameters.add(database.escapeStringForDatabase(tableName)); + } + + if (!isBulkFetchMode && indexName != null) { + sql += " AND i.NAME = ?"; + parameters.add(database.escapeStringForDatabase(indexName)); + } + + sql += "ORDER BY i.NAME, k.COLSEQ"; + + returnList.addAll(executeAndExtract(database, sql, parameters.toArray())); + } else if (!(database instanceof MariaDBDatabase) && database instanceof MySQLDatabase) { + + //mysql 8.0.13 introduced support for indexes on `lower(first_name)` which comes back in an "expression" column + String filterConditionValue = "NULL"; + if (database.getDatabaseMajorVersion() > 8 || (database.getDatabaseMajorVersion() == 8 && ((MySQLDatabase) database).getDatabasePatchVersion() >= 13)) { + filterConditionValue = "EXPRESSION"; + } + + StringBuilder sql = new StringBuilder("SELECT TABLE_CATALOG AS TABLE_CAT, TABLE_SCHEMA AS TABLE_SCHEM,"); + sql.append(" TABLE_NAME, NON_UNIQUE, NULL AS INDEX_QUALIFIER, INDEX_NAME,"); + sql.append(DatabaseMetaData.tableIndexOther); + sql.append(" AS TYPE, SEQ_IN_INDEX AS ORDINAL_POSITION, COLUMN_NAME,"); + sql.append("COLLATION AS ASC_OR_DESC, CARDINALITY, 0 AS PAGES, " + filterConditionValue + " AS FILTER_CONDITION FROM INFORMATION_SCHEMA.STATISTICS WHERE"); + sql.append(" TABLE_SCHEMA = '").append(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)).append("'"); + + if (!isBulkFetchMode && tableName != null) { + sql.append(" AND TABLE_NAME = '").append(database.escapeStringForDatabase(tableName)).append("'"); + } + + if (!isBulkFetchMode && indexName != null) { + sql.append(" AND INDEX_NAME='").append(database.escapeStringForDatabase(indexName)).append("'"); + } + + sql.append("ORDER BY NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX"); + + returnList.addAll(executeAndExtract(sql.toString(), database)); + } else { + /* + * If we do not know in which table to look for the index, things get a little bit ugly. + * First, we get a collection of all tables within the catalogAndSchema, then iterate through + * them until we (hopefully) find the index we are looking for. + */ + List tables = new ArrayList<>(); + if (tableName == null) { + // Build a list of all candidate tables in the catalog/schema that might contain the index + for (CachedRow row : getTables(((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), null)) { + tables.add(row.getString("TABLE_NAME")); + } + } else { + tables.add(tableName); + } + + // Iterate through all the candidate tables and try to find the index. + for (String tableName1 : tables) { + ResultSet rs = databaseMetaData.getIndexInfo( + ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), + tableName1, + false, + true); + List rows = extract(rs, (database instanceof InformixDatabase)); + returnList.addAll(rows); + } + } + + return returnList; + } + + private List setIndexExpressions(List c) throws DatabaseException, SQLException { + Map expressionMap = getCachedExpressionMap(); + c.forEach(row -> { + row.set("FILTER_CONDITION", null); + String key = row.getString("INDEX_OWNER") + "::" + row.getString("INDEX_NAME") + "::" + + row.getInt("ORDINAL_POSITION"); + CachedRow fromMap = expressionMap.get(key); + if (fromMap != null) { + row.set("FILTER_CONDITION", fromMap.get("COLUMN_EXPRESSION")); + } + }); + return c; + } + + private Map getCachedExpressionMap() throws DatabaseException, SQLException { + if (cachedExpressionMap != null) { + return cachedExpressionMap; + } + String expSql = "SELECT e.column_expression, e.index_owner, e.index_name, e.column_position FROM all_ind_expressions e"; + List ec = executeAndExtract(expSql, database); + cachedExpressionMap = new HashMap<>(); + ec.forEach(row -> { + String key = row.getString("INDEX_OWNER") + "::" + row.getString("INDEX_NAME") + "::" + + row.getInt("COLUMN_POSITION"); + cachedExpressionMap.put(key, row); + }); + return cachedExpressionMap; + } + + @Override + public List bulkFetch() throws SQLException, DatabaseException { + this.isBulkFetchMode = true; + return fastFetch(); + } + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + if (database instanceof OracleDatabase || database instanceof MSSQLDatabase) { + return JdbcDatabaseSnapshot.this.getAllCatalogsStringScratchData() != null || (tableName == null && indexName == null) || super.shouldBulkSelect(schemaKey, resultSetCache); + } + return false; + } + }); + } + + + protected void warnAboutDbaRecycleBin() { + if (!ignoreWarnAboutDbaRecycleBin && !warnedAboutDbaRecycleBin && !(((OracleDatabase) database).canAccessDbaRecycleBin())) { + Scope.getCurrentScope().getLog(getClass()).warning(((OracleDatabase) database).getDbaRecycleBinWarning()); + warnedAboutDbaRecycleBin = true; + } + } + + /** + * Return the columns for the given catalog, schema, table, and column. + */ + public List getColumns(final String catalogName, final String schemaName, final String tableName, final String columnName) throws SQLException, DatabaseException { + + if ((database instanceof MSSQLDatabase) && (userDefinedTypes == null)) { + userDefinedTypes = new HashSet<>(); + DatabaseConnection databaseConnection = database.getConnection(); + if (databaseConnection instanceof JdbcConnection) { + Statement stmt = null; + ResultSet resultSet = null; + try { + stmt = ((JdbcConnection) databaseConnection).getUnderlyingConnection().createStatement(); + resultSet = stmt.executeQuery("select name from " + (catalogName == null ? "" : "[" + catalogName + "].") + "sys.types where is_user_defined=1"); + while (resultSet.next()) { + userDefinedTypes.add(resultSet.getString("name").toLowerCase()); + } + } finally { + JdbcUtil.close(resultSet, stmt); + } + } + } + GetColumnResultSetCache getColumnResultSetCache = new GetColumnResultSetCache(database, catalogName, + schemaName, tableName, columnName); + return getResultSetCache("getColumns").get(getColumnResultSetCache); + } + + /** + * Return the NotNullConstraints for the given catalog, schema, table, and column. + */ + public List getNotNullConst(final String catalogName, final String schemaName, + final String tableName) throws DatabaseException { + if (!(database instanceof OracleDatabase)) { + return Collections.emptyList(); + } + GetNotNullConstraintsResultSetCache getNotNullConstraintsResultSetCache = new GetNotNullConstraintsResultSetCache(database, catalogName, + schemaName, tableName); + return getResultSetCache("getNotNullConst").get(getNotNullConstraintsResultSetCache); + } + + private class GetColumnResultSetCache extends ResultSetCache.SingleResultSetExtractor { + final String catalogName; + final String schemaName; + final String tableName; + final String columnName; + + private GetColumnResultSetCache(Database database, String catalogName, String schemaName, String tableName, String columnName) { + super(database); + this.catalogName = catalogName; + this.schemaName = schemaName; + this.tableName = tableName; + this.columnName = columnName; + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME"), row.getString("COLUMN_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, tableName, columnName); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + String catalogs = getAllCatalogsStringScratchData(); + return catalogs != null && schemaKey != null + && catalogs.contains("'" + schemaKey.toUpperCase() + "'") + && database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEM"); + } + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + LiquibaseTableNamesFactory liquibaseTableNamesFactory = Scope.getCurrentScope().getSingleton(LiquibaseTableNamesFactory.class); + List liquibaseTableNames = liquibaseTableNamesFactory.getLiquibaseTableNames(database); + return liquibaseTableNames.stream().noneMatch(tableName::equalsIgnoreCase); + } + + @Override + public List fastFetchQuery() throws SQLException, DatabaseException { + if (database instanceof OracleDatabase) { + return oracleQuery(false); + } else if (database instanceof MSSQLDatabase) { + return mssqlQuery(false); + } + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + try { + List returnList = + extract( + databaseMetaData.getColumns( + ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), + escapeForLike(((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), database), + escapeForLike(tableName, database), + SQL_FILTER_MATCH_ALL) + ); + // + // IF MARIADB OR SQL ANYWHERE + // Query to get actual data types and then map each column to its CachedRow + // + determineActualDataTypes(returnList, tableName); + return returnList; + } catch (SQLException e) { + if (shouldReturnEmptyColumns(e)) { //view with table already dropped. Act like it has no columns. + return new ArrayList<>(); + } else { + throw e; + } + } + } + + @Override + public List bulkFetchQuery() throws SQLException, DatabaseException { + if (database instanceof OracleDatabase) { + return oracleQuery(true); + } else if (database instanceof MSSQLDatabase) { + return mssqlQuery(true); + } + + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + try { + List returnList = + extract(databaseMetaData.getColumns(((AbstractJdbcDatabase) database) + .getJdbcCatalogName(catalogAndSchema), + escapeForLike(((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), database), + SQL_FILTER_MATCH_ALL, SQL_FILTER_MATCH_ALL)); + // + // IF MARIADB OR SQL ANYWHERE + // Query to get actual data types and then map each column to its CachedRow + // + determineActualDataTypes(returnList, null); + return returnList; + } catch (SQLException e) { + if (shouldReturnEmptyColumns(e)) { + return new ArrayList<>(); + } else { + throw e; + } + } + } + + // + // For MariaDB, query for the data type column so that we can correctly + // set the DATETIME(6) type if specified + // + // For SQL Anywhere, query for the scale column so we can correctly + // set the size unit + // + private void determineActualDataTypes(List returnList, String tableName) throws SQLException { + // + // If not MariaDB / SQL Anywhere then just return + // + if (!(database instanceof MariaDBDatabase || database instanceof SybaseASADatabase)) { + return; + } + + if (database instanceof SybaseASADatabase) { + // + // Query for actual data type for column. The actual SYSTABCOL.scale column value is + // not reported by the DatabaseMetadata.getColumns() query for CHAR-limited (in contrast + // to BYTE-limited) columns, and it is needed to capture the kind if limitation. + // The actual SYSTABCOL.column_type is not reported by the DatabaseMetadata.getColumns() + // query as the IS_GENERATEDCOLUMN columns is missing in the result set, and it is needed to + // capture the kind of column (regular or computed). + // + // See https://help.sap.com/docs/SAP_SQL_Anywhere/93079d4ba8e44920ae63ffb4def91f5b/3beaa3956c5f1014883cb0c3e3559cc9.html. + // + String selectStatement = + "SELECT table_name, column_name, scale, column_type FROM SYSTABCOL KEY JOIN SYSTAB KEY JOIN SYSUSER " + + "WHERE user_name = ? AND ? IS NULL OR table_name = ?"; + Connection underlyingConnection = ((JdbcConnection) database.getConnection()).getUnderlyingConnection(); + try (PreparedStatement stmt = underlyingConnection.prepareStatement(selectStatement)) { + stmt.setString(1, schemaName); + stmt.setString(2, tableName); + stmt.setString(3, tableName); + try (ResultSet columnSelectRS = stmt.executeQuery()) { + while (columnSelectRS.next()) { + String selectedTableName = columnSelectRS.getString("table_name"); + String selectedColumnName = columnSelectRS.getString("column_name"); + int selectedScale = columnSelectRS.getInt("scale"); + String selectedColumnType = columnSelectRS.getString("column_type"); + for (CachedRow row : returnList) { + String rowTableName = row.getString("TABLE_NAME"); + String rowColumnName = row.getString("COLUMN_NAME"); + if (rowTableName.equalsIgnoreCase(selectedTableName) && + rowColumnName.equalsIgnoreCase(selectedColumnName)) { + int rowDataType = row.getInt("DATA_TYPE"); + if (rowDataType == Types.VARCHAR || rowDataType == Types.CHAR) { + row.set("scale", selectedScale); + } + row.set("IS_GENERATEDCOLUMN", "C".equals(selectedColumnType) ? "YES" : "NO"); + break; + } + } + } + } + } catch (SQLException sqle) { + throw new RuntimeException(sqle); + // + // Do not stop + // + } + return; + } + + // + // Query for actual data type for column. The actual DATA_TYPE column string is + // not returned by the DatabaseMetadata.getColumns() query, and it is needed + // to capture DATETIME() data types. + // + StringBuilder selectStatement = new StringBuilder( + "SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, DATA_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = ?"); + if(tableName != null) { + selectStatement.append(" AND TABLE_NAME = ?"); + } + Connection underlyingConnection = ((JdbcConnection) database.getConnection()).getUnderlyingConnection(); + PreparedStatement statement = underlyingConnection.prepareStatement(selectStatement.toString()); + statement.setString(1, schemaName); + if (tableName != null) { + statement.setString(2, tableName); + } + try { + ResultSet columnSelectRS = statement.executeQuery(selectStatement.toString()); + // + // Iterate the result set from the query and match the rows + // to the rows that were returned by getColumns() in order + // to assign the actual DATA_TYPE string to the appropriate row. + // + while (columnSelectRS.next()) { + String selectedTableName = columnSelectRS.getString("TABLE_NAME"); + String selectedColumnName = columnSelectRS.getString("COLUMN_NAME"); + String actualDataType = columnSelectRS.getString("DATA_TYPE"); + for (CachedRow row : returnList) { + String rowTableName = row.getString("TABLE_NAME"); + String rowColumnName = row.getString("COLUMN_NAME"); + String rowTypeName = row.getString("TYPE_NAME"); + int rowDataType = row.getInt("DATA_TYPE"); + if (rowTableName.equalsIgnoreCase(selectedTableName) && + rowColumnName.equalsIgnoreCase(selectedColumnName) && + rowTypeName.equalsIgnoreCase("datetime") && + rowDataType == Types.OTHER && + !rowTypeName.equalsIgnoreCase(actualDataType)) { + row.set("TYPE_NAME", actualDataType); + row.set("DATA_TYPE", Types.TIMESTAMP); + break; + } + } + } + } catch (SQLException sqle) { + // + // Do not stop + // + } + finally { + JdbcUtil.closeStatement(statement); + } + } + + protected boolean shouldReturnEmptyColumns(SQLException e) { + return e.getMessage().contains("references invalid table"); //view with table already dropped. Act like it has no columns. + } + + protected List oracleQuery(boolean bulk) throws DatabaseException, SQLException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + boolean collectIdentityData = database.getDatabaseMajorVersion() >= OracleDatabase.ORACLE_12C_MAJOR_VERSION; + + String sql = "select NULL AS TABLE_CAT, OWNER AS TABLE_SCHEM, 'NO' as IS_AUTOINCREMENT, cc.COMMENTS AS REMARKS," + + "OWNER, TABLE_NAME, COLUMN_NAME, DATA_TYPE AS DATA_TYPE_NAME, DATA_TYPE_MOD, DATA_TYPE_OWNER, " + + // note: oracle reports DATA_LENGTH=4*CHAR_LENGTH when using VARCHAR( CHAR ), thus BYTEs + "DECODE (c.data_type, 'CHAR', 1, 'VARCHAR2', 12, 'NUMBER', 3, 'LONG', -1, 'DATE', " + "93" + ", 'RAW', -3, 'LONG RAW', -4, 'BLOB', 2004, 'CLOB', 2005, 'BFILE', -13, 'FLOAT', 6, 'TIMESTAMP(6)', 93, 'TIMESTAMP(6) WITH TIME ZONE', -101, 'TIMESTAMP(6) WITH LOCAL TIME ZONE', -102, 'INTERVAL YEAR(2) TO MONTH', -103, 'INTERVAL DAY(2) TO SECOND(6)', -104, 'BINARY_FLOAT', 100, 'BINARY_DOUBLE', 101, 'XMLTYPE', 2009, 1111) AS data_type, " + + "DECODE( CHAR_USED, 'C',CHAR_LENGTH, DATA_LENGTH ) as DATA_LENGTH, " + + "DATA_PRECISION, DATA_SCALE, NULLABLE, COLUMN_ID as ORDINAL_POSITION, DEFAULT_LENGTH, " + + "DATA_DEFAULT, " + + "NUM_BUCKETS, CHARACTER_SET_NAME, " + + "CHAR_COL_DECL_LENGTH, CHAR_LENGTH, " + + "CHAR_USED, VIRTUAL_COLUMN "; + if (collectIdentityData) { + sql += ", DEFAULT_ON_NULL, IDENTITY_COLUMN, ic.GENERATION_TYPE "; + } + sql += "FROM ALL_TAB_COLS c " + + "JOIN ALL_COL_COMMENTS cc USING ( OWNER, TABLE_NAME, COLUMN_NAME ) "; + if (collectIdentityData) { + sql += "LEFT JOIN ALL_TAB_IDENTITY_COLS ic USING (OWNER, TABLE_NAME, COLUMN_NAME ) "; + } + if (!bulk || getAllCatalogsStringScratchData() == null) { + sql += "WHERE OWNER='" + jdbcSchemaName + "' AND hidden_column='NO'"; + } else { + sql += "WHERE OWNER IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ") AND hidden_column='NO'"; + } + + if (!bulk) { + if (tableName != null) { + sql += " AND TABLE_NAME='" + database.escapeStringForDatabase(tableName) + "'"; + } + if (columnName != null) { + sql += " AND COLUMN_NAME='" + database.escapeStringForDatabase(columnName) + "'"; + } + } + sql += " AND " + ((OracleDatabase) database).getSystemTableWhereClause("TABLE_NAME"); + sql += " ORDER BY OWNER, TABLE_NAME, c.COLUMN_ID"; + + return this.executeAndExtract(sql, database); + } + + + protected List mssqlQuery(boolean bulk) throws DatabaseException, SQLException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + String databaseName = StringUtil.trimToNull(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)); + String dbIdParam; + String databasePrefix; + if (databaseName == null) { + databasePrefix = ""; + dbIdParam = ""; + } else { + dbIdParam = ", db_id('" + databaseName + "')"; + databasePrefix = "[" + databaseName + "]."; + } + + String sql = "select " + + "db_name(" + (databaseName == null ? "" : "db_id('" + databaseName + "')") + ") AS TABLE_CAT, " + + "object_schema_name(c.object_id" + dbIdParam + ") AS TABLE_SCHEM, " + + "object_name(c.object_id" + dbIdParam + ") AS TABLE_NAME, " + + "c.name AS COLUMN_NAME, " + + "is_filestream AS IS_FILESTREAM, " + + "is_rowguidcol AS IS_ROWGUIDCOL, " + + "CASE WHEN c.is_identity = 'true' THEN 'YES' ELSE 'NO' END as IS_AUTOINCREMENT, " + + "{REMARKS_COLUMN_PLACEHOLDER}" + + "t.name AS TYPE_NAME, " + + "dc.name as COLUMN_DEF_NAME, " + + "dc.definition as COLUMN_DEF, " + + // data type mapping from https://msdn.microsoft.com/en-us/library/ms378878(v=sql.110).aspx + "CASE t.name " + + "WHEN 'bigint' THEN " + java.sql.Types.BIGINT + " " + + "WHEN 'binary' THEN " + java.sql.Types.BINARY + " " + + "WHEN 'bit' THEN " + java.sql.Types.BIT + " " + + "WHEN 'char' THEN " + java.sql.Types.CHAR + " " + + "WHEN 'date' THEN " + java.sql.Types.DATE + " " + + "WHEN 'datetime' THEN " + java.sql.Types.TIMESTAMP + " " + + "WHEN 'datetime2' THEN " + java.sql.Types.TIMESTAMP + " " + + "WHEN 'datetimeoffset' THEN -155 " + + "WHEN 'decimal' THEN " + java.sql.Types.DECIMAL + " " + + "WHEN 'float' THEN " + java.sql.Types.DOUBLE + " " + + "WHEN 'image' THEN " + java.sql.Types.LONGVARBINARY + " " + + "WHEN 'int' THEN " + java.sql.Types.INTEGER + " " + + "WHEN 'money' THEN " + java.sql.Types.DECIMAL + " " + + "WHEN 'nchar' THEN " + java.sql.Types.NCHAR + " " + + "WHEN 'ntext' THEN " + java.sql.Types.LONGNVARCHAR + " " + + "WHEN 'numeric' THEN " + java.sql.Types.NUMERIC + " " + + "WHEN 'nvarchar' THEN " + java.sql.Types.NVARCHAR + " " + + "WHEN 'real' THEN " + Types.REAL + " " + + "WHEN 'smalldatetime' THEN " + java.sql.Types.TIMESTAMP + " " + + "WHEN 'smallint' THEN " + java.sql.Types.SMALLINT + " " + + "WHEN 'smallmoney' THEN " + java.sql.Types.DECIMAL + " " + + "WHEN 'text' THEN " + java.sql.Types.LONGVARCHAR + " " + + "WHEN 'time' THEN " + java.sql.Types.TIME + " " + + "WHEN 'timestamp' THEN " + java.sql.Types.BINARY + " " + + "WHEN 'tinyint' THEN " + java.sql.Types.TINYINT + " " + + "WHEN 'udt' THEN " + java.sql.Types.VARBINARY + " " + + "WHEN 'uniqueidentifier' THEN " + java.sql.Types.CHAR + " " + + "WHEN 'varbinary' THEN " + java.sql.Types.VARBINARY + " " + + "WHEN 'varbinary(max)' THEN " + java.sql.Types.VARBINARY + " " + + "WHEN 'varchar' THEN " + java.sql.Types.VARCHAR + " " + + "WHEN 'varchar(max)' THEN " + java.sql.Types.VARCHAR + " " + + "WHEN 'xml' THEN " + java.sql.Types.LONGVARCHAR + " " + + "WHEN 'LONGNVARCHAR' THEN " + java.sql.Types.SQLXML + " " + + "ELSE " + Types.OTHER + " END AS DATA_TYPE, " + + "CASE WHEN c.is_nullable = 'true' THEN 1 ELSE 0 END AS NULLABLE, " + + "10 as NUM_PREC_RADIX, " + + "c.column_id as ORDINAL_POSITION, " + + "c.scale as DECIMAL_DIGITS, " + + "c.max_length as COLUMN_SIZE, " + + "c.precision as DATA_PRECISION, " + + "c.is_computed as IS_COMPUTED " + + "FROM " + databasePrefix + "sys.columns c " + + "inner join " + databasePrefix + "sys.types t on c.user_type_id=t.user_type_id " + + "{REMARKS_JOIN_PLACEHOLDER}" + + "left outer join " + databasePrefix + "sys.default_constraints dc on dc.parent_column_id = c.column_id AND dc.parent_object_id=c.object_id AND type_desc='DEFAULT_CONSTRAINT' " + + "WHERE object_schema_name(c.object_id" + dbIdParam + ")='" + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema) + "'"; + + + if (!bulk) { + if (tableName != null) { + sql += " and object_name(c.object_id" + dbIdParam + ")='" + database.escapeStringForDatabase(tableName) + "'"; + } + if (columnName != null) { + sql += " and c.name='" + database.escapeStringForDatabase(columnName) + "'"; + } + } + sql += "order by object_schema_name(c.object_id" + dbIdParam + "), object_name(c.object_id" + dbIdParam + "), c.column_id"; + + + // sys.extended_properties is added to Azure on V12: https://feedback.azure.com/forums/217321-sql-database/suggestions/6549815-add-sys-extended-properties-for-meta-data-support + if ((!((MSSQLDatabase) database).isAzureDb()) // Either NOT AzureDB (=SQL Server 2008 or higher) + || (database.getDatabaseMajorVersion() >= 12)) { // or at least AzureDB v12 + // SQL Server 2005 or later + // https://technet.microsoft.com/en-us/library/ms177541.aspx + sql = sql.replace("{REMARKS_COLUMN_PLACEHOLDER}", "CAST([ep].[value] AS [nvarchar](MAX)) AS [REMARKS], "); + sql = sql.replace("{REMARKS_JOIN_PLACEHOLDER}", "left outer join " + databasePrefix + "[sys].[extended_properties] AS [ep] ON [ep].[class] = 1 " + + "AND [ep].[major_id] = c.object_id " + + "AND [ep].[minor_id] = column_id " + + "AND [ep].[name] = 'MS_Description' "); + } else { + sql = sql.replace("{REMARKS_COLUMN_PLACEHOLDER}", ""); + sql = sql.replace("{REMARKS_JOIN_PLACEHOLDER}", ""); + } + + List rows = this.executeAndExtract(sql, database); + + for (CachedRow row : rows) { + String typeName = row.getString("TYPE_NAME"); + if ("nvarchar".equals(typeName) || "nchar".equals(typeName)) { + Integer size = row.getInt("COLUMN_SIZE"); + if (size > 0) { + row.set("COLUMN_SIZE", size / 2); + } + } else if ((row.getInt("DATA_PRECISION") != null) && (row.getInt("DATA_PRECISION") > 0)) { + row.set("COLUMN_SIZE", row.getInt("DATA_PRECISION")); + } + } + + return rows; + } + + @Override + protected List extract(ResultSet resultSet, boolean informixIndexTrimHint) throws SQLException { + List rows = super.extract(resultSet, informixIndexTrimHint); + if ((database instanceof MSSQLDatabase) && !userDefinedTypes.isEmpty()) { //UDT types in MSSQL don't take parameters + for (CachedRow row : rows) { + String dataType = (String) row.get("TYPE_NAME"); + if (userDefinedTypes.contains(dataType.toLowerCase())) { + row.set("COLUMN_SIZE", null); + row.set("DECIMAL_DIGITS ", null); + } + } + } + return rows; + } + } + + private class ForeignKeysResultSetCache extends ResultSetCache.UnionResultSetExtractor { + final String catalogName; + final String schemaName; + final String tableName; + final String fkName; + + private ForeignKeysResultSetCache(Database database, String catalogName, String schemaName, String tableName, String fkName) { + super(database); + this.catalogName = catalogName; + this.schemaName = schemaName; + this.tableName = tableName; + this.fkName = fkName; + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("FKTABLE_CAT"), row.getString("FKTABLE_SCHEM"), database, row.getString("FKTABLE_NAME"), row.getString("FK_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, tableName, fkName); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("FKTABLE_SCHEM"); + } + + @Override + public List fastFetch() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + String jdbcCatalogName = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + + if (database instanceof DB2Database) { + if (database.getDatabaseProductName().startsWith("DB2 UDB for AS/400")) { + return executeAndExtract(getDB2ForAs400Sql(jdbcSchemaName, tableName), database); + } + return querytDB2Luw(jdbcSchemaName, tableName); + } else if (database instanceof Db2zDatabase) { + return queryDb2Zos(catalogAndSchema, tableName); + } else { + List tables = new ArrayList<>(); + if (tableName == null) { + for (CachedRow row : getTables(jdbcCatalogName, jdbcSchemaName, null)) { + tables.add(row.getString("TABLE_NAME")); + } + } else { + tables.add(tableName); + } + + List returnList = new ArrayList<>(); + for (String foundTable : tables) { + if (database instanceof OracleDatabase) { + throw new RuntimeException("Should have bulk selected"); + } else { + returnList.addAll(extract(databaseMetaData.getImportedKeys(jdbcCatalogName, jdbcSchemaName, foundTable))); + } + } + + return returnList; + } + } + + @Override + public List bulkFetch() throws SQLException, DatabaseException { + if (database instanceof OracleDatabase) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + String sql = getOracleSql(jdbcSchemaName); + return executeAndExtract(sql, database); + } else if (database instanceof DB2Database) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + if (database.getDatabaseProductName().startsWith("DB2 UDB for AS/400")) { + return executeAndExtract(getDB2ForAs400Sql(jdbcSchemaName, null), database); + } + return querytDB2Luw(jdbcSchemaName, null); + } else if (database instanceof Db2zDatabase) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + return queryDb2Zos(catalogAndSchema, null); + } else if (database instanceof MSSQLDatabase) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + String sql = getMSSQLSql(jdbcSchemaName, tableName); + return executeAndExtract(sql, database); + } else { + throw new RuntimeException("Cannot bulk select"); + } + } + + protected String getOracleSql(String jdbcSchemaName) { + String sql = "SELECT /*+rule*/" + + " NULL AS pktable_cat, " + + " p.owner as pktable_schem, " + + " p.table_name as pktable_name, " + + " pc.column_name as pkcolumn_name, " + + " NULL as fktable_cat, " + + " f.owner as fktable_schem, " + + " f.table_name as fktable_name, " + + " fc.column_name as fkcolumn_name, " + + " fc.position as key_seq, " + + " NULL as update_rule, " + + " decode (f.delete_rule, 'CASCADE', 0, 'SET NULL', 2, 1) as delete_rule, " + + " f.constraint_name as fk_name, " + + " p.constraint_name as pk_name, " + + " decode(f.deferrable, 'DEFERRABLE', 5, 'NOT DEFERRABLE', 7, 'DEFERRED', 6) deferrability, " + + " f.validated as fk_validate " + + "FROM " + + "all_cons_columns pc " + + "INNER JOIN all_constraints p " + + "ON pc.owner = p.owner " + + "AND pc.constraint_name = p.constraint_name " + + "INNER JOIN all_constraints f " + + "ON pc.owner = f.r_owner " + + "AND pc.constraint_name = f.r_constraint_name " + + "INNER JOIN all_cons_columns fc " + + "ON fc.owner = f.owner " + + "AND fc.constraint_name = f.constraint_name " + + "AND fc.position = pc.position "; + if (getAllCatalogsStringScratchData() == null) { + sql += "WHERE f.owner = '" + jdbcSchemaName + "' "; + } else { + sql += "WHERE f.owner IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ") "; + } + sql += "AND p.constraint_type in ('P', 'U') " + + "AND f.constraint_type = 'R' " + + "AND p.table_name NOT LIKE 'BIN$%' " + + "ORDER BY fktable_schem, fktable_name, key_seq"; + return sql; + } + + protected String getMSSQLSql(String jdbcSchemaName, String tableName) { + //comes from select object_definition(object_id('sp_fkeys')) + return "select " + + "convert(sysname,db_name()) AS PKTABLE_CAT, " + + "convert(sysname,schema_name(o1.schema_id)) AS PKTABLE_SCHEM, " + + "convert(sysname,o1.name) AS PKTABLE_NAME, " + + "convert(sysname,c1.name) AS PKCOLUMN_NAME, " + + "convert(sysname,db_name()) AS FKTABLE_CAT, " + + "convert(sysname,schema_name(o2.schema_id)) AS FKTABLE_SCHEM, " + + "convert(sysname,o2.name) AS FKTABLE_NAME, " + + "convert(sysname,c2.name) AS FKCOLUMN_NAME, " + + "isnull(convert(smallint,k.constraint_column_id), convert(smallint,0)) AS KEY_SEQ, " + + "convert(smallint, case ObjectProperty(f.object_id, 'CnstIsUpdateCascade') when 1 then 0 else 1 end) AS UPDATE_RULE, " + + "convert(smallint, case ObjectProperty(f.object_id, 'CnstIsDeleteCascade') when 1 then 0 else 1 end) AS DELETE_RULE, " + + "convert(sysname,object_name(f.object_id)) AS FK_NAME, " + + "convert(sysname,i.name) AS PK_NAME, " + + "convert(smallint, 7) AS DEFERRABILITY " + + "from " + + "sys.objects o1, " + + "sys.objects o2, " + + "sys.columns c1, " + + "sys.columns c2, " + + "sys.foreign_keys f inner join " + + "sys.foreign_key_columns k on (k.constraint_object_id = f.object_id) inner join " + + "sys.indexes i on (f.referenced_object_id = i.object_id and f.key_index_id = i.index_id) " + + "where " + + "o1.object_id = f.referenced_object_id and " + + "o2.object_id = f.parent_object_id and " + + "c1.object_id = f.referenced_object_id and " + + "c2.object_id = f.parent_object_id and " + + "c1.column_id = k.referenced_column_id and " + + "c2.column_id = k.parent_column_id and " + + "((object_schema_name(o1.object_id)='" + jdbcSchemaName + "'" + + " and convert(sysname,schema_name(o2.schema_id))='" + jdbcSchemaName + "' and " + + "convert(sysname,o2.name)='" + tableName + "' ) or ( convert(sysname,schema_name" + + "(o2.schema_id))='" + jdbcSchemaName + "' and convert(sysname,o2.name)='" + tableName + + "' )) order by 5, 6, 7, 9, 8"; + } + + private List querytDB2Luw(String jdbcSchemaName, String tableName) throws DatabaseException, SQLException { + List parameters = new ArrayList<>(2); + StringBuilder sql = new StringBuilder ("SELECT " + + " pk_col.tabschema AS pktable_cat, " + + " pk_col.tabname as pktable_name, " + + " pk_col.colname as pkcolumn_name, " + + " fk_col.tabschema as fktable_cat, " + + " fk_col.tabname as fktable_name, " + + " fk_col.colname as fkcolumn_name, " + + " fk_col.colseq as key_seq, " + + " decode (ref.updaterule, 'A', 3, 'R', 1, 1) as update_rule, " + + " decode (ref.deleterule, 'A', 3, 'C', 0, 'N', 2, 'R', 1, 1) as delete_rule, " + + " ref.constname as fk_name, " + + " ref.refkeyname as pk_name, " + + " 7 as deferrability " + + "FROM " + + "syscat.references ref " + + "join syscat.keycoluse fk_col on ref.constname=fk_col.constname and ref.tabschema=fk_col.tabschema and ref.tabname=fk_col.tabname " + + "join syscat.keycoluse pk_col on ref.refkeyname=pk_col.constname and ref.reftabschema=pk_col.tabschema and ref.reftabname=pk_col.tabname and pk_col.colseq=fk_col.colseq " + + "WHERE ref.tabschema = ? "); + parameters.add(jdbcSchemaName); + if (tableName != null) { + sql.append("and fk_col.tabname = ? "); + parameters.add(tableName); + } + sql.append("ORDER BY fk_col.colseq"); + return executeAndExtract(database, sql.toString(), parameters.toArray()); + } + + private String getDB2ForAs400Sql(String jdbcSchemaName, String tableName) { + return "SELECT " + + "pktable_cat, " + + "pktable_name, " + + "pkcolumn_name, " + + "fktable_cat, " + + "fktable_name, " + + "fkcolumn_name, " + + "key_seq, " + + "update_rule, " + + "delete_rule, " + + "fk_name, " + + "pk_name, " + + "deferrability " + + "FROM " + + "sysibm.SQLFORKEYS " + + "WHERE " + + "FKTABLE_SCHEM = '" + jdbcSchemaName + "' " + + "AND FKTABLE_NAME = '" + tableName + "'"; + } + + protected List queryDb2Zos(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { + + List parameters = new ArrayList<>(2); + StringBuilder sql = new StringBuilder("SELECT " + + " ref.REFTBCREATOR AS pktable_cat, " + + " ref.REFTBNAME as pktable_name, " + + " pk_col.colname as pkcolumn_name, " + + " ref.CREATOR as fktable_cat, " + + " ref.TBNAME as fktable_name, " + + " fk_col.colname as fkcolumn_name, " + + " fk_col.colseq as key_seq, " + + " decode (ref.deleterule, 'A', 3, 'C', 0, 'N', 2, 'R', 1, 1) as delete_rule, " + + " ref.relname as fk_name, " + + " pk_col.colname as pk_name, " + + " 7 as deferrability " + + "FROM " + + "SYSIBM.SYSRELS ref " + + "join SYSIBM.SYSFOREIGNKEYS fk_col " + + "on ref.relname = fk_col.RELNAME " + + "and ref.CREATOR = fk_col.CREATOR " + + "and ref.TBNAME = fk_col.TBNAME " + + "join SYSIBM.SYSKEYCOLUSE pk_col " + + "on ref.REFTBCREATOR = pk_col.TBCREATOR " + + "and ref.REFTBNAME = pk_col.TBNAME " + + "and pk_col.colseq=fk_col.colseq " + + "WHERE ref.CREATOR = ? "); + parameters.add(((AbstractJdbcDatabase) CachingDatabaseMetaData.this.database).getJdbcSchemaName(catalogAndSchema)); + if (tableName != null) { + sql.append("AND ref.TBNAME = ? "); + parameters.add(tableName); + } + sql.append("ORDER BY fk_col.colseq"); + + return executeAndExtract(CachingDatabaseMetaData.this.database, sql.toString(), parameters.toArray()); + } + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + if (database instanceof AbstractDb2Database || database instanceof MSSQLDatabase) { + return super.shouldBulkSelect(schemaKey, resultSetCache); //can bulk and fast fetch + } else { + return database instanceof OracleDatabase; //oracle is slow, always bulk select while you are at it. Other databases need to go through all tables. + } + } + } + + private class GetNotNullConstraintsResultSetCache extends ResultSetCache.SingleResultSetExtractor { + final String catalogName; + final String schemaName; + final String tableName; + + private GetNotNullConstraintsResultSetCache(Database database, String catalogName, String schemaName, String tableName) { + super(database); + this.catalogName = catalogName; + this.schemaName = schemaName; + this.tableName = tableName; + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEMA"), + database, row.getString("TABLE_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, tableName); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEMA"); + } + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + LiquibaseTableNamesFactory liquibaseTableNamesFactory = Scope.getCurrentScope().getSingleton(LiquibaseTableNamesFactory.class); + List liquibaseTableNames = liquibaseTableNamesFactory.getLiquibaseTableNames(database); + return liquibaseTableNames.stream().noneMatch(tableName::equalsIgnoreCase); + } + + @Override + public List fastFetchQuery() throws SQLException, DatabaseException { + if (database instanceof OracleDatabase) { + return oracleQuery(false); + } + return Collections.emptyList(); + } + + @Override + public List bulkFetchQuery() throws SQLException, DatabaseException { + if (database instanceof OracleDatabase) { + return oracleQuery(true); + } + return Collections.emptyList(); + } + + private List oracleQuery(boolean bulk) throws DatabaseException, SQLException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + String jdbcSchemaName = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + String jdbcTableName = database.escapeStringForDatabase(tableName); + String sqlToSelectNotNullConstraints = "SELECT NULL AS TABLE_CAT, atc.OWNER AS TABLE_SCHEMA, atc.OWNER, atc.TABLE_NAME, " + + "atc.COLUMN_NAME, NULLABLE, ac.VALIDATED as VALIDATED, ac.SEARCH_CONDITION, ac.CONSTRAINT_NAME " + + "FROM ALL_TAB_COLS atc " + + "JOIN all_cons_columns acc ON atc.OWNER = acc.OWNER AND atc.TABLE_NAME = acc.TABLE_NAME AND atc.COLUMN_NAME = acc.COLUMN_NAME " + + "JOIN all_constraints ac ON atc.OWNER = ac.OWNER AND atc.TABLE_NAME = ac.TABLE_NAME AND acc.CONSTRAINT_NAME = ac.CONSTRAINT_NAME "; + + if (!bulk || getAllCatalogsStringScratchData() == null) { + sqlToSelectNotNullConstraints += " WHERE atc.OWNER='" + jdbcSchemaName + "' AND atc.hidden_column='NO' AND ac.CONSTRAINT_TYPE='C' and ac.search_condition is not null "; + } else { + sqlToSelectNotNullConstraints += " WHERE atc.OWNER IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ") " + + " AND atc.hidden_column='NO' AND ac.CONSTRAINT_TYPE='C' and ac.search_condition is not null "; + } + + sqlToSelectNotNullConstraints += (!bulk && tableName != null && !tableName.isEmpty()) ? " AND atc.TABLE_NAME='" + jdbcTableName + "'" : ""; + + return this.executeAndExtract(sqlToSelectNotNullConstraints, database); + } + + @Override + protected List extract(ResultSet resultSet, boolean informixIndexTrimHint) throws SQLException { + List cachedRowList = new ArrayList<>(); + if (!(database instanceof OracleDatabase)) { + return cachedRowList; + } + + resultSet.setFetchSize(database.getFetchSize()); + + try { + List result = (List) new RowMapperNotNullConstraintsResultSetExtractor(new ColumnMapRowMapper(database.isCaseSensitive()) { + @Override + protected Object getColumnValue(ResultSet rs, int index) throws SQLException { + Object value = super.getColumnValue(rs, index); + if (!(value instanceof String)) { + return value; + } + return value.toString().trim(); + } + }).extractData(resultSet); + + for (Map row : result) { + cachedRowList.add(new CachedRow(row)); + } + } finally { + JdbcUtil.closeResultSet(resultSet); + } + return cachedRowList; + + } + } + + public List getTables(final String catalogName, final String schemaName, final String table) throws DatabaseException { + return getResultSetCache("getTables").get(new ResultSetCache.SingleResultSetExtractor(database) { + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + return table == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, table); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEM"); + } + + @Override + public List fastFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + if (database instanceof OracleDatabase) { + return queryOracle(catalogAndSchema, table); + } else if (database instanceof MSSQLDatabase) { + return queryMssql(catalogAndSchema, table); + } else if (database instanceof Db2zDatabase) { + return queryDb2Zos(catalogAndSchema, table); + } else if (database instanceof PostgresDatabase) { + return queryPostgres(catalogAndSchema, table); + } + + String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), ((table == null) ? + SQL_FILTER_MATCH_ALL : escapeForLike(table, database)), new String[]{"TABLE"})); + } + + @Override + public List bulkFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + if (database instanceof OracleDatabase) { + return queryOracle(catalogAndSchema, null); + } else if (database instanceof MSSQLDatabase) { + return queryMssql(catalogAndSchema, null); + } else if (database instanceof Db2zDatabase) { + return queryDb2Zos(catalogAndSchema, null); + } else if (database instanceof PostgresDatabase) { + return queryPostgres(catalogAndSchema, table); + } + + String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), SQL_FILTER_MATCH_ALL, new String[]{"TABLE"})); + } + + private List queryMssql(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { + String ownerName = database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class); + + String databaseName = StringUtil.trimToNull(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)); + String dbIdParam; + String databasePrefix; + if (databaseName == null) { + databasePrefix = ""; + dbIdParam = ""; + } else { + dbIdParam = ", db_id('" + databaseName + "')"; + databasePrefix = "[" + databaseName + "]."; + } + + + //From select object_definition(object_id('sp_tables')) + String sql = "select " + + "db_name(" + (databaseName == null ? "" : "db_id('" + databaseName + "')") + ") AS TABLE_CAT, " + + "convert(sysname,object_schema_name(o.object_id" + dbIdParam + ")) AS TABLE_SCHEM, " + + "convert(sysname,o.name) AS TABLE_NAME, " + + "'TABLE' AS TABLE_TYPE, " + + "CAST(ep.value as varchar(max)) as REMARKS " + + "from " + databasePrefix + "sys.all_objects o " + + "left outer join sys.extended_properties ep on ep.name='MS_Description' and major_id=o.object_id and minor_id=0 " + + "where " + + "o.type in ('U') " + + "and has_perms_by_name(" + (databaseName == null ? "" : "quotename('" + databaseName + "') + '.' + ") + "quotename(object_schema_name(o.object_id" + dbIdParam + ")) + '.' + quotename(o.name), 'object', 'select') = 1 " + + "and charindex(substring(o.type,1,1),'U') <> 0 " + + "and object_schema_name(o.object_id" + dbIdParam + ")='" + database.escapeStringForDatabase(ownerName) + "'"; + if (tableName != null) { + sql += " AND o.name='" + database.escapeStringForDatabase(tableName) + "' "; + } + sql += "order by 4, 1, 2, 3"; + + return executeAndExtract(sql, database); + } + + private List queryOracle(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { + String ownerName = database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class); + + String sql = "SELECT null as TABLE_CAT, a.OWNER as TABLE_SCHEM, a.TABLE_NAME as TABLE_NAME, " + + "a.TEMPORARY as TEMPORARY, a.DURATION as DURATION, 'TABLE' as TABLE_TYPE, " + + "c.COMMENTS as REMARKS, A.tablespace_name as tablespace_name, CASE WHEN A.tablespace_name = " + + "(SELECT DEFAULT_TABLESPACE FROM USER_USERS) THEN 'true' ELSE null END as default_tablespace " + + "from ALL_TABLES a " + + "join ALL_TAB_COMMENTS c on a.TABLE_NAME=c.table_name and a.owner=c.owner " + + "left outer join ALL_QUEUE_TABLES q ON a.TABLE_NAME = q.QUEUE_TABLE and a.OWNER = q.OWNER " + + "WHERE q.QUEUE_TABLE is null "; + String allCatalogsString = getAllCatalogsStringScratchData(); + if (tableName != null || allCatalogsString == null) { + sql += "AND a.OWNER='" + ownerName + "'"; + } else { + sql += "AND a.OWNER IN ('" + ownerName + "', " + allCatalogsString + ")"; + } + if (tableName != null) { + sql += " AND a.TABLE_NAME='" + tableName + "'"; + } + + return executeAndExtract(sql, database); + } + + private List queryDb2Zos(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { + String ownerName = database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class); + + String sql = "SELECT CREATOR AS TABLE_SCHEM, " + + "NAME AS TABLE_NAME, " + + "'TABLE' AS TABLE_TYPE, " + + "REMARKS " + + "FROM SYSIBM.SYSTABLES " + + "WHERE TYPE = 'T'"; + List parameters = new ArrayList<>(2); + if (ownerName != null) { + sql += " AND CREATOR = ?"; + parameters.add(ownerName); + } + if (tableName != null) { + sql += " AND NAME = ?"; + parameters.add(tableName); + } + + return executeAndExtract(database, sql, parameters.toArray()); + } + + private List queryPostgres(CatalogAndSchema catalogAndSchema, String tableName) throws SQLException { + String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), ((tableName == null) ? + SQL_FILTER_MATCH_ALL : escapeForLike(tableName, database)), new String[]{"TABLE", "PARTITIONED TABLE"})); + + } + }); + } + + public List getViews(final String catalogName, final String schemaName, String viewName) throws DatabaseException { + final String view; + if (database instanceof DB2Database) { + view = database.correctObjectName(viewName, View.class); + } else { + view = viewName; + } + return getResultSetCache("getViews").get(new ResultSetCache.SingleResultSetExtractor(database) { + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + return view == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME")); + } + + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, view); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEM"); + } + + + @Override + public List fastFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + if (database instanceof OracleDatabase) { + return queryOracle(catalogAndSchema, view); + } else if (database instanceof MSSQLDatabase) { + return queryMssql(catalogAndSchema, view); + } + + String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), ((view == null) ? SQL_FILTER_MATCH_ALL + : escapeForLike(view, database)), new String[]{"VIEW"})); + } + + @Override + public List bulkFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + if (database instanceof OracleDatabase) { + return queryOracle(catalogAndSchema, null); + } else if (database instanceof MSSQLDatabase) { + return queryMssql(catalogAndSchema, null); + } + + String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); + String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema, database), SQL_FILTER_MATCH_ALL, new String[]{"VIEW"})); + } + + private List queryMssql(CatalogAndSchema catalogAndSchema, String viewName) throws DatabaseException, SQLException { + String ownerName = database.correctObjectName(catalogAndSchema.getSchemaName(), Schema.class); + String databaseName = StringUtil.trimToNull(database.correctObjectName(catalogAndSchema.getCatalogName(), Catalog.class)); + String dbIdParam = ""; + String databasePrefix = ""; + boolean haveDatabaseName = databaseName != null; + + if (haveDatabaseName) { + dbIdParam = ", db_id('" + databaseName + "')"; + databasePrefix = "[" + databaseName + "]."; + } + String tableCatParam = haveDatabaseName ? "db_id('" + databaseName + "')" : ""; + String permsParam = haveDatabaseName ? "quotename('" + databaseName + "') + '.' + " : ""; + + String sql = "select " + + "db_name(" + tableCatParam + ") AS TABLE_CAT, " + + "convert(sysname,object_schema_name(o.object_id" + dbIdParam + ")) AS TABLE_SCHEM, " + + "convert(sysname,o.name) AS TABLE_NAME, " + + "'VIEW' AS TABLE_TYPE, " + + "CAST(ep.value as varchar(max)) as REMARKS " + + "from " + databasePrefix + "sys.all_objects o " + + "left join sys.extended_properties ep on ep.name='MS_Description' and major_id=o.object_id and minor_id=0 " + + "where " + + "o.type in ('V') " + + "and has_perms_by_name(" + permsParam + "quotename(object_schema_name(o.object_id" + dbIdParam + ")) + '.' + quotename(o.name), 'object', 'select') = 1 " + + "and charindex(substring(o.type,1,1),'V') <> 0 " + + "and object_schema_name(o.object_id" + dbIdParam + ")='" + database.escapeStringForDatabase(ownerName) + "'"; + if (viewName != null) { + sql += " AND o.name='" + database.escapeStringForDatabase(viewName) + "' "; + } + sql += "order by 4, 1, 2, 3"; + + return executeAndExtract(sql, database); + } + + + private List queryOracle(CatalogAndSchema catalogAndSchema, String viewName) throws DatabaseException, SQLException { + String ownerName = database.correctObjectName(catalogAndSchema.getCatalogName(), Schema.class); + + String sql = "SELECT null as TABLE_CAT, a.OWNER as TABLE_SCHEM, a.VIEW_NAME as TABLE_NAME, 'TABLE' as TABLE_TYPE, c.COMMENTS as REMARKS, TEXT as OBJECT_BODY"; + if (database.getDatabaseMajorVersion() > 10) { + sql += ", EDITIONING_VIEW"; + } + sql += " from ALL_VIEWS a " + + "join ALL_TAB_COMMENTS c on a.VIEW_NAME=c.table_name and a.owner=c.owner "; + if (viewName != null || getAllCatalogsStringScratchData() == null) { + sql += "WHERE a.OWNER='" + ownerName + "'"; + } else { + sql += "WHERE a.OWNER IN ('" + ownerName + "', " + getAllCatalogsStringScratchData() + ")"; + } + if (viewName != null) { + sql += " AND a.VIEW_NAME='" + database.correctObjectName(viewName, View.class) + "'"; + } + sql += " AND a.VIEW_NAME not in (select mv.name from all_registered_mviews mv where mv.owner=a.owner)"; + + return executeAndExtract(sql, database); + } + }); + } + + public List getPrimaryKeys(final String catalogName, final String schemaName, final String table) throws DatabaseException { + return getResultSetCache("getPrimaryKeys").get(new ResultSetCache.SingleResultSetExtractor(database) { + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(row.getString("TABLE_CAT"), row.getString("TABLE_SCHEM"), database, row.getString("TABLE_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, table); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("TABLE_SCHEM"); + } + + @Override + public List fastFetchQuery() throws SQLException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + try { + List foundPks = new ArrayList<>(); + if (table == null) { + List tables = CachingDatabaseMetaData.this.getTables(catalogName, schemaName, null); + for (CachedRow table : tables) { + List pkInfo = getPkInfo(catalogAndSchema, table.getString("TABLE_NAME")); + if (pkInfo != null) { + foundPks.addAll(pkInfo); + } + } + return foundPks; + } else { + List pkInfo = getPkInfo(catalogAndSchema, table); + if (pkInfo != null) { + foundPks.addAll(pkInfo); + } + } + return foundPks; + } catch (DatabaseException e) { + throw new SQLException(e); + } + } + + private List getPkInfo(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { + List pkInfo; + if (database instanceof MSSQLDatabase) { + String sql = mssqlSql(catalogAndSchema, tableName); + pkInfo = executeAndExtract(sql, database); + } else { + if (database instanceof Db2zDatabase) { + String sql = "SELECT 'NULL' AS TABLE_CAT," + + " SYSTAB.TBCREATOR AS TABLE_SCHEM, " + + "SYSTAB.TBNAME AS TABLE_NAME, " + + "COLUSE.COLNAME AS COLUMN_NAME, " + + "COLUSE.COLSEQ AS KEY_SEQ, " + + "SYSTAB.CONSTNAME AS PK_NAME " + + "FROM SYSIBM.SYSTABCONST SYSTAB " + + "JOIN SYSIBM.SYSKEYCOLUSE COLUSE " + + "ON SYSTAB.TBCREATOR = COLUSE.TBCREATOR " + + "WHERE SYSTAB.TYPE = 'P' " + + "AND SYSTAB.TBNAME = ? " + + "AND SYSTAB.TBCREATOR = ? " + + "AND SYSTAB.TBNAME=COLUSE.TBNAME " + + "AND SYSTAB.CONSTNAME=COLUSE.CONSTNAME " + + "ORDER BY COLUSE.COLNAME"; + try { + return executeAndExtract(database, sql, table, ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema)); + } catch (DatabaseException e) { + throw new SQLException(e); + } + } else if (database instanceof OracleDatabase) { + warnAboutDbaRecycleBin(); + + String sql = "SELECT NULL AS table_cat, c.owner AS table_schem, c.table_name, c.column_name as COLUMN_NAME, c.position AS key_seq, c.constraint_name AS pk_name, k.VALIDATED as VALIDATED " + + "FROM all_cons_columns c, all_constraints k " + + "LEFT JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=k.table_name " + + "WHERE k.constraint_type = 'P' " + + "AND d.object_name IS NULL " + + "AND k.table_name = '" + table + "' " + + "AND k.owner = '" + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema) + "' " + + "AND k.constraint_name = c.constraint_name " + + "AND k.table_name = c.table_name " + + "AND k.owner = c.owner " + + "ORDER BY column_name"; + try { + return executeAndExtract(sql, database); + } catch (DatabaseException e) { + throw new SQLException(e); + } + } else if (database instanceof CockroachDatabase) { + // This is the same as the query generated by PGJDBC's getPrimaryKeys method, except it + // also adds an `asc_or_desc` column to the result. + String sql = "SELECT " + + " result.table_cat, " + + " result.table_schem, " + + " result.table_name, " + + " result.column_name, " + + " result.key_seq, " + + " result.pk_name, " + + " CASE result.indoption[result.key_seq - 1] & 1 " + + " WHEN 1 THEN 'D' " + + " ELSE 'A' " + + " END AS asc_or_desc " + + "FROM " + + " (" + + " SELECT " + + " NULL AS table_cat, " + + " n.nspname AS table_schem, " + + " ct.relname AS table_name, " + + " a.attname AS column_name, " + + " (information_schema._pg_expandarray(i.indkey)).n " + + " AS key_seq, " + + " ci.relname AS pk_name, " + + " information_schema._pg_expandarray(i.indkey) AS keys, " + + " i.indoption, " + + " a.attnum AS a_attnum " + + " FROM " + + " pg_catalog.pg_class AS ct " + + " JOIN pg_catalog.pg_attribute AS a ON (ct.oid = a.attrelid) " + + " JOIN pg_catalog.pg_namespace AS n ON " + + " (ct.relnamespace = n.oid) " + + " JOIN pg_catalog.pg_index AS i ON (a.attrelid = i.indrelid) " + + " JOIN pg_catalog.pg_class AS ci ON (ci.oid = i.indexrelid) " + + " WHERE " + + " true " + + " AND n.nspname = '" + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema) + "' " + + " AND ct.relname = '" + table + "' " + + " AND i.indisprimary" + + " ) " + + " AS result " + + "WHERE " + + " result.a_attnum = (result.keys).x " + + "ORDER BY " + + " result.table_name, result.pk_name, result.key_seq"; + + try { + return executeAndExtract(sql, database); + } catch (DatabaseException e) { + throw new SQLException(e); + } + } else { + return extract( + databaseMetaData.getPrimaryKeys( + ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema), + ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema), + table + ) + ); + } + } + return pkInfo; + } + + private String mssqlSql(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException { + String sql; + sql = + "SELECT " + + "DB_NAME() AS [TABLE_CAT], " + + "[s].[name] AS [TABLE_SCHEM], " + + "[t].[name] AS [TABLE_NAME], " + + "[c].[name] AS [COLUMN_NAME], " + + "CASE [ic].[is_descending_key] WHEN 0 THEN N'A' WHEN 1 THEN N'D' END AS [ASC_OR_DESC], " + + "[ic].[key_ordinal] AS [KEY_SEQ], " + + "[kc].[name] AS [PK_NAME] " + + "FROM [sys].[schemas] AS [s] " + + "INNER JOIN [sys].[tables] AS [t] " + + "ON [t].[schema_id] = [s].[schema_id] " + + "INNER JOIN [sys].[key_constraints] AS [kc] " + + "ON [kc].[parent_object_id] = [t].[object_id] " + + "INNER JOIN [sys].[indexes] AS [i] " + + "ON [i].[object_id] = [kc].[parent_object_id] " + + "AND [i].[index_id] = [kc].[unique_index_id] " + + "INNER JOIN [sys].[index_columns] AS [ic] " + + "ON [ic].[object_id] = [i].[object_id] " + + "AND [ic].[index_id] = [i].[index_id] " + + "INNER JOIN [sys].[columns] AS [c] " + + "ON [c].[object_id] = [ic].[object_id] " + + "AND [c].[column_id] = [ic].[column_id] " + + "WHERE [s].[name] = N'" + database.escapeStringForDatabase(catalogAndSchema.getSchemaName()) + "' " + // The schema name was corrected in the customized CatalogAndSchema + (tableName == null ? "" : "AND [t].[name] = N'" + database.escapeStringForDatabase(database.correctObjectName(tableName, Table.class)) + "' ") + + "AND [kc].[type] = 'PK' " + + "AND [ic].[key_ordinal] > 0 " + + "ORDER BY " + + "[ic].[key_ordinal]"; + return sql; + } + + @Override + public List bulkFetchQuery() throws SQLException { + if (database instanceof OracleDatabase) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + warnAboutDbaRecycleBin(); + try { + String sql = "SELECT NULL AS table_cat, c.owner AS table_schem, c.table_name, c.column_name, c.position AS key_seq,c.constraint_name AS pk_name, k.VALIDATED as VALIDATED FROM " + + "all_cons_columns c, " + + "all_constraints k " + + "LEFT JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=k.table_name " + + "WHERE k.constraint_type = 'P' " + + "AND d.object_name IS NULL "; + if (getAllCatalogsStringScratchData() == null) { + sql += "AND k.owner='" + catalogAndSchema.getCatalogName() + "' "; + } else { + sql += "AND k.owner IN ('" + catalogAndSchema.getCatalogName() + "', " + getAllCatalogsStringScratchData() + ")"; + } + sql += "AND k.constraint_name = c.constraint_name " + + "AND k.table_name = c.table_name " + + "AND k.owner = c.owner " + + "ORDER BY column_name"; + return executeAndExtract(sql, database); + } catch (DatabaseException e) { + throw new SQLException(e); + } + } else if (database instanceof MSSQLDatabase) { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + try { + return executeAndExtract(mssqlSql(catalogAndSchema, null), database); + } catch (DatabaseException e) { + throw new SQLException(e); + } + } + return null; + } + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + if ((database instanceof OracleDatabase) || (database instanceof MSSQLDatabase)) { + return table == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); + } else { + return false; + } + } + }); + } + + public List getUniqueConstraints(final String catalogName, final String schemaName, final String tableName) throws DatabaseException { + return getResultSetCache("getUniqueConstraints").get(new ResultSetCache.SingleResultSetExtractor(database) { + + @Override + protected boolean shouldBulkSelect(String schemaKey, ResultSetCache resultSetCache) { + return tableName == null || getAllCatalogsStringScratchData() != null || super.shouldBulkSelect(schemaKey, resultSetCache); + } + + @Override + public boolean bulkContainsSchema(String schemaKey) { + return database instanceof OracleDatabase; + } + + @Override + public String getSchemaKey(CachedRow row) { + return row.getString("CONSTRAINT_SCHEM"); + } + + @Override + public ResultSetCache.RowData rowKeyParameters(CachedRow row) { + return new ResultSetCache.RowData(catalogName, schemaName, database, row.getString("TABLE_NAME")); + } + + @Override + public ResultSetCache.RowData wantedKeyParameters() { + return new ResultSetCache.RowData(catalogName, schemaName, database, tableName); + } + + @Override + public List fastFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + return queryDb(catalogAndSchema, tableName); + } + + @Override + public List bulkFetchQuery() throws SQLException, DatabaseException { + CatalogAndSchema catalogAndSchema = new CatalogAndSchema(catalogName, schemaName).customize(database); + + return queryDb(catalogAndSchema, null); + } + + private List queryDb(CatalogAndSchema catalogAndSchema, String tableName) throws SQLException, DatabaseException { + + String jdbcCatalogName = catalogAndSchema.getCatalogName(); + String jdbcSchemaName = catalogAndSchema.getSchemaName(); + + Database database = getDatabase(); + List parameters = new ArrayList<>(3); + String sql = null; + if (database instanceof Ingres9Database) { + sql = "select CONSTRAINT_NAME, TABLE_NAME from iiconstraints where schema_name ='" + + schemaName + "' and constraint_type='U'"; + if (tableName != null) { + sql += " and table_name='" + tableName + "'"; + } + } else if ((database instanceof MySQLDatabase) || (database instanceof HsqlDatabase) || (database + instanceof MariaDBDatabase)) { + sql = "select CONSTRAINT_NAME, TABLE_NAME " + + "from " + database.getSystemSchema() + ".table_constraints " + + "where constraint_schema='" + jdbcCatalogName + "' " + + "and constraint_type='UNIQUE'"; + if (tableName != null) { + sql += " and table_name='" + tableName + "'"; + } + } else if (database instanceof PostgresDatabase) { + sql = "select CONSTRAINT_NAME, TABLE_NAME " + + "from " + database.getSystemSchema() + ".table_constraints " + + "where constraint_catalog='" + jdbcCatalogName + "' " + + "and constraint_schema='" + jdbcSchemaName + "' " + + "and constraint_type='UNIQUE'"; + if (tableName != null) { + sql += " and table_name='" + tableName + "'"; + } + } else if (database.getClass().getName().contains("MaxDB")) { //have to check classname as this is currently an extension + sql = "select distinct tablename AS TABLE_NAME, constraintname AS CONSTRAINT_NAME from CONSTRAINTCOLUMNS WHERE CONSTRAINTTYPE = 'UNIQUE_CONST'"; + if (tableName != null) { + sql += " and tablename='" + tableName + "'"; + } + } else if (database instanceof MSSQLDatabase) { + sql = + "SELECT " + + "[TC].[CONSTRAINT_NAME], " + + "[TC].[TABLE_NAME], " + + "[TC].[CONSTRAINT_CATALOG] AS INDEX_CATALOG, " + + "[TC].[CONSTRAINT_SCHEMA] AS INDEX_SCHEMA, " + + "[IDX].[TYPE_DESC], " + + "[IDX].[name] AS INDEX_NAME " + + "FROM [INFORMATION_SCHEMA].[TABLE_CONSTRAINTS] AS [TC] " + + "JOIN sys.indexes AS IDX ON IDX.name=[TC].[CONSTRAINT_NAME] AND object_schema_name(object_id)=[TC].[CONSTRAINT_SCHEMA] " + + "WHERE [TC].[CONSTRAINT_TYPE] = 'UNIQUE' " + + "AND [TC].[CONSTRAINT_CATALOG] = N'" + database.escapeStringForDatabase(jdbcCatalogName) + "' " + + "AND [TC].[CONSTRAINT_SCHEMA] = N'" + database.escapeStringForDatabase(jdbcSchemaName) + "'"; + if (tableName != null) { + sql += " AND [TC].[TABLE_NAME] = N'" + database.escapeStringForDatabase(database.correctObjectName(tableName, Table.class)) + "'"; + } + } else if (database instanceof OracleDatabase) { + warnAboutDbaRecycleBin(); + + sql = "select uc.owner AS CONSTRAINT_SCHEM, uc.constraint_name, uc.table_name,uc.status,uc.deferrable,uc.deferred,ui.tablespace_name, ui.index_name, ui.owner as INDEX_CATALOG, uc.VALIDATED as VALIDATED, ac.COLUMN_NAME as COLUMN_NAME " + + "from all_constraints uc " + + "join all_indexes ui on uc.index_name = ui.index_name and uc.owner=ui.table_owner and uc.table_name=ui.table_name " + + "LEFT OUTER JOIN " + (((OracleDatabase) database).canAccessDbaRecycleBin() ? "dba_recyclebin" : "user_recyclebin") + " d ON d.object_name=ui.table_name " + + "LEFT JOIN all_cons_columns ac ON ac.OWNER = uc.OWNER AND ac.TABLE_NAME = uc.TABLE_NAME AND ac.CONSTRAINT_NAME = uc.CONSTRAINT_NAME " + + "where uc.constraint_type='U' "; + if (tableName != null || getAllCatalogsStringScratchData() == null) { + sql += "and uc.owner = '" + jdbcSchemaName + "'"; + } else { + sql += "and uc.owner IN ('" + jdbcSchemaName + "', " + getAllCatalogsStringScratchData() + ")"; + } + sql += "AND d.object_name IS NULL "; + + if (tableName != null) { + sql += " and uc.table_name = '" + tableName + "'"; + } + } else if (database instanceof DB2Database) { + // if we are on DB2 AS400 iSeries + if (database.getDatabaseProductName().startsWith("DB2 UDB for AS/400")) { + sql = "select constraint_name as constraint_name, table_name as table_name from QSYS2.TABLE_CONSTRAINTS where table_schema='" + jdbcSchemaName + "' and constraint_type='UNIQUE'"; + if (tableName != null) { + sql += " and table_name = '" + tableName + "'"; + } + // DB2 z/OS + } + // here we are on DB2 UDB + else { + sql = "select distinct k.constname as constraint_name, t.tabname as TABLE_NAME " + + "from syscat.keycoluse k " + + "inner join syscat.tabconst t " + + "on k.constname = t.constname " + + "where t.tabschema = ? " + + "and t.type = 'U'"; + parameters.add(jdbcSchemaName); + if (tableName != null) { + sql += " and t.tabname = ?"; + parameters.add(tableName); + } + } + } else if (database instanceof Db2zDatabase) { + sql = "select k.constname as constraint_name, t.tbname as TABLE_NAME" + + " from SYSIBM.SYSKEYCOLUSE k" + + " inner join SYSIBM.SYSTABCONST t" + + " on k.constname = t.constname" + + " and k.TBCREATOR = t.TBCREATOR" + + " and k.TBNAME = t.TBNAME" + + " where t.TBCREATOR = ?" + + " and t.TYPE = 'U'"; + parameters.add(jdbcSchemaName); + if (tableName != null) { + sql += " and t.TBNAME = ?"; + parameters.add(tableName); + } + } else if (database instanceof FirebirdDatabase) { + sql = "SELECT TRIM(RDB$INDICES.RDB$INDEX_NAME) AS CONSTRAINT_NAME, " + + "TRIM(RDB$INDICES.RDB$RELATION_NAME) AS TABLE_NAME " + + "FROM RDB$INDICES " + + "LEFT JOIN RDB$RELATION_CONSTRAINTS " + + "ON RDB$RELATION_CONSTRAINTS.RDB$INDEX_NAME = RDB$INDICES.RDB$INDEX_NAME " + + "WHERE RDB$INDICES.RDB$UNIQUE_FLAG IS NOT NULL " + + "AND (" + + "RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_TYPE IS NULL " + + "OR TRIM(RDB$RELATION_CONSTRAINTS.RDB$CONSTRAINT_TYPE)='UNIQUE') " + + "AND NOT(RDB$INDICES.RDB$INDEX_NAME LIKE 'RDB$%')"; + if (tableName != null) { + sql += " AND TRIM(RDB$INDICES.RDB$RELATION_NAME)='" + tableName + "'"; + } + } else if (database instanceof DerbyDatabase) { + sql = "select c.constraintname as CONSTRAINT_NAME, tablename AS TABLE_NAME " + + "from sys.systables t, sys.sysconstraints c, sys.sysschemas s " + + "where s.schemaname='" + jdbcCatalogName + "' " + + "and t.tableid = c.tableid " + + "and t.schemaid=s.schemaid " + + "and c.type = 'U'"; + if (tableName != null) { + sql += " AND t.tablename = '" + tableName + "'"; + } + } else if (database instanceof InformixDatabase) { + sql = "select unique sysindexes.idxname as CONSTRAINT_NAME, sysindexes.idxtype, systables.tabname as TABLE_NAME " + + "from sysindexes, systables " + + "left outer join sysconstraints on sysconstraints.tabid = systables.tabid and sysconstraints.constrtype = 'P' " + + "where sysindexes.tabid = systables.tabid and sysindexes.idxtype = 'U' " + + "and sysconstraints.idxname != sysindexes.idxname " + + "and sysconstraints.tabid = sysindexes.tabid"; + if (tableName != null) { + sql += " and systables.tabname = '" + database.correctObjectName(tableName, Table.class) + "'"; + } + } else if (database instanceof SybaseDatabase) { + sql = "select idx.name as CONSTRAINT_NAME, tbl.name as TABLE_NAME " + + "from sysindexes idx " + + "inner join sysobjects tbl on tbl.id = idx.id " + + "where idx.indid between 1 and 254 " + + "and (idx.status & 2) = 2 " + + "and tbl.type = 'U'"; + if (tableName != null) { + sql += " and tbl.name = '" + database.correctObjectName(tableName, Table.class) + "'"; + } + } else if (database instanceof SybaseASADatabase) { + sql = "select sysconstraint.constraint_name, sysconstraint.constraint_type, systable.table_name " + + "from sysconstraint, systable " + + "where sysconstraint.table_object_id = systable.object_id " + + "and sysconstraint.constraint_type = 'U'"; + if (tableName != null) { + sql += " and systable.table_name = '" + tableName + "'"; + } + } else { + if (database instanceof H2Database) { + try { + if (database.getDatabaseMajorVersion() >= 2) { + sql = "select CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME " + + "from " + database.getSystemSchema() + ".table_constraints " + + "where constraint_schema='" + jdbcSchemaName + "' " + + "and constraint_catalog='" + jdbcCatalogName + "' " + + "and constraint_type='UNIQUE'"; + if (tableName != null) { + sql += " and table_name='" + tableName + "'"; + } + } + } catch (DatabaseException e) { + Scope.getCurrentScope().getLog(getClass()).fine("Cannot determine h2 version, using default unique constraint query"); + } + } + if (sql == null) { + + sql = "select CONSTRAINT_NAME, CONSTRAINT_TYPE, TABLE_NAME " + + "from " + database.getSystemSchema() + ".constraints " + + "where constraint_schema='" + jdbcSchemaName + "' " + + "and constraint_catalog='" + jdbcCatalogName + "' " + + "and constraint_type='UNIQUE'"; + if (tableName != null) { + sql += " and table_name='" + tableName + "'"; + } + } + } + + return executeAndExtract(database, database instanceof InformixDatabase, sql, parameters.toArray()); + } + }); + } + } + + private String getAllCatalogsStringScratchData() { + return (String) getScratchData(ALL_CATALOGS_STRING_SCRATCH_KEY); + } + + private String escapeForLike(String string, Database database) { + if (string == null) { + return null; + } + + if (database instanceof SQLiteDatabase || database instanceof DmDatabase) { + //sqlite jdbc's queries does not support escaped patterns. + // DM 也不支持转义的匹配方式,需要兼容 + return string; + } + + return string + .replace("%", "\\%") + .replace("_", "\\_"); + } +} diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java b/zt-module-bpm/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java new file mode 100644 index 0000000..ed2f0c9 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/java/org/flowable/common/engine/impl/db/DbSqlSessionFactory.java @@ -0,0 +1,354 @@ +/* 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.flowable.common.engine.impl.db; + +import org.apache.ibatis.session.SqlSessionFactory; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.impl.context.Context; +import org.flowable.common.engine.impl.interceptor.CommandContext; +import org.flowable.common.engine.impl.interceptor.Session; +import org.flowable.common.engine.impl.interceptor.SessionFactory; +import org.flowable.common.engine.impl.persistence.cache.EntityCache; +import org.flowable.common.engine.impl.persistence.entity.Entity; + +import java.sql.SQLException; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author Tom Baeyens + * @author Joram Barrez + */ +public class DbSqlSessionFactory implements SessionFactory { + + protected Map> databaseSpecificStatements = new HashMap<>(); + + protected String databaseType; + protected String databaseTablePrefix = ""; + protected boolean tablePrefixIsSchema; + + protected String databaseCatalog; + protected String databaseSchema; + protected SqlSessionFactory sqlSessionFactory; + protected Map statementMappings; + + protected Map, String> insertStatements = new ConcurrentHashMap<>(); + protected Map, String> updateStatements = new ConcurrentHashMap<>(); + protected Map, String> deleteStatements = new ConcurrentHashMap<>(); + protected Map, String> selectStatements = new ConcurrentHashMap<>(); + + protected List> insertionOrder = new ArrayList<>(); + protected List> deletionOrder = new ArrayList<>(); + + protected boolean isDbHistoryUsed = true; + + protected Set> bulkInserteableEntityClasses = new HashSet<>(); + protected Map, String> bulkInsertStatements = new ConcurrentHashMap<>(); + + protected int maxNrOfStatementsInBulkInsert = 100; + + protected Map> logicalNameToClassMapping = new ConcurrentHashMap<>(); + + protected boolean usePrefixId; + + public DbSqlSessionFactory(boolean usePrefixId) { + this.usePrefixId = usePrefixId; + } + + @Override + public Class getSessionType() { + return DbSqlSession.class; + } + + @Override + public Session openSession(CommandContext commandContext) { + DbSqlSession dbSqlSession = createDbSqlSession(); + // 当前系统适配 dm,如果存在 schema 为空的情况,从 connection 获取 + try { + if (getDatabaseSchema() == null || getDatabaseSchema().length() == 0){ + setDatabaseSchema(dbSqlSession.getSqlSession().getConnection().getSchema()); + } + dbSqlSession.getSqlSession().getConnection().getSchema(); + } catch (SQLException e) { + throw new RuntimeException(e); + } + + if (getDatabaseSchema() != null && getDatabaseSchema().length() > 0) { + try { + dbSqlSession.getSqlSession().getConnection().setSchema(getDatabaseSchema()); + } catch (SQLException e) { + throw new FlowableException("Could not set database schema on connection", e); + } + } + if (getDatabaseCatalog() != null && getDatabaseCatalog().length() > 0) { + try { + dbSqlSession.getSqlSession().getConnection().setCatalog(getDatabaseCatalog()); + } catch (SQLException e) { + throw new FlowableException("Could not set database catalog on connection", e); + } + } + if (dbSqlSession.getSqlSession().getConnection() == null) { + throw new FlowableException("Invalid dbSqlSession: no active connection found"); + } + return dbSqlSession; + } + + protected DbSqlSession createDbSqlSession() { + return new DbSqlSession(this, Context.getCommandContext().getSession(EntityCache.class)); + } + + // insert, update and delete statements + // ///////////////////////////////////// + + public String getInsertStatement(Entity object) { + return getStatement(object.getClass(), insertStatements, "insert"); + } + + public String getInsertStatement(Class clazz) { + return getStatement(clazz, insertStatements, "insert"); + } + + public String getUpdateStatement(Entity object) { + return getStatement(object.getClass(), updateStatements, "update"); + } + + public String getDeleteStatement(Class entityClass) { + return getStatement(entityClass, deleteStatements, "delete"); + } + + public String getSelectStatement(Class entityClass) { + return getStatement(entityClass, selectStatements, "select"); + } + + protected String getStatement(Class entityClass, Map, String> cachedStatements, String prefix) { + String statement = cachedStatements.get(entityClass); + if (statement != null) { + return statement; + } + statement = prefix + entityClass.getSimpleName(); + if (statement.endsWith("Impl")) { + statement = statement.substring(0, statement.length() - 10); // removing 'entityImpl' + } else { + statement = statement.substring(0, statement.length() - 6); // removing 'entity' + } + cachedStatements.put(entityClass, statement); + return statement; + } + + // db specific mappings + // ///////////////////////////////////////////////////// + + protected void addDatabaseSpecificStatement(String databaseType, String activitiStatement, String ibatisStatement) { + Map specificStatements = databaseSpecificStatements.get(databaseType); + if (specificStatements == null) { + specificStatements = new HashMap<>(); + databaseSpecificStatements.put(databaseType, specificStatements); + } + specificStatements.put(activitiStatement, ibatisStatement); + } + + public String mapStatement(String statement) { + if (statementMappings == null) { + return statement; + } + String mappedStatement = statementMappings.get(statement); + return (mappedStatement != null ? mappedStatement : statement); + } + + // customized getters and setters + // /////////////////////////////////////////// + + public void setDatabaseType(String databaseType) { + this.databaseType = databaseType; + this.statementMappings = databaseSpecificStatements.get(databaseType); + } + + public boolean isMysql() { + return "mysql".equals(getDatabaseType()); + } + + public boolean isOracle() { + return "oracle".equals(getDatabaseType()); + } + + public Boolean isBulkInsertable(Class entityClass) { + return bulkInserteableEntityClasses != null && bulkInserteableEntityClasses.contains(entityClass); + } + + @SuppressWarnings("rawtypes") + public String getBulkInsertStatement(Class clazz) { + return getStatement(clazz, bulkInsertStatements, "bulkInsert"); + } + + public Set> getBulkInserteableEntityClasses() { + return bulkInserteableEntityClasses; + } + + public void setBulkInserteableEntityClasses(Set> bulkInserteableEntityClasses) { + this.bulkInserteableEntityClasses = bulkInserteableEntityClasses; + } + + public int getMaxNrOfStatementsInBulkInsert() { + return maxNrOfStatementsInBulkInsert; + } + + public void setMaxNrOfStatementsInBulkInsert(int maxNrOfStatementsInBulkInsert) { + this.maxNrOfStatementsInBulkInsert = maxNrOfStatementsInBulkInsert; + } + + public Map, String> getBulkInsertStatements() { + return bulkInsertStatements; + } + + public void setBulkInsertStatements(Map, String> bulkInsertStatements) { + this.bulkInsertStatements = bulkInsertStatements; + } + + // getters and setters ////////////////////////////////////////////////////// + + public SqlSessionFactory getSqlSessionFactory() { + return sqlSessionFactory; + } + + public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) { + this.sqlSessionFactory = sqlSessionFactory; + } + + public String getDatabaseType() { + return databaseType; + } + + public Map> getDatabaseSpecificStatements() { + return databaseSpecificStatements; + } + + public void setDatabaseSpecificStatements(Map> databaseSpecificStatements) { + this.databaseSpecificStatements = databaseSpecificStatements; + } + + public Map getStatementMappings() { + return statementMappings; + } + + public void setStatementMappings(Map statementMappings) { + this.statementMappings = statementMappings; + } + + public Map, String> getInsertStatements() { + return insertStatements; + } + + public void setInsertStatements(Map, String> insertStatements) { + this.insertStatements = insertStatements; + } + + public Map, String> getUpdateStatements() { + return updateStatements; + } + + public void setUpdateStatements(Map, String> updateStatements) { + this.updateStatements = updateStatements; + } + + public Map, String> getDeleteStatements() { + return deleteStatements; + } + + public void setDeleteStatements(Map, String> deleteStatements) { + this.deleteStatements = deleteStatements; + } + + public Map, String> getSelectStatements() { + return selectStatements; + } + + public void setSelectStatements(Map, String> selectStatements) { + this.selectStatements = selectStatements; + } + + public boolean isDbHistoryUsed() { + return isDbHistoryUsed; + } + + public void setDbHistoryUsed(boolean isDbHistoryUsed) { + this.isDbHistoryUsed = isDbHistoryUsed; + } + + public void setDatabaseTablePrefix(String databaseTablePrefix) { + this.databaseTablePrefix = databaseTablePrefix; + } + + public String getDatabaseTablePrefix() { + return databaseTablePrefix; + } + + public String getDatabaseCatalog() { + return databaseCatalog; + } + + public void setDatabaseCatalog(String databaseCatalog) { + this.databaseCatalog = databaseCatalog; + } + + public String getDatabaseSchema() { + return databaseSchema; + } + + public void setDatabaseSchema(String databaseSchema) { + this.databaseSchema = databaseSchema; + } + + public void setTablePrefixIsSchema(boolean tablePrefixIsSchema) { + this.tablePrefixIsSchema = tablePrefixIsSchema; + } + + public boolean isTablePrefixIsSchema() { + return tablePrefixIsSchema; + } + + public List> getInsertionOrder() { + return insertionOrder; + } + + public void setInsertionOrder(List> insertionOrder) { + this.insertionOrder = insertionOrder; + } + + public List> getDeletionOrder() { + return deletionOrder; + } + + public void setDeletionOrder(List> deletionOrder) { + this.deletionOrder = deletionOrder; + } + public void addLogicalEntityClassMapping(String logicalName, Class entityClass) { + logicalNameToClassMapping.put(logicalName, entityClass); + } + + public Map> getLogicalNameToClassMapping() { + return logicalNameToClassMapping; + } + + public void setLogicalNameToClassMapping(Map> logicalNameToClassMapping) { + this.logicalNameToClassMapping = logicalNameToClassMapping; + } + + public boolean isUsePrefixId() { + return usePrefixId; + } + + public void setUsePrefixId(boolean usePrefixId) { + this.usePrefixId = usePrefixId; + } +} diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType b/zt-module-bpm/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType new file mode 100644 index 0000000..5be88a3 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/META-INF/services/liquibase.datatype.LiquibaseDataType @@ -0,0 +1 @@ +liquibase.datatype.core.DmBooleanType diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql new file mode 100644 index 0000000..19dca82 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/create/flowable.oracle.create.batch.sql @@ -0,0 +1,41 @@ +create table FLW_RU_BATCH ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + TYPE_ VARCHAR2(64) not null, + SEARCH_KEY_ VARCHAR2(255), + SEARCH_KEY2_ VARCHAR2(255), + CREATE_TIME_ TIMESTAMP(6) not null, + COMPLETE_TIME_ TIMESTAMP(6), + STATUS_ VARCHAR2(255), + BATCH_DOC_ID_ VARCHAR2(64), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create table FLW_RU_BATCH_PART ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + BATCH_ID_ VARCHAR2(64), + TYPE_ VARCHAR2(64) not null, + SCOPE_ID_ VARCHAR2(64), + SUB_SCOPE_ID_ VARCHAR2(64), + SCOPE_TYPE_ VARCHAR2(64), + SEARCH_KEY_ VARCHAR2(255), + SEARCH_KEY2_ VARCHAR2(255), + CREATE_TIME_ TIMESTAMP(6) not null, + COMPLETE_TIME_ TIMESTAMP(6), + STATUS_ VARCHAR2(255), + RESULT_DOC_ID_ VARCHAR2(64), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create index FLW_IDX_BATCH_PART on FLW_RU_BATCH_PART(BATCH_ID_); + +alter table FLW_RU_BATCH_PART + add constraint FLW_FK_BATCH_PART_PARENT + foreign key (BATCH_ID_) + references FLW_RU_BATCH (ID_); + +insert into ACT_GE_PROPERTY values ('batch.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql new file mode 100644 index 0000000..d16ba1c --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/batch/service/db/drop/flowable.oracle.drop.batch.sql @@ -0,0 +1,4 @@ +drop index FLW_IDX_BATCH_PART; + +drop table FLW_RU_BATCH_PART; +drop table FLW_RU_BATCH; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql new file mode 100644 index 0000000..4ef0d2e --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/common/db/create/flowable.oracle.create.common.sql @@ -0,0 +1,23 @@ +create table ACT_GE_PROPERTY ( + NAME_ VARCHAR2(64), + VALUE_ VARCHAR2(300), + REV_ INTEGER, + primary key (NAME_) +); + +create table ACT_GE_BYTEARRAY ( + ID_ VARCHAR2(64), + REV_ INTEGER, + NAME_ VARCHAR2(255), + DEPLOYMENT_ID_ VARCHAR2(64), + BYTES_ BLOB, + GENERATED_ NUMBER(1) CHECK (GENERATED_ IN (1,0)), + primary key (ID_) +); + +insert into ACT_GE_PROPERTY +values ('common.schema.version', '7.0.1.1', 1); + +insert into ACT_GE_PROPERTY +values ('next.dbid', '1', 1); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql new file mode 100644 index 0000000..9019cb9 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/common/db/drop/flowable.oracle.drop.common.sql @@ -0,0 +1,2 @@ +drop table ACT_GE_BYTEARRAY; +drop table ACT_GE_PROPERTY; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql new file mode 100644 index 0000000..d0139b7 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.engine.sql @@ -0,0 +1,355 @@ +create table ACT_RE_DEPLOYMENT ( + ID_ VARCHAR2(64), + NAME_ VARCHAR2(255), + CATEGORY_ VARCHAR2(255), + KEY_ VARCHAR2(255), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + DEPLOY_TIME_ TIMESTAMP(6), + DERIVED_FROM_ VARCHAR2(64), + DERIVED_FROM_ROOT_ VARCHAR2(64), + PARENT_DEPLOYMENT_ID_ VARCHAR2(255), + ENGINE_VERSION_ VARCHAR2(255), + primary key (ID_) +); + +create table ACT_RE_MODEL ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + NAME_ VARCHAR2(255), + KEY_ VARCHAR2(255), + CATEGORY_ VARCHAR2(255), + CREATE_TIME_ TIMESTAMP(6), + LAST_UPDATE_TIME_ TIMESTAMP(6), + VERSION_ INTEGER, + META_INFO_ VARCHAR2(2000), + DEPLOYMENT_ID_ VARCHAR2(64), + EDITOR_SOURCE_VALUE_ID_ VARCHAR2(64), + EDITOR_SOURCE_EXTRA_VALUE_ID_ VARCHAR2(64), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_EXECUTION ( + ID_ VARCHAR2(64), + REV_ INTEGER, + PROC_INST_ID_ VARCHAR2(64), + BUSINESS_KEY_ VARCHAR2(255), + PARENT_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + SUPER_EXEC_ VARCHAR2(64), + ROOT_PROC_INST_ID_ VARCHAR2(64), + ACT_ID_ VARCHAR2(255), + IS_ACTIVE_ NUMBER(1) CHECK (IS_ACTIVE_ IN (1,0)), + IS_CONCURRENT_ NUMBER(1) CHECK (IS_CONCURRENT_ IN (1,0)), + IS_SCOPE_ NUMBER(1) CHECK (IS_SCOPE_ IN (1,0)), + IS_EVENT_SCOPE_ NUMBER(1) CHECK (IS_EVENT_SCOPE_ IN (1,0)), + IS_MI_ROOT_ NUMBER(1) CHECK (IS_MI_ROOT_ IN (1,0)), + SUSPENSION_STATE_ INTEGER, + CACHED_ENT_STATE_ INTEGER, + TENANT_ID_ VARCHAR2(255) DEFAULT '', + NAME_ VARCHAR2(255), + START_ACT_ID_ VARCHAR2(255), + START_TIME_ TIMESTAMP(6), + START_USER_ID_ VARCHAR2(255), + LOCK_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + IS_COUNT_ENABLED_ NUMBER(1) CHECK (IS_COUNT_ENABLED_ IN (1,0)), + EVT_SUBSCR_COUNT_ INTEGER, + TASK_COUNT_ INTEGER, + JOB_COUNT_ INTEGER, + TIMER_JOB_COUNT_ INTEGER, + SUSP_JOB_COUNT_ INTEGER, + DEADLETTER_JOB_COUNT_ INTEGER, + EXTERNAL_WORKER_JOB_COUNT_ INTEGER, + VAR_COUNT_ INTEGER, + ID_LINK_COUNT_ INTEGER, + CALLBACK_ID_ VARCHAR2(255), + CALLBACK_TYPE_ VARCHAR2(255), + REFERENCE_ID_ VARCHAR2(255), + REFERENCE_TYPE_ VARCHAR2(255), + PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), + BUSINESS_STATUS_ VARCHAR2(255), + primary key (ID_) +); + +create table ACT_RE_PROCDEF ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + NAME_ VARCHAR2(255), + KEY_ VARCHAR2(255) NOT NULL, + VERSION_ INTEGER NOT NULL, + DEPLOYMENT_ID_ VARCHAR2(64), + RESOURCE_NAME_ VARCHAR2(2000), + DGRM_RESOURCE_NAME_ VARCHAR2(4000), + DESCRIPTION_ VARCHAR2(2000), + HAS_START_FORM_KEY_ NUMBER(1) CHECK (HAS_START_FORM_KEY_ IN (1,0)), + HAS_GRAPHICAL_NOTATION_ NUMBER(1) CHECK (HAS_GRAPHICAL_NOTATION_ IN (1,0)), + SUSPENSION_STATE_ INTEGER, + TENANT_ID_ VARCHAR2(255) DEFAULT '', + DERIVED_FROM_ VARCHAR2(64), + DERIVED_FROM_ROOT_ VARCHAR2(64), + DERIVED_VERSION_ INTEGER DEFAULT 0 NOT NULL, + ENGINE_VERSION_ VARCHAR2(255), + primary key (ID_) +); + +create table ACT_EVT_LOG ( + LOG_NR_ NUMBER(19), + TYPE_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + EXECUTION_ID_ VARCHAR2(64), + TASK_ID_ VARCHAR2(64), + TIME_STAMP_ TIMESTAMP(6) not null, + USER_ID_ VARCHAR2(255), + DATA_ BLOB, + LOCK_OWNER_ VARCHAR2(255), + LOCK_TIME_ TIMESTAMP(6) null, + IS_PROCESSED_ NUMBER(3) default 0, + primary key (LOG_NR_) +); + +create sequence act_evt_log_seq; + +create table ACT_PROCDEF_INFO ( + ID_ VARCHAR2(64) not null, + PROC_DEF_ID_ VARCHAR2(64) not null, + REV_ integer, + INFO_JSON_ID_ VARCHAR2(64), + primary key (ID_) +); + +create table ACT_RU_ACTINST ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER default 1, + PROC_DEF_ID_ VARCHAR2(64) not null, + PROC_INST_ID_ VARCHAR2(64) not null, + EXECUTION_ID_ VARCHAR2(64) not null, + ACT_ID_ VARCHAR2(255) not null, + TASK_ID_ VARCHAR2(64), + CALL_PROC_INST_ID_ VARCHAR2(64), + ACT_NAME_ VARCHAR2(255), + ACT_TYPE_ VARCHAR2(255) not null, + ASSIGNEE_ VARCHAR2(255), + START_TIME_ TIMESTAMP(6) not null, + END_TIME_ TIMESTAMP(6), + DURATION_ NUMBER(19,0), + TRANSACTION_ORDER_ INTEGER, + DELETE_REASON_ VARCHAR2(2000), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create index ACT_IDX_EXEC_BUSKEY on ACT_RU_EXECUTION(BUSINESS_KEY_); +create index ACT_IDX_EXEC_ROOT on ACT_RU_EXECUTION(ROOT_PROC_INST_ID_); +create index ACT_IDX_EXEC_REF_ID_ on ACT_RU_EXECUTION(REFERENCE_ID_); +create index ACT_IDX_VARIABLE_TASK_ID on ACT_RU_VARIABLE(TASK_ID_); + +create index ACT_IDX_RU_ACTI_START on ACT_RU_ACTINST(START_TIME_); +create index ACT_IDX_RU_ACTI_END on ACT_RU_ACTINST(END_TIME_); +create index ACT_IDX_RU_ACTI_PROC on ACT_RU_ACTINST(PROC_INST_ID_); +create index ACT_IDX_RU_ACTI_PROC_ACT on ACT_RU_ACTINST(PROC_INST_ID_, ACT_ID_); +create index ACT_IDX_RU_ACTI_EXEC on ACT_RU_ACTINST(EXECUTION_ID_); +create index ACT_IDX_RU_ACTI_EXEC_ACT on ACT_RU_ACTINST(EXECUTION_ID_, ACT_ID_); +create index ACT_IDX_RU_ACTI_TASK on ACT_RU_ACTINST(TASK_ID_); + +create index ACT_IDX_BYTEAR_DEPL on ACT_GE_BYTEARRAY(DEPLOYMENT_ID_); +alter table ACT_GE_BYTEARRAY + add constraint ACT_FK_BYTEARR_DEPL + foreign key (DEPLOYMENT_ID_) + references ACT_RE_DEPLOYMENT (ID_); + +alter table ACT_RE_PROCDEF + add constraint ACT_UNIQ_PROCDEF + unique (KEY_,VERSION_, DERIVED_VERSION_, TENANT_ID_); + +create index ACT_IDX_EXE_PROCINST on ACT_RU_EXECUTION(PROC_INST_ID_); +alter table ACT_RU_EXECUTION + add constraint ACT_FK_EXE_PROCINST + foreign key (PROC_INST_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_EXE_PARENT on ACT_RU_EXECUTION(PARENT_ID_); +alter table ACT_RU_EXECUTION + add constraint ACT_FK_EXE_PARENT + foreign key (PARENT_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_EXE_SUPER on ACT_RU_EXECUTION(SUPER_EXEC_); +alter table ACT_RU_EXECUTION + add constraint ACT_FK_EXE_SUPER + foreign key (SUPER_EXEC_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_EXE_PROCDEF on ACT_RU_EXECUTION(PROC_DEF_ID_); +alter table ACT_RU_EXECUTION + add constraint ACT_FK_EXE_PROCDEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_TSKASS_TASK on ACT_RU_IDENTITYLINK(TASK_ID_); +alter table ACT_RU_IDENTITYLINK + add constraint ACT_FK_TSKASS_TASK + foreign key (TASK_ID_) + references ACT_RU_TASK (ID_); + +create index ACT_IDX_ATHRZ_PROCEDEF on ACT_RU_IDENTITYLINK(PROC_DEF_ID_); +alter table ACT_RU_IDENTITYLINK + add constraint ACT_FK_ATHRZ_PROCEDEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_IDL_PROCINST on ACT_RU_IDENTITYLINK(PROC_INST_ID_); +alter table ACT_RU_IDENTITYLINK + add constraint ACT_FK_IDL_PROCINST + foreign key (PROC_INST_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_TASK_EXEC on ACT_RU_TASK(EXECUTION_ID_); +alter table ACT_RU_TASK + add constraint ACT_FK_TASK_EXE + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_TASK_PROCINST on ACT_RU_TASK(PROC_INST_ID_); +alter table ACT_RU_TASK + add constraint ACT_FK_TASK_PROCINST + foreign key (PROC_INST_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_TASK_PROCDEF on ACT_RU_TASK(PROC_DEF_ID_); +alter table ACT_RU_TASK + add constraint ACT_FK_TASK_PROCDEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_VAR_EXE on ACT_RU_VARIABLE(EXECUTION_ID_); +alter table ACT_RU_VARIABLE + add constraint ACT_FK_VAR_EXE + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_VAR_PROCINST on ACT_RU_VARIABLE(PROC_INST_ID_); +alter table ACT_RU_VARIABLE + add constraint ACT_FK_VAR_PROCINST + foreign key (PROC_INST_ID_) + references ACT_RU_EXECUTION(ID_); + +create index ACT_IDX_JOB_EXECUTION_ID on ACT_RU_JOB(EXECUTION_ID_); +alter table ACT_RU_JOB + add constraint ACT_FK_JOB_EXECUTION + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_JOB_PROC_INST_ID on ACT_RU_JOB(PROCESS_INSTANCE_ID_); +alter table ACT_RU_JOB + add constraint ACT_FK_JOB_PROCESS_INSTANCE + foreign key (PROCESS_INSTANCE_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_JOB_PROC_DEF_ID on ACT_RU_JOB(PROC_DEF_ID_); +alter table ACT_RU_JOB + add constraint ACT_FK_JOB_PROC_DEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_TJOB_EXECUTION_ID on ACT_RU_TIMER_JOB(EXECUTION_ID_); +alter table ACT_RU_TIMER_JOB + add constraint ACT_FK_TJOB_EXECUTION + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_TJOB_PROC_INST_ID on ACT_RU_TIMER_JOB(PROCESS_INSTANCE_ID_); +alter table ACT_RU_TIMER_JOB + add constraint ACT_FK_TJOB_PROCESS_INSTANCE + foreign key (PROCESS_INSTANCE_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_TJOB_PROC_DEF_ID on ACT_RU_TIMER_JOB(PROC_DEF_ID_); +alter table ACT_RU_TIMER_JOB + add constraint ACT_FK_TJOB_PROC_DEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_SJOB_EXECUTION_ID on ACT_RU_SUSPENDED_JOB(EXECUTION_ID_); +alter table ACT_RU_SUSPENDED_JOB + add constraint ACT_FK_SJOB_EXECUTION + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_SJOB_PROC_INST_ID on ACT_RU_SUSPENDED_JOB(PROCESS_INSTANCE_ID_); +alter table ACT_RU_SUSPENDED_JOB + add constraint ACT_FK_SJOB_PROCESS_INSTANCE + foreign key (PROCESS_INSTANCE_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_SJOB_PROC_DEF_ID on ACT_RU_SUSPENDED_JOB(PROC_DEF_ID_); +alter table ACT_RU_SUSPENDED_JOB + add constraint ACT_FK_SJOB_PROC_DEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +create index ACT_IDX_DJOB_EXECUTION_ID on ACT_RU_DEADLETTER_JOB(EXECUTION_ID_); +alter table ACT_RU_DEADLETTER_JOB + add constraint ACT_FK_DJOB_EXECUTION + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_DJOB_PROC_INST_ID on ACT_RU_DEADLETTER_JOB(PROCESS_INSTANCE_ID_); +alter table ACT_RU_DEADLETTER_JOB + add constraint ACT_FK_DJOB_PROCESS_INSTANCE + foreign key (PROCESS_INSTANCE_ID_) + references ACT_RU_EXECUTION (ID_); + +create index ACT_IDX_DJOB_PROC_DEF_ID on ACT_RU_DEADLETTER_JOB(PROC_DEF_ID_); +alter table ACT_RU_DEADLETTER_JOB + add constraint ACT_FK_DJOB_PROC_DEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +alter table ACT_RU_EVENT_SUBSCR + add constraint ACT_FK_EVENT_EXEC + foreign key (EXECUTION_ID_) + references ACT_RU_EXECUTION(ID_); + +create index ACT_IDX_MODEL_SOURCE on ACT_RE_MODEL(EDITOR_SOURCE_VALUE_ID_); +alter table ACT_RE_MODEL + add constraint ACT_FK_MODEL_SOURCE + foreign key (EDITOR_SOURCE_VALUE_ID_) + references ACT_GE_BYTEARRAY (ID_); + +create index ACT_IDX_MODEL_SOURCE_EXTRA on ACT_RE_MODEL(EDITOR_SOURCE_EXTRA_VALUE_ID_); +alter table ACT_RE_MODEL + add constraint ACT_FK_MODEL_SOURCE_EXTRA + foreign key (EDITOR_SOURCE_EXTRA_VALUE_ID_) + references ACT_GE_BYTEARRAY (ID_); + +create index ACT_IDX_MODEL_DEPLOYMENT on ACT_RE_MODEL(DEPLOYMENT_ID_); +alter table ACT_RE_MODEL + add constraint ACT_FK_MODEL_DEPLOYMENT + foreign key (DEPLOYMENT_ID_) + references ACT_RE_DEPLOYMENT (ID_); + +create index ACT_IDX_PROCDEF_INFO_JSON on ACT_PROCDEF_INFO(INFO_JSON_ID_); +alter table ACT_PROCDEF_INFO + add constraint ACT_FK_INFO_JSON_BA + foreign key (INFO_JSON_ID_) + references ACT_GE_BYTEARRAY (ID_); + +create index ACT_IDX_PROCDEF_INFO_PROC on ACT_PROCDEF_INFO(PROC_DEF_ID_); +alter table ACT_PROCDEF_INFO + add constraint ACT_FK_INFO_PROCDEF + foreign key (PROC_DEF_ID_) + references ACT_RE_PROCDEF (ID_); + +alter table ACT_PROCDEF_INFO + add constraint ACT_UNIQ_INFO_PROCDEF + unique (PROC_DEF_ID_); + +insert into ACT_GE_PROPERTY +values ('schema.version', '7.0.1.1', 1); + +insert into ACT_GE_PROPERTY +values ('schema.history', 'create(7.0.1.1)', 1); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql new file mode 100644 index 0000000..75782f4 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/create/flowable.oracle.create.history.sql @@ -0,0 +1,114 @@ +create table ACT_HI_PROCINST ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER default 1, + PROC_INST_ID_ VARCHAR2(64) not null, + BUSINESS_KEY_ VARCHAR2(255), + PROC_DEF_ID_ VARCHAR2(64) not null, + START_TIME_ TIMESTAMP(6) not null, + END_TIME_ TIMESTAMP(6), + DURATION_ NUMBER(19,0), + START_USER_ID_ VARCHAR2(255), + START_ACT_ID_ VARCHAR2(255), + END_ACT_ID_ VARCHAR2(255), + SUPER_PROCESS_INSTANCE_ID_ VARCHAR2(64), + DELETE_REASON_ VARCHAR2(2000), + TENANT_ID_ VARCHAR2(255) default '', + NAME_ VARCHAR2(255), + CALLBACK_ID_ VARCHAR2(255), + CALLBACK_TYPE_ VARCHAR2(255), + REFERENCE_ID_ VARCHAR2(255), + REFERENCE_TYPE_ VARCHAR2(255), + PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), + BUSINESS_STATUS_ VARCHAR2(255), + primary key (ID_), + unique (PROC_INST_ID_) +); + +create table ACT_HI_ACTINST ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER default 1, + PROC_DEF_ID_ VARCHAR2(64) not null, + PROC_INST_ID_ VARCHAR2(64) not null, + EXECUTION_ID_ VARCHAR2(64) not null, + ACT_ID_ VARCHAR2(255) not null, + TASK_ID_ VARCHAR2(64), + CALL_PROC_INST_ID_ VARCHAR2(64), + ACT_NAME_ VARCHAR2(255), + ACT_TYPE_ VARCHAR2(255) not null, + ASSIGNEE_ VARCHAR2(255), + START_TIME_ TIMESTAMP(6) not null, + END_TIME_ TIMESTAMP(6), + TRANSACTION_ORDER_ INTEGER, + DURATION_ NUMBER(19,0), + DELETE_REASON_ VARCHAR2(2000), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create table ACT_HI_DETAIL ( + ID_ VARCHAR2(64) not null, + TYPE_ VARCHAR2(255) not null, + PROC_INST_ID_ VARCHAR2(64), + EXECUTION_ID_ VARCHAR2(64), + TASK_ID_ VARCHAR2(64), + ACT_INST_ID_ VARCHAR2(64), + NAME_ VARCHAR2(255) not null, + VAR_TYPE_ VARCHAR2(64), + REV_ INTEGER, + TIME_ TIMESTAMP(6) not null, + BYTEARRAY_ID_ VARCHAR2(64), + DOUBLE_ NUMBER(38,10), + LONG_ NUMBER(19,0), + TEXT_ VARCHAR2(2000), + TEXT2_ VARCHAR2(2000), + primary key (ID_) +); + +create table ACT_HI_COMMENT ( + ID_ VARCHAR2(64) not null, + TYPE_ VARCHAR2(255), + TIME_ TIMESTAMP(6) not null, + USER_ID_ VARCHAR2(255), + TASK_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + ACTION_ VARCHAR2(255), + MESSAGE_ VARCHAR2(2000), + FULL_MSG_ BLOB, + primary key (ID_) +); + +create table ACT_HI_ATTACHMENT ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + USER_ID_ VARCHAR2(255), + NAME_ VARCHAR2(255), + DESCRIPTION_ VARCHAR2(2000), + TYPE_ VARCHAR2(255), + TASK_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + URL_ VARCHAR2(2000), + CONTENT_ID_ VARCHAR2(64), + TIME_ TIMESTAMP(6), + primary key (ID_) +); + +create index ACT_IDX_HI_PRO_INST_END on ACT_HI_PROCINST(END_TIME_); +create index ACT_IDX_HI_PRO_I_BUSKEY on ACT_HI_PROCINST(BUSINESS_KEY_); +create index ACT_IDX_HI_PRO_SUPER_PROCINST on ACT_HI_PROCINST(SUPER_PROCESS_INSTANCE_ID_); +create index ACT_IDX_HI_ACT_INST_START on ACT_HI_ACTINST(START_TIME_); +create index ACT_IDX_HI_ACT_INST_END on ACT_HI_ACTINST(END_TIME_); +create index ACT_IDX_HI_DETAIL_PROC_INST on ACT_HI_DETAIL(PROC_INST_ID_); +create index ACT_IDX_HI_DETAIL_ACT_INST on ACT_HI_DETAIL(ACT_INST_ID_); +create index ACT_IDX_HI_DETAIL_TIME on ACT_HI_DETAIL(TIME_); +create index ACT_IDX_HI_DETAIL_NAME on ACT_HI_DETAIL(NAME_); +create index ACT_IDX_HI_DETAIL_TASK_ID on ACT_HI_DETAIL(TASK_ID_); +create index ACT_IDX_HI_PROCVAR_PROC_INST on ACT_HI_VARINST(PROC_INST_ID_); +create index ACT_IDX_HI_PROCVAR_TASK_ID on ACT_HI_VARINST(TASK_ID_); +create index ACT_IDX_HI_PROCVAR_EXE on ACT_HI_VARINST(EXECUTION_ID_); +create index ACT_IDX_HI_IDENT_LNK_TASK on ACT_HI_IDENTITYLINK(TASK_ID_); +create index ACT_IDX_HI_IDENT_LNK_PROCINST on ACT_HI_IDENTITYLINK(PROC_INST_ID_); + +create index ACT_IDX_HI_ACT_INST_PROCINST on ACT_HI_ACTINST(PROC_INST_ID_, ACT_ID_); +create index ACT_IDX_HI_ACT_INST_EXEC on ACT_HI_ACTINST(EXECUTION_ID_, ACT_ID_); +create index ACT_IDX_HI_TASK_INST_PROCINST on ACT_HI_TASKINST(PROC_INST_ID_); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql new file mode 100644 index 0000000..58537ba --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.engine.sql @@ -0,0 +1,148 @@ +drop index ACT_IDX_BYTEAR_DEPL; +drop index ACT_IDX_EXE_PROCINST; +drop index ACT_IDX_EXE_PARENT; +drop index ACT_IDX_EXE_SUPER; +drop index ACT_IDX_TSKASS_TASK; +drop index ACT_IDX_TASK_EXEC; +drop index ACT_IDX_TASK_PROCINST; +drop index ACT_IDX_TASK_PROCDEF; +drop index ACT_IDX_VAR_EXE; +drop index ACT_IDX_VAR_PROCINST; +drop index ACT_IDX_JOB_EXECUTION_ID; +drop index ACT_IDX_JOB_PROC_INST_ID; +drop index ACT_IDX_JOB_PROC_DEF_ID; +drop index ACT_IDX_TJOB_EXECUTION_ID; +drop index ACT_IDX_TJOB_PROC_INST_ID; +drop index ACT_IDX_TJOB_PROC_DEF_ID; +drop index ACT_IDX_SJOB_EXECUTION_ID; +drop index ACT_IDX_SJOB_PROC_INST_ID; +drop index ACT_IDX_SJOB_PROC_DEF_ID; +drop index ACT_IDX_DJOB_EXECUTION_ID; +drop index ACT_IDX_DJOB_PROC_INST_ID; +drop index ACT_IDX_DJOB_PROC_DEF_ID; +drop index ACT_IDX_MODEL_SOURCE; +drop index ACT_IDX_MODEL_SOURCE_EXTRA; +drop index ACT_IDX_MODEL_DEPLOYMENT; +drop index ACT_IDX_PROCDEF_INFO_JSON; + +drop index ACT_IDX_EXEC_BUSKEY; +drop index ACT_IDX_VARIABLE_TASK_ID; + +drop index ACT_IDX_RU_ACTI_START; +drop index ACT_IDX_RU_ACTI_END; +drop index ACT_IDX_RU_ACTI_PROC; +drop index ACT_IDX_RU_ACTI_PROC_ACT; +drop index ACT_IDX_RU_ACTI_EXEC; +drop index ACT_IDX_RU_ACTI_EXEC_ACT; + +alter table ACT_GE_BYTEARRAY + drop CONSTRAINT ACT_FK_BYTEARR_DEPL; + +alter table ACT_RU_EXECUTION + drop CONSTRAINT ACT_FK_EXE_PROCINST; + +alter table ACT_RU_EXECUTION + drop CONSTRAINT ACT_FK_EXE_PARENT; + +alter table ACT_RU_EXECUTION + drop CONSTRAINT ACT_FK_EXE_SUPER; + +alter table ACT_RU_EXECUTION + drop CONSTRAINT ACT_FK_EXE_PROCDEF; + +alter table ACT_RU_IDENTITYLINK + drop CONSTRAINT ACT_FK_TSKASS_TASK; + +alter table ACT_RU_IDENTITYLINK + drop CONSTRAINT ACT_FK_IDL_PROCINST; + +alter table ACT_RU_IDENTITYLINK + drop CONSTRAINT ACT_FK_ATHRZ_PROCEDEF; + +alter table ACT_RU_TASK + drop CONSTRAINT ACT_FK_TASK_EXE; + +alter table ACT_RU_TASK + drop CONSTRAINT ACT_FK_TASK_PROCINST; + +alter table ACT_RU_TASK + drop CONSTRAINT ACT_FK_TASK_PROCDEF; + +alter table ACT_RU_VARIABLE + drop CONSTRAINT ACT_FK_VAR_EXE; + +alter table ACT_RU_VARIABLE + drop CONSTRAINT ACT_FK_VAR_PROCINST; + +alter table ACT_RU_JOB + drop CONSTRAINT ACT_FK_JOB_EXECUTION; + +alter table ACT_RU_JOB + drop CONSTRAINT ACT_FK_JOB_PROCESS_INSTANCE; + +alter table ACT_RU_JOB + drop CONSTRAINT ACT_FK_JOB_PROC_DEF; + +alter table ACT_RU_TIMER_JOB + drop CONSTRAINT ACT_FK_TJOB_EXECUTION; + +alter table ACT_RU_TIMER_JOB + drop CONSTRAINT ACT_FK_TJOB_PROCESS_INSTANCE; + +alter table ACT_RU_TIMER_JOB + drop CONSTRAINT ACT_FK_TJOB_PROC_DEF; + +alter table ACT_RU_SUSPENDED_JOB + drop CONSTRAINT ACT_FK_SJOB_EXECUTION; + +alter table ACT_RU_SUSPENDED_JOB + drop CONSTRAINT ACT_FK_SJOB_PROCESS_INSTANCE; + +alter table ACT_RU_SUSPENDED_JOB + drop CONSTRAINT ACT_FK_SJOB_PROC_DEF; + +alter table ACT_RU_DEADLETTER_JOB + drop CONSTRAINT ACT_FK_DJOB_EXECUTION; + +alter table ACT_RU_DEADLETTER_JOB + drop CONSTRAINT ACT_FK_DJOB_PROCESS_INSTANCE; + +alter table ACT_RU_DEADLETTER_JOB + drop CONSTRAINT ACT_FK_DJOB_PROC_DEF; + +alter table ACT_RU_EVENT_SUBSCR + drop CONSTRAINT ACT_FK_EVENT_EXEC; + +alter table ACT_RE_PROCDEF + drop CONSTRAINT ACT_UNIQ_PROCDEF; + +alter table ACT_RE_MODEL + drop CONSTRAINT ACT_FK_MODEL_SOURCE; + +alter table ACT_RE_MODEL + drop CONSTRAINT ACT_FK_MODEL_SOURCE_EXTRA; + +alter table ACT_RE_MODEL + drop CONSTRAINT ACT_FK_MODEL_DEPLOYMENT; + +alter table ACT_PROCDEF_INFO + drop CONSTRAINT ACT_UNIQ_INFO_PROCDEF; + +alter table ACT_PROCDEF_INFO + drop CONSTRAINT ACT_FK_INFO_JSON_BA; + +alter table ACT_PROCDEF_INFO + drop CONSTRAINT ACT_FK_INFO_PROCDEF; + +drop index ACT_IDX_ATHRZ_PROCEDEF; +drop index ACT_IDX_PROCDEF_INFO_PROC; + +drop table ACT_RU_ACTINST; +drop table ACT_RE_DEPLOYMENT; +drop table ACT_RE_MODEL; +drop table ACT_RE_PROCDEF; +drop table ACT_RU_EXECUTION; + +drop sequence act_evt_log_seq; +drop table ACT_EVT_LOG; +drop table ACT_PROCDEF_INFO; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql new file mode 100644 index 0000000..2a31cc4 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/db/drop/flowable.oracle.drop.history.sql @@ -0,0 +1,23 @@ +drop index ACT_IDX_HI_PRO_INST_END; +drop index ACT_IDX_HI_PRO_I_BUSKEY; +drop index ACT_IDX_HI_ACT_INST_START; +drop index ACT_IDX_HI_ACT_INST_END; +drop index ACT_IDX_HI_DETAIL_PROC_INST; +drop index ACT_IDX_HI_DETAIL_ACT_INST; +drop index ACT_IDX_HI_DETAIL_TIME; +drop index ACT_IDX_HI_DETAIL_NAME; +drop index ACT_IDX_HI_DETAIL_TASK_ID; +drop index ACT_IDX_HI_PROCVAR_PROC_INST; +drop index ACT_IDX_HI_PROCVAR_TASK_ID; +drop index ACT_IDX_HI_PROCVAR_EXE; +drop index ACT_IDX_HI_ACT_INST_PROCINST; +drop index ACT_IDX_HI_IDENT_LNK_TASK; +drop index ACT_IDX_HI_IDENT_LNK_PROCINST; +drop index ACT_IDX_HI_TASK_INST_PROCINST; + +drop table ACT_HI_PROCINST; +drop table ACT_HI_ACTINST; +drop table ACT_HI_DETAIL; +drop table ACT_HI_COMMENT; +drop table ACT_HI_ATTACHMENT; + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql new file mode 100644 index 0000000..55c5dbe --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.history.sql @@ -0,0 +1,23 @@ +create table ACT_HI_ENTITYLINK ( + ID_ VARCHAR2(64), + LINK_TYPE_ VARCHAR2(255), + CREATE_TIME_ TIMESTAMP(6), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + PARENT_ELEMENT_ID_ VARCHAR2(255), + REF_SCOPE_ID_ VARCHAR2(255), + REF_SCOPE_TYPE_ VARCHAR2(255), + REF_SCOPE_DEFINITION_ID_ VARCHAR2(255), + ROOT_SCOPE_ID_ VARCHAR2(255), + ROOT_SCOPE_TYPE_ VARCHAR2(255), + HIERARCHY_TYPE_ VARCHAR2(255), + primary key (ID_) +); + +create index ACT_IDX_HI_ENT_LNK_SCOPE on ACT_HI_ENTITYLINK(SCOPE_ID_, SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_HI_ENT_LNK_REF_SCOPE on ACT_HI_ENTITYLINK(REF_SCOPE_ID_, REF_SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_HI_ENT_LNK_ROOT_SCOPE on ACT_HI_ENTITYLINK(ROOT_SCOPE_ID_, ROOT_SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_HI_ENT_LNK_SCOPE_DEF on ACT_HI_ENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_, LINK_TYPE_); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql new file mode 100644 index 0000000..de08451 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/create/flowable.oracle.create.entitylink.sql @@ -0,0 +1,26 @@ +create table ACT_RU_ENTITYLINK ( + ID_ VARCHAR2(64), + REV_ INTEGER, + CREATE_TIME_ TIMESTAMP(6), + LINK_TYPE_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + PARENT_ELEMENT_ID_ VARCHAR2(255), + REF_SCOPE_ID_ VARCHAR2(255), + REF_SCOPE_TYPE_ VARCHAR2(255), + REF_SCOPE_DEFINITION_ID_ VARCHAR2(255), + ROOT_SCOPE_ID_ VARCHAR2(255), + ROOT_SCOPE_TYPE_ VARCHAR2(255), + HIERARCHY_TYPE_ VARCHAR2(255), + primary key (ID_) +); + +create index ACT_IDX_ENT_LNK_SCOPE on ACT_RU_ENTITYLINK(SCOPE_ID_, SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_ENT_LNK_REF_SCOPE on ACT_RU_ENTITYLINK(REF_SCOPE_ID_, REF_SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_ENT_LNK_ROOT_SCOPE on ACT_RU_ENTITYLINK(ROOT_SCOPE_ID_, ROOT_SCOPE_TYPE_, LINK_TYPE_); +create index ACT_IDX_ENT_LNK_SCOPE_DEF on ACT_RU_ENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_, LINK_TYPE_); + +insert into ACT_GE_PROPERTY values ('entitylink.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql new file mode 100644 index 0000000..a908877 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.history.sql @@ -0,0 +1,4 @@ +drop index ACT_IDX_HI_ENT_LNK_SCOPE; +drop index ACT_IDX_HI_ENT_LNK_SCOPE_DEF; + +drop table ACT_HI_ENTITYLINK; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql new file mode 100644 index 0000000..aedbacd --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/entitylink/service/db/drop/flowable.oracle.drop.entitylink.sql @@ -0,0 +1,4 @@ +drop index ACT_IDX_ENT_LNK_SCOPE; +drop index ACT_IDX_ENT_LNK_SCOPE_DEF; + +drop table ACT_RU_ENTITYLINK; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql new file mode 100644 index 0000000..eb22164 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/create/flowable.oracle.create.eventsubscription.sql @@ -0,0 +1,28 @@ +create table ACT_RU_EVENT_SUBSCR ( + ID_ VARCHAR2(64) not null, + REV_ integer, + EVENT_TYPE_ VARCHAR2(255) not null, + EVENT_NAME_ VARCHAR2(255), + EXECUTION_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + ACTIVITY_ID_ VARCHAR2(64), + CONFIGURATION_ VARCHAR2(255), + CREATED_ TIMESTAMP(6) not null, + PROC_DEF_ID_ VARCHAR2(64), + SUB_SCOPE_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(64), + SCOPE_DEFINITION_ID_ VARCHAR2(64), + SCOPE_DEFINITION_KEY_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(64), + LOCK_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create index ACT_IDX_EVENT_SUBSCR_CONFIG_ on ACT_RU_EVENT_SUBSCR(CONFIGURATION_); +create index ACT_IDX_EVENT_SUBSCR on ACT_RU_EVENT_SUBSCR(EXECUTION_ID_); +create index ACT_IDX_EVENT_SUBSCR_SCOPEREF_ on ACT_RU_EVENT_SUBSCR(SCOPE_ID_, SCOPE_TYPE_); + +insert into ACT_GE_PROPERTY values ('eventsubscription.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql new file mode 100644 index 0000000..c85ad74 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/eventsubscription/service/db/drop/flowable.oracle.drop.eventsubscription.sql @@ -0,0 +1,5 @@ +drop index ACT_IDX_EVENT_SUBSCR_CONFIG_; +drop index ACT_IDX_EVENT_SUBSCR; +drop index ACT_IDX_EVENT_SUBSCR_SCOPEREF_; + +drop table ACT_RU_EVENT_SUBSCR; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql new file mode 100644 index 0000000..2305f0a --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.history.sql @@ -0,0 +1,20 @@ +create table ACT_HI_IDENTITYLINK ( + ID_ VARCHAR2(64), + GROUP_ID_ VARCHAR2(255), + TYPE_ VARCHAR2(255), + USER_ID_ VARCHAR2(255), + TASK_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + PROC_INST_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + primary key (ID_) +); + +create index ACT_IDX_HI_IDENT_LNK_USER on ACT_HI_IDENTITYLINK(USER_ID_); +create index ACT_IDX_HI_IDENT_LNK_SCOPE on ACT_HI_IDENTITYLINK(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_HI_IDENT_LNK_SUB_SCOPE on ACT_HI_IDENTITYLINK(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_HI_IDENT_LNK_SCOPE_DEF on ACT_HI_IDENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql new file mode 100644 index 0000000..e290879 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/create/flowable.oracle.create.identitylink.sql @@ -0,0 +1,24 @@ +create table ACT_RU_IDENTITYLINK ( + ID_ VARCHAR2(64), + REV_ INTEGER, + GROUP_ID_ VARCHAR2(255), + TYPE_ VARCHAR2(255), + USER_ID_ VARCHAR2(255), + TASK_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + primary key (ID_) +); + +create index ACT_IDX_IDENT_LNK_USER on ACT_RU_IDENTITYLINK(USER_ID_); +create index ACT_IDX_IDENT_LNK_GROUP on ACT_RU_IDENTITYLINK(GROUP_ID_); +create index ACT_IDX_IDENT_LNK_SCOPE on ACT_RU_IDENTITYLINK(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_IDENT_LNK_SUB_SCOPE on ACT_RU_IDENTITYLINK(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_IDENT_LNK_SCOPE_DEF on ACT_RU_IDENTITYLINK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +insert into ACT_GE_PROPERTY values ('identitylink.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql new file mode 100644 index 0000000..7cff665 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.history.sql @@ -0,0 +1,6 @@ +drop index ACT_IDX_HI_IDENT_LNK_USER; +drop index ACT_IDX_HI_IDENT_LNK_SCOPE; +drop index ACT_IDX_HI_IDENT_LNK_SUB_SCOPE; +drop index ACT_IDX_HI_IDENT_LNK_SCOPE_DEF; + +drop table ACT_HI_IDENTITYLINK; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql new file mode 100644 index 0000000..485344a --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/identitylink/service/db/drop/flowable.oracle.drop.identitylink.sql @@ -0,0 +1,7 @@ +drop index ACT_IDX_IDENT_LNK_USER; +drop index ACT_IDX_IDENT_LNK_GROUP; +drop index ACT_IDX_IDENT_LNK_SCOPE; +drop index ACT_IDX_IDENT_LNK_SUB_SCOPE; +drop index ACT_IDX_IDENT_LNK_SCOPE_DEF; + +drop table ACT_RU_IDENTITYLINK; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql new file mode 100644 index 0000000..562f45e --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/create/flowable.oracle.create.identity.sql @@ -0,0 +1,108 @@ +create table ACT_ID_PROPERTY ( + NAME_ VARCHAR2(64), + VALUE_ VARCHAR2(300), + REV_ INTEGER, + primary key (NAME_) +); + +insert into ACT_ID_PROPERTY +values ('schema.version', '7.0.1.1', 1); + +create table ACT_ID_BYTEARRAY ( + ID_ VARCHAR2(64), + REV_ INTEGER, + NAME_ VARCHAR2(255), + BYTES_ BLOB, + primary key (ID_) +); + +create table ACT_ID_GROUP ( + ID_ VARCHAR2(64), + REV_ INTEGER, + NAME_ VARCHAR2(255), + TYPE_ VARCHAR2(255), + primary key (ID_) +); + +create table ACT_ID_MEMBERSHIP ( + USER_ID_ VARCHAR2(64), + GROUP_ID_ VARCHAR2(64), + primary key (USER_ID_, GROUP_ID_) +); + +create table ACT_ID_USER ( + ID_ VARCHAR2(64), + REV_ INTEGER, + FIRST_ VARCHAR2(255), + LAST_ VARCHAR2(255), + DISPLAY_NAME_ VARCHAR2(255), + EMAIL_ VARCHAR2(255), + PWD_ VARCHAR2(255), + PICTURE_ID_ VARCHAR2(64), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create table ACT_ID_INFO ( + ID_ VARCHAR2(64), + REV_ INTEGER, + USER_ID_ VARCHAR2(64), + TYPE_ VARCHAR2(64), + KEY_ VARCHAR2(255), + VALUE_ VARCHAR2(255), + PASSWORD_ BLOB, + PARENT_ID_ VARCHAR2(255), + primary key (ID_) +); + +create table ACT_ID_TOKEN ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + TOKEN_VALUE_ VARCHAR2(255), + TOKEN_DATE_ TIMESTAMP(6), + IP_ADDRESS_ VARCHAR2(255), + USER_AGENT_ VARCHAR2(255), + USER_ID_ VARCHAR2(255), + TOKEN_DATA_ VARCHAR2(2000), + primary key (ID_) +); + +create table ACT_ID_PRIV ( + ID_ VARCHAR2(64) not null, + NAME_ VARCHAR2(255) not null, + primary key (ID_) +); + +create table ACT_ID_PRIV_MAPPING ( + ID_ VARCHAR2(64) not null, + PRIV_ID_ VARCHAR2(64) not null, + USER_ID_ VARCHAR2(255), + GROUP_ID_ VARCHAR2(255), + primary key (ID_) +); + +create index ACT_IDX_MEMB_GROUP on ACT_ID_MEMBERSHIP(GROUP_ID_); +alter table ACT_ID_MEMBERSHIP + add constraint ACT_FK_MEMB_GROUP + foreign key (GROUP_ID_) + references ACT_ID_GROUP (ID_); + +create index ACT_IDX_MEMB_USER on ACT_ID_MEMBERSHIP(USER_ID_); +alter table ACT_ID_MEMBERSHIP + add constraint ACT_FK_MEMB_USER + foreign key (USER_ID_) + references ACT_ID_USER (ID_); + +create index ACT_IDX_PRIV_MAPPING on ACT_ID_PRIV_MAPPING(PRIV_ID_); +alter table ACT_ID_PRIV_MAPPING + add constraint ACT_FK_PRIV_MAPPING + foreign key (PRIV_ID_) + references ACT_ID_PRIV (ID_); + +create index ACT_IDX_PRIV_USER on ACT_ID_PRIV_MAPPING(USER_ID_); +create index ACT_IDX_PRIV_GROUP on ACT_ID_PRIV_MAPPING(GROUP_ID_); + +alter table ACT_ID_PRIV + add constraint ACT_UNIQ_PRIV_NAME + unique (NAME_); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql new file mode 100644 index 0000000..5cac418 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/idm/db/drop/flowable.oracle.drop.identity.sql @@ -0,0 +1,22 @@ +alter table ACT_ID_MEMBERSHIP + drop CONSTRAINT ACT_FK_MEMB_GROUP; + +alter table ACT_ID_MEMBERSHIP + drop CONSTRAINT ACT_FK_MEMB_USER; + +alter table ACT_ID_PRIV_MAPPING + drop CONSTRAINT ACT_FK_PRIV_MAPPING; + +drop index ACT_IDX_MEMB_GROUP; +drop index ACT_IDX_MEMB_USER; +drop index ACT_IDX_PRIV_MAPPING; + +drop table ACT_ID_PROPERTY; +drop table ACT_ID_BYTEARRAY; +drop table ACT_ID_INFO; +drop table ACT_ID_MEMBERSHIP; +drop table ACT_ID_GROUP; +drop table ACT_ID_USER; +drop table ACT_ID_TOKEN; +drop table ACT_ID_PRIV; +drop table ACT_ID_PRIV_MAPPING; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql new file mode 100644 index 0000000..8b3e79b --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/create/flowable.oracle.create.job.sql @@ -0,0 +1,261 @@ +create table ACT_RU_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + TYPE_ VARCHAR2(255) NOT NULL, + LOCK_EXP_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), + EXECUTION_ID_ VARCHAR2(64), + PROCESS_INSTANCE_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + ELEMENT_ID_ VARCHAR2(255), + ELEMENT_NAME_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + CORRELATION_ID_ VARCHAR2(255), + RETRIES_ INTEGER, + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + DUEDATE_ TIMESTAMP(6), + REPEAT_ VARCHAR2(255), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_TIMER_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + TYPE_ VARCHAR2(255) NOT NULL, + LOCK_EXP_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), + EXECUTION_ID_ VARCHAR2(64), + PROCESS_INSTANCE_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + ELEMENT_ID_ VARCHAR2(255), + ELEMENT_NAME_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + CORRELATION_ID_ VARCHAR2(255), + RETRIES_ INTEGER, + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + DUEDATE_ TIMESTAMP(6), + REPEAT_ VARCHAR2(255), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_SUSPENDED_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + TYPE_ VARCHAR2(255) NOT NULL, + EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), + EXECUTION_ID_ VARCHAR2(64), + PROCESS_INSTANCE_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + ELEMENT_ID_ VARCHAR2(255), + ELEMENT_NAME_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + CORRELATION_ID_ VARCHAR2(255), + RETRIES_ INTEGER, + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + DUEDATE_ TIMESTAMP(6), + REPEAT_ VARCHAR2(255), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_DEADLETTER_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + TYPE_ VARCHAR2(255) NOT NULL, + EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), + EXECUTION_ID_ VARCHAR2(64), + PROCESS_INSTANCE_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + ELEMENT_ID_ VARCHAR2(255), + ELEMENT_NAME_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + CORRELATION_ID_ VARCHAR2(255), + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + DUEDATE_ TIMESTAMP(6), + REPEAT_ VARCHAR2(255), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_HISTORY_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + LOCK_EXP_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + RETRIES_ INTEGER, + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + ADV_HANDLER_CFG_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + SCOPE_TYPE_ VARCHAR2(255), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create table ACT_RU_EXTERNAL_JOB ( + ID_ VARCHAR2(64) NOT NULL, + REV_ INTEGER, + CATEGORY_ VARCHAR2(255), + TYPE_ VARCHAR2(255) NOT NULL, + LOCK_EXP_TIME_ TIMESTAMP(6), + LOCK_OWNER_ VARCHAR2(255), + EXCLUSIVE_ NUMBER(1) CHECK (EXCLUSIVE_ IN (1,0)), + EXECUTION_ID_ VARCHAR2(64), + PROCESS_INSTANCE_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + ELEMENT_ID_ VARCHAR2(255), + ELEMENT_NAME_ VARCHAR2(255), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + CORRELATION_ID_ VARCHAR2(255), + RETRIES_ INTEGER, + EXCEPTION_STACK_ID_ VARCHAR2(64), + EXCEPTION_MSG_ VARCHAR2(2000), + DUEDATE_ TIMESTAMP(6), + REPEAT_ VARCHAR2(255), + HANDLER_TYPE_ VARCHAR2(255), + HANDLER_CFG_ VARCHAR2(2000), + CUSTOM_VALUES_ID_ VARCHAR2(64), + CREATE_TIME_ TIMESTAMP(6), + TENANT_ID_ VARCHAR2(255) DEFAULT '', + primary key (ID_) +); + +create index ACT_IDX_JOB_EXCEPTION on ACT_RU_JOB(EXCEPTION_STACK_ID_); +create index ACT_IDX_JOB_CUSTOM_VAL_ID on ACT_RU_JOB(CUSTOM_VALUES_ID_); +create index ACT_IDX_JOB_CORRELATION_ID on ACT_RU_JOB(CORRELATION_ID_); + +create index ACT_IDX_TJOB_EXCEPTION on ACT_RU_TIMER_JOB(EXCEPTION_STACK_ID_); +create index ACT_IDX_TJOB_CUSTOM_VAL_ID on ACT_RU_TIMER_JOB(CUSTOM_VALUES_ID_); +create index ACT_IDX_TJOB_CORRELATION_ID on ACT_RU_TIMER_JOB(CORRELATION_ID_); +create index ACT_IDX_TJOB_DUEDATE on ACT_RU_TIMER_JOB(DUEDATE_); + +create index ACT_IDX_SJOB_EXCEPTION on ACT_RU_SUSPENDED_JOB(EXCEPTION_STACK_ID_); +create index ACT_IDX_SJOB_CUSTOM_VAL_ID on ACT_RU_SUSPENDED_JOB(CUSTOM_VALUES_ID_); +create index ACT_IDX_SJOB_CORRELATION_ID on ACT_RU_SUSPENDED_JOB(CORRELATION_ID_); + +create index ACT_IDX_DJOB_EXCEPTION on ACT_RU_DEADLETTER_JOB(EXCEPTION_STACK_ID_); +create index ACT_IDX_DJOB_CUSTOM_VAL_ID on ACT_RU_DEADLETTER_JOB(CUSTOM_VALUES_ID_); +create index ACT_IDX_DJOB_CORRELATION_ID on ACT_RU_DEADLETTER_JOB(CORRELATION_ID_); + +create index ACT_IDX_EJOB_EXCEPTION on ACT_RU_EXTERNAL_JOB(EXCEPTION_STACK_ID_); +create index ACT_IDX_EJOB_CUSTOM_VAL_ID on ACT_RU_EXTERNAL_JOB(CUSTOM_VALUES_ID_); +create index ACT_IDX_EJOB_CORRELATION_ID on ACT_RU_EXTERNAL_JOB(CORRELATION_ID_); + +alter table ACT_RU_JOB + add constraint ACT_FK_JOB_EXCEPTION + foreign key (EXCEPTION_STACK_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_JOB + add constraint ACT_FK_JOB_CUSTOM_VAL + foreign key (CUSTOM_VALUES_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_TIMER_JOB + add constraint ACT_FK_TJOB_EXCEPTION + foreign key (EXCEPTION_STACK_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_TIMER_JOB + add constraint ACT_FK_TJOB_CUSTOM_VAL + foreign key (CUSTOM_VALUES_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_SUSPENDED_JOB + add constraint ACT_FK_SJOB_EXCEPTION + foreign key (EXCEPTION_STACK_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_SUSPENDED_JOB + add constraint ACT_FK_SJOB_CUSTOM_VAL + foreign key (CUSTOM_VALUES_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_DEADLETTER_JOB + add constraint ACT_FK_DJOB_EXCEPTION + foreign key (EXCEPTION_STACK_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_DEADLETTER_JOB + add constraint ACT_FK_DJOB_CUSTOM_VAL + foreign key (CUSTOM_VALUES_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_EXTERNAL_JOB + add constraint ACT_FK_EJOB_EXCEPTION + foreign key (EXCEPTION_STACK_ID_) + references ACT_GE_BYTEARRAY (ID_); + +alter table ACT_RU_EXTERNAL_JOB + add constraint ACT_FK_EJOB_CUSTOM_VAL + foreign key (CUSTOM_VALUES_ID_) + references ACT_GE_BYTEARRAY (ID_); + +create index ACT_IDX_JOB_SCOPE on ACT_RU_JOB(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_JOB_SUB_SCOPE on ACT_RU_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_JOB_SCOPE_DEF on ACT_RU_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +create index ACT_IDX_TJOB_SCOPE on ACT_RU_TIMER_JOB(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_TJOB_SUB_SCOPE on ACT_RU_TIMER_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_TJOB_SCOPE_DEF on ACT_RU_TIMER_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +create index ACT_IDX_SJOB_SCOPE on ACT_RU_SUSPENDED_JOB(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_SJOB_SUB_SCOPE on ACT_RU_SUSPENDED_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_SJOB_SCOPE_DEF on ACT_RU_SUSPENDED_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +create index ACT_IDX_DJOB_SCOPE on ACT_RU_DEADLETTER_JOB(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_DJOB_SUB_SCOPE on ACT_RU_DEADLETTER_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_DJOB_SCOPE_DEF on ACT_RU_DEADLETTER_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +create index ACT_IDX_EJOB_SCOPE on ACT_RU_EXTERNAL_JOB(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_EJOB_SUB_SCOPE on ACT_RU_EXTERNAL_JOB(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_EJOB_SCOPE_DEF on ACT_RU_EXTERNAL_JOB(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +insert into ACT_GE_PROPERTY values ('job.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql new file mode 100644 index 0000000..a219e97 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/job/service/db/drop/flowable.oracle.drop.job.sql @@ -0,0 +1,74 @@ +drop index ACT_IDX_JOB_SCOPE; +drop index ACT_IDX_JOB_SUB_SCOPE; +drop index ACT_IDX_JOB_SCOPE_DEF; +drop index ACT_IDX_TJOB_SCOPE; +drop index ACT_IDX_TJOB_SUB_SCOPE; +drop index ACT_IDX_TJOB_SCOPE_DEF; +drop index ACT_IDX_SJOB_SCOPE; +drop index ACT_IDX_SJOB_SUB_SCOPE; +drop index ACT_IDX_SJOB_SCOPE_DEF; +drop index ACT_IDX_DJOB_SCOPE; +drop index ACT_IDX_DJOB_SUB_SCOPE; +drop index ACT_IDX_DJOB_SCOPE_DEF; +drop index ACT_IDX_EJOB_SCOPE; +drop index ACT_IDX_EJOB_SUB_SCOPE; +drop index ACT_IDX_EJOB_SCOPE_DEF; + +drop index ACT_IDX_JOB_EXCEPTION; +drop index ACT_IDX_JOB_CUSTOM_VAL_ID; +drop index ACT_IDX_JOB_CORRELATION_ID; + +drop index ACT_IDX_TJOB_EXCEPTION; +drop index ACT_IDX_TJOB_CUSTOM_VAL_ID; +drop index ACT_IDX_TJOB_CORRELATION_ID; +drop index ACT_IDX_TJOB_DUEDATE; + +drop index ACT_IDX_SJOB_EXCEPTION; +drop index ACT_IDX_SJOB_CUSTOM_VAL_ID; +drop index ACT_IDX_SJOB_CORRELATION_ID; + +drop index ACT_IDX_DJOB_EXCEPTION; +drop index ACT_IDX_DJOB_CUSTOM_VAL_ID; +drop index ACT_IDX_DJOB_CORRELATION_ID; + +drop index ACT_IDX_EJOB_EXCEPTION; +drop index ACT_IDX_EJOB_CUSTOM_VAL_ID; +drop index ACT_IDX_EJOB_CORRELATION_ID; + +alter table ACT_RU_JOB + drop CONSTRAINT ACT_FK_JOB_EXCEPTION; + +alter table ACT_RU_JOB + drop CONSTRAINT ACT_FK_JOB_CUSTOM_VAL; + +alter table ACT_RU_TIMER_JOB + drop CONSTRAINT ACT_FK_TJOB_EXCEPTION; + +alter table ACT_RU_TIMER_JOB + drop CONSTRAINT ACT_FK_TJOB_CUSTOM_VAL; + +alter table ACT_RU_SUSPENDED_JOB + drop CONSTRAINT ACT_FK_SJOB_EXCEPTION; + +alter table ACT_RU_SUSPENDED_JOB + drop CONSTRAINT ACT_FK_SJOB_CUSTOM_VAL; + +alter table ACT_RU_DEADLETTER_JOB + drop CONSTRAINT ACT_FK_DJOB_EXCEPTION; + +alter table ACT_RU_DEADLETTER_JOB + drop CONSTRAINT ACT_FK_DJOB_CUSTOM_VAL; + +alter table ACT_RU_EXTERNAL_JOB + drop CONSTRAINT ACT_FK_DJOB_EXCEPTION; + +alter table ACT_RU_EXTERNAL_JOB + drop CONSTRAINT ACT_FK_DJOB_CUSTOM_VAL; + +drop table ACT_RU_JOB; +drop table ACT_RU_TIMER_JOB; +drop table ACT_RU_SUSPENDED_JOB; +drop table ACT_RU_DEADLETTER_JOB; +drop table ACT_RU_HISTORY_JOB; +drop table ACT_RU_EXTERNAL_JOB; + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql new file mode 100644 index 0000000..1651c0c --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.history.sql @@ -0,0 +1,64 @@ +create table ACT_HI_TASKINST ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER default 1, + PROC_DEF_ID_ VARCHAR2(64), + TASK_DEF_ID_ VARCHAR2(64), + TASK_DEF_KEY_ VARCHAR2(255), + PROC_INST_ID_ VARCHAR2(64), + EXECUTION_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), + PARENT_TASK_ID_ VARCHAR2(64), + STATE_ VARCHAR2(255), + NAME_ VARCHAR2(255), + DESCRIPTION_ VARCHAR2(2000), + OWNER_ VARCHAR2(255), + ASSIGNEE_ VARCHAR2(255), + START_TIME_ TIMESTAMP(6) not null, + IN_PROGRESS_TIME_ TIMESTAMP(6), + IN_PROGRESS_STARTED_BY_ VARCHAR2(255), + CLAIM_TIME_ TIMESTAMP(6), + CLAIMED_BY_ VARCHAR2(255), + SUSPENDED_TIME_ TIMESTAMP(6), + SUSPENDED_BY_ VARCHAR2(255), + END_TIME_ TIMESTAMP(6), + COMPLETED_BY_ VARCHAR2(255), + DURATION_ NUMBER(19,0), + DELETE_REASON_ VARCHAR2(2000), + PRIORITY_ INTEGER, + IN_PROGRESS_DUE_DATE_ TIMESTAMP(6), + DUE_DATE_ TIMESTAMP(6), + FORM_KEY_ VARCHAR2(255), + CATEGORY_ VARCHAR2(255), + TENANT_ID_ VARCHAR2(255) default '', + LAST_UPDATED_TIME_ TIMESTAMP(6), + primary key (ID_) +); + +create table ACT_HI_TSK_LOG ( + ID_ NUMBER(19), + TYPE_ VARCHAR2(64), + TASK_ID_ VARCHAR2(64) not null, + TIME_STAMP_ TIMESTAMP(6) not null, + USER_ID_ VARCHAR2(255), + DATA_ VARCHAR2(2000), + EXECUTION_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + TENANT_ID_ VARCHAR2(255) default '', + primary key (ID_) +); + +create sequence act_hi_task_evt_log_seq start with 1 increment by 1; + +create index ACT_IDX_HI_TASK_SCOPE on ACT_HI_TASKINST(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_HI_TASK_SUB_SCOPE on ACT_HI_TASKINST(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_HI_TASK_SCOPE_DEF on ACT_HI_TASKINST(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql new file mode 100644 index 0000000..9430a1c --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/create/flowable.oracle.create.task.sql @@ -0,0 +1,48 @@ +create table ACT_RU_TASK ( + ID_ VARCHAR2(64), + REV_ INTEGER, + EXECUTION_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + PROC_DEF_ID_ VARCHAR2(64), + TASK_DEF_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + SCOPE_DEFINITION_ID_ VARCHAR2(255), + PROPAGATED_STAGE_INST_ID_ VARCHAR2(255), + STATE_ VARCHAR2(255), + NAME_ VARCHAR2(255), + PARENT_TASK_ID_ VARCHAR2(64), + DESCRIPTION_ VARCHAR2(2000), + TASK_DEF_KEY_ VARCHAR2(255), + OWNER_ VARCHAR2(255), + ASSIGNEE_ VARCHAR2(255), + DELEGATION_ VARCHAR2(64), + PRIORITY_ INTEGER, + CREATE_TIME_ TIMESTAMP(6), + IN_PROGRESS_TIME_ TIMESTAMP(6), + IN_PROGRESS_STARTED_BY_ VARCHAR2(255), + CLAIM_TIME_ TIMESTAMP(6), + CLAIMED_BY_ VARCHAR2(255), + SUSPENDED_TIME_ TIMESTAMP(6), + SUSPENDED_BY_ VARCHAR2(255), + IN_PROGRESS_DUE_DATE_ TIMESTAMP(6), + DUE_DATE_ TIMESTAMP(6), + CATEGORY_ VARCHAR2(255), + SUSPENSION_STATE_ INTEGER, + TENANT_ID_ VARCHAR2(255) DEFAULT '', + FORM_KEY_ VARCHAR2(255), + IS_COUNT_ENABLED_ NUMBER(1) CHECK (IS_COUNT_ENABLED_ IN (1,0)), + VAR_COUNT_ INTEGER, + ID_LINK_COUNT_ INTEGER, + SUB_TASK_COUNT_ INTEGER, + primary key (ID_) +); + +create index ACT_IDX_TASK_CREATE on ACT_RU_TASK(CREATE_TIME_); +create index ACT_IDX_TASK_SCOPE on ACT_RU_TASK(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_TASK_SUB_SCOPE on ACT_RU_TASK(SUB_SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_TASK_SCOPE_DEF on ACT_RU_TASK(SCOPE_DEFINITION_ID_, SCOPE_TYPE_); + +insert into ACT_GE_PROPERTY values ('task.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql new file mode 100644 index 0000000..c5ce7bb --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.history.sql @@ -0,0 +1,8 @@ +drop index ACT_IDX_HI_TASK_SCOPE; +drop index ACT_IDX_HI_TASK_SUB_SCOPE; +drop index ACT_IDX_HI_TASK_SCOPE_DEF; + +drop sequence act_hi_task_evt_log_seq; + +drop table ACT_HI_TASKINST; +drop table ACT_HI_TSK_LOG; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql new file mode 100644 index 0000000..9ecd1e9 --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/task/service/db/drop/flowable.oracle.drop.task.sql @@ -0,0 +1,6 @@ +drop index ACT_IDX_TASK_CREATE; +drop index ACT_IDX_TASK_SCOPE; +drop index ACT_IDX_TASK_SUB_SCOPE; +drop index ACT_IDX_TASK_SCOPE_DEF; + +drop table ACT_RU_TASK; diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql new file mode 100644 index 0000000..3d9b50f --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.history.sql @@ -0,0 +1,26 @@ +create table ACT_HI_VARINST ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER default 1, + PROC_INST_ID_ VARCHAR2(64), + EXECUTION_ID_ VARCHAR2(64), + TASK_ID_ VARCHAR2(64), + NAME_ VARCHAR2(255) not null, + VAR_TYPE_ VARCHAR2(100), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + BYTEARRAY_ID_ VARCHAR2(64), + DOUBLE_ NUMBER(38,10), + LONG_ NUMBER(19,0), + TEXT_ VARCHAR2(2000), + TEXT2_ VARCHAR2(2000), + META_INFO_ VARCHAR2(2000), + CREATE_TIME_ TIMESTAMP(6), + LAST_UPDATED_TIME_ TIMESTAMP(6), + primary key (ID_) +); + +create index ACT_IDX_HI_PROCVAR_NAME_TYPE on ACT_HI_VARINST(NAME_, VAR_TYPE_); +create index ACT_IDX_HI_VAR_SCOPE_ID_TYPE on ACT_HI_VARINST(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_HI_VAR_SUB_ID_TYPE on ACT_HI_VARINST(SUB_SCOPE_ID_, SCOPE_TYPE_); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql new file mode 100644 index 0000000..7c02f7f --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/create/flowable.oracle.create.variable.sql @@ -0,0 +1,31 @@ +create table ACT_RU_VARIABLE ( + ID_ VARCHAR2(64) not null, + REV_ INTEGER, + TYPE_ VARCHAR2(255) not null, + NAME_ VARCHAR2(255) not null, + EXECUTION_ID_ VARCHAR2(64), + PROC_INST_ID_ VARCHAR2(64), + TASK_ID_ VARCHAR2(64), + SCOPE_ID_ VARCHAR2(255), + SUB_SCOPE_ID_ VARCHAR2(255), + SCOPE_TYPE_ VARCHAR2(255), + BYTEARRAY_ID_ VARCHAR2(64), + DOUBLE_ NUMBER(38,10), + LONG_ NUMBER(19,0), + TEXT_ VARCHAR2(2000), + TEXT2_ VARCHAR2(2000), + META_INFO_ VARCHAR2(2000), + primary key (ID_) +); + +create index ACT_IDX_RU_VAR_SCOPE_ID_TYPE on ACT_RU_VARIABLE(SCOPE_ID_, SCOPE_TYPE_); +create index ACT_IDX_RU_VAR_SUB_ID_TYPE on ACT_RU_VARIABLE(SUB_SCOPE_ID_, SCOPE_TYPE_); + +create index ACT_IDX_VAR_BYTEARRAY on ACT_RU_VARIABLE(BYTEARRAY_ID_); +alter table ACT_RU_VARIABLE + add constraint ACT_FK_VAR_BYTEARRAY + foreign key (BYTEARRAY_ID_) + references ACT_GE_BYTEARRAY (ID_); + +insert into ACT_GE_PROPERTY values ('variable.schema.version', '7.0.1.1', 1); + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql new file mode 100644 index 0000000..efcc68d --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.history.sql @@ -0,0 +1,6 @@ +drop index ACT_IDX_HI_PROCVAR_NAME_TYPE; +drop index ACT_IDX_HI_VAR_SCOPE_ID_TYPE; +drop index ACT_IDX_HI_VAR_SUB_ID_TYPE; + +drop table ACT_HI_VARINST; + diff --git a/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql new file mode 100644 index 0000000..0d0a95f --- /dev/null +++ b/zt-module-bpm/zt-module-bpm-server/src/main/resources/org/flowable/variable/service/db/drop/flowable.oracle.drop.variable.sql @@ -0,0 +1,9 @@ +drop index ACT_IDX_VAR_BYTEARRAY; +drop index ACT_IDX_RU_VAR_SCOPE_ID_TYPE; +drop index ACT_IDX_RU_VAR_SUB_ID_TYPE; + +alter table ACT_RU_VARIABLE + drop CONSTRAINT ACT_FK_VAR_BYTEARRAY; + +drop table ACT_RU_VARIABLE; +