From aff4a1f7cce2e076a968c7a553947f767ad404a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A7=9C=E7=8E=89=E7=90=A6?= <7507756+jiang_yuqi@user.noreply.gitee.com> Date: Mon, 19 Aug 2024 00:41:01 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96flowable?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + pom.xml | 9 +- .../yanzhu/system/api/RemoteFlowService.java | 51 + .../yanzhu/system/api/RemoteRoleService.java | 32 + .../yanzhu/system/api/RemoteUserService.java | 24 + .../factory/RemoteFlowFallbackFactory.java | 47 + .../factory/RemoteRoleFallbackFactory.java | 30 + .../factory/RemoteUserFallbackFactory.java | 15 + .../yanzhu/system/api/model/LoginUser.java | 26 + ...ot.autoconfigure.AutoConfiguration.imports | 2 + .../yanzhu/auth/service/SysLoginService.java | 11 + .../common/core/enums/FlowableEnums.java | 34 + .../core/exception/CustomException.java | 43 + .../yanzhu/common/core/utils/StringUtils.java | 103 +- .../flowable/domain/FlowCommentDto.java | 25 + .../flowable/domain/FlowFromFieldDTO.java | 15 + .../yanzhu/flowable/domain/FlowNextDto.java | 19 + .../flowable/domain/FlowProcDefDto.java | 70 + .../yanzhu/flowable/domain/FlowSaveXmlVo.java | 43 + .../yanzhu/flowable/domain/FlowTaskDto.java | 103 ++ .../flowable/domain/FlowTaskEntity.java | 370 +++++ .../yanzhu/flowable/domain/FlowViewerDto.java | 23 + .../yanzhu/flowable/domain/SysDeployForm.java | 64 + .../yanzhu/flowable/domain/SysExpression.java | 83 ++ .../com/yanzhu/flowable/domain/SysForm.java | 70 + .../yanzhu/flowable/domain/SysListener.java | 126 ++ .../yanzhu/flowable/domain/SysTaskForm.java | 65 + .../yanzhu/flowable/domain/vo/FlowDeptVo.java | 47 + .../flowable/domain/vo/FlowQueryVo.java | 49 + .../yanzhu/flowable/domain/vo/FlowTaskVo.java | 59 + .../flowable/domain/vo/ProcKeyRole.java | 50 + .../flowable/domain/vo/ReturnTaskNodeVo.java | 23 + .../flowable/domain/vo/StartTaskVO.java | 63 + .../mapper/FlowBusinessKeyMapper.java | 112 ++ .../flowable/mapper/FlowDeployMapper.java | 76 + .../flowable/mapper/SysDeployFormMapper.java | 70 + .../flowable/mapper/SysExpressionMapper.java | 62 + .../yanzhu/flowable/mapper/SysFormMapper.java | 62 + .../flowable/mapper/SysListenerMapper.java | 62 + .../flowable/mapper/SysTaskFormMapper.java | 62 + .../main/java/com/yanzhu/mapper/domain/init | 0 .../mapper/flowable/FlowBusinessKeyMapper.xml | 515 +++++++ .../mapper/flowable/FlowDeployMapper.xml | 125 ++ .../mapper/flowable/SysDeployFormMapper.xml | 66 + .../mapper/flowable/SysExpressionMapper.xml | 86 ++ .../mapper/flowable/SysFormMapper.xml | 82 ++ .../mapper/flowable/SysListenerMapper.xml | 115 ++ .../mapper/flowable/SysTaskFormMapper.xml | 61 + .../common/security/utils/SecurityUtils.java | 10 + yanzhu-modules/pom.xml | 3 +- yanzhu-modules/yanzhu-flowable/pom.xml | 182 +++ .../flowable/YanZhuFlowableApplication.java | 34 + .../common/constant/ProcessConstants.java | 80 + .../flowable/common/enums/FlowComment.java | 43 + .../flowable/config/FlowableConfig.java | 32 + .../config/FlowableGlobalListenerConfig.java | 41 + .../controller/FlowBusinessKeyController.java | 146 ++ .../controller/FlowDefinitionController.java | 199 +++ .../controller/FlowInstanceController.java | 63 + .../controller/FlowTaskController.java | 268 ++++ .../controller/SysFormController.java | 112 ++ .../flowable/factory/FlowServiceFactory.java | 41 + .../flow/CustomProcessDiagramCanvas.java | 370 +++++ .../flow/CustomProcessDiagramGenerator.java | 404 +++++ .../flowable/flow/FindNextNodeUtil.java | 257 ++++ .../yanzhu/flowable/flow/FlowableUtils.java | 589 ++++++++ .../com/yanzhu/flowable/flow/ModelUtils.java | 372 +++++ .../listener/GlobalEventListener.java | 42 + .../service/IFlowBusinessKeyService.java | 56 + .../service/IFlowDefinitionService.java | 81 + .../service/IFlowInstanceService.java | 54 + .../flowable/service/IFlowTaskService.java | 208 +++ .../service/ISysDeployFormService.java | 70 + .../service/ISysExpressionService.java | 62 + .../flowable/service/ISysFormService.java | 61 + .../flowable/service/ISysTaskFormService.java | 62 + .../impl/FlowBusinessKeyServiceImpl.java | 102 ++ .../impl/FlowDefinitionServiceImpl.java | 279 ++++ .../service/impl/FlowInstanceServiceImpl.java | 117 ++ .../service/impl/FlowTaskServiceImpl.java | 1307 +++++++++++++++++ .../impl/SysDeployFormServiceImpl.java | 112 ++ .../impl/SysExpressionServiceImpl.java | 97 ++ .../service/impl/SysFormServiceImpl.java | 97 ++ .../service/impl/SysTaskFormServiceImpl.java | 94 ++ .../src/main/resources/banner.txt | 10 + .../src/main/resources/bootstrap.yml | 25 + .../src/main/resources/logback.xml | 74 + yanzhu-modules/yanzhu-manage/pom.xml | 2 +- .../manage/YanZhuManageApplication.java | 2 +- .../system/controller/SysRoleController.java | 39 +- .../system/controller/SysUserController.java | 32 + .../yanzhu/system/mapper/SysUserMapper.java | 18 + .../system/service/ISysUserService.java | 18 + .../service/impl/SysUserServiceImpl.java | 23 + .../resources/mapper/system/SysUserMapper.xml | 11 +- 95 files changed, 9733 insertions(+), 19 deletions(-) create mode 100644 yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteFlowService.java create mode 100644 yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteRoleService.java create mode 100644 yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteFlowFallbackFactory.java create mode 100644 yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteRoleFallbackFactory.java create mode 100644 yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/enums/FlowableEnums.java create mode 100644 yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/exception/CustomException.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowCommentDto.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowFromFieldDTO.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowNextDto.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowProcDefDto.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowSaveXmlVo.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskDto.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskEntity.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowViewerDto.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysDeployForm.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysExpression.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysForm.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysListener.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysTaskForm.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowDeptVo.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowQueryVo.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowTaskVo.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ProcKeyRole.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ReturnTaskNodeVo.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java delete mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/mapper/domain/init create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml create mode 100644 yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml create mode 100644 yanzhu-modules/yanzhu-flowable/pom.xml create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/SysDeployFormServiceImpl.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/SysExpressionServiceImpl.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/SysFormServiceImpl.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/SysTaskFormServiceImpl.java create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/resources/banner.txt create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/resources/bootstrap.yml create mode 100644 yanzhu-modules/yanzhu-flowable/src/main/resources/logback.xml diff --git a/README.md b/README.md index 6a4ba87c..77bf884c 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ com.yanzhu │ └── yanzhu-gen // 代码生成 [9202] │ └── yanzhu-job // 定时任务 [9203] │ └── yanzhu-file // 文件服务 [9300] +│ └── yanzhu-flowable // 工作流服务 [9205] ├── yanzhu-monitor // 监控中心 [9100] ├── yanzhu-rocketMQ // 消息队列 [9207] ├── yanzhu-ui-vue3 // 前端框架 [80] diff --git a/pom.xml b/pom.xml index 015e8c07..15a4b486 100644 --- a/pom.xml +++ b/pom.xml @@ -34,6 +34,7 @@ 0.9.1 8.2.2 4.1.2 + 6.7.2 2.14.2 @@ -68,6 +69,12 @@ import + + org.flowable + flowable-spring-boot-starter + ${flowable.version} + + com.github.tobato @@ -217,8 +224,6 @@ yanzhu-modules yanzhu-monitor yanzhu-rocketmq - yanzhu-modules/yanzhu-wechat - yanzhu-modules/yanzhu-manage pom diff --git a/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteFlowService.java b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteFlowService.java new file mode 100644 index 00000000..255a555e --- /dev/null +++ b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteFlowService.java @@ -0,0 +1,51 @@ +package com.yanzhu.system.api; + +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.constant.ServiceNameConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.system.api.factory.RemoteLogFallbackFactory; +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.RequestHeader; + +import java.util.Map; + +/** + * 工作流服务 + * + * @author JiangYuQi + */ +@FeignClient(contextId = "remoteFlowService", value = ServiceNameConstants.MANAGE_SERVICE, fallbackFactory = RemoteLogFallbackFactory.class) +public interface RemoteFlowService { + + /** + * 保存分包单位信息 + * + * @param data 表单信息 + * @param source 请求来源 + * @return 结果 + */ + @PostMapping("/proProjectInfoSubdepts/add") + public R addSubdeptsEntity(@RequestBody Map data, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + /** + * 保存分包单位班组信息 + * + * @param data 表单信息 + * @param source 请求来源 + * @return 结果 + */ + @PostMapping("/proProjectInfoSubdeptsGroup/add") + public R addSubdeptsGroupEntity(@RequestBody Map data, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + /** + * 保存分包单位班组用户信息 + * + * @param data 表单信息 + * @param source 请求来源 + * @return 结果 + */ + @PostMapping("/proProjectInfoSubdeptsUsers/add") + public R addSubdeptsUsersEntity(@RequestBody Map data, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); +} diff --git a/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteRoleService.java b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteRoleService.java new file mode 100644 index 00000000..b603fa52 --- /dev/null +++ b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteRoleService.java @@ -0,0 +1,32 @@ +package com.yanzhu.system.api; + +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.constant.ServiceNameConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.factory.RemoteUserFallbackFactory; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestHeader; + +/** + * 用户服务 + * + * @author ruoyi + */ +@FeignClient(contextId = "remoteRoleService", value = ServiceNameConstants.SYSTEM_SERVICE, fallbackFactory = RemoteUserFallbackFactory.class) +public interface RemoteRoleService +{ + + /** + * 通过用户主键查询角色信息 + * + * @param roleId 角色主键 + * @param source 请求来源 + * @return 结果 + */ + @GetMapping("/role/info/{roleId}") + public R getRoleInfo(@PathVariable("roleId") Long roleId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + +} diff --git a/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteUserService.java b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteUserService.java index e57c9a85..7e73114b 100644 --- a/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteUserService.java +++ b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/RemoteUserService.java @@ -13,6 +13,9 @@ import com.yanzhu.common.core.domain.R; import com.yanzhu.system.api.domain.SysUser; import com.yanzhu.system.api.factory.RemoteUserFallbackFactory; +import java.util.List; +import java.util.Map; + /** * 用户服务 * @@ -31,6 +34,16 @@ public interface RemoteUserService @GetMapping("/user/info/{username}") public R getUserInfo(@PathVariable("username") String username, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + /** + * 通过用户主键查询用户信息 + * + * @param userId 用户主键 + * @param source 请求来源 + * @return 结果 + */ + @GetMapping("/user/sysUser/{userId}") + public R getSysUserInfo(@PathVariable("userId") Long userId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + /** * 注册用户信息 * @@ -40,4 +53,15 @@ public interface RemoteUserService */ @PostMapping("/user/register") public R registerUserInfo(@RequestBody SysUser sysUser, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + + /** + * 获取当前用户入场项目信息 + * + * @param userId 用户主键 + * @param source 请求来源 + * @return 结果 + */ + @GetMapping("/user/projects/{userId}") + public R>> getProjects(@PathVariable("userId") Long userId, @RequestHeader(SecurityConstants.FROM_SOURCE) String source); + } diff --git a/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteFlowFallbackFactory.java b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteFlowFallbackFactory.java new file mode 100644 index 00000000..6d039065 --- /dev/null +++ b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteFlowFallbackFactory.java @@ -0,0 +1,47 @@ +package com.yanzhu.system.api.factory; + +import com.yanzhu.common.core.domain.R; +import com.yanzhu.system.api.RemoteFlowService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +import java.util.Map; + +/** + * 用户服务降级处理 + * + * @author ruoyi + */ +@Component +public class RemoteFlowFallbackFactory implements FallbackFactory +{ + private static final Logger log = LoggerFactory.getLogger(RemoteFlowFallbackFactory.class); + + @Override + public RemoteFlowService create(Throwable throwable) + { + log.error("业务服务调用失败:{}", throwable.getMessage()); + return new RemoteFlowService() + { + @Override + public R addSubdeptsEntity(Map data, String source) + { + return R.fail("保存分包单位信息失败:" + throwable.getMessage()); + } + + @Override + public R addSubdeptsGroupEntity(Map data, String source) + { + return R.fail("保存分包单位班组信息失败:" + throwable.getMessage()); + } + + @Override + public R addSubdeptsUsersEntity(Map data, String source) + { + return R.fail("保存分包单位班组用户信息失败:" + throwable.getMessage()); + } + }; + } +} diff --git a/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteRoleFallbackFactory.java b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteRoleFallbackFactory.java new file mode 100644 index 00000000..74728838 --- /dev/null +++ b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteRoleFallbackFactory.java @@ -0,0 +1,30 @@ +package com.yanzhu.system.api.factory; + +import com.yanzhu.common.core.domain.R; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.domain.SysRole; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cloud.openfeign.FallbackFactory; +import org.springframework.stereotype.Component; + +/** + * 角色服务降级处理 + * + * @author ruoyi + */ +@Component +public class RemoteRoleFallbackFactory implements FallbackFactory { + private static final Logger log = LoggerFactory.getLogger(RemoteRoleFallbackFactory.class); + + @Override + public RemoteRoleService create(Throwable throwable) { + log.error("用户服务调用失败:{}", throwable.getMessage()); + return new RemoteRoleService() { + @Override + public R getRoleInfo(Long roleId, String source) { + return R.fail("获取角色失败:" + throwable.getMessage()); + } + }; + } +} diff --git a/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteUserFallbackFactory.java b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteUserFallbackFactory.java index bca69663..698b4453 100644 --- a/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteUserFallbackFactory.java +++ b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/factory/RemoteUserFallbackFactory.java @@ -9,6 +9,9 @@ import org.springframework.stereotype.Component; import com.yanzhu.common.core.domain.R; import com.yanzhu.system.api.domain.SysUser; +import java.util.List; +import java.util.Map; + /** * 用户服务降级处理 * @@ -31,11 +34,23 @@ public class RemoteUserFallbackFactory implements FallbackFactory getSysUserInfo(Long userId, String source) + { + return R.fail("获取用户失败:" + throwable.getMessage()); + } + @Override public R registerUserInfo(SysUser sysUser, String source) { return R.fail("注册用户失败:" + throwable.getMessage()); } + + @Override + public R>> getProjects(Long userId, String source) + { + return R.fail("获取用户项目信息失败:" + throwable.getMessage()); + } }; } } diff --git a/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/model/LoginUser.java b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/model/LoginUser.java index 30e41ae9..578e3019 100644 --- a/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/model/LoginUser.java +++ b/yanzhu-api/yanzhu-api-system/src/main/java/com/yanzhu/system/api/model/LoginUser.java @@ -58,6 +58,16 @@ public class LoginUser implements Serializable */ private SysUser sysUser; + /** + * 项目信息 + */ + private Long projectId; + + /** + * 项目单位信息 + */ + private Long projectDeptId; + public String getToken() { return token; @@ -147,4 +157,20 @@ public class LoginUser implements Serializable { this.sysUser = sysUser; } + + public Long getProjectId() { + return projectId; + } + + public void setProjectId(Long projectId) { + this.projectId = projectId; + } + + public Long getProjectDeptId() { + return projectDeptId; + } + + public void setProjectDeptId(Long projectDeptId) { + this.projectDeptId = projectDeptId; + } } diff --git a/yanzhu-api/yanzhu-api-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/yanzhu-api/yanzhu-api-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index 30145fdc..64362674 100644 --- a/yanzhu-api/yanzhu-api-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/yanzhu-api/yanzhu-api-system/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -1,3 +1,5 @@ com.yanzhu.system.api.factory.RemoteUserFallbackFactory com.yanzhu.system.api.factory.RemoteLogFallbackFactory com.yanzhu.system.api.factory.RemoteFileFallbackFactory +com.yanzhu.system.api.factory.RemoteFlowFallbackFactory +com.yanzhu.system.api.factory.RemoteRoleFallbackFactory \ No newline at end of file diff --git a/yanzhu-auth/src/main/java/com/yanzhu/auth/service/SysLoginService.java b/yanzhu-auth/src/main/java/com/yanzhu/auth/service/SysLoginService.java index 3bae6fc0..452cb992 100644 --- a/yanzhu-auth/src/main/java/com/yanzhu/auth/service/SysLoginService.java +++ b/yanzhu-auth/src/main/java/com/yanzhu/auth/service/SysLoginService.java @@ -18,6 +18,9 @@ import com.yanzhu.system.api.RemoteUserService; import com.yanzhu.system.api.domain.SysUser; import com.yanzhu.system.api.model.LoginUser; +import java.util.List; +import java.util.Map; + /** * 登录校验方法 * @@ -98,6 +101,14 @@ public class SysLoginService } passwordService.validate(user, password); recordLogService.recordLogininfor(username, Constants.LOGIN_SUCCESS, "登录成功"); + + // 用户项目信息 + R>> projectResult = remoteUserService.getProjects(userInfo.getUserid(), SecurityConstants.INNER); + if(StringUtils.isNotNull(projectResult) && StringUtils.isNotNull(projectResult.getData())){ + List> list = projectResult.getData(); + userInfo.setProjectId(Convert.toLong(list.get(0).get("project_id"))); + userInfo.setProjectDeptId(Convert.toLong(list.get(0).get("dept_id"))); + } return userInfo; } diff --git a/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/enums/FlowableEnums.java b/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/enums/FlowableEnums.java new file mode 100644 index 00000000..011ffc0f --- /dev/null +++ b/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/enums/FlowableEnums.java @@ -0,0 +1,34 @@ +package com.yanzhu.common.core.enums; + +/** + * 工作流枚举 + * + * @author JiangYuQi + */ +public enum FlowableEnums { + + SUB_DEPTS("1", "分包单位审批"), + SUB_DEPTS_GROUP("2", "分包单位班组审批"), + SUB_DEPTS_USERS("3", "分包单位班组用户审批"); + + private final String code; + private final String info; + + FlowableEnums(String code, String info) + { + this.code = code; + this.info = info; + } + + public String getCode() + { + return code; + } + + public String getInfo() + { + return info; + } + + public static final String FLOW_KEY_CACHE = "flowable:"; +} diff --git a/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/exception/CustomException.java b/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/exception/CustomException.java new file mode 100644 index 00000000..b7a74b08 --- /dev/null +++ b/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/exception/CustomException.java @@ -0,0 +1,43 @@ +package com.yanzhu.common.core.exception; + +/** + * 自定义异常 + * + * @author yanZhu + */ +public class CustomException extends RuntimeException +{ + private static final long serialVersionUID = 1L; + + private Integer code; + + private String message; + + public CustomException(String message) + { + this.message = message; + } + + public CustomException(String message, Integer code) + { + this.message = message; + this.code = code; + } + + public CustomException(String message, Throwable e) + { + super(message, e); + this.message = message; + } + + @Override + public String getMessage() + { + return message; + } + + public Integer getCode() + { + return code; + } +} diff --git a/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/utils/StringUtils.java b/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/utils/StringUtils.java index 333c1839..9ee0b7c9 100644 --- a/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/utils/StringUtils.java +++ b/yanzhu-common/yanzhu-common-core/src/main/java/com/yanzhu/common/core/utils/StringUtils.java @@ -1,8 +1,13 @@ package com.yanzhu.common.core.utils; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; + +import feign.form.util.CharsetUtil; import org.springframework.util.AntPathMatcher; import com.yanzhu.common.core.constant.Constants; import com.yanzhu.common.core.text.StrFormatter; @@ -261,6 +266,21 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils return false; } + /** + * 判断指定集合是否包含指定值,如果集合为空(null或者空),返回{@code false},否则找到元素返回{@code true} + * + * @param collection 集合 + * @param value 需要查找的值 + * @return 如果集合为空(null或者空),返回{@code false},否则找到元素返回{@code true} + * @throws ClassCastException 如果类型不一致会抛出转换异常 + * @throws NullPointerException 当指定的元素 值为 null ,或集合类不支持null 时抛出该异常 + * @see Collection#contains(Object) + * @since 4.1.10 + */ + public static boolean contains(Collection> collection, Object value) { + return isNotEmpty(collection) && collection.contains(value); + } + /** * 格式化文本, {} 表示占位符 * 此方法只是简单将占位符 {} 按照顺序替换为参数 @@ -297,7 +317,7 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils /** * 判断给定的set列表中是否包含数组array 判断给定的数组array中是否包含给定的元素value * - * @param set 给定的集合 + * @param collection 给定的集合 * @param array 给定的数组 * @return boolean 结果 */ @@ -522,6 +542,87 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils return padl(num.toString(), size, '0'); } + /** + * 将对象转为字符串 + * + * + * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 + * 2、对象数组会调用Arrays.toString方法 + * + * + * @param obj 对象 + * @return 字符串 + */ + public static String utf8Str(Object obj) { + return str(obj, CharsetUtil.UTF_8); + } + + /** + * 将对象转为字符串 + * + * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 + * 2、对象数组会调用Arrays.toString方法 + * + * + * @param obj 对象 + * @param charset 字符集 + * @return 字符串 + */ + public static String str(Object obj, Charset charset) { + if (null == obj) { + return null; + } + if (obj instanceof String) { + return (String) obj; + } else if (obj instanceof byte[]) { + return str((byte[]) obj, charset); + } else if (obj instanceof Byte[]) { + return str((Byte[]) obj, charset); + } else if (obj instanceof ByteBuffer) { + return str((ByteBuffer) obj, charset); + } else if (isArray(obj)) { + return toString(obj); + } + return obj.toString(); + } + + /** + * 数组或集合转String + * + * @param obj 集合或数组对象 + * @return 数组字符串,与集合转字符串格式相同 + */ + public static String toString(Object obj) { + if (null == obj) { + return null; + } + if (obj instanceof long[]) { + return Arrays.toString((long[]) obj); + } else if (obj instanceof int[]) { + return Arrays.toString((int[]) obj); + } else if (obj instanceof short[]) { + return Arrays.toString((short[]) obj); + } else if (obj instanceof char[]) { + return Arrays.toString((char[]) obj); + } else if (obj instanceof byte[]) { + return Arrays.toString((byte[]) obj); + } else if (obj instanceof boolean[]) { + return Arrays.toString((boolean[]) obj); + } else if (obj instanceof float[]) { + return Arrays.toString((float[]) obj); + } else if (obj instanceof double[]) { + return Arrays.toString((double[]) obj); + } else if (isArray(obj)) { + // 对象数组 + try { + return Arrays.deepToString((Object[]) obj); + } catch (Exception ignore) { + //ignore + } + } + return obj.toString(); + } + /** * 字符串左补齐。如果原始字符串s长度大于size,则只保留最后size个字符。 * diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowCommentDto.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowCommentDto.java new file mode 100644 index 00000000..4b9d597c --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowCommentDto.java @@ -0,0 +1,25 @@ +package com.yanzhu.flowable.domain; + +import lombok.Builder; +import lombok.Data; + +import java.io.Serializable; + +/** + * @author Tony + * @date 2021/3/28 15:50 + */ +@Data +@Builder +public class FlowCommentDto implements Serializable { + + /** + * 意见类别 0 正常意见 1 退回意见 2 驳回意见 + */ + private String type; + + /** + * 意见内容 + */ + private String comment; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowFromFieldDTO.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowFromFieldDTO.java new file mode 100644 index 00000000..e5912074 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowFromFieldDTO.java @@ -0,0 +1,15 @@ +package com.yanzhu.flowable.domain; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author Tony + * @date 2021/3/31 23:20 + */ +@Data +public class FlowFromFieldDTO implements Serializable { + + private Object fields; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowNextDto.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowNextDto.java new file mode 100644 index 00000000..70b506a0 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowNextDto.java @@ -0,0 +1,19 @@ +package com.yanzhu.flowable.domain; + +import lombok.Data; + +import java.io.Serializable; + +/** + * 动态人员、组 + * @author Tony + * @date 2021/4/17 22:59 + */ +@Data +public class FlowNextDto implements Serializable { + + private String type; + private String dataType; + private String vars; + +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowProcDefDto.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowProcDefDto.java new file mode 100644 index 00000000..74d1fb1f --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowProcDefDto.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.domain; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; +import java.util.Date; + +/** + * 流程定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@ApiModel("流程定义") +public class FlowProcDefDto implements Serializable { + + @ApiModelProperty("流程id") + private String id; + + @ApiModelProperty("流程名称") + private String name; + + @ApiModelProperty("流程key") + private String flowKey; + + @ApiModelProperty("流程分类") + private String category; + + @ApiModelProperty("流程分类名称") + private String categoryName; + + @ApiModelProperty("配置表单名称") + private String formName; + + @ApiModelProperty("配置表单id") + private Long formId; + + @ApiModelProperty("版本") + private int version; + + @ApiModelProperty("部署ID") + private String deploymentId; + + @ApiModelProperty("流程定义状态: 1:激活 , 2:中止") + private int suspensionState; + + @ApiModelProperty("部署时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date deploymentTime; + + @ApiModelProperty("单位主键") + private Long deptId; + + @ApiModelProperty("单位名称") + private String deptName; + + @ApiModelProperty("项目主键") + private Long projectId; + + @ApiModelProperty("项目名称") + private String projectName; + +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowSaveXmlVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowSaveXmlVo.java new file mode 100644 index 00000000..a06d435a --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowSaveXmlVo.java @@ -0,0 +1,43 @@ +package com.yanzhu.flowable.domain; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author Tony + * @date 2021/3/28 19:48 + */ +@Data +public class FlowSaveXmlVo implements Serializable { + + /** + * 流程名称 + */ + private String name; + + /** + * 流程分类 + */ + private String category; + + /** + * 流程定义类型 + */ + private String prtype; + + /** + * 流程单位 + */ + private String ownerDeptId; + + /** + * 流程项目 + */ + private String OwnerProjectId; + + /** + * xml 文件 + */ + private String xml; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskDto.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskDto.java new file mode 100644 index 00000000..5db80e9b --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskDto.java @@ -0,0 +1,103 @@ +package com.yanzhu.flowable.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Date; + +/** + * 工作流任务 + * + * @author Tony + * @date 2021-04-03 + */ +@Getter +@Setter +@ApiModel("工作流任务相关-返回参数") +public class FlowTaskDto implements Serializable { + + @ApiModelProperty("任务编号") + private String taskId; + + @ApiModelProperty("任务执行编号") + private String executionId; + + @ApiModelProperty("任务名称") + private String taskName; + + @ApiModelProperty("任务Key") + private String taskDefKey; + + @ApiModelProperty("任务执行人Id") + private Long assigneeId; + + @ApiModelProperty("部门名称") + private String deptName; + + @ApiModelProperty("流程发起人部门名称") + private String startDeptName; + + @ApiModelProperty("任务执行人名称") + private String assigneeName; + + @ApiModelProperty("任务执行人部门") + private String assigneeDeptName;; + + @ApiModelProperty("流程发起人Id") + private String startUserId; + + @ApiModelProperty("流程发起人名称") + private String startUserName; + + @ApiModelProperty("流程类型") + private String category; + + @ApiModelProperty("流程变量信息") + private Object procVars; + + @ApiModelProperty("局部变量信息") + private Object taskLocalVars; + + @ApiModelProperty("流程部署编号") + private String deployId; + + @ApiModelProperty("流程ID") + private String procDefId; + + @ApiModelProperty("流程key") + private String procDefKey; + + @ApiModelProperty("流程定义名称") + private String procDefName; + + @ApiModelProperty("流程定义内置使用版本") + private int procDefVersion; + + @ApiModelProperty("流程实例ID") + private String procInsId; + + @ApiModelProperty("历史流程实例ID") + private String hisProcInsId; + + @ApiModelProperty("任务耗时") + private String duration; + + @ApiModelProperty("任务意见") + private FlowCommentDto comment; + + @ApiModelProperty("候选执行人") + private String candidate; + + @ApiModelProperty("任务创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @ApiModelProperty("任务完成时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date finishTime; + +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskEntity.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskEntity.java new file mode 100644 index 00000000..33f90b41 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskEntity.java @@ -0,0 +1,370 @@ +package com.yanzhu.flowable.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.yanzhu.common.core.web.domain.BaseEntity; +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; +import java.util.List; + +/** + * 工作流任务 + * + * @author JiangYuQi + * @date 2021-04-03 + */ +public class FlowTaskEntity extends BaseEntity { + + private static final long serialVersionUID = 1L; + + private String businessKey; + + @ApiModelProperty("业务名称") + private String businessKeyName; + + @ApiModelProperty("任务编号") + private String taskId; + + @ApiModelProperty("任务执行编号") + private String executionId; + + @ApiModelProperty("任务名称") + private String taskName; + + @ApiModelProperty("任务Key") + private String taskDefKey; + + @ApiModelProperty("任务执行人Id") + private Long assigneeId; + + @ApiModelProperty("部门名称") + private String deptName; + + @ApiModelProperty("流程发起人部门名称") + private String startDeptName; + + @ApiModelProperty("任务执行人名称") + private String assigneeName; + + @ApiModelProperty("任务执行人部门") + private String assigneeDeptName;; + + @ApiModelProperty("流程发起人Id") + private String startUserId; + + @ApiModelProperty("流程发起人名称") + private String startUserName; + + @ApiModelProperty("流程类型") + private String category; + + @ApiModelProperty("流程变量信息") + private Object procVars; + + @ApiModelProperty("局部变量信息") + private Object taskLocalVars; + + @ApiModelProperty("流程部署编号") + private String deployId; + + @ApiModelProperty("流程ID") + private String procDefId; + + @ApiModelProperty("流程key") + private String procDefKey; + + @ApiModelProperty("流程定义名称") + private String procDefName; + + @ApiModelProperty("流程定义内置使用版本") + private int procDefVersion; + + @ApiModelProperty("流程实例ID") + private String procInsId; + + @ApiModelProperty("历史流程实例ID") + private String hisProcInsId; + + @ApiModelProperty("任务耗时") + private String duration; + + @ApiModelProperty("候选执行人") + private String candidate; + + @ApiModelProperty("任务创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @ApiModelProperty("任务完成时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date finishTime; + + @ApiModelProperty("任务完成类型") + private String taskComType; + + private long deptId; + private long projectId; + + @ApiModelProperty("单位祖籍列表") + private String deptAncestors; + + public long getDeptId() { + return deptId; + } + + public void setDeptId(long deptId) { + this.deptId = deptId; + } + + public long getProjectId() { + return projectId; + } + + public void setProjectId(long projectId) { + this.projectId = projectId; + } + + private List roleIds; + + public String getBusinessKeyName() { + return businessKeyName; + } + + public void setBusinessKeyName(String businessKeyName) { + this.businessKeyName = businessKeyName; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getExecutionId() { + return executionId; + } + + public void setExecutionId(String executionId) { + this.executionId = executionId; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getTaskDefKey() { + return taskDefKey; + } + + public void setTaskDefKey(String taskDefKey) { + this.taskDefKey = taskDefKey; + } + + public Long getAssigneeId() { + return assigneeId; + } + + public void setAssigneeId(Long assigneeId) { + this.assigneeId = assigneeId; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getStartDeptName() { + return startDeptName; + } + + public void setStartDeptName(String startDeptName) { + this.startDeptName = startDeptName; + } + + public String getAssigneeName() { + return assigneeName; + } + + public void setAssigneeName(String assigneeName) { + this.assigneeName = assigneeName; + } + + public String getAssigneeDeptName() { + return assigneeDeptName; + } + + public void setAssigneeDeptName(String assigneeDeptName) { + this.assigneeDeptName = assigneeDeptName; + } + + public String getStartUserId() { + return startUserId; + } + + public void setStartUserId(String startUserId) { + this.startUserId = startUserId; + } + + public String getStartUserName() { + return startUserName; + } + + public void setStartUserName(String startUserName) { + this.startUserName = startUserName; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public Object getProcVars() { + return procVars; + } + + public void setProcVars(Object procVars) { + this.procVars = procVars; + } + + public Object getTaskLocalVars() { + return taskLocalVars; + } + + public void setTaskLocalVars(Object taskLocalVars) { + this.taskLocalVars = taskLocalVars; + } + + public String getDeployId() { + return deployId; + } + + public void setDeployId(String deployId) { + this.deployId = deployId; + } + + public String getProcDefId() { + return procDefId; + } + + public void setProcDefId(String procDefId) { + this.procDefId = procDefId; + } + + public String getProcDefKey() { + return procDefKey; + } + + public void setProcDefKey(String procDefKey) { + this.procDefKey = procDefKey; + } + + public String getProcDefName() { + return procDefName; + } + + public void setProcDefName(String procDefName) { + this.procDefName = procDefName; + } + + public int getProcDefVersion() { + return procDefVersion; + } + + public void setProcDefVersion(int procDefVersion) { + this.procDefVersion = procDefVersion; + } + + public String getProcInsId() { + return procInsId; + } + + public void setProcInsId(String procInsId) { + this.procInsId = procInsId; + } + + public String getHisProcInsId() { + return hisProcInsId; + } + + public void setHisProcInsId(String hisProcInsId) { + this.hisProcInsId = hisProcInsId; + } + + public String getDuration() { + return duration; + } + + public void setDuration(String duration) { + this.duration = duration; + } + + public String getCandidate() { + return candidate; + } + + public void setCandidate(String candidate) { + this.candidate = candidate; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getFinishTime() { + return finishTime; + } + + public void setFinishTime(Date finishTime) { + this.finishTime = finishTime; + } + + public List getRoleIds() { + return roleIds; + } + + public void setRoleIds(List roleIds) { + this.roleIds = roleIds; + } + + public String getBusinessKey() { + return businessKey; + } + + public void setBusinessKey(String businessKey) { + this.businessKey = businessKey; + } + + public String getTaskComType() { + return taskComType; + } + + public void setTaskComType(String taskComType) { + this.taskComType = taskComType; + } + + public String getDeptAncestors() { + return deptAncestors; + } + + public void setDeptAncestors(String deptAncestors) { + this.deptAncestors = deptAncestors; + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowViewerDto.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowViewerDto.java new file mode 100644 index 00000000..60b221fb --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowViewerDto.java @@ -0,0 +1,23 @@ +package com.yanzhu.flowable.domain; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author Tony + * @date 2021/4/21 20:55 + */ +@Data +public class FlowViewerDto implements Serializable { + + /** + * 流程key + */ + private String key; + + /** + * 是否完成(已经审批) + */ + private boolean completed; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysDeployForm.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysDeployForm.java new file mode 100644 index 00000000..f91d88ca --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysDeployForm.java @@ -0,0 +1,64 @@ +package com.yanzhu.flowable.domain; + +import com.yanzhu.common.core.annotation.Excel; +import com.yanzhu.common.core.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 流程实例关联表单对象 sys_instance_form + * + * @author Tony + * @date 2021-03-30 + */ +public class SysDeployForm extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键 */ + private Long id; + + /** 表单主键 */ + @Excel(name = "表单主键") + private Long formId; + + /** 流程定义主键 */ + @Excel(name = "流程定义主键") + private String deployId; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setFormId(Long formId) + { + this.formId = formId; + } + + public Long getFormId() + { + return formId; + } + + public String getDeployId() { + return deployId; + } + + public void setDeployId(String deployId) { + this.deployId = deployId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("formId", getFormId()) + .append("deployId", getDeployId()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysExpression.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysExpression.java new file mode 100644 index 00000000..ab8d405b --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysExpression.java @@ -0,0 +1,83 @@ +package com.yanzhu.flowable.domain; + +import com.yanzhu.common.core.annotation.Excel; +import com.yanzhu.common.core.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 流程达式对象 sys_expression + * + * @author ruoyi + * @date 2022-12-12 + */ +public class SysExpression extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 表单主键 */ + private Long id; + + /** 表达式名称 */ + @Excel(name = "表达式名称") + private String name; + + /** 表达式内容 */ + @Excel(name = "表达式内容") + private String expression; + + /** 状态 */ + private Integer status; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setName(String name) + { + this.name = name; + } + + public String getName() + { + return name; + } + public void setExpression(String expression) + { + this.expression = expression; + } + + public String getExpression() + { + return expression; + } + public void setStatus(Integer status) + { + this.status = status; + } + + public Integer getStatus() + { + return status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("name", getName()) + .append("expression", getExpression()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("createBy", getCreateBy()) + .append("updateBy", getUpdateBy()) + .append("status", getStatus()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysForm.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysForm.java new file mode 100644 index 00000000..c60b2f57 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysForm.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.domain; + +import com.yanzhu.common.core.annotation.Excel; +import com.yanzhu.common.core.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 流程表单对象 sys_task_form + * + * @author Tony + * @date 2021-03-30 + */ +public class SysForm extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 表单主键 */ + private Long formId; + + /** 表单名称 */ + @Excel(name = "表单名称") + private String formName; + + /** 表单内容 */ + @Excel(name = "表单内容") + private String formContent; + + public void setFormId(Long formId) + { + this.formId = formId; + } + + public Long getFormId() + { + return formId; + } + public void setFormName(String formName) + { + this.formName = formName; + } + + public String getFormName() + { + return formName; + } + public void setFormContent(String formContent) + { + this.formContent = formContent; + } + + public String getFormContent() + { + return formContent; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("formId", getFormId()) + .append("formName", getFormName()) + .append("formContent", getFormContent()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("createBy", getCreateBy()) + .append("updateBy", getUpdateBy()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysListener.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysListener.java new file mode 100644 index 00000000..488dc4bf --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysListener.java @@ -0,0 +1,126 @@ +package com.yanzhu.flowable.domain; + +import com.yanzhu.common.core.annotation.Excel; +import com.yanzhu.common.core.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 流程监听对象 sys_listener + * + * @author Tony + * @date 2022-12-25 + */ +public class SysListener extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 表单主键 */ + private Long id; + + /** 名称 */ + @Excel(name = "名称") + private String name; + + /** 监听类型 */ + @Excel(name = "监听类型") + private String type; + + /** 事件类型 */ + @Excel(name = "事件类型") + private String eventType; + + /** 值类型 */ + @Excel(name = "值类型") + private String valueType; + + /** 执行内容 */ + @Excel(name = "执行内容") + private String value; + + /** 状态 */ + @Excel(name = "状态") + private Integer status; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setName(String name) + { + this.name = name; + } + + public String getName() + { + return name; + } + public void setType(String type) + { + this.type = type; + } + + public String getType() + { + return type; + } + public void setEventType(String eventType) + { + this.eventType = eventType; + } + + public String getEventType() + { + return eventType; + } + public void setValueType(String valueType) + { + this.valueType = valueType; + } + + public String getValueType() + { + return valueType; + } + public void setValue(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + public void setStatus(Integer status) + { + this.status = status; + } + + public Integer getStatus() + { + return status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("name", getName()) + .append("type", getType()) + .append("eventType", getEventType()) + .append("valueType", getValueType()) + .append("value", getValue()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("createBy", getCreateBy()) + .append("updateBy", getUpdateBy()) + .append("status", getStatus()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysTaskForm.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysTaskForm.java new file mode 100644 index 00000000..6a033889 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysTaskForm.java @@ -0,0 +1,65 @@ +package com.yanzhu.flowable.domain; + +import com.yanzhu.common.core.annotation.Excel; +import com.yanzhu.common.core.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 流程任务关联单对象 sys_task_form + * + * @author Tony + * @date 2021-04-03 + */ +public class SysTaskForm extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键 */ + private Long id; + + /** 表单主键 */ + @Excel(name = "表单主键") + private Long formId; + + /** 所属任务 */ + @Excel(name = "所属任务") + private String taskId; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setFormId(Long formId) + { + this.formId = formId; + } + + public Long getFormId() + { + return formId; + } + public void setTaskId(String taskId) + { + this.taskId = taskId; + } + + public String getTaskId() + { + return taskId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("formId", getFormId()) + .append("taskId", getTaskId()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowDeptVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowDeptVo.java new file mode 100644 index 00000000..fb33f0d1 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowDeptVo.java @@ -0,0 +1,47 @@ +package com.yanzhu.flowable.domain.vo; + +import com.yanzhu.common.core.web.domain.BaseEntity; +import io.swagger.annotations.ApiModel; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + + +/** + * 工作流项目单位关系 + * + * @author JiangYuQi + * @date 2024-02-23 + */ +@Data +@ApiModel("工作流项目单位关系") +public class FlowDeptVo extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** 流程定义主键 */ + private String procdefId; + + /** 流程定义类型 */ + private String prtype; + + /** 流程定义单位 */ + private Long deptId; + + /** 流程定义项目 */ + private Long projId; + + /** 流程定义排序 */ + private String sort; + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("procdefId", getProcdefId()) + .append("prtype", getPrtype()) + .append("deptId", getDeptId()) + .append("projId", getProjId()) + .append("sort", getSort()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowQueryVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowQueryVo.java new file mode 100644 index 00000000..4b08f213 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowQueryVo.java @@ -0,0 +1,49 @@ +package com.yanzhu.flowable.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 流程任务 + * + * @author Tony + * @date 2021-04-03 + */ +@Data +@ApiModel("工作流任务相关--请求参数") +public class FlowQueryVo { + + @ApiModelProperty("流程名称") + private String name; + + @ApiModelProperty("流程类别") + private String category; + + @ApiModelProperty("开始时间") + private String startTime; + + @ApiModelProperty("结束时间") + private String endTime; + + @ApiModelProperty("当前页码") + private Integer pageNum; + + @ApiModelProperty("每页条数") + private Integer pageSize; + + @ApiModelProperty("单位主键") + private Long deptId; + + @ApiModelProperty("单位名称") + private String deptName; + + @ApiModelProperty("项目主键") + private Long projectId; + + @ApiModelProperty("项目名称") + private String projectName; + + @ApiModelProperty("单位祖籍列表") + private String deptAncestors; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowTaskVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowTaskVo.java new file mode 100644 index 00000000..c05a8ba4 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowTaskVo.java @@ -0,0 +1,59 @@ +package com.yanzhu.flowable.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * 流程任务 + * + * @author Tony + * @date 2021-04-03 + */ +@Data +@ApiModel("工作流任务相关--请求参数") +public class FlowTaskVo { + + @ApiModelProperty("任务Id") + private String taskId; + + @ApiModelProperty("用户Id") + private String userId; + + @ApiModelProperty("任务意见") + private String comment; + + @ApiModelProperty("流程实例Id") + private String instanceId; + + @ApiModelProperty("节点") + private String targetKey; + + private String deploymentId; + @ApiModelProperty("流程环节定义ID") + private String defId; + + @ApiModelProperty("子执行流ID") + private String currentChildExecutionId; + + @ApiModelProperty("子执行流是否已执行") + private Boolean flag; + + @ApiModelProperty("流程变量信息") + private Map variables; + + @ApiModelProperty("审批信息") + private Map applyInfos; + + @ApiModelProperty("审批人") + private String assignee; + + @ApiModelProperty("候选人") + private List candidateUsers; + + @ApiModelProperty("审批组") + private List candidateGroups; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ProcKeyRole.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ProcKeyRole.java new file mode 100644 index 00000000..e31a5612 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ProcKeyRole.java @@ -0,0 +1,50 @@ +package com.yanzhu.flowable.domain.vo; + +import java.io.Serializable; +import java.util.List; + +public class ProcKeyRole implements Serializable +{ + + private static final long serialVersionUID = 1L; + + private String roleId; + + private String key; + + private String sort; + + private List keys; + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public List getKeys() { + return keys; + } + + public void setKeys(List keys) { + this.keys = keys; + } + + public String getSort() { + return sort; + } + + public void setSort(String sort) { + this.sort = sort; + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ReturnTaskNodeVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ReturnTaskNodeVo.java new file mode 100644 index 00000000..0085e543 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ReturnTaskNodeVo.java @@ -0,0 +1,23 @@ +package com.yanzhu.flowable.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 可退回节点 + * + * @author tony + * @date 2022-04-23 11:01:52 + */ +@Data +@ApiModel("可退回节点") +public class ReturnTaskNodeVo { + + @ApiModelProperty("任务Id") + private String id; + + @ApiModelProperty("用户Id") + private String name; + +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java new file mode 100644 index 00000000..b5b29240 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java @@ -0,0 +1,63 @@ +package com.yanzhu.flowable.domain.vo; + +import java.io.Serializable; +import java.util.Map; + +/** + * 流程实例启动 + * + * @author JiangYuQi + * @date 2020-07-07 + */ +public class StartTaskVO implements Serializable +{ + + private static final long serialVersionUID = 1L; + + //流程实例ID + private String procDefId; + private String userId; + private String userName; + private String nickName; + private Map variables; + + public String getProcDefId() { + return procDefId; + } + + public void setProcDefId(String procDefId) { + this.procDefId = procDefId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public Map getVariables() { + return variables; + } + + public void setVariables(Map variables) { + this.variables = variables; + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java new file mode 100644 index 00000000..0f7afa58 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java @@ -0,0 +1,112 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.FlowTaskEntity; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/*** + * 工作流业务相关 + */ +public interface FlowBusinessKeyMapper { + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件查询流程任务 + * @param procInsId + * @return + */ + public FlowTaskEntity findFlowTaskByProcInsId(String procInsId); + + /** + * 查询项目信息 + * @param proId + * @return + */ + public Map selectSurProjectById(Long proId); + + /** + * 根据流程Id查询操作日志 + * @param procInsId + * @return + */ + public List> selectCommentByProcInsId(String procInsId); + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + public Map selectLastCommentByProcInsId(String procInsId); + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + public List> selectFormDatasByProcInsId(String procInsId); + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件查询我的代办分组数量统计 + * @param flowTaskEntity + * @return + */ + public List> findAwaitCountGroupByCategory(FlowTaskEntity flowTaskEntity); + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity); + + public List groupByCategory(FlowTaskEntity where); + + public List groupByUnit(FlowTaskEntity where); + + public List listByUnit(FlowTaskEntity where); + + public List listByState(FlowTaskEntity where); + + public List groupByUnitTotal(FlowTaskEntity where); + + public List groupByUnitFinish(FlowTaskEntity where); + + public List groupByProject(@Param("deptId")long deptId,@Param("proType")String proType); + + /** + * 根据条件查询安全类作业工作流列表 + * @param flowTaskEntity + * @return + */ + public List findSafetyWorkList(FlowTaskEntity flowTaskEntity); + + public List listByCategory(FlowTaskEntity where); + + /** + * 工程管理-分包单位占比 + * @param where + * @return + */ + public List groupFlowBySubDeptType(FlowTaskEntity where); + /** + * 工程管理-分包单位资质审批明细 + * @param where + * @return + */ + public List listFlowBySubDeptType(FlowTaskEntity where); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java new file mode 100644 index 00000000..7d32f616 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java @@ -0,0 +1,76 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.vo.FlowDeptVo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 流程定义查询 + * + * @author Tony + * @email + * @date 2022/1/29 5:44 下午 + **/ +public interface FlowDeployMapper { + + /** + * 流程定义列表 + * @param roleId 发起角色 + * @param category 流程类别 + * @param name 流程名称 + * @return + */ + List selectDeployList(@Param("roleId")String roleId, @Param("category")String category, @Param("name")String name); + + /** + * 我的流程定义列表 + * @param username 登录用户 + * @param category 流程类别 + * @param name 流程名称 + * @return + */ + List selectMyDeployList(@Param("username")String username, @Param("category")String category, @Param("name")String name); + + /** + * 流程定义列表 + * @param flowQueryVo 流程查询 + * @return + */ + List selectDeployList(FlowQueryVo flowQueryVo); + + /** + * 新增单位流程关系 + * + * @param flowDeptVo 流程 单位关系 + * @return 结果 + */ + public int insertActReProcdefDept(FlowDeptVo flowDeptVo); + + /** + * 根据角色删除流程定义 + * @param roleId 橘色 + * @return + */ + int deleteDeployByRoleId(String roleId); + + + /** + * 批量新增流程定义角色配置 + * @param items 列表 + * @return + */ + int batchDeployRoleRole(List> items); + + /** + * 流程发起排序设置 + * @param roleId 角色主键 + * @param key 流程 + * @param sort 排序 + * @return + */ + int updateProcKeyRoleSort(@Param("roleId")String roleId, @Param("key")String key, @Param("sort")String sort); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java new file mode 100644 index 00000000..1f7bd205 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程实例关联表单Mapper接口 + * + * @author Tony + * @date 2021-03-30 + */ +public interface SysDeployFormMapper +{ + /** + * 查询流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 流程实例关联表单 + */ + public SysDeployForm selectSysDeployFormById(Long id); + + /** + * 查询流程实例关联表单列表 + * + * @param SysDeployForm 流程实例关联表单 + * @return 流程实例关联表单集合 + */ + public List selectSysDeployFormList(SysDeployForm SysDeployForm); + + /** + * 新增流程实例关联表单 + * + * @param SysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int insertSysDeployForm(SysDeployForm SysDeployForm); + + /** + * 修改流程实例关联表单 + * + * @param SysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int updateSysDeployForm(SysDeployForm SysDeployForm); + + /** + * 删除流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormById(Long id); + + /** + * 批量删除流程实例关联表单 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteSysDeployFormByIds(Long[] ids); + + /** + * 查询流程挂着的表单 + * @param deployId + * @return + */ + SysForm selectSysDeployFormByDeployId(String deployId); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java new file mode 100644 index 00000000..bdeef941 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysExpression; + +import java.util.List; + +/** + * 流程达式Mapper接口 + * + * @author ruoyi + * @date 2022-12-12 + */ +public interface SysExpressionMapper +{ + /** + * 查询流程达式 + * + * @param id 流程达式主键 + * @return 流程达式 + */ + public SysExpression selectSysExpressionById(Long id); + + /** + * 查询流程达式列表 + * + * @param sysExpression 流程达式 + * @return 流程达式集合 + */ + public List selectSysExpressionList(SysExpression sysExpression); + + /** + * 新增流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int insertSysExpression(SysExpression sysExpression); + + /** + * 修改流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int updateSysExpression(SysExpression sysExpression); + + /** + * 删除流程达式 + * + * @param id 流程达式主键 + * @return 结果 + */ + public int deleteSysExpressionById(Long id); + + /** + * 批量删除流程达式 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteSysExpressionByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java new file mode 100644 index 00000000..183f9630 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程表单Mapper接口 + * + * @author Tony + * @date 2021-03-30 + */ +public interface SysFormMapper +{ + /** + * 查询流程表单 + * + * @param formId 流程表单ID + * @return 流程表单 + */ + public SysForm selectSysFormById(Long formId); + + /** + * 查询流程表单列表 + * + * @param sysForm 流程表单 + * @return 流程表单集合 + */ + public List selectSysFormList(SysForm sysForm); + + /** + * 新增流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int insertSysForm(SysForm sysForm); + + /** + * 修改流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int updateSysForm(SysForm sysForm); + + /** + * 删除流程表单 + * + * @param formId 流程表单ID + * @return 结果 + */ + public int deleteSysFormById(Long formId); + + /** + * 批量删除流程表单 + * + * @param formIds 需要删除的数据ID + * @return 结果 + */ + public int deleteSysFormByIds(Long[] formIds); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java new file mode 100644 index 00000000..c4b43efe --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysListener; + +import java.util.List; + +/** + * 流程监听Mapper接口 + * + * @author Tony + * @date 2022-12-25 + */ +public interface SysListenerMapper +{ + /** + * 查询流程监听 + * + * @param id 流程监听主键 + * @return 流程监听 + */ + public SysListener selectSysListenerById(Long id); + + /** + * 查询流程监听列表 + * + * @param sysListener 流程监听 + * @return 流程监听集合 + */ + public List selectSysListenerList(SysListener sysListener); + + /** + * 新增流程监听 + * + * @param sysListener 流程监听 + * @return 结果 + */ + public int insertSysListener(SysListener sysListener); + + /** + * 修改流程监听 + * + * @param sysListener 流程监听 + * @return 结果 + */ + public int updateSysListener(SysListener sysListener); + + /** + * 删除流程监听 + * + * @param id 流程监听主键 + * @return 结果 + */ + public int deleteSysListenerById(Long id); + + /** + * 批量删除流程监听 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteSysListenerByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java new file mode 100644 index 00000000..0d0a1d68 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysTaskForm; + +import java.util.List; + +/** + * 流程任务关联单Mapper接口 + * + * @author Tony + * @date 2021-04-03 + */ +public interface SysTaskFormMapper +{ + /** + * 查询流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 流程任务关联单 + */ + public SysTaskForm selectSysTaskFormById(Long id); + + /** + * 查询流程任务关联单列表 + * + * @param sysTaskForm 流程任务关联单 + * @return 流程任务关联单集合 + */ + public List selectSysTaskFormList(SysTaskForm sysTaskForm); + + /** + * 新增流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int insertSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 修改流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int updateSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 删除流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormById(Long id); + + /** + * 批量删除流程任务关联单 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteSysTaskFormByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/mapper/domain/init b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/mapper/domain/init deleted file mode 100644 index e69de29b..00000000 diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml new file mode 100644 index 00000000..df7e58d3 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml @@ -0,0 +1,515 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT * FROM + vw_flow_all fa + + and fa.taskName like concat('%', #{taskName}, '%') + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.procDefKey = #{procDefKey} + and fa.businessKey = #{businessKey} + and fa.startDeptName like concat('%', #{startDeptName}, '%') + and fa.startUserName like concat('%', #{startUserName}, '%') + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.startUserId = #{nowUser} + and fa.finishTime is null + and fa.finishTime is not null + + order by fa.createTime desc + + + + + select vf.* as startUserPhone from vw_flow_all vf + where vf.procInsId = #{procInsId} + + + + + select * from vw_flow_comment where procInstId = #{procInstId} order by startTime DESC + + + + select * from vw_flow_comment where procInstId = #{procInstId} order by startTime DESC limit 1 + + + + + select hv.NAME_ as `name`,hv.TEXT_ as text from act_hi_varinst hv where hv.PROC_INST_ID_ = #{procInstId} + + + + + select fa.* from vw_flow_await fa + where + 1=1 + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.businessKey in (select DISTINCT project_id from sur_project_userinfo where user_id=#{nowUser} and is_del=0) + AND (fa.ASSIGNEE_ = #{nowUser} + OR ( + fa.ASSIGNEE_ IS NULL + AND ( + fa.USER_ID_ = #{nowUser} + OR ( + fa.GROUP_ID_ IN + + #{roleId} + + ) + ) + ) + ) + order by fa.createTime desc + + + + + select fa.category,count(1) as total from vw_flow_await fa + where + 1=1 + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.businessKey in (select DISTINCT project_id from sur_project_userinfo where user_id=#{nowUser} and is_del=0) + AND (fa.ASSIGNEE_ = #{nowUser} + OR ( + fa.ASSIGNEE_ IS NULL + AND ( + fa.USER_ID_ = #{nowUser} + OR ( + fa.GROUP_ID_ IN + + #{roleId} + + ) + ) + ) + ) + group by fa.category + + + + + select fa.* from vw_flow_finished fa + where + fa.ASSIGNEE_=#{nowUser} + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.endTime between #{params.beginDate} and #{params.endDate} + order by fa.endTime desc + + + + SELECT a.dict_label taskName,b.cnt assigneeId,c.cnt procDefVersion FROM + ( SELECT * FROM sys_dict_data WHERE dict_type = 'sys_process_category') a + LEFT JOIN (SELECT category,COUNT(1) cnt FROM vw_flow_all WHERE finishTime IS NOT NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + GROUP BY category) b ON a.dict_value=b.category + LEFT JOIN (SELECT category,COUNT(1) cnt FROM vw_flow_all WHERE finishTime IS NULL and taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + GROUP BY category) c ON a.dict_value=c.category + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '总包%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '监理%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '设计%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '甲方%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '工程%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE taskName LIKE '总包%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE taskName LIKE '监理%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE taskName LIKE '设计%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE taskName LIKE '甲方%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE taskName LIKE '工程%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE taskName LIKE '总包%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE taskName LIKE '监理%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE taskName LIKE '设计%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE taskName LIKE '甲方%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE taskName LIKE '工程%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT * FROM vw_flow_all + + and category = #{category} + and businessDeptId=#{deptId} + and businessType = #{proType} + and businessKey=#{projectId} + + and businessKey in + + #{item} + + + + + + + SELECT * FROM vw_flow_all WHERE finishTime IS NULL + + AND taskName LIKE '总包%' + AND taskName LIKE '监理%' + AND taskName LIKE '设计%' + AND taskName LIKE '甲方%' + AND taskName LIKE '工程%' + + and businessDeptId=#{deptId} + and businessType = #{proType} + and businessKey=#{projectId} + + and businessKey in + + #{item} + + + + + + SELECT * FROM vw_flow_all + + + AND taskName!='提交申请' + AND finishTime IS NULL AND taskName!='提交申请' + AND finishTime IS NOT NULL + + and businessDeptId=#{deptId} + and businessKey=#{projectId} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + + + + SELECT a.businessKey,a.cnt duration ,b.projectName businessKeyName FROM ( + SELECT businessKey,COUNT(1) cnt FROM vw_flow_all WHERE taskName!='提交申请' + and businessDeptId=#{deptId} + and businessType = #{proType} + GROUP BY businessKey ) a,sur_project b WHERE a.businessKey=b.id + ORDER BY a.cnt DESC + + + + SELECT + fa.procInsId, + fa.deployId, + fa.createTime, + fa.finishTime, + fa.duration, + fa.procDefKey, + fa.procDefName, + fa.procDefVersion, + fa.category, + fa.businessKey, + fa.businessDeptId, + fa.businessKeyName, + fa.startUserId, + fa.startUserName, + fa.startDeptName, + fa.taskId, + fa.taskName, + fa.assigneeId, + fa.assigneeName, + fa.assigneeDeptName + FROM + vw_flow_all fa + + and fa.businessType = #{proType} + and fa.procDefKey = #{procDefKey} + and fa.businessKey = #{businessKey} + + and fa.businessDeptId = #{projectDeptId} + + order by fa.createTime desc + + + + select * from sur_project where id = #{proId} + + + + select x.dict_label taskName,y.taskId from ( + select dict_value,dict_label + from sys_dict_data where dict_type='flow_sub_dept_type' + ) x left join ( + SELECT d.dict_value, d.dict_label,count(1) taskId FROM + vw_flow_all a + LEFT JOIN act_hi_varinst b ON a.procInsId=b.PROC_INST_ID_ AND b.NAME_='subDeptType' + LEFT JOIN act_hi_varinst c ON a.procInsId=c.PROC_INST_ID_ AND c.NAME_='subDeptName' + LEFT JOIN sys_dict_data D ON b.TEXT_=d.dict_value and d.dict_type='flow_sub_dept_type' + left join sur_project sp on a.businesskey=sp.id + where a.procDefKey='flow_fbzzsp_fbszzsp' + AND (A.finishTime is null OR (A.finishTime is not null and A.taskComType =1)) + and sp.progressVisible=0 and sp.isDel=0 + and a.businessDeptId=#{deptId} + and a.businessKey=#{projectId} + + and a.businessKey in + + #{item} + + + and sp.projectType = #{proType} + group by d.dict_value,d.dict_label + ) y on x.dict_value=y.dict_value + + + SELECT a.procInsId,a.deployId,a.createTime,a.finishTime,a.businessKey,a.businessKeyName,a.startDeptName,b.TEXT_ as taskId, c.TEXT_ as deptName,d.dict_label taskName FROM + vw_flow_all a + LEFT JOIN act_hi_varinst b ON a.procInsId=b.PROC_INST_ID_ AND b.NAME_='subDeptType' + LEFT JOIN act_hi_varinst c ON a.procInsId=c.PROC_INST_ID_ AND c.NAME_='subDeptName' + LEFT JOIN sys_dict_data D ON b.TEXT_=d.dict_value and d.dict_type='flow_sub_dept_type' + left join sur_project sp on a.businesskey=sp.id + where a.procDefKey='flow_fbzzsp_fbszzsp' + AND (A.finishTime is null OR (A.finishTime is not null and A.taskComType =1)) + and sp.progressVisible=0 and sp.isDel=0 + and a.businessDeptId=#{deptId} + and a.businessKey=#{projectId} + + and a.businessKey in + + #{item} + + + and sp.projectType = #{proType} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml new file mode 100644 index 00000000..f8a188d2 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml @@ -0,0 +1,125 @@ + + + + + + SELECT + rp.id_ as id, + rp.deployment_id_ as deploymentId, + rd.name_ as name, + rd.category_ as category, + rp.key_ as flowKey, + rp.version_ as version, + rp.suspension_state_ as suspensionState, + rd.deploy_time_ as deploymentTime, + rpd.DEPT_ID_ as deptId, + sd.dept_name as deptName, + rpd.PROJ_ID_ as projectId, + spi.project_name as projectName + FROM + act_re_procdef rp + LEFT JOIN act_re_deployment rd ON rp.deployment_id_ = rd.id_ + left join act_re_procdef_dept rpd on rp.id_ = rpd.PROCDEF_ID_ + left join sys_dept sd on sd.dept_id = rpd.DEPT_ID_ + left join pro_project_info spi on spi.id = rpd.PROJ_ID_ + + rp.SUSPENSION_STATE_ = 1 + + and rpd.DEPT_ID_ = #{deptId} + + + and sd.dept_name like concat('%', #{deptName}, '%') + + + and rpd.PROJ_ID_ = #{projectId} + + + and spi.project_name like concat('%', #{projectName}, '%') + + + and rd.name_ like concat('%', #{name}, '%') + + + and rd.category_ = #{category} + + + and rd.key_ = #{flowKey} + + + order by rpr.SORT_ + + + + SELECT * FROM ( + SELECT + rp.id_ as id, + rp.deployment_id_ as deploymentId, + rd.name_ as name, + rd.category_ as category, + sdd.dict_label as categoryName, + rp.key_ as flowKey, + rp.version_ as version, + rp.suspension_state_ as suspensionState, + rd.deploy_time_ as deploymentTime, + ROW_NUMBER () OVER ( + PARTITION BY rp.key_ + ORDER BY + rp.version_ DESC + ) AS rn + FROM + act_re_procdef rp + LEFT JOIN act_re_deployment rd ON rp.deployment_id_ = rd.id_ + left join sys_dict_data sdd on sdd.dict_type='sys_process_category' and sdd.dict_value = rd.category_ + left join act_re_procdef_role rpr on rp.key_ = rpr.PROCDEF_KEY_ + left join sys_user_role sur on sur.role_id = rpr.ROLE_ID_ + left join sys_user su on su.user_id = sur.user_id + + rp.SUSPENSION_STATE_ = 1 and su.user_name = #{username} + + and rd.name_ like concat('%', #{name}, '%') + + + and rd.category_ = #{category} + + + order by rd.deploy_time_ desc + ) v WHERE v.rn = 1 + + + + delete from act_re_procdef_role where ROLE_ID_ = #{roleId} + + + + insert into act_re_procdef_role( PROCDEF_KEY_, ROLE_ID_) values + + ( #{item.key}, #{item.roleId}) + + + + + update act_re_procdef_role set SORT_=#{sort} where PROCDEF_KEY_=#{key} and ROLE_ID_=#{roleId} + + + + + insert into act_re_procdef_dept + + PROCDEF_ID_, + TYPE_, + DEPT_ID_, + PROJ_ID_, + SORT_, + + + #{procdefId}, + #{prtype}, + #{deptId}, + #{projId}, + #{sort}, + + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml new file mode 100644 index 00000000..f3423af4 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + select id, form_id, deploy_id from sys_deploy_form + + + + + + and form_id = #{formId} + and deploy_id = #{deployId} + + + + + + where id = #{id} + + + + select t1.form_content as formContent,t1.form_name as formName,t1.form_id as formId from sys_form t1 left join sys_deploy_form t2 on t1.form_id = t2.form_id + where t2.deploy_id = #{deployId} limit 1 + + + + insert into sys_deploy_form + + form_id, + deploy_id, + + + #{formId}, + #{deployId}, + + + + + update sys_deploy_form + + form_id = #{formId}, + deploy_id = #{deployId}, + + where id = #{id} + + + + delete from sys_deploy_form where id = #{id} + + + + delete from sys_deploy_form where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml new file mode 100644 index 00000000..504e522c --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + select id, name, expression, create_time, update_time, create_by, update_by, status, remark from sys_expression + + + + + + and name like concat('%', #{name}, '%') + and expression = #{expression} + and status = #{status} + + + + + + where id = #{id} + + + + insert into sys_expression + + name, + expression, + create_time, + update_time, + create_by, + update_by, + status, + remark, + + + #{name}, + #{expression}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{status}, + #{remark}, + + + + + update sys_expression + + name = #{name}, + expression = #{expression}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + status = #{status}, + remark = #{remark}, + + where id = #{id} + + + + delete from sys_expression where id = #{id} + + + + delete from sys_expression where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml new file mode 100644 index 00000000..6e686742 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + select form_id, form_name, form_content, create_time, update_time, create_by, update_by, remark from sys_form + + + + + + and form_name like concat('%', #{formName}, '%') + and form_content = #{formContent} + + order by create_time desc + + + + + where form_id = #{formId} + + + + insert into sys_form + + form_name, + form_content, + create_time, + update_time, + create_by, + update_by, + remark, + + + #{formName}, + #{formContent}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{remark}, + + + + + update sys_form + + form_name = #{formName}, + form_content = #{formContent}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + remark = #{remark}, + + where form_id = #{formId} + + + + delete from sys_form where form_id = #{formId} + + + + delete from sys_form where form_id in + + #{formId} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml new file mode 100644 index 00000000..a8403a06 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + select id, + name, + type, + event_type, + value_type, + value, + create_time, + update_time, + create_by, + update_by, + status, + remark + from sys_listener + + + + + + and name like concat('%', #{name}, '%') + and type = #{type} + and event_type = #{eventType} + and value_type = #{valueType} + and value = #{value} + and status = #{status} + + + + + + where id = #{id} + + + + insert into sys_listener + + name, + type, + event_type, + value_type, + value, + create_time, + update_time, + create_by, + update_by, + status, + remark, + + + #{name}, + #{type}, + #{eventType}, + #{valueType}, + #{value}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{status}, + #{remark}, + + + + + update sys_listener + + name = #{name}, + type = #{type}, + event_type = #{eventType}, + value_type = #{valueType}, + value = #{value}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + status = #{status}, + remark = #{remark}, + + where id = #{id} + + + + delete + from sys_listener + where id = #{id} + + + + delete from sys_listener where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml new file mode 100644 index 00000000..b7ec90fe --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + select id, form_id, task_id from sys_task_form + + + + + + and form_id = #{formId} + and task_id = #{taskId} + + + + + + where id = #{id} + + + + insert into sys_task_form + + form_id, + task_id, + + + #{formId}, + #{taskId}, + + + + + update sys_task_form + + form_id = #{formId}, + task_id = #{taskId}, + + where id = #{id} + + + + delete from sys_task_form where id = #{id} + + + + delete from sys_task_form where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java b/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java index 7faf4f46..ba3490f9 100644 --- a/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java +++ b/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java @@ -91,6 +91,16 @@ public class SecurityUtils return userId != null && 1L == userId; } + /** + * 是否为公司管理员 + * + * @return 结果 + */ + public static boolean isGSAdmin() + { + return getLoginUser().getRoles().contains("gsAdmin"); + } + /** * 生成BCryptPasswordEncoder密码 * diff --git a/yanzhu-modules/pom.xml b/yanzhu-modules/pom.xml index 20037b42..800e5a5a 100644 --- a/yanzhu-modules/pom.xml +++ b/yanzhu-modules/pom.xml @@ -15,13 +15,14 @@ yanzhu-file yanzhu-manage yanzhu-wechat + yanzhu-flowable yanzhu-modules pom - yanzhu-modules业务模块 + yanzhu-modules分布式模块 diff --git a/yanzhu-modules/yanzhu-flowable/pom.xml b/yanzhu-modules/yanzhu-flowable/pom.xml new file mode 100644 index 00000000..a94efdf1 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/pom.xml @@ -0,0 +1,182 @@ + + + + yanzhu + com.yanzhu + 3.6.2 + ../../pom.xml + + 4.0.0 + + yanzhu-modules-flowable + + + 17 + 17 + + + + yanzhu-modules-flowable工作流模块 + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + io.springfox + springfox-swagger-ui + ${swagger.fox.version} + + + + + org.apache.velocity + velocity-engine-core + + + + + mysql + mysql-connector-java + + + + + com.yanzhu + yanzhu-common-log + + + + + com.yanzhu + yanzhu-common-swagger + + + + org.projectlombok + lombok + provided + + + + io.seata + seata-all + 1.5.2 + compile + + + + + com.yanzhu + yanzhu-common-mapper + 3.6.2 + compile + + + + + com.googlecode.aviator + aviator + 5.3.3 + + + + org.flowable + flowable-spring-boot-starter + + + + org.flowable + flowable-spring-security + + + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + com.spotify + docker-maven-plugin + 1.2.0 + + + ${project.build.finalName} + latest + + openjdk:latest + + ["java", "-jar", "/${project.build.finalName}.jar"] + + + ${configuration.docker-two-Host} + + + / + + ${project.build.directory} + + ${project.build.finalName}.jar + + + + + + build-image + package + + build + + + + + + + + + src/main/resources + true + + + + + \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java new file mode 100644 index 00000000..1edc2bc8 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java @@ -0,0 +1,34 @@ +package com.yanzhu.flowable; + +import com.yanzhu.common.security.annotation.EnableCustomConfig; +import com.yanzhu.common.security.annotation.EnableRyFeignClients; +import com.yanzhu.common.swagger.annotation.EnableCustomSwagger2; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 工作流模块 + * + * @author ruoyi + */ +@EnableCustomConfig +@EnableCustomSwagger2 +@EnableRyFeignClients +@SpringBootApplication +public class YanZhuFlowableApplication +{ + public static void main(String[] args) + { + SpringApplication.run(YanZhuFlowableApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 工作流模块启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java new file mode 100644 index 00000000..54488c3e --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java @@ -0,0 +1,80 @@ +package com.yanzhu.flowable.common.constant; + +/** + * 流程常量信息 + * + * @author Tony + * @date 2021/4/17 22:46 + */ +public class ProcessConstants { + + /** + * 动态数据 + */ + public static final String DYNAMIC = "dynamic"; + + /** + * 固定任务接收 + */ + public static final String FIXED = "fixed"; + + /** + * 单个审批人 + */ + public static final String ASSIGNEE = "assignee"; + + + /** + * 候选人 + */ + public static final String CANDIDATE_USERS = "candidateUsers"; + + + /** + * 审批组 + */ + public static final String CANDIDATE_GROUPS = "candidateGroups"; + + /** + * 单个审批人 + */ + public static final String PROCESS_APPROVAL = "approval"; + + /** + * 会签人员 + */ + public static final String PROCESS_MULTI_INSTANCE_USER = "userList"; + + /** + * nameapace + */ + public static final String NAMASPASE = "http://flowable.org/bpmn"; + + /** + * 会签节点 + */ + public static final String PROCESS_MULTI_INSTANCE = "multiInstance"; + + /** + * 自定义属性 dataType + */ + public static final String PROCESS_CUSTOM_DATA_TYPE = "dataType"; + + /** + * 自定义属性 userType + */ + public static final String PROCESS_CUSTOM_USER_TYPE = "userType"; + + /** + * 初始化人员 + */ + public static final String PROCESS_INITIATOR = "INITIATOR"; + + + /** + * 流程跳过 + */ + public static final String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java new file mode 100644 index 00000000..06aa8bff --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java @@ -0,0 +1,43 @@ +package com.yanzhu.flowable.common.enums; + +/** + * 流程意见类型 + * + * @author Tony + * @date 2021/4/19 + */ +public enum FlowComment { + + /** + * 说明 + */ + NORMAL("1", "正常意见"), + REBACK("2", "退回意见"), + REJECT("3", "驳回意见"), + DELEGATE("4", "委派意见"), + ASSIGN("5", "转办意见"), + STOP("6", "终止流程"), + REVOKE("7", "撤回流程"); + /** + * 类型 + */ + private final String type; + + /** + * 说明 + */ + private final String remark; + + FlowComment(String type, String remark) { + this.type = type; + this.remark = remark; + } + + public String getType() { + return type; + } + + public String getRemark() { + return remark; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java new file mode 100644 index 00000000..414d2d04 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java @@ -0,0 +1,32 @@ +package com.yanzhu.flowable.config; + +import org.flowable.engine.impl.db.DbIdGenerator; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncListenableTaskExecutor; +import org.springframework.core.task.SimpleAsyncTaskExecutor; + +/** + * 流程id生成处理 + * @author Tony + * @date 2022-12-26 10:24 + */ +@Configuration +public class FlowableConfig implements EngineConfigurationConfigurer { + + @Bean + public AsyncListenableTaskExecutor applicationTaskExecutor() { + return new SimpleAsyncTaskExecutor(); + } + + @Override + public void configure(SpringProcessEngineConfiguration engineConfiguration) { + engineConfiguration.setActivityFontName("宋体"); + engineConfiguration.setLabelFontName("宋体"); + engineConfiguration.setAnnotationFontName("宋体"); + engineConfiguration.setIdGenerator(new DbIdGenerator()); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java new file mode 100644 index 00000000..227de838 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java @@ -0,0 +1,41 @@ +package com.yanzhu.flowable.config; + +import com.yanzhu.flowable.listener.GlobalEventListener; +import lombok.RequiredArgsConstructor; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; + +/** + * Flowable添加全局监听器 + * + * @author JiangYuQi + */ +@Configuration +@RequiredArgsConstructor +public class FlowableGlobalListenerConfig implements ApplicationListener { + + private final SpringProcessEngineConfiguration configuration; + + private final GlobalEventListener globalEventListener; + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + FlowableEventDispatcher dispatcher = configuration.getEventDispatcher(); + /** + * 任务创建全局监听-待办消息发送 + * PROCESS_CREATED 流程创建 + * TASK_CREATED 任务创建 + * TASK_COMPLETED 任务完成 + * PROCESS_COMPLETED 流程完成 + * 流程创建、任务创建、任务完成、流程完成 + */ + dispatcher.addEventListener(globalEventListener,FlowableEngineEventType.TASK_CREATED); + dispatcher.addEventListener(globalEventListener,FlowableEngineEventType.PROCESS_COMPLETED); + } + +} + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java new file mode 100644 index 00000000..983f676c --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java @@ -0,0 +1,146 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.FlowTaskEntity; +import com.yanzhu.flowable.service.IFlowBusinessKeyService; +import com.yanzhu.system.api.domain.SysUser; +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * + * 业务工作流程 + * + * + * @author JiangYuQi + * @date 2024-04-03 + */ +@Slf4j +@Api(tags = "业务工作流程") +@RestController +@RequestMapping("/mapper/flowable/businessKey") +public class FlowBusinessKeyController extends BaseController { + + @Autowired + private IFlowBusinessKeyService flowBusinessKeyService; + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/allList") + public TableDataInfo allList(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()); + } + startPage(); + return getDataTable(flowBusinessKeyService.selectAllFlowTaskByParams(flowTaskEntity)); + } + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/queryCount") + public AjaxResult queryCount(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()); + } + return success(flowBusinessKeyService.quueryCount(flowTaskEntity)); + } + + /** + * 根据流程Id查询操作日志 + * @param procInsId + * @return + */ + @GetMapping(value = "/findCommentByProcInsId") + public AjaxResult findCommentByProcInsId(String procInsId) { + return success(flowBusinessKeyService.selectCommentByProcInsId(procInsId)); + } + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + @GetMapping(value = "/findFormDatasByProcInsId") + public AjaxResult findFormDatasByProcInsId(String procInsId) { + return success(flowBusinessKeyService.selectFormDatasByProcInsId(procInsId)); + } + + /** + * 根据条件查询我的代办任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/myAwaitFlowTaskList") + public TableDataInfo myAwaitFlowTaskList(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setAssigneeId(sysUser.getUserId()); + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + flowTaskEntity.setRoleIds(sysUser.getRoles().stream().map(role -> role.getRoleId()).collect(Collectors.toList())); + } + startPage(); + return getDataTable(flowBusinessKeyService.selectMyAwaitFlowTask(flowTaskEntity)); + } + + /** + * 根据条件查询我的已办任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/myFinishedFlowTaskList") + public TableDataInfo myFinishedFlowTaskList(FlowTaskEntity flowTaskEntity) { + startPage(); + flowTaskEntity.setAssigneeId(SecurityUtils.getUserId()); + return getDataTable(flowBusinessKeyService.selectMyFinishedFlowTask(flowTaskEntity)); + } + + /** + * 查询我的代办任务数量 + * @return AjaxResult 统计结果 + */ + @GetMapping(value = "/findMyTask") + public AjaxResult findMyTask() { + FlowTaskEntity flowTaskEntity = new FlowTaskEntity(); + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setAssigneeId(sysUser.getUserId()); + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + flowTaskEntity.setRoleIds(sysUser.getRoles().stream().map(role -> role.getRoleId()).collect(Collectors.toList())); + } + List> list = flowBusinessKeyService.selectMyAwaitFlowTask(flowTaskEntity); + Map data = new HashMap<>(); + if(CollectionUtils.isNotEmpty(list)){ + data.put("todo",list.size()); + }else{ + data.put("todo",0); + } + return success(data); + } + + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java new file mode 100644 index 00000000..c7aa591c --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java @@ -0,0 +1,199 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.FlowSaveXmlVo; +import com.yanzhu.flowable.domain.SysExpression; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.service.IFlowDefinitionService; +import com.yanzhu.flowable.service.ISysExpressionService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.RemoteUserService; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.domain.SysUser; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * + * 工作流程定义 + * + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "流程定义") +@RestController +@RequestMapping("/mapper/flowable/definition") +public class FlowDefinitionController extends BaseController { + + @Autowired + private IFlowDefinitionService flowDefinitionService; + + @Resource + private ISysExpressionService sysExpressionService; + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + @GetMapping(value = "/list") + @ApiOperation(value = "流程定义列表", response = FlowProcDefDto.class) + public TableDataInfo list(FlowQueryVo flowQueryVo) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowQueryVo.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + } + return flowDefinitionService.list(flowQueryVo); + } + + @ApiOperation(value = "导入流程文件", notes = "上传bpmn20的xml文件") + @Log(title = "导入流程文件", businessType = BusinessType.INSERT) + @PostMapping("/import") + public AjaxResult importFile(@RequestParam(required = false) String name, + @RequestParam(required = false) String category, + MultipartFile file) { + InputStream in = null; + try { + in = file.getInputStream(); + flowDefinitionService.importFile(name, category, null, null, null, in); + } catch (Exception e) { + log.error("导入失败:", e); + return AjaxResult.success(e.getMessage()); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + log.error("关闭输入流出错", e); + } + } + + return AjaxResult.success("导入成功"); + } + + @ApiOperation(value = "读取xml文件") + @GetMapping("/readXml/{deployId}") + public AjaxResult readXml(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId) { + try { + return flowDefinitionService.readXml(deployId); + } catch (Exception e) { + return AjaxResult.error("加载xml文件异常"); + } + } + + @ApiOperation(value = "读取图片文件") + @GetMapping("/readImage/{deployId}") + public void readImage(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId, HttpServletResponse response) { + OutputStream os = null; + BufferedImage image = null; + try { + image = ImageIO.read(flowDefinitionService.readImage(deployId)); + response.setContentType("image/png"); + os = response.getOutputStream(); + if (image != null) { + ImageIO.write(image, "png", os); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.flush(); + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @ApiOperation(value = "保存流程设计器内的xml文件") + @Log(title = "保存流程设计", businessType = BusinessType.INSERT) + @PostMapping("/save") + public AjaxResult save(@RequestBody FlowSaveXmlVo vo) { + InputStream in = null; + try { + in = new ByteArrayInputStream(vo.getXml().getBytes(StandardCharsets.UTF_8)); + flowDefinitionService.importFile(vo.getName(), vo.getCategory(), vo.getPrtype(), vo.getOwnerDeptId(), vo.getOwnerProjectId(), in); + } catch (Exception e) { + log.error("导入失败:", e); + return AjaxResult.error(e.getMessage()); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + log.error("关闭输入流出错", e); + } + } + return AjaxResult.success("导入成功"); + } + + + @ApiOperation(value = "发起流程") + @Log(title = "发起流程申请", businessType = BusinessType.INSERT) + @PostMapping("/start/{procDefId}") + public AjaxResult start(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId, + @ApiParam(value = "申请表单") @RequestBody Map applyInfos) { + return flowDefinitionService.startProcessInstanceById(procDefId, applyInfos); + } + + @ApiOperation(value = "激活或挂起流程定义") + @Log(title = "激活/挂起流程", businessType = BusinessType.UPDATE) + @PutMapping(value = "/updateState") + public AjaxResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state, + @ApiParam(value = "流程部署ID", required = true) @RequestParam String deployId) { + flowDefinitionService.updateState(state, deployId); + return AjaxResult.success(); + } + + @ApiOperation(value = "删除流程") + @Log(title = "删除流程定义", businessType = BusinessType.UPDATE) + @DeleteMapping(value = "/{deployIds}") + public AjaxResult delete(@PathVariable String[] deployIds) { + for (String deployId : deployIds) { + flowDefinitionService.delete(deployId); + } + return AjaxResult.success(); + } + + @ApiOperation(value = "指定流程达式列表") + @GetMapping("/expList") + public AjaxResult expList(SysExpression sysExpression) { + List list = sysExpressionService.selectSysExpressionList(sysExpression); + return AjaxResult.success(list); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java new file mode 100644 index 00000000..5850b4c7 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java @@ -0,0 +1,63 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.service.IFlowInstanceService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 工作流流程实例管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "工作流流程实例管理") +@RestController +@RequestMapping("/mapper/flowable/instance") +public class FlowInstanceController { + + @Autowired + private IFlowInstanceService flowInstanceService; + + @ApiOperation(value = "根据流程定义id启动流程实例") + @PostMapping("/startBy/{procDefId}") + public AjaxResult startById(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId, + @ApiParam(value = "变量集合,json对象") @RequestBody Map variables) { + return flowInstanceService.startProcessInstanceById(procDefId, variables); + + } + + + @ApiOperation(value = "激活或挂起流程实例") + @PostMapping(value = "/updateState") + public AjaxResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state, + @ApiParam(value = "流程实例ID", required = true) @RequestParam String instanceId) { + flowInstanceService.updateState(state,instanceId); + return AjaxResult.success(); + } + + @ApiOperation("结束流程实例") + @PostMapping(value = "/stopProcessInstance") + public AjaxResult stopProcessInstance(@RequestBody FlowTaskVo flowTaskVo) { + flowInstanceService.stopProcessInstance(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "删除流程实例") + @DeleteMapping(value = "/delete/{instanceIds}") + public AjaxResult delete(@ApiParam(value = "流程实例ID", required = true) @PathVariable String[] instanceIds, + @ApiParam(value = "删除原因") @RequestParam(required = false) String deleteReason) { + for (String instanceId : instanceIds) { + flowInstanceService.delete(instanceId,deleteReason); + } + return AjaxResult.success(); + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java new file mode 100644 index 00000000..643ef51b --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java @@ -0,0 +1,268 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.flowable.domain.FlowTaskDto; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.service.IFlowTaskService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * 工作流任务管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "工作流流程任务管理") +@RestController +@RequestMapping("/mapper/flowable/task") +public class FlowTaskController { + + @Autowired + private IFlowTaskService flowTaskService; + + @ApiOperation(value = "我发起的流程", response = FlowTaskDto.class) + @GetMapping(value = "/myProcess") + public AjaxResult myProcess(FlowQueryVo queryVo) { + return flowTaskService.myProcess(queryVo); + } + + @ApiOperation(value = "取消申请", response = FlowTaskDto.class) + @Log(title = "终止申请", businessType = BusinessType.UPDATE) + @PostMapping(value = "/stopProcess") + public AjaxResult stopProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.stopProcess(flowTaskVo); + } + + @ApiOperation(value = "撤回流程", response = FlowTaskDto.class) + @Log(title = "撤回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/revokeProcess") + public AjaxResult revokeProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.revokeProcess(flowTaskVo); + } + + @ApiOperation(value = "获取待办列表", response = FlowTaskDto.class) + @GetMapping(value = "/todoList") + public AjaxResult todoList(FlowQueryVo queryVo) { + return flowTaskService.todoList(queryVo); + } + + @ApiOperation(value = "获取已办任务", response = FlowTaskDto.class) + @GetMapping(value = "/finishedList") + public AjaxResult finishedList(FlowQueryVo queryVo) { + return flowTaskService.finishedList(queryVo); + } + + + @ApiOperation(value = "流程历史流转记录", response = FlowTaskDto.class) + @GetMapping(value = "/flowRecord") + public AjaxResult flowRecord(String procInsId, String deployId) { + return flowTaskService.flowRecord(procInsId, deployId); + } + + + @ApiOperation(value = "流程初始化表单", response = FlowTaskDto.class) + @GetMapping(value = "/flowFormData") + public AjaxResult flowFormData(String deployId) { + return flowTaskService.flowFormData(deployId); + } + + @ApiOperation(value = "获取流程变量", response = FlowTaskDto.class) + @GetMapping(value = "/processVariables/{taskId}") + public AjaxResult processVariables(@ApiParam(value = "流程任务Id") @PathVariable(value = "taskId") String taskId) { + return flowTaskService.processVariables(taskId); + } + + @ApiOperation(value = "审批任务") + @Log(title = "审批流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/complete") + public AjaxResult complete(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "重新提交流程") + @Log(title = "重新提交流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/completeAndModify") + public AjaxResult completeAndModify(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "驳回任务") + @Log(title = "驳回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/reject") + public AjaxResult taskReject(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReject(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "退回任务") + @Log(title = "退回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/return") + public AjaxResult taskReturn(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReturn(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "获取所有可回退的节点") + @PostMapping(value = "/returnList") + public AjaxResult findReturnTaskList(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.findReturnTaskList(flowTaskVo); + } + + @ApiOperation(value = "删除任务") + @Log(title = "删除流程", businessType = BusinessType.DELETE) + @DeleteMapping(value = "/delete") + public AjaxResult delete(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "认领/签收任务") + @Log(title = "认领/签收流程", businessType = BusinessType.INSERT) + @PostMapping(value = "/claim") + public AjaxResult claim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.claim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "取消认领/签收任务") + @Log(title = "取消认领/签收流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/unClaim") + public AjaxResult unClaim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.unClaim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "委派任务") + @Log(title = "委派流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/delegateTask") + public AjaxResult delegate(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.delegateTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "任务归还") + @Log(title = "归还流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/resolveTask") + public AjaxResult resolveTask(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.resolveTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "转办任务") + @Log(title = "转办流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/assignTask") + public AjaxResult assign(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.assignTask(flowTaskVo); + return AjaxResult.success(); + } + + @PostMapping(value = "/addMultiInstanceExecution") + @ApiOperation(value = "多实例加签") + public AjaxResult addMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.addMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("加签成功"); + } + + @PostMapping(value = "/deleteMultiInstanceExecution") + @ApiOperation(value = "多实例减签") + public AjaxResult deleteMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("减签成功"); + } + + @ApiOperation(value = "获取下一节点") + @PostMapping(value = "/nextFlowNode") + public AjaxResult getNextFlowNode(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNode(flowTaskVo); + } + + @ApiOperation(value = "流程发起时获取下一节点") + @PostMapping(value = "/nextFlowNodeByStart") + public AjaxResult getNextFlowNodeByStart(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNodeByStart(flowTaskVo); + } + + /** + * 生成流程图 + * + * @param processId 任务ID + */ + @GetMapping("/diagram/{processId}") + public void genProcessDiagram(HttpServletResponse response, + @PathVariable("processId") String processId) { + InputStream inputStream = flowTaskService.diagram(processId); + OutputStream os = null; + BufferedImage image = null; + try { + image = ImageIO.read(inputStream); + response.setContentType("image/png"); + os = response.getOutputStream(); + if (image != null) { + ImageIO.write(image, "png", os); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.flush(); + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 获取流程执行节点 + * + * @param procInsId 流程实例编号 + * @param procInsId 任务执行编号 + */ + @GetMapping("/flowViewer/{procInsId}/{executionId}") + public AjaxResult getFlowViewer(@PathVariable("procInsId") String procInsId, + @PathVariable("executionId") String executionId) { + return flowTaskService.getFlowViewer(procInsId, executionId); + } + + /** + * 流程节点信息 + * + * @param procInsId 流程实例id + * @return + */ + @GetMapping("/flowXmlAndNode") + public AjaxResult flowXmlAndNode(@RequestParam(value = "procInsId", required = false) String procInsId, + @RequestParam(value = "deployId", required = false) String deployId) { + return flowTaskService.flowXmlAndNode(procInsId, deployId); + } + + /** + * 流程节点表单 + * + * @param taskId 流程任务编号 + * @return + */ + @GetMapping("/flowTaskForm") + public AjaxResult flowTaskForm(@RequestParam(value = "taskId", required = false) String taskId) throws Exception { + return flowTaskService.flowTaskForm(taskId); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java new file mode 100644 index 00000000..74cbad80 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java @@ -0,0 +1,112 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.utils.poi.ExcelUtil; +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.common.security.annotation.RequiresPermissions; +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 流程表单Controller + * + * @author Tony + * @date 2021-04-03 + */ +@RestController +@RequestMapping("/mapper/flowable/form") +public class SysFormController extends BaseController { + + @Autowired + private ISysFormService SysFormService; + + @Autowired + private ISysDeployFormService sysDeployFormService; + + /** + * 查询流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:list')") + @GetMapping("/list") + public TableDataInfo list(SysForm sysForm) { + startPage(); + List list = SysFormService.selectSysFormList(sysForm); + return getDataTable(list); + } + + @GetMapping("/formList") + public AjaxResult formList(SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + return AjaxResult.success(list); + } + /** + * 导出流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:export')") + @Log(title = "流程表单", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public void export(HttpServletResponse response, SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + ExcelUtil util = new ExcelUtil(SysForm.class); + util.exportExcel(response, list, "form"); + } + + /** + * 获取流程表单详细信息 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:query')") + @GetMapping(value = "/{formId}") + public AjaxResult getInfo(@PathVariable("formId") Long formId) { + return AjaxResult.success(SysFormService.selectSysFormById(formId)); + } + + /** + * 新增流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:add')") + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.insertSysForm(sysForm)); + } + + /** + * 修改流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:edit')") + @Log(title = "流程表单", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.updateSysForm(sysForm)); + } + + /** + * 删除流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:remove')") + @Log(title = "流程表单", businessType = BusinessType.DELETE) + @DeleteMapping("/{formIds}") + public AjaxResult remove(@PathVariable Long[] formIds) { + return toAjax(SysFormService.deleteSysFormByIds(formIds)); + } + + + /** + * 挂载流程表单 + */ + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping("/addDeployForm") + public AjaxResult addDeployForm(@RequestBody SysDeployForm sysDeployForm) { + return toAjax(sysDeployFormService.insertSysDeployForm(sysDeployForm)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java new file mode 100644 index 00000000..e66efd70 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java @@ -0,0 +1,41 @@ +package com.yanzhu.flowable.factory; + +import lombok.Getter; +import org.flowable.engine.*; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * flowable 引擎注入封装 + * @author Tony + * @date 2021-04-03 + */ +@Component +@Getter +public class FlowServiceFactory { + + @Resource + protected RepositoryService repositoryService; + + @Resource + protected RuntimeService runtimeService; + + @Resource + protected IdentityService identityService; + + @Resource + protected TaskService taskService; + + @Resource + protected HistoryService historyService; + + @Resource + protected ManagementService managementService; + + @Qualifier("processEngine") + @Resource + protected ProcessEngine processEngine; + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java new file mode 100644 index 00000000..7a0baca5 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java @@ -0,0 +1,370 @@ +package com.yanzhu.flowable.flow; + +import org.flowable.bpmn.model.AssociationDirection; +import org.flowable.bpmn.model.GraphicInfo; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.util.ReflectUtil; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; + +/** + * @author Tony + * @date 2021/4/4 23:58 + */ +public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas { + //定义走过流程连线颜色为绿色 + protected static Color HIGHLIGHT_SequenceFlow_COLOR = Color.GREEN; + //设置未走过流程的连接线颜色 + protected static Color CONNECTION_COLOR = Color.BLACK; + //设置flows连接线字体颜色red + protected static Color LABEL_COLOR = new Color(0, 0, 0); + //高亮显示task框颜色 + protected static Color HIGHLIGHT_COLOR = Color.GREEN; + protected static Color HIGHLIGHT_COLOR1 = Color.RED; + + public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + this.initialize(imageType); + } + + /** + * 重写绘制连线的方式,设置绘制颜色 + * @param xPoints + * @param yPoints + * @param conditional + * @param isDefault + * @param connectionType + * @param associationDirection + * @param highLighted + * @param scaleFactor + */ + @Override + public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) { + Paint originalPaint = this.g.getPaint(); + Stroke originalStroke = this.g.getStroke(); + this.g.setPaint(CONNECTION_COLOR); + if (connectionType.equals("association")) { + this.g.setStroke(ASSOCIATION_STROKE); + } else if (highLighted) { + this.g.setPaint(HIGHLIGHT_SequenceFlow_COLOR); + this.g.setStroke(HIGHLIGHT_FLOW_STROKE); + } + + for (int i = 1; i < xPoints.length; ++i) { + Integer sourceX = xPoints[i - 1]; + Integer sourceY = yPoints[i - 1]; + Integer targetX = xPoints[i]; + Integer targetY = yPoints[i]; + java.awt.geom.Line2D.Double line = new java.awt.geom.Line2D.Double((double) sourceX, (double) sourceY, (double) targetX, (double) targetY); + this.g.draw(line); + } + + java.awt.geom.Line2D.Double line; + if (isDefault) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawDefaultSequenceFlowIndicator(line, scaleFactor); + } + + if (conditional) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawConditionalSequenceFlowIndicator(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.ONE) || associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[xPoints.length - 2], (double) yPoints[xPoints.length - 2], (double) xPoints[xPoints.length - 1], (double) yPoints[xPoints.length - 1]); + this.drawArrowHead(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[1], (double) yPoints[1], (double) xPoints[0], (double) yPoints[0]); + this.drawArrowHead(line, scaleFactor); + } + + this.g.setPaint(originalPaint); + this.g.setStroke(originalStroke); + } + + /** + * 设置字体大小图标颜色 + * @param imageType + */ + @Override + public void initialize(String imageType) { + if ("png".equalsIgnoreCase(imageType)) { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 2); + } else { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 1); + } + + this.g = this.processDiagram.createGraphics(); + if (!"png".equalsIgnoreCase(imageType)) { + this.g.setBackground(new Color(255, 255, 255, 0)); + this.g.clearRect(0, 0, this.canvasWidth, this.canvasHeight); + } + + this.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + //修改图标颜色,修改图标字体大小 + this.g.setPaint(Color.black); + Font font = new Font(this.activityFontName, 10, 14); + this.g.setFont(font); + this.fontMetrics = this.g.getFontMetrics(); + //修改连接线字体大小 + LABEL_FONT = new Font(this.labelFontName, 10, 15); + ANNOTATION_FONT = new Font(this.annotationFontName, 0, 11); + + try { + USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/userTask.png", this.customClassLoader)); + SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/scriptTask.png", this.customClassLoader)); + SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/serviceTask.png", this.customClassLoader)); + RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/receiveTask.png", this.customClassLoader)); + SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/sendTask.png", this.customClassLoader)); + MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/manualTask.png", this.customClassLoader)); + BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/businessRuleTask.png", this.customClassLoader)); + SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/shellTask.png", this.customClassLoader)); + DMN_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/dmnTask.png", this.customClassLoader)); + CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/camelTask.png", this.customClassLoader)); + MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/muleTask.png", this.customClassLoader)); + HTTP_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/httpTask.png", this.customClassLoader)); + TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", this.customClassLoader)); + COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate-throw.png", this.customClassLoader)); + COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate.png", this.customClassLoader)); + ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error-throw.png", this.customClassLoader)); + ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error.png", this.customClassLoader)); + MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message-throw.png", this.customClassLoader)); + MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message.png", this.customClassLoader)); + SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal-throw.png", this.customClassLoader)); + SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal.png", this.customClassLoader)); + } catch (IOException var4) { + LOGGER.warn("Could not load image for process diagram creation: {}", var4.getMessage()); + } + + } + + /** + * 设置连接线字体 + * @param text + * @param graphicInfo + * @param centered + */ + @Override + public void drawLabel(String text, GraphicInfo graphicInfo, boolean centered) { + float interline = 1.0f; + + // text + if (text != null && text.length() > 0) { + Paint originalPaint = g.getPaint(); + Font originalFont = g.getFont(); + + g.setPaint(LABEL_COLOR); + g.setFont(LABEL_FONT); + + int wrapWidth = 100; + int textY = (int) graphicInfo.getY(); + + // TODO: use drawMultilineText() + AttributedString as = new AttributedString(text); + as.addAttribute(TextAttribute.FOREGROUND, g.getPaint()); + as.addAttribute(TextAttribute.FONT, g.getFont()); + AttributedCharacterIterator aci = as.getIterator(); + FontRenderContext frc = new FontRenderContext(null, true, false); + LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc); + + while (lbm.getPosition() < text.length()) { + TextLayout tl = lbm.nextLayout(wrapWidth); + textY += tl.getAscent(); + + Rectangle2D bb = tl.getBounds(); + double tX = graphicInfo.getX(); + + if (centered) { + tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2); + } + tl.draw(g, (float) tX, textY); + textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent(); + } + + // restore originals + g.setFont(originalFont); + g.setPaint(originalPaint); + } + } + + /** + * 高亮显示task框完成的 + * @param x + * @param y + * @param width + * @param height + */ + @Override + public void drawHighLight(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义task框当前的位置 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightNow(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR1); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义结束节点 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightEnd(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * task框自定义文字 + * @param name + * @param graphicInfo + * @param thickBorder + * @param scaleFactor + */ + @Override + protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder, double scaleFactor) { + + Paint originalPaint = g.getPaint(); + int x = (int) graphicInfo.getX(); + int y = (int) graphicInfo.getY(); + int width = (int) graphicInfo.getWidth(); + int height = (int) graphicInfo.getHeight(); + + // Create a new gradient paint for every task box, gradient depends on x and y and is not relative + g.setPaint(TASK_BOX_COLOR); + + int arcR = 6; + if (thickBorder) { + arcR = 3; + } + + // shape + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcR, arcR); + g.fill(rect); + g.setPaint(TASK_BORDER_COLOR); + + if (thickBorder) { + Stroke originalStroke = g.getStroke(); + g.setStroke(THICK_TASK_BORDER_STROKE); + g.draw(rect); + g.setStroke(originalStroke); + } else { + g.draw(rect); + } + + g.setPaint(originalPaint); + // text + if (scaleFactor == 1.0 && name != null && name.length() > 0) { + int boxWidth = width - (2 * TEXT_PADDING); + int boxHeight = height - 16 - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2; + int boxX = x + width / 2 - boxWidth / 2; + int boxY = y + height / 2 - boxHeight / 2 + ICON_PADDING + ICON_PADDING - 2 - 2; + + drawMultilineCentredText(name, boxX, boxY, boxWidth, boxHeight); + } + } + + protected static Color EVENT_COLOR = new Color(255, 255, 255); + + /** + * 重写开始事件 + * @param graphicInfo + * @param image + * @param scaleFactor + */ + @Override + public void drawStartEvent(GraphicInfo graphicInfo, BufferedImage image, double scaleFactor) { + Paint originalPaint = g.getPaint(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); + g.draw(circle); + g.setPaint(originalPaint); + if (image != null) { + // calculate coordinates to center image + int imageX = (int) Math.round(graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / (2 * scaleFactor))); + int imageY = (int) Math.round(graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / (2 * scaleFactor))); + g.drawImage(image, imageX, imageY, + (int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null); + } + + } + + /** + * 重写结束事件 + * @param graphicInfo + * @param scaleFactor + */ + @Override + public void drawNoneEndEvent(GraphicInfo graphicInfo, double scaleFactor) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); +// g.setPaint(HIGHLIGHT_COLOR); + if (scaleFactor == 1.0) { + g.setStroke(END_EVENT_STROKE); + } else { + g.setStroke(new BasicStroke(2.0f)); + } + g.draw(circle); + g.setStroke(originalStroke); + g.setPaint(originalPaint); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java new file mode 100644 index 00000000..d55e30dd --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java @@ -0,0 +1,404 @@ +package com.yanzhu.flowable.flow; + + +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; + +import java.util.Iterator; +import java.util.List; + +/** + * @author Tony + * @date 2021/4/5 0:31 + */ +public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator { + @Override + protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + this.prepareBpmnModel(bpmnModel); + DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + Iterator var13 = bpmnModel.getPools().iterator(); + + while (var13.hasNext()) { + Pool process = (Pool) var13.next(); + GraphicInfo subProcesses = bpmnModel.getGraphicInfo(process.getId()); + processDiagramCanvas.drawPoolOrLane(process.getName(), subProcesses, scaleFactor); + } + + var13 = bpmnModel.getProcesses().iterator(); + + Process process1; + Iterator subProcesses1; + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.getLanes().iterator(); + + while (subProcesses1.hasNext()) { + Lane artifact = (Lane) subProcesses1.next(); + GraphicInfo subProcess = bpmnModel.getGraphicInfo(artifact.getId()); + processDiagramCanvas.drawPoolOrLane(artifact.getName(), subProcess, scaleFactor); + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.findFlowElementsOfType(FlowNode.class).iterator(); + + while (subProcesses1.hasNext()) { + FlowNode artifact1 = (FlowNode) subProcesses1.next(); + if (!this.isPartOfCollapsedSubProcess(artifact1, bpmnModel)) { + this.drawActivity(processDiagramCanvas, bpmnModel, artifact1, highLightedActivities, highLightedFlows, scaleFactor, Boolean.valueOf(drawSequenceFlowNameWithNoLabelDI)); + } + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + label75: + while (true) { + List subProcesses2; + do { + if (!var13.hasNext()) { + return processDiagramCanvas; + } + + process1 = (Process) var13.next(); + subProcesses1 = process1.getArtifacts().iterator(); + + while (subProcesses1.hasNext()) { + Artifact artifact2 = (Artifact) subProcesses1.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, artifact2); + } + + subProcesses2 = process1.findFlowElementsOfType(SubProcess.class, true); + } while (subProcesses2 == null); + + Iterator artifact3 = subProcesses2.iterator(); + + while (true) { + GraphicInfo graphicInfo; + SubProcess subProcess1; + do { + do { + if (!artifact3.hasNext()) { + continue label75; + } + + subProcess1 = (SubProcess) artifact3.next(); + graphicInfo = bpmnModel.getGraphicInfo(subProcess1.getId()); + } while (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded().booleanValue()); + } while (this.isPartOfCollapsedSubProcess(subProcess1, bpmnModel)); + + Iterator var19 = subProcess1.getArtifacts().iterator(); + + while (var19.hasNext()) { + Artifact subProcessArtifact = (Artifact) var19.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact); + } + } + } + } + + protected static DefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + double minX = 1.7976931348623157E308D; + double maxX = 0.0D; + double minY = 1.7976931348623157E308D; + double maxY = 0.0D; + + GraphicInfo nrOfLanes; + for (Iterator flowNodes = bpmnModel.getPools().iterator(); flowNodes.hasNext(); maxY = nrOfLanes.getY() + nrOfLanes.getHeight()) { + Pool artifacts = (Pool) flowNodes.next(); + nrOfLanes = bpmnModel.getGraphicInfo(artifacts.getId()); + minX = nrOfLanes.getX(); + maxX = nrOfLanes.getX() + nrOfLanes.getWidth(); + minY = nrOfLanes.getY(); + } + + List var23 = gatherAllFlowNodes(bpmnModel); + Iterator var24 = var23.iterator(); + + label155: + while (var24.hasNext()) { + FlowNode var26 = (FlowNode) var24.next(); + GraphicInfo artifact = bpmnModel.getGraphicInfo(var26.getId()); + if (artifact.getX() + artifact.getWidth() > maxX) { + maxX = artifact.getX() + artifact.getWidth(); + } + + if (artifact.getX() < minX) { + minX = artifact.getX(); + } + + if (artifact.getY() + artifact.getHeight() > maxY) { + maxY = artifact.getY() + artifact.getHeight(); + } + + if (artifact.getY() < minY) { + minY = artifact.getY(); + } + + Iterator process = var26.getOutgoingFlows().iterator(); + + while (true) { + List l; + do { + if (!process.hasNext()) { + continue label155; + } + + SequenceFlow graphicInfoList = (SequenceFlow) process.next(); + l = bpmnModel.getFlowLocationGraphicInfo(graphicInfoList.getId()); + } while (l == null); + + Iterator graphicInfo = l.iterator(); + + while (graphicInfo.hasNext()) { + GraphicInfo graphicInfo1 = (GraphicInfo) graphicInfo.next(); + if (graphicInfo1.getX() > maxX) { + maxX = graphicInfo1.getX(); + } + + if (graphicInfo1.getX() < minX) { + minX = graphicInfo1.getX(); + } + + if (graphicInfo1.getY() > maxY) { + maxY = graphicInfo1.getY(); + } + + if (graphicInfo1.getY() < minY) { + minY = graphicInfo1.getY(); + } + } + } + } + + List var25 = gatherAllArtifacts(bpmnModel); + Iterator var27 = var25.iterator(); + + GraphicInfo var37; + while (var27.hasNext()) { + Artifact var29 = (Artifact) var27.next(); + GraphicInfo var31 = bpmnModel.getGraphicInfo(var29.getId()); + if (var31 != null) { + if (var31.getX() + var31.getWidth() > maxX) { + maxX = var31.getX() + var31.getWidth(); + } + + if (var31.getX() < minX) { + minX = var31.getX(); + } + + if (var31.getY() + var31.getHeight() > maxY) { + maxY = var31.getY() + var31.getHeight(); + } + + if (var31.getY() < minY) { + minY = var31.getY(); + } + } + + List var33 = bpmnModel.getFlowLocationGraphicInfo(var29.getId()); + if (var33 != null) { + Iterator var35 = var33.iterator(); + + while (var35.hasNext()) { + var37 = (GraphicInfo) var35.next(); + if (var37.getX() > maxX) { + maxX = var37.getX(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() > maxY) { + maxY = var37.getY(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + } + + int var28 = 0; + Iterator var30 = bpmnModel.getProcesses().iterator(); + + while (var30.hasNext()) { + Process var32 = (Process) var30.next(); + Iterator var34 = var32.getLanes().iterator(); + + while (var34.hasNext()) { + Lane var36 = (Lane) var34.next(); + ++var28; + var37 = bpmnModel.getGraphicInfo(var36.getId()); + if (var37.getX() + var37.getWidth() > maxX) { + maxX = var37.getX() + var37.getWidth(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() + var37.getHeight() > maxY) { + maxY = var37.getY() + var37.getHeight(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + + if (var23.isEmpty() && bpmnModel.getPools().isEmpty() && var28 == 0) { + minX = 0.0D; + minY = 0.0D; + } + + return new CustomProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + } + + + private static void drawHighLight(DefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightNow(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightNow((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightEnd(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightEnd((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + @Override + protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, + FlowNode flowNode, List highLightedActivities, List highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) { + + DefaultProcessDiagramGenerator.ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass()); + if (drawInstruction != null) { + + drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode); + + // Gather info on the multi instance marker + boolean multiInstanceSequential = false; + boolean multiInstanceParallel = false; + boolean collapsed = false; + if (flowNode instanceof Activity) { + Activity activity = (Activity) flowNode; + MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics(); + if (multiInstanceLoopCharacteristics != null) { + multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential(); + multiInstanceParallel = !multiInstanceSequential; + } + } + + // Gather info on the collapsed marker + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + if (flowNode instanceof SubProcess) { + collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded(); + } else if (flowNode instanceof CallActivity) { + collapsed = true; + } + + if (scaleFactor == 1.0) { + // Actually draw the markers + processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), + multiInstanceSequential, multiInstanceParallel, collapsed); + } + + // Draw highlighted activities + if (highLightedActivities.contains(flowNode.getId())) { + + if (highLightedActivities.get(highLightedActivities.size() - 1).equals(flowNode.getId()) + && !"endenv".equals(flowNode.getId())) { + if ((flowNode.getId().contains("Event_"))) { + drawHighLightEnd((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } else { + drawHighLightNow((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + } else { + drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + + + } + + } + + // Outgoing transitions of activity + for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) { + boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId())); + String defaultFlow = null; + if (flowNode instanceof Activity) { + defaultFlow = ((Activity) flowNode).getDefaultFlow(); + } else if (flowNode instanceof Gateway) { + defaultFlow = ((Gateway) flowNode).getDefaultFlow(); + } + + boolean isDefault = false; + if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) { + isDefault = true; + } + boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && !(flowNode instanceof Gateway); + + String sourceRef = sequenceFlow.getSourceRef(); + String targetRef = sequenceFlow.getTargetRef(); + FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef); + FlowElement targetElement = bpmnModel.getFlowElement(targetRef); + List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId()); + if (graphicInfoList != null && graphicInfoList.size() > 0) { + graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList); + int xPoints[] = new int[graphicInfoList.size()]; + int yPoints[] = new int[graphicInfoList.size()]; + + for (int i = 1; i < graphicInfoList.size(); i++) { + GraphicInfo graphicInfo = graphicInfoList.get(i); + GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); + + if (i == 1) { + xPoints[0] = (int) previousGraphicInfo.getX(); + yPoints[0] = (int) previousGraphicInfo.getY(); + } + xPoints[i] = (int) graphicInfo.getX(); + yPoints[i] = (int) graphicInfo.getY(); + + } + + processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor); + + + // Draw sequenceflow label + GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId()); + if (labelGraphicInfo != null) { + processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false); + } else { + if (drawSequenceFlowNameWithNoLabelDI) { + GraphicInfo lineCenter = getLineCenter(graphicInfoList); + processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false); + } + + } + } + } + + // Nested elements + if (flowNode instanceof FlowElementsContainer) { + for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) { + if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) { + drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement, + highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + } + } + } +} + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java new file mode 100644 index 00000000..a4bd2442 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java @@ -0,0 +1,257 @@ +package com.yanzhu.flowable.flow; + +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.Expression; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.ProcessDefinition; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author Tony + * @date 2021/4/19 20:51 + */ +public class FindNextNodeUtil { + + /** + * 获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasks(RepositoryService repositoryService, org.flowable.task.api.Task task, Map map) { + List data = new ArrayList<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = task.getTaskDefinitionKey(); + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + /** + * 启动流程时获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasksByStart(RepositoryService repositoryService, ProcessDefinition processDefinition, Map map) { + List data = new ArrayList<>(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = null; + // 找到开始节点 并获取唯一key + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + key = flowElement.getId(); + break; + } + } + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + + + /** + * 查找下一节点 + * + * @param flowElements + * @param flowElement + * @param map + * @param nextUser + */ + public static void next(Collection flowElements, FlowElement flowElement, Map map, List nextUser) { + //如果是结束节点 + if (flowElement instanceof EndEvent) { + //如果是子任务的结束节点 + if (getSubProcess(flowElements, flowElement) != null) { + flowElement = getSubProcess(flowElements, flowElement); + } + } + //获取Task的出线信息--可以拥有多个 + List outGoingFlows = null; + if (flowElement instanceof Task) { + outGoingFlows = ((Task) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof Gateway) { + outGoingFlows = ((Gateway) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof StartEvent) { + outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof SubProcess) { + outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof CallActivity) { + outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows(); + } + if (outGoingFlows != null && outGoingFlows.size() > 0) { + //遍历所有的出线--找到可以正确执行的那一条 + for (SequenceFlow sequenceFlow : outGoingFlows) { + //1.有表达式,且为true + //2.无表达式 + String expression = sequenceFlow.getConditionExpression(); + if (expression == null || + expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) { + //出线的下一节点 + String nextFlowElementID = sequenceFlow.getTargetRef(); + if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) { + continue; + } + + //查询下一节点的信息 + FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements); + //调用流程 + if (nextFlowElement instanceof CallActivity) { + CallActivity ca = (CallActivity) nextFlowElement; + if (ca.getLoopCharacteristics() != null) { + UserTask userTask = new UserTask(); + userTask.setId(ca.getId()); + + userTask.setId(ca.getId()); + userTask.setLoopCharacteristics(ca.getLoopCharacteristics()); + userTask.setName(ca.getName()); + nextUser.add(userTask); + } + next(flowElements, nextFlowElement, map, nextUser); + } + //用户任务 + if (nextFlowElement instanceof UserTask) { + nextUser.add((UserTask) nextFlowElement); + } + //排他网关 + else if (nextFlowElement instanceof ExclusiveGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //并行网关 + else if (nextFlowElement instanceof ParallelGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //接收任务 + else if (nextFlowElement instanceof ReceiveTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //服务任务 + else if (nextFlowElement instanceof ServiceTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //子任务的起点 + else if (nextFlowElement instanceof StartEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + //结束节点 + else if (nextFlowElement instanceof EndEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + } + } + } + } + + /** + * 判断是否是多实例子流程并且需要设置集合类型变量 + */ + public static boolean checkSubProcess(String Id, Collection flowElements, List nextUser) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess && flowElement1.getId().equals(Id)) { + + SubProcess sp = (SubProcess) flowElement1; + if (sp.getLoopCharacteristics() != null) { + String inputDataItem = sp.getLoopCharacteristics().getInputDataItem(); + UserTask userTask = new UserTask(); + userTask.setId(sp.getId()); + userTask.setLoopCharacteristics(sp.getLoopCharacteristics()); + userTask.setName(sp.getName()); + nextUser.add(userTask); + return true; + } + } + } + + return false; + + } + + /** + * 查询一个节点的是否子任务中的节点,如果是,返回子任务 + * + * @param flowElements 全流程的节点集合 + * @param flowElement 当前节点 + * @return + */ + public static FlowElement getSubProcess(Collection flowElements, FlowElement flowElement) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess) { + for (FlowElement flowElement2 : ((SubProcess) flowElement1).getFlowElements()) { + if (flowElement.equals(flowElement2)) { + return flowElement1; + } + } + } + } + return null; + } + + + /** + * 根据ID查询流程节点对象, 如果是子任务,则返回子任务的开始节点 + * + * @param Id 节点ID + * @param flowElements 流程节点集合 + * @return + */ + public static FlowElement getFlowElementById(String Id, Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement.getId().equals(Id)) { + //如果是子任务,则查询出子任务的开始节点 + if (flowElement instanceof SubProcess) { + return getStartFlowElement(((SubProcess) flowElement).getFlowElements()); + } + return flowElement; + } + if (flowElement instanceof SubProcess) { + FlowElement flowElement1 = getFlowElementById(Id, ((SubProcess) flowElement).getFlowElements()); + if (flowElement1 != null) { + return flowElement1; + } + } + } + return null; + } + + /** + * 返回流程的开始节点 + * + * @param flowElements 节点集合 + * @description: + */ + public static FlowElement getStartFlowElement(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return flowElement; + } + } + return null; + } + + /** + * 校验el表达式 + * + * @param map + * @param expression + * @return + */ + public static boolean expressionResult(Map map, String expression) { + Expression exp = AviatorEvaluator.compile(expression); + final Object execute = exp.execute(map); + return Boolean.parseBoolean(String.valueOf(execute)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java new file mode 100644 index 00000000..530cf038 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java @@ -0,0 +1,589 @@ +package com.yanzhu.flowable.flow; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.*; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.task.api.history.HistoricTaskInstance; + +import java.util.*; + +/** + * @author Tony + * @date 2021-04-03 23:57 + */ +@Slf4j +public class FlowableUtils { + + /** + * 根据节点,获取入口连线 + * @param source + * @return + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getIncomingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getIncomingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getIncomingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getIncomingFlows(); + } + return sequenceFlows; + } + + /** + * 根据节点,获取出口连线 + * @param source + * @return + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getOutgoingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getOutgoingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getOutgoingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取全部节点列表,包含子流程节点 + * @param flowElements + * @param allElements + * @return + */ + public static Collection getAllElements(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + + for (FlowElement flowElement : flowElements) { + allElements.add(flowElement); + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 迭代获取父级任务节点列表,向前找 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 已找到的用户任务节点 + * @return + */ + public static List iteratorFindParentUserTasks(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 = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 类型为用户节点,则新增父级节点 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); + continue; + } + // 类型为子流程,则添加子流程开始节点出口处相连的节点 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + // 获取子流程用户任务节点 + List childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 + * @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 EndEvent && source.getSubProcess() != null) { + userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + 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 (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 迭代获取子流程用户任务节点 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return + */ + public static List findChildProcessUserTasks(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) { + // 循环找到目标元素 + 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 = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 从后向前寻路,获取所有脏线路上的点 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 目标脏线路终点 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set iteratorFindDirtyRoads(FlowElement source, List passRoads, Set hasSequenceFlow, List targets, Set dirtyRoads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 新增经过的路线 + passRoads.add(sequenceFlow.getSourceFlowElement().getId()); + // 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线 + if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) { + dirtyRoads.addAll(passRoads); + continue; + } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, dirtyRoads); + // 是否存在子流程上,true 是,false 否 + Boolean isInChildProcess = dirtyTargetInChildProcess((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, targets, null); + if (isInChildProcess) { + // 已在子流程上找到,该路线结束 + continue; + } + } + // 继续迭代 + dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 迭代获取子流程脏路线 + * 说明,假如回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set findChildProcessAllDirtyRoad(FlowElement source, Set hasSequenceFlow, Set dirtyRoads) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加脏路线 + dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId()); + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, dirtyRoads); + } + // 继续迭代 + dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 判断脏路线结束节点是否在子流程上 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止 + * @param inChildProcess 是否存在子流程上,true 是,false 否 + * @return + */ + public static Boolean dirtyTargetInChildProcess(FlowElement source, Set hasSequenceFlow, List targets, Boolean inChildProcess) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + inChildProcess = inChildProcess != null && inChildProcess; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null && !inChildProcess) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果发现目标点在子流程上存在,说明只到子流程为止 + if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) { + inChildProcess = true; + break; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + inChildProcess = dirtyTargetInChildProcess((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, targets, inChildProcess); + } + // 继续迭代 + inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, targets, inChildProcess); + } + } + return inChildProcess; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param isSequential 是否串行 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targetKsy 目标节点 + * @return + */ + public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, Set hasSequenceFlow, Boolean isSequential) { + isSequential = isSequential == null || isSequential; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, isSequential); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果目标节点已被判断为并行,后面都不需要执行,直接返回 + if (!isSequential) { + break; + } + // 这条线路存在目标节点,这条线路完成,进入下个线路 + if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) { + continue; + } + if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) { + isSequential = false; + break; + } + // 否则就继续迭代 + isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, hasSequenceFlow, isSequential); + } + } + return isSequential; + } + + /** + * 从后向前寻路,获取到达节点的所有路线 + * 不存在直接回退到子流程,但是存在回退到父级流程的情况 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param roads 路线 + * @return + */ + public static List> findRoad(FlowElement source, List passRoads, Set hasSequenceFlow, List> roads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + roads = roads == null ? new ArrayList<>() : roads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null && sequenceFlows.size() != 0) { + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加经过路线 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + passRoads.add((UserTask) sequenceFlow.getSourceFlowElement()); + } + // 继续迭代 + roads = findRoad(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, roads); + } + } else { + // 添加路线 + roads.add(passRoads); + } + return roads; + } + + /** + * 历史节点数据清洗,清洗掉又回滚导致的脏数据 + * @param allElements 全部节点信息 + * @param historicTaskInstanceList 历史任务实例信息,数据采用开始时间升序 + * @return + */ + public static List historicTaskInstanceClean(Collection allElements, List historicTaskInstanceList) { + // 会签节点收集 + List multiTask = new ArrayList<>(); + allElements.forEach(flowElement -> { + if (flowElement instanceof UserTask) { + // 如果该节点的行为为会签行为,说明该节点为会签节点 + if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) { + multiTask.add(flowElement.getId()); + } + } + }); + // 循环放入栈,栈 LIFO:后进先出 + Stack stack = new Stack<>(); + historicTaskInstanceList.forEach(stack::push); + // 清洗后的历史任务实例 + List lastHistoricTaskInstanceList = new ArrayList<>(); + // 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗 + // 临时用户任务 key + StringBuilder userTaskKey = null; + // 临时被删掉的任务 key,存在并行情况 + List deleteKeyList = new ArrayList<>(); + // 临时脏数据线路 + List> dirtyDataLineList = new ArrayList<>(); + // 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到 + // 会签特殊处理下标 + int multiIndex = -1; + // 会签特殊处理 key + StringBuilder multiKey = null; + // 会签特殊处理操作标识 + boolean multiOpera = false; + while (!stack.empty()) { + // 从这里开始 userTaskKey 都还是上个栈的 key + // 是否是脏数据线路上的点 + final boolean[] isDirtyData = {false}; + for (Set oldDirtyDataLine : dirtyDataLineList) { + if (oldDirtyDataLine.contains(stack.peek().getTaskDefinitionKey())) { + isDirtyData[0] = true; + } + } + // 删除原因不为空,说明从这条数据开始回跳或者回退的 + // MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内 + if (stack.peek().getDeleteReason() != null && !"MI_END".equals(stack.peek().getDeleteReason())) { + // 可以理解为脏线路起点 + String dirtyPoint = ""; + if (stack.peek().getDeleteReason().contains("Change activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", ""); + } + // 会签回退删除原因有点不同 + if (stack.peek().getDeleteReason().contains("Change parent activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", ""); + } + FlowElement dirtyTask = null; + // 获取变更节点的对应的入口处连线 + // 如果是网关并行回退情况,会变成两条脏数据路线,效果一样 + for (FlowElement flowElement : allElements) { + if (flowElement.getId().equals(stack.peek().getTaskDefinitionKey())) { + dirtyTask = flowElement; + } + } + // 获取脏数据线路 + Set dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, Arrays.asList(dirtyPoint.split(",")), null); + // 自己本身也是脏线路上的点,加进去 + dirtyDataLine.add(stack.peek().getTaskDefinitionKey()); + log.info(stack.peek().getTaskDefinitionKey() + "点脏路线集合:" + dirtyDataLine); + // 是全新的需要添加的脏线路 + boolean isNewDirtyData = true; + for (int i = 0; i < dirtyDataLineList.size(); i++) { + // 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回 + // 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全 + if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) { + isNewDirtyData = false; + dirtyDataLineList.get(i).addAll(dirtyDataLine); + } + } + // 已确定时全新的脏线路 + if (isNewDirtyData) { + // deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成 + // 按照逻辑,回退后立刻生成的实例记录就是回退的记录 + // 至于驳回所生成的 Key,直接从删除原因中获取,因为存在驳回到并行的情况 + deleteKeyList.add(dirtyPoint + ","); + dirtyDataLineList.add(dirtyDataLine); + } + // 添加后,现在这个点变成脏线路上的点了 + isDirtyData[0] = true; + } + // 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key + if (!isDirtyData[0]) { + lastHistoricTaskInstanceList.add(stack.peek().getTaskDefinitionKey()); + } + // 校验脏线路是否结束 + for (int i = 0; i < deleteKeyList.size(); i ++) { + // 如果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始 + if (multiKey == null && multiTask.contains(stack.peek().getTaskDefinitionKey()) + && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + multiIndex = i; + multiKey = new StringBuilder(stack.peek().getTaskDefinitionKey()); + } + // 会签脏数据处理,节点退回会签清空 + // 如果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉 + if (multiKey != null && !multiKey.toString().equals(stack.peek().getTaskDefinitionKey())) { + deleteKeyList.set(multiIndex , deleteKeyList.get(multiIndex).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + multiKey = null; + // 结束进行下校验删除 + multiOpera = true; + } + // 其他脏数据处理 + // 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息 + // 脏数据产生的新实例中是否包含这条数据 + if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + // 删除匹配到的部分 + deleteKeyList.set(i , deleteKeyList.get(i).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + } + // 如果每组中的元素都以匹配过,说明脏数据结束 + if ("".equals(deleteKeyList.get(i))) { + // 同时删除脏数据 + deleteKeyList.remove(i); + dirtyDataLineList.remove(i); + break; + } + } + // 会签数据处理需要在循环外处理,否则可能导致溢出 + // 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下 + if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) { + // 同时删除脏数据 + deleteKeyList.remove(multiIndex); + dirtyDataLineList.remove(multiIndex); + multiIndex = -1; + multiOpera = false; + } + // pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除 + // 保存新的 userTaskKey 在下个循环中使用 + userTaskKey = new StringBuilder(stack.pop().getTaskDefinitionKey()); + } + log.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList); + return lastHistoricTaskInstanceList; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java new file mode 100644 index 00000000..1a81e6ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java @@ -0,0 +1,372 @@ +package com.yanzhu.flowable.flow; + +import com.yanzhu.common.core.utils.StringUtils; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.common.engine.impl.util.io.StringStreamSource; + +import java.util.*; + +/** + * @author KonBAI + * @createTime 2022/3/26 19:04 + */ +public class ModelUtils { + + private static final BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter(); + + /** + * xml转bpmnModel对象 + * + * @param xml xml + * @return bpmnModel对象 + */ + public static BpmnModel getBpmnModel(String xml) { + return bpmnXMLConverter.convertToBpmnModel(new StringStreamSource(xml), false, false); + } + + /** + * bpmnModel转xml字符串 + * + * @deprecated 存在会丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml字符串 + */ + @Deprecated + public static String getBpmnXmlStr(BpmnModel bpmnModel) { + return StringUtils.utf8Str(getBpmnXml(bpmnModel)); + } + + /** + * bpmnModel转xml对象 + * + * @deprecated 存在丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml + */ + @Deprecated + public static byte[] getBpmnXml(BpmnModel bpmnModel) { + return bpmnXMLConverter.convertToXML(bpmnModel); + } + + /** + * 根据节点,获取入口连线 + * + * @param source 起始节点 + * @return 入口连线列表 + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } + return sequenceFlows; + } + + + /** + * 根据节点,获取出口连线 + * + * @param source 起始节点 + * @return 出口连线列表 + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取开始节点 + * + * @param model bpmnModel对象 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(BpmnModel model) { + Process process = model.getMainProcess(); + FlowElement startElement = process.getInitialFlowElement(); + if (startElement instanceof StartEvent) { + return (StartEvent) startElement; + } + return getStartEvent(process.getFlowElements()); + } + + /** + * 获取开始节点 + * + * @param flowElements 流程元素集合 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return (StartEvent) flowElement; + } + } + return null; + } + + /** + * 获取结束节点 + * + * @param model bpmnModel对象 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(BpmnModel model) { + Process process = model.getMainProcess(); + return getEndEvent(process.getFlowElements()); + } + + /** + * 获取结束节点 + * + * @param flowElements 流程元素集合 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof EndEvent) { + return (EndEvent) flowElement; + } + } + return null; + } + + public static UserTask getUserTaskByKey(BpmnModel model, String taskKey) { + Process process = model.getMainProcess(); + FlowElement flowElement = process.getFlowElement(taskKey); + if (flowElement instanceof UserTask) { + return (UserTask) flowElement; + } + return null; + } + + /** + * 获取流程元素信息 + * + * @param model bpmnModel对象 + * @param flowElementId 元素ID + * @return 元素信息 + */ + public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { + Process process = model.getMainProcess(); + return process.getFlowElement(flowElementId); + } + + /** + * 获取元素表单Key(限开始节点和用户节点可用) + * + * @param flowElement 元素 + * @return 表单Key + */ + public static String getFormKey(FlowElement flowElement) { + if (flowElement != null) { + if (flowElement instanceof StartEvent) { + return ((StartEvent) flowElement).getFormKey(); + } else if (flowElement instanceof UserTask) { + return ((UserTask) flowElement).getFormKey(); + } + } + return null; + } + + /** + * 获取开始节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getStartEventAttributeValue(BpmnModel model, String name) { + StartEvent startEvent = getStartEvent(model); + return getElementAttributeValue(startEvent, name); + } + + /** + * 获取结束节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getEndEventAttributeValue(BpmnModel model, String name) { + EndEvent endEvent = getEndEvent(model); + return getElementAttributeValue(endEvent, name); + } + + /** + * 获取用户任务节点属性值 + * @param model bpmnModel对象 + * @param taskKey 任务Key + * @param name 属性名 + * @return 属性值 + */ + public static String getUserTaskAttributeValue(BpmnModel model, String taskKey, String name) { + UserTask userTask = getUserTaskByKey(model, taskKey); + return getElementAttributeValue(userTask, name); + } + + /** + * 获取元素属性值 + * @param baseElement 流程元素 + * @param name 属性名 + * @return 属性值 + */ + public static String getElementAttributeValue(BaseElement baseElement, String name) { + if (baseElement != null) { + List attributes = baseElement.getAttributes().get(name); + if (attributes != null && !attributes.isEmpty()) { + attributes.iterator().next().getValue(); + Iterator attrIterator = attributes.iterator(); + if(attrIterator.hasNext()) { + ExtensionAttribute attribute = attrIterator.next(); + return attribute.getValue(); + } + } + } + return null; + } + + public static boolean isMultiInstance(BpmnModel model, String taskKey) { + UserTask userTask = getUserTaskByKey(model, taskKey); + if (!Objects.isNull(userTask)) { + return userTask.hasMultiInstanceLoopCharacteristics(); + } + return false; + } + + /** + * 获取所有用户任务节点 + * + * @param model bpmnModel对象 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(BpmnModel model) { + Process process = model.getMainProcess(); + Collection flowElements = process.getFlowElements(); + return getAllUserTaskEvent(flowElements, null); + } + + /** + * 获取所有用户任务节点 + * @param flowElements 流程元素集合 + * @param allElements 所有流程元素集合 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof UserTask) { + allElements.add((UserTask) flowElement); + } + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = getAllUserTaskEvent(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source) { + return findNextUserTasks(source, null, null); + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 用户任务列表 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source, Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>()); + userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>()); + // 获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (!sequenceFlows.isEmpty()) { + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement(); + if (targetFlowElement instanceof UserTask) { + // 若节点为用户任务,加入到结果列表中 + userTaskList.add((UserTask) targetFlowElement); + } else { + // 若节点非用户任务,继续递归查找下一个节点 + findNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList); + } + } + } + return userTaskList; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param target 目标节点 + * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 + * @return 结果 + */ + 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 (sequenceFlows != null && sequenceFlows.size() > 0) { + // 循环找到目标元素 + 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; + } + // 否则就继续迭代 + boolean isSequential = isSequentialReachable(sourceFlowElement, target, visitedElements); + if (!isSequential) { + return false; + } + } + } + return true; + } + + protected 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; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java new file mode 100644 index 00000000..a937b6ce --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java @@ -0,0 +1,42 @@ +package com.yanzhu.flowable.listener; + +import com.yanzhu.common.redis.service.RedisService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Flowable全局监听器 + * + * @author JiangYuQi + */ +@Slf4j +@Component +public class GlobalEventListener extends AbstractFlowableEngineEventListener { + + @Autowired + private RedisService redisService; + + @Autowired + private TaskService taskService; + + @Autowired + private RuntimeService runtimeService; + + @Autowired + private RepositoryService repositoryService; + + @Override + protected void processCompleted(FlowableEngineEntityEvent event) { + log.info("任务流程审批完成...{}",event.getProcessInstanceId()); + super.processCompleted(event); + } + +} + + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java new file mode 100644 index 00000000..1e6f1f7d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java @@ -0,0 +1,56 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.FlowTaskEntity; + +import java.util.List; +import java.util.Map; + +/** + * @author JiangYuQi + * @date 2021-04-03 14:41 + */ +public interface IFlowBusinessKeyService { + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + public Map quueryCount(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + public List> selectCommentByProcInsId(String procInsId); + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + public Map selectFormDatasByProcInsId(String procInsId); + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity); + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java new file mode 100644 index 00000000..80258211 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java @@ -0,0 +1,81 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:41 + */ +public interface IFlowDefinitionService { + + boolean exist(String processDefinitionKey); + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + TableDataInfo list(FlowQueryVo flowQueryVo); + + /** + * 导入流程文件 + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name + * @param category + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in + */ + void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in); + + /** + * 读取xml + * @param deployId + * @return + */ + AjaxResult readXml(String deployId) throws IOException; + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程ID + * @param applyInfos 申请表单 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map applyInfos); + + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + void updateState(Integer state, String deployId); + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + void delete(String deployId); + + + /** + * 读取图片文件 + * @param deployId + * @return + */ + InputStream readImage(String deployId); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java new file mode 100644 index 00000000..acc69698 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java @@ -0,0 +1,54 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.engine.history.HistoricProcessInstance; + +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:40 + */ +public interface IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + void stopProcessInstance(FlowTaskVo vo); + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + void updateState(Integer state, String instanceId); + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + void delete(String instanceId, String deleteReason); + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId); + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map variables); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java new file mode 100644 index 00000000..c23dd4ec --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java @@ -0,0 +1,208 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.task.api.Task; + +import java.io.InputStream; + +/** + * @author Tony + * @date 2021-04-03 14:42 + */ +public interface IFlowTaskService { + + /** + * 审批任务 + * + * @param task 请求实体参数 + */ + AjaxResult complete(FlowTaskVo task); + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + void taskReject(FlowTaskVo flowTaskVo); + + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + void taskReturn(FlowTaskVo flowTaskVo); + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo); + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + void deleteTask(FlowTaskVo flowTaskVo); + + /** + * 认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void claim(FlowTaskVo flowTaskVo); + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void unClaim(FlowTaskVo flowTaskVo); + + /** + * 委派任务 + * + * @param flowTaskVo 请求实体参数 + */ + void delegateTask(FlowTaskVo flowTaskVo); + + /** + * 任务归还 + * + * @param flowTaskVo 请求实体参数 + */ + void resolveTask(FlowTaskVo flowTaskVo); + + + /** + * 转办任务 + * + * @param flowTaskVo 请求实体参数 + */ + void assignTask(FlowTaskVo flowTaskVo); + + + /** + * 多实例加签 + * @param flowTaskVo + */ + void addMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 多实例减签 + * @param flowTaskVo + */ + void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 我发起的流程 + * @param queryVo 请求参数 + * @return + */ + AjaxResult myProcess(FlowQueryVo queryVo); + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * @param flowTaskVo + * @return + */ + AjaxResult stopProcess(FlowTaskVo flowTaskVo); + + /** + * 撤回流程 + * @param flowTaskVo + * @return + */ + AjaxResult revokeProcess(FlowTaskVo flowTaskVo); + + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult todoList(FlowQueryVo queryVo); + + + /** + * 已办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult finishedList(FlowQueryVo queryVo); + + /** + * 流程历史流转记录 + * + * @param procInsId 流程实例Id + * @return + */ + AjaxResult flowRecord(String procInsId,String deployId); + + /** + * 根据任务ID查询挂载的表单信息 + * + * @param taskId 任务Id + * @return + */ + Task getTaskForm(String taskId); + + /** + * 获取流程过程图 + * @param processId + * @return + */ + InputStream diagram(String processId); + + /** + * 获取流程执行节点 + * @param procInsId + * @return + */ + AjaxResult getFlowViewer(String procInsId,String executionId); + + /** + * 获取流程变量 + * @param taskId + * @return + */ + AjaxResult processVariables(String taskId); + + /** + * 获取下一节点 + * @param flowTaskVo 任务 + * @return + */ + AjaxResult getNextFlowNode(FlowTaskVo flowTaskVo); + + AjaxResult getNextFlowNodeByStart(FlowTaskVo flowTaskVo); + + /** + * 流程初始化表单 + * @param deployId + * @return + */ + AjaxResult flowFormData(String deployId); + + /** + * 流程节点信息 + * @param procInsId + * @return + */ + AjaxResult flowXmlAndNode(String procInsId,String deployId); + + /** + * 流程节点表单 + * @param taskId 流程任务编号 + * @return + */ + AjaxResult flowTaskForm(String taskId) throws Exception; +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java new file mode 100644 index 00000000..01ff72bc --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程实例关联表单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +public interface ISysDeployFormService +{ + /** + * 查询流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 流程实例关联表单 + */ + public SysDeployForm selectSysDeployFormById(Long id); + + /** + * 查询流程实例关联表单列表 + * + * @param sysDeployForm 流程实例关联表单 + * @return 流程实例关联表单集合 + */ + public List selectSysDeployFormList(SysDeployForm sysDeployForm); + + /** + * 新增流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int insertSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 修改流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int updateSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 批量删除流程实例关联表单 + * + * @param ids 需要删除的流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormByIds(Long[] ids); + + /** + * 删除流程实例关联表单信息 + * + * @param id 流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormById(Long id); + + /** + * 查询流程挂着的表单 + * @param deployId + * @return + */ + SysForm selectSysDeployFormByDeployId(String deployId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java new file mode 100644 index 00000000..4f230788 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysExpression; + +import java.util.List; + +/** + * 流程达式Service接口 + * + * @author yanZhu + * @date 2022-12-12 + */ +public interface ISysExpressionService +{ + /** + * 查询流程达式 + * + * @param id 流程达式主键 + * @return 流程达式 + */ + public SysExpression selectSysExpressionById(Long id); + + /** + * 查询流程达式列表 + * + * @param sysExpression 流程达式 + * @return 流程达式集合 + */ + public List selectSysExpressionList(SysExpression sysExpression); + + /** + * 新增流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int insertSysExpression(SysExpression sysExpression); + + /** + * 修改流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int updateSysExpression(SysExpression sysExpression); + + /** + * 批量删除流程达式 + * + * @param ids 需要删除的流程达式主键集合 + * @return 结果 + */ + public int deleteSysExpressionByIds(Long[] ids); + + /** + * 删除流程达式信息 + * + * @param id 流程达式主键 + * @return 结果 + */ + public int deleteSysExpressionById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java new file mode 100644 index 00000000..98a844ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java @@ -0,0 +1,61 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 表单 + * @author Tony + * @date 2021-04-03 + */ +public interface ISysFormService +{ + /** + * 查询流程表单 + * + * @param formId 流程表单ID + * @return 流程表单 + */ + public SysForm selectSysFormById(Long formId); + + /** + * 查询流程表单列表 + * + * @param sysForm 流程表单 + * @return 流程表单集合 + */ + public List selectSysFormList(SysForm sysForm); + + /** + * 新增流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int insertSysForm(SysForm sysForm); + + /** + * 修改流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int updateSysForm(SysForm sysForm); + + /** + * 批量删除流程表单 + * + * @param formIds 需要删除的流程表单ID + * @return 结果 + */ + public int deleteSysFormByIds(Long[] formIds); + + /** + * 删除流程表单信息 + * + * @param formId 流程表单ID + * @return 结果 + */ + public int deleteSysFormById(Long formId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java new file mode 100644 index 00000000..c269e24b --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysTaskForm; + +import java.util.List; + +/** + * 流程任务关联单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +@Deprecated +public interface ISysTaskFormService { + /** + * 查询流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 流程任务关联单 + */ + public SysTaskForm selectSysTaskFormById(Long id); + + /** + * 查询流程任务关联单列表 + * + * @param sysTaskForm 流程任务关联单 + * @return 流程任务关联单集合 + */ + public List selectSysTaskFormList(SysTaskForm sysTaskForm); + + /** + * 新增流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int insertSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 修改流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int updateSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 批量删除流程任务关联单 + * + * @param ids 需要删除的流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormByIds(Long[] ids); + + /** + * 删除流程任务关联单信息 + * + * @param id 流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java new file mode 100644 index 00000000..f5150cbb --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java @@ -0,0 +1,102 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.flowable.domain.FlowTaskEntity; +import com.yanzhu.flowable.mapper.FlowBusinessKeyMapper; +import com.yanzhu.flowable.service.IFlowBusinessKeyService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * 业务工作流程自定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowBusinessKeyServiceImpl implements IFlowBusinessKeyService { + + @Resource + private FlowBusinessKeyMapper flowBusinessKeyMapper; + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity); + } + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public Map quueryCount(FlowTaskEntity flowTaskEntity) { + flowTaskEntity.setActiveName("await"); + int awaitSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + flowTaskEntity.setActiveName("finished"); + int finishedSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + Map dataMap = new HashMap<>(); + dataMap.put("await",awaitSize); + dataMap.put("finished",finishedSize); + return dataMap; + } + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + @Override + public List> selectCommentByProcInsId(String procInsId) { + return flowBusinessKeyMapper.selectCommentByProcInsId(procInsId); + } + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + @Override + public Map selectFormDatasByProcInsId(String procInsId) { + Map dataMap = new HashMap<>(); + List> list = flowBusinessKeyMapper.selectFormDatasByProcInsId(procInsId); + Iterator> it = list.iterator(); + while (it.hasNext()) { + Map map = it.next(); + dataMap.put(Convert.toStr(map.get("name")), map.get("text")); + } + return dataMap; + } + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity){ + return flowBusinessKeyMapper.selectMyAwaitFlowTask(flowTaskEntity); + } + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectMyFinishedFlowTask(flowTaskEntity); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java new file mode 100644 index 00000000..0d9f656f --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java @@ -0,0 +1,279 @@ +package com.yanzhu.flowable.service.impl; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.yanzhu.common.core.constant.HttpStatus; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.ServiceException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.SysExpression; +import com.yanzhu.flowable.domain.vo.FlowDeptVo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.mapper.FlowDeployMapper; +import com.yanzhu.flowable.service.IFlowDefinitionService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.repository.ProcessDefinitionQuery; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; +import org.flowable.task.api.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 流程定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFlowDefinitionService { + + @Autowired + private RedisService redisService; + + @Resource + private FlowDeployMapper flowDeployMapper; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private ISysDeployFormService sysDeployFormService; + + private static final String BPMN_FILE_SUFFIX = ".bpmn"; + + @Override + public boolean exist(String processDefinitionKey) { + ProcessDefinitionQuery processDefinitionQuery + = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey); + long count = processDefinitionQuery.count(); + return count > 0 ? true : false; + } + + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + @Override + public TableDataInfo list(FlowQueryVo flowQueryVo) { + TableDataInfo rspData = new TableDataInfo(); + PageHelper.startPage(flowQueryVo.getPageNum(), flowQueryVo.getPageSize()); + List list = flowDeployMapper.selectDeployList(flowQueryVo); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + + /** + * 导入流程文件 + * + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name 流程名称 + * @param category 流程类型 + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in 流程文件流 + */ + @Override + @Transactional + public void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in) { + if(SecurityUtils.isAdmin(SecurityUtils.getUserId())){ + throw new ServiceException("请选择项目后导入流程文件..."); + } + Deployment deploy = repositoryService.createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).category(category).deploy(); + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), category); + /**新增流程单位关系**/ + FlowDeptVo flowDeptVo = new FlowDeptVo(); + flowDeptVo.setProcdefId(definition.getId()); + //超管不能导入流程文件 + if(SecurityUtils.isGSAdmin()){ + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setDeptId(Convert.toLong(OwnerDeptId)); + } + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setProjId(Convert.toLong(OwnerProjectId)); + } + }else{ + // 从用户信息中获取项目单位信息... + flowDeptVo.setPrtype(prtype); + flowDeptVo.setProjId(SecurityUtils.getLoginUser().getProjectId()); + flowDeptVo.setDeptId(SecurityUtils.getLoginUser().getProjectDeptId()); + } + if(flowDeptVo.getDeptId()!=null){ + flowDeployMapper.insertActReProcdefDept(flowDeptVo); + } + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public AjaxResult readXml(String deployId) throws IOException { + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName()); + String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name()); + return AjaxResult.success("", result); + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public InputStream readImage(String deployId) { + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + //获得图片流 + DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + //输出为图片 + return diagramGenerator.generateDiagram( + bpmnModel, + "png", + Collections.emptyList(), + Collections.emptyList(), + "宋体", + "宋体", + "宋体", + null, + 1.0, + false); + + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程模板ID + * @param applyInfos 流程表单 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map applyInfos) { + try { + /**保存流程表单**/ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+procDefId); + R res; + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsGroupEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsUsersEntity(applyInfos, SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + if(StringUtils.isNotNull(res.getData())){ + Map variables = new HashMap<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId) + .latestVersion().singleResult(); + if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) { + return AjaxResult.error("流程已被挂起,请联系管理员激活流程"); + } + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + String userId = Convert.toStr(sysUser.getUserId()); + // 设置流程发起人Id到流程中 + identityService.setAuthenticatedUserId(userId); + variables.put(ProcessConstants.PROCESS_INITIATOR, userId); + variables.put("businessKey", res.getData()); + ProcessInstance processInstance; + processInstance = runtimeService.startProcessInstanceById(procDefId, Convert.toStr(res.getData()),variables); + // 给第一步申请人节点设置任务执行人和意见 + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult(); + if (Objects.nonNull(task)) { + taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), sysUser.getNickName() + "发起流程申请"); + taskService.setAssignee(task.getId(), Convert.toStr(userId)); + taskService.complete(task.getId(), variables); + } + return AjaxResult.success("流程启动成功"); + }else{ + return AjaxResult.error("流程表单保存失败"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + @Override + public void updateState(Integer state, String deployId) { + ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + // 激活 + if (state == 1) { + repositoryService.activateProcessDefinitionById(procDef.getId(), true, null); + } + // 挂起 + if (state == 2) { + repositoryService.suspendProcessDefinitionById(procDef.getId(), true, null); + } + } + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + @Override + public void delete(String deployId) { + // true 允许级联删除 ,不设置会导致数据库外键关联异常 + repositoryService.deleteDeployment(deployId, true); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java new file mode 100644 index 00000000..4facc05d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java @@ -0,0 +1,117 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.service.IFlowInstanceService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.engine.history.HistoricProcessInstance; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Map; +import java.util.Objects; + +/** + * 工作流流程实例管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowInstanceServiceImpl extends FlowServiceFactory implements IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + @Override + public void stopProcessInstance(FlowTaskVo vo) { + String taskId = vo.getTaskId(); + + } + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + @Override + public void updateState(Integer state, String instanceId) { + + // 激活 + if (state == 1) { + runtimeService.activateProcessInstanceById(instanceId); + } + // 挂起 + if (state == 2) { + runtimeService.suspendProcessInstanceById(instanceId); + } + } + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(String instanceId, String deleteReason) { + + // 查询历史数据 + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(instanceId); + if (historicProcessInstance.getEndTime() != null) { + historyService.deleteHistoricProcessInstance(historicProcessInstance.getId()); + return; + } + // 删除流程实例 + runtimeService.deleteProcessInstance(instanceId, deleteReason); + // 删除历史流程实例 + historyService.deleteHistoricProcessInstance(instanceId); + } + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + @Override + public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) { + HistoricProcessInstance historicProcessInstance = + historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + if (Objects.isNull(historicProcessInstance)) { + throw new FlowableObjectNotFoundException("流程实例不存在: " + processInstanceId); + } + return historicProcessInstance; + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map variables) { + + try { + // 设置流程发起人Id到流程中 + Long userId = SecurityUtils.getUserId(); + variables.put("initiator",userId); + variables.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true); + runtimeService.startProcessInstanceById(procDefId, variables); + return AjaxResult.success("流程启动成功"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java new file mode 100644 index 00000000..50208812 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java @@ -0,0 +1,1307 @@ +package com.yanzhu.flowable.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.CustomException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.*; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.flow.CustomProcessDiagramGenerator; +import com.yanzhu.flowable.flow.FindNextNodeUtil; +import com.yanzhu.flowable.flow.FlowableUtils; +import com.yanzhu.flowable.flow.ModelUtils; +import com.yanzhu.flowable.service.IFlowTaskService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.RemoteUserService; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.impl.cmd.AddMultiInstanceExecutionCmd; +import org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.task.Comment; +import org.flowable.identitylink.api.history.HistoricIdentityLink; +import org.flowable.image.ProcessDiagramGenerator; +import org.flowable.task.api.DelegationState; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * @author Tony + * @date 2021-04-03 + **/ +@Service +@Slf4j +public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTaskService { + + @Autowired + private RedisService redisService; + + @Resource + private ISysFormService sysFormService; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private RemoteUserService remoteUserService; + + @Resource + private RemoteRoleService remoteRoleService; + + @Resource + private ISysDeployFormService sysInstanceFormService; + + /** + * 完成任务 + * + * @param taskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public AjaxResult complete(FlowTaskVo taskVo) { + Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult(); + /**表单不为空时,重新修改表单**/ + if(StringUtils.isNotNull(taskVo.getApplyInfos())){ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+task.getProcessDefinitionId()); + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsGroupEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsUsersEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + } + if (Objects.isNull(task)) { + return AjaxResult.error("任务不存在"); + } + if (DelegationState.PENDING.equals(task.getDelegationState())) { + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment()); + taskService.resolveTask(taskVo.getTaskId()); + } else { + Long userId = SecurityUtils.getUserId(); + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment()); + taskService.setAssignee(taskVo.getTaskId(), Convert.toStr(userId)); + taskService.complete(taskVo.getTaskId()); + } + return AjaxResult.success(); + } + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + @Override + public void taskReject(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态!"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + // 获取节点信息 + source = flowElement; + } + } + } + + // 目的获取所有跳转到的节点 targetIds + // 获取当前节点的所有父级用户任务节点 + // 深度优先算法思想:延边迭代深入 + List parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null); + if (parentUserTaskList == null || parentUserTaskList.size() == 0) { + throw new CustomException("当前节点为初始任务节点,不能驳回"); + } + // 获取活动 ID 即节点 Key + List parentUserTaskKeyList = new ArrayList<>(); + parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId())); + // 获取全部历史节点活动实例,即已经走过的节点历史,数据采用开始时间升序 + List historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list(); + // 数据清洗,将回滚导致的脏数据清洗掉 + List lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList); + // 此时历史任务实例为倒序,获取最后走的节点 + List targetIds = new ArrayList<>(); + // 循环结束标识,遇到当前目标节点的次数 + int number = 0; + StringBuilder parentHistoricTaskKey = new StringBuilder(); + for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) { + // 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过 + if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) { + continue; + } + parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey); + if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) { + number++; + } + // 在数据清洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次 + // 在流程中如果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环 + // number == 1,第一次遇到当前节点 + // number == 2,第二次遇到,代表最后一次的循环范围 + if (number == 2) { + break; + } + // 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点 + if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) { + targetIds.add(historicTaskInstanceKey); + } + } + + + // 目的获取所有需要被跳转的节点 currentIds + // 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路 + UserTask oneUserTask = parentUserTaskList.get(0); + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需驳回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + + // 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况 + if (targetIds.size() > 1 && currentIds.size() > 1) { + throw new CustomException("任务出现多对多情况,无法撤回"); + } + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置驳回意见 + currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment())); + + try { + // 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况 + if (targetIds.size() > 1) { + // 1 对 多任务跳转,currentIds 当前节点(1),targetIds 跳转到的节点(多) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()). + moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState(); + } + // 如果父级任务只有一个,因此当前任务可能为网关中的任务 + if (targetIds.size() == 1) { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetIds.get(0) 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState(); + } + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + + } + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void taskReturn(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + // 获取跳转的节点元素 + FlowElement target = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 当前任务节点元素 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = flowElement; + } + // 跳转的节点元素 + if (flowElement.getId().equals(flowTaskVo.getTargetKey())) { + target = flowElement; + } + } + } + + // 从当前节点向前扫描 + // 如果存在路线上不存在目标节点,说明目标节点是在网关上或非同一路线上,不可跳转 + // 否则目标节点相对于当前节点,属于串行 + Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, flowTaskVo.getTargetKey(), null, null); + if (!isSequential) { + throw new CustomException("当前节点相对于目标节点,不属于串行关系,无法回退"); + } + + + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需退回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置回退意见 + currentTaskIds.forEach(currentTaskId -> taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), flowTaskVo.getComment())); + + try { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, flowTaskVo.getTargetKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + } + + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + @Override + public AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo) { +// // 当前任务 task +// Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); +// // 从流程历史任务中获取可退回节点 +//// List hisActIns = historyService.createHistoricActivityInstanceQuery() +//// .executionId(task.getExecutionId()) +//// .activityType("userTask") +//// .orderByHistoricActivityInstanceStartTime() +//// .finished() +//// .desc() +//// .list(); +//// +//// // 可回退的节点列表 +//// List returnTaskNodeList = new ArrayList<>(); +//// ReturnTaskNodeVo returnTaskNodeVo; +//// for (HistoricActivityInstance activityInstance : hisActIns) { +//// returnTaskNodeVo = new ReturnTaskNodeVo(); +//// returnTaskNodeVo.setId(activityInstance.getActivityId()); +//// // 根据流程节点处理时间校验改节点是否已完成 +//// returnTaskNodeVo.setName(activityInstance.getActivityName()); +//// returnTaskNodeList.add(returnTaskNodeVo); +//// } +// List userTaskList = new ArrayList<>(); +// // 获取流程定义信息 +// ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); +// // 获取所有节点信息,暂不考虑子流程情况 +// Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); +// Collection flowElements = process.getFlowElements(); +// // 获取当前任务节点元素 +// UserTask source = null; +// if (flowElements != null) { +// for (FlowElement flowElement : flowElements) { +// // 类型为用户节点 +// if (flowElement.getId().equals(task.getTaskDefinitionKey())) { +// source = (UserTask) flowElement; +// } +// } +// } +// // 获取节点的所有路线 +// List> roads = FlowableUtils.findRoad(source, null, null, null); +// +// for (List road : roads) { +// if (userTaskList.size() == 0) { +// // 还没有可回退节点直接添加 +// userTaskList = road; +// } else { +// // 如果已有回退节点,则比对取交集部分 +// userTaskList.retainAll(road); +// } +// } +// return AjaxResult.success(userTaskList); + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息,暂不考虑子流程情况 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + Collection flowElements = process.getFlowElements(); + // 获取当前任务节点元素 + UserTask source = null; + if (flowElements != null) { + for (FlowElement flowElement : flowElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = (UserTask) flowElement; + } + } + } + // 获取节点的所有路线 + List> roads = FlowableUtils.findRoad(source, null, null, null); + // 可回退的节点列表 + List userTaskList = new ArrayList<>(); + for (List road : roads) { + if (userTaskList.size() == 0) { + // 还没有可回退节点直接添加 + userTaskList = road; + } else { + // 如果已有回退节点,则比对取交集部分 + userTaskList.retainAll(road); + } + } + return AjaxResult.success(userTaskList); + } + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + public void deleteTask(FlowTaskVo flowTaskVo) { + // todo 待确认删除任务是物理删除任务 还是逻辑删除,让这个任务直接通过? + taskService.deleteTask(flowTaskVo.getTaskId(), flowTaskVo.getComment()); + } + + /** + * 认领/签收任务 + * 认领以后,这个用户就会成为任务的执行人,任务会从其他成员的任务列表中消失 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void claim(FlowTaskVo flowTaskVo) { + taskService.claim(flowTaskVo.getTaskId(), flowTaskVo.getUserId()); + } + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void unClaim(FlowTaskVo flowTaskVo) { + taskService.unclaim(flowTaskVo.getTaskId()); + } + + /** + * 委派任务 + * 任务委派只是委派人将当前的任务交给被委派人进行审批,处理任务后又重新回到委派人身上。 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.DELEGATE.getType(), flowTaskVo.getComment()); + taskService.delegateTask(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + } + + /** + * 任务归还 + * 被委派人完成任务之后,将任务归还委派人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void resolveTask(FlowTaskVo flowTaskVo) { + taskService.resolveTask(flowTaskVo.getTaskId()); + } + + + /** + * 转办任务 + * 直接将办理人换成别人,这时任务的拥有者不再是转办人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void assignTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.ASSIGN.getType(), flowTaskVo.getComment()); + // 直接转派就可以覆盖掉之前的 + taskService.setAssignee(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); +// // 删除指派人重新指派 +// taskService.deleteCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// taskService.addCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// // 如果要查询转给他人处理的任务,可以同时将OWNER进行设置: +// taskService.setOwner(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + + } + + /** + * 多实例加签 + * act_ru_task、act_ru_identitylink各生成一条记录 + * + * @param flowTaskVo + */ + @Override + public void addMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new AddMultiInstanceExecutionCmd(flowTaskVo.getDefId(), flowTaskVo.getInstanceId(), flowTaskVo.getVariables())); + } + + /** + * 多实例减签 + * act_ru_task减1、act_ru_identitylink不变 + * + * @param flowTaskVo + */ + @Override + public void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new DeleteMultiInstanceExecutionCmd(flowTaskVo.getCurrentChildExecutionId(), flowTaskVo.getFlag())); + } + + /** + * 我发起的流程 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult myProcess(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + Long userId = SecurityUtils.getUserId(); + HistoricProcessInstanceQuery historicProcessInstanceQuery; + //超管查询所有数据据 + if(SecurityUtils.isAdmin(userId)){ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .orderByProcessInstanceStartTime() + .desc(); + }else{ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .startedBy(userId.toString()) + .orderByProcessInstanceStartTime() + .desc(); + } + List historicProcessInstances = historicProcessInstanceQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + page.setTotal(historicProcessInstanceQuery.count()); + List flowList = new ArrayList<>(); + for (HistoricProcessInstance hisIns : historicProcessInstances) { + FlowTaskDto flowTask = new FlowTaskDto(); + flowTask.setCreateTime(hisIns.getStartTime()); + flowTask.setFinishTime(hisIns.getEndTime()); + flowTask.setProcInsId(hisIns.getId()); + + // 计算耗时 + if (Objects.nonNull(hisIns.getEndTime())) { + long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } else { + long time = System.currentTimeMillis() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } + // 流程定义信息 + ProcessDefinition pd = repositoryService.createProcessDefinitionQuery() + .processDefinitionId(hisIns.getProcessDefinitionId()) + .singleResult(); + flowTask.setDeployId(pd.getDeploymentId()); + flowTask.setProcDefName(pd.getName()); + flowTask.setProcDefVersion(pd.getVersion()); + flowTask.setCategory(pd.getCategory()); + flowTask.setProcDefVersion(pd.getVersion()); + // 当前所处流程 + List taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list(); + if (CollectionUtils.isNotEmpty(taskList)) { + flowTask.setTaskId(taskList.get(0).getId()); + flowTask.setTaskName(taskList.get(0).getName()); + if (StringUtils.isNotBlank(taskList.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(taskList.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } else { + List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list(); + flowTask.setTaskId(historicTaskInstance.get(0).getId()); + flowTask.setTaskName(historicTaskInstance.get(0).getName()); + if (StringUtils.isNotBlank(historicTaskInstance.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(historicTaskInstance.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } + flowList.add(flowTask); + } + page.setRows(flowList); + return AjaxResult.success(page); + } + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult stopProcess(FlowTaskVo flowTaskVo) { + List task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).list(); + if (CollectionUtils.isEmpty(task)) { + throw new CustomException("流程未启动或已执行完成,取消申请失败"); + } + // 获取当前流程实例 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(flowTaskVo.getInstanceId()) + .singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); + if (Objects.nonNull(bpmnModel)) { + Process process = bpmnModel.getMainProcess(); + List endNodes = process.findFlowElementsOfType(EndEvent.class, false); + if (CollectionUtils.isNotEmpty(endNodes)) { + String userIdStr = Convert.toStr(SecurityUtils.getUserId()); + if(StringUtils.isNotEmpty(userIdStr)){ + Authentication.setAuthenticatedUserId(userIdStr); + taskService.addComment(task.get(0).getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(), + StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment()); + // 设置流程经办人为当前登录人员 + taskService.setAssignee(task.get(0).getId(), userIdStr); + // 获取当前流程最后一个节点 + String endId = endNodes.get(0).getId(); + List executions = runtimeService.createExecutionQuery() + .parentId(processInstance.getProcessInstanceId()).list(); + List executionIds = new ArrayList<>(); + executions.forEach(execution -> executionIds.add(execution.getId())); + // 变更流程为已结束状态 + runtimeService.createChangeActivityStateBuilder() + .moveExecutionsToSingleActivityId(executionIds, endId).changeState(); + return AjaxResult.success(); + } + } + } + return AjaxResult.error(); + } + + /** + * 撤回流程 目前存在错误 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult revokeProcess(FlowTaskVo flowTaskVo) { + String procInsId = flowTaskVo.getInstanceId(); + String taskId = flowTaskVo.getTaskId(); + // 校验流程是否结束 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(procInsId) + .active() + .singleResult(); + if(Objects.isNull(processInstance)) { + throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作"); + } + // 获取待撤回任务实例 + HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .taskAssignee(flowTaskVo.getUserId()) + .singleResult(); + if (Objects.isNull(currTaskIns)) { + throw new RuntimeException("当前任务不存在,无法执行撤回操作"); + } + // 获取 bpmn 模型 + BpmnModel bpmnModel = repositoryService.getBpmnModel(currTaskIns.getProcessDefinitionId()); + UserTask currUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currTaskIns.getTaskDefinitionKey()); + // 查找下一级用户任务列表 + List nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask); + List nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList()); + + // 获取当前节点之后已完成的流程历史节点 + List finishedTaskInsList = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(procInsId) + .taskCreatedAfter(currTaskIns.getEndTime()) + .finished() + .list(); + for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) { + // 检查已完成流程历史节点是否存在下一级中 + if (StringUtils.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) { + throw new RuntimeException("下一流程已处理,无法执行撤回操作"); + } + } + // 获取所有激活的任务节点,找到需要撤回的任务 + List activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list(); + List revokeExecutionIds = new ArrayList<>(); + for (Task task : activateTaskList) { + // 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点 + if (StringUtils.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) { + // 添加撤回审批信息 + taskService.setAssignee(task.getId(), flowTaskVo.getUserId()); + taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), flowTaskVo.getAssignee() + "撤回流程审批"); + revokeExecutionIds.add(task.getExecutionId()); + } + } + try { + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(procInsId) + .moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new RuntimeException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new RuntimeException("执行撤回操作失败"); + } + return AjaxResult.success(); + } + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult todoList(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + // 只查看自己的数据 + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + TaskQuery taskQuery = taskService.createTaskQuery() + .active() + .includeProcessVariables() + .taskCandidateGroupIn(sysUser.getRoles().stream().map(role -> role.getRoleId().toString()).collect(Collectors.toList())) + .taskCandidateOrAssigned(sysUser.getUserId().toString()) + .orderByTaskCreateTime().desc(); + + page.setTotal(taskQuery.count()); + List taskList = taskQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + List
+ * 1、Byte数组和ByteBuffer会被转换为对应字符串的数组 + * 2、对象数组会调用Arrays.toString方法 + *
流程定义
+ * + * @author Tony + * @date 2021-04-03 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@ApiModel("流程定义") +public class FlowProcDefDto implements Serializable { + + @ApiModelProperty("流程id") + private String id; + + @ApiModelProperty("流程名称") + private String name; + + @ApiModelProperty("流程key") + private String flowKey; + + @ApiModelProperty("流程分类") + private String category; + + @ApiModelProperty("流程分类名称") + private String categoryName; + + @ApiModelProperty("配置表单名称") + private String formName; + + @ApiModelProperty("配置表单id") + private Long formId; + + @ApiModelProperty("版本") + private int version; + + @ApiModelProperty("部署ID") + private String deploymentId; + + @ApiModelProperty("流程定义状态: 1:激活 , 2:中止") + private int suspensionState; + + @ApiModelProperty("部署时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date deploymentTime; + + @ApiModelProperty("单位主键") + private Long deptId; + + @ApiModelProperty("单位名称") + private String deptName; + + @ApiModelProperty("项目主键") + private Long projectId; + + @ApiModelProperty("项目名称") + private String projectName; + +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowSaveXmlVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowSaveXmlVo.java new file mode 100644 index 00000000..a06d435a --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowSaveXmlVo.java @@ -0,0 +1,43 @@ +package com.yanzhu.flowable.domain; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author Tony + * @date 2021/3/28 19:48 + */ +@Data +public class FlowSaveXmlVo implements Serializable { + + /** + * 流程名称 + */ + private String name; + + /** + * 流程分类 + */ + private String category; + + /** + * 流程定义类型 + */ + private String prtype; + + /** + * 流程单位 + */ + private String ownerDeptId; + + /** + * 流程项目 + */ + private String OwnerProjectId; + + /** + * xml 文件 + */ + private String xml; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskDto.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskDto.java new file mode 100644 index 00000000..5db80e9b --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskDto.java @@ -0,0 +1,103 @@ +package com.yanzhu.flowable.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; +import java.util.Date; + +/** + *
工作流任务
+ * + * @author Tony + * @date 2021-04-03 + */ +@Getter +@Setter +@ApiModel("工作流任务相关-返回参数") +public class FlowTaskDto implements Serializable { + + @ApiModelProperty("任务编号") + private String taskId; + + @ApiModelProperty("任务执行编号") + private String executionId; + + @ApiModelProperty("任务名称") + private String taskName; + + @ApiModelProperty("任务Key") + private String taskDefKey; + + @ApiModelProperty("任务执行人Id") + private Long assigneeId; + + @ApiModelProperty("部门名称") + private String deptName; + + @ApiModelProperty("流程发起人部门名称") + private String startDeptName; + + @ApiModelProperty("任务执行人名称") + private String assigneeName; + + @ApiModelProperty("任务执行人部门") + private String assigneeDeptName;; + + @ApiModelProperty("流程发起人Id") + private String startUserId; + + @ApiModelProperty("流程发起人名称") + private String startUserName; + + @ApiModelProperty("流程类型") + private String category; + + @ApiModelProperty("流程变量信息") + private Object procVars; + + @ApiModelProperty("局部变量信息") + private Object taskLocalVars; + + @ApiModelProperty("流程部署编号") + private String deployId; + + @ApiModelProperty("流程ID") + private String procDefId; + + @ApiModelProperty("流程key") + private String procDefKey; + + @ApiModelProperty("流程定义名称") + private String procDefName; + + @ApiModelProperty("流程定义内置使用版本") + private int procDefVersion; + + @ApiModelProperty("流程实例ID") + private String procInsId; + + @ApiModelProperty("历史流程实例ID") + private String hisProcInsId; + + @ApiModelProperty("任务耗时") + private String duration; + + @ApiModelProperty("任务意见") + private FlowCommentDto comment; + + @ApiModelProperty("候选执行人") + private String candidate; + + @ApiModelProperty("任务创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @ApiModelProperty("任务完成时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date finishTime; + +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskEntity.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskEntity.java new file mode 100644 index 00000000..33f90b41 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowTaskEntity.java @@ -0,0 +1,370 @@ +package com.yanzhu.flowable.domain; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.yanzhu.common.core.web.domain.BaseEntity; +import io.swagger.annotations.ApiModelProperty; + +import java.util.Date; +import java.util.List; + +/** + *
+ * + * @author JiangYuQi + * @date 2021-04-03 + */ +public class FlowTaskEntity extends BaseEntity { + + private static final long serialVersionUID = 1L; + + private String businessKey; + + @ApiModelProperty("业务名称") + private String businessKeyName; + + @ApiModelProperty("任务编号") + private String taskId; + + @ApiModelProperty("任务执行编号") + private String executionId; + + @ApiModelProperty("任务名称") + private String taskName; + + @ApiModelProperty("任务Key") + private String taskDefKey; + + @ApiModelProperty("任务执行人Id") + private Long assigneeId; + + @ApiModelProperty("部门名称") + private String deptName; + + @ApiModelProperty("流程发起人部门名称") + private String startDeptName; + + @ApiModelProperty("任务执行人名称") + private String assigneeName; + + @ApiModelProperty("任务执行人部门") + private String assigneeDeptName;; + + @ApiModelProperty("流程发起人Id") + private String startUserId; + + @ApiModelProperty("流程发起人名称") + private String startUserName; + + @ApiModelProperty("流程类型") + private String category; + + @ApiModelProperty("流程变量信息") + private Object procVars; + + @ApiModelProperty("局部变量信息") + private Object taskLocalVars; + + @ApiModelProperty("流程部署编号") + private String deployId; + + @ApiModelProperty("流程ID") + private String procDefId; + + @ApiModelProperty("流程key") + private String procDefKey; + + @ApiModelProperty("流程定义名称") + private String procDefName; + + @ApiModelProperty("流程定义内置使用版本") + private int procDefVersion; + + @ApiModelProperty("流程实例ID") + private String procInsId; + + @ApiModelProperty("历史流程实例ID") + private String hisProcInsId; + + @ApiModelProperty("任务耗时") + private String duration; + + @ApiModelProperty("候选执行人") + private String candidate; + + @ApiModelProperty("任务创建时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + @ApiModelProperty("任务完成时间") + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date finishTime; + + @ApiModelProperty("任务完成类型") + private String taskComType; + + private long deptId; + private long projectId; + + @ApiModelProperty("单位祖籍列表") + private String deptAncestors; + + public long getDeptId() { + return deptId; + } + + public void setDeptId(long deptId) { + this.deptId = deptId; + } + + public long getProjectId() { + return projectId; + } + + public void setProjectId(long projectId) { + this.projectId = projectId; + } + + private List roleIds; + + public String getBusinessKeyName() { + return businessKeyName; + } + + public void setBusinessKeyName(String businessKeyName) { + this.businessKeyName = businessKeyName; + } + + public String getTaskId() { + return taskId; + } + + public void setTaskId(String taskId) { + this.taskId = taskId; + } + + public String getExecutionId() { + return executionId; + } + + public void setExecutionId(String executionId) { + this.executionId = executionId; + } + + public String getTaskName() { + return taskName; + } + + public void setTaskName(String taskName) { + this.taskName = taskName; + } + + public String getTaskDefKey() { + return taskDefKey; + } + + public void setTaskDefKey(String taskDefKey) { + this.taskDefKey = taskDefKey; + } + + public Long getAssigneeId() { + return assigneeId; + } + + public void setAssigneeId(Long assigneeId) { + this.assigneeId = assigneeId; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getStartDeptName() { + return startDeptName; + } + + public void setStartDeptName(String startDeptName) { + this.startDeptName = startDeptName; + } + + public String getAssigneeName() { + return assigneeName; + } + + public void setAssigneeName(String assigneeName) { + this.assigneeName = assigneeName; + } + + public String getAssigneeDeptName() { + return assigneeDeptName; + } + + public void setAssigneeDeptName(String assigneeDeptName) { + this.assigneeDeptName = assigneeDeptName; + } + + public String getStartUserId() { + return startUserId; + } + + public void setStartUserId(String startUserId) { + this.startUserId = startUserId; + } + + public String getStartUserName() { + return startUserName; + } + + public void setStartUserName(String startUserName) { + this.startUserName = startUserName; + } + + public String getCategory() { + return category; + } + + public void setCategory(String category) { + this.category = category; + } + + public Object getProcVars() { + return procVars; + } + + public void setProcVars(Object procVars) { + this.procVars = procVars; + } + + public Object getTaskLocalVars() { + return taskLocalVars; + } + + public void setTaskLocalVars(Object taskLocalVars) { + this.taskLocalVars = taskLocalVars; + } + + public String getDeployId() { + return deployId; + } + + public void setDeployId(String deployId) { + this.deployId = deployId; + } + + public String getProcDefId() { + return procDefId; + } + + public void setProcDefId(String procDefId) { + this.procDefId = procDefId; + } + + public String getProcDefKey() { + return procDefKey; + } + + public void setProcDefKey(String procDefKey) { + this.procDefKey = procDefKey; + } + + public String getProcDefName() { + return procDefName; + } + + public void setProcDefName(String procDefName) { + this.procDefName = procDefName; + } + + public int getProcDefVersion() { + return procDefVersion; + } + + public void setProcDefVersion(int procDefVersion) { + this.procDefVersion = procDefVersion; + } + + public String getProcInsId() { + return procInsId; + } + + public void setProcInsId(String procInsId) { + this.procInsId = procInsId; + } + + public String getHisProcInsId() { + return hisProcInsId; + } + + public void setHisProcInsId(String hisProcInsId) { + this.hisProcInsId = hisProcInsId; + } + + public String getDuration() { + return duration; + } + + public void setDuration(String duration) { + this.duration = duration; + } + + public String getCandidate() { + return candidate; + } + + public void setCandidate(String candidate) { + this.candidate = candidate; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public Date getFinishTime() { + return finishTime; + } + + public void setFinishTime(Date finishTime) { + this.finishTime = finishTime; + } + + public List getRoleIds() { + return roleIds; + } + + public void setRoleIds(List roleIds) { + this.roleIds = roleIds; + } + + public String getBusinessKey() { + return businessKey; + } + + public void setBusinessKey(String businessKey) { + this.businessKey = businessKey; + } + + public String getTaskComType() { + return taskComType; + } + + public void setTaskComType(String taskComType) { + this.taskComType = taskComType; + } + + public String getDeptAncestors() { + return deptAncestors; + } + + public void setDeptAncestors(String deptAncestors) { + this.deptAncestors = deptAncestors; + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowViewerDto.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowViewerDto.java new file mode 100644 index 00000000..60b221fb --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/FlowViewerDto.java @@ -0,0 +1,23 @@ +package com.yanzhu.flowable.domain; + +import lombok.Data; + +import java.io.Serializable; + +/** + * @author Tony + * @date 2021/4/21 20:55 + */ +@Data +public class FlowViewerDto implements Serializable { + + /** + * 流程key + */ + private String key; + + /** + * 是否完成(已经审批) + */ + private boolean completed; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysDeployForm.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysDeployForm.java new file mode 100644 index 00000000..f91d88ca --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysDeployForm.java @@ -0,0 +1,64 @@ +package com.yanzhu.flowable.domain; + +import com.yanzhu.common.core.annotation.Excel; +import com.yanzhu.common.core.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 流程实例关联表单对象 sys_instance_form + * + * @author Tony + * @date 2021-03-30 + */ +public class SysDeployForm extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键 */ + private Long id; + + /** 表单主键 */ + @Excel(name = "表单主键") + private Long formId; + + /** 流程定义主键 */ + @Excel(name = "流程定义主键") + private String deployId; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setFormId(Long formId) + { + this.formId = formId; + } + + public Long getFormId() + { + return formId; + } + + public String getDeployId() { + return deployId; + } + + public void setDeployId(String deployId) { + this.deployId = deployId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("formId", getFormId()) + .append("deployId", getDeployId()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysExpression.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysExpression.java new file mode 100644 index 00000000..ab8d405b --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysExpression.java @@ -0,0 +1,83 @@ +package com.yanzhu.flowable.domain; + +import com.yanzhu.common.core.annotation.Excel; +import com.yanzhu.common.core.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 流程达式对象 sys_expression + * + * @author ruoyi + * @date 2022-12-12 + */ +public class SysExpression extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 表单主键 */ + private Long id; + + /** 表达式名称 */ + @Excel(name = "表达式名称") + private String name; + + /** 表达式内容 */ + @Excel(name = "表达式内容") + private String expression; + + /** 状态 */ + private Integer status; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setName(String name) + { + this.name = name; + } + + public String getName() + { + return name; + } + public void setExpression(String expression) + { + this.expression = expression; + } + + public String getExpression() + { + return expression; + } + public void setStatus(Integer status) + { + this.status = status; + } + + public Integer getStatus() + { + return status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("name", getName()) + .append("expression", getExpression()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("createBy", getCreateBy()) + .append("updateBy", getUpdateBy()) + .append("status", getStatus()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysForm.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysForm.java new file mode 100644 index 00000000..c60b2f57 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysForm.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.domain; + +import com.yanzhu.common.core.annotation.Excel; +import com.yanzhu.common.core.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 流程表单对象 sys_task_form + * + * @author Tony + * @date 2021-03-30 + */ +public class SysForm extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 表单主键 */ + private Long formId; + + /** 表单名称 */ + @Excel(name = "表单名称") + private String formName; + + /** 表单内容 */ + @Excel(name = "表单内容") + private String formContent; + + public void setFormId(Long formId) + { + this.formId = formId; + } + + public Long getFormId() + { + return formId; + } + public void setFormName(String formName) + { + this.formName = formName; + } + + public String getFormName() + { + return formName; + } + public void setFormContent(String formContent) + { + this.formContent = formContent; + } + + public String getFormContent() + { + return formContent; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("formId", getFormId()) + .append("formName", getFormName()) + .append("formContent", getFormContent()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("createBy", getCreateBy()) + .append("updateBy", getUpdateBy()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysListener.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysListener.java new file mode 100644 index 00000000..488dc4bf --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysListener.java @@ -0,0 +1,126 @@ +package com.yanzhu.flowable.domain; + +import com.yanzhu.common.core.annotation.Excel; +import com.yanzhu.common.core.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 流程监听对象 sys_listener + * + * @author Tony + * @date 2022-12-25 + */ +public class SysListener extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 表单主键 */ + private Long id; + + /** 名称 */ + @Excel(name = "名称") + private String name; + + /** 监听类型 */ + @Excel(name = "监听类型") + private String type; + + /** 事件类型 */ + @Excel(name = "事件类型") + private String eventType; + + /** 值类型 */ + @Excel(name = "值类型") + private String valueType; + + /** 执行内容 */ + @Excel(name = "执行内容") + private String value; + + /** 状态 */ + @Excel(name = "状态") + private Integer status; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setName(String name) + { + this.name = name; + } + + public String getName() + { + return name; + } + public void setType(String type) + { + this.type = type; + } + + public String getType() + { + return type; + } + public void setEventType(String eventType) + { + this.eventType = eventType; + } + + public String getEventType() + { + return eventType; + } + public void setValueType(String valueType) + { + this.valueType = valueType; + } + + public String getValueType() + { + return valueType; + } + public void setValue(String value) + { + this.value = value; + } + + public String getValue() + { + return value; + } + public void setStatus(Integer status) + { + this.status = status; + } + + public Integer getStatus() + { + return status; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("name", getName()) + .append("type", getType()) + .append("eventType", getEventType()) + .append("valueType", getValueType()) + .append("value", getValue()) + .append("createTime", getCreateTime()) + .append("updateTime", getUpdateTime()) + .append("createBy", getCreateBy()) + .append("updateBy", getUpdateBy()) + .append("status", getStatus()) + .append("remark", getRemark()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysTaskForm.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysTaskForm.java new file mode 100644 index 00000000..6a033889 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/SysTaskForm.java @@ -0,0 +1,65 @@ +package com.yanzhu.flowable.domain; + +import com.yanzhu.common.core.annotation.Excel; +import com.yanzhu.common.core.web.domain.BaseEntity; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + * 流程任务关联单对象 sys_task_form + * + * @author Tony + * @date 2021-04-03 + */ +public class SysTaskForm extends BaseEntity +{ + private static final long serialVersionUID = 1L; + + /** 主键 */ + private Long id; + + /** 表单主键 */ + @Excel(name = "表单主键") + private Long formId; + + /** 所属任务 */ + @Excel(name = "所属任务") + private String taskId; + + public void setId(Long id) + { + this.id = id; + } + + public Long getId() + { + return id; + } + public void setFormId(Long formId) + { + this.formId = formId; + } + + public Long getFormId() + { + return formId; + } + public void setTaskId(String taskId) + { + this.taskId = taskId; + } + + public String getTaskId() + { + return taskId; + } + + @Override + public String toString() { + return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE) + .append("id", getId()) + .append("formId", getFormId()) + .append("taskId", getTaskId()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowDeptVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowDeptVo.java new file mode 100644 index 00000000..fb33f0d1 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowDeptVo.java @@ -0,0 +1,47 @@ +package com.yanzhu.flowable.domain.vo; + +import com.yanzhu.common.core.web.domain.BaseEntity; +import io.swagger.annotations.ApiModel; +import lombok.Data; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + + +/** + * 工作流项目单位关系 + * + * @author JiangYuQi + * @date 2024-02-23 + */ +@Data +@ApiModel("工作流项目单位关系") +public class FlowDeptVo extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** 流程定义主键 */ + private String procdefId; + + /** 流程定义类型 */ + private String prtype; + + /** 流程定义单位 */ + private Long deptId; + + /** 流程定义项目 */ + private Long projId; + + /** 流程定义排序 */ + private String sort; + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("procdefId", getProcdefId()) + .append("prtype", getPrtype()) + .append("deptId", getDeptId()) + .append("projId", getProjId()) + .append("sort", getSort()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowQueryVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowQueryVo.java new file mode 100644 index 00000000..4b08f213 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowQueryVo.java @@ -0,0 +1,49 @@ +package com.yanzhu.flowable.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 流程任务 + * + * @author Tony + * @date 2021-04-03 + */ +@Data +@ApiModel("工作流任务相关--请求参数") +public class FlowQueryVo { + + @ApiModelProperty("流程名称") + private String name; + + @ApiModelProperty("流程类别") + private String category; + + @ApiModelProperty("开始时间") + private String startTime; + + @ApiModelProperty("结束时间") + private String endTime; + + @ApiModelProperty("当前页码") + private Integer pageNum; + + @ApiModelProperty("每页条数") + private Integer pageSize; + + @ApiModelProperty("单位主键") + private Long deptId; + + @ApiModelProperty("单位名称") + private String deptName; + + @ApiModelProperty("项目主键") + private Long projectId; + + @ApiModelProperty("项目名称") + private String projectName; + + @ApiModelProperty("单位祖籍列表") + private String deptAncestors; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowTaskVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowTaskVo.java new file mode 100644 index 00000000..c05a8ba4 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowTaskVo.java @@ -0,0 +1,59 @@ +package com.yanzhu.flowable.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + * 流程任务 + * + * @author Tony + * @date 2021-04-03 + */ +@Data +@ApiModel("工作流任务相关--请求参数") +public class FlowTaskVo { + + @ApiModelProperty("任务Id") + private String taskId; + + @ApiModelProperty("用户Id") + private String userId; + + @ApiModelProperty("任务意见") + private String comment; + + @ApiModelProperty("流程实例Id") + private String instanceId; + + @ApiModelProperty("节点") + private String targetKey; + + private String deploymentId; + @ApiModelProperty("流程环节定义ID") + private String defId; + + @ApiModelProperty("子执行流ID") + private String currentChildExecutionId; + + @ApiModelProperty("子执行流是否已执行") + private Boolean flag; + + @ApiModelProperty("流程变量信息") + private Map variables; + + @ApiModelProperty("审批信息") + private Map applyInfos; + + @ApiModelProperty("审批人") + private String assignee; + + @ApiModelProperty("候选人") + private List candidateUsers; + + @ApiModelProperty("审批组") + private List candidateGroups; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ProcKeyRole.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ProcKeyRole.java new file mode 100644 index 00000000..e31a5612 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ProcKeyRole.java @@ -0,0 +1,50 @@ +package com.yanzhu.flowable.domain.vo; + +import java.io.Serializable; +import java.util.List; + +public class ProcKeyRole implements Serializable +{ + + private static final long serialVersionUID = 1L; + + private String roleId; + + private String key; + + private String sort; + + private List keys; + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public List getKeys() { + return keys; + } + + public void setKeys(List keys) { + this.keys = keys; + } + + public String getSort() { + return sort; + } + + public void setSort(String sort) { + this.sort = sort; + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ReturnTaskNodeVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ReturnTaskNodeVo.java new file mode 100644 index 00000000..0085e543 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ReturnTaskNodeVo.java @@ -0,0 +1,23 @@ +package com.yanzhu.flowable.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 可退回节点 + * + * @author tony + * @date 2022-04-23 11:01:52 + */ +@Data +@ApiModel("可退回节点") +public class ReturnTaskNodeVo { + + @ApiModelProperty("任务Id") + private String id; + + @ApiModelProperty("用户Id") + private String name; + +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java new file mode 100644 index 00000000..b5b29240 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java @@ -0,0 +1,63 @@ +package com.yanzhu.flowable.domain.vo; + +import java.io.Serializable; +import java.util.Map; + +/** + * 流程实例启动 + * + * @author JiangYuQi + * @date 2020-07-07 + */ +public class StartTaskVO implements Serializable +{ + + private static final long serialVersionUID = 1L; + + //流程实例ID + private String procDefId; + private String userId; + private String userName; + private String nickName; + private Map variables; + + public String getProcDefId() { + return procDefId; + } + + public void setProcDefId(String procDefId) { + this.procDefId = procDefId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public Map getVariables() { + return variables; + } + + public void setVariables(Map variables) { + this.variables = variables; + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java new file mode 100644 index 00000000..0f7afa58 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java @@ -0,0 +1,112 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.FlowTaskEntity; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/*** + * 工作流业务相关 + */ +public interface FlowBusinessKeyMapper { + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件查询流程任务 + * @param procInsId + * @return + */ + public FlowTaskEntity findFlowTaskByProcInsId(String procInsId); + + /** + * 查询项目信息 + * @param proId + * @return + */ + public Map selectSurProjectById(Long proId); + + /** + * 根据流程Id查询操作日志 + * @param procInsId + * @return + */ + public List> selectCommentByProcInsId(String procInsId); + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + public Map selectLastCommentByProcInsId(String procInsId); + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + public List> selectFormDatasByProcInsId(String procInsId); + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件查询我的代办分组数量统计 + * @param flowTaskEntity + * @return + */ + public List> findAwaitCountGroupByCategory(FlowTaskEntity flowTaskEntity); + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity); + + public List groupByCategory(FlowTaskEntity where); + + public List groupByUnit(FlowTaskEntity where); + + public List listByUnit(FlowTaskEntity where); + + public List listByState(FlowTaskEntity where); + + public List groupByUnitTotal(FlowTaskEntity where); + + public List groupByUnitFinish(FlowTaskEntity where); + + public List groupByProject(@Param("deptId")long deptId,@Param("proType")String proType); + + /** + * 根据条件查询安全类作业工作流列表 + * @param flowTaskEntity + * @return + */ + public List findSafetyWorkList(FlowTaskEntity flowTaskEntity); + + public List listByCategory(FlowTaskEntity where); + + /** + * 工程管理-分包单位占比 + * @param where + * @return + */ + public List groupFlowBySubDeptType(FlowTaskEntity where); + /** + * 工程管理-分包单位资质审批明细 + * @param where + * @return + */ + public List listFlowBySubDeptType(FlowTaskEntity where); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java new file mode 100644 index 00000000..7d32f616 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java @@ -0,0 +1,76 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.vo.FlowDeptVo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 流程定义查询 + * + * @author Tony + * @email + * @date 2022/1/29 5:44 下午 + **/ +public interface FlowDeployMapper { + + /** + * 流程定义列表 + * @param roleId 发起角色 + * @param category 流程类别 + * @param name 流程名称 + * @return + */ + List selectDeployList(@Param("roleId")String roleId, @Param("category")String category, @Param("name")String name); + + /** + * 我的流程定义列表 + * @param username 登录用户 + * @param category 流程类别 + * @param name 流程名称 + * @return + */ + List selectMyDeployList(@Param("username")String username, @Param("category")String category, @Param("name")String name); + + /** + * 流程定义列表 + * @param flowQueryVo 流程查询 + * @return + */ + List selectDeployList(FlowQueryVo flowQueryVo); + + /** + * 新增单位流程关系 + * + * @param flowDeptVo 流程 单位关系 + * @return 结果 + */ + public int insertActReProcdefDept(FlowDeptVo flowDeptVo); + + /** + * 根据角色删除流程定义 + * @param roleId 橘色 + * @return + */ + int deleteDeployByRoleId(String roleId); + + + /** + * 批量新增流程定义角色配置 + * @param items 列表 + * @return + */ + int batchDeployRoleRole(List> items); + + /** + * 流程发起排序设置 + * @param roleId 角色主键 + * @param key 流程 + * @param sort 排序 + * @return + */ + int updateProcKeyRoleSort(@Param("roleId")String roleId, @Param("key")String key, @Param("sort")String sort); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java new file mode 100644 index 00000000..1f7bd205 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程实例关联表单Mapper接口 + * + * @author Tony + * @date 2021-03-30 + */ +public interface SysDeployFormMapper +{ + /** + * 查询流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 流程实例关联表单 + */ + public SysDeployForm selectSysDeployFormById(Long id); + + /** + * 查询流程实例关联表单列表 + * + * @param SysDeployForm 流程实例关联表单 + * @return 流程实例关联表单集合 + */ + public List selectSysDeployFormList(SysDeployForm SysDeployForm); + + /** + * 新增流程实例关联表单 + * + * @param SysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int insertSysDeployForm(SysDeployForm SysDeployForm); + + /** + * 修改流程实例关联表单 + * + * @param SysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int updateSysDeployForm(SysDeployForm SysDeployForm); + + /** + * 删除流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormById(Long id); + + /** + * 批量删除流程实例关联表单 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteSysDeployFormByIds(Long[] ids); + + /** + * 查询流程挂着的表单 + * @param deployId + * @return + */ + SysForm selectSysDeployFormByDeployId(String deployId); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java new file mode 100644 index 00000000..bdeef941 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysExpression; + +import java.util.List; + +/** + * 流程达式Mapper接口 + * + * @author ruoyi + * @date 2022-12-12 + */ +public interface SysExpressionMapper +{ + /** + * 查询流程达式 + * + * @param id 流程达式主键 + * @return 流程达式 + */ + public SysExpression selectSysExpressionById(Long id); + + /** + * 查询流程达式列表 + * + * @param sysExpression 流程达式 + * @return 流程达式集合 + */ + public List selectSysExpressionList(SysExpression sysExpression); + + /** + * 新增流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int insertSysExpression(SysExpression sysExpression); + + /** + * 修改流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int updateSysExpression(SysExpression sysExpression); + + /** + * 删除流程达式 + * + * @param id 流程达式主键 + * @return 结果 + */ + public int deleteSysExpressionById(Long id); + + /** + * 批量删除流程达式 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteSysExpressionByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java new file mode 100644 index 00000000..183f9630 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程表单Mapper接口 + * + * @author Tony + * @date 2021-03-30 + */ +public interface SysFormMapper +{ + /** + * 查询流程表单 + * + * @param formId 流程表单ID + * @return 流程表单 + */ + public SysForm selectSysFormById(Long formId); + + /** + * 查询流程表单列表 + * + * @param sysForm 流程表单 + * @return 流程表单集合 + */ + public List selectSysFormList(SysForm sysForm); + + /** + * 新增流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int insertSysForm(SysForm sysForm); + + /** + * 修改流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int updateSysForm(SysForm sysForm); + + /** + * 删除流程表单 + * + * @param formId 流程表单ID + * @return 结果 + */ + public int deleteSysFormById(Long formId); + + /** + * 批量删除流程表单 + * + * @param formIds 需要删除的数据ID + * @return 结果 + */ + public int deleteSysFormByIds(Long[] formIds); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java new file mode 100644 index 00000000..c4b43efe --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysListener; + +import java.util.List; + +/** + * 流程监听Mapper接口 + * + * @author Tony + * @date 2022-12-25 + */ +public interface SysListenerMapper +{ + /** + * 查询流程监听 + * + * @param id 流程监听主键 + * @return 流程监听 + */ + public SysListener selectSysListenerById(Long id); + + /** + * 查询流程监听列表 + * + * @param sysListener 流程监听 + * @return 流程监听集合 + */ + public List selectSysListenerList(SysListener sysListener); + + /** + * 新增流程监听 + * + * @param sysListener 流程监听 + * @return 结果 + */ + public int insertSysListener(SysListener sysListener); + + /** + * 修改流程监听 + * + * @param sysListener 流程监听 + * @return 结果 + */ + public int updateSysListener(SysListener sysListener); + + /** + * 删除流程监听 + * + * @param id 流程监听主键 + * @return 结果 + */ + public int deleteSysListenerById(Long id); + + /** + * 批量删除流程监听 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteSysListenerByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java new file mode 100644 index 00000000..0d0a1d68 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysTaskForm; + +import java.util.List; + +/** + * 流程任务关联单Mapper接口 + * + * @author Tony + * @date 2021-04-03 + */ +public interface SysTaskFormMapper +{ + /** + * 查询流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 流程任务关联单 + */ + public SysTaskForm selectSysTaskFormById(Long id); + + /** + * 查询流程任务关联单列表 + * + * @param sysTaskForm 流程任务关联单 + * @return 流程任务关联单集合 + */ + public List selectSysTaskFormList(SysTaskForm sysTaskForm); + + /** + * 新增流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int insertSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 修改流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int updateSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 删除流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormById(Long id); + + /** + * 批量删除流程任务关联单 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteSysTaskFormByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/mapper/domain/init b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/mapper/domain/init deleted file mode 100644 index e69de29b..00000000 diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml new file mode 100644 index 00000000..df7e58d3 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml @@ -0,0 +1,515 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT * FROM + vw_flow_all fa + + and fa.taskName like concat('%', #{taskName}, '%') + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.procDefKey = #{procDefKey} + and fa.businessKey = #{businessKey} + and fa.startDeptName like concat('%', #{startDeptName}, '%') + and fa.startUserName like concat('%', #{startUserName}, '%') + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.startUserId = #{nowUser} + and fa.finishTime is null + and fa.finishTime is not null + + order by fa.createTime desc + + + + + select vf.* as startUserPhone from vw_flow_all vf + where vf.procInsId = #{procInsId} + + + + + select * from vw_flow_comment where procInstId = #{procInstId} order by startTime DESC + + + + select * from vw_flow_comment where procInstId = #{procInstId} order by startTime DESC limit 1 + + + + + select hv.NAME_ as `name`,hv.TEXT_ as text from act_hi_varinst hv where hv.PROC_INST_ID_ = #{procInstId} + + + + + select fa.* from vw_flow_await fa + where + 1=1 + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.businessKey in (select DISTINCT project_id from sur_project_userinfo where user_id=#{nowUser} and is_del=0) + AND (fa.ASSIGNEE_ = #{nowUser} + OR ( + fa.ASSIGNEE_ IS NULL + AND ( + fa.USER_ID_ = #{nowUser} + OR ( + fa.GROUP_ID_ IN + + #{roleId} + + ) + ) + ) + ) + order by fa.createTime desc + + + + + select fa.category,count(1) as total from vw_flow_await fa + where + 1=1 + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.businessKey in (select DISTINCT project_id from sur_project_userinfo where user_id=#{nowUser} and is_del=0) + AND (fa.ASSIGNEE_ = #{nowUser} + OR ( + fa.ASSIGNEE_ IS NULL + AND ( + fa.USER_ID_ = #{nowUser} + OR ( + fa.GROUP_ID_ IN + + #{roleId} + + ) + ) + ) + ) + group by fa.category + + + + + select fa.* from vw_flow_finished fa + where + fa.ASSIGNEE_=#{nowUser} + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.endTime between #{params.beginDate} and #{params.endDate} + order by fa.endTime desc + + + + SELECT a.dict_label taskName,b.cnt assigneeId,c.cnt procDefVersion FROM + ( SELECT * FROM sys_dict_data WHERE dict_type = 'sys_process_category') a + LEFT JOIN (SELECT category,COUNT(1) cnt FROM vw_flow_all WHERE finishTime IS NOT NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + GROUP BY category) b ON a.dict_value=b.category + LEFT JOIN (SELECT category,COUNT(1) cnt FROM vw_flow_all WHERE finishTime IS NULL and taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + GROUP BY category) c ON a.dict_value=c.category + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '总包%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '监理%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '设计%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '甲方%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '工程%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE taskName LIKE '总包%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE taskName LIKE '监理%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE taskName LIKE '设计%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE taskName LIKE '甲方%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE taskName LIKE '工程%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE taskName LIKE '总包%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE taskName LIKE '监理%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE taskName LIKE '设计%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE taskName LIKE '甲方%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE taskName LIKE '工程%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT * FROM vw_flow_all + + and category = #{category} + and businessDeptId=#{deptId} + and businessType = #{proType} + and businessKey=#{projectId} + + and businessKey in + + #{item} + + + + + + + SELECT * FROM vw_flow_all WHERE finishTime IS NULL + + AND taskName LIKE '总包%' + AND taskName LIKE '监理%' + AND taskName LIKE '设计%' + AND taskName LIKE '甲方%' + AND taskName LIKE '工程%' + + and businessDeptId=#{deptId} + and businessType = #{proType} + and businessKey=#{projectId} + + and businessKey in + + #{item} + + + + + + SELECT * FROM vw_flow_all + + + AND taskName!='提交申请' + AND finishTime IS NULL AND taskName!='提交申请' + AND finishTime IS NOT NULL + + and businessDeptId=#{deptId} + and businessKey=#{projectId} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + + + + SELECT a.businessKey,a.cnt duration ,b.projectName businessKeyName FROM ( + SELECT businessKey,COUNT(1) cnt FROM vw_flow_all WHERE taskName!='提交申请' + and businessDeptId=#{deptId} + and businessType = #{proType} + GROUP BY businessKey ) a,sur_project b WHERE a.businessKey=b.id + ORDER BY a.cnt DESC + + + + SELECT + fa.procInsId, + fa.deployId, + fa.createTime, + fa.finishTime, + fa.duration, + fa.procDefKey, + fa.procDefName, + fa.procDefVersion, + fa.category, + fa.businessKey, + fa.businessDeptId, + fa.businessKeyName, + fa.startUserId, + fa.startUserName, + fa.startDeptName, + fa.taskId, + fa.taskName, + fa.assigneeId, + fa.assigneeName, + fa.assigneeDeptName + FROM + vw_flow_all fa + + and fa.businessType = #{proType} + and fa.procDefKey = #{procDefKey} + and fa.businessKey = #{businessKey} + + and fa.businessDeptId = #{projectDeptId} + + order by fa.createTime desc + + + + select * from sur_project where id = #{proId} + + + + select x.dict_label taskName,y.taskId from ( + select dict_value,dict_label + from sys_dict_data where dict_type='flow_sub_dept_type' + ) x left join ( + SELECT d.dict_value, d.dict_label,count(1) taskId FROM + vw_flow_all a + LEFT JOIN act_hi_varinst b ON a.procInsId=b.PROC_INST_ID_ AND b.NAME_='subDeptType' + LEFT JOIN act_hi_varinst c ON a.procInsId=c.PROC_INST_ID_ AND c.NAME_='subDeptName' + LEFT JOIN sys_dict_data D ON b.TEXT_=d.dict_value and d.dict_type='flow_sub_dept_type' + left join sur_project sp on a.businesskey=sp.id + where a.procDefKey='flow_fbzzsp_fbszzsp' + AND (A.finishTime is null OR (A.finishTime is not null and A.taskComType =1)) + and sp.progressVisible=0 and sp.isDel=0 + and a.businessDeptId=#{deptId} + and a.businessKey=#{projectId} + + and a.businessKey in + + #{item} + + + and sp.projectType = #{proType} + group by d.dict_value,d.dict_label + ) y on x.dict_value=y.dict_value + + + SELECT a.procInsId,a.deployId,a.createTime,a.finishTime,a.businessKey,a.businessKeyName,a.startDeptName,b.TEXT_ as taskId, c.TEXT_ as deptName,d.dict_label taskName FROM + vw_flow_all a + LEFT JOIN act_hi_varinst b ON a.procInsId=b.PROC_INST_ID_ AND b.NAME_='subDeptType' + LEFT JOIN act_hi_varinst c ON a.procInsId=c.PROC_INST_ID_ AND c.NAME_='subDeptName' + LEFT JOIN sys_dict_data D ON b.TEXT_=d.dict_value and d.dict_type='flow_sub_dept_type' + left join sur_project sp on a.businesskey=sp.id + where a.procDefKey='flow_fbzzsp_fbszzsp' + AND (A.finishTime is null OR (A.finishTime is not null and A.taskComType =1)) + and sp.progressVisible=0 and sp.isDel=0 + and a.businessDeptId=#{deptId} + and a.businessKey=#{projectId} + + and a.businessKey in + + #{item} + + + and sp.projectType = #{proType} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml new file mode 100644 index 00000000..f8a188d2 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml @@ -0,0 +1,125 @@ + + + + + + SELECT + rp.id_ as id, + rp.deployment_id_ as deploymentId, + rd.name_ as name, + rd.category_ as category, + rp.key_ as flowKey, + rp.version_ as version, + rp.suspension_state_ as suspensionState, + rd.deploy_time_ as deploymentTime, + rpd.DEPT_ID_ as deptId, + sd.dept_name as deptName, + rpd.PROJ_ID_ as projectId, + spi.project_name as projectName + FROM + act_re_procdef rp + LEFT JOIN act_re_deployment rd ON rp.deployment_id_ = rd.id_ + left join act_re_procdef_dept rpd on rp.id_ = rpd.PROCDEF_ID_ + left join sys_dept sd on sd.dept_id = rpd.DEPT_ID_ + left join pro_project_info spi on spi.id = rpd.PROJ_ID_ + + rp.SUSPENSION_STATE_ = 1 + + and rpd.DEPT_ID_ = #{deptId} + + + and sd.dept_name like concat('%', #{deptName}, '%') + + + and rpd.PROJ_ID_ = #{projectId} + + + and spi.project_name like concat('%', #{projectName}, '%') + + + and rd.name_ like concat('%', #{name}, '%') + + + and rd.category_ = #{category} + + + and rd.key_ = #{flowKey} + + + order by rpr.SORT_ + + + + SELECT * FROM ( + SELECT + rp.id_ as id, + rp.deployment_id_ as deploymentId, + rd.name_ as name, + rd.category_ as category, + sdd.dict_label as categoryName, + rp.key_ as flowKey, + rp.version_ as version, + rp.suspension_state_ as suspensionState, + rd.deploy_time_ as deploymentTime, + ROW_NUMBER () OVER ( + PARTITION BY rp.key_ + ORDER BY + rp.version_ DESC + ) AS rn + FROM + act_re_procdef rp + LEFT JOIN act_re_deployment rd ON rp.deployment_id_ = rd.id_ + left join sys_dict_data sdd on sdd.dict_type='sys_process_category' and sdd.dict_value = rd.category_ + left join act_re_procdef_role rpr on rp.key_ = rpr.PROCDEF_KEY_ + left join sys_user_role sur on sur.role_id = rpr.ROLE_ID_ + left join sys_user su on su.user_id = sur.user_id + + rp.SUSPENSION_STATE_ = 1 and su.user_name = #{username} + + and rd.name_ like concat('%', #{name}, '%') + + + and rd.category_ = #{category} + + + order by rd.deploy_time_ desc + ) v WHERE v.rn = 1 + + + + delete from act_re_procdef_role where ROLE_ID_ = #{roleId} + + + + insert into act_re_procdef_role( PROCDEF_KEY_, ROLE_ID_) values + + ( #{item.key}, #{item.roleId}) + + + + + update act_re_procdef_role set SORT_=#{sort} where PROCDEF_KEY_=#{key} and ROLE_ID_=#{roleId} + + + + + insert into act_re_procdef_dept + + PROCDEF_ID_, + TYPE_, + DEPT_ID_, + PROJ_ID_, + SORT_, + + + #{procdefId}, + #{prtype}, + #{deptId}, + #{projId}, + #{sort}, + + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml new file mode 100644 index 00000000..f3423af4 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + select id, form_id, deploy_id from sys_deploy_form + + + + + + and form_id = #{formId} + and deploy_id = #{deployId} + + + + + + where id = #{id} + + + + select t1.form_content as formContent,t1.form_name as formName,t1.form_id as formId from sys_form t1 left join sys_deploy_form t2 on t1.form_id = t2.form_id + where t2.deploy_id = #{deployId} limit 1 + + + + insert into sys_deploy_form + + form_id, + deploy_id, + + + #{formId}, + #{deployId}, + + + + + update sys_deploy_form + + form_id = #{formId}, + deploy_id = #{deployId}, + + where id = #{id} + + + + delete from sys_deploy_form where id = #{id} + + + + delete from sys_deploy_form where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml new file mode 100644 index 00000000..504e522c --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + select id, name, expression, create_time, update_time, create_by, update_by, status, remark from sys_expression + + + + + + and name like concat('%', #{name}, '%') + and expression = #{expression} + and status = #{status} + + + + + + where id = #{id} + + + + insert into sys_expression + + name, + expression, + create_time, + update_time, + create_by, + update_by, + status, + remark, + + + #{name}, + #{expression}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{status}, + #{remark}, + + + + + update sys_expression + + name = #{name}, + expression = #{expression}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + status = #{status}, + remark = #{remark}, + + where id = #{id} + + + + delete from sys_expression where id = #{id} + + + + delete from sys_expression where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml new file mode 100644 index 00000000..6e686742 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + select form_id, form_name, form_content, create_time, update_time, create_by, update_by, remark from sys_form + + + + + + and form_name like concat('%', #{formName}, '%') + and form_content = #{formContent} + + order by create_time desc + + + + + where form_id = #{formId} + + + + insert into sys_form + + form_name, + form_content, + create_time, + update_time, + create_by, + update_by, + remark, + + + #{formName}, + #{formContent}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{remark}, + + + + + update sys_form + + form_name = #{formName}, + form_content = #{formContent}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + remark = #{remark}, + + where form_id = #{formId} + + + + delete from sys_form where form_id = #{formId} + + + + delete from sys_form where form_id in + + #{formId} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml new file mode 100644 index 00000000..a8403a06 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + select id, + name, + type, + event_type, + value_type, + value, + create_time, + update_time, + create_by, + update_by, + status, + remark + from sys_listener + + + + + + and name like concat('%', #{name}, '%') + and type = #{type} + and event_type = #{eventType} + and value_type = #{valueType} + and value = #{value} + and status = #{status} + + + + + + where id = #{id} + + + + insert into sys_listener + + name, + type, + event_type, + value_type, + value, + create_time, + update_time, + create_by, + update_by, + status, + remark, + + + #{name}, + #{type}, + #{eventType}, + #{valueType}, + #{value}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{status}, + #{remark}, + + + + + update sys_listener + + name = #{name}, + type = #{type}, + event_type = #{eventType}, + value_type = #{valueType}, + value = #{value}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + status = #{status}, + remark = #{remark}, + + where id = #{id} + + + + delete + from sys_listener + where id = #{id} + + + + delete from sys_listener where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml new file mode 100644 index 00000000..b7ec90fe --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + select id, form_id, task_id from sys_task_form + + + + + + and form_id = #{formId} + and task_id = #{taskId} + + + + + + where id = #{id} + + + + insert into sys_task_form + + form_id, + task_id, + + + #{formId}, + #{taskId}, + + + + + update sys_task_form + + form_id = #{formId}, + task_id = #{taskId}, + + where id = #{id} + + + + delete from sys_task_form where id = #{id} + + + + delete from sys_task_form where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java b/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java index 7faf4f46..ba3490f9 100644 --- a/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java +++ b/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java @@ -91,6 +91,16 @@ public class SecurityUtils return userId != null && 1L == userId; } + /** + * 是否为公司管理员 + * + * @return 结果 + */ + public static boolean isGSAdmin() + { + return getLoginUser().getRoles().contains("gsAdmin"); + } + /** * 生成BCryptPasswordEncoder密码 * diff --git a/yanzhu-modules/pom.xml b/yanzhu-modules/pom.xml index 20037b42..800e5a5a 100644 --- a/yanzhu-modules/pom.xml +++ b/yanzhu-modules/pom.xml @@ -15,13 +15,14 @@ yanzhu-file yanzhu-manage yanzhu-wechat + yanzhu-flowable yanzhu-modules pom - yanzhu-modules业务模块 + yanzhu-modules分布式模块 diff --git a/yanzhu-modules/yanzhu-flowable/pom.xml b/yanzhu-modules/yanzhu-flowable/pom.xml new file mode 100644 index 00000000..a94efdf1 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/pom.xml @@ -0,0 +1,182 @@ + + + + yanzhu + com.yanzhu + 3.6.2 + ../../pom.xml + + 4.0.0 + + yanzhu-modules-flowable + + + 17 + 17 + + + + yanzhu-modules-flowable工作流模块 + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + io.springfox + springfox-swagger-ui + ${swagger.fox.version} + + + + + org.apache.velocity + velocity-engine-core + + + + + mysql + mysql-connector-java + + + + + com.yanzhu + yanzhu-common-log + + + + + com.yanzhu + yanzhu-common-swagger + + + + org.projectlombok + lombok + provided + + + + io.seata + seata-all + 1.5.2 + compile + + + + + com.yanzhu + yanzhu-common-mapper + 3.6.2 + compile + + + + + com.googlecode.aviator + aviator + 5.3.3 + + + + org.flowable + flowable-spring-boot-starter + + + + org.flowable + flowable-spring-security + + + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + com.spotify + docker-maven-plugin + 1.2.0 + + + ${project.build.finalName} + latest + + openjdk:latest + + ["java", "-jar", "/${project.build.finalName}.jar"] + + + ${configuration.docker-two-Host} + + + / + + ${project.build.directory} + + ${project.build.finalName}.jar + + + + + + build-image + package + + build + + + + + + + + + src/main/resources + true + + + + + \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java new file mode 100644 index 00000000..1edc2bc8 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java @@ -0,0 +1,34 @@ +package com.yanzhu.flowable; + +import com.yanzhu.common.security.annotation.EnableCustomConfig; +import com.yanzhu.common.security.annotation.EnableRyFeignClients; +import com.yanzhu.common.swagger.annotation.EnableCustomSwagger2; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 工作流模块 + * + * @author ruoyi + */ +@EnableCustomConfig +@EnableCustomSwagger2 +@EnableRyFeignClients +@SpringBootApplication +public class YanZhuFlowableApplication +{ + public static void main(String[] args) + { + SpringApplication.run(YanZhuFlowableApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 工作流模块启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java new file mode 100644 index 00000000..54488c3e --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java @@ -0,0 +1,80 @@ +package com.yanzhu.flowable.common.constant; + +/** + * 流程常量信息 + * + * @author Tony + * @date 2021/4/17 22:46 + */ +public class ProcessConstants { + + /** + * 动态数据 + */ + public static final String DYNAMIC = "dynamic"; + + /** + * 固定任务接收 + */ + public static final String FIXED = "fixed"; + + /** + * 单个审批人 + */ + public static final String ASSIGNEE = "assignee"; + + + /** + * 候选人 + */ + public static final String CANDIDATE_USERS = "candidateUsers"; + + + /** + * 审批组 + */ + public static final String CANDIDATE_GROUPS = "candidateGroups"; + + /** + * 单个审批人 + */ + public static final String PROCESS_APPROVAL = "approval"; + + /** + * 会签人员 + */ + public static final String PROCESS_MULTI_INSTANCE_USER = "userList"; + + /** + * nameapace + */ + public static final String NAMASPASE = "http://flowable.org/bpmn"; + + /** + * 会签节点 + */ + public static final String PROCESS_MULTI_INSTANCE = "multiInstance"; + + /** + * 自定义属性 dataType + */ + public static final String PROCESS_CUSTOM_DATA_TYPE = "dataType"; + + /** + * 自定义属性 userType + */ + public static final String PROCESS_CUSTOM_USER_TYPE = "userType"; + + /** + * 初始化人员 + */ + public static final String PROCESS_INITIATOR = "INITIATOR"; + + + /** + * 流程跳过 + */ + public static final String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java new file mode 100644 index 00000000..06aa8bff --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java @@ -0,0 +1,43 @@ +package com.yanzhu.flowable.common.enums; + +/** + * 流程意见类型 + * + * @author Tony + * @date 2021/4/19 + */ +public enum FlowComment { + + /** + * 说明 + */ + NORMAL("1", "正常意见"), + REBACK("2", "退回意见"), + REJECT("3", "驳回意见"), + DELEGATE("4", "委派意见"), + ASSIGN("5", "转办意见"), + STOP("6", "终止流程"), + REVOKE("7", "撤回流程"); + /** + * 类型 + */ + private final String type; + + /** + * 说明 + */ + private final String remark; + + FlowComment(String type, String remark) { + this.type = type; + this.remark = remark; + } + + public String getType() { + return type; + } + + public String getRemark() { + return remark; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java new file mode 100644 index 00000000..414d2d04 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java @@ -0,0 +1,32 @@ +package com.yanzhu.flowable.config; + +import org.flowable.engine.impl.db.DbIdGenerator; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncListenableTaskExecutor; +import org.springframework.core.task.SimpleAsyncTaskExecutor; + +/** + * 流程id生成处理 + * @author Tony + * @date 2022-12-26 10:24 + */ +@Configuration +public class FlowableConfig implements EngineConfigurationConfigurer { + + @Bean + public AsyncListenableTaskExecutor applicationTaskExecutor() { + return new SimpleAsyncTaskExecutor(); + } + + @Override + public void configure(SpringProcessEngineConfiguration engineConfiguration) { + engineConfiguration.setActivityFontName("宋体"); + engineConfiguration.setLabelFontName("宋体"); + engineConfiguration.setAnnotationFontName("宋体"); + engineConfiguration.setIdGenerator(new DbIdGenerator()); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java new file mode 100644 index 00000000..227de838 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java @@ -0,0 +1,41 @@ +package com.yanzhu.flowable.config; + +import com.yanzhu.flowable.listener.GlobalEventListener; +import lombok.RequiredArgsConstructor; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; + +/** + * Flowable添加全局监听器 + * + * @author JiangYuQi + */ +@Configuration +@RequiredArgsConstructor +public class FlowableGlobalListenerConfig implements ApplicationListener { + + private final SpringProcessEngineConfiguration configuration; + + private final GlobalEventListener globalEventListener; + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + FlowableEventDispatcher dispatcher = configuration.getEventDispatcher(); + /** + * 任务创建全局监听-待办消息发送 + * PROCESS_CREATED 流程创建 + * TASK_CREATED 任务创建 + * TASK_COMPLETED 任务完成 + * PROCESS_COMPLETED 流程完成 + * 流程创建、任务创建、任务完成、流程完成 + */ + dispatcher.addEventListener(globalEventListener,FlowableEngineEventType.TASK_CREATED); + dispatcher.addEventListener(globalEventListener,FlowableEngineEventType.PROCESS_COMPLETED); + } + +} + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java new file mode 100644 index 00000000..983f676c --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java @@ -0,0 +1,146 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.FlowTaskEntity; +import com.yanzhu.flowable.service.IFlowBusinessKeyService; +import com.yanzhu.system.api.domain.SysUser; +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * + * 业务工作流程 + * + * + * @author JiangYuQi + * @date 2024-04-03 + */ +@Slf4j +@Api(tags = "业务工作流程") +@RestController +@RequestMapping("/mapper/flowable/businessKey") +public class FlowBusinessKeyController extends BaseController { + + @Autowired + private IFlowBusinessKeyService flowBusinessKeyService; + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/allList") + public TableDataInfo allList(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()); + } + startPage(); + return getDataTable(flowBusinessKeyService.selectAllFlowTaskByParams(flowTaskEntity)); + } + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/queryCount") + public AjaxResult queryCount(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()); + } + return success(flowBusinessKeyService.quueryCount(flowTaskEntity)); + } + + /** + * 根据流程Id查询操作日志 + * @param procInsId + * @return + */ + @GetMapping(value = "/findCommentByProcInsId") + public AjaxResult findCommentByProcInsId(String procInsId) { + return success(flowBusinessKeyService.selectCommentByProcInsId(procInsId)); + } + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + @GetMapping(value = "/findFormDatasByProcInsId") + public AjaxResult findFormDatasByProcInsId(String procInsId) { + return success(flowBusinessKeyService.selectFormDatasByProcInsId(procInsId)); + } + + /** + * 根据条件查询我的代办任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/myAwaitFlowTaskList") + public TableDataInfo myAwaitFlowTaskList(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setAssigneeId(sysUser.getUserId()); + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + flowTaskEntity.setRoleIds(sysUser.getRoles().stream().map(role -> role.getRoleId()).collect(Collectors.toList())); + } + startPage(); + return getDataTable(flowBusinessKeyService.selectMyAwaitFlowTask(flowTaskEntity)); + } + + /** + * 根据条件查询我的已办任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/myFinishedFlowTaskList") + public TableDataInfo myFinishedFlowTaskList(FlowTaskEntity flowTaskEntity) { + startPage(); + flowTaskEntity.setAssigneeId(SecurityUtils.getUserId()); + return getDataTable(flowBusinessKeyService.selectMyFinishedFlowTask(flowTaskEntity)); + } + + /** + * 查询我的代办任务数量 + * @return AjaxResult 统计结果 + */ + @GetMapping(value = "/findMyTask") + public AjaxResult findMyTask() { + FlowTaskEntity flowTaskEntity = new FlowTaskEntity(); + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setAssigneeId(sysUser.getUserId()); + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + flowTaskEntity.setRoleIds(sysUser.getRoles().stream().map(role -> role.getRoleId()).collect(Collectors.toList())); + } + List> list = flowBusinessKeyService.selectMyAwaitFlowTask(flowTaskEntity); + Map data = new HashMap<>(); + if(CollectionUtils.isNotEmpty(list)){ + data.put("todo",list.size()); + }else{ + data.put("todo",0); + } + return success(data); + } + + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java new file mode 100644 index 00000000..c7aa591c --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java @@ -0,0 +1,199 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.FlowSaveXmlVo; +import com.yanzhu.flowable.domain.SysExpression; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.service.IFlowDefinitionService; +import com.yanzhu.flowable.service.ISysExpressionService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.RemoteUserService; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.domain.SysUser; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * + * 工作流程定义 + * + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "流程定义") +@RestController +@RequestMapping("/mapper/flowable/definition") +public class FlowDefinitionController extends BaseController { + + @Autowired + private IFlowDefinitionService flowDefinitionService; + + @Resource + private ISysExpressionService sysExpressionService; + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + @GetMapping(value = "/list") + @ApiOperation(value = "流程定义列表", response = FlowProcDefDto.class) + public TableDataInfo list(FlowQueryVo flowQueryVo) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowQueryVo.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + } + return flowDefinitionService.list(flowQueryVo); + } + + @ApiOperation(value = "导入流程文件", notes = "上传bpmn20的xml文件") + @Log(title = "导入流程文件", businessType = BusinessType.INSERT) + @PostMapping("/import") + public AjaxResult importFile(@RequestParam(required = false) String name, + @RequestParam(required = false) String category, + MultipartFile file) { + InputStream in = null; + try { + in = file.getInputStream(); + flowDefinitionService.importFile(name, category, null, null, null, in); + } catch (Exception e) { + log.error("导入失败:", e); + return AjaxResult.success(e.getMessage()); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + log.error("关闭输入流出错", e); + } + } + + return AjaxResult.success("导入成功"); + } + + @ApiOperation(value = "读取xml文件") + @GetMapping("/readXml/{deployId}") + public AjaxResult readXml(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId) { + try { + return flowDefinitionService.readXml(deployId); + } catch (Exception e) { + return AjaxResult.error("加载xml文件异常"); + } + } + + @ApiOperation(value = "读取图片文件") + @GetMapping("/readImage/{deployId}") + public void readImage(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId, HttpServletResponse response) { + OutputStream os = null; + BufferedImage image = null; + try { + image = ImageIO.read(flowDefinitionService.readImage(deployId)); + response.setContentType("image/png"); + os = response.getOutputStream(); + if (image != null) { + ImageIO.write(image, "png", os); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.flush(); + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @ApiOperation(value = "保存流程设计器内的xml文件") + @Log(title = "保存流程设计", businessType = BusinessType.INSERT) + @PostMapping("/save") + public AjaxResult save(@RequestBody FlowSaveXmlVo vo) { + InputStream in = null; + try { + in = new ByteArrayInputStream(vo.getXml().getBytes(StandardCharsets.UTF_8)); + flowDefinitionService.importFile(vo.getName(), vo.getCategory(), vo.getPrtype(), vo.getOwnerDeptId(), vo.getOwnerProjectId(), in); + } catch (Exception e) { + log.error("导入失败:", e); + return AjaxResult.error(e.getMessage()); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + log.error("关闭输入流出错", e); + } + } + return AjaxResult.success("导入成功"); + } + + + @ApiOperation(value = "发起流程") + @Log(title = "发起流程申请", businessType = BusinessType.INSERT) + @PostMapping("/start/{procDefId}") + public AjaxResult start(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId, + @ApiParam(value = "申请表单") @RequestBody Map applyInfos) { + return flowDefinitionService.startProcessInstanceById(procDefId, applyInfos); + } + + @ApiOperation(value = "激活或挂起流程定义") + @Log(title = "激活/挂起流程", businessType = BusinessType.UPDATE) + @PutMapping(value = "/updateState") + public AjaxResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state, + @ApiParam(value = "流程部署ID", required = true) @RequestParam String deployId) { + flowDefinitionService.updateState(state, deployId); + return AjaxResult.success(); + } + + @ApiOperation(value = "删除流程") + @Log(title = "删除流程定义", businessType = BusinessType.UPDATE) + @DeleteMapping(value = "/{deployIds}") + public AjaxResult delete(@PathVariable String[] deployIds) { + for (String deployId : deployIds) { + flowDefinitionService.delete(deployId); + } + return AjaxResult.success(); + } + + @ApiOperation(value = "指定流程达式列表") + @GetMapping("/expList") + public AjaxResult expList(SysExpression sysExpression) { + List list = sysExpressionService.selectSysExpressionList(sysExpression); + return AjaxResult.success(list); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java new file mode 100644 index 00000000..5850b4c7 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java @@ -0,0 +1,63 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.service.IFlowInstanceService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 工作流流程实例管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "工作流流程实例管理") +@RestController +@RequestMapping("/mapper/flowable/instance") +public class FlowInstanceController { + + @Autowired + private IFlowInstanceService flowInstanceService; + + @ApiOperation(value = "根据流程定义id启动流程实例") + @PostMapping("/startBy/{procDefId}") + public AjaxResult startById(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId, + @ApiParam(value = "变量集合,json对象") @RequestBody Map variables) { + return flowInstanceService.startProcessInstanceById(procDefId, variables); + + } + + + @ApiOperation(value = "激活或挂起流程实例") + @PostMapping(value = "/updateState") + public AjaxResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state, + @ApiParam(value = "流程实例ID", required = true) @RequestParam String instanceId) { + flowInstanceService.updateState(state,instanceId); + return AjaxResult.success(); + } + + @ApiOperation("结束流程实例") + @PostMapping(value = "/stopProcessInstance") + public AjaxResult stopProcessInstance(@RequestBody FlowTaskVo flowTaskVo) { + flowInstanceService.stopProcessInstance(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "删除流程实例") + @DeleteMapping(value = "/delete/{instanceIds}") + public AjaxResult delete(@ApiParam(value = "流程实例ID", required = true) @PathVariable String[] instanceIds, + @ApiParam(value = "删除原因") @RequestParam(required = false) String deleteReason) { + for (String instanceId : instanceIds) { + flowInstanceService.delete(instanceId,deleteReason); + } + return AjaxResult.success(); + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java new file mode 100644 index 00000000..643ef51b --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java @@ -0,0 +1,268 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.flowable.domain.FlowTaskDto; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.service.IFlowTaskService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * 工作流任务管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "工作流流程任务管理") +@RestController +@RequestMapping("/mapper/flowable/task") +public class FlowTaskController { + + @Autowired + private IFlowTaskService flowTaskService; + + @ApiOperation(value = "我发起的流程", response = FlowTaskDto.class) + @GetMapping(value = "/myProcess") + public AjaxResult myProcess(FlowQueryVo queryVo) { + return flowTaskService.myProcess(queryVo); + } + + @ApiOperation(value = "取消申请", response = FlowTaskDto.class) + @Log(title = "终止申请", businessType = BusinessType.UPDATE) + @PostMapping(value = "/stopProcess") + public AjaxResult stopProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.stopProcess(flowTaskVo); + } + + @ApiOperation(value = "撤回流程", response = FlowTaskDto.class) + @Log(title = "撤回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/revokeProcess") + public AjaxResult revokeProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.revokeProcess(flowTaskVo); + } + + @ApiOperation(value = "获取待办列表", response = FlowTaskDto.class) + @GetMapping(value = "/todoList") + public AjaxResult todoList(FlowQueryVo queryVo) { + return flowTaskService.todoList(queryVo); + } + + @ApiOperation(value = "获取已办任务", response = FlowTaskDto.class) + @GetMapping(value = "/finishedList") + public AjaxResult finishedList(FlowQueryVo queryVo) { + return flowTaskService.finishedList(queryVo); + } + + + @ApiOperation(value = "流程历史流转记录", response = FlowTaskDto.class) + @GetMapping(value = "/flowRecord") + public AjaxResult flowRecord(String procInsId, String deployId) { + return flowTaskService.flowRecord(procInsId, deployId); + } + + + @ApiOperation(value = "流程初始化表单", response = FlowTaskDto.class) + @GetMapping(value = "/flowFormData") + public AjaxResult flowFormData(String deployId) { + return flowTaskService.flowFormData(deployId); + } + + @ApiOperation(value = "获取流程变量", response = FlowTaskDto.class) + @GetMapping(value = "/processVariables/{taskId}") + public AjaxResult processVariables(@ApiParam(value = "流程任务Id") @PathVariable(value = "taskId") String taskId) { + return flowTaskService.processVariables(taskId); + } + + @ApiOperation(value = "审批任务") + @Log(title = "审批流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/complete") + public AjaxResult complete(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "重新提交流程") + @Log(title = "重新提交流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/completeAndModify") + public AjaxResult completeAndModify(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "驳回任务") + @Log(title = "驳回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/reject") + public AjaxResult taskReject(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReject(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "退回任务") + @Log(title = "退回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/return") + public AjaxResult taskReturn(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReturn(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "获取所有可回退的节点") + @PostMapping(value = "/returnList") + public AjaxResult findReturnTaskList(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.findReturnTaskList(flowTaskVo); + } + + @ApiOperation(value = "删除任务") + @Log(title = "删除流程", businessType = BusinessType.DELETE) + @DeleteMapping(value = "/delete") + public AjaxResult delete(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "认领/签收任务") + @Log(title = "认领/签收流程", businessType = BusinessType.INSERT) + @PostMapping(value = "/claim") + public AjaxResult claim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.claim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "取消认领/签收任务") + @Log(title = "取消认领/签收流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/unClaim") + public AjaxResult unClaim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.unClaim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "委派任务") + @Log(title = "委派流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/delegateTask") + public AjaxResult delegate(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.delegateTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "任务归还") + @Log(title = "归还流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/resolveTask") + public AjaxResult resolveTask(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.resolveTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "转办任务") + @Log(title = "转办流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/assignTask") + public AjaxResult assign(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.assignTask(flowTaskVo); + return AjaxResult.success(); + } + + @PostMapping(value = "/addMultiInstanceExecution") + @ApiOperation(value = "多实例加签") + public AjaxResult addMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.addMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("加签成功"); + } + + @PostMapping(value = "/deleteMultiInstanceExecution") + @ApiOperation(value = "多实例减签") + public AjaxResult deleteMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("减签成功"); + } + + @ApiOperation(value = "获取下一节点") + @PostMapping(value = "/nextFlowNode") + public AjaxResult getNextFlowNode(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNode(flowTaskVo); + } + + @ApiOperation(value = "流程发起时获取下一节点") + @PostMapping(value = "/nextFlowNodeByStart") + public AjaxResult getNextFlowNodeByStart(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNodeByStart(flowTaskVo); + } + + /** + * 生成流程图 + * + * @param processId 任务ID + */ + @GetMapping("/diagram/{processId}") + public void genProcessDiagram(HttpServletResponse response, + @PathVariable("processId") String processId) { + InputStream inputStream = flowTaskService.diagram(processId); + OutputStream os = null; + BufferedImage image = null; + try { + image = ImageIO.read(inputStream); + response.setContentType("image/png"); + os = response.getOutputStream(); + if (image != null) { + ImageIO.write(image, "png", os); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.flush(); + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 获取流程执行节点 + * + * @param procInsId 流程实例编号 + * @param procInsId 任务执行编号 + */ + @GetMapping("/flowViewer/{procInsId}/{executionId}") + public AjaxResult getFlowViewer(@PathVariable("procInsId") String procInsId, + @PathVariable("executionId") String executionId) { + return flowTaskService.getFlowViewer(procInsId, executionId); + } + + /** + * 流程节点信息 + * + * @param procInsId 流程实例id + * @return + */ + @GetMapping("/flowXmlAndNode") + public AjaxResult flowXmlAndNode(@RequestParam(value = "procInsId", required = false) String procInsId, + @RequestParam(value = "deployId", required = false) String deployId) { + return flowTaskService.flowXmlAndNode(procInsId, deployId); + } + + /** + * 流程节点表单 + * + * @param taskId 流程任务编号 + * @return + */ + @GetMapping("/flowTaskForm") + public AjaxResult flowTaskForm(@RequestParam(value = "taskId", required = false) String taskId) throws Exception { + return flowTaskService.flowTaskForm(taskId); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java new file mode 100644 index 00000000..74cbad80 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java @@ -0,0 +1,112 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.utils.poi.ExcelUtil; +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.common.security.annotation.RequiresPermissions; +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 流程表单Controller + * + * @author Tony + * @date 2021-04-03 + */ +@RestController +@RequestMapping("/mapper/flowable/form") +public class SysFormController extends BaseController { + + @Autowired + private ISysFormService SysFormService; + + @Autowired + private ISysDeployFormService sysDeployFormService; + + /** + * 查询流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:list')") + @GetMapping("/list") + public TableDataInfo list(SysForm sysForm) { + startPage(); + List list = SysFormService.selectSysFormList(sysForm); + return getDataTable(list); + } + + @GetMapping("/formList") + public AjaxResult formList(SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + return AjaxResult.success(list); + } + /** + * 导出流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:export')") + @Log(title = "流程表单", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public void export(HttpServletResponse response, SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + ExcelUtil util = new ExcelUtil(SysForm.class); + util.exportExcel(response, list, "form"); + } + + /** + * 获取流程表单详细信息 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:query')") + @GetMapping(value = "/{formId}") + public AjaxResult getInfo(@PathVariable("formId") Long formId) { + return AjaxResult.success(SysFormService.selectSysFormById(formId)); + } + + /** + * 新增流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:add')") + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.insertSysForm(sysForm)); + } + + /** + * 修改流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:edit')") + @Log(title = "流程表单", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.updateSysForm(sysForm)); + } + + /** + * 删除流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:remove')") + @Log(title = "流程表单", businessType = BusinessType.DELETE) + @DeleteMapping("/{formIds}") + public AjaxResult remove(@PathVariable Long[] formIds) { + return toAjax(SysFormService.deleteSysFormByIds(formIds)); + } + + + /** + * 挂载流程表单 + */ + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping("/addDeployForm") + public AjaxResult addDeployForm(@RequestBody SysDeployForm sysDeployForm) { + return toAjax(sysDeployFormService.insertSysDeployForm(sysDeployForm)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java new file mode 100644 index 00000000..e66efd70 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java @@ -0,0 +1,41 @@ +package com.yanzhu.flowable.factory; + +import lombok.Getter; +import org.flowable.engine.*; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * flowable 引擎注入封装 + * @author Tony + * @date 2021-04-03 + */ +@Component +@Getter +public class FlowServiceFactory { + + @Resource + protected RepositoryService repositoryService; + + @Resource + protected RuntimeService runtimeService; + + @Resource + protected IdentityService identityService; + + @Resource + protected TaskService taskService; + + @Resource + protected HistoryService historyService; + + @Resource + protected ManagementService managementService; + + @Qualifier("processEngine") + @Resource + protected ProcessEngine processEngine; + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java new file mode 100644 index 00000000..7a0baca5 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java @@ -0,0 +1,370 @@ +package com.yanzhu.flowable.flow; + +import org.flowable.bpmn.model.AssociationDirection; +import org.flowable.bpmn.model.GraphicInfo; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.util.ReflectUtil; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; + +/** + * @author Tony + * @date 2021/4/4 23:58 + */ +public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas { + //定义走过流程连线颜色为绿色 + protected static Color HIGHLIGHT_SequenceFlow_COLOR = Color.GREEN; + //设置未走过流程的连接线颜色 + protected static Color CONNECTION_COLOR = Color.BLACK; + //设置flows连接线字体颜色red + protected static Color LABEL_COLOR = new Color(0, 0, 0); + //高亮显示task框颜色 + protected static Color HIGHLIGHT_COLOR = Color.GREEN; + protected static Color HIGHLIGHT_COLOR1 = Color.RED; + + public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + this.initialize(imageType); + } + + /** + * 重写绘制连线的方式,设置绘制颜色 + * @param xPoints + * @param yPoints + * @param conditional + * @param isDefault + * @param connectionType + * @param associationDirection + * @param highLighted + * @param scaleFactor + */ + @Override + public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) { + Paint originalPaint = this.g.getPaint(); + Stroke originalStroke = this.g.getStroke(); + this.g.setPaint(CONNECTION_COLOR); + if (connectionType.equals("association")) { + this.g.setStroke(ASSOCIATION_STROKE); + } else if (highLighted) { + this.g.setPaint(HIGHLIGHT_SequenceFlow_COLOR); + this.g.setStroke(HIGHLIGHT_FLOW_STROKE); + } + + for (int i = 1; i < xPoints.length; ++i) { + Integer sourceX = xPoints[i - 1]; + Integer sourceY = yPoints[i - 1]; + Integer targetX = xPoints[i]; + Integer targetY = yPoints[i]; + java.awt.geom.Line2D.Double line = new java.awt.geom.Line2D.Double((double) sourceX, (double) sourceY, (double) targetX, (double) targetY); + this.g.draw(line); + } + + java.awt.geom.Line2D.Double line; + if (isDefault) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawDefaultSequenceFlowIndicator(line, scaleFactor); + } + + if (conditional) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawConditionalSequenceFlowIndicator(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.ONE) || associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[xPoints.length - 2], (double) yPoints[xPoints.length - 2], (double) xPoints[xPoints.length - 1], (double) yPoints[xPoints.length - 1]); + this.drawArrowHead(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[1], (double) yPoints[1], (double) xPoints[0], (double) yPoints[0]); + this.drawArrowHead(line, scaleFactor); + } + + this.g.setPaint(originalPaint); + this.g.setStroke(originalStroke); + } + + /** + * 设置字体大小图标颜色 + * @param imageType + */ + @Override + public void initialize(String imageType) { + if ("png".equalsIgnoreCase(imageType)) { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 2); + } else { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 1); + } + + this.g = this.processDiagram.createGraphics(); + if (!"png".equalsIgnoreCase(imageType)) { + this.g.setBackground(new Color(255, 255, 255, 0)); + this.g.clearRect(0, 0, this.canvasWidth, this.canvasHeight); + } + + this.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + //修改图标颜色,修改图标字体大小 + this.g.setPaint(Color.black); + Font font = new Font(this.activityFontName, 10, 14); + this.g.setFont(font); + this.fontMetrics = this.g.getFontMetrics(); + //修改连接线字体大小 + LABEL_FONT = new Font(this.labelFontName, 10, 15); + ANNOTATION_FONT = new Font(this.annotationFontName, 0, 11); + + try { + USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/userTask.png", this.customClassLoader)); + SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/scriptTask.png", this.customClassLoader)); + SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/serviceTask.png", this.customClassLoader)); + RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/receiveTask.png", this.customClassLoader)); + SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/sendTask.png", this.customClassLoader)); + MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/manualTask.png", this.customClassLoader)); + BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/businessRuleTask.png", this.customClassLoader)); + SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/shellTask.png", this.customClassLoader)); + DMN_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/dmnTask.png", this.customClassLoader)); + CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/camelTask.png", this.customClassLoader)); + MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/muleTask.png", this.customClassLoader)); + HTTP_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/httpTask.png", this.customClassLoader)); + TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", this.customClassLoader)); + COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate-throw.png", this.customClassLoader)); + COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate.png", this.customClassLoader)); + ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error-throw.png", this.customClassLoader)); + ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error.png", this.customClassLoader)); + MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message-throw.png", this.customClassLoader)); + MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message.png", this.customClassLoader)); + SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal-throw.png", this.customClassLoader)); + SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal.png", this.customClassLoader)); + } catch (IOException var4) { + LOGGER.warn("Could not load image for process diagram creation: {}", var4.getMessage()); + } + + } + + /** + * 设置连接线字体 + * @param text + * @param graphicInfo + * @param centered + */ + @Override + public void drawLabel(String text, GraphicInfo graphicInfo, boolean centered) { + float interline = 1.0f; + + // text + if (text != null && text.length() > 0) { + Paint originalPaint = g.getPaint(); + Font originalFont = g.getFont(); + + g.setPaint(LABEL_COLOR); + g.setFont(LABEL_FONT); + + int wrapWidth = 100; + int textY = (int) graphicInfo.getY(); + + // TODO: use drawMultilineText() + AttributedString as = new AttributedString(text); + as.addAttribute(TextAttribute.FOREGROUND, g.getPaint()); + as.addAttribute(TextAttribute.FONT, g.getFont()); + AttributedCharacterIterator aci = as.getIterator(); + FontRenderContext frc = new FontRenderContext(null, true, false); + LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc); + + while (lbm.getPosition() < text.length()) { + TextLayout tl = lbm.nextLayout(wrapWidth); + textY += tl.getAscent(); + + Rectangle2D bb = tl.getBounds(); + double tX = graphicInfo.getX(); + + if (centered) { + tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2); + } + tl.draw(g, (float) tX, textY); + textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent(); + } + + // restore originals + g.setFont(originalFont); + g.setPaint(originalPaint); + } + } + + /** + * 高亮显示task框完成的 + * @param x + * @param y + * @param width + * @param height + */ + @Override + public void drawHighLight(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义task框当前的位置 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightNow(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR1); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义结束节点 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightEnd(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * task框自定义文字 + * @param name + * @param graphicInfo + * @param thickBorder + * @param scaleFactor + */ + @Override + protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder, double scaleFactor) { + + Paint originalPaint = g.getPaint(); + int x = (int) graphicInfo.getX(); + int y = (int) graphicInfo.getY(); + int width = (int) graphicInfo.getWidth(); + int height = (int) graphicInfo.getHeight(); + + // Create a new gradient paint for every task box, gradient depends on x and y and is not relative + g.setPaint(TASK_BOX_COLOR); + + int arcR = 6; + if (thickBorder) { + arcR = 3; + } + + // shape + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcR, arcR); + g.fill(rect); + g.setPaint(TASK_BORDER_COLOR); + + if (thickBorder) { + Stroke originalStroke = g.getStroke(); + g.setStroke(THICK_TASK_BORDER_STROKE); + g.draw(rect); + g.setStroke(originalStroke); + } else { + g.draw(rect); + } + + g.setPaint(originalPaint); + // text + if (scaleFactor == 1.0 && name != null && name.length() > 0) { + int boxWidth = width - (2 * TEXT_PADDING); + int boxHeight = height - 16 - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2; + int boxX = x + width / 2 - boxWidth / 2; + int boxY = y + height / 2 - boxHeight / 2 + ICON_PADDING + ICON_PADDING - 2 - 2; + + drawMultilineCentredText(name, boxX, boxY, boxWidth, boxHeight); + } + } + + protected static Color EVENT_COLOR = new Color(255, 255, 255); + + /** + * 重写开始事件 + * @param graphicInfo + * @param image + * @param scaleFactor + */ + @Override + public void drawStartEvent(GraphicInfo graphicInfo, BufferedImage image, double scaleFactor) { + Paint originalPaint = g.getPaint(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); + g.draw(circle); + g.setPaint(originalPaint); + if (image != null) { + // calculate coordinates to center image + int imageX = (int) Math.round(graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / (2 * scaleFactor))); + int imageY = (int) Math.round(graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / (2 * scaleFactor))); + g.drawImage(image, imageX, imageY, + (int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null); + } + + } + + /** + * 重写结束事件 + * @param graphicInfo + * @param scaleFactor + */ + @Override + public void drawNoneEndEvent(GraphicInfo graphicInfo, double scaleFactor) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); +// g.setPaint(HIGHLIGHT_COLOR); + if (scaleFactor == 1.0) { + g.setStroke(END_EVENT_STROKE); + } else { + g.setStroke(new BasicStroke(2.0f)); + } + g.draw(circle); + g.setStroke(originalStroke); + g.setPaint(originalPaint); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java new file mode 100644 index 00000000..d55e30dd --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java @@ -0,0 +1,404 @@ +package com.yanzhu.flowable.flow; + + +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; + +import java.util.Iterator; +import java.util.List; + +/** + * @author Tony + * @date 2021/4/5 0:31 + */ +public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator { + @Override + protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + this.prepareBpmnModel(bpmnModel); + DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + Iterator var13 = bpmnModel.getPools().iterator(); + + while (var13.hasNext()) { + Pool process = (Pool) var13.next(); + GraphicInfo subProcesses = bpmnModel.getGraphicInfo(process.getId()); + processDiagramCanvas.drawPoolOrLane(process.getName(), subProcesses, scaleFactor); + } + + var13 = bpmnModel.getProcesses().iterator(); + + Process process1; + Iterator subProcesses1; + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.getLanes().iterator(); + + while (subProcesses1.hasNext()) { + Lane artifact = (Lane) subProcesses1.next(); + GraphicInfo subProcess = bpmnModel.getGraphicInfo(artifact.getId()); + processDiagramCanvas.drawPoolOrLane(artifact.getName(), subProcess, scaleFactor); + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.findFlowElementsOfType(FlowNode.class).iterator(); + + while (subProcesses1.hasNext()) { + FlowNode artifact1 = (FlowNode) subProcesses1.next(); + if (!this.isPartOfCollapsedSubProcess(artifact1, bpmnModel)) { + this.drawActivity(processDiagramCanvas, bpmnModel, artifact1, highLightedActivities, highLightedFlows, scaleFactor, Boolean.valueOf(drawSequenceFlowNameWithNoLabelDI)); + } + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + label75: + while (true) { + List subProcesses2; + do { + if (!var13.hasNext()) { + return processDiagramCanvas; + } + + process1 = (Process) var13.next(); + subProcesses1 = process1.getArtifacts().iterator(); + + while (subProcesses1.hasNext()) { + Artifact artifact2 = (Artifact) subProcesses1.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, artifact2); + } + + subProcesses2 = process1.findFlowElementsOfType(SubProcess.class, true); + } while (subProcesses2 == null); + + Iterator artifact3 = subProcesses2.iterator(); + + while (true) { + GraphicInfo graphicInfo; + SubProcess subProcess1; + do { + do { + if (!artifact3.hasNext()) { + continue label75; + } + + subProcess1 = (SubProcess) artifact3.next(); + graphicInfo = bpmnModel.getGraphicInfo(subProcess1.getId()); + } while (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded().booleanValue()); + } while (this.isPartOfCollapsedSubProcess(subProcess1, bpmnModel)); + + Iterator var19 = subProcess1.getArtifacts().iterator(); + + while (var19.hasNext()) { + Artifact subProcessArtifact = (Artifact) var19.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact); + } + } + } + } + + protected static DefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + double minX = 1.7976931348623157E308D; + double maxX = 0.0D; + double minY = 1.7976931348623157E308D; + double maxY = 0.0D; + + GraphicInfo nrOfLanes; + for (Iterator flowNodes = bpmnModel.getPools().iterator(); flowNodes.hasNext(); maxY = nrOfLanes.getY() + nrOfLanes.getHeight()) { + Pool artifacts = (Pool) flowNodes.next(); + nrOfLanes = bpmnModel.getGraphicInfo(artifacts.getId()); + minX = nrOfLanes.getX(); + maxX = nrOfLanes.getX() + nrOfLanes.getWidth(); + minY = nrOfLanes.getY(); + } + + List var23 = gatherAllFlowNodes(bpmnModel); + Iterator var24 = var23.iterator(); + + label155: + while (var24.hasNext()) { + FlowNode var26 = (FlowNode) var24.next(); + GraphicInfo artifact = bpmnModel.getGraphicInfo(var26.getId()); + if (artifact.getX() + artifact.getWidth() > maxX) { + maxX = artifact.getX() + artifact.getWidth(); + } + + if (artifact.getX() < minX) { + minX = artifact.getX(); + } + + if (artifact.getY() + artifact.getHeight() > maxY) { + maxY = artifact.getY() + artifact.getHeight(); + } + + if (artifact.getY() < minY) { + minY = artifact.getY(); + } + + Iterator process = var26.getOutgoingFlows().iterator(); + + while (true) { + List l; + do { + if (!process.hasNext()) { + continue label155; + } + + SequenceFlow graphicInfoList = (SequenceFlow) process.next(); + l = bpmnModel.getFlowLocationGraphicInfo(graphicInfoList.getId()); + } while (l == null); + + Iterator graphicInfo = l.iterator(); + + while (graphicInfo.hasNext()) { + GraphicInfo graphicInfo1 = (GraphicInfo) graphicInfo.next(); + if (graphicInfo1.getX() > maxX) { + maxX = graphicInfo1.getX(); + } + + if (graphicInfo1.getX() < minX) { + minX = graphicInfo1.getX(); + } + + if (graphicInfo1.getY() > maxY) { + maxY = graphicInfo1.getY(); + } + + if (graphicInfo1.getY() < minY) { + minY = graphicInfo1.getY(); + } + } + } + } + + List var25 = gatherAllArtifacts(bpmnModel); + Iterator var27 = var25.iterator(); + + GraphicInfo var37; + while (var27.hasNext()) { + Artifact var29 = (Artifact) var27.next(); + GraphicInfo var31 = bpmnModel.getGraphicInfo(var29.getId()); + if (var31 != null) { + if (var31.getX() + var31.getWidth() > maxX) { + maxX = var31.getX() + var31.getWidth(); + } + + if (var31.getX() < minX) { + minX = var31.getX(); + } + + if (var31.getY() + var31.getHeight() > maxY) { + maxY = var31.getY() + var31.getHeight(); + } + + if (var31.getY() < minY) { + minY = var31.getY(); + } + } + + List var33 = bpmnModel.getFlowLocationGraphicInfo(var29.getId()); + if (var33 != null) { + Iterator var35 = var33.iterator(); + + while (var35.hasNext()) { + var37 = (GraphicInfo) var35.next(); + if (var37.getX() > maxX) { + maxX = var37.getX(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() > maxY) { + maxY = var37.getY(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + } + + int var28 = 0; + Iterator var30 = bpmnModel.getProcesses().iterator(); + + while (var30.hasNext()) { + Process var32 = (Process) var30.next(); + Iterator var34 = var32.getLanes().iterator(); + + while (var34.hasNext()) { + Lane var36 = (Lane) var34.next(); + ++var28; + var37 = bpmnModel.getGraphicInfo(var36.getId()); + if (var37.getX() + var37.getWidth() > maxX) { + maxX = var37.getX() + var37.getWidth(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() + var37.getHeight() > maxY) { + maxY = var37.getY() + var37.getHeight(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + + if (var23.isEmpty() && bpmnModel.getPools().isEmpty() && var28 == 0) { + minX = 0.0D; + minY = 0.0D; + } + + return new CustomProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + } + + + private static void drawHighLight(DefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightNow(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightNow((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightEnd(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightEnd((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + @Override + protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, + FlowNode flowNode, List highLightedActivities, List highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) { + + DefaultProcessDiagramGenerator.ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass()); + if (drawInstruction != null) { + + drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode); + + // Gather info on the multi instance marker + boolean multiInstanceSequential = false; + boolean multiInstanceParallel = false; + boolean collapsed = false; + if (flowNode instanceof Activity) { + Activity activity = (Activity) flowNode; + MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics(); + if (multiInstanceLoopCharacteristics != null) { + multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential(); + multiInstanceParallel = !multiInstanceSequential; + } + } + + // Gather info on the collapsed marker + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + if (flowNode instanceof SubProcess) { + collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded(); + } else if (flowNode instanceof CallActivity) { + collapsed = true; + } + + if (scaleFactor == 1.0) { + // Actually draw the markers + processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), + multiInstanceSequential, multiInstanceParallel, collapsed); + } + + // Draw highlighted activities + if (highLightedActivities.contains(flowNode.getId())) { + + if (highLightedActivities.get(highLightedActivities.size() - 1).equals(flowNode.getId()) + && !"endenv".equals(flowNode.getId())) { + if ((flowNode.getId().contains("Event_"))) { + drawHighLightEnd((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } else { + drawHighLightNow((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + } else { + drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + + + } + + } + + // Outgoing transitions of activity + for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) { + boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId())); + String defaultFlow = null; + if (flowNode instanceof Activity) { + defaultFlow = ((Activity) flowNode).getDefaultFlow(); + } else if (flowNode instanceof Gateway) { + defaultFlow = ((Gateway) flowNode).getDefaultFlow(); + } + + boolean isDefault = false; + if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) { + isDefault = true; + } + boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && !(flowNode instanceof Gateway); + + String sourceRef = sequenceFlow.getSourceRef(); + String targetRef = sequenceFlow.getTargetRef(); + FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef); + FlowElement targetElement = bpmnModel.getFlowElement(targetRef); + List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId()); + if (graphicInfoList != null && graphicInfoList.size() > 0) { + graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList); + int xPoints[] = new int[graphicInfoList.size()]; + int yPoints[] = new int[graphicInfoList.size()]; + + for (int i = 1; i < graphicInfoList.size(); i++) { + GraphicInfo graphicInfo = graphicInfoList.get(i); + GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); + + if (i == 1) { + xPoints[0] = (int) previousGraphicInfo.getX(); + yPoints[0] = (int) previousGraphicInfo.getY(); + } + xPoints[i] = (int) graphicInfo.getX(); + yPoints[i] = (int) graphicInfo.getY(); + + } + + processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor); + + + // Draw sequenceflow label + GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId()); + if (labelGraphicInfo != null) { + processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false); + } else { + if (drawSequenceFlowNameWithNoLabelDI) { + GraphicInfo lineCenter = getLineCenter(graphicInfoList); + processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false); + } + + } + } + } + + // Nested elements + if (flowNode instanceof FlowElementsContainer) { + for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) { + if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) { + drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement, + highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + } + } + } +} + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java new file mode 100644 index 00000000..a4bd2442 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java @@ -0,0 +1,257 @@ +package com.yanzhu.flowable.flow; + +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.Expression; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.ProcessDefinition; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author Tony + * @date 2021/4/19 20:51 + */ +public class FindNextNodeUtil { + + /** + * 获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasks(RepositoryService repositoryService, org.flowable.task.api.Task task, Map map) { + List data = new ArrayList<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = task.getTaskDefinitionKey(); + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + /** + * 启动流程时获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasksByStart(RepositoryService repositoryService, ProcessDefinition processDefinition, Map map) { + List data = new ArrayList<>(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = null; + // 找到开始节点 并获取唯一key + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + key = flowElement.getId(); + break; + } + } + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + + + /** + * 查找下一节点 + * + * @param flowElements + * @param flowElement + * @param map + * @param nextUser + */ + public static void next(Collection flowElements, FlowElement flowElement, Map map, List nextUser) { + //如果是结束节点 + if (flowElement instanceof EndEvent) { + //如果是子任务的结束节点 + if (getSubProcess(flowElements, flowElement) != null) { + flowElement = getSubProcess(flowElements, flowElement); + } + } + //获取Task的出线信息--可以拥有多个 + List outGoingFlows = null; + if (flowElement instanceof Task) { + outGoingFlows = ((Task) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof Gateway) { + outGoingFlows = ((Gateway) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof StartEvent) { + outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof SubProcess) { + outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof CallActivity) { + outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows(); + } + if (outGoingFlows != null && outGoingFlows.size() > 0) { + //遍历所有的出线--找到可以正确执行的那一条 + for (SequenceFlow sequenceFlow : outGoingFlows) { + //1.有表达式,且为true + //2.无表达式 + String expression = sequenceFlow.getConditionExpression(); + if (expression == null || + expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) { + //出线的下一节点 + String nextFlowElementID = sequenceFlow.getTargetRef(); + if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) { + continue; + } + + //查询下一节点的信息 + FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements); + //调用流程 + if (nextFlowElement instanceof CallActivity) { + CallActivity ca = (CallActivity) nextFlowElement; + if (ca.getLoopCharacteristics() != null) { + UserTask userTask = new UserTask(); + userTask.setId(ca.getId()); + + userTask.setId(ca.getId()); + userTask.setLoopCharacteristics(ca.getLoopCharacteristics()); + userTask.setName(ca.getName()); + nextUser.add(userTask); + } + next(flowElements, nextFlowElement, map, nextUser); + } + //用户任务 + if (nextFlowElement instanceof UserTask) { + nextUser.add((UserTask) nextFlowElement); + } + //排他网关 + else if (nextFlowElement instanceof ExclusiveGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //并行网关 + else if (nextFlowElement instanceof ParallelGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //接收任务 + else if (nextFlowElement instanceof ReceiveTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //服务任务 + else if (nextFlowElement instanceof ServiceTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //子任务的起点 + else if (nextFlowElement instanceof StartEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + //结束节点 + else if (nextFlowElement instanceof EndEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + } + } + } + } + + /** + * 判断是否是多实例子流程并且需要设置集合类型变量 + */ + public static boolean checkSubProcess(String Id, Collection flowElements, List nextUser) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess && flowElement1.getId().equals(Id)) { + + SubProcess sp = (SubProcess) flowElement1; + if (sp.getLoopCharacteristics() != null) { + String inputDataItem = sp.getLoopCharacteristics().getInputDataItem(); + UserTask userTask = new UserTask(); + userTask.setId(sp.getId()); + userTask.setLoopCharacteristics(sp.getLoopCharacteristics()); + userTask.setName(sp.getName()); + nextUser.add(userTask); + return true; + } + } + } + + return false; + + } + + /** + * 查询一个节点的是否子任务中的节点,如果是,返回子任务 + * + * @param flowElements 全流程的节点集合 + * @param flowElement 当前节点 + * @return + */ + public static FlowElement getSubProcess(Collection flowElements, FlowElement flowElement) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess) { + for (FlowElement flowElement2 : ((SubProcess) flowElement1).getFlowElements()) { + if (flowElement.equals(flowElement2)) { + return flowElement1; + } + } + } + } + return null; + } + + + /** + * 根据ID查询流程节点对象, 如果是子任务,则返回子任务的开始节点 + * + * @param Id 节点ID + * @param flowElements 流程节点集合 + * @return + */ + public static FlowElement getFlowElementById(String Id, Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement.getId().equals(Id)) { + //如果是子任务,则查询出子任务的开始节点 + if (flowElement instanceof SubProcess) { + return getStartFlowElement(((SubProcess) flowElement).getFlowElements()); + } + return flowElement; + } + if (flowElement instanceof SubProcess) { + FlowElement flowElement1 = getFlowElementById(Id, ((SubProcess) flowElement).getFlowElements()); + if (flowElement1 != null) { + return flowElement1; + } + } + } + return null; + } + + /** + * 返回流程的开始节点 + * + * @param flowElements 节点集合 + * @description: + */ + public static FlowElement getStartFlowElement(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return flowElement; + } + } + return null; + } + + /** + * 校验el表达式 + * + * @param map + * @param expression + * @return + */ + public static boolean expressionResult(Map map, String expression) { + Expression exp = AviatorEvaluator.compile(expression); + final Object execute = exp.execute(map); + return Boolean.parseBoolean(String.valueOf(execute)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java new file mode 100644 index 00000000..530cf038 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java @@ -0,0 +1,589 @@ +package com.yanzhu.flowable.flow; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.*; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.task.api.history.HistoricTaskInstance; + +import java.util.*; + +/** + * @author Tony + * @date 2021-04-03 23:57 + */ +@Slf4j +public class FlowableUtils { + + /** + * 根据节点,获取入口连线 + * @param source + * @return + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getIncomingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getIncomingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getIncomingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getIncomingFlows(); + } + return sequenceFlows; + } + + /** + * 根据节点,获取出口连线 + * @param source + * @return + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getOutgoingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getOutgoingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getOutgoingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取全部节点列表,包含子流程节点 + * @param flowElements + * @param allElements + * @return + */ + public static Collection getAllElements(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + + for (FlowElement flowElement : flowElements) { + allElements.add(flowElement); + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 迭代获取父级任务节点列表,向前找 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 已找到的用户任务节点 + * @return + */ + public static List iteratorFindParentUserTasks(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 = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 类型为用户节点,则新增父级节点 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); + continue; + } + // 类型为子流程,则添加子流程开始节点出口处相连的节点 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + // 获取子流程用户任务节点 + List childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 + * @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 EndEvent && source.getSubProcess() != null) { + userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + 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 (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 迭代获取子流程用户任务节点 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return + */ + public static List findChildProcessUserTasks(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) { + // 循环找到目标元素 + 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 = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 从后向前寻路,获取所有脏线路上的点 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 目标脏线路终点 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set iteratorFindDirtyRoads(FlowElement source, List passRoads, Set hasSequenceFlow, List targets, Set dirtyRoads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 新增经过的路线 + passRoads.add(sequenceFlow.getSourceFlowElement().getId()); + // 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线 + if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) { + dirtyRoads.addAll(passRoads); + continue; + } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, dirtyRoads); + // 是否存在子流程上,true 是,false 否 + Boolean isInChildProcess = dirtyTargetInChildProcess((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, targets, null); + if (isInChildProcess) { + // 已在子流程上找到,该路线结束 + continue; + } + } + // 继续迭代 + dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 迭代获取子流程脏路线 + * 说明,假如回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set findChildProcessAllDirtyRoad(FlowElement source, Set hasSequenceFlow, Set dirtyRoads) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加脏路线 + dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId()); + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, dirtyRoads); + } + // 继续迭代 + dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 判断脏路线结束节点是否在子流程上 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止 + * @param inChildProcess 是否存在子流程上,true 是,false 否 + * @return + */ + public static Boolean dirtyTargetInChildProcess(FlowElement source, Set hasSequenceFlow, List targets, Boolean inChildProcess) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + inChildProcess = inChildProcess != null && inChildProcess; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null && !inChildProcess) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果发现目标点在子流程上存在,说明只到子流程为止 + if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) { + inChildProcess = true; + break; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + inChildProcess = dirtyTargetInChildProcess((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, targets, inChildProcess); + } + // 继续迭代 + inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, targets, inChildProcess); + } + } + return inChildProcess; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param isSequential 是否串行 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targetKsy 目标节点 + * @return + */ + public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, Set hasSequenceFlow, Boolean isSequential) { + isSequential = isSequential == null || isSequential; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, isSequential); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果目标节点已被判断为并行,后面都不需要执行,直接返回 + if (!isSequential) { + break; + } + // 这条线路存在目标节点,这条线路完成,进入下个线路 + if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) { + continue; + } + if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) { + isSequential = false; + break; + } + // 否则就继续迭代 + isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, hasSequenceFlow, isSequential); + } + } + return isSequential; + } + + /** + * 从后向前寻路,获取到达节点的所有路线 + * 不存在直接回退到子流程,但是存在回退到父级流程的情况 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param roads 路线 + * @return + */ + public static List> findRoad(FlowElement source, List passRoads, Set hasSequenceFlow, List> roads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + roads = roads == null ? new ArrayList<>() : roads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null && sequenceFlows.size() != 0) { + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加经过路线 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + passRoads.add((UserTask) sequenceFlow.getSourceFlowElement()); + } + // 继续迭代 + roads = findRoad(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, roads); + } + } else { + // 添加路线 + roads.add(passRoads); + } + return roads; + } + + /** + * 历史节点数据清洗,清洗掉又回滚导致的脏数据 + * @param allElements 全部节点信息 + * @param historicTaskInstanceList 历史任务实例信息,数据采用开始时间升序 + * @return + */ + public static List historicTaskInstanceClean(Collection allElements, List historicTaskInstanceList) { + // 会签节点收集 + List multiTask = new ArrayList<>(); + allElements.forEach(flowElement -> { + if (flowElement instanceof UserTask) { + // 如果该节点的行为为会签行为,说明该节点为会签节点 + if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) { + multiTask.add(flowElement.getId()); + } + } + }); + // 循环放入栈,栈 LIFO:后进先出 + Stack stack = new Stack<>(); + historicTaskInstanceList.forEach(stack::push); + // 清洗后的历史任务实例 + List lastHistoricTaskInstanceList = new ArrayList<>(); + // 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗 + // 临时用户任务 key + StringBuilder userTaskKey = null; + // 临时被删掉的任务 key,存在并行情况 + List deleteKeyList = new ArrayList<>(); + // 临时脏数据线路 + List> dirtyDataLineList = new ArrayList<>(); + // 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到 + // 会签特殊处理下标 + int multiIndex = -1; + // 会签特殊处理 key + StringBuilder multiKey = null; + // 会签特殊处理操作标识 + boolean multiOpera = false; + while (!stack.empty()) { + // 从这里开始 userTaskKey 都还是上个栈的 key + // 是否是脏数据线路上的点 + final boolean[] isDirtyData = {false}; + for (Set oldDirtyDataLine : dirtyDataLineList) { + if (oldDirtyDataLine.contains(stack.peek().getTaskDefinitionKey())) { + isDirtyData[0] = true; + } + } + // 删除原因不为空,说明从这条数据开始回跳或者回退的 + // MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内 + if (stack.peek().getDeleteReason() != null && !"MI_END".equals(stack.peek().getDeleteReason())) { + // 可以理解为脏线路起点 + String dirtyPoint = ""; + if (stack.peek().getDeleteReason().contains("Change activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", ""); + } + // 会签回退删除原因有点不同 + if (stack.peek().getDeleteReason().contains("Change parent activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", ""); + } + FlowElement dirtyTask = null; + // 获取变更节点的对应的入口处连线 + // 如果是网关并行回退情况,会变成两条脏数据路线,效果一样 + for (FlowElement flowElement : allElements) { + if (flowElement.getId().equals(stack.peek().getTaskDefinitionKey())) { + dirtyTask = flowElement; + } + } + // 获取脏数据线路 + Set dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, Arrays.asList(dirtyPoint.split(",")), null); + // 自己本身也是脏线路上的点,加进去 + dirtyDataLine.add(stack.peek().getTaskDefinitionKey()); + log.info(stack.peek().getTaskDefinitionKey() + "点脏路线集合:" + dirtyDataLine); + // 是全新的需要添加的脏线路 + boolean isNewDirtyData = true; + for (int i = 0; i < dirtyDataLineList.size(); i++) { + // 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回 + // 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全 + if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) { + isNewDirtyData = false; + dirtyDataLineList.get(i).addAll(dirtyDataLine); + } + } + // 已确定时全新的脏线路 + if (isNewDirtyData) { + // deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成 + // 按照逻辑,回退后立刻生成的实例记录就是回退的记录 + // 至于驳回所生成的 Key,直接从删除原因中获取,因为存在驳回到并行的情况 + deleteKeyList.add(dirtyPoint + ","); + dirtyDataLineList.add(dirtyDataLine); + } + // 添加后,现在这个点变成脏线路上的点了 + isDirtyData[0] = true; + } + // 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key + if (!isDirtyData[0]) { + lastHistoricTaskInstanceList.add(stack.peek().getTaskDefinitionKey()); + } + // 校验脏线路是否结束 + for (int i = 0; i < deleteKeyList.size(); i ++) { + // 如果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始 + if (multiKey == null && multiTask.contains(stack.peek().getTaskDefinitionKey()) + && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + multiIndex = i; + multiKey = new StringBuilder(stack.peek().getTaskDefinitionKey()); + } + // 会签脏数据处理,节点退回会签清空 + // 如果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉 + if (multiKey != null && !multiKey.toString().equals(stack.peek().getTaskDefinitionKey())) { + deleteKeyList.set(multiIndex , deleteKeyList.get(multiIndex).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + multiKey = null; + // 结束进行下校验删除 + multiOpera = true; + } + // 其他脏数据处理 + // 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息 + // 脏数据产生的新实例中是否包含这条数据 + if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + // 删除匹配到的部分 + deleteKeyList.set(i , deleteKeyList.get(i).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + } + // 如果每组中的元素都以匹配过,说明脏数据结束 + if ("".equals(deleteKeyList.get(i))) { + // 同时删除脏数据 + deleteKeyList.remove(i); + dirtyDataLineList.remove(i); + break; + } + } + // 会签数据处理需要在循环外处理,否则可能导致溢出 + // 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下 + if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) { + // 同时删除脏数据 + deleteKeyList.remove(multiIndex); + dirtyDataLineList.remove(multiIndex); + multiIndex = -1; + multiOpera = false; + } + // pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除 + // 保存新的 userTaskKey 在下个循环中使用 + userTaskKey = new StringBuilder(stack.pop().getTaskDefinitionKey()); + } + log.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList); + return lastHistoricTaskInstanceList; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java new file mode 100644 index 00000000..1a81e6ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java @@ -0,0 +1,372 @@ +package com.yanzhu.flowable.flow; + +import com.yanzhu.common.core.utils.StringUtils; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.common.engine.impl.util.io.StringStreamSource; + +import java.util.*; + +/** + * @author KonBAI + * @createTime 2022/3/26 19:04 + */ +public class ModelUtils { + + private static final BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter(); + + /** + * xml转bpmnModel对象 + * + * @param xml xml + * @return bpmnModel对象 + */ + public static BpmnModel getBpmnModel(String xml) { + return bpmnXMLConverter.convertToBpmnModel(new StringStreamSource(xml), false, false); + } + + /** + * bpmnModel转xml字符串 + * + * @deprecated 存在会丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml字符串 + */ + @Deprecated + public static String getBpmnXmlStr(BpmnModel bpmnModel) { + return StringUtils.utf8Str(getBpmnXml(bpmnModel)); + } + + /** + * bpmnModel转xml对象 + * + * @deprecated 存在丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml + */ + @Deprecated + public static byte[] getBpmnXml(BpmnModel bpmnModel) { + return bpmnXMLConverter.convertToXML(bpmnModel); + } + + /** + * 根据节点,获取入口连线 + * + * @param source 起始节点 + * @return 入口连线列表 + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } + return sequenceFlows; + } + + + /** + * 根据节点,获取出口连线 + * + * @param source 起始节点 + * @return 出口连线列表 + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取开始节点 + * + * @param model bpmnModel对象 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(BpmnModel model) { + Process process = model.getMainProcess(); + FlowElement startElement = process.getInitialFlowElement(); + if (startElement instanceof StartEvent) { + return (StartEvent) startElement; + } + return getStartEvent(process.getFlowElements()); + } + + /** + * 获取开始节点 + * + * @param flowElements 流程元素集合 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return (StartEvent) flowElement; + } + } + return null; + } + + /** + * 获取结束节点 + * + * @param model bpmnModel对象 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(BpmnModel model) { + Process process = model.getMainProcess(); + return getEndEvent(process.getFlowElements()); + } + + /** + * 获取结束节点 + * + * @param flowElements 流程元素集合 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof EndEvent) { + return (EndEvent) flowElement; + } + } + return null; + } + + public static UserTask getUserTaskByKey(BpmnModel model, String taskKey) { + Process process = model.getMainProcess(); + FlowElement flowElement = process.getFlowElement(taskKey); + if (flowElement instanceof UserTask) { + return (UserTask) flowElement; + } + return null; + } + + /** + * 获取流程元素信息 + * + * @param model bpmnModel对象 + * @param flowElementId 元素ID + * @return 元素信息 + */ + public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { + Process process = model.getMainProcess(); + return process.getFlowElement(flowElementId); + } + + /** + * 获取元素表单Key(限开始节点和用户节点可用) + * + * @param flowElement 元素 + * @return 表单Key + */ + public static String getFormKey(FlowElement flowElement) { + if (flowElement != null) { + if (flowElement instanceof StartEvent) { + return ((StartEvent) flowElement).getFormKey(); + } else if (flowElement instanceof UserTask) { + return ((UserTask) flowElement).getFormKey(); + } + } + return null; + } + + /** + * 获取开始节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getStartEventAttributeValue(BpmnModel model, String name) { + StartEvent startEvent = getStartEvent(model); + return getElementAttributeValue(startEvent, name); + } + + /** + * 获取结束节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getEndEventAttributeValue(BpmnModel model, String name) { + EndEvent endEvent = getEndEvent(model); + return getElementAttributeValue(endEvent, name); + } + + /** + * 获取用户任务节点属性值 + * @param model bpmnModel对象 + * @param taskKey 任务Key + * @param name 属性名 + * @return 属性值 + */ + public static String getUserTaskAttributeValue(BpmnModel model, String taskKey, String name) { + UserTask userTask = getUserTaskByKey(model, taskKey); + return getElementAttributeValue(userTask, name); + } + + /** + * 获取元素属性值 + * @param baseElement 流程元素 + * @param name 属性名 + * @return 属性值 + */ + public static String getElementAttributeValue(BaseElement baseElement, String name) { + if (baseElement != null) { + List attributes = baseElement.getAttributes().get(name); + if (attributes != null && !attributes.isEmpty()) { + attributes.iterator().next().getValue(); + Iterator attrIterator = attributes.iterator(); + if(attrIterator.hasNext()) { + ExtensionAttribute attribute = attrIterator.next(); + return attribute.getValue(); + } + } + } + return null; + } + + public static boolean isMultiInstance(BpmnModel model, String taskKey) { + UserTask userTask = getUserTaskByKey(model, taskKey); + if (!Objects.isNull(userTask)) { + return userTask.hasMultiInstanceLoopCharacteristics(); + } + return false; + } + + /** + * 获取所有用户任务节点 + * + * @param model bpmnModel对象 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(BpmnModel model) { + Process process = model.getMainProcess(); + Collection flowElements = process.getFlowElements(); + return getAllUserTaskEvent(flowElements, null); + } + + /** + * 获取所有用户任务节点 + * @param flowElements 流程元素集合 + * @param allElements 所有流程元素集合 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof UserTask) { + allElements.add((UserTask) flowElement); + } + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = getAllUserTaskEvent(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source) { + return findNextUserTasks(source, null, null); + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 用户任务列表 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source, Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>()); + userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>()); + // 获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (!sequenceFlows.isEmpty()) { + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement(); + if (targetFlowElement instanceof UserTask) { + // 若节点为用户任务,加入到结果列表中 + userTaskList.add((UserTask) targetFlowElement); + } else { + // 若节点非用户任务,继续递归查找下一个节点 + findNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList); + } + } + } + return userTaskList; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param target 目标节点 + * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 + * @return 结果 + */ + 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 (sequenceFlows != null && sequenceFlows.size() > 0) { + // 循环找到目标元素 + 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; + } + // 否则就继续迭代 + boolean isSequential = isSequentialReachable(sourceFlowElement, target, visitedElements); + if (!isSequential) { + return false; + } + } + } + return true; + } + + protected 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; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java new file mode 100644 index 00000000..a937b6ce --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java @@ -0,0 +1,42 @@ +package com.yanzhu.flowable.listener; + +import com.yanzhu.common.redis.service.RedisService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Flowable全局监听器 + * + * @author JiangYuQi + */ +@Slf4j +@Component +public class GlobalEventListener extends AbstractFlowableEngineEventListener { + + @Autowired + private RedisService redisService; + + @Autowired + private TaskService taskService; + + @Autowired + private RuntimeService runtimeService; + + @Autowired + private RepositoryService repositoryService; + + @Override + protected void processCompleted(FlowableEngineEntityEvent event) { + log.info("任务流程审批完成...{}",event.getProcessInstanceId()); + super.processCompleted(event); + } + +} + + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java new file mode 100644 index 00000000..1e6f1f7d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java @@ -0,0 +1,56 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.FlowTaskEntity; + +import java.util.List; +import java.util.Map; + +/** + * @author JiangYuQi + * @date 2021-04-03 14:41 + */ +public interface IFlowBusinessKeyService { + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + public Map quueryCount(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + public List> selectCommentByProcInsId(String procInsId); + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + public Map selectFormDatasByProcInsId(String procInsId); + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity); + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java new file mode 100644 index 00000000..80258211 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java @@ -0,0 +1,81 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:41 + */ +public interface IFlowDefinitionService { + + boolean exist(String processDefinitionKey); + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + TableDataInfo list(FlowQueryVo flowQueryVo); + + /** + * 导入流程文件 + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name + * @param category + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in + */ + void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in); + + /** + * 读取xml + * @param deployId + * @return + */ + AjaxResult readXml(String deployId) throws IOException; + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程ID + * @param applyInfos 申请表单 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map applyInfos); + + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + void updateState(Integer state, String deployId); + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + void delete(String deployId); + + + /** + * 读取图片文件 + * @param deployId + * @return + */ + InputStream readImage(String deployId); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java new file mode 100644 index 00000000..acc69698 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java @@ -0,0 +1,54 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.engine.history.HistoricProcessInstance; + +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:40 + */ +public interface IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + void stopProcessInstance(FlowTaskVo vo); + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + void updateState(Integer state, String instanceId); + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + void delete(String instanceId, String deleteReason); + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId); + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map variables); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java new file mode 100644 index 00000000..c23dd4ec --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java @@ -0,0 +1,208 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.task.api.Task; + +import java.io.InputStream; + +/** + * @author Tony + * @date 2021-04-03 14:42 + */ +public interface IFlowTaskService { + + /** + * 审批任务 + * + * @param task 请求实体参数 + */ + AjaxResult complete(FlowTaskVo task); + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + void taskReject(FlowTaskVo flowTaskVo); + + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + void taskReturn(FlowTaskVo flowTaskVo); + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo); + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + void deleteTask(FlowTaskVo flowTaskVo); + + /** + * 认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void claim(FlowTaskVo flowTaskVo); + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void unClaim(FlowTaskVo flowTaskVo); + + /** + * 委派任务 + * + * @param flowTaskVo 请求实体参数 + */ + void delegateTask(FlowTaskVo flowTaskVo); + + /** + * 任务归还 + * + * @param flowTaskVo 请求实体参数 + */ + void resolveTask(FlowTaskVo flowTaskVo); + + + /** + * 转办任务 + * + * @param flowTaskVo 请求实体参数 + */ + void assignTask(FlowTaskVo flowTaskVo); + + + /** + * 多实例加签 + * @param flowTaskVo + */ + void addMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 多实例减签 + * @param flowTaskVo + */ + void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 我发起的流程 + * @param queryVo 请求参数 + * @return + */ + AjaxResult myProcess(FlowQueryVo queryVo); + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * @param flowTaskVo + * @return + */ + AjaxResult stopProcess(FlowTaskVo flowTaskVo); + + /** + * 撤回流程 + * @param flowTaskVo + * @return + */ + AjaxResult revokeProcess(FlowTaskVo flowTaskVo); + + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult todoList(FlowQueryVo queryVo); + + + /** + * 已办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult finishedList(FlowQueryVo queryVo); + + /** + * 流程历史流转记录 + * + * @param procInsId 流程实例Id + * @return + */ + AjaxResult flowRecord(String procInsId,String deployId); + + /** + * 根据任务ID查询挂载的表单信息 + * + * @param taskId 任务Id + * @return + */ + Task getTaskForm(String taskId); + + /** + * 获取流程过程图 + * @param processId + * @return + */ + InputStream diagram(String processId); + + /** + * 获取流程执行节点 + * @param procInsId + * @return + */ + AjaxResult getFlowViewer(String procInsId,String executionId); + + /** + * 获取流程变量 + * @param taskId + * @return + */ + AjaxResult processVariables(String taskId); + + /** + * 获取下一节点 + * @param flowTaskVo 任务 + * @return + */ + AjaxResult getNextFlowNode(FlowTaskVo flowTaskVo); + + AjaxResult getNextFlowNodeByStart(FlowTaskVo flowTaskVo); + + /** + * 流程初始化表单 + * @param deployId + * @return + */ + AjaxResult flowFormData(String deployId); + + /** + * 流程节点信息 + * @param procInsId + * @return + */ + AjaxResult flowXmlAndNode(String procInsId,String deployId); + + /** + * 流程节点表单 + * @param taskId 流程任务编号 + * @return + */ + AjaxResult flowTaskForm(String taskId) throws Exception; +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java new file mode 100644 index 00000000..01ff72bc --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程实例关联表单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +public interface ISysDeployFormService +{ + /** + * 查询流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 流程实例关联表单 + */ + public SysDeployForm selectSysDeployFormById(Long id); + + /** + * 查询流程实例关联表单列表 + * + * @param sysDeployForm 流程实例关联表单 + * @return 流程实例关联表单集合 + */ + public List selectSysDeployFormList(SysDeployForm sysDeployForm); + + /** + * 新增流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int insertSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 修改流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int updateSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 批量删除流程实例关联表单 + * + * @param ids 需要删除的流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormByIds(Long[] ids); + + /** + * 删除流程实例关联表单信息 + * + * @param id 流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormById(Long id); + + /** + * 查询流程挂着的表单 + * @param deployId + * @return + */ + SysForm selectSysDeployFormByDeployId(String deployId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java new file mode 100644 index 00000000..4f230788 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysExpression; + +import java.util.List; + +/** + * 流程达式Service接口 + * + * @author yanZhu + * @date 2022-12-12 + */ +public interface ISysExpressionService +{ + /** + * 查询流程达式 + * + * @param id 流程达式主键 + * @return 流程达式 + */ + public SysExpression selectSysExpressionById(Long id); + + /** + * 查询流程达式列表 + * + * @param sysExpression 流程达式 + * @return 流程达式集合 + */ + public List selectSysExpressionList(SysExpression sysExpression); + + /** + * 新增流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int insertSysExpression(SysExpression sysExpression); + + /** + * 修改流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int updateSysExpression(SysExpression sysExpression); + + /** + * 批量删除流程达式 + * + * @param ids 需要删除的流程达式主键集合 + * @return 结果 + */ + public int deleteSysExpressionByIds(Long[] ids); + + /** + * 删除流程达式信息 + * + * @param id 流程达式主键 + * @return 结果 + */ + public int deleteSysExpressionById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java new file mode 100644 index 00000000..98a844ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java @@ -0,0 +1,61 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 表单 + * @author Tony + * @date 2021-04-03 + */ +public interface ISysFormService +{ + /** + * 查询流程表单 + * + * @param formId 流程表单ID + * @return 流程表单 + */ + public SysForm selectSysFormById(Long formId); + + /** + * 查询流程表单列表 + * + * @param sysForm 流程表单 + * @return 流程表单集合 + */ + public List selectSysFormList(SysForm sysForm); + + /** + * 新增流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int insertSysForm(SysForm sysForm); + + /** + * 修改流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int updateSysForm(SysForm sysForm); + + /** + * 批量删除流程表单 + * + * @param formIds 需要删除的流程表单ID + * @return 结果 + */ + public int deleteSysFormByIds(Long[] formIds); + + /** + * 删除流程表单信息 + * + * @param formId 流程表单ID + * @return 结果 + */ + public int deleteSysFormById(Long formId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java new file mode 100644 index 00000000..c269e24b --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysTaskForm; + +import java.util.List; + +/** + * 流程任务关联单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +@Deprecated +public interface ISysTaskFormService { + /** + * 查询流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 流程任务关联单 + */ + public SysTaskForm selectSysTaskFormById(Long id); + + /** + * 查询流程任务关联单列表 + * + * @param sysTaskForm 流程任务关联单 + * @return 流程任务关联单集合 + */ + public List selectSysTaskFormList(SysTaskForm sysTaskForm); + + /** + * 新增流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int insertSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 修改流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int updateSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 批量删除流程任务关联单 + * + * @param ids 需要删除的流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormByIds(Long[] ids); + + /** + * 删除流程任务关联单信息 + * + * @param id 流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java new file mode 100644 index 00000000..f5150cbb --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java @@ -0,0 +1,102 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.flowable.domain.FlowTaskEntity; +import com.yanzhu.flowable.mapper.FlowBusinessKeyMapper; +import com.yanzhu.flowable.service.IFlowBusinessKeyService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * 业务工作流程自定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowBusinessKeyServiceImpl implements IFlowBusinessKeyService { + + @Resource + private FlowBusinessKeyMapper flowBusinessKeyMapper; + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity); + } + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public Map quueryCount(FlowTaskEntity flowTaskEntity) { + flowTaskEntity.setActiveName("await"); + int awaitSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + flowTaskEntity.setActiveName("finished"); + int finishedSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + Map dataMap = new HashMap<>(); + dataMap.put("await",awaitSize); + dataMap.put("finished",finishedSize); + return dataMap; + } + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + @Override + public List> selectCommentByProcInsId(String procInsId) { + return flowBusinessKeyMapper.selectCommentByProcInsId(procInsId); + } + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + @Override + public Map selectFormDatasByProcInsId(String procInsId) { + Map dataMap = new HashMap<>(); + List> list = flowBusinessKeyMapper.selectFormDatasByProcInsId(procInsId); + Iterator> it = list.iterator(); + while (it.hasNext()) { + Map map = it.next(); + dataMap.put(Convert.toStr(map.get("name")), map.get("text")); + } + return dataMap; + } + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity){ + return flowBusinessKeyMapper.selectMyAwaitFlowTask(flowTaskEntity); + } + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectMyFinishedFlowTask(flowTaskEntity); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java new file mode 100644 index 00000000..0d9f656f --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java @@ -0,0 +1,279 @@ +package com.yanzhu.flowable.service.impl; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.yanzhu.common.core.constant.HttpStatus; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.ServiceException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.SysExpression; +import com.yanzhu.flowable.domain.vo.FlowDeptVo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.mapper.FlowDeployMapper; +import com.yanzhu.flowable.service.IFlowDefinitionService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.repository.ProcessDefinitionQuery; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; +import org.flowable.task.api.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 流程定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFlowDefinitionService { + + @Autowired + private RedisService redisService; + + @Resource + private FlowDeployMapper flowDeployMapper; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private ISysDeployFormService sysDeployFormService; + + private static final String BPMN_FILE_SUFFIX = ".bpmn"; + + @Override + public boolean exist(String processDefinitionKey) { + ProcessDefinitionQuery processDefinitionQuery + = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey); + long count = processDefinitionQuery.count(); + return count > 0 ? true : false; + } + + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + @Override + public TableDataInfo list(FlowQueryVo flowQueryVo) { + TableDataInfo rspData = new TableDataInfo(); + PageHelper.startPage(flowQueryVo.getPageNum(), flowQueryVo.getPageSize()); + List list = flowDeployMapper.selectDeployList(flowQueryVo); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + + /** + * 导入流程文件 + * + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name 流程名称 + * @param category 流程类型 + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in 流程文件流 + */ + @Override + @Transactional + public void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in) { + if(SecurityUtils.isAdmin(SecurityUtils.getUserId())){ + throw new ServiceException("请选择项目后导入流程文件..."); + } + Deployment deploy = repositoryService.createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).category(category).deploy(); + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), category); + /**新增流程单位关系**/ + FlowDeptVo flowDeptVo = new FlowDeptVo(); + flowDeptVo.setProcdefId(definition.getId()); + //超管不能导入流程文件 + if(SecurityUtils.isGSAdmin()){ + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setDeptId(Convert.toLong(OwnerDeptId)); + } + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setProjId(Convert.toLong(OwnerProjectId)); + } + }else{ + // 从用户信息中获取项目单位信息... + flowDeptVo.setPrtype(prtype); + flowDeptVo.setProjId(SecurityUtils.getLoginUser().getProjectId()); + flowDeptVo.setDeptId(SecurityUtils.getLoginUser().getProjectDeptId()); + } + if(flowDeptVo.getDeptId()!=null){ + flowDeployMapper.insertActReProcdefDept(flowDeptVo); + } + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public AjaxResult readXml(String deployId) throws IOException { + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName()); + String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name()); + return AjaxResult.success("", result); + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public InputStream readImage(String deployId) { + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + //获得图片流 + DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + //输出为图片 + return diagramGenerator.generateDiagram( + bpmnModel, + "png", + Collections.emptyList(), + Collections.emptyList(), + "宋体", + "宋体", + "宋体", + null, + 1.0, + false); + + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程模板ID + * @param applyInfos 流程表单 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map applyInfos) { + try { + /**保存流程表单**/ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+procDefId); + R res; + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsGroupEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsUsersEntity(applyInfos, SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + if(StringUtils.isNotNull(res.getData())){ + Map variables = new HashMap<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId) + .latestVersion().singleResult(); + if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) { + return AjaxResult.error("流程已被挂起,请联系管理员激活流程"); + } + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + String userId = Convert.toStr(sysUser.getUserId()); + // 设置流程发起人Id到流程中 + identityService.setAuthenticatedUserId(userId); + variables.put(ProcessConstants.PROCESS_INITIATOR, userId); + variables.put("businessKey", res.getData()); + ProcessInstance processInstance; + processInstance = runtimeService.startProcessInstanceById(procDefId, Convert.toStr(res.getData()),variables); + // 给第一步申请人节点设置任务执行人和意见 + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult(); + if (Objects.nonNull(task)) { + taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), sysUser.getNickName() + "发起流程申请"); + taskService.setAssignee(task.getId(), Convert.toStr(userId)); + taskService.complete(task.getId(), variables); + } + return AjaxResult.success("流程启动成功"); + }else{ + return AjaxResult.error("流程表单保存失败"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + @Override + public void updateState(Integer state, String deployId) { + ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + // 激活 + if (state == 1) { + repositoryService.activateProcessDefinitionById(procDef.getId(), true, null); + } + // 挂起 + if (state == 2) { + repositoryService.suspendProcessDefinitionById(procDef.getId(), true, null); + } + } + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + @Override + public void delete(String deployId) { + // true 允许级联删除 ,不设置会导致数据库外键关联异常 + repositoryService.deleteDeployment(deployId, true); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java new file mode 100644 index 00000000..4facc05d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java @@ -0,0 +1,117 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.service.IFlowInstanceService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.engine.history.HistoricProcessInstance; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Map; +import java.util.Objects; + +/** + * 工作流流程实例管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowInstanceServiceImpl extends FlowServiceFactory implements IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + @Override + public void stopProcessInstance(FlowTaskVo vo) { + String taskId = vo.getTaskId(); + + } + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + @Override + public void updateState(Integer state, String instanceId) { + + // 激活 + if (state == 1) { + runtimeService.activateProcessInstanceById(instanceId); + } + // 挂起 + if (state == 2) { + runtimeService.suspendProcessInstanceById(instanceId); + } + } + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(String instanceId, String deleteReason) { + + // 查询历史数据 + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(instanceId); + if (historicProcessInstance.getEndTime() != null) { + historyService.deleteHistoricProcessInstance(historicProcessInstance.getId()); + return; + } + // 删除流程实例 + runtimeService.deleteProcessInstance(instanceId, deleteReason); + // 删除历史流程实例 + historyService.deleteHistoricProcessInstance(instanceId); + } + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + @Override + public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) { + HistoricProcessInstance historicProcessInstance = + historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + if (Objects.isNull(historicProcessInstance)) { + throw new FlowableObjectNotFoundException("流程实例不存在: " + processInstanceId); + } + return historicProcessInstance; + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map variables) { + + try { + // 设置流程发起人Id到流程中 + Long userId = SecurityUtils.getUserId(); + variables.put("initiator",userId); + variables.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true); + runtimeService.startProcessInstanceById(procDefId, variables); + return AjaxResult.success("流程启动成功"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java new file mode 100644 index 00000000..50208812 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java @@ -0,0 +1,1307 @@ +package com.yanzhu.flowable.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.CustomException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.*; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.flow.CustomProcessDiagramGenerator; +import com.yanzhu.flowable.flow.FindNextNodeUtil; +import com.yanzhu.flowable.flow.FlowableUtils; +import com.yanzhu.flowable.flow.ModelUtils; +import com.yanzhu.flowable.service.IFlowTaskService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.RemoteUserService; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.impl.cmd.AddMultiInstanceExecutionCmd; +import org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.task.Comment; +import org.flowable.identitylink.api.history.HistoricIdentityLink; +import org.flowable.image.ProcessDiagramGenerator; +import org.flowable.task.api.DelegationState; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * @author Tony + * @date 2021-04-03 + **/ +@Service +@Slf4j +public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTaskService { + + @Autowired + private RedisService redisService; + + @Resource + private ISysFormService sysFormService; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private RemoteUserService remoteUserService; + + @Resource + private RemoteRoleService remoteRoleService; + + @Resource + private ISysDeployFormService sysInstanceFormService; + + /** + * 完成任务 + * + * @param taskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public AjaxResult complete(FlowTaskVo taskVo) { + Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult(); + /**表单不为空时,重新修改表单**/ + if(StringUtils.isNotNull(taskVo.getApplyInfos())){ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+task.getProcessDefinitionId()); + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsGroupEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsUsersEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + } + if (Objects.isNull(task)) { + return AjaxResult.error("任务不存在"); + } + if (DelegationState.PENDING.equals(task.getDelegationState())) { + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment()); + taskService.resolveTask(taskVo.getTaskId()); + } else { + Long userId = SecurityUtils.getUserId(); + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment()); + taskService.setAssignee(taskVo.getTaskId(), Convert.toStr(userId)); + taskService.complete(taskVo.getTaskId()); + } + return AjaxResult.success(); + } + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + @Override + public void taskReject(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态!"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + // 获取节点信息 + source = flowElement; + } + } + } + + // 目的获取所有跳转到的节点 targetIds + // 获取当前节点的所有父级用户任务节点 + // 深度优先算法思想:延边迭代深入 + List parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null); + if (parentUserTaskList == null || parentUserTaskList.size() == 0) { + throw new CustomException("当前节点为初始任务节点,不能驳回"); + } + // 获取活动 ID 即节点 Key + List parentUserTaskKeyList = new ArrayList<>(); + parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId())); + // 获取全部历史节点活动实例,即已经走过的节点历史,数据采用开始时间升序 + List historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list(); + // 数据清洗,将回滚导致的脏数据清洗掉 + List lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList); + // 此时历史任务实例为倒序,获取最后走的节点 + List targetIds = new ArrayList<>(); + // 循环结束标识,遇到当前目标节点的次数 + int number = 0; + StringBuilder parentHistoricTaskKey = new StringBuilder(); + for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) { + // 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过 + if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) { + continue; + } + parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey); + if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) { + number++; + } + // 在数据清洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次 + // 在流程中如果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环 + // number == 1,第一次遇到当前节点 + // number == 2,第二次遇到,代表最后一次的循环范围 + if (number == 2) { + break; + } + // 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点 + if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) { + targetIds.add(historicTaskInstanceKey); + } + } + + + // 目的获取所有需要被跳转的节点 currentIds + // 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路 + UserTask oneUserTask = parentUserTaskList.get(0); + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需驳回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + + // 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况 + if (targetIds.size() > 1 && currentIds.size() > 1) { + throw new CustomException("任务出现多对多情况,无法撤回"); + } + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置驳回意见 + currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment())); + + try { + // 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况 + if (targetIds.size() > 1) { + // 1 对 多任务跳转,currentIds 当前节点(1),targetIds 跳转到的节点(多) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()). + moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState(); + } + // 如果父级任务只有一个,因此当前任务可能为网关中的任务 + if (targetIds.size() == 1) { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetIds.get(0) 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState(); + } + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + + } + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void taskReturn(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + // 获取跳转的节点元素 + FlowElement target = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 当前任务节点元素 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = flowElement; + } + // 跳转的节点元素 + if (flowElement.getId().equals(flowTaskVo.getTargetKey())) { + target = flowElement; + } + } + } + + // 从当前节点向前扫描 + // 如果存在路线上不存在目标节点,说明目标节点是在网关上或非同一路线上,不可跳转 + // 否则目标节点相对于当前节点,属于串行 + Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, flowTaskVo.getTargetKey(), null, null); + if (!isSequential) { + throw new CustomException("当前节点相对于目标节点,不属于串行关系,无法回退"); + } + + + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需退回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置回退意见 + currentTaskIds.forEach(currentTaskId -> taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), flowTaskVo.getComment())); + + try { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, flowTaskVo.getTargetKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + } + + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + @Override + public AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo) { +// // 当前任务 task +// Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); +// // 从流程历史任务中获取可退回节点 +//// List hisActIns = historyService.createHistoricActivityInstanceQuery() +//// .executionId(task.getExecutionId()) +//// .activityType("userTask") +//// .orderByHistoricActivityInstanceStartTime() +//// .finished() +//// .desc() +//// .list(); +//// +//// // 可回退的节点列表 +//// List returnTaskNodeList = new ArrayList<>(); +//// ReturnTaskNodeVo returnTaskNodeVo; +//// for (HistoricActivityInstance activityInstance : hisActIns) { +//// returnTaskNodeVo = new ReturnTaskNodeVo(); +//// returnTaskNodeVo.setId(activityInstance.getActivityId()); +//// // 根据流程节点处理时间校验改节点是否已完成 +//// returnTaskNodeVo.setName(activityInstance.getActivityName()); +//// returnTaskNodeList.add(returnTaskNodeVo); +//// } +// List userTaskList = new ArrayList<>(); +// // 获取流程定义信息 +// ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); +// // 获取所有节点信息,暂不考虑子流程情况 +// Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); +// Collection flowElements = process.getFlowElements(); +// // 获取当前任务节点元素 +// UserTask source = null; +// if (flowElements != null) { +// for (FlowElement flowElement : flowElements) { +// // 类型为用户节点 +// if (flowElement.getId().equals(task.getTaskDefinitionKey())) { +// source = (UserTask) flowElement; +// } +// } +// } +// // 获取节点的所有路线 +// List> roads = FlowableUtils.findRoad(source, null, null, null); +// +// for (List road : roads) { +// if (userTaskList.size() == 0) { +// // 还没有可回退节点直接添加 +// userTaskList = road; +// } else { +// // 如果已有回退节点,则比对取交集部分 +// userTaskList.retainAll(road); +// } +// } +// return AjaxResult.success(userTaskList); + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息,暂不考虑子流程情况 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + Collection flowElements = process.getFlowElements(); + // 获取当前任务节点元素 + UserTask source = null; + if (flowElements != null) { + for (FlowElement flowElement : flowElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = (UserTask) flowElement; + } + } + } + // 获取节点的所有路线 + List> roads = FlowableUtils.findRoad(source, null, null, null); + // 可回退的节点列表 + List userTaskList = new ArrayList<>(); + for (List road : roads) { + if (userTaskList.size() == 0) { + // 还没有可回退节点直接添加 + userTaskList = road; + } else { + // 如果已有回退节点,则比对取交集部分 + userTaskList.retainAll(road); + } + } + return AjaxResult.success(userTaskList); + } + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + public void deleteTask(FlowTaskVo flowTaskVo) { + // todo 待确认删除任务是物理删除任务 还是逻辑删除,让这个任务直接通过? + taskService.deleteTask(flowTaskVo.getTaskId(), flowTaskVo.getComment()); + } + + /** + * 认领/签收任务 + * 认领以后,这个用户就会成为任务的执行人,任务会从其他成员的任务列表中消失 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void claim(FlowTaskVo flowTaskVo) { + taskService.claim(flowTaskVo.getTaskId(), flowTaskVo.getUserId()); + } + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void unClaim(FlowTaskVo flowTaskVo) { + taskService.unclaim(flowTaskVo.getTaskId()); + } + + /** + * 委派任务 + * 任务委派只是委派人将当前的任务交给被委派人进行审批,处理任务后又重新回到委派人身上。 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.DELEGATE.getType(), flowTaskVo.getComment()); + taskService.delegateTask(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + } + + /** + * 任务归还 + * 被委派人完成任务之后,将任务归还委派人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void resolveTask(FlowTaskVo flowTaskVo) { + taskService.resolveTask(flowTaskVo.getTaskId()); + } + + + /** + * 转办任务 + * 直接将办理人换成别人,这时任务的拥有者不再是转办人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void assignTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.ASSIGN.getType(), flowTaskVo.getComment()); + // 直接转派就可以覆盖掉之前的 + taskService.setAssignee(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); +// // 删除指派人重新指派 +// taskService.deleteCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// taskService.addCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// // 如果要查询转给他人处理的任务,可以同时将OWNER进行设置: +// taskService.setOwner(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + + } + + /** + * 多实例加签 + * act_ru_task、act_ru_identitylink各生成一条记录 + * + * @param flowTaskVo + */ + @Override + public void addMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new AddMultiInstanceExecutionCmd(flowTaskVo.getDefId(), flowTaskVo.getInstanceId(), flowTaskVo.getVariables())); + } + + /** + * 多实例减签 + * act_ru_task减1、act_ru_identitylink不变 + * + * @param flowTaskVo + */ + @Override + public void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new DeleteMultiInstanceExecutionCmd(flowTaskVo.getCurrentChildExecutionId(), flowTaskVo.getFlag())); + } + + /** + * 我发起的流程 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult myProcess(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + Long userId = SecurityUtils.getUserId(); + HistoricProcessInstanceQuery historicProcessInstanceQuery; + //超管查询所有数据据 + if(SecurityUtils.isAdmin(userId)){ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .orderByProcessInstanceStartTime() + .desc(); + }else{ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .startedBy(userId.toString()) + .orderByProcessInstanceStartTime() + .desc(); + } + List historicProcessInstances = historicProcessInstanceQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + page.setTotal(historicProcessInstanceQuery.count()); + List flowList = new ArrayList<>(); + for (HistoricProcessInstance hisIns : historicProcessInstances) { + FlowTaskDto flowTask = new FlowTaskDto(); + flowTask.setCreateTime(hisIns.getStartTime()); + flowTask.setFinishTime(hisIns.getEndTime()); + flowTask.setProcInsId(hisIns.getId()); + + // 计算耗时 + if (Objects.nonNull(hisIns.getEndTime())) { + long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } else { + long time = System.currentTimeMillis() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } + // 流程定义信息 + ProcessDefinition pd = repositoryService.createProcessDefinitionQuery() + .processDefinitionId(hisIns.getProcessDefinitionId()) + .singleResult(); + flowTask.setDeployId(pd.getDeploymentId()); + flowTask.setProcDefName(pd.getName()); + flowTask.setProcDefVersion(pd.getVersion()); + flowTask.setCategory(pd.getCategory()); + flowTask.setProcDefVersion(pd.getVersion()); + // 当前所处流程 + List taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list(); + if (CollectionUtils.isNotEmpty(taskList)) { + flowTask.setTaskId(taskList.get(0).getId()); + flowTask.setTaskName(taskList.get(0).getName()); + if (StringUtils.isNotBlank(taskList.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(taskList.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } else { + List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list(); + flowTask.setTaskId(historicTaskInstance.get(0).getId()); + flowTask.setTaskName(historicTaskInstance.get(0).getName()); + if (StringUtils.isNotBlank(historicTaskInstance.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(historicTaskInstance.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } + flowList.add(flowTask); + } + page.setRows(flowList); + return AjaxResult.success(page); + } + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult stopProcess(FlowTaskVo flowTaskVo) { + List task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).list(); + if (CollectionUtils.isEmpty(task)) { + throw new CustomException("流程未启动或已执行完成,取消申请失败"); + } + // 获取当前流程实例 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(flowTaskVo.getInstanceId()) + .singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); + if (Objects.nonNull(bpmnModel)) { + Process process = bpmnModel.getMainProcess(); + List endNodes = process.findFlowElementsOfType(EndEvent.class, false); + if (CollectionUtils.isNotEmpty(endNodes)) { + String userIdStr = Convert.toStr(SecurityUtils.getUserId()); + if(StringUtils.isNotEmpty(userIdStr)){ + Authentication.setAuthenticatedUserId(userIdStr); + taskService.addComment(task.get(0).getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(), + StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment()); + // 设置流程经办人为当前登录人员 + taskService.setAssignee(task.get(0).getId(), userIdStr); + // 获取当前流程最后一个节点 + String endId = endNodes.get(0).getId(); + List executions = runtimeService.createExecutionQuery() + .parentId(processInstance.getProcessInstanceId()).list(); + List executionIds = new ArrayList<>(); + executions.forEach(execution -> executionIds.add(execution.getId())); + // 变更流程为已结束状态 + runtimeService.createChangeActivityStateBuilder() + .moveExecutionsToSingleActivityId(executionIds, endId).changeState(); + return AjaxResult.success(); + } + } + } + return AjaxResult.error(); + } + + /** + * 撤回流程 目前存在错误 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult revokeProcess(FlowTaskVo flowTaskVo) { + String procInsId = flowTaskVo.getInstanceId(); + String taskId = flowTaskVo.getTaskId(); + // 校验流程是否结束 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(procInsId) + .active() + .singleResult(); + if(Objects.isNull(processInstance)) { + throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作"); + } + // 获取待撤回任务实例 + HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .taskAssignee(flowTaskVo.getUserId()) + .singleResult(); + if (Objects.isNull(currTaskIns)) { + throw new RuntimeException("当前任务不存在,无法执行撤回操作"); + } + // 获取 bpmn 模型 + BpmnModel bpmnModel = repositoryService.getBpmnModel(currTaskIns.getProcessDefinitionId()); + UserTask currUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currTaskIns.getTaskDefinitionKey()); + // 查找下一级用户任务列表 + List nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask); + List nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList()); + + // 获取当前节点之后已完成的流程历史节点 + List finishedTaskInsList = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(procInsId) + .taskCreatedAfter(currTaskIns.getEndTime()) + .finished() + .list(); + for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) { + // 检查已完成流程历史节点是否存在下一级中 + if (StringUtils.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) { + throw new RuntimeException("下一流程已处理,无法执行撤回操作"); + } + } + // 获取所有激活的任务节点,找到需要撤回的任务 + List activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list(); + List revokeExecutionIds = new ArrayList<>(); + for (Task task : activateTaskList) { + // 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点 + if (StringUtils.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) { + // 添加撤回审批信息 + taskService.setAssignee(task.getId(), flowTaskVo.getUserId()); + taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), flowTaskVo.getAssignee() + "撤回流程审批"); + revokeExecutionIds.add(task.getExecutionId()); + } + } + try { + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(procInsId) + .moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new RuntimeException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new RuntimeException("执行撤回操作失败"); + } + return AjaxResult.success(); + } + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult todoList(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + // 只查看自己的数据 + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + TaskQuery taskQuery = taskService.createTaskQuery() + .active() + .includeProcessVariables() + .taskCandidateGroupIn(sysUser.getRoles().stream().map(role -> role.getRoleId().toString()).collect(Collectors.toList())) + .taskCandidateOrAssigned(sysUser.getUserId().toString()) + .orderByTaskCreateTime().desc(); + + page.setTotal(taskQuery.count()); + List taskList = taskQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + List
工作流项目单位关系
+ * + * @author JiangYuQi + * @date 2024-02-23 + */ +@Data +@ApiModel("工作流项目单位关系") +public class FlowDeptVo extends BaseEntity { + + private static final long serialVersionUID = 1L; + + /** 流程定义主键 */ + private String procdefId; + + /** 流程定义类型 */ + private String prtype; + + /** 流程定义单位 */ + private Long deptId; + + /** 流程定义项目 */ + private Long projId; + + /** 流程定义排序 */ + private String sort; + + @Override + public String toString() { + return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .append("procdefId", getProcdefId()) + .append("prtype", getPrtype()) + .append("deptId", getDeptId()) + .append("projId", getProjId()) + .append("sort", getSort()) + .toString(); + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowQueryVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowQueryVo.java new file mode 100644 index 00000000..4b08f213 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowQueryVo.java @@ -0,0 +1,49 @@ +package com.yanzhu.flowable.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + *
流程任务
+ * + * @author Tony + * @date 2021-04-03 + */ +@Data +@ApiModel("工作流任务相关--请求参数") +public class FlowQueryVo { + + @ApiModelProperty("流程名称") + private String name; + + @ApiModelProperty("流程类别") + private String category; + + @ApiModelProperty("开始时间") + private String startTime; + + @ApiModelProperty("结束时间") + private String endTime; + + @ApiModelProperty("当前页码") + private Integer pageNum; + + @ApiModelProperty("每页条数") + private Integer pageSize; + + @ApiModelProperty("单位主键") + private Long deptId; + + @ApiModelProperty("单位名称") + private String deptName; + + @ApiModelProperty("项目主键") + private Long projectId; + + @ApiModelProperty("项目名称") + private String projectName; + + @ApiModelProperty("单位祖籍列表") + private String deptAncestors; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowTaskVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowTaskVo.java new file mode 100644 index 00000000..c05a8ba4 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/FlowTaskVo.java @@ -0,0 +1,59 @@ +package com.yanzhu.flowable.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +import java.util.List; +import java.util.Map; + +/** + *
+ * + * @author Tony + * @date 2021-04-03 + */ +@Data +@ApiModel("工作流任务相关--请求参数") +public class FlowTaskVo { + + @ApiModelProperty("任务Id") + private String taskId; + + @ApiModelProperty("用户Id") + private String userId; + + @ApiModelProperty("任务意见") + private String comment; + + @ApiModelProperty("流程实例Id") + private String instanceId; + + @ApiModelProperty("节点") + private String targetKey; + + private String deploymentId; + @ApiModelProperty("流程环节定义ID") + private String defId; + + @ApiModelProperty("子执行流ID") + private String currentChildExecutionId; + + @ApiModelProperty("子执行流是否已执行") + private Boolean flag; + + @ApiModelProperty("流程变量信息") + private Map variables; + + @ApiModelProperty("审批信息") + private Map applyInfos; + + @ApiModelProperty("审批人") + private String assignee; + + @ApiModelProperty("候选人") + private List candidateUsers; + + @ApiModelProperty("审批组") + private List candidateGroups; +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ProcKeyRole.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ProcKeyRole.java new file mode 100644 index 00000000..e31a5612 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ProcKeyRole.java @@ -0,0 +1,50 @@ +package com.yanzhu.flowable.domain.vo; + +import java.io.Serializable; +import java.util.List; + +public class ProcKeyRole implements Serializable +{ + + private static final long serialVersionUID = 1L; + + private String roleId; + + private String key; + + private String sort; + + private List keys; + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public List getKeys() { + return keys; + } + + public void setKeys(List keys) { + this.keys = keys; + } + + public String getSort() { + return sort; + } + + public void setSort(String sort) { + this.sort = sort; + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ReturnTaskNodeVo.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ReturnTaskNodeVo.java new file mode 100644 index 00000000..0085e543 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/ReturnTaskNodeVo.java @@ -0,0 +1,23 @@ +package com.yanzhu.flowable.domain.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 可退回节点 + * + * @author tony + * @date 2022-04-23 11:01:52 + */ +@Data +@ApiModel("可退回节点") +public class ReturnTaskNodeVo { + + @ApiModelProperty("任务Id") + private String id; + + @ApiModelProperty("用户Id") + private String name; + +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java new file mode 100644 index 00000000..b5b29240 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java @@ -0,0 +1,63 @@ +package com.yanzhu.flowable.domain.vo; + +import java.io.Serializable; +import java.util.Map; + +/** + * 流程实例启动 + * + * @author JiangYuQi + * @date 2020-07-07 + */ +public class StartTaskVO implements Serializable +{ + + private static final long serialVersionUID = 1L; + + //流程实例ID + private String procDefId; + private String userId; + private String userName; + private String nickName; + private Map variables; + + public String getProcDefId() { + return procDefId; + } + + public void setProcDefId(String procDefId) { + this.procDefId = procDefId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public Map getVariables() { + return variables; + } + + public void setVariables(Map variables) { + this.variables = variables; + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java new file mode 100644 index 00000000..0f7afa58 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java @@ -0,0 +1,112 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.FlowTaskEntity; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/*** + * 工作流业务相关 + */ +public interface FlowBusinessKeyMapper { + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件查询流程任务 + * @param procInsId + * @return + */ + public FlowTaskEntity findFlowTaskByProcInsId(String procInsId); + + /** + * 查询项目信息 + * @param proId + * @return + */ + public Map selectSurProjectById(Long proId); + + /** + * 根据流程Id查询操作日志 + * @param procInsId + * @return + */ + public List> selectCommentByProcInsId(String procInsId); + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + public Map selectLastCommentByProcInsId(String procInsId); + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + public List> selectFormDatasByProcInsId(String procInsId); + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件查询我的代办分组数量统计 + * @param flowTaskEntity + * @return + */ + public List> findAwaitCountGroupByCategory(FlowTaskEntity flowTaskEntity); + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity); + + public List groupByCategory(FlowTaskEntity where); + + public List groupByUnit(FlowTaskEntity where); + + public List listByUnit(FlowTaskEntity where); + + public List listByState(FlowTaskEntity where); + + public List groupByUnitTotal(FlowTaskEntity where); + + public List groupByUnitFinish(FlowTaskEntity where); + + public List groupByProject(@Param("deptId")long deptId,@Param("proType")String proType); + + /** + * 根据条件查询安全类作业工作流列表 + * @param flowTaskEntity + * @return + */ + public List findSafetyWorkList(FlowTaskEntity flowTaskEntity); + + public List listByCategory(FlowTaskEntity where); + + /** + * 工程管理-分包单位占比 + * @param where + * @return + */ + public List groupFlowBySubDeptType(FlowTaskEntity where); + /** + * 工程管理-分包单位资质审批明细 + * @param where + * @return + */ + public List listFlowBySubDeptType(FlowTaskEntity where); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java new file mode 100644 index 00000000..7d32f616 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java @@ -0,0 +1,76 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.vo.FlowDeptVo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 流程定义查询 + * + * @author Tony + * @email + * @date 2022/1/29 5:44 下午 + **/ +public interface FlowDeployMapper { + + /** + * 流程定义列表 + * @param roleId 发起角色 + * @param category 流程类别 + * @param name 流程名称 + * @return + */ + List selectDeployList(@Param("roleId")String roleId, @Param("category")String category, @Param("name")String name); + + /** + * 我的流程定义列表 + * @param username 登录用户 + * @param category 流程类别 + * @param name 流程名称 + * @return + */ + List selectMyDeployList(@Param("username")String username, @Param("category")String category, @Param("name")String name); + + /** + * 流程定义列表 + * @param flowQueryVo 流程查询 + * @return + */ + List selectDeployList(FlowQueryVo flowQueryVo); + + /** + * 新增单位流程关系 + * + * @param flowDeptVo 流程 单位关系 + * @return 结果 + */ + public int insertActReProcdefDept(FlowDeptVo flowDeptVo); + + /** + * 根据角色删除流程定义 + * @param roleId 橘色 + * @return + */ + int deleteDeployByRoleId(String roleId); + + + /** + * 批量新增流程定义角色配置 + * @param items 列表 + * @return + */ + int batchDeployRoleRole(List> items); + + /** + * 流程发起排序设置 + * @param roleId 角色主键 + * @param key 流程 + * @param sort 排序 + * @return + */ + int updateProcKeyRoleSort(@Param("roleId")String roleId, @Param("key")String key, @Param("sort")String sort); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java new file mode 100644 index 00000000..1f7bd205 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程实例关联表单Mapper接口 + * + * @author Tony + * @date 2021-03-30 + */ +public interface SysDeployFormMapper +{ + /** + * 查询流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 流程实例关联表单 + */ + public SysDeployForm selectSysDeployFormById(Long id); + + /** + * 查询流程实例关联表单列表 + * + * @param SysDeployForm 流程实例关联表单 + * @return 流程实例关联表单集合 + */ + public List selectSysDeployFormList(SysDeployForm SysDeployForm); + + /** + * 新增流程实例关联表单 + * + * @param SysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int insertSysDeployForm(SysDeployForm SysDeployForm); + + /** + * 修改流程实例关联表单 + * + * @param SysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int updateSysDeployForm(SysDeployForm SysDeployForm); + + /** + * 删除流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormById(Long id); + + /** + * 批量删除流程实例关联表单 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteSysDeployFormByIds(Long[] ids); + + /** + * 查询流程挂着的表单 + * @param deployId + * @return + */ + SysForm selectSysDeployFormByDeployId(String deployId); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java new file mode 100644 index 00000000..bdeef941 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysExpression; + +import java.util.List; + +/** + * 流程达式Mapper接口 + * + * @author ruoyi + * @date 2022-12-12 + */ +public interface SysExpressionMapper +{ + /** + * 查询流程达式 + * + * @param id 流程达式主键 + * @return 流程达式 + */ + public SysExpression selectSysExpressionById(Long id); + + /** + * 查询流程达式列表 + * + * @param sysExpression 流程达式 + * @return 流程达式集合 + */ + public List selectSysExpressionList(SysExpression sysExpression); + + /** + * 新增流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int insertSysExpression(SysExpression sysExpression); + + /** + * 修改流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int updateSysExpression(SysExpression sysExpression); + + /** + * 删除流程达式 + * + * @param id 流程达式主键 + * @return 结果 + */ + public int deleteSysExpressionById(Long id); + + /** + * 批量删除流程达式 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteSysExpressionByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java new file mode 100644 index 00000000..183f9630 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程表单Mapper接口 + * + * @author Tony + * @date 2021-03-30 + */ +public interface SysFormMapper +{ + /** + * 查询流程表单 + * + * @param formId 流程表单ID + * @return 流程表单 + */ + public SysForm selectSysFormById(Long formId); + + /** + * 查询流程表单列表 + * + * @param sysForm 流程表单 + * @return 流程表单集合 + */ + public List selectSysFormList(SysForm sysForm); + + /** + * 新增流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int insertSysForm(SysForm sysForm); + + /** + * 修改流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int updateSysForm(SysForm sysForm); + + /** + * 删除流程表单 + * + * @param formId 流程表单ID + * @return 结果 + */ + public int deleteSysFormById(Long formId); + + /** + * 批量删除流程表单 + * + * @param formIds 需要删除的数据ID + * @return 结果 + */ + public int deleteSysFormByIds(Long[] formIds); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java new file mode 100644 index 00000000..c4b43efe --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysListener; + +import java.util.List; + +/** + * 流程监听Mapper接口 + * + * @author Tony + * @date 2022-12-25 + */ +public interface SysListenerMapper +{ + /** + * 查询流程监听 + * + * @param id 流程监听主键 + * @return 流程监听 + */ + public SysListener selectSysListenerById(Long id); + + /** + * 查询流程监听列表 + * + * @param sysListener 流程监听 + * @return 流程监听集合 + */ + public List selectSysListenerList(SysListener sysListener); + + /** + * 新增流程监听 + * + * @param sysListener 流程监听 + * @return 结果 + */ + public int insertSysListener(SysListener sysListener); + + /** + * 修改流程监听 + * + * @param sysListener 流程监听 + * @return 结果 + */ + public int updateSysListener(SysListener sysListener); + + /** + * 删除流程监听 + * + * @param id 流程监听主键 + * @return 结果 + */ + public int deleteSysListenerById(Long id); + + /** + * 批量删除流程监听 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteSysListenerByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java new file mode 100644 index 00000000..0d0a1d68 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysTaskForm; + +import java.util.List; + +/** + * 流程任务关联单Mapper接口 + * + * @author Tony + * @date 2021-04-03 + */ +public interface SysTaskFormMapper +{ + /** + * 查询流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 流程任务关联单 + */ + public SysTaskForm selectSysTaskFormById(Long id); + + /** + * 查询流程任务关联单列表 + * + * @param sysTaskForm 流程任务关联单 + * @return 流程任务关联单集合 + */ + public List selectSysTaskFormList(SysTaskForm sysTaskForm); + + /** + * 新增流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int insertSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 修改流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int updateSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 删除流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormById(Long id); + + /** + * 批量删除流程任务关联单 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteSysTaskFormByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/mapper/domain/init b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/mapper/domain/init deleted file mode 100644 index e69de29b..00000000 diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml new file mode 100644 index 00000000..df7e58d3 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml @@ -0,0 +1,515 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT * FROM + vw_flow_all fa + + and fa.taskName like concat('%', #{taskName}, '%') + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.procDefKey = #{procDefKey} + and fa.businessKey = #{businessKey} + and fa.startDeptName like concat('%', #{startDeptName}, '%') + and fa.startUserName like concat('%', #{startUserName}, '%') + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.startUserId = #{nowUser} + and fa.finishTime is null + and fa.finishTime is not null + + order by fa.createTime desc + + + + + select vf.* as startUserPhone from vw_flow_all vf + where vf.procInsId = #{procInsId} + + + + + select * from vw_flow_comment where procInstId = #{procInstId} order by startTime DESC + + + + select * from vw_flow_comment where procInstId = #{procInstId} order by startTime DESC limit 1 + + + + + select hv.NAME_ as `name`,hv.TEXT_ as text from act_hi_varinst hv where hv.PROC_INST_ID_ = #{procInstId} + + + + + select fa.* from vw_flow_await fa + where + 1=1 + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.businessKey in (select DISTINCT project_id from sur_project_userinfo where user_id=#{nowUser} and is_del=0) + AND (fa.ASSIGNEE_ = #{nowUser} + OR ( + fa.ASSIGNEE_ IS NULL + AND ( + fa.USER_ID_ = #{nowUser} + OR ( + fa.GROUP_ID_ IN + + #{roleId} + + ) + ) + ) + ) + order by fa.createTime desc + + + + + select fa.category,count(1) as total from vw_flow_await fa + where + 1=1 + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.businessKey in (select DISTINCT project_id from sur_project_userinfo where user_id=#{nowUser} and is_del=0) + AND (fa.ASSIGNEE_ = #{nowUser} + OR ( + fa.ASSIGNEE_ IS NULL + AND ( + fa.USER_ID_ = #{nowUser} + OR ( + fa.GROUP_ID_ IN + + #{roleId} + + ) + ) + ) + ) + group by fa.category + + + + + select fa.* from vw_flow_finished fa + where + fa.ASSIGNEE_=#{nowUser} + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.endTime between #{params.beginDate} and #{params.endDate} + order by fa.endTime desc + + + + SELECT a.dict_label taskName,b.cnt assigneeId,c.cnt procDefVersion FROM + ( SELECT * FROM sys_dict_data WHERE dict_type = 'sys_process_category') a + LEFT JOIN (SELECT category,COUNT(1) cnt FROM vw_flow_all WHERE finishTime IS NOT NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + GROUP BY category) b ON a.dict_value=b.category + LEFT JOIN (SELECT category,COUNT(1) cnt FROM vw_flow_all WHERE finishTime IS NULL and taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + GROUP BY category) c ON a.dict_value=c.category + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '总包%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '监理%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '设计%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '甲方%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '工程%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE taskName LIKE '总包%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE taskName LIKE '监理%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE taskName LIKE '设计%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE taskName LIKE '甲方%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE taskName LIKE '工程%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE taskName LIKE '总包%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE taskName LIKE '监理%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE taskName LIKE '设计%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE taskName LIKE '甲方%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE taskName LIKE '工程%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT * FROM vw_flow_all + + and category = #{category} + and businessDeptId=#{deptId} + and businessType = #{proType} + and businessKey=#{projectId} + + and businessKey in + + #{item} + + + + + + + SELECT * FROM vw_flow_all WHERE finishTime IS NULL + + AND taskName LIKE '总包%' + AND taskName LIKE '监理%' + AND taskName LIKE '设计%' + AND taskName LIKE '甲方%' + AND taskName LIKE '工程%' + + and businessDeptId=#{deptId} + and businessType = #{proType} + and businessKey=#{projectId} + + and businessKey in + + #{item} + + + + + + SELECT * FROM vw_flow_all + + + AND taskName!='提交申请' + AND finishTime IS NULL AND taskName!='提交申请' + AND finishTime IS NOT NULL + + and businessDeptId=#{deptId} + and businessKey=#{projectId} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + + + + SELECT a.businessKey,a.cnt duration ,b.projectName businessKeyName FROM ( + SELECT businessKey,COUNT(1) cnt FROM vw_flow_all WHERE taskName!='提交申请' + and businessDeptId=#{deptId} + and businessType = #{proType} + GROUP BY businessKey ) a,sur_project b WHERE a.businessKey=b.id + ORDER BY a.cnt DESC + + + + SELECT + fa.procInsId, + fa.deployId, + fa.createTime, + fa.finishTime, + fa.duration, + fa.procDefKey, + fa.procDefName, + fa.procDefVersion, + fa.category, + fa.businessKey, + fa.businessDeptId, + fa.businessKeyName, + fa.startUserId, + fa.startUserName, + fa.startDeptName, + fa.taskId, + fa.taskName, + fa.assigneeId, + fa.assigneeName, + fa.assigneeDeptName + FROM + vw_flow_all fa + + and fa.businessType = #{proType} + and fa.procDefKey = #{procDefKey} + and fa.businessKey = #{businessKey} + + and fa.businessDeptId = #{projectDeptId} + + order by fa.createTime desc + + + + select * from sur_project where id = #{proId} + + + + select x.dict_label taskName,y.taskId from ( + select dict_value,dict_label + from sys_dict_data where dict_type='flow_sub_dept_type' + ) x left join ( + SELECT d.dict_value, d.dict_label,count(1) taskId FROM + vw_flow_all a + LEFT JOIN act_hi_varinst b ON a.procInsId=b.PROC_INST_ID_ AND b.NAME_='subDeptType' + LEFT JOIN act_hi_varinst c ON a.procInsId=c.PROC_INST_ID_ AND c.NAME_='subDeptName' + LEFT JOIN sys_dict_data D ON b.TEXT_=d.dict_value and d.dict_type='flow_sub_dept_type' + left join sur_project sp on a.businesskey=sp.id + where a.procDefKey='flow_fbzzsp_fbszzsp' + AND (A.finishTime is null OR (A.finishTime is not null and A.taskComType =1)) + and sp.progressVisible=0 and sp.isDel=0 + and a.businessDeptId=#{deptId} + and a.businessKey=#{projectId} + + and a.businessKey in + + #{item} + + + and sp.projectType = #{proType} + group by d.dict_value,d.dict_label + ) y on x.dict_value=y.dict_value + + + SELECT a.procInsId,a.deployId,a.createTime,a.finishTime,a.businessKey,a.businessKeyName,a.startDeptName,b.TEXT_ as taskId, c.TEXT_ as deptName,d.dict_label taskName FROM + vw_flow_all a + LEFT JOIN act_hi_varinst b ON a.procInsId=b.PROC_INST_ID_ AND b.NAME_='subDeptType' + LEFT JOIN act_hi_varinst c ON a.procInsId=c.PROC_INST_ID_ AND c.NAME_='subDeptName' + LEFT JOIN sys_dict_data D ON b.TEXT_=d.dict_value and d.dict_type='flow_sub_dept_type' + left join sur_project sp on a.businesskey=sp.id + where a.procDefKey='flow_fbzzsp_fbszzsp' + AND (A.finishTime is null OR (A.finishTime is not null and A.taskComType =1)) + and sp.progressVisible=0 and sp.isDel=0 + and a.businessDeptId=#{deptId} + and a.businessKey=#{projectId} + + and a.businessKey in + + #{item} + + + and sp.projectType = #{proType} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml new file mode 100644 index 00000000..f8a188d2 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml @@ -0,0 +1,125 @@ + + + + + + SELECT + rp.id_ as id, + rp.deployment_id_ as deploymentId, + rd.name_ as name, + rd.category_ as category, + rp.key_ as flowKey, + rp.version_ as version, + rp.suspension_state_ as suspensionState, + rd.deploy_time_ as deploymentTime, + rpd.DEPT_ID_ as deptId, + sd.dept_name as deptName, + rpd.PROJ_ID_ as projectId, + spi.project_name as projectName + FROM + act_re_procdef rp + LEFT JOIN act_re_deployment rd ON rp.deployment_id_ = rd.id_ + left join act_re_procdef_dept rpd on rp.id_ = rpd.PROCDEF_ID_ + left join sys_dept sd on sd.dept_id = rpd.DEPT_ID_ + left join pro_project_info spi on spi.id = rpd.PROJ_ID_ + + rp.SUSPENSION_STATE_ = 1 + + and rpd.DEPT_ID_ = #{deptId} + + + and sd.dept_name like concat('%', #{deptName}, '%') + + + and rpd.PROJ_ID_ = #{projectId} + + + and spi.project_name like concat('%', #{projectName}, '%') + + + and rd.name_ like concat('%', #{name}, '%') + + + and rd.category_ = #{category} + + + and rd.key_ = #{flowKey} + + + order by rpr.SORT_ + + + + SELECT * FROM ( + SELECT + rp.id_ as id, + rp.deployment_id_ as deploymentId, + rd.name_ as name, + rd.category_ as category, + sdd.dict_label as categoryName, + rp.key_ as flowKey, + rp.version_ as version, + rp.suspension_state_ as suspensionState, + rd.deploy_time_ as deploymentTime, + ROW_NUMBER () OVER ( + PARTITION BY rp.key_ + ORDER BY + rp.version_ DESC + ) AS rn + FROM + act_re_procdef rp + LEFT JOIN act_re_deployment rd ON rp.deployment_id_ = rd.id_ + left join sys_dict_data sdd on sdd.dict_type='sys_process_category' and sdd.dict_value = rd.category_ + left join act_re_procdef_role rpr on rp.key_ = rpr.PROCDEF_KEY_ + left join sys_user_role sur on sur.role_id = rpr.ROLE_ID_ + left join sys_user su on su.user_id = sur.user_id + + rp.SUSPENSION_STATE_ = 1 and su.user_name = #{username} + + and rd.name_ like concat('%', #{name}, '%') + + + and rd.category_ = #{category} + + + order by rd.deploy_time_ desc + ) v WHERE v.rn = 1 + + + + delete from act_re_procdef_role where ROLE_ID_ = #{roleId} + + + + insert into act_re_procdef_role( PROCDEF_KEY_, ROLE_ID_) values + + ( #{item.key}, #{item.roleId}) + + + + + update act_re_procdef_role set SORT_=#{sort} where PROCDEF_KEY_=#{key} and ROLE_ID_=#{roleId} + + + + + insert into act_re_procdef_dept + + PROCDEF_ID_, + TYPE_, + DEPT_ID_, + PROJ_ID_, + SORT_, + + + #{procdefId}, + #{prtype}, + #{deptId}, + #{projId}, + #{sort}, + + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml new file mode 100644 index 00000000..f3423af4 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + select id, form_id, deploy_id from sys_deploy_form + + + + + + and form_id = #{formId} + and deploy_id = #{deployId} + + + + + + where id = #{id} + + + + select t1.form_content as formContent,t1.form_name as formName,t1.form_id as formId from sys_form t1 left join sys_deploy_form t2 on t1.form_id = t2.form_id + where t2.deploy_id = #{deployId} limit 1 + + + + insert into sys_deploy_form + + form_id, + deploy_id, + + + #{formId}, + #{deployId}, + + + + + update sys_deploy_form + + form_id = #{formId}, + deploy_id = #{deployId}, + + where id = #{id} + + + + delete from sys_deploy_form where id = #{id} + + + + delete from sys_deploy_form where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml new file mode 100644 index 00000000..504e522c --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + select id, name, expression, create_time, update_time, create_by, update_by, status, remark from sys_expression + + + + + + and name like concat('%', #{name}, '%') + and expression = #{expression} + and status = #{status} + + + + + + where id = #{id} + + + + insert into sys_expression + + name, + expression, + create_time, + update_time, + create_by, + update_by, + status, + remark, + + + #{name}, + #{expression}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{status}, + #{remark}, + + + + + update sys_expression + + name = #{name}, + expression = #{expression}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + status = #{status}, + remark = #{remark}, + + where id = #{id} + + + + delete from sys_expression where id = #{id} + + + + delete from sys_expression where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml new file mode 100644 index 00000000..6e686742 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + select form_id, form_name, form_content, create_time, update_time, create_by, update_by, remark from sys_form + + + + + + and form_name like concat('%', #{formName}, '%') + and form_content = #{formContent} + + order by create_time desc + + + + + where form_id = #{formId} + + + + insert into sys_form + + form_name, + form_content, + create_time, + update_time, + create_by, + update_by, + remark, + + + #{formName}, + #{formContent}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{remark}, + + + + + update sys_form + + form_name = #{formName}, + form_content = #{formContent}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + remark = #{remark}, + + where form_id = #{formId} + + + + delete from sys_form where form_id = #{formId} + + + + delete from sys_form where form_id in + + #{formId} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml new file mode 100644 index 00000000..a8403a06 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + select id, + name, + type, + event_type, + value_type, + value, + create_time, + update_time, + create_by, + update_by, + status, + remark + from sys_listener + + + + + + and name like concat('%', #{name}, '%') + and type = #{type} + and event_type = #{eventType} + and value_type = #{valueType} + and value = #{value} + and status = #{status} + + + + + + where id = #{id} + + + + insert into sys_listener + + name, + type, + event_type, + value_type, + value, + create_time, + update_time, + create_by, + update_by, + status, + remark, + + + #{name}, + #{type}, + #{eventType}, + #{valueType}, + #{value}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{status}, + #{remark}, + + + + + update sys_listener + + name = #{name}, + type = #{type}, + event_type = #{eventType}, + value_type = #{valueType}, + value = #{value}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + status = #{status}, + remark = #{remark}, + + where id = #{id} + + + + delete + from sys_listener + where id = #{id} + + + + delete from sys_listener where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml new file mode 100644 index 00000000..b7ec90fe --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + select id, form_id, task_id from sys_task_form + + + + + + and form_id = #{formId} + and task_id = #{taskId} + + + + + + where id = #{id} + + + + insert into sys_task_form + + form_id, + task_id, + + + #{formId}, + #{taskId}, + + + + + update sys_task_form + + form_id = #{formId}, + task_id = #{taskId}, + + where id = #{id} + + + + delete from sys_task_form where id = #{id} + + + + delete from sys_task_form where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java b/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java index 7faf4f46..ba3490f9 100644 --- a/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java +++ b/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java @@ -91,6 +91,16 @@ public class SecurityUtils return userId != null && 1L == userId; } + /** + * 是否为公司管理员 + * + * @return 结果 + */ + public static boolean isGSAdmin() + { + return getLoginUser().getRoles().contains("gsAdmin"); + } + /** * 生成BCryptPasswordEncoder密码 * diff --git a/yanzhu-modules/pom.xml b/yanzhu-modules/pom.xml index 20037b42..800e5a5a 100644 --- a/yanzhu-modules/pom.xml +++ b/yanzhu-modules/pom.xml @@ -15,13 +15,14 @@ yanzhu-file yanzhu-manage yanzhu-wechat + yanzhu-flowable yanzhu-modules pom - yanzhu-modules业务模块 + yanzhu-modules分布式模块 diff --git a/yanzhu-modules/yanzhu-flowable/pom.xml b/yanzhu-modules/yanzhu-flowable/pom.xml new file mode 100644 index 00000000..a94efdf1 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/pom.xml @@ -0,0 +1,182 @@ + + + + yanzhu + com.yanzhu + 3.6.2 + ../../pom.xml + + 4.0.0 + + yanzhu-modules-flowable + + + 17 + 17 + + + + yanzhu-modules-flowable工作流模块 + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + io.springfox + springfox-swagger-ui + ${swagger.fox.version} + + + + + org.apache.velocity + velocity-engine-core + + + + + mysql + mysql-connector-java + + + + + com.yanzhu + yanzhu-common-log + + + + + com.yanzhu + yanzhu-common-swagger + + + + org.projectlombok + lombok + provided + + + + io.seata + seata-all + 1.5.2 + compile + + + + + com.yanzhu + yanzhu-common-mapper + 3.6.2 + compile + + + + + com.googlecode.aviator + aviator + 5.3.3 + + + + org.flowable + flowable-spring-boot-starter + + + + org.flowable + flowable-spring-security + + + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + com.spotify + docker-maven-plugin + 1.2.0 + + + ${project.build.finalName} + latest + + openjdk:latest + + ["java", "-jar", "/${project.build.finalName}.jar"] + + + ${configuration.docker-two-Host} + + + / + + ${project.build.directory} + + ${project.build.finalName}.jar + + + + + + build-image + package + + build + + + + + + + + + src/main/resources + true + + + + + \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java new file mode 100644 index 00000000..1edc2bc8 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java @@ -0,0 +1,34 @@ +package com.yanzhu.flowable; + +import com.yanzhu.common.security.annotation.EnableCustomConfig; +import com.yanzhu.common.security.annotation.EnableRyFeignClients; +import com.yanzhu.common.swagger.annotation.EnableCustomSwagger2; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 工作流模块 + * + * @author ruoyi + */ +@EnableCustomConfig +@EnableCustomSwagger2 +@EnableRyFeignClients +@SpringBootApplication +public class YanZhuFlowableApplication +{ + public static void main(String[] args) + { + SpringApplication.run(YanZhuFlowableApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 工作流模块启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java new file mode 100644 index 00000000..54488c3e --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java @@ -0,0 +1,80 @@ +package com.yanzhu.flowable.common.constant; + +/** + * 流程常量信息 + * + * @author Tony + * @date 2021/4/17 22:46 + */ +public class ProcessConstants { + + /** + * 动态数据 + */ + public static final String DYNAMIC = "dynamic"; + + /** + * 固定任务接收 + */ + public static final String FIXED = "fixed"; + + /** + * 单个审批人 + */ + public static final String ASSIGNEE = "assignee"; + + + /** + * 候选人 + */ + public static final String CANDIDATE_USERS = "candidateUsers"; + + + /** + * 审批组 + */ + public static final String CANDIDATE_GROUPS = "candidateGroups"; + + /** + * 单个审批人 + */ + public static final String PROCESS_APPROVAL = "approval"; + + /** + * 会签人员 + */ + public static final String PROCESS_MULTI_INSTANCE_USER = "userList"; + + /** + * nameapace + */ + public static final String NAMASPASE = "http://flowable.org/bpmn"; + + /** + * 会签节点 + */ + public static final String PROCESS_MULTI_INSTANCE = "multiInstance"; + + /** + * 自定义属性 dataType + */ + public static final String PROCESS_CUSTOM_DATA_TYPE = "dataType"; + + /** + * 自定义属性 userType + */ + public static final String PROCESS_CUSTOM_USER_TYPE = "userType"; + + /** + * 初始化人员 + */ + public static final String PROCESS_INITIATOR = "INITIATOR"; + + + /** + * 流程跳过 + */ + public static final String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java new file mode 100644 index 00000000..06aa8bff --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java @@ -0,0 +1,43 @@ +package com.yanzhu.flowable.common.enums; + +/** + * 流程意见类型 + * + * @author Tony + * @date 2021/4/19 + */ +public enum FlowComment { + + /** + * 说明 + */ + NORMAL("1", "正常意见"), + REBACK("2", "退回意见"), + REJECT("3", "驳回意见"), + DELEGATE("4", "委派意见"), + ASSIGN("5", "转办意见"), + STOP("6", "终止流程"), + REVOKE("7", "撤回流程"); + /** + * 类型 + */ + private final String type; + + /** + * 说明 + */ + private final String remark; + + FlowComment(String type, String remark) { + this.type = type; + this.remark = remark; + } + + public String getType() { + return type; + } + + public String getRemark() { + return remark; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java new file mode 100644 index 00000000..414d2d04 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java @@ -0,0 +1,32 @@ +package com.yanzhu.flowable.config; + +import org.flowable.engine.impl.db.DbIdGenerator; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncListenableTaskExecutor; +import org.springframework.core.task.SimpleAsyncTaskExecutor; + +/** + * 流程id生成处理 + * @author Tony + * @date 2022-12-26 10:24 + */ +@Configuration +public class FlowableConfig implements EngineConfigurationConfigurer { + + @Bean + public AsyncListenableTaskExecutor applicationTaskExecutor() { + return new SimpleAsyncTaskExecutor(); + } + + @Override + public void configure(SpringProcessEngineConfiguration engineConfiguration) { + engineConfiguration.setActivityFontName("宋体"); + engineConfiguration.setLabelFontName("宋体"); + engineConfiguration.setAnnotationFontName("宋体"); + engineConfiguration.setIdGenerator(new DbIdGenerator()); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java new file mode 100644 index 00000000..227de838 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java @@ -0,0 +1,41 @@ +package com.yanzhu.flowable.config; + +import com.yanzhu.flowable.listener.GlobalEventListener; +import lombok.RequiredArgsConstructor; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; + +/** + * Flowable添加全局监听器 + * + * @author JiangYuQi + */ +@Configuration +@RequiredArgsConstructor +public class FlowableGlobalListenerConfig implements ApplicationListener { + + private final SpringProcessEngineConfiguration configuration; + + private final GlobalEventListener globalEventListener; + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + FlowableEventDispatcher dispatcher = configuration.getEventDispatcher(); + /** + * 任务创建全局监听-待办消息发送 + * PROCESS_CREATED 流程创建 + * TASK_CREATED 任务创建 + * TASK_COMPLETED 任务完成 + * PROCESS_COMPLETED 流程完成 + * 流程创建、任务创建、任务完成、流程完成 + */ + dispatcher.addEventListener(globalEventListener,FlowableEngineEventType.TASK_CREATED); + dispatcher.addEventListener(globalEventListener,FlowableEngineEventType.PROCESS_COMPLETED); + } + +} + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java new file mode 100644 index 00000000..983f676c --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java @@ -0,0 +1,146 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.FlowTaskEntity; +import com.yanzhu.flowable.service.IFlowBusinessKeyService; +import com.yanzhu.system.api.domain.SysUser; +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * + * 业务工作流程 + * + * + * @author JiangYuQi + * @date 2024-04-03 + */ +@Slf4j +@Api(tags = "业务工作流程") +@RestController +@RequestMapping("/mapper/flowable/businessKey") +public class FlowBusinessKeyController extends BaseController { + + @Autowired + private IFlowBusinessKeyService flowBusinessKeyService; + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/allList") + public TableDataInfo allList(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()); + } + startPage(); + return getDataTable(flowBusinessKeyService.selectAllFlowTaskByParams(flowTaskEntity)); + } + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/queryCount") + public AjaxResult queryCount(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()); + } + return success(flowBusinessKeyService.quueryCount(flowTaskEntity)); + } + + /** + * 根据流程Id查询操作日志 + * @param procInsId + * @return + */ + @GetMapping(value = "/findCommentByProcInsId") + public AjaxResult findCommentByProcInsId(String procInsId) { + return success(flowBusinessKeyService.selectCommentByProcInsId(procInsId)); + } + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + @GetMapping(value = "/findFormDatasByProcInsId") + public AjaxResult findFormDatasByProcInsId(String procInsId) { + return success(flowBusinessKeyService.selectFormDatasByProcInsId(procInsId)); + } + + /** + * 根据条件查询我的代办任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/myAwaitFlowTaskList") + public TableDataInfo myAwaitFlowTaskList(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setAssigneeId(sysUser.getUserId()); + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + flowTaskEntity.setRoleIds(sysUser.getRoles().stream().map(role -> role.getRoleId()).collect(Collectors.toList())); + } + startPage(); + return getDataTable(flowBusinessKeyService.selectMyAwaitFlowTask(flowTaskEntity)); + } + + /** + * 根据条件查询我的已办任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/myFinishedFlowTaskList") + public TableDataInfo myFinishedFlowTaskList(FlowTaskEntity flowTaskEntity) { + startPage(); + flowTaskEntity.setAssigneeId(SecurityUtils.getUserId()); + return getDataTable(flowBusinessKeyService.selectMyFinishedFlowTask(flowTaskEntity)); + } + + /** + * 查询我的代办任务数量 + * @return AjaxResult 统计结果 + */ + @GetMapping(value = "/findMyTask") + public AjaxResult findMyTask() { + FlowTaskEntity flowTaskEntity = new FlowTaskEntity(); + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setAssigneeId(sysUser.getUserId()); + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + flowTaskEntity.setRoleIds(sysUser.getRoles().stream().map(role -> role.getRoleId()).collect(Collectors.toList())); + } + List> list = flowBusinessKeyService.selectMyAwaitFlowTask(flowTaskEntity); + Map data = new HashMap<>(); + if(CollectionUtils.isNotEmpty(list)){ + data.put("todo",list.size()); + }else{ + data.put("todo",0); + } + return success(data); + } + + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java new file mode 100644 index 00000000..c7aa591c --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java @@ -0,0 +1,199 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.FlowSaveXmlVo; +import com.yanzhu.flowable.domain.SysExpression; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.service.IFlowDefinitionService; +import com.yanzhu.flowable.service.ISysExpressionService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.RemoteUserService; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.domain.SysUser; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * + * 工作流程定义 + * + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "流程定义") +@RestController +@RequestMapping("/mapper/flowable/definition") +public class FlowDefinitionController extends BaseController { + + @Autowired + private IFlowDefinitionService flowDefinitionService; + + @Resource + private ISysExpressionService sysExpressionService; + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + @GetMapping(value = "/list") + @ApiOperation(value = "流程定义列表", response = FlowProcDefDto.class) + public TableDataInfo list(FlowQueryVo flowQueryVo) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowQueryVo.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + } + return flowDefinitionService.list(flowQueryVo); + } + + @ApiOperation(value = "导入流程文件", notes = "上传bpmn20的xml文件") + @Log(title = "导入流程文件", businessType = BusinessType.INSERT) + @PostMapping("/import") + public AjaxResult importFile(@RequestParam(required = false) String name, + @RequestParam(required = false) String category, + MultipartFile file) { + InputStream in = null; + try { + in = file.getInputStream(); + flowDefinitionService.importFile(name, category, null, null, null, in); + } catch (Exception e) { + log.error("导入失败:", e); + return AjaxResult.success(e.getMessage()); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + log.error("关闭输入流出错", e); + } + } + + return AjaxResult.success("导入成功"); + } + + @ApiOperation(value = "读取xml文件") + @GetMapping("/readXml/{deployId}") + public AjaxResult readXml(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId) { + try { + return flowDefinitionService.readXml(deployId); + } catch (Exception e) { + return AjaxResult.error("加载xml文件异常"); + } + } + + @ApiOperation(value = "读取图片文件") + @GetMapping("/readImage/{deployId}") + public void readImage(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId, HttpServletResponse response) { + OutputStream os = null; + BufferedImage image = null; + try { + image = ImageIO.read(flowDefinitionService.readImage(deployId)); + response.setContentType("image/png"); + os = response.getOutputStream(); + if (image != null) { + ImageIO.write(image, "png", os); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.flush(); + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @ApiOperation(value = "保存流程设计器内的xml文件") + @Log(title = "保存流程设计", businessType = BusinessType.INSERT) + @PostMapping("/save") + public AjaxResult save(@RequestBody FlowSaveXmlVo vo) { + InputStream in = null; + try { + in = new ByteArrayInputStream(vo.getXml().getBytes(StandardCharsets.UTF_8)); + flowDefinitionService.importFile(vo.getName(), vo.getCategory(), vo.getPrtype(), vo.getOwnerDeptId(), vo.getOwnerProjectId(), in); + } catch (Exception e) { + log.error("导入失败:", e); + return AjaxResult.error(e.getMessage()); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + log.error("关闭输入流出错", e); + } + } + return AjaxResult.success("导入成功"); + } + + + @ApiOperation(value = "发起流程") + @Log(title = "发起流程申请", businessType = BusinessType.INSERT) + @PostMapping("/start/{procDefId}") + public AjaxResult start(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId, + @ApiParam(value = "申请表单") @RequestBody Map applyInfos) { + return flowDefinitionService.startProcessInstanceById(procDefId, applyInfos); + } + + @ApiOperation(value = "激活或挂起流程定义") + @Log(title = "激活/挂起流程", businessType = BusinessType.UPDATE) + @PutMapping(value = "/updateState") + public AjaxResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state, + @ApiParam(value = "流程部署ID", required = true) @RequestParam String deployId) { + flowDefinitionService.updateState(state, deployId); + return AjaxResult.success(); + } + + @ApiOperation(value = "删除流程") + @Log(title = "删除流程定义", businessType = BusinessType.UPDATE) + @DeleteMapping(value = "/{deployIds}") + public AjaxResult delete(@PathVariable String[] deployIds) { + for (String deployId : deployIds) { + flowDefinitionService.delete(deployId); + } + return AjaxResult.success(); + } + + @ApiOperation(value = "指定流程达式列表") + @GetMapping("/expList") + public AjaxResult expList(SysExpression sysExpression) { + List list = sysExpressionService.selectSysExpressionList(sysExpression); + return AjaxResult.success(list); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java new file mode 100644 index 00000000..5850b4c7 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java @@ -0,0 +1,63 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.service.IFlowInstanceService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 工作流流程实例管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "工作流流程实例管理") +@RestController +@RequestMapping("/mapper/flowable/instance") +public class FlowInstanceController { + + @Autowired + private IFlowInstanceService flowInstanceService; + + @ApiOperation(value = "根据流程定义id启动流程实例") + @PostMapping("/startBy/{procDefId}") + public AjaxResult startById(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId, + @ApiParam(value = "变量集合,json对象") @RequestBody Map variables) { + return flowInstanceService.startProcessInstanceById(procDefId, variables); + + } + + + @ApiOperation(value = "激活或挂起流程实例") + @PostMapping(value = "/updateState") + public AjaxResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state, + @ApiParam(value = "流程实例ID", required = true) @RequestParam String instanceId) { + flowInstanceService.updateState(state,instanceId); + return AjaxResult.success(); + } + + @ApiOperation("结束流程实例") + @PostMapping(value = "/stopProcessInstance") + public AjaxResult stopProcessInstance(@RequestBody FlowTaskVo flowTaskVo) { + flowInstanceService.stopProcessInstance(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "删除流程实例") + @DeleteMapping(value = "/delete/{instanceIds}") + public AjaxResult delete(@ApiParam(value = "流程实例ID", required = true) @PathVariable String[] instanceIds, + @ApiParam(value = "删除原因") @RequestParam(required = false) String deleteReason) { + for (String instanceId : instanceIds) { + flowInstanceService.delete(instanceId,deleteReason); + } + return AjaxResult.success(); + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java new file mode 100644 index 00000000..643ef51b --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java @@ -0,0 +1,268 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.flowable.domain.FlowTaskDto; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.service.IFlowTaskService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * 工作流任务管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "工作流流程任务管理") +@RestController +@RequestMapping("/mapper/flowable/task") +public class FlowTaskController { + + @Autowired + private IFlowTaskService flowTaskService; + + @ApiOperation(value = "我发起的流程", response = FlowTaskDto.class) + @GetMapping(value = "/myProcess") + public AjaxResult myProcess(FlowQueryVo queryVo) { + return flowTaskService.myProcess(queryVo); + } + + @ApiOperation(value = "取消申请", response = FlowTaskDto.class) + @Log(title = "终止申请", businessType = BusinessType.UPDATE) + @PostMapping(value = "/stopProcess") + public AjaxResult stopProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.stopProcess(flowTaskVo); + } + + @ApiOperation(value = "撤回流程", response = FlowTaskDto.class) + @Log(title = "撤回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/revokeProcess") + public AjaxResult revokeProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.revokeProcess(flowTaskVo); + } + + @ApiOperation(value = "获取待办列表", response = FlowTaskDto.class) + @GetMapping(value = "/todoList") + public AjaxResult todoList(FlowQueryVo queryVo) { + return flowTaskService.todoList(queryVo); + } + + @ApiOperation(value = "获取已办任务", response = FlowTaskDto.class) + @GetMapping(value = "/finishedList") + public AjaxResult finishedList(FlowQueryVo queryVo) { + return flowTaskService.finishedList(queryVo); + } + + + @ApiOperation(value = "流程历史流转记录", response = FlowTaskDto.class) + @GetMapping(value = "/flowRecord") + public AjaxResult flowRecord(String procInsId, String deployId) { + return flowTaskService.flowRecord(procInsId, deployId); + } + + + @ApiOperation(value = "流程初始化表单", response = FlowTaskDto.class) + @GetMapping(value = "/flowFormData") + public AjaxResult flowFormData(String deployId) { + return flowTaskService.flowFormData(deployId); + } + + @ApiOperation(value = "获取流程变量", response = FlowTaskDto.class) + @GetMapping(value = "/processVariables/{taskId}") + public AjaxResult processVariables(@ApiParam(value = "流程任务Id") @PathVariable(value = "taskId") String taskId) { + return flowTaskService.processVariables(taskId); + } + + @ApiOperation(value = "审批任务") + @Log(title = "审批流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/complete") + public AjaxResult complete(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "重新提交流程") + @Log(title = "重新提交流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/completeAndModify") + public AjaxResult completeAndModify(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "驳回任务") + @Log(title = "驳回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/reject") + public AjaxResult taskReject(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReject(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "退回任务") + @Log(title = "退回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/return") + public AjaxResult taskReturn(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReturn(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "获取所有可回退的节点") + @PostMapping(value = "/returnList") + public AjaxResult findReturnTaskList(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.findReturnTaskList(flowTaskVo); + } + + @ApiOperation(value = "删除任务") + @Log(title = "删除流程", businessType = BusinessType.DELETE) + @DeleteMapping(value = "/delete") + public AjaxResult delete(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "认领/签收任务") + @Log(title = "认领/签收流程", businessType = BusinessType.INSERT) + @PostMapping(value = "/claim") + public AjaxResult claim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.claim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "取消认领/签收任务") + @Log(title = "取消认领/签收流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/unClaim") + public AjaxResult unClaim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.unClaim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "委派任务") + @Log(title = "委派流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/delegateTask") + public AjaxResult delegate(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.delegateTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "任务归还") + @Log(title = "归还流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/resolveTask") + public AjaxResult resolveTask(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.resolveTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "转办任务") + @Log(title = "转办流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/assignTask") + public AjaxResult assign(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.assignTask(flowTaskVo); + return AjaxResult.success(); + } + + @PostMapping(value = "/addMultiInstanceExecution") + @ApiOperation(value = "多实例加签") + public AjaxResult addMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.addMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("加签成功"); + } + + @PostMapping(value = "/deleteMultiInstanceExecution") + @ApiOperation(value = "多实例减签") + public AjaxResult deleteMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("减签成功"); + } + + @ApiOperation(value = "获取下一节点") + @PostMapping(value = "/nextFlowNode") + public AjaxResult getNextFlowNode(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNode(flowTaskVo); + } + + @ApiOperation(value = "流程发起时获取下一节点") + @PostMapping(value = "/nextFlowNodeByStart") + public AjaxResult getNextFlowNodeByStart(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNodeByStart(flowTaskVo); + } + + /** + * 生成流程图 + * + * @param processId 任务ID + */ + @GetMapping("/diagram/{processId}") + public void genProcessDiagram(HttpServletResponse response, + @PathVariable("processId") String processId) { + InputStream inputStream = flowTaskService.diagram(processId); + OutputStream os = null; + BufferedImage image = null; + try { + image = ImageIO.read(inputStream); + response.setContentType("image/png"); + os = response.getOutputStream(); + if (image != null) { + ImageIO.write(image, "png", os); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.flush(); + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 获取流程执行节点 + * + * @param procInsId 流程实例编号 + * @param procInsId 任务执行编号 + */ + @GetMapping("/flowViewer/{procInsId}/{executionId}") + public AjaxResult getFlowViewer(@PathVariable("procInsId") String procInsId, + @PathVariable("executionId") String executionId) { + return flowTaskService.getFlowViewer(procInsId, executionId); + } + + /** + * 流程节点信息 + * + * @param procInsId 流程实例id + * @return + */ + @GetMapping("/flowXmlAndNode") + public AjaxResult flowXmlAndNode(@RequestParam(value = "procInsId", required = false) String procInsId, + @RequestParam(value = "deployId", required = false) String deployId) { + return flowTaskService.flowXmlAndNode(procInsId, deployId); + } + + /** + * 流程节点表单 + * + * @param taskId 流程任务编号 + * @return + */ + @GetMapping("/flowTaskForm") + public AjaxResult flowTaskForm(@RequestParam(value = "taskId", required = false) String taskId) throws Exception { + return flowTaskService.flowTaskForm(taskId); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java new file mode 100644 index 00000000..74cbad80 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java @@ -0,0 +1,112 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.utils.poi.ExcelUtil; +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.common.security.annotation.RequiresPermissions; +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 流程表单Controller + * + * @author Tony + * @date 2021-04-03 + */ +@RestController +@RequestMapping("/mapper/flowable/form") +public class SysFormController extends BaseController { + + @Autowired + private ISysFormService SysFormService; + + @Autowired + private ISysDeployFormService sysDeployFormService; + + /** + * 查询流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:list')") + @GetMapping("/list") + public TableDataInfo list(SysForm sysForm) { + startPage(); + List list = SysFormService.selectSysFormList(sysForm); + return getDataTable(list); + } + + @GetMapping("/formList") + public AjaxResult formList(SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + return AjaxResult.success(list); + } + /** + * 导出流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:export')") + @Log(title = "流程表单", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public void export(HttpServletResponse response, SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + ExcelUtil util = new ExcelUtil(SysForm.class); + util.exportExcel(response, list, "form"); + } + + /** + * 获取流程表单详细信息 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:query')") + @GetMapping(value = "/{formId}") + public AjaxResult getInfo(@PathVariable("formId") Long formId) { + return AjaxResult.success(SysFormService.selectSysFormById(formId)); + } + + /** + * 新增流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:add')") + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.insertSysForm(sysForm)); + } + + /** + * 修改流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:edit')") + @Log(title = "流程表单", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.updateSysForm(sysForm)); + } + + /** + * 删除流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:remove')") + @Log(title = "流程表单", businessType = BusinessType.DELETE) + @DeleteMapping("/{formIds}") + public AjaxResult remove(@PathVariable Long[] formIds) { + return toAjax(SysFormService.deleteSysFormByIds(formIds)); + } + + + /** + * 挂载流程表单 + */ + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping("/addDeployForm") + public AjaxResult addDeployForm(@RequestBody SysDeployForm sysDeployForm) { + return toAjax(sysDeployFormService.insertSysDeployForm(sysDeployForm)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java new file mode 100644 index 00000000..e66efd70 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java @@ -0,0 +1,41 @@ +package com.yanzhu.flowable.factory; + +import lombok.Getter; +import org.flowable.engine.*; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * flowable 引擎注入封装 + * @author Tony + * @date 2021-04-03 + */ +@Component +@Getter +public class FlowServiceFactory { + + @Resource + protected RepositoryService repositoryService; + + @Resource + protected RuntimeService runtimeService; + + @Resource + protected IdentityService identityService; + + @Resource + protected TaskService taskService; + + @Resource + protected HistoryService historyService; + + @Resource + protected ManagementService managementService; + + @Qualifier("processEngine") + @Resource + protected ProcessEngine processEngine; + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java new file mode 100644 index 00000000..7a0baca5 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java @@ -0,0 +1,370 @@ +package com.yanzhu.flowable.flow; + +import org.flowable.bpmn.model.AssociationDirection; +import org.flowable.bpmn.model.GraphicInfo; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.util.ReflectUtil; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; + +/** + * @author Tony + * @date 2021/4/4 23:58 + */ +public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas { + //定义走过流程连线颜色为绿色 + protected static Color HIGHLIGHT_SequenceFlow_COLOR = Color.GREEN; + //设置未走过流程的连接线颜色 + protected static Color CONNECTION_COLOR = Color.BLACK; + //设置flows连接线字体颜色red + protected static Color LABEL_COLOR = new Color(0, 0, 0); + //高亮显示task框颜色 + protected static Color HIGHLIGHT_COLOR = Color.GREEN; + protected static Color HIGHLIGHT_COLOR1 = Color.RED; + + public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + this.initialize(imageType); + } + + /** + * 重写绘制连线的方式,设置绘制颜色 + * @param xPoints + * @param yPoints + * @param conditional + * @param isDefault + * @param connectionType + * @param associationDirection + * @param highLighted + * @param scaleFactor + */ + @Override + public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) { + Paint originalPaint = this.g.getPaint(); + Stroke originalStroke = this.g.getStroke(); + this.g.setPaint(CONNECTION_COLOR); + if (connectionType.equals("association")) { + this.g.setStroke(ASSOCIATION_STROKE); + } else if (highLighted) { + this.g.setPaint(HIGHLIGHT_SequenceFlow_COLOR); + this.g.setStroke(HIGHLIGHT_FLOW_STROKE); + } + + for (int i = 1; i < xPoints.length; ++i) { + Integer sourceX = xPoints[i - 1]; + Integer sourceY = yPoints[i - 1]; + Integer targetX = xPoints[i]; + Integer targetY = yPoints[i]; + java.awt.geom.Line2D.Double line = new java.awt.geom.Line2D.Double((double) sourceX, (double) sourceY, (double) targetX, (double) targetY); + this.g.draw(line); + } + + java.awt.geom.Line2D.Double line; + if (isDefault) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawDefaultSequenceFlowIndicator(line, scaleFactor); + } + + if (conditional) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawConditionalSequenceFlowIndicator(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.ONE) || associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[xPoints.length - 2], (double) yPoints[xPoints.length - 2], (double) xPoints[xPoints.length - 1], (double) yPoints[xPoints.length - 1]); + this.drawArrowHead(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[1], (double) yPoints[1], (double) xPoints[0], (double) yPoints[0]); + this.drawArrowHead(line, scaleFactor); + } + + this.g.setPaint(originalPaint); + this.g.setStroke(originalStroke); + } + + /** + * 设置字体大小图标颜色 + * @param imageType + */ + @Override + public void initialize(String imageType) { + if ("png".equalsIgnoreCase(imageType)) { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 2); + } else { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 1); + } + + this.g = this.processDiagram.createGraphics(); + if (!"png".equalsIgnoreCase(imageType)) { + this.g.setBackground(new Color(255, 255, 255, 0)); + this.g.clearRect(0, 0, this.canvasWidth, this.canvasHeight); + } + + this.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + //修改图标颜色,修改图标字体大小 + this.g.setPaint(Color.black); + Font font = new Font(this.activityFontName, 10, 14); + this.g.setFont(font); + this.fontMetrics = this.g.getFontMetrics(); + //修改连接线字体大小 + LABEL_FONT = new Font(this.labelFontName, 10, 15); + ANNOTATION_FONT = new Font(this.annotationFontName, 0, 11); + + try { + USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/userTask.png", this.customClassLoader)); + SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/scriptTask.png", this.customClassLoader)); + SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/serviceTask.png", this.customClassLoader)); + RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/receiveTask.png", this.customClassLoader)); + SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/sendTask.png", this.customClassLoader)); + MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/manualTask.png", this.customClassLoader)); + BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/businessRuleTask.png", this.customClassLoader)); + SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/shellTask.png", this.customClassLoader)); + DMN_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/dmnTask.png", this.customClassLoader)); + CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/camelTask.png", this.customClassLoader)); + MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/muleTask.png", this.customClassLoader)); + HTTP_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/httpTask.png", this.customClassLoader)); + TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", this.customClassLoader)); + COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate-throw.png", this.customClassLoader)); + COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate.png", this.customClassLoader)); + ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error-throw.png", this.customClassLoader)); + ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error.png", this.customClassLoader)); + MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message-throw.png", this.customClassLoader)); + MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message.png", this.customClassLoader)); + SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal-throw.png", this.customClassLoader)); + SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal.png", this.customClassLoader)); + } catch (IOException var4) { + LOGGER.warn("Could not load image for process diagram creation: {}", var4.getMessage()); + } + + } + + /** + * 设置连接线字体 + * @param text + * @param graphicInfo + * @param centered + */ + @Override + public void drawLabel(String text, GraphicInfo graphicInfo, boolean centered) { + float interline = 1.0f; + + // text + if (text != null && text.length() > 0) { + Paint originalPaint = g.getPaint(); + Font originalFont = g.getFont(); + + g.setPaint(LABEL_COLOR); + g.setFont(LABEL_FONT); + + int wrapWidth = 100; + int textY = (int) graphicInfo.getY(); + + // TODO: use drawMultilineText() + AttributedString as = new AttributedString(text); + as.addAttribute(TextAttribute.FOREGROUND, g.getPaint()); + as.addAttribute(TextAttribute.FONT, g.getFont()); + AttributedCharacterIterator aci = as.getIterator(); + FontRenderContext frc = new FontRenderContext(null, true, false); + LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc); + + while (lbm.getPosition() < text.length()) { + TextLayout tl = lbm.nextLayout(wrapWidth); + textY += tl.getAscent(); + + Rectangle2D bb = tl.getBounds(); + double tX = graphicInfo.getX(); + + if (centered) { + tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2); + } + tl.draw(g, (float) tX, textY); + textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent(); + } + + // restore originals + g.setFont(originalFont); + g.setPaint(originalPaint); + } + } + + /** + * 高亮显示task框完成的 + * @param x + * @param y + * @param width + * @param height + */ + @Override + public void drawHighLight(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义task框当前的位置 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightNow(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR1); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义结束节点 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightEnd(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * task框自定义文字 + * @param name + * @param graphicInfo + * @param thickBorder + * @param scaleFactor + */ + @Override + protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder, double scaleFactor) { + + Paint originalPaint = g.getPaint(); + int x = (int) graphicInfo.getX(); + int y = (int) graphicInfo.getY(); + int width = (int) graphicInfo.getWidth(); + int height = (int) graphicInfo.getHeight(); + + // Create a new gradient paint for every task box, gradient depends on x and y and is not relative + g.setPaint(TASK_BOX_COLOR); + + int arcR = 6; + if (thickBorder) { + arcR = 3; + } + + // shape + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcR, arcR); + g.fill(rect); + g.setPaint(TASK_BORDER_COLOR); + + if (thickBorder) { + Stroke originalStroke = g.getStroke(); + g.setStroke(THICK_TASK_BORDER_STROKE); + g.draw(rect); + g.setStroke(originalStroke); + } else { + g.draw(rect); + } + + g.setPaint(originalPaint); + // text + if (scaleFactor == 1.0 && name != null && name.length() > 0) { + int boxWidth = width - (2 * TEXT_PADDING); + int boxHeight = height - 16 - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2; + int boxX = x + width / 2 - boxWidth / 2; + int boxY = y + height / 2 - boxHeight / 2 + ICON_PADDING + ICON_PADDING - 2 - 2; + + drawMultilineCentredText(name, boxX, boxY, boxWidth, boxHeight); + } + } + + protected static Color EVENT_COLOR = new Color(255, 255, 255); + + /** + * 重写开始事件 + * @param graphicInfo + * @param image + * @param scaleFactor + */ + @Override + public void drawStartEvent(GraphicInfo graphicInfo, BufferedImage image, double scaleFactor) { + Paint originalPaint = g.getPaint(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); + g.draw(circle); + g.setPaint(originalPaint); + if (image != null) { + // calculate coordinates to center image + int imageX = (int) Math.round(graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / (2 * scaleFactor))); + int imageY = (int) Math.round(graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / (2 * scaleFactor))); + g.drawImage(image, imageX, imageY, + (int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null); + } + + } + + /** + * 重写结束事件 + * @param graphicInfo + * @param scaleFactor + */ + @Override + public void drawNoneEndEvent(GraphicInfo graphicInfo, double scaleFactor) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); +// g.setPaint(HIGHLIGHT_COLOR); + if (scaleFactor == 1.0) { + g.setStroke(END_EVENT_STROKE); + } else { + g.setStroke(new BasicStroke(2.0f)); + } + g.draw(circle); + g.setStroke(originalStroke); + g.setPaint(originalPaint); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java new file mode 100644 index 00000000..d55e30dd --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java @@ -0,0 +1,404 @@ +package com.yanzhu.flowable.flow; + + +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; + +import java.util.Iterator; +import java.util.List; + +/** + * @author Tony + * @date 2021/4/5 0:31 + */ +public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator { + @Override + protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + this.prepareBpmnModel(bpmnModel); + DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + Iterator var13 = bpmnModel.getPools().iterator(); + + while (var13.hasNext()) { + Pool process = (Pool) var13.next(); + GraphicInfo subProcesses = bpmnModel.getGraphicInfo(process.getId()); + processDiagramCanvas.drawPoolOrLane(process.getName(), subProcesses, scaleFactor); + } + + var13 = bpmnModel.getProcesses().iterator(); + + Process process1; + Iterator subProcesses1; + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.getLanes().iterator(); + + while (subProcesses1.hasNext()) { + Lane artifact = (Lane) subProcesses1.next(); + GraphicInfo subProcess = bpmnModel.getGraphicInfo(artifact.getId()); + processDiagramCanvas.drawPoolOrLane(artifact.getName(), subProcess, scaleFactor); + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.findFlowElementsOfType(FlowNode.class).iterator(); + + while (subProcesses1.hasNext()) { + FlowNode artifact1 = (FlowNode) subProcesses1.next(); + if (!this.isPartOfCollapsedSubProcess(artifact1, bpmnModel)) { + this.drawActivity(processDiagramCanvas, bpmnModel, artifact1, highLightedActivities, highLightedFlows, scaleFactor, Boolean.valueOf(drawSequenceFlowNameWithNoLabelDI)); + } + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + label75: + while (true) { + List subProcesses2; + do { + if (!var13.hasNext()) { + return processDiagramCanvas; + } + + process1 = (Process) var13.next(); + subProcesses1 = process1.getArtifacts().iterator(); + + while (subProcesses1.hasNext()) { + Artifact artifact2 = (Artifact) subProcesses1.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, artifact2); + } + + subProcesses2 = process1.findFlowElementsOfType(SubProcess.class, true); + } while (subProcesses2 == null); + + Iterator artifact3 = subProcesses2.iterator(); + + while (true) { + GraphicInfo graphicInfo; + SubProcess subProcess1; + do { + do { + if (!artifact3.hasNext()) { + continue label75; + } + + subProcess1 = (SubProcess) artifact3.next(); + graphicInfo = bpmnModel.getGraphicInfo(subProcess1.getId()); + } while (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded().booleanValue()); + } while (this.isPartOfCollapsedSubProcess(subProcess1, bpmnModel)); + + Iterator var19 = subProcess1.getArtifacts().iterator(); + + while (var19.hasNext()) { + Artifact subProcessArtifact = (Artifact) var19.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact); + } + } + } + } + + protected static DefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + double minX = 1.7976931348623157E308D; + double maxX = 0.0D; + double minY = 1.7976931348623157E308D; + double maxY = 0.0D; + + GraphicInfo nrOfLanes; + for (Iterator flowNodes = bpmnModel.getPools().iterator(); flowNodes.hasNext(); maxY = nrOfLanes.getY() + nrOfLanes.getHeight()) { + Pool artifacts = (Pool) flowNodes.next(); + nrOfLanes = bpmnModel.getGraphicInfo(artifacts.getId()); + minX = nrOfLanes.getX(); + maxX = nrOfLanes.getX() + nrOfLanes.getWidth(); + minY = nrOfLanes.getY(); + } + + List var23 = gatherAllFlowNodes(bpmnModel); + Iterator var24 = var23.iterator(); + + label155: + while (var24.hasNext()) { + FlowNode var26 = (FlowNode) var24.next(); + GraphicInfo artifact = bpmnModel.getGraphicInfo(var26.getId()); + if (artifact.getX() + artifact.getWidth() > maxX) { + maxX = artifact.getX() + artifact.getWidth(); + } + + if (artifact.getX() < minX) { + minX = artifact.getX(); + } + + if (artifact.getY() + artifact.getHeight() > maxY) { + maxY = artifact.getY() + artifact.getHeight(); + } + + if (artifact.getY() < minY) { + minY = artifact.getY(); + } + + Iterator process = var26.getOutgoingFlows().iterator(); + + while (true) { + List l; + do { + if (!process.hasNext()) { + continue label155; + } + + SequenceFlow graphicInfoList = (SequenceFlow) process.next(); + l = bpmnModel.getFlowLocationGraphicInfo(graphicInfoList.getId()); + } while (l == null); + + Iterator graphicInfo = l.iterator(); + + while (graphicInfo.hasNext()) { + GraphicInfo graphicInfo1 = (GraphicInfo) graphicInfo.next(); + if (graphicInfo1.getX() > maxX) { + maxX = graphicInfo1.getX(); + } + + if (graphicInfo1.getX() < minX) { + minX = graphicInfo1.getX(); + } + + if (graphicInfo1.getY() > maxY) { + maxY = graphicInfo1.getY(); + } + + if (graphicInfo1.getY() < minY) { + minY = graphicInfo1.getY(); + } + } + } + } + + List var25 = gatherAllArtifacts(bpmnModel); + Iterator var27 = var25.iterator(); + + GraphicInfo var37; + while (var27.hasNext()) { + Artifact var29 = (Artifact) var27.next(); + GraphicInfo var31 = bpmnModel.getGraphicInfo(var29.getId()); + if (var31 != null) { + if (var31.getX() + var31.getWidth() > maxX) { + maxX = var31.getX() + var31.getWidth(); + } + + if (var31.getX() < minX) { + minX = var31.getX(); + } + + if (var31.getY() + var31.getHeight() > maxY) { + maxY = var31.getY() + var31.getHeight(); + } + + if (var31.getY() < minY) { + minY = var31.getY(); + } + } + + List var33 = bpmnModel.getFlowLocationGraphicInfo(var29.getId()); + if (var33 != null) { + Iterator var35 = var33.iterator(); + + while (var35.hasNext()) { + var37 = (GraphicInfo) var35.next(); + if (var37.getX() > maxX) { + maxX = var37.getX(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() > maxY) { + maxY = var37.getY(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + } + + int var28 = 0; + Iterator var30 = bpmnModel.getProcesses().iterator(); + + while (var30.hasNext()) { + Process var32 = (Process) var30.next(); + Iterator var34 = var32.getLanes().iterator(); + + while (var34.hasNext()) { + Lane var36 = (Lane) var34.next(); + ++var28; + var37 = bpmnModel.getGraphicInfo(var36.getId()); + if (var37.getX() + var37.getWidth() > maxX) { + maxX = var37.getX() + var37.getWidth(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() + var37.getHeight() > maxY) { + maxY = var37.getY() + var37.getHeight(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + + if (var23.isEmpty() && bpmnModel.getPools().isEmpty() && var28 == 0) { + minX = 0.0D; + minY = 0.0D; + } + + return new CustomProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + } + + + private static void drawHighLight(DefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightNow(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightNow((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightEnd(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightEnd((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + @Override + protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, + FlowNode flowNode, List highLightedActivities, List highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) { + + DefaultProcessDiagramGenerator.ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass()); + if (drawInstruction != null) { + + drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode); + + // Gather info on the multi instance marker + boolean multiInstanceSequential = false; + boolean multiInstanceParallel = false; + boolean collapsed = false; + if (flowNode instanceof Activity) { + Activity activity = (Activity) flowNode; + MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics(); + if (multiInstanceLoopCharacteristics != null) { + multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential(); + multiInstanceParallel = !multiInstanceSequential; + } + } + + // Gather info on the collapsed marker + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + if (flowNode instanceof SubProcess) { + collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded(); + } else if (flowNode instanceof CallActivity) { + collapsed = true; + } + + if (scaleFactor == 1.0) { + // Actually draw the markers + processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), + multiInstanceSequential, multiInstanceParallel, collapsed); + } + + // Draw highlighted activities + if (highLightedActivities.contains(flowNode.getId())) { + + if (highLightedActivities.get(highLightedActivities.size() - 1).equals(flowNode.getId()) + && !"endenv".equals(flowNode.getId())) { + if ((flowNode.getId().contains("Event_"))) { + drawHighLightEnd((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } else { + drawHighLightNow((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + } else { + drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + + + } + + } + + // Outgoing transitions of activity + for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) { + boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId())); + String defaultFlow = null; + if (flowNode instanceof Activity) { + defaultFlow = ((Activity) flowNode).getDefaultFlow(); + } else if (flowNode instanceof Gateway) { + defaultFlow = ((Gateway) flowNode).getDefaultFlow(); + } + + boolean isDefault = false; + if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) { + isDefault = true; + } + boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && !(flowNode instanceof Gateway); + + String sourceRef = sequenceFlow.getSourceRef(); + String targetRef = sequenceFlow.getTargetRef(); + FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef); + FlowElement targetElement = bpmnModel.getFlowElement(targetRef); + List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId()); + if (graphicInfoList != null && graphicInfoList.size() > 0) { + graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList); + int xPoints[] = new int[graphicInfoList.size()]; + int yPoints[] = new int[graphicInfoList.size()]; + + for (int i = 1; i < graphicInfoList.size(); i++) { + GraphicInfo graphicInfo = graphicInfoList.get(i); + GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); + + if (i == 1) { + xPoints[0] = (int) previousGraphicInfo.getX(); + yPoints[0] = (int) previousGraphicInfo.getY(); + } + xPoints[i] = (int) graphicInfo.getX(); + yPoints[i] = (int) graphicInfo.getY(); + + } + + processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor); + + + // Draw sequenceflow label + GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId()); + if (labelGraphicInfo != null) { + processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false); + } else { + if (drawSequenceFlowNameWithNoLabelDI) { + GraphicInfo lineCenter = getLineCenter(graphicInfoList); + processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false); + } + + } + } + } + + // Nested elements + if (flowNode instanceof FlowElementsContainer) { + for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) { + if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) { + drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement, + highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + } + } + } +} + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java new file mode 100644 index 00000000..a4bd2442 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java @@ -0,0 +1,257 @@ +package com.yanzhu.flowable.flow; + +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.Expression; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.ProcessDefinition; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author Tony + * @date 2021/4/19 20:51 + */ +public class FindNextNodeUtil { + + /** + * 获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasks(RepositoryService repositoryService, org.flowable.task.api.Task task, Map map) { + List data = new ArrayList<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = task.getTaskDefinitionKey(); + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + /** + * 启动流程时获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasksByStart(RepositoryService repositoryService, ProcessDefinition processDefinition, Map map) { + List data = new ArrayList<>(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = null; + // 找到开始节点 并获取唯一key + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + key = flowElement.getId(); + break; + } + } + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + + + /** + * 查找下一节点 + * + * @param flowElements + * @param flowElement + * @param map + * @param nextUser + */ + public static void next(Collection flowElements, FlowElement flowElement, Map map, List nextUser) { + //如果是结束节点 + if (flowElement instanceof EndEvent) { + //如果是子任务的结束节点 + if (getSubProcess(flowElements, flowElement) != null) { + flowElement = getSubProcess(flowElements, flowElement); + } + } + //获取Task的出线信息--可以拥有多个 + List outGoingFlows = null; + if (flowElement instanceof Task) { + outGoingFlows = ((Task) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof Gateway) { + outGoingFlows = ((Gateway) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof StartEvent) { + outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof SubProcess) { + outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof CallActivity) { + outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows(); + } + if (outGoingFlows != null && outGoingFlows.size() > 0) { + //遍历所有的出线--找到可以正确执行的那一条 + for (SequenceFlow sequenceFlow : outGoingFlows) { + //1.有表达式,且为true + //2.无表达式 + String expression = sequenceFlow.getConditionExpression(); + if (expression == null || + expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) { + //出线的下一节点 + String nextFlowElementID = sequenceFlow.getTargetRef(); + if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) { + continue; + } + + //查询下一节点的信息 + FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements); + //调用流程 + if (nextFlowElement instanceof CallActivity) { + CallActivity ca = (CallActivity) nextFlowElement; + if (ca.getLoopCharacteristics() != null) { + UserTask userTask = new UserTask(); + userTask.setId(ca.getId()); + + userTask.setId(ca.getId()); + userTask.setLoopCharacteristics(ca.getLoopCharacteristics()); + userTask.setName(ca.getName()); + nextUser.add(userTask); + } + next(flowElements, nextFlowElement, map, nextUser); + } + //用户任务 + if (nextFlowElement instanceof UserTask) { + nextUser.add((UserTask) nextFlowElement); + } + //排他网关 + else if (nextFlowElement instanceof ExclusiveGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //并行网关 + else if (nextFlowElement instanceof ParallelGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //接收任务 + else if (nextFlowElement instanceof ReceiveTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //服务任务 + else if (nextFlowElement instanceof ServiceTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //子任务的起点 + else if (nextFlowElement instanceof StartEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + //结束节点 + else if (nextFlowElement instanceof EndEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + } + } + } + } + + /** + * 判断是否是多实例子流程并且需要设置集合类型变量 + */ + public static boolean checkSubProcess(String Id, Collection flowElements, List nextUser) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess && flowElement1.getId().equals(Id)) { + + SubProcess sp = (SubProcess) flowElement1; + if (sp.getLoopCharacteristics() != null) { + String inputDataItem = sp.getLoopCharacteristics().getInputDataItem(); + UserTask userTask = new UserTask(); + userTask.setId(sp.getId()); + userTask.setLoopCharacteristics(sp.getLoopCharacteristics()); + userTask.setName(sp.getName()); + nextUser.add(userTask); + return true; + } + } + } + + return false; + + } + + /** + * 查询一个节点的是否子任务中的节点,如果是,返回子任务 + * + * @param flowElements 全流程的节点集合 + * @param flowElement 当前节点 + * @return + */ + public static FlowElement getSubProcess(Collection flowElements, FlowElement flowElement) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess) { + for (FlowElement flowElement2 : ((SubProcess) flowElement1).getFlowElements()) { + if (flowElement.equals(flowElement2)) { + return flowElement1; + } + } + } + } + return null; + } + + + /** + * 根据ID查询流程节点对象, 如果是子任务,则返回子任务的开始节点 + * + * @param Id 节点ID + * @param flowElements 流程节点集合 + * @return + */ + public static FlowElement getFlowElementById(String Id, Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement.getId().equals(Id)) { + //如果是子任务,则查询出子任务的开始节点 + if (flowElement instanceof SubProcess) { + return getStartFlowElement(((SubProcess) flowElement).getFlowElements()); + } + return flowElement; + } + if (flowElement instanceof SubProcess) { + FlowElement flowElement1 = getFlowElementById(Id, ((SubProcess) flowElement).getFlowElements()); + if (flowElement1 != null) { + return flowElement1; + } + } + } + return null; + } + + /** + * 返回流程的开始节点 + * + * @param flowElements 节点集合 + * @description: + */ + public static FlowElement getStartFlowElement(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return flowElement; + } + } + return null; + } + + /** + * 校验el表达式 + * + * @param map + * @param expression + * @return + */ + public static boolean expressionResult(Map map, String expression) { + Expression exp = AviatorEvaluator.compile(expression); + final Object execute = exp.execute(map); + return Boolean.parseBoolean(String.valueOf(execute)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java new file mode 100644 index 00000000..530cf038 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java @@ -0,0 +1,589 @@ +package com.yanzhu.flowable.flow; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.*; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.task.api.history.HistoricTaskInstance; + +import java.util.*; + +/** + * @author Tony + * @date 2021-04-03 23:57 + */ +@Slf4j +public class FlowableUtils { + + /** + * 根据节点,获取入口连线 + * @param source + * @return + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getIncomingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getIncomingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getIncomingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getIncomingFlows(); + } + return sequenceFlows; + } + + /** + * 根据节点,获取出口连线 + * @param source + * @return + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getOutgoingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getOutgoingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getOutgoingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取全部节点列表,包含子流程节点 + * @param flowElements + * @param allElements + * @return + */ + public static Collection getAllElements(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + + for (FlowElement flowElement : flowElements) { + allElements.add(flowElement); + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 迭代获取父级任务节点列表,向前找 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 已找到的用户任务节点 + * @return + */ + public static List iteratorFindParentUserTasks(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 = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 类型为用户节点,则新增父级节点 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); + continue; + } + // 类型为子流程,则添加子流程开始节点出口处相连的节点 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + // 获取子流程用户任务节点 + List childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 + * @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 EndEvent && source.getSubProcess() != null) { + userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + 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 (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 迭代获取子流程用户任务节点 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return + */ + public static List findChildProcessUserTasks(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) { + // 循环找到目标元素 + 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 = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 从后向前寻路,获取所有脏线路上的点 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 目标脏线路终点 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set iteratorFindDirtyRoads(FlowElement source, List passRoads, Set hasSequenceFlow, List targets, Set dirtyRoads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 新增经过的路线 + passRoads.add(sequenceFlow.getSourceFlowElement().getId()); + // 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线 + if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) { + dirtyRoads.addAll(passRoads); + continue; + } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, dirtyRoads); + // 是否存在子流程上,true 是,false 否 + Boolean isInChildProcess = dirtyTargetInChildProcess((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, targets, null); + if (isInChildProcess) { + // 已在子流程上找到,该路线结束 + continue; + } + } + // 继续迭代 + dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 迭代获取子流程脏路线 + * 说明,假如回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set findChildProcessAllDirtyRoad(FlowElement source, Set hasSequenceFlow, Set dirtyRoads) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加脏路线 + dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId()); + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, dirtyRoads); + } + // 继续迭代 + dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 判断脏路线结束节点是否在子流程上 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止 + * @param inChildProcess 是否存在子流程上,true 是,false 否 + * @return + */ + public static Boolean dirtyTargetInChildProcess(FlowElement source, Set hasSequenceFlow, List targets, Boolean inChildProcess) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + inChildProcess = inChildProcess != null && inChildProcess; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null && !inChildProcess) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果发现目标点在子流程上存在,说明只到子流程为止 + if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) { + inChildProcess = true; + break; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + inChildProcess = dirtyTargetInChildProcess((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, targets, inChildProcess); + } + // 继续迭代 + inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, targets, inChildProcess); + } + } + return inChildProcess; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param isSequential 是否串行 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targetKsy 目标节点 + * @return + */ + public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, Set hasSequenceFlow, Boolean isSequential) { + isSequential = isSequential == null || isSequential; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, isSequential); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果目标节点已被判断为并行,后面都不需要执行,直接返回 + if (!isSequential) { + break; + } + // 这条线路存在目标节点,这条线路完成,进入下个线路 + if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) { + continue; + } + if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) { + isSequential = false; + break; + } + // 否则就继续迭代 + isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, hasSequenceFlow, isSequential); + } + } + return isSequential; + } + + /** + * 从后向前寻路,获取到达节点的所有路线 + * 不存在直接回退到子流程,但是存在回退到父级流程的情况 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param roads 路线 + * @return + */ + public static List> findRoad(FlowElement source, List passRoads, Set hasSequenceFlow, List> roads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + roads = roads == null ? new ArrayList<>() : roads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null && sequenceFlows.size() != 0) { + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加经过路线 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + passRoads.add((UserTask) sequenceFlow.getSourceFlowElement()); + } + // 继续迭代 + roads = findRoad(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, roads); + } + } else { + // 添加路线 + roads.add(passRoads); + } + return roads; + } + + /** + * 历史节点数据清洗,清洗掉又回滚导致的脏数据 + * @param allElements 全部节点信息 + * @param historicTaskInstanceList 历史任务实例信息,数据采用开始时间升序 + * @return + */ + public static List historicTaskInstanceClean(Collection allElements, List historicTaskInstanceList) { + // 会签节点收集 + List multiTask = new ArrayList<>(); + allElements.forEach(flowElement -> { + if (flowElement instanceof UserTask) { + // 如果该节点的行为为会签行为,说明该节点为会签节点 + if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) { + multiTask.add(flowElement.getId()); + } + } + }); + // 循环放入栈,栈 LIFO:后进先出 + Stack stack = new Stack<>(); + historicTaskInstanceList.forEach(stack::push); + // 清洗后的历史任务实例 + List lastHistoricTaskInstanceList = new ArrayList<>(); + // 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗 + // 临时用户任务 key + StringBuilder userTaskKey = null; + // 临时被删掉的任务 key,存在并行情况 + List deleteKeyList = new ArrayList<>(); + // 临时脏数据线路 + List> dirtyDataLineList = new ArrayList<>(); + // 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到 + // 会签特殊处理下标 + int multiIndex = -1; + // 会签特殊处理 key + StringBuilder multiKey = null; + // 会签特殊处理操作标识 + boolean multiOpera = false; + while (!stack.empty()) { + // 从这里开始 userTaskKey 都还是上个栈的 key + // 是否是脏数据线路上的点 + final boolean[] isDirtyData = {false}; + for (Set oldDirtyDataLine : dirtyDataLineList) { + if (oldDirtyDataLine.contains(stack.peek().getTaskDefinitionKey())) { + isDirtyData[0] = true; + } + } + // 删除原因不为空,说明从这条数据开始回跳或者回退的 + // MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内 + if (stack.peek().getDeleteReason() != null && !"MI_END".equals(stack.peek().getDeleteReason())) { + // 可以理解为脏线路起点 + String dirtyPoint = ""; + if (stack.peek().getDeleteReason().contains("Change activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", ""); + } + // 会签回退删除原因有点不同 + if (stack.peek().getDeleteReason().contains("Change parent activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", ""); + } + FlowElement dirtyTask = null; + // 获取变更节点的对应的入口处连线 + // 如果是网关并行回退情况,会变成两条脏数据路线,效果一样 + for (FlowElement flowElement : allElements) { + if (flowElement.getId().equals(stack.peek().getTaskDefinitionKey())) { + dirtyTask = flowElement; + } + } + // 获取脏数据线路 + Set dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, Arrays.asList(dirtyPoint.split(",")), null); + // 自己本身也是脏线路上的点,加进去 + dirtyDataLine.add(stack.peek().getTaskDefinitionKey()); + log.info(stack.peek().getTaskDefinitionKey() + "点脏路线集合:" + dirtyDataLine); + // 是全新的需要添加的脏线路 + boolean isNewDirtyData = true; + for (int i = 0; i < dirtyDataLineList.size(); i++) { + // 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回 + // 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全 + if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) { + isNewDirtyData = false; + dirtyDataLineList.get(i).addAll(dirtyDataLine); + } + } + // 已确定时全新的脏线路 + if (isNewDirtyData) { + // deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成 + // 按照逻辑,回退后立刻生成的实例记录就是回退的记录 + // 至于驳回所生成的 Key,直接从删除原因中获取,因为存在驳回到并行的情况 + deleteKeyList.add(dirtyPoint + ","); + dirtyDataLineList.add(dirtyDataLine); + } + // 添加后,现在这个点变成脏线路上的点了 + isDirtyData[0] = true; + } + // 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key + if (!isDirtyData[0]) { + lastHistoricTaskInstanceList.add(stack.peek().getTaskDefinitionKey()); + } + // 校验脏线路是否结束 + for (int i = 0; i < deleteKeyList.size(); i ++) { + // 如果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始 + if (multiKey == null && multiTask.contains(stack.peek().getTaskDefinitionKey()) + && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + multiIndex = i; + multiKey = new StringBuilder(stack.peek().getTaskDefinitionKey()); + } + // 会签脏数据处理,节点退回会签清空 + // 如果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉 + if (multiKey != null && !multiKey.toString().equals(stack.peek().getTaskDefinitionKey())) { + deleteKeyList.set(multiIndex , deleteKeyList.get(multiIndex).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + multiKey = null; + // 结束进行下校验删除 + multiOpera = true; + } + // 其他脏数据处理 + // 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息 + // 脏数据产生的新实例中是否包含这条数据 + if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + // 删除匹配到的部分 + deleteKeyList.set(i , deleteKeyList.get(i).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + } + // 如果每组中的元素都以匹配过,说明脏数据结束 + if ("".equals(deleteKeyList.get(i))) { + // 同时删除脏数据 + deleteKeyList.remove(i); + dirtyDataLineList.remove(i); + break; + } + } + // 会签数据处理需要在循环外处理,否则可能导致溢出 + // 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下 + if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) { + // 同时删除脏数据 + deleteKeyList.remove(multiIndex); + dirtyDataLineList.remove(multiIndex); + multiIndex = -1; + multiOpera = false; + } + // pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除 + // 保存新的 userTaskKey 在下个循环中使用 + userTaskKey = new StringBuilder(stack.pop().getTaskDefinitionKey()); + } + log.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList); + return lastHistoricTaskInstanceList; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java new file mode 100644 index 00000000..1a81e6ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java @@ -0,0 +1,372 @@ +package com.yanzhu.flowable.flow; + +import com.yanzhu.common.core.utils.StringUtils; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.common.engine.impl.util.io.StringStreamSource; + +import java.util.*; + +/** + * @author KonBAI + * @createTime 2022/3/26 19:04 + */ +public class ModelUtils { + + private static final BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter(); + + /** + * xml转bpmnModel对象 + * + * @param xml xml + * @return bpmnModel对象 + */ + public static BpmnModel getBpmnModel(String xml) { + return bpmnXMLConverter.convertToBpmnModel(new StringStreamSource(xml), false, false); + } + + /** + * bpmnModel转xml字符串 + * + * @deprecated 存在会丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml字符串 + */ + @Deprecated + public static String getBpmnXmlStr(BpmnModel bpmnModel) { + return StringUtils.utf8Str(getBpmnXml(bpmnModel)); + } + + /** + * bpmnModel转xml对象 + * + * @deprecated 存在丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml + */ + @Deprecated + public static byte[] getBpmnXml(BpmnModel bpmnModel) { + return bpmnXMLConverter.convertToXML(bpmnModel); + } + + /** + * 根据节点,获取入口连线 + * + * @param source 起始节点 + * @return 入口连线列表 + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } + return sequenceFlows; + } + + + /** + * 根据节点,获取出口连线 + * + * @param source 起始节点 + * @return 出口连线列表 + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取开始节点 + * + * @param model bpmnModel对象 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(BpmnModel model) { + Process process = model.getMainProcess(); + FlowElement startElement = process.getInitialFlowElement(); + if (startElement instanceof StartEvent) { + return (StartEvent) startElement; + } + return getStartEvent(process.getFlowElements()); + } + + /** + * 获取开始节点 + * + * @param flowElements 流程元素集合 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return (StartEvent) flowElement; + } + } + return null; + } + + /** + * 获取结束节点 + * + * @param model bpmnModel对象 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(BpmnModel model) { + Process process = model.getMainProcess(); + return getEndEvent(process.getFlowElements()); + } + + /** + * 获取结束节点 + * + * @param flowElements 流程元素集合 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof EndEvent) { + return (EndEvent) flowElement; + } + } + return null; + } + + public static UserTask getUserTaskByKey(BpmnModel model, String taskKey) { + Process process = model.getMainProcess(); + FlowElement flowElement = process.getFlowElement(taskKey); + if (flowElement instanceof UserTask) { + return (UserTask) flowElement; + } + return null; + } + + /** + * 获取流程元素信息 + * + * @param model bpmnModel对象 + * @param flowElementId 元素ID + * @return 元素信息 + */ + public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { + Process process = model.getMainProcess(); + return process.getFlowElement(flowElementId); + } + + /** + * 获取元素表单Key(限开始节点和用户节点可用) + * + * @param flowElement 元素 + * @return 表单Key + */ + public static String getFormKey(FlowElement flowElement) { + if (flowElement != null) { + if (flowElement instanceof StartEvent) { + return ((StartEvent) flowElement).getFormKey(); + } else if (flowElement instanceof UserTask) { + return ((UserTask) flowElement).getFormKey(); + } + } + return null; + } + + /** + * 获取开始节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getStartEventAttributeValue(BpmnModel model, String name) { + StartEvent startEvent = getStartEvent(model); + return getElementAttributeValue(startEvent, name); + } + + /** + * 获取结束节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getEndEventAttributeValue(BpmnModel model, String name) { + EndEvent endEvent = getEndEvent(model); + return getElementAttributeValue(endEvent, name); + } + + /** + * 获取用户任务节点属性值 + * @param model bpmnModel对象 + * @param taskKey 任务Key + * @param name 属性名 + * @return 属性值 + */ + public static String getUserTaskAttributeValue(BpmnModel model, String taskKey, String name) { + UserTask userTask = getUserTaskByKey(model, taskKey); + return getElementAttributeValue(userTask, name); + } + + /** + * 获取元素属性值 + * @param baseElement 流程元素 + * @param name 属性名 + * @return 属性值 + */ + public static String getElementAttributeValue(BaseElement baseElement, String name) { + if (baseElement != null) { + List attributes = baseElement.getAttributes().get(name); + if (attributes != null && !attributes.isEmpty()) { + attributes.iterator().next().getValue(); + Iterator attrIterator = attributes.iterator(); + if(attrIterator.hasNext()) { + ExtensionAttribute attribute = attrIterator.next(); + return attribute.getValue(); + } + } + } + return null; + } + + public static boolean isMultiInstance(BpmnModel model, String taskKey) { + UserTask userTask = getUserTaskByKey(model, taskKey); + if (!Objects.isNull(userTask)) { + return userTask.hasMultiInstanceLoopCharacteristics(); + } + return false; + } + + /** + * 获取所有用户任务节点 + * + * @param model bpmnModel对象 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(BpmnModel model) { + Process process = model.getMainProcess(); + Collection flowElements = process.getFlowElements(); + return getAllUserTaskEvent(flowElements, null); + } + + /** + * 获取所有用户任务节点 + * @param flowElements 流程元素集合 + * @param allElements 所有流程元素集合 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof UserTask) { + allElements.add((UserTask) flowElement); + } + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = getAllUserTaskEvent(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source) { + return findNextUserTasks(source, null, null); + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 用户任务列表 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source, Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>()); + userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>()); + // 获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (!sequenceFlows.isEmpty()) { + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement(); + if (targetFlowElement instanceof UserTask) { + // 若节点为用户任务,加入到结果列表中 + userTaskList.add((UserTask) targetFlowElement); + } else { + // 若节点非用户任务,继续递归查找下一个节点 + findNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList); + } + } + } + return userTaskList; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param target 目标节点 + * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 + * @return 结果 + */ + 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 (sequenceFlows != null && sequenceFlows.size() > 0) { + // 循环找到目标元素 + 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; + } + // 否则就继续迭代 + boolean isSequential = isSequentialReachable(sourceFlowElement, target, visitedElements); + if (!isSequential) { + return false; + } + } + } + return true; + } + + protected 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; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java new file mode 100644 index 00000000..a937b6ce --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java @@ -0,0 +1,42 @@ +package com.yanzhu.flowable.listener; + +import com.yanzhu.common.redis.service.RedisService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Flowable全局监听器 + * + * @author JiangYuQi + */ +@Slf4j +@Component +public class GlobalEventListener extends AbstractFlowableEngineEventListener { + + @Autowired + private RedisService redisService; + + @Autowired + private TaskService taskService; + + @Autowired + private RuntimeService runtimeService; + + @Autowired + private RepositoryService repositoryService; + + @Override + protected void processCompleted(FlowableEngineEntityEvent event) { + log.info("任务流程审批完成...{}",event.getProcessInstanceId()); + super.processCompleted(event); + } + +} + + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java new file mode 100644 index 00000000..1e6f1f7d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java @@ -0,0 +1,56 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.FlowTaskEntity; + +import java.util.List; +import java.util.Map; + +/** + * @author JiangYuQi + * @date 2021-04-03 14:41 + */ +public interface IFlowBusinessKeyService { + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + public Map quueryCount(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + public List> selectCommentByProcInsId(String procInsId); + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + public Map selectFormDatasByProcInsId(String procInsId); + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity); + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java new file mode 100644 index 00000000..80258211 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java @@ -0,0 +1,81 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:41 + */ +public interface IFlowDefinitionService { + + boolean exist(String processDefinitionKey); + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + TableDataInfo list(FlowQueryVo flowQueryVo); + + /** + * 导入流程文件 + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name + * @param category + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in + */ + void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in); + + /** + * 读取xml + * @param deployId + * @return + */ + AjaxResult readXml(String deployId) throws IOException; + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程ID + * @param applyInfos 申请表单 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map applyInfos); + + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + void updateState(Integer state, String deployId); + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + void delete(String deployId); + + + /** + * 读取图片文件 + * @param deployId + * @return + */ + InputStream readImage(String deployId); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java new file mode 100644 index 00000000..acc69698 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java @@ -0,0 +1,54 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.engine.history.HistoricProcessInstance; + +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:40 + */ +public interface IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + void stopProcessInstance(FlowTaskVo vo); + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + void updateState(Integer state, String instanceId); + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + void delete(String instanceId, String deleteReason); + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId); + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map variables); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java new file mode 100644 index 00000000..c23dd4ec --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java @@ -0,0 +1,208 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.task.api.Task; + +import java.io.InputStream; + +/** + * @author Tony + * @date 2021-04-03 14:42 + */ +public interface IFlowTaskService { + + /** + * 审批任务 + * + * @param task 请求实体参数 + */ + AjaxResult complete(FlowTaskVo task); + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + void taskReject(FlowTaskVo flowTaskVo); + + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + void taskReturn(FlowTaskVo flowTaskVo); + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo); + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + void deleteTask(FlowTaskVo flowTaskVo); + + /** + * 认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void claim(FlowTaskVo flowTaskVo); + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void unClaim(FlowTaskVo flowTaskVo); + + /** + * 委派任务 + * + * @param flowTaskVo 请求实体参数 + */ + void delegateTask(FlowTaskVo flowTaskVo); + + /** + * 任务归还 + * + * @param flowTaskVo 请求实体参数 + */ + void resolveTask(FlowTaskVo flowTaskVo); + + + /** + * 转办任务 + * + * @param flowTaskVo 请求实体参数 + */ + void assignTask(FlowTaskVo flowTaskVo); + + + /** + * 多实例加签 + * @param flowTaskVo + */ + void addMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 多实例减签 + * @param flowTaskVo + */ + void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 我发起的流程 + * @param queryVo 请求参数 + * @return + */ + AjaxResult myProcess(FlowQueryVo queryVo); + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * @param flowTaskVo + * @return + */ + AjaxResult stopProcess(FlowTaskVo flowTaskVo); + + /** + * 撤回流程 + * @param flowTaskVo + * @return + */ + AjaxResult revokeProcess(FlowTaskVo flowTaskVo); + + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult todoList(FlowQueryVo queryVo); + + + /** + * 已办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult finishedList(FlowQueryVo queryVo); + + /** + * 流程历史流转记录 + * + * @param procInsId 流程实例Id + * @return + */ + AjaxResult flowRecord(String procInsId,String deployId); + + /** + * 根据任务ID查询挂载的表单信息 + * + * @param taskId 任务Id + * @return + */ + Task getTaskForm(String taskId); + + /** + * 获取流程过程图 + * @param processId + * @return + */ + InputStream diagram(String processId); + + /** + * 获取流程执行节点 + * @param procInsId + * @return + */ + AjaxResult getFlowViewer(String procInsId,String executionId); + + /** + * 获取流程变量 + * @param taskId + * @return + */ + AjaxResult processVariables(String taskId); + + /** + * 获取下一节点 + * @param flowTaskVo 任务 + * @return + */ + AjaxResult getNextFlowNode(FlowTaskVo flowTaskVo); + + AjaxResult getNextFlowNodeByStart(FlowTaskVo flowTaskVo); + + /** + * 流程初始化表单 + * @param deployId + * @return + */ + AjaxResult flowFormData(String deployId); + + /** + * 流程节点信息 + * @param procInsId + * @return + */ + AjaxResult flowXmlAndNode(String procInsId,String deployId); + + /** + * 流程节点表单 + * @param taskId 流程任务编号 + * @return + */ + AjaxResult flowTaskForm(String taskId) throws Exception; +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java new file mode 100644 index 00000000..01ff72bc --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程实例关联表单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +public interface ISysDeployFormService +{ + /** + * 查询流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 流程实例关联表单 + */ + public SysDeployForm selectSysDeployFormById(Long id); + + /** + * 查询流程实例关联表单列表 + * + * @param sysDeployForm 流程实例关联表单 + * @return 流程实例关联表单集合 + */ + public List selectSysDeployFormList(SysDeployForm sysDeployForm); + + /** + * 新增流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int insertSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 修改流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int updateSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 批量删除流程实例关联表单 + * + * @param ids 需要删除的流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormByIds(Long[] ids); + + /** + * 删除流程实例关联表单信息 + * + * @param id 流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormById(Long id); + + /** + * 查询流程挂着的表单 + * @param deployId + * @return + */ + SysForm selectSysDeployFormByDeployId(String deployId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java new file mode 100644 index 00000000..4f230788 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysExpression; + +import java.util.List; + +/** + * 流程达式Service接口 + * + * @author yanZhu + * @date 2022-12-12 + */ +public interface ISysExpressionService +{ + /** + * 查询流程达式 + * + * @param id 流程达式主键 + * @return 流程达式 + */ + public SysExpression selectSysExpressionById(Long id); + + /** + * 查询流程达式列表 + * + * @param sysExpression 流程达式 + * @return 流程达式集合 + */ + public List selectSysExpressionList(SysExpression sysExpression); + + /** + * 新增流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int insertSysExpression(SysExpression sysExpression); + + /** + * 修改流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int updateSysExpression(SysExpression sysExpression); + + /** + * 批量删除流程达式 + * + * @param ids 需要删除的流程达式主键集合 + * @return 结果 + */ + public int deleteSysExpressionByIds(Long[] ids); + + /** + * 删除流程达式信息 + * + * @param id 流程达式主键 + * @return 结果 + */ + public int deleteSysExpressionById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java new file mode 100644 index 00000000..98a844ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java @@ -0,0 +1,61 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 表单 + * @author Tony + * @date 2021-04-03 + */ +public interface ISysFormService +{ + /** + * 查询流程表单 + * + * @param formId 流程表单ID + * @return 流程表单 + */ + public SysForm selectSysFormById(Long formId); + + /** + * 查询流程表单列表 + * + * @param sysForm 流程表单 + * @return 流程表单集合 + */ + public List selectSysFormList(SysForm sysForm); + + /** + * 新增流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int insertSysForm(SysForm sysForm); + + /** + * 修改流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int updateSysForm(SysForm sysForm); + + /** + * 批量删除流程表单 + * + * @param formIds 需要删除的流程表单ID + * @return 结果 + */ + public int deleteSysFormByIds(Long[] formIds); + + /** + * 删除流程表单信息 + * + * @param formId 流程表单ID + * @return 结果 + */ + public int deleteSysFormById(Long formId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java new file mode 100644 index 00000000..c269e24b --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysTaskForm; + +import java.util.List; + +/** + * 流程任务关联单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +@Deprecated +public interface ISysTaskFormService { + /** + * 查询流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 流程任务关联单 + */ + public SysTaskForm selectSysTaskFormById(Long id); + + /** + * 查询流程任务关联单列表 + * + * @param sysTaskForm 流程任务关联单 + * @return 流程任务关联单集合 + */ + public List selectSysTaskFormList(SysTaskForm sysTaskForm); + + /** + * 新增流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int insertSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 修改流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int updateSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 批量删除流程任务关联单 + * + * @param ids 需要删除的流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormByIds(Long[] ids); + + /** + * 删除流程任务关联单信息 + * + * @param id 流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java new file mode 100644 index 00000000..f5150cbb --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java @@ -0,0 +1,102 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.flowable.domain.FlowTaskEntity; +import com.yanzhu.flowable.mapper.FlowBusinessKeyMapper; +import com.yanzhu.flowable.service.IFlowBusinessKeyService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * 业务工作流程自定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowBusinessKeyServiceImpl implements IFlowBusinessKeyService { + + @Resource + private FlowBusinessKeyMapper flowBusinessKeyMapper; + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity); + } + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public Map quueryCount(FlowTaskEntity flowTaskEntity) { + flowTaskEntity.setActiveName("await"); + int awaitSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + flowTaskEntity.setActiveName("finished"); + int finishedSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + Map dataMap = new HashMap<>(); + dataMap.put("await",awaitSize); + dataMap.put("finished",finishedSize); + return dataMap; + } + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + @Override + public List> selectCommentByProcInsId(String procInsId) { + return flowBusinessKeyMapper.selectCommentByProcInsId(procInsId); + } + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + @Override + public Map selectFormDatasByProcInsId(String procInsId) { + Map dataMap = new HashMap<>(); + List> list = flowBusinessKeyMapper.selectFormDatasByProcInsId(procInsId); + Iterator> it = list.iterator(); + while (it.hasNext()) { + Map map = it.next(); + dataMap.put(Convert.toStr(map.get("name")), map.get("text")); + } + return dataMap; + } + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity){ + return flowBusinessKeyMapper.selectMyAwaitFlowTask(flowTaskEntity); + } + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectMyFinishedFlowTask(flowTaskEntity); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java new file mode 100644 index 00000000..0d9f656f --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java @@ -0,0 +1,279 @@ +package com.yanzhu.flowable.service.impl; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.yanzhu.common.core.constant.HttpStatus; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.ServiceException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.SysExpression; +import com.yanzhu.flowable.domain.vo.FlowDeptVo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.mapper.FlowDeployMapper; +import com.yanzhu.flowable.service.IFlowDefinitionService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.repository.ProcessDefinitionQuery; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; +import org.flowable.task.api.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 流程定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFlowDefinitionService { + + @Autowired + private RedisService redisService; + + @Resource + private FlowDeployMapper flowDeployMapper; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private ISysDeployFormService sysDeployFormService; + + private static final String BPMN_FILE_SUFFIX = ".bpmn"; + + @Override + public boolean exist(String processDefinitionKey) { + ProcessDefinitionQuery processDefinitionQuery + = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey); + long count = processDefinitionQuery.count(); + return count > 0 ? true : false; + } + + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + @Override + public TableDataInfo list(FlowQueryVo flowQueryVo) { + TableDataInfo rspData = new TableDataInfo(); + PageHelper.startPage(flowQueryVo.getPageNum(), flowQueryVo.getPageSize()); + List list = flowDeployMapper.selectDeployList(flowQueryVo); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + + /** + * 导入流程文件 + * + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name 流程名称 + * @param category 流程类型 + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in 流程文件流 + */ + @Override + @Transactional + public void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in) { + if(SecurityUtils.isAdmin(SecurityUtils.getUserId())){ + throw new ServiceException("请选择项目后导入流程文件..."); + } + Deployment deploy = repositoryService.createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).category(category).deploy(); + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), category); + /**新增流程单位关系**/ + FlowDeptVo flowDeptVo = new FlowDeptVo(); + flowDeptVo.setProcdefId(definition.getId()); + //超管不能导入流程文件 + if(SecurityUtils.isGSAdmin()){ + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setDeptId(Convert.toLong(OwnerDeptId)); + } + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setProjId(Convert.toLong(OwnerProjectId)); + } + }else{ + // 从用户信息中获取项目单位信息... + flowDeptVo.setPrtype(prtype); + flowDeptVo.setProjId(SecurityUtils.getLoginUser().getProjectId()); + flowDeptVo.setDeptId(SecurityUtils.getLoginUser().getProjectDeptId()); + } + if(flowDeptVo.getDeptId()!=null){ + flowDeployMapper.insertActReProcdefDept(flowDeptVo); + } + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public AjaxResult readXml(String deployId) throws IOException { + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName()); + String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name()); + return AjaxResult.success("", result); + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public InputStream readImage(String deployId) { + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + //获得图片流 + DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + //输出为图片 + return diagramGenerator.generateDiagram( + bpmnModel, + "png", + Collections.emptyList(), + Collections.emptyList(), + "宋体", + "宋体", + "宋体", + null, + 1.0, + false); + + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程模板ID + * @param applyInfos 流程表单 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map applyInfos) { + try { + /**保存流程表单**/ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+procDefId); + R res; + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsGroupEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsUsersEntity(applyInfos, SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + if(StringUtils.isNotNull(res.getData())){ + Map variables = new HashMap<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId) + .latestVersion().singleResult(); + if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) { + return AjaxResult.error("流程已被挂起,请联系管理员激活流程"); + } + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + String userId = Convert.toStr(sysUser.getUserId()); + // 设置流程发起人Id到流程中 + identityService.setAuthenticatedUserId(userId); + variables.put(ProcessConstants.PROCESS_INITIATOR, userId); + variables.put("businessKey", res.getData()); + ProcessInstance processInstance; + processInstance = runtimeService.startProcessInstanceById(procDefId, Convert.toStr(res.getData()),variables); + // 给第一步申请人节点设置任务执行人和意见 + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult(); + if (Objects.nonNull(task)) { + taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), sysUser.getNickName() + "发起流程申请"); + taskService.setAssignee(task.getId(), Convert.toStr(userId)); + taskService.complete(task.getId(), variables); + } + return AjaxResult.success("流程启动成功"); + }else{ + return AjaxResult.error("流程表单保存失败"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + @Override + public void updateState(Integer state, String deployId) { + ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + // 激活 + if (state == 1) { + repositoryService.activateProcessDefinitionById(procDef.getId(), true, null); + } + // 挂起 + if (state == 2) { + repositoryService.suspendProcessDefinitionById(procDef.getId(), true, null); + } + } + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + @Override + public void delete(String deployId) { + // true 允许级联删除 ,不设置会导致数据库外键关联异常 + repositoryService.deleteDeployment(deployId, true); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java new file mode 100644 index 00000000..4facc05d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java @@ -0,0 +1,117 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.service.IFlowInstanceService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.engine.history.HistoricProcessInstance; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Map; +import java.util.Objects; + +/** + * 工作流流程实例管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowInstanceServiceImpl extends FlowServiceFactory implements IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + @Override + public void stopProcessInstance(FlowTaskVo vo) { + String taskId = vo.getTaskId(); + + } + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + @Override + public void updateState(Integer state, String instanceId) { + + // 激活 + if (state == 1) { + runtimeService.activateProcessInstanceById(instanceId); + } + // 挂起 + if (state == 2) { + runtimeService.suspendProcessInstanceById(instanceId); + } + } + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(String instanceId, String deleteReason) { + + // 查询历史数据 + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(instanceId); + if (historicProcessInstance.getEndTime() != null) { + historyService.deleteHistoricProcessInstance(historicProcessInstance.getId()); + return; + } + // 删除流程实例 + runtimeService.deleteProcessInstance(instanceId, deleteReason); + // 删除历史流程实例 + historyService.deleteHistoricProcessInstance(instanceId); + } + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + @Override + public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) { + HistoricProcessInstance historicProcessInstance = + historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + if (Objects.isNull(historicProcessInstance)) { + throw new FlowableObjectNotFoundException("流程实例不存在: " + processInstanceId); + } + return historicProcessInstance; + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map variables) { + + try { + // 设置流程发起人Id到流程中 + Long userId = SecurityUtils.getUserId(); + variables.put("initiator",userId); + variables.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true); + runtimeService.startProcessInstanceById(procDefId, variables); + return AjaxResult.success("流程启动成功"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java new file mode 100644 index 00000000..50208812 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java @@ -0,0 +1,1307 @@ +package com.yanzhu.flowable.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.CustomException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.*; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.flow.CustomProcessDiagramGenerator; +import com.yanzhu.flowable.flow.FindNextNodeUtil; +import com.yanzhu.flowable.flow.FlowableUtils; +import com.yanzhu.flowable.flow.ModelUtils; +import com.yanzhu.flowable.service.IFlowTaskService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.RemoteUserService; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.impl.cmd.AddMultiInstanceExecutionCmd; +import org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.task.Comment; +import org.flowable.identitylink.api.history.HistoricIdentityLink; +import org.flowable.image.ProcessDiagramGenerator; +import org.flowable.task.api.DelegationState; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * @author Tony + * @date 2021-04-03 + **/ +@Service +@Slf4j +public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTaskService { + + @Autowired + private RedisService redisService; + + @Resource + private ISysFormService sysFormService; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private RemoteUserService remoteUserService; + + @Resource + private RemoteRoleService remoteRoleService; + + @Resource + private ISysDeployFormService sysInstanceFormService; + + /** + * 完成任务 + * + * @param taskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public AjaxResult complete(FlowTaskVo taskVo) { + Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult(); + /**表单不为空时,重新修改表单**/ + if(StringUtils.isNotNull(taskVo.getApplyInfos())){ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+task.getProcessDefinitionId()); + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsGroupEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsUsersEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + } + if (Objects.isNull(task)) { + return AjaxResult.error("任务不存在"); + } + if (DelegationState.PENDING.equals(task.getDelegationState())) { + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment()); + taskService.resolveTask(taskVo.getTaskId()); + } else { + Long userId = SecurityUtils.getUserId(); + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment()); + taskService.setAssignee(taskVo.getTaskId(), Convert.toStr(userId)); + taskService.complete(taskVo.getTaskId()); + } + return AjaxResult.success(); + } + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + @Override + public void taskReject(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态!"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + // 获取节点信息 + source = flowElement; + } + } + } + + // 目的获取所有跳转到的节点 targetIds + // 获取当前节点的所有父级用户任务节点 + // 深度优先算法思想:延边迭代深入 + List parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null); + if (parentUserTaskList == null || parentUserTaskList.size() == 0) { + throw new CustomException("当前节点为初始任务节点,不能驳回"); + } + // 获取活动 ID 即节点 Key + List parentUserTaskKeyList = new ArrayList<>(); + parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId())); + // 获取全部历史节点活动实例,即已经走过的节点历史,数据采用开始时间升序 + List historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list(); + // 数据清洗,将回滚导致的脏数据清洗掉 + List lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList); + // 此时历史任务实例为倒序,获取最后走的节点 + List targetIds = new ArrayList<>(); + // 循环结束标识,遇到当前目标节点的次数 + int number = 0; + StringBuilder parentHistoricTaskKey = new StringBuilder(); + for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) { + // 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过 + if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) { + continue; + } + parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey); + if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) { + number++; + } + // 在数据清洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次 + // 在流程中如果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环 + // number == 1,第一次遇到当前节点 + // number == 2,第二次遇到,代表最后一次的循环范围 + if (number == 2) { + break; + } + // 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点 + if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) { + targetIds.add(historicTaskInstanceKey); + } + } + + + // 目的获取所有需要被跳转的节点 currentIds + // 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路 + UserTask oneUserTask = parentUserTaskList.get(0); + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需驳回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + + // 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况 + if (targetIds.size() > 1 && currentIds.size() > 1) { + throw new CustomException("任务出现多对多情况,无法撤回"); + } + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置驳回意见 + currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment())); + + try { + // 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况 + if (targetIds.size() > 1) { + // 1 对 多任务跳转,currentIds 当前节点(1),targetIds 跳转到的节点(多) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()). + moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState(); + } + // 如果父级任务只有一个,因此当前任务可能为网关中的任务 + if (targetIds.size() == 1) { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetIds.get(0) 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState(); + } + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + + } + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void taskReturn(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + // 获取跳转的节点元素 + FlowElement target = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 当前任务节点元素 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = flowElement; + } + // 跳转的节点元素 + if (flowElement.getId().equals(flowTaskVo.getTargetKey())) { + target = flowElement; + } + } + } + + // 从当前节点向前扫描 + // 如果存在路线上不存在目标节点,说明目标节点是在网关上或非同一路线上,不可跳转 + // 否则目标节点相对于当前节点,属于串行 + Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, flowTaskVo.getTargetKey(), null, null); + if (!isSequential) { + throw new CustomException("当前节点相对于目标节点,不属于串行关系,无法回退"); + } + + + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需退回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置回退意见 + currentTaskIds.forEach(currentTaskId -> taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), flowTaskVo.getComment())); + + try { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, flowTaskVo.getTargetKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + } + + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + @Override + public AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo) { +// // 当前任务 task +// Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); +// // 从流程历史任务中获取可退回节点 +//// List hisActIns = historyService.createHistoricActivityInstanceQuery() +//// .executionId(task.getExecutionId()) +//// .activityType("userTask") +//// .orderByHistoricActivityInstanceStartTime() +//// .finished() +//// .desc() +//// .list(); +//// +//// // 可回退的节点列表 +//// List returnTaskNodeList = new ArrayList<>(); +//// ReturnTaskNodeVo returnTaskNodeVo; +//// for (HistoricActivityInstance activityInstance : hisActIns) { +//// returnTaskNodeVo = new ReturnTaskNodeVo(); +//// returnTaskNodeVo.setId(activityInstance.getActivityId()); +//// // 根据流程节点处理时间校验改节点是否已完成 +//// returnTaskNodeVo.setName(activityInstance.getActivityName()); +//// returnTaskNodeList.add(returnTaskNodeVo); +//// } +// List userTaskList = new ArrayList<>(); +// // 获取流程定义信息 +// ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); +// // 获取所有节点信息,暂不考虑子流程情况 +// Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); +// Collection flowElements = process.getFlowElements(); +// // 获取当前任务节点元素 +// UserTask source = null; +// if (flowElements != null) { +// for (FlowElement flowElement : flowElements) { +// // 类型为用户节点 +// if (flowElement.getId().equals(task.getTaskDefinitionKey())) { +// source = (UserTask) flowElement; +// } +// } +// } +// // 获取节点的所有路线 +// List> roads = FlowableUtils.findRoad(source, null, null, null); +// +// for (List road : roads) { +// if (userTaskList.size() == 0) { +// // 还没有可回退节点直接添加 +// userTaskList = road; +// } else { +// // 如果已有回退节点,则比对取交集部分 +// userTaskList.retainAll(road); +// } +// } +// return AjaxResult.success(userTaskList); + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息,暂不考虑子流程情况 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + Collection flowElements = process.getFlowElements(); + // 获取当前任务节点元素 + UserTask source = null; + if (flowElements != null) { + for (FlowElement flowElement : flowElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = (UserTask) flowElement; + } + } + } + // 获取节点的所有路线 + List> roads = FlowableUtils.findRoad(source, null, null, null); + // 可回退的节点列表 + List userTaskList = new ArrayList<>(); + for (List road : roads) { + if (userTaskList.size() == 0) { + // 还没有可回退节点直接添加 + userTaskList = road; + } else { + // 如果已有回退节点,则比对取交集部分 + userTaskList.retainAll(road); + } + } + return AjaxResult.success(userTaskList); + } + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + public void deleteTask(FlowTaskVo flowTaskVo) { + // todo 待确认删除任务是物理删除任务 还是逻辑删除,让这个任务直接通过? + taskService.deleteTask(flowTaskVo.getTaskId(), flowTaskVo.getComment()); + } + + /** + * 认领/签收任务 + * 认领以后,这个用户就会成为任务的执行人,任务会从其他成员的任务列表中消失 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void claim(FlowTaskVo flowTaskVo) { + taskService.claim(flowTaskVo.getTaskId(), flowTaskVo.getUserId()); + } + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void unClaim(FlowTaskVo flowTaskVo) { + taskService.unclaim(flowTaskVo.getTaskId()); + } + + /** + * 委派任务 + * 任务委派只是委派人将当前的任务交给被委派人进行审批,处理任务后又重新回到委派人身上。 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.DELEGATE.getType(), flowTaskVo.getComment()); + taskService.delegateTask(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + } + + /** + * 任务归还 + * 被委派人完成任务之后,将任务归还委派人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void resolveTask(FlowTaskVo flowTaskVo) { + taskService.resolveTask(flowTaskVo.getTaskId()); + } + + + /** + * 转办任务 + * 直接将办理人换成别人,这时任务的拥有者不再是转办人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void assignTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.ASSIGN.getType(), flowTaskVo.getComment()); + // 直接转派就可以覆盖掉之前的 + taskService.setAssignee(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); +// // 删除指派人重新指派 +// taskService.deleteCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// taskService.addCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// // 如果要查询转给他人处理的任务,可以同时将OWNER进行设置: +// taskService.setOwner(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + + } + + /** + * 多实例加签 + * act_ru_task、act_ru_identitylink各生成一条记录 + * + * @param flowTaskVo + */ + @Override + public void addMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new AddMultiInstanceExecutionCmd(flowTaskVo.getDefId(), flowTaskVo.getInstanceId(), flowTaskVo.getVariables())); + } + + /** + * 多实例减签 + * act_ru_task减1、act_ru_identitylink不变 + * + * @param flowTaskVo + */ + @Override + public void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new DeleteMultiInstanceExecutionCmd(flowTaskVo.getCurrentChildExecutionId(), flowTaskVo.getFlag())); + } + + /** + * 我发起的流程 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult myProcess(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + Long userId = SecurityUtils.getUserId(); + HistoricProcessInstanceQuery historicProcessInstanceQuery; + //超管查询所有数据据 + if(SecurityUtils.isAdmin(userId)){ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .orderByProcessInstanceStartTime() + .desc(); + }else{ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .startedBy(userId.toString()) + .orderByProcessInstanceStartTime() + .desc(); + } + List historicProcessInstances = historicProcessInstanceQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + page.setTotal(historicProcessInstanceQuery.count()); + List flowList = new ArrayList<>(); + for (HistoricProcessInstance hisIns : historicProcessInstances) { + FlowTaskDto flowTask = new FlowTaskDto(); + flowTask.setCreateTime(hisIns.getStartTime()); + flowTask.setFinishTime(hisIns.getEndTime()); + flowTask.setProcInsId(hisIns.getId()); + + // 计算耗时 + if (Objects.nonNull(hisIns.getEndTime())) { + long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } else { + long time = System.currentTimeMillis() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } + // 流程定义信息 + ProcessDefinition pd = repositoryService.createProcessDefinitionQuery() + .processDefinitionId(hisIns.getProcessDefinitionId()) + .singleResult(); + flowTask.setDeployId(pd.getDeploymentId()); + flowTask.setProcDefName(pd.getName()); + flowTask.setProcDefVersion(pd.getVersion()); + flowTask.setCategory(pd.getCategory()); + flowTask.setProcDefVersion(pd.getVersion()); + // 当前所处流程 + List taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list(); + if (CollectionUtils.isNotEmpty(taskList)) { + flowTask.setTaskId(taskList.get(0).getId()); + flowTask.setTaskName(taskList.get(0).getName()); + if (StringUtils.isNotBlank(taskList.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(taskList.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } else { + List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list(); + flowTask.setTaskId(historicTaskInstance.get(0).getId()); + flowTask.setTaskName(historicTaskInstance.get(0).getName()); + if (StringUtils.isNotBlank(historicTaskInstance.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(historicTaskInstance.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } + flowList.add(flowTask); + } + page.setRows(flowList); + return AjaxResult.success(page); + } + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult stopProcess(FlowTaskVo flowTaskVo) { + List task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).list(); + if (CollectionUtils.isEmpty(task)) { + throw new CustomException("流程未启动或已执行完成,取消申请失败"); + } + // 获取当前流程实例 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(flowTaskVo.getInstanceId()) + .singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); + if (Objects.nonNull(bpmnModel)) { + Process process = bpmnModel.getMainProcess(); + List endNodes = process.findFlowElementsOfType(EndEvent.class, false); + if (CollectionUtils.isNotEmpty(endNodes)) { + String userIdStr = Convert.toStr(SecurityUtils.getUserId()); + if(StringUtils.isNotEmpty(userIdStr)){ + Authentication.setAuthenticatedUserId(userIdStr); + taskService.addComment(task.get(0).getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(), + StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment()); + // 设置流程经办人为当前登录人员 + taskService.setAssignee(task.get(0).getId(), userIdStr); + // 获取当前流程最后一个节点 + String endId = endNodes.get(0).getId(); + List executions = runtimeService.createExecutionQuery() + .parentId(processInstance.getProcessInstanceId()).list(); + List executionIds = new ArrayList<>(); + executions.forEach(execution -> executionIds.add(execution.getId())); + // 变更流程为已结束状态 + runtimeService.createChangeActivityStateBuilder() + .moveExecutionsToSingleActivityId(executionIds, endId).changeState(); + return AjaxResult.success(); + } + } + } + return AjaxResult.error(); + } + + /** + * 撤回流程 目前存在错误 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult revokeProcess(FlowTaskVo flowTaskVo) { + String procInsId = flowTaskVo.getInstanceId(); + String taskId = flowTaskVo.getTaskId(); + // 校验流程是否结束 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(procInsId) + .active() + .singleResult(); + if(Objects.isNull(processInstance)) { + throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作"); + } + // 获取待撤回任务实例 + HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .taskAssignee(flowTaskVo.getUserId()) + .singleResult(); + if (Objects.isNull(currTaskIns)) { + throw new RuntimeException("当前任务不存在,无法执行撤回操作"); + } + // 获取 bpmn 模型 + BpmnModel bpmnModel = repositoryService.getBpmnModel(currTaskIns.getProcessDefinitionId()); + UserTask currUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currTaskIns.getTaskDefinitionKey()); + // 查找下一级用户任务列表 + List nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask); + List nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList()); + + // 获取当前节点之后已完成的流程历史节点 + List finishedTaskInsList = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(procInsId) + .taskCreatedAfter(currTaskIns.getEndTime()) + .finished() + .list(); + for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) { + // 检查已完成流程历史节点是否存在下一级中 + if (StringUtils.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) { + throw new RuntimeException("下一流程已处理,无法执行撤回操作"); + } + } + // 获取所有激活的任务节点,找到需要撤回的任务 + List activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list(); + List revokeExecutionIds = new ArrayList<>(); + for (Task task : activateTaskList) { + // 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点 + if (StringUtils.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) { + // 添加撤回审批信息 + taskService.setAssignee(task.getId(), flowTaskVo.getUserId()); + taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), flowTaskVo.getAssignee() + "撤回流程审批"); + revokeExecutionIds.add(task.getExecutionId()); + } + } + try { + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(procInsId) + .moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new RuntimeException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new RuntimeException("执行撤回操作失败"); + } + return AjaxResult.success(); + } + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult todoList(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + // 只查看自己的数据 + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + TaskQuery taskQuery = taskService.createTaskQuery() + .active() + .includeProcessVariables() + .taskCandidateGroupIn(sysUser.getRoles().stream().map(role -> role.getRoleId().toString()).collect(Collectors.toList())) + .taskCandidateOrAssigned(sysUser.getUserId().toString()) + .orderByTaskCreateTime().desc(); + + page.setTotal(taskQuery.count()); + List taskList = taskQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + List
可退回节点
+ * + * @author tony + * @date 2022-04-23 11:01:52 + */ +@Data +@ApiModel("可退回节点") +public class ReturnTaskNodeVo { + + @ApiModelProperty("任务Id") + private String id; + + @ApiModelProperty("用户Id") + private String name; + +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java new file mode 100644 index 00000000..b5b29240 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/domain/vo/StartTaskVO.java @@ -0,0 +1,63 @@ +package com.yanzhu.flowable.domain.vo; + +import java.io.Serializable; +import java.util.Map; + +/** + * 流程实例启动 + * + * @author JiangYuQi + * @date 2020-07-07 + */ +public class StartTaskVO implements Serializable +{ + + private static final long serialVersionUID = 1L; + + //流程实例ID + private String procDefId; + private String userId; + private String userName; + private String nickName; + private Map variables; + + public String getProcDefId() { + return procDefId; + } + + public void setProcDefId(String procDefId) { + this.procDefId = procDefId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getNickName() { + return nickName; + } + + public void setNickName(String nickName) { + this.nickName = nickName; + } + + public Map getVariables() { + return variables; + } + + public void setVariables(Map variables) { + this.variables = variables; + } +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java new file mode 100644 index 00000000..0f7afa58 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowBusinessKeyMapper.java @@ -0,0 +1,112 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.FlowTaskEntity; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/*** + * 工作流业务相关 + */ +public interface FlowBusinessKeyMapper { + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件查询流程任务 + * @param procInsId + * @return + */ + public FlowTaskEntity findFlowTaskByProcInsId(String procInsId); + + /** + * 查询项目信息 + * @param proId + * @return + */ + public Map selectSurProjectById(Long proId); + + /** + * 根据流程Id查询操作日志 + * @param procInsId + * @return + */ + public List> selectCommentByProcInsId(String procInsId); + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + public Map selectLastCommentByProcInsId(String procInsId); + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + public List> selectFormDatasByProcInsId(String procInsId); + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件查询我的代办分组数量统计 + * @param flowTaskEntity + * @return + */ + public List> findAwaitCountGroupByCategory(FlowTaskEntity flowTaskEntity); + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity); + + public List groupByCategory(FlowTaskEntity where); + + public List groupByUnit(FlowTaskEntity where); + + public List listByUnit(FlowTaskEntity where); + + public List listByState(FlowTaskEntity where); + + public List groupByUnitTotal(FlowTaskEntity where); + + public List groupByUnitFinish(FlowTaskEntity where); + + public List groupByProject(@Param("deptId")long deptId,@Param("proType")String proType); + + /** + * 根据条件查询安全类作业工作流列表 + * @param flowTaskEntity + * @return + */ + public List findSafetyWorkList(FlowTaskEntity flowTaskEntity); + + public List listByCategory(FlowTaskEntity where); + + /** + * 工程管理-分包单位占比 + * @param where + * @return + */ + public List groupFlowBySubDeptType(FlowTaskEntity where); + /** + * 工程管理-分包单位资质审批明细 + * @param where + * @return + */ + public List listFlowBySubDeptType(FlowTaskEntity where); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java new file mode 100644 index 00000000..7d32f616 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/FlowDeployMapper.java @@ -0,0 +1,76 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.vo.FlowDeptVo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Map; + +/** + * 流程定义查询 + * + * @author Tony + * @email + * @date 2022/1/29 5:44 下午 + **/ +public interface FlowDeployMapper { + + /** + * 流程定义列表 + * @param roleId 发起角色 + * @param category 流程类别 + * @param name 流程名称 + * @return + */ + List selectDeployList(@Param("roleId")String roleId, @Param("category")String category, @Param("name")String name); + + /** + * 我的流程定义列表 + * @param username 登录用户 + * @param category 流程类别 + * @param name 流程名称 + * @return + */ + List selectMyDeployList(@Param("username")String username, @Param("category")String category, @Param("name")String name); + + /** + * 流程定义列表 + * @param flowQueryVo 流程查询 + * @return + */ + List selectDeployList(FlowQueryVo flowQueryVo); + + /** + * 新增单位流程关系 + * + * @param flowDeptVo 流程 单位关系 + * @return 结果 + */ + public int insertActReProcdefDept(FlowDeptVo flowDeptVo); + + /** + * 根据角色删除流程定义 + * @param roleId 橘色 + * @return + */ + int deleteDeployByRoleId(String roleId); + + + /** + * 批量新增流程定义角色配置 + * @param items 列表 + * @return + */ + int batchDeployRoleRole(List> items); + + /** + * 流程发起排序设置 + * @param roleId 角色主键 + * @param key 流程 + * @param sort 排序 + * @return + */ + int updateProcKeyRoleSort(@Param("roleId")String roleId, @Param("key")String key, @Param("sort")String sort); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java new file mode 100644 index 00000000..1f7bd205 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysDeployFormMapper.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程实例关联表单Mapper接口 + * + * @author Tony + * @date 2021-03-30 + */ +public interface SysDeployFormMapper +{ + /** + * 查询流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 流程实例关联表单 + */ + public SysDeployForm selectSysDeployFormById(Long id); + + /** + * 查询流程实例关联表单列表 + * + * @param SysDeployForm 流程实例关联表单 + * @return 流程实例关联表单集合 + */ + public List selectSysDeployFormList(SysDeployForm SysDeployForm); + + /** + * 新增流程实例关联表单 + * + * @param SysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int insertSysDeployForm(SysDeployForm SysDeployForm); + + /** + * 修改流程实例关联表单 + * + * @param SysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int updateSysDeployForm(SysDeployForm SysDeployForm); + + /** + * 删除流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormById(Long id); + + /** + * 批量删除流程实例关联表单 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteSysDeployFormByIds(Long[] ids); + + /** + * 查询流程挂着的表单 + * @param deployId + * @return + */ + SysForm selectSysDeployFormByDeployId(String deployId); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java new file mode 100644 index 00000000..bdeef941 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysExpressionMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysExpression; + +import java.util.List; + +/** + * 流程达式Mapper接口 + * + * @author ruoyi + * @date 2022-12-12 + */ +public interface SysExpressionMapper +{ + /** + * 查询流程达式 + * + * @param id 流程达式主键 + * @return 流程达式 + */ + public SysExpression selectSysExpressionById(Long id); + + /** + * 查询流程达式列表 + * + * @param sysExpression 流程达式 + * @return 流程达式集合 + */ + public List selectSysExpressionList(SysExpression sysExpression); + + /** + * 新增流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int insertSysExpression(SysExpression sysExpression); + + /** + * 修改流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int updateSysExpression(SysExpression sysExpression); + + /** + * 删除流程达式 + * + * @param id 流程达式主键 + * @return 结果 + */ + public int deleteSysExpressionById(Long id); + + /** + * 批量删除流程达式 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteSysExpressionByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java new file mode 100644 index 00000000..183f9630 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysFormMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程表单Mapper接口 + * + * @author Tony + * @date 2021-03-30 + */ +public interface SysFormMapper +{ + /** + * 查询流程表单 + * + * @param formId 流程表单ID + * @return 流程表单 + */ + public SysForm selectSysFormById(Long formId); + + /** + * 查询流程表单列表 + * + * @param sysForm 流程表单 + * @return 流程表单集合 + */ + public List selectSysFormList(SysForm sysForm); + + /** + * 新增流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int insertSysForm(SysForm sysForm); + + /** + * 修改流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int updateSysForm(SysForm sysForm); + + /** + * 删除流程表单 + * + * @param formId 流程表单ID + * @return 结果 + */ + public int deleteSysFormById(Long formId); + + /** + * 批量删除流程表单 + * + * @param formIds 需要删除的数据ID + * @return 结果 + */ + public int deleteSysFormByIds(Long[] formIds); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java new file mode 100644 index 00000000..c4b43efe --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysListenerMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysListener; + +import java.util.List; + +/** + * 流程监听Mapper接口 + * + * @author Tony + * @date 2022-12-25 + */ +public interface SysListenerMapper +{ + /** + * 查询流程监听 + * + * @param id 流程监听主键 + * @return 流程监听 + */ + public SysListener selectSysListenerById(Long id); + + /** + * 查询流程监听列表 + * + * @param sysListener 流程监听 + * @return 流程监听集合 + */ + public List selectSysListenerList(SysListener sysListener); + + /** + * 新增流程监听 + * + * @param sysListener 流程监听 + * @return 结果 + */ + public int insertSysListener(SysListener sysListener); + + /** + * 修改流程监听 + * + * @param sysListener 流程监听 + * @return 结果 + */ + public int updateSysListener(SysListener sysListener); + + /** + * 删除流程监听 + * + * @param id 流程监听主键 + * @return 结果 + */ + public int deleteSysListenerById(Long id); + + /** + * 批量删除流程监听 + * + * @param ids 需要删除的数据主键集合 + * @return 结果 + */ + public int deleteSysListenerByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java new file mode 100644 index 00000000..0d0a1d68 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/flowable/mapper/SysTaskFormMapper.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.mapper; + +import com.yanzhu.flowable.domain.SysTaskForm; + +import java.util.List; + +/** + * 流程任务关联单Mapper接口 + * + * @author Tony + * @date 2021-04-03 + */ +public interface SysTaskFormMapper +{ + /** + * 查询流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 流程任务关联单 + */ + public SysTaskForm selectSysTaskFormById(Long id); + + /** + * 查询流程任务关联单列表 + * + * @param sysTaskForm 流程任务关联单 + * @return 流程任务关联单集合 + */ + public List selectSysTaskFormList(SysTaskForm sysTaskForm); + + /** + * 新增流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int insertSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 修改流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int updateSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 删除流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormById(Long id); + + /** + * 批量删除流程任务关联单 + * + * @param ids 需要删除的数据ID + * @return 结果 + */ + public int deleteSysTaskFormByIds(Long[] ids); +} diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/mapper/domain/init b/yanzhu-common/yanzhu-common-mapper/src/main/java/com/yanzhu/mapper/domain/init deleted file mode 100644 index e69de29b..00000000 diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml new file mode 100644 index 00000000..df7e58d3 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowBusinessKeyMapper.xml @@ -0,0 +1,515 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT * FROM + vw_flow_all fa + + and fa.taskName like concat('%', #{taskName}, '%') + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.procDefKey = #{procDefKey} + and fa.businessKey = #{businessKey} + and fa.startDeptName like concat('%', #{startDeptName}, '%') + and fa.startUserName like concat('%', #{startUserName}, '%') + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.startUserId = #{nowUser} + and fa.finishTime is null + and fa.finishTime is not null + + order by fa.createTime desc + + + + + select vf.* as startUserPhone from vw_flow_all vf + where vf.procInsId = #{procInsId} + + + + + select * from vw_flow_comment where procInstId = #{procInstId} order by startTime DESC + + + + select * from vw_flow_comment where procInstId = #{procInstId} order by startTime DESC limit 1 + + + + + select hv.NAME_ as `name`,hv.TEXT_ as text from act_hi_varinst hv where hv.PROC_INST_ID_ = #{procInstId} + + + + + select fa.* from vw_flow_await fa + where + 1=1 + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.businessKey in (select DISTINCT project_id from sur_project_userinfo where user_id=#{nowUser} and is_del=0) + AND (fa.ASSIGNEE_ = #{nowUser} + OR ( + fa.ASSIGNEE_ IS NULL + AND ( + fa.USER_ID_ = #{nowUser} + OR ( + fa.GROUP_ID_ IN + + #{roleId} + + ) + ) + ) + ) + order by fa.createTime desc + + + + + select fa.category,count(1) as total from vw_flow_await fa + where + 1=1 + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.createTime between #{params.beginDate} and #{params.endDate} + + and fa.businessDeptId = #{projectDeptId} + + and fa.businessDeptId = #{nowDept} + + and fa.businessKey in (select DISTINCT projectId from sur_project_unit_info where unitId=#{nowDept} and del_flag=0) + + and fa.businessKey in (select DISTINCT project_id from sur_project_userinfo where user_id=#{nowUser} and is_del=0) + AND (fa.ASSIGNEE_ = #{nowUser} + OR ( + fa.ASSIGNEE_ IS NULL + AND ( + fa.USER_ID_ = #{nowUser} + OR ( + fa.GROUP_ID_ IN + + #{roleId} + + ) + ) + ) + ) + group by fa.category + + + + + select fa.* from vw_flow_finished fa + where + fa.ASSIGNEE_=#{nowUser} + and fa.procDefName like concat('%', #{procDefName}, '%') + and fa.businessKey = #{businessKey} + and fa.businessKeyName like concat('%', #{businessKeyName}, '%') + and fa.category = #{category} + and fa.endTime between #{params.beginDate} and #{params.endDate} + order by fa.endTime desc + + + + SELECT a.dict_label taskName,b.cnt assigneeId,c.cnt procDefVersion FROM + ( SELECT * FROM sys_dict_data WHERE dict_type = 'sys_process_category') a + LEFT JOIN (SELECT category,COUNT(1) cnt FROM vw_flow_all WHERE finishTime IS NOT NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + GROUP BY category) b ON a.dict_value=b.category + LEFT JOIN (SELECT category,COUNT(1) cnt FROM vw_flow_all WHERE finishTime IS NULL and taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + GROUP BY category) c ON a.dict_value=c.category + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '总包%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '监理%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '设计%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '甲方%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE finishTime IS NULL AND taskName LIKE '工程%' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE taskName LIKE '总包%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE taskName LIKE '监理%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE taskName LIKE '设计%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE taskName LIKE '甲方%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE taskName LIKE '工程%' AND taskName!='提交申请' and finishTime IS not NULL + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT cat taskName, COUNT(1) assigneeId FROM ( + SELECT *,'总包单位' cat FROM vw_flow_all WHERE taskName LIKE '总包%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'监理单位' cat FROM vw_flow_all WHERE taskName LIKE '监理%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'设计单位' cat FROM vw_flow_all WHERE taskName LIKE '设计%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'甲方代表' cat FROM vw_flow_all WHERE taskName LIKE '甲方%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + UNION + SELECT *,'子公司' cat FROM vw_flow_all WHERE taskName LIKE '工程%' AND taskName!='提交申请' + and businessDeptId=#{projectDeptId} + and businessKey=#{businessKey} + and businessType = #{proType} + + and businessKey in + + #{item} + + + ) a + GROUP BY cat + + + + SELECT * FROM vw_flow_all + + and category = #{category} + and businessDeptId=#{deptId} + and businessType = #{proType} + and businessKey=#{projectId} + + and businessKey in + + #{item} + + + + + + + SELECT * FROM vw_flow_all WHERE finishTime IS NULL + + AND taskName LIKE '总包%' + AND taskName LIKE '监理%' + AND taskName LIKE '设计%' + AND taskName LIKE '甲方%' + AND taskName LIKE '工程%' + + and businessDeptId=#{deptId} + and businessType = #{proType} + and businessKey=#{projectId} + + and businessKey in + + #{item} + + + + + + SELECT * FROM vw_flow_all + + + AND taskName!='提交申请' + AND finishTime IS NULL AND taskName!='提交申请' + AND finishTime IS NOT NULL + + and businessDeptId=#{deptId} + and businessKey=#{projectId} + and businessType = #{proType} + + and businessKey in + + #{item} + + + + + + + SELECT a.businessKey,a.cnt duration ,b.projectName businessKeyName FROM ( + SELECT businessKey,COUNT(1) cnt FROM vw_flow_all WHERE taskName!='提交申请' + and businessDeptId=#{deptId} + and businessType = #{proType} + GROUP BY businessKey ) a,sur_project b WHERE a.businessKey=b.id + ORDER BY a.cnt DESC + + + + SELECT + fa.procInsId, + fa.deployId, + fa.createTime, + fa.finishTime, + fa.duration, + fa.procDefKey, + fa.procDefName, + fa.procDefVersion, + fa.category, + fa.businessKey, + fa.businessDeptId, + fa.businessKeyName, + fa.startUserId, + fa.startUserName, + fa.startDeptName, + fa.taskId, + fa.taskName, + fa.assigneeId, + fa.assigneeName, + fa.assigneeDeptName + FROM + vw_flow_all fa + + and fa.businessType = #{proType} + and fa.procDefKey = #{procDefKey} + and fa.businessKey = #{businessKey} + + and fa.businessDeptId = #{projectDeptId} + + order by fa.createTime desc + + + + select * from sur_project where id = #{proId} + + + + select x.dict_label taskName,y.taskId from ( + select dict_value,dict_label + from sys_dict_data where dict_type='flow_sub_dept_type' + ) x left join ( + SELECT d.dict_value, d.dict_label,count(1) taskId FROM + vw_flow_all a + LEFT JOIN act_hi_varinst b ON a.procInsId=b.PROC_INST_ID_ AND b.NAME_='subDeptType' + LEFT JOIN act_hi_varinst c ON a.procInsId=c.PROC_INST_ID_ AND c.NAME_='subDeptName' + LEFT JOIN sys_dict_data D ON b.TEXT_=d.dict_value and d.dict_type='flow_sub_dept_type' + left join sur_project sp on a.businesskey=sp.id + where a.procDefKey='flow_fbzzsp_fbszzsp' + AND (A.finishTime is null OR (A.finishTime is not null and A.taskComType =1)) + and sp.progressVisible=0 and sp.isDel=0 + and a.businessDeptId=#{deptId} + and a.businessKey=#{projectId} + + and a.businessKey in + + #{item} + + + and sp.projectType = #{proType} + group by d.dict_value,d.dict_label + ) y on x.dict_value=y.dict_value + + + SELECT a.procInsId,a.deployId,a.createTime,a.finishTime,a.businessKey,a.businessKeyName,a.startDeptName,b.TEXT_ as taskId, c.TEXT_ as deptName,d.dict_label taskName FROM + vw_flow_all a + LEFT JOIN act_hi_varinst b ON a.procInsId=b.PROC_INST_ID_ AND b.NAME_='subDeptType' + LEFT JOIN act_hi_varinst c ON a.procInsId=c.PROC_INST_ID_ AND c.NAME_='subDeptName' + LEFT JOIN sys_dict_data D ON b.TEXT_=d.dict_value and d.dict_type='flow_sub_dept_type' + left join sur_project sp on a.businesskey=sp.id + where a.procDefKey='flow_fbzzsp_fbszzsp' + AND (A.finishTime is null OR (A.finishTime is not null and A.taskComType =1)) + and sp.progressVisible=0 and sp.isDel=0 + and a.businessDeptId=#{deptId} + and a.businessKey=#{projectId} + + and a.businessKey in + + #{item} + + + and sp.projectType = #{proType} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml new file mode 100644 index 00000000..f8a188d2 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/FlowDeployMapper.xml @@ -0,0 +1,125 @@ + + + + + + SELECT + rp.id_ as id, + rp.deployment_id_ as deploymentId, + rd.name_ as name, + rd.category_ as category, + rp.key_ as flowKey, + rp.version_ as version, + rp.suspension_state_ as suspensionState, + rd.deploy_time_ as deploymentTime, + rpd.DEPT_ID_ as deptId, + sd.dept_name as deptName, + rpd.PROJ_ID_ as projectId, + spi.project_name as projectName + FROM + act_re_procdef rp + LEFT JOIN act_re_deployment rd ON rp.deployment_id_ = rd.id_ + left join act_re_procdef_dept rpd on rp.id_ = rpd.PROCDEF_ID_ + left join sys_dept sd on sd.dept_id = rpd.DEPT_ID_ + left join pro_project_info spi on spi.id = rpd.PROJ_ID_ + + rp.SUSPENSION_STATE_ = 1 + + and rpd.DEPT_ID_ = #{deptId} + + + and sd.dept_name like concat('%', #{deptName}, '%') + + + and rpd.PROJ_ID_ = #{projectId} + + + and spi.project_name like concat('%', #{projectName}, '%') + + + and rd.name_ like concat('%', #{name}, '%') + + + and rd.category_ = #{category} + + + and rd.key_ = #{flowKey} + + + order by rpr.SORT_ + + + + SELECT * FROM ( + SELECT + rp.id_ as id, + rp.deployment_id_ as deploymentId, + rd.name_ as name, + rd.category_ as category, + sdd.dict_label as categoryName, + rp.key_ as flowKey, + rp.version_ as version, + rp.suspension_state_ as suspensionState, + rd.deploy_time_ as deploymentTime, + ROW_NUMBER () OVER ( + PARTITION BY rp.key_ + ORDER BY + rp.version_ DESC + ) AS rn + FROM + act_re_procdef rp + LEFT JOIN act_re_deployment rd ON rp.deployment_id_ = rd.id_ + left join sys_dict_data sdd on sdd.dict_type='sys_process_category' and sdd.dict_value = rd.category_ + left join act_re_procdef_role rpr on rp.key_ = rpr.PROCDEF_KEY_ + left join sys_user_role sur on sur.role_id = rpr.ROLE_ID_ + left join sys_user su on su.user_id = sur.user_id + + rp.SUSPENSION_STATE_ = 1 and su.user_name = #{username} + + and rd.name_ like concat('%', #{name}, '%') + + + and rd.category_ = #{category} + + + order by rd.deploy_time_ desc + ) v WHERE v.rn = 1 + + + + delete from act_re_procdef_role where ROLE_ID_ = #{roleId} + + + + insert into act_re_procdef_role( PROCDEF_KEY_, ROLE_ID_) values + + ( #{item.key}, #{item.roleId}) + + + + + update act_re_procdef_role set SORT_=#{sort} where PROCDEF_KEY_=#{key} and ROLE_ID_=#{roleId} + + + + + insert into act_re_procdef_dept + + PROCDEF_ID_, + TYPE_, + DEPT_ID_, + PROJ_ID_, + SORT_, + + + #{procdefId}, + #{prtype}, + #{deptId}, + #{projId}, + #{sort}, + + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml new file mode 100644 index 00000000..f3423af4 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysDeployFormMapper.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + select id, form_id, deploy_id from sys_deploy_form + + + + + + and form_id = #{formId} + and deploy_id = #{deployId} + + + + + + where id = #{id} + + + + select t1.form_content as formContent,t1.form_name as formName,t1.form_id as formId from sys_form t1 left join sys_deploy_form t2 on t1.form_id = t2.form_id + where t2.deploy_id = #{deployId} limit 1 + + + + insert into sys_deploy_form + + form_id, + deploy_id, + + + #{formId}, + #{deployId}, + + + + + update sys_deploy_form + + form_id = #{formId}, + deploy_id = #{deployId}, + + where id = #{id} + + + + delete from sys_deploy_form where id = #{id} + + + + delete from sys_deploy_form where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml new file mode 100644 index 00000000..504e522c --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysExpressionMapper.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + select id, name, expression, create_time, update_time, create_by, update_by, status, remark from sys_expression + + + + + + and name like concat('%', #{name}, '%') + and expression = #{expression} + and status = #{status} + + + + + + where id = #{id} + + + + insert into sys_expression + + name, + expression, + create_time, + update_time, + create_by, + update_by, + status, + remark, + + + #{name}, + #{expression}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{status}, + #{remark}, + + + + + update sys_expression + + name = #{name}, + expression = #{expression}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + status = #{status}, + remark = #{remark}, + + where id = #{id} + + + + delete from sys_expression where id = #{id} + + + + delete from sys_expression where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml new file mode 100644 index 00000000..6e686742 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysFormMapper.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + + + + select form_id, form_name, form_content, create_time, update_time, create_by, update_by, remark from sys_form + + + + + + and form_name like concat('%', #{formName}, '%') + and form_content = #{formContent} + + order by create_time desc + + + + + where form_id = #{formId} + + + + insert into sys_form + + form_name, + form_content, + create_time, + update_time, + create_by, + update_by, + remark, + + + #{formName}, + #{formContent}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{remark}, + + + + + update sys_form + + form_name = #{formName}, + form_content = #{formContent}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + remark = #{remark}, + + where form_id = #{formId} + + + + delete from sys_form where form_id = #{formId} + + + + delete from sys_form where form_id in + + #{formId} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml new file mode 100644 index 00000000..a8403a06 --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysListenerMapper.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + select id, + name, + type, + event_type, + value_type, + value, + create_time, + update_time, + create_by, + update_by, + status, + remark + from sys_listener + + + + + + and name like concat('%', #{name}, '%') + and type = #{type} + and event_type = #{eventType} + and value_type = #{valueType} + and value = #{value} + and status = #{status} + + + + + + where id = #{id} + + + + insert into sys_listener + + name, + type, + event_type, + value_type, + value, + create_time, + update_time, + create_by, + update_by, + status, + remark, + + + #{name}, + #{type}, + #{eventType}, + #{valueType}, + #{value}, + #{createTime}, + #{updateTime}, + #{createBy}, + #{updateBy}, + #{status}, + #{remark}, + + + + + update sys_listener + + name = #{name}, + type = #{type}, + event_type = #{eventType}, + value_type = #{valueType}, + value = #{value}, + create_time = #{createTime}, + update_time = #{updateTime}, + create_by = #{createBy}, + update_by = #{updateBy}, + status = #{status}, + remark = #{remark}, + + where id = #{id} + + + + delete + from sys_listener + where id = #{id} + + + + delete from sys_listener where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml new file mode 100644 index 00000000..b7ec90fe --- /dev/null +++ b/yanzhu-common/yanzhu-common-mapper/src/main/resources/mapper/flowable/SysTaskFormMapper.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + select id, form_id, task_id from sys_task_form + + + + + + and form_id = #{formId} + and task_id = #{taskId} + + + + + + where id = #{id} + + + + insert into sys_task_form + + form_id, + task_id, + + + #{formId}, + #{taskId}, + + + + + update sys_task_form + + form_id = #{formId}, + task_id = #{taskId}, + + where id = #{id} + + + + delete from sys_task_form where id = #{id} + + + + delete from sys_task_form where id in + + #{id} + + + \ No newline at end of file diff --git a/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java b/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java index 7faf4f46..ba3490f9 100644 --- a/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java +++ b/yanzhu-common/yanzhu-common-security/src/main/java/com/yanzhu/common/security/utils/SecurityUtils.java @@ -91,6 +91,16 @@ public class SecurityUtils return userId != null && 1L == userId; } + /** + * 是否为公司管理员 + * + * @return 结果 + */ + public static boolean isGSAdmin() + { + return getLoginUser().getRoles().contains("gsAdmin"); + } + /** * 生成BCryptPasswordEncoder密码 * diff --git a/yanzhu-modules/pom.xml b/yanzhu-modules/pom.xml index 20037b42..800e5a5a 100644 --- a/yanzhu-modules/pom.xml +++ b/yanzhu-modules/pom.xml @@ -15,13 +15,14 @@ yanzhu-file yanzhu-manage yanzhu-wechat + yanzhu-flowable yanzhu-modules pom - yanzhu-modules业务模块 + yanzhu-modules分布式模块 diff --git a/yanzhu-modules/yanzhu-flowable/pom.xml b/yanzhu-modules/yanzhu-flowable/pom.xml new file mode 100644 index 00000000..a94efdf1 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/pom.xml @@ -0,0 +1,182 @@ + + + + yanzhu + com.yanzhu + 3.6.2 + ../../pom.xml + + 4.0.0 + + yanzhu-modules-flowable + + + 17 + 17 + + + + yanzhu-modules-flowable工作流模块 + + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-sentinel + + + + + org.springframework.boot + spring-boot-starter-actuator + + + + + io.springfox + springfox-swagger-ui + ${swagger.fox.version} + + + + + org.apache.velocity + velocity-engine-core + + + + + mysql + mysql-connector-java + + + + + com.yanzhu + yanzhu-common-log + + + + + com.yanzhu + yanzhu-common-swagger + + + + org.projectlombok + lombok + provided + + + + io.seata + seata-all + 1.5.2 + compile + + + + + com.yanzhu + yanzhu-common-mapper + 3.6.2 + compile + + + + + com.googlecode.aviator + aviator + 5.3.3 + + + + org.flowable + flowable-spring-boot-starter + + + + org.flowable + flowable-spring-security + + + + + + + + ${project.artifactId} + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + com.spotify + docker-maven-plugin + 1.2.0 + + + ${project.build.finalName} + latest + + openjdk:latest + + ["java", "-jar", "/${project.build.finalName}.jar"] + + + ${configuration.docker-two-Host} + + + / + + ${project.build.directory} + + ${project.build.finalName}.jar + + + + + + build-image + package + + build + + + + + + + + + src/main/resources + true + + + + + \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java new file mode 100644 index 00000000..1edc2bc8 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/YanZhuFlowableApplication.java @@ -0,0 +1,34 @@ +package com.yanzhu.flowable; + +import com.yanzhu.common.security.annotation.EnableCustomConfig; +import com.yanzhu.common.security.annotation.EnableRyFeignClients; +import com.yanzhu.common.swagger.annotation.EnableCustomSwagger2; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 工作流模块 + * + * @author ruoyi + */ +@EnableCustomConfig +@EnableCustomSwagger2 +@EnableRyFeignClients +@SpringBootApplication +public class YanZhuFlowableApplication +{ + public static void main(String[] args) + { + SpringApplication.run(YanZhuFlowableApplication.class, args); + System.out.println("(♥◠‿◠)ノ゙ 工作流模块启动成功 ლ(´ڡ`ლ)゙ \n" + + " .-------. ____ __ \n" + + " | _ _ \\ \\ \\ / / \n" + + " | ( ' ) | \\ _. / ' \n" + + " |(_ o _) / _( )_ .' \n" + + " | (_,_).' __ ___(_ o _)' \n" + + " | |\\ \\ | || |(_,_)' \n" + + " | | \\ `' /| `-' / \n" + + " | | \\ / \\ / \n" + + " ''-' `'-' `-..-' "); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java new file mode 100644 index 00000000..54488c3e --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/constant/ProcessConstants.java @@ -0,0 +1,80 @@ +package com.yanzhu.flowable.common.constant; + +/** + * 流程常量信息 + * + * @author Tony + * @date 2021/4/17 22:46 + */ +public class ProcessConstants { + + /** + * 动态数据 + */ + public static final String DYNAMIC = "dynamic"; + + /** + * 固定任务接收 + */ + public static final String FIXED = "fixed"; + + /** + * 单个审批人 + */ + public static final String ASSIGNEE = "assignee"; + + + /** + * 候选人 + */ + public static final String CANDIDATE_USERS = "candidateUsers"; + + + /** + * 审批组 + */ + public static final String CANDIDATE_GROUPS = "candidateGroups"; + + /** + * 单个审批人 + */ + public static final String PROCESS_APPROVAL = "approval"; + + /** + * 会签人员 + */ + public static final String PROCESS_MULTI_INSTANCE_USER = "userList"; + + /** + * nameapace + */ + public static final String NAMASPASE = "http://flowable.org/bpmn"; + + /** + * 会签节点 + */ + public static final String PROCESS_MULTI_INSTANCE = "multiInstance"; + + /** + * 自定义属性 dataType + */ + public static final String PROCESS_CUSTOM_DATA_TYPE = "dataType"; + + /** + * 自定义属性 userType + */ + public static final String PROCESS_CUSTOM_USER_TYPE = "userType"; + + /** + * 初始化人员 + */ + public static final String PROCESS_INITIATOR = "INITIATOR"; + + + /** + * 流程跳过 + */ + public static final String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; + + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java new file mode 100644 index 00000000..06aa8bff --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/common/enums/FlowComment.java @@ -0,0 +1,43 @@ +package com.yanzhu.flowable.common.enums; + +/** + * 流程意见类型 + * + * @author Tony + * @date 2021/4/19 + */ +public enum FlowComment { + + /** + * 说明 + */ + NORMAL("1", "正常意见"), + REBACK("2", "退回意见"), + REJECT("3", "驳回意见"), + DELEGATE("4", "委派意见"), + ASSIGN("5", "转办意见"), + STOP("6", "终止流程"), + REVOKE("7", "撤回流程"); + /** + * 类型 + */ + private final String type; + + /** + * 说明 + */ + private final String remark; + + FlowComment(String type, String remark) { + this.type = type; + this.remark = remark; + } + + public String getType() { + return type; + } + + public String getRemark() { + return remark; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java new file mode 100644 index 00000000..414d2d04 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableConfig.java @@ -0,0 +1,32 @@ +package com.yanzhu.flowable.config; + +import org.flowable.engine.impl.db.DbIdGenerator; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.flowable.spring.boot.EngineConfigurationConfigurer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncListenableTaskExecutor; +import org.springframework.core.task.SimpleAsyncTaskExecutor; + +/** + * 流程id生成处理 + * @author Tony + * @date 2022-12-26 10:24 + */ +@Configuration +public class FlowableConfig implements EngineConfigurationConfigurer { + + @Bean + public AsyncListenableTaskExecutor applicationTaskExecutor() { + return new SimpleAsyncTaskExecutor(); + } + + @Override + public void configure(SpringProcessEngineConfiguration engineConfiguration) { + engineConfiguration.setActivityFontName("宋体"); + engineConfiguration.setLabelFontName("宋体"); + engineConfiguration.setAnnotationFontName("宋体"); + engineConfiguration.setIdGenerator(new DbIdGenerator()); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java new file mode 100644 index 00000000..227de838 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/config/FlowableGlobalListenerConfig.java @@ -0,0 +1,41 @@ +package com.yanzhu.flowable.config; + +import com.yanzhu.flowable.listener.GlobalEventListener; +import lombok.RequiredArgsConstructor; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher; +import org.flowable.spring.SpringProcessEngineConfiguration; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; + +/** + * Flowable添加全局监听器 + * + * @author JiangYuQi + */ +@Configuration +@RequiredArgsConstructor +public class FlowableGlobalListenerConfig implements ApplicationListener { + + private final SpringProcessEngineConfiguration configuration; + + private final GlobalEventListener globalEventListener; + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + FlowableEventDispatcher dispatcher = configuration.getEventDispatcher(); + /** + * 任务创建全局监听-待办消息发送 + * PROCESS_CREATED 流程创建 + * TASK_CREATED 任务创建 + * TASK_COMPLETED 任务完成 + * PROCESS_COMPLETED 流程完成 + * 流程创建、任务创建、任务完成、流程完成 + */ + dispatcher.addEventListener(globalEventListener,FlowableEngineEventType.TASK_CREATED); + dispatcher.addEventListener(globalEventListener,FlowableEngineEventType.PROCESS_COMPLETED); + } + +} + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java new file mode 100644 index 00000000..983f676c --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowBusinessKeyController.java @@ -0,0 +1,146 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.FlowTaskEntity; +import com.yanzhu.flowable.service.IFlowBusinessKeyService; +import com.yanzhu.system.api.domain.SysUser; +import io.swagger.annotations.Api; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * + * 业务工作流程 + * + * + * @author JiangYuQi + * @date 2024-04-03 + */ +@Slf4j +@Api(tags = "业务工作流程") +@RestController +@RequestMapping("/mapper/flowable/businessKey") +public class FlowBusinessKeyController extends BaseController { + + @Autowired + private IFlowBusinessKeyService flowBusinessKeyService; + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/allList") + public TableDataInfo allList(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()); + } + startPage(); + return getDataTable(flowBusinessKeyService.selectAllFlowTaskByParams(flowTaskEntity)); + } + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/queryCount") + public AjaxResult queryCount(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()); + } + return success(flowBusinessKeyService.quueryCount(flowTaskEntity)); + } + + /** + * 根据流程Id查询操作日志 + * @param procInsId + * @return + */ + @GetMapping(value = "/findCommentByProcInsId") + public AjaxResult findCommentByProcInsId(String procInsId) { + return success(flowBusinessKeyService.selectCommentByProcInsId(procInsId)); + } + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + @GetMapping(value = "/findFormDatasByProcInsId") + public AjaxResult findFormDatasByProcInsId(String procInsId) { + return success(flowBusinessKeyService.selectFormDatasByProcInsId(procInsId)); + } + + /** + * 根据条件查询我的代办任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/myAwaitFlowTaskList") + public TableDataInfo myAwaitFlowTaskList(FlowTaskEntity flowTaskEntity) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setAssigneeId(sysUser.getUserId()); + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + flowTaskEntity.setRoleIds(sysUser.getRoles().stream().map(role -> role.getRoleId()).collect(Collectors.toList())); + } + startPage(); + return getDataTable(flowBusinessKeyService.selectMyAwaitFlowTask(flowTaskEntity)); + } + + /** + * 根据条件查询我的已办任务 + * @param flowTaskEntity + * @return + */ + @GetMapping(value = "/myFinishedFlowTaskList") + public TableDataInfo myFinishedFlowTaskList(FlowTaskEntity flowTaskEntity) { + startPage(); + flowTaskEntity.setAssigneeId(SecurityUtils.getUserId()); + return getDataTable(flowBusinessKeyService.selectMyFinishedFlowTask(flowTaskEntity)); + } + + /** + * 查询我的代办任务数量 + * @return AjaxResult 统计结果 + */ + @GetMapping(value = "/findMyTask") + public AjaxResult findMyTask() { + FlowTaskEntity flowTaskEntity = new FlowTaskEntity(); + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowTaskEntity.setAssigneeId(sysUser.getUserId()); + flowTaskEntity.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + flowTaskEntity.setRoleIds(sysUser.getRoles().stream().map(role -> role.getRoleId()).collect(Collectors.toList())); + } + List> list = flowBusinessKeyService.selectMyAwaitFlowTask(flowTaskEntity); + Map data = new HashMap<>(); + if(CollectionUtils.isNotEmpty(list)){ + data.put("todo",list.size()); + }else{ + data.put("todo",0); + } + return success(data); + } + + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java new file mode 100644 index 00000000..c7aa591c --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowDefinitionController.java @@ -0,0 +1,199 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.FlowSaveXmlVo; +import com.yanzhu.flowable.domain.SysExpression; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.service.IFlowDefinitionService; +import com.yanzhu.flowable.service.ISysExpressionService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.RemoteUserService; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.domain.SysUser; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.annotation.Resource; +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * + * 工作流程定义 + * + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "流程定义") +@RestController +@RequestMapping("/mapper/flowable/definition") +public class FlowDefinitionController extends BaseController { + + @Autowired + private IFlowDefinitionService flowDefinitionService; + + @Resource + private ISysExpressionService sysExpressionService; + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + @GetMapping(value = "/list") + @ApiOperation(value = "流程定义列表", response = FlowProcDefDto.class) + public TableDataInfo list(FlowQueryVo flowQueryVo) { + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + //超管查询所有数据 + if(!SecurityUtils.isAdmin(sysUser.getUserId())){ + flowQueryVo.setDeptAncestors(sysUser.getDept().getAncestors()+","+sysUser.getDeptId()); + } + return flowDefinitionService.list(flowQueryVo); + } + + @ApiOperation(value = "导入流程文件", notes = "上传bpmn20的xml文件") + @Log(title = "导入流程文件", businessType = BusinessType.INSERT) + @PostMapping("/import") + public AjaxResult importFile(@RequestParam(required = false) String name, + @RequestParam(required = false) String category, + MultipartFile file) { + InputStream in = null; + try { + in = file.getInputStream(); + flowDefinitionService.importFile(name, category, null, null, null, in); + } catch (Exception e) { + log.error("导入失败:", e); + return AjaxResult.success(e.getMessage()); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + log.error("关闭输入流出错", e); + } + } + + return AjaxResult.success("导入成功"); + } + + @ApiOperation(value = "读取xml文件") + @GetMapping("/readXml/{deployId}") + public AjaxResult readXml(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId) { + try { + return flowDefinitionService.readXml(deployId); + } catch (Exception e) { + return AjaxResult.error("加载xml文件异常"); + } + } + + @ApiOperation(value = "读取图片文件") + @GetMapping("/readImage/{deployId}") + public void readImage(@ApiParam(value = "流程定义id") @PathVariable(value = "deployId") String deployId, HttpServletResponse response) { + OutputStream os = null; + BufferedImage image = null; + try { + image = ImageIO.read(flowDefinitionService.readImage(deployId)); + response.setContentType("image/png"); + os = response.getOutputStream(); + if (image != null) { + ImageIO.write(image, "png", os); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.flush(); + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + @ApiOperation(value = "保存流程设计器内的xml文件") + @Log(title = "保存流程设计", businessType = BusinessType.INSERT) + @PostMapping("/save") + public AjaxResult save(@RequestBody FlowSaveXmlVo vo) { + InputStream in = null; + try { + in = new ByteArrayInputStream(vo.getXml().getBytes(StandardCharsets.UTF_8)); + flowDefinitionService.importFile(vo.getName(), vo.getCategory(), vo.getPrtype(), vo.getOwnerDeptId(), vo.getOwnerProjectId(), in); + } catch (Exception e) { + log.error("导入失败:", e); + return AjaxResult.error(e.getMessage()); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException e) { + log.error("关闭输入流出错", e); + } + } + return AjaxResult.success("导入成功"); + } + + + @ApiOperation(value = "发起流程") + @Log(title = "发起流程申请", businessType = BusinessType.INSERT) + @PostMapping("/start/{procDefId}") + public AjaxResult start(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId, + @ApiParam(value = "申请表单") @RequestBody Map applyInfos) { + return flowDefinitionService.startProcessInstanceById(procDefId, applyInfos); + } + + @ApiOperation(value = "激活或挂起流程定义") + @Log(title = "激活/挂起流程", businessType = BusinessType.UPDATE) + @PutMapping(value = "/updateState") + public AjaxResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state, + @ApiParam(value = "流程部署ID", required = true) @RequestParam String deployId) { + flowDefinitionService.updateState(state, deployId); + return AjaxResult.success(); + } + + @ApiOperation(value = "删除流程") + @Log(title = "删除流程定义", businessType = BusinessType.UPDATE) + @DeleteMapping(value = "/{deployIds}") + public AjaxResult delete(@PathVariable String[] deployIds) { + for (String deployId : deployIds) { + flowDefinitionService.delete(deployId); + } + return AjaxResult.success(); + } + + @ApiOperation(value = "指定流程达式列表") + @GetMapping("/expList") + public AjaxResult expList(SysExpression sysExpression) { + List list = sysExpressionService.selectSysExpressionList(sysExpression); + return AjaxResult.success(list); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java new file mode 100644 index 00000000..5850b4c7 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowInstanceController.java @@ -0,0 +1,63 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.service.IFlowInstanceService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +/** + * 工作流流程实例管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "工作流流程实例管理") +@RestController +@RequestMapping("/mapper/flowable/instance") +public class FlowInstanceController { + + @Autowired + private IFlowInstanceService flowInstanceService; + + @ApiOperation(value = "根据流程定义id启动流程实例") + @PostMapping("/startBy/{procDefId}") + public AjaxResult startById(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId, + @ApiParam(value = "变量集合,json对象") @RequestBody Map variables) { + return flowInstanceService.startProcessInstanceById(procDefId, variables); + + } + + + @ApiOperation(value = "激活或挂起流程实例") + @PostMapping(value = "/updateState") + public AjaxResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state, + @ApiParam(value = "流程实例ID", required = true) @RequestParam String instanceId) { + flowInstanceService.updateState(state,instanceId); + return AjaxResult.success(); + } + + @ApiOperation("结束流程实例") + @PostMapping(value = "/stopProcessInstance") + public AjaxResult stopProcessInstance(@RequestBody FlowTaskVo flowTaskVo) { + flowInstanceService.stopProcessInstance(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "删除流程实例") + @DeleteMapping(value = "/delete/{instanceIds}") + public AjaxResult delete(@ApiParam(value = "流程实例ID", required = true) @PathVariable String[] instanceIds, + @ApiParam(value = "删除原因") @RequestParam(required = false) String deleteReason) { + for (String instanceId : instanceIds) { + flowInstanceService.delete(instanceId,deleteReason); + } + return AjaxResult.success(); + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java new file mode 100644 index 00000000..643ef51b --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java @@ -0,0 +1,268 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.flowable.domain.FlowTaskDto; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.service.IFlowTaskService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * 工作流任务管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "工作流流程任务管理") +@RestController +@RequestMapping("/mapper/flowable/task") +public class FlowTaskController { + + @Autowired + private IFlowTaskService flowTaskService; + + @ApiOperation(value = "我发起的流程", response = FlowTaskDto.class) + @GetMapping(value = "/myProcess") + public AjaxResult myProcess(FlowQueryVo queryVo) { + return flowTaskService.myProcess(queryVo); + } + + @ApiOperation(value = "取消申请", response = FlowTaskDto.class) + @Log(title = "终止申请", businessType = BusinessType.UPDATE) + @PostMapping(value = "/stopProcess") + public AjaxResult stopProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.stopProcess(flowTaskVo); + } + + @ApiOperation(value = "撤回流程", response = FlowTaskDto.class) + @Log(title = "撤回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/revokeProcess") + public AjaxResult revokeProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.revokeProcess(flowTaskVo); + } + + @ApiOperation(value = "获取待办列表", response = FlowTaskDto.class) + @GetMapping(value = "/todoList") + public AjaxResult todoList(FlowQueryVo queryVo) { + return flowTaskService.todoList(queryVo); + } + + @ApiOperation(value = "获取已办任务", response = FlowTaskDto.class) + @GetMapping(value = "/finishedList") + public AjaxResult finishedList(FlowQueryVo queryVo) { + return flowTaskService.finishedList(queryVo); + } + + + @ApiOperation(value = "流程历史流转记录", response = FlowTaskDto.class) + @GetMapping(value = "/flowRecord") + public AjaxResult flowRecord(String procInsId, String deployId) { + return flowTaskService.flowRecord(procInsId, deployId); + } + + + @ApiOperation(value = "流程初始化表单", response = FlowTaskDto.class) + @GetMapping(value = "/flowFormData") + public AjaxResult flowFormData(String deployId) { + return flowTaskService.flowFormData(deployId); + } + + @ApiOperation(value = "获取流程变量", response = FlowTaskDto.class) + @GetMapping(value = "/processVariables/{taskId}") + public AjaxResult processVariables(@ApiParam(value = "流程任务Id") @PathVariable(value = "taskId") String taskId) { + return flowTaskService.processVariables(taskId); + } + + @ApiOperation(value = "审批任务") + @Log(title = "审批流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/complete") + public AjaxResult complete(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "重新提交流程") + @Log(title = "重新提交流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/completeAndModify") + public AjaxResult completeAndModify(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "驳回任务") + @Log(title = "驳回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/reject") + public AjaxResult taskReject(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReject(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "退回任务") + @Log(title = "退回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/return") + public AjaxResult taskReturn(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReturn(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "获取所有可回退的节点") + @PostMapping(value = "/returnList") + public AjaxResult findReturnTaskList(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.findReturnTaskList(flowTaskVo); + } + + @ApiOperation(value = "删除任务") + @Log(title = "删除流程", businessType = BusinessType.DELETE) + @DeleteMapping(value = "/delete") + public AjaxResult delete(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "认领/签收任务") + @Log(title = "认领/签收流程", businessType = BusinessType.INSERT) + @PostMapping(value = "/claim") + public AjaxResult claim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.claim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "取消认领/签收任务") + @Log(title = "取消认领/签收流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/unClaim") + public AjaxResult unClaim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.unClaim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "委派任务") + @Log(title = "委派流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/delegateTask") + public AjaxResult delegate(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.delegateTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "任务归还") + @Log(title = "归还流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/resolveTask") + public AjaxResult resolveTask(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.resolveTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "转办任务") + @Log(title = "转办流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/assignTask") + public AjaxResult assign(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.assignTask(flowTaskVo); + return AjaxResult.success(); + } + + @PostMapping(value = "/addMultiInstanceExecution") + @ApiOperation(value = "多实例加签") + public AjaxResult addMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.addMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("加签成功"); + } + + @PostMapping(value = "/deleteMultiInstanceExecution") + @ApiOperation(value = "多实例减签") + public AjaxResult deleteMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("减签成功"); + } + + @ApiOperation(value = "获取下一节点") + @PostMapping(value = "/nextFlowNode") + public AjaxResult getNextFlowNode(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNode(flowTaskVo); + } + + @ApiOperation(value = "流程发起时获取下一节点") + @PostMapping(value = "/nextFlowNodeByStart") + public AjaxResult getNextFlowNodeByStart(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNodeByStart(flowTaskVo); + } + + /** + * 生成流程图 + * + * @param processId 任务ID + */ + @GetMapping("/diagram/{processId}") + public void genProcessDiagram(HttpServletResponse response, + @PathVariable("processId") String processId) { + InputStream inputStream = flowTaskService.diagram(processId); + OutputStream os = null; + BufferedImage image = null; + try { + image = ImageIO.read(inputStream); + response.setContentType("image/png"); + os = response.getOutputStream(); + if (image != null) { + ImageIO.write(image, "png", os); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.flush(); + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 获取流程执行节点 + * + * @param procInsId 流程实例编号 + * @param procInsId 任务执行编号 + */ + @GetMapping("/flowViewer/{procInsId}/{executionId}") + public AjaxResult getFlowViewer(@PathVariable("procInsId") String procInsId, + @PathVariable("executionId") String executionId) { + return flowTaskService.getFlowViewer(procInsId, executionId); + } + + /** + * 流程节点信息 + * + * @param procInsId 流程实例id + * @return + */ + @GetMapping("/flowXmlAndNode") + public AjaxResult flowXmlAndNode(@RequestParam(value = "procInsId", required = false) String procInsId, + @RequestParam(value = "deployId", required = false) String deployId) { + return flowTaskService.flowXmlAndNode(procInsId, deployId); + } + + /** + * 流程节点表单 + * + * @param taskId 流程任务编号 + * @return + */ + @GetMapping("/flowTaskForm") + public AjaxResult flowTaskForm(@RequestParam(value = "taskId", required = false) String taskId) throws Exception { + return flowTaskService.flowTaskForm(taskId); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java new file mode 100644 index 00000000..74cbad80 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java @@ -0,0 +1,112 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.utils.poi.ExcelUtil; +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.common.security.annotation.RequiresPermissions; +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 流程表单Controller + * + * @author Tony + * @date 2021-04-03 + */ +@RestController +@RequestMapping("/mapper/flowable/form") +public class SysFormController extends BaseController { + + @Autowired + private ISysFormService SysFormService; + + @Autowired + private ISysDeployFormService sysDeployFormService; + + /** + * 查询流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:list')") + @GetMapping("/list") + public TableDataInfo list(SysForm sysForm) { + startPage(); + List list = SysFormService.selectSysFormList(sysForm); + return getDataTable(list); + } + + @GetMapping("/formList") + public AjaxResult formList(SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + return AjaxResult.success(list); + } + /** + * 导出流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:export')") + @Log(title = "流程表单", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public void export(HttpServletResponse response, SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + ExcelUtil util = new ExcelUtil(SysForm.class); + util.exportExcel(response, list, "form"); + } + + /** + * 获取流程表单详细信息 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:query')") + @GetMapping(value = "/{formId}") + public AjaxResult getInfo(@PathVariable("formId") Long formId) { + return AjaxResult.success(SysFormService.selectSysFormById(formId)); + } + + /** + * 新增流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:add')") + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.insertSysForm(sysForm)); + } + + /** + * 修改流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:edit')") + @Log(title = "流程表单", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.updateSysForm(sysForm)); + } + + /** + * 删除流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:remove')") + @Log(title = "流程表单", businessType = BusinessType.DELETE) + @DeleteMapping("/{formIds}") + public AjaxResult remove(@PathVariable Long[] formIds) { + return toAjax(SysFormService.deleteSysFormByIds(formIds)); + } + + + /** + * 挂载流程表单 + */ + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping("/addDeployForm") + public AjaxResult addDeployForm(@RequestBody SysDeployForm sysDeployForm) { + return toAjax(sysDeployFormService.insertSysDeployForm(sysDeployForm)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java new file mode 100644 index 00000000..e66efd70 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java @@ -0,0 +1,41 @@ +package com.yanzhu.flowable.factory; + +import lombok.Getter; +import org.flowable.engine.*; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * flowable 引擎注入封装 + * @author Tony + * @date 2021-04-03 + */ +@Component +@Getter +public class FlowServiceFactory { + + @Resource + protected RepositoryService repositoryService; + + @Resource + protected RuntimeService runtimeService; + + @Resource + protected IdentityService identityService; + + @Resource + protected TaskService taskService; + + @Resource + protected HistoryService historyService; + + @Resource + protected ManagementService managementService; + + @Qualifier("processEngine") + @Resource + protected ProcessEngine processEngine; + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java new file mode 100644 index 00000000..7a0baca5 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java @@ -0,0 +1,370 @@ +package com.yanzhu.flowable.flow; + +import org.flowable.bpmn.model.AssociationDirection; +import org.flowable.bpmn.model.GraphicInfo; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.util.ReflectUtil; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; + +/** + * @author Tony + * @date 2021/4/4 23:58 + */ +public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas { + //定义走过流程连线颜色为绿色 + protected static Color HIGHLIGHT_SequenceFlow_COLOR = Color.GREEN; + //设置未走过流程的连接线颜色 + protected static Color CONNECTION_COLOR = Color.BLACK; + //设置flows连接线字体颜色red + protected static Color LABEL_COLOR = new Color(0, 0, 0); + //高亮显示task框颜色 + protected static Color HIGHLIGHT_COLOR = Color.GREEN; + protected static Color HIGHLIGHT_COLOR1 = Color.RED; + + public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + this.initialize(imageType); + } + + /** + * 重写绘制连线的方式,设置绘制颜色 + * @param xPoints + * @param yPoints + * @param conditional + * @param isDefault + * @param connectionType + * @param associationDirection + * @param highLighted + * @param scaleFactor + */ + @Override + public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) { + Paint originalPaint = this.g.getPaint(); + Stroke originalStroke = this.g.getStroke(); + this.g.setPaint(CONNECTION_COLOR); + if (connectionType.equals("association")) { + this.g.setStroke(ASSOCIATION_STROKE); + } else if (highLighted) { + this.g.setPaint(HIGHLIGHT_SequenceFlow_COLOR); + this.g.setStroke(HIGHLIGHT_FLOW_STROKE); + } + + for (int i = 1; i < xPoints.length; ++i) { + Integer sourceX = xPoints[i - 1]; + Integer sourceY = yPoints[i - 1]; + Integer targetX = xPoints[i]; + Integer targetY = yPoints[i]; + java.awt.geom.Line2D.Double line = new java.awt.geom.Line2D.Double((double) sourceX, (double) sourceY, (double) targetX, (double) targetY); + this.g.draw(line); + } + + java.awt.geom.Line2D.Double line; + if (isDefault) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawDefaultSequenceFlowIndicator(line, scaleFactor); + } + + if (conditional) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawConditionalSequenceFlowIndicator(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.ONE) || associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[xPoints.length - 2], (double) yPoints[xPoints.length - 2], (double) xPoints[xPoints.length - 1], (double) yPoints[xPoints.length - 1]); + this.drawArrowHead(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[1], (double) yPoints[1], (double) xPoints[0], (double) yPoints[0]); + this.drawArrowHead(line, scaleFactor); + } + + this.g.setPaint(originalPaint); + this.g.setStroke(originalStroke); + } + + /** + * 设置字体大小图标颜色 + * @param imageType + */ + @Override + public void initialize(String imageType) { + if ("png".equalsIgnoreCase(imageType)) { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 2); + } else { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 1); + } + + this.g = this.processDiagram.createGraphics(); + if (!"png".equalsIgnoreCase(imageType)) { + this.g.setBackground(new Color(255, 255, 255, 0)); + this.g.clearRect(0, 0, this.canvasWidth, this.canvasHeight); + } + + this.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + //修改图标颜色,修改图标字体大小 + this.g.setPaint(Color.black); + Font font = new Font(this.activityFontName, 10, 14); + this.g.setFont(font); + this.fontMetrics = this.g.getFontMetrics(); + //修改连接线字体大小 + LABEL_FONT = new Font(this.labelFontName, 10, 15); + ANNOTATION_FONT = new Font(this.annotationFontName, 0, 11); + + try { + USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/userTask.png", this.customClassLoader)); + SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/scriptTask.png", this.customClassLoader)); + SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/serviceTask.png", this.customClassLoader)); + RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/receiveTask.png", this.customClassLoader)); + SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/sendTask.png", this.customClassLoader)); + MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/manualTask.png", this.customClassLoader)); + BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/businessRuleTask.png", this.customClassLoader)); + SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/shellTask.png", this.customClassLoader)); + DMN_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/dmnTask.png", this.customClassLoader)); + CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/camelTask.png", this.customClassLoader)); + MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/muleTask.png", this.customClassLoader)); + HTTP_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/httpTask.png", this.customClassLoader)); + TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", this.customClassLoader)); + COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate-throw.png", this.customClassLoader)); + COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate.png", this.customClassLoader)); + ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error-throw.png", this.customClassLoader)); + ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error.png", this.customClassLoader)); + MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message-throw.png", this.customClassLoader)); + MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message.png", this.customClassLoader)); + SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal-throw.png", this.customClassLoader)); + SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal.png", this.customClassLoader)); + } catch (IOException var4) { + LOGGER.warn("Could not load image for process diagram creation: {}", var4.getMessage()); + } + + } + + /** + * 设置连接线字体 + * @param text + * @param graphicInfo + * @param centered + */ + @Override + public void drawLabel(String text, GraphicInfo graphicInfo, boolean centered) { + float interline = 1.0f; + + // text + if (text != null && text.length() > 0) { + Paint originalPaint = g.getPaint(); + Font originalFont = g.getFont(); + + g.setPaint(LABEL_COLOR); + g.setFont(LABEL_FONT); + + int wrapWidth = 100; + int textY = (int) graphicInfo.getY(); + + // TODO: use drawMultilineText() + AttributedString as = new AttributedString(text); + as.addAttribute(TextAttribute.FOREGROUND, g.getPaint()); + as.addAttribute(TextAttribute.FONT, g.getFont()); + AttributedCharacterIterator aci = as.getIterator(); + FontRenderContext frc = new FontRenderContext(null, true, false); + LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc); + + while (lbm.getPosition() < text.length()) { + TextLayout tl = lbm.nextLayout(wrapWidth); + textY += tl.getAscent(); + + Rectangle2D bb = tl.getBounds(); + double tX = graphicInfo.getX(); + + if (centered) { + tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2); + } + tl.draw(g, (float) tX, textY); + textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent(); + } + + // restore originals + g.setFont(originalFont); + g.setPaint(originalPaint); + } + } + + /** + * 高亮显示task框完成的 + * @param x + * @param y + * @param width + * @param height + */ + @Override + public void drawHighLight(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义task框当前的位置 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightNow(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR1); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义结束节点 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightEnd(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * task框自定义文字 + * @param name + * @param graphicInfo + * @param thickBorder + * @param scaleFactor + */ + @Override + protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder, double scaleFactor) { + + Paint originalPaint = g.getPaint(); + int x = (int) graphicInfo.getX(); + int y = (int) graphicInfo.getY(); + int width = (int) graphicInfo.getWidth(); + int height = (int) graphicInfo.getHeight(); + + // Create a new gradient paint for every task box, gradient depends on x and y and is not relative + g.setPaint(TASK_BOX_COLOR); + + int arcR = 6; + if (thickBorder) { + arcR = 3; + } + + // shape + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcR, arcR); + g.fill(rect); + g.setPaint(TASK_BORDER_COLOR); + + if (thickBorder) { + Stroke originalStroke = g.getStroke(); + g.setStroke(THICK_TASK_BORDER_STROKE); + g.draw(rect); + g.setStroke(originalStroke); + } else { + g.draw(rect); + } + + g.setPaint(originalPaint); + // text + if (scaleFactor == 1.0 && name != null && name.length() > 0) { + int boxWidth = width - (2 * TEXT_PADDING); + int boxHeight = height - 16 - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2; + int boxX = x + width / 2 - boxWidth / 2; + int boxY = y + height / 2 - boxHeight / 2 + ICON_PADDING + ICON_PADDING - 2 - 2; + + drawMultilineCentredText(name, boxX, boxY, boxWidth, boxHeight); + } + } + + protected static Color EVENT_COLOR = new Color(255, 255, 255); + + /** + * 重写开始事件 + * @param graphicInfo + * @param image + * @param scaleFactor + */ + @Override + public void drawStartEvent(GraphicInfo graphicInfo, BufferedImage image, double scaleFactor) { + Paint originalPaint = g.getPaint(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); + g.draw(circle); + g.setPaint(originalPaint); + if (image != null) { + // calculate coordinates to center image + int imageX = (int) Math.round(graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / (2 * scaleFactor))); + int imageY = (int) Math.round(graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / (2 * scaleFactor))); + g.drawImage(image, imageX, imageY, + (int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null); + } + + } + + /** + * 重写结束事件 + * @param graphicInfo + * @param scaleFactor + */ + @Override + public void drawNoneEndEvent(GraphicInfo graphicInfo, double scaleFactor) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); +// g.setPaint(HIGHLIGHT_COLOR); + if (scaleFactor == 1.0) { + g.setStroke(END_EVENT_STROKE); + } else { + g.setStroke(new BasicStroke(2.0f)); + } + g.draw(circle); + g.setStroke(originalStroke); + g.setPaint(originalPaint); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java new file mode 100644 index 00000000..d55e30dd --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java @@ -0,0 +1,404 @@ +package com.yanzhu.flowable.flow; + + +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; + +import java.util.Iterator; +import java.util.List; + +/** + * @author Tony + * @date 2021/4/5 0:31 + */ +public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator { + @Override + protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + this.prepareBpmnModel(bpmnModel); + DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + Iterator var13 = bpmnModel.getPools().iterator(); + + while (var13.hasNext()) { + Pool process = (Pool) var13.next(); + GraphicInfo subProcesses = bpmnModel.getGraphicInfo(process.getId()); + processDiagramCanvas.drawPoolOrLane(process.getName(), subProcesses, scaleFactor); + } + + var13 = bpmnModel.getProcesses().iterator(); + + Process process1; + Iterator subProcesses1; + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.getLanes().iterator(); + + while (subProcesses1.hasNext()) { + Lane artifact = (Lane) subProcesses1.next(); + GraphicInfo subProcess = bpmnModel.getGraphicInfo(artifact.getId()); + processDiagramCanvas.drawPoolOrLane(artifact.getName(), subProcess, scaleFactor); + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.findFlowElementsOfType(FlowNode.class).iterator(); + + while (subProcesses1.hasNext()) { + FlowNode artifact1 = (FlowNode) subProcesses1.next(); + if (!this.isPartOfCollapsedSubProcess(artifact1, bpmnModel)) { + this.drawActivity(processDiagramCanvas, bpmnModel, artifact1, highLightedActivities, highLightedFlows, scaleFactor, Boolean.valueOf(drawSequenceFlowNameWithNoLabelDI)); + } + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + label75: + while (true) { + List subProcesses2; + do { + if (!var13.hasNext()) { + return processDiagramCanvas; + } + + process1 = (Process) var13.next(); + subProcesses1 = process1.getArtifacts().iterator(); + + while (subProcesses1.hasNext()) { + Artifact artifact2 = (Artifact) subProcesses1.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, artifact2); + } + + subProcesses2 = process1.findFlowElementsOfType(SubProcess.class, true); + } while (subProcesses2 == null); + + Iterator artifact3 = subProcesses2.iterator(); + + while (true) { + GraphicInfo graphicInfo; + SubProcess subProcess1; + do { + do { + if (!artifact3.hasNext()) { + continue label75; + } + + subProcess1 = (SubProcess) artifact3.next(); + graphicInfo = bpmnModel.getGraphicInfo(subProcess1.getId()); + } while (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded().booleanValue()); + } while (this.isPartOfCollapsedSubProcess(subProcess1, bpmnModel)); + + Iterator var19 = subProcess1.getArtifacts().iterator(); + + while (var19.hasNext()) { + Artifact subProcessArtifact = (Artifact) var19.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact); + } + } + } + } + + protected static DefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + double minX = 1.7976931348623157E308D; + double maxX = 0.0D; + double minY = 1.7976931348623157E308D; + double maxY = 0.0D; + + GraphicInfo nrOfLanes; + for (Iterator flowNodes = bpmnModel.getPools().iterator(); flowNodes.hasNext(); maxY = nrOfLanes.getY() + nrOfLanes.getHeight()) { + Pool artifacts = (Pool) flowNodes.next(); + nrOfLanes = bpmnModel.getGraphicInfo(artifacts.getId()); + minX = nrOfLanes.getX(); + maxX = nrOfLanes.getX() + nrOfLanes.getWidth(); + minY = nrOfLanes.getY(); + } + + List var23 = gatherAllFlowNodes(bpmnModel); + Iterator var24 = var23.iterator(); + + label155: + while (var24.hasNext()) { + FlowNode var26 = (FlowNode) var24.next(); + GraphicInfo artifact = bpmnModel.getGraphicInfo(var26.getId()); + if (artifact.getX() + artifact.getWidth() > maxX) { + maxX = artifact.getX() + artifact.getWidth(); + } + + if (artifact.getX() < minX) { + minX = artifact.getX(); + } + + if (artifact.getY() + artifact.getHeight() > maxY) { + maxY = artifact.getY() + artifact.getHeight(); + } + + if (artifact.getY() < minY) { + minY = artifact.getY(); + } + + Iterator process = var26.getOutgoingFlows().iterator(); + + while (true) { + List l; + do { + if (!process.hasNext()) { + continue label155; + } + + SequenceFlow graphicInfoList = (SequenceFlow) process.next(); + l = bpmnModel.getFlowLocationGraphicInfo(graphicInfoList.getId()); + } while (l == null); + + Iterator graphicInfo = l.iterator(); + + while (graphicInfo.hasNext()) { + GraphicInfo graphicInfo1 = (GraphicInfo) graphicInfo.next(); + if (graphicInfo1.getX() > maxX) { + maxX = graphicInfo1.getX(); + } + + if (graphicInfo1.getX() < minX) { + minX = graphicInfo1.getX(); + } + + if (graphicInfo1.getY() > maxY) { + maxY = graphicInfo1.getY(); + } + + if (graphicInfo1.getY() < minY) { + minY = graphicInfo1.getY(); + } + } + } + } + + List var25 = gatherAllArtifacts(bpmnModel); + Iterator var27 = var25.iterator(); + + GraphicInfo var37; + while (var27.hasNext()) { + Artifact var29 = (Artifact) var27.next(); + GraphicInfo var31 = bpmnModel.getGraphicInfo(var29.getId()); + if (var31 != null) { + if (var31.getX() + var31.getWidth() > maxX) { + maxX = var31.getX() + var31.getWidth(); + } + + if (var31.getX() < minX) { + minX = var31.getX(); + } + + if (var31.getY() + var31.getHeight() > maxY) { + maxY = var31.getY() + var31.getHeight(); + } + + if (var31.getY() < minY) { + minY = var31.getY(); + } + } + + List var33 = bpmnModel.getFlowLocationGraphicInfo(var29.getId()); + if (var33 != null) { + Iterator var35 = var33.iterator(); + + while (var35.hasNext()) { + var37 = (GraphicInfo) var35.next(); + if (var37.getX() > maxX) { + maxX = var37.getX(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() > maxY) { + maxY = var37.getY(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + } + + int var28 = 0; + Iterator var30 = bpmnModel.getProcesses().iterator(); + + while (var30.hasNext()) { + Process var32 = (Process) var30.next(); + Iterator var34 = var32.getLanes().iterator(); + + while (var34.hasNext()) { + Lane var36 = (Lane) var34.next(); + ++var28; + var37 = bpmnModel.getGraphicInfo(var36.getId()); + if (var37.getX() + var37.getWidth() > maxX) { + maxX = var37.getX() + var37.getWidth(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() + var37.getHeight() > maxY) { + maxY = var37.getY() + var37.getHeight(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + + if (var23.isEmpty() && bpmnModel.getPools().isEmpty() && var28 == 0) { + minX = 0.0D; + minY = 0.0D; + } + + return new CustomProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + } + + + private static void drawHighLight(DefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightNow(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightNow((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightEnd(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightEnd((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + @Override + protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, + FlowNode flowNode, List highLightedActivities, List highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) { + + DefaultProcessDiagramGenerator.ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass()); + if (drawInstruction != null) { + + drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode); + + // Gather info on the multi instance marker + boolean multiInstanceSequential = false; + boolean multiInstanceParallel = false; + boolean collapsed = false; + if (flowNode instanceof Activity) { + Activity activity = (Activity) flowNode; + MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics(); + if (multiInstanceLoopCharacteristics != null) { + multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential(); + multiInstanceParallel = !multiInstanceSequential; + } + } + + // Gather info on the collapsed marker + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + if (flowNode instanceof SubProcess) { + collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded(); + } else if (flowNode instanceof CallActivity) { + collapsed = true; + } + + if (scaleFactor == 1.0) { + // Actually draw the markers + processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), + multiInstanceSequential, multiInstanceParallel, collapsed); + } + + // Draw highlighted activities + if (highLightedActivities.contains(flowNode.getId())) { + + if (highLightedActivities.get(highLightedActivities.size() - 1).equals(flowNode.getId()) + && !"endenv".equals(flowNode.getId())) { + if ((flowNode.getId().contains("Event_"))) { + drawHighLightEnd((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } else { + drawHighLightNow((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + } else { + drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + + + } + + } + + // Outgoing transitions of activity + for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) { + boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId())); + String defaultFlow = null; + if (flowNode instanceof Activity) { + defaultFlow = ((Activity) flowNode).getDefaultFlow(); + } else if (flowNode instanceof Gateway) { + defaultFlow = ((Gateway) flowNode).getDefaultFlow(); + } + + boolean isDefault = false; + if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) { + isDefault = true; + } + boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && !(flowNode instanceof Gateway); + + String sourceRef = sequenceFlow.getSourceRef(); + String targetRef = sequenceFlow.getTargetRef(); + FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef); + FlowElement targetElement = bpmnModel.getFlowElement(targetRef); + List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId()); + if (graphicInfoList != null && graphicInfoList.size() > 0) { + graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList); + int xPoints[] = new int[graphicInfoList.size()]; + int yPoints[] = new int[graphicInfoList.size()]; + + for (int i = 1; i < graphicInfoList.size(); i++) { + GraphicInfo graphicInfo = graphicInfoList.get(i); + GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); + + if (i == 1) { + xPoints[0] = (int) previousGraphicInfo.getX(); + yPoints[0] = (int) previousGraphicInfo.getY(); + } + xPoints[i] = (int) graphicInfo.getX(); + yPoints[i] = (int) graphicInfo.getY(); + + } + + processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor); + + + // Draw sequenceflow label + GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId()); + if (labelGraphicInfo != null) { + processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false); + } else { + if (drawSequenceFlowNameWithNoLabelDI) { + GraphicInfo lineCenter = getLineCenter(graphicInfoList); + processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false); + } + + } + } + } + + // Nested elements + if (flowNode instanceof FlowElementsContainer) { + for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) { + if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) { + drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement, + highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + } + } + } +} + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java new file mode 100644 index 00000000..a4bd2442 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java @@ -0,0 +1,257 @@ +package com.yanzhu.flowable.flow; + +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.Expression; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.ProcessDefinition; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author Tony + * @date 2021/4/19 20:51 + */ +public class FindNextNodeUtil { + + /** + * 获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasks(RepositoryService repositoryService, org.flowable.task.api.Task task, Map map) { + List data = new ArrayList<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = task.getTaskDefinitionKey(); + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + /** + * 启动流程时获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasksByStart(RepositoryService repositoryService, ProcessDefinition processDefinition, Map map) { + List data = new ArrayList<>(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = null; + // 找到开始节点 并获取唯一key + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + key = flowElement.getId(); + break; + } + } + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + + + /** + * 查找下一节点 + * + * @param flowElements + * @param flowElement + * @param map + * @param nextUser + */ + public static void next(Collection flowElements, FlowElement flowElement, Map map, List nextUser) { + //如果是结束节点 + if (flowElement instanceof EndEvent) { + //如果是子任务的结束节点 + if (getSubProcess(flowElements, flowElement) != null) { + flowElement = getSubProcess(flowElements, flowElement); + } + } + //获取Task的出线信息--可以拥有多个 + List outGoingFlows = null; + if (flowElement instanceof Task) { + outGoingFlows = ((Task) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof Gateway) { + outGoingFlows = ((Gateway) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof StartEvent) { + outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof SubProcess) { + outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof CallActivity) { + outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows(); + } + if (outGoingFlows != null && outGoingFlows.size() > 0) { + //遍历所有的出线--找到可以正确执行的那一条 + for (SequenceFlow sequenceFlow : outGoingFlows) { + //1.有表达式,且为true + //2.无表达式 + String expression = sequenceFlow.getConditionExpression(); + if (expression == null || + expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) { + //出线的下一节点 + String nextFlowElementID = sequenceFlow.getTargetRef(); + if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) { + continue; + } + + //查询下一节点的信息 + FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements); + //调用流程 + if (nextFlowElement instanceof CallActivity) { + CallActivity ca = (CallActivity) nextFlowElement; + if (ca.getLoopCharacteristics() != null) { + UserTask userTask = new UserTask(); + userTask.setId(ca.getId()); + + userTask.setId(ca.getId()); + userTask.setLoopCharacteristics(ca.getLoopCharacteristics()); + userTask.setName(ca.getName()); + nextUser.add(userTask); + } + next(flowElements, nextFlowElement, map, nextUser); + } + //用户任务 + if (nextFlowElement instanceof UserTask) { + nextUser.add((UserTask) nextFlowElement); + } + //排他网关 + else if (nextFlowElement instanceof ExclusiveGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //并行网关 + else if (nextFlowElement instanceof ParallelGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //接收任务 + else if (nextFlowElement instanceof ReceiveTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //服务任务 + else if (nextFlowElement instanceof ServiceTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //子任务的起点 + else if (nextFlowElement instanceof StartEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + //结束节点 + else if (nextFlowElement instanceof EndEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + } + } + } + } + + /** + * 判断是否是多实例子流程并且需要设置集合类型变量 + */ + public static boolean checkSubProcess(String Id, Collection flowElements, List nextUser) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess && flowElement1.getId().equals(Id)) { + + SubProcess sp = (SubProcess) flowElement1; + if (sp.getLoopCharacteristics() != null) { + String inputDataItem = sp.getLoopCharacteristics().getInputDataItem(); + UserTask userTask = new UserTask(); + userTask.setId(sp.getId()); + userTask.setLoopCharacteristics(sp.getLoopCharacteristics()); + userTask.setName(sp.getName()); + nextUser.add(userTask); + return true; + } + } + } + + return false; + + } + + /** + * 查询一个节点的是否子任务中的节点,如果是,返回子任务 + * + * @param flowElements 全流程的节点集合 + * @param flowElement 当前节点 + * @return + */ + public static FlowElement getSubProcess(Collection flowElements, FlowElement flowElement) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess) { + for (FlowElement flowElement2 : ((SubProcess) flowElement1).getFlowElements()) { + if (flowElement.equals(flowElement2)) { + return flowElement1; + } + } + } + } + return null; + } + + + /** + * 根据ID查询流程节点对象, 如果是子任务,则返回子任务的开始节点 + * + * @param Id 节点ID + * @param flowElements 流程节点集合 + * @return + */ + public static FlowElement getFlowElementById(String Id, Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement.getId().equals(Id)) { + //如果是子任务,则查询出子任务的开始节点 + if (flowElement instanceof SubProcess) { + return getStartFlowElement(((SubProcess) flowElement).getFlowElements()); + } + return flowElement; + } + if (flowElement instanceof SubProcess) { + FlowElement flowElement1 = getFlowElementById(Id, ((SubProcess) flowElement).getFlowElements()); + if (flowElement1 != null) { + return flowElement1; + } + } + } + return null; + } + + /** + * 返回流程的开始节点 + * + * @param flowElements 节点集合 + * @description: + */ + public static FlowElement getStartFlowElement(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return flowElement; + } + } + return null; + } + + /** + * 校验el表达式 + * + * @param map + * @param expression + * @return + */ + public static boolean expressionResult(Map map, String expression) { + Expression exp = AviatorEvaluator.compile(expression); + final Object execute = exp.execute(map); + return Boolean.parseBoolean(String.valueOf(execute)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java new file mode 100644 index 00000000..530cf038 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java @@ -0,0 +1,589 @@ +package com.yanzhu.flowable.flow; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.*; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.task.api.history.HistoricTaskInstance; + +import java.util.*; + +/** + * @author Tony + * @date 2021-04-03 23:57 + */ +@Slf4j +public class FlowableUtils { + + /** + * 根据节点,获取入口连线 + * @param source + * @return + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getIncomingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getIncomingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getIncomingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getIncomingFlows(); + } + return sequenceFlows; + } + + /** + * 根据节点,获取出口连线 + * @param source + * @return + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getOutgoingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getOutgoingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getOutgoingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取全部节点列表,包含子流程节点 + * @param flowElements + * @param allElements + * @return + */ + public static Collection getAllElements(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + + for (FlowElement flowElement : flowElements) { + allElements.add(flowElement); + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 迭代获取父级任务节点列表,向前找 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 已找到的用户任务节点 + * @return + */ + public static List iteratorFindParentUserTasks(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 = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 类型为用户节点,则新增父级节点 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); + continue; + } + // 类型为子流程,则添加子流程开始节点出口处相连的节点 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + // 获取子流程用户任务节点 + List childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 + * @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 EndEvent && source.getSubProcess() != null) { + userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + 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 (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 迭代获取子流程用户任务节点 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return + */ + public static List findChildProcessUserTasks(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) { + // 循环找到目标元素 + 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 = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 从后向前寻路,获取所有脏线路上的点 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 目标脏线路终点 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set iteratorFindDirtyRoads(FlowElement source, List passRoads, Set hasSequenceFlow, List targets, Set dirtyRoads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 新增经过的路线 + passRoads.add(sequenceFlow.getSourceFlowElement().getId()); + // 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线 + if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) { + dirtyRoads.addAll(passRoads); + continue; + } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, dirtyRoads); + // 是否存在子流程上,true 是,false 否 + Boolean isInChildProcess = dirtyTargetInChildProcess((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, targets, null); + if (isInChildProcess) { + // 已在子流程上找到,该路线结束 + continue; + } + } + // 继续迭代 + dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 迭代获取子流程脏路线 + * 说明,假如回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set findChildProcessAllDirtyRoad(FlowElement source, Set hasSequenceFlow, Set dirtyRoads) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加脏路线 + dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId()); + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, dirtyRoads); + } + // 继续迭代 + dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 判断脏路线结束节点是否在子流程上 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止 + * @param inChildProcess 是否存在子流程上,true 是,false 否 + * @return + */ + public static Boolean dirtyTargetInChildProcess(FlowElement source, Set hasSequenceFlow, List targets, Boolean inChildProcess) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + inChildProcess = inChildProcess != null && inChildProcess; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null && !inChildProcess) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果发现目标点在子流程上存在,说明只到子流程为止 + if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) { + inChildProcess = true; + break; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + inChildProcess = dirtyTargetInChildProcess((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, targets, inChildProcess); + } + // 继续迭代 + inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, targets, inChildProcess); + } + } + return inChildProcess; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param isSequential 是否串行 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targetKsy 目标节点 + * @return + */ + public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, Set hasSequenceFlow, Boolean isSequential) { + isSequential = isSequential == null || isSequential; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, isSequential); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果目标节点已被判断为并行,后面都不需要执行,直接返回 + if (!isSequential) { + break; + } + // 这条线路存在目标节点,这条线路完成,进入下个线路 + if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) { + continue; + } + if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) { + isSequential = false; + break; + } + // 否则就继续迭代 + isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, hasSequenceFlow, isSequential); + } + } + return isSequential; + } + + /** + * 从后向前寻路,获取到达节点的所有路线 + * 不存在直接回退到子流程,但是存在回退到父级流程的情况 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param roads 路线 + * @return + */ + public static List> findRoad(FlowElement source, List passRoads, Set hasSequenceFlow, List> roads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + roads = roads == null ? new ArrayList<>() : roads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null && sequenceFlows.size() != 0) { + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加经过路线 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + passRoads.add((UserTask) sequenceFlow.getSourceFlowElement()); + } + // 继续迭代 + roads = findRoad(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, roads); + } + } else { + // 添加路线 + roads.add(passRoads); + } + return roads; + } + + /** + * 历史节点数据清洗,清洗掉又回滚导致的脏数据 + * @param allElements 全部节点信息 + * @param historicTaskInstanceList 历史任务实例信息,数据采用开始时间升序 + * @return + */ + public static List historicTaskInstanceClean(Collection allElements, List historicTaskInstanceList) { + // 会签节点收集 + List multiTask = new ArrayList<>(); + allElements.forEach(flowElement -> { + if (flowElement instanceof UserTask) { + // 如果该节点的行为为会签行为,说明该节点为会签节点 + if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) { + multiTask.add(flowElement.getId()); + } + } + }); + // 循环放入栈,栈 LIFO:后进先出 + Stack stack = new Stack<>(); + historicTaskInstanceList.forEach(stack::push); + // 清洗后的历史任务实例 + List lastHistoricTaskInstanceList = new ArrayList<>(); + // 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗 + // 临时用户任务 key + StringBuilder userTaskKey = null; + // 临时被删掉的任务 key,存在并行情况 + List deleteKeyList = new ArrayList<>(); + // 临时脏数据线路 + List> dirtyDataLineList = new ArrayList<>(); + // 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到 + // 会签特殊处理下标 + int multiIndex = -1; + // 会签特殊处理 key + StringBuilder multiKey = null; + // 会签特殊处理操作标识 + boolean multiOpera = false; + while (!stack.empty()) { + // 从这里开始 userTaskKey 都还是上个栈的 key + // 是否是脏数据线路上的点 + final boolean[] isDirtyData = {false}; + for (Set oldDirtyDataLine : dirtyDataLineList) { + if (oldDirtyDataLine.contains(stack.peek().getTaskDefinitionKey())) { + isDirtyData[0] = true; + } + } + // 删除原因不为空,说明从这条数据开始回跳或者回退的 + // MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内 + if (stack.peek().getDeleteReason() != null && !"MI_END".equals(stack.peek().getDeleteReason())) { + // 可以理解为脏线路起点 + String dirtyPoint = ""; + if (stack.peek().getDeleteReason().contains("Change activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", ""); + } + // 会签回退删除原因有点不同 + if (stack.peek().getDeleteReason().contains("Change parent activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", ""); + } + FlowElement dirtyTask = null; + // 获取变更节点的对应的入口处连线 + // 如果是网关并行回退情况,会变成两条脏数据路线,效果一样 + for (FlowElement flowElement : allElements) { + if (flowElement.getId().equals(stack.peek().getTaskDefinitionKey())) { + dirtyTask = flowElement; + } + } + // 获取脏数据线路 + Set dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, Arrays.asList(dirtyPoint.split(",")), null); + // 自己本身也是脏线路上的点,加进去 + dirtyDataLine.add(stack.peek().getTaskDefinitionKey()); + log.info(stack.peek().getTaskDefinitionKey() + "点脏路线集合:" + dirtyDataLine); + // 是全新的需要添加的脏线路 + boolean isNewDirtyData = true; + for (int i = 0; i < dirtyDataLineList.size(); i++) { + // 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回 + // 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全 + if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) { + isNewDirtyData = false; + dirtyDataLineList.get(i).addAll(dirtyDataLine); + } + } + // 已确定时全新的脏线路 + if (isNewDirtyData) { + // deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成 + // 按照逻辑,回退后立刻生成的实例记录就是回退的记录 + // 至于驳回所生成的 Key,直接从删除原因中获取,因为存在驳回到并行的情况 + deleteKeyList.add(dirtyPoint + ","); + dirtyDataLineList.add(dirtyDataLine); + } + // 添加后,现在这个点变成脏线路上的点了 + isDirtyData[0] = true; + } + // 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key + if (!isDirtyData[0]) { + lastHistoricTaskInstanceList.add(stack.peek().getTaskDefinitionKey()); + } + // 校验脏线路是否结束 + for (int i = 0; i < deleteKeyList.size(); i ++) { + // 如果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始 + if (multiKey == null && multiTask.contains(stack.peek().getTaskDefinitionKey()) + && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + multiIndex = i; + multiKey = new StringBuilder(stack.peek().getTaskDefinitionKey()); + } + // 会签脏数据处理,节点退回会签清空 + // 如果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉 + if (multiKey != null && !multiKey.toString().equals(stack.peek().getTaskDefinitionKey())) { + deleteKeyList.set(multiIndex , deleteKeyList.get(multiIndex).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + multiKey = null; + // 结束进行下校验删除 + multiOpera = true; + } + // 其他脏数据处理 + // 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息 + // 脏数据产生的新实例中是否包含这条数据 + if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + // 删除匹配到的部分 + deleteKeyList.set(i , deleteKeyList.get(i).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + } + // 如果每组中的元素都以匹配过,说明脏数据结束 + if ("".equals(deleteKeyList.get(i))) { + // 同时删除脏数据 + deleteKeyList.remove(i); + dirtyDataLineList.remove(i); + break; + } + } + // 会签数据处理需要在循环外处理,否则可能导致溢出 + // 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下 + if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) { + // 同时删除脏数据 + deleteKeyList.remove(multiIndex); + dirtyDataLineList.remove(multiIndex); + multiIndex = -1; + multiOpera = false; + } + // pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除 + // 保存新的 userTaskKey 在下个循环中使用 + userTaskKey = new StringBuilder(stack.pop().getTaskDefinitionKey()); + } + log.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList); + return lastHistoricTaskInstanceList; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java new file mode 100644 index 00000000..1a81e6ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java @@ -0,0 +1,372 @@ +package com.yanzhu.flowable.flow; + +import com.yanzhu.common.core.utils.StringUtils; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.common.engine.impl.util.io.StringStreamSource; + +import java.util.*; + +/** + * @author KonBAI + * @createTime 2022/3/26 19:04 + */ +public class ModelUtils { + + private static final BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter(); + + /** + * xml转bpmnModel对象 + * + * @param xml xml + * @return bpmnModel对象 + */ + public static BpmnModel getBpmnModel(String xml) { + return bpmnXMLConverter.convertToBpmnModel(new StringStreamSource(xml), false, false); + } + + /** + * bpmnModel转xml字符串 + * + * @deprecated 存在会丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml字符串 + */ + @Deprecated + public static String getBpmnXmlStr(BpmnModel bpmnModel) { + return StringUtils.utf8Str(getBpmnXml(bpmnModel)); + } + + /** + * bpmnModel转xml对象 + * + * @deprecated 存在丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml + */ + @Deprecated + public static byte[] getBpmnXml(BpmnModel bpmnModel) { + return bpmnXMLConverter.convertToXML(bpmnModel); + } + + /** + * 根据节点,获取入口连线 + * + * @param source 起始节点 + * @return 入口连线列表 + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } + return sequenceFlows; + } + + + /** + * 根据节点,获取出口连线 + * + * @param source 起始节点 + * @return 出口连线列表 + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取开始节点 + * + * @param model bpmnModel对象 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(BpmnModel model) { + Process process = model.getMainProcess(); + FlowElement startElement = process.getInitialFlowElement(); + if (startElement instanceof StartEvent) { + return (StartEvent) startElement; + } + return getStartEvent(process.getFlowElements()); + } + + /** + * 获取开始节点 + * + * @param flowElements 流程元素集合 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return (StartEvent) flowElement; + } + } + return null; + } + + /** + * 获取结束节点 + * + * @param model bpmnModel对象 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(BpmnModel model) { + Process process = model.getMainProcess(); + return getEndEvent(process.getFlowElements()); + } + + /** + * 获取结束节点 + * + * @param flowElements 流程元素集合 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof EndEvent) { + return (EndEvent) flowElement; + } + } + return null; + } + + public static UserTask getUserTaskByKey(BpmnModel model, String taskKey) { + Process process = model.getMainProcess(); + FlowElement flowElement = process.getFlowElement(taskKey); + if (flowElement instanceof UserTask) { + return (UserTask) flowElement; + } + return null; + } + + /** + * 获取流程元素信息 + * + * @param model bpmnModel对象 + * @param flowElementId 元素ID + * @return 元素信息 + */ + public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { + Process process = model.getMainProcess(); + return process.getFlowElement(flowElementId); + } + + /** + * 获取元素表单Key(限开始节点和用户节点可用) + * + * @param flowElement 元素 + * @return 表单Key + */ + public static String getFormKey(FlowElement flowElement) { + if (flowElement != null) { + if (flowElement instanceof StartEvent) { + return ((StartEvent) flowElement).getFormKey(); + } else if (flowElement instanceof UserTask) { + return ((UserTask) flowElement).getFormKey(); + } + } + return null; + } + + /** + * 获取开始节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getStartEventAttributeValue(BpmnModel model, String name) { + StartEvent startEvent = getStartEvent(model); + return getElementAttributeValue(startEvent, name); + } + + /** + * 获取结束节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getEndEventAttributeValue(BpmnModel model, String name) { + EndEvent endEvent = getEndEvent(model); + return getElementAttributeValue(endEvent, name); + } + + /** + * 获取用户任务节点属性值 + * @param model bpmnModel对象 + * @param taskKey 任务Key + * @param name 属性名 + * @return 属性值 + */ + public static String getUserTaskAttributeValue(BpmnModel model, String taskKey, String name) { + UserTask userTask = getUserTaskByKey(model, taskKey); + return getElementAttributeValue(userTask, name); + } + + /** + * 获取元素属性值 + * @param baseElement 流程元素 + * @param name 属性名 + * @return 属性值 + */ + public static String getElementAttributeValue(BaseElement baseElement, String name) { + if (baseElement != null) { + List attributes = baseElement.getAttributes().get(name); + if (attributes != null && !attributes.isEmpty()) { + attributes.iterator().next().getValue(); + Iterator attrIterator = attributes.iterator(); + if(attrIterator.hasNext()) { + ExtensionAttribute attribute = attrIterator.next(); + return attribute.getValue(); + } + } + } + return null; + } + + public static boolean isMultiInstance(BpmnModel model, String taskKey) { + UserTask userTask = getUserTaskByKey(model, taskKey); + if (!Objects.isNull(userTask)) { + return userTask.hasMultiInstanceLoopCharacteristics(); + } + return false; + } + + /** + * 获取所有用户任务节点 + * + * @param model bpmnModel对象 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(BpmnModel model) { + Process process = model.getMainProcess(); + Collection flowElements = process.getFlowElements(); + return getAllUserTaskEvent(flowElements, null); + } + + /** + * 获取所有用户任务节点 + * @param flowElements 流程元素集合 + * @param allElements 所有流程元素集合 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof UserTask) { + allElements.add((UserTask) flowElement); + } + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = getAllUserTaskEvent(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source) { + return findNextUserTasks(source, null, null); + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 用户任务列表 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source, Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>()); + userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>()); + // 获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (!sequenceFlows.isEmpty()) { + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement(); + if (targetFlowElement instanceof UserTask) { + // 若节点为用户任务,加入到结果列表中 + userTaskList.add((UserTask) targetFlowElement); + } else { + // 若节点非用户任务,继续递归查找下一个节点 + findNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList); + } + } + } + return userTaskList; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param target 目标节点 + * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 + * @return 结果 + */ + 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 (sequenceFlows != null && sequenceFlows.size() > 0) { + // 循环找到目标元素 + 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; + } + // 否则就继续迭代 + boolean isSequential = isSequentialReachable(sourceFlowElement, target, visitedElements); + if (!isSequential) { + return false; + } + } + } + return true; + } + + protected 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; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java new file mode 100644 index 00000000..a937b6ce --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java @@ -0,0 +1,42 @@ +package com.yanzhu.flowable.listener; + +import com.yanzhu.common.redis.service.RedisService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Flowable全局监听器 + * + * @author JiangYuQi + */ +@Slf4j +@Component +public class GlobalEventListener extends AbstractFlowableEngineEventListener { + + @Autowired + private RedisService redisService; + + @Autowired + private TaskService taskService; + + @Autowired + private RuntimeService runtimeService; + + @Autowired + private RepositoryService repositoryService; + + @Override + protected void processCompleted(FlowableEngineEntityEvent event) { + log.info("任务流程审批完成...{}",event.getProcessInstanceId()); + super.processCompleted(event); + } + +} + + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java new file mode 100644 index 00000000..1e6f1f7d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java @@ -0,0 +1,56 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.FlowTaskEntity; + +import java.util.List; +import java.util.Map; + +/** + * @author JiangYuQi + * @date 2021-04-03 14:41 + */ +public interface IFlowBusinessKeyService { + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + public Map quueryCount(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + public List> selectCommentByProcInsId(String procInsId); + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + public Map selectFormDatasByProcInsId(String procInsId); + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity); + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java new file mode 100644 index 00000000..80258211 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java @@ -0,0 +1,81 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:41 + */ +public interface IFlowDefinitionService { + + boolean exist(String processDefinitionKey); + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + TableDataInfo list(FlowQueryVo flowQueryVo); + + /** + * 导入流程文件 + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name + * @param category + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in + */ + void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in); + + /** + * 读取xml + * @param deployId + * @return + */ + AjaxResult readXml(String deployId) throws IOException; + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程ID + * @param applyInfos 申请表单 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map applyInfos); + + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + void updateState(Integer state, String deployId); + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + void delete(String deployId); + + + /** + * 读取图片文件 + * @param deployId + * @return + */ + InputStream readImage(String deployId); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java new file mode 100644 index 00000000..acc69698 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java @@ -0,0 +1,54 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.engine.history.HistoricProcessInstance; + +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:40 + */ +public interface IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + void stopProcessInstance(FlowTaskVo vo); + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + void updateState(Integer state, String instanceId); + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + void delete(String instanceId, String deleteReason); + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId); + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map variables); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java new file mode 100644 index 00000000..c23dd4ec --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java @@ -0,0 +1,208 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.task.api.Task; + +import java.io.InputStream; + +/** + * @author Tony + * @date 2021-04-03 14:42 + */ +public interface IFlowTaskService { + + /** + * 审批任务 + * + * @param task 请求实体参数 + */ + AjaxResult complete(FlowTaskVo task); + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + void taskReject(FlowTaskVo flowTaskVo); + + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + void taskReturn(FlowTaskVo flowTaskVo); + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo); + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + void deleteTask(FlowTaskVo flowTaskVo); + + /** + * 认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void claim(FlowTaskVo flowTaskVo); + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void unClaim(FlowTaskVo flowTaskVo); + + /** + * 委派任务 + * + * @param flowTaskVo 请求实体参数 + */ + void delegateTask(FlowTaskVo flowTaskVo); + + /** + * 任务归还 + * + * @param flowTaskVo 请求实体参数 + */ + void resolveTask(FlowTaskVo flowTaskVo); + + + /** + * 转办任务 + * + * @param flowTaskVo 请求实体参数 + */ + void assignTask(FlowTaskVo flowTaskVo); + + + /** + * 多实例加签 + * @param flowTaskVo + */ + void addMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 多实例减签 + * @param flowTaskVo + */ + void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 我发起的流程 + * @param queryVo 请求参数 + * @return + */ + AjaxResult myProcess(FlowQueryVo queryVo); + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * @param flowTaskVo + * @return + */ + AjaxResult stopProcess(FlowTaskVo flowTaskVo); + + /** + * 撤回流程 + * @param flowTaskVo + * @return + */ + AjaxResult revokeProcess(FlowTaskVo flowTaskVo); + + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult todoList(FlowQueryVo queryVo); + + + /** + * 已办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult finishedList(FlowQueryVo queryVo); + + /** + * 流程历史流转记录 + * + * @param procInsId 流程实例Id + * @return + */ + AjaxResult flowRecord(String procInsId,String deployId); + + /** + * 根据任务ID查询挂载的表单信息 + * + * @param taskId 任务Id + * @return + */ + Task getTaskForm(String taskId); + + /** + * 获取流程过程图 + * @param processId + * @return + */ + InputStream diagram(String processId); + + /** + * 获取流程执行节点 + * @param procInsId + * @return + */ + AjaxResult getFlowViewer(String procInsId,String executionId); + + /** + * 获取流程变量 + * @param taskId + * @return + */ + AjaxResult processVariables(String taskId); + + /** + * 获取下一节点 + * @param flowTaskVo 任务 + * @return + */ + AjaxResult getNextFlowNode(FlowTaskVo flowTaskVo); + + AjaxResult getNextFlowNodeByStart(FlowTaskVo flowTaskVo); + + /** + * 流程初始化表单 + * @param deployId + * @return + */ + AjaxResult flowFormData(String deployId); + + /** + * 流程节点信息 + * @param procInsId + * @return + */ + AjaxResult flowXmlAndNode(String procInsId,String deployId); + + /** + * 流程节点表单 + * @param taskId 流程任务编号 + * @return + */ + AjaxResult flowTaskForm(String taskId) throws Exception; +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java new file mode 100644 index 00000000..01ff72bc --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程实例关联表单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +public interface ISysDeployFormService +{ + /** + * 查询流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 流程实例关联表单 + */ + public SysDeployForm selectSysDeployFormById(Long id); + + /** + * 查询流程实例关联表单列表 + * + * @param sysDeployForm 流程实例关联表单 + * @return 流程实例关联表单集合 + */ + public List selectSysDeployFormList(SysDeployForm sysDeployForm); + + /** + * 新增流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int insertSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 修改流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int updateSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 批量删除流程实例关联表单 + * + * @param ids 需要删除的流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormByIds(Long[] ids); + + /** + * 删除流程实例关联表单信息 + * + * @param id 流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormById(Long id); + + /** + * 查询流程挂着的表单 + * @param deployId + * @return + */ + SysForm selectSysDeployFormByDeployId(String deployId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java new file mode 100644 index 00000000..4f230788 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysExpression; + +import java.util.List; + +/** + * 流程达式Service接口 + * + * @author yanZhu + * @date 2022-12-12 + */ +public interface ISysExpressionService +{ + /** + * 查询流程达式 + * + * @param id 流程达式主键 + * @return 流程达式 + */ + public SysExpression selectSysExpressionById(Long id); + + /** + * 查询流程达式列表 + * + * @param sysExpression 流程达式 + * @return 流程达式集合 + */ + public List selectSysExpressionList(SysExpression sysExpression); + + /** + * 新增流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int insertSysExpression(SysExpression sysExpression); + + /** + * 修改流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int updateSysExpression(SysExpression sysExpression); + + /** + * 批量删除流程达式 + * + * @param ids 需要删除的流程达式主键集合 + * @return 结果 + */ + public int deleteSysExpressionByIds(Long[] ids); + + /** + * 删除流程达式信息 + * + * @param id 流程达式主键 + * @return 结果 + */ + public int deleteSysExpressionById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java new file mode 100644 index 00000000..98a844ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java @@ -0,0 +1,61 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 表单 + * @author Tony + * @date 2021-04-03 + */ +public interface ISysFormService +{ + /** + * 查询流程表单 + * + * @param formId 流程表单ID + * @return 流程表单 + */ + public SysForm selectSysFormById(Long formId); + + /** + * 查询流程表单列表 + * + * @param sysForm 流程表单 + * @return 流程表单集合 + */ + public List selectSysFormList(SysForm sysForm); + + /** + * 新增流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int insertSysForm(SysForm sysForm); + + /** + * 修改流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int updateSysForm(SysForm sysForm); + + /** + * 批量删除流程表单 + * + * @param formIds 需要删除的流程表单ID + * @return 结果 + */ + public int deleteSysFormByIds(Long[] formIds); + + /** + * 删除流程表单信息 + * + * @param formId 流程表单ID + * @return 结果 + */ + public int deleteSysFormById(Long formId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java new file mode 100644 index 00000000..c269e24b --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysTaskForm; + +import java.util.List; + +/** + * 流程任务关联单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +@Deprecated +public interface ISysTaskFormService { + /** + * 查询流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 流程任务关联单 + */ + public SysTaskForm selectSysTaskFormById(Long id); + + /** + * 查询流程任务关联单列表 + * + * @param sysTaskForm 流程任务关联单 + * @return 流程任务关联单集合 + */ + public List selectSysTaskFormList(SysTaskForm sysTaskForm); + + /** + * 新增流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int insertSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 修改流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int updateSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 批量删除流程任务关联单 + * + * @param ids 需要删除的流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormByIds(Long[] ids); + + /** + * 删除流程任务关联单信息 + * + * @param id 流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java new file mode 100644 index 00000000..f5150cbb --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java @@ -0,0 +1,102 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.flowable.domain.FlowTaskEntity; +import com.yanzhu.flowable.mapper.FlowBusinessKeyMapper; +import com.yanzhu.flowable.service.IFlowBusinessKeyService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * 业务工作流程自定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowBusinessKeyServiceImpl implements IFlowBusinessKeyService { + + @Resource + private FlowBusinessKeyMapper flowBusinessKeyMapper; + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity); + } + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public Map quueryCount(FlowTaskEntity flowTaskEntity) { + flowTaskEntity.setActiveName("await"); + int awaitSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + flowTaskEntity.setActiveName("finished"); + int finishedSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + Map dataMap = new HashMap<>(); + dataMap.put("await",awaitSize); + dataMap.put("finished",finishedSize); + return dataMap; + } + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + @Override + public List> selectCommentByProcInsId(String procInsId) { + return flowBusinessKeyMapper.selectCommentByProcInsId(procInsId); + } + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + @Override + public Map selectFormDatasByProcInsId(String procInsId) { + Map dataMap = new HashMap<>(); + List> list = flowBusinessKeyMapper.selectFormDatasByProcInsId(procInsId); + Iterator> it = list.iterator(); + while (it.hasNext()) { + Map map = it.next(); + dataMap.put(Convert.toStr(map.get("name")), map.get("text")); + } + return dataMap; + } + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity){ + return flowBusinessKeyMapper.selectMyAwaitFlowTask(flowTaskEntity); + } + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectMyFinishedFlowTask(flowTaskEntity); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java new file mode 100644 index 00000000..0d9f656f --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java @@ -0,0 +1,279 @@ +package com.yanzhu.flowable.service.impl; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.yanzhu.common.core.constant.HttpStatus; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.ServiceException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.SysExpression; +import com.yanzhu.flowable.domain.vo.FlowDeptVo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.mapper.FlowDeployMapper; +import com.yanzhu.flowable.service.IFlowDefinitionService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.repository.ProcessDefinitionQuery; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; +import org.flowable.task.api.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 流程定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFlowDefinitionService { + + @Autowired + private RedisService redisService; + + @Resource + private FlowDeployMapper flowDeployMapper; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private ISysDeployFormService sysDeployFormService; + + private static final String BPMN_FILE_SUFFIX = ".bpmn"; + + @Override + public boolean exist(String processDefinitionKey) { + ProcessDefinitionQuery processDefinitionQuery + = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey); + long count = processDefinitionQuery.count(); + return count > 0 ? true : false; + } + + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + @Override + public TableDataInfo list(FlowQueryVo flowQueryVo) { + TableDataInfo rspData = new TableDataInfo(); + PageHelper.startPage(flowQueryVo.getPageNum(), flowQueryVo.getPageSize()); + List list = flowDeployMapper.selectDeployList(flowQueryVo); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + + /** + * 导入流程文件 + * + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name 流程名称 + * @param category 流程类型 + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in 流程文件流 + */ + @Override + @Transactional + public void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in) { + if(SecurityUtils.isAdmin(SecurityUtils.getUserId())){ + throw new ServiceException("请选择项目后导入流程文件..."); + } + Deployment deploy = repositoryService.createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).category(category).deploy(); + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), category); + /**新增流程单位关系**/ + FlowDeptVo flowDeptVo = new FlowDeptVo(); + flowDeptVo.setProcdefId(definition.getId()); + //超管不能导入流程文件 + if(SecurityUtils.isGSAdmin()){ + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setDeptId(Convert.toLong(OwnerDeptId)); + } + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setProjId(Convert.toLong(OwnerProjectId)); + } + }else{ + // 从用户信息中获取项目单位信息... + flowDeptVo.setPrtype(prtype); + flowDeptVo.setProjId(SecurityUtils.getLoginUser().getProjectId()); + flowDeptVo.setDeptId(SecurityUtils.getLoginUser().getProjectDeptId()); + } + if(flowDeptVo.getDeptId()!=null){ + flowDeployMapper.insertActReProcdefDept(flowDeptVo); + } + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public AjaxResult readXml(String deployId) throws IOException { + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName()); + String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name()); + return AjaxResult.success("", result); + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public InputStream readImage(String deployId) { + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + //获得图片流 + DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + //输出为图片 + return diagramGenerator.generateDiagram( + bpmnModel, + "png", + Collections.emptyList(), + Collections.emptyList(), + "宋体", + "宋体", + "宋体", + null, + 1.0, + false); + + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程模板ID + * @param applyInfos 流程表单 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map applyInfos) { + try { + /**保存流程表单**/ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+procDefId); + R res; + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsGroupEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsUsersEntity(applyInfos, SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + if(StringUtils.isNotNull(res.getData())){ + Map variables = new HashMap<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId) + .latestVersion().singleResult(); + if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) { + return AjaxResult.error("流程已被挂起,请联系管理员激活流程"); + } + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + String userId = Convert.toStr(sysUser.getUserId()); + // 设置流程发起人Id到流程中 + identityService.setAuthenticatedUserId(userId); + variables.put(ProcessConstants.PROCESS_INITIATOR, userId); + variables.put("businessKey", res.getData()); + ProcessInstance processInstance; + processInstance = runtimeService.startProcessInstanceById(procDefId, Convert.toStr(res.getData()),variables); + // 给第一步申请人节点设置任务执行人和意见 + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult(); + if (Objects.nonNull(task)) { + taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), sysUser.getNickName() + "发起流程申请"); + taskService.setAssignee(task.getId(), Convert.toStr(userId)); + taskService.complete(task.getId(), variables); + } + return AjaxResult.success("流程启动成功"); + }else{ + return AjaxResult.error("流程表单保存失败"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + @Override + public void updateState(Integer state, String deployId) { + ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + // 激活 + if (state == 1) { + repositoryService.activateProcessDefinitionById(procDef.getId(), true, null); + } + // 挂起 + if (state == 2) { + repositoryService.suspendProcessDefinitionById(procDef.getId(), true, null); + } + } + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + @Override + public void delete(String deployId) { + // true 允许级联删除 ,不设置会导致数据库外键关联异常 + repositoryService.deleteDeployment(deployId, true); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java new file mode 100644 index 00000000..4facc05d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java @@ -0,0 +1,117 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.service.IFlowInstanceService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.engine.history.HistoricProcessInstance; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Map; +import java.util.Objects; + +/** + * 工作流流程实例管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowInstanceServiceImpl extends FlowServiceFactory implements IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + @Override + public void stopProcessInstance(FlowTaskVo vo) { + String taskId = vo.getTaskId(); + + } + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + @Override + public void updateState(Integer state, String instanceId) { + + // 激活 + if (state == 1) { + runtimeService.activateProcessInstanceById(instanceId); + } + // 挂起 + if (state == 2) { + runtimeService.suspendProcessInstanceById(instanceId); + } + } + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(String instanceId, String deleteReason) { + + // 查询历史数据 + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(instanceId); + if (historicProcessInstance.getEndTime() != null) { + historyService.deleteHistoricProcessInstance(historicProcessInstance.getId()); + return; + } + // 删除流程实例 + runtimeService.deleteProcessInstance(instanceId, deleteReason); + // 删除历史流程实例 + historyService.deleteHistoricProcessInstance(instanceId); + } + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + @Override + public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) { + HistoricProcessInstance historicProcessInstance = + historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + if (Objects.isNull(historicProcessInstance)) { + throw new FlowableObjectNotFoundException("流程实例不存在: " + processInstanceId); + } + return historicProcessInstance; + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map variables) { + + try { + // 设置流程发起人Id到流程中 + Long userId = SecurityUtils.getUserId(); + variables.put("initiator",userId); + variables.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true); + runtimeService.startProcessInstanceById(procDefId, variables); + return AjaxResult.success("流程启动成功"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java new file mode 100644 index 00000000..50208812 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java @@ -0,0 +1,1307 @@ +package com.yanzhu.flowable.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.CustomException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.*; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.flow.CustomProcessDiagramGenerator; +import com.yanzhu.flowable.flow.FindNextNodeUtil; +import com.yanzhu.flowable.flow.FlowableUtils; +import com.yanzhu.flowable.flow.ModelUtils; +import com.yanzhu.flowable.service.IFlowTaskService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.RemoteUserService; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.impl.cmd.AddMultiInstanceExecutionCmd; +import org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.task.Comment; +import org.flowable.identitylink.api.history.HistoricIdentityLink; +import org.flowable.image.ProcessDiagramGenerator; +import org.flowable.task.api.DelegationState; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * @author Tony + * @date 2021-04-03 + **/ +@Service +@Slf4j +public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTaskService { + + @Autowired + private RedisService redisService; + + @Resource + private ISysFormService sysFormService; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private RemoteUserService remoteUserService; + + @Resource + private RemoteRoleService remoteRoleService; + + @Resource + private ISysDeployFormService sysInstanceFormService; + + /** + * 完成任务 + * + * @param taskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public AjaxResult complete(FlowTaskVo taskVo) { + Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult(); + /**表单不为空时,重新修改表单**/ + if(StringUtils.isNotNull(taskVo.getApplyInfos())){ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+task.getProcessDefinitionId()); + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsGroupEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsUsersEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + } + if (Objects.isNull(task)) { + return AjaxResult.error("任务不存在"); + } + if (DelegationState.PENDING.equals(task.getDelegationState())) { + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment()); + taskService.resolveTask(taskVo.getTaskId()); + } else { + Long userId = SecurityUtils.getUserId(); + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment()); + taskService.setAssignee(taskVo.getTaskId(), Convert.toStr(userId)); + taskService.complete(taskVo.getTaskId()); + } + return AjaxResult.success(); + } + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + @Override + public void taskReject(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态!"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + // 获取节点信息 + source = flowElement; + } + } + } + + // 目的获取所有跳转到的节点 targetIds + // 获取当前节点的所有父级用户任务节点 + // 深度优先算法思想:延边迭代深入 + List parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null); + if (parentUserTaskList == null || parentUserTaskList.size() == 0) { + throw new CustomException("当前节点为初始任务节点,不能驳回"); + } + // 获取活动 ID 即节点 Key + List parentUserTaskKeyList = new ArrayList<>(); + parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId())); + // 获取全部历史节点活动实例,即已经走过的节点历史,数据采用开始时间升序 + List historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list(); + // 数据清洗,将回滚导致的脏数据清洗掉 + List lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList); + // 此时历史任务实例为倒序,获取最后走的节点 + List targetIds = new ArrayList<>(); + // 循环结束标识,遇到当前目标节点的次数 + int number = 0; + StringBuilder parentHistoricTaskKey = new StringBuilder(); + for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) { + // 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过 + if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) { + continue; + } + parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey); + if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) { + number++; + } + // 在数据清洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次 + // 在流程中如果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环 + // number == 1,第一次遇到当前节点 + // number == 2,第二次遇到,代表最后一次的循环范围 + if (number == 2) { + break; + } + // 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点 + if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) { + targetIds.add(historicTaskInstanceKey); + } + } + + + // 目的获取所有需要被跳转的节点 currentIds + // 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路 + UserTask oneUserTask = parentUserTaskList.get(0); + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需驳回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + + // 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况 + if (targetIds.size() > 1 && currentIds.size() > 1) { + throw new CustomException("任务出现多对多情况,无法撤回"); + } + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置驳回意见 + currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment())); + + try { + // 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况 + if (targetIds.size() > 1) { + // 1 对 多任务跳转,currentIds 当前节点(1),targetIds 跳转到的节点(多) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()). + moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState(); + } + // 如果父级任务只有一个,因此当前任务可能为网关中的任务 + if (targetIds.size() == 1) { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetIds.get(0) 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState(); + } + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + + } + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void taskReturn(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + // 获取跳转的节点元素 + FlowElement target = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 当前任务节点元素 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = flowElement; + } + // 跳转的节点元素 + if (flowElement.getId().equals(flowTaskVo.getTargetKey())) { + target = flowElement; + } + } + } + + // 从当前节点向前扫描 + // 如果存在路线上不存在目标节点,说明目标节点是在网关上或非同一路线上,不可跳转 + // 否则目标节点相对于当前节点,属于串行 + Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, flowTaskVo.getTargetKey(), null, null); + if (!isSequential) { + throw new CustomException("当前节点相对于目标节点,不属于串行关系,无法回退"); + } + + + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需退回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置回退意见 + currentTaskIds.forEach(currentTaskId -> taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), flowTaskVo.getComment())); + + try { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, flowTaskVo.getTargetKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + } + + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + @Override + public AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo) { +// // 当前任务 task +// Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); +// // 从流程历史任务中获取可退回节点 +//// List hisActIns = historyService.createHistoricActivityInstanceQuery() +//// .executionId(task.getExecutionId()) +//// .activityType("userTask") +//// .orderByHistoricActivityInstanceStartTime() +//// .finished() +//// .desc() +//// .list(); +//// +//// // 可回退的节点列表 +//// List returnTaskNodeList = new ArrayList<>(); +//// ReturnTaskNodeVo returnTaskNodeVo; +//// for (HistoricActivityInstance activityInstance : hisActIns) { +//// returnTaskNodeVo = new ReturnTaskNodeVo(); +//// returnTaskNodeVo.setId(activityInstance.getActivityId()); +//// // 根据流程节点处理时间校验改节点是否已完成 +//// returnTaskNodeVo.setName(activityInstance.getActivityName()); +//// returnTaskNodeList.add(returnTaskNodeVo); +//// } +// List userTaskList = new ArrayList<>(); +// // 获取流程定义信息 +// ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); +// // 获取所有节点信息,暂不考虑子流程情况 +// Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); +// Collection flowElements = process.getFlowElements(); +// // 获取当前任务节点元素 +// UserTask source = null; +// if (flowElements != null) { +// for (FlowElement flowElement : flowElements) { +// // 类型为用户节点 +// if (flowElement.getId().equals(task.getTaskDefinitionKey())) { +// source = (UserTask) flowElement; +// } +// } +// } +// // 获取节点的所有路线 +// List> roads = FlowableUtils.findRoad(source, null, null, null); +// +// for (List road : roads) { +// if (userTaskList.size() == 0) { +// // 还没有可回退节点直接添加 +// userTaskList = road; +// } else { +// // 如果已有回退节点,则比对取交集部分 +// userTaskList.retainAll(road); +// } +// } +// return AjaxResult.success(userTaskList); + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息,暂不考虑子流程情况 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + Collection flowElements = process.getFlowElements(); + // 获取当前任务节点元素 + UserTask source = null; + if (flowElements != null) { + for (FlowElement flowElement : flowElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = (UserTask) flowElement; + } + } + } + // 获取节点的所有路线 + List> roads = FlowableUtils.findRoad(source, null, null, null); + // 可回退的节点列表 + List userTaskList = new ArrayList<>(); + for (List road : roads) { + if (userTaskList.size() == 0) { + // 还没有可回退节点直接添加 + userTaskList = road; + } else { + // 如果已有回退节点,则比对取交集部分 + userTaskList.retainAll(road); + } + } + return AjaxResult.success(userTaskList); + } + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + public void deleteTask(FlowTaskVo flowTaskVo) { + // todo 待确认删除任务是物理删除任务 还是逻辑删除,让这个任务直接通过? + taskService.deleteTask(flowTaskVo.getTaskId(), flowTaskVo.getComment()); + } + + /** + * 认领/签收任务 + * 认领以后,这个用户就会成为任务的执行人,任务会从其他成员的任务列表中消失 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void claim(FlowTaskVo flowTaskVo) { + taskService.claim(flowTaskVo.getTaskId(), flowTaskVo.getUserId()); + } + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void unClaim(FlowTaskVo flowTaskVo) { + taskService.unclaim(flowTaskVo.getTaskId()); + } + + /** + * 委派任务 + * 任务委派只是委派人将当前的任务交给被委派人进行审批,处理任务后又重新回到委派人身上。 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.DELEGATE.getType(), flowTaskVo.getComment()); + taskService.delegateTask(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + } + + /** + * 任务归还 + * 被委派人完成任务之后,将任务归还委派人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void resolveTask(FlowTaskVo flowTaskVo) { + taskService.resolveTask(flowTaskVo.getTaskId()); + } + + + /** + * 转办任务 + * 直接将办理人换成别人,这时任务的拥有者不再是转办人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void assignTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.ASSIGN.getType(), flowTaskVo.getComment()); + // 直接转派就可以覆盖掉之前的 + taskService.setAssignee(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); +// // 删除指派人重新指派 +// taskService.deleteCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// taskService.addCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// // 如果要查询转给他人处理的任务,可以同时将OWNER进行设置: +// taskService.setOwner(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + + } + + /** + * 多实例加签 + * act_ru_task、act_ru_identitylink各生成一条记录 + * + * @param flowTaskVo + */ + @Override + public void addMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new AddMultiInstanceExecutionCmd(flowTaskVo.getDefId(), flowTaskVo.getInstanceId(), flowTaskVo.getVariables())); + } + + /** + * 多实例减签 + * act_ru_task减1、act_ru_identitylink不变 + * + * @param flowTaskVo + */ + @Override + public void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new DeleteMultiInstanceExecutionCmd(flowTaskVo.getCurrentChildExecutionId(), flowTaskVo.getFlag())); + } + + /** + * 我发起的流程 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult myProcess(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + Long userId = SecurityUtils.getUserId(); + HistoricProcessInstanceQuery historicProcessInstanceQuery; + //超管查询所有数据据 + if(SecurityUtils.isAdmin(userId)){ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .orderByProcessInstanceStartTime() + .desc(); + }else{ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .startedBy(userId.toString()) + .orderByProcessInstanceStartTime() + .desc(); + } + List historicProcessInstances = historicProcessInstanceQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + page.setTotal(historicProcessInstanceQuery.count()); + List flowList = new ArrayList<>(); + for (HistoricProcessInstance hisIns : historicProcessInstances) { + FlowTaskDto flowTask = new FlowTaskDto(); + flowTask.setCreateTime(hisIns.getStartTime()); + flowTask.setFinishTime(hisIns.getEndTime()); + flowTask.setProcInsId(hisIns.getId()); + + // 计算耗时 + if (Objects.nonNull(hisIns.getEndTime())) { + long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } else { + long time = System.currentTimeMillis() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } + // 流程定义信息 + ProcessDefinition pd = repositoryService.createProcessDefinitionQuery() + .processDefinitionId(hisIns.getProcessDefinitionId()) + .singleResult(); + flowTask.setDeployId(pd.getDeploymentId()); + flowTask.setProcDefName(pd.getName()); + flowTask.setProcDefVersion(pd.getVersion()); + flowTask.setCategory(pd.getCategory()); + flowTask.setProcDefVersion(pd.getVersion()); + // 当前所处流程 + List taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list(); + if (CollectionUtils.isNotEmpty(taskList)) { + flowTask.setTaskId(taskList.get(0).getId()); + flowTask.setTaskName(taskList.get(0).getName()); + if (StringUtils.isNotBlank(taskList.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(taskList.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } else { + List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list(); + flowTask.setTaskId(historicTaskInstance.get(0).getId()); + flowTask.setTaskName(historicTaskInstance.get(0).getName()); + if (StringUtils.isNotBlank(historicTaskInstance.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(historicTaskInstance.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } + flowList.add(flowTask); + } + page.setRows(flowList); + return AjaxResult.success(page); + } + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult stopProcess(FlowTaskVo flowTaskVo) { + List task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).list(); + if (CollectionUtils.isEmpty(task)) { + throw new CustomException("流程未启动或已执行完成,取消申请失败"); + } + // 获取当前流程实例 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(flowTaskVo.getInstanceId()) + .singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); + if (Objects.nonNull(bpmnModel)) { + Process process = bpmnModel.getMainProcess(); + List endNodes = process.findFlowElementsOfType(EndEvent.class, false); + if (CollectionUtils.isNotEmpty(endNodes)) { + String userIdStr = Convert.toStr(SecurityUtils.getUserId()); + if(StringUtils.isNotEmpty(userIdStr)){ + Authentication.setAuthenticatedUserId(userIdStr); + taskService.addComment(task.get(0).getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(), + StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment()); + // 设置流程经办人为当前登录人员 + taskService.setAssignee(task.get(0).getId(), userIdStr); + // 获取当前流程最后一个节点 + String endId = endNodes.get(0).getId(); + List executions = runtimeService.createExecutionQuery() + .parentId(processInstance.getProcessInstanceId()).list(); + List executionIds = new ArrayList<>(); + executions.forEach(execution -> executionIds.add(execution.getId())); + // 变更流程为已结束状态 + runtimeService.createChangeActivityStateBuilder() + .moveExecutionsToSingleActivityId(executionIds, endId).changeState(); + return AjaxResult.success(); + } + } + } + return AjaxResult.error(); + } + + /** + * 撤回流程 目前存在错误 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult revokeProcess(FlowTaskVo flowTaskVo) { + String procInsId = flowTaskVo.getInstanceId(); + String taskId = flowTaskVo.getTaskId(); + // 校验流程是否结束 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(procInsId) + .active() + .singleResult(); + if(Objects.isNull(processInstance)) { + throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作"); + } + // 获取待撤回任务实例 + HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .taskAssignee(flowTaskVo.getUserId()) + .singleResult(); + if (Objects.isNull(currTaskIns)) { + throw new RuntimeException("当前任务不存在,无法执行撤回操作"); + } + // 获取 bpmn 模型 + BpmnModel bpmnModel = repositoryService.getBpmnModel(currTaskIns.getProcessDefinitionId()); + UserTask currUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currTaskIns.getTaskDefinitionKey()); + // 查找下一级用户任务列表 + List nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask); + List nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList()); + + // 获取当前节点之后已完成的流程历史节点 + List finishedTaskInsList = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(procInsId) + .taskCreatedAfter(currTaskIns.getEndTime()) + .finished() + .list(); + for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) { + // 检查已完成流程历史节点是否存在下一级中 + if (StringUtils.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) { + throw new RuntimeException("下一流程已处理,无法执行撤回操作"); + } + } + // 获取所有激活的任务节点,找到需要撤回的任务 + List activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list(); + List revokeExecutionIds = new ArrayList<>(); + for (Task task : activateTaskList) { + // 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点 + if (StringUtils.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) { + // 添加撤回审批信息 + taskService.setAssignee(task.getId(), flowTaskVo.getUserId()); + taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), flowTaskVo.getAssignee() + "撤回流程审批"); + revokeExecutionIds.add(task.getExecutionId()); + } + } + try { + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(procInsId) + .moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new RuntimeException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new RuntimeException("执行撤回操作失败"); + } + return AjaxResult.success(); + } + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult todoList(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + // 只查看自己的数据 + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + TaskQuery taskQuery = taskService.createTaskQuery() + .active() + .includeProcessVariables() + .taskCandidateGroupIn(sysUser.getRoles().stream().map(role -> role.getRoleId().toString()).collect(Collectors.toList())) + .taskCandidateOrAssigned(sysUser.getUserId().toString()) + .orderByTaskCreateTime().desc(); + + page.setTotal(taskQuery.count()); + List taskList = taskQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + List
+ * 业务工作流程 + *
+ * 工作流程定义 + *
工作流流程实例管理
+ * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "工作流流程实例管理") +@RestController +@RequestMapping("/mapper/flowable/instance") +public class FlowInstanceController { + + @Autowired + private IFlowInstanceService flowInstanceService; + + @ApiOperation(value = "根据流程定义id启动流程实例") + @PostMapping("/startBy/{procDefId}") + public AjaxResult startById(@ApiParam(value = "流程定义id") @PathVariable(value = "procDefId") String procDefId, + @ApiParam(value = "变量集合,json对象") @RequestBody Map variables) { + return flowInstanceService.startProcessInstanceById(procDefId, variables); + + } + + + @ApiOperation(value = "激活或挂起流程实例") + @PostMapping(value = "/updateState") + public AjaxResult updateState(@ApiParam(value = "1:激活,2:挂起", required = true) @RequestParam Integer state, + @ApiParam(value = "流程实例ID", required = true) @RequestParam String instanceId) { + flowInstanceService.updateState(state,instanceId); + return AjaxResult.success(); + } + + @ApiOperation("结束流程实例") + @PostMapping(value = "/stopProcessInstance") + public AjaxResult stopProcessInstance(@RequestBody FlowTaskVo flowTaskVo) { + flowInstanceService.stopProcessInstance(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "删除流程实例") + @DeleteMapping(value = "/delete/{instanceIds}") + public AjaxResult delete(@ApiParam(value = "流程实例ID", required = true) @PathVariable String[] instanceIds, + @ApiParam(value = "删除原因") @RequestParam(required = false) String deleteReason) { + for (String instanceId : instanceIds) { + flowInstanceService.delete(instanceId,deleteReason); + } + return AjaxResult.success(); + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java new file mode 100644 index 00000000..643ef51b --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/FlowTaskController.java @@ -0,0 +1,268 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.flowable.domain.FlowTaskDto; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.service.IFlowTaskService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.imageio.ImageIO; +import javax.servlet.http.HttpServletResponse; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * 工作流任务管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "工作流流程任务管理") +@RestController +@RequestMapping("/mapper/flowable/task") +public class FlowTaskController { + + @Autowired + private IFlowTaskService flowTaskService; + + @ApiOperation(value = "我发起的流程", response = FlowTaskDto.class) + @GetMapping(value = "/myProcess") + public AjaxResult myProcess(FlowQueryVo queryVo) { + return flowTaskService.myProcess(queryVo); + } + + @ApiOperation(value = "取消申请", response = FlowTaskDto.class) + @Log(title = "终止申请", businessType = BusinessType.UPDATE) + @PostMapping(value = "/stopProcess") + public AjaxResult stopProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.stopProcess(flowTaskVo); + } + + @ApiOperation(value = "撤回流程", response = FlowTaskDto.class) + @Log(title = "撤回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/revokeProcess") + public AjaxResult revokeProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.revokeProcess(flowTaskVo); + } + + @ApiOperation(value = "获取待办列表", response = FlowTaskDto.class) + @GetMapping(value = "/todoList") + public AjaxResult todoList(FlowQueryVo queryVo) { + return flowTaskService.todoList(queryVo); + } + + @ApiOperation(value = "获取已办任务", response = FlowTaskDto.class) + @GetMapping(value = "/finishedList") + public AjaxResult finishedList(FlowQueryVo queryVo) { + return flowTaskService.finishedList(queryVo); + } + + + @ApiOperation(value = "流程历史流转记录", response = FlowTaskDto.class) + @GetMapping(value = "/flowRecord") + public AjaxResult flowRecord(String procInsId, String deployId) { + return flowTaskService.flowRecord(procInsId, deployId); + } + + + @ApiOperation(value = "流程初始化表单", response = FlowTaskDto.class) + @GetMapping(value = "/flowFormData") + public AjaxResult flowFormData(String deployId) { + return flowTaskService.flowFormData(deployId); + } + + @ApiOperation(value = "获取流程变量", response = FlowTaskDto.class) + @GetMapping(value = "/processVariables/{taskId}") + public AjaxResult processVariables(@ApiParam(value = "流程任务Id") @PathVariable(value = "taskId") String taskId) { + return flowTaskService.processVariables(taskId); + } + + @ApiOperation(value = "审批任务") + @Log(title = "审批流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/complete") + public AjaxResult complete(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "重新提交流程") + @Log(title = "重新提交流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/completeAndModify") + public AjaxResult completeAndModify(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "驳回任务") + @Log(title = "驳回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/reject") + public AjaxResult taskReject(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReject(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "退回任务") + @Log(title = "退回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/return") + public AjaxResult taskReturn(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReturn(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "获取所有可回退的节点") + @PostMapping(value = "/returnList") + public AjaxResult findReturnTaskList(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.findReturnTaskList(flowTaskVo); + } + + @ApiOperation(value = "删除任务") + @Log(title = "删除流程", businessType = BusinessType.DELETE) + @DeleteMapping(value = "/delete") + public AjaxResult delete(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "认领/签收任务") + @Log(title = "认领/签收流程", businessType = BusinessType.INSERT) + @PostMapping(value = "/claim") + public AjaxResult claim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.claim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "取消认领/签收任务") + @Log(title = "取消认领/签收流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/unClaim") + public AjaxResult unClaim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.unClaim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "委派任务") + @Log(title = "委派流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/delegateTask") + public AjaxResult delegate(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.delegateTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "任务归还") + @Log(title = "归还流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/resolveTask") + public AjaxResult resolveTask(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.resolveTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "转办任务") + @Log(title = "转办流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/assignTask") + public AjaxResult assign(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.assignTask(flowTaskVo); + return AjaxResult.success(); + } + + @PostMapping(value = "/addMultiInstanceExecution") + @ApiOperation(value = "多实例加签") + public AjaxResult addMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.addMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("加签成功"); + } + + @PostMapping(value = "/deleteMultiInstanceExecution") + @ApiOperation(value = "多实例减签") + public AjaxResult deleteMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("减签成功"); + } + + @ApiOperation(value = "获取下一节点") + @PostMapping(value = "/nextFlowNode") + public AjaxResult getNextFlowNode(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNode(flowTaskVo); + } + + @ApiOperation(value = "流程发起时获取下一节点") + @PostMapping(value = "/nextFlowNodeByStart") + public AjaxResult getNextFlowNodeByStart(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNodeByStart(flowTaskVo); + } + + /** + * 生成流程图 + * + * @param processId 任务ID + */ + @GetMapping("/diagram/{processId}") + public void genProcessDiagram(HttpServletResponse response, + @PathVariable("processId") String processId) { + InputStream inputStream = flowTaskService.diagram(processId); + OutputStream os = null; + BufferedImage image = null; + try { + image = ImageIO.read(inputStream); + response.setContentType("image/png"); + os = response.getOutputStream(); + if (image != null) { + ImageIO.write(image, "png", os); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.flush(); + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 获取流程执行节点 + * + * @param procInsId 流程实例编号 + * @param procInsId 任务执行编号 + */ + @GetMapping("/flowViewer/{procInsId}/{executionId}") + public AjaxResult getFlowViewer(@PathVariable("procInsId") String procInsId, + @PathVariable("executionId") String executionId) { + return flowTaskService.getFlowViewer(procInsId, executionId); + } + + /** + * 流程节点信息 + * + * @param procInsId 流程实例id + * @return + */ + @GetMapping("/flowXmlAndNode") + public AjaxResult flowXmlAndNode(@RequestParam(value = "procInsId", required = false) String procInsId, + @RequestParam(value = "deployId", required = false) String deployId) { + return flowTaskService.flowXmlAndNode(procInsId, deployId); + } + + /** + * 流程节点表单 + * + * @param taskId 流程任务编号 + * @return + */ + @GetMapping("/flowTaskForm") + public AjaxResult flowTaskForm(@RequestParam(value = "taskId", required = false) String taskId) throws Exception { + return flowTaskService.flowTaskForm(taskId); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java new file mode 100644 index 00000000..74cbad80 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java @@ -0,0 +1,112 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.utils.poi.ExcelUtil; +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.common.security.annotation.RequiresPermissions; +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 流程表单Controller + * + * @author Tony + * @date 2021-04-03 + */ +@RestController +@RequestMapping("/mapper/flowable/form") +public class SysFormController extends BaseController { + + @Autowired + private ISysFormService SysFormService; + + @Autowired + private ISysDeployFormService sysDeployFormService; + + /** + * 查询流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:list')") + @GetMapping("/list") + public TableDataInfo list(SysForm sysForm) { + startPage(); + List list = SysFormService.selectSysFormList(sysForm); + return getDataTable(list); + } + + @GetMapping("/formList") + public AjaxResult formList(SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + return AjaxResult.success(list); + } + /** + * 导出流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:export')") + @Log(title = "流程表单", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public void export(HttpServletResponse response, SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + ExcelUtil util = new ExcelUtil(SysForm.class); + util.exportExcel(response, list, "form"); + } + + /** + * 获取流程表单详细信息 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:query')") + @GetMapping(value = "/{formId}") + public AjaxResult getInfo(@PathVariable("formId") Long formId) { + return AjaxResult.success(SysFormService.selectSysFormById(formId)); + } + + /** + * 新增流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:add')") + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.insertSysForm(sysForm)); + } + + /** + * 修改流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:edit')") + @Log(title = "流程表单", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.updateSysForm(sysForm)); + } + + /** + * 删除流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:remove')") + @Log(title = "流程表单", businessType = BusinessType.DELETE) + @DeleteMapping("/{formIds}") + public AjaxResult remove(@PathVariable Long[] formIds) { + return toAjax(SysFormService.deleteSysFormByIds(formIds)); + } + + + /** + * 挂载流程表单 + */ + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping("/addDeployForm") + public AjaxResult addDeployForm(@RequestBody SysDeployForm sysDeployForm) { + return toAjax(sysDeployFormService.insertSysDeployForm(sysDeployForm)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java new file mode 100644 index 00000000..e66efd70 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java @@ -0,0 +1,41 @@ +package com.yanzhu.flowable.factory; + +import lombok.Getter; +import org.flowable.engine.*; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * flowable 引擎注入封装 + * @author Tony + * @date 2021-04-03 + */ +@Component +@Getter +public class FlowServiceFactory { + + @Resource + protected RepositoryService repositoryService; + + @Resource + protected RuntimeService runtimeService; + + @Resource + protected IdentityService identityService; + + @Resource + protected TaskService taskService; + + @Resource + protected HistoryService historyService; + + @Resource + protected ManagementService managementService; + + @Qualifier("processEngine") + @Resource + protected ProcessEngine processEngine; + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java new file mode 100644 index 00000000..7a0baca5 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java @@ -0,0 +1,370 @@ +package com.yanzhu.flowable.flow; + +import org.flowable.bpmn.model.AssociationDirection; +import org.flowable.bpmn.model.GraphicInfo; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.util.ReflectUtil; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; + +/** + * @author Tony + * @date 2021/4/4 23:58 + */ +public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas { + //定义走过流程连线颜色为绿色 + protected static Color HIGHLIGHT_SequenceFlow_COLOR = Color.GREEN; + //设置未走过流程的连接线颜色 + protected static Color CONNECTION_COLOR = Color.BLACK; + //设置flows连接线字体颜色red + protected static Color LABEL_COLOR = new Color(0, 0, 0); + //高亮显示task框颜色 + protected static Color HIGHLIGHT_COLOR = Color.GREEN; + protected static Color HIGHLIGHT_COLOR1 = Color.RED; + + public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + this.initialize(imageType); + } + + /** + * 重写绘制连线的方式,设置绘制颜色 + * @param xPoints + * @param yPoints + * @param conditional + * @param isDefault + * @param connectionType + * @param associationDirection + * @param highLighted + * @param scaleFactor + */ + @Override + public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) { + Paint originalPaint = this.g.getPaint(); + Stroke originalStroke = this.g.getStroke(); + this.g.setPaint(CONNECTION_COLOR); + if (connectionType.equals("association")) { + this.g.setStroke(ASSOCIATION_STROKE); + } else if (highLighted) { + this.g.setPaint(HIGHLIGHT_SequenceFlow_COLOR); + this.g.setStroke(HIGHLIGHT_FLOW_STROKE); + } + + for (int i = 1; i < xPoints.length; ++i) { + Integer sourceX = xPoints[i - 1]; + Integer sourceY = yPoints[i - 1]; + Integer targetX = xPoints[i]; + Integer targetY = yPoints[i]; + java.awt.geom.Line2D.Double line = new java.awt.geom.Line2D.Double((double) sourceX, (double) sourceY, (double) targetX, (double) targetY); + this.g.draw(line); + } + + java.awt.geom.Line2D.Double line; + if (isDefault) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawDefaultSequenceFlowIndicator(line, scaleFactor); + } + + if (conditional) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawConditionalSequenceFlowIndicator(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.ONE) || associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[xPoints.length - 2], (double) yPoints[xPoints.length - 2], (double) xPoints[xPoints.length - 1], (double) yPoints[xPoints.length - 1]); + this.drawArrowHead(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[1], (double) yPoints[1], (double) xPoints[0], (double) yPoints[0]); + this.drawArrowHead(line, scaleFactor); + } + + this.g.setPaint(originalPaint); + this.g.setStroke(originalStroke); + } + + /** + * 设置字体大小图标颜色 + * @param imageType + */ + @Override + public void initialize(String imageType) { + if ("png".equalsIgnoreCase(imageType)) { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 2); + } else { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 1); + } + + this.g = this.processDiagram.createGraphics(); + if (!"png".equalsIgnoreCase(imageType)) { + this.g.setBackground(new Color(255, 255, 255, 0)); + this.g.clearRect(0, 0, this.canvasWidth, this.canvasHeight); + } + + this.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + //修改图标颜色,修改图标字体大小 + this.g.setPaint(Color.black); + Font font = new Font(this.activityFontName, 10, 14); + this.g.setFont(font); + this.fontMetrics = this.g.getFontMetrics(); + //修改连接线字体大小 + LABEL_FONT = new Font(this.labelFontName, 10, 15); + ANNOTATION_FONT = new Font(this.annotationFontName, 0, 11); + + try { + USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/userTask.png", this.customClassLoader)); + SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/scriptTask.png", this.customClassLoader)); + SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/serviceTask.png", this.customClassLoader)); + RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/receiveTask.png", this.customClassLoader)); + SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/sendTask.png", this.customClassLoader)); + MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/manualTask.png", this.customClassLoader)); + BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/businessRuleTask.png", this.customClassLoader)); + SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/shellTask.png", this.customClassLoader)); + DMN_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/dmnTask.png", this.customClassLoader)); + CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/camelTask.png", this.customClassLoader)); + MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/muleTask.png", this.customClassLoader)); + HTTP_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/httpTask.png", this.customClassLoader)); + TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", this.customClassLoader)); + COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate-throw.png", this.customClassLoader)); + COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate.png", this.customClassLoader)); + ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error-throw.png", this.customClassLoader)); + ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error.png", this.customClassLoader)); + MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message-throw.png", this.customClassLoader)); + MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message.png", this.customClassLoader)); + SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal-throw.png", this.customClassLoader)); + SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal.png", this.customClassLoader)); + } catch (IOException var4) { + LOGGER.warn("Could not load image for process diagram creation: {}", var4.getMessage()); + } + + } + + /** + * 设置连接线字体 + * @param text + * @param graphicInfo + * @param centered + */ + @Override + public void drawLabel(String text, GraphicInfo graphicInfo, boolean centered) { + float interline = 1.0f; + + // text + if (text != null && text.length() > 0) { + Paint originalPaint = g.getPaint(); + Font originalFont = g.getFont(); + + g.setPaint(LABEL_COLOR); + g.setFont(LABEL_FONT); + + int wrapWidth = 100; + int textY = (int) graphicInfo.getY(); + + // TODO: use drawMultilineText() + AttributedString as = new AttributedString(text); + as.addAttribute(TextAttribute.FOREGROUND, g.getPaint()); + as.addAttribute(TextAttribute.FONT, g.getFont()); + AttributedCharacterIterator aci = as.getIterator(); + FontRenderContext frc = new FontRenderContext(null, true, false); + LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc); + + while (lbm.getPosition() < text.length()) { + TextLayout tl = lbm.nextLayout(wrapWidth); + textY += tl.getAscent(); + + Rectangle2D bb = tl.getBounds(); + double tX = graphicInfo.getX(); + + if (centered) { + tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2); + } + tl.draw(g, (float) tX, textY); + textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent(); + } + + // restore originals + g.setFont(originalFont); + g.setPaint(originalPaint); + } + } + + /** + * 高亮显示task框完成的 + * @param x + * @param y + * @param width + * @param height + */ + @Override + public void drawHighLight(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义task框当前的位置 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightNow(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR1); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义结束节点 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightEnd(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * task框自定义文字 + * @param name + * @param graphicInfo + * @param thickBorder + * @param scaleFactor + */ + @Override + protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder, double scaleFactor) { + + Paint originalPaint = g.getPaint(); + int x = (int) graphicInfo.getX(); + int y = (int) graphicInfo.getY(); + int width = (int) graphicInfo.getWidth(); + int height = (int) graphicInfo.getHeight(); + + // Create a new gradient paint for every task box, gradient depends on x and y and is not relative + g.setPaint(TASK_BOX_COLOR); + + int arcR = 6; + if (thickBorder) { + arcR = 3; + } + + // shape + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcR, arcR); + g.fill(rect); + g.setPaint(TASK_BORDER_COLOR); + + if (thickBorder) { + Stroke originalStroke = g.getStroke(); + g.setStroke(THICK_TASK_BORDER_STROKE); + g.draw(rect); + g.setStroke(originalStroke); + } else { + g.draw(rect); + } + + g.setPaint(originalPaint); + // text + if (scaleFactor == 1.0 && name != null && name.length() > 0) { + int boxWidth = width - (2 * TEXT_PADDING); + int boxHeight = height - 16 - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2; + int boxX = x + width / 2 - boxWidth / 2; + int boxY = y + height / 2 - boxHeight / 2 + ICON_PADDING + ICON_PADDING - 2 - 2; + + drawMultilineCentredText(name, boxX, boxY, boxWidth, boxHeight); + } + } + + protected static Color EVENT_COLOR = new Color(255, 255, 255); + + /** + * 重写开始事件 + * @param graphicInfo + * @param image + * @param scaleFactor + */ + @Override + public void drawStartEvent(GraphicInfo graphicInfo, BufferedImage image, double scaleFactor) { + Paint originalPaint = g.getPaint(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); + g.draw(circle); + g.setPaint(originalPaint); + if (image != null) { + // calculate coordinates to center image + int imageX = (int) Math.round(graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / (2 * scaleFactor))); + int imageY = (int) Math.round(graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / (2 * scaleFactor))); + g.drawImage(image, imageX, imageY, + (int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null); + } + + } + + /** + * 重写结束事件 + * @param graphicInfo + * @param scaleFactor + */ + @Override + public void drawNoneEndEvent(GraphicInfo graphicInfo, double scaleFactor) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); +// g.setPaint(HIGHLIGHT_COLOR); + if (scaleFactor == 1.0) { + g.setStroke(END_EVENT_STROKE); + } else { + g.setStroke(new BasicStroke(2.0f)); + } + g.draw(circle); + g.setStroke(originalStroke); + g.setPaint(originalPaint); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java new file mode 100644 index 00000000..d55e30dd --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java @@ -0,0 +1,404 @@ +package com.yanzhu.flowable.flow; + + +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; + +import java.util.Iterator; +import java.util.List; + +/** + * @author Tony + * @date 2021/4/5 0:31 + */ +public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator { + @Override + protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + this.prepareBpmnModel(bpmnModel); + DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + Iterator var13 = bpmnModel.getPools().iterator(); + + while (var13.hasNext()) { + Pool process = (Pool) var13.next(); + GraphicInfo subProcesses = bpmnModel.getGraphicInfo(process.getId()); + processDiagramCanvas.drawPoolOrLane(process.getName(), subProcesses, scaleFactor); + } + + var13 = bpmnModel.getProcesses().iterator(); + + Process process1; + Iterator subProcesses1; + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.getLanes().iterator(); + + while (subProcesses1.hasNext()) { + Lane artifact = (Lane) subProcesses1.next(); + GraphicInfo subProcess = bpmnModel.getGraphicInfo(artifact.getId()); + processDiagramCanvas.drawPoolOrLane(artifact.getName(), subProcess, scaleFactor); + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.findFlowElementsOfType(FlowNode.class).iterator(); + + while (subProcesses1.hasNext()) { + FlowNode artifact1 = (FlowNode) subProcesses1.next(); + if (!this.isPartOfCollapsedSubProcess(artifact1, bpmnModel)) { + this.drawActivity(processDiagramCanvas, bpmnModel, artifact1, highLightedActivities, highLightedFlows, scaleFactor, Boolean.valueOf(drawSequenceFlowNameWithNoLabelDI)); + } + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + label75: + while (true) { + List subProcesses2; + do { + if (!var13.hasNext()) { + return processDiagramCanvas; + } + + process1 = (Process) var13.next(); + subProcesses1 = process1.getArtifacts().iterator(); + + while (subProcesses1.hasNext()) { + Artifact artifact2 = (Artifact) subProcesses1.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, artifact2); + } + + subProcesses2 = process1.findFlowElementsOfType(SubProcess.class, true); + } while (subProcesses2 == null); + + Iterator artifact3 = subProcesses2.iterator(); + + while (true) { + GraphicInfo graphicInfo; + SubProcess subProcess1; + do { + do { + if (!artifact3.hasNext()) { + continue label75; + } + + subProcess1 = (SubProcess) artifact3.next(); + graphicInfo = bpmnModel.getGraphicInfo(subProcess1.getId()); + } while (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded().booleanValue()); + } while (this.isPartOfCollapsedSubProcess(subProcess1, bpmnModel)); + + Iterator var19 = subProcess1.getArtifacts().iterator(); + + while (var19.hasNext()) { + Artifact subProcessArtifact = (Artifact) var19.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact); + } + } + } + } + + protected static DefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + double minX = 1.7976931348623157E308D; + double maxX = 0.0D; + double minY = 1.7976931348623157E308D; + double maxY = 0.0D; + + GraphicInfo nrOfLanes; + for (Iterator flowNodes = bpmnModel.getPools().iterator(); flowNodes.hasNext(); maxY = nrOfLanes.getY() + nrOfLanes.getHeight()) { + Pool artifacts = (Pool) flowNodes.next(); + nrOfLanes = bpmnModel.getGraphicInfo(artifacts.getId()); + minX = nrOfLanes.getX(); + maxX = nrOfLanes.getX() + nrOfLanes.getWidth(); + minY = nrOfLanes.getY(); + } + + List var23 = gatherAllFlowNodes(bpmnModel); + Iterator var24 = var23.iterator(); + + label155: + while (var24.hasNext()) { + FlowNode var26 = (FlowNode) var24.next(); + GraphicInfo artifact = bpmnModel.getGraphicInfo(var26.getId()); + if (artifact.getX() + artifact.getWidth() > maxX) { + maxX = artifact.getX() + artifact.getWidth(); + } + + if (artifact.getX() < minX) { + minX = artifact.getX(); + } + + if (artifact.getY() + artifact.getHeight() > maxY) { + maxY = artifact.getY() + artifact.getHeight(); + } + + if (artifact.getY() < minY) { + minY = artifact.getY(); + } + + Iterator process = var26.getOutgoingFlows().iterator(); + + while (true) { + List l; + do { + if (!process.hasNext()) { + continue label155; + } + + SequenceFlow graphicInfoList = (SequenceFlow) process.next(); + l = bpmnModel.getFlowLocationGraphicInfo(graphicInfoList.getId()); + } while (l == null); + + Iterator graphicInfo = l.iterator(); + + while (graphicInfo.hasNext()) { + GraphicInfo graphicInfo1 = (GraphicInfo) graphicInfo.next(); + if (graphicInfo1.getX() > maxX) { + maxX = graphicInfo1.getX(); + } + + if (graphicInfo1.getX() < minX) { + minX = graphicInfo1.getX(); + } + + if (graphicInfo1.getY() > maxY) { + maxY = graphicInfo1.getY(); + } + + if (graphicInfo1.getY() < minY) { + minY = graphicInfo1.getY(); + } + } + } + } + + List var25 = gatherAllArtifacts(bpmnModel); + Iterator var27 = var25.iterator(); + + GraphicInfo var37; + while (var27.hasNext()) { + Artifact var29 = (Artifact) var27.next(); + GraphicInfo var31 = bpmnModel.getGraphicInfo(var29.getId()); + if (var31 != null) { + if (var31.getX() + var31.getWidth() > maxX) { + maxX = var31.getX() + var31.getWidth(); + } + + if (var31.getX() < minX) { + minX = var31.getX(); + } + + if (var31.getY() + var31.getHeight() > maxY) { + maxY = var31.getY() + var31.getHeight(); + } + + if (var31.getY() < minY) { + minY = var31.getY(); + } + } + + List var33 = bpmnModel.getFlowLocationGraphicInfo(var29.getId()); + if (var33 != null) { + Iterator var35 = var33.iterator(); + + while (var35.hasNext()) { + var37 = (GraphicInfo) var35.next(); + if (var37.getX() > maxX) { + maxX = var37.getX(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() > maxY) { + maxY = var37.getY(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + } + + int var28 = 0; + Iterator var30 = bpmnModel.getProcesses().iterator(); + + while (var30.hasNext()) { + Process var32 = (Process) var30.next(); + Iterator var34 = var32.getLanes().iterator(); + + while (var34.hasNext()) { + Lane var36 = (Lane) var34.next(); + ++var28; + var37 = bpmnModel.getGraphicInfo(var36.getId()); + if (var37.getX() + var37.getWidth() > maxX) { + maxX = var37.getX() + var37.getWidth(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() + var37.getHeight() > maxY) { + maxY = var37.getY() + var37.getHeight(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + + if (var23.isEmpty() && bpmnModel.getPools().isEmpty() && var28 == 0) { + minX = 0.0D; + minY = 0.0D; + } + + return new CustomProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + } + + + private static void drawHighLight(DefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightNow(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightNow((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightEnd(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightEnd((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + @Override + protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, + FlowNode flowNode, List highLightedActivities, List highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) { + + DefaultProcessDiagramGenerator.ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass()); + if (drawInstruction != null) { + + drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode); + + // Gather info on the multi instance marker + boolean multiInstanceSequential = false; + boolean multiInstanceParallel = false; + boolean collapsed = false; + if (flowNode instanceof Activity) { + Activity activity = (Activity) flowNode; + MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics(); + if (multiInstanceLoopCharacteristics != null) { + multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential(); + multiInstanceParallel = !multiInstanceSequential; + } + } + + // Gather info on the collapsed marker + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + if (flowNode instanceof SubProcess) { + collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded(); + } else if (flowNode instanceof CallActivity) { + collapsed = true; + } + + if (scaleFactor == 1.0) { + // Actually draw the markers + processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), + multiInstanceSequential, multiInstanceParallel, collapsed); + } + + // Draw highlighted activities + if (highLightedActivities.contains(flowNode.getId())) { + + if (highLightedActivities.get(highLightedActivities.size() - 1).equals(flowNode.getId()) + && !"endenv".equals(flowNode.getId())) { + if ((flowNode.getId().contains("Event_"))) { + drawHighLightEnd((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } else { + drawHighLightNow((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + } else { + drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + + + } + + } + + // Outgoing transitions of activity + for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) { + boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId())); + String defaultFlow = null; + if (flowNode instanceof Activity) { + defaultFlow = ((Activity) flowNode).getDefaultFlow(); + } else if (flowNode instanceof Gateway) { + defaultFlow = ((Gateway) flowNode).getDefaultFlow(); + } + + boolean isDefault = false; + if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) { + isDefault = true; + } + boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && !(flowNode instanceof Gateway); + + String sourceRef = sequenceFlow.getSourceRef(); + String targetRef = sequenceFlow.getTargetRef(); + FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef); + FlowElement targetElement = bpmnModel.getFlowElement(targetRef); + List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId()); + if (graphicInfoList != null && graphicInfoList.size() > 0) { + graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList); + int xPoints[] = new int[graphicInfoList.size()]; + int yPoints[] = new int[graphicInfoList.size()]; + + for (int i = 1; i < graphicInfoList.size(); i++) { + GraphicInfo graphicInfo = graphicInfoList.get(i); + GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); + + if (i == 1) { + xPoints[0] = (int) previousGraphicInfo.getX(); + yPoints[0] = (int) previousGraphicInfo.getY(); + } + xPoints[i] = (int) graphicInfo.getX(); + yPoints[i] = (int) graphicInfo.getY(); + + } + + processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor); + + + // Draw sequenceflow label + GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId()); + if (labelGraphicInfo != null) { + processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false); + } else { + if (drawSequenceFlowNameWithNoLabelDI) { + GraphicInfo lineCenter = getLineCenter(graphicInfoList); + processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false); + } + + } + } + } + + // Nested elements + if (flowNode instanceof FlowElementsContainer) { + for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) { + if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) { + drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement, + highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + } + } + } +} + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java new file mode 100644 index 00000000..a4bd2442 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java @@ -0,0 +1,257 @@ +package com.yanzhu.flowable.flow; + +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.Expression; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.ProcessDefinition; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author Tony + * @date 2021/4/19 20:51 + */ +public class FindNextNodeUtil { + + /** + * 获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasks(RepositoryService repositoryService, org.flowable.task.api.Task task, Map map) { + List data = new ArrayList<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = task.getTaskDefinitionKey(); + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + /** + * 启动流程时获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasksByStart(RepositoryService repositoryService, ProcessDefinition processDefinition, Map map) { + List data = new ArrayList<>(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = null; + // 找到开始节点 并获取唯一key + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + key = flowElement.getId(); + break; + } + } + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + + + /** + * 查找下一节点 + * + * @param flowElements + * @param flowElement + * @param map + * @param nextUser + */ + public static void next(Collection flowElements, FlowElement flowElement, Map map, List nextUser) { + //如果是结束节点 + if (flowElement instanceof EndEvent) { + //如果是子任务的结束节点 + if (getSubProcess(flowElements, flowElement) != null) { + flowElement = getSubProcess(flowElements, flowElement); + } + } + //获取Task的出线信息--可以拥有多个 + List outGoingFlows = null; + if (flowElement instanceof Task) { + outGoingFlows = ((Task) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof Gateway) { + outGoingFlows = ((Gateway) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof StartEvent) { + outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof SubProcess) { + outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof CallActivity) { + outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows(); + } + if (outGoingFlows != null && outGoingFlows.size() > 0) { + //遍历所有的出线--找到可以正确执行的那一条 + for (SequenceFlow sequenceFlow : outGoingFlows) { + //1.有表达式,且为true + //2.无表达式 + String expression = sequenceFlow.getConditionExpression(); + if (expression == null || + expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) { + //出线的下一节点 + String nextFlowElementID = sequenceFlow.getTargetRef(); + if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) { + continue; + } + + //查询下一节点的信息 + FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements); + //调用流程 + if (nextFlowElement instanceof CallActivity) { + CallActivity ca = (CallActivity) nextFlowElement; + if (ca.getLoopCharacteristics() != null) { + UserTask userTask = new UserTask(); + userTask.setId(ca.getId()); + + userTask.setId(ca.getId()); + userTask.setLoopCharacteristics(ca.getLoopCharacteristics()); + userTask.setName(ca.getName()); + nextUser.add(userTask); + } + next(flowElements, nextFlowElement, map, nextUser); + } + //用户任务 + if (nextFlowElement instanceof UserTask) { + nextUser.add((UserTask) nextFlowElement); + } + //排他网关 + else if (nextFlowElement instanceof ExclusiveGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //并行网关 + else if (nextFlowElement instanceof ParallelGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //接收任务 + else if (nextFlowElement instanceof ReceiveTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //服务任务 + else if (nextFlowElement instanceof ServiceTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //子任务的起点 + else if (nextFlowElement instanceof StartEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + //结束节点 + else if (nextFlowElement instanceof EndEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + } + } + } + } + + /** + * 判断是否是多实例子流程并且需要设置集合类型变量 + */ + public static boolean checkSubProcess(String Id, Collection flowElements, List nextUser) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess && flowElement1.getId().equals(Id)) { + + SubProcess sp = (SubProcess) flowElement1; + if (sp.getLoopCharacteristics() != null) { + String inputDataItem = sp.getLoopCharacteristics().getInputDataItem(); + UserTask userTask = new UserTask(); + userTask.setId(sp.getId()); + userTask.setLoopCharacteristics(sp.getLoopCharacteristics()); + userTask.setName(sp.getName()); + nextUser.add(userTask); + return true; + } + } + } + + return false; + + } + + /** + * 查询一个节点的是否子任务中的节点,如果是,返回子任务 + * + * @param flowElements 全流程的节点集合 + * @param flowElement 当前节点 + * @return + */ + public static FlowElement getSubProcess(Collection flowElements, FlowElement flowElement) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess) { + for (FlowElement flowElement2 : ((SubProcess) flowElement1).getFlowElements()) { + if (flowElement.equals(flowElement2)) { + return flowElement1; + } + } + } + } + return null; + } + + + /** + * 根据ID查询流程节点对象, 如果是子任务,则返回子任务的开始节点 + * + * @param Id 节点ID + * @param flowElements 流程节点集合 + * @return + */ + public static FlowElement getFlowElementById(String Id, Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement.getId().equals(Id)) { + //如果是子任务,则查询出子任务的开始节点 + if (flowElement instanceof SubProcess) { + return getStartFlowElement(((SubProcess) flowElement).getFlowElements()); + } + return flowElement; + } + if (flowElement instanceof SubProcess) { + FlowElement flowElement1 = getFlowElementById(Id, ((SubProcess) flowElement).getFlowElements()); + if (flowElement1 != null) { + return flowElement1; + } + } + } + return null; + } + + /** + * 返回流程的开始节点 + * + * @param flowElements 节点集合 + * @description: + */ + public static FlowElement getStartFlowElement(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return flowElement; + } + } + return null; + } + + /** + * 校验el表达式 + * + * @param map + * @param expression + * @return + */ + public static boolean expressionResult(Map map, String expression) { + Expression exp = AviatorEvaluator.compile(expression); + final Object execute = exp.execute(map); + return Boolean.parseBoolean(String.valueOf(execute)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java new file mode 100644 index 00000000..530cf038 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java @@ -0,0 +1,589 @@ +package com.yanzhu.flowable.flow; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.*; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.task.api.history.HistoricTaskInstance; + +import java.util.*; + +/** + * @author Tony + * @date 2021-04-03 23:57 + */ +@Slf4j +public class FlowableUtils { + + /** + * 根据节点,获取入口连线 + * @param source + * @return + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getIncomingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getIncomingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getIncomingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getIncomingFlows(); + } + return sequenceFlows; + } + + /** + * 根据节点,获取出口连线 + * @param source + * @return + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getOutgoingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getOutgoingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getOutgoingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取全部节点列表,包含子流程节点 + * @param flowElements + * @param allElements + * @return + */ + public static Collection getAllElements(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + + for (FlowElement flowElement : flowElements) { + allElements.add(flowElement); + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 迭代获取父级任务节点列表,向前找 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 已找到的用户任务节点 + * @return + */ + public static List iteratorFindParentUserTasks(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 = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 类型为用户节点,则新增父级节点 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); + continue; + } + // 类型为子流程,则添加子流程开始节点出口处相连的节点 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + // 获取子流程用户任务节点 + List childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 + * @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 EndEvent && source.getSubProcess() != null) { + userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + 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 (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 迭代获取子流程用户任务节点 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return + */ + public static List findChildProcessUserTasks(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) { + // 循环找到目标元素 + 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 = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 从后向前寻路,获取所有脏线路上的点 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 目标脏线路终点 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set iteratorFindDirtyRoads(FlowElement source, List passRoads, Set hasSequenceFlow, List targets, Set dirtyRoads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 新增经过的路线 + passRoads.add(sequenceFlow.getSourceFlowElement().getId()); + // 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线 + if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) { + dirtyRoads.addAll(passRoads); + continue; + } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, dirtyRoads); + // 是否存在子流程上,true 是,false 否 + Boolean isInChildProcess = dirtyTargetInChildProcess((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, targets, null); + if (isInChildProcess) { + // 已在子流程上找到,该路线结束 + continue; + } + } + // 继续迭代 + dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 迭代获取子流程脏路线 + * 说明,假如回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set findChildProcessAllDirtyRoad(FlowElement source, Set hasSequenceFlow, Set dirtyRoads) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加脏路线 + dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId()); + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, dirtyRoads); + } + // 继续迭代 + dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 判断脏路线结束节点是否在子流程上 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止 + * @param inChildProcess 是否存在子流程上,true 是,false 否 + * @return + */ + public static Boolean dirtyTargetInChildProcess(FlowElement source, Set hasSequenceFlow, List targets, Boolean inChildProcess) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + inChildProcess = inChildProcess != null && inChildProcess; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null && !inChildProcess) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果发现目标点在子流程上存在,说明只到子流程为止 + if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) { + inChildProcess = true; + break; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + inChildProcess = dirtyTargetInChildProcess((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, targets, inChildProcess); + } + // 继续迭代 + inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, targets, inChildProcess); + } + } + return inChildProcess; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param isSequential 是否串行 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targetKsy 目标节点 + * @return + */ + public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, Set hasSequenceFlow, Boolean isSequential) { + isSequential = isSequential == null || isSequential; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, isSequential); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果目标节点已被判断为并行,后面都不需要执行,直接返回 + if (!isSequential) { + break; + } + // 这条线路存在目标节点,这条线路完成,进入下个线路 + if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) { + continue; + } + if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) { + isSequential = false; + break; + } + // 否则就继续迭代 + isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, hasSequenceFlow, isSequential); + } + } + return isSequential; + } + + /** + * 从后向前寻路,获取到达节点的所有路线 + * 不存在直接回退到子流程,但是存在回退到父级流程的情况 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param roads 路线 + * @return + */ + public static List> findRoad(FlowElement source, List passRoads, Set hasSequenceFlow, List> roads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + roads = roads == null ? new ArrayList<>() : roads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null && sequenceFlows.size() != 0) { + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加经过路线 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + passRoads.add((UserTask) sequenceFlow.getSourceFlowElement()); + } + // 继续迭代 + roads = findRoad(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, roads); + } + } else { + // 添加路线 + roads.add(passRoads); + } + return roads; + } + + /** + * 历史节点数据清洗,清洗掉又回滚导致的脏数据 + * @param allElements 全部节点信息 + * @param historicTaskInstanceList 历史任务实例信息,数据采用开始时间升序 + * @return + */ + public static List historicTaskInstanceClean(Collection allElements, List historicTaskInstanceList) { + // 会签节点收集 + List multiTask = new ArrayList<>(); + allElements.forEach(flowElement -> { + if (flowElement instanceof UserTask) { + // 如果该节点的行为为会签行为,说明该节点为会签节点 + if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) { + multiTask.add(flowElement.getId()); + } + } + }); + // 循环放入栈,栈 LIFO:后进先出 + Stack stack = new Stack<>(); + historicTaskInstanceList.forEach(stack::push); + // 清洗后的历史任务实例 + List lastHistoricTaskInstanceList = new ArrayList<>(); + // 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗 + // 临时用户任务 key + StringBuilder userTaskKey = null; + // 临时被删掉的任务 key,存在并行情况 + List deleteKeyList = new ArrayList<>(); + // 临时脏数据线路 + List> dirtyDataLineList = new ArrayList<>(); + // 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到 + // 会签特殊处理下标 + int multiIndex = -1; + // 会签特殊处理 key + StringBuilder multiKey = null; + // 会签特殊处理操作标识 + boolean multiOpera = false; + while (!stack.empty()) { + // 从这里开始 userTaskKey 都还是上个栈的 key + // 是否是脏数据线路上的点 + final boolean[] isDirtyData = {false}; + for (Set oldDirtyDataLine : dirtyDataLineList) { + if (oldDirtyDataLine.contains(stack.peek().getTaskDefinitionKey())) { + isDirtyData[0] = true; + } + } + // 删除原因不为空,说明从这条数据开始回跳或者回退的 + // MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内 + if (stack.peek().getDeleteReason() != null && !"MI_END".equals(stack.peek().getDeleteReason())) { + // 可以理解为脏线路起点 + String dirtyPoint = ""; + if (stack.peek().getDeleteReason().contains("Change activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", ""); + } + // 会签回退删除原因有点不同 + if (stack.peek().getDeleteReason().contains("Change parent activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", ""); + } + FlowElement dirtyTask = null; + // 获取变更节点的对应的入口处连线 + // 如果是网关并行回退情况,会变成两条脏数据路线,效果一样 + for (FlowElement flowElement : allElements) { + if (flowElement.getId().equals(stack.peek().getTaskDefinitionKey())) { + dirtyTask = flowElement; + } + } + // 获取脏数据线路 + Set dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, Arrays.asList(dirtyPoint.split(",")), null); + // 自己本身也是脏线路上的点,加进去 + dirtyDataLine.add(stack.peek().getTaskDefinitionKey()); + log.info(stack.peek().getTaskDefinitionKey() + "点脏路线集合:" + dirtyDataLine); + // 是全新的需要添加的脏线路 + boolean isNewDirtyData = true; + for (int i = 0; i < dirtyDataLineList.size(); i++) { + // 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回 + // 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全 + if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) { + isNewDirtyData = false; + dirtyDataLineList.get(i).addAll(dirtyDataLine); + } + } + // 已确定时全新的脏线路 + if (isNewDirtyData) { + // deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成 + // 按照逻辑,回退后立刻生成的实例记录就是回退的记录 + // 至于驳回所生成的 Key,直接从删除原因中获取,因为存在驳回到并行的情况 + deleteKeyList.add(dirtyPoint + ","); + dirtyDataLineList.add(dirtyDataLine); + } + // 添加后,现在这个点变成脏线路上的点了 + isDirtyData[0] = true; + } + // 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key + if (!isDirtyData[0]) { + lastHistoricTaskInstanceList.add(stack.peek().getTaskDefinitionKey()); + } + // 校验脏线路是否结束 + for (int i = 0; i < deleteKeyList.size(); i ++) { + // 如果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始 + if (multiKey == null && multiTask.contains(stack.peek().getTaskDefinitionKey()) + && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + multiIndex = i; + multiKey = new StringBuilder(stack.peek().getTaskDefinitionKey()); + } + // 会签脏数据处理,节点退回会签清空 + // 如果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉 + if (multiKey != null && !multiKey.toString().equals(stack.peek().getTaskDefinitionKey())) { + deleteKeyList.set(multiIndex , deleteKeyList.get(multiIndex).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + multiKey = null; + // 结束进行下校验删除 + multiOpera = true; + } + // 其他脏数据处理 + // 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息 + // 脏数据产生的新实例中是否包含这条数据 + if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + // 删除匹配到的部分 + deleteKeyList.set(i , deleteKeyList.get(i).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + } + // 如果每组中的元素都以匹配过,说明脏数据结束 + if ("".equals(deleteKeyList.get(i))) { + // 同时删除脏数据 + deleteKeyList.remove(i); + dirtyDataLineList.remove(i); + break; + } + } + // 会签数据处理需要在循环外处理,否则可能导致溢出 + // 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下 + if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) { + // 同时删除脏数据 + deleteKeyList.remove(multiIndex); + dirtyDataLineList.remove(multiIndex); + multiIndex = -1; + multiOpera = false; + } + // pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除 + // 保存新的 userTaskKey 在下个循环中使用 + userTaskKey = new StringBuilder(stack.pop().getTaskDefinitionKey()); + } + log.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList); + return lastHistoricTaskInstanceList; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java new file mode 100644 index 00000000..1a81e6ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java @@ -0,0 +1,372 @@ +package com.yanzhu.flowable.flow; + +import com.yanzhu.common.core.utils.StringUtils; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.common.engine.impl.util.io.StringStreamSource; + +import java.util.*; + +/** + * @author KonBAI + * @createTime 2022/3/26 19:04 + */ +public class ModelUtils { + + private static final BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter(); + + /** + * xml转bpmnModel对象 + * + * @param xml xml + * @return bpmnModel对象 + */ + public static BpmnModel getBpmnModel(String xml) { + return bpmnXMLConverter.convertToBpmnModel(new StringStreamSource(xml), false, false); + } + + /** + * bpmnModel转xml字符串 + * + * @deprecated 存在会丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml字符串 + */ + @Deprecated + public static String getBpmnXmlStr(BpmnModel bpmnModel) { + return StringUtils.utf8Str(getBpmnXml(bpmnModel)); + } + + /** + * bpmnModel转xml对象 + * + * @deprecated 存在丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml + */ + @Deprecated + public static byte[] getBpmnXml(BpmnModel bpmnModel) { + return bpmnXMLConverter.convertToXML(bpmnModel); + } + + /** + * 根据节点,获取入口连线 + * + * @param source 起始节点 + * @return 入口连线列表 + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } + return sequenceFlows; + } + + + /** + * 根据节点,获取出口连线 + * + * @param source 起始节点 + * @return 出口连线列表 + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取开始节点 + * + * @param model bpmnModel对象 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(BpmnModel model) { + Process process = model.getMainProcess(); + FlowElement startElement = process.getInitialFlowElement(); + if (startElement instanceof StartEvent) { + return (StartEvent) startElement; + } + return getStartEvent(process.getFlowElements()); + } + + /** + * 获取开始节点 + * + * @param flowElements 流程元素集合 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return (StartEvent) flowElement; + } + } + return null; + } + + /** + * 获取结束节点 + * + * @param model bpmnModel对象 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(BpmnModel model) { + Process process = model.getMainProcess(); + return getEndEvent(process.getFlowElements()); + } + + /** + * 获取结束节点 + * + * @param flowElements 流程元素集合 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof EndEvent) { + return (EndEvent) flowElement; + } + } + return null; + } + + public static UserTask getUserTaskByKey(BpmnModel model, String taskKey) { + Process process = model.getMainProcess(); + FlowElement flowElement = process.getFlowElement(taskKey); + if (flowElement instanceof UserTask) { + return (UserTask) flowElement; + } + return null; + } + + /** + * 获取流程元素信息 + * + * @param model bpmnModel对象 + * @param flowElementId 元素ID + * @return 元素信息 + */ + public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { + Process process = model.getMainProcess(); + return process.getFlowElement(flowElementId); + } + + /** + * 获取元素表单Key(限开始节点和用户节点可用) + * + * @param flowElement 元素 + * @return 表单Key + */ + public static String getFormKey(FlowElement flowElement) { + if (flowElement != null) { + if (flowElement instanceof StartEvent) { + return ((StartEvent) flowElement).getFormKey(); + } else if (flowElement instanceof UserTask) { + return ((UserTask) flowElement).getFormKey(); + } + } + return null; + } + + /** + * 获取开始节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getStartEventAttributeValue(BpmnModel model, String name) { + StartEvent startEvent = getStartEvent(model); + return getElementAttributeValue(startEvent, name); + } + + /** + * 获取结束节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getEndEventAttributeValue(BpmnModel model, String name) { + EndEvent endEvent = getEndEvent(model); + return getElementAttributeValue(endEvent, name); + } + + /** + * 获取用户任务节点属性值 + * @param model bpmnModel对象 + * @param taskKey 任务Key + * @param name 属性名 + * @return 属性值 + */ + public static String getUserTaskAttributeValue(BpmnModel model, String taskKey, String name) { + UserTask userTask = getUserTaskByKey(model, taskKey); + return getElementAttributeValue(userTask, name); + } + + /** + * 获取元素属性值 + * @param baseElement 流程元素 + * @param name 属性名 + * @return 属性值 + */ + public static String getElementAttributeValue(BaseElement baseElement, String name) { + if (baseElement != null) { + List attributes = baseElement.getAttributes().get(name); + if (attributes != null && !attributes.isEmpty()) { + attributes.iterator().next().getValue(); + Iterator attrIterator = attributes.iterator(); + if(attrIterator.hasNext()) { + ExtensionAttribute attribute = attrIterator.next(); + return attribute.getValue(); + } + } + } + return null; + } + + public static boolean isMultiInstance(BpmnModel model, String taskKey) { + UserTask userTask = getUserTaskByKey(model, taskKey); + if (!Objects.isNull(userTask)) { + return userTask.hasMultiInstanceLoopCharacteristics(); + } + return false; + } + + /** + * 获取所有用户任务节点 + * + * @param model bpmnModel对象 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(BpmnModel model) { + Process process = model.getMainProcess(); + Collection flowElements = process.getFlowElements(); + return getAllUserTaskEvent(flowElements, null); + } + + /** + * 获取所有用户任务节点 + * @param flowElements 流程元素集合 + * @param allElements 所有流程元素集合 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof UserTask) { + allElements.add((UserTask) flowElement); + } + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = getAllUserTaskEvent(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source) { + return findNextUserTasks(source, null, null); + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 用户任务列表 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source, Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>()); + userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>()); + // 获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (!sequenceFlows.isEmpty()) { + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement(); + if (targetFlowElement instanceof UserTask) { + // 若节点为用户任务,加入到结果列表中 + userTaskList.add((UserTask) targetFlowElement); + } else { + // 若节点非用户任务,继续递归查找下一个节点 + findNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList); + } + } + } + return userTaskList; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param target 目标节点 + * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 + * @return 结果 + */ + 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 (sequenceFlows != null && sequenceFlows.size() > 0) { + // 循环找到目标元素 + 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; + } + // 否则就继续迭代 + boolean isSequential = isSequentialReachable(sourceFlowElement, target, visitedElements); + if (!isSequential) { + return false; + } + } + } + return true; + } + + protected 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; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java new file mode 100644 index 00000000..a937b6ce --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java @@ -0,0 +1,42 @@ +package com.yanzhu.flowable.listener; + +import com.yanzhu.common.redis.service.RedisService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Flowable全局监听器 + * + * @author JiangYuQi + */ +@Slf4j +@Component +public class GlobalEventListener extends AbstractFlowableEngineEventListener { + + @Autowired + private RedisService redisService; + + @Autowired + private TaskService taskService; + + @Autowired + private RuntimeService runtimeService; + + @Autowired + private RepositoryService repositoryService; + + @Override + protected void processCompleted(FlowableEngineEntityEvent event) { + log.info("任务流程审批完成...{}",event.getProcessInstanceId()); + super.processCompleted(event); + } + +} + + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java new file mode 100644 index 00000000..1e6f1f7d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java @@ -0,0 +1,56 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.FlowTaskEntity; + +import java.util.List; +import java.util.Map; + +/** + * @author JiangYuQi + * @date 2021-04-03 14:41 + */ +public interface IFlowBusinessKeyService { + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + public Map quueryCount(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + public List> selectCommentByProcInsId(String procInsId); + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + public Map selectFormDatasByProcInsId(String procInsId); + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity); + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java new file mode 100644 index 00000000..80258211 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java @@ -0,0 +1,81 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:41 + */ +public interface IFlowDefinitionService { + + boolean exist(String processDefinitionKey); + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + TableDataInfo list(FlowQueryVo flowQueryVo); + + /** + * 导入流程文件 + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name + * @param category + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in + */ + void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in); + + /** + * 读取xml + * @param deployId + * @return + */ + AjaxResult readXml(String deployId) throws IOException; + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程ID + * @param applyInfos 申请表单 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map applyInfos); + + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + void updateState(Integer state, String deployId); + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + void delete(String deployId); + + + /** + * 读取图片文件 + * @param deployId + * @return + */ + InputStream readImage(String deployId); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java new file mode 100644 index 00000000..acc69698 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java @@ -0,0 +1,54 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.engine.history.HistoricProcessInstance; + +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:40 + */ +public interface IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + void stopProcessInstance(FlowTaskVo vo); + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + void updateState(Integer state, String instanceId); + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + void delete(String instanceId, String deleteReason); + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId); + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map variables); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java new file mode 100644 index 00000000..c23dd4ec --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java @@ -0,0 +1,208 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.task.api.Task; + +import java.io.InputStream; + +/** + * @author Tony + * @date 2021-04-03 14:42 + */ +public interface IFlowTaskService { + + /** + * 审批任务 + * + * @param task 请求实体参数 + */ + AjaxResult complete(FlowTaskVo task); + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + void taskReject(FlowTaskVo flowTaskVo); + + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + void taskReturn(FlowTaskVo flowTaskVo); + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo); + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + void deleteTask(FlowTaskVo flowTaskVo); + + /** + * 认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void claim(FlowTaskVo flowTaskVo); + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void unClaim(FlowTaskVo flowTaskVo); + + /** + * 委派任务 + * + * @param flowTaskVo 请求实体参数 + */ + void delegateTask(FlowTaskVo flowTaskVo); + + /** + * 任务归还 + * + * @param flowTaskVo 请求实体参数 + */ + void resolveTask(FlowTaskVo flowTaskVo); + + + /** + * 转办任务 + * + * @param flowTaskVo 请求实体参数 + */ + void assignTask(FlowTaskVo flowTaskVo); + + + /** + * 多实例加签 + * @param flowTaskVo + */ + void addMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 多实例减签 + * @param flowTaskVo + */ + void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 我发起的流程 + * @param queryVo 请求参数 + * @return + */ + AjaxResult myProcess(FlowQueryVo queryVo); + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * @param flowTaskVo + * @return + */ + AjaxResult stopProcess(FlowTaskVo flowTaskVo); + + /** + * 撤回流程 + * @param flowTaskVo + * @return + */ + AjaxResult revokeProcess(FlowTaskVo flowTaskVo); + + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult todoList(FlowQueryVo queryVo); + + + /** + * 已办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult finishedList(FlowQueryVo queryVo); + + /** + * 流程历史流转记录 + * + * @param procInsId 流程实例Id + * @return + */ + AjaxResult flowRecord(String procInsId,String deployId); + + /** + * 根据任务ID查询挂载的表单信息 + * + * @param taskId 任务Id + * @return + */ + Task getTaskForm(String taskId); + + /** + * 获取流程过程图 + * @param processId + * @return + */ + InputStream diagram(String processId); + + /** + * 获取流程执行节点 + * @param procInsId + * @return + */ + AjaxResult getFlowViewer(String procInsId,String executionId); + + /** + * 获取流程变量 + * @param taskId + * @return + */ + AjaxResult processVariables(String taskId); + + /** + * 获取下一节点 + * @param flowTaskVo 任务 + * @return + */ + AjaxResult getNextFlowNode(FlowTaskVo flowTaskVo); + + AjaxResult getNextFlowNodeByStart(FlowTaskVo flowTaskVo); + + /** + * 流程初始化表单 + * @param deployId + * @return + */ + AjaxResult flowFormData(String deployId); + + /** + * 流程节点信息 + * @param procInsId + * @return + */ + AjaxResult flowXmlAndNode(String procInsId,String deployId); + + /** + * 流程节点表单 + * @param taskId 流程任务编号 + * @return + */ + AjaxResult flowTaskForm(String taskId) throws Exception; +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java new file mode 100644 index 00000000..01ff72bc --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程实例关联表单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +public interface ISysDeployFormService +{ + /** + * 查询流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 流程实例关联表单 + */ + public SysDeployForm selectSysDeployFormById(Long id); + + /** + * 查询流程实例关联表单列表 + * + * @param sysDeployForm 流程实例关联表单 + * @return 流程实例关联表单集合 + */ + public List selectSysDeployFormList(SysDeployForm sysDeployForm); + + /** + * 新增流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int insertSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 修改流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int updateSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 批量删除流程实例关联表单 + * + * @param ids 需要删除的流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormByIds(Long[] ids); + + /** + * 删除流程实例关联表单信息 + * + * @param id 流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormById(Long id); + + /** + * 查询流程挂着的表单 + * @param deployId + * @return + */ + SysForm selectSysDeployFormByDeployId(String deployId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java new file mode 100644 index 00000000..4f230788 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysExpression; + +import java.util.List; + +/** + * 流程达式Service接口 + * + * @author yanZhu + * @date 2022-12-12 + */ +public interface ISysExpressionService +{ + /** + * 查询流程达式 + * + * @param id 流程达式主键 + * @return 流程达式 + */ + public SysExpression selectSysExpressionById(Long id); + + /** + * 查询流程达式列表 + * + * @param sysExpression 流程达式 + * @return 流程达式集合 + */ + public List selectSysExpressionList(SysExpression sysExpression); + + /** + * 新增流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int insertSysExpression(SysExpression sysExpression); + + /** + * 修改流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int updateSysExpression(SysExpression sysExpression); + + /** + * 批量删除流程达式 + * + * @param ids 需要删除的流程达式主键集合 + * @return 结果 + */ + public int deleteSysExpressionByIds(Long[] ids); + + /** + * 删除流程达式信息 + * + * @param id 流程达式主键 + * @return 结果 + */ + public int deleteSysExpressionById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java new file mode 100644 index 00000000..98a844ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java @@ -0,0 +1,61 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 表单 + * @author Tony + * @date 2021-04-03 + */ +public interface ISysFormService +{ + /** + * 查询流程表单 + * + * @param formId 流程表单ID + * @return 流程表单 + */ + public SysForm selectSysFormById(Long formId); + + /** + * 查询流程表单列表 + * + * @param sysForm 流程表单 + * @return 流程表单集合 + */ + public List selectSysFormList(SysForm sysForm); + + /** + * 新增流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int insertSysForm(SysForm sysForm); + + /** + * 修改流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int updateSysForm(SysForm sysForm); + + /** + * 批量删除流程表单 + * + * @param formIds 需要删除的流程表单ID + * @return 结果 + */ + public int deleteSysFormByIds(Long[] formIds); + + /** + * 删除流程表单信息 + * + * @param formId 流程表单ID + * @return 结果 + */ + public int deleteSysFormById(Long formId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java new file mode 100644 index 00000000..c269e24b --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysTaskForm; + +import java.util.List; + +/** + * 流程任务关联单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +@Deprecated +public interface ISysTaskFormService { + /** + * 查询流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 流程任务关联单 + */ + public SysTaskForm selectSysTaskFormById(Long id); + + /** + * 查询流程任务关联单列表 + * + * @param sysTaskForm 流程任务关联单 + * @return 流程任务关联单集合 + */ + public List selectSysTaskFormList(SysTaskForm sysTaskForm); + + /** + * 新增流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int insertSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 修改流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int updateSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 批量删除流程任务关联单 + * + * @param ids 需要删除的流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormByIds(Long[] ids); + + /** + * 删除流程任务关联单信息 + * + * @param id 流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java new file mode 100644 index 00000000..f5150cbb --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java @@ -0,0 +1,102 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.flowable.domain.FlowTaskEntity; +import com.yanzhu.flowable.mapper.FlowBusinessKeyMapper; +import com.yanzhu.flowable.service.IFlowBusinessKeyService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * 业务工作流程自定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowBusinessKeyServiceImpl implements IFlowBusinessKeyService { + + @Resource + private FlowBusinessKeyMapper flowBusinessKeyMapper; + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity); + } + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public Map quueryCount(FlowTaskEntity flowTaskEntity) { + flowTaskEntity.setActiveName("await"); + int awaitSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + flowTaskEntity.setActiveName("finished"); + int finishedSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + Map dataMap = new HashMap<>(); + dataMap.put("await",awaitSize); + dataMap.put("finished",finishedSize); + return dataMap; + } + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + @Override + public List> selectCommentByProcInsId(String procInsId) { + return flowBusinessKeyMapper.selectCommentByProcInsId(procInsId); + } + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + @Override + public Map selectFormDatasByProcInsId(String procInsId) { + Map dataMap = new HashMap<>(); + List> list = flowBusinessKeyMapper.selectFormDatasByProcInsId(procInsId); + Iterator> it = list.iterator(); + while (it.hasNext()) { + Map map = it.next(); + dataMap.put(Convert.toStr(map.get("name")), map.get("text")); + } + return dataMap; + } + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity){ + return flowBusinessKeyMapper.selectMyAwaitFlowTask(flowTaskEntity); + } + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectMyFinishedFlowTask(flowTaskEntity); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java new file mode 100644 index 00000000..0d9f656f --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java @@ -0,0 +1,279 @@ +package com.yanzhu.flowable.service.impl; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.yanzhu.common.core.constant.HttpStatus; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.ServiceException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.SysExpression; +import com.yanzhu.flowable.domain.vo.FlowDeptVo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.mapper.FlowDeployMapper; +import com.yanzhu.flowable.service.IFlowDefinitionService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.repository.ProcessDefinitionQuery; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; +import org.flowable.task.api.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 流程定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFlowDefinitionService { + + @Autowired + private RedisService redisService; + + @Resource + private FlowDeployMapper flowDeployMapper; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private ISysDeployFormService sysDeployFormService; + + private static final String BPMN_FILE_SUFFIX = ".bpmn"; + + @Override + public boolean exist(String processDefinitionKey) { + ProcessDefinitionQuery processDefinitionQuery + = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey); + long count = processDefinitionQuery.count(); + return count > 0 ? true : false; + } + + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + @Override + public TableDataInfo list(FlowQueryVo flowQueryVo) { + TableDataInfo rspData = new TableDataInfo(); + PageHelper.startPage(flowQueryVo.getPageNum(), flowQueryVo.getPageSize()); + List list = flowDeployMapper.selectDeployList(flowQueryVo); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + + /** + * 导入流程文件 + * + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name 流程名称 + * @param category 流程类型 + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in 流程文件流 + */ + @Override + @Transactional + public void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in) { + if(SecurityUtils.isAdmin(SecurityUtils.getUserId())){ + throw new ServiceException("请选择项目后导入流程文件..."); + } + Deployment deploy = repositoryService.createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).category(category).deploy(); + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), category); + /**新增流程单位关系**/ + FlowDeptVo flowDeptVo = new FlowDeptVo(); + flowDeptVo.setProcdefId(definition.getId()); + //超管不能导入流程文件 + if(SecurityUtils.isGSAdmin()){ + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setDeptId(Convert.toLong(OwnerDeptId)); + } + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setProjId(Convert.toLong(OwnerProjectId)); + } + }else{ + // 从用户信息中获取项目单位信息... + flowDeptVo.setPrtype(prtype); + flowDeptVo.setProjId(SecurityUtils.getLoginUser().getProjectId()); + flowDeptVo.setDeptId(SecurityUtils.getLoginUser().getProjectDeptId()); + } + if(flowDeptVo.getDeptId()!=null){ + flowDeployMapper.insertActReProcdefDept(flowDeptVo); + } + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public AjaxResult readXml(String deployId) throws IOException { + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName()); + String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name()); + return AjaxResult.success("", result); + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public InputStream readImage(String deployId) { + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + //获得图片流 + DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + //输出为图片 + return diagramGenerator.generateDiagram( + bpmnModel, + "png", + Collections.emptyList(), + Collections.emptyList(), + "宋体", + "宋体", + "宋体", + null, + 1.0, + false); + + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程模板ID + * @param applyInfos 流程表单 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map applyInfos) { + try { + /**保存流程表单**/ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+procDefId); + R res; + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsGroupEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsUsersEntity(applyInfos, SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + if(StringUtils.isNotNull(res.getData())){ + Map variables = new HashMap<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId) + .latestVersion().singleResult(); + if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) { + return AjaxResult.error("流程已被挂起,请联系管理员激活流程"); + } + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + String userId = Convert.toStr(sysUser.getUserId()); + // 设置流程发起人Id到流程中 + identityService.setAuthenticatedUserId(userId); + variables.put(ProcessConstants.PROCESS_INITIATOR, userId); + variables.put("businessKey", res.getData()); + ProcessInstance processInstance; + processInstance = runtimeService.startProcessInstanceById(procDefId, Convert.toStr(res.getData()),variables); + // 给第一步申请人节点设置任务执行人和意见 + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult(); + if (Objects.nonNull(task)) { + taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), sysUser.getNickName() + "发起流程申请"); + taskService.setAssignee(task.getId(), Convert.toStr(userId)); + taskService.complete(task.getId(), variables); + } + return AjaxResult.success("流程启动成功"); + }else{ + return AjaxResult.error("流程表单保存失败"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + @Override + public void updateState(Integer state, String deployId) { + ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + // 激活 + if (state == 1) { + repositoryService.activateProcessDefinitionById(procDef.getId(), true, null); + } + // 挂起 + if (state == 2) { + repositoryService.suspendProcessDefinitionById(procDef.getId(), true, null); + } + } + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + @Override + public void delete(String deployId) { + // true 允许级联删除 ,不设置会导致数据库外键关联异常 + repositoryService.deleteDeployment(deployId, true); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java new file mode 100644 index 00000000..4facc05d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java @@ -0,0 +1,117 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.service.IFlowInstanceService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.engine.history.HistoricProcessInstance; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Map; +import java.util.Objects; + +/** + * 工作流流程实例管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowInstanceServiceImpl extends FlowServiceFactory implements IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + @Override + public void stopProcessInstance(FlowTaskVo vo) { + String taskId = vo.getTaskId(); + + } + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + @Override + public void updateState(Integer state, String instanceId) { + + // 激活 + if (state == 1) { + runtimeService.activateProcessInstanceById(instanceId); + } + // 挂起 + if (state == 2) { + runtimeService.suspendProcessInstanceById(instanceId); + } + } + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(String instanceId, String deleteReason) { + + // 查询历史数据 + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(instanceId); + if (historicProcessInstance.getEndTime() != null) { + historyService.deleteHistoricProcessInstance(historicProcessInstance.getId()); + return; + } + // 删除流程实例 + runtimeService.deleteProcessInstance(instanceId, deleteReason); + // 删除历史流程实例 + historyService.deleteHistoricProcessInstance(instanceId); + } + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + @Override + public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) { + HistoricProcessInstance historicProcessInstance = + historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + if (Objects.isNull(historicProcessInstance)) { + throw new FlowableObjectNotFoundException("流程实例不存在: " + processInstanceId); + } + return historicProcessInstance; + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map variables) { + + try { + // 设置流程发起人Id到流程中 + Long userId = SecurityUtils.getUserId(); + variables.put("initiator",userId); + variables.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true); + runtimeService.startProcessInstanceById(procDefId, variables); + return AjaxResult.success("流程启动成功"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java new file mode 100644 index 00000000..50208812 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java @@ -0,0 +1,1307 @@ +package com.yanzhu.flowable.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.CustomException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.*; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.flow.CustomProcessDiagramGenerator; +import com.yanzhu.flowable.flow.FindNextNodeUtil; +import com.yanzhu.flowable.flow.FlowableUtils; +import com.yanzhu.flowable.flow.ModelUtils; +import com.yanzhu.flowable.service.IFlowTaskService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.RemoteUserService; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.impl.cmd.AddMultiInstanceExecutionCmd; +import org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.task.Comment; +import org.flowable.identitylink.api.history.HistoricIdentityLink; +import org.flowable.image.ProcessDiagramGenerator; +import org.flowable.task.api.DelegationState; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * @author Tony + * @date 2021-04-03 + **/ +@Service +@Slf4j +public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTaskService { + + @Autowired + private RedisService redisService; + + @Resource + private ISysFormService sysFormService; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private RemoteUserService remoteUserService; + + @Resource + private RemoteRoleService remoteRoleService; + + @Resource + private ISysDeployFormService sysInstanceFormService; + + /** + * 完成任务 + * + * @param taskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public AjaxResult complete(FlowTaskVo taskVo) { + Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult(); + /**表单不为空时,重新修改表单**/ + if(StringUtils.isNotNull(taskVo.getApplyInfos())){ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+task.getProcessDefinitionId()); + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsGroupEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsUsersEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + } + if (Objects.isNull(task)) { + return AjaxResult.error("任务不存在"); + } + if (DelegationState.PENDING.equals(task.getDelegationState())) { + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment()); + taskService.resolveTask(taskVo.getTaskId()); + } else { + Long userId = SecurityUtils.getUserId(); + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment()); + taskService.setAssignee(taskVo.getTaskId(), Convert.toStr(userId)); + taskService.complete(taskVo.getTaskId()); + } + return AjaxResult.success(); + } + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + @Override + public void taskReject(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态!"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + // 获取节点信息 + source = flowElement; + } + } + } + + // 目的获取所有跳转到的节点 targetIds + // 获取当前节点的所有父级用户任务节点 + // 深度优先算法思想:延边迭代深入 + List parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null); + if (parentUserTaskList == null || parentUserTaskList.size() == 0) { + throw new CustomException("当前节点为初始任务节点,不能驳回"); + } + // 获取活动 ID 即节点 Key + List parentUserTaskKeyList = new ArrayList<>(); + parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId())); + // 获取全部历史节点活动实例,即已经走过的节点历史,数据采用开始时间升序 + List historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list(); + // 数据清洗,将回滚导致的脏数据清洗掉 + List lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList); + // 此时历史任务实例为倒序,获取最后走的节点 + List targetIds = new ArrayList<>(); + // 循环结束标识,遇到当前目标节点的次数 + int number = 0; + StringBuilder parentHistoricTaskKey = new StringBuilder(); + for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) { + // 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过 + if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) { + continue; + } + parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey); + if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) { + number++; + } + // 在数据清洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次 + // 在流程中如果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环 + // number == 1,第一次遇到当前节点 + // number == 2,第二次遇到,代表最后一次的循环范围 + if (number == 2) { + break; + } + // 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点 + if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) { + targetIds.add(historicTaskInstanceKey); + } + } + + + // 目的获取所有需要被跳转的节点 currentIds + // 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路 + UserTask oneUserTask = parentUserTaskList.get(0); + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需驳回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + + // 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况 + if (targetIds.size() > 1 && currentIds.size() > 1) { + throw new CustomException("任务出现多对多情况,无法撤回"); + } + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置驳回意见 + currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment())); + + try { + // 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况 + if (targetIds.size() > 1) { + // 1 对 多任务跳转,currentIds 当前节点(1),targetIds 跳转到的节点(多) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()). + moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState(); + } + // 如果父级任务只有一个,因此当前任务可能为网关中的任务 + if (targetIds.size() == 1) { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetIds.get(0) 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState(); + } + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + + } + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void taskReturn(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + // 获取跳转的节点元素 + FlowElement target = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 当前任务节点元素 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = flowElement; + } + // 跳转的节点元素 + if (flowElement.getId().equals(flowTaskVo.getTargetKey())) { + target = flowElement; + } + } + } + + // 从当前节点向前扫描 + // 如果存在路线上不存在目标节点,说明目标节点是在网关上或非同一路线上,不可跳转 + // 否则目标节点相对于当前节点,属于串行 + Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, flowTaskVo.getTargetKey(), null, null); + if (!isSequential) { + throw new CustomException("当前节点相对于目标节点,不属于串行关系,无法回退"); + } + + + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需退回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置回退意见 + currentTaskIds.forEach(currentTaskId -> taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), flowTaskVo.getComment())); + + try { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, flowTaskVo.getTargetKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + } + + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + @Override + public AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo) { +// // 当前任务 task +// Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); +// // 从流程历史任务中获取可退回节点 +//// List hisActIns = historyService.createHistoricActivityInstanceQuery() +//// .executionId(task.getExecutionId()) +//// .activityType("userTask") +//// .orderByHistoricActivityInstanceStartTime() +//// .finished() +//// .desc() +//// .list(); +//// +//// // 可回退的节点列表 +//// List returnTaskNodeList = new ArrayList<>(); +//// ReturnTaskNodeVo returnTaskNodeVo; +//// for (HistoricActivityInstance activityInstance : hisActIns) { +//// returnTaskNodeVo = new ReturnTaskNodeVo(); +//// returnTaskNodeVo.setId(activityInstance.getActivityId()); +//// // 根据流程节点处理时间校验改节点是否已完成 +//// returnTaskNodeVo.setName(activityInstance.getActivityName()); +//// returnTaskNodeList.add(returnTaskNodeVo); +//// } +// List userTaskList = new ArrayList<>(); +// // 获取流程定义信息 +// ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); +// // 获取所有节点信息,暂不考虑子流程情况 +// Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); +// Collection flowElements = process.getFlowElements(); +// // 获取当前任务节点元素 +// UserTask source = null; +// if (flowElements != null) { +// for (FlowElement flowElement : flowElements) { +// // 类型为用户节点 +// if (flowElement.getId().equals(task.getTaskDefinitionKey())) { +// source = (UserTask) flowElement; +// } +// } +// } +// // 获取节点的所有路线 +// List> roads = FlowableUtils.findRoad(source, null, null, null); +// +// for (List road : roads) { +// if (userTaskList.size() == 0) { +// // 还没有可回退节点直接添加 +// userTaskList = road; +// } else { +// // 如果已有回退节点,则比对取交集部分 +// userTaskList.retainAll(road); +// } +// } +// return AjaxResult.success(userTaskList); + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息,暂不考虑子流程情况 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + Collection flowElements = process.getFlowElements(); + // 获取当前任务节点元素 + UserTask source = null; + if (flowElements != null) { + for (FlowElement flowElement : flowElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = (UserTask) flowElement; + } + } + } + // 获取节点的所有路线 + List> roads = FlowableUtils.findRoad(source, null, null, null); + // 可回退的节点列表 + List userTaskList = new ArrayList<>(); + for (List road : roads) { + if (userTaskList.size() == 0) { + // 还没有可回退节点直接添加 + userTaskList = road; + } else { + // 如果已有回退节点,则比对取交集部分 + userTaskList.retainAll(road); + } + } + return AjaxResult.success(userTaskList); + } + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + public void deleteTask(FlowTaskVo flowTaskVo) { + // todo 待确认删除任务是物理删除任务 还是逻辑删除,让这个任务直接通过? + taskService.deleteTask(flowTaskVo.getTaskId(), flowTaskVo.getComment()); + } + + /** + * 认领/签收任务 + * 认领以后,这个用户就会成为任务的执行人,任务会从其他成员的任务列表中消失 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void claim(FlowTaskVo flowTaskVo) { + taskService.claim(flowTaskVo.getTaskId(), flowTaskVo.getUserId()); + } + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void unClaim(FlowTaskVo flowTaskVo) { + taskService.unclaim(flowTaskVo.getTaskId()); + } + + /** + * 委派任务 + * 任务委派只是委派人将当前的任务交给被委派人进行审批,处理任务后又重新回到委派人身上。 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.DELEGATE.getType(), flowTaskVo.getComment()); + taskService.delegateTask(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + } + + /** + * 任务归还 + * 被委派人完成任务之后,将任务归还委派人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void resolveTask(FlowTaskVo flowTaskVo) { + taskService.resolveTask(flowTaskVo.getTaskId()); + } + + + /** + * 转办任务 + * 直接将办理人换成别人,这时任务的拥有者不再是转办人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void assignTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.ASSIGN.getType(), flowTaskVo.getComment()); + // 直接转派就可以覆盖掉之前的 + taskService.setAssignee(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); +// // 删除指派人重新指派 +// taskService.deleteCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// taskService.addCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// // 如果要查询转给他人处理的任务,可以同时将OWNER进行设置: +// taskService.setOwner(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + + } + + /** + * 多实例加签 + * act_ru_task、act_ru_identitylink各生成一条记录 + * + * @param flowTaskVo + */ + @Override + public void addMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new AddMultiInstanceExecutionCmd(flowTaskVo.getDefId(), flowTaskVo.getInstanceId(), flowTaskVo.getVariables())); + } + + /** + * 多实例减签 + * act_ru_task减1、act_ru_identitylink不变 + * + * @param flowTaskVo + */ + @Override + public void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new DeleteMultiInstanceExecutionCmd(flowTaskVo.getCurrentChildExecutionId(), flowTaskVo.getFlag())); + } + + /** + * 我发起的流程 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult myProcess(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + Long userId = SecurityUtils.getUserId(); + HistoricProcessInstanceQuery historicProcessInstanceQuery; + //超管查询所有数据据 + if(SecurityUtils.isAdmin(userId)){ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .orderByProcessInstanceStartTime() + .desc(); + }else{ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .startedBy(userId.toString()) + .orderByProcessInstanceStartTime() + .desc(); + } + List historicProcessInstances = historicProcessInstanceQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + page.setTotal(historicProcessInstanceQuery.count()); + List flowList = new ArrayList<>(); + for (HistoricProcessInstance hisIns : historicProcessInstances) { + FlowTaskDto flowTask = new FlowTaskDto(); + flowTask.setCreateTime(hisIns.getStartTime()); + flowTask.setFinishTime(hisIns.getEndTime()); + flowTask.setProcInsId(hisIns.getId()); + + // 计算耗时 + if (Objects.nonNull(hisIns.getEndTime())) { + long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } else { + long time = System.currentTimeMillis() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } + // 流程定义信息 + ProcessDefinition pd = repositoryService.createProcessDefinitionQuery() + .processDefinitionId(hisIns.getProcessDefinitionId()) + .singleResult(); + flowTask.setDeployId(pd.getDeploymentId()); + flowTask.setProcDefName(pd.getName()); + flowTask.setProcDefVersion(pd.getVersion()); + flowTask.setCategory(pd.getCategory()); + flowTask.setProcDefVersion(pd.getVersion()); + // 当前所处流程 + List taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list(); + if (CollectionUtils.isNotEmpty(taskList)) { + flowTask.setTaskId(taskList.get(0).getId()); + flowTask.setTaskName(taskList.get(0).getName()); + if (StringUtils.isNotBlank(taskList.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(taskList.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } else { + List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list(); + flowTask.setTaskId(historicTaskInstance.get(0).getId()); + flowTask.setTaskName(historicTaskInstance.get(0).getName()); + if (StringUtils.isNotBlank(historicTaskInstance.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(historicTaskInstance.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } + flowList.add(flowTask); + } + page.setRows(flowList); + return AjaxResult.success(page); + } + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult stopProcess(FlowTaskVo flowTaskVo) { + List task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).list(); + if (CollectionUtils.isEmpty(task)) { + throw new CustomException("流程未启动或已执行完成,取消申请失败"); + } + // 获取当前流程实例 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(flowTaskVo.getInstanceId()) + .singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); + if (Objects.nonNull(bpmnModel)) { + Process process = bpmnModel.getMainProcess(); + List endNodes = process.findFlowElementsOfType(EndEvent.class, false); + if (CollectionUtils.isNotEmpty(endNodes)) { + String userIdStr = Convert.toStr(SecurityUtils.getUserId()); + if(StringUtils.isNotEmpty(userIdStr)){ + Authentication.setAuthenticatedUserId(userIdStr); + taskService.addComment(task.get(0).getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(), + StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment()); + // 设置流程经办人为当前登录人员 + taskService.setAssignee(task.get(0).getId(), userIdStr); + // 获取当前流程最后一个节点 + String endId = endNodes.get(0).getId(); + List executions = runtimeService.createExecutionQuery() + .parentId(processInstance.getProcessInstanceId()).list(); + List executionIds = new ArrayList<>(); + executions.forEach(execution -> executionIds.add(execution.getId())); + // 变更流程为已结束状态 + runtimeService.createChangeActivityStateBuilder() + .moveExecutionsToSingleActivityId(executionIds, endId).changeState(); + return AjaxResult.success(); + } + } + } + return AjaxResult.error(); + } + + /** + * 撤回流程 目前存在错误 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult revokeProcess(FlowTaskVo flowTaskVo) { + String procInsId = flowTaskVo.getInstanceId(); + String taskId = flowTaskVo.getTaskId(); + // 校验流程是否结束 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(procInsId) + .active() + .singleResult(); + if(Objects.isNull(processInstance)) { + throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作"); + } + // 获取待撤回任务实例 + HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .taskAssignee(flowTaskVo.getUserId()) + .singleResult(); + if (Objects.isNull(currTaskIns)) { + throw new RuntimeException("当前任务不存在,无法执行撤回操作"); + } + // 获取 bpmn 模型 + BpmnModel bpmnModel = repositoryService.getBpmnModel(currTaskIns.getProcessDefinitionId()); + UserTask currUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currTaskIns.getTaskDefinitionKey()); + // 查找下一级用户任务列表 + List nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask); + List nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList()); + + // 获取当前节点之后已完成的流程历史节点 + List finishedTaskInsList = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(procInsId) + .taskCreatedAfter(currTaskIns.getEndTime()) + .finished() + .list(); + for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) { + // 检查已完成流程历史节点是否存在下一级中 + if (StringUtils.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) { + throw new RuntimeException("下一流程已处理,无法执行撤回操作"); + } + } + // 获取所有激活的任务节点,找到需要撤回的任务 + List activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list(); + List revokeExecutionIds = new ArrayList<>(); + for (Task task : activateTaskList) { + // 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点 + if (StringUtils.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) { + // 添加撤回审批信息 + taskService.setAssignee(task.getId(), flowTaskVo.getUserId()); + taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), flowTaskVo.getAssignee() + "撤回流程审批"); + revokeExecutionIds.add(task.getExecutionId()); + } + } + try { + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(procInsId) + .moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new RuntimeException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new RuntimeException("执行撤回操作失败"); + } + return AjaxResult.success(); + } + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult todoList(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + // 只查看自己的数据 + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + TaskQuery taskQuery = taskService.createTaskQuery() + .active() + .includeProcessVariables() + .taskCandidateGroupIn(sysUser.getRoles().stream().map(role -> role.getRoleId().toString()).collect(Collectors.toList())) + .taskCandidateOrAssigned(sysUser.getUserId().toString()) + .orderByTaskCreateTime().desc(); + + page.setTotal(taskQuery.count()); + List taskList = taskQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + List
工作流任务管理
+ * + * @author Tony + * @date 2021-04-03 + */ +@Slf4j +@Api(tags = "工作流流程任务管理") +@RestController +@RequestMapping("/mapper/flowable/task") +public class FlowTaskController { + + @Autowired + private IFlowTaskService flowTaskService; + + @ApiOperation(value = "我发起的流程", response = FlowTaskDto.class) + @GetMapping(value = "/myProcess") + public AjaxResult myProcess(FlowQueryVo queryVo) { + return flowTaskService.myProcess(queryVo); + } + + @ApiOperation(value = "取消申请", response = FlowTaskDto.class) + @Log(title = "终止申请", businessType = BusinessType.UPDATE) + @PostMapping(value = "/stopProcess") + public AjaxResult stopProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.stopProcess(flowTaskVo); + } + + @ApiOperation(value = "撤回流程", response = FlowTaskDto.class) + @Log(title = "撤回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/revokeProcess") + public AjaxResult revokeProcess(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.revokeProcess(flowTaskVo); + } + + @ApiOperation(value = "获取待办列表", response = FlowTaskDto.class) + @GetMapping(value = "/todoList") + public AjaxResult todoList(FlowQueryVo queryVo) { + return flowTaskService.todoList(queryVo); + } + + @ApiOperation(value = "获取已办任务", response = FlowTaskDto.class) + @GetMapping(value = "/finishedList") + public AjaxResult finishedList(FlowQueryVo queryVo) { + return flowTaskService.finishedList(queryVo); + } + + + @ApiOperation(value = "流程历史流转记录", response = FlowTaskDto.class) + @GetMapping(value = "/flowRecord") + public AjaxResult flowRecord(String procInsId, String deployId) { + return flowTaskService.flowRecord(procInsId, deployId); + } + + + @ApiOperation(value = "流程初始化表单", response = FlowTaskDto.class) + @GetMapping(value = "/flowFormData") + public AjaxResult flowFormData(String deployId) { + return flowTaskService.flowFormData(deployId); + } + + @ApiOperation(value = "获取流程变量", response = FlowTaskDto.class) + @GetMapping(value = "/processVariables/{taskId}") + public AjaxResult processVariables(@ApiParam(value = "流程任务Id") @PathVariable(value = "taskId") String taskId) { + return flowTaskService.processVariables(taskId); + } + + @ApiOperation(value = "审批任务") + @Log(title = "审批流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/complete") + public AjaxResult complete(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "重新提交流程") + @Log(title = "重新提交流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/completeAndModify") + public AjaxResult completeAndModify(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.complete(flowTaskVo); + } + + @ApiOperation(value = "驳回任务") + @Log(title = "驳回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/reject") + public AjaxResult taskReject(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReject(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "退回任务") + @Log(title = "退回流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/return") + public AjaxResult taskReturn(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.taskReturn(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "获取所有可回退的节点") + @PostMapping(value = "/returnList") + public AjaxResult findReturnTaskList(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.findReturnTaskList(flowTaskVo); + } + + @ApiOperation(value = "删除任务") + @Log(title = "删除流程", businessType = BusinessType.DELETE) + @DeleteMapping(value = "/delete") + public AjaxResult delete(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "认领/签收任务") + @Log(title = "认领/签收流程", businessType = BusinessType.INSERT) + @PostMapping(value = "/claim") + public AjaxResult claim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.claim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "取消认领/签收任务") + @Log(title = "取消认领/签收流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/unClaim") + public AjaxResult unClaim(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.unClaim(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "委派任务") + @Log(title = "委派流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/delegateTask") + public AjaxResult delegate(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.delegateTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "任务归还") + @Log(title = "归还流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/resolveTask") + public AjaxResult resolveTask(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.resolveTask(flowTaskVo); + return AjaxResult.success(); + } + + @ApiOperation(value = "转办任务") + @Log(title = "转办流程", businessType = BusinessType.UPDATE) + @PostMapping(value = "/assignTask") + public AjaxResult assign(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.assignTask(flowTaskVo); + return AjaxResult.success(); + } + + @PostMapping(value = "/addMultiInstanceExecution") + @ApiOperation(value = "多实例加签") + public AjaxResult addMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.addMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("加签成功"); + } + + @PostMapping(value = "/deleteMultiInstanceExecution") + @ApiOperation(value = "多实例减签") + public AjaxResult deleteMultiInstanceExecution(@RequestBody FlowTaskVo flowTaskVo) { + flowTaskService.deleteMultiInstanceExecution(flowTaskVo); + return AjaxResult.success("减签成功"); + } + + @ApiOperation(value = "获取下一节点") + @PostMapping(value = "/nextFlowNode") + public AjaxResult getNextFlowNode(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNode(flowTaskVo); + } + + @ApiOperation(value = "流程发起时获取下一节点") + @PostMapping(value = "/nextFlowNodeByStart") + public AjaxResult getNextFlowNodeByStart(@RequestBody FlowTaskVo flowTaskVo) { + return flowTaskService.getNextFlowNodeByStart(flowTaskVo); + } + + /** + * 生成流程图 + * + * @param processId 任务ID + */ + @GetMapping("/diagram/{processId}") + public void genProcessDiagram(HttpServletResponse response, + @PathVariable("processId") String processId) { + InputStream inputStream = flowTaskService.diagram(processId); + OutputStream os = null; + BufferedImage image = null; + try { + image = ImageIO.read(inputStream); + response.setContentType("image/png"); + os = response.getOutputStream(); + if (image != null) { + ImageIO.write(image, "png", os); + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try { + if (os != null) { + os.flush(); + os.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + /** + * 获取流程执行节点 + * + * @param procInsId 流程实例编号 + * @param procInsId 任务执行编号 + */ + @GetMapping("/flowViewer/{procInsId}/{executionId}") + public AjaxResult getFlowViewer(@PathVariable("procInsId") String procInsId, + @PathVariable("executionId") String executionId) { + return flowTaskService.getFlowViewer(procInsId, executionId); + } + + /** + * 流程节点信息 + * + * @param procInsId 流程实例id + * @return + */ + @GetMapping("/flowXmlAndNode") + public AjaxResult flowXmlAndNode(@RequestParam(value = "procInsId", required = false) String procInsId, + @RequestParam(value = "deployId", required = false) String deployId) { + return flowTaskService.flowXmlAndNode(procInsId, deployId); + } + + /** + * 流程节点表单 + * + * @param taskId 流程任务编号 + * @return + */ + @GetMapping("/flowTaskForm") + public AjaxResult flowTaskForm(@RequestParam(value = "taskId", required = false) String taskId) throws Exception { + return flowTaskService.flowTaskForm(taskId); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java new file mode 100644 index 00000000..74cbad80 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/controller/SysFormController.java @@ -0,0 +1,112 @@ +package com.yanzhu.flowable.controller; + +import com.yanzhu.common.core.utils.poi.ExcelUtil; +import com.yanzhu.common.core.web.controller.BaseController; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.log.annotation.Log; +import com.yanzhu.common.log.enums.BusinessType; +import com.yanzhu.common.security.annotation.RequiresPermissions; +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletResponse; +import java.util.List; + +/** + * 流程表单Controller + * + * @author Tony + * @date 2021-04-03 + */ +@RestController +@RequestMapping("/mapper/flowable/form") +public class SysFormController extends BaseController { + + @Autowired + private ISysFormService SysFormService; + + @Autowired + private ISysDeployFormService sysDeployFormService; + + /** + * 查询流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:list')") + @GetMapping("/list") + public TableDataInfo list(SysForm sysForm) { + startPage(); + List list = SysFormService.selectSysFormList(sysForm); + return getDataTable(list); + } + + @GetMapping("/formList") + public AjaxResult formList(SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + return AjaxResult.success(list); + } + /** + * 导出流程表单列表 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:export')") + @Log(title = "流程表单", businessType = BusinessType.EXPORT) + @GetMapping("/export") + public void export(HttpServletResponse response, SysForm sysForm) { + List list = SysFormService.selectSysFormList(sysForm); + ExcelUtil util = new ExcelUtil(SysForm.class); + util.exportExcel(response, list, "form"); + } + + /** + * 获取流程表单详细信息 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:query')") + @GetMapping(value = "/{formId}") + public AjaxResult getInfo(@PathVariable("formId") Long formId) { + return AjaxResult.success(SysFormService.selectSysFormById(formId)); + } + + /** + * 新增流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:add')") + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping + public AjaxResult add(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.insertSysForm(sysForm)); + } + + /** + * 修改流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:edit')") + @Log(title = "流程表单", businessType = BusinessType.UPDATE) + @PutMapping + public AjaxResult edit(@RequestBody SysForm sysForm) { + return toAjax(SysFormService.updateSysForm(sysForm)); + } + + /** + * 删除流程表单 + */ + @RequiresPermissions("@ss.hasPermi('flowable:form:remove')") + @Log(title = "流程表单", businessType = BusinessType.DELETE) + @DeleteMapping("/{formIds}") + public AjaxResult remove(@PathVariable Long[] formIds) { + return toAjax(SysFormService.deleteSysFormByIds(formIds)); + } + + + /** + * 挂载流程表单 + */ + @Log(title = "流程表单", businessType = BusinessType.INSERT) + @PostMapping("/addDeployForm") + public AjaxResult addDeployForm(@RequestBody SysDeployForm sysDeployForm) { + return toAjax(sysDeployFormService.insertSysDeployForm(sysDeployForm)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java new file mode 100644 index 00000000..e66efd70 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/factory/FlowServiceFactory.java @@ -0,0 +1,41 @@ +package com.yanzhu.flowable.factory; + +import lombok.Getter; +import org.flowable.engine.*; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + +/** + * flowable 引擎注入封装 + * @author Tony + * @date 2021-04-03 + */ +@Component +@Getter +public class FlowServiceFactory { + + @Resource + protected RepositoryService repositoryService; + + @Resource + protected RuntimeService runtimeService; + + @Resource + protected IdentityService identityService; + + @Resource + protected TaskService taskService; + + @Resource + protected HistoryService historyService; + + @Resource + protected ManagementService managementService; + + @Qualifier("processEngine") + @Resource + protected ProcessEngine processEngine; + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java new file mode 100644 index 00000000..7a0baca5 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramCanvas.java @@ -0,0 +1,370 @@ +package com.yanzhu.flowable.flow; + +import org.flowable.bpmn.model.AssociationDirection; +import org.flowable.bpmn.model.GraphicInfo; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.util.ReflectUtil; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.font.LineBreakMeasurer; +import java.awt.font.TextAttribute; +import java.awt.font.TextLayout; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Rectangle2D; +import java.awt.geom.RoundRectangle2D; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.text.AttributedCharacterIterator; +import java.text.AttributedString; + +/** + * @author Tony + * @date 2021/4/4 23:58 + */ +public class CustomProcessDiagramCanvas extends DefaultProcessDiagramCanvas { + //定义走过流程连线颜色为绿色 + protected static Color HIGHLIGHT_SequenceFlow_COLOR = Color.GREEN; + //设置未走过流程的连接线颜色 + protected static Color CONNECTION_COLOR = Color.BLACK; + //设置flows连接线字体颜色red + protected static Color LABEL_COLOR = new Color(0, 0, 0); + //高亮显示task框颜色 + protected static Color HIGHLIGHT_COLOR = Color.GREEN; + protected static Color HIGHLIGHT_COLOR1 = Color.RED; + + public CustomProcessDiagramCanvas(int width, int height, int minX, int minY, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + super(width, height, minX, minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + this.initialize(imageType); + } + + /** + * 重写绘制连线的方式,设置绘制颜色 + * @param xPoints + * @param yPoints + * @param conditional + * @param isDefault + * @param connectionType + * @param associationDirection + * @param highLighted + * @param scaleFactor + */ + @Override + public void drawConnection(int[] xPoints, int[] yPoints, boolean conditional, boolean isDefault, String connectionType, AssociationDirection associationDirection, boolean highLighted, double scaleFactor) { + Paint originalPaint = this.g.getPaint(); + Stroke originalStroke = this.g.getStroke(); + this.g.setPaint(CONNECTION_COLOR); + if (connectionType.equals("association")) { + this.g.setStroke(ASSOCIATION_STROKE); + } else if (highLighted) { + this.g.setPaint(HIGHLIGHT_SequenceFlow_COLOR); + this.g.setStroke(HIGHLIGHT_FLOW_STROKE); + } + + for (int i = 1; i < xPoints.length; ++i) { + Integer sourceX = xPoints[i - 1]; + Integer sourceY = yPoints[i - 1]; + Integer targetX = xPoints[i]; + Integer targetY = yPoints[i]; + java.awt.geom.Line2D.Double line = new java.awt.geom.Line2D.Double((double) sourceX, (double) sourceY, (double) targetX, (double) targetY); + this.g.draw(line); + } + + java.awt.geom.Line2D.Double line; + if (isDefault) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawDefaultSequenceFlowIndicator(line, scaleFactor); + } + + if (conditional) { + line = new java.awt.geom.Line2D.Double((double) xPoints[0], (double) yPoints[0], (double) xPoints[1], (double) yPoints[1]); + this.drawConditionalSequenceFlowIndicator(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.ONE) || associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[xPoints.length - 2], (double) yPoints[xPoints.length - 2], (double) xPoints[xPoints.length - 1], (double) yPoints[xPoints.length - 1]); + this.drawArrowHead(line, scaleFactor); + } + + if (associationDirection.equals(AssociationDirection.BOTH)) { + line = new java.awt.geom.Line2D.Double((double) xPoints[1], (double) yPoints[1], (double) xPoints[0], (double) yPoints[0]); + this.drawArrowHead(line, scaleFactor); + } + + this.g.setPaint(originalPaint); + this.g.setStroke(originalStroke); + } + + /** + * 设置字体大小图标颜色 + * @param imageType + */ + @Override + public void initialize(String imageType) { + if ("png".equalsIgnoreCase(imageType)) { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 2); + } else { + this.processDiagram = new BufferedImage(this.canvasWidth, this.canvasHeight, 1); + } + + this.g = this.processDiagram.createGraphics(); + if (!"png".equalsIgnoreCase(imageType)) { + this.g.setBackground(new Color(255, 255, 255, 0)); + this.g.clearRect(0, 0, this.canvasWidth, this.canvasHeight); + } + + this.g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + //修改图标颜色,修改图标字体大小 + this.g.setPaint(Color.black); + Font font = new Font(this.activityFontName, 10, 14); + this.g.setFont(font); + this.fontMetrics = this.g.getFontMetrics(); + //修改连接线字体大小 + LABEL_FONT = new Font(this.labelFontName, 10, 15); + ANNOTATION_FONT = new Font(this.annotationFontName, 0, 11); + + try { + USERTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/userTask.png", this.customClassLoader)); + SCRIPTTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/scriptTask.png", this.customClassLoader)); + SERVICETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/serviceTask.png", this.customClassLoader)); + RECEIVETASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/receiveTask.png", this.customClassLoader)); + SENDTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/sendTask.png", this.customClassLoader)); + MANUALTASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/manualTask.png", this.customClassLoader)); + BUSINESS_RULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/businessRuleTask.png", this.customClassLoader)); + SHELL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/shellTask.png", this.customClassLoader)); + DMN_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/dmnTask.png", this.customClassLoader)); + CAMEL_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/camelTask.png", this.customClassLoader)); + MULE_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/muleTask.png", this.customClassLoader)); + HTTP_TASK_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/httpTask.png", this.customClassLoader)); + TIMER_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/timer.png", this.customClassLoader)); + COMPENSATE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate-throw.png", this.customClassLoader)); + COMPENSATE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/compensate.png", this.customClassLoader)); + ERROR_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error-throw.png", this.customClassLoader)); + ERROR_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/error.png", this.customClassLoader)); + MESSAGE_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message-throw.png", this.customClassLoader)); + MESSAGE_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/message.png", this.customClassLoader)); + SIGNAL_THROW_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal-throw.png", this.customClassLoader)); + SIGNAL_CATCH_IMAGE = ImageIO.read(ReflectUtil.getResource("org/flowable/icons/signal.png", this.customClassLoader)); + } catch (IOException var4) { + LOGGER.warn("Could not load image for process diagram creation: {}", var4.getMessage()); + } + + } + + /** + * 设置连接线字体 + * @param text + * @param graphicInfo + * @param centered + */ + @Override + public void drawLabel(String text, GraphicInfo graphicInfo, boolean centered) { + float interline = 1.0f; + + // text + if (text != null && text.length() > 0) { + Paint originalPaint = g.getPaint(); + Font originalFont = g.getFont(); + + g.setPaint(LABEL_COLOR); + g.setFont(LABEL_FONT); + + int wrapWidth = 100; + int textY = (int) graphicInfo.getY(); + + // TODO: use drawMultilineText() + AttributedString as = new AttributedString(text); + as.addAttribute(TextAttribute.FOREGROUND, g.getPaint()); + as.addAttribute(TextAttribute.FONT, g.getFont()); + AttributedCharacterIterator aci = as.getIterator(); + FontRenderContext frc = new FontRenderContext(null, true, false); + LineBreakMeasurer lbm = new LineBreakMeasurer(aci, frc); + + while (lbm.getPosition() < text.length()) { + TextLayout tl = lbm.nextLayout(wrapWidth); + textY += tl.getAscent(); + + Rectangle2D bb = tl.getBounds(); + double tX = graphicInfo.getX(); + + if (centered) { + tX += (int) (graphicInfo.getWidth() / 2 - bb.getWidth() / 2); + } + tl.draw(g, (float) tX, textY); + textY += tl.getDescent() + tl.getLeading() + (interline - 1.0f) * tl.getAscent(); + } + + // restore originals + g.setFont(originalFont); + g.setPaint(originalPaint); + } + } + + /** + * 高亮显示task框完成的 + * @param x + * @param y + * @param width + * @param height + */ + @Override + public void drawHighLight(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义task框当前的位置 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightNow(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR1); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * 自定义结束节点 + * @param x + * @param y + * @param width + * @param height + */ + public void drawHighLightEnd(int x, int y, int width, int height) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + + g.setPaint(HIGHLIGHT_COLOR); + g.setStroke(THICK_TASK_BORDER_STROKE); + + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, 20, 20); + g.draw(rect); + + g.setPaint(originalPaint); + g.setStroke(originalStroke); + } + + /** + * task框自定义文字 + * @param name + * @param graphicInfo + * @param thickBorder + * @param scaleFactor + */ + @Override + protected void drawTask(String name, GraphicInfo graphicInfo, boolean thickBorder, double scaleFactor) { + + Paint originalPaint = g.getPaint(); + int x = (int) graphicInfo.getX(); + int y = (int) graphicInfo.getY(); + int width = (int) graphicInfo.getWidth(); + int height = (int) graphicInfo.getHeight(); + + // Create a new gradient paint for every task box, gradient depends on x and y and is not relative + g.setPaint(TASK_BOX_COLOR); + + int arcR = 6; + if (thickBorder) { + arcR = 3; + } + + // shape + RoundRectangle2D rect = new RoundRectangle2D.Double(x, y, width, height, arcR, arcR); + g.fill(rect); + g.setPaint(TASK_BORDER_COLOR); + + if (thickBorder) { + Stroke originalStroke = g.getStroke(); + g.setStroke(THICK_TASK_BORDER_STROKE); + g.draw(rect); + g.setStroke(originalStroke); + } else { + g.draw(rect); + } + + g.setPaint(originalPaint); + // text + if (scaleFactor == 1.0 && name != null && name.length() > 0) { + int boxWidth = width - (2 * TEXT_PADDING); + int boxHeight = height - 16 - ICON_PADDING - ICON_PADDING - MARKER_WIDTH - 2 - 2; + int boxX = x + width / 2 - boxWidth / 2; + int boxY = y + height / 2 - boxHeight / 2 + ICON_PADDING + ICON_PADDING - 2 - 2; + + drawMultilineCentredText(name, boxX, boxY, boxWidth, boxHeight); + } + } + + protected static Color EVENT_COLOR = new Color(255, 255, 255); + + /** + * 重写开始事件 + * @param graphicInfo + * @param image + * @param scaleFactor + */ + @Override + public void drawStartEvent(GraphicInfo graphicInfo, BufferedImage image, double scaleFactor) { + Paint originalPaint = g.getPaint(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); + g.draw(circle); + g.setPaint(originalPaint); + if (image != null) { + // calculate coordinates to center image + int imageX = (int) Math.round(graphicInfo.getX() + (graphicInfo.getWidth() / 2) - (image.getWidth() / (2 * scaleFactor))); + int imageY = (int) Math.round(graphicInfo.getY() + (graphicInfo.getHeight() / 2) - (image.getHeight() / (2 * scaleFactor))); + g.drawImage(image, imageX, imageY, + (int) (image.getWidth() / scaleFactor), (int) (image.getHeight() / scaleFactor), null); + } + + } + + /** + * 重写结束事件 + * @param graphicInfo + * @param scaleFactor + */ + @Override + public void drawNoneEndEvent(GraphicInfo graphicInfo, double scaleFactor) { + Paint originalPaint = g.getPaint(); + Stroke originalStroke = g.getStroke(); + g.setPaint(EVENT_COLOR); + Ellipse2D circle = new Ellipse2D.Double(graphicInfo.getX(), graphicInfo.getY(), + graphicInfo.getWidth(), graphicInfo.getHeight()); + g.fill(circle); + g.setPaint(EVENT_BORDER_COLOR); +// g.setPaint(HIGHLIGHT_COLOR); + if (scaleFactor == 1.0) { + g.setStroke(END_EVENT_STROKE); + } else { + g.setStroke(new BasicStroke(2.0f)); + } + g.draw(circle); + g.setStroke(originalStroke); + g.setPaint(originalPaint); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java new file mode 100644 index 00000000..d55e30dd --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/CustomProcessDiagramGenerator.java @@ -0,0 +1,404 @@ +package com.yanzhu.flowable.flow; + + +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.image.impl.DefaultProcessDiagramCanvas; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; + +import java.util.Iterator; +import java.util.List; + +/** + * @author Tony + * @date 2021/4/5 0:31 + */ +public class CustomProcessDiagramGenerator extends DefaultProcessDiagramGenerator { + @Override + protected DefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType, List highLightedActivities, List highLightedFlows, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) { + this.prepareBpmnModel(bpmnModel); + DefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + Iterator var13 = bpmnModel.getPools().iterator(); + + while (var13.hasNext()) { + Pool process = (Pool) var13.next(); + GraphicInfo subProcesses = bpmnModel.getGraphicInfo(process.getId()); + processDiagramCanvas.drawPoolOrLane(process.getName(), subProcesses, scaleFactor); + } + + var13 = bpmnModel.getProcesses().iterator(); + + Process process1; + Iterator subProcesses1; + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.getLanes().iterator(); + + while (subProcesses1.hasNext()) { + Lane artifact = (Lane) subProcesses1.next(); + GraphicInfo subProcess = bpmnModel.getGraphicInfo(artifact.getId()); + processDiagramCanvas.drawPoolOrLane(artifact.getName(), subProcess, scaleFactor); + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + while (var13.hasNext()) { + process1 = (Process) var13.next(); + subProcesses1 = process1.findFlowElementsOfType(FlowNode.class).iterator(); + + while (subProcesses1.hasNext()) { + FlowNode artifact1 = (FlowNode) subProcesses1.next(); + if (!this.isPartOfCollapsedSubProcess(artifact1, bpmnModel)) { + this.drawActivity(processDiagramCanvas, bpmnModel, artifact1, highLightedActivities, highLightedFlows, scaleFactor, Boolean.valueOf(drawSequenceFlowNameWithNoLabelDI)); + } + } + } + + var13 = bpmnModel.getProcesses().iterator(); + + label75: + while (true) { + List subProcesses2; + do { + if (!var13.hasNext()) { + return processDiagramCanvas; + } + + process1 = (Process) var13.next(); + subProcesses1 = process1.getArtifacts().iterator(); + + while (subProcesses1.hasNext()) { + Artifact artifact2 = (Artifact) subProcesses1.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, artifact2); + } + + subProcesses2 = process1.findFlowElementsOfType(SubProcess.class, true); + } while (subProcesses2 == null); + + Iterator artifact3 = subProcesses2.iterator(); + + while (true) { + GraphicInfo graphicInfo; + SubProcess subProcess1; + do { + do { + if (!artifact3.hasNext()) { + continue label75; + } + + subProcess1 = (SubProcess) artifact3.next(); + graphicInfo = bpmnModel.getGraphicInfo(subProcess1.getId()); + } while (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded().booleanValue()); + } while (this.isPartOfCollapsedSubProcess(subProcess1, bpmnModel)); + + Iterator var19 = subProcess1.getArtifacts().iterator(); + + while (var19.hasNext()) { + Artifact subProcessArtifact = (Artifact) var19.next(); + this.drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact); + } + } + } + } + + protected static DefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType, String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) { + double minX = 1.7976931348623157E308D; + double maxX = 0.0D; + double minY = 1.7976931348623157E308D; + double maxY = 0.0D; + + GraphicInfo nrOfLanes; + for (Iterator flowNodes = bpmnModel.getPools().iterator(); flowNodes.hasNext(); maxY = nrOfLanes.getY() + nrOfLanes.getHeight()) { + Pool artifacts = (Pool) flowNodes.next(); + nrOfLanes = bpmnModel.getGraphicInfo(artifacts.getId()); + minX = nrOfLanes.getX(); + maxX = nrOfLanes.getX() + nrOfLanes.getWidth(); + minY = nrOfLanes.getY(); + } + + List var23 = gatherAllFlowNodes(bpmnModel); + Iterator var24 = var23.iterator(); + + label155: + while (var24.hasNext()) { + FlowNode var26 = (FlowNode) var24.next(); + GraphicInfo artifact = bpmnModel.getGraphicInfo(var26.getId()); + if (artifact.getX() + artifact.getWidth() > maxX) { + maxX = artifact.getX() + artifact.getWidth(); + } + + if (artifact.getX() < minX) { + minX = artifact.getX(); + } + + if (artifact.getY() + artifact.getHeight() > maxY) { + maxY = artifact.getY() + artifact.getHeight(); + } + + if (artifact.getY() < minY) { + minY = artifact.getY(); + } + + Iterator process = var26.getOutgoingFlows().iterator(); + + while (true) { + List l; + do { + if (!process.hasNext()) { + continue label155; + } + + SequenceFlow graphicInfoList = (SequenceFlow) process.next(); + l = bpmnModel.getFlowLocationGraphicInfo(graphicInfoList.getId()); + } while (l == null); + + Iterator graphicInfo = l.iterator(); + + while (graphicInfo.hasNext()) { + GraphicInfo graphicInfo1 = (GraphicInfo) graphicInfo.next(); + if (graphicInfo1.getX() > maxX) { + maxX = graphicInfo1.getX(); + } + + if (graphicInfo1.getX() < minX) { + minX = graphicInfo1.getX(); + } + + if (graphicInfo1.getY() > maxY) { + maxY = graphicInfo1.getY(); + } + + if (graphicInfo1.getY() < minY) { + minY = graphicInfo1.getY(); + } + } + } + } + + List var25 = gatherAllArtifacts(bpmnModel); + Iterator var27 = var25.iterator(); + + GraphicInfo var37; + while (var27.hasNext()) { + Artifact var29 = (Artifact) var27.next(); + GraphicInfo var31 = bpmnModel.getGraphicInfo(var29.getId()); + if (var31 != null) { + if (var31.getX() + var31.getWidth() > maxX) { + maxX = var31.getX() + var31.getWidth(); + } + + if (var31.getX() < minX) { + minX = var31.getX(); + } + + if (var31.getY() + var31.getHeight() > maxY) { + maxY = var31.getY() + var31.getHeight(); + } + + if (var31.getY() < minY) { + minY = var31.getY(); + } + } + + List var33 = bpmnModel.getFlowLocationGraphicInfo(var29.getId()); + if (var33 != null) { + Iterator var35 = var33.iterator(); + + while (var35.hasNext()) { + var37 = (GraphicInfo) var35.next(); + if (var37.getX() > maxX) { + maxX = var37.getX(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() > maxY) { + maxY = var37.getY(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + } + + int var28 = 0; + Iterator var30 = bpmnModel.getProcesses().iterator(); + + while (var30.hasNext()) { + Process var32 = (Process) var30.next(); + Iterator var34 = var32.getLanes().iterator(); + + while (var34.hasNext()) { + Lane var36 = (Lane) var34.next(); + ++var28; + var37 = bpmnModel.getGraphicInfo(var36.getId()); + if (var37.getX() + var37.getWidth() > maxX) { + maxX = var37.getX() + var37.getWidth(); + } + + if (var37.getX() < minX) { + minX = var37.getX(); + } + + if (var37.getY() + var37.getHeight() > maxY) { + maxY = var37.getY() + var37.getHeight(); + } + + if (var37.getY() < minY) { + minY = var37.getY(); + } + } + } + + if (var23.isEmpty() && bpmnModel.getPools().isEmpty() && var28 == 0) { + minX = 0.0D; + minY = 0.0D; + } + + return new CustomProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader); + } + + + private static void drawHighLight(DefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightNow(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightNow((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + private static void drawHighLightEnd(CustomProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) { + processDiagramCanvas.drawHighLightEnd((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight()); + + } + + @Override + protected void drawActivity(DefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, + FlowNode flowNode, List highLightedActivities, List highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) { + + DefaultProcessDiagramGenerator.ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass()); + if (drawInstruction != null) { + + drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode); + + // Gather info on the multi instance marker + boolean multiInstanceSequential = false; + boolean multiInstanceParallel = false; + boolean collapsed = false; + if (flowNode instanceof Activity) { + Activity activity = (Activity) flowNode; + MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics(); + if (multiInstanceLoopCharacteristics != null) { + multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential(); + multiInstanceParallel = !multiInstanceSequential; + } + } + + // Gather info on the collapsed marker + GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId()); + if (flowNode instanceof SubProcess) { + collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded(); + } else if (flowNode instanceof CallActivity) { + collapsed = true; + } + + if (scaleFactor == 1.0) { + // Actually draw the markers + processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(), + multiInstanceSequential, multiInstanceParallel, collapsed); + } + + // Draw highlighted activities + if (highLightedActivities.contains(flowNode.getId())) { + + if (highLightedActivities.get(highLightedActivities.size() - 1).equals(flowNode.getId()) + && !"endenv".equals(flowNode.getId())) { + if ((flowNode.getId().contains("Event_"))) { + drawHighLightEnd((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } else { + drawHighLightNow((CustomProcessDiagramCanvas) processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + } else { + drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId())); + } + + + } + + } + + // Outgoing transitions of activity + for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) { + boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId())); + String defaultFlow = null; + if (flowNode instanceof Activity) { + defaultFlow = ((Activity) flowNode).getDefaultFlow(); + } else if (flowNode instanceof Gateway) { + defaultFlow = ((Gateway) flowNode).getDefaultFlow(); + } + + boolean isDefault = false; + if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) { + isDefault = true; + } + boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && !(flowNode instanceof Gateway); + + String sourceRef = sequenceFlow.getSourceRef(); + String targetRef = sequenceFlow.getTargetRef(); + FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef); + FlowElement targetElement = bpmnModel.getFlowElement(targetRef); + List graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId()); + if (graphicInfoList != null && graphicInfoList.size() > 0) { + graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList); + int xPoints[] = new int[graphicInfoList.size()]; + int yPoints[] = new int[graphicInfoList.size()]; + + for (int i = 1; i < graphicInfoList.size(); i++) { + GraphicInfo graphicInfo = graphicInfoList.get(i); + GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1); + + if (i == 1) { + xPoints[0] = (int) previousGraphicInfo.getX(); + yPoints[0] = (int) previousGraphicInfo.getY(); + } + xPoints[i] = (int) graphicInfo.getX(); + yPoints[i] = (int) graphicInfo.getY(); + + } + + processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor); + + + // Draw sequenceflow label + GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId()); + if (labelGraphicInfo != null) { + processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false); + } else { + if (drawSequenceFlowNameWithNoLabelDI) { + GraphicInfo lineCenter = getLineCenter(graphicInfoList); + processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false); + } + + } + } + } + + // Nested elements + if (flowNode instanceof FlowElementsContainer) { + for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) { + if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) { + drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement, + highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI); + } + } + } + } +} + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java new file mode 100644 index 00000000..a4bd2442 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FindNextNodeUtil.java @@ -0,0 +1,257 @@ +package com.yanzhu.flowable.flow; + +import com.googlecode.aviator.AviatorEvaluator; +import com.googlecode.aviator.Expression; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.repository.ProcessDefinition; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author Tony + * @date 2021/4/19 20:51 + */ +public class FindNextNodeUtil { + + /** + * 获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasks(RepositoryService repositoryService, org.flowable.task.api.Task task, Map map) { + List data = new ArrayList<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = task.getTaskDefinitionKey(); + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + /** + * 启动流程时获取下一步骤的用户任务 + * + * @param repositoryService + * @param map + * @return + */ + public static List getNextUserTasksByStart(RepositoryService repositoryService, ProcessDefinition processDefinition, Map map) { + List data = new ArrayList<>(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + Process mainProcess = bpmnModel.getMainProcess(); + Collection flowElements = mainProcess.getFlowElements(); + String key = null; + // 找到开始节点 并获取唯一key + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + key = flowElement.getId(); + break; + } + } + FlowElement flowElement = bpmnModel.getFlowElement(key); + next(flowElements, flowElement, map, data); + return data; + } + + + + /** + * 查找下一节点 + * + * @param flowElements + * @param flowElement + * @param map + * @param nextUser + */ + public static void next(Collection flowElements, FlowElement flowElement, Map map, List nextUser) { + //如果是结束节点 + if (flowElement instanceof EndEvent) { + //如果是子任务的结束节点 + if (getSubProcess(flowElements, flowElement) != null) { + flowElement = getSubProcess(flowElements, flowElement); + } + } + //获取Task的出线信息--可以拥有多个 + List outGoingFlows = null; + if (flowElement instanceof Task) { + outGoingFlows = ((Task) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof Gateway) { + outGoingFlows = ((Gateway) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof StartEvent) { + outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof SubProcess) { + outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows(); + } else if (flowElement instanceof CallActivity) { + outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows(); + } + if (outGoingFlows != null && outGoingFlows.size() > 0) { + //遍历所有的出线--找到可以正确执行的那一条 + for (SequenceFlow sequenceFlow : outGoingFlows) { + //1.有表达式,且为true + //2.无表达式 + String expression = sequenceFlow.getConditionExpression(); + if (expression == null || + expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) { + //出线的下一节点 + String nextFlowElementID = sequenceFlow.getTargetRef(); + if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) { + continue; + } + + //查询下一节点的信息 + FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements); + //调用流程 + if (nextFlowElement instanceof CallActivity) { + CallActivity ca = (CallActivity) nextFlowElement; + if (ca.getLoopCharacteristics() != null) { + UserTask userTask = new UserTask(); + userTask.setId(ca.getId()); + + userTask.setId(ca.getId()); + userTask.setLoopCharacteristics(ca.getLoopCharacteristics()); + userTask.setName(ca.getName()); + nextUser.add(userTask); + } + next(flowElements, nextFlowElement, map, nextUser); + } + //用户任务 + if (nextFlowElement instanceof UserTask) { + nextUser.add((UserTask) nextFlowElement); + } + //排他网关 + else if (nextFlowElement instanceof ExclusiveGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //并行网关 + else if (nextFlowElement instanceof ParallelGateway) { + next(flowElements, nextFlowElement, map, nextUser); + } + //接收任务 + else if (nextFlowElement instanceof ReceiveTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //服务任务 + else if (nextFlowElement instanceof ServiceTask) { + next(flowElements, nextFlowElement, map, nextUser); + } + //子任务的起点 + else if (nextFlowElement instanceof StartEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + //结束节点 + else if (nextFlowElement instanceof EndEvent) { + next(flowElements, nextFlowElement, map, nextUser); + } + } + } + } + } + + /** + * 判断是否是多实例子流程并且需要设置集合类型变量 + */ + public static boolean checkSubProcess(String Id, Collection flowElements, List nextUser) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess && flowElement1.getId().equals(Id)) { + + SubProcess sp = (SubProcess) flowElement1; + if (sp.getLoopCharacteristics() != null) { + String inputDataItem = sp.getLoopCharacteristics().getInputDataItem(); + UserTask userTask = new UserTask(); + userTask.setId(sp.getId()); + userTask.setLoopCharacteristics(sp.getLoopCharacteristics()); + userTask.setName(sp.getName()); + nextUser.add(userTask); + return true; + } + } + } + + return false; + + } + + /** + * 查询一个节点的是否子任务中的节点,如果是,返回子任务 + * + * @param flowElements 全流程的节点集合 + * @param flowElement 当前节点 + * @return + */ + public static FlowElement getSubProcess(Collection flowElements, FlowElement flowElement) { + for (FlowElement flowElement1 : flowElements) { + if (flowElement1 instanceof SubProcess) { + for (FlowElement flowElement2 : ((SubProcess) flowElement1).getFlowElements()) { + if (flowElement.equals(flowElement2)) { + return flowElement1; + } + } + } + } + return null; + } + + + /** + * 根据ID查询流程节点对象, 如果是子任务,则返回子任务的开始节点 + * + * @param Id 节点ID + * @param flowElements 流程节点集合 + * @return + */ + public static FlowElement getFlowElementById(String Id, Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement.getId().equals(Id)) { + //如果是子任务,则查询出子任务的开始节点 + if (flowElement instanceof SubProcess) { + return getStartFlowElement(((SubProcess) flowElement).getFlowElements()); + } + return flowElement; + } + if (flowElement instanceof SubProcess) { + FlowElement flowElement1 = getFlowElementById(Id, ((SubProcess) flowElement).getFlowElements()); + if (flowElement1 != null) { + return flowElement1; + } + } + } + return null; + } + + /** + * 返回流程的开始节点 + * + * @param flowElements 节点集合 + * @description: + */ + public static FlowElement getStartFlowElement(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return flowElement; + } + } + return null; + } + + /** + * 校验el表达式 + * + * @param map + * @param expression + * @return + */ + public static boolean expressionResult(Map map, String expression) { + Expression exp = AviatorEvaluator.compile(expression); + final Object execute = exp.execute(map); + return Boolean.parseBoolean(String.valueOf(execute)); + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java new file mode 100644 index 00000000..530cf038 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/FlowableUtils.java @@ -0,0 +1,589 @@ +package com.yanzhu.flowable.flow; + +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.*; +import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; +import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; +import org.flowable.task.api.history.HistoricTaskInstance; + +import java.util.*; + +/** + * @author Tony + * @date 2021-04-03 23:57 + */ +@Slf4j +public class FlowableUtils { + + /** + * 根据节点,获取入口连线 + * @param source + * @return + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getIncomingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getIncomingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getIncomingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getIncomingFlows(); + } + return sequenceFlows; + } + + /** + * 根据节点,获取出口连线 + * @param source + * @return + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getOutgoingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getOutgoingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getOutgoingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取全部节点列表,包含子流程节点 + * @param flowElements + * @param allElements + * @return + */ + public static Collection getAllElements(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + + for (FlowElement flowElement : flowElements) { + allElements.add(flowElement); + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 迭代获取父级任务节点列表,向前找 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 已找到的用户任务节点 + * @return + */ + public static List iteratorFindParentUserTasks(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 = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 类型为用户节点,则新增父级节点 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); + continue; + } + // 类型为子流程,则添加子流程开始节点出口处相连的节点 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + // 获取子流程用户任务节点 + List childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 + * @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 EndEvent && source.getSubProcess() != null) { + userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + 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 (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 迭代获取子流程用户任务节点 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return + */ + public static List findChildProcessUserTasks(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) { + // 循环找到目标元素 + 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 = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); + } + } + return userTaskList; + } + + /** + * 从后向前寻路,获取所有脏线路上的点 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 目标脏线路终点 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set iteratorFindDirtyRoads(FlowElement source, List passRoads, Set hasSequenceFlow, List targets, Set dirtyRoads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 新增经过的路线 + passRoads.add(sequenceFlow.getSourceFlowElement().getId()); + // 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线 + if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) { + dirtyRoads.addAll(passRoads); + continue; + } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, dirtyRoads); + // 是否存在子流程上,true 是,false 否 + Boolean isInChildProcess = dirtyTargetInChildProcess((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, targets, null); + if (isInChildProcess) { + // 已在子流程上找到,该路线结束 + continue; + } + } + // 继续迭代 + dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, targets, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 迭代获取子流程脏路线 + * 说明,假如回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 + * @return + */ + public static Set findChildProcessAllDirtyRoad(FlowElement source, Set hasSequenceFlow, Set dirtyRoads) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加脏路线 + dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId()); + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, dirtyRoads); + } + // 继续迭代 + dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, dirtyRoads); + } + } + return dirtyRoads; + } + + /** + * 判断脏路线结束节点是否在子流程上 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止 + * @param inChildProcess 是否存在子流程上,true 是,false 否 + * @return + */ + public static Boolean dirtyTargetInChildProcess(FlowElement source, Set hasSequenceFlow, List targets, Boolean inChildProcess) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + inChildProcess = inChildProcess != null && inChildProcess; + + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + + if (sequenceFlows != null && !inChildProcess) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果发现目标点在子流程上存在,说明只到子流程为止 + if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) { + inChildProcess = true; + break; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + inChildProcess = dirtyTargetInChildProcess((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, targets, inChildProcess); + } + // 继续迭代 + inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, targets, inChildProcess); + } + } + return inChildProcess; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param isSequential 是否串行 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param targetKsy 目标节点 + * @return + */ + public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, Set hasSequenceFlow, Boolean isSequential) { + isSequential = isSequential == null || isSequential; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, isSequential); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果目标节点已被判断为并行,后面都不需要执行,直接返回 + if (!isSequential) { + break; + } + // 这条线路存在目标节点,这条线路完成,进入下个线路 + if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) { + continue; + } + if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) { + isSequential = false; + break; + } + // 否则就继续迭代 + isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, hasSequenceFlow, isSequential); + } + } + return isSequential; + } + + /** + * 从后向前寻路,获取到达节点的所有路线 + * 不存在直接回退到子流程,但是存在回退到父级流程的情况 + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param roads 路线 + * @return + */ + public static List> findRoad(FlowElement source, List passRoads, Set hasSequenceFlow, List> roads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + roads = roads == null ? new ArrayList<>() : roads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads); + } + + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); + + if (sequenceFlows != null && sequenceFlows.size() != 0) { + for (SequenceFlow sequenceFlow: sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加经过路线 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + passRoads.add((UserTask) sequenceFlow.getSourceFlowElement()); + } + // 继续迭代 + roads = findRoad(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, roads); + } + } else { + // 添加路线 + roads.add(passRoads); + } + return roads; + } + + /** + * 历史节点数据清洗,清洗掉又回滚导致的脏数据 + * @param allElements 全部节点信息 + * @param historicTaskInstanceList 历史任务实例信息,数据采用开始时间升序 + * @return + */ + public static List historicTaskInstanceClean(Collection allElements, List historicTaskInstanceList) { + // 会签节点收集 + List multiTask = new ArrayList<>(); + allElements.forEach(flowElement -> { + if (flowElement instanceof UserTask) { + // 如果该节点的行为为会签行为,说明该节点为会签节点 + if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) { + multiTask.add(flowElement.getId()); + } + } + }); + // 循环放入栈,栈 LIFO:后进先出 + Stack stack = new Stack<>(); + historicTaskInstanceList.forEach(stack::push); + // 清洗后的历史任务实例 + List lastHistoricTaskInstanceList = new ArrayList<>(); + // 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗 + // 临时用户任务 key + StringBuilder userTaskKey = null; + // 临时被删掉的任务 key,存在并行情况 + List deleteKeyList = new ArrayList<>(); + // 临时脏数据线路 + List> dirtyDataLineList = new ArrayList<>(); + // 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到 + // 会签特殊处理下标 + int multiIndex = -1; + // 会签特殊处理 key + StringBuilder multiKey = null; + // 会签特殊处理操作标识 + boolean multiOpera = false; + while (!stack.empty()) { + // 从这里开始 userTaskKey 都还是上个栈的 key + // 是否是脏数据线路上的点 + final boolean[] isDirtyData = {false}; + for (Set oldDirtyDataLine : dirtyDataLineList) { + if (oldDirtyDataLine.contains(stack.peek().getTaskDefinitionKey())) { + isDirtyData[0] = true; + } + } + // 删除原因不为空,说明从这条数据开始回跳或者回退的 + // MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内 + if (stack.peek().getDeleteReason() != null && !"MI_END".equals(stack.peek().getDeleteReason())) { + // 可以理解为脏线路起点 + String dirtyPoint = ""; + if (stack.peek().getDeleteReason().contains("Change activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", ""); + } + // 会签回退删除原因有点不同 + if (stack.peek().getDeleteReason().contains("Change parent activity to ")) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", ""); + } + FlowElement dirtyTask = null; + // 获取变更节点的对应的入口处连线 + // 如果是网关并行回退情况,会变成两条脏数据路线,效果一样 + for (FlowElement flowElement : allElements) { + if (flowElement.getId().equals(stack.peek().getTaskDefinitionKey())) { + dirtyTask = flowElement; + } + } + // 获取脏数据线路 + Set dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, Arrays.asList(dirtyPoint.split(",")), null); + // 自己本身也是脏线路上的点,加进去 + dirtyDataLine.add(stack.peek().getTaskDefinitionKey()); + log.info(stack.peek().getTaskDefinitionKey() + "点脏路线集合:" + dirtyDataLine); + // 是全新的需要添加的脏线路 + boolean isNewDirtyData = true; + for (int i = 0; i < dirtyDataLineList.size(); i++) { + // 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回 + // 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全 + if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) { + isNewDirtyData = false; + dirtyDataLineList.get(i).addAll(dirtyDataLine); + } + } + // 已确定时全新的脏线路 + if (isNewDirtyData) { + // deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成 + // 按照逻辑,回退后立刻生成的实例记录就是回退的记录 + // 至于驳回所生成的 Key,直接从删除原因中获取,因为存在驳回到并行的情况 + deleteKeyList.add(dirtyPoint + ","); + dirtyDataLineList.add(dirtyDataLine); + } + // 添加后,现在这个点变成脏线路上的点了 + isDirtyData[0] = true; + } + // 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key + if (!isDirtyData[0]) { + lastHistoricTaskInstanceList.add(stack.peek().getTaskDefinitionKey()); + } + // 校验脏线路是否结束 + for (int i = 0; i < deleteKeyList.size(); i ++) { + // 如果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始 + if (multiKey == null && multiTask.contains(stack.peek().getTaskDefinitionKey()) + && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + multiIndex = i; + multiKey = new StringBuilder(stack.peek().getTaskDefinitionKey()); + } + // 会签脏数据处理,节点退回会签清空 + // 如果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉 + if (multiKey != null && !multiKey.toString().equals(stack.peek().getTaskDefinitionKey())) { + deleteKeyList.set(multiIndex , deleteKeyList.get(multiIndex).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + multiKey = null; + // 结束进行下校验删除 + multiOpera = true; + } + // 其他脏数据处理 + // 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息 + // 脏数据产生的新实例中是否包含这条数据 + if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { + // 删除匹配到的部分 + deleteKeyList.set(i , deleteKeyList.get(i).replace(stack.peek().getTaskDefinitionKey() + ",", "")); + } + // 如果每组中的元素都以匹配过,说明脏数据结束 + if ("".equals(deleteKeyList.get(i))) { + // 同时删除脏数据 + deleteKeyList.remove(i); + dirtyDataLineList.remove(i); + break; + } + } + // 会签数据处理需要在循环外处理,否则可能导致溢出 + // 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下 + if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) { + // 同时删除脏数据 + deleteKeyList.remove(multiIndex); + dirtyDataLineList.remove(multiIndex); + multiIndex = -1; + multiOpera = false; + } + // pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除 + // 保存新的 userTaskKey 在下个循环中使用 + userTaskKey = new StringBuilder(stack.pop().getTaskDefinitionKey()); + } + log.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList); + return lastHistoricTaskInstanceList; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java new file mode 100644 index 00000000..1a81e6ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/flow/ModelUtils.java @@ -0,0 +1,372 @@ +package com.yanzhu.flowable.flow; + +import com.yanzhu.common.core.utils.StringUtils; +import org.flowable.bpmn.converter.BpmnXMLConverter; +import org.flowable.bpmn.model.*; +import org.flowable.bpmn.model.Process; +import org.flowable.common.engine.impl.util.io.StringStreamSource; + +import java.util.*; + +/** + * @author KonBAI + * @createTime 2022/3/26 19:04 + */ +public class ModelUtils { + + private static final BpmnXMLConverter bpmnXMLConverter = new BpmnXMLConverter(); + + /** + * xml转bpmnModel对象 + * + * @param xml xml + * @return bpmnModel对象 + */ + public static BpmnModel getBpmnModel(String xml) { + return bpmnXMLConverter.convertToBpmnModel(new StringStreamSource(xml), false, false); + } + + /** + * bpmnModel转xml字符串 + * + * @deprecated 存在会丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml字符串 + */ + @Deprecated + public static String getBpmnXmlStr(BpmnModel bpmnModel) { + return StringUtils.utf8Str(getBpmnXml(bpmnModel)); + } + + /** + * bpmnModel转xml对象 + * + * @deprecated 存在丢失 bpmn 连线问题 + * @param bpmnModel bpmnModel对象 + * @return xml + */ + @Deprecated + public static byte[] getBpmnXml(BpmnModel bpmnModel) { + return bpmnXMLConverter.convertToXML(bpmnModel); + } + + /** + * 根据节点,获取入口连线 + * + * @param source 起始节点 + * @return 入口连线列表 + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getIncomingFlows(); + } + return sequenceFlows; + } + + + /** + * 根据节点,获取出口连线 + * + * @param source 起始节点 + * @return 出口连线列表 + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = new ArrayList<>(); + if (source instanceof FlowNode) { + sequenceFlows = ((FlowNode) source).getOutgoingFlows(); + } + return sequenceFlows; + } + + /** + * 获取开始节点 + * + * @param model bpmnModel对象 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(BpmnModel model) { + Process process = model.getMainProcess(); + FlowElement startElement = process.getInitialFlowElement(); + if (startElement instanceof StartEvent) { + return (StartEvent) startElement; + } + return getStartEvent(process.getFlowElements()); + } + + /** + * 获取开始节点 + * + * @param flowElements 流程元素集合 + * @return 开始节点(未找到开始节点,返回null) + */ + public static StartEvent getStartEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof StartEvent) { + return (StartEvent) flowElement; + } + } + return null; + } + + /** + * 获取结束节点 + * + * @param model bpmnModel对象 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(BpmnModel model) { + Process process = model.getMainProcess(); + return getEndEvent(process.getFlowElements()); + } + + /** + * 获取结束节点 + * + * @param flowElements 流程元素集合 + * @return 结束节点(未找到开始节点,返回null) + */ + public static EndEvent getEndEvent(Collection flowElements) { + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof EndEvent) { + return (EndEvent) flowElement; + } + } + return null; + } + + public static UserTask getUserTaskByKey(BpmnModel model, String taskKey) { + Process process = model.getMainProcess(); + FlowElement flowElement = process.getFlowElement(taskKey); + if (flowElement instanceof UserTask) { + return (UserTask) flowElement; + } + return null; + } + + /** + * 获取流程元素信息 + * + * @param model bpmnModel对象 + * @param flowElementId 元素ID + * @return 元素信息 + */ + public static FlowElement getFlowElementById(BpmnModel model, String flowElementId) { + Process process = model.getMainProcess(); + return process.getFlowElement(flowElementId); + } + + /** + * 获取元素表单Key(限开始节点和用户节点可用) + * + * @param flowElement 元素 + * @return 表单Key + */ + public static String getFormKey(FlowElement flowElement) { + if (flowElement != null) { + if (flowElement instanceof StartEvent) { + return ((StartEvent) flowElement).getFormKey(); + } else if (flowElement instanceof UserTask) { + return ((UserTask) flowElement).getFormKey(); + } + } + return null; + } + + /** + * 获取开始节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getStartEventAttributeValue(BpmnModel model, String name) { + StartEvent startEvent = getStartEvent(model); + return getElementAttributeValue(startEvent, name); + } + + /** + * 获取结束节点属性值 + * @param model bpmnModel对象 + * @param name 属性名 + * @return 属性值 + */ + public static String getEndEventAttributeValue(BpmnModel model, String name) { + EndEvent endEvent = getEndEvent(model); + return getElementAttributeValue(endEvent, name); + } + + /** + * 获取用户任务节点属性值 + * @param model bpmnModel对象 + * @param taskKey 任务Key + * @param name 属性名 + * @return 属性值 + */ + public static String getUserTaskAttributeValue(BpmnModel model, String taskKey, String name) { + UserTask userTask = getUserTaskByKey(model, taskKey); + return getElementAttributeValue(userTask, name); + } + + /** + * 获取元素属性值 + * @param baseElement 流程元素 + * @param name 属性名 + * @return 属性值 + */ + public static String getElementAttributeValue(BaseElement baseElement, String name) { + if (baseElement != null) { + List attributes = baseElement.getAttributes().get(name); + if (attributes != null && !attributes.isEmpty()) { + attributes.iterator().next().getValue(); + Iterator attrIterator = attributes.iterator(); + if(attrIterator.hasNext()) { + ExtensionAttribute attribute = attrIterator.next(); + return attribute.getValue(); + } + } + } + return null; + } + + public static boolean isMultiInstance(BpmnModel model, String taskKey) { + UserTask userTask = getUserTaskByKey(model, taskKey); + if (!Objects.isNull(userTask)) { + return userTask.hasMultiInstanceLoopCharacteristics(); + } + return false; + } + + /** + * 获取所有用户任务节点 + * + * @param model bpmnModel对象 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(BpmnModel model) { + Process process = model.getMainProcess(); + Collection flowElements = process.getFlowElements(); + return getAllUserTaskEvent(flowElements, null); + } + + /** + * 获取所有用户任务节点 + * @param flowElements 流程元素集合 + * @param allElements 所有流程元素集合 + * @return 用户任务节点列表 + */ + public static Collection getAllUserTaskEvent(Collection flowElements, Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; + for (FlowElement flowElement : flowElements) { + if (flowElement instanceof UserTask) { + allElements.add((UserTask) flowElement); + } + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = getAllUserTaskEvent(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source) { + return findNextUserTasks(source, null, null); + } + + /** + * 查找起始节点下一个用户任务列表列表 + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 + * @param userTaskList 用户任务列表 + * @return 结果 + */ + public static List findNextUserTasks(FlowElement source, Set hasSequenceFlow, List userTaskList) { + hasSequenceFlow = Optional.ofNullable(hasSequenceFlow).orElse(new HashSet<>()); + userTaskList = Optional.ofNullable(userTaskList).orElse(new ArrayList<>()); + // 获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); + if (!sequenceFlows.isEmpty()) { + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + FlowElement targetFlowElement = sequenceFlow.getTargetFlowElement(); + if (targetFlowElement instanceof UserTask) { + // 若节点为用户任务,加入到结果列表中 + userTaskList.add((UserTask) targetFlowElement); + } else { + // 若节点非用户任务,继续递归查找下一个节点 + findNextUserTasks(targetFlowElement, hasSequenceFlow, userTaskList); + } + } + } + return userTaskList; + } + + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 + * @param source 起始节点 + * @param target 目标节点 + * @param visitedElements 已经经过的连线的 ID,用于判断线路是否重复 + * @return 结果 + */ + 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 (sequenceFlows != null && sequenceFlows.size() > 0) { + // 循环找到目标元素 + 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; + } + // 否则就继续迭代 + boolean isSequential = isSequentialReachable(sourceFlowElement, target, visitedElements); + if (!isSequential) { + return false; + } + } + } + return true; + } + + protected 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; + } +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java new file mode 100644 index 00000000..a937b6ce --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/listener/GlobalEventListener.java @@ -0,0 +1,42 @@ +package com.yanzhu.flowable.listener; + +import com.yanzhu.common.redis.service.RedisService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.engine.RepositoryService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +/** + * Flowable全局监听器 + * + * @author JiangYuQi + */ +@Slf4j +@Component +public class GlobalEventListener extends AbstractFlowableEngineEventListener { + + @Autowired + private RedisService redisService; + + @Autowired + private TaskService taskService; + + @Autowired + private RuntimeService runtimeService; + + @Autowired + private RepositoryService repositoryService; + + @Override + protected void processCompleted(FlowableEngineEntityEvent event) { + log.info("任务流程审批完成...{}",event.getProcessInstanceId()); + super.processCompleted(event); + } + +} + + diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java new file mode 100644 index 00000000..1e6f1f7d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowBusinessKeyService.java @@ -0,0 +1,56 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.FlowTaskEntity; + +import java.util.List; +import java.util.Map; + +/** + * @author JiangYuQi + * @date 2021-04-03 14:41 + */ +public interface IFlowBusinessKeyService { + + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + public Map quueryCount(FlowTaskEntity flowTaskEntity); + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + public List> selectCommentByProcInsId(String procInsId); + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + public Map selectFormDatasByProcInsId(String procInsId); + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity); + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java new file mode 100644 index 00000000..80258211 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowDefinitionService.java @@ -0,0 +1,81 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:41 + */ +public interface IFlowDefinitionService { + + boolean exist(String processDefinitionKey); + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + TableDataInfo list(FlowQueryVo flowQueryVo); + + /** + * 导入流程文件 + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name + * @param category + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in + */ + void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in); + + /** + * 读取xml + * @param deployId + * @return + */ + AjaxResult readXml(String deployId) throws IOException; + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程ID + * @param applyInfos 申请表单 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map applyInfos); + + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + void updateState(Integer state, String deployId); + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + void delete(String deployId); + + + /** + * 读取图片文件 + * @param deployId + * @return + */ + InputStream readImage(String deployId); + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java new file mode 100644 index 00000000..acc69698 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowInstanceService.java @@ -0,0 +1,54 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.engine.history.HistoricProcessInstance; + +import java.util.Map; + +/** + * @author Tony + * @date 2021-04-03 14:40 + */ +public interface IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + void stopProcessInstance(FlowTaskVo vo); + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + void updateState(Integer state, String instanceId); + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + void delete(String instanceId, String deleteReason); + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId); + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + AjaxResult startProcessInstanceById(String procDefId, Map variables); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java new file mode 100644 index 00000000..c23dd4ec --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/IFlowTaskService.java @@ -0,0 +1,208 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import org.flowable.task.api.Task; + +import java.io.InputStream; + +/** + * @author Tony + * @date 2021-04-03 14:42 + */ +public interface IFlowTaskService { + + /** + * 审批任务 + * + * @param task 请求实体参数 + */ + AjaxResult complete(FlowTaskVo task); + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + void taskReject(FlowTaskVo flowTaskVo); + + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + void taskReturn(FlowTaskVo flowTaskVo); + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo); + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + void deleteTask(FlowTaskVo flowTaskVo); + + /** + * 认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void claim(FlowTaskVo flowTaskVo); + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + void unClaim(FlowTaskVo flowTaskVo); + + /** + * 委派任务 + * + * @param flowTaskVo 请求实体参数 + */ + void delegateTask(FlowTaskVo flowTaskVo); + + /** + * 任务归还 + * + * @param flowTaskVo 请求实体参数 + */ + void resolveTask(FlowTaskVo flowTaskVo); + + + /** + * 转办任务 + * + * @param flowTaskVo 请求实体参数 + */ + void assignTask(FlowTaskVo flowTaskVo); + + + /** + * 多实例加签 + * @param flowTaskVo + */ + void addMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 多实例减签 + * @param flowTaskVo + */ + void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo); + + /** + * 我发起的流程 + * @param queryVo 请求参数 + * @return + */ + AjaxResult myProcess(FlowQueryVo queryVo); + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * @param flowTaskVo + * @return + */ + AjaxResult stopProcess(FlowTaskVo flowTaskVo); + + /** + * 撤回流程 + * @param flowTaskVo + * @return + */ + AjaxResult revokeProcess(FlowTaskVo flowTaskVo); + + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult todoList(FlowQueryVo queryVo); + + + /** + * 已办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + AjaxResult finishedList(FlowQueryVo queryVo); + + /** + * 流程历史流转记录 + * + * @param procInsId 流程实例Id + * @return + */ + AjaxResult flowRecord(String procInsId,String deployId); + + /** + * 根据任务ID查询挂载的表单信息 + * + * @param taskId 任务Id + * @return + */ + Task getTaskForm(String taskId); + + /** + * 获取流程过程图 + * @param processId + * @return + */ + InputStream diagram(String processId); + + /** + * 获取流程执行节点 + * @param procInsId + * @return + */ + AjaxResult getFlowViewer(String procInsId,String executionId); + + /** + * 获取流程变量 + * @param taskId + * @return + */ + AjaxResult processVariables(String taskId); + + /** + * 获取下一节点 + * @param flowTaskVo 任务 + * @return + */ + AjaxResult getNextFlowNode(FlowTaskVo flowTaskVo); + + AjaxResult getNextFlowNodeByStart(FlowTaskVo flowTaskVo); + + /** + * 流程初始化表单 + * @param deployId + * @return + */ + AjaxResult flowFormData(String deployId); + + /** + * 流程节点信息 + * @param procInsId + * @return + */ + AjaxResult flowXmlAndNode(String procInsId,String deployId); + + /** + * 流程节点表单 + * @param taskId 流程任务编号 + * @return + */ + AjaxResult flowTaskForm(String taskId) throws Exception; +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java new file mode 100644 index 00000000..01ff72bc --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysDeployFormService.java @@ -0,0 +1,70 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysDeployForm; +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 流程实例关联表单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +public interface ISysDeployFormService +{ + /** + * 查询流程实例关联表单 + * + * @param id 流程实例关联表单ID + * @return 流程实例关联表单 + */ + public SysDeployForm selectSysDeployFormById(Long id); + + /** + * 查询流程实例关联表单列表 + * + * @param sysDeployForm 流程实例关联表单 + * @return 流程实例关联表单集合 + */ + public List selectSysDeployFormList(SysDeployForm sysDeployForm); + + /** + * 新增流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int insertSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 修改流程实例关联表单 + * + * @param sysDeployForm 流程实例关联表单 + * @return 结果 + */ + public int updateSysDeployForm(SysDeployForm sysDeployForm); + + /** + * 批量删除流程实例关联表单 + * + * @param ids 需要删除的流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormByIds(Long[] ids); + + /** + * 删除流程实例关联表单信息 + * + * @param id 流程实例关联表单ID + * @return 结果 + */ + public int deleteSysDeployFormById(Long id); + + /** + * 查询流程挂着的表单 + * @param deployId + * @return + */ + SysForm selectSysDeployFormByDeployId(String deployId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java new file mode 100644 index 00000000..4f230788 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysExpressionService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysExpression; + +import java.util.List; + +/** + * 流程达式Service接口 + * + * @author yanZhu + * @date 2022-12-12 + */ +public interface ISysExpressionService +{ + /** + * 查询流程达式 + * + * @param id 流程达式主键 + * @return 流程达式 + */ + public SysExpression selectSysExpressionById(Long id); + + /** + * 查询流程达式列表 + * + * @param sysExpression 流程达式 + * @return 流程达式集合 + */ + public List selectSysExpressionList(SysExpression sysExpression); + + /** + * 新增流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int insertSysExpression(SysExpression sysExpression); + + /** + * 修改流程达式 + * + * @param sysExpression 流程达式 + * @return 结果 + */ + public int updateSysExpression(SysExpression sysExpression); + + /** + * 批量删除流程达式 + * + * @param ids 需要删除的流程达式主键集合 + * @return 结果 + */ + public int deleteSysExpressionByIds(Long[] ids); + + /** + * 删除流程达式信息 + * + * @param id 流程达式主键 + * @return 结果 + */ + public int deleteSysExpressionById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java new file mode 100644 index 00000000..98a844ad --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysFormService.java @@ -0,0 +1,61 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysForm; + +import java.util.List; + +/** + * 表单 + * @author Tony + * @date 2021-04-03 + */ +public interface ISysFormService +{ + /** + * 查询流程表单 + * + * @param formId 流程表单ID + * @return 流程表单 + */ + public SysForm selectSysFormById(Long formId); + + /** + * 查询流程表单列表 + * + * @param sysForm 流程表单 + * @return 流程表单集合 + */ + public List selectSysFormList(SysForm sysForm); + + /** + * 新增流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int insertSysForm(SysForm sysForm); + + /** + * 修改流程表单 + * + * @param sysForm 流程表单 + * @return 结果 + */ + public int updateSysForm(SysForm sysForm); + + /** + * 批量删除流程表单 + * + * @param formIds 需要删除的流程表单ID + * @return 结果 + */ + public int deleteSysFormByIds(Long[] formIds); + + /** + * 删除流程表单信息 + * + * @param formId 流程表单ID + * @return 结果 + */ + public int deleteSysFormById(Long formId); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java new file mode 100644 index 00000000..c269e24b --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/ISysTaskFormService.java @@ -0,0 +1,62 @@ +package com.yanzhu.flowable.service; + +import com.yanzhu.flowable.domain.SysTaskForm; + +import java.util.List; + +/** + * 流程任务关联单Service接口 + * + * @author Tony + * @date 2021-04-03 + */ +@Deprecated +public interface ISysTaskFormService { + /** + * 查询流程任务关联单 + * + * @param id 流程任务关联单ID + * @return 流程任务关联单 + */ + public SysTaskForm selectSysTaskFormById(Long id); + + /** + * 查询流程任务关联单列表 + * + * @param sysTaskForm 流程任务关联单 + * @return 流程任务关联单集合 + */ + public List selectSysTaskFormList(SysTaskForm sysTaskForm); + + /** + * 新增流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int insertSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 修改流程任务关联单 + * + * @param sysTaskForm 流程任务关联单 + * @return 结果 + */ + public int updateSysTaskForm(SysTaskForm sysTaskForm); + + /** + * 批量删除流程任务关联单 + * + * @param ids 需要删除的流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormByIds(Long[] ids); + + /** + * 删除流程任务关联单信息 + * + * @param id 流程任务关联单ID + * @return 结果 + */ + public int deleteSysTaskFormById(Long id); +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java new file mode 100644 index 00000000..f5150cbb --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowBusinessKeyServiceImpl.java @@ -0,0 +1,102 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.flowable.domain.FlowTaskEntity; +import com.yanzhu.flowable.mapper.FlowBusinessKeyMapper; +import com.yanzhu.flowable.service.IFlowBusinessKeyService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * 业务工作流程自定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowBusinessKeyServiceImpl implements IFlowBusinessKeyService { + + @Resource + private FlowBusinessKeyMapper flowBusinessKeyMapper; + /** + * 根据条件查询所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public List selectAllFlowTaskByParams(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity); + } + + /** + * 根据条件统计所有流任务 + * @param flowTaskEntity + * @return + */ + @Override + public Map quueryCount(FlowTaskEntity flowTaskEntity) { + flowTaskEntity.setActiveName("await"); + int awaitSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + flowTaskEntity.setActiveName("finished"); + int finishedSize = flowBusinessKeyMapper.selectAllFlowTaskByParams(flowTaskEntity).size(); + Map dataMap = new HashMap<>(); + dataMap.put("await",awaitSize); + dataMap.put("finished",finishedSize); + return dataMap; + } + + /** + * 根据条件流程Id查询操作日志 + * @param procInsId + * @return + */ + @Override + public List> selectCommentByProcInsId(String procInsId) { + return flowBusinessKeyMapper.selectCommentByProcInsId(procInsId); + } + + /** + * 根据流程Id查询表单数据 + * @param procInsId + * @return + */ + @Override + public Map selectFormDatasByProcInsId(String procInsId) { + Map dataMap = new HashMap<>(); + List> list = flowBusinessKeyMapper.selectFormDatasByProcInsId(procInsId); + Iterator> it = list.iterator(); + while (it.hasNext()) { + Map map = it.next(); + dataMap.put(Convert.toStr(map.get("name")), map.get("text")); + } + return dataMap; + } + + /** + * 查询我的代办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyAwaitFlowTask(FlowTaskEntity flowTaskEntity){ + return flowBusinessKeyMapper.selectMyAwaitFlowTask(flowTaskEntity); + } + + /** + * 查询我的已办任务 + * @param flowTaskEntity + * @return + */ + @Override + public List> selectMyFinishedFlowTask(FlowTaskEntity flowTaskEntity) { + return flowBusinessKeyMapper.selectMyFinishedFlowTask(flowTaskEntity); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java new file mode 100644 index 00000000..0d9f656f --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowDefinitionServiceImpl.java @@ -0,0 +1,279 @@ +package com.yanzhu.flowable.service.impl; + +import com.github.pagehelper.PageHelper; +import com.github.pagehelper.PageInfo; +import com.yanzhu.common.core.constant.HttpStatus; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.ServiceException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.FlowProcDefDto; +import com.yanzhu.flowable.domain.SysExpression; +import com.yanzhu.flowable.domain.vo.FlowDeptVo; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.mapper.FlowDeployMapper; +import com.yanzhu.flowable.service.IFlowDefinitionService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.engine.repository.Deployment; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.repository.ProcessDefinitionQuery; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.image.impl.DefaultProcessDiagramGenerator; +import org.flowable.task.api.Task; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * 流程定义 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowDefinitionServiceImpl extends FlowServiceFactory implements IFlowDefinitionService { + + @Autowired + private RedisService redisService; + + @Resource + private FlowDeployMapper flowDeployMapper; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private ISysDeployFormService sysDeployFormService; + + private static final String BPMN_FILE_SUFFIX = ".bpmn"; + + @Override + public boolean exist(String processDefinitionKey) { + ProcessDefinitionQuery processDefinitionQuery + = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey); + long count = processDefinitionQuery.count(); + return count > 0 ? true : false; + } + + + /** + * 流程定义列表 + * + * @param flowQueryVo 流程查询 + * @return 流程定义分页列表数据 + */ + @Override + public TableDataInfo list(FlowQueryVo flowQueryVo) { + TableDataInfo rspData = new TableDataInfo(); + PageHelper.startPage(flowQueryVo.getPageNum(), flowQueryVo.getPageSize()); + List list = flowDeployMapper.selectDeployList(flowQueryVo); + rspData.setCode(HttpStatus.SUCCESS); + rspData.setMsg("查询成功"); + rspData.setRows(list); + rspData.setTotal(new PageInfo(list).getTotal()); + return rspData; + } + + + /** + * 导入流程文件 + * + * 当每个key的流程第一次部署时,指定版本为1。对其后所有使用相同key的流程定义, + * 部署时版本会在该key当前已部署的最高版本号基础上加1。key参数用于区分流程定义 + * @param name 流程名称 + * @param category 流程类型 + * @param prtype 流程定义类型 + * @param OwnerDeptId 流程单位 + * @param OwnerProjectId 流程项目 + * @param in 流程文件流 + */ + @Override + @Transactional + public void importFile(String name, String category, String prtype, String OwnerDeptId, String OwnerProjectId, InputStream in) { + if(SecurityUtils.isAdmin(SecurityUtils.getUserId())){ + throw new ServiceException("请选择项目后导入流程文件..."); + } + Deployment deploy = repositoryService.createDeployment().addInputStream(name + BPMN_FILE_SUFFIX, in).name(name).category(category).deploy(); + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult(); + repositoryService.setProcessDefinitionCategory(definition.getId(), category); + /**新增流程单位关系**/ + FlowDeptVo flowDeptVo = new FlowDeptVo(); + flowDeptVo.setProcdefId(definition.getId()); + //超管不能导入流程文件 + if(SecurityUtils.isGSAdmin()){ + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setDeptId(Convert.toLong(OwnerDeptId)); + } + if(StringUtils.isNotEmpty(OwnerDeptId)){ + // OwnerDeptId转换Long失败...默认返回null + flowDeptVo.setProjId(Convert.toLong(OwnerProjectId)); + } + }else{ + // 从用户信息中获取项目单位信息... + flowDeptVo.setPrtype(prtype); + flowDeptVo.setProjId(SecurityUtils.getLoginUser().getProjectId()); + flowDeptVo.setDeptId(SecurityUtils.getLoginUser().getProjectDeptId()); + } + if(flowDeptVo.getDeptId()!=null){ + flowDeployMapper.insertActReProcdefDept(flowDeptVo); + } + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public AjaxResult readXml(String deployId) throws IOException { + ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + InputStream inputStream = repositoryService.getResourceAsStream(definition.getDeploymentId(), definition.getResourceName()); + String result = IOUtils.toString(inputStream, StandardCharsets.UTF_8.name()); + return AjaxResult.success("", result); + } + + /** + * 读取xml + * + * @param deployId + * @return + */ + @Override + public InputStream readImage(String deployId) { + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + //获得图片流 + DefaultProcessDiagramGenerator diagramGenerator = new DefaultProcessDiagramGenerator(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); + //输出为图片 + return diagramGenerator.generateDiagram( + bpmnModel, + "png", + Collections.emptyList(), + Collections.emptyList(), + "宋体", + "宋体", + "宋体", + null, + 1.0, + false); + + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程模板ID + * @param applyInfos 流程表单 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map applyInfos) { + try { + /**保存流程表单**/ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+procDefId); + R res; + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsGroupEntity(applyInfos, SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + res = remoteFlowService.addSubdeptsUsersEntity(applyInfos, SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + if(StringUtils.isNotNull(res.getData())){ + Map variables = new HashMap<>(); + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(procDefId) + .latestVersion().singleResult(); + if (Objects.nonNull(processDefinition) && processDefinition.isSuspended()) { + return AjaxResult.error("流程已被挂起,请联系管理员激活流程"); + } + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + String userId = Convert.toStr(sysUser.getUserId()); + // 设置流程发起人Id到流程中 + identityService.setAuthenticatedUserId(userId); + variables.put(ProcessConstants.PROCESS_INITIATOR, userId); + variables.put("businessKey", res.getData()); + ProcessInstance processInstance; + processInstance = runtimeService.startProcessInstanceById(procDefId, Convert.toStr(res.getData()),variables); + // 给第一步申请人节点设置任务执行人和意见 + Task task = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId()).singleResult(); + if (Objects.nonNull(task)) { + taskService.addComment(task.getId(), processInstance.getProcessInstanceId(), FlowComment.NORMAL.getType(), sysUser.getNickName() + "发起流程申请"); + taskService.setAssignee(task.getId(), Convert.toStr(userId)); + taskService.complete(task.getId(), variables); + } + return AjaxResult.success("流程启动成功"); + }else{ + return AjaxResult.error("流程表单保存失败"); + } + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } + + /** + * 激活或挂起流程定义 + * + * @param state 状态 + * @param deployId 流程部署ID + */ + @Override + public void updateState(Integer state, String deployId) { + ProcessDefinition procDef = repositoryService.createProcessDefinitionQuery().deploymentId(deployId).singleResult(); + // 激活 + if (state == 1) { + repositoryService.activateProcessDefinitionById(procDef.getId(), true, null); + } + // 挂起 + if (state == 2) { + repositoryService.suspendProcessDefinitionById(procDef.getId(), true, null); + } + } + + + /** + * 删除流程定义 + * + * @param deployId 流程部署ID act_ge_bytearray 表中 deployment_id值 + */ + @Override + public void delete(String deployId) { + // true 允许级联删除 ,不设置会导致数据库外键关联异常 + repositoryService.deleteDeployment(deployId, true); + } + +} diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java new file mode 100644 index 00000000..4facc05d --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowInstanceServiceImpl.java @@ -0,0 +1,117 @@ +package com.yanzhu.flowable.service.impl; + +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.service.IFlowInstanceService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.engine.history.HistoricProcessInstance; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Map; +import java.util.Objects; + +/** + * 工作流流程实例管理 + * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowInstanceServiceImpl extends FlowServiceFactory implements IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + @Override + public void stopProcessInstance(FlowTaskVo vo) { + String taskId = vo.getTaskId(); + + } + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + @Override + public void updateState(Integer state, String instanceId) { + + // 激活 + if (state == 1) { + runtimeService.activateProcessInstanceById(instanceId); + } + // 挂起 + if (state == 2) { + runtimeService.suspendProcessInstanceById(instanceId); + } + } + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(String instanceId, String deleteReason) { + + // 查询历史数据 + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(instanceId); + if (historicProcessInstance.getEndTime() != null) { + historyService.deleteHistoricProcessInstance(historicProcessInstance.getId()); + return; + } + // 删除流程实例 + runtimeService.deleteProcessInstance(instanceId, deleteReason); + // 删除历史流程实例 + historyService.deleteHistoricProcessInstance(instanceId); + } + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + @Override + public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) { + HistoricProcessInstance historicProcessInstance = + historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + if (Objects.isNull(historicProcessInstance)) { + throw new FlowableObjectNotFoundException("流程实例不存在: " + processInstanceId); + } + return historicProcessInstance; + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map variables) { + + try { + // 设置流程发起人Id到流程中 + Long userId = SecurityUtils.getUserId(); + variables.put("initiator",userId); + variables.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true); + runtimeService.startProcessInstanceById(procDefId, variables); + return AjaxResult.success("流程启动成功"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java new file mode 100644 index 00000000..50208812 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java @@ -0,0 +1,1307 @@ +package com.yanzhu.flowable.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.CustomException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.*; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.flow.CustomProcessDiagramGenerator; +import com.yanzhu.flowable.flow.FindNextNodeUtil; +import com.yanzhu.flowable.flow.FlowableUtils; +import com.yanzhu.flowable.flow.ModelUtils; +import com.yanzhu.flowable.service.IFlowTaskService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.RemoteUserService; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.impl.cmd.AddMultiInstanceExecutionCmd; +import org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.task.Comment; +import org.flowable.identitylink.api.history.HistoricIdentityLink; +import org.flowable.image.ProcessDiagramGenerator; +import org.flowable.task.api.DelegationState; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * @author Tony + * @date 2021-04-03 + **/ +@Service +@Slf4j +public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTaskService { + + @Autowired + private RedisService redisService; + + @Resource + private ISysFormService sysFormService; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private RemoteUserService remoteUserService; + + @Resource + private RemoteRoleService remoteRoleService; + + @Resource + private ISysDeployFormService sysInstanceFormService; + + /** + * 完成任务 + * + * @param taskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public AjaxResult complete(FlowTaskVo taskVo) { + Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult(); + /**表单不为空时,重新修改表单**/ + if(StringUtils.isNotNull(taskVo.getApplyInfos())){ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+task.getProcessDefinitionId()); + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsGroupEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsUsersEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + } + if (Objects.isNull(task)) { + return AjaxResult.error("任务不存在"); + } + if (DelegationState.PENDING.equals(task.getDelegationState())) { + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment()); + taskService.resolveTask(taskVo.getTaskId()); + } else { + Long userId = SecurityUtils.getUserId(); + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment()); + taskService.setAssignee(taskVo.getTaskId(), Convert.toStr(userId)); + taskService.complete(taskVo.getTaskId()); + } + return AjaxResult.success(); + } + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + @Override + public void taskReject(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态!"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + // 获取节点信息 + source = flowElement; + } + } + } + + // 目的获取所有跳转到的节点 targetIds + // 获取当前节点的所有父级用户任务节点 + // 深度优先算法思想:延边迭代深入 + List parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null); + if (parentUserTaskList == null || parentUserTaskList.size() == 0) { + throw new CustomException("当前节点为初始任务节点,不能驳回"); + } + // 获取活动 ID 即节点 Key + List parentUserTaskKeyList = new ArrayList<>(); + parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId())); + // 获取全部历史节点活动实例,即已经走过的节点历史,数据采用开始时间升序 + List historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list(); + // 数据清洗,将回滚导致的脏数据清洗掉 + List lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList); + // 此时历史任务实例为倒序,获取最后走的节点 + List targetIds = new ArrayList<>(); + // 循环结束标识,遇到当前目标节点的次数 + int number = 0; + StringBuilder parentHistoricTaskKey = new StringBuilder(); + for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) { + // 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过 + if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) { + continue; + } + parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey); + if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) { + number++; + } + // 在数据清洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次 + // 在流程中如果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环 + // number == 1,第一次遇到当前节点 + // number == 2,第二次遇到,代表最后一次的循环范围 + if (number == 2) { + break; + } + // 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点 + if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) { + targetIds.add(historicTaskInstanceKey); + } + } + + + // 目的获取所有需要被跳转的节点 currentIds + // 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路 + UserTask oneUserTask = parentUserTaskList.get(0); + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需驳回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + + // 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况 + if (targetIds.size() > 1 && currentIds.size() > 1) { + throw new CustomException("任务出现多对多情况,无法撤回"); + } + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置驳回意见 + currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment())); + + try { + // 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况 + if (targetIds.size() > 1) { + // 1 对 多任务跳转,currentIds 当前节点(1),targetIds 跳转到的节点(多) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()). + moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState(); + } + // 如果父级任务只有一个,因此当前任务可能为网关中的任务 + if (targetIds.size() == 1) { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetIds.get(0) 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState(); + } + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + + } + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void taskReturn(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + // 获取跳转的节点元素 + FlowElement target = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 当前任务节点元素 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = flowElement; + } + // 跳转的节点元素 + if (flowElement.getId().equals(flowTaskVo.getTargetKey())) { + target = flowElement; + } + } + } + + // 从当前节点向前扫描 + // 如果存在路线上不存在目标节点,说明目标节点是在网关上或非同一路线上,不可跳转 + // 否则目标节点相对于当前节点,属于串行 + Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, flowTaskVo.getTargetKey(), null, null); + if (!isSequential) { + throw new CustomException("当前节点相对于目标节点,不属于串行关系,无法回退"); + } + + + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需退回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置回退意见 + currentTaskIds.forEach(currentTaskId -> taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), flowTaskVo.getComment())); + + try { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, flowTaskVo.getTargetKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + } + + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + @Override + public AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo) { +// // 当前任务 task +// Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); +// // 从流程历史任务中获取可退回节点 +//// List hisActIns = historyService.createHistoricActivityInstanceQuery() +//// .executionId(task.getExecutionId()) +//// .activityType("userTask") +//// .orderByHistoricActivityInstanceStartTime() +//// .finished() +//// .desc() +//// .list(); +//// +//// // 可回退的节点列表 +//// List returnTaskNodeList = new ArrayList<>(); +//// ReturnTaskNodeVo returnTaskNodeVo; +//// for (HistoricActivityInstance activityInstance : hisActIns) { +//// returnTaskNodeVo = new ReturnTaskNodeVo(); +//// returnTaskNodeVo.setId(activityInstance.getActivityId()); +//// // 根据流程节点处理时间校验改节点是否已完成 +//// returnTaskNodeVo.setName(activityInstance.getActivityName()); +//// returnTaskNodeList.add(returnTaskNodeVo); +//// } +// List userTaskList = new ArrayList<>(); +// // 获取流程定义信息 +// ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); +// // 获取所有节点信息,暂不考虑子流程情况 +// Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); +// Collection flowElements = process.getFlowElements(); +// // 获取当前任务节点元素 +// UserTask source = null; +// if (flowElements != null) { +// for (FlowElement flowElement : flowElements) { +// // 类型为用户节点 +// if (flowElement.getId().equals(task.getTaskDefinitionKey())) { +// source = (UserTask) flowElement; +// } +// } +// } +// // 获取节点的所有路线 +// List> roads = FlowableUtils.findRoad(source, null, null, null); +// +// for (List road : roads) { +// if (userTaskList.size() == 0) { +// // 还没有可回退节点直接添加 +// userTaskList = road; +// } else { +// // 如果已有回退节点,则比对取交集部分 +// userTaskList.retainAll(road); +// } +// } +// return AjaxResult.success(userTaskList); + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息,暂不考虑子流程情况 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + Collection flowElements = process.getFlowElements(); + // 获取当前任务节点元素 + UserTask source = null; + if (flowElements != null) { + for (FlowElement flowElement : flowElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = (UserTask) flowElement; + } + } + } + // 获取节点的所有路线 + List> roads = FlowableUtils.findRoad(source, null, null, null); + // 可回退的节点列表 + List userTaskList = new ArrayList<>(); + for (List road : roads) { + if (userTaskList.size() == 0) { + // 还没有可回退节点直接添加 + userTaskList = road; + } else { + // 如果已有回退节点,则比对取交集部分 + userTaskList.retainAll(road); + } + } + return AjaxResult.success(userTaskList); + } + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + public void deleteTask(FlowTaskVo flowTaskVo) { + // todo 待确认删除任务是物理删除任务 还是逻辑删除,让这个任务直接通过? + taskService.deleteTask(flowTaskVo.getTaskId(), flowTaskVo.getComment()); + } + + /** + * 认领/签收任务 + * 认领以后,这个用户就会成为任务的执行人,任务会从其他成员的任务列表中消失 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void claim(FlowTaskVo flowTaskVo) { + taskService.claim(flowTaskVo.getTaskId(), flowTaskVo.getUserId()); + } + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void unClaim(FlowTaskVo flowTaskVo) { + taskService.unclaim(flowTaskVo.getTaskId()); + } + + /** + * 委派任务 + * 任务委派只是委派人将当前的任务交给被委派人进行审批,处理任务后又重新回到委派人身上。 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.DELEGATE.getType(), flowTaskVo.getComment()); + taskService.delegateTask(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + } + + /** + * 任务归还 + * 被委派人完成任务之后,将任务归还委派人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void resolveTask(FlowTaskVo flowTaskVo) { + taskService.resolveTask(flowTaskVo.getTaskId()); + } + + + /** + * 转办任务 + * 直接将办理人换成别人,这时任务的拥有者不再是转办人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void assignTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.ASSIGN.getType(), flowTaskVo.getComment()); + // 直接转派就可以覆盖掉之前的 + taskService.setAssignee(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); +// // 删除指派人重新指派 +// taskService.deleteCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// taskService.addCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// // 如果要查询转给他人处理的任务,可以同时将OWNER进行设置: +// taskService.setOwner(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + + } + + /** + * 多实例加签 + * act_ru_task、act_ru_identitylink各生成一条记录 + * + * @param flowTaskVo + */ + @Override + public void addMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new AddMultiInstanceExecutionCmd(flowTaskVo.getDefId(), flowTaskVo.getInstanceId(), flowTaskVo.getVariables())); + } + + /** + * 多实例减签 + * act_ru_task减1、act_ru_identitylink不变 + * + * @param flowTaskVo + */ + @Override + public void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new DeleteMultiInstanceExecutionCmd(flowTaskVo.getCurrentChildExecutionId(), flowTaskVo.getFlag())); + } + + /** + * 我发起的流程 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult myProcess(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + Long userId = SecurityUtils.getUserId(); + HistoricProcessInstanceQuery historicProcessInstanceQuery; + //超管查询所有数据据 + if(SecurityUtils.isAdmin(userId)){ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .orderByProcessInstanceStartTime() + .desc(); + }else{ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .startedBy(userId.toString()) + .orderByProcessInstanceStartTime() + .desc(); + } + List historicProcessInstances = historicProcessInstanceQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + page.setTotal(historicProcessInstanceQuery.count()); + List flowList = new ArrayList<>(); + for (HistoricProcessInstance hisIns : historicProcessInstances) { + FlowTaskDto flowTask = new FlowTaskDto(); + flowTask.setCreateTime(hisIns.getStartTime()); + flowTask.setFinishTime(hisIns.getEndTime()); + flowTask.setProcInsId(hisIns.getId()); + + // 计算耗时 + if (Objects.nonNull(hisIns.getEndTime())) { + long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } else { + long time = System.currentTimeMillis() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } + // 流程定义信息 + ProcessDefinition pd = repositoryService.createProcessDefinitionQuery() + .processDefinitionId(hisIns.getProcessDefinitionId()) + .singleResult(); + flowTask.setDeployId(pd.getDeploymentId()); + flowTask.setProcDefName(pd.getName()); + flowTask.setProcDefVersion(pd.getVersion()); + flowTask.setCategory(pd.getCategory()); + flowTask.setProcDefVersion(pd.getVersion()); + // 当前所处流程 + List taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list(); + if (CollectionUtils.isNotEmpty(taskList)) { + flowTask.setTaskId(taskList.get(0).getId()); + flowTask.setTaskName(taskList.get(0).getName()); + if (StringUtils.isNotBlank(taskList.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(taskList.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } else { + List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list(); + flowTask.setTaskId(historicTaskInstance.get(0).getId()); + flowTask.setTaskName(historicTaskInstance.get(0).getName()); + if (StringUtils.isNotBlank(historicTaskInstance.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(historicTaskInstance.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } + flowList.add(flowTask); + } + page.setRows(flowList); + return AjaxResult.success(page); + } + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult stopProcess(FlowTaskVo flowTaskVo) { + List task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).list(); + if (CollectionUtils.isEmpty(task)) { + throw new CustomException("流程未启动或已执行完成,取消申请失败"); + } + // 获取当前流程实例 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(flowTaskVo.getInstanceId()) + .singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); + if (Objects.nonNull(bpmnModel)) { + Process process = bpmnModel.getMainProcess(); + List endNodes = process.findFlowElementsOfType(EndEvent.class, false); + if (CollectionUtils.isNotEmpty(endNodes)) { + String userIdStr = Convert.toStr(SecurityUtils.getUserId()); + if(StringUtils.isNotEmpty(userIdStr)){ + Authentication.setAuthenticatedUserId(userIdStr); + taskService.addComment(task.get(0).getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(), + StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment()); + // 设置流程经办人为当前登录人员 + taskService.setAssignee(task.get(0).getId(), userIdStr); + // 获取当前流程最后一个节点 + String endId = endNodes.get(0).getId(); + List executions = runtimeService.createExecutionQuery() + .parentId(processInstance.getProcessInstanceId()).list(); + List executionIds = new ArrayList<>(); + executions.forEach(execution -> executionIds.add(execution.getId())); + // 变更流程为已结束状态 + runtimeService.createChangeActivityStateBuilder() + .moveExecutionsToSingleActivityId(executionIds, endId).changeState(); + return AjaxResult.success(); + } + } + } + return AjaxResult.error(); + } + + /** + * 撤回流程 目前存在错误 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult revokeProcess(FlowTaskVo flowTaskVo) { + String procInsId = flowTaskVo.getInstanceId(); + String taskId = flowTaskVo.getTaskId(); + // 校验流程是否结束 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(procInsId) + .active() + .singleResult(); + if(Objects.isNull(processInstance)) { + throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作"); + } + // 获取待撤回任务实例 + HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .taskAssignee(flowTaskVo.getUserId()) + .singleResult(); + if (Objects.isNull(currTaskIns)) { + throw new RuntimeException("当前任务不存在,无法执行撤回操作"); + } + // 获取 bpmn 模型 + BpmnModel bpmnModel = repositoryService.getBpmnModel(currTaskIns.getProcessDefinitionId()); + UserTask currUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currTaskIns.getTaskDefinitionKey()); + // 查找下一级用户任务列表 + List nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask); + List nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList()); + + // 获取当前节点之后已完成的流程历史节点 + List finishedTaskInsList = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(procInsId) + .taskCreatedAfter(currTaskIns.getEndTime()) + .finished() + .list(); + for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) { + // 检查已完成流程历史节点是否存在下一级中 + if (StringUtils.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) { + throw new RuntimeException("下一流程已处理,无法执行撤回操作"); + } + } + // 获取所有激活的任务节点,找到需要撤回的任务 + List activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list(); + List revokeExecutionIds = new ArrayList<>(); + for (Task task : activateTaskList) { + // 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点 + if (StringUtils.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) { + // 添加撤回审批信息 + taskService.setAssignee(task.getId(), flowTaskVo.getUserId()); + taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), flowTaskVo.getAssignee() + "撤回流程审批"); + revokeExecutionIds.add(task.getExecutionId()); + } + } + try { + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(procInsId) + .moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new RuntimeException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new RuntimeException("执行撤回操作失败"); + } + return AjaxResult.success(); + } + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult todoList(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + // 只查看自己的数据 + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + TaskQuery taskQuery = taskService.createTaskQuery() + .active() + .includeProcessVariables() + .taskCandidateGroupIn(sysUser.getRoles().stream().map(role -> role.getRoleId().toString()).collect(Collectors.toList())) + .taskCandidateOrAssigned(sysUser.getUserId().toString()) + .orderByTaskCreateTime().desc(); + + page.setTotal(taskQuery.count()); + List taskList = taskQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + List
+ * + * @author Tony + * @date 2021-04-03 + */ +@Service +@Slf4j +public class FlowInstanceServiceImpl extends FlowServiceFactory implements IFlowInstanceService { + + /** + * 结束流程实例 + * + * @param vo + */ + @Override + public void stopProcessInstance(FlowTaskVo vo) { + String taskId = vo.getTaskId(); + + } + + /** + * 激活或挂起流程实例 + * + * @param state 状态 + * @param instanceId 流程实例ID + */ + @Override + public void updateState(Integer state, String instanceId) { + + // 激活 + if (state == 1) { + runtimeService.activateProcessInstanceById(instanceId); + } + // 挂起 + if (state == 2) { + runtimeService.suspendProcessInstanceById(instanceId); + } + } + + /** + * 删除流程实例ID + * + * @param instanceId 流程实例ID + * @param deleteReason 删除原因 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delete(String instanceId, String deleteReason) { + + // 查询历史数据 + HistoricProcessInstance historicProcessInstance = getHistoricProcessInstanceById(instanceId); + if (historicProcessInstance.getEndTime() != null) { + historyService.deleteHistoricProcessInstance(historicProcessInstance.getId()); + return; + } + // 删除流程实例 + runtimeService.deleteProcessInstance(instanceId, deleteReason); + // 删除历史流程实例 + historyService.deleteHistoricProcessInstance(instanceId); + } + + /** + * 根据实例ID查询历史实例数据 + * + * @param processInstanceId + * @return + */ + @Override + public HistoricProcessInstance getHistoricProcessInstanceById(String processInstanceId) { + HistoricProcessInstance historicProcessInstance = + historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); + if (Objects.isNull(historicProcessInstance)) { + throw new FlowableObjectNotFoundException("流程实例不存在: " + processInstanceId); + } + return historicProcessInstance; + } + + /** + * 根据流程定义ID启动流程实例 + * + * @param procDefId 流程定义Id + * @param variables 流程变量 + * @return + */ + @Override + public AjaxResult startProcessInstanceById(String procDefId, Map variables) { + + try { + // 设置流程发起人Id到流程中 + Long userId = SecurityUtils.getUserId(); + variables.put("initiator",userId); + variables.put("_FLOWABLE_SKIP_EXPRESSION_ENABLED", true); + runtimeService.startProcessInstanceById(procDefId, variables); + return AjaxResult.success("流程启动成功"); + } catch (Exception e) { + e.printStackTrace(); + return AjaxResult.error("流程启动错误"); + } + } +} \ No newline at end of file diff --git a/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java new file mode 100644 index 00000000..50208812 --- /dev/null +++ b/yanzhu-modules/yanzhu-flowable/src/main/java/com/yanzhu/flowable/service/impl/FlowTaskServiceImpl.java @@ -0,0 +1,1307 @@ +package com.yanzhu.flowable.service.impl; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONObject; +import com.alibaba.fastjson2.TypeReference; +import com.yanzhu.common.core.constant.SecurityConstants; +import com.yanzhu.common.core.domain.R; +import com.yanzhu.common.core.enums.FlowableEnums; +import com.yanzhu.common.core.exception.CustomException; +import com.yanzhu.common.core.text.Convert; +import com.yanzhu.common.core.utils.StringUtils; +import com.yanzhu.common.core.web.domain.AjaxResult; +import com.yanzhu.common.core.web.page.TableDataInfo; +import com.yanzhu.common.redis.service.RedisService; +import com.yanzhu.common.security.utils.SecurityUtils; +import com.yanzhu.flowable.common.constant.ProcessConstants; +import com.yanzhu.flowable.common.enums.FlowComment; +import com.yanzhu.flowable.domain.*; +import com.yanzhu.flowable.domain.vo.FlowQueryVo; +import com.yanzhu.flowable.domain.vo.FlowTaskVo; +import com.yanzhu.flowable.factory.FlowServiceFactory; +import com.yanzhu.flowable.flow.CustomProcessDiagramGenerator; +import com.yanzhu.flowable.flow.FindNextNodeUtil; +import com.yanzhu.flowable.flow.FlowableUtils; +import com.yanzhu.flowable.flow.ModelUtils; +import com.yanzhu.flowable.service.IFlowTaskService; +import com.yanzhu.flowable.service.ISysDeployFormService; +import com.yanzhu.flowable.service.ISysFormService; +import com.yanzhu.system.api.RemoteFlowService; +import com.yanzhu.system.api.RemoteRoleService; +import com.yanzhu.system.api.RemoteUserService; +import com.yanzhu.system.api.domain.SysRole; +import com.yanzhu.system.api.domain.SysUser; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.io.IOUtils; +import org.flowable.bpmn.model.Process; +import org.flowable.bpmn.model.*; +import org.flowable.common.engine.api.FlowableException; +import org.flowable.common.engine.api.FlowableObjectNotFoundException; +import org.flowable.common.engine.impl.identity.Authentication; +import org.flowable.engine.ProcessEngineConfiguration; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.history.HistoricProcessInstanceQuery; +import org.flowable.engine.impl.cmd.AddMultiInstanceExecutionCmd; +import org.flowable.engine.impl.cmd.DeleteMultiInstanceExecutionCmd; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.Execution; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.task.Comment; +import org.flowable.identitylink.api.history.HistoricIdentityLink; +import org.flowable.image.ProcessDiagramGenerator; +import org.flowable.task.api.DelegationState; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskQuery; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.api.history.HistoricTaskInstanceQuery; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.annotation.Resource; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Collectors; + +/** + * @author Tony + * @date 2021-04-03 + **/ +@Service +@Slf4j +public class FlowTaskServiceImpl extends FlowServiceFactory implements IFlowTaskService { + + @Autowired + private RedisService redisService; + + @Resource + private ISysFormService sysFormService; + + @Autowired + private RemoteFlowService remoteFlowService; + + @Resource + private RemoteUserService remoteUserService; + + @Resource + private RemoteRoleService remoteRoleService; + + @Resource + private ISysDeployFormService sysInstanceFormService; + + /** + * 完成任务 + * + * @param taskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public AjaxResult complete(FlowTaskVo taskVo) { + Task task = taskService.createTaskQuery().taskId(taskVo.getTaskId()).singleResult(); + /**表单不为空时,重新修改表单**/ + if(StringUtils.isNotNull(taskVo.getApplyInfos())){ + Map procDefData = redisService.getCacheObject(FlowableEnums.FLOW_KEY_CACHE+task.getProcessDefinitionId()); + if(StringUtils.isNotNull(procDefData)){ + String procType = Convert.toStr(procDefData.get("TYPE_")); + if(StringUtils.isNotNull(procType)){ + if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsGroupEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else if(Objects.equals(procType,FlowableEnums.SUB_DEPTS.getCode())){ + remoteFlowService.addSubdeptsUsersEntity(taskVo.getApplyInfos(), SecurityConstants.INNER); + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("审批流程配置异常,请联系管理员配置"); + } + }else{ + return AjaxResult.error("未配置审批流程信息,请联系管理员配置"); + } + } + if (Objects.isNull(task)) { + return AjaxResult.error("任务不存在"); + } + if (DelegationState.PENDING.equals(task.getDelegationState())) { + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.DELEGATE.getType(), taskVo.getComment()); + taskService.resolveTask(taskVo.getTaskId()); + } else { + Long userId = SecurityUtils.getUserId(); + taskService.addComment(taskVo.getTaskId(), taskVo.getInstanceId(), FlowComment.NORMAL.getType(), taskVo.getComment()); + taskService.setAssignee(taskVo.getTaskId(), Convert.toStr(userId)); + taskService.complete(taskVo.getTaskId()); + } + return AjaxResult.success(); + } + + /** + * 驳回任务 + * + * @param flowTaskVo + */ + @Override + public void taskReject(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态!"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + // 获取节点信息 + source = flowElement; + } + } + } + + // 目的获取所有跳转到的节点 targetIds + // 获取当前节点的所有父级用户任务节点 + // 深度优先算法思想:延边迭代深入 + List parentUserTaskList = FlowableUtils.iteratorFindParentUserTasks(source, null, null); + if (parentUserTaskList == null || parentUserTaskList.size() == 0) { + throw new CustomException("当前节点为初始任务节点,不能驳回"); + } + // 获取活动 ID 即节点 Key + List parentUserTaskKeyList = new ArrayList<>(); + parentUserTaskList.forEach(item -> parentUserTaskKeyList.add(item.getId())); + // 获取全部历史节点活动实例,即已经走过的节点历史,数据采用开始时间升序 + List historicTaskInstanceList = historyService.createHistoricTaskInstanceQuery().processInstanceId(task.getProcessInstanceId()).orderByHistoricTaskInstanceStartTime().asc().list(); + // 数据清洗,将回滚导致的脏数据清洗掉 + List lastHistoricTaskInstanceList = FlowableUtils.historicTaskInstanceClean(allElements, historicTaskInstanceList); + // 此时历史任务实例为倒序,获取最后走的节点 + List targetIds = new ArrayList<>(); + // 循环结束标识,遇到当前目标节点的次数 + int number = 0; + StringBuilder parentHistoricTaskKey = new StringBuilder(); + for (String historicTaskInstanceKey : lastHistoricTaskInstanceList) { + // 当会签时候会出现特殊的,连续都是同一个节点历史数据的情况,这种时候跳过 + if (parentHistoricTaskKey.toString().equals(historicTaskInstanceKey)) { + continue; + } + parentHistoricTaskKey = new StringBuilder(historicTaskInstanceKey); + if (historicTaskInstanceKey.equals(task.getTaskDefinitionKey())) { + number++; + } + // 在数据清洗后,历史节点就是唯一一条从起始到当前节点的历史记录,理论上每个点只会出现一次 + // 在流程中如果出现循环,那么每次循环中间的点也只会出现一次,再出现就是下次循环 + // number == 1,第一次遇到当前节点 + // number == 2,第二次遇到,代表最后一次的循环范围 + if (number == 2) { + break; + } + // 如果当前历史节点,属于父级的节点,说明最后一次经过了这个点,需要退回这个点 + if (parentUserTaskKeyList.contains(historicTaskInstanceKey)) { + targetIds.add(historicTaskInstanceKey); + } + } + + + // 目的获取所有需要被跳转的节点 currentIds + // 取其中一个父级任务,因为后续要么存在公共网关,要么就是串行公共线路 + UserTask oneUserTask = parentUserTaskList.get(0); + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需驳回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(oneUserTask, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + + // 规定:并行网关之前节点必须需存在唯一用户任务节点,如果出现多个任务节点,则并行网关节点默认为结束节点,原因为不考虑多对多情况 + if (targetIds.size() > 1 && currentIds.size() > 1) { + throw new CustomException("任务出现多对多情况,无法撤回"); + } + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置驳回意见 + currentTaskIds.forEach(item -> taskService.addComment(item, task.getProcessInstanceId(), FlowComment.REJECT.getType(), flowTaskVo.getComment())); + + try { + // 如果父级任务多于 1 个,说明当前节点不是并行节点,原因为不考虑多对多情况 + if (targetIds.size() > 1) { + // 1 对 多任务跳转,currentIds 当前节点(1),targetIds 跳转到的节点(多) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()). + moveSingleActivityIdToActivityIds(currentIds.get(0), targetIds).changeState(); + } + // 如果父级任务只有一个,因此当前任务可能为网关中的任务 + if (targetIds.size() == 1) { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetIds.get(0) 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, targetIds.get(0)).changeState(); + } + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + + } + + /** + * 退回任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Transactional(rollbackFor = Exception.class) + @Override + public void taskReturn(FlowTaskVo flowTaskVo) { + if (taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult().isSuspended()) { + throw new CustomException("任务处于挂起状态"); + } + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + // 获取全部节点列表,包含子节点 + Collection allElements = FlowableUtils.getAllElements(process.getFlowElements(), null); + // 获取当前任务节点元素 + FlowElement source = null; + // 获取跳转的节点元素 + FlowElement target = null; + if (allElements != null) { + for (FlowElement flowElement : allElements) { + // 当前任务节点元素 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = flowElement; + } + // 跳转的节点元素 + if (flowElement.getId().equals(flowTaskVo.getTargetKey())) { + target = flowElement; + } + } + } + + // 从当前节点向前扫描 + // 如果存在路线上不存在目标节点,说明目标节点是在网关上或非同一路线上,不可跳转 + // 否则目标节点相对于当前节点,属于串行 + Boolean isSequential = FlowableUtils.iteratorCheckSequentialReferTarget(source, flowTaskVo.getTargetKey(), null, null); + if (!isSequential) { + throw new CustomException("当前节点相对于目标节点,不属于串行关系,无法回退"); + } + + + // 获取所有正常进行的任务节点 Key,这些任务不能直接使用,需要找出其中需要撤回的任务 + List runTaskList = taskService.createTaskQuery().processInstanceId(task.getProcessInstanceId()).list(); + List runTaskKeyList = new ArrayList<>(); + runTaskList.forEach(item -> runTaskKeyList.add(item.getTaskDefinitionKey())); + // 需退回任务列表 + List currentIds = new ArrayList<>(); + // 通过父级网关的出口连线,结合 runTaskList 比对,获取需要撤回的任务 + List currentUserTaskList = FlowableUtils.iteratorFindChildUserTasks(target, runTaskKeyList, null, null); + currentUserTaskList.forEach(item -> currentIds.add(item.getId())); + + // 循环获取那些需要被撤回的节点的ID,用来设置驳回原因 + List currentTaskIds = new ArrayList<>(); + currentIds.forEach(currentId -> runTaskList.forEach(runTask -> { + if (currentId.equals(runTask.getTaskDefinitionKey())) { + currentTaskIds.add(runTask.getId()); + } + })); + // 设置回退意见 + currentTaskIds.forEach(currentTaskId -> taskService.addComment(currentTaskId, task.getProcessInstanceId(), FlowComment.REBACK.getType(), flowTaskVo.getComment())); + + try { + // 1 对 1 或 多 对 1 情况,currentIds 当前要跳转的节点列表(1或多),targetKey 跳转到的节点(1) + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(task.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(currentIds, flowTaskVo.getTargetKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new CustomException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new CustomException("无法取消或开始活动"); + } + } + + + /** + * 获取所有可回退的节点 + * + * @param flowTaskVo + * @return + */ + @Override + public AjaxResult findReturnTaskList(FlowTaskVo flowTaskVo) { +// // 当前任务 task +// Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); +// // 从流程历史任务中获取可退回节点 +//// List hisActIns = historyService.createHistoricActivityInstanceQuery() +//// .executionId(task.getExecutionId()) +//// .activityType("userTask") +//// .orderByHistoricActivityInstanceStartTime() +//// .finished() +//// .desc() +//// .list(); +//// +//// // 可回退的节点列表 +//// List returnTaskNodeList = new ArrayList<>(); +//// ReturnTaskNodeVo returnTaskNodeVo; +//// for (HistoricActivityInstance activityInstance : hisActIns) { +//// returnTaskNodeVo = new ReturnTaskNodeVo(); +//// returnTaskNodeVo.setId(activityInstance.getActivityId()); +//// // 根据流程节点处理时间校验改节点是否已完成 +//// returnTaskNodeVo.setName(activityInstance.getActivityName()); +//// returnTaskNodeList.add(returnTaskNodeVo); +//// } +// List userTaskList = new ArrayList<>(); +// // 获取流程定义信息 +// ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); +// // 获取所有节点信息,暂不考虑子流程情况 +// Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); +// Collection flowElements = process.getFlowElements(); +// // 获取当前任务节点元素 +// UserTask source = null; +// if (flowElements != null) { +// for (FlowElement flowElement : flowElements) { +// // 类型为用户节点 +// if (flowElement.getId().equals(task.getTaskDefinitionKey())) { +// source = (UserTask) flowElement; +// } +// } +// } +// // 获取节点的所有路线 +// List> roads = FlowableUtils.findRoad(source, null, null, null); +// +// for (List road : roads) { +// if (userTaskList.size() == 0) { +// // 还没有可回退节点直接添加 +// userTaskList = road; +// } else { +// // 如果已有回退节点,则比对取交集部分 +// userTaskList.retainAll(road); +// } +// } +// return AjaxResult.success(userTaskList); + // 当前任务 task + Task task = taskService.createTaskQuery().taskId(flowTaskVo.getTaskId()).singleResult(); + // 获取流程定义信息 + ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); + // 获取所有节点信息,暂不考虑子流程情况 + Process process = repositoryService.getBpmnModel(processDefinition.getId()).getProcesses().get(0); + Collection flowElements = process.getFlowElements(); + // 获取当前任务节点元素 + UserTask source = null; + if (flowElements != null) { + for (FlowElement flowElement : flowElements) { + // 类型为用户节点 + if (flowElement.getId().equals(task.getTaskDefinitionKey())) { + source = (UserTask) flowElement; + } + } + } + // 获取节点的所有路线 + List> roads = FlowableUtils.findRoad(source, null, null, null); + // 可回退的节点列表 + List userTaskList = new ArrayList<>(); + for (List road : roads) { + if (userTaskList.size() == 0) { + // 还没有可回退节点直接添加 + userTaskList = road; + } else { + // 如果已有回退节点,则比对取交集部分 + userTaskList.retainAll(road); + } + } + return AjaxResult.success(userTaskList); + } + + /** + * 删除任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + public void deleteTask(FlowTaskVo flowTaskVo) { + // todo 待确认删除任务是物理删除任务 还是逻辑删除,让这个任务直接通过? + taskService.deleteTask(flowTaskVo.getTaskId(), flowTaskVo.getComment()); + } + + /** + * 认领/签收任务 + * 认领以后,这个用户就会成为任务的执行人,任务会从其他成员的任务列表中消失 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void claim(FlowTaskVo flowTaskVo) { + taskService.claim(flowTaskVo.getTaskId(), flowTaskVo.getUserId()); + } + + /** + * 取消认领/签收任务 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void unClaim(FlowTaskVo flowTaskVo) { + taskService.unclaim(flowTaskVo.getTaskId()); + } + + /** + * 委派任务 + * 任务委派只是委派人将当前的任务交给被委派人进行审批,处理任务后又重新回到委派人身上。 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.DELEGATE.getType(), flowTaskVo.getComment()); + taskService.delegateTask(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + } + + /** + * 任务归还 + * 被委派人完成任务之后,将任务归还委派人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void resolveTask(FlowTaskVo flowTaskVo) { + taskService.resolveTask(flowTaskVo.getTaskId()); + } + + + /** + * 转办任务 + * 直接将办理人换成别人,这时任务的拥有者不再是转办人 + * + * @param flowTaskVo 请求实体参数 + */ + @Override + @Transactional(rollbackFor = Exception.class) + public void assignTask(FlowTaskVo flowTaskVo) { + taskService.addComment(flowTaskVo.getTaskId(), flowTaskVo.getInstanceId(), FlowComment.ASSIGN.getType(), flowTaskVo.getComment()); + // 直接转派就可以覆盖掉之前的 + taskService.setAssignee(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); +// // 删除指派人重新指派 +// taskService.deleteCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// taskService.addCandidateUser(flowTaskVo.getTaskId(),flowTaskVo.getAssignee()); +// // 如果要查询转给他人处理的任务,可以同时将OWNER进行设置: +// taskService.setOwner(flowTaskVo.getTaskId(), flowTaskVo.getAssignee()); + + } + + /** + * 多实例加签 + * act_ru_task、act_ru_identitylink各生成一条记录 + * + * @param flowTaskVo + */ + @Override + public void addMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new AddMultiInstanceExecutionCmd(flowTaskVo.getDefId(), flowTaskVo.getInstanceId(), flowTaskVo.getVariables())); + } + + /** + * 多实例减签 + * act_ru_task减1、act_ru_identitylink不变 + * + * @param flowTaskVo + */ + @Override + public void deleteMultiInstanceExecution(FlowTaskVo flowTaskVo) { + managementService.executeCommand(new DeleteMultiInstanceExecutionCmd(flowTaskVo.getCurrentChildExecutionId(), flowTaskVo.getFlag())); + } + + /** + * 我发起的流程 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult myProcess(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + Long userId = SecurityUtils.getUserId(); + HistoricProcessInstanceQuery historicProcessInstanceQuery; + //超管查询所有数据据 + if(SecurityUtils.isAdmin(userId)){ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .orderByProcessInstanceStartTime() + .desc(); + }else{ + historicProcessInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .startedBy(userId.toString()) + .orderByProcessInstanceStartTime() + .desc(); + } + List historicProcessInstances = historicProcessInstanceQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + page.setTotal(historicProcessInstanceQuery.count()); + List flowList = new ArrayList<>(); + for (HistoricProcessInstance hisIns : historicProcessInstances) { + FlowTaskDto flowTask = new FlowTaskDto(); + flowTask.setCreateTime(hisIns.getStartTime()); + flowTask.setFinishTime(hisIns.getEndTime()); + flowTask.setProcInsId(hisIns.getId()); + + // 计算耗时 + if (Objects.nonNull(hisIns.getEndTime())) { + long time = hisIns.getEndTime().getTime() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } else { + long time = System.currentTimeMillis() - hisIns.getStartTime().getTime(); + flowTask.setDuration(getDate(time)); + } + // 流程定义信息 + ProcessDefinition pd = repositoryService.createProcessDefinitionQuery() + .processDefinitionId(hisIns.getProcessDefinitionId()) + .singleResult(); + flowTask.setDeployId(pd.getDeploymentId()); + flowTask.setProcDefName(pd.getName()); + flowTask.setProcDefVersion(pd.getVersion()); + flowTask.setCategory(pd.getCategory()); + flowTask.setProcDefVersion(pd.getVersion()); + // 当前所处流程 + List taskList = taskService.createTaskQuery().processInstanceId(hisIns.getId()).list(); + if (CollectionUtils.isNotEmpty(taskList)) { + flowTask.setTaskId(taskList.get(0).getId()); + flowTask.setTaskName(taskList.get(0).getName()); + if (StringUtils.isNotBlank(taskList.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(taskList.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } else { + List historicTaskInstance = historyService.createHistoricTaskInstanceQuery().processInstanceId(hisIns.getId()).orderByHistoricTaskInstanceEndTime().desc().list(); + flowTask.setTaskId(historicTaskInstance.get(0).getId()); + flowTask.setTaskName(historicTaskInstance.get(0).getName()); + if (StringUtils.isNotBlank(historicTaskInstance.get(0).getAssignee())) { + // 当前任务节点办理人信息 + R userResult = remoteUserService.getSysUserInfo(Long.parseLong(historicTaskInstance.get(0).getAssignee()), SecurityConstants.INNER); + if (Objects.nonNull(userResult)) { + SysUser sysUser = userResult.getData(); + flowTask.setAssigneeId(sysUser.getUserId()); + flowTask.setAssigneeName(sysUser.getNickName()); + flowTask.setAssigneeDeptName(Objects.nonNull(sysUser.getDept()) ? sysUser.getDept().getDeptName() : ""); + } + } + } + flowList.add(flowTask); + } + page.setRows(flowList); + return AjaxResult.success(page); + } + + /** + * 取消申请 + * 目前实现方式: 直接将当前流程变更为已完成 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult stopProcess(FlowTaskVo flowTaskVo) { + List task = taskService.createTaskQuery().processInstanceId(flowTaskVo.getInstanceId()).list(); + if (CollectionUtils.isEmpty(task)) { + throw new CustomException("流程未启动或已执行完成,取消申请失败"); + } + // 获取当前流程实例 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(flowTaskVo.getInstanceId()) + .singleResult(); + BpmnModel bpmnModel = repositoryService.getBpmnModel(processInstance.getProcessDefinitionId()); + if (Objects.nonNull(bpmnModel)) { + Process process = bpmnModel.getMainProcess(); + List endNodes = process.findFlowElementsOfType(EndEvent.class, false); + if (CollectionUtils.isNotEmpty(endNodes)) { + String userIdStr = Convert.toStr(SecurityUtils.getUserId()); + if(StringUtils.isNotEmpty(userIdStr)){ + Authentication.setAuthenticatedUserId(userIdStr); + taskService.addComment(task.get(0).getId(), processInstance.getProcessInstanceId(), FlowComment.STOP.getType(), + StringUtils.isBlank(flowTaskVo.getComment()) ? "取消申请" : flowTaskVo.getComment()); + // 设置流程经办人为当前登录人员 + taskService.setAssignee(task.get(0).getId(), userIdStr); + // 获取当前流程最后一个节点 + String endId = endNodes.get(0).getId(); + List executions = runtimeService.createExecutionQuery() + .parentId(processInstance.getProcessInstanceId()).list(); + List executionIds = new ArrayList<>(); + executions.forEach(execution -> executionIds.add(execution.getId())); + // 变更流程为已结束状态 + runtimeService.createChangeActivityStateBuilder() + .moveExecutionsToSingleActivityId(executionIds, endId).changeState(); + return AjaxResult.success(); + } + } + } + return AjaxResult.error(); + } + + /** + * 撤回流程 目前存在错误 + * + * @param flowTaskVo + * @return + */ + @Override + @Transactional + public AjaxResult revokeProcess(FlowTaskVo flowTaskVo) { + String procInsId = flowTaskVo.getInstanceId(); + String taskId = flowTaskVo.getTaskId(); + // 校验流程是否结束 + ProcessInstance processInstance = runtimeService.createProcessInstanceQuery() + .processInstanceId(procInsId) + .active() + .singleResult(); + if(Objects.isNull(processInstance)) { + throw new RuntimeException("流程已结束或已挂起,无法执行撤回操作"); + } + // 获取待撤回任务实例 + HistoricTaskInstance currTaskIns = historyService.createHistoricTaskInstanceQuery() + .taskId(taskId) + .taskAssignee(flowTaskVo.getUserId()) + .singleResult(); + if (Objects.isNull(currTaskIns)) { + throw new RuntimeException("当前任务不存在,无法执行撤回操作"); + } + // 获取 bpmn 模型 + BpmnModel bpmnModel = repositoryService.getBpmnModel(currTaskIns.getProcessDefinitionId()); + UserTask currUserTask = ModelUtils.getUserTaskByKey(bpmnModel, currTaskIns.getTaskDefinitionKey()); + // 查找下一级用户任务列表 + List nextUserTaskList = ModelUtils.findNextUserTasks(currUserTask); + List nextUserTaskKeys = nextUserTaskList.stream().map(UserTask::getId).collect(Collectors.toList()); + + // 获取当前节点之后已完成的流程历史节点 + List finishedTaskInsList = historyService.createHistoricTaskInstanceQuery() + .processInstanceId(procInsId) + .taskCreatedAfter(currTaskIns.getEndTime()) + .finished() + .list(); + for (HistoricTaskInstance finishedTaskInstance : finishedTaskInsList) { + // 检查已完成流程历史节点是否存在下一级中 + if (StringUtils.contains(nextUserTaskKeys, finishedTaskInstance.getTaskDefinitionKey())) { + throw new RuntimeException("下一流程已处理,无法执行撤回操作"); + } + } + // 获取所有激活的任务节点,找到需要撤回的任务 + List activateTaskList = taskService.createTaskQuery().processInstanceId(procInsId).list(); + List revokeExecutionIds = new ArrayList<>(); + for (Task task : activateTaskList) { + // 检查激活的任务节点是否存在下一级中,如果存在,则加入到需要撤回的节点 + if (StringUtils.contains(nextUserTaskKeys, task.getTaskDefinitionKey())) { + // 添加撤回审批信息 + taskService.setAssignee(task.getId(), flowTaskVo.getUserId()); + taskService.addComment(task.getId(), task.getProcessInstanceId(), FlowComment.REVOKE.getType(), flowTaskVo.getAssignee() + "撤回流程审批"); + revokeExecutionIds.add(task.getExecutionId()); + } + } + try { + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(procInsId) + .moveExecutionsToSingleActivityId(revokeExecutionIds, currTaskIns.getTaskDefinitionKey()).changeState(); + } catch (FlowableObjectNotFoundException e) { + throw new RuntimeException("未找到流程实例,流程可能已发生变化"); + } catch (FlowableException e) { + throw new RuntimeException("执行撤回操作失败"); + } + return AjaxResult.success(); + } + + /** + * 代办任务列表 + * + * @param queryVo 请求参数 + * @return + */ + @Override + public AjaxResult todoList(FlowQueryVo queryVo) { + TableDataInfo page = new TableDataInfo(); + // 只查看自己的数据 + SysUser sysUser = SecurityUtils.getLoginUser().getSysUser(); + TaskQuery taskQuery = taskService.createTaskQuery() + .active() + .includeProcessVariables() + .taskCandidateGroupIn(sysUser.getRoles().stream().map(role -> role.getRoleId().toString()).collect(Collectors.toList())) + .taskCandidateOrAssigned(sysUser.getUserId().toString()) + .orderByTaskCreateTime().desc(); + + page.setTotal(taskQuery.count()); + List taskList = taskQuery.listPage(queryVo.getPageSize() * (queryVo.getPageNum() - 1), queryVo.getPageSize()); + List