diff --git a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/dto/BizFlowBaseDto.java b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/dto/BizFlowBaseDto.java new file mode 100644 index 000000000..98987bcec --- /dev/null +++ b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/dto/BizFlowBaseDto.java @@ -0,0 +1,31 @@ +package com.pig4cloud.pigx.flow.dto; + +import com.baomidou.mybatisplus.annotation.TableField; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.HashMap; +import java.util.Map; + +/** + * 自定义业务流基础数据传输对象 + * + * @author lengleng + * @date 2025/10/16 + */ +@Data +public class BizFlowBaseDto { + + /** + * 流程实例的编号 + */ + @Schema(description = "流程实例的编号") + private String processInstanceId; + + /** + * 流程参数 (用于存储用户选择的审批人等信息) + */ + @TableField(exist = false) + @Schema(description = "流程参数(审批人信息)") + private Map flowParamMap = new HashMap<>(); +} diff --git a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/entity/BpmOaLeaveEntity.java b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/entity/BpmOaLeaveEntity.java index 5ec9f614e..ddde98bd0 100644 --- a/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/entity/BpmOaLeaveEntity.java +++ b/pigx-flow/pigx-flow-api/src/main/java/com/pig4cloud/pigx/flow/entity/BpmOaLeaveEntity.java @@ -2,6 +2,7 @@ package com.pig4cloud.pigx.flow.entity; import com.baomidou.mybatisplus.annotation.*; import com.pig4cloud.pigx.common.core.util.TenantTable; +import com.pig4cloud.pigx.flow.dto.BizFlowBaseDto; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; @@ -15,103 +16,99 @@ import java.time.LocalDateTime; @TenantTable @TableName("bpm_oa_leave") @Schema(description = "请假表单") -public class BpmOaLeaveEntity { +public class BpmOaLeaveEntity extends BizFlowBaseDto { - /** - * 请假表单主键 - */ - @TableId(type = IdType.ASSIGN_ID) - @Schema(description = "请假表单主键") - private Long id; + /** + * 请假表单主键 + */ + @TableId(type = IdType.ASSIGN_ID) + @Schema(description = "请假表单主键") + private Long id; - /** - * 申请人的用户编号 - */ - @Schema(description = "申请人的用户编号") - private String username; + /** + * 申请人的用户编号 + */ + @Schema(description = "申请人的用户编号") + private String username; - /** - * 请假类型 - */ - @Schema(description = "请假类型") - private Integer leaveType; + /** + * 请假类型 + */ + @Schema(description = "请假类型") + private Integer leaveType; - /** - * 请假原因 - */ - @Schema(description = "请假原因") - private String leaveReason; + /** + * 请假原因 + */ + @Schema(description = "请假原因") + private String leaveReason; - /** - * 开始时间 - */ - @Schema(description = "开始时间") - private LocalDateTime startTime; + /** + * 开始时间 + */ + @Schema(description = "开始时间") + private LocalDateTime startTime; - /** - * 结束时间 - */ - @Schema(description = "结束时间") - private LocalDateTime endTime; + /** + * 结束时间 + */ + @Schema(description = "结束时间") + private LocalDateTime endTime; - /** - * 请假天数 - */ - @Schema(description = "请假天数") - private Integer leaveDay; + /** + * 请假天数 + */ + @Schema(description = "请假天数") + private Integer leaveDay; - /** - * 请假结果 - */ - @Schema(description = "请假结果") - private Integer leaveStatus; + /** + * 请假结果 + */ + @Schema(description = "请假结果") + private Integer leaveStatus; - /** - * 流程实例的编号 - */ - @Schema(description = "流程实例的编号") - private String processInstanceId; - /** - * 创建者 - */ - @TableField(fill = FieldFill.INSERT) - @Schema(description = "创建者") - private String createBy; + /** + * 创建者 + */ + @TableField(fill = FieldFill.INSERT) + @Schema(description = "创建者") + private String createBy; - /** - * 创建时间 - */ - @TableField(fill = FieldFill.INSERT) - @Schema(description = "创建时间") - private LocalDateTime createTime; + /** + * 创建时间 + */ + @TableField(fill = FieldFill.INSERT) + @Schema(description = "创建时间") + private LocalDateTime createTime; - /** - * 更新者 - */ - @TableField(fill = FieldFill.INSERT_UPDATE) - @Schema(description = "更新者") - private String updateBy; + /** + * 更新者 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @Schema(description = "更新者") + private String updateBy; - /** - * 更新时间 - */ - @TableField(fill = FieldFill.INSERT_UPDATE) - @Schema(description = "更新时间") - private LocalDateTime updateTime; + /** + * 更新时间 + */ + @TableField(fill = FieldFill.INSERT_UPDATE) + @Schema(description = "更新时间") + private LocalDateTime updateTime; - /** - * 是否删除 - */ - @TableLogic - @TableField(fill = FieldFill.INSERT) - @Schema(description = "是否删除") - private String delFlag; + /** + * 是否删除 + */ + @TableLogic + @TableField(fill = FieldFill.INSERT) + @Schema(description = "是否删除") + private String delFlag; + + /** + * 租户编号 + */ + @Schema(description = "租户编号") + private Long tenantId; - /** - * 租户编号 - */ - @Schema(description = "租户编号") - private Long tenantId; } diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/BpmOaLeaveServiceImpl.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/BpmOaLeaveServiceImpl.java index d17f876fd..2eb56d47d 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/BpmOaLeaveServiceImpl.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/service/impl/BpmOaLeaveServiceImpl.java @@ -1,23 +1,18 @@ package com.pig4cloud.pigx.flow.service.impl; -import cn.hutool.core.collection.CollUtil; -import cn.hutool.core.lang.Dict; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.pig4cloud.pigx.common.core.util.R; import com.pig4cloud.pigx.common.security.util.SecurityUtils; import com.pig4cloud.pigx.flow.api.feign.RemoteFlowApiFlowService; -import com.pig4cloud.pigx.flow.constant.NodeUserTypeEnum; import com.pig4cloud.pigx.flow.dto.ProcessInstanceParamDto; import com.pig4cloud.pigx.flow.entity.BpmOaLeaveEntity; import com.pig4cloud.pigx.flow.mapper.BpmOaLeaveMapper; import com.pig4cloud.pigx.flow.service.BpmOaLeaveService; +import com.pig4cloud.pigx.flow.support.utils.FlowableUtils; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; -import java.util.HashMap; -import java.util.Map; - /** * 请假表单 * @@ -27,57 +22,53 @@ import java.util.Map; @Service @RequiredArgsConstructor public class BpmOaLeaveServiceImpl extends ServiceImpl - implements BpmOaLeaveService { + implements BpmOaLeaveService { - private final RemoteFlowApiFlowService flowApiFlowService; + private final RemoteFlowApiFlowService flowApiFlowService; - /** - * 获取监听的流程定义Key - * @return 监听的流程定义Key - */ - @Override - public String getFlowId() { - return "LEAVE"; - } + /** + * 获取监听的流程定义Key + * + * @return 监听的流程定义Key + */ + @Override + public String getFlowId() { + return "LEAVE"; + } - /** - * 处理状态事件 - * @param paramDto 流程节点记录分配用户参数DTO - */ - @Override - public void handleStatusEvent(ProcessInstanceParamDto paramDto) { - this.update(Wrappers.lambdaUpdate() - .set(BpmOaLeaveEntity::getLeaveStatus, paramDto.getStatus()) - .eq(BpmOaLeaveEntity::getProcessInstanceId, paramDto.getProcessInstanceId())); - } + /** + * 处理状态事件 + * + * @param paramDto 流程节点记录分配用户参数DTO + */ + @Override + public void handleStatusEvent(ProcessInstanceParamDto paramDto) { + this.update(Wrappers.lambdaUpdate() + .set(BpmOaLeaveEntity::getLeaveStatus, paramDto.getStatus()) + .eq(BpmOaLeaveEntity::getProcessInstanceId, paramDto.getProcessInstanceId())); + } - /** - * 保存并启动请假流程 - * @param bpmOaLeave 请假实体对象 - * @return 保存后的请假实体对象 - */ - @Override - public BpmOaLeaveEntity saveAndStartProcess(BpmOaLeaveEntity bpmOaLeave) { - bpmOaLeave.setUsername(SecurityUtils.getUser().getUsername()); - baseMapper.insert(bpmOaLeave); - // 获取流程配置详情 - ProcessInstanceParamDto processInstanceParamDto = new ProcessInstanceParamDto(); - processInstanceParamDto.setFlowId(this.getFlowId()); - processInstanceParamDto.setStartUserId(String.valueOf(SecurityUtils.getUser().getId())); + /** + * 保存并启动请假流程 + * + * @param bpmOaLeave 请假实体对象 + * @return 保存后的请假实体对象 + */ + @Override + public BpmOaLeaveEntity saveAndStartProcess(BpmOaLeaveEntity bpmOaLeave) { + bpmOaLeave.setUsername(SecurityUtils.getUser().getUsername()); + baseMapper.insert(bpmOaLeave); + // 获取流程配置详情 + ProcessInstanceParamDto processInstanceParamDto = new ProcessInstanceParamDto(); + // 发起哪个流程 (LEAVE) + processInstanceParamDto.setFlowId(this.getFlowId()); + // 设置流程审批人/发起人信息 + processInstanceParamDto.setParamMap(FlowableUtils.getFlowParamMap(bpmOaLeave)); - Map paramMap = new HashMap<>(); - // 设置发起人 - Dict rootUser = Dict.create() - .set("id", processInstanceParamDto.getStartUserId()) - .set("name", SecurityUtils.getUser().getUsername()) - .set("type", NodeUserTypeEnum.USER.getKey()); - paramMap.put("root", CollUtil.newArrayList(rootUser)); - processInstanceParamDto.setParamMap(paramMap); - - R stringR = flowApiFlowService.startProcessInstance(processInstanceParamDto); - bpmOaLeave.setProcessInstanceId(stringR.getData()); - baseMapper.updateById(bpmOaLeave); - return bpmOaLeave; - } + R stringR = flowApiFlowService.startProcessInstance(processInstanceParamDto); + bpmOaLeave.setProcessInstanceId(stringR.getData()); + baseMapper.updateById(bpmOaLeave); + return bpmOaLeave; + } } diff --git a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/utils/FlowableUtils.java b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/utils/FlowableUtils.java index 65dab9fd0..1240151c9 100644 --- a/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/utils/FlowableUtils.java +++ b/pigx-flow/pigx-flow-biz/src/main/java/com/pig4cloud/pigx/flow/support/utils/FlowableUtils.java @@ -1,5 +1,10 @@ package com.pig4cloud.pigx.flow.support.utils; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Dict; +import com.pig4cloud.pigx.common.security.util.SecurityUtils; +import com.pig4cloud.pigx.flow.constant.NodeUserTypeEnum; +import com.pig4cloud.pigx.flow.dto.BizFlowBaseDto; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.flowable.bpmn.model.*; @@ -35,706 +40,713 @@ import java.util.*; */ public class FlowableUtils { - public static final Logger logger = LogManager.getLogger(FlowableUtils.class); + public static final Logger logger = LogManager.getLogger(FlowableUtils.class); - /** - * 根据节点获取入口连线集合 - *

- * 支持多种流程元素类型,包括任务、网关、子流程、开始/结束事件等。 - * 入口连线是指向该节点的所有连线。 - * - * @param source 流程元素节点 - * @return 入口连线集合,如果节点类型不支持则返回null - */ - public static List getElementIncomingFlows(FlowElement source) { - List sequenceFlows = null; - if (source instanceof Task) { - sequenceFlows = ((Task) source).getIncomingFlows(); - } - else if (source instanceof Gateway) { - sequenceFlows = ((Gateway) source).getIncomingFlows(); - } - else if (source instanceof SubProcess) { - sequenceFlows = ((SubProcess) source).getIncomingFlows(); - } - else if (source instanceof StartEvent) { - sequenceFlows = ((StartEvent) source).getIncomingFlows(); - } - else if (source instanceof EndEvent) { - sequenceFlows = ((EndEvent) source).getIncomingFlows(); - } - return sequenceFlows; - } + /** + * 根据节点获取入口连线集合 + *

+ * 支持多种流程元素类型,包括任务、网关、子流程、开始/结束事件等。 + * 入口连线是指向该节点的所有连线。 + * + * @param source 流程元素节点 + * @return 入口连线集合,如果节点类型不支持则返回null + */ + public static List getElementIncomingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof Task) { + sequenceFlows = ((Task) source).getIncomingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getIncomingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getIncomingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getIncomingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getIncomingFlows(); + } + return sequenceFlows; + } - /** - * 根据节点获取出口连线集合 - *

- * 支持多种流程元素类型,包括任务、网关、子流程、开始/结束事件等。 - * 出口连线是从该节点出发的所有连线。 - * - * @param source 流程元素节点 - * @return 出口连线集合,如果节点类型不支持则返回null - */ - public static List getElementOutgoingFlows(FlowElement source) { - List sequenceFlows = null; - if (source instanceof Task) { - sequenceFlows = ((Task) source).getOutgoingFlows(); - } - else if (source instanceof Gateway) { - sequenceFlows = ((Gateway) source).getOutgoingFlows(); - } - else if (source instanceof SubProcess) { - sequenceFlows = ((SubProcess) source).getOutgoingFlows(); - } - else if (source instanceof StartEvent) { - sequenceFlows = ((StartEvent) source).getOutgoingFlows(); - } - else if (source instanceof EndEvent) { - sequenceFlows = ((EndEvent) source).getOutgoingFlows(); - } - return sequenceFlows; - } + /** + * 根据节点获取出口连线集合 + *

+ * 支持多种流程元素类型,包括任务、网关、子流程、开始/结束事件等。 + * 出口连线是从该节点出发的所有连线。 + * + * @param source 流程元素节点 + * @return 出口连线集合,如果节点类型不支持则返回null + */ + public static List getElementOutgoingFlows(FlowElement source) { + List sequenceFlows = null; + if (source instanceof Task) { + sequenceFlows = ((Task) source).getOutgoingFlows(); + } else if (source instanceof Gateway) { + sequenceFlows = ((Gateway) source).getOutgoingFlows(); + } else if (source instanceof SubProcess) { + sequenceFlows = ((SubProcess) source).getOutgoingFlows(); + } else if (source instanceof StartEvent) { + sequenceFlows = ((StartEvent) source).getOutgoingFlows(); + } else if (source instanceof EndEvent) { + sequenceFlows = ((EndEvent) source).getOutgoingFlows(); + } + return sequenceFlows; + } - /** - * 递归获取流程定义所有的流程元素 - *

- * 深度优先遍历流程定义,包括子流程中的所有元素。 - * 该方法常用于分析整个流程结构或查找特定元素。 - * - * @param flowElements 当前级别流程元素集合 - * @param allElements 用于存储的全部流程元素集合,可为null - * @return 全部流程元素集合,包含所有层级的元素 - */ - public static Collection getAllElements(Collection flowElements, - Collection allElements) { - allElements = allElements == null ? new ArrayList<>() : allElements; + /** + * 递归获取流程定义所有的流程元素 + *

+ * 深度优先遍历流程定义,包括子流程中的所有元素。 + * 该方法常用于分析整个流程结构或查找特定元素。 + * + * @param flowElements 当前级别流程元素集合 + * @param allElements 用于存储的全部流程元素集合,可为null + * @return 全部流程元素集合,包含所有层级的元素 + */ + public static Collection getAllElements(Collection flowElements, + Collection allElements) { + allElements = allElements == null ? new ArrayList<>() : allElements; - for (FlowElement flowElement : flowElements) { - allElements.add(flowElement); - if (flowElement instanceof SubProcess) { - // 继续深入子流程,进一步获取子流程 - allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements); - } - } - return allElements; - } + for (FlowElement flowElement : flowElements) { + allElements.add(flowElement); + if (flowElement instanceof SubProcess) { + // 继续深入子流程,进一步获取子流程 + allElements = FlowableUtils.getAllElements(((SubProcess) flowElement).getFlowElements(), allElements); + } + } + return allElements; + } - /** - * 从后向前递归获取父级用户任务节点 - *

- * 从当前节点开始,沿着入口连线向前查找所有的用户任务节点。 - * 主要用于流程回退、驳回等场景,找到可以回退的目标节点。 - * 支持子流程的遍历。 - * - * @param source 当前节点 - * @param hasSequenceFlow 已处理过的顺序流集合,避免循环,可为null - * @param userTaskList 递归获取的用户任务节点集合,可为null - * @return 用户任务节点集合 - */ - public static List iteratorFindParentUserTasks(FlowElement source, Set hasSequenceFlow, - List userTaskList) { - userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; - hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + /** + * 从后向前递归获取父级用户任务节点 + *

+ * 从当前节点开始,沿着入口连线向前查找所有的用户任务节点。 + * 主要用于流程回退、驳回等场景,找到可以回退的目标节点。 + * 支持子流程的遍历。 + * + * @param source 当前节点 + * @param hasSequenceFlow 已处理过的顺序流集合,避免循环,可为null + * @param userTaskList 递归获取的用户任务节点集合,可为null + * @return 用户任务节点集合 + */ + public static List iteratorFindParentUserTasks(FlowElement source, Set hasSequenceFlow, + List userTaskList) { + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; - // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 - if (source instanceof StartEvent && source.getSubProcess() != null) { - userTaskList = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList); - } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + userTaskList = iteratorFindParentUserTasks(source.getSubProcess(), hasSequenceFlow, userTaskList); + } - // 根据类型,获取入口连线 - List sequenceFlows = getElementIncomingFlows(source); + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); - if (sequenceFlows != null) { - // 循环找到目标元素 - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (hasSequenceFlow.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - hasSequenceFlow.add(sequenceFlow.getId()); - // 类型为用户节点,则新增父级节点 - if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { - userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); - continue; - } - // 类型为子流程,则添加子流程开始节点出口处相连的节点 - if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { - // 获取子流程用户任务节点 - List childUserTaskList = findChildProcessUserTasks( - (StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements() - .toArray()[0], - null, null); - // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 - if (childUserTaskList != null && childUserTaskList.size() > 0) { - userTaskList.addAll(childUserTaskList); - continue; - } - } - // 继续迭代 - // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 - // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 - userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), - new HashSet<>(hasSequenceFlow), userTaskList); - } - } - return userTaskList; - } + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 类型为用户节点,则新增父级节点 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getSourceFlowElement()); + continue; + } + // 类型为子流程,则添加子流程开始节点出口处相连的节点 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + // 获取子流程用户任务节点 + List childUserTaskList = findChildProcessUserTasks( + (StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements() + .toArray()[0], + null, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), + new HashSet<>(hasSequenceFlow), userTaskList); + } + } + return userTaskList; + } - /** - * 根据正在运行的任务节点,迭代获取子级任务节点列表 - *

- * 从当前节点开始,沿着出口连线向后查找正在运行的任务节点。 - * 主要用于找到需要撤回或影响的下游节点。 - * - * @param source 起始节点 - * @param runActiveIdList 正在运行的任务Key,用于校验任务节点是否是正在运行的节点 - * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 - * @param flowElementList 需要撤回的用户任务列表 - * @return 找到的流程元素列表 - */ - public static List iteratorFindChildUserTasks(FlowElement source, List runActiveIdList, - Set hasSequenceFlow, List flowElementList) { - hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; - flowElementList = flowElementList == null ? new ArrayList<>() : flowElementList; + /** + * 根据正在运行的任务节点,迭代获取子级任务节点列表 + *

+ * 从当前节点开始,沿着出口连线向后查找正在运行的任务节点。 + * 主要用于找到需要撤回或影响的下游节点。 + * + * @param source 起始节点 + * @param runActiveIdList 正在运行的任务Key,用于校验任务节点是否是正在运行的节点 + * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 + * @param flowElementList 需要撤回的用户任务列表 + * @return 找到的流程元素列表 + */ + public static List iteratorFindChildUserTasks(FlowElement source, List runActiveIdList, + Set hasSequenceFlow, List flowElementList) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + flowElementList = flowElementList == null ? new ArrayList<>() : flowElementList; - // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 - if (source instanceof EndEvent && source.getSubProcess() != null) { - flowElementList = iteratorFindChildUserTasks(source.getSubProcess(), runActiveIdList, hasSequenceFlow, - flowElementList); - } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof EndEvent && source.getSubProcess() != null) { + flowElementList = iteratorFindChildUserTasks(source.getSubProcess(), runActiveIdList, hasSequenceFlow, + flowElementList); + } - // 根据类型,获取出口连线 - List sequenceFlows = getElementOutgoingFlows(source); + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); - if (sequenceFlows != null) { - // 循环找到目标元素 - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (hasSequenceFlow.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - hasSequenceFlow.add(sequenceFlow.getId()); - // 如果为用户任务类型,或者为网关 - // 活动节点ID 在运行的任务中存在,添加 - if ((sequenceFlow.getTargetFlowElement() instanceof UserTask - || sequenceFlow.getTargetFlowElement() instanceof Gateway) - && runActiveIdList.contains((sequenceFlow.getTargetFlowElement()).getId())) { - flowElementList.add(sequenceFlow.getTargetFlowElement()); - continue; - } - // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 - if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { - List childUserTaskList = iteratorFindChildUserTasks( - (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() - .toArray()[0]), - runActiveIdList, hasSequenceFlow, null); - // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 - if (childUserTaskList != null && childUserTaskList.size() > 0) { - flowElementList.addAll(childUserTaskList); - continue; - } - } - // 继续迭代 - // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 - // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 - flowElementList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runActiveIdList, - new HashSet<>(hasSequenceFlow), flowElementList); - } - } - return flowElementList; - } + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果为用户任务类型,或者为网关 + // 活动节点ID 在运行的任务中存在,添加 + if ((sequenceFlow.getTargetFlowElement() instanceof UserTask + || sequenceFlow.getTargetFlowElement() instanceof Gateway) + && runActiveIdList.contains((sequenceFlow.getTargetFlowElement()).getId())) { + flowElementList.add(sequenceFlow.getTargetFlowElement()); + continue; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + List childUserTaskList = iteratorFindChildUserTasks( + (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() + .toArray()[0]), + runActiveIdList, hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + flowElementList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + flowElementList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runActiveIdList, + new HashSet<>(hasSequenceFlow), flowElementList); + } + } + return flowElementList; + } - /** - * 迭代获取子流程用户任务节点 - *

- * 专门用于处理子流程内部的用户任务节点查找。 - * 从子流程的起始节点开始,找到所有的用户任务节点。 - * - * @param source 起始节点 - * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 - * @param userTaskList 需要撤回的用户任务列表 - * @return 用户任务节点列表 - */ - public static List findChildProcessUserTasks(FlowElement source, Set hasSequenceFlow, - List userTaskList) { - hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; - userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; + /** + * 迭代获取子流程用户任务节点 + *

+ * 专门用于处理子流程内部的用户任务节点查找。 + * 从子流程的起始节点开始,找到所有的用户任务节点。 + * + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 + * @param userTaskList 需要撤回的用户任务列表 + * @return 用户任务节点列表 + */ + public static List findChildProcessUserTasks(FlowElement source, Set hasSequenceFlow, + List userTaskList) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; - // 根据类型,获取出口连线 - List sequenceFlows = getElementOutgoingFlows(source); + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); - if (sequenceFlows != null) { - // 循环找到目标元素 - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (hasSequenceFlow.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - hasSequenceFlow.add(sequenceFlow.getId()); - // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 - if (sequenceFlow.getTargetFlowElement() instanceof UserTask) { - userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); - continue; - } - // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 - if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { - List childUserTaskList = findChildProcessUserTasks( - (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() - .toArray()[0]), - hasSequenceFlow, null); - // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 - if (childUserTaskList != null && childUserTaskList.size() > 0) { - userTaskList.addAll(childUserTaskList); - continue; - } - } - // 继续迭代 - // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 - // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 - userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), - new HashSet<>(hasSequenceFlow), userTaskList); - } - } - return userTaskList; - } + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果为用户任务类型,且任务节点的 Key 正在运行的任务中存在,添加 + if (sequenceFlow.getTargetFlowElement() instanceof UserTask) { + userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); + continue; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + List childUserTaskList = findChildProcessUserTasks( + (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() + .toArray()[0]), + hasSequenceFlow, null); + // 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续 + if (childUserTaskList != null && childUserTaskList.size() > 0) { + userTaskList.addAll(childUserTaskList); + continue; + } + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), + new HashSet<>(hasSequenceFlow), userTaskList); + } + } + return userTaskList; + } - /** - * 从后向前寻路,获取所有脏线路上的点 - *

- * 脏线路是指由于流程回退、驳回等操作产生的废弃路径。 - * 该方法用于识别这些废弃路径上的所有节点,以便进行清理或过滤。 - * - * @param source 起始节点 - * @param passRoads 已经经过的点集合 - * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 - * @param targets 目标脏线路终点 - * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用set存储 - * @return 脏线路上的所有节点ID集合 - */ - public static Set iteratorFindDirtyRoads(FlowElement source, List passRoads, - Set hasSequenceFlow, List targets, Set dirtyRoads) { - passRoads = passRoads == null ? new ArrayList<>() : passRoads; - dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; - hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + /** + * 从后向前寻路,获取所有脏线路上的点 + *

+ * 脏线路是指由于流程回退、驳回等操作产生的废弃路径。 + * 该方法用于识别这些废弃路径上的所有节点,以便进行清理或过滤。 + * + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 + * @param targets 目标脏线路终点 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用set存储 + * @return 脏线路上的所有节点ID集合 + */ + public static Set iteratorFindDirtyRoads(FlowElement source, List passRoads, + Set hasSequenceFlow, List targets, Set dirtyRoads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; - // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 - if (source instanceof StartEvent && source.getSubProcess() != null) { - dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, - dirtyRoads); - } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + dirtyRoads = iteratorFindDirtyRoads(source.getSubProcess(), passRoads, hasSequenceFlow, targets, + dirtyRoads); + } - // 根据类型,获取入口连线 - List sequenceFlows = getElementIncomingFlows(source); + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); - if (sequenceFlows != null) { - // 循环找到目标元素 - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (hasSequenceFlow.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - hasSequenceFlow.add(sequenceFlow.getId()); - // 新增经过的路线 - passRoads.add(sequenceFlow.getSourceFlowElement().getId()); - // 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线 - if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) { - dirtyRoads.addAll(passRoads); - continue; - } - // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 - if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { - dirtyRoads = findChildProcessAllDirtyRoad( - (StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements() - .toArray()[0], - null, dirtyRoads); - // 是否存在子流程上,true 是,false 否 - Boolean isInChildProcess = dirtyTargetInChildProcess( - (StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements() - .toArray()[0], - null, targets, null); - if (isInChildProcess) { - // 已在子流程上找到,该路线结束 - continue; - } - } - // 继续迭代 - // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 - // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 - dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), new ArrayList<>(passRoads), - new HashSet<>(hasSequenceFlow), targets, dirtyRoads); - } - } - return dirtyRoads; - } + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 新增经过的路线 + passRoads.add(sequenceFlow.getSourceFlowElement().getId()); + // 如果此点为目标点,确定经过的路线为脏线路,添加点到脏线路中,然后找下个连线 + if (targets.contains(sequenceFlow.getSourceFlowElement().getId())) { + dirtyRoads.addAll(passRoads); + continue; + } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (sequenceFlow.getSourceFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad( + (StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements() + .toArray()[0], + null, dirtyRoads); + // 是否存在子流程上,true 是,false 否 + Boolean isInChildProcess = dirtyTargetInChildProcess( + (StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements() + .toArray()[0], + null, targets, null); + if (isInChildProcess) { + // 已在子流程上找到,该路线结束 + continue; + } + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), new ArrayList<>(passRoads), + new HashSet<>(hasSequenceFlow), targets, dirtyRoads); + } + } + return dirtyRoads; + } - /** - * 迭代获取子流程脏路线 - *

- * 当回退点是子流程时,子流程中的所有节点都被认为是脏路线。 - * 该方法递归遍历子流程,获取其中的所有节点。 - * - * @param source 起始节点 - * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 - * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用set存储 - * @return 子流程中所有节点的ID集合 - */ - public static Set findChildProcessAllDirtyRoad(FlowElement source, Set hasSequenceFlow, - Set dirtyRoads) { - hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; - dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; + /** + * 迭代获取子流程脏路线 + *

+ * 当回退点是子流程时,子流程中的所有节点都被认为是脏路线。 + * 该方法递归遍历子流程,获取其中的所有节点。 + * + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 + * @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用set存储 + * @return 子流程中所有节点的ID集合 + */ + public static Set findChildProcessAllDirtyRoad(FlowElement source, Set hasSequenceFlow, + Set dirtyRoads) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; - // 根据类型,获取出口连线 - List sequenceFlows = getElementOutgoingFlows(source); + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); - if (sequenceFlows != null) { - // 循环找到目标元素 - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (hasSequenceFlow.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - hasSequenceFlow.add(sequenceFlow.getId()); - // 添加脏路线 - dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId()); - // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 - if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { - dirtyRoads = findChildProcessAllDirtyRoad( - (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() - .toArray()[0]), - hasSequenceFlow, dirtyRoads); - } - // 继续迭代 - // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 - // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 - dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), - new HashSet<>(hasSequenceFlow), dirtyRoads); - } - } - return dirtyRoads; - } + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加脏路线 + dirtyRoads.add(sequenceFlow.getTargetFlowElement().getId()); + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + dirtyRoads = findChildProcessAllDirtyRoad( + (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() + .toArray()[0]), + hasSequenceFlow, dirtyRoads); + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), + new HashSet<>(hasSequenceFlow), dirtyRoads); + } + } + return dirtyRoads; + } - /** - * 判断脏路线结束节点是否在子流程上 - *

- * 用于判断脏路线的范围,如果脏路线终点在子流程中, - * 说明脏路线只到子流程为止,不会影响父流程。 - * - * @param source 起始节点 - * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 - * @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止 - * @param inChildProcess 是否存在子流程上,true是,false否 - * @return 是否在子流程中 - */ - public static Boolean dirtyTargetInChildProcess(FlowElement source, Set hasSequenceFlow, - List targets, Boolean inChildProcess) { - hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; - inChildProcess = inChildProcess == null ? false : inChildProcess; + /** + * 判断脏路线结束节点是否在子流程上 + *

+ * 用于判断脏路线的范围,如果脏路线终点在子流程中, + * 说明脏路线只到子流程为止,不会影响父流程。 + * + * @param source 起始节点 + * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 + * @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止 + * @param inChildProcess 是否存在子流程上,true是,false否 + * @return 是否在子流程中 + */ + public static Boolean dirtyTargetInChildProcess(FlowElement source, Set hasSequenceFlow, + List targets, Boolean inChildProcess) { + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + inChildProcess = inChildProcess == null ? false : inChildProcess; - // 根据类型,获取出口连线 - List sequenceFlows = getElementOutgoingFlows(source); + // 根据类型,获取出口连线 + List sequenceFlows = getElementOutgoingFlows(source); - if (sequenceFlows != null && !inChildProcess) { - // 循环找到目标元素 - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (hasSequenceFlow.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - hasSequenceFlow.add(sequenceFlow.getId()); - // 如果发现目标点在子流程上存在,说明只到子流程为止 - if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) { - inChildProcess = true; - break; - } - // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 - if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { - inChildProcess = dirtyTargetInChildProcess( - (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() - .toArray()[0]), - hasSequenceFlow, targets, inChildProcess); - } - // 继续迭代 - // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 - // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 - inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), - new HashSet<>(hasSequenceFlow), targets, inChildProcess); - } - } - return inChildProcess; - } + if (sequenceFlows != null && !inChildProcess) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果发现目标点在子流程上存在,说明只到子流程为止 + if (targets.contains(sequenceFlow.getTargetFlowElement().getId())) { + inChildProcess = true; + break; + } + // 如果节点为子流程节点情况,则从节点中的第一个节点开始获取 + if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { + inChildProcess = dirtyTargetInChildProcess( + (FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements() + .toArray()[0]), + hasSequenceFlow, targets, inChildProcess); + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), + new HashSet<>(hasSequenceFlow), targets, inChildProcess); + } + } + return inChildProcess; + } - /** - * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 - *

- * 用于判断两个节点之间是否存在并行关系。 - * 如果两个节点之间只有唯一路径,则为串行;如果存在多条路径,则为并行。 - * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况。 - * - * @param source 起始节点 - * @param targetKsy 目标节点ID - * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 - * @param isSequential 是否串行,默认为true - * @return true表示串行,false表示并行 - */ - public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, - Set hasSequenceFlow, Boolean isSequential) { - isSequential = isSequential == null ? true : isSequential; - hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + /** + * 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 + *

+ * 用于判断两个节点之间是否存在并行关系。 + * 如果两个节点之间只有唯一路径,则为串行;如果存在多条路径,则为并行。 + * 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况。 + * + * @param source 起始节点 + * @param targetKsy 目标节点ID + * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 + * @param isSequential 是否串行,默认为true + * @return true表示串行,false表示并行 + */ + public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, + Set hasSequenceFlow, Boolean isSequential) { + isSequential = isSequential == null ? true : isSequential; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; - // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 - if (source instanceof StartEvent && source.getSubProcess() != null) { - isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, - isSequential); - } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, + isSequential); + } - // 根据类型,获取入口连线 - List sequenceFlows = getElementIncomingFlows(source); + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); - if (sequenceFlows != null) { - // 循环找到目标元素 - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (hasSequenceFlow.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - hasSequenceFlow.add(sequenceFlow.getId()); - // 如果目标节点已被判断为并行,后面都不需要执行,直接返回 - if (isSequential == false) { - break; - } - // 这条线路存在目标节点,这条线路完成,进入下个线路 - if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) { - continue; - } - if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) { - isSequential = false; - break; - } - // 否则就继续迭代 - // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 - // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 - isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, - new HashSet<>(hasSequenceFlow), isSequential); - } - } - return isSequential; - } + if (sequenceFlows != null) { + // 循环找到目标元素 + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 如果目标节点已被判断为并行,后面都不需要执行,直接返回 + if (isSequential == false) { + break; + } + // 这条线路存在目标节点,这条线路完成,进入下个线路 + if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) { + continue; + } + if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) { + isSequential = false; + break; + } + // 否则就继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, + new HashSet<>(hasSequenceFlow), isSequential); + } + } + return isSequential; + } - /** - * 从后向前寻路,获取到达节点的所有路线 - *

- * 找到从当前节点到开始节点的所有可能路径。 - * 每条路径是一个用户任务节点的有序列表。 - * 不存在直接回退到子流程,但是存在回退到父级流程的情况。 - * - * @param source 起始节点 - * @param passRoads 已经经过的点集合 - * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 - * @param roads 路线集合 - * @return 所有可能的路径列表 - */ - public static List> findRoad(FlowElement source, List passRoads, - Set hasSequenceFlow, List> roads) { - passRoads = passRoads == null ? new ArrayList<>() : passRoads; - roads = roads == null ? new ArrayList<>() : roads; - hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; + /** + * 从后向前寻路,获取到达节点的所有路线 + *

+ * 找到从当前节点到开始节点的所有可能路径。 + * 每条路径是一个用户任务节点的有序列表。 + * 不存在直接回退到子流程,但是存在回退到父级流程的情况。 + * + * @param source 起始节点 + * @param passRoads 已经经过的点集合 + * @param hasSequenceFlow 已经经过的连线的ID,用于判断线路是否重复 + * @param roads 路线集合 + * @return 所有可能的路径列表 + */ + public static List> findRoad(FlowElement source, List passRoads, + Set hasSequenceFlow, List> roads) { + passRoads = passRoads == null ? new ArrayList<>() : passRoads; + roads = roads == null ? new ArrayList<>() : roads; + hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; - // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 - if (source instanceof StartEvent && source.getSubProcess() != null) { - roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads); - } + // 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代 + if (source instanceof StartEvent && source.getSubProcess() != null) { + roads = findRoad(source.getSubProcess(), passRoads, hasSequenceFlow, roads); + } - // 根据类型,获取入口连线 - List sequenceFlows = getElementIncomingFlows(source); + // 根据类型,获取入口连线 + List sequenceFlows = getElementIncomingFlows(source); - if (sequenceFlows != null && sequenceFlows.size() != 0) { - for (SequenceFlow sequenceFlow : sequenceFlows) { - // 如果发现连线重复,说明循环了,跳过这个循环 - if (hasSequenceFlow.contains(sequenceFlow.getId())) { - continue; - } - // 添加已经走过的连线 - hasSequenceFlow.add(sequenceFlow.getId()); - // 添加经过路线 - if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { - passRoads.add((UserTask) sequenceFlow.getSourceFlowElement()); - } - // 继续迭代 - // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 - // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 - roads = findRoad(sequenceFlow.getSourceFlowElement(), new ArrayList<>(passRoads), - new HashSet<>(hasSequenceFlow), roads); - } - } - else { - // 添加路线 - roads.add(passRoads); - } - return roads; - } + if (sequenceFlows != null && sequenceFlows.size() != 0) { + for (SequenceFlow sequenceFlow : sequenceFlows) { + // 如果发现连线重复,说明循环了,跳过这个循环 + if (hasSequenceFlow.contains(sequenceFlow.getId())) { + continue; + } + // 添加已经走过的连线 + hasSequenceFlow.add(sequenceFlow.getId()); + // 添加经过路线 + if (sequenceFlow.getSourceFlowElement() instanceof UserTask) { + passRoads.add((UserTask) sequenceFlow.getSourceFlowElement()); + } + // 继续迭代 + // 注意:已经经过的节点与连线都应该用浅拷贝出来的对象 + // 比如分支:a->b->c与a->d->c,走完a->b->c后走另一个路线是,已经经过的节点应该不包含a->b->c路线的数据 + roads = findRoad(sequenceFlow.getSourceFlowElement(), new ArrayList<>(passRoads), + new HashSet<>(hasSequenceFlow), roads); + } + } else { + // 添加路线 + roads.add(passRoads); + } + return roads; + } - /** - * 历史节点数据清洗,清洗掉由于回滚导致的脏数据 - *

- * 在流程回退、驳回等操作后,历史数据中会存在废弃的节点记录。 - * 该方法通过分析删除原因和节点关系,识别并过滤掉这些脏数据, - * 返回真实有效的历史节点记录。 - *

- * 特别处理: - * - 会签节点的多实例情况 - * - 并行网关的多分支情况 - * - 连续驳回的情况 - * - * @param allElements 全部节点信息 - * @param historicActivityIdList 历史任务实例信息,数据采用开始时间升序 - * @return 清洗后的有效历史节点ID列表 - */ - public static List historicTaskInstanceClean(Collection allElements, - List historicActivityIdList) { - // 会签节点收集 - List multiTask = new ArrayList<>(); - allElements.forEach(flowElement -> { - if (flowElement instanceof UserTask) { - // 如果该节点的行为为会签行为,说明该节点为会签节点 - if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior - || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) { - multiTask.add(flowElement.getId()); - } - } - }); - // 循环放入栈,栈 LIFO:后进先出 - Stack stack = new Stack<>(); - historicActivityIdList.forEach(item -> stack.push(item)); - // 清洗后的历史任务实例 - List lastHistoricTaskInstanceList = new ArrayList<>(); - // 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗 - // 临时用户任务 key - StringBuilder userTaskKey = null; - // 临时被删掉的任务 key,存在并行情况 - List deleteKeyList = new ArrayList<>(); - // 临时脏数据线路 - List> dirtyDataLineList = new ArrayList<>(); - // 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到 - // 会签特殊处理下标 - int multiIndex = -1; - // 会签特殊处理 key - StringBuilder multiKey = null; - // 会签特殊处理操作标识 - boolean multiOpera = false; - while (!stack.empty()) { - // 从这里开始 userTaskKey 都还是上个栈的 key - // 是否是脏数据线路上的点 - final boolean[] isDirtyData = { false }; - for (Set oldDirtyDataLine : dirtyDataLineList) { - if (oldDirtyDataLine.contains(stack.peek().getActivityId())) { - isDirtyData[0] = true; - } - } - // 删除原因不为空,说明从这条数据开始回跳或者回退的 - // MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内 - if (stack.peek().getDeleteReason() != null && !stack.peek().getDeleteReason().equals("MI_END")) { - // 可以理解为脏线路起点 - String dirtyPoint = ""; - if (stack.peek().getDeleteReason().indexOf("Change activity to ") >= 0) { - dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", ""); - } - // 会签回退删除原因有点不同 - if (stack.peek().getDeleteReason().indexOf("Change parent activity to ") >= 0) { - dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", ""); - } - FlowElement dirtyTask = null; - // 获取变更节点的对应的入口处连线 - // 如果是网关并行回退情况,会变成两条脏数据路线,效果一样 - for (FlowElement flowElement : allElements) { - if (flowElement.getId().equals(stack.peek().getActivityId())) { - dirtyTask = flowElement; - } - } - // 获取脏数据线路 - Set dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, - Arrays.asList(dirtyPoint.split(",")), null); - // 自己本身也是脏线路上的点,加进去 - dirtyDataLine.add(stack.peek().getActivityId()); - logger.info(stack.peek().getActivityId() + "点脏路线集合:" + dirtyDataLine); - // 是全新的需要添加的脏线路 - boolean isNewDirtyData = true; - for (int i = 0; i < dirtyDataLineList.size(); i++) { - // 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回 - // 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全 - if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) { - isNewDirtyData = false; - dirtyDataLineList.get(i).addAll(dirtyDataLine); - } - } - // 已确定时全新的脏线路 - if (isNewDirtyData) { - // deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成 - // 按照逻辑,回退后立刻生成的实例记录就是回退的记录 - // 至于驳回所生成的 Key,直接从删除原因中获取,因为存在驳回到并行的情况 - deleteKeyList.add(dirtyPoint + ","); - dirtyDataLineList.add(dirtyDataLine); - } - // 添加后,现在这个点变成脏线路上的点了 - isDirtyData[0] = true; - } - // 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key - if (!isDirtyData[0]) { - lastHistoricTaskInstanceList.add(stack.peek().getActivityId()); - } - // 校验脏线路是否结束 - for (int i = 0; i < deleteKeyList.size(); i++) { - // 如果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始 - if (multiKey == null && multiTask.contains(stack.peek().getActivityId()) - && deleteKeyList.get(i).contains(stack.peek().getActivityId())) { - multiIndex = i; - multiKey = new StringBuilder(stack.peek().getActivityId()); - } - // 会签脏数据处理,节点退回会签清空 - // 如果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉 - if (multiKey != null && !multiKey.toString().equals(stack.peek().getActivityId())) { - deleteKeyList.set(multiIndex, - deleteKeyList.get(multiIndex).replace(stack.peek().getActivityId() + ",", "")); - multiKey = null; - // 结束进行下校验删除 - multiOpera = true; - } - // 其他脏数据处理 - // 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息 - // 脏数据产生的新实例中是否包含这条数据 - if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getActivityId())) { - // 删除匹配到的部分 - deleteKeyList.set(i, deleteKeyList.get(i).replace(stack.peek().getActivityId() + ",", "")); - } - // 如果每组中的元素都以匹配过,说明脏数据结束 - if ("".equals(deleteKeyList.get(i))) { - // 同时删除脏数据 - deleteKeyList.remove(i); - dirtyDataLineList.remove(i); - break; - } - } - // 会签数据处理需要在循环外处理,否则可能导致溢出 - // 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下 - if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) { - // 同时删除脏数据 - deleteKeyList.remove(multiIndex); - dirtyDataLineList.remove(multiIndex); - multiIndex = -1; - multiOpera = false; - } - // pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除 - // 保存新的 userTaskKey 在下个循环中使用 - userTaskKey = new StringBuilder(stack.pop().getActivityId()); - } - logger.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList); - return lastHistoricTaskInstanceList; - } + /** + * 历史节点数据清洗,清洗掉由于回滚导致的脏数据 + *

+ * 在流程回退、驳回等操作后,历史数据中会存在废弃的节点记录。 + * 该方法通过分析删除原因和节点关系,识别并过滤掉这些脏数据, + * 返回真实有效的历史节点记录。 + *

+ * 特别处理: + * - 会签节点的多实例情况 + * - 并行网关的多分支情况 + * - 连续驳回的情况 + * + * @param allElements 全部节点信息 + * @param historicActivityIdList 历史任务实例信息,数据采用开始时间升序 + * @return 清洗后的有效历史节点ID列表 + */ + public static List historicTaskInstanceClean(Collection allElements, + List historicActivityIdList) { + // 会签节点收集 + List multiTask = new ArrayList<>(); + allElements.forEach(flowElement -> { + if (flowElement instanceof UserTask) { + // 如果该节点的行为为会签行为,说明该节点为会签节点 + if (((UserTask) flowElement).getBehavior() instanceof ParallelMultiInstanceBehavior + || ((UserTask) flowElement).getBehavior() instanceof SequentialMultiInstanceBehavior) { + multiTask.add(flowElement.getId()); + } + } + }); + // 循环放入栈,栈 LIFO:后进先出 + Stack stack = new Stack<>(); + historicActivityIdList.forEach(item -> stack.push(item)); + // 清洗后的历史任务实例 + List lastHistoricTaskInstanceList = new ArrayList<>(); + // 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗 + // 临时用户任务 key + StringBuilder userTaskKey = null; + // 临时被删掉的任务 key,存在并行情况 + List deleteKeyList = new ArrayList<>(); + // 临时脏数据线路 + List> dirtyDataLineList = new ArrayList<>(); + // 由某个点跳到会签点,此时出现多个会签实例对应 1 个跳转情况,需要把这些连续脏数据都找到 + // 会签特殊处理下标 + int multiIndex = -1; + // 会签特殊处理 key + StringBuilder multiKey = null; + // 会签特殊处理操作标识 + boolean multiOpera = false; + while (!stack.empty()) { + // 从这里开始 userTaskKey 都还是上个栈的 key + // 是否是脏数据线路上的点 + final boolean[] isDirtyData = {false}; + for (Set oldDirtyDataLine : dirtyDataLineList) { + if (oldDirtyDataLine.contains(stack.peek().getActivityId())) { + isDirtyData[0] = true; + } + } + // 删除原因不为空,说明从这条数据开始回跳或者回退的 + // MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内 + if (stack.peek().getDeleteReason() != null && !stack.peek().getDeleteReason().equals("MI_END")) { + // 可以理解为脏线路起点 + String dirtyPoint = ""; + if (stack.peek().getDeleteReason().indexOf("Change activity to ") >= 0) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", ""); + } + // 会签回退删除原因有点不同 + if (stack.peek().getDeleteReason().indexOf("Change parent activity to ") >= 0) { + dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", ""); + } + FlowElement dirtyTask = null; + // 获取变更节点的对应的入口处连线 + // 如果是网关并行回退情况,会变成两条脏数据路线,效果一样 + for (FlowElement flowElement : allElements) { + if (flowElement.getId().equals(stack.peek().getActivityId())) { + dirtyTask = flowElement; + } + } + // 获取脏数据线路 + Set dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, + Arrays.asList(dirtyPoint.split(",")), null); + // 自己本身也是脏线路上的点,加进去 + dirtyDataLine.add(stack.peek().getActivityId()); + logger.info(stack.peek().getActivityId() + "点脏路线集合:" + dirtyDataLine); + // 是全新的需要添加的脏线路 + boolean isNewDirtyData = true; + for (int i = 0; i < dirtyDataLineList.size(); i++) { + // 如果发现他的上个节点在脏线路内,说明这个点可能是并行的节点,或者连续驳回 + // 这时,都以之前的脏线路节点为标准,只需合并脏线路即可,也就是路线补全 + if (dirtyDataLineList.get(i).contains(userTaskKey.toString())) { + isNewDirtyData = false; + dirtyDataLineList.get(i).addAll(dirtyDataLine); + } + } + // 已确定时全新的脏线路 + if (isNewDirtyData) { + // deleteKey 单一路线驳回到并行,这种同时生成多个新实例记录情况,这时 deleteKey 其实是由多个值组成 + // 按照逻辑,回退后立刻生成的实例记录就是回退的记录 + // 至于驳回所生成的 Key,直接从删除原因中获取,因为存在驳回到并行的情况 + deleteKeyList.add(dirtyPoint + ","); + dirtyDataLineList.add(dirtyDataLine); + } + // 添加后,现在这个点变成脏线路上的点了 + isDirtyData[0] = true; + } + // 如果不是脏线路上的点,说明是有效数据,添加历史实例 Key + if (!isDirtyData[0]) { + lastHistoricTaskInstanceList.add(stack.peek().getActivityId()); + } + // 校验脏线路是否结束 + for (int i = 0; i < deleteKeyList.size(); i++) { + // 如果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始 + if (multiKey == null && multiTask.contains(stack.peek().getActivityId()) + && deleteKeyList.get(i).contains(stack.peek().getActivityId())) { + multiIndex = i; + multiKey = new StringBuilder(stack.peek().getActivityId()); + } + // 会签脏数据处理,节点退回会签清空 + // 如果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉 + if (multiKey != null && !multiKey.toString().equals(stack.peek().getActivityId())) { + deleteKeyList.set(multiIndex, + deleteKeyList.get(multiIndex).replace(stack.peek().getActivityId() + ",", "")); + multiKey = null; + // 结束进行下校验删除 + multiOpera = true; + } + // 其他脏数据处理 + // 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息 + // 脏数据产生的新实例中是否包含这条数据 + if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getActivityId())) { + // 删除匹配到的部分 + deleteKeyList.set(i, deleteKeyList.get(i).replace(stack.peek().getActivityId() + ",", "")); + } + // 如果每组中的元素都以匹配过,说明脏数据结束 + if ("".equals(deleteKeyList.get(i))) { + // 同时删除脏数据 + deleteKeyList.remove(i); + dirtyDataLineList.remove(i); + break; + } + } + // 会签数据处理需要在循环外处理,否则可能导致溢出 + // 会签的数据肯定是之前放进去的所以理论上不会溢出,但还是校验下 + if (multiOpera && deleteKeyList.size() > multiIndex && "".equals(deleteKeyList.get(multiIndex))) { + // 同时删除脏数据 + deleteKeyList.remove(multiIndex); + dirtyDataLineList.remove(multiIndex); + multiIndex = -1; + multiOpera = false; + } + // pop() 方法与 peek() 方法不同,在返回值的同时,会把值从栈中移除 + // 保存新的 userTaskKey 在下个循环中使用 + userTaskKey = new StringBuilder(stack.pop().getActivityId()); + } + logger.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList); + return lastHistoricTaskInstanceList; + } + /** + * 获取流程参数Map,并设置发起人信息 + * + * @param flowBaseDto 流程基础数据传输对象 + * @return 包含发起人信息的流程参数Map + */ + public static Map getFlowParamMap(BizFlowBaseDto flowBaseDto) { + Map paramMap = flowBaseDto.getFlowParamMap(); + // 设置发起人 (当前登录用户) + Dict rootUser = Dict.create() + .set("id", SecurityUtils.getUser().getId()) + .set("name", SecurityUtils.getUser().getUsername()) + .set("type", NodeUserTypeEnum.USER.getKey()); + paramMap.put("root", CollUtil.newArrayList(rootUser)); + return paramMap; + } }