集成Activiti-Modeler流程设计器
Activiti Modeler 是 Activiti 官方提供的一款在线流程设计的前端插件,可以方便流程设计与开发人员绘制流程图,保存流程模型,部署至流程定义等等。
1、材料准备
首先我们需要获取activiti-explorer.zip,这个是activiti-5.22.0才有的。
链接:https://pan.baidu.com/s/1zZ8vcjR63_hgzcLl6soiDw
提取码:1e8a
2、集成
2.1 集成静态资源
其中的editor-app就是编辑器,modeler.html是编辑器的入口页面。
diagram-viewer是流程跟踪插件,虽然这次用不着,但之后会用到。
还有一个界面组件文件,在resource下,名称叫stencilset.json。本身是英文的,可以通过替换它来达到汉化的效果。但现在还是先把它放到项目中去。
在editor-app/app-cfg.js中配置一下项目url。这个url是编辑器相关的后台服务的url。
ACTIVITI.CONFIG = {
'contextRoot' : '/service',
};
我去掉了项目名。
3.后端部分
先引入两个activiti的模块,因为编辑器会用到这两个模块。
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-modeler</artifactId>
<version>${activiti.version}</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>${activiti.version}</version>
</dependency>
其中需要将modeler模块的源代码放到src中,因为需要在其中做部分修改,主要是url的映射。
源码:
https://github.com/Activiti/Activiti/tree/master/modules/activiti-modeler
其中有3个类,都是Controller:
StencilsetRestResource #获取编辑器组件及配置项信息。
ModelEditorJsonRestResource #根据modelId获取model的节点信息,编辑器根据返回的json进行绘图。
ModelSaveRestResource #编辑器制图之后,将节点信息以json的形式提交给这个Controller,然后由其进行持久化操作。
需要修改的地方就三个,在每个Controller类上加上@RequestMapping注解,并指定值为"service"(对应前台app-cfg.js中配置的url)。
···
@RequestMapping("service")
public class StencilsetRestResource {
···
···
@RequestMapping("service")
public class ModelEditorJsonRestResource implements ModelDataJsonConstants {
···
···
@RequestMapping("service")
public class ModelSaveRestResource implements ModelDataJsonConstants {
···
最后别忘了添加包扫描,扫描activiti提供的这些controller。
@SpringBootApplication
@ComponentScan({"org.activiti","com.jerryl"})
public class SpringBootWithActivitiApplication {
···
这样整合部分就基本结束了,此时编辑器已经可以使用了。
至于界面的汉化,界面上各个组件,各个标签上的文字都是在resource下的stencilset.json文件中设置的,可以在网上找一个汉化后的stencilset.json文件替换掉,就能看到中文界面了。
modeler相关方法的封装
主要需要封装4个方法:1.新建一个空的模型;2.所有模型列表;3.发布模型;4.删除模型;(activiti已提供了保存和获取模型节点信息的方法,就是上面的那3个类)
由于这里涉及前后端交互,实现方式随意,主要是activiti的api的调用。
参考代码:
/**
* Created by liuruijie on 2017/2/21.
* 模型管理
*/
@RestController
@RequestMapping("models")
public class ModelerController {
@Autowired
ProcessEngine processEngine;
@Autowired
ObjectMapper objectMapper;
/**
* 新建一个空模型
* @return
* @throws UnsupportedEncodingException
*/
@PostMapping
public Object newModel() throws UnsupportedEncodingException {
RepositoryService repositoryService = processEngine.getRepositoryService();
//初始化一个空模型
Model model = repositoryService.newModel();
//设置一些默认信息
String name = "new-process";
String description = "";
int revision = 1;
String key = "process";
ObjectNode modelNode = objectMapper.createObjectNode();
modelNode.put(ModelDataJsonConstants.MODEL_NAME, name);
modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
modelNode.put(ModelDataJsonConstants.MODEL_REVISION, revision);
model.setName(name);
model.setKey(key);
model.setMetaInfo(modelNode.toString());
repositoryService.saveModel(model);
String id = model.getId();
//完善ModelEditorSource
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);
repositoryService.addModelEditorSource(id,editorNode.toString().getBytes("utf-8"));
return ToWeb.buildResult().redirectUrl("/modeler.html?modelId="+id);
}
/**
* 获取所有模型
* @return
*/
@GetMapping
public Object modelList(){
RepositoryService repositoryService = processEngine.getRepositoryService();
List<Model> models = repositoryService.createModelQuery().list();
return ToWeb.buildResult().putData("models", models);
}
/**
* 删除模型
* @param id
* @return
*/
@DeleteMapping("{id}")
public Object deleteModel(@PathVariable("id")String id){
RepositoryService repositoryService = processEngine.getRepositoryService();
repositoryService.deleteModel(id);
return ToWeb.buildResult().refresh();
}
/**
* 发布模型为流程定义
* @param id
* @return
* @throws Exception
*/
@PostMapping("{id}/deployment")
public Object deploy(@PathVariable("id")String id) throws Exception {
//获取模型
RepositoryService repositoryService = processEngine.getRepositoryService();
Model modelData = repositoryService.getModel(id);
byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
if (bytes == null) {
return ToWeb.buildResult().status(Config.FAIL)
.msg("模型数据为空,请先设计流程并成功保存,再进行发布。");
}
JsonNode modelNode = new ObjectMapper().readTree(bytes);
BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
if(model.getProcesses().size()==0){
return ToWeb.buildResult().status(Config.FAIL)
.msg("数据模型不符要求,请至少设计一条主线流程。");
}
byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);
//发布流程
String processName = modelData.getName() + ".bpmn20.xml";
Deployment deployment = repositoryService.createDeployment()
.name(modelData.getName())
.addString(processName, new String(bpmnBytes, "UTF-8"))
.deploy();
modelData.setDeploymentId(deployment.getId());
repositoryService.saveModel(modelData);
return ToWeb.buildResult().refresh();
}
}
springboot静态文件处理
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry){
registry.addResourceHandler("/html/**").addResourceLocations("classpath:/html/");
registry.addResourceHandler("/diagram-viewer/**").addResourceLocations("classpath:/html/");
registry.addResourceHandler("/editor-app/**").addResourceLocations("classpath:/html/");
registry.addResourceHandler("/js/**").addResourceLocations("classpath:/js/");
registry.addResourceHandler("/css/**").addResourceLocations("classpath:/css/");
}
}
获取json数据的请求
@RestController
public class Test01Controller {
@RequestMapping("/editor/stencilset")
public String test01(){
InputStream stream=this.getClass().getClassLoader().getResourceAsStream("stencilset.json");
try{
return IOUtils.toString(stream,"utf-8");
}catch (Exception e){
}
return null;
}
}
在线设计流程新建model
@Controller
@RequestMapping("model")
public class ModelTest {
@RequestMapping("create")
public void createModel(HttpServletRequest request, HttpServletResponse response){
try{
String modelName = "modelName6";
String modelKey = "modelKey66";
String description = "modelKey666";
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService repositoryService = processEngine.getRepositoryService();
ObjectMapper objectMapper = new ObjectMapper();
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);
Model modelData = repositoryService.newModel();
ObjectNode modelObjectNode = objectMapper.createObjectNode();
modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, modelName);
modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
modelData.setMetaInfo(modelObjectNode.toString());
modelData.setName(modelName);
modelData.setKey(modelKey);
//保存模型
repositoryService.saveModel(modelData);
repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));
response.sendRedirect(request.getContextPath() + "/html/modeler.html?modelId=" + modelData.getId());
}catch (Exception e){
}
}
}
画流程图保存请求
@RequestMapping("/model/{modelId}/save")
@ResponseStatus(HttpStatus.OK)
public void saveModel(@PathVariable String modelId, HttpServletRequest req, HttpServletResponse resp) {
try {
Model model = this.repositoryService.getModel(modelId);
ObjectNode modelJson = (ObjectNode)this.objectMapper.readTree(model.getMetaInfo());
modelJson.put("name", req.getParameter("name"));
modelJson.put("description",req.getParameter("description"));
model.setMetaInfo(modelJson.toString());
model.setName(req.getParameter("name"));
this.repositoryService.saveModel(model);
this.repositoryService.addModelEditorSource(model.getId(), (req.getParameter("json_xml")).getBytes("utf-8"));
InputStream svgStream = new ByteArrayInputStream((req.getParameter("svg_xml")).getBytes("utf-8"));
TranscoderInput input = new TranscoderInput(svgStream);
PNGTranscoder transcoder = new PNGTranscoder();
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
TranscoderOutput output = new TranscoderOutput(outStream);
transcoder.transcode(input, output);
byte[] result = outStream.toByteArray();
System.out.println(new String(result,"utf-8"));
this.repositoryService.addModelEditorSourceExtra(model.getId(), result);
outStream.close();
} catch (Exception var11) {
LOGGER.error("Error saving model", var11);
throw new ActivitiException("Error saving model", var11);
}
}
流程图查看的请求
@RequestMapping(
value = {"/model/{modelId}/json"},
method = {RequestMethod.GET},
produces = {"application/json"}
)
public ObjectNode getEditorJson(@PathVariable String modelId) {
ObjectNode modelNode = null;
Model model = this.repositoryService.getModel(modelId);
if (model != null) {
try {
if (StringUtils.isNotEmpty(model.getMetaInfo())) {
modelNode = (ObjectNode)this.objectMapper.readTree(model.getMetaInfo());
} else {
modelNode = this.objectMapper.createObjectNode();
modelNode.put("name", model.getName());
}
modelNode.put("modelId", model.getId());
ObjectNode editorJsonNode = (ObjectNode)this.objectMapper.readTree(new String(this.repositoryService.getModelEditorSource(model.getId()), "utf-8"));
modelNode.put("model", editorJsonNode);
} catch (Exception var5) {
LOGGER.error("Error creating model JSON", var5);
throw new ActivitiException("Error creating model JSON", var5);
}
}
return modelNode;
}
流程图保存成功以后ACT_RE_MODEL这张表会有数据
程图部署以后ACT_GE_BYTEARRAY会出现bomn结尾的文件
部署流程的请求,根据modelId
@RequestMapping("/model/test02")
public void test02(Object obj) throws Exception {
Model modelData = repositoryService.getModel("40001");
ObjectNode modelNode = (ObjectNode) new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
byte[] bpmnBytes = null;
BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
bpmnBytes = new BpmnXMLConverter().convertToXML(model);
String processName = modelData.getName() + ".bpmn";
Deployment deployment = repositoryService.createDeployment()
.name(modelData.getName()).addString(processName, new String(bpmnBytes,"UTF-8"))
.deploy();
}