前言
鉴于交流学习时候很多同学吐槽 开源的星云erp很多功能不完整,特别重要的审批流程管理没有等问题。现在决定基于开源的星云erp 最新版整合activiti。
开发目标
在最新版 星云erp中整合activiti ,并完成 创建流程、部署流程、启动任务、查询代办、处理任务、查询历史任务的相关测试。
准备工作
- 基于版本
目前 gitee上master分支下的 星云erp,同学们自行下载,也可以下载我已经整合了的版本。
(注意: 这里只是做技术交流分享,同学们自觉遵守原作的相关协议)
- 相关技术栈
Springboot 2.2.2.RELEASE
MyBatis-plus 3.4.2
Spring-session-data-redis 2.2.0.RELEASE
HuTool 5.7.17
Lombok 1.18.10
EasyExcel 2.2.10
JDK 1.8
Mysql 5.7.18
Redis 4.0.8
Activiti 7.1.0.M4 (用7.1.0.M6 会强制需要SpringSecurity认证)
(以上技术栈要求版本进攻参考,大家可以根据自己开发环境调整)
- 项目架构分析
(为了方便直奔主题,决定直接在 xingyun-api下整合)
整合Activiti
1、引入Activiti相关依赖
在后端工程 xingyun-api模块的 pom.xml文件中添加依赖
<!--activity工作流依赖-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M4</version>
<exclusions>
<exclusion>
<artifactId>mybatis</artifactId>
<groupId>org.mybatis</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-image-generator</artifactId>
<version>7.1.0.M4</version>
</dependency>
<!--结束activity工作流依赖-->
- 配置application.yml文件
在spring标签下添加activiti的相关配置
spring:
activiti:
db-history-used: true #进行相关activiti操作时,要生成history相关的表
history-level: full #history相关表中的历史记录都要被记录
至此,如没有问题则可以启动 项目,会自动生成 activiti的 25(为什么不是28张,请自行百度)张数据表。
运行结果如下:
通过Navicat查看到数据库中新增了以下相关表,证明 activiti整合成功。
创建测试流程
1、idea中安装 BPMN-Activiti-Diagram 插件
- 在file---setting --plugins 中搜索组件 BPMN-Activiti-Diagram
- 安装完成后
- 创建流程
(阶段一、流程的设计直接使用插件来创建,后期会整合到PC版)
- 在 resources文件夹中 创建审批流程的xml
- 这里我已经创建了一个 借支流程测试的 工作流程(自行到gitee下下载)
<?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/processdef">
<process id="jz" name="借支申请" isExecutable="true">
<startEvent id="sid-2f65ca73-6ae5-411f-80d6-61b87da2868c"/>
<userTask id="sid-bac0db30-99bf-4c15-b563-f8d90229359d" name="借支申请" activiti:assignee="${name}"/>
<userTask id="sid-ae0b0e78-1fd6-4864-8fa6-a9cfdac4eaaa" name="部门主管审批" activiti:assignee="张三"/>
<userTask id="sid-7a04db13-b607-4b56-a008-82bf49e87b5b" name="财务审批" activiti:assignee="李四"/>
<userTask id="sid-9a50a142-d5c4-49d8-91b5-fdd6823b7e0b" name="老板审批" activiti:assignee="王五"/>
<endEvent id="sid-05c6d003-a2b1-416c-920a-7a1e5dfb3112"/>
<sequenceFlow id="sid-ceda75ac-fedb-4509-96c1-22b4b39d1bdf" sourceRef="sid-2f65ca73-6ae5-411f-80d6-61b87da2868c" targetRef="sid-bac0db30-99bf-4c15-b563-f8d90229359d"/>
<sequenceFlow id="sid-315bb768-b704-4d6a-9b58-e22afecfe575" sourceRef="sid-bac0db30-99bf-4c15-b563-f8d90229359d" targetRef="sid-ae0b0e78-1fd6-4864-8fa6-a9cfdac4eaaa"/>
<exclusiveGateway id="sid-b8aef085-b4ee-434d-ab5e-b989336d6d75"/>
<sequenceFlow id="sid-ed8a6b21-c4b2-48f9-902a-b6724070c6f5" sourceRef="sid-ae0b0e78-1fd6-4864-8fa6-a9cfdac4eaaa" targetRef="sid-b8aef085-b4ee-434d-ab5e-b989336d6d75"/>
<sequenceFlow id="sid-db35498f-3561-418c-a181-804aa14ecbc9" sourceRef="sid-b8aef085-b4ee-434d-ab5e-b989336d6d75" targetRef="sid-7a04db13-b607-4b56-a008-82bf49e87b5b" name="同意">
<conditionExpression xsi:type="tFormalExpression">${sp==1}</conditionExpression>
</sequenceFlow>
<exclusiveGateway id="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35"/>
<sequenceFlow id="sid-40d5bb77-d68d-42bd-a8e2-e437566c1c76" sourceRef="sid-7a04db13-b607-4b56-a008-82bf49e87b5b" targetRef="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35"/>
<sequenceFlow id="sid-2c8957f3-4ed2-45a6-bf5d-f4ef07c3ce5e" sourceRef="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35" targetRef="sid-9a50a142-d5c4-49d8-91b5-fdd6823b7e0b" name="借支金额大于3000">
<conditionExpression xsi:type="tFormalExpression">${sp1==1&&money>3000}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="sid-f42a08d5-2f62-4079-89d6-d9a031727976" sourceRef="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35" targetRef="sid-05c6d003-a2b1-416c-920a-7a1e5dfb3112" name="借支金额小于或等于3000">
<conditionExpression xsi:type="tFormalExpression">${sp1==1&&money==3000}</conditionExpression>
</sequenceFlow>
<endEvent id="sid-de1dac9b-ac4f-44af-9431-e8c41724040d"/>
<sequenceFlow id="sid-91a7bfa6-8e6c-4040-ba3c-3b4f0b2936f9" sourceRef="sid-b8aef085-b4ee-434d-ab5e-b989336d6d75" targetRef="sid-de1dac9b-ac4f-44af-9431-e8c41724040d" name="驳回">
<conditionExpression xsi:type="tFormalExpression">${sp==0}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="sid-e52aacd6-dfd8-4dd0-8b2d-55005519fc18" sourceRef="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35" targetRef="sid-de1dac9b-ac4f-44af-9431-e8c41724040d" name="驳回">
<conditionExpression xsi:type="tFormalExpression">${sp1==0}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="sid-457e06a6-1bb9-48f1-93a6-8905c36daaa7" sourceRef="sid-9a50a142-d5c4-49d8-91b5-fdd6823b7e0b" targetRef="sid-05c6d003-a2b1-416c-920a-7a1e5dfb3112">
<conditionExpression>${sp2==1}</conditionExpression>
</sequenceFlow>
<sequenceFlow id="sid-d44d1aa5-63a2-4091-9854-dec3d31648e5" sourceRef="sid-9a50a142-d5c4-49d8-91b5-fdd6823b7e0b" targetRef="sid-de1dac9b-ac4f-44af-9431-e8c41724040d" name="驳回">
<conditionExpression>${sp2==0}</conditionExpression>
</sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_借支申请">
<bpmndi:BPMNPlane bpmnElement="jz" id="BPMNPlane_借支申请">
<bpmndi:BPMNShape id="shape-e58741b4-6e31-4074-935f-374c6b28948d" bpmnElement="sid-2f65ca73-6ae5-411f-80d6-61b87da2868c">
<omgdc:Bounds x="-185.0" y="-65.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-4e82b3f5-9b68-4e6d-9979-e1797046a8dc" bpmnElement="sid-bac0db30-99bf-4c15-b563-f8d90229359d">
<omgdc:Bounds x="-105.0" y="-90.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-8738aadc-c9b0-4425-8aa6-72bbc112810f" bpmnElement="sid-ae0b0e78-1fd6-4864-8fa6-a9cfdac4eaaa">
<omgdc:Bounds x="40.0" y="-90.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-5c376243-ed67-4880-9409-7bf2a5b8dae7" bpmnElement="sid-7a04db13-b607-4b56-a008-82bf49e87b5b">
<omgdc:Bounds x="280.0" y="-90.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-f4d77dfe-e759-4a2c-b999-4d38e883c3f3" bpmnElement="sid-9a50a142-d5c4-49d8-91b5-fdd6823b7e0b">
<omgdc:Bounds x="589.99994" y="-200.0" width="100.0" height="80.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape id="shape-95f6c6a9-cf28-410a-9a3b-793f64127a58" bpmnElement="sid-05c6d003-a2b1-416c-920a-7a1e5dfb3112">
<omgdc:Bounds x="625.0" y="-65.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="edge-76f787ac-1d25-41c9-bbe9-b764552a3efb" bpmnElement="sid-ceda75ac-fedb-4509-96c1-22b4b39d1bdf">
<omgdi:waypoint x="-154.99998" y="-50.000004"/>
<omgdi:waypoint x="-105.0" y="-50.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-f1dfb186-d63f-4ab1-9919-0916d2a30398" bpmnElement="sid-315bb768-b704-4d6a-9b58-e22afecfe575">
<omgdi:waypoint x="-5.0" y="-50.0"/>
<omgdi:waypoint x="40.0" y="-50.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="shape-19cca33f-c048-4e9d-a1f0-97f8f8ec8ca4" bpmnElement="sid-b8aef085-b4ee-434d-ab5e-b989336d6d75">
<omgdc:Bounds x="175.0" y="-70.0" width="40.0" height="40.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="edge-149b8ca9-3916-44d3-ad2f-474eef6c9f94" bpmnElement="sid-ed8a6b21-c4b2-48f9-902a-b6724070c6f5">
<omgdi:waypoint x="140.0" y="-50.0"/>
<omgdi:waypoint x="175.0" y="-50.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-28443c4d-dc3b-4da7-8d62-5a54400f301f" bpmnElement="sid-db35498f-3561-418c-a181-804aa14ecbc9">
<omgdi:waypoint x="215.0" y="-50.0"/>
<omgdi:waypoint x="280.0" y="-50.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="shape-f7b93b8f-6ce5-4350-a070-08fe7e6fecd3" bpmnElement="sid-5e5ead45-e3c8-41dc-96ef-c9d853a48c35">
<omgdc:Bounds x="400.0" y="-70.0" width="40.0" height="40.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="edge-60e6cdc2-cec6-4d15-b526-d89ccd6a93c7" bpmnElement="sid-40d5bb77-d68d-42bd-a8e2-e437566c1c76">
<omgdi:waypoint x="380.0" y="-50.0"/>
<omgdi:waypoint x="400.0" y="-50.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-abb25af8-44c2-4235-b38a-e30bcc433ae4" bpmnElement="sid-2c8957f3-4ed2-45a6-bf5d-f4ef07c3ce5e">
<omgdi:waypoint x="420.0" y="-70.0"/>
<omgdi:waypoint x="589.99994" y="-160.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-754f9d94-a77b-4eef-a481-88a754263399" bpmnElement="sid-f42a08d5-2f62-4079-89d6-d9a031727976">
<omgdi:waypoint x="440.0" y="-50.0"/>
<omgdi:waypoint x="625.0" y="-50.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNShape id="shape-54161de0-db42-4b4e-81b0-ff591997ba36" bpmnElement="sid-de1dac9b-ac4f-44af-9431-e8c41724040d">
<omgdc:Bounds x="180.0" y="-180.0" width="30.0" height="30.0"/>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge id="edge-9a7697b0-b324-4edd-b1ac-632a35774be7" bpmnElement="sid-91a7bfa6-8e6c-4040-ba3c-3b4f0b2936f9">
<omgdi:waypoint x="195.0" y="-70.0"/>
<omgdi:waypoint x="195.0" y="-150.00002"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-f51508e0-aa41-42c3-abb6-fe0c0fd190fc" bpmnElement="sid-e52aacd6-dfd8-4dd0-8b2d-55005519fc18">
<omgdi:waypoint x="420.0" y="-65.0"/>
<omgdi:waypoint x="420.00003" y="-167.5"/>
<omgdi:waypoint x="210.00002" y="-165.00002"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-0da791b8-acbc-4ff0-acb2-4226aa9a42b5" bpmnElement="sid-457e06a6-1bb9-48f1-93a6-8905c36daaa7">
<omgdi:waypoint x="639.99994" y="-120.0"/>
<omgdi:waypoint x="640.0" y="-65.0"/>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge id="edge-778941f6-726b-464c-b439-c3d3695e6428" bpmnElement="sid-d44d1aa5-63a2-4091-9854-dec3d31648e5">
<omgdi:waypoint x="639.99994" y="-199.99998"/>
<omgdi:waypoint x="639.9999" y="-215.0"/>
<omgdi:waypoint x="420.00003" y="-215.0"/>
<omgdi:waypoint x="195.0" y="-215.0"/>
<omgdi:waypoint x="195.0" y="-200.00002"/>
<omgdi:waypoint x="195.0" y="-172.50002"/>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
- 流程视图
测试流程
这里为了测试和理解activiti的使用,我暂时使用 Test单元测试
- 创建测试类
(这里为了更直观理解,测试类 违反 命名规则 中文命名 可忽略)
import com.lframework.xingyun.api.XingYunApiApplication;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowNode;
import org.activiti.bpmn.model.SequenceFlow;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricVariableInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskQuery;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.util.CollectionUtils;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 借支流程测试
*
*/
@SpringBootTest(classes = XingYunApiApplication.class)
@Slf4j
public class 借支测试 {
@Autowired
RepositoryService ps;
@Test
public void 部署流程() {
Deployment deploy = ps.createDeployment().addClasspathResource("借支申请.bpmn20.xml").deploy();
System.out.println(deploy.getId());
}
@Autowired
RuntimeService runtimeService;
@Test
public void 申请借支() {
Map<String,Object> map = new HashMap<>();
map.put("name","刚哥");
// 根据流程定义的ID启动流程
ProcessInstance instance = runtimeService.startProcessInstanceByKey("jz","jz1",map);
// 查询此任务并继续
Task task = taskService.createTaskQuery().processInstanceBusinessKey("jz1").singleResult();
taskService.complete(task.getId());
}
@Autowired
TaskService taskService;
@Test
public void 查询待审核的任务() {
TaskQuery query = taskService.createTaskQuery();
List<Task> taskList = query.
processInstanceBusinessKey("jz1"). // 以业务id作为条件
// processDefinitionKey("qj"). // 以流程id作为条件
// taskAssignee("张三"). // 以任务办理人作为条件
list();
// 3.输出任务信息
for (Task task : taskList) {
System.out.println("任务ID:"+task.getId());
System.out.println("任务名称:"+task.getName());
System.out.println("任务的创建时间:"+task.getCreateTime());
System.out.println("任务的办理人:"+task.getAssignee());
System.out.println("流程实例ID:"+task.getProcessInstanceId());
System.out.println("执行对象ID:"+task.getExecutionId());
System.out.println("流程定义ID:"+task.getProcessDefinitionId());
}
}
//办理任务(主要操作ACT_RU_EXECUTION、ACT_RU_TASK表)
@Test
public void 办理任务() {
// processInstanceBusinessKey 业务id
// taskAssignee 任务处理人
Task task = taskService.createTaskQuery().
processInstanceBusinessKey("jz1")
// .taskAssignee("刚哥")
.singleResult();
// 设置审批意见
taskService.addComment(task.getId(),task.getProcessInstanceId(),"同意");
// 任务id 完成任务进入下一步
Map<String, Object> map = new HashMap<>();
map.put("sp", 1);
map.put("money", 3500);
taskService.complete(task.getId(), map);
System.out.println("任务ID:"+task.getId());
System.out.println("任务名称:"+task.getName());
System.out.println("任务审批完成");
}
@Autowired
HistoryService historyService;
/**
* 历史活动实例查询
*/
@Test
public void queryHistoryTask() {
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery() // 创建历史活动实例查询
// .processInstanceId("b6d83887-ad4b-11ee-b76f-005056c00008") // 执行流程实例id
.processInstanceBusinessKey("jz1") //根据业务id,查询当前业务下创建的流程实例
.orderByTaskCreateTime()
.asc()
.list();
for (HistoricTaskInstance hai : list) {
System.out.println("活动ID:" + hai.getId());
System.out.println("流程实例ID:" + hai.getProcessInstanceId());
System.out.println("活动名称:" + hai.getName());
System.out.println("办理人:" + hai.getAssignee());
System.out.println("开始时间:" + hai.getStartTime());
System.out.println("结束时间:" + hai.getEndTime());
List<Comment> taskComments = taskService.getTaskComments(hai.getId());
for (Comment taskComment : taskComments) {
System.out.println("---"+taskComment.toString());
System.out.println("审批意见:" + taskComment.getFullMessage());
}
}
}
@Test
public void 根据业务id判断流程是否结束(){
ProcessInstance pi = runtimeService.createProcessInstanceQuery().
processInstanceBusinessKey("qj:10086").singleResult();
System.out.println(pi==null?"结束":"未结束");
}
/**
* 查询历史流程变量
*/
@Test
public void queryHisProVariable(){
// 先根据流程id查询出历史任务
HistoricTaskInstance task = historyService.createHistoricTaskInstanceQuery().processInstanceBusinessKey("jz5")
.list().get(0);
// 查询出该任务执行中所有的变量
List<HistoricVariableInstance> historicVariableInstanceList = historyService
.createHistoricVariableInstanceQuery().
processInstanceId(task.getProcessInstanceId()).list();
boolean b = true;
for(HistoricVariableInstance v: historicVariableInstanceList){
if(v.getVariableName().endsWith("sp")){
int sp = (int) v.getValue();
if (sp == 0) {
b = false;
break;
}
}
}
System.out.println("(b?:\"\") = " + (b?"流程审批通过":"流程审批不通过"));
}
@Autowired
RepositoryService repositoryService;
@Test
public void 流程图(){
try {
// 获取历史流程实例
HistoricProcessInstance historicProcessInstance = historyService
.createHistoricProcessInstanceQuery()
.processInstanceBusinessKey("jz1").singleResult();
// 获取流程中已经执行的节点,按照执行先后顺序排序
List<HistoricActivityInstance> historicActivityInstances = historyService
.createHistoricActivityInstanceQuery()
.processInstanceId(historicProcessInstance.getId())
.orderByHistoricActivityInstanceId()
.asc().list();
// 高亮已经执行流程节点ID集合
List<String> highLightedActivitiIds = new ArrayList<>();
int index = 1;
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
// 用默认颜色
highLightedActivitiIds.add(historicActivityInstance.getActivityId());
index++;
}
ProcessDiagramGenerator processDiagramGenerator = null;
// 使用默认的程序图片生成器
processDiagramGenerator = new DefaultProcessDiagramGenerator();
BpmnModel bpmnModel = repositoryService.getBpmnModel(historicProcessInstance.getProcessDefinitionId());
// 高亮流程已发生流转的线id集合
List<String> highLightedFlowIds = getHighLightedFlows(bpmnModel, historicActivityInstances);
// 使用默认配置获得流程图表生成器,并生成追踪图片字符流
InputStream imageStream = processDiagramGenerator.generateDiagram(bpmnModel,
highLightedActivitiIds, highLightedFlowIds, "宋体","微软雅黑", "黑体");
// 输出图片内容
int byteSize = 1024;
byte[] b = new byte[byteSize];
OutputStream os = Files.newOutputStream(Paths.get("E:\\ATTPD\\activity\\src\\main\\resources\\a.svg"));
int len;
while ((len = imageStream.read(b, 0, byteSize)) != -1) {
os.write(b, 0, len);
}
os.close();
} catch (Exception e) {
}
}
/**
* 获取已经流转的线
*
* @param bpmnModel
* @param historicActivityInstances
* @return
*/
private static List<String> getHighLightedFlows(BpmnModel bpmnModel, List<HistoricActivityInstance> historicActivityInstances) {
// 高亮流程已发生流转的线id集合
List<String> highLightedFlowIds = new ArrayList<>();
// 全部活动节点
List<FlowNode> historicActivityNodes = new ArrayList<>();
// 已完成的历史活动节点
List<HistoricActivityInstance> finishedActivityInstances = new ArrayList<>();
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
FlowNode flowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(historicActivityInstance.getActivityId(), true);
historicActivityNodes.add(flowNode);
if (historicActivityInstance.getEndTime() != null) {
finishedActivityInstances.add(historicActivityInstance);
}
}
FlowNode currentFlowNode = null;
FlowNode targetFlowNode = null;
// 遍历已完成的活动实例,从每个实例的outgoingFlows中找到已执行的
for (HistoricActivityInstance currentActivityInstance : finishedActivityInstances) {
// 获得当前活动对应的节点信息及outgoingFlows信息
currentFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(currentActivityInstance.getActivityId(), true);
List<SequenceFlow> sequenceFlows = currentFlowNode.getOutgoingFlows();
/**
* 遍历outgoingFlows并找到已已流转的 满足如下条件认为已已流转: 1.当前节点是并行网关或兼容网关,则通过outgoingFlows能够在历史活动中找到的全部节点均为已流转 2.当前节点是以上两种类型之外的,通过outgoingFlows查找到的时间最早的流转节点视为有效流转
*/
if ("parallelGateway".equals(currentActivityInstance.getActivityType()) || "inclusiveGateway".equals(currentActivityInstance.getActivityType())) {
// 遍历历史活动节点,找到匹配流程目标节点的
for (SequenceFlow sequenceFlow : sequenceFlows) {
targetFlowNode = (FlowNode) bpmnModel.getMainProcess().getFlowElement(sequenceFlow.getTargetRef(), true);
if (historicActivityNodes.contains(targetFlowNode)) {
highLightedFlowIds.add(targetFlowNode.getId());
}
}
} else {
List<Map<String, Object>> tempMapList = new ArrayList<>();
for (SequenceFlow sequenceFlow : sequenceFlows) {
for (HistoricActivityInstance historicActivityInstance : historicActivityInstances) {
if (historicActivityInstance.getActivityId().equals(sequenceFlow.getTargetRef())) {
Map<String, Object> map = new HashMap<>();
map.put("highLightedFlowId", sequenceFlow.getId());
map.put("highLightedFlowStartTime", historicActivityInstance.getStartTime().getTime());
tempMapList.add(map);
}
}
}
if (!CollectionUtils.isEmpty(tempMapList)) {
// 遍历匹配的集合,取得开始时间最早的一个
long earliestStamp = 0L;
String highLightedFlowId = null;
for (Map<String, Object> map : tempMapList) {
long highLightedFlowStartTime = Long.valueOf(map.get("highLightedFlowStartTime").toString());
if (earliestStamp == 0 || earliestStamp >= highLightedFlowStartTime) {
highLightedFlowId = map.get("highLightedFlowId").toString();
earliestStamp = highLightedFlowStartTime;
}
}
highLightedFlowIds.add(highLightedFlowId);
}
}
}
return highLightedFlowIds;
}
}
测试结果
- 部署流程
结果
也可以在数据库中看到
- 启动流程,创建申请任务
结果
也可在数据库中查看
- 查看待审批任务
测试结果
- 处理任务
处理结果
- 查看历史任务
结果
至此阶段一的开发目标已经达到
下阶段目标
- 目标
把任务的相关管理整合到PC端,并能通过界面管理
- 效果图