项目源码仓库
Flowable诞生于Activiti,是一个使用Java编写的轻量级业务流程引擎。Flowable流程引擎可用于部署BPMN 2.0流程定义,可以十分灵活地加入你的应用/服务/构架。
本文介绍4种绘制流程图的方式,前3种是在后台绘制静态图(image/png格式),以Stream形式返回前端显示。最后1种是后端生成JSON形式的结构化数据,由前端使用Snap.svg绘制的交互式SVG动画流程图。
<dependency>
<groupId>org.flowablegroupId>
<artifactId>flowable-spring-boot-starter-basicartifactId>
<version>6.4.1version>
dependency>
<dependency>
<groupId>org.flowablegroupId>
<artifactId>flowable-json-converterartifactId>
<version>6.4.1version>
dependency>
适用于流程管理或启动流程时查看已经部署的流程图,流程图不包括任务办理实际流转信息。
使用流程定义(ProcessDefinition.id),通过调用flowable 的RepositoryService来导出其流程定义的图片资源。
源码:
@RestController
@Slf4j
public class ProcessController {
@Autowired
private MyProcessService processService;
/**
* 通过processDefinition.id和resType导出流程XML或图片资源
* @param id processDefinition.id
* @param resType 取值 “image/png”或“text/xml”
* @param response
* @throws Exception
*/
@GetMapping(value = "/res/exp")
@ApiOperation("通过processDefinition.id和resType导出流程XML或图片资源")
public void resourceRead(@RequestParam("id") String id,@RequestParam("resType") String resType, HttpServletResponse response) throws Exception {
/** resType取值 “image/png”或“text/xml” **/
InputStream resourceAsStream = processService.resourceRead(id,resType);
byte[] b = new byte[1024];
int len = -1;
while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
}
}
@Service
public class MyProcessServiceImpl implements MyProcessService {
@Autowired
private RepositoryService repositoryService;
@Override
public InputStream resourceRead(String id, String resType) throws Exception {
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(id).singleResult();
String resourceName = "";
if (resType.equals("image/png")) {
resourceName = processDefinition.getDiagramResourceName();
} else if (resType.equals("text/xml")) {
resourceName = processDefinition.getResourceName();
}
InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);
return resourceAsStream;
}
}
适用于查看待办任务列表时,绘制该任务的流程图,流程图可强调显示当前待办任务所处的流程节点。使用流程实例(processInstanceId)ID,通过调用flowable引擎来绘制流程图。
源码:
@RequestMapping("/api/workflow/auth/activiti/task")
@RestController
@Slf4j
public class TaskController {
@Autowired
private MyTaskService myTaskService;
/**
* 绘制强调当前节点的流程图
*/
@GetMapping(value = "/process/diagram")
@ApiOperation("绘制强调当前节点的流程图")
public void genProcessDiagram(@RequestParam("processInstanceId") String processInstanceId, HttpServletResponse httpServletResponse) throws Exception {
InputStream resourceAsStream = myTaskService.genProcessDiagram(processInstanceId);
byte[] b = new byte[1024];
int len = -1;
while ((len = resourceAsStream.read(b, 0, 1024)) != -1) {
httpServletResponse.getOutputStream().write(b, 0, len);
}
}
}
@Slf4j
@Service
public class MyTaskServiceImpl implements MyTaskService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private RepositoryService repositoryService;
@Resource
private ProcessEngine processEngine;
@Override
public InputStream genProcessDiagram(String processId) throws Exception {
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(processId).singleResult();
//流程走完的不显示图
if (pi == null) {
return null;
}
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
//使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
String InstanceId = task.getProcessInstanceId();
List<Execution> executions = runtimeService
.createExecutionQuery()
.processInstanceId(InstanceId)
.list();
//得到正在执行的Activity的Id
List<String> activityIds = new ArrayList<>();
List<String> flows = new ArrayList<>();
for (Execution exe : executions) {
List<String> ids = runtimeService.getActiveActivityIds(exe.getId());
activityIds.addAll(ids);
}
//获取流程图
BpmnModel bpmnModel = repositoryService.getBpmnModel(pi.getProcessDefinitionId());
ProcessEngineConfiguration engconf = processEngine.getProcessEngineConfiguration();
ProcessDiagramGenerator diagramGenerator = engconf.getProcessDiagramGenerator();
InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", activityIds, flows, engconf.getActivityFontName(), engconf.getLabelFontName(), engconf.getAnnotationFontName(), engconf.getClassLoader(), 2.0,true);
return in;
}
}
适用于查看已办任务列表时,绘制该任务的流程图,流程图可强调显示任务办理实际经过的路径。使用流程实例(processInstanceId)ID,通过调用flowable引擎来绘制流程图。
源码:
@RequestMapping("/api/workflow/auth/activiti/task")
@RestController
@Slf4j
public class TaskController {
@Autowired
private MyTaskService myTaskService;
/**
* 读取带跟踪的图片
*/
@GetMapping(value = "/trace/photo")
public void tracePhoto(@RequestParam("processInstanceId") String processInstanceId, HttpServletResponse response) throws Exception {
InputStream inputStream = myTaskService.genTracePhoto(processInstanceId);
// 输出资源内容到相应对象
byte[] b = new byte[1024];
int len;
while ((len = inputStream.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
}
}
@Slf4j
@Service
public class MyTaskServiceImpl implements MyTaskService {
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Resource
private HistoryService historyService;
@Autowired
private RepositoryService repositoryService;
@Override
public InputStream genTracePhoto(String processInstanceId) throws Exception {
String procDefId;
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId(processInstanceId)
.singleResult();
if (processInstance == null) {
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
procDefId = historicProcessInstance.getProcessDefinitionId();
} else {
procDefId = processInstance.getProcessDefinitionId();
}
BpmnModel bpmnModel = repositoryService.getBpmnModel(procDefId);
DefaultProcessDiagramGenerator defaultProcessDiagramGenerator = new DefaultProcessDiagramGenerator(); // 创建默认的流程图生成器
String imageType = "png"; // 生成图片的类型
List<String> highLightedActivities = new ArrayList<>(); // 高亮节点集合
List<String> highLightedFlows = new ArrayList<>(); // 高亮连线集合
List<HistoricActivityInstance> hisActInsList = historyService.createHistoricActivityInstanceQuery()
.processInstanceId(processInstanceId)
.list(); // 查询所有历史节点信息
hisActInsList.forEach(historicActivityInstance -> { // 遍历
if("sequenceFlow".equals(historicActivityInstance.getActivityType())) {
// 添加高亮连线
highLightedFlows.add(historicActivityInstance.getActivityId());
} else {
// 添加高亮节点
highLightedActivities.add(historicActivityInstance.getActivityId());
}
});
String activityFontName = "宋体"; // 节点字体
String labelFontName = "微软雅黑"; // 连线标签字体
String annotationFontName = "宋体"; // 连线标签字体
ClassLoader customClassLoader = null; // 类加载器
double scaleFactor = 1.0d; // 比例因子,默认即可
boolean drawSequenceFlowNameWithNoLabelDI = true; // 不设置连线标签不会画
// 生成图片
InputStream inputStream = defaultProcessDiagramGenerator.generateDiagram(bpmnModel, imageType, highLightedActivities
, highLightedFlows, activityFontName, labelFontName, annotationFontName, customClassLoader,
scaleFactor, drawSequenceFlowNameWithNoLabelDI); // 获取输入流
return inputStream;
}
}
无论是待办、已办,亦或是流转中、已结束的流程实例,通过使用JS绘制SVG格式的交互式流程图,与以上三种方式相比,在效果上都具有明显优势。
运行效果如下图所示:
具体内容参见下一篇博文
项目源码仓库