activiti6工作流开发
转载请标明出处:鸭梨的药丸哥
工作流数据库准备
activiti6依赖于SQL数据库,要运行activiti6需要准备好SQL数据库,在activiti部署前要求准备好表的创建。
Activiti有3种表创建方式:
DB_SCHEMA_UPDATE_FALSE //不能自动创建表,需要表存在DB_SCHEMA_UPDATE_CREATE_DROP //启动引擎时创建表,关闭引擎时删除表DB_SCHEMA_UPDATE_TRUE //如果表不存在,自动创建表
Activiti有3种启动方式:
- 通过编码的方式,
ProcessEngineConfiguration 硬编码配置信息,然后启动activiti - 自定义配置文件方式,读过读取指定指定xml配置文件的配置启动activiti
- 通过默认方式(读取classpath路径下activiti.cfg.xml文件)
启动方式一
通过编码的方式启动activiti,启动时注意表的创建方式,如果需要完全初始化的方式启动,请使用DB_SCHEMA_UPDATE_CREATE_DROP 。
public void initTable(){
ProcessEngineConfiguration engineConfiguration = ProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
ProcessEngine processEngine = engineConfiguration.setJdbcDriver("com.mysql.cj.jdbc.Driver")
.setJdbcUrl("jdbc:mysql://localhost:3306/activiti?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=false")
.setJdbcUsername("root")
.setJdbcPassword("xxx")
.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE)
.buildProcessEngine();
}
启动方式二
添加配置信息
<?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.cj.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/activiti?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8"></property>
<property name="jdbcUsername" value="root"></property>
<property name="jdbcPassword" value="xxxxx"></property>
<property name="databaseSchemaUpdate" value="true"></property>
</bean>
</beans>
使用指定配置文件
public void initTable(){
ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
ProcessEngine processEngine = processEngineConfiguration.buildProcessEngine();
}
启动方式三
private ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
表的生成
日志表:
act_evt_log 存储事件处理日志,方便管理员跟踪处理
通用数据表:
act_ge_bytearray 二进制数据表,一些文件存在这个表。
act_ge_property 属性数据表存储整个流程引擎级别的数据,初始化表结构时,会默认插入三条记录
历史数据表:
act_hi_actinst 历史节点表
act_hi_attachment 历史附件表
act_hi_comment 历史意见表
act_hi_detail 历史详情表,提供历史变量的查询
act_hi_identitylink 历史流程人员表
act_hi_procinst 历史流程实例表
act_hi_taskinst 历史任务实例表
act_hi_varinst 历史变量表
用户组织表:
act_id_group 用户组信息表
act_id_info 用户扩展信息表
act_id_membership 用户与用户组对应信息表
act_id_user 用户信息表
资源流程规则表:
act_procdef_info 流程定义信息
act_re_deployment 部署信息表
act_re_model 流程设计模型部署表
act_re_procdef 流程定义数据表
运行时数据库表
act_ru_event_subscr 监听表
act_ru_execution 运行时流程执行实例表
act_ru_identitylink 运行时流程人员表,主要存储任务节点与参与者的相关信息
act_ru_job 运行时定时任务数据表
act_ru_task 运行时任务节点表
act_ru_variable 运行时流程变量数据表
绘制流程图
如果是idea2019版本之前的可以使用actiBPM插件进行绘制。
如果是idea2019版本之后,则需要使用activiti bpmn visualizer等其他插件进行代替。
下面步骤将按照这个流程进行。
流程部署
使用bpmn文件部署流程
public String deploy(){
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deploy = repositoryService.createDeployment()
.addClasspathResource("store.bpmn")
.name("入库审批流程")
.deploy();
return deploy.getId();
}
使用压缩包部署流程
使用压缩包时,压缩包里面必须有bpmn文件,否则是无法进行解压部署的
public String deploy(){
RepositoryService repositoryService = processEngine.getRepositoryService();
InputStream in = this.getClass().getClassLoader().getResourceAsStream("store.zip");
ZipInputStream zipInputStream = new ZipInputStream(in);
Deployment deploy = repositoryService.createDeployment()
.addZipInputStream(zipInputStream)
.name("入库审批流程")
.deploy();
return deploy.getId();
}
流程部署分析
- 在流程定义数据表(
act_re_procdef )中生成流程声明,id是流程图的id:版本:序列号(系统生成),KEY是流程图的id。 - 在二进制数据表(
act_ge_bytearray )中保存数据文件,如bpmn,png(流程图片会自动生成)等 - 在部署信息表(
act_re_deployment ) 中生成一条部署信息
流程启动
每启动一个流程,那么就会在数据库生成相应的流程实例信息。
通过流程声明KEY启动
流程声明KEY于定义该流程声明的流程图id一致
public void start(){
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_1");
System.out.println("实例ID:"+processInstance.getId());
System.out.println("流程定义ID:"+processInstance.getProcessDefinitionId());
}
通过流程定义ID启动
public void start(){
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceById("myProcess_1:1:2504");
System.out.println("实例ID:"+processInstance.getId());
System.out.println("流程定义ID:"+processInstance.getProcessDefinitionId());
}
流程启动分析
当流程启动后,会生成流程实例信息,act_ru_execution记录基本的流程实例信息,act_ru_task记录当前有哪些任务在运行,act_ru_identitylink记录任务相关的人物信息。
- 在运行时流程任务表(act_ru_task),跟新执行任务信息。其中act_ru_task的
PROC_INST_ID_ 记录这该任务与哪个流程实例信息绑定。PROC_DEF_ID_ 记录的是流程声明的id(act_re_procdef中的ID) - 跟新运行时流程执行表(act_ru_execution),跟新现在正在执行的流程实例信息。
- 跟新执行主体相关信息表(act_ru_identitylink),跟新执行任务的相关人物信息
- 在历史流程人员表(act_hi_identitylink)生成一条历史记录
任务查找
通过任务代理人名称查询
public void find(){
TaskService taskService = processEngine.getTaskService();
List<Task> taskList = taskService.createTaskQuery()
.taskAssignee("维修工")
.list();
for (Task task : taskList) {
System.out.println("任务ID:"+task.getId());
System.out.println("任务名称:"+task.getName());
System.out.println("执行者:"+task.getAssignee());
System.out.println("流程定义ID:"+task.getProcessDefinitionId());
}
}
通过流程实例id查询
根据正在运行的流程实例的id查找当前要实现的任务
public void find(){
TaskService taskService = processEngine.getTaskService();
List<Task> taskList = taskService.createTaskQuery()
.processInstanceId("5001")
.list();
for (Task task : taskList) {
System.out.println("任务ID:"+task.getId());
System.out.println("任务名称:"+task.getName());
System.out.println("执行者:"+task.getAssignee());
System.out.println("流程定义ID:"+task.getProcessDefinitionId());
}
}
任务执行
通过任务id执行任务
public void complete(){
TaskService taskService = processEngine.getTaskService();
taskService.complete("5004");
}
任务执行分析
-
如果当前流程实例的所有任务已经完成,那么运行时流程执行实例表(act_ru_execution )流程实例信息将会销毁,以及对应在运行时任务节点表(act_ru_task )的任务信息销毁(因为已经执行完了),运行时流程人员表(act_ru_identitylink )对应的流程人员会被销毁(任务完成了,不需要了)。 -
如果当前流程实例尚未完成,那么将会跟新运行时任务节点表(act_ru_task )的任务信息,并更新运行时流程人员表(act_ru_identitylink )对应的流程人员
流程变量
流程实例能捆绑变量,如:提交请假申请(往往会捆绑一个请假单),通过申请单的id那么就可以获取申请了多少假期(申请单应该存储在另一个业务系统中)。
为了能捆绑第三方业务系统的信息,activiti提供了流程执行的过程中捆绑变量的功能,通过进行变量的捆绑,每个流程实例都可以拥有独自的变量信息。
流程中的变量会跟随流程的进行而传递,也就是说上一个任务设置的变量在下一个任务中也都能获取,不会因为流程任务状态更新而导致变量丢失不见的问题
假设现在在第三方业务系统上有一个账单信息实体类,必须实现序列化:
@Data
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
public class RepairBillDetail implements Serializable {
private static final long serialVersionUID = -1914045242557039080L;
private Long id;
private String repairInfo;
private BigDecimal price;
}
启动流程时传递变量
public void startByVar(){
RepairBillDetail repairBillDetail = new RepairBillDetail(10001L, "维修账单", BigDecimal.valueOf(500.5));
HashMap<String, Object> map = new HashMap<>();
map.put("bill",repairBillDetail);
RuntimeService runtimeService = processEngine.getRuntimeService();
ProcessInstance processInstance = runtimeService.startProcessInstanceById("myProcess_1:1:2504",map);
System.out.println("实例ID:"+processInstance.getId());
System.out.println("流程定义ID:"+processInstance.getProcessDefinitionId());
}
指定任务传递变量
public void setVarByTaskId(){
TaskService taskService = processEngine.getTaskService();
String taskId = "5012";
RepairBillDetail repairBillDetail = new RepairBillDetail(10001L, "维修账单2", BigDecimal.valueOf(500.5));
taskService.setVariable(taskId,"bill2",repairBillDetail);
Map<String, Object> variables = taskService.getVariables(taskId);
System.out.println(variables);
}
完成任务时传递变量
public void completeAndSetVar(){
RepairBillDetail repairBillDetail = new RepairBillDetail(10001L, "维修账单3", BigDecimal.valueOf(500.5));
HashMap<String, Object> map = new HashMap<>();
map.put("bill",repairBillDetail);
map.put("var2","字符串变量");
map.put("var3",100);
String taskId = "5012";
TaskService taskService = processEngine.getTaskService();
taskService.complete(taskId,map);
}
获取变量
public void getVarByTaskId(){
Map<String, Object> variables = taskService.getVariables(taskId);
System.out.println(variables);
}
变量存储分析
- 普通的变量是存储在运行时流程变量数据表(
act_ru_variable ),比如Long,String等 - 对象变量是存储在运行时流程变量数据表(
act_ru_variable ),二进制数据表(act_ge_bytearray )这两张表中,其中act_ru_variable 记录着变量的详情,而对象序列化数据是存储在act_ge_bytearray 。
变量在流程中使用案例
画图插件的问题
在IDEA2020版本后插件actiBPM 已经不能兼容使用了,这里我使用画图工具activiti BPMN visualizer 。
这里使用activiti BPMN visualizer 插件进行流程图的开发。
请假示例
画下面流程图,其中请假的人使用变量
s
t
u
N
a
m
e
来
代
替
,
班
主
任
使
用
{stuName}来代替,班主任使用
stuName来代替,班主任使用{tchName}来动态指定,系主任使用${deanName}指定,应该注意的是这些变量应该是来自第三方业务系统的。
简单的流程代码
public class LeaveFlow {
private final ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
public Deployment deploy(String resource, String name){
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeployment()
.addClasspathResource("test.bpmn20.xml")
.name("请假申请流程")
.deploy();
System.out.println("部署信息id:"+deployment.getId());
return deployment;
}
public ProcessInstance start(ProcessDefinition definition,String assignee){
RuntimeService runtimeService = processEngine.getRuntimeService();
HashMap<String, Object> map = new HashMap<>();
map.put("stuName",assignee);
ProcessInstance processInstance = runtimeService.startProcessInstanceById(definition.getId(),map);
return processInstance;
}
public void completTask(String taskId, Map var){
TaskService taskService = processEngine.getTaskService();
taskService.complete(taskId,var);
}
public Task findTask(ProcessInstance instance,String assignee){
TaskService taskService = processEngine.getTaskService();
Task task = taskService.createTaskQuery()
.processInstanceId(instance.getId())
.taskAssignee(assignee)
.singleResult();
return task;
}
public ProcessDefinition findProcessDefinition(Deployment deployment){
RepositoryService repositoryService = processEngine.getRepositoryService();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
.deploymentId(deployment.getId())
.singleResult();
return processDefinition;
}
public Deployment findDeployment(String deployName){
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deployment = repositoryService.createDeploymentQuery()
.deploymentName(deployName)
.singleResult();
return deployment;
}
public static void main(String[] args) {
LeaveFlow leaveFlow = new LeaveFlow();
String resure = "test.bpmn20.xml";
String deployName = "请假申请流程";
Deployment deploy = leaveFlow.findDeployment(deployName);
ProcessDefinition processDefinition = leaveFlow.findProcessDefinition(deploy);
ProcessInstance processInstance = leaveFlow.start(processDefinition, "张三");
Task task = leaveFlow.findTask(processInstance, "张三");
HashMap hashMap = new HashMap() {{
put("days",5);
put("tchName","陈老师");
}};
leaveFlow.completTask(task.getId(),hashMap);
Task task2 = leaveFlow.findTask(processInstance, "陈老师");
hashMap.put("deanName","张主任");
leaveFlow.completTask(task2.getId(),hashMap);
Task task3 = leaveFlow.findTask(processInstance, "张主任");
if (task3!=null)leaveFlow.completTask(task3.getId(),null);
}
}
流程分析
步骤流程如下:
- 部署,在(
act_re_deployment )(act_re_procdef )(act_ge_bytearray )分别生成一条或条信息。分别是流程部署信息(包含部署id,部署名称等),流程定义(包含定义id,流程图识别符key,),二进制数据(包含两条,分别是图片,流程声明文件) - 启动流程,通过流程定义的id或者流程定义中的key(流程图id)启动流程,生成一个流程实例。流程实例存储在(
act_ru_execution ),而当前正要执行的任务存放在(act_ru_task )。 - 执行任务,执行任务需要指定要实现任务的id,通常通过
流程实例id 和执行人name 去锁定某个流程实例中的任务节点,锁定任务节点后,完成任务。 - 条件判断和变量赋值,不同的任务有不同的判断条件和变量,根据条件去确定下一个任务节点,条件中的变量可以在流程执行的过程中进行添加,如条件判断变量,任务执行者变量等。
流程网关
activiti中有两种流程网关,一种是并行网关,一种是排他网关,包含网关,事件网关。
排他网关
排他官网流程跟上面的请假示例相识,是根据条件来判断流程执行步骤,设置变量即可能进行流程判断了。
并行网关
并行网关没有判断条件,当请假任务完成后,将会同时生成两个任务节点,要完成两个任务节点才能进入下一任务节点。如下,要完成班主任审核,系主任审核才能完成请假流程。
包含网关
包含网关是排他和并行的结合,如下:当满足请假天数days>3这个添加时,需要班主任和系主任同时审核,当days<=3时,只需要班主任审核。
流程任务
ServiceTask
服务任务ServiceTask,通过委派模式供activiti执行第三方任务代码。
委派监听类如下:
public class ServiceTask implements JavaDelegate {
@Autowired
private MyService myService;
@Override
public void execute(DelegateExecution execution) throws Exception {
myService.doSome();
execution.setVariable("var","设置变量");
}
}
MailTask
邮件发送任务,使用POP3邮件接受协议和SMTP邮件发送协议实现邮件发送。
添加配置信息
只需要在
<property name="mailServerHost" value="smtp.qq.com"/>
<property name="mailServerPort" value="465"/>
<property name="mailServerDefaultFrom" value="xxxxx@qq.com"/>
<property name="mailServerUsername" value="xxxxx@qq.com"/>
<property name="mailServerPassword" value="123456487"/>
<property name="mailServerUseSSL" value="true"/>
画图
|