• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

springboot集成activiti和使用方法排除spring security

武飞扬头像
像风琢磨不住
帮助1

一、activiti依赖及工具

1、springboot依赖

注意排除mybatis和el表达式依赖

   <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring-boot-starter</artifactId>
        <version>7.0.0.Beta2</version>
        <exclusions>
                <exclusion>
                    <artifactId>mybatis</artifactId>
                    <groupId>org.mybatis</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>el-api</artifactId>
                    <groupId>javax.el</groupId>
                </exclusion>
            </exclusions>
   </dependency>

2、下载idea相关插件

画流程图相关插件
学新通

二、配置activiti

1、参数相关介绍如下

spring:
  activiti:
    #自动更新数据库结构
    #1.flase:默认值。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常
    #2.true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建
    #3.create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)
    #4.drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)
    database-schema-update: true
    #activiti7默认不生成历史信息表,开启历史表
    db-history-used: true
    #记录历史等级 可配置的历史级别有none, activity, audit, full
    #none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。
    #activity:级别高于none,保存流程实例与流程行为,其他数据不保存。
    #audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。
    #full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。
    history-level: full
    # =============================
    #自动检查、部署流程定义文件 启动时自动部署定义的流程
    check-process-definitions: true
    # asyncExecutorActivate是指activiti在流程引擎启动就激活AsyncExecutor,异步:true-开启(默认)、false-关闭
    async-executor-activate: true
  #流程定义文件存放目录,要具体到某个目录
  #      process-definition-location-prefix: classpath:/processes/holliday/
  #process-definition-location-suffixes: #流程文件格式
  #  - **.bpmn20.xml
  #  - **.bpmn
学新通

2、创建流程文件

创建新流程文件默认在此目录下,当然也可以在yml文件中通过配置process-definition-location-prefix指定位置。
学新通

3、创建新流程文件

学新通

4、springboot排除springSecurity自动装配

@SpringBootApplication(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
//@SpringBootApplication
@MapperScan(basePackages = "com.jt.dao")
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

5、增加config跳过springSecurity验证

此config仅仅是为了跳过springSecurity验证

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;


/**
 * @author jiangtao
 * 该配置类仅仅为了跳过spring-security的验证
 */
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
}

6、启动项目

启动该springboot项目,会在mysql中创建25张表
学新通

三、画流程图

  1. 最简单的双节点流程图
    学新通

  2. 生成的xml文件

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.activiti.org/processdef">
  <process id="leaveDemo" name="leaveDemo" isExecutable="true">
    <userTask id="_3" name="secondUser" activiti:assignee="李四"/>
    <startEvent id="_1" name="startEvent"/>
    <endEvent id="_4" name="endEvent"/>
    <userTask id="_2" name="firstUser" activiti:assignee="张三"/>
    <sequenceFlow id="sid-f594c092-ac67-40ee-a41d-ce952d8844ad" sourceRef="_1" targetRef="_2"/>
    <sequenceFlow id="sid-c685f76f-b3a2-4edd-8a58-f8b8c055a070" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="sid-145ed254-20fd-47d3-9c08-1f545b99f35b" sourceRef="_3" targetRef="_4"/>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_leave">
    <bpmndi:BPMNPlane bpmnElement="leaveDemo" id="BPMNPlane_leave">
      <bpmndi:BPMNShape id="shape-3c81f674-87f0-4fea-8040-7700dbb20a94" bpmnElement="_3">
        <omgdc:Bounds x="44.04801" y="80.552" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-0a797082-f4d4-4378-a571-751e7a94fcda" bpmnElement="_1">
        <omgdc:Bounds x="60.385612" y="-149.3312" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-938cb97e-5123-433f-9822-4cfb66208a80" bpmnElement="_4">
        <omgdc:Bounds x="69.5556" y="206.69162" width="30.0" height="30.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape id="shape-0eef7a46-93d8-4571-aba2-fa4524294cd1" bpmnElement="_2">
        <omgdc:Bounds x="34.555603" y="-69.0972" width="100.0" height="80.0"/>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge id="edge-6965c761-2b2c-4a73-a3bd-5aa9fa568784" bpmnElement="sid-f594c092-ac67-40ee-a41d-ce952d8844ad">
        <omgdi:waypoint x="75.38561" y="-119.33121"/>
        <omgdi:waypoint x="84.5556" y="-69.0972"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-2f655aef-dfd0-415d-9e2b-c4730cc74b73" bpmnElement="sid-c685f76f-b3a2-4edd-8a58-f8b8c055a070">
        <omgdi:waypoint x="84.5556" y="10.9028015"/>
        <omgdi:waypoint x="84.5556" y="80.552"/>
        <omgdi:waypoint x="94.04801" y="80.552"/>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge id="edge-424dfd94-8841-44ca-bfbf-2542b172bfc2" bpmnElement="sid-145ed254-20fd-47d3-9c08-1f545b99f35b">
        <omgdi:waypoint x="94.04801" y="160.552"/>
        <omgdi:waypoint x="92.0556" y="206.69162"/>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>
