Commit 4d49952c52a299e9ead659dbc415df08c8d2994b

Authored by jason
1 parent 633a7c50

仿钉钉流程设计- 加签拒绝处理

yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/definition/BpmFieldPermissionEnum.java
@@ -13,10 +13,8 @@ import lombok.Getter; @@ -13,10 +13,8 @@ import lombok.Getter;
13 @AllArgsConstructor 13 @AllArgsConstructor
14 public enum BpmFieldPermissionEnum { 14 public enum BpmFieldPermissionEnum {
15 15
16 - // TODO @jason:这个顺序要不要改下,和页面保持一致;只读(1)、编辑(2)、隐藏(3)  
17 - // @芋艿 我看钉钉页面的顺序 是 可编辑 只读 隐藏  
18 - WRITE(1, "可编辑"),  
19 - READ(2, "只读"), 16 + READ(1, "只读"),
  17 + WRITE(2, "可编辑"),
20 NONE(3, "隐藏"); 18 NONE(3, "隐藏");
21 19
22 /** 20 /**
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmCommentTypeEnum.java
@@ -22,6 +22,7 @@ public enum BpmCommentTypeEnum { @@ -22,6 +22,7 @@ public enum BpmCommentTypeEnum {
22 TRANSFER("7", "转派", "[{}]将任务转派给[{}],转派理由为:{}"), 22 TRANSFER("7", "转派", "[{}]将任务转派给[{}],转派理由为:{}"),
23 ADD_SIGN("8", "加签", "[{}]{}给了[{}],理由为:{}"), 23 ADD_SIGN("8", "加签", "[{}]{}给了[{}],理由为:{}"),
24 SUB_SIGN("9", "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"), 24 SUB_SIGN("9", "减签", "[{}]操作了【减签】,审批人[{}]的任务被取消"),
  25 + REJECT_BY_ADD_SIGN_TASK_REJECT("10", "不通过","系统自动不通过,原因是:加签任务不通过")
25 ; 26 ;
26 27
27 /** 28 /**
yudao-module-bpm/yudao-module-bpm-api/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmDeleteReasonEnum.java
@@ -22,6 +22,7 @@ public enum BpmDeleteReasonEnum { @@ -22,6 +22,7 @@ public enum BpmDeleteReasonEnum {
22 // ========== 流程任务的独有原因 ========== 22 // ========== 流程任务的独有原因 ==========
23 23
24 CANCEL_BY_SYSTEM("系统自动取消"), // 场景:非常多,比如说:1)多任务审批已经满足条件,无需审批该任务;2)流程实例被取消,无需审批该任务;等等 24 CANCEL_BY_SYSTEM("系统自动取消"), // 场景:非常多,比如说:1)多任务审批已经满足条件,无需审批该任务;2)流程实例被取消,无需审批该任务;等等
  25 + AUTO_REJECT_BY_ADD_SIGN_REJECT("系统自动拒绝,原因:加签任务被拒绝") // 加签任务审批不通过,导致任务不通过
25 ; 26 ;
26 27
27 private final String reason; 28 private final String reason;
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTaskEventListener.java
1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener; 1 package cn.iocoder.yudao.module.bpm.framework.flowable.core.listener;
2 2
3 -import cn.hutool.core.collection.CollUtil;  
4 -import cn.hutool.core.util.StrUtil;  
5 import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService; 3 import cn.iocoder.yudao.module.bpm.service.task.BpmActivityService;
6 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService; 4 import cn.iocoder.yudao.module.bpm.service.task.BpmTaskService;
7 import com.google.common.collect.ImmutableSet; 5 import com.google.common.collect.ImmutableSet;
@@ -11,12 +9,10 @@ import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent; @@ -11,12 +9,10 @@ import org.flowable.common.engine.api.delegate.event.FlowableEngineEntityEvent;
11 import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType; 9 import org.flowable.common.engine.api.delegate.event.FlowableEngineEventType;
12 import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener; 10 import org.flowable.engine.delegate.event.AbstractFlowableEngineEventListener;
13 import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent; 11 import org.flowable.engine.delegate.event.FlowableActivityCancelledEvent;
14 -import org.flowable.engine.history.HistoricActivityInstance;  
15 import org.flowable.task.api.Task; 12 import org.flowable.task.api.Task;
16 import org.springframework.context.annotation.Lazy; 13 import org.springframework.context.annotation.Lazy;
17 import org.springframework.stereotype.Component; 14 import org.springframework.stereotype.Component;
18 15
19 -import java.util.List;  
20 import java.util.Set; 16 import java.util.Set;
21 17
22 /** 18 /**
@@ -38,8 +34,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { @@ -38,8 +34,7 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
38 public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder() 34 public static final Set<FlowableEngineEventType> TASK_EVENTS = ImmutableSet.<FlowableEngineEventType>builder()
39 .add(FlowableEngineEventType.TASK_CREATED) 35 .add(FlowableEngineEventType.TASK_CREATED)
40 .add(FlowableEngineEventType.TASK_ASSIGNED) 36 .add(FlowableEngineEventType.TASK_ASSIGNED)
41 - //.add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,所以不需要监听了。  
42 -// .add(FlowableEngineEventType.ACTIVITY_MESSAGE_RECEIVED) 37 +// .add(FlowableEngineEventType.TASK_COMPLETED) // 由于审批通过时,已经记录了 task 的 status 为通过,所以不需要监听了。
43 .add(FlowableEngineEventType.ACTIVITY_CANCELLED) 38 .add(FlowableEngineEventType.ACTIVITY_CANCELLED)
44 .build(); 39 .build();
45 40
@@ -59,18 +54,18 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener { @@ -59,18 +54,18 @@ public class BpmTaskEventListener extends AbstractFlowableEngineEventListener {
59 54
60 @Override 55 @Override
61 protected void activityCancelled(FlowableActivityCancelledEvent event) { 56 protected void activityCancelled(FlowableActivityCancelledEvent event) {
62 - List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId());  
63 - if (CollUtil.isEmpty(activityList)) {  
64 - log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId());  
65 - return;  
66 - }  
67 - // 遍历处理  
68 - activityList.forEach(activity -> {  
69 - if (StrUtil.isEmpty(activity.getTaskId())) {  
70 - return;  
71 - }  
72 - taskService.updateTaskStatusWhenCanceled(activity.getTaskId());  
73 - }); 57 + // @芋艿。 这里是不是就可以不要了, 取消的任务状态,在rejectTask 里面做了, 如果在 updateTaskStatusWhenCanceled 里面修改会报错。
  58 +// List<HistoricActivityInstance> activityList = activityService.getHistoricActivityListByExecutionId(event.getExecutionId());
  59 +// if (CollUtil.isEmpty(activityList)) {
  60 +// log.error("[activityCancelled][使用 executionId({}) 查找不到对应的活动实例]", event.getExecutionId());
  61 +// return;
  62 +// }
  63 +// // 遍历处理
  64 +// activityList.forEach(activity -> {
  65 +// if (StrUtil.isEmpty(activity.getTaskId())) {
  66 +// return;
  67 +// }
  68 +// taskService.updateTaskStatusWhenCanceled(activity.getTaskId());
  69 +// });
74 } 70 }
75 -  
76 } 71 }
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/listener/BpmTimerFiredEventListener.java
@@ -81,7 +81,8 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe @@ -81,7 +81,8 @@ public class BpmTimerFiredEventListener extends AbstractFlowableEngineEventListe
81 BpmUserTaskTimeoutActionEnum userTaskTimeoutAction = BpmUserTaskTimeoutActionEnum.actionOf(timeoutAction); 81 BpmUserTaskTimeoutActionEnum userTaskTimeoutAction = BpmUserTaskTimeoutActionEnum.actionOf(timeoutAction);
82 if (userTaskTimeoutAction != null) { 82 if (userTaskTimeoutAction != null) {
83 // 查询超时未处理的任务 TODO 加签的情况会不会有问题 ??? 83 // 查询超时未处理的任务 TODO 加签的情况会不会有问题 ???
84 - List<Task> taskList = bpmTaskService.getTaskListByProcessInstanceIdAndAssigned(processInstanceId, null, taskDefKey); 84 + List<Task> taskList = bpmTaskService.getRunningTaskListByProcessInstanceId(processInstanceId, true,
  85 + null, taskDefKey);
85 taskList.forEach(task -> { 86 taskList.forEach(task -> {
86 // 自动提醒 87 // 自动提醒
87 if (userTaskTimeoutAction == BpmUserTaskTimeoutActionEnum.AUTO_REMINDER) { 88 if (userTaskTimeoutAction == BpmUserTaskTimeoutActionEnum.AUTO_REMINDER) {
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceService.java
@@ -136,11 +136,12 @@ public interface BpmProcessInstanceService { @@ -136,11 +136,12 @@ public interface BpmProcessInstanceService {
136 /** 136 /**
137 * 更新 ProcessInstance 为不通过 137 * 更新 ProcessInstance 为不通过
138 * 138 *
139 - * @param id 流程编号  
140 - * @param currentActivityId 当前的活动编号 139 + * @param processInstance 流程实例
  140 + * @param activityIds 当前未完成活动节点 Id
  141 + * @param endId 结束节点 Id
141 * @param reason 理由。例如说,审批不通过时,需要传递该值 142 * @param reason 理由。例如说,审批不通过时,需要传递该值
142 */ 143 */
143 - void updateProcessInstanceReject(String id, String currentActivityId, String reason); 144 + void updateProcessInstanceReject(ProcessInstance processInstance, List<String> activityIds, String endId, String reason);
144 145
145 /** 146 /**
146 * 当流程结束时候,更新 ProcessInstance 为通过 147 * 当流程结束时候,更新 ProcessInstance 为通过
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
1 -package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import jakarta.validation.Valid; import lombok.extern.slf4j.Slf4j; import org.flowable.bpmn.model.BpmnModel; import org.flowable.bpmn.model.EndEvent; 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.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private BpmMessageService messageService; @Resource private AdminUserApi adminUserApi; @Resource private BpmProcessInstanceEventPublisher processInstanceEventPublisher; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> 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<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getStartUserSelectAssignees()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getStartUserSelectAssignees()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 1.2 校验发起人自选审批人 validateStartUserSelectAssignees(definition, startUserSelectAssignees); // 2. 创建流程实例 if (variables == null) { variables = new HashMap<>(); } FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); if (CollUtil.isNotEmpty(startUserSelectAssignees)) { variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); } ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); if (CollUtil.isEmpty(userTaskList)) { return; } // 2. 校验发起人自选审批人的 UserTask 是否都配置了 userTaskList.forEach(userTask -> { List<Long> assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName()); } Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assignees); assignees.forEach(assignee -> { if (userMap.get(assignee) == null) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee); } }); }); } @Override public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 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; // } // 1. 更新流程实例 status runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); // 2. 发送流程实例的状态事件 // 注意:此时如果去查询 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 currentActivityId, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(id, BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); // 2. 跳转到流程结束 EndEvent 节点,结束流程 // TODO @jason:需要测试下,如果这么走。当前其它审批任务,会不会结束,以及它们的状态是什么?!【重要】 // @芋艿,如果是未完成的会签任务。会被取消, 流程会结束。 但是貌似并行任务的时候。流程是不会结束的。任务还是在进行中的 ProcessInstance processInstance = getProcessInstance(id); BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(processInstance.getProcessDefinitionId()); EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel); Assert.notNull(endEvent, "结束节点不能为空"); runtimeService.createChangeActivityStateBuilder() .processInstanceId(id) .moveActivityIdTo(currentActivityId, endEvent.getId()) // 当前节点 => 结束节点 .changeState(); // 3. 发送流程被【不通过】的消息 messageService.sendMessageWhenProcessInstanceReject( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); } @Override public void updateProcessInstanceWhenCompleted(ProcessInstance instance) { Integer status = (Integer) instance.getProcessVariables().get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS); // 当流程状态还是审批状态中, 更新为审批通过 if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { updateProcessInstanceWhenApprove(instance); } // 审批不通过状态。已经在 updateProcessInstanceReject 处理 } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } }  
2 \ No newline at end of file 1 \ No newline at end of file
  2 +package cn.iocoder.yudao.module.bpm.service.task; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.object.PageUtils; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCancelReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstanceCreateReqVO; import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.instance.BpmProcessInstancePageReqVO; import cn.iocoder.yudao.module.bpm.convert.task.BpmProcessInstanceConvert; import cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum; import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; import cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.BpmTaskCandidateStartUserSelectStrategy; import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmConstants; import cn.iocoder.yudao.module.bpm.framework.flowable.core.event.BpmProcessInstanceEventPublisher; import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.FlowableUtils; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.bpm.service.message.BpmMessageService; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import jakarta.annotation.Resource; import jakarta.validation.Valid; 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.history.HistoricProcessInstanceQuery; import org.flowable.engine.repository.ProcessDefinition; import org.flowable.engine.runtime.ProcessInstance; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; /** * 流程实例 Service 实现类 * * ProcessDefinition & ProcessInstance & Execution & Task 的关系: * 1. <a href="https://blog.csdn.net/bobozai86/article/details/105210414" /> * * HistoricProcessInstance & ProcessInstance 的关系: * 1. <a href=" https://my.oschina.net/843294669/blog/71902" /> * * 简单来说,前者 = 历史 + 运行中的流程实例,后者仅是运行中的流程实例 * * @author 芋道源码 */ @Service @Validated @Slf4j public class BpmProcessInstanceServiceImpl implements BpmProcessInstanceService { @Resource private RuntimeService runtimeService; @Resource private HistoryService historyService; @Resource private BpmProcessDefinitionService processDefinitionService; @Resource private BpmMessageService messageService; @Resource private AdminUserApi adminUserApi; @Resource private BpmProcessInstanceEventPublisher processInstanceEventPublisher; @Override public ProcessInstance getProcessInstance(String id) { return runtimeService.createProcessInstanceQuery() .includeProcessVariables() .processInstanceId(id) .singleResult(); } @Override public List<ProcessInstance> getProcessInstances(Set<String> ids) { return runtimeService.createProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public HistoricProcessInstance getHistoricProcessInstance(String id) { return historyService.createHistoricProcessInstanceQuery().processInstanceId(id).includeProcessVariables().singleResult(); } @Override public List<HistoricProcessInstance> getHistoricProcessInstances(Set<String> ids) { return historyService.createHistoricProcessInstanceQuery().processInstanceIds(ids).list(); } @Override public PageResult<HistoricProcessInstance> 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<HistoricProcessInstance> processInstanceList = processInstanceQuery.listPage(PageUtils.getStart(pageReqVO), pageReqVO.getPageSize()); return new PageResult<>(processInstanceList, processInstanceCount); } @Override @Transactional(rollbackFor = Exception.class) public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqVO createReqVO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getProcessDefinition(createReqVO.getProcessDefinitionId()); // 发起流程 return createProcessInstance0(userId, definition, createReqVO.getVariables(), null, createReqVO.getStartUserSelectAssignees()); } @Override public String createProcessInstance(Long userId, @Valid BpmProcessInstanceCreateReqDTO createReqDTO) { // 获得流程定义 ProcessDefinition definition = processDefinitionService.getActiveProcessDefinition(createReqDTO.getProcessDefinitionKey()); // 发起流程 return createProcessInstance0(userId, definition, createReqDTO.getVariables(), createReqDTO.getBusinessKey(), createReqDTO.getStartUserSelectAssignees()); } private String createProcessInstance0(Long userId, ProcessDefinition definition, Map<String, Object> variables, String businessKey, Map<String, List<Long>> startUserSelectAssignees) { // 1.1 校验流程定义 if (definition == null) { throw exception(PROCESS_DEFINITION_NOT_EXISTS); } if (definition.isSuspended()) { throw exception(PROCESS_DEFINITION_IS_SUSPENDED); } // 1.2 校验发起人自选审批人 validateStartUserSelectAssignees(definition, startUserSelectAssignees); // 2. 创建流程实例 if (variables == null) { variables = new HashMap<>(); } FlowableUtils.filterProcessInstanceFormVariable(variables); // 过滤一下,避免 ProcessInstance 系统级的变量被占用 variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, // 流程实例状态:审批中 BpmProcessInstanceStatusEnum.RUNNING.getStatus()); if (CollUtil.isNotEmpty(startUserSelectAssignees)) { variables.put(BpmConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES, startUserSelectAssignees); } ProcessInstance instance = runtimeService.createProcessInstanceBuilder() .processDefinitionId(definition.getId()) .businessKey(businessKey) .name(definition.getName().trim()) .variables(variables) .start(); return instance.getId(); } private void validateStartUserSelectAssignees(ProcessDefinition definition, Map<String, List<Long>> startUserSelectAssignees) { // 1. 获得发起人自选审批人的 UserTask 列表 BpmnModel bpmnModel = processDefinitionService.getProcessDefinitionBpmnModel(definition.getId()); List<UserTask> userTaskList = BpmTaskCandidateStartUserSelectStrategy.getStartUserSelectUserTaskList(bpmnModel); if (CollUtil.isEmpty(userTaskList)) { return; } // 2. 校验发起人自选审批人的 UserTask 是否都配置了 userTaskList.forEach(userTask -> { List<Long> assignees = startUserSelectAssignees != null ? startUserSelectAssignees.get(userTask.getId()) : null; if (CollUtil.isEmpty(assignees)) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_CONFIG, userTask.getName()); } Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(assignees); assignees.forEach(assignee -> { if (userMap.get(assignee) == null) { throw exception(PROCESS_INSTANCE_START_USER_SELECT_ASSIGNEES_NOT_EXISTS, userTask.getName(), assignee); } }); }); } @Override public void cancelProcessInstanceByStartUser(Long userId, @Valid BpmProcessInstanceCancelReqVO cancelReqVO) { // 1.1 校验流程实例存在 ProcessInstance instance = getProcessInstance(cancelReqVO.getId()); if (instance == null) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_EXISTS); } // 1.2 只能取消自己的 if (!Objects.equals(instance.getStartUserId(), String.valueOf(userId))) { throw exception(PROCESS_INSTANCE_CANCEL_FAIL_NOT_SELF); } // 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; // } // 1. 更新流程实例 status runtimeService.setVariable(event.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.CANCEL.getStatus()); // 2. 发送流程实例的状态事件 // 注意:此时如果去查询 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(ProcessInstance processInstance, List<String> activityIds, String endId, String reason) { // 1. 更新流程实例 status runtimeService.setVariable(processInstance.getProcessInstanceId(), BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS, BpmProcessInstanceStatusEnum.REJECT.getStatus()); // 2. 跳转到流程结束 EndEvent 节点,结束流程 runtimeService.createChangeActivityStateBuilder() .processInstanceId(processInstance.getProcessInstanceId()) .moveActivityIdsToSingleActivityId(CollUtil.newArrayList(activityIds), endId) .changeState(); // 3. 发送流程被【不通过】的消息 messageService.sendMessageWhenProcessInstanceReject( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceRejectMessage(processInstance, reason)); // 4. 发送流程实例的状态事件 processInstanceEventPublisher.sendProcessInstanceResultEvent( BpmProcessInstanceConvert.INSTANCE.buildProcessInstanceStatusEvent(this, processInstance, BpmProcessInstanceStatusEnum.REJECT.getStatus())); } @Override public void updateProcessInstanceWhenCompleted(ProcessInstance instance) { Integer status = (Integer) instance.getProcessVariables().get(BpmConstants.PROCESS_INSTANCE_VARIABLE_STATUS); // 当流程状态还是审批状态中, 更新为审批通过 if (Objects.equals(status, BpmProcessInstanceStatusEnum.RUNNING.getStatus())) { updateProcessInstanceWhenApprove(instance); } // 审批不通过状态。已经在 updateProcessInstanceReject 处理 } private void deleteProcessInstance(String id, String reason) { runtimeService.deleteProcessInstance(id, reason); } }
3 \ No newline at end of file 3 \ No newline at end of file
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
@@ -129,14 +129,15 @@ public interface BpmTaskService { @@ -129,14 +129,15 @@ public interface BpmTaskService {
129 */ 129 */
130 Task getTask(String id); 130 Task getTask(String id);
131 131
132 - // TODO @jason:jason:这个貌似可以去掉了。  
133 /** 132 /**
134 - * 根据条件查询已经分配的用户任务列表 133 + * 根据条件查询正在进行中的任务
  134 + *
135 * @param processInstanceId 流程实例编号,不允许为空 135 * @param processInstanceId 流程实例编号,不允许为空
  136 + * @param assigned 是否分配了审批人
136 * @param executionId execution Id 137 * @param executionId execution Id
137 * @param taskDefineKey 任务定义 Key 138 * @param taskDefineKey 任务定义 Key
138 */ 139 */
139 - List<Task> getTaskListByProcessInstanceIdAndAssigned(String processInstanceId, String executionId, String taskDefineKey); 140 + List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String executionId, String taskDefineKey);
140 141
141 /** 142 /**
142 * 获取当前任务的可回退的 UserTask 集合 143 * 获取当前任务的可回退的 UserTask 集合
yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
1 package cn.iocoder.yudao.module.bpm.service.task; 1 package cn.iocoder.yudao.module.bpm.service.task;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 -import cn.hutool.core.util.ArrayUtil;  
5 -import cn.hutool.core.util.IdUtil;  
6 -import cn.hutool.core.util.ObjectUtil;  
7 -import cn.hutool.core.util.StrUtil; 4 +import cn.hutool.core.util.*;
8 import cn.iocoder.yudao.framework.common.pojo.PageResult; 5 import cn.iocoder.yudao.framework.common.pojo.PageResult;
  6 +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
9 import cn.iocoder.yudao.framework.common.util.date.DateUtils; 7 import cn.iocoder.yudao.framework.common.util.date.DateUtils;
10 import cn.iocoder.yudao.framework.common.util.number.NumberUtils; 8 import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
11 import cn.iocoder.yudao.framework.common.util.object.PageUtils; 9 import cn.iocoder.yudao.framework.common.util.object.PageUtils;
@@ -28,6 +26,7 @@ import jakarta.annotation.Resource; @@ -28,6 +26,7 @@ import jakarta.annotation.Resource;
28 import jakarta.validation.Valid; 26 import jakarta.validation.Valid;
29 import lombok.extern.slf4j.Slf4j; 27 import lombok.extern.slf4j.Slf4j;
30 import org.flowable.bpmn.model.BpmnModel; 28 import org.flowable.bpmn.model.BpmnModel;
  29 +import org.flowable.bpmn.model.EndEvent;
31 import org.flowable.bpmn.model.FlowElement; 30 import org.flowable.bpmn.model.FlowElement;
32 import org.flowable.bpmn.model.UserTask; 31 import org.flowable.bpmn.model.UserTask;
33 import org.flowable.engine.HistoryService; 32 import org.flowable.engine.HistoryService;
@@ -54,6 +53,8 @@ import java.util.stream.Stream; @@ -54,6 +53,8 @@ import java.util.stream.Stream;
54 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 53 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
55 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 54 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
56 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*; 55 import static cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants.*;
  56 +import static cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum.REJECT_BY_ADD_SIGN_TASK_REJECT;
  57 +import static cn.iocoder.yudao.module.bpm.enums.task.BpmDeleteReasonEnum.AUTO_REJECT_BY_ADD_SIGN_REJECT;
57 58
58 /** 59 /**
59 * 流程任务实例 Service 实现类 60 * 流程任务实例 Service 实现类
@@ -326,18 +327,40 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -326,18 +327,40 @@ public class BpmTaskServiceImpl implements BpmTaskService {
326 if (instance == null) { 327 if (instance == null) {
327 throw exception(PROCESS_INSTANCE_NOT_EXISTS); 328 throw exception(PROCESS_INSTANCE_NOT_EXISTS);
328 } 329 }
329 -  
330 - // 2.1 更新流程实例为不通过 330 + // 2.1 更新流程任务为不通过
331 updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason()); 331 updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.REJECT.getStatus(), reqVO.getReason());
332 // 2.2 添加评论 332 // 2.2 添加评论
333 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(), 333 taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(),
334 BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason())); 334 BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason()));
335 335
336 - // 3.1 解析用户任务的拒绝处理类型 336 + // 3.1 如果是被加签任务且是后加签。 更新加签任务状态为取消
  337 + if (BpmTaskSignTypeEnum.AFTER.getType().equals(task.getScopeType())) {
  338 + List<Task> childTaskList = getTaskListByParentTaskId(task.getId());
  339 + updateTaskStatusWhenCanceled(childTaskList, reqVO.getReason());
  340 + }
  341 + // 3.2 如果是加签的任务
  342 + if (StrUtil.isNotEmpty(task.getParentTaskId())) {
  343 + Task signTask = validateTaskExist(task.getParentTaskId());
  344 + // 3.2.1 更新被加签的任务为不通过
  345 + if (BpmTaskSignTypeEnum.BEFORE.getType().equals(signTask.getScopeType())) {
  346 + updateTaskStatusAndReason(task.getParentTaskId(), BpmTaskStatusEnum.REJECT.getStatus(), AUTO_REJECT_BY_ADD_SIGN_REJECT.getReason());
  347 + } else if (BpmTaskSignTypeEnum.AFTER.getType().equals(signTask.getScopeType())) {
  348 + updateTaskStatus(task.getParentTaskId(), BpmTaskStatusEnum.REJECT.getStatus());
  349 + // 后加签 不添加拒绝意见。因为会把原来的意见覆盖.
  350 + }
  351 + // 3.2.2 添加评论
  352 + taskService.addComment(task.getParentTaskId(), task.getProcessInstanceId(),
  353 + BpmCommentTypeEnum.REJECT.getType(), BpmCommentTypeEnum.REJECT.formatComment(REJECT_BY_ADD_SIGN_TASK_REJECT));
  354 + // 3.2.3 更新还在进行中的加签任务状态为取消
  355 + List<Task> addSignTaskList = getTaskListByParentTaskId(task.getParentTaskId());
  356 + updateTaskStatusWhenCanceled(CollectionUtils.filterList(addSignTaskList, item -> !item.getId().equals(task.getId())),
  357 + reqVO.getReason());
  358 + }
  359 +
  360 + // 4.1 驳回到指定的任务节点
337 BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId()); 361 BpmnModel bpmnModel = bpmModelService.getBpmnModelByDefinitionId(task.getProcessDefinitionId());
338 FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey()); 362 FlowElement flowElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
339 BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement); 363 BpmUserTaskRejectHandlerType userTaskRejectHandlerType = BpmnModelUtils.parseRejectHandlerType(flowElement);
340 - // 3.2 类型为驳回到指定的任务节点  
341 if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) { 364 if (userTaskRejectHandlerType == BpmUserTaskRejectHandlerType.RETURN_USER_TASK) {
342 String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement); 365 String returnTaskId = BpmnModelUtils.parseReturnTaskId(flowElement);
343 Assert.notNull(returnTaskId, "回退的节点不能为空"); 366 Assert.notNull(returnTaskId, "回退的节点不能为空");
@@ -346,9 +369,25 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -346,9 +369,25 @@ public class BpmTaskServiceImpl implements BpmTaskService {
346 returnTask(userId, returnReq); 369 returnTask(userId, returnReq);
347 return; 370 return;
348 } 371 }
349 - // 3.3 其他情况 终止流程。  
350 - processInstanceService.updateProcessInstanceReject(instance.getProcessInstanceId(),  
351 - task.getTaskDefinitionKey(), reqVO.getReason()); 372 +
  373 + // 4.2.1 更新其它正在运行的任务状态为取消。需要过滤掉当前任务和被加签的任务
  374 + List<Task> taskList = getRunningTaskListByProcessInstanceId(instance.getProcessInstanceId(), false, null, null);
  375 + updateTaskStatusWhenCanceled(CollectionUtils.filterList(taskList, item -> !item.getId().equals(task.getId()) && !item.getId().equals(task.getParentTaskId())),
  376 + reqVO.getReason());
  377 + // 4.2.2 终止流程
  378 + List<String> activityIds = convertList(taskList, Task::getTaskDefinitionKey);
  379 + EndEvent endEvent = BpmnModelUtils.getEndEvent(bpmnModel);
  380 + Assert.notNull(endEvent, "结束节点不能未空");
  381 + processInstanceService.updateProcessInstanceReject(instance, activityIds, endEvent.getId(), reqVO.getReason());
  382 + }
  383 +
  384 + private void updateTaskStatusWhenCanceled(List<Task> taskList, String reason) {
  385 + taskList.forEach(task -> {
  386 + updateTaskStatusAndReason(task.getId(), BpmTaskStatusEnum.CANCEL.getStatus(), BpmDeleteReasonEnum.CANCEL_BY_SYSTEM.getReason());
  387 + taskService.addComment(task.getId(), task.getProcessInstanceId(),
  388 + BpmCommentTypeEnum.CANCEL.getType(), BpmCommentTypeEnum.CANCEL.formatComment(reason));
  389 +
  390 + });
352 } 391 }
353 392
354 /** 393 /**
@@ -399,6 +438,7 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -399,6 +438,7 @@ public class BpmTaskServiceImpl implements BpmTaskService {
399 438
400 @Override 439 @Override
401 public void updateTaskStatusWhenCanceled(String taskId) { 440 public void updateTaskStatusWhenCanceled(String taskId) {
  441 + // @芋艿。这里是不是可以不要了,要不然。 updateTaskStatusAndReason 会报错 task 已经删除了。
402 Task task = getTask(taskId); 442 Task task = getTask(taskId);
403 // 1. 可能只是活动,不是任务,所以查询不到 443 // 1. 可能只是活动,不是任务,所以查询不到
404 if (task == null) { 444 if (task == null) {
@@ -450,10 +490,13 @@ public class BpmTaskServiceImpl implements BpmTaskService { @@ -450,10 +490,13 @@ public class BpmTaskServiceImpl implements BpmTaskService {
450 } 490 }
451 491
452 @Override 492 @Override
453 - public List<Task> getTaskListByProcessInstanceIdAndAssigned(String processInstanceId, String executionId, String defineKey) { 493 + public List<Task> getRunningTaskListByProcessInstanceId(String processInstanceId, Boolean assigned, String executionId, String defineKey) {
454 Assert.notNull(processInstanceId, "processInstanceId 不能为空"); 494 Assert.notNull(processInstanceId, "processInstanceId 不能为空");
455 - TaskQuery taskQuery = taskService.createTaskQuery().taskAssigned().processInstanceId(processInstanceId).active() 495 + TaskQuery taskQuery = taskService.createTaskQuery().processInstanceId(processInstanceId).active()
456 .includeTaskLocalVariables(); 496 .includeTaskLocalVariables();
  497 + if (BooleanUtil.isTrue(assigned)) {
  498 + taskQuery.taskAssigned();
  499 + }
457 if (StrUtil.isNotEmpty(executionId)) { 500 if (StrUtil.isNotEmpty(executionId)) {
458 taskQuery.executionId(executionId); 501 taskQuery.executionId(executionId);
459 } 502 }