From eb9818dd7fac8ba16ad8412e9f07dd39b27dbfb4 Mon Sep 17 00:00:00 2001 From: hongawen <83944980@qq.com> Date: Sun, 12 May 2024 16:15:34 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B5=81=E8=BD=AC=E5=B7=A5=E4=BD=9C=E6=B5=81?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../njcn/bpm/api/BpmProcessFeignClient.java | 2 +- .../njcn/bpm/enums/BpmCommentTypeEnum.java | 46 + .../njcn/bpm/enums/BpmDeleteReasonEnum.java | 45 + .../com/njcn/bpm/enums/BpmResponseEnum.java | 50 +- .../njcn/bpm/enums/BpmTaskSignTypeEnum.java | 47 + .../com/njcn/bpm/enums/BpmTaskStatusEnum.java | 61 ++ .../njcn/bpm/enums/WebFilterOrderEnum.java | 34 + ...eSendWhenProcessInstanceApproveReqDTO.java | 27 + ...geSendWhenProcessInstanceRejectReqDTO.java | 34 + .../njcn/bpm/pojo/param/BpmCategoryParam.java | 4 +- .../com/njcn/bpm/pojo/param/BpmFormParam.java | 8 +- .../njcn/bpm/pojo/param/BpmModelParam.java | 4 +- .../BpmProcessInstanceCancelParam.java | 21 + .../BpmProcessInstanceCreateParam.java | 26 + .../instance/BpmProcessInstancePageParam.java | 28 + .../pojo/param/task/BpmTaskApproveParam.java | 29 + .../pojo/param/task/BpmTaskDelegateParam.java | 26 + .../bpm/pojo/param/task/BpmTaskParam.java | 35 + .../pojo/param/task/BpmTaskRejectParam.java | 24 + .../pojo/param/task/BpmTaskReturnParam.java | 29 + .../param/task/BpmTaskSignCreateParam.java | 31 + .../param/task/BpmTaskSignDeleteParam.java | 21 + .../pojo/param/task/BpmTaskTransferParam.java | 26 + .../bpm/pojo/po/BpmProcessDefinitionInfo.java | 2 +- .../bpm/pojo/po/task/ProcessInstanceCopy.java | 70 ++ .../pojo/vo/BpmProcessDefinitionInfoVO.java | 1 - .../vo/instance/BpmProcessInstanceVO.java | 93 ++ .../njcn/bpm/pojo/vo/task/BpmActivityVO.java | 27 + .../com/njcn/bpm/pojo/vo/task/BpmTaskVO.java | 106 +++ .../java/com/njcn/bpm/utils/BeanUtils.java | 31 +- .../java/com/njcn/bpm/utils/KeyValue.java | 22 + .../java/com/njcn/bpm/utils/MapUtils.java | 67 ++ .../java/com/njcn/bpm/utils/ObjectUtils.java | 63 ++ .../BpmProcessInstanceEventListener.java | 47 + .../njcn/bpm/config/BpmTaskEventListener.java | 75 ++ .../njcn/bpm/config/BpmWebConfiguration.java | 35 + .../njcn/bpm/config/FlowableWebFilter.java | 38 + .../bpm/controller/BpmActivityController.java | 41 + .../BpmProcessInstanceController.java | 195 ++++ .../bpm/controller/BpmTaskController.java | 302 +++++++ .../task/ProcessInstanceCopyMapper.java | 16 + .../task/impl/ProcessInstanceCopyMapper.xml | 5 + .../bpm/service/task/IBpmActivityService.java | 31 + .../task/IBpmProcessInstanceService.java | 186 ++-- .../bpm/service/task/IBpmTaskService.java | 187 ++++ .../task/IProcessInstanceCopyService.java | 36 + .../task/impl/BpmActivityServiceImpl.java | 41 + .../impl/BpmProcessInstanceServiceImpl.java | 332 +++---- .../service/task/impl/BpmTaskServiceImpl.java | 836 ++++++++++++++++++ .../impl/ProcessInstanceCopyServiceImpl.java | 82 ++ .../bpm/strategy/BpmTaskCandidateInvoker.java | 7 +- .../njcn/bpm/utils/BpmActivityConvert.java | 29 + .../bpm/utils/BpmProcessInstanceConvert.java | 125 +++ .../com/njcn/bpm/utils/BpmTaskConvert.java | 180 ++++ .../com/njcn/bpm/utils/FlowableUtils.java | 4 +- .../com/njcn/autocode/utils/GenerateCode.java | 8 +- .../njcn/common/pojo/constant/ServerInfo.java | 1 + .../supervision/enums/UserNatureEnum.java | 2 + .../pojo/vo/user/UserReportVO.java | 7 + pqs-supervision/supervision-boot/pom.xml | 6 + .../user/UserReportManageController.java | 16 +- .../service/user/UserReportPOService.java | 2 + .../user/impl/UserReportPOServiceImpl.java | 26 +- .../com/njcn/user/api/UserFeignClient.java | 3 + .../UserFeignClientFallbackFactory.java | 6 + .../java/com/njcn/user/pojo/vo/UserVO.java | 6 + .../njcn/user/controller/UserController.java | 11 + .../java/com/njcn/user/mapper/UserMapper.java | 2 + .../njcn/user/mapper/mapping/UserMapper.xml | 22 + .../com/njcn/user/service/IUserService.java | 4 + .../user/service/impl/UserServiceImpl.java | 20 +- 71 files changed, 3810 insertions(+), 302 deletions(-) create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmCommentTypeEnum.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmDeleteReasonEnum.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskSignTypeEnum.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskStatusEnum.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/WebFilterOrderEnum.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstanceCancelParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstanceCreateParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstancePageParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskApproveParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskDelegateParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskRejectParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskReturnParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskSignCreateParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskSignDeleteParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskTransferParam.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/task/ProcessInstanceCopy.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/instance/BpmProcessInstanceVO.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/task/BpmActivityVO.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/task/BpmTaskVO.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/KeyValue.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/MapUtils.java create mode 100644 pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ObjectUtils.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmProcessInstanceEventListener.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmTaskEventListener.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmWebConfiguration.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/FlowableWebFilter.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmActivityController.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmProcessInstanceController.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmTaskController.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/task/ProcessInstanceCopyMapper.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/task/impl/ProcessInstanceCopyMapper.xml create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmActivityService.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmTaskService.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IProcessInstanceCopyService.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmActivityServiceImpl.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmTaskServiceImpl.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/ProcessInstanceCopyServiceImpl.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmActivityConvert.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmProcessInstanceConvert.java create mode 100644 pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmTaskConvert.java diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/api/BpmProcessFeignClient.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/api/BpmProcessFeignClient.java index 8304d2e40..1a541a1d9 100644 --- a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/api/BpmProcessFeignClient.java +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/api/BpmProcessFeignClient.java @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RequestParam; * * @author 芋道源码 */ -@FeignClient(value = ServerInfo.SUPERVISION,path = "/process",fallbackFactory = BpmProcessFeignClientFallbackFactory.class) +@FeignClient(value = ServerInfo.BPM,path = "/bpm/processDefinition",fallbackFactory = BpmProcessFeignClientFallbackFactory.class) public interface BpmProcessFeignClient { /** diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmCommentTypeEnum.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmCommentTypeEnum.java new file mode 100644 index 000000000..16f95ea54 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmCommentTypeEnum.java @@ -0,0 +1,46 @@ +package com.njcn.bpm.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程任务的 Comment 评论类型枚举 + * + * @author kehaiyou + */ +@Getter +@AllArgsConstructor +public enum BpmCommentTypeEnum { + + APPROVE("1", "审批通过", "审批通过,原因是:{}"), + REJECT("2", "不通过", "审批不通过:原因是:{}"), + CANCEL("3", "已取消", "系统自动取消,原因是:{}"), + RETURN("4", "退回", "任务被退回,原因是:{}"), + DELEGATE_START("5", "委派发起", "[{}]将任务委派给[{}],委派理由为:{}"), + DELEGATE_END("6", "委派完成", "[{}]完成委派任务,任务重新回到[{}]手中,审批建议为:{}"), + TRANSFER("7", "转派", "[{}]将任务转派给[{}],转派理由为:{}"), + ADD_SIGN("8", "加签", "[{}]{}给了[{}],理由为:{}"), + SUB_SIGN("9", "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"), + ; + + /** + * 操作类型 + * + * 由于 BPM Comment 类型为 String,所以这里就不使用 Integer + */ + private final String type; + /** + * 操作名字 + */ + private final String name; + /** + * 操作描述 + */ + private final String comment; + + public String formatComment(Object... params) { + return StrUtil.format(comment, params); + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmDeleteReasonEnum.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmDeleteReasonEnum.java new file mode 100644 index 000000000..06a9e8af7 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmDeleteReasonEnum.java @@ -0,0 +1,45 @@ +package com.njcn.bpm.enums; + +import cn.hutool.core.util.StrUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程实例/任务的删除原因枚举 + * + * @author 芋道源码 + */ +@Getter +@AllArgsConstructor +public enum BpmDeleteReasonEnum { + + // ========== 流程实例的独有原因 ========== + + REJECT_TASK("审批不通过任务,原因:{}"), // 场景:用户审批不通过任务。修改文案时,需要注意 isRejectReason 方法 + CANCEL_PROCESS_INSTANCE_BY_START_USER("用户主动取消流程,原因:{}"), // 场景:用户主动取消流程 + CANCEL_PROCESS_INSTANCE_BY_ADMIN("管理员【{}】取消流程,原因:{}"), // 场景:管理员取消流程 + + // ========== 流程任务的独有原因 ========== + + CANCEL_BY_SYSTEM("系统自动取消"), // 场景:非常多,比如说:1)多任务审批已经满足条件,无需审批该任务;2)流程实例被取消,无需审批该任务;等等 + ; + + private final String reason; + + /** + * 格式化理由 + * + * @param args 参数 + * @return 理由 + */ + public String format(Object... args) { + return StrUtil.format(reason, args); + } + + // ========== 逻辑 ========== + + public static boolean isRejectReason(String reason) { + return StrUtil.startWith(reason, "审批不通过任务,原因:"); + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmResponseEnum.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmResponseEnum.java index 73b287423..43aa29794 100644 --- a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmResponseEnum.java +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmResponseEnum.java @@ -4,6 +4,7 @@ import lombok.Getter; /** * 异常处理类 + * * @author qijian * @version 1.0.0 * @date 2022年11月11日 09:56 @@ -15,33 +16,58 @@ public enum BpmResponseEnum { * 过程监督异常响应码的范围: * A00550 ~ A00649 */ - BPM_COMMON_ERROR("A00568","工作流模块异常"), + BPM_COMMON_ERROR("A00568", "工作流模块异常"), - BPM_XML_ERROR("A00568","流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!"), + BPM_XML_ERROR("A00568", "流程标识格式不正确,需要以字母或下划线开头,后接任意字母、数字、中划线、下划线、句点!"), - BPM_MODEL_REPEAT("A00568","流程标识已存在"), + BPM_MODEL_REPEAT("A00568", "流程标识已存在"), - BPM_MODEL_NOT_EXIST("A00568","流程模型不存在"), + BPM_MODEL_NOT_EXIST("A00568", "流程模型不存在"), - PROCESS_DEFINITION_NOT_EXISTS("A00568","流程定义不存在"), + PROCESS_DEFINITION_NOT_EXISTS("A00568", "流程定义不存在"), + TASK_NOT_EXISTS("A00568", "流程任务不存在"), + TASK_OPERATE_FAIL_ASSIGN_NOT_SELF("A00568", "操作失败,原因:该任务的审批人不是你"), PROCESS_DEFINITION_IS_SUSPENDED("A00568", "流程定义处于挂起状态"), - FORM_NOT_EXISTS("A00568","动态表单不存在"), + FORM_NOT_EXISTS("A00568", "动态表单不存在"), - MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG("A00568","部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"), + MODEL_DEPLOY_FAIL_FORM_NOT_CONFIG("A00568", "部署流程失败,原因:流程表单未配置,请点击【修改流程】按钮进行配置"), - BPM_START_EVENT_NOT_EXIST("A00568","起始事件不存在"), + BPM_START_EVENT_NOT_EXIST("A00568", "起始事件不存在"), - MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS("A00568","部署流程失败,原因:BPMN 流程图中,用户任务的名字不存在"), + MODEL_DEPLOY_FAIL_BPMN_USER_TASK_NAME_NOT_EXISTS("A00568", "部署流程失败,原因:BPMN 流程图中,用户任务的名字不存在"), - REPEAT_NAME_FORM("A00568","流程表单名称重复"), + REPEAT_NAME_FORM("A00568", "流程表单名称重复"), - REPEAT_CATEGORY_NAME_FORM("A00568","流程类型名称重复"), + REPEAT_CATEGORY_NAME_FORM("A00568", "流程类型名称重复"), + + PROCESS_INSTANCE_NOT_EXISTS ("A00568", "流程实例不存在"), + + TASK_IS_PENDING ("A00568", "当前任务处于挂起状态,不能操作"), + + TASK_TARGET_NODE_NOT_EXISTS ("A00568", " 目标节点不存在"), + + TASK_RETURN_FAIL_SOURCE_TARGET_ERROR ("A00568", "回退任务失败,目标节点是在并行网关上或非同一路线上,不可跳转"), + + PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS("A00568", "流程取消失败,流程不处于运行中"), + + TASK_DELEGATE_FAIL_USER_REPEAT("A00568", "任务委派失败,委派人和当前审批人为同一人"), + + TASK_DELEGATE_FAIL_USER_NOT_EXISTS("A00568", "任务委派失败,被委派人不存在"), + + TASK_TRANSFER_FAIL_USER_REPEAT("A00568", "任务转办失败,转办人和当前审批人为同一人"), + + TASK_TRANSFER_FAIL_USER_NOT_EXISTS("A00568", "任务转办失败,转办人不存在"), + + TASK_SIGN_CREATE_USER_NOT_EXIST("A00568", "任务加签:选择的用户不存在"), + + TASK_SIGN_DELETE_NO_PARENT("A00568", "任务减签失败,被减签的任务必须是通过加签生成的任务"), + + PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF("A00568", "流程取消失败,该流程不是你发起的"); - ; private final String code; diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskSignTypeEnum.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskSignTypeEnum.java new file mode 100644 index 000000000..21cddfb2e --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskSignTypeEnum.java @@ -0,0 +1,47 @@ +package com.njcn.bpm.enums; + +import cn.hutool.core.util.ArrayUtil; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程任务的加签类型枚举 + * + * @author kehaiyou + */ +@Getter +@AllArgsConstructor +public enum BpmTaskSignTypeEnum { + + /** + * 向前加签,需要前置任务审批完成,才回到原审批人 + */ + BEFORE("before", "向前加签"), + /** + * 向后加签,需要后置任务全部审批完,才会通过原审批人节点 + */ + AFTER("after", "向后加签"); + + /** + * 类型 + */ + private final String type; + /** + * 名字 + */ + private final String name; + + public static String nameOfType(String type) { + for (BpmTaskSignTypeEnum value : values()) { + if (value.type.equals(type)) { + return value.name; + } + } + return null; + } + + public static BpmTaskSignTypeEnum of(String type) { + return ArrayUtil.firstMatch(value -> value.getType().equals(type), values()); + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskStatusEnum.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskStatusEnum.java new file mode 100644 index 000000000..7affa1de5 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/BpmTaskStatusEnum.java @@ -0,0 +1,61 @@ +package com.njcn.bpm.enums; + +import com.njcn.bpm.utils.ObjectUtils; +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 流程任务 Task 的状态枚举 + * + * @author jason + */ +@Getter +@AllArgsConstructor +public enum BpmTaskStatusEnum { + + RUNNING(1, "审批中"), + APPROVE(2, "审批通过"), + REJECT(3, "审批不通过"), + CANCEL(4, "已取消"), + + RETURN(5, "已退回"), + DELEGATE(6, "委派中"), + + /** + * 使用场景: + * 1. 任务被向后【加签】时,它在审批通过后,会变成 APPROVING 这个状态,然后等到【加签】出来的任务都被审批后,才会变成 APPROVE 审批通过 + */ + APPROVING(7, "审批通过中"), + /** + * 使用场景: + * 1. 任务被向前【加签】时,它会变成 WAIT 状态,需要等待【加签】出来的任务被审批后,它才能继续变为 RUNNING 继续审批 + * 2. 任务被向后【加签】时,【加签】出来的任务处于 WAIT 状态,它们需要等待该任务被审批后,它们才能继续变为 RUNNING 继续审批 + */ + WAIT(0, "待审批"); + + /** + * 状态 + *

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

+ * 主要用于一些状态更新的逻辑,如果已经是最终状态,就不再进行更新 + * + * @param status 状态 + * @return 是否 + */ + public static boolean isEndStatus(Integer status) { + return ObjectUtils.equalsAny(status, + APPROVE.getStatus(), REJECT.getStatus(), CANCEL.getStatus(), + RETURN.getStatus(), APPROVING.getStatus()); + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/WebFilterOrderEnum.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/WebFilterOrderEnum.java new file mode 100644 index 000000000..28197e7fa --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/enums/WebFilterOrderEnum.java @@ -0,0 +1,34 @@ +package com.njcn.bpm.enums; + +/** + * Web 过滤器顺序的枚举类,保证过滤器按照符合我们的预期 + * + * 考虑到每个 starter 都需要用到该工具类,所以放到 common 模块下的 enums 包下 + * + * @author 芋道源码 + */ +public interface WebFilterOrderEnum { + + int CORS_FILTER = Integer.MIN_VALUE; + + int TRACE_FILTER = CORS_FILTER + 1; + + int REQUEST_BODY_CACHE_FILTER = Integer.MIN_VALUE + 500; + + // OrderedRequestContextFilter 默认为 -105,用于国际化上下文等等 + + int TENANT_CONTEXT_FILTER = - 104; // 需要保证在 ApiAccessLogFilter 前面 + + int API_ACCESS_LOG_FILTER = -103; // 需要保证在 RequestBodyCacheFilter 后面 + + int XSS_FILTER = -102; // 需要保证在 RequestBodyCacheFilter 后面 + + // Spring Security Filter 默认为 -100,可见 org.springframework.boot.autoconfigure.security.SecurityProperties 配置属性类 + + int TENANT_SECURITY_FILTER = -99; // 需要保证在 Spring Security 过滤器后面 + + int FLOWABLE_FILTER = -98; // 需要保证在 Spring Security 过滤后面 + + int DEMO_FILTER = Integer.MAX_VALUE; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java new file mode 100644 index 000000000..aec9aeb08 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmMessageSendWhenProcessInstanceApproveReqDTO.java @@ -0,0 +1,27 @@ +package com.njcn.bpm.pojo.dto; + +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * BPM 发送流程实例被通过 Request DTO + */ +@Data +public class BpmMessageSendWhenProcessInstanceApproveReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + @NotNull(message = "发起人的用户编号") + private String startUserId; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java new file mode 100644 index 000000000..d5aaa1461 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/dto/BpmMessageSendWhenProcessInstanceRejectReqDTO.java @@ -0,0 +1,34 @@ +package com.njcn.bpm.pojo.dto; + +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +/** + * BPM 发送流程实例被不通过 Request DTO + */ +@Data +public class BpmMessageSendWhenProcessInstanceRejectReqDTO { + + /** + * 流程实例的编号 + */ + @NotEmpty(message = "流程实例的编号不能为空") + private String processInstanceId; + /** + * 流程实例的名字 + */ + @NotEmpty(message = "流程实例的名字不能为空") + private String processInstanceName; + + @NotNull(message = "发起人的用户编号") + private String startUserId; + + /** + * 不通过理由 + */ + @NotEmpty(message = "不通过理由不能为空") + private String reason; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmCategoryParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmCategoryParam.java index fa7a964ed..0961bb217 100644 --- a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmCategoryParam.java +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmCategoryParam.java @@ -24,14 +24,14 @@ public class BpmCategoryParam implements Serializable { * 分类名 */ @ApiModelProperty("分类名") - @NotNull(message = "分类名不能为空") + @NotBlank(message = "分类名不能为空") private String name; /** * 分类标志 */ @ApiModelProperty("分类标志") - @NotNull(message = "分类标志不能为空") + @NotBlank(message = "分类标志不能为空") private String code; /** diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmFormParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmFormParam.java index 549225c5b..54581548a 100644 --- a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmFormParam.java +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmFormParam.java @@ -20,21 +20,21 @@ public class BpmFormParam implements Serializable { * 表单名 */ @ApiModelProperty("表单名称") - @NotNull(message = "表单名称不能为空") + @NotBlank(message = "表单名称不能为空") private String name; /** * 状态 */ @ApiModelProperty("表单状态") - @NotNull(message = "表单状态不能为空") + @NotBlank(message = "表单状态不能为空") private Integer status; /** * 表单的配置 */ @ApiModelProperty("表单的配置-JSON 字符串") - @NotNull(message = "表单的配置不能为空") + @NotBlank(message = "表单的配置不能为空") private String conf; /** @@ -42,7 +42,7 @@ public class BpmFormParam implements Serializable { * */ @ApiModelProperty("表单项的数组-JSON 字符串的数组") - @NotNull(message = "表单项的数组不能为空") + @NotBlank(message = "表单项的数组不能为空") private List fields; /** diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmModelParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmModelParam.java index 5ba5ea04d..e9ea14e9e 100644 --- a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmModelParam.java +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/BpmModelParam.java @@ -20,11 +20,11 @@ public class BpmModelParam implements Serializable { @ApiModelProperty("流程标识") - @NotNull(message = "流程标识不能为空") + @NotBlank(message = "流程标识不能为空") private String key; @ApiModelProperty("流程名称") - @NotNull(message = "流程名称不能为空") + @NotBlank(message = "流程名称不能为空") private String name; @ApiModelProperty( "流程图标") diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstanceCancelParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstanceCancelParam.java new file mode 100644 index 000000000..cc0eac84b --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstanceCancelParam.java @@ -0,0 +1,21 @@ +package com.njcn.bpm.pojo.param.instance; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +@Schema(description = "管理后台 - 流程实例的取消 Request VO") +@Data +public class BpmProcessInstanceCancelParam { + + @ApiModelProperty("流程实例的编号") + @NotEmpty(message = "流程实例的编号不能为空") + private String id; + + @ApiModelProperty("取消原因") + @NotEmpty(message = "取消原因不能为空") + private String reason; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstanceCreateParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstanceCreateParam.java new file mode 100644 index 000000000..dcc90f1d2 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstanceCreateParam.java @@ -0,0 +1,26 @@ +package com.njcn.bpm.pojo.param.instance; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 流程实例的创建 Request VO") +@Data +public class BpmProcessInstanceCreateParam { + + @NotEmpty(message = "流程定义编号不能为空") + @ApiModelProperty("流程定义的编号") + private String processDefinitionId; + + @Schema(description = "变量实例(动态表单)") + @ApiModelProperty("变量实例(动态表单)") + private Map variables; + + @ApiModelProperty("发起人自选审批人 Map") + private Map> startUserSelectAssignees; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstancePageParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstancePageParam.java new file mode 100644 index 000000000..493e8d212 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/instance/BpmProcessInstancePageParam.java @@ -0,0 +1,28 @@ +package com.njcn.bpm.pojo.param.instance; + +import com.njcn.web.pojo.param.BaseParam; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + + +@Schema(description = "管理后台 - 流程实例分页") +@Data +public class BpmProcessInstancePageParam extends BaseParam { + + @ApiModelProperty("流程名称") + private String name; + + @ApiModelProperty("流程定义的编号") + private String processDefinitionId; + + @ApiModelProperty("流程实例的状态") + private Integer status; + + @ApiModelProperty("流程分类") + private String category; + + @ApiModelProperty("发起用户编号") + private String startUserId; // 注意,只有在【流程实例】菜单,才使用该参数 + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskApproveParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskApproveParam.java new file mode 100644 index 000000000..295985157 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskApproveParam.java @@ -0,0 +1,29 @@ +package com.njcn.bpm.pojo.param.task; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import java.util.Collection; +import java.util.Map; + +@Schema(description = "管理后台 - 通过流程任务的 Request VO") +@Data +public class BpmTaskApproveParam { + + @ApiModelProperty("任务编号") + @NotBlank(message = "任务编号不能为空") + private String id; + + @ApiModelProperty("审批意见") + @NotBlank(message = "审批意见不能为空") + private String reason; + + @ApiModelProperty("抄送的用户编号数组") + private Collection copyUserIds; + + @ApiModelProperty("变量实例(动态表单)") + private Map variables; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskDelegateParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskDelegateParam.java new file mode 100644 index 000000000..91db4cb96 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskDelegateParam.java @@ -0,0 +1,26 @@ +package com.njcn.bpm.pojo.param.task; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 委派流程任务的 Request VO") +@Data +public class BpmTaskDelegateParam { + + @ApiModelProperty("任务编号") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @ApiModelProperty("被委派人ID") + @NotNull(message = "被委派人 ID 不能为空") + private String delegateUserId; + + @ApiModelProperty("委派原因") + @NotEmpty(message = "委派原因不能为空") + private String reason; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskParam.java new file mode 100644 index 000000000..4887b0ee1 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskParam.java @@ -0,0 +1,35 @@ +package com.njcn.bpm.pojo.param.task; + +import com.njcn.bpm.pojo.param.PageParam; +import com.njcn.web.pojo.param.BaseParam; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.EqualsAndHashCode; + + +@Schema(description = "管理后台 - 流程任务的的分页 Request VO") // 待办、已办,都使用该分页 +@Data +public class BpmTaskParam extends PageParam { + + @Schema(description = "流程任务名", example = "芋道") + private String name; + + + + /** + * 待办事项分页查询实体 + */ + @Data + @EqualsAndHashCode(callSuper = true) + public static class BpmTaskQueryParam extends BaseParam { + + /** + * 表单名称 + */ + @ApiModelProperty("流程任务名") + private String name; + + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskRejectParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskRejectParam.java new file mode 100644 index 000000000..ef5e1420a --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskRejectParam.java @@ -0,0 +1,24 @@ +package com.njcn.bpm.pojo.param.task; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; + +@Schema(description = "管理后台 - 不通过流程任务的 Request VO") +@Data +public class BpmTaskRejectParam { + + + @ApiModelProperty("任务编号") + @NotBlank(message = "任务编号不能为空") + private String id; + + + @ApiModelProperty("审批意见") + @NotBlank(message = "审批意见不能为空") + private String reason; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskReturnParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskReturnParam.java new file mode 100644 index 000000000..9c5a17b7b --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskReturnParam.java @@ -0,0 +1,29 @@ +package com.njcn.bpm.pojo.param.task; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; + +@Schema(description = "管理后台 - 回退流程任务的 Request VO") +@Data +public class BpmTaskReturnParam { + + + @ApiModelProperty("任务编号") + @NotBlank(message = "任务编号不能为空") + private String id; + + + @ApiModelProperty("回退到的任务 Key") + @NotBlank(message = "回退到的任务 Key 不能为空") + private String targetTaskDefinitionKey; + + + @ApiModelProperty("回退意见") + @NotBlank(message = "回退意见不能为空") + private String reason; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskSignCreateParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskSignCreateParam.java new file mode 100644 index 000000000..a38ae6f4b --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskSignCreateParam.java @@ -0,0 +1,31 @@ +package com.njcn.bpm.pojo.param.task; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import java.util.List; +import java.util.Set; + +@Schema(description = "管理后台 - 加签任务的创建(加签) Request VO") +@Data +public class BpmTaskSignCreateParam { + + @ApiModelProperty("需要加签的任务编号") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @ApiModelProperty("加签的用户编号") + @NotEmpty(message = "加签用户不能为空") + private List userIds; + + @ApiModelProperty("加签类型") + @NotEmpty(message = "加签类型不能为空") + private String type; // 参见 BpmTaskSignTypeEnum 枚举 + + @ApiModelProperty("加签原因") + @NotEmpty(message = "加签原因不能为空") + private String reason; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskSignDeleteParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskSignDeleteParam.java new file mode 100644 index 000000000..60867695d --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskSignDeleteParam.java @@ -0,0 +1,21 @@ +package com.njcn.bpm.pojo.param.task; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; + +@Schema(description = "管理后台 - 加签任务的删除(减签) Request VO") +@Data +public class BpmTaskSignDeleteParam { + + @ApiModelProperty("被减签的任务编号") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @ApiModelProperty("减签原因") + @NotEmpty(message = "减签原因不能为空") + private String reason; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskTransferParam.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskTransferParam.java new file mode 100644 index 000000000..1dbf8b2f3 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/param/task/BpmTaskTransferParam.java @@ -0,0 +1,26 @@ +package com.njcn.bpm.pojo.param.task; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; + +@Schema(description = "管理后台 - 流程任务的转办 Request VO") +@Data +public class BpmTaskTransferParam { + + @ApiModelProperty("任务编号") + @NotEmpty(message = "任务编号不能为空") + private String id; + + @ApiModelProperty("新审批人的用户编号") + @NotNull(message = "新审批人的用户编号不能为空") + private String assigneeUserId; + + @ApiModelProperty("转办原因") + @NotEmpty(message = "转办原因不能为空") + private String reason; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmProcessDefinitionInfo.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmProcessDefinitionInfo.java index 876f74e22..d9f964846 100644 --- a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmProcessDefinitionInfo.java +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/BpmProcessDefinitionInfo.java @@ -15,7 +15,7 @@ import java.util.List; /** * BPM 流程定义的拓信息 - * 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表 + * 主要解决 Flowable {@link} 不支持拓展字段,所以新建该表 * */ @TableName(value = "bpm_process_definition_info", autoResultMap = true) diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/task/ProcessInstanceCopy.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/task/ProcessInstanceCopy.java new file mode 100644 index 000000000..2c8c694ef --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/po/task/ProcessInstanceCopy.java @@ -0,0 +1,70 @@ +package com.njcn.bpm.pojo.po.task; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.njcn.db.bo.BaseEntity; + +import java.io.Serializable; + +import lombok.Data; + +/** + *

+ * BPM 流程实例抄送表 + *

+ * + * @author hongawen + * @since 2024-05-10 + */ +@Data +@TableName("bpm_process_instance_copy") +public class ProcessInstanceCopy extends BaseEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 编号 + */ + private String id; + + /** + * 用户编号,被抄送人 + */ + private String userId; + + /** + * 发起流程的用户编号 + */ + private String startUserId; + + /** + * 流程实例的id + */ + private String processInstanceId; + + /** + * 流程实例的名字 + */ + private String processInstanceName; + + /** + * 流程定义的分类 + */ + private String category; + + /** + * 发起抄送的任务编号 + */ + private String taskId; + + /** + * 任务的名字 + */ + private String taskName; + + /** + * 状态:0-删除 1-正常 + */ + private Integer state; + + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmProcessDefinitionInfoVO.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmProcessDefinitionInfoVO.java index b20e60fbb..666ecaa0b 100644 --- a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmProcessDefinitionInfoVO.java +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/BpmProcessDefinitionInfoVO.java @@ -13,7 +13,6 @@ import java.util.List; /** * BPM 流程定义的拓信息 - * 主要解决 Flowable {@link org.flowable.engine.repository.ProcessDefinition} 不支持拓展字段,所以新建该表 * */ @Data diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/instance/BpmProcessInstanceVO.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/instance/BpmProcessInstanceVO.java new file mode 100644 index 000000000..e713b064b --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/instance/BpmProcessInstanceVO.java @@ -0,0 +1,93 @@ +package com.njcn.bpm.pojo.vo.instance; + +import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台 - 流程实例的VO") +@Data +public class BpmProcessInstanceVO { + + @ApiModelProperty("流程实例的编号") + private String id; + + @ApiModelProperty("流程名称") + private String name; + + @ApiModelProperty("流程分类") + private String category; + + @ApiModelProperty("流程分类名称") + private String categoryName; + + @ApiModelProperty("流程实例的状态") + private Integer status; // 参见 BpmProcessInstanceStatusEnum 枚举 + + @ApiModelProperty("发起时间") + private LocalDateTime startTime; + + @ApiModelProperty("结束时间") + private LocalDateTime endTime; + + @ApiModelProperty("持续时间") + private Long durationInMillis; + + @ApiModelProperty("提交的表单值") + private Map formVariables; + + @ApiModelProperty("业务的唯一标识") + private String businessKey; + + /** + * 发起流程的用户 + */ + private User startUser; + + @ApiModelProperty("流程定义的编号") + private String processDefinitionId; + /** + * 流程定义 + */ + private BpmProcessDefinitionInfoVO processDefinition; + + /** + * 当前审批中的任务 + */ + private List tasks; // 仅在流程实例分页才返回 + + @Schema(description = "用户信息") + @Data + public static class User { + + @ApiModelProperty("用户编号") + private String id; + + @ApiModelProperty("用户昵称") + private String name; + + @ApiModelProperty("部门编号") + private String deptId; + + @ApiModelProperty("部门名称") + private String deptName; + + } + + @Schema(description = "流程任务") + @Data + public static class Task { + + @ApiModelProperty("流程任务的编号") + private String id; + + @ApiModelProperty("任务名称") + private String name; + + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/task/BpmActivityVO.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/task/BpmActivityVO.java new file mode 100644 index 000000000..f12a29c5c --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/task/BpmActivityVO.java @@ -0,0 +1,27 @@ +package com.njcn.bpm.pojo.vo.task; + +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - 流程活动的 Response VO") +@Data +public class BpmActivityVO { + + @ApiModelProperty("流程活动的标识") + private String key; + @ApiModelProperty("流程活动的类型") + private String type; + + @ApiModelProperty("流程活动的开始时间") + private LocalDateTime startTime; + + @ApiModelProperty("流程活动的结束时间") + private LocalDateTime endTime; + + @ApiModelProperty("关联的流程任务的编号") + private String taskId; // 关联的流程任务,只有 UserTask 等类型才有 + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/task/BpmTaskVO.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/task/BpmTaskVO.java new file mode 100644 index 000000000..c9db955fe --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/pojo/vo/task/BpmTaskVO.java @@ -0,0 +1,106 @@ +package com.njcn.bpm.pojo.vo.task; + +import com.njcn.bpm.pojo.vo.instance.BpmProcessInstanceVO; +import io.swagger.annotations.ApiModelProperty; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Map; + +@Schema(description = "管理后台-流程任务VO") +@Data +public class BpmTaskVO { + + @ApiModelProperty("任务编号") + private String id; + + @ApiModelProperty("任务名字") + private String name; + + @ApiModelProperty("创建时间") + private LocalDateTime createTime; + + @ApiModelProperty("结束时间") + private LocalDateTime endTime; + + @Schema(description = "持续时间", example = "1000") + @ApiModelProperty("持续时间") + private Long durationInMillis; + + @ApiModelProperty("任务状态") + private Integer status; // 参见 BpmTaskStatusEnum 枚举 + + @ApiModelProperty("审批理由") + private String reason; + + /** + * 负责人的用户信息 + */ + private BpmProcessInstanceVO.User ownerUser; + + /** + * 审核的用户信息 + */ + private BpmProcessInstanceVO.User assigneeUser; + + + @ApiModelProperty("任务定义的标识") + private String taskDefinitionKey; + + @ApiModelProperty("所属流程实例编号") + private String processInstanceId; + /** + * 所属流程实例 + */ + private ProcessInstance processInstance; + + @ApiModelProperty("父任务编号") + private String parentTaskId; + + + @ApiModelProperty("子任务列表(由加签生成)") + private List children; + + @ApiModelProperty("表单编号") + private String formId; + + + @ApiModelProperty("表单名字") + private String formName; + + + @ApiModelProperty("表单的配置-JSON 字符串") + private String formConf; + + @ApiModelProperty("表单项的数组") + private List formFields; + + @ApiModelProperty("提交的表单值") + private Map formVariables; + + @Data + @Schema(description = "流程实例") + public static class ProcessInstance { + + @ApiModelProperty("流程实例编号") + private String id; + + @ApiModelProperty("流程实例名称") + private String name; + + @ApiModelProperty("提交时间") + private LocalDateTime createTime; + + @ApiModelProperty("流程定义的编号") + private String processDefinitionId; + + /** + * 发起人的用户信息 + */ + private BpmProcessInstanceVO.User startUser; + + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/BeanUtils.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/BeanUtils.java index 942f3d39a..f4d6becbf 100644 --- a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/BeanUtils.java +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/BeanUtils.java @@ -2,6 +2,7 @@ package com.njcn.bpm.utils; import cn.hutool.core.bean.BeanUtil; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.dto.PageResult; import java.util.List; import java.util.function.Consumer; @@ -42,20 +43,20 @@ public class BeanUtils { } return list; } -// -// public static Page toBean(Page source, Class targetType) { -// return toBean(source, targetType, null); -// } -// -// public static Page toBean(Page source, Class targetType, Consumer peek) { -// if (source == null) { -// return null; -// } -// List list = toBean(source.getRecords(), targetType); -// if (peek != null) { -// list.forEach(peek); -// } -// return new Page<>(list, source.getTotal()); -// } + + public static PageResult toBean(PageResult source, Class targetType) { + return toBean(source, targetType, null); + } + + public static PageResult toBean(PageResult source, Class targetType, Consumer peek) { + if (source == null) { + return null; + } + List list = toBean(source.getList(), targetType); + if (peek != null) { + list.forEach(peek); + } + return new PageResult<>(list, source.getTotal()); + } } \ No newline at end of file diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/KeyValue.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/KeyValue.java new file mode 100644 index 000000000..1e6472f16 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/KeyValue.java @@ -0,0 +1,22 @@ +package com.njcn.bpm.utils; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +/** + * Key Value 的键值对 + * + * @author 芋道源码 + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class KeyValue implements Serializable { + + private K key; + private V value; + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/MapUtils.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/MapUtils.java new file mode 100644 index 000000000..78a3f6c85 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/MapUtils.java @@ -0,0 +1,67 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjUtil; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +/** + * Map 工具类 + * + * @author 芋道源码 + */ +public class MapUtils { + + /** + * 从哈希表表中,获得 keys 对应的所有 value 数组 + * + * @param multimap 哈希表 + * @param keys keys + * @return value 数组 + */ + public static List getList(Multimap multimap, Collection keys) { + List result = new ArrayList<>(); + keys.forEach(k -> { + Collection values = multimap.get(k); + if (CollectionUtil.isEmpty(values)) { + return; + } + result.addAll(values); + }); + return result; + } + + /** + * 从哈希表查找到 key 对应的 value,然后进一步处理 + * key 为 null 时, 不处理 + * 注意,如果查找到的 value 为 null 时,不进行处理 + * + * @param map 哈希表 + * @param key key + * @param consumer 进一步处理的逻辑 + */ + public static void findAndThen(Map map, K key, Consumer consumer) { + if (ObjUtil.isNull(key) || CollUtil.isEmpty(map)) { + return; + } + V value = map.get(key); + if (value == null) { + return; + } + consumer.accept(value); + } + + public static Map convertMap(List> keyValues) { + Map map = Maps.newLinkedHashMapWithExpectedSize(keyValues.size()); + keyValues.forEach(keyValue -> map.put(keyValue.getKey(), keyValue.getValue())); + return map; + } + +} diff --git a/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ObjectUtils.java b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ObjectUtils.java new file mode 100644 index 000000000..164548068 --- /dev/null +++ b/pqs-bpm/bpm-api/src/main/java/com/njcn/bpm/utils/ObjectUtils.java @@ -0,0 +1,63 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; + +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.function.Consumer; + +/** + * Object 工具类 + * + * @author 芋道源码 + */ +public class ObjectUtils { + + /** + * 复制对象,并忽略 Id 编号 + * + * @param object 被复制对象 + * @param consumer 消费者,可以二次编辑被复制对象 + * @return 复制后的对象 + */ + public static T cloneIgnoreId(T object, Consumer consumer) { + T result = ObjectUtil.clone(object); + // 忽略 id 编号 + Field field = ReflectUtil.getField(object.getClass(), "id"); + if (field != null) { + ReflectUtil.setFieldValue(result, field, null); + } + // 二次编辑 + if (result != null) { + consumer.accept(result); + } + return result; + } + + public static > T max(T obj1, T obj2) { + if (obj1 == null) { + return obj2; + } + if (obj2 == null) { + return obj1; + } + return obj1.compareTo(obj2) > 0 ? obj1 : obj2; + } + + @SafeVarargs + public static T defaultIfNull(T... array) { + for (T item : array) { + if (item != null) { + return item; + } + } + return null; + } + + @SafeVarargs + public static boolean equalsAny(T obj, T... array) { + return Arrays.asList(array).contains(obj); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmProcessInstanceEventListener.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmProcessInstanceEventListener.java new file mode 100644 index 000000000..42d1b009e --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmProcessInstanceEventListener.java @@ -0,0 +1,47 @@ +package com.njcn.bpm.config; + +import com.google.common.collect.ImmutableSet; +import com.njcn.bpm.service.task.IBpmProcessInstanceService; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.flowable.engine.delegate.event.FlowableCancelledEvent; +import org.flowable.engine.runtime.ProcessInstance; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.Set; + +/** + * 监听 {@link ProcessInstance} 的状态变更,更新其对应的 status 状态 + * + * @author jason + */ +@Component +public class BpmProcessInstanceEventListener extends AbstractFlowableEngineEventListener { + + @Resource + @Lazy + private IBpmProcessInstanceService processInstanceService; + + public static final Set PROCESS_INSTANCE_EVENTS = ImmutableSet.builder() + .add(FlowableEngineEventType.PROCESS_CANCELLED) + .add(FlowableEngineEventType.PROCESS_COMPLETED) + .build(); + + public BpmProcessInstanceEventListener(){ + super(PROCESS_INSTANCE_EVENTS); + } + + @Override + protected void processCancelled(FlowableCancelledEvent event) { + processInstanceService.updateProcessInstanceWhenCancel(event); + } + + @Override + protected void processCompleted(FlowableEngineEntityEvent event) { + processInstanceService.updateProcessInstanceWhenApprove((ProcessInstance)event.getEntity()); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmTaskEventListener.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmTaskEventListener.java new file mode 100644 index 000000000..5efd4adc4 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmTaskEventListener.java @@ -0,0 +1,75 @@ +package com.njcn.bpm.config; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.google.common.collect.ImmutableSet; +import com.njcn.bpm.service.task.IBpmActivityService; +import com.njcn.bpm.service.task.IBpmTaskService; +import lombok.extern.slf4j.Slf4j; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; +import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; +import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; +import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent; +import org.flowable.engine.history.HistoricActivityInstance; +import org.flowable.task.api.Task; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; +import java.util.List; +import java.util.Set; + +/** + * 监听 {@link Task} 的开始与完成 + * + * @author jason + */ +@Component +@Slf4j +public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { + + @Resource + @Lazy // 解决循环依赖 + private IBpmTaskService taskService; + @Resource + @Lazy // 解决循环依赖 + private IBpmActivityService activityService; + + public static final Set TASK_EVENTS = ImmutableSet.builder() + .add(FlowableEngineEventType.TASK_CREATED) + .add(FlowableEngineEventType.TASK_ASSIGNED) +// .add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,所以不需要监听了。 + .add(FlowableEngineEventType.ACTIVITY_CANCELLED) + .build(); + + public BpmTaskEventListener(){ + super(TASK_EVENTS); + } + + @Override + protected void taskCreated(FlowableEngineEntityEvent event) { + taskService.updateTaskStatusWhenCreated((Task) event.getEntity()); + } + + @Override + protected void taskAssigned(FlowableEngineEntityEvent event) { + taskService.updateTaskExtAssign((Task)event.getEntity()); + } + + @Override + protected void activityCancelled(FlowableActivityCancelledEvent event) { + List activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId()); + if (CollUtil.isEmpty(activityList)) { + log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId()); + return; + } + // 遍历处理 + activityList.forEach(activity -> { + if (StrUtil.isEmpty(activity.getTaskId())) { + return; + } + taskService.updateTaskStatusWhenCanceled(activity.getTaskId()); + }); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmWebConfiguration.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmWebConfiguration.java new file mode 100644 index 000000000..c78e3706a --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/BpmWebConfiguration.java @@ -0,0 +1,35 @@ +package com.njcn.bpm.config; + +import com.njcn.bpm.enums.WebFilterOrderEnum; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * bpm 模块的 web 组件的 Configuration + * + * @author 芋道源码 + */ +@Configuration(proxyBeanMethods = false) +public class BpmWebConfiguration { + +// /** +// * bpm 模块的 API 分组 +// */ +// @Bean +// public GroupedOpenApi bpmGroupedOpenApi() { +// return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("bpm"); +// } + + /** + * 配置 Flowable Web 过滤器 + */ + @Bean + public FilterRegistrationBean flowableWebFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new FlowableWebFilter()); + registrationBean.setOrder(WebFilterOrderEnum.FLOWABLE_FILTER); + return registrationBean; + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/FlowableWebFilter.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/FlowableWebFilter.java new file mode 100644 index 000000000..8a0a5aa9c --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/config/FlowableWebFilter.java @@ -0,0 +1,38 @@ +package com.njcn.bpm.config; + +import cn.hutool.core.util.StrUtil; +import com.njcn.bpm.utils.FlowableUtils; +import com.njcn.web.utils.RequestUtil; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Flowable Web 过滤器,将 userId 设置到 {@link org.flowable.common.engine.impl.identity.Authentication} 中 + * + * @author jason + */ +public class FlowableWebFilter extends OncePerRequestFilter { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) + throws ServletException, IOException { + try { + String userIndex = RequestUtil.getUserIndex(); + // 设置工作流的用户 + if (StrUtil.isNotBlank(userIndex)) { + FlowableUtils.setAuthenticatedUserId(userIndex); + } + // 过滤 + chain.doFilter(request, response); + } finally { + // 清理 + FlowableUtils.clearAuthenticatedUserId(); + } + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmActivityController.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmActivityController.java new file mode 100644 index 000000000..3b781a10c --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmActivityController.java @@ -0,0 +1,41 @@ +package com.njcn.bpm.controller; + +import com.njcn.bpm.pojo.vo.task.BpmActivityVO; +import com.njcn.bpm.service.task.IBpmActivityService; +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.HttpResultUtil; +import com.njcn.web.controller.BaseController; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.List; + + +@Tag(name = "管理后台 - 流程活动实例") +@RestController +@RequestMapping("/bpm/activity") +@Validated +public class BpmActivityController extends BaseController { + + @Resource + private IBpmActivityService activityService; + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @GetMapping("/list") + @ApiOperation("生成指定流程实例的高亮流程图") + @ApiImplicitParam(name = "processInstanceId", value = "查询参数", required = true) + public HttpResult> getActivityList(String processInstanceId) { + String methodDescribe = getMethodDescribe("getActivityList"); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, activityService.getActivityListByProcessInstanceId(processInstanceId), methodDescribe); + } +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmProcessInstanceController.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmProcessInstanceController.java new file mode 100644 index 000000000..d0fc3a941 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmProcessInstanceController.java @@ -0,0 +1,195 @@ +package com.njcn.bpm.controller; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.dto.BpmProcessInstanceCreateReqDTO; +import com.njcn.bpm.pojo.dto.PageResult; +import com.njcn.bpm.pojo.param.instance.BpmProcessInstanceCancelParam; +import com.njcn.bpm.pojo.param.instance.BpmProcessInstanceCreateParam; +import com.njcn.bpm.pojo.param.instance.BpmProcessInstancePageParam; +import com.njcn.bpm.pojo.po.BpmCategory; +import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo; +import com.njcn.bpm.pojo.vo.instance.BpmProcessInstanceVO; +import com.njcn.bpm.pojo.vo.task.BpmTaskVO; +import com.njcn.bpm.service.IBpmCategoryService; +import com.njcn.bpm.service.IBpmProcessDefinitionService; +import com.njcn.bpm.service.task.IBpmProcessInstanceService; +import com.njcn.bpm.service.task.IBpmTaskService; +import com.njcn.bpm.utils.BpmProcessInstanceConvert; +import com.njcn.bpm.utils.BpmnModelUtils; +import com.njcn.bpm.utils.CollectionUtils; +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.HttpResultUtil; +import com.njcn.user.api.DeptFeignClient; +import com.njcn.user.api.UserFeignClient; +import com.njcn.user.pojo.po.Dept; +import com.njcn.user.pojo.vo.UserVO; +import com.njcn.web.controller.BaseController; +import com.njcn.web.factory.PageFactory; +import com.njcn.web.utils.RequestUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.List; +import java.util.Map; + +import static com.njcn.bpm.utils.CollectionUtils.convertList; +import static com.njcn.bpm.utils.CollectionUtils.convertSet; + + +@RestController +@RequestMapping("/bpm/processInstance") +@Validated +@Slf4j +@Api(tags = "管理后台 - 流程实例") +@RequiredArgsConstructor +public class BpmProcessInstanceController extends BaseController { + + @Resource + private IBpmProcessInstanceService processInstanceService; + @Resource + private IBpmTaskService taskService; + @Resource + private IBpmProcessDefinitionService processDefinitionService; + @Resource + private IBpmCategoryService categoryService; + + @Resource + private UserFeignClient userFeignClient; + @Resource + private DeptFeignClient deptFeignClient; + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/myPage") + @ApiOperation("获得我的实例分页列表") + public HttpResult> getProcessInstanceMyPage(@Validated @RequestBody BpmProcessInstancePageParam bpmProcessInstancePageParam) { + String methodDescribe = getMethodDescribe("getProcessInstanceMyPage"); + PageResult pageResult = processInstanceService.getProcessInstancePage(RequestUtil.getUserIndex(), bpmProcessInstancePageParam); + if (CollUtil.isEmpty(pageResult.getList())) { + Page result = new Page<>(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + + // 拼接返回 + Map> taskMap = taskService.getTaskMapByProcessInstanceIds( + convertList(pageResult.getList(), HistoricProcessInstance::getId)); + Map processDefinitionMap = processDefinitionService.getProcessDefinitionMap( + convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + Map categoryMap = categoryService.getCategoryMap( + convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); + PageResult bpmProcessInstanceVOPageResult = BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, processDefinitionMap, categoryMap, taskMap, null, null); + //封装调整为当前系统的格式 + Page page = new Page<>(); + page.setRecords(bpmProcessInstanceVOPageResult.getList()); + page.setTotal(bpmProcessInstanceVOPageResult.getTotal()); + page.setSize(PageFactory.getPageSize(bpmProcessInstancePageParam)); + page.setCurrent(PageFactory.getPageNum(bpmProcessInstancePageParam)); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, page, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/managerPage") + @ApiOperation("获得管理流程实例的分页列表") + public HttpResult> getProcessInstanceManagerPage(@Validated @RequestBody BpmProcessInstancePageParam bpmProcessInstancePageParam) { + String methodDescribe = getMethodDescribe("getProcessInstanceManagerPage"); + PageResult pageResult = processInstanceService.getProcessInstancePage( + null, bpmProcessInstancePageParam); + if (CollUtil.isEmpty(pageResult.getList())) { + Page result = new Page<>(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + // 拼接返回 + Map> taskMap = taskService.getTaskMapByProcessInstanceIds( + convertList(pageResult.getList(), HistoricProcessInstance::getId)); + Map processDefinitionMap = processDefinitionService.getProcessDefinitionMap( + convertSet(pageResult.getList(), HistoricProcessInstance::getProcessDefinitionId)); + Map categoryMap = categoryService.getCategoryMap( + convertSet(processDefinitionMap.values(), ProcessDefinition::getCategory)); + + // 发起人信息 + List userList = userFeignClient.getUserVOByIdList(convertList(pageResult.getList(), HistoricProcessInstance::getStartUserId)).getData(); + Map userMap = CollectionUtils.convertMap(userList, UserVO::getId); + + List deptList = deptFeignClient.getDeptInfoListByIds(convertList(userMap.values(), UserVO::getDeptId)).getData(); + Map deptMap = CollectionUtils.convertMap(deptList, Dept::getId); + + PageResult bpmProcessInstanceVOPageResult = BpmProcessInstanceConvert.INSTANCE.buildProcessInstancePage(pageResult, + processDefinitionMap, categoryMap, taskMap, userMap, deptMap); + //封装调整为当前系统的格式 + Page page = new Page<>(); + page.setRecords(bpmProcessInstanceVOPageResult.getList()); + page.setTotal(bpmProcessInstanceVOPageResult.getTotal()); + page.setSize(PageFactory.getPageSize(bpmProcessInstancePageParam)); + page.setCurrent(PageFactory.getPageNum(bpmProcessInstancePageParam)); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, page, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/create") + @ApiOperation("新建流程实例") + public HttpResult createProcessInstance(@Validated @RequestBody BpmProcessInstanceCreateReqDTO bpmProcessInstanceCreateReqDTO) { + String methodDescribe = getMethodDescribe("createProcessInstance"); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, processInstanceService.createProcessInstance(RequestUtil.getUserIndex(), bpmProcessInstanceCreateReqDTO), methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @GetMapping("/get") + @ApiOperation("获得指定流程实例") + public HttpResult getProcessInstance(String id) { + String methodDescribe = getMethodDescribe("getProcessInstance"); + HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(id); + if (processInstance == null) { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + // 拼接返回 + ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( + processInstance.getProcessDefinitionId()); + BpmProcessDefinitionInfo processDefinitionInfo = processDefinitionService.getProcessDefinitionInfo( + processInstance.getProcessDefinitionId()); + String bpmnXml = BpmnModelUtils.getBpmnXml( + processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId())); + UserVO startUser = userFeignClient.getUserById(processInstance.getStartUserId()).getData(); + Dept dept = null; + if (startUser != null) { + dept = deptFeignClient.getDeptById(startUser.getDeptId()).getData(); + } + BpmProcessInstanceVO bpmProcessInstanceVO = BpmProcessInstanceConvert.INSTANCE.buildProcessInstance(processInstance, + processDefinition, processDefinitionInfo, bpmnXml, startUser, dept); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, bpmProcessInstanceVO, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/cancelByStartUser") + @ApiOperation("用户取消流程实例") + public HttpResult cancelProcessInstanceByStartUser(@Valid @RequestBody BpmProcessInstanceCancelParam cancelReqVO) { + String methodDescribe = getMethodDescribe("cancelProcessInstanceByStartUser"); + processInstanceService.cancelProcessInstanceByStartUser(RequestUtil.getUserIndex(), cancelReqVO); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/cancelByAdmin") + @ApiOperation("管理员取消流程实例") + public HttpResult cancelProcessInstanceByManager( + @Valid @RequestBody BpmProcessInstanceCancelParam cancelReqVO) { + String methodDescribe = getMethodDescribe("cancelProcessInstanceByManager"); + processInstanceService.cancelProcessInstanceByAdmin(RequestUtil.getUserIndex(), cancelReqVO); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmTaskController.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmTaskController.java new file mode 100644 index 000000000..b7e82e675 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/controller/BpmTaskController.java @@ -0,0 +1,302 @@ +package com.njcn.bpm.controller; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.dto.PageResult; +import com.njcn.bpm.pojo.param.task.*; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.vo.task.BpmTaskVO; +import com.njcn.bpm.service.IBpmFormService; +import com.njcn.bpm.service.task.IBpmTaskService; +import com.njcn.bpm.utils.BpmTaskConvert; +import com.njcn.bpm.utils.CollectionUtils; +import com.njcn.common.pojo.annotation.OperateInfo; +import com.njcn.common.pojo.enums.common.LogEnum; +import com.njcn.common.pojo.enums.response.CommonResponseEnum; +import com.njcn.common.pojo.response.HttpResult; +import com.njcn.common.utils.HttpResultUtil; +import com.njcn.user.api.DeptFeignClient; +import com.njcn.user.api.UserFeignClient; +import com.njcn.user.pojo.po.Dept; +import com.njcn.user.pojo.po.User; +import com.njcn.user.pojo.vo.UserVO; +import com.njcn.web.controller.BaseController; +import com.njcn.web.factory.PageFactory; +import com.njcn.web.utils.RequestUtil; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiImplicitParam; +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.TaskInfo; +import org.flowable.bpmn.model.UserTask; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import com.njcn.bpm.service.task.IBpmProcessInstanceService; +import org.flowable.task.api.history.HistoricTaskInstance; + +import javax.annotation.Resource; +import javax.validation.Valid; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.njcn.bpm.utils.CollectionUtils.*; + + +@RestController +@RequestMapping("/bpm/task") +@Validated +@Slf4j +@Api(tags = "管理后台 - 流程任务实例") +@RequiredArgsConstructor +public class BpmTaskController extends BaseController { + + @Resource + private IBpmTaskService taskService; + + @Resource + private IBpmProcessInstanceService processInstanceService; + + @Resource + private IBpmFormService formService; + + @Resource + private UserFeignClient userFeignClient; + + @Resource + private DeptFeignClient deptFeignClient; + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/todoList") + @ApiOperation("获取待办任务分页") + public HttpResult> getTaskTodoPage(@Validated @RequestBody BpmTaskParam.BpmTaskQueryParam bpmTaskQueryParam) { + String methodDescribe = getMethodDescribe("getTaskTodoPage"); + PageResult pageResult = taskService.getTaskTodoPage(RequestUtil.getUserIndex(), bpmTaskQueryParam); + if (CollUtil.isEmpty(pageResult.getList())) { + Page result = new Page<>(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + // 拼接数据 + Map processInstanceMap = processInstanceService.getProcessInstanceMap( + convertSet(pageResult.getList(), Task::getProcessInstanceId)); + List userList = userFeignClient.getUserVOByIdList(convertList(processInstanceMap.values(), ProcessInstance::getStartUserId)).getData(); + Map userMap = CollectionUtils.convertMap(userList, UserVO::getId); + PageResult bpmTaskVOPageResult = BpmTaskConvert.INSTANCE.buildTodoTaskPage(pageResult, processInstanceMap, userMap); + //封装调整为当前系统的格式 + Page page = new Page<>(); + page.setRecords(bpmTaskVOPageResult.getList()); + page.setTotal(bpmTaskVOPageResult.getTotal()); + page.setSize(PageFactory.getPageSize(bpmTaskQueryParam)); + page.setCurrent(PageFactory.getPageNum(bpmTaskQueryParam)); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, page, methodDescribe); + } + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/doneList") + @ApiOperation("获取已办任务分页") + @ApiImplicitParam(name = "bpmTaskQueryParam", value = "查询参数", required = true) + public HttpResult> getTaskDonePage(@Validated @RequestBody BpmTaskParam.BpmTaskQueryParam bpmTaskQueryParam) { + String methodDescribe = getMethodDescribe("getTaskDonePage"); + PageResult pageResult = taskService.getTaskDonePage(RequestUtil.getUserIndex(), bpmTaskQueryParam); + if (CollUtil.isEmpty(pageResult.getList())) { + Page result = new Page<>(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + // 拼接数据 + Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap( + convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); + List userList = userFeignClient.getUserVOByIdList(convertList(processInstanceMap.values(), HistoricProcessInstance::getStartUserId)).getData(); + Map userMap = CollectionUtils.convertMap(userList, UserVO::getId); + PageResult bpmTaskRespVOPageResult = BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, null); + //封装调整为当前系统的格式 + Page page = new Page<>(); + page.setRecords(bpmTaskRespVOPageResult.getList()); + page.setTotal(bpmTaskRespVOPageResult.getTotal()); + page.setSize(PageFactory.getPageSize(bpmTaskQueryParam)); + page.setCurrent(PageFactory.getPageNum(bpmTaskQueryParam)); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, page, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/AllList") + @ApiOperation("获取全部任务的分页") + @ApiImplicitParam(name = "bpmTaskQueryParam", value = "查询参数", required = true) + public HttpResult> getTaskManagerPage(@Validated @RequestBody BpmTaskParam.BpmTaskQueryParam bpmTaskQueryParam) { + String methodDescribe = getMethodDescribe("getTaskManagerPage"); + PageResult pageResult = taskService.getTaskPage(RequestUtil.getUserIndex(), bpmTaskQueryParam); + if (CollUtil.isEmpty(pageResult.getList())) { + Page result = new Page<>(); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, result, methodDescribe); + } + + // 拼接数据 + Map processInstanceMap = processInstanceService.getHistoricProcessInstanceMap( + convertSet(pageResult.getList(), HistoricTaskInstance::getProcessInstanceId)); + // 获得 User 和 Dept Map + List userIds = convertList(processInstanceMap.values(), HistoricProcessInstance::getStartUserId); + userIds.addAll(convertSet(pageResult.getList(), TaskInfo::getAssignee)); + + List userList = userFeignClient.getUserVOByIdList(userIds).getData(); + Map userMap = CollectionUtils.convertMap(userList, UserVO::getId); + List deptList = deptFeignClient.getDeptInfoListByIds(convertList(userMap.values(), UserVO::getDeptId)).getData(); + Map deptMap = CollectionUtils.convertMap(deptList, Dept::getId); + PageResult bpmTaskVOPageResult = BpmTaskConvert.INSTANCE.buildTaskPage(pageResult, processInstanceMap, userMap, deptMap); + + //封装调整为当前系统的格式 + Page page = new Page<>(); + page.setRecords(bpmTaskVOPageResult.getList()); + page.setTotal(bpmTaskVOPageResult.getTotal()); + page.setSize(PageFactory.getPageSize(bpmTaskQueryParam)); + page.setCurrent(PageFactory.getPageNum(bpmTaskQueryParam)); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, page, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @GetMapping("/listByProcessInstanceId") + @ApiOperation("获得指定流程实例的任务列表") + @ApiImplicitParam(name = "processInstanceId", value = "查询参数", required = true) + public HttpResult> getTaskListByProcessInstanceId(String processInstanceId) { + String methodDescribe = getMethodDescribe("getTaskListByProcessInstanceId"); + List taskList = taskService.getTaskListByProcessInstanceId(processInstanceId); + if (CollUtil.isEmpty(taskList)) { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, null, methodDescribe); + } + + // 拼接数据 + HistoricProcessInstance processInstance = processInstanceService.getHistoricProcessInstance(processInstanceId); + // 获得 User 和 Dept Map + List userIds = convertListByFlatMap(taskList, task -> Stream.of(task.getAssignee(), task.getOwner())); + userIds.add(processInstance.getStartUserId()); + List userList = userFeignClient.getUserVOByIdList(userIds).getData(); + Map userMap = CollectionUtils.convertMap(userList, UserVO::getId); + + List deptList = deptFeignClient.getDeptInfoListByIds(convertList(userMap.values(), UserVO::getDeptId)).getData(); + Map deptMap = CollectionUtils.convertMap(deptList, Dept::getId); + // 获得 Form Map + Map formMap = formService.getFormMap(convertSet(taskList, TaskInfo::getFormKey)); + + List bpmTaskRespVOS = BpmTaskConvert.INSTANCE.buildTaskListByProcessInstanceId(taskList, processInstance, + formMap, userMap, deptMap); + + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, bpmTaskRespVOS, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/approve") + @ApiOperation("通过任务") + @ApiImplicitParam(name = "bpmTaskApproveParam", value = "查询参数", required = true) + public HttpResult approveTask(@Validated @RequestBody BpmTaskApproveParam bpmTaskApproveParam) { + String methodDescribe = getMethodDescribe("approveTask"); + taskService.approveTask(RequestUtil.getUserIndex(), bpmTaskApproveParam); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); + } + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/reject") + @ApiOperation("不通过任务") + @ApiImplicitParam(name = "bpmTaskRejectParam", value = "查询参数", required = true) + public HttpResult rejectTask(@Validated @RequestBody BpmTaskRejectParam bpmTaskRejectParam) { + String methodDescribe = getMethodDescribe("rejectTask"); + taskService.rejectTask(RequestUtil.getUserIndex(), bpmTaskRejectParam); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @GetMapping("/listByReturn") + @ApiOperation("获取所有可回退的节点") + @ApiImplicitParam(name = "taskId", value = "查询参数", required = true) + public HttpResult> getTaskListByReturn(@RequestParam("id") String id) { + String methodDescribe = getMethodDescribe("getTaskListByReturn"); + List userTaskList = taskService.getUserTaskListByReturn(id); + List taskList = userTaskList.stream().map(userTask -> { + BpmTaskVO bpmTaskVO = new BpmTaskVO(); + bpmTaskVO.setName(userTask.getName()); + bpmTaskVO.setTaskDefinitionKey(userTask.getId()); + return bpmTaskVO; + }).collect(Collectors.toList()); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, taskList, methodDescribe); + } + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @GetMapping("/return") + @ApiOperation("回退任务") + @ApiImplicitParam(name = "bpmTaskReturnParam", value = "查询参数", required = true) + public HttpResult returnTask(@Validated @RequestBody BpmTaskReturnParam bpmTaskReturnParam) { + String methodDescribe = getMethodDescribe("returnTask"); + taskService.returnTask(RequestUtil.getUserIndex(), bpmTaskReturnParam); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); + } + + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/delegate") + @ApiOperation("委派任务") + @ApiImplicitParam(name = "reqVO", value = "查询参数", required = true) + public HttpResult delegateTask(@Validated @RequestBody BpmTaskDelegateParam reqVO) { + String methodDescribe = getMethodDescribe("delegateTask"); + taskService.delegateTask(RequestUtil.getUserIndex(), reqVO); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/transfer") + @ApiOperation("转派任务") + @ApiImplicitParam(name = "reqVO", value = "查询参数", required = true) + public HttpResult transferTask(@Validated @RequestBody BpmTaskTransferParam reqVO) { + String methodDescribe = getMethodDescribe("transferTask"); + taskService.transferTask(RequestUtil.getUserIndex(), reqVO); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/createSign") + @ApiOperation("加签") + @ApiImplicitParam(name = "reqVO", value = "查询参数", required = true) + public HttpResult createSignTask(@Validated @RequestBody BpmTaskSignCreateParam reqVO) { + String methodDescribe = getMethodDescribe("createSignTask"); + taskService.createSignTask(RequestUtil.getUserIndex(), reqVO); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @PostMapping("/deleteSign") + @ApiOperation("减签") + @ApiImplicitParam(name = "reqVO", value = "查询参数", required = true) + public HttpResult deleteSignTask(@Validated @RequestBody BpmTaskSignDeleteParam reqVO) { + String methodDescribe = getMethodDescribe("createSignTask"); + taskService.deleteSignTask(RequestUtil.getUserIndex(), reqVO); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, true, methodDescribe); + } + + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @GetMapping("/listByParentTaskId") + @ApiOperation("获得指定父级任务的子任务列表") + @ApiImplicitParam(name = "parentTaskId", value = "查询参数", required = true) + public HttpResult> getTaskListByParentTaskId(String parentTaskId) { + String methodDescribe = getMethodDescribe("getTaskListByParentTaskId"); + List taskList = taskService.getTaskListByParentTaskId(parentTaskId); + if (CollUtil.isEmpty(taskList)) { + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, new ArrayList<>(), methodDescribe); + } + List userIds = convertListByFlatMap(taskList, user -> Stream.of(user.getAssignee(),user.getOwner())); + userIds = userIds.stream().distinct().collect(Collectors.toList()); + List userList = userFeignClient.getUserVOByIdList(userIds).getData(); + Map userMap = CollectionUtils.convertMap(userList, UserVO::getId); + List deptList = deptFeignClient.getDeptInfoListByIds(convertList(userMap.values(), UserVO::getDeptId)).getData(); + Map deptMap = CollectionUtils.convertMap(deptList, Dept::getId); + List bpmTaskVOS = BpmTaskConvert.INSTANCE.buildTaskListByParentTaskId(taskList, userMap, deptMap); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, bpmTaskVOS, methodDescribe); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/task/ProcessInstanceCopyMapper.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/task/ProcessInstanceCopyMapper.java new file mode 100644 index 000000000..c415708c0 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/task/ProcessInstanceCopyMapper.java @@ -0,0 +1,16 @@ +package com.njcn.bpm.mapper.task; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.njcn.bpm.pojo.po.task.ProcessInstanceCopy; + +/** + *

+ * BPM 流程实例抄送表 Mapper 接口 + *

+ * + * @author hongawen + * @since 2024-05-10 + */ +public interface ProcessInstanceCopyMapper extends BaseMapper { + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/task/impl/ProcessInstanceCopyMapper.xml b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/task/impl/ProcessInstanceCopyMapper.xml new file mode 100644 index 000000000..9ed6d55bc --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/mapper/task/impl/ProcessInstanceCopyMapper.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmActivityService.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmActivityService.java new file mode 100644 index 000000000..56808eb48 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmActivityService.java @@ -0,0 +1,31 @@ +package com.njcn.bpm.service.task; + +import com.njcn.bpm.pojo.vo.task.BpmActivityVO; +import org.flowable.engine.history.HistoricActivityInstance; + +import java.util.List; + +/** + * BPM 活动实例 Service 接口 + * + * @author 芋道源码 + */ +public interface IBpmActivityService { + + /** + * 获得指定流程实例的活动实例列表 + * + * @param processInstanceId 流程实例的编号 + * @return 活动实例列表 + */ + List getActivityListByProcessInstanceId(String processInstanceId); + + /** + * 获得执行编号对应的活动实例 + * + * @param executionId 执行编号 + * @return 活动实例 + */ + List getHistoricActivityListByExecutionId(String executionId); + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmProcessInstanceService.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmProcessInstanceService.java index e79f95e46..93e558181 100644 --- a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmProcessInstanceService.java +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmProcessInstanceService.java @@ -1,6 +1,9 @@ package com.njcn.bpm.service.task; import com.njcn.bpm.pojo.dto.BpmProcessInstanceCreateReqDTO; +import com.njcn.bpm.pojo.dto.PageResult; +import com.njcn.bpm.pojo.param.instance.BpmProcessInstanceCancelParam; +import com.njcn.bpm.pojo.param.instance.BpmProcessInstancePageParam; import org.flowable.engine.delegate.event.FlowableCancelledEvent; import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.runtime.ProcessInstance; @@ -39,59 +42,59 @@ public interface IBpmProcessInstanceService { String createProcessInstance(String userId, BpmProcessInstanceCreateReqDTO createReqDTO); -// -// /** -// * 获得流程实例列表 -// * -// * @param ids 流程实例的编号集合 -// * @return 流程实例列表 -// */ -// List getProcessInstances(Set ids); -// -// /** -// * 获得流程实例 Map -// * -// * @param ids 流程实例的编号集合 -// * @return 流程实例列表 Map -// */ -// default Map getProcessInstanceMap(Set ids) { -// return convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId); -// } -// -// /** -// * 获得历史的流程实例 -// * -// * @param id 流程实例的编号 -// * @return 历史的流程实例 -// */ -// HistoricProcessInstance getHistoricProcessInstance(String id); -// -// /** -// * 获得历史的流程实例列表 -// * -// * @param ids 流程实例的编号集合 -// * @return 历史的流程实例列表 -// */ -// List getHistoricProcessInstances(Set ids); -// -// /** -// * 获得历史的流程实例 Map -// * -// * @param ids 流程实例的编号集合 -// * @return 历史的流程实例列表 Map -// */ -// default Map getHistoricProcessInstanceMap(Set ids) { -// return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId); -// } -// -// /** -// * 获得流程实例的分页 -// * -// * @param userId 用户编号 -// * @param pageReqVO 分页请求 -// * @return 流程实例的分页 -// */ -// PageResult getProcessInstancePage(Long userId, @Valid BpmProcessInstancePageReqVO pageReqVO); + + /** + * 获得流程实例列表 + * + * @param ids 流程实例的编号集合 + * @return 流程实例列表 + */ + List getProcessInstances(Set ids); + + /** + * 获得流程实例 Map + * + * @param ids 流程实例的编号集合 + * @return 流程实例列表 Map + */ + default Map getProcessInstanceMap(Set ids) { + return convertMap(getProcessInstances(ids), ProcessInstance::getProcessInstanceId); + } + + /** + * 获得历史的流程实例 + * + * @param id 流程实例的编号 + * @return 历史的流程实例 + */ + HistoricProcessInstance getHistoricProcessInstance(String id); + + /** + * 获得历史的流程实例列表 + * + * @param ids 流程实例的编号集合 + * @return 历史的流程实例列表 + */ + List getHistoricProcessInstances(Set ids); + + /** + * 获得历史的流程实例 Map + * + * @param ids 流程实例的编号集合 + * @return 历史的流程实例列表 Map + */ + default Map getHistoricProcessInstanceMap(Set ids) { + return convertMap(getHistoricProcessInstances(ids), HistoricProcessInstance::getId); + } + + /** + * 获得流程实例的分页 + * + * @param userId 用户编号 + * @param bpmProcessInstancePageParam 分页请求 + * @return 流程实例的分页 + */ + PageResult getProcessInstancePage(String userId, BpmProcessInstancePageParam bpmProcessInstancePageParam); // // /** // * 创建流程实例(提供给前端) @@ -103,43 +106,44 @@ public interface IBpmProcessInstanceService { // String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO); // -// -// /** -// * 发起人取消流程实例 -// * -// * @param userId 用户编号 -// * @param cancelReqVO 取消信息 -// */ -// void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO); -// -// /** -// * 管理员取消流程实例 -// * -// * @param userId 用户编号 -// * @param cancelReqVO 取消信息 -// */ -// void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO); -// -// /** -// * 更新 ProcessInstance 拓展记录为取消 -// * -// * @param event 流程取消事件 -// */ -// void updateProcessInstanceWhenCancel(FlowableCancelledEvent event); -// -// /** -// * 更新 ProcessInstance 拓展记录为完成 -// * -// * @param instance 流程任务 -// */ -// void updateProcessInstanceWhenApprove(ProcessInstance instance); -// -// /** -// * 更新 ProcessInstance 拓展记录为不通过 -// * -// * @param id 流程编号 -// * @param reason 理由。例如说,审批不通过时,需要传递该值 -// */ -// void updateProcessInstanceReject(String id, String reason); + + /** + * 发起人取消流程实例 + * + * @param userId 用户编号 + * @param cancelReqVO 取消信息 + */ + void cancelProcessInstanceByStartUser(String userId, BpmProcessInstanceCancelParam cancelReqVO); + + + /** + * 管理员取消流程实例 + * + * @param userId 用户编号 + * @param cancelReqVO 取消信息 + */ + void cancelProcessInstanceByAdmin(String userId, BpmProcessInstanceCancelParam cancelReqVO); + + /** + * 更新 ProcessInstance 拓展记录为取消 + * + * @param event 流程取消事件 + */ + void updateProcessInstanceWhenCancel(FlowableCancelledEvent event); + + /** + * 更新 ProcessInstance 拓展记录为完成 + * + * @param instance 流程任务 + */ + void updateProcessInstanceWhenApprove(ProcessInstance instance); + + /** + * 更新 ProcessInstance 拓展记录为不通过 + * + * @param id 流程编号 + * @param reason 理由。例如说,审批不通过时,需要传递该值 + */ + void updateProcessInstanceReject(String id, String reason); } diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmTaskService.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmTaskService.java new file mode 100644 index 000000000..2180f4794 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IBpmTaskService.java @@ -0,0 +1,187 @@ +package com.njcn.bpm.service.task; + +import com.njcn.bpm.pojo.dto.PageResult; +import com.njcn.bpm.pojo.param.task.*; +import com.njcn.bpm.utils.CollectionUtils; +import org.flowable.bpmn.model.UserTask; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; + +import java.util.List; +import java.util.Map; + +/** + * 流程任务实例 Service 接口 + * + * @author jason + * @author 芋道源码 + */ +public interface IBpmTaskService { + + /** + * 获得待办的流程任务分页 + * + * @param userId 用户编号 + * @param bpmTaskQueryParam 分页请求 + * @return 流程任务分页 + */ + PageResult getTaskTodoPage(String userId, BpmTaskParam.BpmTaskQueryParam bpmTaskQueryParam); + + /** + * 获得已办的流程任务分页 + * + * @param userId 用户编号 + * @param bpmTaskQueryParam 分页请求 + * @return 流程任务分页 + */ + PageResult getTaskDonePage(String userId, BpmTaskParam.BpmTaskQueryParam bpmTaskQueryParam); + + /** + * 获得全部的流程任务分页 + * + * @param userId 用户编号 + * @param bpmTaskQueryParam 分页请求 + * @return 流程任务分页 + */ + PageResult getTaskPage(String userId, BpmTaskParam.BpmTaskQueryParam bpmTaskQueryParam); + + /** + * 获得流程任务 Map + * + * @param processInstanceIds 流程实例的编号数组 + * @return 流程任务 Map + */ + default Map> getTaskMapByProcessInstanceIds(List processInstanceIds) { + return CollectionUtils.convertMultiMap(getTasksByProcessInstanceIds(processInstanceIds), + Task::getProcessInstanceId); + } + + /** + * 获得流程任务列表 + * + * @param processInstanceIds 流程实例的编号数组 + * @return 流程任务列表 + */ + List getTasksByProcessInstanceIds(List processInstanceIds); + + + /** + * 获得指定流程实例的流程任务列表,包括所有状态的 + * + * @param processInstanceId 流程实例的编号 + * @return 流程任务列表 + */ + List getTaskListByProcessInstanceId(String processInstanceId); + + /** + * 通过任务 + * + * @param userId 用户编号 + * @param bpmTaskApproveParam 通过请求 + */ + void approveTask(String userId, BpmTaskApproveParam bpmTaskApproveParam); + + /** + * 不通过任务 + * + * @param userId 用户编号 + * @param bpmTaskRejectParam 不通过请求 + */ + void rejectTask(String userId, BpmTaskRejectParam bpmTaskRejectParam); + + /** + * 将流程任务分配给指定用户 + * + * @param userId 用户编号 + * @param reqVO 分配请求 + */ + void transferTask(String userId, BpmTaskTransferParam reqVO); + + /** + * 更新 Task 状态,在创建时 + * + * @param task 任务实体 + */ + void updateTaskStatusWhenCreated(Task task); + + /** + * 更新 Task 状态,在取消时 + * + * @param taskId 任务的编号 + */ + void updateTaskStatusWhenCanceled(String taskId); + + /** + * 更新 Task 拓展记录,并发送通知 + * + * @param task 任务实体 + */ + void updateTaskExtAssign(Task task); + + + /** + * 获取任务 + * + * @param id 任务编号 + * @return 任务 + */ + Task getTask(String id); + + /** + * 获取当前任务的可回退的 UserTask 集合 + * + * @param id 当前的任务 ID + * @return 可以回退的节点列表 + */ + List getUserTaskListByReturn(String id); + + /** + * 将任务回退到指定的 targetDefinitionKey 位置 + * + * @param userId 用户编号 + * @param bpmTaskReturnParam 回退的任务key和当前所在的任务ID + */ + void returnTask(String userId, BpmTaskReturnParam bpmTaskReturnParam); + + /** + * 将指定任务委派给其他人处理,等接收人处理后再回到原审批人手中审批 + * + * @param userId 用户编号 + * @param reqVO 被委派人和被委派的任务编号理由参数 + */ + void delegateTask(String userId, BpmTaskDelegateParam reqVO); + + /** + * 任务加签 + * + * @param userId 被加签的用户和任务 ID,加签类型 + * @param reqVO 当前用户 ID + */ + void createSignTask(String userId, BpmTaskSignCreateParam reqVO); + + /** + * 任务减签 + * + * @param userId 当前用户ID + * @param reqVO 被减签的任务 ID,理由 + */ + void deleteSignTask(String userId, BpmTaskSignDeleteParam reqVO); + + + /** + * 获取指定任务的子任务列表 + * + * @param parentTaskId 父任务ID + * @return 子任务列表 + */ + List getTaskListByParentTaskId(String parentTaskId); +// +// /** +// * 通过任务 ID,查询任务名 Map +// * +// * @param taskIds 任务 ID +// * @return 任务 ID 与名字的 Map +// */ +// Map getTaskNameByTaskIds(Collection taskIds); + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IProcessInstanceCopyService.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IProcessInstanceCopyService.java new file mode 100644 index 000000000..7f5e47308 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/IProcessInstanceCopyService.java @@ -0,0 +1,36 @@ +package com.njcn.bpm.service.task; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.njcn.bpm.pojo.po.task.ProcessInstanceCopy; + +import java.util.Collection; + +/** + *

+ * BPM 流程实例抄送表 服务类 + *

+ * + * @author hongawen + * @since 2024-05-10 + */ +public interface IProcessInstanceCopyService extends IService { + + /** + * 流程实例的抄送 + * + * @param userIds 抄送的用户编号 + * @param taskId 流程任务编号 + */ + void createProcessInstanceCopy(Collection userIds, String taskId); + +// /** +// * 获得抄送的流程的分页 +// * +// * @param userId 当前登录用户 +// * @param pageReqVO 分页请求 +// * @return 抄送的分页结果 +// */ +// PageResult getProcessInstanceCopyPage(Long userId, BpmProcessInstanceCopyPageReqVO pageReqVO); + + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmActivityServiceImpl.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmActivityServiceImpl.java new file mode 100644 index 000000000..c871344dd --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmActivityServiceImpl.java @@ -0,0 +1,41 @@ +package com.njcn.bpm.service.task.impl; + +import com.njcn.bpm.pojo.vo.task.BpmActivityVO; +import com.njcn.bpm.service.task.IBpmActivityService; +import com.njcn.bpm.utils.BpmActivityConvert; +import lombok.extern.slf4j.Slf4j; +import org.flowable.engine.HistoryService; +import org.flowable.engine.history.HistoricActivityInstance; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.annotation.Resource; +import java.util.List; + + +/** + * BPM 活动实例 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Slf4j +@Validated +public class BpmActivityServiceImpl implements IBpmActivityService { + + @Resource + private HistoryService historyService; + + @Override + public List getActivityListByProcessInstanceId(String processInstanceId) { + List activityList = historyService.createHistoricActivityInstanceQuery() + .processInstanceId(processInstanceId).list(); + return BpmActivityConvert.INSTANCE.convertList(activityList); + } + + @Override + public List getHistoricActivityListByExecutionId(String executionId) { + return historyService.createHistoricActivityInstanceQuery().executionId(executionId).list(); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java index 372437b07..7514e9aa6 100644 --- a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmProcessInstanceServiceImpl.java @@ -1,30 +1,47 @@ package com.njcn.bpm.service.task.impl; import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.util.StrUtil; import com.njcn.bpm.constant.BpmConstants; +import com.njcn.bpm.enums.BpmDeleteReasonEnum; import com.njcn.bpm.enums.BpmProcessInstanceStatusEnum; import com.njcn.bpm.enums.BpmResponseEnum; +import com.njcn.bpm.event.BpmProcessInstanceEventPublisher; import com.njcn.bpm.pojo.dto.BpmProcessInstanceCreateReqDTO; +import com.njcn.bpm.pojo.dto.PageResult; +import com.njcn.bpm.pojo.param.instance.BpmProcessInstanceCancelParam; +import com.njcn.bpm.pojo.param.instance.BpmProcessInstancePageParam; import com.njcn.bpm.service.IBpmProcessDefinitionService; import com.njcn.bpm.service.task.IBpmProcessInstanceService; import com.njcn.bpm.strategy.BpmTaskCandidateStartUserSelectStrategy; +import com.njcn.bpm.utils.BpmProcessInstanceConvert; import com.njcn.bpm.utils.CollectionUtils; +import com.njcn.bpm.utils.DateUtils; import com.njcn.common.pojo.exception.BusinessException; import com.njcn.user.api.UserFeignClient; import com.njcn.user.pojo.po.User; +import com.njcn.user.pojo.vo.UserVO; +import com.njcn.web.factory.PageFactory; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.UserTask; import org.flowable.engine.HistoryService; import org.flowable.engine.RuntimeService; +import org.flowable.engine.delegate.event.FlowableCancelledEvent; +import org.flowable.engine.history.HistoricProcessInstance; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.engine.runtime.ProcessInstanceBuilder; +import org.flowable.engine.history.HistoricProcessInstanceQuery; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import com.njcn.bpm.utils.FlowableUtils; import javax.annotation.Resource; -import javax.validation.Valid; +import java.time.LocalDateTime; import java.util.*; @@ -60,9 +77,9 @@ public class BpmProcessInstanceServiceImpl implements IBpmProcessInstanceService // @Resource private UserFeignClient userFeignClient; -// -// @Resource -// private BpmProcessInstanceEventPublisher processInstanceEventPublisher; + + @Resource + private BpmProcessInstanceEventPublisher processInstanceEventPublisher; @Override public ProcessInstance getProcessInstance(String id) { @@ -73,61 +90,64 @@ public class BpmProcessInstanceServiceImpl implements IBpmProcessInstanceService } -// -// @Override -// public List getProcessInstances(Set ids) { -// return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); -// } -// -// @Override -// public HistoricProcessInstance getHistoricProcessInstance(String id) { -// return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); -// } -// -// @Override -// public List getHistoricProcessInstances(Set ids) { -// return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); -// } -// -// @Override -// public PageResult getProcessInstancePage(Long userId, -// BpmProcessInstancePageReqVO pageReqVO) { -// // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 -// HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() -// .includeProcessVariables() -// .processInstanceTenantId(FlowableUtils.getTenantId()) -// .orderByProcessInstanceStartTime().desc(); -// if (userId != null) { // 【我的流程】菜单时,需要传递该字段 -// processInstanceQuery.startedBy(String.valueOf(userId)); -// } else if (pageReqVO.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 -// processInstanceQuery.startedBy(String.valueOf(pageReqVO.getStartUserId())); -// } -// if (StrUtil.isNotEmpty(pageReqVO.getName())) { -// processInstanceQuery.processInstanceNameLike("%" + pageReqVO.getName() + "%"); -// } -// if (StrUtil.isNotEmpty(pageReqVO.getProcessDefinitionId())) { -// processInstanceQuery.processDefinitionId("%" + pageReqVO.getProcessDefinitionId() + "%"); -// } -// if (StrUtil.isNotEmpty(pageReqVO.getCategory())) { -// processInstanceQuery.processDefinitionCategory(pageReqVO.getCategory()); -// } -// if (pageReqVO.getStatus() != null) { -// processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, pageReqVO.getStatus()); -// } -// if (ArrayUtil.isNotEmpty(pageReqVO.getCreateTime())) { -// processInstanceQuery.startedAfter(DateUtils.of(pageReqVO.getCreateTime()[0])); -// processInstanceQuery.startedBefore(DateUtils.of(pageReqVO.getCreateTime()[1])); -// } -// // 查询数量 -// long processInstanceCount = processInstanceQuery.count(); -// if (processInstanceCount == 0) { -// return PageResult.empty(processInstanceCount); -// } -// // 查询列表 -// List processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); -// return new PageResult<>(processInstanceList, processInstanceCount); -// } -// + + @Override + public List getProcessInstances(Set ids) { + return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); + } + + @Override + public HistoricProcessInstance getHistoricProcessInstance(String id) { + return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); + } + + @Override + public List getHistoricProcessInstances(Set ids) { + return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); + } + + @Override + public PageResult getProcessInstancePage(String userId, BpmProcessInstancePageParam bpmProcessInstancePageParam) { + // 通过 BpmProcessInstanceExtDO 表,先查询到对应的分页 + HistoricProcessInstanceQuery processInstanceQuery = historyService.createHistoricProcessInstanceQuery() + .includeProcessVariables() + .processInstanceTenantId(FlowableUtils.getTenantId()) + .orderByProcessInstanceStartTime().desc(); + if (userId != null) { // 【我的流程】菜单时,需要传递该字段 + processInstanceQuery.startedBy(userId); + } else if (bpmProcessInstancePageParam.getStartUserId() != null) { // 【管理流程】菜单时,才会传递该字段 + processInstanceQuery.startedBy(bpmProcessInstancePageParam.getStartUserId()); + } + if (StrUtil.isNotEmpty(bpmProcessInstancePageParam.getName())) { + processInstanceQuery.processInstanceNameLike("%" + bpmProcessInstancePageParam.getName() + "%"); + } + if (StrUtil.isNotEmpty(bpmProcessInstancePageParam.getProcessDefinitionId())) { + processInstanceQuery.processDefinitionId("%" + bpmProcessInstancePageParam.getProcessDefinitionId() + "%"); + } + if (StrUtil.isNotEmpty(bpmProcessInstancePageParam.getCategory())) { + processInstanceQuery.processDefinitionCategory(bpmProcessInstancePageParam.getCategory()); + } + if (bpmProcessInstancePageParam.getStatus() != null) { + processInstanceQuery.variableValueEquals(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, bpmProcessInstancePageParam.getStatus()); + } + + String beginTimeStr = bpmProcessInstancePageParam.getSearchBeginTime(); + String endTimeStr = bpmProcessInstancePageParam.getSearchEndTime(); + LocalDateTime beginTime = LocalDateTimeUtil.parse(beginTimeStr, DatePattern.NORM_DATE_PATTERN); + LocalDateTime endTime = LocalDateTimeUtil.parse(endTimeStr, DatePattern.NORM_DATE_PATTERN); + processInstanceQuery.startedAfter(DateUtils.of(beginTime)); + processInstanceQuery.startedBefore(DateUtils.of(endTime)); + // 查询数量 + long processInstanceCount = processInstanceQuery.count(); + if (processInstanceCount == 0) { + return PageResult.empty(processInstanceCount); + } + // 查询列表 + int offset = PageFactory.getPageSize(bpmProcessInstancePageParam) * (PageFactory.getPageNum(bpmProcessInstancePageParam) - 1); + List processInstanceList = processInstanceQuery.listPage(offset, PageFactory.getPageSize(bpmProcessInstancePageParam)); + return new PageResult<>(processInstanceList, processInstanceCount); + } + // @Override // @Transactional(rollbackFor = Exception.class) // public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { @@ -139,7 +159,7 @@ public class BpmProcessInstanceServiceImpl implements IBpmProcessInstanceService // } @Override - public String createProcessInstance(String userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { + public String createProcessInstance(String userId, BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 @@ -194,8 +214,8 @@ public class BpmProcessInstanceServiceImpl implements IBpmProcessInstanceService if (CollUtil.isEmpty(assignees)) { throw new BusinessException("审批任务(" + userTask.getName() + ")的审批人未配置"); } - List userList = userFeignClient.getUserByIdList(assignees).getData(); - Map userMap = CollectionUtils.convertMap(userList, User::getId); + List userList = userFeignClient.getUserVOByIdList(assignees).getData(); + Map userMap = CollectionUtils.convertMap(userList, UserVO::getId); assignees.forEach(assignee -> { if (userMap.get(assignee) == null) { throw new BusinessException("审批任务(" + userTask.getName() + ")的审批人(" + assignee + ")不存在"); @@ -203,101 +223,101 @@ public class BpmProcessInstanceServiceImpl implements IBpmProcessInstanceService }); }); } -// -// @Override -// public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { -// // 1.1 校验流程实例存在 -// ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); -// if (instance == null) { -// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); -// } -// // 1.2 只能取消自己的 -// if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { -// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); -// } -// -// // 2. 通过删除流程实例,实现流程实例的取消, -// // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 -// deleteProcessInstance(cancelReqVO.getId(), -// BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); -// -// // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 -// } -// -// @Override -// public void cancelProcessInstanceByAdmin(Long userId, BpmProcessInstanceCancelReqVO cancelReqVO) { -// // 1.1 校验流程实例存在 -// ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); -// if (instance == null) { -// throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); -// } -// // 1.2 管理员取消,不用校验是否为自己的 -// AdminUserRespDTO user = adminUserApi.getUser(userId); -// -// // 2. 通过删除流程实例,实现流程实例的取消, -// // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 -// deleteProcessInstance(cancelReqVO.getId(), -// BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getNickname(), cancelReqVO.getReason())); -// -// // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 -// } -// -// @Override -// public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { -// // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. -// // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 -// if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { -// return; -// } -// -// // 2. 更新流程实例 status -// runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, -// BpmProcessInstanceStatusEnum.CANCEL.getStatus()); -// -// // 3. 发送流程实例的状态事件 -// // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance -// HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); -// // 发送流程实例的状态事件 -// processInstanceEventPublisher.sendProcessInstanceResultEvent( -// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); -// } -// -// @Override -// public void updateProcessInstanceWhenApprove(ProcessInstance instance) { -// // 1. 更新流程实例 status -// runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, -// BpmProcessInstanceStatusEnum.APPROVE.getStatus()); -// -// // 2. 发送流程被【通过】的消息 + + @Override + public void cancelProcessInstanceByStartUser(String userId, BpmProcessInstanceCancelParam cancelReqVO) { + // 1.1 校验流程实例存在 + ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); + if (instance == null) { + throw new BusinessException(BpmResponseEnum.PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); + } + // 1.2 只能取消自己的 + if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { + throw new BusinessException(BpmResponseEnum.PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); + } + + // 2. 通过删除流程实例,实现流程实例的取消, + // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 + deleteProcessInstance(cancelReqVO.getId(), + BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_START_USER.format(cancelReqVO.getReason())); + + // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 + } + + @Override + public void cancelProcessInstanceByAdmin(String userId, BpmProcessInstanceCancelParam cancelReqVO) { + // 1.1 校验流程实例存在 + ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); + if (instance == null) { + throw new BusinessException(BpmResponseEnum.PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); + } + // 1.2 管理员取消,不用校验是否为自己的 + UserVO user = userFeignClient.getUserById(userId).getData(); + + // 2. 通过删除流程实例,实现流程实例的取消, + // 删除流程实例,正则执行任务 ACT_RU_TASK. 任务会被删除。 + deleteProcessInstance(cancelReqVO.getId(), + BpmDeleteReasonEnum.CANCEL_PROCESS_INSTANCE_BY_ADMIN.format(user.getName(), cancelReqVO.getReason())); + + // 3. 进一步的处理,交给 updateProcessInstanceCancel 方法 + } + + @Override + public void updateProcessInstanceWhenCancel(FlowableCancelledEvent event) { + // 1. 判断是否为 Reject 不通过。如果是,则不进行更新. + // 因为,updateProcessInstanceReject 方法(审批不通过),已经进行更新了 + if (BpmDeleteReasonEnum.isRejectReason((String) event.getCause())) { + return; + } + + // 2. 更新流程实例 status + runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, + BpmProcessInstanceStatusEnum.CANCEL.getStatus()); + + // 3. 发送流程实例的状态事件 + // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance + HistoricProcessInstance processInstance = getHistoricProcessInstance(event.getProcessInstanceId()); + // 发送流程实例的状态事件 + processInstanceEventPublisher.sendProcessInstanceResultEvent( + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.CANCEL.getStatus())); + } + + @Override + public void updateProcessInstanceWhenApprove(ProcessInstance instance) { + // 1. 更新流程实例 status + runtimeService.setVariable(instance.getId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, + BpmProcessInstanceStatusEnum.APPROVE.getStatus()); + + // 2. 发送流程被【通过】的消息 // messageService.sendMessageWhenProcessInstanceApprove(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceApproveMessage(instance)); -// -// // 3. 发送流程实例的状态事件 -// // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance -// HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); -// processInstanceEventPublisher.sendProcessInstanceResultEvent( -// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); -// } -// -// @Override -// @Transactional(rollbackFor = Exception.class) -// public void updateProcessInstanceReject(String id, String reason) { -// // 1. 更新流程实例 status -// runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); -// -// // 2. 删除流程实例,以实现驳回任务时,取消整个审批流程 -// ProcessInstance processInstance = getProcessInstance(id); -// deleteProcessInstance(id, StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reason))); -// -// // 3. 发送流程被【不通过】的消息 + + // 3. 发送流程实例的状态事件 + // 注意:此时如果去查询 ProcessInstance 的话,字段是不全的,所以去查询了 HistoricProcessInstance + HistoricProcessInstance processInstance = getHistoricProcessInstance(instance.getId()); + processInstanceEventPublisher.sendProcessInstanceResultEvent( + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.APPROVE.getStatus())); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void updateProcessInstanceReject(String id, String reason) { + // 1. 更新流程实例 status + runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); + + // 2. 删除流程实例,以实现驳回任务时,取消整个审批流程 + ProcessInstance processInstance = getProcessInstance(id); + deleteProcessInstance(id, StrUtil.format(BpmDeleteReasonEnum.REJECT_TASK.format(reason))); + + // 3. 发送流程被【不通过】的消息 // messageService.sendMessageWhenProcessInstanceReject(BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason)); -// -// // 4. 发送流程实例的状态事件 -// processInstanceEventPublisher.sendProcessInstanceResultEvent( -// BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); -// } -// -// private void deleteProcessInstance(String id, String reason) { -// runtimeService.deleteProcessInstance(id, reason); -// } + + // 4. 发送流程实例的状态事件 + processInstanceEventPublisher.sendProcessInstanceResultEvent( + BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); + } + + private void deleteProcessInstance(String id, String reason) { + runtimeService.deleteProcessInstance(id, reason); + } } diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmTaskServiceImpl.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmTaskServiceImpl.java new file mode 100644 index 000000000..cc8a01235 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/BpmTaskServiceImpl.java @@ -0,0 +1,836 @@ +package com.njcn.bpm.service.task.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.njcn.bpm.constant.BpmConstants; +import com.njcn.bpm.enums.*; +import com.njcn.bpm.pojo.dto.PageResult; +import com.njcn.bpm.pojo.param.task.*; +import com.njcn.bpm.service.IBpmModelService; +import com.njcn.bpm.service.task.IBpmProcessInstanceService; +import com.njcn.bpm.service.task.IBpmTaskService; +import com.njcn.bpm.service.task.IProcessInstanceCopyService; +import com.njcn.bpm.utils.BpmTaskConvert; +import com.njcn.bpm.utils.BpmnModelUtils; +import com.njcn.bpm.utils.DateUtils; +import com.njcn.bpm.utils.FlowableUtils; +import com.njcn.common.pojo.exception.BusinessException; +import com.njcn.common.utils.PubUtils; +import com.njcn.user.api.UserFeignClient; +import com.njcn.user.pojo.po.User; +import com.njcn.user.pojo.vo.UserVO; +import com.njcn.web.factory.PageFactory; +import com.njcn.web.utils.RequestUtil; +import lombok.extern.slf4j.Slf4j; +import org.flowable.bpmn.model.BpmnModel; +import org.flowable.bpmn.model.FlowElement; +import org.flowable.bpmn.model.UserTask; +import org.flowable.engine.HistoryService; +import org.flowable.engine.ManagementService; +import org.flowable.engine.RuntimeService; +import org.flowable.engine.TaskService; +import org.flowable.engine.runtime.ProcessInstance; +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.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionSynchronization; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +import javax.annotation.Resource; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Stream; + +import static com.njcn.bpm.utils.CollectionUtils.convertList; +import static com.njcn.bpm.utils.CollectionUtils.convertListByFlatMap; + + +/** + * 流程任务实例 Service 实现类 + * + * @author 芋道源码 + * @author jason + */ +@Slf4j +@Service +public class BpmTaskServiceImpl implements IBpmTaskService { + + @Resource + private TaskService taskService; + @Resource + private HistoryService historyService; + @Resource + private RuntimeService runtimeService; + @Resource + private ManagementService managementService; + + @Resource + private IBpmProcessInstanceService processInstanceService; + @Resource + private IProcessInstanceCopyService processInstanceCopyService; + @Resource + private IBpmModelService bpmModelService; +// @Resource +// private BpmMessageService messageService; + + @Resource + private UserFeignClient adminUserApi; + + @Override + public PageResult getTaskTodoPage(String userId, BpmTaskParam.BpmTaskQueryParam bpmTaskQueryParam) { + TaskQuery taskQuery = taskService.createTaskQuery() + .taskAssignee(userId) // 分配给自己 + .active() + .includeProcessVariables() + .orderByTaskCreateTime().desc(); // 创建时间倒序 + if (StrUtil.isNotBlank(bpmTaskQueryParam.getSearchValue())) { + taskQuery.taskNameLike("%" + bpmTaskQueryParam.getSearchValue() + "%"); + } + String beginTimeStr = bpmTaskQueryParam.getSearchBeginTime(); + String endTimeStr = bpmTaskQueryParam.getSearchEndTime(); + LocalDateTime beginTime = LocalDateTimeUtil.parse(beginTimeStr, DatePattern.NORM_DATE_PATTERN); + LocalDateTime endTime = PubUtils.endTimeToLocalDateTime(endTimeStr); + taskQuery.taskCreatedBefore(DateUtils.of(endTime)); + taskQuery.taskCreatedAfter(DateUtils.of(beginTime)); + long count = taskQuery.count(); + if (count == 0) { + return PageResult.empty(); + } + int offset = PageFactory.getPageSize(bpmTaskQueryParam) * (PageFactory.getPageNum(bpmTaskQueryParam) - 1); + List tasks = taskQuery.listPage(offset, PageFactory.getPageSize(bpmTaskQueryParam)); + return new PageResult<>(tasks, count); + } + + @Override + public PageResult getTaskDonePage(String userId, BpmTaskParam.BpmTaskQueryParam bpmTaskQueryParam) { + HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery() + .finished() // 已完成 + .taskAssignee(String.valueOf(userId)) // 分配给自己 + .includeTaskLocalVariables() + .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序 + if (StrUtil.isNotBlank(bpmTaskQueryParam.getSearchValue())) { + taskQuery.taskNameLike("%" + bpmTaskQueryParam.getSearchValue() + "%"); + } + String beginTimeStr = bpmTaskQueryParam.getSearchBeginTime(); + String endTimeStr = bpmTaskQueryParam.getSearchEndTime(); + LocalDateTime beginTime = LocalDateTimeUtil.parse(beginTimeStr, DatePattern.NORM_DATE_PATTERN); + LocalDateTime endTime = PubUtils.endTimeToLocalDateTime(endTimeStr); + taskQuery.taskCreatedBefore(DateUtils.of(endTime)); + taskQuery.taskCreatedAfter(DateUtils.of(beginTime)); + // 执行查询 + long count = taskQuery.count(); + if (count == 0) { + return PageResult.empty(); + } + int offset = PageFactory.getPageSize(bpmTaskQueryParam) * (PageFactory.getPageNum(bpmTaskQueryParam) - 1); + List tasks = taskQuery.listPage(offset, PageFactory.getPageSize(bpmTaskQueryParam)); + return new PageResult<>(tasks, count); + } + + @Override + public PageResult getTaskPage(String userId, BpmTaskParam.BpmTaskQueryParam bpmTaskQueryParam) { + HistoricTaskInstanceQuery taskQuery = historyService.createHistoricTaskInstanceQuery() + .includeTaskLocalVariables() + .taskTenantId(FlowableUtils.getTenantId()) + .orderByHistoricTaskInstanceEndTime().desc(); // 审批时间倒序 + + if (StrUtil.isNotBlank(bpmTaskQueryParam.getSearchValue())) { + taskQuery.taskNameLike("%" + bpmTaskQueryParam.getSearchValue() + "%"); + } + String beginTimeStr = bpmTaskQueryParam.getSearchBeginTime(); + String endTimeStr = bpmTaskQueryParam.getSearchEndTime(); + LocalDateTime beginTime = LocalDateTimeUtil.parse(beginTimeStr, DatePattern.NORM_DATE_PATTERN); + LocalDateTime endTime = PubUtils.endTimeToLocalDateTime(endTimeStr); + taskQuery.taskCreatedBefore(DateUtils.of(endTime)); + taskQuery.taskCreatedAfter(DateUtils.of(beginTime)); + // 执行查询 + long count = taskQuery.count(); + if (count == 0) { + return PageResult.empty(); + } + int offset = PageFactory.getPageSize(bpmTaskQueryParam) * (PageFactory.getPageNum(bpmTaskQueryParam) - 1); + List tasks = taskQuery.listPage(offset, PageFactory.getPageSize(bpmTaskQueryParam)); + return new PageResult<>(tasks, count); + } + + @Override + public List getTasksByProcessInstanceIds(List processInstanceIds) { + if (CollectionUtil.isEmpty(processInstanceIds)) { + return Collections.emptyList(); + } + return taskService.createTaskQuery().processInstanceIdIn(processInstanceIds).list(); + } + + @Override + public List getTaskListByProcessInstanceId(String processInstanceId) { + List tasks = historyService.createHistoricTaskInstanceQuery() + .includeTaskLocalVariables() + .processInstanceId(processInstanceId) + .orderByHistoricTaskInstanceStartTime().desc() // 创建时间倒序 + .list(); + if (CollectionUtil.isEmpty(tasks)) { + return Collections.emptyList(); + } + return tasks; + } + + + @Override + @Transactional(rollbackFor = Exception.class) + public void approveTask(String userId, BpmTaskApproveParam bpmTaskApproveParam) { + // 1.1 校验任务存在 + Task task = validateTask(userId, bpmTaskApproveParam.getId()); + // 1.2 校验流程实例存在 + ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw new BusinessException(BpmResponseEnum.PROCESS_INSTANCE_NOT_EXISTS); + } + + // 2. 抄送用户 + if (CollectionUtil.isNotEmpty(bpmTaskApproveParam.getCopyUserIds())) { + processInstanceCopyService.createProcessInstanceCopy(bpmTaskApproveParam.getCopyUserIds(), bpmTaskApproveParam.getId()); + } + + // 情况一:被委派的任务,不调用 complete 去完成任务 + if (DelegationState.PENDING.equals(task.getDelegationState())) { + approveDelegateTask(bpmTaskApproveParam, task); + return; + } + + // 情况二:审批有【后】加签的任务 + if (BpmTaskSignTypeEnum.AFTER.getType().equals(task.getScopeType())) { + approveAfterSignTask(task, bpmTaskApproveParam); + return; + } + + // 情况三:审批普通的任务。大多数情况下,都是这样 + // 3.1 更新 task 状态、原因 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.APPROVE.getStatus(), bpmTaskApproveParam.getReason()); + // 3.2 添加评论 + taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(), + BpmCommentTypeEnum.APPROVE.formatComment(bpmTaskApproveParam.getReason())); + // 3.3 调用 BPM complete 去完成任务 + // 其中,variables 是存储动态表单到 local 任务级别。过滤一下,避免 ProcessInstance 系统级的变量被占用 + if (CollectionUtil.isNotEmpty(bpmTaskApproveParam.getVariables())) { + Map variables = FlowableUtils.filterTaskFormVariable(bpmTaskApproveParam.getVariables()); + taskService.complete(task.getId(), variables, true); + } else { + taskService.complete(task.getId()); + } + + // 【加签专属】处理加签任务 + handleParentTaskIfSign(task.getParentTaskId()); + } + + /** + * 审批通过存在“后加签”的任务。 + *

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

+ * 1. 如果是【向前】加签,则需要重新激活父任务,让它可以被审批 + * 2. 如果是【向后】加签,则需要完成父任务,让它完成审批 + * + * @param parentTaskId 父任务编号 + */ + private void handleParentTaskIfSign(String parentTaskId) { + if (StrUtil.isBlank(parentTaskId)) { + return; + } + // 1.1 判断是否还有子任务。如果没有,就不处理 + Long childrenTaskCount = getTaskCountByParentTaskId(parentTaskId); + if (childrenTaskCount > 0) { + return; + } + // 1.2 只处理加签的父任务 + Task parentTask = validateTaskExist(parentTaskId); + String scopeType = parentTask.getScopeType(); + if (BpmTaskSignTypeEnum.of(scopeType) == null) { + return; + } + + // 2. 子任务已处理完成,清空 scopeType 字段,修改 parentTask 信息,方便后续可以继续向前后向后加签 + TaskEntityImpl parentTaskImpl = (TaskEntityImpl) parentTask; + parentTaskImpl.setScopeType(null); + taskService.saveTask(parentTaskImpl); + + // 3.1 情况一:处理向【向前】加签 + if (BpmTaskSignTypeEnum.BEFORE.getType().equals(scopeType)) { + // 3.1.1 owner 重新赋值给父任务的 assignee,这样它就可以被审批 + taskService.resolveTask(parentTaskId); + // 3.1.2 更新流程任务 status + updateTaskStatus(parentTaskId, BpmTaskStatusEnum.RUNNING.getStatus()); + // 3.2 情况二:处理向【向后】加签 + } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(scopeType)) { + // 只有 parentTask 处于 APPROVING 的情况下,才可以继续 complete 完成 + // 否则,一个未审批的 parentTask 任务,在加签出来的任务都被减签的情况下,就直接完成审批,这样会存在问题 + Integer status = (Integer) parentTask.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); + if (ObjectUtil.notEqual(status, BpmTaskStatusEnum.APPROVING.getStatus())) { + return; + } + // 3.2.2 完成自己(因为它已经没有子任务,所以也可以完成) + updateTaskStatus(parentTaskId, BpmTaskStatusEnum.APPROVE.getStatus()); + taskService.complete(parentTaskId); + } + + // 4. 递归处理父任务 + handleParentTaskIfSign(parentTask.getParentTaskId()); + } + + /** + * 审批被委派的任务 + * + * @param bpmTaskApproveParam 前端请求参数,包含当前任务ID,审批意见等 + * @param task 当前被审批的任务 + */ + private void approveDelegateTask(BpmTaskApproveParam bpmTaskApproveParam, Task task) { + // 1. 添加审批意见 + UserVO currentUser = adminUserApi.getUserById(RequestUtil.getUserIndex()).getData(); + UserVO ownerUser = adminUserApi.getUserById(task.getOwner()).getData(); // 发起委托的用户 + Assert.notNull(ownerUser, "委派任务找不到原审批人,需要检查数据"); + taskService.addComment(bpmTaskApproveParam.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_END.getType(), + BpmCommentTypeEnum.DELEGATE_END.formatComment(currentUser.getName(), ownerUser.getName(), bpmTaskApproveParam.getReason())); + + // 2.1 调用 resolveTask 完成任务。 + // 底层调用 TaskHelper.changeTaskAssignee(task, task.getOwner()):将 owner 设置为 assignee + taskService.resolveTask(task.getId()); + // 2.2 更新 task 状态 + 原因 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus(), bpmTaskApproveParam.getReason()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void rejectTask(String userId, BpmTaskRejectParam bpmTaskRejectParam) { + // 1.1 校验任务存在 + Task task = validateTask(userId, bpmTaskRejectParam.getId()); + // 1.2 校验流程实例存在 + ProcessInstance instance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + if (instance == null) { + throw new BusinessException(BpmResponseEnum.PROCESS_INSTANCE_NOT_EXISTS); + } + + // 2.1 更新流程实例为不通过 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), bpmTaskRejectParam.getReason()); + // 2.2 添加评论 + taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), + BpmCommentTypeEnum.REJECT.formatComment(bpmTaskRejectParam.getReason())); + + // 3. 更新流程实例,审批不通过! + processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(), bpmTaskRejectParam.getReason()); + } + + /** + * 更新流程任务的 status 状态 + * + * @param id 任务编号 + * @param status 状态 + */ + private void updateTaskStatus(String id, Integer status) { + taskService.setVariableLocal(id, BpmConstants.TASK_VARIABLE_STATUS, status); + } + + /** + * 更新流程任务的 status 状态、reason 理由 + * + * @param id 任务编号 + * @param status 状态 + * @param reason 理由(审批通过、审批不通过的理由) + */ + private void updateTaskStatusAndReason(String id, Integer status, String reason) { + updateTaskStatus(id, status); + taskService.setVariableLocal(id, BpmConstants.TASK_VARIABLE_REASON, reason); + } + + /** + * 校验任务是否存在,并且是否是分配给自己的任务 + * + * @param userId 用户 id + * @param taskId task id + */ + private Task validateTask(String userId, String taskId) { + Task task = validateTaskExist(taskId); + if (!Objects.equals(userId, task.getAssignee())) { + throw new BusinessException(BpmResponseEnum.TASK_OPERATE_FAIL_ASSIGN_NOT_SELF); + } + return task; + } + + + @Override + public void updateTaskStatusWhenCreated(Task task) { + Integer status = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); + if (status != null) { + log.error("[updateTaskStatusWhenCreated][taskId({}) 已经有状态({})]", task.getId(), status); + return; + } + updateTaskStatus(task.getId(), BpmTaskStatusEnum.RUNNING.getStatus()); + } + + @Override + public void updateTaskStatusWhenCanceled(String taskId) { + Task task = getTask(taskId); + // 1. 可能只是活动,不是任务,所以查询不到 + if (task == null) { + log.error("[updateTaskStatusWhenCanceled][taskId({}) 任务不存在]", taskId); + return; + } + + // 2. 更新 task 状态 + 原因 + Integer status = (Integer) task.getTaskLocalVariables().get(BpmConstants.TASK_VARIABLE_STATUS); + if (BpmTaskStatusEnum.isEndStatus(status)) { + log.error("[updateTaskStatusWhenCanceled][taskId({}) 处于结果({}),无需进行更新]", taskId, status); + return; + } + updateTaskStatusAndReason(taskId, BpmTaskStatusEnum.CANCEL.getStatus(), BpmDeleteReasonEnum.CANCEL_BY_SYSTEM.getReason()); + // 补充说明:由于 Task 被删除成 HistoricTask 后,无法通过 taskService.addComment 添加理由,所以无法存储具体的取消理由 + } + + @Override + public void updateTaskExtAssign(Task task) { + // 发送通知。在事务提交时,批量执行操作,所以直接查询会无法查询到 ProcessInstance,所以这里是通过监听事务的提交来实现。 + TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() { + + @Override + public void afterCommit() { + if (StrUtil.isEmpty(task.getAssignee())) { + return; + } + ProcessInstance processInstance = processInstanceService.getProcessInstance(task.getProcessInstanceId()); + UserVO startUser = adminUserApi.getUserById(processInstance.getStartUserId()).getData(); + //发送消息 +// messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task)); + } + + }); + } + + private Task validateTaskExist(String id) { + Task task = getTask(id); + if (task == null) { + throw new BusinessException(BpmResponseEnum.TASK_NOT_EXISTS); + } + return task; + } + + @Override + public Task getTask(String id) { + return taskService.createTaskQuery().taskId(id).includeTaskLocalVariables().singleResult(); + } + + // +// private HistoricTaskInstance getHistoricTask(String id) { +// return historyService.createHistoricTaskInstanceQuery().taskId(id).includeTaskLocalVariables().singleResult(); +// } +// + @Override + public List getUserTaskListByReturn(String id) { + // 1.1 校验当前任务 task 存在 + Task task = validateTaskExist(id); + // 1.2 根据流程定义获取流程模型信息 + BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); + FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); + if (source == null) { + throw new BusinessException(BpmResponseEnum.TASK_NOT_EXISTS); + } + // 2.1 查询该任务的前置任务节点的 key 集合 + List previousUserList = BpmnModelUtils.getPreviousUserTaskList(source, null, null); + if (CollectionUtil.isEmpty(previousUserList)) { + return Collections.emptyList(); + } + // 2.2 过滤:只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回 + previousUserList.removeIf(userTask -> !BpmnModelUtils.isSequentialReachable(source, userTask, null)); + return previousUserList; + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void returnTask(String userId, BpmTaskReturnParam bpmTaskReturnParam) { + // 1.1 当前任务 task + Task task = validateTask(userId, bpmTaskReturnParam.getId()); + if (task.isSuspended()) { + throw new BusinessException(BpmResponseEnum.TASK_IS_PENDING); + } + // 1.2 校验源头和目标节点的关系,并返回目标元素 + FlowElement targetElement = validateTargetTaskCanReturn(task.getTaskDefinitionKey(), + bpmTaskReturnParam.getTargetTaskDefinitionKey(), task.getProcessDefinitionId()); + + // 2. 调用 Flowable 框架的回退逻辑 + returnTask(task, targetElement, bpmTaskReturnParam); + } + + /** + * 回退流程节点时,校验目标任务节点是否可回退 + * + * @param sourceKey 当前任务节点 Key + * @param targetKey 目标任务节点 key + * @param processDefinitionId 当前流程定义 ID + * @return 目标任务节点元素 + */ + private FlowElement validateTargetTaskCanReturn(String sourceKey, String targetKey, String processDefinitionId) { + // 1.1 获取流程模型信息 + BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(processDefinitionId); + // 1.3 获取当前任务节点元素 + FlowElement source = BpmnModelUtils.getFlowElementById(bpmnModel, sourceKey); + // 1.3 获取跳转的节点元素 + FlowElement target = BpmnModelUtils.getFlowElementById(bpmnModel, targetKey); + if (target == null) { + throw new BusinessException(BpmResponseEnum.TASK_TARGET_NODE_NOT_EXISTS); + } + + // 2.2 只有串行可到达的节点,才可以回退。类似非串行、子流程无法退回 + if (!BpmnModelUtils.isSequentialReachable(source, target, null)) { + throw new BusinessException(BpmResponseEnum.TASK_RETURN_FAIL_SOURCE_TARGET_ERROR); + } + return target; + } + + /** + * 执行回退逻辑 + * + * @param currentTask 当前回退的任务 + * @param targetElement 需要回退到的目标任务 + * @param bpmTaskReturnParam 前端参数封装 + */ + public void returnTask(Task currentTask, FlowElement targetElement, BpmTaskReturnParam bpmTaskReturnParam) { + // 1. 获得所有需要回撤的任务 taskDefinitionKey,用于稍后的 moveActivityIdsToSingleActivityId 回撤 + // 1.1 获取所有正常进行的任务节点 Key + List taskList = taskService.createTaskQuery().processInstanceId(currentTask.getProcessInstanceId()).list(); + List runTaskKeyList = convertList(taskList, Task::getTaskDefinitionKey); + // 1.2 通过 targetElement 的出口连线,计算在 runTaskKeyList 有哪些 key 需要被撤回 + // 为什么不直接使用 runTaskKeyList 呢?因为可能存在多个审批分支,例如说:A -> B -> C 和 D -> F,而只要 C 撤回到 A,需要排除掉 F + List returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null); + List returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId); + + // 2. 给当前要被回退的 task 数组,设置回退意见 + taskList.forEach(task -> { + // 需要排除掉,不需要设置回退意见的任务 + if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) { + return; + } + // 2.1 添加评论 + taskService.addComment(task.getId(), currentTask.getProcessInstanceId(), BpmCommentTypeEnum.RETURN.getType(), + BpmCommentTypeEnum.RETURN.formatComment(bpmTaskReturnParam.getReason())); + // 2.2 更新 task 状态 + 原因 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.RETURN.getStatus(), bpmTaskReturnParam.getReason()); + }); + + // 3. 执行驳回 + runtimeService.createChangeActivityStateBuilder() + .processInstanceId(currentTask.getProcessInstanceId()) + .moveActivityIdsToSingleActivityId(returnTaskKeyList, // 当前要跳转的节点列表( 1 或多) + bpmTaskReturnParam.getTargetTaskDefinitionKey()) // targetKey 跳转到的节点(1) + .changeState(); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void delegateTask(String userId, BpmTaskDelegateParam reqVO) { + String taskId = reqVO.getId(); + // 1.1 校验任务 + Task task = validateTask(userId, reqVO.getId()); + if (task.getAssignee().equals(reqVO.getDelegateUserId())) { // 校验当前审批人和被委派人不是同一人 + throw new BusinessException(BpmResponseEnum.TASK_DELEGATE_FAIL_USER_REPEAT); + } + // 1.2 校验目标用户存在 + UserVO delegateUser = adminUserApi.getUserById(reqVO.getDelegateUserId()).getData(); + if (delegateUser == null) { + throw new BusinessException(BpmResponseEnum.TASK_DELEGATE_FAIL_USER_NOT_EXISTS); + } + + // 2. 添加委托意见 + UserVO currentUser = adminUserApi.getUserById(userId).getData(); + taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.DELEGATE_START.getType(), + BpmCommentTypeEnum.DELEGATE_START.formatComment(currentUser.getName(), delegateUser.getName(), reqVO.getReason())); + // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) + taskService.setOwner(taskId, task.getAssignee()); + // 3.2 执行委派,将任务委派给 delegateUser + taskService.delegateTask(taskId, reqVO.getDelegateUserId()); + // 3.3 更新 task 状态。 + // 为什么不更新原因?因为原因目前主要给审批通过、不通过时使用 + updateTaskStatus(taskId, BpmTaskStatusEnum.DELEGATE.getStatus()); + } + + @Override + public void transferTask(String userId, BpmTaskTransferParam reqVO) { + String taskId = reqVO.getId(); + // 1.1 校验任务 + Task task = validateTask(userId, reqVO.getId()); + if (task.getAssignee().equals(reqVO.getAssigneeUserId())) { // 校验当前审批人和被转派人不是同一人 + throw new BusinessException(BpmResponseEnum.TASK_DELEGATE_FAIL_USER_NOT_EXISTS); + } + // 1.2 校验目标用户存在 + UserVO assigneeUser = adminUserApi.getUserById(reqVO.getAssigneeUserId()).getData(); + if (assigneeUser == null) { + throw new BusinessException(BpmResponseEnum.TASK_TRANSFER_FAIL_USER_NOT_EXISTS); + } + + // 2. 添加委托意见 + UserVO currentUser = adminUserApi.getUserById(userId).getData(); + taskService.addComment(taskId, task.getProcessInstanceId(), BpmCommentTypeEnum.TRANSFER.getType(), + BpmCommentTypeEnum.TRANSFER.formatComment(currentUser.getName(), assigneeUser.getName(), reqVO.getReason())); + + // 3.1 设置任务所有人 (owner) 为原任务的处理人 (assignee) + taskService.setOwner(taskId, task.getAssignee()); + // 3.2 执行转派(审批人),将任务转派给 assigneeUser + // 委托( delegate)和转派(transfer)的差别,就在这块的调用!!!! + taskService.setAssignee(taskId, reqVO.getAssigneeUserId().toString()); + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void createSignTask(String userId, BpmTaskSignCreateParam reqVO) { + // 1. 获取和校验任务 + TaskEntityImpl taskEntity = validateTaskCanCreateSign(userId, reqVO); + List userList = adminUserApi.getUserVOByIdList(reqVO.getUserIds()).getData(); + if (CollUtil.isEmpty(userList)) { + throw new BusinessException(BpmResponseEnum.TASK_SIGN_CREATE_USER_NOT_EXIST); + } + + // 2. 处理当前任务 + // 2.1 开启计数功能,主要用于为了让表 ACT_RU_TASK 中的 SUB_TASK_COUNT_ 字段记录下总共有多少子任务,后续可能有用 + taskEntity.setCountEnabled(true); + // 2.2 向前加签,设置 owner,置空 assign。等子任务都完成后,再调用 resolveTask 重新将 owner 设置为 assign + // 原因是:不能和向前加签的子任务一起审批,需要等前面的子任务都完成才能审批 + if (reqVO.getType().equals(BpmTaskSignTypeEnum.BEFORE.getType())) { + taskEntity.setOwner(taskEntity.getAssignee()); + taskEntity.setAssignee(null); + } + // 2.4 记录加签方式,完成任务时需要用到判断 + taskEntity.setScopeType(reqVO.getType()); + // 2.5 保存当前任务修改后的值 + taskService.saveTask(taskEntity); + // 2.6 更新 task 状态为 WAIT,只有在向前加签的时候 + if (reqVO.getType().equals(BpmTaskSignTypeEnum.BEFORE.getType())) { + updateTaskStatus(taskEntity.getId(), BpmTaskStatusEnum.WAIT.getStatus()); + } + + // 3. 创建加签任务 + createSignTaskList(convertList(reqVO.getUserIds(), String::valueOf), taskEntity); + + // 4. 记录加签的评论到 task 任务 + UserVO currentUser = adminUserApi.getUserById(userId).getData(); + String comment = StrUtil.format(BpmCommentTypeEnum.ADD_SIGN.getComment(), + currentUser.getName(), BpmTaskSignTypeEnum.nameOfType(reqVO.getType()), + String.join(",", convertList(userList, UserVO::getName)), reqVO.getReason()); + taskService.addComment(reqVO.getId(), taskEntity.getProcessInstanceId(), BpmCommentTypeEnum.ADD_SIGN.getType(), comment); + } + + /** + * 校验任务是否可以加签,主要校验加签类型是否一致: + *

+ * 1. 如果存在“向前加签”的任务,则不能“向后加签” + * 2. 如果存在“向后加签”的任务,则不能“向前加签” + * + * @param userId 当前用户 ID + * @param reqVO 请求参数,包含任务 ID 和加签类型 + * @return 当前任务 + */ + private TaskEntityImpl validateTaskCanCreateSign(String userId, BpmTaskSignCreateParam reqVO) { + TaskEntityImpl taskEntity = (TaskEntityImpl) validateTask(userId, reqVO.getId()); + // 向前加签和向后加签不能同时存在 + if (taskEntity.getScopeType() != null + && ObjectUtil.notEqual(taskEntity.getScopeType(), reqVO.getType())) { + throw new BusinessException("任务加签:当前任务已经" + BpmTaskSignTypeEnum.nameOfType(taskEntity.getScopeType()) + ",不能" + BpmTaskSignTypeEnum.nameOfType(reqVO.getType())); + + } + // 同一个 key 的任务,审批人不重复 + List taskList = taskService.createTaskQuery().processInstanceId(taskEntity.getProcessInstanceId()) + .taskDefinitionKey(taskEntity.getTaskDefinitionKey()).list(); + List currentAssigneeList = convertListByFlatMap(taskList, task -> // 需要考虑 owner 的情况,因为向后加签时,它暂时没 assignee 而是 owner + Stream.of(task.getAssignee(), task.getOwner())); + if (CollUtil.containsAny(currentAssigneeList, reqVO.getUserIds())) { + List userList = adminUserApi.getUserVOByIdList((List) CollUtil.intersection(currentAssigneeList, reqVO.getUserIds())).getData(); + throw new BusinessException("任务加签失败,加签人与现有审批人[" + String.join(",", convertList(userList, UserVO::getName)) + "]重复"); + } + return taskEntity; + } + + + /** + * 创建加签子任务 + * + * @param userIds 被加签的用户 ID + * @param taskEntity 被加签的任务 + */ + private void createSignTaskList(List userIds, TaskEntityImpl taskEntity) { + if (CollUtil.isEmpty(userIds)) { + return; + } + // 创建加签人的新任务,全部基于 taskEntity 为父任务来创建 + for (String addSignId : userIds) { + if (StrUtil.isBlank(addSignId)) { + continue; + } + createSignTask(taskEntity, addSignId); + } + } + + /** + * 创建加签子任务 + * + * @param parentTask 父任务 + * @param assignee 子任务的执行人 + */ + private void createSignTask(TaskEntityImpl parentTask, String assignee) { + // 1. 生成子任务 + TaskEntityImpl task = (TaskEntityImpl) taskService.newTask(IdUtil.fastSimpleUUID()); + BpmTaskConvert.INSTANCE.copyTo(parentTask, task); + + // 2.1 向前加签,设置审批人 + if (BpmTaskSignTypeEnum.BEFORE.getType().equals(parentTask.getScopeType())) { + task.setAssignee(assignee); + // 2.2 向后加签,设置 owner 不设置 assignee 是因为不能同时审批,需要等父任务完成 + } else { + task.setOwner(assignee); + } + // 2.3 保存子任务 + taskService.saveTask(task); + + // 3. 向后前签,设置子任务的状态为 WAIT,因为需要等父任务审批完 + if (BpmTaskSignTypeEnum.AFTER.getType().equals(parentTask.getScopeType())) { + updateTaskStatus(task.getId(), BpmTaskStatusEnum.WAIT.getStatus()); + } + } + + @Override + @Transactional(rollbackFor = Exception.class) + public void deleteSignTask(String userId, BpmTaskSignDeleteParam reqVO) { + // 1.1 校验 task 可以被减签 + Task task = validateTaskCanSignDelete(reqVO.getId()); + // 1.2 校验取消人存在 + UserVO cancelUser = null; + if (StrUtil.isNotBlank(task.getAssignee())) { + cancelUser = adminUserApi.getUserById(task.getAssignee()).getData(); + } + if (cancelUser == null && StrUtil.isNotBlank(task.getOwner())) { + cancelUser = adminUserApi.getUserById(task.getOwner()).getData(); + } + Assert.notNull(cancelUser, "任务中没有所有者和审批人,数据错误"); + + // 2.1 获得子任务列表,包括子任务的子任务 + List childTaskList = getAllChildTaskList(task); + childTaskList.add(task); + // 2.2 更新子任务为已取消 + String cancelReason = StrUtil.format("任务被取消,原因:由于[{}]操作[减签],", cancelUser.getName()); + childTaskList.forEach(childTask -> updateTaskStatusAndReason(childTask.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), cancelReason)); + // 2.3 删除任务和所有子任务 + taskService.deleteTasks(convertList(childTaskList, Task::getId)); + // 3. 记录日志到父任务中。先记录日志是因为,通过 handleParentTask 方法之后,任务可能被完成了,并且不存在了,会报异常,所以先记录 + UserVO user = adminUserApi.getUserById(userId).getData(); + taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(), BpmCommentTypeEnum.SUB_SIGN.getType(), + StrUtil.format(BpmCommentTypeEnum.SUB_SIGN.getComment(), user.getName(), cancelUser.getName())); + + // 4. 处理当前任务的父任务 + handleParentTaskIfSign(task.getParentTaskId()); + } + + /** + * 校验任务是否能被减签 + * + * @param id 任务编号 + * @return 任务信息 + */ + private Task validateTaskCanSignDelete(String id) { + Task task = validateTaskExist(id); + if (task.getParentTaskId() == null) { + throw new BusinessException(BpmResponseEnum.TASK_SIGN_DELETE_NO_PARENT); + } + Task parentTask = getTask(task.getParentTaskId()); + if (parentTask == null) { + throw new BusinessException(BpmResponseEnum.TASK_SIGN_DELETE_NO_PARENT); + } + if (BpmTaskSignTypeEnum.of(parentTask.getScopeType()) == null) { + throw new BusinessException(BpmResponseEnum.TASK_SIGN_DELETE_NO_PARENT); + } + return task; + } + + /** + * 获得所有子任务列表 + * + * @param parentTask 父任务 + * @return 所有子任务列表 + */ + private List getAllChildTaskList(Task parentTask) { + List result = new ArrayList<>(); + // 1. 递归获取子级 + Stack stack = new Stack<>(); + stack.push(parentTask); + // 2. 递归遍历 + for (int i = 0; i < Short.MAX_VALUE; i++) { + if (stack.isEmpty()) { + break; + } + // 2.1 获取子任务们 + Task task = stack.pop(); + List childTaskList = getTaskListByParentTaskId(task.getId()); + // 2.2 如果非空,则添加到 stack 进一步递归 + if (CollUtil.isNotEmpty(childTaskList)) { + stack.addAll(childTaskList); + result.addAll(childTaskList); + } + } + return result; + } + + @Override + public List getTaskListByParentTaskId(String parentTaskId) { + String tableName = managementService.getTableName(TaskEntity.class); + // taskService.createTaskQuery() 没有 parentId 参数,所以写 sql 查询 + String sql = "select ID_,NAME_,OWNER_,ASSIGNEE_ from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}"; + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).list(); + } + + /** + * 获取子任务个数 + * + * @param parentTaskId 父任务 ID + * @return 剩余子任务个数 + */ + private Long getTaskCountByParentTaskId(String parentTaskId) { + String tableName = managementService.getTableName(TaskEntity.class); + String sql = "SELECT COUNT(1) from " + tableName + " WHERE PARENT_TASK_ID_=#{parentTaskId}"; + return taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count(); + } +// +// @Override +// public Map getTaskNameByTaskIds(Collection taskIds) { +// if (CollUtil.isEmpty(taskIds)) { +// return Collections.emptyMap(); +// } +// List tasks = taskService.createTaskQuery().taskIds(taskIds).list(); +// return convertMap(tasks, Task::getId, Task::getName); +// } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/ProcessInstanceCopyServiceImpl.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/ProcessInstanceCopyServiceImpl.java new file mode 100644 index 000000000..f06864256 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/service/task/impl/ProcessInstanceCopyServiceImpl.java @@ -0,0 +1,82 @@ +package com.njcn.bpm.service.task.impl; + +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.njcn.bpm.enums.BpmResponseEnum; +import com.njcn.bpm.mapper.task.ProcessInstanceCopyMapper; +import com.njcn.bpm.pojo.po.task.ProcessInstanceCopy; +import com.njcn.bpm.service.IBpmProcessDefinitionService; +import com.njcn.bpm.service.task.IBpmProcessInstanceService; +import com.njcn.bpm.service.task.IBpmTaskService; +import com.njcn.bpm.service.task.IProcessInstanceCopyService; +import com.njcn.common.pojo.exception.BusinessException; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + *

+ * BPM 流程实例抄送表 服务实现类 + *

+ * + * @author hongawen + * @since 2024-05-10 + */ +@Service +public class ProcessInstanceCopyServiceImpl extends ServiceImpl implements IProcessInstanceCopyService { + + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private IBpmTaskService taskService; + + @Resource + @Lazy // 延迟加载,避免循环依赖 + private IBpmProcessInstanceService processInstanceService; + @Resource + @Lazy // 延迟加载,避免循环依赖 + private IBpmProcessDefinitionService processDefinitionService; + + @Override + public void createProcessInstanceCopy(Collection userIds, String taskId) { + // 1.1 校验任务存在 + Task task = taskService.getTask(taskId); + if (ObjectUtil.isNull(task)) { + throw new BusinessException(BpmResponseEnum.TASK_NOT_EXISTS); + } + // 1.2 校验流程实例存在 + String processInstanceId = task.getProcessInstanceId(); + ProcessInstance processInstance = processInstanceService.getProcessInstance(processInstanceId); + if (processInstance == null) { + throw new BusinessException(BpmResponseEnum.PROCESS_INSTANCE_NOT_EXISTS); + } + // 1.3 校验流程定义存在 + ProcessDefinition processDefinition = processDefinitionService.getProcessDefinition( + processInstance.getProcessDefinitionId()); + if (processDefinition == null) { + throw new BusinessException(BpmResponseEnum.PROCESS_DEFINITION_NOT_EXISTS); + } + + // 2. 创建抄送流程 + List copyList = userIds.stream().map(userId -> { + ProcessInstanceCopy processInstanceCopy = new ProcessInstanceCopy(); + processInstanceCopy.setUserId(userId); + processInstanceCopy.setStartUserId(processInstance.getStartUserId()); + processInstanceCopy.setProcessInstanceId(processInstanceId); + processInstanceCopy.setProcessInstanceName(processInstance.getName()); + processInstanceCopy.setCategory(processDefinition.getCategory()); + processInstanceCopy.setTaskId(taskId); + processInstanceCopy.setTaskName(task.getName()); + return processInstanceCopy; + }).collect(Collectors.toList()); + + this.saveBatch(copyList); + } +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateInvoker.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateInvoker.java index 90a5a236c..17905b488 100644 --- a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateInvoker.java +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/strategy/BpmTaskCandidateInvoker.java @@ -11,6 +11,7 @@ import com.njcn.common.pojo.enums.common.DataStateEnum; import com.njcn.common.pojo.exception.BusinessException; import com.njcn.user.api.UserFeignClient; import com.njcn.user.pojo.po.User; +import com.njcn.user.pojo.vo.UserVO; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.UserTask; @@ -94,10 +95,10 @@ public class BpmTaskCandidateInvoker { if (CollUtil.isEmpty(assigneeUserIds)) { return; } - List users = adminUserApi.getUserByIdList(assigneeUserIds).getData(); - Map userMap = CollectionUtils.convertMap(users, User::getId); + List users = adminUserApi.getUserVOByIdList(assigneeUserIds).getData(); + Map userMap = CollectionUtils.convertMap(users, UserVO::getId); assigneeUserIds.removeIf(id -> { - User user = userMap.get(id); + UserVO user = userMap.get(id); return user == null || !DataStateEnum.ENABLE.getCode().equals(user.getState()); }); } diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmActivityConvert.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmActivityConvert.java new file mode 100644 index 000000000..8de2db875 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmActivityConvert.java @@ -0,0 +1,29 @@ +package com.njcn.bpm.utils; + +import com.njcn.bpm.pojo.vo.task.BpmActivityVO; +import org.flowable.engine.history.HistoricActivityInstance; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + +import java.util.List; + +/** + * BPM 活动 Convert + * + * @author 芋道源码 + */ +@Mapper(uses = DateUtils.class) +public interface BpmActivityConvert { + + BpmActivityConvert INSTANCE = Mappers.getMapper(BpmActivityConvert.class); + + List convertList(List list); + + @Mappings({ + @Mapping(source = "activityId", target = "key"), + @Mapping(source = "activityType", target = "type") + }) + BpmActivityVO convert(HistoricActivityInstance bean); +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmProcessInstanceConvert.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmProcessInstanceConvert.java new file mode 100644 index 000000000..65c20c817 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmProcessInstanceConvert.java @@ -0,0 +1,125 @@ +package com.njcn.bpm.utils; + +import com.njcn.bpm.event.BpmProcessInstanceStatusEvent; +import com.njcn.bpm.pojo.dto.BpmMessageSendWhenProcessInstanceApproveReqDTO; +import com.njcn.bpm.pojo.dto.BpmMessageSendWhenProcessInstanceRejectReqDTO; +import com.njcn.bpm.pojo.dto.PageResult; +import com.njcn.bpm.pojo.po.BpmCategory; +import com.njcn.bpm.pojo.po.BpmProcessDefinitionInfo; +import com.njcn.bpm.pojo.vo.BpmProcessDefinitionInfoVO; +import com.njcn.bpm.pojo.vo.instance.BpmProcessInstanceVO; +import com.njcn.user.pojo.po.Dept; +import com.njcn.user.pojo.po.User; +import com.njcn.user.pojo.vo.UserVO; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.repository.ProcessDefinition; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import java.util.List; +import java.util.Map; + +/** + * 流程实例 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface BpmProcessInstanceConvert { + + BpmProcessInstanceConvert INSTANCE = Mappers.getMapper(BpmProcessInstanceConvert.class); + + default PageResult buildProcessInstancePage(PageResult pageResult, + Map processDefinitionMap, + Map categoryMap, + Map> taskMap, + Map userMap, + Map deptMap) { + PageResult vpPageResult = BeanUtils.toBean(pageResult, BpmProcessInstanceVO.class); + for (int i = 0; i < pageResult.getList().size(); i++) { + BpmProcessInstanceVO respVO = vpPageResult.getList().get(i); + respVO.setStatus(FlowableUtils.getProcessInstanceStatus(pageResult.getList().get(i))); + MapUtils.findAndThen(processDefinitionMap, respVO.getProcessDefinitionId(), + processDefinition -> respVO.setCategory(processDefinition.getCategory())); + MapUtils.findAndThen(categoryMap, respVO.getCategory(), category -> respVO.setCategoryName(category.getName())); + respVO.setTasks(BeanUtils.toBean(taskMap.get(respVO.getId()), BpmProcessInstanceVO.Task.class)); + // user + if (userMap != null) { + UserVO startUser = userMap.get(pageResult.getList().get(i).getStartUserId()); + if (startUser != null) { + respVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceVO.User.class)); + MapUtils.findAndThen(deptMap, startUser.getDeptId(), dept -> respVO.getStartUser().setDeptName(dept.getName())); + } + } + } + return vpPageResult; + } + + default BpmProcessInstanceVO buildProcessInstance(HistoricProcessInstance processInstance, + ProcessDefinition processDefinition, + BpmProcessDefinitionInfo processDefinitionExt, + String bpmnXml, + UserVO startUser, + Dept dept) { + BpmProcessInstanceVO respVO = BeanUtils.toBean(processInstance, BpmProcessInstanceVO.class); + respVO.setStatus(FlowableUtils.getProcessInstanceStatus(processInstance)); + respVO.setFormVariables(FlowableUtils.getProcessInstanceFormVariable(processInstance)); + // definition + respVO.setProcessDefinition(BeanUtils.toBean(processDefinition, BpmProcessDefinitionInfoVO.class)); + copyTo(processDefinitionExt, respVO.getProcessDefinition()); + respVO.getProcessDefinition().setBpmnXml(bpmnXml); + // user + if (startUser != null) { + respVO.setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceVO.User.class)); + if (dept != null) { + respVO.getStartUser().setDeptName(dept.getName()); + } + } + return respVO; + } + + @Mapping(source = "from.id", target = "to.id", ignore = true) + void copyTo(BpmProcessDefinitionInfo from, @MappingTarget BpmProcessDefinitionInfoVO to); + + default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, HistoricProcessInstance instance, Integer status) { + BpmProcessInstanceStatusEvent bpmProcessInstanceStatusEvent = new BpmProcessInstanceStatusEvent(source); + bpmProcessInstanceStatusEvent.setId(instance.getId()); + bpmProcessInstanceStatusEvent.setStatus(status); + bpmProcessInstanceStatusEvent.setProcessDefinitionKey(instance.getProcessDefinitionKey()); + bpmProcessInstanceStatusEvent.setBusinessKey(instance.getBusinessKey()); + return bpmProcessInstanceStatusEvent; + } + + default BpmProcessInstanceStatusEvent buildProcessInstanceStatusEvent(Object source, ProcessInstance instance, Integer status) { + BpmProcessInstanceStatusEvent bpmProcessInstanceStatusEvent = new BpmProcessInstanceStatusEvent(source); + bpmProcessInstanceStatusEvent.setId(instance.getId()); + bpmProcessInstanceStatusEvent.setStatus(status); + bpmProcessInstanceStatusEvent.setProcessDefinitionKey(instance.getProcessDefinitionKey()); + bpmProcessInstanceStatusEvent.setBusinessKey(instance.getBusinessKey()); + return bpmProcessInstanceStatusEvent; + } + + default BpmMessageSendWhenProcessInstanceApproveReqDTO buildProcessInstanceApproveMessage(ProcessInstance instance) { + BpmMessageSendWhenProcessInstanceApproveReqDTO bpmMessageSendWhenProcessInstanceApproveReqDTO = new BpmMessageSendWhenProcessInstanceApproveReqDTO(); + bpmMessageSendWhenProcessInstanceApproveReqDTO.setStartUserId(instance.getStartUserId()); + bpmMessageSendWhenProcessInstanceApproveReqDTO.setProcessInstanceId(instance.getId()); + bpmMessageSendWhenProcessInstanceApproveReqDTO.setProcessInstanceName(instance.getName()); + + return bpmMessageSendWhenProcessInstanceApproveReqDTO; + } + + default BpmMessageSendWhenProcessInstanceRejectReqDTO buildProcessInstanceRejectMessage(ProcessInstance instance, String reason) { + BpmMessageSendWhenProcessInstanceRejectReqDTO bpmMessageSendWhenProcessInstanceRejectReqDTO = new BpmMessageSendWhenProcessInstanceRejectReqDTO(); + bpmMessageSendWhenProcessInstanceRejectReqDTO.setProcessInstanceName(instance.getName()); + bpmMessageSendWhenProcessInstanceRejectReqDTO.setProcessInstanceId(instance.getId()); + bpmMessageSendWhenProcessInstanceRejectReqDTO.setReason(reason); + bpmMessageSendWhenProcessInstanceRejectReqDTO.setStartUserId(instance.getStartUserId()); + return bpmMessageSendWhenProcessInstanceRejectReqDTO; + } + + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmTaskConvert.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmTaskConvert.java new file mode 100644 index 000000000..35af50924 --- /dev/null +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/BpmTaskConvert.java @@ -0,0 +1,180 @@ +package com.njcn.bpm.utils; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.njcn.bpm.pojo.dto.PageResult; +import com.njcn.bpm.pojo.po.BpmForm; +import com.njcn.bpm.pojo.vo.instance.BpmProcessInstanceVO; +import com.njcn.bpm.pojo.vo.task.BpmTaskVO; +import com.njcn.user.pojo.po.Dept; +import com.njcn.user.pojo.vo.UserVO; +import org.flowable.engine.history.HistoricProcessInstance; +import org.flowable.engine.runtime.ProcessInstance; +import org.flowable.task.api.Task; +import org.flowable.task.api.history.HistoricTaskInstance; +import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +import java.util.Date; +import java.util.List; +import java.util.Map; + +import static com.njcn.bpm.utils.CollectionUtils.*; +import static com.njcn.bpm.utils.MapUtils.findAndThen; + + +/** + * Bpm 任务 Convert + * + * @author 芋道源码 + */ +@Mapper +public interface BpmTaskConvert { + + BpmTaskConvert INSTANCE = Mappers.getMapper(BpmTaskConvert.class); + + default PageResult buildTodoTaskPage(PageResult pageResult, + Map processInstanceMap, + Map userMap) { + + return BeanUtils.toBean(pageResult, BpmTaskVO.class, taskVO -> { + ProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId()); + if (processInstance == null) { + return; + } + taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskVO.ProcessInstance.class)); + UserVO startUserVO = userMap.get(processInstance.getStartUserId()); + taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUserVO, BpmProcessInstanceVO.User.class)); + }); + + } + + + default PageResult buildTaskPage(PageResult pageResult, + Map processInstanceMap, + Map userMap, + Map deptMap) { + List taskVOList = convertList(pageResult.getList(), task -> { + BpmTaskVO taskVO = BeanUtils.toBean(task, BpmTaskVO.class); + taskVO.setStatus(FlowableUtils.getTaskStatus(task)); + taskVO.setReason(FlowableUtils.getTaskReason(task)); + // 用户信息 + UserVO assignUser = userMap.get(task.getAssignee()); + if (assignUser != null) { + taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, BpmProcessInstanceVO.User.class)); + findAndThen(deptMap, assignUser.getDeptId(), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName())); + } + // 流程实例 + HistoricProcessInstance processInstance = processInstanceMap.get(taskVO.getProcessInstanceId()); + if (processInstance != null) { + UserVO startUser = userMap.get(processInstance.getStartUserId()); + taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskVO.ProcessInstance.class)); + taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceVO.User.class)); + } + return taskVO; + }); + return new PageResult<>(taskVOList, pageResult.getTotal()); + } + + default List buildTaskListByProcessInstanceId(List taskList, + HistoricProcessInstance processInstance, + Map formMap, + Map userMap, + Map deptMap) { + List taskVOList = convertList(taskList, task -> { + BpmTaskVO taskVO = BeanUtils.toBean(task, BpmTaskVO.class); + taskVO.setStatus(FlowableUtils.getTaskStatus(task)); + taskVO.setReason(FlowableUtils.getTaskReason(task)); + // 流程实例 + UserVO startUser = userMap.get(processInstance.getStartUserId()); + taskVO.setProcessInstance(BeanUtils.toBean(processInstance, BpmTaskVO.ProcessInstance.class)); + taskVO.getProcessInstance().setStartUser(BeanUtils.toBean(startUser, BpmProcessInstanceVO.User.class)); + // 表单信息 + BpmForm form = MapUtil.get(formMap, task.getFormKey(), BpmForm.class); + if (form != null) { + taskVO.setFormId(form.getId()); + taskVO.setFormName(form.getName()); + taskVO.setFormConf(form.getConf()); + taskVO.setFormFields(form.getFields()); + taskVO.setFormVariables(FlowableUtils.getTaskFormVariable(task)); + } + // 用户信息 + UserVO assignUser = userMap.get(task.getAssignee()); + if (assignUser != null) { + taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, BpmProcessInstanceVO.User.class)); + findAndThen(deptMap, assignUser.getDeptId(), dept -> taskVO.getAssigneeUser().setDeptName(dept.getName())); + } + UserVO ownerUser = userMap.get(task.getOwner()); + if (ownerUser != null) { + taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, BpmProcessInstanceVO.User.class)); + findAndThen(deptMap, ownerUser.getDeptId(), dept -> taskVO.getOwnerUser().setDeptName(dept.getName())); + } + return taskVO; + }); + + // 拼接父子关系 + Map> childrenTaskMap = convertMultiMap( + filterList(taskVOList, r -> StrUtil.isNotEmpty(r.getParentTaskId())), + BpmTaskVO::getParentTaskId); + for (BpmTaskVO taskVO : taskVOList) { + taskVO.setChildren(childrenTaskMap.get(taskVO.getId())); + } + return filterList(taskVOList, r -> StrUtil.isEmpty(r.getParentTaskId())); + } + + default List buildTaskListByParentTaskId(List taskList, + Map userMap, + Map deptMap) { + return convertList(taskList, task -> BeanUtils.toBean(task, BpmTaskVO.class, taskVO -> { + UserVO assignUser = userMap.get(task.getAssignee()); + if (assignUser != null) { + taskVO.setAssigneeUser(BeanUtils.toBean(assignUser, BpmProcessInstanceVO.User.class)); + Dept dept = deptMap.get(assignUser.getDeptId()); + if (dept != null) { + taskVO.getAssigneeUser().setDeptName(dept.getName()); + } + } + UserVO ownerUser = userMap.get(task.getOwner()); + if (ownerUser != null) { + taskVO.setOwnerUser(BeanUtils.toBean(ownerUser, BpmProcessInstanceVO.User.class)); + findAndThen(deptMap, ownerUser.getDeptId(), dept -> taskVO.getOwnerUser().setDeptName(dept.getName())); + } + })); + } + +// default BpmMessageSendWhenTaskCreatedReqDTO convert(ProcessInstance processInstance, AdminUserRespDTO startUser, +// Task task) { +// BpmMessageSendWhenTaskCreatedReqDTO reqDTO = new BpmMessageSendWhenTaskCreatedReqDTO(); +// reqDTO.setProcessInstanceId(processInstance.getProcessInstanceId()) +// .setProcessInstanceName(processInstance.getName()).setStartUserId(startUser.getId()) +// .setStartUserNickname(startUser.getNickname()).setTaskId(task.getId()).setTaskName(task.getName()) +// .setAssigneeUserId(NumberUtils.parseLong(task.getAssignee())); +// return reqDTO; +// } + + /** + * 将父任务的属性,拷贝到子任务(加签任务) + *

+ * 为什么不使用 mapstruct 映射?因为 TaskEntityImpl 还有很多其他属性,这里我们只设置我们需要的。 + * 使用 mapstruct 会将里面嵌套的各个属性值都设置进去,会出现意想不到的问题。 + * + * @param parentTask 父任务 + * @param childTask 加签任务 + */ + default void copyTo(TaskEntityImpl parentTask, TaskEntityImpl childTask) { + childTask.setName(parentTask.getName()); + childTask.setDescription(parentTask.getDescription()); + childTask.setCategory(parentTask.getCategory()); + childTask.setParentTaskId(parentTask.getId()); + childTask.setProcessDefinitionId(parentTask.getProcessDefinitionId()); + childTask.setProcessInstanceId(parentTask.getProcessInstanceId()); +// childTask.setExecutionId(parentTask.getExecutionId()); // TODO 芋艿:新加的,不太确定;尴尬,不加时,子任务不通过会失败(报错);加了,子任务审批通过会失败(报错) + childTask.setTaskDefinitionKey(parentTask.getTaskDefinitionKey()); + childTask.setTaskDefinitionId(parentTask.getTaskDefinitionId()); + childTask.setPriority(parentTask.getPriority()); + childTask.setCreateTime(new Date()); + childTask.setTenantId(parentTask.getTenantId()); + } + +} diff --git a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/FlowableUtils.java b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/FlowableUtils.java index 1ed66e20e..fe924e801 100644 --- a/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/FlowableUtils.java +++ b/pqs-bpm/bpm-boot/src/main/java/com/njcn/bpm/utils/FlowableUtils.java @@ -25,8 +25,8 @@ public class FlowableUtils { // ========== User 相关的工具方法 ========== - public static void setAuthenticatedUserId(Long userId) { - Authentication.setAuthenticatedUserId(String.valueOf(userId)); + public static void setAuthenticatedUserId(String userId) { + Authentication.setAuthenticatedUserId(userId); } public static void clearAuthenticatedUserId() { diff --git a/pqs-common/common-autocode/src/main/java/com/njcn/autocode/utils/GenerateCode.java b/pqs-common/common-autocode/src/main/java/com/njcn/autocode/utils/GenerateCode.java index 7708c47f5..8fa80a5ca 100644 --- a/pqs-common/common-autocode/src/main/java/com/njcn/autocode/utils/GenerateCode.java +++ b/pqs-common/common-autocode/src/main/java/com/njcn/autocode/utils/GenerateCode.java @@ -21,7 +21,7 @@ public class GenerateCode { private static final String TARGET_DIR = "D://code"; - private static final String DB_URL = "jdbc:mysql://192.168.1.24:13306/pqsinfo_hn_process"; + private static final String DB_URL = "jdbc:mysql://192.168.1.24:13306/bpm"; // private static final String DB_URL = "jdbc:oracle:thin:@192.168.1.170:1521:pqsbase"; private static final String USERNAME = "root"; @@ -30,9 +30,9 @@ public class GenerateCode { public static void main(String[] args) { List modules = Stream.of( - new Module("hongawen", "com.njcn.process", "workflow", Stream.of( - "wf_deploy_form" - ).collect(Collectors.toList()), "wf_") + new Module("hongawen", "com.njcn.bpm", "workflow", Stream.of( + "bpm_process_instance_copy" + ).collect(Collectors.toList()), "bpm_") ).collect(Collectors.toList()); generateJavaFile(modules); } diff --git a/pqs-common/common-core/src/main/java/com/njcn/common/pojo/constant/ServerInfo.java b/pqs-common/common-core/src/main/java/com/njcn/common/pojo/constant/ServerInfo.java index 762204568..75f4ff059 100644 --- a/pqs-common/common-core/src/main/java/com/njcn/common/pojo/constant/ServerInfo.java +++ b/pqs-common/common-core/src/main/java/com/njcn/common/pojo/constant/ServerInfo.java @@ -16,6 +16,7 @@ public interface ServerInfo { String DEVICE = "device-boot"; String EVENT = "event-boot"; String SUPERVISION = "supervision-boot"; + String BPM = "bpm-boot"; String HARMONIC = "harmonic-boot"; String REPORT = "report-boot"; String SYSTEM = "system-boot"; diff --git a/pqs-supervision/supervision-api/src/main/java/com/njcn/supervision/enums/UserNatureEnum.java b/pqs-supervision/supervision-api/src/main/java/com/njcn/supervision/enums/UserNatureEnum.java index 4267434ff..35803f0d8 100644 --- a/pqs-supervision/supervision-api/src/main/java/com/njcn/supervision/enums/UserNatureEnum.java +++ b/pqs-supervision/supervision-api/src/main/java/com/njcn/supervision/enums/UserNatureEnum.java @@ -12,10 +12,12 @@ public enum UserNatureEnum { BUILD_POWER_GRID(0, "新建电网工程"), EXTEND_POWER_GRID(1, "扩建电网工程"), + BUILD_NON_LINEAR_LOAD(2, "新建非线性负荷用户"), EXTEND_NON_LINEAR_LOAD(3, "扩建非线性负荷用户"), BUILD_NEW_ENERGY_POWER_STATION(4, "新建新能源发电站"), EXTEND_NEW_ENERGY_POWER_STATION(5, "扩建新能源发电站"), + SENSITIVE_USER(6, "敏感及重要用户"); private final Integer code; diff --git a/pqs-supervision/supervision-api/src/main/java/com/njcn/supervision/pojo/vo/user/UserReportVO.java b/pqs-supervision/supervision-api/src/main/java/com/njcn/supervision/pojo/vo/user/UserReportVO.java index 8b4f75449..91f4c2f3f 100644 --- a/pqs-supervision/supervision-api/src/main/java/com/njcn/supervision/pojo/vo/user/UserReportVO.java +++ b/pqs-supervision/supervision-api/src/main/java/com/njcn/supervision/pojo/vo/user/UserReportVO.java @@ -40,6 +40,12 @@ public class UserReportVO { @ApiModelProperty(value = "填报部门") private String orgId; + /** + * 填报部门 + */ + @ApiModelProperty(value = "填报部门名称") + private String orgName; + /** * 工程预期投产日期 */ @@ -71,6 +77,7 @@ public class UserReportVO { @ApiModelProperty(value = "用户状态") private Integer userStatus; + /** * 变电站 */ diff --git a/pqs-supervision/supervision-boot/pom.xml b/pqs-supervision/supervision-boot/pom.xml index 153369092..d6878262b 100644 --- a/pqs-supervision/supervision-boot/pom.xml +++ b/pqs-supervision/supervision-boot/pom.xml @@ -30,6 +30,12 @@ ${project.version} + + com.njcn + user-api + ${project.version} + + com.njcn common-web diff --git a/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/controller/user/UserReportManageController.java b/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/controller/user/UserReportManageController.java index 6637508d8..8f90b976f 100644 --- a/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/controller/user/UserReportManageController.java +++ b/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/controller/user/UserReportManageController.java @@ -1,6 +1,7 @@ package com.njcn.supervision.controller.user; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.njcn.bpm.pojo.po.BpmForm; import com.njcn.common.pojo.annotation.OperateInfo; import com.njcn.common.pojo.constant.OperateType; import com.njcn.common.pojo.enums.common.LogEnum; @@ -14,6 +15,8 @@ import com.njcn.web.controller.BaseController; import io.swagger.annotations.Api; import io.swagger.annotations.ApiImplicitParam; import io.swagger.annotations.ApiOperation; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; @@ -39,7 +42,7 @@ public class UserReportManageController extends BaseController { @OperateInfo(info = LogEnum.BUSINESS_COMMON,operateType= OperateType.ADD) @PostMapping("/add") - @ApiOperation("新增干扰源用户(未建档干扰源用户管理)") + @ApiOperation("新增干扰源用户") @ApiImplicitParam(name = "userReportParam", value = "实体参数", required = true) public HttpResult addUserReport(@RequestBody @Validated UserReportParam userReportParam){ String methodDescribe = getMethodDescribe("addUserReport"); @@ -50,7 +53,7 @@ public class UserReportManageController extends BaseController { @OperateInfo(info = LogEnum.BUSINESS_COMMON,operateType= OperateType.ADD) @PostMapping("/auditUserReport") - @ApiOperation("修改干扰源用户(未建档干扰源用户管理)") + @ApiOperation("修改干扰源用户") @ApiImplicitParam(name = "userReportUpdate", value = "实体参数", required = true) public HttpResult auditUserReport(@RequestBody @Validated UserReportParam.UserReportUpdate userReportUpdate){ String methodDescribe = getMethodDescribe("auditUserReport"); @@ -80,4 +83,13 @@ public class UserReportManageController extends BaseController { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, flag, methodDescribe); } + @GetMapping("/getById") + @OperateInfo(info = LogEnum.BUSINESS_COMMON) + @Operation(summary = "根据id获取用户档案录入的详细数据") + public HttpResult getById(String id) { + String methodDescribe = getMethodDescribe("getById"); + UserReportVO userReportVO = userReportPOService.getVOById(id); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, userReportVO, methodDescribe); + } + } diff --git a/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/service/user/UserReportPOService.java b/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/service/user/UserReportPOService.java index 621723cab..44dc7ee25 100644 --- a/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/service/user/UserReportPOService.java +++ b/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/service/user/UserReportPOService.java @@ -25,4 +25,6 @@ public interface UserReportPOService extends IService { Page getUserReport(UserReportParam.UserReportQueryParam userReportQueryParam); Boolean removeUserReport(List ids); + + UserReportVO getVOById(String id); } diff --git a/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/service/user/impl/UserReportPOServiceImpl.java b/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/service/user/impl/UserReportPOServiceImpl.java index d89094b7d..d57c7e751 100644 --- a/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/service/user/impl/UserReportPOServiceImpl.java +++ b/pqs-supervision/supervision-boot/src/main/java/com/njcn/supervision/service/user/impl/UserReportPOServiceImpl.java @@ -1,5 +1,6 @@ package com.njcn.supervision.service.user.impl; +import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -23,6 +24,9 @@ import com.njcn.supervision.service.user.UserReportPOService; import com.njcn.supervision.service.user.UserReportProjectPOService; import com.njcn.supervision.service.user.UserReportSensitivePOService; import com.njcn.supervision.service.user.UserReportSubstationPOService; +import com.njcn.user.api.DeptFeignClient; +import com.njcn.user.api.UserFeignClient; +import com.njcn.user.pojo.vo.UserVO; import com.njcn.web.factory.PageFactory; import com.njcn.web.utils.RequestUtil; import lombok.RequiredArgsConstructor; @@ -55,6 +59,8 @@ public class UserReportPOServiceImpl extends ServiceImpl> getUserListByIds(@RequestBody List ids); + @PostMapping("/getUserVOByIdList") + HttpResult> getUserVOByIdList(@RequestBody List ids); + } diff --git a/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/UserFeignClientFallbackFactory.java b/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/UserFeignClientFallbackFactory.java index f2352f880..6967acbf2 100644 --- a/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/UserFeignClientFallbackFactory.java +++ b/pqs-user/user-api/src/main/java/com/njcn/user/api/fallback/UserFeignClientFallbackFactory.java @@ -111,6 +111,12 @@ public class UserFeignClientFallbackFactory implements FallbackFactory> getUserVOByIdList(List ids) { + log.error("{}异常,降级处理,异常为:{}","根据用户id集合获取用户集合",cause.toString()); + throw new BusinessException(finalExceptionEnum); + } + }; } } diff --git a/pqs-user/user-api/src/main/java/com/njcn/user/pojo/vo/UserVO.java b/pqs-user/user-api/src/main/java/com/njcn/user/pojo/vo/UserVO.java index b50e68f97..02895ae72 100644 --- a/pqs-user/user-api/src/main/java/com/njcn/user/pojo/vo/UserVO.java +++ b/pqs-user/user-api/src/main/java/com/njcn/user/pojo/vo/UserVO.java @@ -22,6 +22,9 @@ public class UserVO extends UserParam implements Serializable { @ApiModelProperty("用户Id") private String id; + @ApiModelProperty("用户名") + private String name; + @ApiModelProperty("登录名") private String loginName; @@ -34,6 +37,9 @@ public class UserVO extends UserParam implements Serializable { @ApiModelProperty("登录时间") private String loginTime; + @ApiModelProperty("部门编号") + private String deptId; + @ApiModelProperty("部门名称") private String deptName; diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/controller/UserController.java b/pqs-user/user-boot/src/main/java/com/njcn/user/controller/UserController.java index 0c7a9023c..bd93d4282 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/controller/UserController.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/controller/UserController.java @@ -442,6 +442,17 @@ public class UserController extends BaseController { return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, users, methodDescribe); } + @OperateInfo(info = LogEnum.SYSTEM_COMMON) + @PostMapping("/getUserVOByIdList") + @ApiOperation("根据用户id集合查询用户VO") + @ApiImplicitParam(name = "ids", value = "用户id集合", required = true) + public HttpResult> getUserVOByIdList(@RequestBody List ids) { + String methodDescribe = getMethodDescribe("getUserVOByIdList"); + LogUtil.njcnDebug(log, "{},用户id为:{}", methodDescribe, ids); + List userVO = userService.getUserVOByIdList(ids); + return HttpResultUtil.assembleCommonResponseResult(CommonResponseEnum.SUCCESS, userVO, methodDescribe); + } + @OperateInfo(info = LogEnum.SYSTEM_COMMON) @PostMapping("/appuserByIdList") @ApiOperation("根据用户id集合查询用户信息") diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/mapper/UserMapper.java b/pqs-user/user-boot/src/main/java/com/njcn/user/mapper/UserMapper.java index 693709175..afd797e89 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/mapper/UserMapper.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/mapper/UserMapper.java @@ -56,4 +56,6 @@ public interface UserMapper extends BaseMapper { * @return 查询出的用户数据 */ List queryExportUser(@Param("ew") QueryWrapper queryWrapper); + + List getUserVOByIdList(@Param("ids") List ids); } diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/mapper/mapping/UserMapper.xml b/pqs-user/user-boot/src/main/java/com/njcn/user/mapper/mapping/UserMapper.xml index 85ea5ac65..3ee0a7b0a 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/mapper/mapping/UserMapper.xml +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/mapper/mapping/UserMapper.xml @@ -65,5 +65,27 @@ + + + + diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/service/IUserService.java b/pqs-user/user-boot/src/main/java/com/njcn/user/service/IUserService.java index 8ca56b737..f5bf513f7 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/service/IUserService.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/service/IUserService.java @@ -193,7 +193,11 @@ public interface IUserService extends IService { List listAllUserByDeptId(String deptId); + List getUserVOByIdList(List ids); + List getUserIdByRoleId(List roleId); List simpleList(); + + } diff --git a/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/UserServiceImpl.java b/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/UserServiceImpl.java index 99b5e309b..c0c59d8d3 100644 --- a/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/UserServiceImpl.java +++ b/pqs-user/user-boot/src/main/java/com/njcn/user/service/impl/UserServiceImpl.java @@ -535,7 +535,7 @@ public class UserServiceImpl extends ServiceImpl implements IU lambdaQueryWrapper.eq(User::getDeptId, deptId); } lambdaQueryWrapper.eq(User::getState, DataStateEnum.ENABLE.getCode()) - .select(User::getName,User::getId); + .select(User::getName, User::getId); List users = this.baseMapper.selectList(lambdaQueryWrapper); if (CollectionUtil.isEmpty(users)) { return new ArrayList<>(); @@ -544,14 +544,24 @@ public class UserServiceImpl extends ServiceImpl implements IU } } + @Override + public List getUserVOByIdList(List ids) { + //查询所有状态正常的用户信息,目前只要id和name + if (CollectionUtil.isEmpty(ids)) { + return new ArrayList<>(); + } + List userVOList = this.baseMapper.getUserVOByIdList(ids); + return userVOList; + } + @Override public List getUserIdByRoleId(List roleId) { LambdaQueryWrapper userRoleLambdaQueryWrapper = new LambdaQueryWrapper<>(); - userRoleLambdaQueryWrapper.in(UserRole::getRoleId,roleId); + userRoleLambdaQueryWrapper.in(UserRole::getRoleId, roleId); List userRole = userRoleService.list(userRoleLambdaQueryWrapper); - if(CollectionUtil.isEmpty(userRole)){ + if (CollectionUtil.isEmpty(userRole)) { return new ArrayList<>(); - }else{ + } else { return userRole.stream().map(UserRole::getUserId).distinct().collect(Collectors.toList()); } } @@ -559,7 +569,7 @@ public class UserServiceImpl extends ServiceImpl implements IU @Override public List simpleList() { LambdaQueryWrapper userLambdaQueryWrapper = new LambdaQueryWrapper<>(); - userLambdaQueryWrapper.select(User::getId,User::getName).eq(User::getState,DataStateEnum.ENABLE.getCode()); + userLambdaQueryWrapper.select(User::getId, User::getName).eq(User::getState, DataStateEnum.ENABLE.getCode()); return this.baseMapper.selectList(userLambdaQueryWrapper); }