17 changed files with 1366 additions and 2 deletions
@ -0,0 +1,78 @@ |
|||
package com.qs.serve.modules.bpm.common.consts; |
|||
|
|||
/** |
|||
* 流程常量信息 |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
public class ProcessConstants { |
|||
|
|||
/** |
|||
* 动态数据 |
|||
*/ |
|||
public static final String DATA_TYPE = "dynamic"; |
|||
|
|||
/** |
|||
* 固定任务接收 |
|||
*/ |
|||
public static final String FIXED = "fixed"; |
|||
|
|||
/** |
|||
* 单个审批人 |
|||
*/ |
|||
public static final String USER_TYPE_ASSIGNEE = "assignee"; |
|||
|
|||
|
|||
/** |
|||
* 候选人 |
|||
*/ |
|||
public static final String USER_TYPE_USERS = "candidateUsers"; |
|||
|
|||
|
|||
/** |
|||
* 审批组 |
|||
*/ |
|||
public static final String USER_TYPE_ROUPS = "candidateGroups"; |
|||
|
|||
/** |
|||
* 单个审批人 |
|||
*/ |
|||
public static final String PROCESS_APPROVAL = "approval"; |
|||
|
|||
/** |
|||
* 会签人员 |
|||
*/ |
|||
public static final String PROCESS_MULTI_INSTANCE_USER = "userList"; |
|||
|
|||
/** |
|||
* nameapace |
|||
*/ |
|||
public static final String NAMASPASE = "http://flowable.org/bpmn"; |
|||
|
|||
/** |
|||
* 会签节点 |
|||
*/ |
|||
public static final String PROCESS_MULTI_INSTANCE = "multiInstance"; |
|||
|
|||
/** |
|||
* 自定义属性 dataType |
|||
*/ |
|||
public static final String PROCESS_CUSTOM_DATA_TYPE = "dataType"; |
|||
|
|||
/** |
|||
* 自定义属性 userType |
|||
*/ |
|||
public static final String PROCESS_CUSTOM_USER_TYPE = "userType"; |
|||
|
|||
/** |
|||
* 初始化人员 |
|||
*/ |
|||
public static final String PROCESS_INITIATOR = "INITIATOR"; |
|||
|
|||
|
|||
/** |
|||
* 流程跳过 |
|||
*/ |
|||
public static final String FLOWABLE_SKIP_EXPRESSION_ENABLED = "_FLOWABLE_SKIP_EXPRESSION_ENABLED"; |
|||
|
|||
} |
@ -0,0 +1,43 @@ |
|||
package com.qs.serve.modules.bpm.common.enums; |
|||
|
|||
/** |
|||
* 流程意见类型 |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
public enum FlowComment { |
|||
|
|||
/** |
|||
* 说明 |
|||
*/ |
|||
NORMAL("1", "正常意见"), |
|||
REBACK("2", "退回意见"), |
|||
REJECT("3", "驳回意见"), |
|||
DELEGATE("4", "委派意见"), |
|||
ASSIGN("5", "转办意见"), |
|||
STOP("6", "终止流程"); |
|||
|
|||
/** |
|||
* 类型 |
|||
*/ |
|||
private final String type; |
|||
|
|||
/** |
|||
* 说明 |
|||
*/ |
|||
private final String remark; |
|||
|
|||
FlowComment(String type, String remark) { |
|||
this.type = type; |
|||
this.remark = remark; |
|||
} |
|||
|
|||
public String getType() { |
|||
return type; |
|||
} |
|||
|
|||
public String getRemark() { |
|||
return remark; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,219 @@ |
|||
package com.qs.serve.modules.bpm.common.util; |
|||
|
|||
import com.googlecode.aviator.AviatorEvaluator; |
|||
import com.googlecode.aviator.Expression; |
|||
import org.flowable.bpmn.model.Process; |
|||
import org.flowable.bpmn.model.*; |
|||
import org.flowable.engine.RepositoryService; |
|||
import org.flowable.engine.repository.ProcessDefinition; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collection; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
public class FindNextNodeUtil { |
|||
|
|||
|
|||
/** |
|||
* 获取下一步骤的用户任务 |
|||
* @param repositoryService |
|||
* @param map |
|||
* @return |
|||
*/ |
|||
public static List<UserTask> getNextUserTasks(RepositoryService repositoryService, org.flowable.task.api.Task task, Map<String, Object> map) { |
|||
List<UserTask> data = new ArrayList<>(); |
|||
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult(); |
|||
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId()); |
|||
Process mainProcess = bpmnModel.getMainProcess(); |
|||
Collection<FlowElement> flowElements = mainProcess.getFlowElements(); |
|||
String key = task.getTaskDefinitionKey(); |
|||
FlowElement flowElement = bpmnModel.getFlowElement(key); |
|||
next(flowElements, flowElement, map, data); |
|||
return data; |
|||
} |
|||
|
|||
public static void next(Collection<FlowElement> flowElements, FlowElement flowElement, Map<String, Object> map, List<UserTask> nextUser) { |
|||
//如果是结束节点
|
|||
if (flowElement instanceof EndEvent) { |
|||
//如果是子任务的结束节点
|
|||
if (getSubProcess(flowElements, flowElement) != null) { |
|||
flowElement = getSubProcess(flowElements, flowElement); |
|||
} |
|||
} |
|||
//获取Task的出线信息--可以拥有多个
|
|||
List<SequenceFlow> outGoingFlows = null; |
|||
if (flowElement instanceof Task) { |
|||
outGoingFlows = ((Task) flowElement).getOutgoingFlows(); |
|||
} else if (flowElement instanceof Gateway) { |
|||
outGoingFlows = ((Gateway) flowElement).getOutgoingFlows(); |
|||
} else if (flowElement instanceof StartEvent) { |
|||
outGoingFlows = ((StartEvent) flowElement).getOutgoingFlows(); |
|||
} else if (flowElement instanceof SubProcess) { |
|||
outGoingFlows = ((SubProcess) flowElement).getOutgoingFlows(); |
|||
} else if (flowElement instanceof CallActivity) { |
|||
outGoingFlows = ((CallActivity) flowElement).getOutgoingFlows(); |
|||
} |
|||
if (outGoingFlows != null && outGoingFlows.size() > 0) { |
|||
//遍历所有的出线--找到可以正确执行的那一条
|
|||
for (SequenceFlow sequenceFlow : outGoingFlows) { |
|||
//1.有表达式,且为true
|
|||
//2.无表达式
|
|||
String expression = sequenceFlow.getConditionExpression(); |
|||
if (expression == null || |
|||
expressionResult(map, expression.substring(expression.lastIndexOf("{") + 1, expression.lastIndexOf("}")))) { |
|||
//出线的下一节点
|
|||
String nextFlowElementID = sequenceFlow.getTargetRef(); |
|||
if (checkSubProcess(nextFlowElementID, flowElements, nextUser)) { |
|||
continue; |
|||
} |
|||
|
|||
//查询下一节点的信息
|
|||
FlowElement nextFlowElement = getFlowElementById(nextFlowElementID, flowElements); |
|||
//调用流程
|
|||
if (nextFlowElement instanceof CallActivity) { |
|||
CallActivity ca = (CallActivity) nextFlowElement; |
|||
if (ca.getLoopCharacteristics() != null) { |
|||
UserTask userTask = new UserTask(); |
|||
userTask.setId(ca.getId()); |
|||
|
|||
userTask.setId(ca.getId()); |
|||
userTask.setLoopCharacteristics(ca.getLoopCharacteristics()); |
|||
userTask.setName(ca.getName()); |
|||
nextUser.add(userTask); |
|||
} |
|||
next(flowElements, nextFlowElement, map, nextUser); |
|||
} |
|||
//用户任务
|
|||
if (nextFlowElement instanceof UserTask) { |
|||
nextUser.add((UserTask) nextFlowElement); |
|||
} |
|||
//排他网关
|
|||
else if (nextFlowElement instanceof ExclusiveGateway) { |
|||
next(flowElements, nextFlowElement, map, nextUser); |
|||
} |
|||
//并行网关
|
|||
else if (nextFlowElement instanceof ParallelGateway) { |
|||
next(flowElements, nextFlowElement, map, nextUser); |
|||
} |
|||
//接收任务
|
|||
else if (nextFlowElement instanceof ReceiveTask) { |
|||
next(flowElements, nextFlowElement, map, nextUser); |
|||
} |
|||
//服务任务
|
|||
else if (nextFlowElement instanceof ServiceTask) { |
|||
next(flowElements, nextFlowElement, map, nextUser); |
|||
} |
|||
//子任务的起点
|
|||
else if (nextFlowElement instanceof StartEvent) { |
|||
next(flowElements, nextFlowElement, map, nextUser); |
|||
} |
|||
//结束节点
|
|||
else if (nextFlowElement instanceof EndEvent) { |
|||
next(flowElements, nextFlowElement, map, nextUser); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 判断是否是多实例子流程并且需要设置集合类型变量 |
|||
*/ |
|||
public static boolean checkSubProcess(String Id, Collection<FlowElement> flowElements, List<UserTask> nextUser) { |
|||
for (FlowElement flowElement1 : flowElements) { |
|||
if (flowElement1 instanceof SubProcess && flowElement1.getId().equals(Id)) { |
|||
|
|||
SubProcess sp = (SubProcess) flowElement1; |
|||
if (sp.getLoopCharacteristics() != null) { |
|||
String inputDataItem = sp.getLoopCharacteristics().getInputDataItem(); |
|||
UserTask userTask = new UserTask(); |
|||
userTask.setId(sp.getId()); |
|||
userTask.setLoopCharacteristics(sp.getLoopCharacteristics()); |
|||
userTask.setName(sp.getName()); |
|||
nextUser.add(userTask); |
|||
return true; |
|||
} |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
|
|||
} |
|||
|
|||
/** |
|||
* 查询一个节点的是否子任务中的节点,如果是,返回子任务 |
|||
* @param flowElements 全流程的节点集合 |
|||
* @param flowElement 当前节点 |
|||
* @return |
|||
*/ |
|||
public static FlowElement getSubProcess(Collection<FlowElement> flowElements, FlowElement flowElement) { |
|||
for (FlowElement flowElement1 : flowElements) { |
|||
if (flowElement1 instanceof SubProcess) { |
|||
for (FlowElement flowElement2 : ((SubProcess) flowElement1).getFlowElements()) { |
|||
if (flowElement.equals(flowElement2)) { |
|||
return flowElement1; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
/** |
|||
* 根据ID查询流程节点对象, 如果是子任务,则返回子任务的开始节点 |
|||
* @param Id 节点ID |
|||
* @param flowElements 流程节点集合 |
|||
* @return |
|||
*/ |
|||
public static FlowElement getFlowElementById(String Id, Collection<FlowElement> flowElements) { |
|||
for (FlowElement flowElement : flowElements) { |
|||
if (flowElement.getId().equals(Id)) { |
|||
//如果是子任务,则查询出子任务的开始节点
|
|||
if (flowElement instanceof SubProcess) { |
|||
return getStartFlowElement(((SubProcess) flowElement).getFlowElements()); |
|||
} |
|||
return flowElement; |
|||
} |
|||
if (flowElement instanceof SubProcess) { |
|||
FlowElement flowElement1 = getFlowElementById(Id, ((SubProcess) flowElement).getFlowElements()); |
|||
if (flowElement1 != null) { |
|||
return flowElement1; |
|||
} |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 返回流程的开始节点 |
|||
* @param flowElements 节点集合 |
|||
* @description: |
|||
*/ |
|||
public static FlowElement getStartFlowElement(Collection<FlowElement> flowElements) { |
|||
for (FlowElement flowElement : flowElements) { |
|||
if (flowElement instanceof StartEvent) { |
|||
return flowElement; |
|||
} |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 校验el表达式 |
|||
* @param map |
|||
* @param expression |
|||
* @return |
|||
*/ |
|||
public static boolean expressionResult(Map<String, Object> map, String expression) { |
|||
Expression exp = AviatorEvaluator.compile(expression); |
|||
final Object execute = exp.execute(map); |
|||
return Boolean.parseBoolean(String.valueOf(execute)); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,590 @@ |
|||
package com.qs.serve.modules.bpm.common.util; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.flowable.bpmn.model.*; |
|||
import org.flowable.engine.impl.bpmn.behavior.ParallelMultiInstanceBehavior; |
|||
import org.flowable.engine.impl.bpmn.behavior.SequentialMultiInstanceBehavior; |
|||
import org.flowable.task.api.history.HistoricTaskInstance; |
|||
|
|||
import java.util.*; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
@Slf4j |
|||
public class FlowableUtils { |
|||
|
|||
/** |
|||
* 根据节点,获取入口连线 |
|||
* @param source |
|||
* @return |
|||
*/ |
|||
public static List<SequenceFlow> getElementIncomingFlows(FlowElement source) { |
|||
List<SequenceFlow> sequenceFlows = null; |
|||
if (source instanceof FlowNode) { |
|||
sequenceFlows = ((FlowNode) 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 |
|||
*/ |
|||
public static List<SequenceFlow> getElementOutgoingFlows(FlowElement source) { |
|||
List<SequenceFlow> sequenceFlows = null; |
|||
if (source instanceof FlowNode) { |
|||
sequenceFlows = ((FlowNode) 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 |
|||
* @return |
|||
*/ |
|||
public static Collection<FlowElement> getAllElements(Collection<FlowElement> flowElements, Collection<FlowElement> 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; |
|||
} |
|||
|
|||
/** |
|||
* 迭代获取父级任务节点列表,向前找 |
|||
* @param source 起始节点 |
|||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 |
|||
* @param userTaskList 已找到的用户任务节点 |
|||
* @return |
|||
*/ |
|||
public static List<UserTask> iteratorFindParentUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> 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); |
|||
} |
|||
|
|||
// 根据类型,获取入口连线
|
|||
List<SequenceFlow> 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<UserTask> childUserTaskList = findChildProcessUserTasks((StartEvent) ((SubProcess) sequenceFlow.getSourceFlowElement()).getFlowElements().toArray()[0], null, null); |
|||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
|||
if (childUserTaskList != null && childUserTaskList.size() > 0) { |
|||
userTaskList.addAll(childUserTaskList); |
|||
continue; |
|||
} |
|||
} |
|||
// 继续迭代
|
|||
userTaskList = iteratorFindParentUserTasks(sequenceFlow.getSourceFlowElement(), hasSequenceFlow, userTaskList); |
|||
} |
|||
} |
|||
return userTaskList; |
|||
} |
|||
|
|||
/** |
|||
* 根据正在运行的任务节点,迭代获取子级任务节点列表,向后找 |
|||
* @param source 起始节点(退回节点) |
|||
* @param runTaskKeyList 正在运行的任务 Key,用于校验任务节点是否是正在运行的节点 |
|||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 |
|||
* @param userTaskList 需要撤回的用户任务列表 |
|||
* @return |
|||
*/ |
|||
public static List<UserTask> iteratorFindChildUserTasks(FlowElement source, List<String> runTaskKeyList, Set<String> hasSequenceFlow, List<UserTask> userTaskList) { |
|||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; |
|||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; |
|||
|
|||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
|||
if (source instanceof EndEvent && source.getSubProcess() != null) { |
|||
userTaskList = iteratorFindChildUserTasks(source.getSubProcess(), runTaskKeyList, hasSequenceFlow, userTaskList); |
|||
} |
|||
|
|||
// 根据类型,获取出口连线
|
|||
List<SequenceFlow> 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 && runTaskKeyList.contains((sequenceFlow.getTargetFlowElement()).getId())) { |
|||
userTaskList.add((UserTask) sequenceFlow.getTargetFlowElement()); |
|||
continue; |
|||
} |
|||
// 如果节点为子流程节点情况,则从节点中的第一个节点开始获取
|
|||
if (sequenceFlow.getTargetFlowElement() instanceof SubProcess) { |
|||
List<UserTask> childUserTaskList = iteratorFindChildUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), runTaskKeyList, hasSequenceFlow, null); |
|||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
|||
if (childUserTaskList != null && childUserTaskList.size() > 0) { |
|||
userTaskList.addAll(childUserTaskList); |
|||
continue; |
|||
} |
|||
} |
|||
// 继续迭代
|
|||
userTaskList = iteratorFindChildUserTasks(sequenceFlow.getTargetFlowElement(), runTaskKeyList, hasSequenceFlow, userTaskList); |
|||
} |
|||
} |
|||
return userTaskList; |
|||
} |
|||
|
|||
/** |
|||
* 迭代获取子流程用户任务节点 |
|||
* @param source 起始节点 |
|||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 |
|||
* @param userTaskList 需要撤回的用户任务列表 |
|||
* @return |
|||
*/ |
|||
public static List<UserTask> findChildProcessUserTasks(FlowElement source, Set<String> hasSequenceFlow, List<UserTask> userTaskList) { |
|||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; |
|||
userTaskList = userTaskList == null ? new ArrayList<>() : userTaskList; |
|||
|
|||
// 根据类型,获取出口连线
|
|||
List<SequenceFlow> 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<UserTask> childUserTaskList = findChildProcessUserTasks((FlowElement) (((SubProcess) sequenceFlow.getTargetFlowElement()).getFlowElements().toArray()[0]), hasSequenceFlow, null); |
|||
// 如果找到节点,则说明该线路找到节点,不继续向下找,反之继续
|
|||
if (childUserTaskList != null && childUserTaskList.size() > 0) { |
|||
userTaskList.addAll(childUserTaskList); |
|||
continue; |
|||
} |
|||
} |
|||
// 继续迭代
|
|||
userTaskList = findChildProcessUserTasks(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, userTaskList); |
|||
} |
|||
} |
|||
return userTaskList; |
|||
} |
|||
|
|||
/** |
|||
* 从后向前寻路,获取所有脏线路上的点 |
|||
* @param source 起始节点 |
|||
* @param passRoads 已经经过的点集合 |
|||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 |
|||
* @param targets 目标脏线路终点 |
|||
* @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 |
|||
* @return |
|||
*/ |
|||
public static Set<String> iteratorFindDirtyRoads(FlowElement source, List<String> passRoads, Set<String> hasSequenceFlow, List<String> targets, Set<String> 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); |
|||
} |
|||
|
|||
// 根据类型,获取入口连线
|
|||
List<SequenceFlow> 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; |
|||
} |
|||
} |
|||
// 继续迭代
|
|||
dirtyRoads = iteratorFindDirtyRoads(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, targets, dirtyRoads); |
|||
} |
|||
} |
|||
return dirtyRoads; |
|||
} |
|||
|
|||
/** |
|||
* 迭代获取子流程脏路线 |
|||
* 说明,假如回退的点就是子流程,那么也肯定会回退到子流程最初的用户任务节点,因此子流程中的节点全是脏路线 |
|||
* @param source 起始节点 |
|||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 |
|||
* @param dirtyRoads 确定为脏数据的点,因为不需要重复,因此使用 set 存储 |
|||
* @return |
|||
*/ |
|||
public static Set<String> findChildProcessAllDirtyRoad(FlowElement source, Set<String> hasSequenceFlow, Set<String> dirtyRoads) { |
|||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; |
|||
dirtyRoads = dirtyRoads == null ? new HashSet<>() : dirtyRoads; |
|||
|
|||
// 根据类型,获取出口连线
|
|||
List<SequenceFlow> 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); |
|||
} |
|||
// 继续迭代
|
|||
dirtyRoads = findChildProcessAllDirtyRoad(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, dirtyRoads); |
|||
} |
|||
} |
|||
return dirtyRoads; |
|||
} |
|||
|
|||
/** |
|||
* 判断脏路线结束节点是否在子流程上 |
|||
* @param source 起始节点 |
|||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 |
|||
* @param targets 判断脏路线节点是否存在子流程上,只要存在一个,说明脏路线只到子流程为止 |
|||
* @param inChildProcess 是否存在子流程上,true 是,false 否 |
|||
* @return |
|||
*/ |
|||
public static Boolean dirtyTargetInChildProcess(FlowElement source, Set<String> hasSequenceFlow, List<String> targets, Boolean inChildProcess) { |
|||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; |
|||
inChildProcess = inChildProcess != null && inChildProcess; |
|||
|
|||
// 根据类型,获取出口连线
|
|||
List<SequenceFlow> 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); |
|||
} |
|||
// 继续迭代
|
|||
inChildProcess = dirtyTargetInChildProcess(sequenceFlow.getTargetFlowElement(), hasSequenceFlow, targets, inChildProcess); |
|||
} |
|||
} |
|||
return inChildProcess; |
|||
} |
|||
|
|||
/** |
|||
* 迭代从后向前扫描,判断目标节点相对于当前节点是否是串行 |
|||
* 不存在直接回退到子流程中的情况,但存在从子流程出去到父流程情况 |
|||
* @param source 起始节点 |
|||
* @param isSequential 是否串行 |
|||
* @param hasSequenceFlow 已经经过的连线的 ID,用于判断线路是否重复 |
|||
* @param targetKsy 目标节点 |
|||
* @return |
|||
*/ |
|||
public static Boolean iteratorCheckSequentialReferTarget(FlowElement source, String targetKsy, Set<String> hasSequenceFlow, Boolean isSequential) { |
|||
isSequential = isSequential == null || isSequential; |
|||
hasSequenceFlow = hasSequenceFlow == null ? new HashSet<>() : hasSequenceFlow; |
|||
|
|||
// 如果该节点为开始节点,且存在上级子节点,则顺着上级子节点继续迭代
|
|||
if (source instanceof StartEvent && source.getSubProcess() != null) { |
|||
isSequential = iteratorCheckSequentialReferTarget(source.getSubProcess(), targetKsy, hasSequenceFlow, isSequential); |
|||
} |
|||
|
|||
// 根据类型,获取入口连线
|
|||
List<SequenceFlow> sequenceFlows = getElementIncomingFlows(source); |
|||
|
|||
if (sequenceFlows != null) { |
|||
// 循环找到目标元素
|
|||
for (SequenceFlow sequenceFlow: sequenceFlows) { |
|||
// 如果发现连线重复,说明循环了,跳过这个循环
|
|||
if (hasSequenceFlow.contains(sequenceFlow.getId())) { |
|||
continue; |
|||
} |
|||
// 添加已经走过的连线
|
|||
hasSequenceFlow.add(sequenceFlow.getId()); |
|||
// 如果目标节点已被判断为并行,后面都不需要执行,直接返回
|
|||
if (!isSequential) { |
|||
break; |
|||
} |
|||
// 这条线路存在目标节点,这条线路完成,进入下个线路
|
|||
if (targetKsy.equals(sequenceFlow.getSourceFlowElement().getId())) { |
|||
continue; |
|||
} |
|||
if (sequenceFlow.getSourceFlowElement() instanceof StartEvent) { |
|||
isSequential = false; |
|||
break; |
|||
} |
|||
// 否则就继续迭代
|
|||
isSequential = iteratorCheckSequentialReferTarget(sequenceFlow.getSourceFlowElement(), targetKsy, hasSequenceFlow, isSequential); |
|||
} |
|||
} |
|||
return isSequential; |
|||
} |
|||
|
|||
/** |
|||
* 从后向前寻路,获取到达节点的所有路线 |
|||
* 不存在直接回退到子流程,但是存在回退到父级流程的情况 |
|||
* @param source 起始节点 |
|||
* @param passRoads 已经经过的点集合 |
|||
* @param roads 路线 |
|||
* @return |
|||
*/ |
|||
public static List<List<UserTask>> findRoad(FlowElement source, List<UserTask> passRoads, Set<String> hasSequenceFlow, List<List<UserTask>> 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); |
|||
} |
|||
|
|||
// 根据类型,获取入口连线
|
|||
List<SequenceFlow> 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()); |
|||
} |
|||
// 继续迭代
|
|||
roads = findRoad(sequenceFlow.getSourceFlowElement(), passRoads, hasSequenceFlow, roads); |
|||
} |
|||
} else { |
|||
// 添加路线
|
|||
roads.add(passRoads); |
|||
} |
|||
return roads; |
|||
} |
|||
|
|||
/** |
|||
* 历史节点数据清洗,清洗掉又回滚导致的脏数据 |
|||
* @param allElements 全部节点信息 |
|||
* @param historicTaskInstanceList 历史任务实例信息,数据采用开始时间升序 |
|||
* @return |
|||
*/ |
|||
public static List<String> historicTaskInstanceClean(Collection<FlowElement> allElements, List<HistoricTaskInstance> historicTaskInstanceList) { |
|||
// 会签节点收集
|
|||
List<String> 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<HistoricTaskInstance> stack = new Stack<>(); |
|||
historicTaskInstanceList.forEach(stack::push); |
|||
// 清洗后的历史任务实例
|
|||
List<String> lastHistoricTaskInstanceList = new ArrayList<>(); |
|||
// 网关存在可能只走了部分分支情况,且还存在跳转废弃数据以及其他分支数据的干扰,因此需要对历史节点数据进行清洗
|
|||
// 临时用户任务 key
|
|||
StringBuilder userTaskKey = null; |
|||
// 临时被删掉的任务 key,存在并行情况
|
|||
List<String> deleteKeyList = new ArrayList<>(); |
|||
// 临时脏数据线路
|
|||
List<Set<String>> 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<String> oldDirtyDataLine : dirtyDataLineList) { |
|||
if (oldDirtyDataLine.contains(stack.peek().getTaskDefinitionKey())) { |
|||
isDirtyData[0] = true; |
|||
} |
|||
} |
|||
// 删除原因不为空,说明从这条数据开始回跳或者回退的
|
|||
// MI_END:会签完成后,其他未签到节点的删除原因,不在处理范围内
|
|||
if (stack.peek().getDeleteReason() != null && !"MI_END".equals(stack.peek().getDeleteReason())) { |
|||
// 可以理解为脏线路起点
|
|||
String dirtyPoint = ""; |
|||
if (stack.peek().getDeleteReason().contains("Change activity to ")) { |
|||
dirtyPoint = stack.peek().getDeleteReason().replace("Change activity to ", ""); |
|||
} |
|||
// 会签回退删除原因有点不同
|
|||
if (stack.peek().getDeleteReason().contains("Change parent activity to ")) { |
|||
dirtyPoint = stack.peek().getDeleteReason().replace("Change parent activity to ", ""); |
|||
} |
|||
FlowElement dirtyTask = null; |
|||
// 获取变更节点的对应的入口处连线
|
|||
// 如果是网关并行回退情况,会变成两条脏数据路线,效果一样
|
|||
for (FlowElement flowElement : allElements) { |
|||
if (flowElement.getId().equals(stack.peek().getTaskDefinitionKey())) { |
|||
dirtyTask = flowElement; |
|||
} |
|||
} |
|||
// 获取脏数据线路
|
|||
Set<String> dirtyDataLine = FlowableUtils.iteratorFindDirtyRoads(dirtyTask, null, null, Arrays.asList(dirtyPoint.split(",")), null); |
|||
// 自己本身也是脏线路上的点,加进去
|
|||
dirtyDataLine.add(stack.peek().getTaskDefinitionKey()); |
|||
log.info(stack.peek().getTaskDefinitionKey() + "点脏路线集合:" + 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().getTaskDefinitionKey()); |
|||
} |
|||
// 校验脏线路是否结束
|
|||
for (int i = 0; i < deleteKeyList.size(); i ++) { |
|||
// 如果发现脏数据属于会签,记录下下标与对应 Key,以备后续比对,会签脏数据范畴开始
|
|||
if (multiKey == null && multiTask.contains(stack.peek().getTaskDefinitionKey()) |
|||
&& deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { |
|||
multiIndex = i; |
|||
multiKey = new StringBuilder(stack.peek().getTaskDefinitionKey()); |
|||
} |
|||
// 会签脏数据处理,节点退回会签清空
|
|||
// 如果在会签脏数据范畴中发现 Key改变,说明会签脏数据在上个节点就结束了,可以把会签脏数据删掉
|
|||
if (multiKey != null && !multiKey.toString().equals(stack.peek().getTaskDefinitionKey())) { |
|||
deleteKeyList.set(multiIndex , deleteKeyList.get(multiIndex).replace(stack.peek().getTaskDefinitionKey() + ",", "")); |
|||
multiKey = null; |
|||
// 结束进行下校验删除
|
|||
multiOpera = true; |
|||
} |
|||
// 其他脏数据处理
|
|||
// 发现该路线最后一条脏数据,说明这条脏数据线路处理完了,删除脏数据信息
|
|||
// 脏数据产生的新实例中是否包含这条数据
|
|||
if (multiKey == null && deleteKeyList.get(i).contains(stack.peek().getTaskDefinitionKey())) { |
|||
// 删除匹配到的部分
|
|||
deleteKeyList.set(i , deleteKeyList.get(i).replace(stack.peek().getTaskDefinitionKey() + ",", "")); |
|||
} |
|||
// 如果每组中的元素都以匹配过,说明脏数据结束
|
|||
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().getTaskDefinitionKey()); |
|||
} |
|||
log.info("清洗后的历史节点数据:" + lastHistoricTaskInstanceList); |
|||
return lastHistoricTaskInstanceList; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,43 @@ |
|||
package com.qs.serve.modules.bpm.conf; |
|||
|
|||
import lombok.Getter; |
|||
import org.flowable.engine.*; |
|||
import org.springframework.beans.factory.annotation.Qualifier; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
import javax.annotation.Resource; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
@Component |
|||
@Getter |
|||
public class FlowServiceFactory { |
|||
|
|||
@Resource |
|||
protected RepositoryService repositoryService; |
|||
|
|||
@Resource |
|||
protected RuntimeService runtimeService; |
|||
|
|||
@Resource |
|||
protected IdentityService identityService; |
|||
|
|||
@Resource |
|||
protected TaskService taskService; |
|||
|
|||
@Resource |
|||
protected FormService formService; |
|||
|
|||
@Resource |
|||
protected HistoryService historyService; |
|||
|
|||
@Resource |
|||
protected ManagementService managementService; |
|||
|
|||
@Qualifier("processEngine") |
|||
@Resource |
|||
protected ProcessEngine processEngine; |
|||
|
|||
} |
@ -0,0 +1,19 @@ |
|||
package com.qs.serve.modules.bpm.conf; |
|||
|
|||
import org.flowable.spring.SpringProcessEngineConfiguration; |
|||
import org.flowable.spring.boot.EngineConfigurationConfigurer; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
public class FlowableConfig implements EngineConfigurationConfigurer<SpringProcessEngineConfiguration> { |
|||
|
|||
@Override |
|||
public void configure(SpringProcessEngineConfiguration engineConfiguration) { |
|||
engineConfiguration.setActivityFontName("宋体"); |
|||
engineConfiguration.setLabelFontName("宋体"); |
|||
engineConfiguration.setAnnotationFontName("宋体"); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,18 @@ |
|||
package com.qs.serve.modules.bpm.conf.listener; |
|||
|
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.flowable.engine.delegate.TaskListener; |
|||
import org.flowable.task.service.delegate.DelegateTask; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
@Slf4j |
|||
public class UserTaskListener implements TaskListener { |
|||
|
|||
@Override |
|||
public void notify(DelegateTask delegateTask) { |
|||
log.debug("UserTaskListener--id:{}",delegateTask.getId()); |
|||
} |
|||
} |
@ -0,0 +1,26 @@ |
|||
package com.qs.serve.modules.bpm.domian.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/8/15 |
|||
*/ |
|||
@Data |
|||
public class BpmnDto { |
|||
|
|||
private static class StartEvent{ |
|||
private String id; |
|||
private String name; |
|||
} |
|||
private static class EndEvent{ |
|||
private String id; |
|||
private String name; |
|||
} |
|||
private static class ServiceTask{ |
|||
private String id; |
|||
private String name; |
|||
private String activitiClass; |
|||
} |
|||
|
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.qs.serve.modules.bpm.domian.dto; |
|||
|
|||
import lombok.Builder; |
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
@Data |
|||
@Builder |
|||
public class FlowCommentDto implements Serializable { |
|||
/** |
|||
* 意见类别 0 正常意见 1 退回意见 2 驳回意见 |
|||
*/ |
|||
private String type; |
|||
|
|||
/** |
|||
* 意见内容 |
|||
*/ |
|||
private String comment; |
|||
} |
@ -0,0 +1,12 @@ |
|||
package com.qs.serve.modules.bpm.domian.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
@Data |
|||
public class FlowFromFieldDTO { |
|||
private Object fields; |
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.qs.serve.modules.bpm.domian.dto; |
|||
|
|||
import com.qs.serve.modules.sys.entity.SysRole; |
|||
import com.qs.serve.modules.sys.entity.SysUser; |
|||
|
|||
import java.io.Serializable; |
|||
import java.util.List; |
|||
|
|||
/** |
|||
* 动态人员、组 |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
public class FlowNextDto implements Serializable { |
|||
|
|||
private String type; |
|||
|
|||
private String vars; |
|||
|
|||
private List<SysUser> userList; |
|||
|
|||
private List<SysRole> roleList; |
|||
|
|||
} |
@ -0,0 +1,143 @@ |
|||
package com.qs.serve.modules.bpm.domian.dto; |
|||
|
|||
import com.fasterxml.jackson.annotation.JsonFormat; |
|||
import lombok.Data; |
|||
|
|||
import java.util.Date; |
|||
|
|||
/** |
|||
* 工作流任务返回参数 |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
@Data |
|||
public class FlowTaskDto { |
|||
|
|||
/** |
|||
* 任务编号 |
|||
*/ |
|||
private String taskId; |
|||
|
|||
/** |
|||
* 任务执行编号 |
|||
*/ |
|||
private String executionId; |
|||
|
|||
/** |
|||
* 任务名称 |
|||
*/ |
|||
private String taskName; |
|||
|
|||
/** |
|||
* 任务Key |
|||
*/ |
|||
private String taskDefKey; |
|||
|
|||
/** |
|||
* 任务执行人Id |
|||
*/ |
|||
private Long assigneeId; |
|||
|
|||
/** |
|||
* 部门名称 |
|||
*/ |
|||
private String deptName; |
|||
|
|||
/** |
|||
* 流程发起人部门名称 |
|||
*/ |
|||
private String startDeptName; |
|||
|
|||
/** |
|||
* 任务执行人名称 |
|||
*/ |
|||
private String assigneeName; |
|||
|
|||
/** |
|||
* 流程发起人Id |
|||
*/ |
|||
private String startUserId; |
|||
|
|||
/** |
|||
* 流程发起人名称 |
|||
*/ |
|||
private String startUserName; |
|||
|
|||
/** |
|||
* 流程类型 |
|||
*/ |
|||
private String category; |
|||
|
|||
/** |
|||
* 流程变量信息 |
|||
*/ |
|||
private Object procVars; |
|||
|
|||
/** |
|||
* 局部变量信息 |
|||
*/ |
|||
private Object taskLocalVars; |
|||
|
|||
/** |
|||
* 流程部署编号 |
|||
*/ |
|||
private String deployId; |
|||
|
|||
/** |
|||
* 流程ID |
|||
*/ |
|||
private String procDefId; |
|||
|
|||
/** |
|||
* 流程key |
|||
*/ |
|||
private String procDefKey; |
|||
|
|||
/** |
|||
* 流程定义名称 |
|||
*/ |
|||
private String procDefName; |
|||
|
|||
/** |
|||
* 流程定义内置使用版本 |
|||
*/ |
|||
private int procDefVersion; |
|||
|
|||
/** |
|||
* 流程实例ID |
|||
*/ |
|||
private String procInsId; |
|||
|
|||
/** |
|||
* 历史流程实例ID |
|||
*/ |
|||
private String hisProcInsId; |
|||
|
|||
/** |
|||
* 任务耗时 |
|||
*/ |
|||
private String duration; |
|||
|
|||
/** |
|||
* 任务意见 |
|||
*/ |
|||
private FlowCommentDto comment; |
|||
|
|||
/** |
|||
* 候选执行人 |
|||
*/ |
|||
private String candidate; |
|||
|
|||
/** |
|||
* 任务创建时间 |
|||
*/ |
|||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
|||
private Date createTime; |
|||
|
|||
/** |
|||
* 任务完成时间 |
|||
*/ |
|||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") |
|||
private Date finishTime; |
|||
|
|||
} |
@ -0,0 +1,24 @@ |
|||
package com.qs.serve.modules.bpm.domian.dto; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.io.Serializable; |
|||
|
|||
/** |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
@Data |
|||
public class FlowViewerDto implements Serializable { |
|||
|
|||
/** |
|||
* 流程key |
|||
*/ |
|||
private String key; |
|||
|
|||
/** |
|||
* 是否完成(已经审批) |
|||
*/ |
|||
private boolean completed; |
|||
|
|||
} |
@ -0,0 +1,60 @@ |
|||
package com.qs.serve.modules.bpm.domian.vo; |
|||
|
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* 工作流任务 请求参数 |
|||
* @author YenHex |
|||
* @since 2022/8/18 |
|||
*/ |
|||
@Data |
|||
public class FlowTaskVo { |
|||
|
|||
/** |
|||
* 任务Id |
|||
*/ |
|||
private String taskId; |
|||
|
|||
/** |
|||
* 用户Id |
|||
*/ |
|||
private String userId; |
|||
|
|||
/** |
|||
* 任务意见 |
|||
*/ |
|||
private String comment; |
|||
|
|||
/** |
|||
* 流程实例Id |
|||
*/ |
|||
private String instanceId; |
|||
|
|||
/** |
|||
* 节点 |
|||
*/ |
|||
private String targetKey; |
|||
|
|||
/** |
|||
* 流程变量信息 |
|||
*/ |
|||
private Map<String, Object> values; |
|||
|
|||
/** |
|||
* 审批人 |
|||
*/ |
|||
private String assignee; |
|||
|
|||
/** |
|||
* 候选人 |
|||
*/ |
|||
private List<String> candidateUsers; |
|||
|
|||
/** |
|||
* 审批组 |
|||
*/ |
|||
private List<String> candidateGroups; |
|||
} |
Loading…
Reference in new issue