学新通

四、开启流程并运行

1、查看所有已经发布的流程定义

List<ProcessDefinition> list = repositoryService
                .createProcessDefinitionQuery()
                .orderByProcessDefinitionVersion()
                .desc()
                .list();
        for (ProcessDefinition item :
                list) {
            System.out.println("========");
            System.out.println(item.getDeploymentId());
            System.out.println(item.getId());
            System.out.println(item.getKey());
            System.out.println(item.getName());
        }

2、开启流程

        String processDefinitionKey = "candidateUser";
        Map<String, Object> map = new HashMap<>();
        map.put("num", 2);
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey(processDefinitionKey);
        if (processInstance != null) {
            System.out.println("=============");
            System.out.println(processInstance.getName());
            System.out.println(processInstance.getProcessDefinitionKey());
            System.out.println(processInstance.getId());
        }

3、查询已经启动的流程实例

List<ProcessInstance> list = runtimeService
                .createProcessInstanceQuery()
                .processInstanceId("e7f7f241-317e-11ed-8ab0-005056c00008")
                .processDefinitionKey("leave")
                .list();
        
        list.forEach(item -> {
            System.out.println("============");
            System.out.println(item.getBusinessKey());
            System.out.println(item.getDeploymentId());
            System.out.println(item.getStartTime());
            System.out.println(item.getProcessDefinitionKey());
            System.out.println(item.getName());
        });

4、查询正在执行的任务

List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey("varAndParaGateway")
//                .taskAssignee("张三")
//                .taskId("d9cc9362-3124-11ed-8715-005056c00008")
//                .processInstanceId("cca885a4-31a3-11ed-a478-005056c00008")
                .list();
        for (int i = 0; i < list.size(); i  ) {
            Task task = list.get(i);
            System.out.println("===========");
            System.out.println(task.getProcessDefinitionId());
            System.out.println(task.getProcessInstanceId());
            System.out.println(task.getAssignee());
            System.out.println(task.getName());
            System.out.println(task.getId());
        }

5、完成个人任务

String assignee = "张三";
        String processInstanceId = "3dc5fbc4-3249-11ed-bf5a-005056c00008";
        String processDefinitionKey = "gateway1";
        List<Task> list = taskService.createTaskQuery()
//                .processDefinitionKey(processDefinitionKey)
//                .taskAssignee(assignee)
                .processInstanceId(processInstanceId)
                .list();
//        taskService.setVariableLocal("862e5029-31e4-11ed-ab1a-005056c00008", "num", "4");
        // executionId 就当是processInstanceId
//        runtimeService.setVariableLocal("862b1bd5-31e4-11ed-ab1a-005056c00008", "num", 3);
        for (int i = 0; i < list.size(); i  ) {
            Task task = list.get(i);
//            taskService.complete(task.getId());
            System.out.println("==========");
            System.out.println(task.getId());
            System.out.println(task.getName());
            System.out.println(task.getAssignee());
            System.out.println(task.getProcessDefinitionId());
            System.out.println(task.getProcessInstanceId());
            System.out.println(task.getTaskDefinitionKey());
            System.out.println(task.getExecutionId());
        }
学新通

6、拒绝个人任务

 ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
//                .processInstanceId(processInstanceId)
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        if (processInstance != null) {
            runtimeService.deleteProcessInstance(processInstance.getId(), endReason);
            log.info("流程删除成功!");
        } else {
            throw new ProcessNotFoundException("流程不存在!");
        }

