activiti 在 5.22.0 后面的版本就不支持在线设计流程图插件,那就从 5.22.0 里面把能拿的东西拿出来,整合到自己项目里面
源码地址:https://gitee.com/wwspace/online-process-design.git
-
下载源码包
官网下载5.22.0的源码包:https://www.activiti.org/get-started
github 不太好下载,我网盘分享一下
链接:https://pan.baidu.com/s/1oMF7wsFCf1w1TWUyI1y5Qw
提取码:a58p
-
添加pom依赖
<!--activiti依赖-->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter-basic</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-json-converter</artifactId>
<version>6.0.0</version>
<exclusions>
<exclusion>
<groupId>org.activiti</groupId>
<artifactId>activiti-bpmn-model</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- activiti依赖-->
<!--xml解析依赖-->
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-codec</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-css</artifactId>
<version> 1.7</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svg-dom</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-svggen</artifactId>
<version>1.7</version>
</dependency>
<!--xml解析依赖-->
<!-- SpringBoot集成thymeleaf模板 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
- 配置文件
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/oa?useSSL=false&nullCatalogMeansCurrent=true
username: root
password: ****
activiti:
# 不自动检查resources/bpmn目录
check-process-definitions: false
thymeleaf:
cache: false
- 5.22.0 版本的静态资源
- 解压activiti-5.22.0的包,解压wars/activiti-explorer.war
- diagram-viewer、editor-app复制到springboot项目下的static目录下;modeler.html(主界面)复制到templates目录下
- activiti-5.22.0\wars\activiti-explorer\WEB-INF\classes\stencilset.json复制到resources目录下
- libs下的activiti-modeler-5.22.0-sources.jar解压。把org\activiti\rest\editor路径下的main、model文件夹里面的三个类复制到你的controller文件夹,主要用于读取stencilset.json。
- 新建 ModelerController
package com.system.web.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.persistence.entity.ProcessDefinitionEntity;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.image.ProcessDiagramGenerator;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 流程控制器
*/
@Controller
@Slf4j
public class ModelerController {
private static final Logger logger = LoggerFactory.getLogger(ModelerController.class);
@Resource
private RepositoryService repositoryService;
@Resource
private ObjectMapper objectMapper;
@Resource
private HistoryService historyService;
@Resource
private RuntimeService runtimeService;
@GetMapping("/")
public void index(HttpServletResponse response){
try {
response.sendRedirect("/create");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 跳转编辑器页面
* @return
*/
@GetMapping("editor")
public String editor(){
return "modeler";
}
/**
* 创建模型
* @param response
* @throws IOException
*/
@RequestMapping("/create")
public void create(HttpServletResponse response,String name,String key) throws IOException {
logger.info("创建模型入参name:{},key:{}",name,key);
Model model = repositoryService.newModel();
ObjectNode modelNode = objectMapper.createObjectNode();
modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, "");
modelNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
model.setName(name);
model.setKey(key);
model.setMetaInfo(modelNode.toString());
repositoryService.saveModel(model);
createObjectNode(model.getId());
response.sendRedirect("/editor?modelId="+ model.getId());
logger.info("创建模型结束,返回模型ID:{}",model.getId());
}
/**
* 创建模型时完善ModelEditorSource
* @param modelId
*/
@SuppressWarnings("deprecation")
private void createObjectNode(String modelId){
logger.info("创建模型完善ModelEditorSource入参模型ID:{}",modelId);
ObjectNode editorNode = objectMapper.createObjectNode();
editorNode.put("id", "canvas");
editorNode.put("resourceId", "canvas");
ObjectNode stencilSetNode = objectMapper.createObjectNode();
stencilSetNode.put("namespace","http://b3mn.org/stencilset/bpmn2.0#");
editorNode.put("stencilset", stencilSetNode);
try {
repositoryService.addModelEditorSource(modelId,editorNode.toString().getBytes("utf-8"));
} catch (Exception e) {
logger.info("创建模型时完善ModelEditorSource服务异常:{}",e);
}
logger.info("创建模型完善ModelEditorSource结束");
}
/**
* 发布流程
* @param modelId 模型ID
* @return
*/
@ResponseBody
@RequestMapping("/publish")
public Object publish(String modelId){
logger.info("流程部署入参modelId:{}",modelId);
Map<String, String> map = new HashMap<String, String>();
try {
Model modelData = repositoryService.getModel(modelId);
byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
if (bytes == null) {
logger.info("部署ID:{}的模型数据为空,请先设计流程并成功保存,再进行发布",modelId);
map.put("code", "FAILURE");
return map;
}
JsonNode modelNode = new ObjectMapper().readTree(bytes);
BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
Deployment deployment = repositoryService.createDeployment()
.name(modelData.getName())
.addBpmnModel(modelData.getKey()+".bpmn20.xml", model)
.deploy();
modelData.setDeploymentId(deployment.getId());
repositoryService.saveModel(modelData);
map.put("code", "SUCCESS");
} catch (Exception e) {
logger.info("部署modelId:{}模型服务异常:{}",modelId,e);
map.put("code", "FAILURE");
}
logger.info("流程部署出参map:{}",map);
return map;
}
/**
* 撤销流程定义
* @param modelId 模型ID
* @return
*/
@ResponseBody
@RequestMapping("/revokePublish")
public Object revokePublish(String modelId){
logger.info("撤销发布流程入参modelId:{}",modelId);
Map<String, String> map = new HashMap<String, String>();
Model modelData = repositoryService.getModel(modelId);
if(null != modelData){
try {
/**
* 参数不加true:为普通删除,如果当前规则下有正在执行的流程,则抛异常
* 参数加true:为级联删除,会删除和当前规则相关的所有信息,包括历史
*/
repositoryService.deleteDeployment(modelData.getDeploymentId(),true);
map.put("code", "SUCCESS");
} catch (Exception e) {
logger.error("撤销已部署流程服务异常:{}",e);
map.put("code", "FAILURE");
}
}
logger.info("撤销发布流程出参map:{}",map);
return map;
}
/**
* 删除流程实例
* @param modelId 模型ID
*
* @return
*/
@ResponseBody
@RequestMapping("/delete")
public Object deleteProcessInstance(String modelId){
logger.info("删除流程实例入参modelId:{}",modelId);
Map<String, String> map = new HashMap<>();
Model modelData = repositoryService.getModel(modelId);
if(null != modelData){
try {
ProcessInstance pi = runtimeService.createProcessInstanceQuery().processDefinitionKey(modelData.getKey()).singleResult();
if(null != pi) {
runtimeService.deleteProcessInstance(pi.getId(), "");
historyService.deleteHistoricProcessInstance(pi.getId());
}
map.put("code", "SUCCESS");
} catch (Exception e) {
logger.error("删除流程实例服务异常:{}",e);
map.put("code", "FAILURE");
}
}
logger.info("删除流程实例出参map:{}",map);
return map;
}
@RequestMapping(value = "/image/{pid}",produces = MediaType.IMAGE_PNG_VALUE)
@ResponseBody
public byte[] definitionImage(@PathVariable("pid") String processDefinitionId) throws IOException {
BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
if (model != null && model.getLocationMap().size() > 0) {
ProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
InputStream imageStream = generator.generateDiagram(model, "png", new ArrayList<>());
byte[] buffer = new byte[imageStream.available()];
imageStream.read(buffer);
imageStream.close();
return buffer;
}
return new byte[0];
}
@GetMapping("/showImage")
public String image(){
return "image";
}
@RequestMapping(value = "/image2/{pid}",produces = MediaType.IMAGE_PNG_VALUE)
@ResponseBody
public byte[] getProcessImage(@PathVariable("pid") String processInstanceId) throws Exception {
// 获取历史流程实例
HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
if (historicProcessInstance == null) {
throw new Exception();
} else {
// 获取流程定义
ProcessDefinitionEntity processDefinition = (ProcessDefinitionEntity) repositoryService
.getProcessDefinition(historicProcessInstance.getProcessDefinitionId());
// 获取流程历史中已执行节点,并按照节点在流程中执行先后顺序排序
List<HistoricActivityInstance> historicActivityInstanceList = historyService
.createHistoricActivityInstanceQuery().processInstanceId(processInstanceId)
.orderByHistoricActivityInstanceId().asc().list();
// 已执行的节点ID集合
List<String> executedActivityIdList = new ArrayList<>();
@SuppressWarnings("unused") int index = 1;
log.info("获取已经执行的节点ID");
for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
executedActivityIdList.add(activityInstance.getActivityId());
log.info("第[" + index + "]个已执行节点=" + activityInstance.getActivityId() + " : " + activityInstance
.getActivityName());
index++;
}
// 获取流程图图像字符流
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinition.getId());
DefaultProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
InputStream imageStream = generator.generateDiagram(bpmnModel, "png", executedActivityIdList);
byte[] buffer = new byte[imageStream.available()];
imageStream.read(buffer);
imageStream.close();
return buffer;
}
}
}
- 修改editor-app下的app-cfg.js,把contextRoot后面改成 ''(这个和ModelerController里面加的requestMapping要一致
- 解决启动报错
@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class ActivitiWebDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ActivitiWebDemoApplication.class, args);
}
}