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