7、判断流程是否结束

 HistoricProcessInstance historicProcessInstance = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId("d8c1137a-31bf-11ed-aee8-005056c00008")
                .singleResult();
        if (historicProcessInstance != null) {
            if (historicProcessInstance.getEndTime() != null) {
                System.out.println("流程已结束!");
            } else {
                System.out.println("流程未结束!");
            }
        } else {
            System.out.println("流程不存在!");
        }

8、查询审批过程

// 历史节点
        List<HistoricActivityInstance> list = historyService
                .createHistoricActivityInstanceQuery()
//                .processInstanceId("5e787d34-31a6-11ed-95b6-005056c00008")
                .processInstanceId("70ebc1e0-3312-11ed-b4b0-005056c00008")
//                .orderByActivityId()
//                .desc()
                .orderByHistoricActivityInstanceEndTime()
                .asc()
                .finished()
//                .unfinished()
                .list()
                .stream()
                .filter(item -> !StringUtils.containsAny(item.getActivityType(), "inclusiveGateway", "parallelGateway"))
                .collect(Collectors.toList());
        // 历史变量
        for (int i = 0; i < list.size(); i  ) {
            System.out.println("========");
            HistoricActivityInstance historicActivityInstance = list.get(i);
//            System.out.println(historicActivityInstance.getActivityId());  // _2
            System.out.println(historicActivityInstance.getActivityName());
            String taskId = historicActivityInstance.getTaskId();
//            System.out.println(taskId);
            String result = "";
            if (StringUtils.isNotEmpty(taskId)) {
                HistoricVariableInstance historicVariableInstance = historyService.createHistoricVariableInstanceQuery().taskId(taskId).variableName("result").singleResult();
                result = (String) historicVariableInstance.getValue();
                // 已经经过的节点不适用该方法查询变量
//                result = (String) taskService.getVariableLocal(taskId, "result");
            }
            System.out.println(historicActivityInstance.getAssignee()   "----"   result);
            System.out.println(historicActivityInstance.getStartTime());
            System.out.println(historicActivityInstance.getEndTime());
//            System.out.println("ExecutionId:"   historicActivityInstance.getExecutionId());
//            System.out.println("ProcessInstanceId:"   historicActivityInstance.getProcessInstanceId());
//            System.out.println(historicActivityInstance.getActivityType()); // userTask
        }
学新通

9、申领任务

// 获取当前用户
        String candidateUser = ActivitiUtils.getActivitiUser();
        if (StringUtils.isBlank(candidateUser)) {
            throw new RuntimeException("candidateUser不能为空!");
        }
        if (StringUtils.isBlank(businessKey)) {
            throw new RuntimeException("businessKey不能为空!");
        }
        Task task = taskService.createTaskQuery()
                .taskCandidateUser(candidateUser)
//                .taskId(taskId)
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        if (task != null) {
            taskService.claim(task.getId(), candidateUser);
            log.info(candidateUser   "申领到了任务!");
            return true;
        }
        return false;
学新通

10、放弃申领到的任务

// 获取当前用户
        String assignee = ActivitiUtils.getActivitiUser();
        if (StringUtils.isBlank(businessKey)) {
            throw new RuntimeException("businessKey不能为空!");
        }
        if (StringUtils.isBlank(assignee)) {
            throw new RuntimeException("assignee不能为空!");

        }
        Task task = taskService.createTaskQuery()
                .taskAssignee(assignee)
//                .taskId(taskId)
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        if (task != null) {
            taskService.setAssignee(task.getId(), null);
            log.info(assignee   "放弃了任务!");
        }
学新通

五、动态指定审批人

1、EL表达式动态指定审批人

在bpmn文件中借助EL表达式,并通过变量赋值,达到动态指定审批人的效果。
以下是流程图,审批人处并没有直接指定相应的审批人,而是通过EL表达式站位。
学新通
对应的xml文件:

<process id="varDemo" name="varDemo" isExecutable="true">
    <startEvent id="_1"/>
    <userTask id="_2" name="一级审批" activiti:assignee="${user1}"/>
    <userTask id="_3" name="二级审批" activiti:assignee="${user2}"/>
    <endEvent id="_4"/>
    <sequenceFlow id="sid-fba31a25-c2aa-487d-9ae5-89c0e2e04524" sourceRef="_1" targetRef="_2"/>
    <sequenceFlow id="sid-8576ba0f-6a70-4ee2-b940-9a336015b744" sourceRef="_2" targetRef="_3"/>
    <sequenceFlow id="sid-e4ff88d6-62e7-42bc-8ef9-a981f31d0f3e" sourceRef="_3" targetRef="_4"/>
  </process>

