Activiti_1
首先要讲一下什么是工作流:
将一组任务组织起来以完成某个经营过程,定义了任务的出发顺序和条件,每个任务可以由一个活多个软件系统完成.
Activiti项目是一项基于Apache许可的开源BPMN平台,支持BPMN 2.0标准.可以将"图"转换为xml文档,最后编程解析.
Activiti官方主页:http://www.activiti.org/index.html
下载:http://www.activiti.org/download.html
用户指南:http://activiti.org/userguide/index.html (用户指南来学习Activiti)
在线API文档: http://activiti.org/javadocs/index.html (开发的时候参阅)
设计表
Activiti共有25张表,来记录存储参与流程的用户主体,组,流程定义的存储,流程的历史信息等.这些表大概分为五类:
ACT_GE_* 开头代表通用表,存放通用数据
ACT_HI_* 开头代表历史数据表
ACT_ID_* 开头是保存身份信息的表
ACT_RE_* 开头是保存静态信息的表(流程的定义,流程的资源)
ACT_RU_* 开头是运行时数据表,流程结束,该表会被清空
留意 ‘act_ru_task ‘,’ act_re_procdef’,’ act_hi_taskinst '
流程概念和术语
ProcessDefinition: 代表业务流程,用于定义流程中不同步骤的结构和行为.
部署流程: 将流程定义加载到Activiti数据库中.
流程process: 运行流程定义后,成为流程process.
processInstance: 是业务流程的一个实例.
StartEvent和 EndEvent: 标识流程的切入点和流程的结束.
任务: 流程切入点和结束之间的所有步骤,成为任务,如UserTask.
assignee: 表示执行者
生成工作流表
非配置方案
1.首先创建db_activiti数据库
2.获取流程引擎配置
// 生成工作流需要的表
public void initTable() {
// 获取流程引擎配置对象,此种方式不需要配置文件
ProcessEngineConfiguration engineConfiguration = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();
engineConfiguration.setJdbcDriver("com.mysql.jdbc.Driver");
engineConfiguration.setJdbcUrl("jdbc:mysql://localhost:3306/db_activiti?characterEncoding=utf-8");
engineConfiguration.setJdbcUsername("root");
engineConfiguration.setJdbcPassword("root");
// 设置 表的更新
engineConfiguration.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
// 得到引擎流对象
ProcessEngine processEngine = engineConfiguration.buildProcessEngine();
// 后续使用 processEngine 进行操作工作流表
}
配置方式
1.resources下创建activiti.cfg.xml文件.
2.在此xml文件中配置工作流引擎.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<!-- 加载数据源-->
<property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db_activiti?characterEncoding=utf-8"/>
<property name="jdbcUsername" value="root"/>
<property name="jdbcPassword" value="root"/>
<!-- 设置数据库表的更新-->
<property name="databaseSchemaUpdate" value="true"/>
</bean>
</beans>
3.加载xml配置文件
public void initTableWithXml() {
// 获取流程引擎配置对象,通过配置文件
ProcessEngineConfiguration processEngineConfiguration =
ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
// 得到引擎流对象
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
// 后续使用 processEngine 进行操作工作流表
}
注意: activiti-engine中已经加入了spring包,即spring-bean,所以整合到项目中时,要注意与spring的包产生冲突(项目中把工作流引擎的包放在最后)
流程图本质–xml文件
我们可以将bpmn文件后缀改成xml,进去可以看到内容主要在标签中,
Demo
部署流程
画好一个流程图test.bpmn后,开始部署流程,有以下两种方式:
//部署流程
public void deploy() {
//得到引擎流对象得到部署的服务层对象
RepositoryService repositoryService = processEngine.getRepositoryService();
//调用部署能力
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("test.bpmn")//加载流程文件
.name("请假")//流程名称
.deploy();//部署
System.out.println("部署ID: " + deploy.getId());
}
// 部署流程,ZIP方式
public void deployWithZip() {
InputStream inputStream = this.getClass()//获取当前class对象
.getClassLoader()//获取类加载器
.getResourceAsStream("qjTest.zip");//指定文件资源输入流对象
ZipInputStream zipInputStream = new ZipInputStream(inputStream);//实例化zip流对象
Deployment deploy = processEngine.getRepositoryService()//部署service
.createDeployment()//创建部署
.name("请假")//流程名称
.addZipInputStream(zipInputStream)//添加zip输入流
.deploy();//部署
System.out.println("流程部署ID: " + deploy.getId());
System.out.println("流程部署名字: "+ deploy.getName());
}
随后测试可以看到,在表 ‘act_re_deployment’,'act_re_prodef ','act_ge_bytearray’中会有相应的数据变化.
启动流程实例
启动流程依靠流程定义表中的ksy_字段启动(即ID)
// 启动流程
public void start() {
// 需要得到运行的服务层对象
RuntimeService runtimeService = processEngine.getRuntimeService();
// 根据key启动流程
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_1");
// 根据流程实例获取具体信息
System.out.println("流程实例ID: " + processInstance.getId());
System.out.println("流程定义ID: " + processInstance.getProcessDefinitionId());
}
随后测试可以看到,在表 ‘act_ru_task’,‘act_ru_execution’,'act_ru_identitylink’中会有相应的数据变化.
查询任务
查看指定用户的任务
// 查询任务
public void find() {
// 得到任务的服务层对象
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery()
.taskAssignee("经理")//查询条件
.list();
for (Task task : list) {
System.out.println("任务ID: " + task.getId());
System.out.println("任务名称: " + task.getName());
System.out.println("执行者: " + task.getAssignee());
System.out.println("流程定义Id: " + task.getProcessDefinitionId());
}
}
完成任务
// 完成任务
public void complete() {
// 得到服务层对象
TaskService taskService = processEngine.getTaskService();
taskService.complete("15002");
}
此时流程已经走完,ru表中数据已被清空,‘act_hi_taskinst’,‘act_hi_procinst’,‘act_hi_identitylink’,'act_hi_actinst’表中数据发生变化.
流程的CRUD
流程修改
流程定义后无法修改,只能通过追加版本号的方式来实现修改(ket_字段值要保持不变,修改后多次部署).
流程查询
流程定义查询
即查询act_re_procdef表,可以通过key查询(返回集合),也可以通过ID查询(返回单个结果)
public void findDef(){
RepositoryService repositoryService = processEngine.getRepositoryService();
List<ProcessDefinition> definitionList = repositoryService.createProcessDefinitionQuery()
.processDefinitionKey("myProcess_1")//按照key查询
.list();
for (ProcessDefinition processDefinition : definitionList) {
System.out.println("流程信息:"+processDefinition.getId()+"\t"+processDefinition.getDeploymentId()+"\t"+processDefinition.getVersion());
}
}
public void findDefId(){
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.processDefinitionId("qjProcess:1:20004")//按照id查询
.singleResult();
System.out.println("流程信息:"+processDefinition.getId()+"\t"+processDefinition.getDeploymentId()+"\t"+processDefinition.getVersion());
}
查询历史
即查询act_hi_taskinst表,使用finished接口,查询已完结的任务,使用unfinished接口,查询未完结的任务;若不加,默认查询全部任务.
public void findHis(){
HistoryService historyService = processEngine.getHistoryService();
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
.finished()
.list();
for (HistoricTaskInstance ht : list) {
System.out.println(ht.getId()+"\t"+ht.getName()+"\t"+ht.getStartTime()+"\t"+ht.getEndTime());
}
}
也可以按照时间查询
//按照时间查询
public void findHisTime() throws ParseException {
String start = "2020-04-28";
String end = "2020-04-29";
HistoryService historyService = processEngine.getHistoryService();
List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery()
.taskCreatedAfter(DateUtils.stringToDate(start))//自定义日期转换工具类
.taskCreatedBefore(DateUtils.stringToDate(end))
.list();
for (HistoricTaskInstance hist : list) {
System.out.println(hist.getId()+"\t"+hist.getName()+"\t"+hist.getStartTime()+"\t"+hist.getEndTime());
}
}
查询流程状态
public void findExe(){
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
.processInstanceId("12501")
.singleResult();
if(processInstance!=null){
System.out.println("流程在运行中");
}else{
System.out.println("流程没有运行");
}
}
流程变量
流程中用来存储数据的变量,支持所有的基本类型,以及序列化对象;每个流程实例的流程变量是独立的.
说白了流程变量就是在执行过程中传递参数的.
设置方式
- 在任务节点获取到Service(TaskService或RuntimeService),并在act_run_task 表中找到id作为任务id
- taskService.setVariables,或通过map封装,随后与任务id绑定,以key-value形式传递参数(对象)
- 下一个任务节点,可通过任务id + 参数的key值,在taskService中getVariable
public class StuDTO implements Serializable {
private long sId;
private String stuName;
}
设置变量 :
TaskService taskService = processEngine.getTaskService();
String taskID = "32504";
// 设置对象参数
Map<String, Object> variables = new HashMap<>();
variables.put("days", "3");
variables.put("reason", "生病休息");
StuDTO stuDTO = new StuDTO(1L, "zhang");
variables.put("stu", stuDTO);
taskService.complete(taskID, variables);
//获取变量
TaskService taskService = processEngine.getTaskService();
// 得到请求的参数
String taskId = "32504";
String days = (String)taskService.getVariable(taskId, "days");
String reason = (String)taskService.getVariable(taskId, "reason");
StuDTO stu = (StuDTO) taskService.getVariable(taskId, "stu");
System.out.println("请假天数: "+days);
System.out.println("原因: "+reason);
System.out.println("学员信息: "+stu.getSId()+"\t"+stu.getStuName());
taskService.complete(taskId);
当然我们也可以在启动流程实例时传递流程变量,在之后的任务节点获取
流程分支
当流程任务出现分支,可以通过以下方式解决
连线
流程实例具体执行时,需要通过设置的流程变量的值来决定具体执行的线路,此时可以设置连线执行表达式
Condition = ${流程变量判断条件}
网关
排他网关
互斥,根据条件只能走一条线路
并行网关
多条线路并行执行,都执行完才继续执行后面的
具体流程变量设置,获取方式,同上文方式
任务分配
- 在流程图的Assignee中直接填写执行人
- 设置Assignee的流程变量,使用EL表达式 ${},可以动态分配,任务执行时也要动态的指定执行人
监听器
事件包括:
create:任务创建后触发
Assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件都触发
执行表达式
Condition = ${流程变量判断条件}
网关
排他网关
互斥,根据条件只能走一条线路
并行网关
多条线路并行执行,都执行完才继续执行后面的
具体流程变量设置,获取方式,同上文方式
任务分配
- 在流程图的Assignee中直接填写执行人
- 设置Assignee的流程变量,使用EL表达式 ${},可以动态分配,任务执行时也要动态的指定执行人
[外链图片转存中…(img-yNAs2ZzR-1597043191180)]
监听器
事件包括:
create:任务创建后触发
Assignment:任务分配后触发
Delete:任务完成后触发
All:所有事件都触发