其实activiti被springboot整合后用起来简单很多,但还是非常推荐各位先去学习下activiti的原生基础运用,因为这样才能真正明白整合里面都干了些什么。废话就不多说了,直接来!
开发的版本: springboot 版本 2.0 + activiti 版本 6.0
开发工具:IDEA
这里用的mybatis链接数据库,还需要引入mysql的驱动(注意:mysql链接驱动的版本)
特别注意:跟 activiti6.0 匹配的mysql驱动版本不能太高!例如springboot2.0以后的默认使用的mysql驱动版本就太高了,版本不兼容会发生各种头疼且解决不了的问题。
mysql
mysql-connector-java
5.1.35
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.0.0
org.activiti
activiti-spring-boot-starter-basic
6.0.0
数据源的信息是mybatis连接数据库的,你可以这样,配置详细的 activiti 的参数,注意 activiti 配置下的datasource 信息要一样
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
activiti:
check-process-definitions: true #自动检查、部署流程定义文件
database-schema-update: true #自动更新数据库结构
#流程定义文件存放目录
process-definition-location-prefix: classpath:/processes/
#process-definition-location-suffixes: #流程文件格式
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
password: 123
initsize: 10
maxActive: 20
minIdle: 10
maxWait: 120000
poolPreparedStatements: false
maxOpenPreparedStatements: -1
validationQuery: select 1
testOnborrow: true
testOnReturn: true
testWhileIdle: true
timeBetweenEvictionRunsMillis: 120000
你也可以只配置 datasource 的配置,这是项目连接的数据源,springboot 整合 activiti 后默认就是去读的这个数据源
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: 123
url: jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
直接运行下面的这个main方法,即可生成28张表(activiti6.0是28张表),每张表的作用这里就不介绍了,网上很多。
没有报错的前提是你的activiti版本跟mysql链接驱动的版本匹配,并且已经在配置文件指定数据源!
package com.liqiye.springbootdemo.test.activiti;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
// 运行生成activiti流程依赖的表
public class ActivitiTable {
public static void main(String[] args) {
// 引擎配置
ProcessEngineConfiguration pec=ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
pec.setJdbcDriver("com.mysql.jdbc.Driver");
pec.setJdbcUrl("jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC");
pec.setJdbcUsername("root");
pec.setJdbcPassword("123");
/**
* false 不能自动创建表
* create-drop 先删除表再创建表
* true 自动创建和更新表
*/
pec.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
// 获取流程引擎对象
ProcessEngine processEngine=pec.buildProcessEngine();
}
}
想要在IDEA 里进行 activiti的开发,必须要安装 actiBPM 插件 ,下图安装即可。
springboot 整合 activiti ,默认是启动项目后,在resources目录下的 processes 目录下读取 bpmn 流程文件,然后自动部署的,没错!不需要再自己写什么部署的方法了,项目自动帮我们完成。
注意:必须是 processes 目录,并且里面一定要有 bpmn 文件,不然项目启动报错!
在新建的processes目录下右键,new—BpmnFile ,起名leave.bpmn
然后画图 ,在界面的右边拖动流程的每一个模块,到中间组成一个流程图,点击对应的组件,在左边填写里面的id,审核人等的信息
这是我画的流程图,以及整个流程图的信息,注意:这里的id在后面开启流程的时候会用到,起名尽量规则且记住
点击申请请假的绿色框,在左边填写 Assignee 的值为 ${user},记住这个标识,到后面启动流程或者审核流程需要传入参数。其实学习过activiti基础操作的同学应该明白在这里设置这个跟在xml里面设置参数是一样的。
同样的,我们在部门领导、公司领导那里也要设置审核人,那里我们就设置 ${users}
画完bpmn图了,因为idea没有给我们提供直接生成png图的功能,我们需要手动修改文件名后缀成xml,然后再右键这个leave.xml 文件 — Diagrams — Show BPMN2.0 Designer...
然后点击下图的位置,选中项目processes目录下,生成png图片
其实springboot 整合 activiti 后,项目启动,会自动到processes目录下扫描,部署里面的bpmn文件,并且自动根据bpmn文件生成png图片的数据保存在数据库,没错,并不需要我们手动生成png图片!到时候可以直接从数据库查询并且显示流程图。
所以上面说了这么久如何生成png图片是做什么?没什么,就是学多点东西而已。
下面放上面流程图对应的xml文件,你可以直接复制到 processes 目录下,然后改后缀名为 bpmn 即可。
在启动项目之前,要先在springboot的启动类上面加上下面这个注解,因为activiti6.0比springboot2.0早很多,那时候还没有这个类,在启动时要排除他,不然就会报找不到该bean的错误。
@SpringBootApplication(exclude = SecurityAutoConfiguration.class) // springboot2.0集成activiti6要加后面这个,因为6比较老版本
上面整合完 activiti 了,画完流程图,并且启动了项目,我们可以发现在之前数据库生成的 act_ge_bytearray 表中 多了两条数据,看名字就可以知道一条是部署的bpmn文件,一条是对应的png图片
接下来我们编写接口来发起流程,审核流程,指定用户查看对应任务,指定发起者查看对应的流程,还有显示流程执行的图
先在controller注入要用到的 activiti 的工具
@Autowired
private ActivityService activityService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private IdentityService identityService;
@Autowired
private TaskService taskService;
@Autowired
private RepositoryService repositoryService;
1)发起流程
注意:如果你在流程图指定了 Assignee ,这里当你启动流程时,必须要传进参数,不然报错。这里参数一般是传用户id进数据库保存
// 发起流程
@RequestMapping("/initiationProcess")
@ResponseBody
public String initiationProcess(){
System.out.println("method startActivityDemo begin....");
System.out.println( "调用流程存储服务,已部署流程数量:"
+ repositoryService.createDeploymentQuery().count());
Map map = new HashMap();
// 流程图里写的${user} ,这里传进去user
map.put("user","liqiye");
//流程启动
identityService.setAuthenticatedUserId("liqiye"); // 指定流程的发起者 不指定发起者的字段就为空,注意跟审核人分开
ExecutionEntity pi = (ExecutionEntity) runtimeService.startProcessInstanceByKey("leave",map);
System.out.println("启动流程成功!");
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
System.out.println("任务ID: "+task.getId());
System.out.println("任务的办理人: "+task.getAssignee());
System.out.println("任务名称: "+task.getName());
System.out.println("任务的创建时间: "+task.getCreateTime());
System.out.println("流程实例ID: "+task.getProcessInstanceId());
Map map2 = new HashMap();
map2.put("users","lisi,wangwu");
taskService.complete(task.getId(),map2); // 开启后,环节会走到发起请假请求,要完成这个环节,才能到下一个审核环节
System.out.println("method startActivityDemo end....");
return "success";
}
2)审核任务
把上面打印出来的taskId 记下来,然后发起下面的接口链接,即可审核任务
// 根据 taskid 审核任务
@RequestMapping("/audit")
@ResponseBody
public String audit(String taskId){
Map map = new HashMap();
// 流程图里写的${users} ,这里传进去users
map.put("users","lisi,wangwu");
taskService.complete(taskId,map);
return "success";
}
3)指定用户查看对应任务
// 通过用户名查询该用户的所有任务
@RequestMapping("/checkByUser")
@ResponseBody
public String checkByUser(String user){
List tasks = taskService//与任务相关的Service
.createTaskQuery()//创建一个任务查询对象
.taskAssignee(user)
.list();
if(tasks !=null && tasks.size()>0){
for(Task task:tasks){
System.out.println("任务ID:"+task.getId());
System.out.println("任务的办理人:"+task.getAssignee());
System.out.println("任务名称:"+task.getName());
System.out.println("任务的创建时间:"+task.getCreateTime());
System.out.println("流程实例ID:"+task.getProcessInstanceId());
}
}
return "success";
}
4)指定发起者查看对应的流程
// 通过发起者查询该用户发起的所有任务
@RequestMapping("/checkByInitiator")
@ResponseBody
public String checkByInitiator(String user){
List list = runtimeService.createProcessInstanceQuery().startedBy(user).list(); //获取该用户发起的所有流程实例
// System.out.println(list.toString());
for (ProcessInstance processInstance : list) {
List tasks = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
if(tasks !=null && tasks.size()>0){
for(Task task:tasks){
System.out.println("任务ID:"+task.getId());
System.out.println("任务的办理人:"+task.getAssignee());
System.out.println("任务名称:"+task.getName());
System.out.println("任务的创建时间:"+task.getCreateTime());
System.out.println("流程实例ID:"+task.getProcessInstanceId());
}
}
}
return "success";
}
5)显示流程执行的图
直接发起这个接口就可以显示对应的流程图
/**
* 获取流程图 执行到哪里高亮显示
* @param procDefId 部署的流程id 在 act_re_procdef 这张表里
* @param execId 要查询的流程执行的id(开启了一个流程就会生成一条执行的数据) 在 act_ru_execution 这张表里(该表下PROC_DEF_ID_字段可以判断哪个流程)
* @param response
* @throws Exception
*/
@RequestMapping("/getActPic/{procDefId}/{execId}")
public void getActPic(@PathVariable("procDefId") String procDefId,
@PathVariable("execId") String execId, HttpServletResponse response)throws Exception {
InputStream imageStream = activityService.tracePhoto(procDefId, execId);
// 输出资源内容到相应对象
byte[] b = new byte[1024];
int len;
while ((len = imageStream.read(b, 0, 1024)) != -1) {
response.getOutputStream().write(b, 0, len);
}
}
ActivitiService 里的方法
// 获取流程图 执行到哪里红色显示
public InputStream tracePhoto(String processDefinitionId, String executionId) {
// ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(executionId).singleResult();
BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
List activeActivityIds = new ArrayList();
if (runtimeService.createExecutionQuery().executionId(executionId).count() > 0){
activeActivityIds = runtimeService.getActiveActivityIds(executionId);
}
// 不使用spring请使用下面的两行代码
// ProcessEngineImpl defaultProcessEngine = (ProcessEngineImpl)ProcessEngines.getDefaultProcessEngine();
// Context.setProcessEngineConfiguration(defaultProcessEngine.getProcessEngineConfiguration());
// 使用spring注入引擎请使用下面的这行代码
Context.setProcessEngineConfiguration(processEngineFactory.getProcessEngineConfiguration());
// return ProcessDiagramGenerator.generateDiagram(bpmnModel, "png", activeActivityIds);
return processEngine.getProcessEngineConfiguration().getProcessDiagramGenerator()
.generateDiagram(bpmnModel, "png", activeActivityIds);
}
我这里只是测试了一个非常简单的流程,当然,实际开发中基本会在流程图里用到互斥网关,或者并行网关什么的,开发起来也差不多,无非是复杂点,这里就不一一介绍了。