如果单纯使用activiti进行流程的自动控制,是可以实现的。但是通常我们都需要结合自定义的表,便于在流程执行中更加清晰的看到每一个流程实例节点的具体信息。关联自定义表与activiti表才能完成真正的业务

BusinessKey关联

// 定义businessKey
@Test
public void addBusinessKey(){
  // 获取流程引擎
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  // 获取启动流程service
  RuntimeService runtimeService = engine.getRuntimeService();
  // 启动流程  并添加businessKey
  // param1:流程ID  param2:businessKey
  ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("forLeave", "1001");
  System.out.println("businessKey=="+processInstance.getBusinessKey());
}

当我们启动流程的时候,activiti将businessKey添加到了act_ru_execution表中。该表就是我们将自定义表和activiti表关联的重点

流程定义的挂起与激活

// 全部流程挂起与激活
@Test
public void suspendAllprocessInstance(){
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  RepositoryService repositoryService = engine.getRepositoryService();
  // 获取流程定义查询对象
  ProcessDefinition forLeave = repositoryService.createProcessDefinitionQuery()
    .processDefinitionKey("forLeave")
    .singleResult();
  // 查询当前流程任务是否为挂起状态  true挂起状态  false
  boolean suspended = forLeave.isSuspended();
  // 流程定义id
  String id = forLeave.getId();
  if (suspended){
    // 挂起就激活
    // param1:流程任务id  param2:是否挂起  param3:激活时间
    repositoryService.activateProcessDefinitionById(id,true,null);
    System.out.println("流程定义id=="+id+"已激活");
    // 激活状态为:1
  }else {
    // 激活就挂起
    // param1:流程定义id  param2:是否暂停  param3:暂停时间
 repositoryService.suspendProcessDefinitionById(id,true,null);
    System.out.println("流程定义id=="+id+"已挂起");
    // 挂起状态为:2
  }

可以通过查看运行时执行表act_ru_execution的SUBPENSION_STATE查看,状态为1表示该任务已激活,状态为2表示已挂起

单个流程实例挂起与激活

@Test
public void suspendSingleProcess(){
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  RuntimeService runtimeService = engine.getRuntimeService();
  ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
    .processInstanceId("27501")
    .singleResult();
  // true 已挂起  false  已激活
  boolean suspended = processInstance.isSuspended();
  // 获取流程实例id
  String id = processInstance.getId();
  // 挂起和激活转换
  if (suspended){
    runtimeService.activateProcessInstanceById(id);
    System.out.println("流程实例id"+id+"已激活");
  }else {
    runtimeService.suspendProcessInstanceById(id);
    System.out.println("流程实例id"+id+"已挂起");
  }
}

可以对比流程定义和流程实例的挂起激活:

流程定义是指某一种任务类型,该类型的全部待处理任务会全部挂起或者执行。比如请假,那么不会单独针对张三或者李四的请假操作,而是针对所有的请假流程进行操作。流程定义的挂起与激活通过的是RepositoryService

流程实例是指某一流程定义下的某一个流程,比如张三的请假审批流程就是一个流程实例。那么就可以通过单个操作来对张三进行激活和挂起操作。流程实例是通过RunTimeService进行操作。

两者的激活都是通过activatePrecess***ById进行转换,挂起都是通过suspendProcess ***ById

任务负责人分配–uel表达式

首先在绘制bpmn时使用${key}表达式,再自定义填充key。

activiti排除springsecurity_spring

填充完毕之后,进行流程部署然后在启动流程时利用map集合进行传参。map集合的key对应上述的uel表达式中的key,activiti会自动帮我们完成该表达式的值替换:

// 测试uel表达式分配负责人
@Test
public void test02(){
  ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
  RuntimeService runtimeService = engine.getRuntimeService();
  // 启动时传入负责人
  // map用于替换bpmn的uel表达式
  Map<String, Object> map = new HashMap<>();
  map.put("assignee0","六六");
  map.put("assignee1","七七");
  ProcessInstance vacationIns = runtimeService.startProcessInstanceByKey("vacation", map);
}

如上述程序所示,最终可以通过查看act_ru_task表会发现对应的assignee字段是我们预先定义好的key,而后面的text字段就是替换后的值value:

activiti排除springsecurity_spring boot_02

流程变量

流程变量:在流程的审批执行过程中,经常会需要一些变量来作为判决跳转下一流程节点的依据,这个变量就称之为流程变量。
举例:员工想要预支薪水,如果预支金额小于月薪那么直接经部门经理审批后上报财务即可,反之如果预支金额大于月薪那么需要上报总经理,通过后再上报财务即可。此时的预支金额就是流程变量

globa变量是流程变量的默认作用域,也就是流程实例,globa变量不允许重复,如果两次或多次使用了统一globa变量名那么后续的值会覆盖前面的值

local变量:针对一个任务task和一个执行实例范围,没有globa范围大。local变量由于存在于不同的任务或者不同的实例中,所以相互间没有影响,变量名可以一致,也可以与globa变量名一致

排他网关ExclusiveGateway

网关用于控制流程分支的走向。
排他网关是当流程执行至此网关时,所有分支都会判断条件是否为true,如果为true则执行该分支。排他网关只会选择一个为true的分支执行,如果有多个分支为true,那么就会选择id值较小的一个执行

springBoot整合activiti

首先导入依赖资源

<!--     activiti整合springboot   -->
<dependency>
  <groupId>org.activiti</groupId>
  <artifactId>activiti-spring-boot-starter</artifactId>
  <version>7.1.0.M1</version>
</dependency>

配置application.yaml文件

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/activiti?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 123456
    type: com.alibaba.druid.pool.DruidDataSource
  activiti:
    #1.flase:默认值。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常
    #2.true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
    #3.create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
    #4.drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
    database-schema-update: true
    # 检测历史信息表 activiti7默认不生成信息表  此处开启
    db-history-used: true
    # 历史记录存储等级
    history-level: full
    check-process-definitions: true

在主程序处排除掉springSecurity框架

@SpringBootApplication(exclude = { 
	DataSourceAutoConfiguration.class, 
	SecurityAutoConfiguration.class, 
	SecurityAutoConfiguration.class,
  ManagementWebSecurityAutoConfiguration.class
})

最后我们需要保证已经编绘好的bpmn文件位于resources文件夹下的processes目录下,否则activiti会找不到文件无法创建表

如下图所示:

activiti排除springsecurity_后端_03

如果是第一次使用activi的话就直接启动主程序,activiti将会自动的在对应数据库中建表,并且如果在你的processes包下扫描到了对应的bpmn文件那么activiti也会自动的进行流程部署

测试启动流程:

@Controller
public class TestController {

    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    @RequestMapping(value = "/hello/{id}")
    @ResponseBody
    public String testHello(@PathVariable("id") String id){
        // 测试完成任务
        Task task = taskService.createTaskQuery()
                .processInstanceId(id)
                .singleResult();
        taskService.complete(task.getId());
        return "完成任务人=="+task.getAssignee()+
                "获取到的id=="+id+
                "get到的id=="+task.getId();
    }

    @RequestMapping(value = "/create")
    @ResponseBody
    public String testCreate(){
        // 定义负责人名称
        Map<String, Object> maps = new HashMap<>();
        maps.put("a1","五一");
        maps.put("a2","六一");
        ProcessInstance holidy = runtimeService.startProcessInstanceByKey("holiday", maps);
        return "流程实例ID=="+holidy.getId()+
                "流程实例ID=="+holidy.getProcessInstanceId()+
                "流程定义Id=="+holidy.getProcessDefinitionId()+
                "流程定义Name=="+holidy.getProcessDefinitionName();
    }
}