web mvc整合activiti-modeler在线流程设计器
1、背景
一直以来,activity工作流的流程设计任务都托付给开发人员,普通人参与不进来。但是我们的客户则提出更高的要求,要求普通的人员也可以参与流程的设计要求。那么,activiti有没有这项功能呢?答案是肯定的。如此,activiti-modeler应运而生。
2、整合
2.1 添加maven依赖
在你的项目中添加以下依赖:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring</artifactId>
<version>5.22.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-modeler</artifactId>
<version>5.22.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-diagram-rest</artifactId>
<version>5.22.0</version>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-explorer</artifactId>
<version>5.22.0</version>
<exclusions>
<exclusion>
<groupId>org.vaadin.addons</groupId>
<artifactId>dcharts-widget</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.fasterxml.uuid</groupId>
<artifactId>java-uuid-generator</artifactId>
<version>3.1.5</version>
</dependency>
2.2 从官网下载示例
Activiti-webapp-explorer2托管在github上,本博客仅用到了engine.properties,stencilset.json,diagram-viewer,editor-app,modeler.html,稍后会提供下载地址。
2.3 添加配置文件
在你的项目src/main/resources下添加engine.properties,stencilset.json,applicationContext-activiti.xml(spring与activiti整合文件)文件。
2.4 添加页面
将diagram-viewer,editor-app,modeler.html拷贝到你的webapp下即可。
以下是我的目录层次:
2.5 配置
因为activiti-modeler是通过rest进行前后台通信的,请求路径已service开头,所以需要修改你的web.xml以service开头,如:
<!-- 配置Spring MVC -->
<servlet>
<servlet-name>act</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/act-model-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>act</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
如果不想修改web.xml还可以重写源码:
1、将StencilsetRestResource.java,ModelSaveRestResource.java,ModelEditorJsonRestResource.java拷贝到你的项目下,添加@RequestMapping(“service”)即可。如:
/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.learn.activity.model.controller;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.ActivitiException;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
* @author Tijs Rademakers
*/
@RestController
@RequestMapping("service")
public class ModelEditorJsonRestResource implements ModelDataJsonConstants {
protected static final Logger LOGGER = LoggerFactory.getLogger(ModelEditorJsonRestResource.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private ObjectMapper objectMapper;
@RequestMapping(value="/model/{modelId}/json", method = RequestMethod.GET, produces = "application/json")
public ObjectNode getEditorJson(@PathVariable String modelId) {
ObjectNode modelNode = null;
Model model = repositoryService.getModel(modelId);
if (model != null) {
try {
if (StringUtils.isNotEmpty(model.getMetaInfo())) {
modelNode = (ObjectNode) objectMapper.readTree(model.getMetaInfo());
} else {
modelNode = objectMapper.createObjectNode();
modelNode.put(MODEL_NAME, model.getName());
}
modelNode.put(MODEL_ID, model.getId());
ObjectNode editorJsonNode = (ObjectNode) objectMapper.readTree(
new String(repositoryService.getModelEditorSource(model.getId()), "utf-8"));
modelNode.put("model", editorJsonNode);
} catch (Exception e) {
LOGGER.error("Error creating model JSON", e);
throw new ActivitiException("Error creating model JSON", e);
}
}
return modelNode;
}
}
最后修改editor-app下的app-cfg.js修改为相应的地址:
如:
ACTIVITI.CONFIG = {
'contextRoot' : '/项目名/service',
};
我的是:
ACTIVITI.CONFIG = {
'contextRoot' : '/service',
};
2.6 编写入口
将以下代码拷贝到你的项目中:
package com.learn.activity.model.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.activiti.bpmn.converter.BpmnXMLConverter;
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.TaskService;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
/**
*
* @author klc
* @date 2018年11月20日
*/
@RestController
public class IndexController {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
@RequestMapping("/")
public String page(String pageName){
return pageName;
}
/**
* 跳转到模型编辑页面
* author klc
* date 2018年11月21日
* @param request
* @param response
*/
@RequestMapping("/create")
public void create(HttpServletRequest request, HttpServletResponse response) {
try {
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode editorNode = objectMapper.createObjectNode();
editorNode.put("id", "leaveBill");
editorNode.put("resourceId", "leaveBill");
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, "leaveBill");
modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
String description = "leaveBill";
modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, description);
modelData.setMetaInfo(modelObjectNode.toString());
modelData.setName("leaveBill");
modelData.setKey("leaveBill");
//保存模型
repositoryService.saveModel(modelData);
repositoryService.addModelEditorSource(modelData.getId(), editorNode.toString().getBytes("utf-8"));
response.sendRedirect(request.getContextPath() + "/modeler.html?modelId=" + modelData.getId());
} catch (Exception e) {
System.out.println("创建模型失败");
}
}
/**
* 部署
* author klc
* date 2018年11月21日
* @param modelId
* @throws Exception
*/
@RequestMapping("/deploy")
public void deploy(String modelId) throws Exception{
Model modelData = this.repositoryService.getModel(modelId);
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() + ".bpmn20.xml";
Deployment deployment = repositoryService.createDeployment()
.name(modelData.getName())
.addString(processName, new String(bpmnBytes)).deploy();
if(deployment != null){
System.out.println("deploy successful......"+deployment.getId());
}
}
/**
* 启动
* author klc
* date 2018年11月21日
* @param modelId
* @throws Exception
*/
@RequestMapping("/start")
public void deploy(String key,String user,String businessId) throws Exception{
Map<String, Object> params = new HashMap<String, Object>();
params.put("user", user);
params.put("busKey", "LeaveBill."+businessId);
ProcessInstance processInstance = this.runtimeService.startProcessInstanceByKey(key, "LeaveBill."+businessId, params);
if(processInstance != null){
System.out.println("start successful......"+processInstance.getId());
}
}
/**
* 查看我的任务
* author klc
* date 2018年11月21日
* @param modelId
* @throws Exception
*/
@RequestMapping("/myTask")
public void myTask(String user) throws Exception{
List<Task> list = this.taskService.createTaskQuery()
.taskAssignee(user)
.orderByTaskCreateTime()
.desc()
.list();
for (Task task : list) {
System.out.println("任务ID:"+ task.getId());
System.out.println("任务办理人:"+ task.getAssignee());
System.out.println("任务名:"+ task.getName());
}
}
/**
* 办理任务
* author klc
* date 2018年11月21日
* @param modelId
* @throws Exception
*/
@RequestMapping("/doTask")
@ResponseBody
public void doTask(String user,String taskId,String comment,String option) throws Exception{
Map<String, Object> variables = new HashMap<String, Object>();
//使用任务ID,查询任务对象,获取流程流程实例ID
Task task = this.taskService.createTaskQuery()//
.taskId(taskId)//使用任务ID查询
.singleResult();
//获取流程实例ID
String processInstanceId = task.getProcessInstanceId();
Authentication.setAuthenticatedUserId(user);
this.taskService.addComment(taskId, processInstanceId, comment);
variables.put("user", user);
variables.put("option", option);
this.taskService.complete(taskId, variables);
ProcessInstance pi = this.runtimeService.createProcessInstanceQuery()//
.processInstanceId(processInstanceId)//使用流程实例ID查询
.singleResult();
//流程结束了
if(pi==null){
System.out.println("workflow end!");
}
}
/**
* 获取批注
* author klc
* date 2018年11月21日
* @param modelId
* @throws Exception
*/
@RequestMapping("/getComment")
@ResponseBody
public void getComment() throws Exception{
String businessKey = "LeaveBill.1";
HistoricProcessInstance processInstance = this.historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).singleResult();
List<Comment> list = this.taskService.getProcessInstanceComments(processInstance.getId());
for (Comment comment : list) {
System.out.println(comment.getUserId() + ":" + comment.getFullMessage());
}
}
}
访问:http://你的项目/service/create,效果图:
需要的文件:
https://pan.baidu.com/s/1lmoGTb-z984Uca7pokC7eA
页面中文处理:https://download.youkuaiyun.com/download/qq_41833259/10819718