Merge remote-tracking branch 'origin/master' into feature/REQ-3581

This commit is contained in:
wangli 2025-03-14 15:15:22 +08:00
commit 79150dedf7
7 changed files with 363 additions and 55 deletions

View File

@ -89,6 +89,15 @@ public interface ProcessTaskApi {
@PostMapping("/api/process/task/back")
CommonResponse<Boolean> backTask(@Validated @RequestBody BpmnTaskBackAuditDTO dto);
/**
* 用于系统内部操作跳转到指定节点
* @param dto 请求参数
* @return 是否成功
*/
@Operation(summary = "系统操作回退任务到指定节点")
@PostMapping("/api/process/task/system/back")
CommonResponse<Boolean> systemBackTask(@Validated @RequestBody BpmnNodeBackSystemOperateDTO dto);
/**
* 驳回
*

View File

@ -0,0 +1,67 @@
package cn.axzo.workflow.common.model.request.bpmn.task;
import cn.axzo.workflow.common.constraint.AttachmentTypeValidator;
import cn.axzo.workflow.common.constraint.AttachmentValidator;
import cn.axzo.workflow.common.enums.AttachmentTypeEnum;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;
import java.io.Serializable;
import java.util.List;
/**
* 审批节点退回模型
*
*/
@ApiModel("审批节点退回模型")
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class BpmnNodeBackSystemOperateDTO implements Serializable {
private static final long serialVersionUID = -4160538355403179298L;
@ApiModelProperty(value = "流程实例id", required = true)
@NotBlank(message = "流程实例id不能为空")
private String processInstanceId;
@ApiModelProperty(value = "当前流程节点id", required = true)
@NotBlank(message = "当前节点id不能为空")
private String currentActivityId;
@ApiModelProperty(value = "目标流程节点id", required = true)
@NotBlank(message = "目标流程节点id不能为空")
private String toActivityId;
@ApiModelProperty(value = "可以指定任务处理", required = true)
private List<String> targetTaskIds;
@ApiModelProperty(value = "意见")
private String advice;
@ApiModelProperty(value = "操作描述")
private String operationDesc;
@ApiModelProperty(value = "操作人")
private BpmnTaskDelegateAssigner operator;
/**
* 附件列表
*/
@ApiModelProperty(value = "附件列表")
@Size(max = 11, message = "附件数量超过限制")
@AttachmentValidator(types = {
@AttachmentTypeValidator(type = AttachmentTypeEnum.image, max = 5),
@AttachmentTypeValidator(type = AttachmentTypeEnum.file, max = 5),
@AttachmentTypeValidator(type = AttachmentTypeEnum.signature, max = 1)}
)
private List<AttachmentDTO> attachmentList;
}

View File

@ -1,9 +1,14 @@
package cn.axzo.workflow.core.engine.cmd;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO;
import cn.axzo.workflow.common.exception.WorkflowEngineException;
import cn.axzo.workflow.common.model.request.bpmn.task.AttachmentDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskDelegateAssigner;
import cn.axzo.workflow.core.common.utils.BpmnModelUtils;
import com.alibaba.fastjson.JSON;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.model.FlowElement;
import org.flowable.bpmn.model.FlowNode;
@ -15,23 +20,27 @@ import org.flowable.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.impl.util.ProcessDefinitionUtil;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.task.service.TaskService;
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.BACK_NODE_CANNOT_REACHABLE;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.BACK_TARGET_ACTIVITY_NOT_EXISTS;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_NOT_EXISTS;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.TASK_COMPLETE_OPERATION_TYPE;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.BACKED;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.BACK_NODE_CANNOT_REACHABLE;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.BACK_TARGET_ACTIVITY_NOT_EXISTS;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.addComment;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.batchAddAttachment;
import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask;
@ -43,66 +52,122 @@ import static cn.axzo.workflow.core.engine.cmd.helper.CustomTaskHelper.validTask
public class CustomBackTaskCmd extends AbstractCommand<Void> implements Serializable {
private static final long serialVersionUID = -1241290344311892346L;
private final BpmnTaskBackAuditDTO dto;
private final List<String> targetTaskIds;
private final String advice;
private final String operationDesc;
private final List<AttachmentDTO> attachmentList;
private final BpmnTaskDelegateAssigner operator;
private final String processInstanceId;
private final String currentActivityId;
private final String toActivityId;
private final boolean validateApprover;
private static final String OPERATION_DESC = "回退至";
public CustomBackTaskCmd(CustomBackParamsDto customBackParamsDto) {
if (customBackParamsDto == null) {
throw new NullPointerException("customBackParamsDto is null");
}
if (customBackParamsDto.getOperator() == null) {
throw new NullPointerException("operator is null");
}
this.targetTaskIds = customBackParamsDto.getTargetTaskIds();
this.advice = customBackParamsDto.getAdvice();
this.operationDesc = customBackParamsDto.getOperationDesc();
this.attachmentList = customBackParamsDto.getAttachmentList();
this.operator = customBackParamsDto.getOperator();
this.processInstanceId = customBackParamsDto.getProcessInstanceId();
this.currentActivityId = customBackParamsDto.getCurrentActivityId();
this.toActivityId = customBackParamsDto.getToActivityId();
this.validateApprover = customBackParamsDto.isValidateApprover();
}
@Override
public String paramToJsonString() {
Map<String, Object> params = new HashMap<>();
params.put("taskId", dto.getTaskId());
params.put("advice", dto.getAdvice());
params.put("targetTaskIds", JSON.toJSONString(this.targetTaskIds));
params.put("advice", advice);
params.put("operationDesc", OPERATION_DESC);
params.put("attachmentList", JSON.toJSONString(dto.getAttachmentList()));
params.put("approver", JSON.toJSONString(dto.getApprover()));
params.put("nodeTypes", JSON.toJSONString(dto.getNodeTypes()));
params.put("attachmentList", JSON.toJSONString(attachmentList));
params.put("operator", JSON.toJSONString(operator));
params.put("toActivityId", JSON.toJSONString(this.toActivityId));
params.put("processInstanceId", JSON.toJSONString(this.processInstanceId));
params.put("validateApprover", validateApprover);
return JSON.toJSONString(params);
}
public CustomBackTaskCmd(BpmnTaskBackAuditDTO dto) {
this.dto = dto;
}
@Override
public Void execute(CommandContext commandContext) {
ProcessEngineConfigurationImpl processEngineConfiguration =
CommandContextUtil.getProcessEngineConfiguration(commandContext);
HistoricTaskInstanceQuery taskQuery =
processEngineConfiguration.getHistoryService().createHistoricTaskInstanceQuery();
TaskService taskService = processEngineConfiguration.getTaskServiceConfiguration().getTaskService();
HistoricTaskInstance historicTaskInstance = taskQuery.taskId(dto.getTaskId()).singleResult();
TaskEntity task = taskService.getTask(dto.getTaskId());
validTask(historicTaskInstance, task, dto.getApprover(), dto.getNodeTypes());
Process process = ProcessDefinitionUtil.getProcess(task.getProcessDefinitionId());
FlowElement targetFlowElement = process.getFlowElement(dto.getToActivityId(), true);
if (Objects.isNull(targetFlowElement)) {
throw new WorkflowEngineException(BACK_TARGET_ACTIVITY_NOT_EXISTS, dto.getToActivityId());
CommandContextUtil.getProcessEngineConfiguration(commandContext);
List<HistoricTaskInstance> taskList = processEngineConfiguration.getHistoryService()
.createHistoricTaskInstanceQuery()
.taskDefinitionKey(currentActivityId)
.processInstanceId(processInstanceId)
.list();
List<HistoricTaskInstance> valueableTaskList = taskList.stream()
.filter(hi -> {
if (hi.getEndTime() != null) {
return false;
}
if (!CollectionUtils.isEmpty(targetTaskIds)) {
return targetTaskIds.contains(hi.getId());
}
return true;
})
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(valueableTaskList)) {
throw new WorkflowEngineException(TASK_COMPLETE_FAIL_NOT_EXISTS);
}
FlowElement sourceFlowElement = process.getFlowElement(task.getTaskDefinitionKey(), true);
TaskService taskService = processEngineConfiguration.getTaskServiceConfiguration().getTaskService();
if (validateApprover) {
valueableTaskList.forEach(hi -> validTask(hi, taskService.getTask(hi.getId()), operator, null));
}
String processDefinitionId = valueableTaskList.get(0).getProcessDefinitionId();
Process process = ProcessDefinitionUtil.getProcess(processDefinitionId);
FlowElement targetFlowElement = process.getFlowElement(toActivityId, true);
if (Objects.isNull(targetFlowElement)) {
throw new WorkflowEngineException(BACK_TARGET_ACTIVITY_NOT_EXISTS, toActivityId);
}
FlowElement sourceFlowElement = process.getFlowElement(currentActivityId, true);
// 退回节点到当前节点不可达到不允许退回
if (!BpmnModelUtils.isReachable(process, (FlowNode) targetFlowElement, (FlowNode) sourceFlowElement)) {
throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, toActivityId);
}
batchAddAttachment(commandContext, task.getProcessInstanceId(), task, dto.getAttachmentList(), dto.getApprover());
Authentication.setAuthenticatedUserId(dto.getApprover().buildAssigneeId());
addComment(commandContext, task, COMMENT_TYPE_ADVICE, dto.getAdvice());
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, OPERATION_DESC + targetFlowElement.getName());
Authentication.setAuthenticatedUserId(null);
RuntimeService runtimeService = processEngineConfiguration.getRuntimeService();
task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + dto.getTaskId(), BACKED.getStatus());
valueableTaskList.forEach(th -> {
TaskEntity task = taskService.getTask(th.getId());
Authentication.setAuthenticatedUserId(operator.buildAssigneeId());
batchAddAttachment(commandContext, processInstanceId, task, attachmentList, operator);
addComment(commandContext, task, COMMENT_TYPE_ADVICE, advice);
addComment(commandContext, task, COMMENT_TYPE_OPERATION_DESC, StringUtils.hasText(operationDesc) ? operationDesc : OPERATION_DESC + targetFlowElement.getName());
Authentication.setAuthenticatedUserId(null);
task.setTransientVariable(TASK_COMPLETE_OPERATION_TYPE + task.getId(), BACKED.getStatus());
});
// 移除回退到的指定节点的变量 EngineExecutionStartListener 重新计算该节点的人
runtimeService.removeVariable(task.getProcessInstanceId(), INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO + dto.getToActivityId());
runtimeService.removeVariable(processInstanceId, INTERNAL_TASK_RELATION_ASSIGNEE_LIST_INFO + toActivityId);
runtimeService.createChangeActivityStateBuilder()
.processInstanceId(task.getProcessInstanceId())
.moveActivityIdsToSingleActivityId(Collections.singletonList(task.getTaskDefinitionKey()), dto.getToActivityId())
.changeState();
.processInstanceId(processInstanceId)
.moveActivityIdsToSingleActivityId(Collections.singletonList(currentActivityId), toActivityId)
.changeState();
return null;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static final class CustomBackParamsDto {
private List<String> targetTaskIds;
private String advice;
private String operationDesc;
private List<AttachmentDTO> attachmentList;
private BpmnTaskDelegateAssigner operator;
private String processInstanceId;
private String currentActivityId;
private String toActivityId;
private boolean validateApprover;
}
}

