From 2ea93ebeb507fe5a1869312290af49a2f49b3fa1 Mon Sep 17 00:00:00 2001 From: zuoqinbo Date: Tue, 14 Nov 2023 10:57:49 +0800 Subject: [PATCH] =?UTF-8?q?feat:1.=E5=85=AC=E5=85=B1=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/impl/BpmnTaskServiceImpl.java | 245 ++++++++++++------ 1 file changed, 169 insertions(+), 76 deletions(-) diff --git a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnTaskServiceImpl.java b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnTaskServiceImpl.java index a382dc1c8..2befea89e 100644 --- a/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnTaskServiceImpl.java +++ b/workflow-engine-core/src/main/java/cn/axzo/workflow/core/service/impl/BpmnTaskServiceImpl.java @@ -1,13 +1,9 @@ package cn.axzo.workflow.core.service.impl; +import cn.axzo.framework.core.util.StringUtil; +import cn.axzo.workflow.common.enums.BpmnCountersignType; import cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAssigneeDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAttachmentDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskAuditDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCommentDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskCountersignDTO; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner; -import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskPageSearchDTO; +import cn.axzo.workflow.common.model.request.bpmn.task.*; import cn.axzo.workflow.common.model.response.BpmPageResult; import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceGroupVO; import cn.axzo.workflow.common.model.response.bpmn.task.BpmnHistoricTaskInstanceVO; @@ -15,6 +11,7 @@ import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskDonePageItemVO; import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskInstanceVO; import cn.axzo.workflow.common.model.response.bpmn.task.BpmnTaskTodoPageItemVO; import cn.axzo.workflow.core.common.exception.WorkflowEngineException; +import cn.axzo.workflow.core.engine.id.TimeBasedIdGenerator; import cn.axzo.workflow.core.service.BpmnTaskService; import cn.axzo.workflow.core.service.converter.BpmnHistoricCommentConverter; import cn.axzo.workflow.core.service.converter.BpmnHistoricTaskInstanceConverter; @@ -22,11 +19,9 @@ import cn.axzo.workflow.core.service.converter.BpmnTaskConverter; import cn.axzo.workflow.core.service.converter.BpmnTaskDonePageItemConverter; import cn.axzo.workflow.core.service.converter.BpmnTaskTodoPageItemConverter; import cn.hutool.core.collection.CollUtil; +import com.google.common.collect.Lists; import lombok.extern.slf4j.Slf4j; -import org.flowable.bpmn.model.Activity; -import org.flowable.bpmn.model.BpmnModel; -import org.flowable.bpmn.model.FlowElement; -import org.flowable.bpmn.model.UserTask; +import org.flowable.bpmn.model.*; import org.flowable.common.engine.impl.identity.Authentication; import org.flowable.engine.HistoryService; import org.flowable.engine.ManagementService; @@ -39,12 +34,14 @@ import org.flowable.engine.impl.persistence.entity.ExecutionEntity; import org.flowable.engine.runtime.ProcessInstance; import org.flowable.engine.task.Comment; import org.flowable.form.api.FormInfo; +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.history.NativeHistoricTaskInstanceQuery; import org.flowable.task.service.impl.persistence.entity.TaskEntity; +import org.flowable.task.service.impl.persistence.entity.TaskEntityImpl; import org.flowable.variable.api.history.HistoricVariableInstance; import org.flowable.variable.api.persistence.entity.VariableInstance; import org.springframework.stereotype.Service; @@ -53,39 +50,17 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import javax.annotation.Resource; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; -import static cn.axzo.workflow.common.constant.BpmnConstants.APPROVAL_ENDS_AUTOMATICALLY; -import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_DELETE_PROCESS_FLAG; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_TENANT_ID; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_ID; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_END_USER_NAME; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_PROCESS_TYPE_REJECT; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_SPECIFY_NEXT_APPROVER; -import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT; -import static cn.axzo.workflow.common.constant.BpmnConstants.MULTI_INSTANCE_LOOP_COUNTER; -import static cn.axzo.workflow.common.constant.BpmnConstants.NUMBER_OF_INSTANCES; -import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE; +import static cn.axzo.workflow.common.constant.BpmnConstants.*; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED; +import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.CANCELLED; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.PROCESSING; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.REJECTED; import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.valueOfStatus; -import static cn.axzo.workflow.core.common.enums.BpmnErrorCode.PROCESS_INSTANCE_ID_NOT_EXISTS; -import static cn.axzo.workflow.core.common.enums.BpmnErrorCode.PROCESS_INSTANCE_NOT_EXISTS; -import static cn.axzo.workflow.core.common.enums.BpmnErrorCode.TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF; -import static cn.axzo.workflow.core.common.enums.BpmnErrorCode.TASK_COMPLETE_FAIL_NOT_EXISTS; +import static cn.axzo.workflow.core.common.enums.BpmnErrorCode.*; import static cn.axzo.workflow.core.common.utils.BpmnCollectionUtils.convertSet; import static cn.axzo.workflow.core.common.utils.BpmnNativeQueryUtil.countSql; import static cn.axzo.workflow.core.common.utils.BpmnNativeQueryUtil.sqlConnectors; @@ -209,7 +184,7 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { NativeHistoricTaskInstanceQuery nativeQuery = historyService.createNativeHistoricTaskInstanceQuery(); String tableName = managementService.getTableName(HistoricTaskInstance.class); baseQuerySql.append("SELECT a.* FROM ").append(tableName).append(" a JOIN " + - "ACT_HI_PROCINST b") + "ACT_HI_PROCINST b") .append(" ON b.PROC_INST_ID_ = a.PROC_INST_ID_"); if (StringUtils.hasLength(dto.getTenantId())) { @@ -288,10 +263,10 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { @Override public void approveTask(BpmnTaskAuditDTO dto) { // 校验任务存在 - Task task = checkTask(dto.getApprover().getTenantId(), dto.getApprover().buildAssigneeId(), dto.getTaskId()); + TaskEntity task = (TaskEntity) checkTask(dto.getApprover().getTenantId(), dto.getApprover().buildAssigneeId(), dto.getTaskId()); // 校验流程实例存在 HistoricProcessInstance instance = processInstanceService.getProcessInstance( - task.getProcessInstanceId(), null, true); + task.getProcessInstanceId(), dto.getApprover().getTenantId(), true); if (instance == null) { throw new WorkflowEngineException(PROCESS_INSTANCE_NOT_EXISTS); } @@ -303,10 +278,123 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { if (Objects.nonNull(dto.getNextApprover())) { runtimeService.setVariable(task.getExecutionId(), INTERNAL_SPECIFY_NEXT_APPROVER, dto.getNextApprover()); } + taskService.setAssignee(task.getId(), dto.getApprover().buildAssigneeId()); // 主动设置下级审批人 // 完成任务,审批通过 // FIXME 如果 task 被重复删除会抛出异常 - taskService.complete(task.getId(), runtimeService.getVariables(task.getExecutionId())); + if (StringUtil.isNotEmpty(task.getExecutionId())) { + taskService.complete(task.getId(), runtimeService.getVariables(task.getExecutionId())); + } else { + //子任务 没有executionId + taskService.complete(task.getId()); + } + String parentTaskId = task.getParentTaskId(); + //add by zuoqinbo 处理加签任务,分为向前加签和向后加签 + if (cn.azxo.framework.common.utils.StringUtils.isNotBlank(parentTaskId)) { + String tableName = managementService.getTableName(TaskEntity.class); + String sql = "select count(1) from " + tableName + " where PARENT_TASK_ID_=#{parentTaskId}"; + long subTaskCount = taskService.createNativeTaskQuery().sql(sql).parameter("parentTaskId", parentTaskId).count(); + TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(parentTaskId).singleResult(); + //当前任务是加签任务,如最后一个审批,parentTask下没有其他子任务 subTaskCount == 0 + //目前子任务是依次顺序审批, taskService.complete(task.getId()) 已经把任务完成,则subTaskCount == 0 + if (subTaskCount == 0) { + //让下一个加签人进行处理 + List remainAssignerList = + (List) task.getVariable("COUNTERSIGN_REMAIN_ASSIGNER_LIST"); + if (!CollectionUtils.isEmpty(remainAssignerList)) { + BpmnTaskDelegateAssigner remainAssigner = remainAssignerList.get(0); + this.createSubTask(taskEntity, parentTaskId, remainAssigner.buildAssigneeId()); + }else { + taskService.resolveTask(parentTaskId);//向后加签 会直接完成该任务 + if (BpmnCountersignType.BACK_COUNTERSIGN.getType().equals(taskEntity.getScopeType())) { + taskService.complete(parentTaskId); + } + } + + } + } + } + + /** + * 创建加签子任务 + * A加签给A A再加签给A + * A加签给B B再加签给A 再加签给B + * A加签给B B加签给C 再加签A + * A加签给B B加签给C 再加签D + * + * @param countersignDTO 加签参数 + * @param taskEntity 父任务 + */ + private void createSignSubTasks(BpmnTaskCountersignDTO countersignDTO, TaskEntity taskEntity) { + //如果还有预留或者继承的加签人 + List targetAssignerList = + (List) taskEntity.getVariable("COUNTERSIGN_REMAIN_ASSIGNER_LIST",List.class); + List newTargetAssignerList = Lists.newArrayList(); + //为了保证顺序,优先使用当前的加签人列表 + newTargetAssignerList.addAll(countersignDTO.getTargetAssignerList()); + //再添加遗留的加签人列表 + if (org.apache.commons.collections.CollectionUtils.isNotEmpty(targetAssignerList)) { + newTargetAssignerList.addAll(targetAssignerList); + } + String originAssigner = countersignDTO.getOriginAssigner().buildAssigneeId(); + if (org.apache.commons.collections.CollectionUtils.isNotEmpty(newTargetAssignerList)) { + String parentTaskId = taskEntity.getParentTaskId(); + if (cn.azxo.framework.common.utils.StringUtils.isBlank(parentTaskId)) { + parentTaskId = taskEntity.getId(); + } + String finalParentTaskId = parentTaskId; + //一次加签只创建一个任务 + BpmnTaskDelegateAssigner removeAssigner = newTargetAssignerList.remove(0); + this.createSubTask(taskEntity, finalParentTaskId, removeAssigner.buildAssigneeId()); + //剩余处理的加签人列表 + taskEntity.setVariable("COUNTERSIGN_REMAIN_ASSIGNER_LIST", newTargetAssignerList); + + String taskId = taskEntity.getId(); + if (cn.azxo.framework.common.utils.StringUtils.isBlank(taskEntity.getParentTaskId())) { + //2.创建加签人的任务并执行完毕 + Task task = this.createSubTask(taskEntity, finalParentTaskId, originAssigner); + taskId = task.getId(); + } + Task taskInfo = taskService.createTaskQuery().taskId(taskId).singleResult(); + if (taskInfo != null) { + taskService.complete(taskId); + } +// //如果是候选人,需要删除运行时候选表种的数据。 +// long candidateCount = taskService.createTaskQuery().taskId(parentTaskId).taskCandidateUser(originAssigner).count(); +// if (candidateCount > 0) { +// taskService.deleteCandidateUser(parentTaskId, originAssigner); +// } + } + } + + /** + * 创建子任务 + * + * @param parentTask 子任务继承原始任务信息 + * @param assignee 子任务的执行人 + * @return + */ + protected TaskEntity createSubTask(TaskEntity parentTask, String parentTaskId, String assignee) { + TaskEntity task = null; + if (parentTask != null) { + //1.生成新的工作流子任务 + task = (TaskEntity) taskService.newTask(new TimeBasedIdGenerator().getNextId()); + task.setCategory(parentTask.getCategory()); + task.setDescription(parentTask.getDescription()); + task.setTenantId(parentTask.getTenantId()); + task.setAssignee(assignee); + task.setName(parentTask.getName()); + task.setParentTaskId(parentTaskId); + task.setProcessDefinitionId(parentTask.getProcessDefinitionId()); + task.setProcessInstanceId(parentTask.getProcessInstanceId()); + task.setTaskDefinitionKey(parentTask.getTaskDefinitionKey()); + task.setTaskDefinitionId(parentTask.getTaskDefinitionId()); + //task.setExecutionId(parentTask.getExecutionId()); + task.setPriority(parentTask.getPriority()); + task.setCreateTime(new Date()); + taskService.saveTask(task); + } + return task; } @Override @@ -379,9 +467,7 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { query.taskTenantId(tenantId); } HistoricProcessInstanceQuery instanceQuery = - historyService.createHistoricProcessInstanceQuery() - .includeProcessVariables() - .processInstanceId(processInstanceId); + historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId); if (StringUtils.hasLength(tenantId)) { instanceQuery.processInstanceTenantId(tenantId); } @@ -401,16 +487,20 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { Map variableInstanceMap = historyService.createHistoricVariableInstanceQuery().processInstanceId(processInstanceId).list() .stream().collect(Collectors.toMap(HistoricVariableInstance::getVariableName, - Function.identity(), (s, t) -> s)); + Function.identity(), (s, t) -> s)); Set taskDefinitionKeys = new HashSet<>(); int count = 0; - BpmnProcessInstanceResultEnum processBusinessStatus = valueOfStatus(instance.getBusinessStatus()); + BpmnProcessInstanceResultEnum finalBusinessStatus = valueOfStatus(instance.getBusinessStatus()); for (BpmnHistoricTaskInstanceVO vo : vos) { - vo.setResult(processBusinessStatus); - if (Objects.nonNull(vo.getEndTime())) { - // 只有拒绝时, 为指定的 taskId 设置过拒绝变量 - vo.setResult((BpmnProcessInstanceResultEnum) instance.getProcessVariables().getOrDefault(TASK_COMPLETE_OPERATION_TYPE + vo.getTaskId(), APPROVED)); + if (count == 0 || taskDefinitionKeys.contains(vo.getTaskDefinitionKey())) { + vo.setResult(finalBusinessStatus); + if (Objects.nonNull(vo.getEndTime()) && !Objects.equals(finalBusinessStatus, REJECTED) + && !Objects.equals(finalBusinessStatus, CANCELLED)) { + vo.setResult(APPROVED); + } + } else { + vo.setResult(APPROVED); } taskDefinitionKeys.add(vo.getTaskDefinitionKey()); List taskComments = commentByTaskIdMap.getOrDefault(vo.getTaskId(), Collections.emptyList()); @@ -485,20 +575,9 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { List vos = bpmnTaskConverter.toVos(query.list()); List snapshotTaskIds = vos.stream().map(i -> INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + i.getTaskId()).collect(Collectors.toList()); - ProcessInstance processInstance = - runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult(); - if (Objects.isNull(processInstance)) { - throw new WorkflowEngineException(PROCESS_INSTANCE_NOT_EXISTS); - } Map instanceMap = runtimeService.getVariableInstances(processInstanceId, snapshotTaskIds); - vos.forEach(i -> { - VariableInstance variableInstance = - instanceMap.get(INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + i.getTaskId()); - if (!Objects.isNull(variableInstance)) { - i.setAssigner((BpmnTaskDelegateAssigner) variableInstance.getValue()); - } - }); + vos.forEach(i -> i.setAssigner((BpmnTaskDelegateAssigner) instanceMap.get(INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT + i.getTaskId()).getValue())); return vos; } @@ -511,11 +590,13 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { * @param taskId 任务ID */ private Task checkTask(String tenantId, String assignee, String taskId) { - Task task = getTask(taskId, null, null); + + Task task = getTask(taskId, assignee, tenantId); + if (Objects.nonNull(task) && !Objects.equals(assignee, task.getAssignee())) { + throw new WorkflowEngineException(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF); + } if (Objects.isNull(task)) { throw new WorkflowEngineException(TASK_COMPLETE_FAIL_NOT_EXISTS); - } else if (!Objects.equals(assignee, task.getAssignee()) && !Objects.equals(tenantId, task.getTenantId())) { - throw new WorkflowEngineException(TASK_COMPLETE_FAIL_ASSIGN_NOT_SELF); } return task; } @@ -585,23 +666,35 @@ public class BpmnTaskServiceImpl implements BpmnTaskService { @Override public void countersignTask(BpmnTaskCountersignDTO countersignDTO) { - String tenantId = countersignDTO.getTargetAssigner().getTenantId(); - TaskEntity taskEntity = (TaskEntity) taskService.createTaskQuery().taskId(countersignDTO.getTaskId()); + String tenantId = countersignDTO.getOriginAssigner().getTenantId(); + TaskEntityImpl taskEntity = (TaskEntityImpl) taskService.createTaskQuery().taskId(countersignDTO.getTaskId()).active().singleResult(); if (Objects.isNull(taskEntity)) { throw new WorkflowEngineException(TASK_COMPLETE_FAIL_NOT_EXISTS); } if (org.apache.commons.lang.StringUtils.isNotEmpty(tenantId)) { taskEntity.setTenantId(tenantId); } - taskEntity.setScopeType(countersignDTO.getCountersignType().getType()); - taskEntity.setOwner(countersignDTO.getTargetAssigner().getPersonId()); - taskEntity.setAssigneeValue(null); - //审批时,需要从Scope中判断是 向前加签还是向后加签 - //taskService.resolveTask(parentTaskId); - //if (BpmnCountersignType.BACK_COUNTERSIGN.equals(task.getScopeType())) { - // taskService.complete(parentTaskId); - //} - taskService.saveTask(taskEntity); + if (BpmnCountersignType.isValidAppType(countersignDTO.getCountersignType()) != null) { + //加签不能改变BPMN2.0原流程定义,因为下一个流程实例,不需要加签 + //如果是加签再加签 parentTaskId!=null + String parentTaskId = taskEntity.getParentTaskId(); + BpmnTaskDelegateAssigner originAssigner = countersignDTO.getOriginAssigner(); + //更新父任务信息 + if (StringUtil.isEmpty(parentTaskId)) { + taskEntity.setOwner(originAssigner.buildAssigneeId()); + taskEntity.setAssignee(null); + taskEntity.setCountEnabled(true); + taskEntity.setScopeType(countersignDTO.getCountersignType()); + taskService.saveTask(taskEntity); + } + //该任务加签时,会创建子任务用于分配其他人加签 + //目前多个子任务时,会按照加签人的顺序依次进行加签, + //例如 加签按顺序依次分配给A、B、C三人,需A任务进行完成后,进行B任务、再进行C任务 + this.createSignSubTasks(countersignDTO, taskEntity); + } else { + throw new WorkflowEngineException(TASK_APOSTILLE_NOT_SUPPORT); + } + } private Activity getActivity(TaskEntity task) {