开发环境版本:
SpringBoot-2.2.1
Activiti-5.22
MySQL8
IDEA2022 社区免费版
下载Activities-5.22 源码包
官网下载地址:Get started | Activiti
导入Activiti 流程设计器依赖静态资源文件:
将activiti-5.22.0/war/activiti-explorer.war解压
将文件夹内 diagram-viewer,editor-app,modeler.html拷贝到项目中resource/public目录下如图
温馨提示:editor-app就是编辑器,modeler.html是编辑器的入口页面。
温馨提示:在editor-app/app-cfg.js中配置一下项目url。设置访问项目前缀,我下面设置为'',因为我没有指定SpringBoot项目名称。
至此,前端相关页面已经全部配置完毕。
SmartOA 项目pom.xml 文件
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.2.1.RELEASE
org.example
SmartOA
1.0-SNAPSHOT
1.8
2.1.1
Greenwich.RC2
5.22.0
org.mybatis.spring.boot
mybatis-spring-boot-starter
1.3.2
com.alibaba
druid-spring-boot-starter
1.1.10
mysql
mysql-connector-java
8.0.12
org.springframework.boot
spring-boot-starter-web
org.activiti
activiti-engine
5.22.0
org.activiti
activiti-spring-boot-starter-basic
5.22.0
org.activiti
activiti-json-converter
5.22.0
org.activiti
activiti-explorer
5.22.0
org.vaadin.addons
dcharts-widget
org.slf4j
slf4j-log4j12
org.activiti
activiti-simple-workflow
5.22.0
org.activiti
activiti-spring
5.22.0
org.apache.xmlgraphics
batik-codec
1.7
org.apache.xmlgraphics
batik-css
1.7
org.apache.xmlgraphics
batik-svg-dom
1.7
org.apache.xmlgraphics
batik-svggen
1.7
org.springframework.boot
spring-boot-starter-test
test
org.junit.vintage
junit-vintage-engine
org.springframework.boot
spring-boot-maven-plugin
SmartOA 项目配置文件application.properties
server.prot=8082
# ???????
spring.datasource.url=jdbc:mysql://localhost:3306/smart_oa?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true&nullCatalogMeansCurrent=true
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
# druid ??
# ?????????????
spring.datasource.druid.initial-size=5
# ???????
spring.datasource.druid.max-active=30
# ???????
spring.datasource.druid.min-idle=5
# ????????????????
spring.datasource.druid.max-wait=60000
# ???????????????????????????????
spring.datasource.druid.time-between-eviction-runs-millis=60000
# ????????????????
spring.datasource.druid.min-evictable-idle-time-millis=300000
# ???????????sql??????????
spring.datasource.druid.validation-query=SELECT 1 FROM DUAL
# ?????true?????????????????????????????????timeBetweenEvictionRunsMillis???validationQuery?????????
spring.datasource.druid.test-while-idle=true
# ???????validationQuery?????????????????????
spring.datasource.druid.test-on-borrow=false
# ???????validationQuery?????????????????????
spring.datasource.druid.test-on-return=false
# ????preparedStatement????PSCache?PSCache???????????????????oracle??mysql??????
spring.datasource.druid.pool-prepared-statements=true
# ???PSCache???????0????0??poolPreparedStatements???????true?
spring.datasource.druid.max-pool-prepared-statement-per-connection-size=50
# ?????????filters????????sql????
spring.datasource.druid.filters=stat,wall,slf4j
# ??connectProperties?????mergeSql????SQL??
spring.datasource.druid.connection-properties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
# ????DruidDataSource?????
spring.datasource.druid.use-global-data-source-stat=true
##### WebStatFilter?? #######
#??StatFilter
spring.datasource.druid.web-stat-filter.enabled=true
#??????
spring.datasource.druid.web-stat-filter.url-pattern=/*
#????????url
spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*
#??session????
spring.datasource.druid.web-stat-filter.session-stat-enable=true
#??sessionStatMaxCount?1000?
spring.datasource.druid.web-stat-filter.session-stat-max-count=1000
#spring.datasource.druid.web-stat-filter.principal-session-name=
#spring.datasource.druid.web-stat-filter.principal-cookie-name=
#spring.datasource.druid.web-stat-filter.profile-enable=
##### StatViewServlet?? #######
#?????????
spring.datasource.druid.stat-view-servlet.enabled=true
#?????????
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
#?? Reset All ??
spring.datasource.druid.stat-view-servlet.reset-enable=false
#???????
spring.datasource.druid.stat-view-servlet.login-username=admin
#??????
spring.datasource.druid.stat-view-servlet.login-password=123
#??????allow?????????????????
spring.datasource.druid.stat-view-servlet.allow=127.0.0.1
#????deny???allow????deny???????allow??????????
spring.datasource.druid.stat-view-servlet.deny=
# mybatis ??
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.zzg.model
# activiti ??
spring.activiti.database-schema-update=true
spring.activiti.check-process-definitions=false
# ????????
logging.level.com.zzg.mapper=debug
SmartOA 项目 添加Activiti自带Controller
将activiti-5.22.0\libs\activiti-modeler-5.22.0-sources.jar,将StencilsetRestResource.java,
ModelEditorJsonRestResource.java,ModelSaveRestResource.java三个文件拷贝到controller目录
温馨提示:类名上不要忘记添加@RestController 注解标签。
SmartOA项目禁用Activiti自动集成的security的权限验证
package com.zzg;
import org.activiti.spring.boot.SecurityAutoConfiguration;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(exclude={SecurityAutoConfiguration.class})
@MapperScan("com.zzg.mapper")
public class OAApplication {
public static void main(String[] args) {
SpringApplication.run(OAApplication.class, args);
}
}
SmartOA项目 新增Activiti 流程设计器Controller
功能说明:在该Controller新增一个model(模型),根据返回的model(模型)编号跳转至Activiti流程设计器HTML页面。
package com.zzg.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.task.Task;
import org.activiti.image.impl.DefaultProcessDiagramGenerator;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletResponse;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@RequestMapping("/activiti")
@RestController
public class ActivitiController {
private static Logger logger= LoggerFactory.getLogger(ActivitiController.class);
@Autowired
private RepositoryService repositoryService;
@Autowired
private HistoryService historyService;
@Autowired
private TaskService taskService;
@Autowired
private ProcessEngine processEngine;
@Autowired
private ObjectMapper objectMapper;
/**
* 根据Model部署流程
*/
@PostMapping(value = "deploy/{modelId}")
public void deploy(@PathVariable("modelId") String modelId) {
try {
Model modelData = 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";
List list = repositoryService.createProcessDefinitionQuery().processDefinitionResourceName(processName).list();
for (ProcessDefinition definition:list){
//删除原有流程定义,正在使用的流程定义无法删除
repositoryService.deleteDeployment(definition.getDeploymentId());
}
Deployment deployment = repositoryService.createDeployment().name(modelData.getName()).addString(processName, new String(bpmnBytes)).deploy();
System.out.println("流程部署id----"+deployment.getId());
} catch (Exception e) {
logger.error("根据模型部署流程失败:modelId={}", modelId, e);
}
}
/**
* 新建模型
* @return
* @throws UnsupportedEncodingException
*/
@GetMapping("/create")
public ModelAndView newModel() throws UnsupportedEncodingException {
// ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// RepositoryService repositoryService = processEngine.getRepositoryService();
// ObjectMapper objectMapper = new ObjectMapper();
//初始化一个空模型
Model model = repositoryService.newModel();
//设置一些默认信息,可以用参数接收
int revision = 1;
String key = "process";
String name = "process";
String description = "新建流程模型";
//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);
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();
repositoryService.addModelEditorSource(id, editorNode.toString().getBytes("utf-8"));
return new ModelAndView("redirect:/modeler.html?modelId=" + id);
}
/**
* 模型编辑
* @param modelId
* @return
*/
@GetMapping("/update/{modelId}")
public ModelAndView updateModel(@PathVariable String modelId) {
System.out.println("用户点击模型编辑");
return new ModelAndView("redirect:/modeler.html?modelId=" + modelId);
}
/**
* 导出指定模型xml 文件
* @param modelId
* @param response
*/
@GetMapping(value = "export/{modelId}")
public void export(@PathVariable String modelId, HttpServletResponse response) {
try {
Model modelData = repositoryService.getModel(modelId);
BpmnJsonConverter jsonConverter = new BpmnJsonConverter();
JsonNode editorNode = new ObjectMapper().readTree(repositoryService.getModelEditorSource(modelData.getId()));
BpmnModel bpmnModel = jsonConverter.convertToBpmnModel(editorNode);
BpmnXMLConverter xmlConverter = new BpmnXMLConverter();
byte[] bpmnBytes = xmlConverter.convertToXML(bpmnModel);
ByteArrayInputStream in = new ByteArrayInputStream(bpmnBytes);
OutputStream outputStream = response.getOutputStream();
IOUtils.copy(in, outputStream);
String filename = bpmnModel.getMainProcess().getId() + ".bpmn.xml";
response.setHeader("content-type", "application/octet-stream");
response.setContentType("application/octet-stream;charset=UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "utf-8"));
outputStream.flush();
outputStream.close();
} catch (Exception e) {
logger.error("导出model的xml文件失败:{}", e.getMessage(), e);
}
}
@GetMapping("import")
public ModelAndView importXml(HttpServletResponse response) {
try {
File file = new File("E:\\activiti_xml\\未定义.bpmn.xml");
InputStream fileInputStream = new FileInputStream(file);
Deployment deployment = repositoryService.createDeployment()
.addInputStream("请假流程" + ".bpmn", fileInputStream)
.deploy();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).singleResult();
String modelId = changeProcessToModel(processDefinition);
return new ModelAndView("redirect:/modeler.html?modelId=" + modelId);
} catch (Exception e) {
logger.error("模型基于xml导入异常:{}",e.getMessage(), e);
}
return new ModelAndView("redirect:/modelError.html");
}
/**
* 流程转化为可编辑模型
*
* @param processDefinition
*/
public String changeProcessToModel(ProcessDefinition processDefinition) {
Model modelData = repositoryService.newModel();
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
// 初始化Model
ObjectMapper objectMapper = new ObjectMapper();
ObjectNode modelObjectNode = objectMapper.createObjectNode();
modelObjectNode.put(ModelDataJsonConstants.MODEL_NAME, processDefinition.getName());
modelObjectNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
modelObjectNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription());
modelData.setMetaInfo(modelObjectNode.toString());
modelData.setName(processDefinition.getName());
modelData.setKey(processDefinition.getKey());
// 保存模型
repositoryService.saveModel(modelData);
String deploymentId = processDefinition.getDeploymentId();
String processDefineResourceName = null;
// 通过deploymentId取得某个部署的资源的名称
List resourceNames = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId);
if (resourceNames != null && resourceNames.size() > 0) {
for (String temp : resourceNames) {
if (temp.indexOf(".bpmn") > 0) {
processDefineResourceName = temp;
}
}
}
InputStream bpmnStream = processEngine.getRepositoryService().getResourceAsStream(deploymentId, processDefineResourceName);
createModelByInputStream(bpmnStream, modelData.getId());
return modelData.getId();
}
public void createModelByInputStream(InputStream bpmnStream, String ModelID) {
XMLInputFactory xif;
InputStreamReader in = null;
XMLStreamReader xtr = null;
try {
xif = XMLInputFactory.newFactory();
in = new InputStreamReader(bpmnStream, "UTF-8");
xtr = xif.createXMLStreamReader(in);
BpmnModel bpmnModel = (new BpmnXMLConverter()).convertToBpmnModel(xtr);
ObjectNode modelNode = new BpmnJsonConverter().convertToJson(bpmnModel);
repositoryService.addModelEditorSource(ModelID, modelNode.toString().getBytes("UTF-8"));
} catch (XMLStreamException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} finally {
if (xtr != null) {
try {
xtr.close();
} catch (XMLStreamException e) {
e.printStackTrace();
}
}
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bpmnStream != null) {
try {
bpmnStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@GetMapping("png/{taskId}")
public void currentProcessInstanceImage(@PathVariable("taskId") String taskId, HttpServletResponse response) throws IOException {
int index;
InputStream inputStream = this.currentProcessInstanceImage(taskId);
OutputStream out = response.getOutputStream();
response.setContentType("image/png");
byte[] bytes = new byte[1024];
while ((index = inputStream.read(bytes)) != -1) {
out.write(bytes, 0, index);
out.flush();
}
out.close();
inputStream.close();
}
/**
* 获取当前任务流程图
*
* @param taskId
* @return
*/
public InputStream currentProcessInstanceImage(String taskId) {
Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
ProcessDefinition processDefinition = repositoryService.getProcessDefinition(task.getProcessDefinitionId());
BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
// ID 为 流程定义Key
org.activiti.bpmn.model.Process process = bpmnModel.getProcessById(processDefinition.getKey());
//Process process = bpmnModel.getProcessById(processDefinition.getKey());
// UserTask userTask = (UserTask) process.getFlowElement(task.getTaskDefinitionKey());
// 流程节点ID
FlowElement flowElement = process.getFlowElement(task.getTaskDefinitionKey());
DefaultProcessDiagramGenerator generator = new DefaultProcessDiagramGenerator();
List highLightedActivities = new ArrayList<>();
highLightedActivities.add(flowElement.getId());
// 生成流程图
// InputStream inputStream = generator.generateJpgDiagram(bpmnModel);
// InputStream inputStream = generator.generatePngDiagram(bpmnModel);
// InputStream inputStream = generator.generateDiagram(bpmnModel, "jpg", highLightedActivities);
// 生成图片
InputStream inputStream = generator.generateDiagram(bpmnModel, "jpg", highLightedActivities, Collections.emptyList(), "宋体", "宋体", "宋体", null, 2.0);
return inputStream;
}
}
SmartOA项目 Activiti 汉化
Activiti汉化:将网络上下载的Activiti 汉化文件拷贝至在resources下。
SmartOA项目启动后,activiti 关联表自动创建
SmartOA项目启动后,访问http://localhost:8080/activiti/create
GitHub地址:https://github.com/zhouzhiwengang/SpringBoot-Project.git
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)