View File

@ -12,6 +12,7 @@ import org.flowable.job.service.impl.persistence.entity.JobEntity;
import org.flowable.task.api.Task;
import org.flowable.variable.api.delegate.VariableScope;
import java.util.Collections;
import java.util.Objects;
@Slf4j
@ -35,7 +36,17 @@ public class AsyncBackTaskJobHandler extends AbstractExecuteWithLockJobHandler i
if (Objects.isNull(task)) {
return;
}
processEngineConfiguration.getCommandExecutor().execute(new CustomBackTaskCmd(dto));
processEngineConfiguration.getCommandExecutor().execute(new CustomBackTaskCmd(
CustomBackTaskCmd.CustomBackParamsDto.builder()
.targetTaskIds(Collections.singletonList(dto.getTaskId()))
.advice(dto.getAdvice())
.operationDesc(dto.getOperationDesc())
.attachmentList(dto.getAttachmentList())
.operator(dto.getApprover())
.processInstanceId(task.getProcessInstanceId())
.currentActivityId(task.getTaskDefinitionKey())
.toActivityId(dto.getToActivityId())
.validateApprover(true)
.build()));
}
}

View File

@ -29,11 +29,18 @@ public interface BpmnProcessTaskService {
void approveTask(BpmnTaskAuditDTO taskAuditDTO);
void approveTaskWithForm(BpmnTaskAuditWithFormDTO taskAuditDto);
/**
* 回退
*/
void backTask(BpmnTaskBackAuditDTO taskAuditDTO);
/**
* 用于系统内部回退操作
* @param taskAuditDTO 请求参数
*/
void systemBackTask(BpmnNodeBackSystemOperateDTO taskAuditDTO);
/**
* 批量同意
*
@ -43,9 +50,16 @@ public interface BpmnProcessTaskService {
/**
* 回退到指定节点可以回退节点选项
* @param taskId 任务id
* @param taskId 流程任务id
*/
List<BpmnOptionalNodeDTO> getBackOptionalNodes(String taskId);
List<BpmnOptionalNodeDTO> getBackOptionalNodesByTaskId(String taskId);
/**
* 回退到指定节点可以回退节点选项
* @param processInstanceId 流程实例id
* @param currentActivityId 当前节点定义id
*/
List<BpmnOptionalNodeDTO> getBackOptionalNodes(String processInstanceId, String currentActivityId);
/**
* 驳回

View File

@ -20,6 +20,21 @@ import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskPageSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskRemindDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskTransferDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.ExtHiTaskSearchDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnNodeBackSystemOperateDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnOptionalNodeDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCompleteDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnRobotTaskCreateDTO;
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.BpmnTaskAuditWithFormDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskBackAuditDTO;
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.BpmnTaskRemindDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.BpmnTaskTransferDTO;
import cn.axzo.workflow.common.model.request.bpmn.task.ExtHiTaskSearchDTO;
import cn.axzo.workflow.common.model.response.BpmPageResult;
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationItemResultVO;
import cn.axzo.workflow.common.model.response.bpmn.BatchOperationResultVO;
@ -143,6 +158,35 @@ import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETE
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.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_ID_NOT_EXISTS;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_INSTANCE_NOT_EXISTS;
import static cn.axzo.workflow.common.code.BpmnInstanceRespCode.PROCESS_TASK_NOT_EXISTS;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.BACK_NODE_CANNOT_REACHABLE;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.FIND_TASK_BY_PERSON_ID_ERROR;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.REACHED_BACKED_MAXIMUM_NUM;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_COMPLETE_FAIL_NOT_EXISTS;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_HAS_BEEN_COMPLETE;
import static cn.axzo.workflow.common.code.BpmnTaskRespCode.TASK_REMIND_ERROR_NOT_EXISTS;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_ADVICE;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_COMMENT_EXT;
import static cn.axzo.workflow.common.constant.BpmnConstants.COMMENT_TYPE_OPERATION_DESC;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_ACTIVITY_RELATION_ASSIGNEE_LIST_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.INTERNAL_TASK_RELATION_ASSIGNEE_INFO;
import static cn.axzo.workflow.common.constant.BpmnConstants.MAX_BACKED_OPERATE_COUNT;
import static cn.axzo.workflow.common.constant.BpmnConstants.NO_ASSIGNEE;
import static cn.axzo.workflow.common.constant.BpmnConstants.OLD_INTERNAL_TASK_RELATION_ASSIGNEE_INFO_SNAPSHOT;
import static cn.axzo.workflow.common.constant.BpmnConstants.WORKFLOW_ENGINE_VERSION;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_BUSINESS;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_CARBON_COPY;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_EMPTY;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_STARTER;
import static cn.axzo.workflow.common.enums.BpmnFlowNodeType.NODE_TASK;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.APPROVED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.BACKED;
import static cn.axzo.workflow.common.enums.BpmnProcessInstanceResultEnum.DELETED;
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.common.util.BpmnNativeQueryUtil.countSql;
import static cn.axzo.workflow.common.util.BpmnNativeQueryUtil.sqlConnectors;
import static cn.axzo.workflow.core.common.utils.BpmnCollectionUtils.convertSet;
@ -361,7 +405,11 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
@Override
@Transactional(rollbackFor = Exception.class)
public void backTask(BpmnTaskBackAuditDTO dto) {
List<BpmnOptionalNodeDTO> backOptionalNodes = getBackOptionalNodes(dto.getTaskId());
Task task = processEngineConfiguration.getTaskService().createTaskQuery().taskId(dto.getTaskId()).singleResult();
if (task == null) {
throw new WorkflowEngineException(PROCESS_TASK_NOT_EXISTS);
}
List<BpmnOptionalNodeDTO> backOptionalNodes = getBackOptionalNodes(task.getProcessInstanceId(), task.getTaskDefinitionKey());
if (CollectionUtils.isEmpty(backOptionalNodes)) {
throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
}
@ -390,17 +438,98 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
if (Boolean.TRUE.equals(dto.getAsync())) {
commandExecutor.execute(new CustomBackTaskAsyncCmd(dto));
} else {
commandExecutor.execute(new CustomBackTaskCmd(dto));
commandExecutor.execute(new CustomBackTaskCmd(CustomBackTaskCmd.CustomBackParamsDto.builder()
.targetTaskIds(Collections.singletonList(dto.getTaskId()))
.advice(dto.getAdvice())
.operationDesc(dto.getOperationDesc())
.attachmentList(dto.getAttachmentList())
.operator(dto.getApprover())
.processInstanceId(task.getProcessInstanceId())
.currentActivityId(task.getTaskDefinitionKey())
.toActivityId(dto.getToActivityId())
.validateApprover(true)
.build()));
}
}
@Override
public List<BpmnOptionalNodeDTO> getBackOptionalNodes(String taskId) {
public void systemBackTask(BpmnNodeBackSystemOperateDTO dto) {
//需要查询当前流程是否停留在当前节点
//需要查询退回的节点是否可达
//然后进行退回操作
List<HistoricTaskInstance> taskList = processEngineConfiguration.getHistoryService()
.createHistoricTaskInstanceQuery()
.taskDefinitionKey(dto.getCurrentActivityId())
.processInstanceId(dto.getProcessInstanceId())
.list();
if (CollectionUtils.isEmpty(taskList)) {
throw new WorkflowEngineException(TASK_COMPLETE_FAIL_NOT_EXISTS);
}
List<HistoricTaskInstance> valuableTasks = taskList.stream()
.filter(t -> Objects.isNull(t.getEndTime()))
.filter(t -> {
if (CollectionUtils.isNotEmpty(dto.getTargetTaskIds())) {
return dto.getTargetTaskIds().contains(t.getId());
}
return true;
})
.collect(Collectors.toList());
if (CollectionUtils.isEmpty(valuableTasks)) {
throw new WorkflowEngineException(TASK_HAS_BEEN_COMPLETE);
}
List<BpmnOptionalNodeDTO> backOptionalNodes = getBackOptionalNodes(valuableTasks.get(0).getProcessInstanceId(), valuableTasks.get(0).getTaskDefinitionKey());
if (CollectionUtils.isEmpty(backOptionalNodes)) {
throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
}
List<String> activityList = backOptionalNodes.stream().map(BpmnOptionalNodeDTO::getProcessActivityId).collect(Collectors.toList());
if (!activityList.contains(dto.getToActivityId())) {
throw new WorkflowEngineException(BACK_NODE_CANNOT_REACHABLE, dto.getToActivityId());
}
Optional<String> instOpt = backOptionalNodes.stream()
.map(BpmnOptionalNodeDTO::getProcessInstanceId)
.filter(StringUtils::hasText)
.findAny();
if (instOpt.isPresent()) {
ExtHiTaskSearchDTO searchDTO = new ExtHiTaskSearchDTO();
searchDTO.setProcessInstanceId(instOpt.get());
List<ExtAxHiTaskInst> extAxHiTaskInsts = extAxHiTaskInstService.queryList(searchDTO);
if (CollectionUtils.isNotEmpty(extAxHiTaskInsts)) {
long backOpeCount = extAxHiTaskInsts.stream()
.filter(ext -> BACKED.getStatus().equals(ext.getStatus()))
.count();
if (backOpeCount > MAX_BACKED_OPERATE_COUNT) {
throw new WorkflowEngineException(REACHED_BACKED_MAXIMUM_NUM, String.valueOf(MAX_BACKED_OPERATE_COUNT));
}
}
}
BpmnTaskDelegateAssigner operator = dto.getOperator();
if (operator == null) {
operator = new BpmnTaskDelegateAssigner("系统", "system", taskList.get(0).getTenantId());
}
CommandExecutor commandExecutor = processEngineConfiguration.getCommandExecutor();
commandExecutor.execute(new CustomBackTaskCmd(CustomBackTaskCmd.CustomBackParamsDto.builder()
.targetTaskIds(valuableTasks.stream().map(TaskInfo::getId).collect(Collectors.toList()))
.advice(dto.getAdvice())
.operationDesc(dto.getOperationDesc())
.attachmentList(dto.getAttachmentList())
.operator(operator)
.processInstanceId(valuableTasks.get(0).getProcessInstanceId())
.currentActivityId(dto.getCurrentActivityId())
.toActivityId(dto.getToActivityId())
.build()));
}
@Override
public List<BpmnOptionalNodeDTO> getBackOptionalNodesByTaskId(String taskId) {
Task task = processEngineConfiguration.getTaskService().createTaskQuery().taskId(taskId).singleResult();
if (task == null) {
throw new WorkflowEngineException(PROCESS_TASK_NOT_EXISTS);
}
String processInstanceId = task.getProcessInstanceId();
return this.getBackOptionalNodes(task.getProcessInstanceId(), task.getTaskDefinitionKey());
}
@Override
public List<BpmnOptionalNodeDTO> getBackOptionalNodes(String processInstanceId, String currentActivityId) {
//1.获取当前的流程实例
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (processInstance == null) {
@ -459,7 +588,7 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
AtomicInteger index = new AtomicInteger(0);
List<BpmnOptionalNodeDTO> resultList = valuableList
.stream()
.filter(pair -> !task.getTaskDefinitionKey().equals(pair.getLeft())) //排除当前节点
.filter(pair -> !currentActivityId.equals(pair.getLeft())) //排除当前节点
.map(pair -> flowElementMap.get(pair.getLeft()))
.filter(flowElement -> {
BpmnFlowNodeType currentNodeType = BpmnMetaParserHelper.getNodeType(flowElement).orElse(NODE_EMPTY);
@ -468,8 +597,8 @@ public class BpmnProcessTaskServiceImpl implements BpmnProcessTaskService {
.filter(flowElement -> !NODE_STARTER.getType().equals(flowElement.getId()))
.map(flowElement -> BpmnOptionalNodeDTO
.builder()
.processInstanceId(task.getProcessInstanceId())
.processDefinitionId(task.getProcessDefinitionId())
.processInstanceId(processInstanceId)
.processDefinitionId(processInstance.getProcessDefinitionId())
.processActivityId(flowElement.getId())
.processActivityName(flowElement.getName())
.processNodeDesc(flowElement.getName())

View File

@ -139,7 +139,7 @@ public class BpmnProcessTaskController extends BasicPopulateAvatarController imp
@GetMapping("/back/optional/nodes")
@Override
public CommonResponse<List<BpmnOptionalNodeDTO>> getBackOptionalNodes(@RequestParam @NotBlank(message = "任务id不能为空") String taskId) {
List<BpmnOptionalNodeDTO> approveOptionalNodes = bpmnProcessTaskService.getBackOptionalNodes(taskId);
List<BpmnOptionalNodeDTO> approveOptionalNodes = bpmnProcessTaskService.getBackOptionalNodesByTaskId(taskId);
return success(approveOptionalNodes);
}
@ -157,6 +157,19 @@ public class BpmnProcessTaskController extends BasicPopulateAvatarController imp
return success(true);
}
/**
* 回退到指定节点
*/
@Operation(summary = "系统操作回退任务到指定节点")
@PostMapping("/system/back")
@RepeatSubmit
@Override
public CommonResponse<Boolean> systemBackTask(@Validated @RequestBody BpmnNodeBackSystemOperateDTO dto) {
log.info("系统回退 systemBackTask===>>>参数:{}", JSON.toJSONString(dto));
bpmnProcessTaskService.systemBackTask(dto);
return success(true);
}
/**
* 驳回
*/