activitiy学习 动态加签,动态流程图,指定节点跳转,指定多人节点跳转
Demo: https://gitee.com/makino_a_mu_silk/activitiydemo.git
POM.XML
<properties>
<java.version>1.8</java.version>
<activiti.version>7.0.0.SR1</activiti.version>
<mysql.version>8.0.22</mysql.version>
<slf4j.version>2.0.5</slf4j.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web-services</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>${activiti.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 模型处理 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 转换 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn json数据转换 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- bpmn 布局 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-layout</artifactId>
<version>${activiti.version}</version>
</dependency>
<!-- mysql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!-- 链接池 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-image-generator</artifactId>
<version>${activiti.version}</version>
</dependency>
<!--mybatis-plus(springboot版)-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
</dependencies>
部署流程
@Test
public void testDeployment(){
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、得到RepositoryService实例
RepositoryService repositoryService = processEngine.getRepositoryService();
// 3、使用RepositoryService进行部署
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("bpmn/test.bpmn") // 添加bpmn资源
//png资源命名是有规范的。Leave.myLeave.png|jpg|gif|svg 或者Leave.png|jpg|gif|svg
.addClasspathResource("bpmn/test.png") // 添加png资源
.name("请假申请流程")
.deploy();
// 4、输出部署信息
System.out.println("流程部署id:" + deployment.getId());
System.out.println("流程部署名称:" + deployment.getName());
}
启动流程
@GetMapping("/startTask")
// @ApiOperation("启动任务:目前只有test;helloword;")
public Object startTask(String taskName) {
return service.startTask(taskName);
}
@Override
public Object startTask(String taskName) {
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 2、获取RunTimeService
RuntimeService runtimeService = processEngine.getRuntimeService();
// 3、根据流程定义Id启动流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(taskName);
// 输出内容
System.out.println("流程定义id:" + processInstance.getProcessDefinitionId());
System.out.println("流程实例id:" + processInstance.getId());
System.out.println("当前活动Id:" + processInstance.getActivityId());
final ProcessInstanceVo processInstanceVo = new ProcessInstanceVo();
processInstanceVo.setProcessDefinitionId(processInstance.getProcessDefinitionId());
processInstanceVo.setId(processInstance.getId());
processInstanceVo.setActivityId(processInstance.getActivityId());
return processInstanceVo;
}
查询待执行任务
@GetMapping("/getTask")
// @ApiOperation("查询人员待执行任务")
public Object getTask(String assignee) {
return service.getTask(assignee);
}
@Override
public Object getTask(String assignee) {
// 任务负责人
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 创建TaskService
TaskService taskService = processEngine.getTaskService();
// 根据流程key 和 任务负责人 查询任务
List<Task> list = taskService.createTaskQuery()
// .processDefinitionKey("test") //流程Key
.taskAssignee(assignee)//只查询该任务负责人的任务
.list();
for (Task task : list) {
System.out.println("----------------------------");
System.out.println("流程实例id:" + task.getProcessInstanceId());
System.out.println("任务id:" + task.getId());
System.out.println("任务负责人:" + task.getAssignee());
System.out.println("任务名称:" + task.getName());
}
final List<TaskVo> data = list.stream().map(item -> {
TaskVo vo = new TaskVo();
vo.setProcessInstanceId(item.getProcessInstanceId());
vo.setId(item.getId());
vo.setAssignee(item.getAssignee());
vo.setName(item.getName());
return vo;
}).collect(Collectors.toList());
return JSONObject.toJSONString(data, true);
}
查询任务历史
@GetMapping("/getTaskHistory")
// @ApiOperation("查询任务历史")
public Object getTaskHistory(String proInsId) {
return service.getTaskHistory(proInsId);
}
@Override
public Object getTaskHistory(String proInsId) {
// 获取引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取HistoryService
HistoryService historyService = processEngine.getHistoryService();
// 获取 actinst表的查询对象
HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
// 查询 actinst表,条件:根据 InstanceId 查询,查询一个流程的所有历史信息
instanceQuery.processInstanceId(proInsId);
// 查询 actinst表,条件:根据 DefinitionId 查询,查询一种流程的所有历史信息
// instanceQuery.processDefinitionId("myLeave:1:22504");
// 增加排序操作,orderByHistoricActivityInstanceStartTime 根据开始时间排序 asc 升序
instanceQuery.orderByHistoricActivityInstanceStartTime().asc();
// 查询所有内容
List<HistoricActivityInstance> activityInstanceList = instanceQuery.list();
// 输出
for (HistoricActivityInstance hi : activityInstanceList) {
if (hi.getActivityName() != null) {
System.out.println(hi.getActivityId());
System.out.println(hi.getActivityName());
System.out.println(hi.getProcessDefinitionId());
System.out.println(hi.getProcessInstanceId());
System.out.println("<==========================>");
}
}
final List<HistoricActivityInstanceVo> data = activityInstanceList.stream().filter(item -> item.getActivityName() != null)
.map(item -> {
HistoricActivityInstanceVo vo = new HistoricActivityInstanceVo();
vo.setActivityId(item.getActivityId());
vo.setActivityName(item.getActivityName());
vo.setProcessDefinitionId(item.getProcessDefinitionId());
vo.setProcessInstanceId(item.getProcessInstanceId());
return vo;
}).collect(Collectors.toList());
return JSONObject.toJSONString(data, true);
}
完成任务
@GetMapping("/completeTask")
// @ApiOperation("完成任务")
public Object completeTask(String taskId, String assignee) {
return service.completeTask(taskId, assignee);
}
@Override
public Object completeTask(String taskId, String assignee) {
// 流程定义的Key
// String key = "test";
// 任务负责人
// String assingee = "张三";
// String assingee = "李四";
// String assingee = "王五";
// String assingee = "张财务";
// 获取流程引擎
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 获取taskservice
TaskService taskService = processEngine.getTaskService();
// 查询任务
Task task = taskService.createTaskQuery()
.taskId(taskId)
.taskAssignee(assignee)
.singleResult();
if(task != null){
// 根据任务id来 完成任务
taskService.complete(task.getId());
}
return "成功";
}
多人会签节点的退回指定节点
@GetMapping("/rollBackToAppointNode")
// @ApiOperation("多人会签节点的退回指定节点")
public Object returnAppointNode(String taskId, String assignee) {
return service.returnAppointNode(taskId, assignee);
}
@Override
public Object returnAppointNode(String taskId, String assignee) {
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
final TaskService taskService = processEngine.getTaskService();
final HistoryService historyService = processEngine.getHistoryService();
final RepositoryService repositoryService = processEngine.getRepositoryService();
final RuntimeService runtimeService = processEngine.getRuntimeService();
Task task1 = taskService.createTaskQuery()
.taskId(taskId)
.taskAssignee(assignee)
.singleResult();
String processInstanceId = task1.getProcessInstanceId();
String nowUserId = assignee;
String tarActivityId = "Task_0wj3769";
//获取待执行的任务节点
List<Task> task = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
if(ObjectUtils.isEmpty(task)){
// throw new Exception("sorry,the process is not started or has finished, cannot be withdrawn");
System.out.println("sorry,the process is not started or has finished, cannot be withdrawn");
}
//找到当前运行的节点
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
String processDefinitionId = processInstance.getProcessDefinitionId();
//获取流程模型
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
//想要回退到的节点位置
System.out.println(tarActivityId);
FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(tarActivityId);
//原本的活动方向
Execution execution = runtimeService.createExecutionQuery().executionId(task.get(0).getExecutionId()).singleResult();
String activityId = execution.getActivityId();
FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
//记录原活动方向
List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
oriSequenceFlows.addAll(flowNode.getOutgoingFlows());
//清理活动方向
flowNode.getOutgoingFlows().clear();
//建立新方向
List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlowId"+" ROLLBACK: "+ LocalDateTime.now());
//新方向的源头---当前节点
newSequenceFlow.setSourceFlowElement(flowNode);
System.out.println("--------------new flow-----------------"+flowNode);
//新方向的目标---要回退的节点
System.out.println("--------------target flow-----------------"+myFlowNode);
newSequenceFlow.setTargetFlowElement(myFlowNode);
newSequenceFlowList.add(newSequenceFlow);
flowNode.setOutgoingFlows(newSequenceFlowList);
Authentication.setAuthenticatedUserId(nowUserId);
for (int i = 0; i < task.size(); i++) {
// TODO 多人会签节点退回 所有节点回退即可
// taskService.deleteTask(task.get(i).getId(), "回退", Boolean.TRUE);
taskService.addComment(task.get(i).getId(), task.get(i).getProcessInstanceId(), "回退");
//完成任务
System.out.println("========================完成任务====================");
taskService.complete(task.get(i).getId());
}
//恢复原方向
flowNode.setOutgoingFlows(oriSequenceFlows);
System.out.println("------------------RollBack successfully!!----------------------------");
return "成功";
}
退回到指定多人会签节点
@GetMapping("/rollBackToAppointNode1")
// @ApiOperation("退回到指定多人会签节点")
public Object returnAppointNode1(String taskId, String assignee) {
return service.returnAppointNode1(taskId, assignee);
}
@Override
public Object returnAppointNode1(String taskId, String assignee) {
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
final TaskService taskService = processEngine.getTaskService();
final HistoryService historyService = processEngine.getHistoryService();
final RepositoryService repositoryService = processEngine.getRepositoryService();
final RuntimeService runtimeService = processEngine.getRuntimeService();
Task task1 = taskService.createTaskQuery()
.taskId(taskId)
.taskAssignee(assignee)
.singleResult();
String processInstanceId = task1.getProcessInstanceId();
String nowUserId = assignee;
//TODO 退回上一节点为 会签节点时 指向上一节点的网关节点
String tarActivityId = "Gateway_1bdorji";
//获取待执行的任务节点
Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
if(task == null){
// throw new Exception("sorry,the process is not started or has finished, cannot be withdrawn");
System.out.println("sorry,the process is not started or has finished, cannot be withdrawn");
}
//通过processInstanceId查询历史节点
List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()
.processInstanceId(processInstanceId)
.orderByTaskCreateTime()
.asc()
.list();
String myTaskId = null;
HistoricTaskInstance myTask = null;
//找到当前运行的节点
HistoricProcessInstance processInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
String processDefinitionId = processInstance.getProcessDefinitionId();
//获取流程模型
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
//想要回退到的节点位置
System.out.println(tarActivityId);
FlowNode myFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(tarActivityId);
//原本的活动方向
Execution execution = runtimeService.createExecutionQuery().executionId(task.getExecutionId()).singleResult();
String activityId = execution.getActivityId();
FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(activityId);
//记录原活动方向
List<SequenceFlow> oriSequenceFlows = new ArrayList<SequenceFlow>();
oriSequenceFlows.addAll(flowNode.getOutgoingFlows());
//清理活动方向
flowNode.getOutgoingFlows().clear();
//建立新方向
List<SequenceFlow> newSequenceFlowList = new ArrayList<SequenceFlow>();
SequenceFlow newSequenceFlow = new SequenceFlow();
newSequenceFlow.setId("newSequenceFlowId"+" ROLLBACK: "+ LocalDateTime.now());
//新方向的源头---当前节点
newSequenceFlow.setSourceFlowElement(flowNode);
System.out.println("--------------new flow-----------------"+flowNode);
//新方向的目标---要回退的节点
System.out.println("--------------target flow-----------------"+myFlowNode);
newSequenceFlow.setTargetFlowElement(myFlowNode);
newSequenceFlowList.add(newSequenceFlow);
flowNode.setOutgoingFlows(newSequenceFlowList);
Authentication.setAuthenticatedUserId(nowUserId);
taskService.addComment(task.getId(), task.getProcessInstanceId(), "回退");
//完成任务
System.out.println("========================完成任务====================");
taskService.complete(task.getId());
//恢复原方向
flowNode.setOutgoingFlows(oriSequenceFlows);
System.out.println("------------------RollBack successfully!!----------------------------");
//将退回完成后的当前节点的task返回
// HistoricActivityInstance historicActivityInstance = historyService.createHistoricActivityInstanceQuery().activityId(tarActivityId).singleResult();
//
// return historicActivityInstance.getTaskId();
return "成功";
}
动态新增节点
@GetMapping("/addTask")
// @ApiOperation("新增节点")
public Object addTask(String taskId, String assignee) {
return service.addTask(taskId,assignee);
}
@Resource
ActGeBytearrayMapper actGeBytearrayMapper;
@Override
public Object addTask(String taskId, String assignee) {
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
final TaskService taskService = processEngine.getTaskService();
final RepositoryService repositoryService = processEngine.getRepositoryService();
Task userTask = taskService.createTaskQuery()
.taskId(taskId)
// .taskAssignee(assignee)
.singleResult();
final BpmnModel bpmnModel = repositoryService.getBpmnModel(userTask.getProcessDefinitionId());
final Process process = bpmnModel.getMainProcess();
process.removeFlowElement("flow4");//移除最终节点连线
process.addFlowElement(createUserTask("add", "First task", "fred"));//新增节点
process.addFlowElement(createSequenceFlow("usertask3", "add"));//新增节点 于原节点连线
process.addFlowElement(createSequenceFlow("add", "endevent1"));//新增节点 于原节点连线
//重新绘画图形
new BpmnAutoLayout(bpmnModel).execute();
BpmnXMLConverter converter = new BpmnXMLConverter();
//把bpmnModel对象转换成字符
byte[] bytes = converter.convertToXML(bpmnModel);
//清除缓存
ProcessEngineConfigurationImpl configuration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
DeploymentManager deploymentManager = configuration.getDeploymentManager();
deploymentManager.getProcessDefinitionCache().remove(userTask.getProcessDefinitionId());
// String xmlContenxt = new String(bytes);
List<ActGeBytearray> actGeBytearrayList = actGeBytearrayMapper.getActGeBytearrayList(userTask.getProcessDefinitionId());
actGeBytearrayList = actGeBytearrayList.stream().filter(item -> item.getName().endsWith(".bpmn")).peek(item -> {
item.setBytes(bytes);
}).collect(Collectors.toList());
if (ObjectUtils.isNotEmpty(actGeBytearrayList)) actGeBytearrayMapper.updateById(actGeBytearrayList.get(0));
return null;
}
protected static UserTask createUserTask(String id, String name, String assignee) {
UserTask userTask = new UserTask();
userTask.setName(name);
userTask.setId(id);
userTask.setAssignee(assignee);
return userTask;
}
protected static SequenceFlow createSequenceFlow(String from, String to) {
SequenceFlow flow = new SequenceFlow();
flow.setSourceRef(from);
flow.setTargetRef(to);
return flow;
}
mapperxml
<select id="getActGeBytearrayList" resultType="com.example.activitiydemo.entity.ActGeBytearray">
select a.*
from act_ge_bytearray a
inner join act_re_procdef b ON a.DEPLOYMENT_ID_ = b.DEPLOYMENT_ID_
where b.ID_ = #{processDefinitionId}
</select>
查看实例流程图,根据流程实例ID获取流程图
@Resource
FlowUtils flowUtils;
/**
* 查看实例流程图,根据流程实例ID获取流程图
*/
@RequestMapping(value="traceprocess",method= RequestMethod.GET)
public void traceprocess(HttpServletResponse response, @RequestParam("proInsId")String proInsId,@RequestParam("fileName") String fileName) throws Exception{
InputStream in = flowUtils.getResourceDiagramInputStream(proInsId);
File file = new File("C:\\Users\\wangshuai9776\\"+ fileName +".svg");
OutputStream output = new FileOutputStream(file);
// ServletOutputStream output = response.getOutputStream();
IOUtils.copy(in, output);
}
**FlowUtils**
@Component
public class FlowUtils {
/**
* 获取历史节点流程图
*
* @param id
* @return
*/
public InputStream getResourceDiagramInputStream(String id) {
try {
// 1、创建ProcessEngine
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
final HistoryService historyService = processEngine.getHistoryService();
final RepositoryService repositoryService = processEngine.getRepositoryService();
// 获取历史流程实例
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(id).singleResult();
// 获取流程中已经执行的节点,按照执行先后顺序排序
List<HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery().processInstanceId(id).orderByHistoricActivityInstanceId().asc().list();
// 构造已执行的节点ID集合
List<String> executedActivityIdList = new ArrayList<String>();
for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
executedActivityIdList.add(activityInstance.getActivityId());
}
// 获取bpmnModel
BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
// 获取流程已发生流转的线ID集合
List<String> flowIds = this.getExecutedFlows(bpmnModel, historicActivityInstanceList);
// 使用默认配置获得流程图表生成器,并生成追踪图片字符流
// ProcessDiagramGenerator processDiagramGenerator = processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator();
//你也可以 new 一个
DefaultProcessDiagramGenerator processDiagramGenerator = new DefaultProcessDiagramGenerator();
InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel, executedActivityIdList, flowIds, "宋体", "微软雅黑", "黑体", true, "demo");
return imageStream;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private List<String> getExecutedFlows(BpmnModel bpmnModel,
List<HistoricActivityInstance> historicActivityInstanceList) {
List<String> executedFlowIdList = new ArrayList<>();
for (int i = 0; i < historicActivityInstanceList.size() - 1; i++) {
HistoricActivityInstance hai = historicActivityInstanceList.get(i);
FlowNode flowNode = (FlowNode) bpmnModel.getFlowElement(hai.getActivityId());
List<SequenceFlow> sequenceFlows = flowNode.getOutgoingFlows();
if (sequenceFlows.size() > 1) {
HistoricActivityInstance nextHai = historicActivityInstanceList.get(i + 1);
sequenceFlows.forEach(sequenceFlow -> {
if (sequenceFlow.getTargetFlowElement().getId().equals(nextHai.getActivityId())) {
executedFlowIdList.add(sequenceFlow.getId());
}
});
} else if (sequenceFlows.size() == 1) {
executedFlowIdList.add(sequenceFlows.get(0).getId());
}
}
return executedFlowIdList;
}
}
BPMN& PNG
diagram
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/test">
<process id="helloworld" name="helloworldProcess" isExecutable="true">
<startEvent id="startevent1" name="Start"></startEvent>
<endEvent id="endevent1" name="End"></endEvent>
<userTask id="applyTask" name="提交申请" activiti:assignee="张三"></userTask>
<userTask id="usertask2" name="审批【部门经理】" activiti:assignee="李四"></userTask>
<userTask id="usertask3" name="审批【总经理】" activiti:assignee="王五"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1" targetRef="applyTask"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="applyTask" targetRef="usertask2"></sequenceFlow>
<sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow>
<sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_helloworld">
<bpmndi:BPMNPlane bpmnElement="helloworld" id="BPMNPlane_helloworld">
<bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="330.0" y="20.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="330.0" y="380.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="applyTask" id="BPMNShape_applyTask">
<omgdc:Bounds height="55.0" width="105.0" x="295.0" y="100.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
<omgdc:Bounds height="55.0" width="105.0" x="295.0" y="200.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
<omgdc:Bounds height="55.0" width="105.0" x="295.0" y="290.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
<omgdi:waypoint x="347.0" y="55.0"></omgdi:waypoint>
<omgdi:waypoint x="347.0" y="100.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
<omgdi:waypoint x="347.0" y="155.0"></omgdi:waypoint>
<omgdi:waypoint x="347.0" y="200.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
<omgdi:waypoint x="347.0" y="255.0"></omgdi:waypoint>
<omgdi:waypoint x="347.0" y="290.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
<omgdi:waypoint x="347.0" y="345.0"></omgdi:waypoint>
<omgdi:waypoint x="347.0" y="380.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
test
<?xml version="1.0" encoding="UTF-8"?>
<bpmn:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" id="Definitions_11qdn8c" targetNamespace="http://bpmn.io/schema/bpmn" exporter="bpmn-js (https://demo.bpmn.io)" exporterVersion="11.3.1">
<bpmn:process id="test" name="testProcess" isExecutable="true">
<bpmn:parallelGateway id="Gateway_1bdorji">
<bpmn:incoming>Flow_0fdd1s5</bpmn:incoming>
<bpmn:outgoing>Flow_112ta1r</bpmn:outgoing>
<bpmn:outgoing>Flow_17fz0xe</bpmn:outgoing>
<bpmn:outgoing>Flow_1krh17g</bpmn:outgoing>
</bpmn:parallelGateway>
<bpmn:userTask id="Task_032ht9n" name="会签1" activiti:assignee="李四1">
<bpmn:incoming>Flow_112ta1r</bpmn:incoming>
<bpmn:outgoing>Flow_0umiwqj</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_112ta1r" sourceRef="Gateway_1bdorji" targetRef="Task_032ht9n" />
<bpmn:userTask id="Task_1tl20uk" name="会签2" activiti:assignee="李四2">
<bpmn:incoming>Flow_17fz0xe</bpmn:incoming>
<bpmn:outgoing>Flow_1tconop</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_17fz0xe" sourceRef="Gateway_1bdorji" targetRef="Task_1tl20uk" />
<bpmn:userTask id="Task_0nha4ou" name="会签3" activiti:assignee="李四3">
<bpmn:incoming>Flow_1krh17g</bpmn:incoming>
<bpmn:outgoing>Flow_1bgxt99</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_1krh17g" sourceRef="Gateway_1bdorji" targetRef="Task_0nha4ou" />
<bpmn:parallelGateway id="Gateway_0lpn6xd">
<bpmn:incoming>Flow_0umiwqj</bpmn:incoming>
<bpmn:incoming>Flow_1tconop</bpmn:incoming>
<bpmn:incoming>Flow_1bgxt99</bpmn:incoming>
<bpmn:outgoing>Flow_1hav98t</bpmn:outgoing>
</bpmn:parallelGateway>
<bpmn:sequenceFlow id="Flow_0umiwqj" sourceRef="Task_032ht9n" targetRef="Gateway_0lpn6xd" />
<bpmn:sequenceFlow id="Flow_1tconop" sourceRef="Task_1tl20uk" targetRef="Gateway_0lpn6xd" />
<bpmn:sequenceFlow id="Flow_1bgxt99" sourceRef="Task_0nha4ou" targetRef="Gateway_0lpn6xd" />
<bpmn:userTask id="Task_0ixmhzp" name="总经理" activiti:assignee="王五">
<bpmn:incoming>Flow_1hav98t</bpmn:incoming>
<bpmn:outgoing>Flow_0i9kc1u</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_1hav98t" sourceRef="Gateway_0lpn6xd" targetRef="Task_0ixmhzp" />
<bpmn:userTask id="Task_0wj3769" name="提交人" activiti:assignee="张三">
<bpmn:incoming>Flow_1ggtxt8</bpmn:incoming>
<bpmn:outgoing>Flow_0fdd1s5</bpmn:outgoing>
</bpmn:userTask>
<bpmn:sequenceFlow id="Flow_0fdd1s5" sourceRef="Task_0wj3769" targetRef="Gateway_1bdorji" />
<bpmn:startEvent id="Event_0dr1q86">
<bpmn:outgoing>Flow_1ggtxt8</bpmn:outgoing>
</bpmn:startEvent>
<bpmn:sequenceFlow id="Flow_1ggtxt8" sourceRef="Event_0dr1q86" targetRef="Task_0wj3769" />
<bpmn:endEvent id="Event_027przs">
<bpmn:incoming>Flow_0i9kc1u</bpmn:incoming>
</bpmn:endEvent>
<bpmn:sequenceFlow id="Flow_0i9kc1u" sourceRef="Task_0ixmhzp" targetRef="Event_027przs" />
</bpmn:process>
<bpmndi:BPMNDiagram id="BPMNDiagram_1">
<bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_00frahi">
<bpmndi:BPMNEdge id="Flow_0i9kc1u_di" bpmnElement="Flow_0i9kc1u">
<di:waypoint x="1190" y="210" />
<di:waypoint x="1312" y="210" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1ggtxt8_di" bpmnElement="Flow_1ggtxt8">
<di:waypoint x="188" y="210" />
<di:waypoint x="300" y="210" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0fdd1s5_di" bpmnElement="Flow_0fdd1s5">
<di:waypoint x="400" y="210" />
<di:waypoint x="545" y="210" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1hav98t_di" bpmnElement="Flow_1hav98t">
<di:waypoint x="975" y="210" />
<di:waypoint x="1090" y="210" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1bgxt99_di" bpmnElement="Flow_1bgxt99">
<di:waypoint x="810" y="320" />
<di:waypoint x="950" y="320" />
<di:waypoint x="950" y="235" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1tconop_di" bpmnElement="Flow_1tconop">
<di:waypoint x="810" y="210" />
<di:waypoint x="925" y="210" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_0umiwqj_di" bpmnElement="Flow_0umiwqj">
<di:waypoint x="810" y="100" />
<di:waypoint x="950" y="100" />
<di:waypoint x="950" y="185" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_1krh17g_di" bpmnElement="Flow_1krh17g">
<di:waypoint x="570" y="235" />
<di:waypoint x="570" y="320" />
<di:waypoint x="710" y="320" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_17fz0xe_di" bpmnElement="Flow_17fz0xe">
<di:waypoint x="595" y="210" />
<di:waypoint x="710" y="210" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="Flow_112ta1r_di" bpmnElement="Flow_112ta1r">
<di:waypoint x="570" y="185" />
<di:waypoint x="570" y="100" />
<di:waypoint x="710" y="100" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="Gateway_1bdorji_di" bpmnElement="Gateway_1bdorji">
<dc:Bounds x="545" y="185" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_032ht9n_di" bpmnElement="Task_032ht9n">
<dc:Bounds x="710" y="60" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_1tl20uk_di" bpmnElement="Task_1tl20uk">
<dc:Bounds x="710" y="170" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0nha4ou_di" bpmnElement="Task_0nha4ou">
<dc:Bounds x="710" y="280" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Gateway_0lpn6xd_di" bpmnElement="Gateway_0lpn6xd">
<dc:Bounds x="925" y="185" width="50" height="50" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0ixmhzp_di" bpmnElement="Task_0ixmhzp">
<dc:Bounds x="1090" y="170" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Activity_0wj3769_di" bpmnElement="Task_0wj3769">
<dc:Bounds x="300" y="170" width="100" height="80" />
<bpmndi:BPMNLabel />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_0dr1q86_di" bpmnElement="Event_0dr1q86">
<dc:Bounds x="152" y="192" width="36" height="36" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="Event_027przs_di" bpmnElement="Event_027przs">
<dc:Bounds x="1312" y="192" width="36" height="36" />
</bpmndi:BPMNShape>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</bpmn:definitions>