1.引入diagram-viewer。
上一篇集成流程设计器时已经把diagram-viewer文件夹拷贝过来了。所以这一步就省略了。
2.引入依赖activiti-diagram-rest。
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>${activiti.version}</version>
</dependency>
3.查看审批跟踪流的url格式。
查看diagram-viewer/index.html文件,我们知道需要传入2个参数processDefinitionId和processInstanceId。
所以url基本格式是:
http://localhost:8080/diagram-viewer/index.html?processDefinitionId={processDefinitionId}&processInstanceId={processInstanceId}
4.初始化数据便于测试。
首先,设计一个简单的流程two-task-process.bpmn20.xml。并部署。
<?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="twoTaskProcess" isExecutable="true">
<startEvent id="sid-CB77D78D-657D-45C2-959C-53E120CD79DF"></startEvent>
<userTask id="sid-35258D72-ABC4-42FB-B793-69243860DFE8" name="任务1" activiti:assignee="1"></userTask>
<sequenceFlow id="sid-B8596EB9-169F-41C5-AA95-55EB32B8CA66" sourceRef="sid-CB77D78D-657D-45C2-959C-53E120CD79DF" targetRef="sid-35258D72-ABC4-42FB-B793-69243860DFE8"></sequenceFlow>
<userTask id="sid-7CFC5FC2-134F-4E18-B3A1-D79672639388" name="任务2" activiti:assignee="2"></userTask>
<sequenceFlow id="sid-4A25046F-34EE-4BDB-9A96-30B98A15BE93" sourceRef="sid-35258D72-ABC4-42FB-B793-69243860DFE8" targetRef="sid-7CFC5FC2-134F-4E18-B3A1-D79672639388"></sequenceFlow>
<endEvent id="sid-1641410B-1B17-4A84-82C4-DA15EF102384"></endEvent>
<sequenceFlow id="sid-F528180B-EFA4-436A-9BB9-1495898DB4A3" sourceRef="sid-7CFC5FC2-134F-4E18-B3A1-D79672639388" targetRef="sid-1641410B-1B17-4A84-82C4-DA15EF102384"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_process">
<bpmndi:BPMNPlane bpmnElement="twoTaskProcess" id="BPMNPlane_process">
<bpmndi:BPMNShape bpmnElement="sid-CB77D78D-657D-45C2-959C-53E120CD79DF" id="BPMNShape_sid-CB77D78D-657D-45C2-959C-53E120CD79DF">
<omgdc:Bounds height="30.0" width="30.0" x="85.0" y="183.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-35258D72-ABC4-42FB-B793-69243860DFE8" id="BPMNShape_sid-35258D72-ABC4-42FB-B793-69243860DFE8">
<omgdc:Bounds height="80.0" width="100.0" x="160.0" y="158.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-7CFC5FC2-134F-4E18-B3A1-D79672639388" id="BPMNShape_sid-7CFC5FC2-134F-4E18-B3A1-D79672639388">
<omgdc:Bounds height="80.0" width="100.0" x="300.0" y="158.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="sid-1641410B-1B17-4A84-82C4-DA15EF102384" id="BPMNShape_sid-1641410B-1B17-4A84-82C4-DA15EF102384">
<omgdc:Bounds height="28.0" width="28.0" x="450.0" y="184.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="sid-4A25046F-34EE-4BDB-9A96-30B98A15BE93" id="BPMNEdge_sid-4A25046F-34EE-4BDB-9A96-30B98A15BE93">
<omgdi:waypoint x="260.0" y="198.0"></omgdi:waypoint>
<omgdi:waypoint x="300.0" y="198.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-B8596EB9-169F-41C5-AA95-55EB32B8CA66" id="BPMNEdge_sid-B8596EB9-169F-41C5-AA95-55EB32B8CA66">
<omgdi:waypoint x="115.0" y="198.0"></omgdi:waypoint>
<omgdi:waypoint x="160.0" y="198.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="sid-F528180B-EFA4-436A-9BB9-1495898DB4A3" id="BPMNEdge_sid-F528180B-EFA4-436A-9BB9-1495898DB4A3">
<omgdi:waypoint x="400.0" y="198.0"></omgdi:waypoint>
<omgdi:waypoint x="450.0" y="198.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
流程图如下:
然后运行项目时启动流程,并按上面url格式打印输出。
@ComponentScan({"xpl.study.activiti","org.activiti.rest"})
@SpringBootApplication(exclude = { org.activiti.spring.boot.SecurityAutoConfiguration.class,
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class })
public class StudyActivitiApplication {
public static void main(String[] args) {
SpringApplication.run(StudyActivitiApplication.class, args);
}
@Bean
public CommandLineRunner init(final RepositoryService repositoryService, final RuntimeService runtimeService,
final TaskService taskService) {
return new CommandLineRunner() {
@Override
public void run(String... strings) throws Exception {
System.out.println(
"Number of process definitions : " + repositoryService.createProcessDefinitionQuery().count());
System.out.println("Number of tasks : " + taskService.createTaskQuery().count());
// runtimeService.startProcessInstanceByKey("oneTaskProcess");
// System.out.println("Number of tasks after process start: " + taskService.createTaskQuery().count());
ProcessInstance procInst = runtimeService.startProcessInstanceByKey("oneTaskProcess");
System.out.println("pid:"+procInst.getActivityId() );
System.out.println("procDefId:"+procInst.getProcessDefinitionId() );
String url = "http://localhost:8080/diagram-viewer/index.html?processDefinitionId="
+ procInst.getProcessDefinitionId() + "&processInstanceId="+procInst.getActivityId();
System.out.println(url);
}
};
}
}
运行结果:
5.复制上面输出的url,浏览器访问,查看效果。
看到上面404错误。
http://localhost:8080/diagram-viewer/service/process-definition/twoTaskProcess:1:5/diagram-layout?callback=jQuery171021142217172191757_1722612596099&_=1722612596116
6.验证activiti-diagram-rest是否生效。
把上面的url去掉/diagram-viewer/service,即使用如下url访问。
http://localhost:8080/process-definition/twoTaskProcess:1:5/diagram-layout?callback=jQuery171021142217172191757_1722612596099&_=1722612596116
结果:
说明该接口是可以访问的。上一章节配置包扫描时把org.activiti.rest加进去了,activiti-diagram-rest刚好相关的api都在这个包下面,所以不需要再启动类上面再添加新的扫描包。
7.修正URL,查看审批跟踪流程图。
修改diagram-viewer/index.html。
修改前:
修改后:
并且把diagram-viewer/js/ActivitiRest.js文件中的jsonp都注释掉。查看activiti-diagram-rest源码,相关实现都没考虑jsonp,要是的api支持jsonp还需要对源码进行修改。本项目可以简单点直接注释jsonp,使用普通的ajax请求就可以了。只有有跨域需求才需要实现jsonp请求功能。
var ActivitiRest = {
options: {},
getProcessDefinitionByKey: function(processDefinitionKey, callback) {
var url = Lang.sub(this.options.processDefinitionByKeyUrl, {processDefinitionKey: processDefinitionKey});
$.ajax({
url: url,
//dataType: 'jsonp',
cache: false,
async: true,
success: function(data, textStatus) {
var processDefinition = data;
if (!processDefinition) {
console.error("Process definition '" + processDefinitionKey + "' not found");
} else {
callback.apply({processDefinitionId: processDefinition.id});
}
}
}).done(function(data, textStatus) {
console.log("ajax done");
}).fail(function(jqXHR, textStatus, error){
console.error('Get diagram layout['+processDefinitionKey+'] failure: ', textStatus, 'error: ', error, jqXHR);
});
},
getProcessDefinition: function(processDefinitionId, callback) {
var url = Lang.sub(this.options.processDefinitionUrl, {processDefinitionId: processDefinitionId});
$.ajax({
url: url,
//dataType: 'jsonp',
cache: false,
async: true,
success: function(data, textStatus) {
var processDefinitionDiagramLayout = data;
if (!processDefinitionDiagramLayout) {
console.error("Process definition diagram layout '" + processDefinitionId + "' not found");
return;
} else {
callback.apply({processDefinitionDiagramLayout: processDefinitionDiagramLayout});
}
}
}).done(function(data, textStatus) {
console.log("ajax done");
}).fail(function(jqXHR, textStatus, error){
console.log('Get diagram layout['+processDefinitionId+'] failure: ', textStatus, jqXHR);
});
},
getHighLights: function(processInstanceId, callback) {
var url = Lang.sub(this.options.processInstanceHighLightsUrl, {processInstanceId: processInstanceId});
$.ajax({
url: url,
//dataType: 'jsonp',
cache: false,
async: true,
success: function(data, textStatus) {
console.log("ajax returned data");
var highLights = data;
if (!highLights) {
console.log("highLights not found");
return;
} else {
callback.apply({highLights: highLights});
}
}
}).done(function(data, textStatus) {
console.log("ajax done");
}).fail(function(jqXHR, textStatus, error){
console.log('Get HighLights['+processInstanceId+'] failure: ', textStatus, jqXHR);
});
}
};
再次访问,还是没有高亮。
调试了一晚上,快凌晨1点了,该是没找出高亮失败原因。睡一觉起来,再次检查了下代码,发现是url拼接过程中,获取流程id的方法写错了!太累了果然就该让大脑休息了。
procInst.getActivityId()
改为
procInst.Id()
StudyActivitiApplication修改后代码:
@Bean
public CommandLineRunner init(final RepositoryService repositoryService, final RuntimeService runtimeService,
final TaskService taskService) {
return new CommandLineRunner() {
@Override
public void run(String... strings) throws Exception {
System.out.println(
"Number of process definitions : " + repositoryService.createProcessDefinitionQuery().count());
System.out.println("Number of tasks : " + taskService.createTaskQuery().count());
// runtimeService.startProcessInstanceByKey("oneTaskProcess");
// System.out.println("Number of tasks after process start: " + taskService.createTaskQuery().count());
// ProcessInstance procInst = runtimeService.startProcessInstanceByKey("oneTaskProcess");
ProcessInstance procInst = runtimeService.startProcessInstanceByKey("twoTaskProcess");
String pId = procInst.getId();
Task firstTask = taskService.createTaskQuery().processInstanceId(pId).singleResult();
taskService.complete(firstTask.getId());
System.out.println("pid:"+ pId );
System.out.println("procDefId:"+procInst.getProcessDefinitionId() );
String url = "http://localhost:8080/diagram-viewer/index.html?processDefinitionId="
+ procInst.getProcessDefinitionId() + "&processInstanceId="+ pId;
System.out.println(url);
}
};
}
8.测试通过。
最后测试结果如下:
至此,官方的流程追踪功能集成完毕。
上面图中可以看到,历史执行的节点和线条没有高亮,不知道是故意不高亮的,还是有bug。正常我们希望的是历史路径都高亮,官方实现没按我们希望的逻辑实现,不清楚是有其他原因,还是就是还不够完善。