对应的一二级审批人为变量user1和user2。通过下面api完成当前任务节点并填充变量。

HashMap<String, Object> map = new HashMap<>();
map.put("user2", "王五");
taskService.complete(task.getId(), map, true);

2、EL javaBean动态指定候选人

候选人概念:候选人和审批人的区别在于候选人只有领取到了任务才能审批任务,否则不能审批任务。常用场景是一个节点多人审批,其中一个人审批通过则该节点通过。
学新通
对应的xml文件:

<process id="candidateUser" name="candidateUser" isExecutable="true">
    <startEvent id="_1" name="开始"/>
    <userTask id="_2" name="一级审批" activiti:candidateUsers="${authService.getCandidateUsers()}"/>
    <endEvent id="_3" name="结束"/>
    <sequenceFlow id="sid-f1c43cee-5b56-4a07-94fe-25d9a271a855" sourceRef="_1" targetRef="_2"/>
    <sequenceFlow id="sid-bcd5f698-98b4-4153-bd8e-fcc10b141ec5" sourceRef="_2" targetRef="_3"/>
  </process>

创建java类,定义方法,方法逻辑为候选人选取逻辑,方法返回list。

@Service
public class AuthService implements Serializable {

    public List<String> getCandidateUsers() {
        List<String> users = new ArrayList<>();
        users.add("小二");
        users.add("小三");
        users.add("小四");
        users.add("小五");
        return users;
    }

候选人领取任务:

// candidateUser为String类型
taskService.claim(task.getId(), candidateUser);

六、网关

1、排他网关

排他网关的特点就是流程只能根据条件走一条流程线,例如:
该审批流程只能经选择二级审批一或者二级审批二其中一条,排他网关不需要新的网关去汇总分支。
学新通

2、并行网关

并行网关顾名思义,经过该网关时所有流程都会被触发,例如:
该网关需要一个并行网关进行汇总,否则就就会在一次网关分流之后各自进行流程,也就是并行网关之后的节点会被执行两次。
学新通

3、包含网关

该网关是排他网关和并行网关的结合,该网关与排他网关不同的是,排他网关只能执行满足条件一条线的流程,而包含网关可以执行所有满足条件的流程;包含网关与并行网关不同的是,包含网关支持设置条件进行限制可以经过哪些流程,并不是所有线的流程都可以被执行。同样的,为了避免经过网关之后产生不同的分支,也是需要相同类型的网关进行汇总。
学新通

七、避坑点

1. 如果排他网关设置条件会怎么样?
不设置条件,并不影响程序执行,不过在经过网关时,会选择xml中最先出现sequenceFlow那条线,所以在使用排他网关时,最好设置上条件。
2. processInstance和execution的联系是什么?
如下两张图片最直观表现他们的关系,一个流程实例会对应一个父级execution,然后真正走实例流程的execution是另外生成的,是父级execution的子executiion。
学新通
学新通

3. taskService.setVariableLocal 和 runtimeService.setVariableLocal的区别?
前者的作用域是当前execution的当前节点上,且数据库当前数据行的task_id有数据,且经过测试,变量仅用于该节点,不能用于节点后面的流程线;后者的作用域是当前execution的所有节点上。
学新通
4. taskService.setVariable 和 runtimeService.setVariable的区别?
前者是如果当前流程变量中没有没有此变量,则默认是最外层execution,即全局变量,如果流程中有此变量,测覆盖此变量,作用域范围不变;后者是最外层execution,也是全局变量。
5. 没有到达任务节点,会不会对后面的节点进行赋值?赋值之后改变流程变量会不会改变已经赋值的节点的值?
学新通

到达二级审批之前,尽管变量中有user2的信息,是不会给user2赋值的,只有到达二级审批节点的时候才会给user2赋值;并且已经给user1赋值之后改变流程变量并不会改变user1的值。

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfhhifh
系列文章
更多 icon
同类精品
更多 icon
继续加载