Spring Boot2.3整合activiti7 实现快速入门

activiti 工作流引擎,主要用于可灵活变动的系统的流程使用,使用activiti 管理自动化流程,摆脱用数据库状态为标识做流程,当流程改变还需要改代码,避免无用工作量

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.onefox</groupId>
    <artifactId>activiti</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>activiti</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>7.1.0.M6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.38</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.5</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.58</version>
        </dependency>
          <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

application.yml

spring:
  activiti:
    database-schema-update: true
    check-process-definitions: false
    db-history-used: true
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf-8&useSSL=false&autoReconect=true&serverTimezone=GMT%2b8
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
    filters: start
    maxActive: 50
    initialSize: 0
    maxWait: 60000
    minIdle: 1
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: select 1 from dual
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true
    maxOpenPreparedStatements: 20
    removeAbandoned: true
    removeAbandonedTimeout: 180
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8
    default-property-inclusion: non_null

Activit核心配置文件activiti.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--  配置数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="url" value="jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true"/>
    </bean>

    <!-- Activiti单独运行的ProcessEngine配置 -->
    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <!-- 数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
</beans>
这时候启动时就可以创建Activiti所需要的25张表了

springboot activemq集成 springboot2整合activiti7_activiti


数据库表命名含义:

ACT_RE_

:'RE’表示Repository。这个前缀的表包含了流程定义和流程静态资源(图片、规则等等)。
ACT_RU_

:'RU’表示Runtime。这些运行时的表,包含流程实例,任务、变量,异步任务等运行中的数据。Activiti只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录。这些运行时表可以一直很小并且速度很快。


ACT_HI_

:'HI’表示History。这些表包含历史数据,比如历史流程实例,变量,任务等等。
ACT_GE_

:'GE’表示General。通用数据,用于不同场景下。

/**
RepositoryService: Activiti的资源管理接口,提供了管理和控制流程发布包和流程定义的操作。
	查询引擎中的发布包和流程定义。
	暂停或激活发布包以及对应全部和特定流程定义。暂停意味着它们不能再在执行任务操作了,激活是对应的反向操作。
	获取多种资源,像包含在发布包中的文件获引擎自动生成的流程图。
	获取流程定义的POJO,可以用解析流程,而不必通过XML。
	
RuntimeService: 流程执行接口,获取流程执行的信息

TaskService:  任务接口,获取任务信息

HistoryService: 历史任务接口,获取历史任务的信息,执行流程等等

ManagementService: Activiti的引擎管理接口,不用在业务中,维护使用
*/

bpmn文件代码,工作流引擎保存的格式文件

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:yaoqiang="http://bpmn.sourceforge.net" exporter="Yaoqiang BPMN Editor" exporterVersion="5.3" expressionLanguage="http://www.w3.org/1999/XPath" id="_1605260257556" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://bpmn.sourceforge.net/schemas/BPMN20.xsd">
  <process id="activitiTest" isClosed="false" isExecutable="true" name="财务审批" processType="None">
    <extensionElements>
      <yaoqiang:description/>
      <yaoqiang:pageFormat height="841.8897637795276" imageableHeight="831.8897637795276" imageableWidth="588.1102362204724" imageableX="5.0" imageableY="5.0" orientation="0" width="598.1102362204724"/>
      <yaoqiang:page background="#FFFFFF" horizontalCount="1" verticalCount="1"/>
    </extensionElements>
    <startEvent id="start" isInterrupting="true" name="开始" parallelMultiple="false">
      <outgoing>flow1</outgoing>
      <outputSet/>
    </startEvent>
    <userTask activiti:assignee="张女士" completionQuantity="1" id="task1" implementation="##unspecified" isForCompensation="false" name="填写财务报销单" startQuantity="1">
      <incoming>flow1</incoming>
      <outgoing>flow2</outgoing>
    </userTask>
    <sequenceFlow id="flow1" sourceRef="start" targetRef="task1"/>
    <userTask activiti:assignee="程经理" completionQuantity="1" id="task2" implementation="##unspecified" isForCompensation="false" name="财务部经理审批" startQuantity="1">
      <incoming>flow2</incoming>
      <outgoing>flow3</outgoing>
    </userTask>
    <sequenceFlow id="flow2" sourceRef="task1" targetRef="task2"/>
    <userTask activiti:assignee="赵总" completionQuantity="1" id="task3" implementation="##unspecified" isForCompensation="false" name="总经理审批" startQuantity="1">
      <incoming>flow3</incoming>
      <outgoing>flow4</outgoing>
    </userTask>
    <sequenceFlow id="flow3" sourceRef="task2" targetRef="task3"/>
    <endEvent id="end" name="结束">
      <incoming>flow4</incoming>
      <inputSet/>
    </endEvent>
    <sequenceFlow id="flow4" sourceRef="task3" targetRef="end"/>
  </process>
  <bpmndi:BPMNDiagram id="zgc-activitiTest" name="Untitled Diagram" resolution="96.0">
    <bpmndi:BPMNPlane bpmnElement="activitiTest">
      <bpmndi:BPMNShape bpmnElement="start" id="Yaoqiang-startevent2">
        <omgdc:Bounds height="32.0" width="32.0" x="100.0" y="160.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="21.02" width="28.0" x="102.0" y="199.45"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="task1" id="zgc-task1">
        <omgdc:Bounds height="55.0" width="105.0" x="180.0" y="150.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="21.02" width="83.0" x="191.0" y="168.99"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="task2" id="zgc-task2">
        <omgdc:Bounds height="55.0" width="105.0" x="330.0" y="150.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="21.02" width="72.0" x="346.5" y="168.99"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="task3" id="zgc-task3">
        <omgdc:Bounds height="55.0" width="105.0" x="480.0" y="150.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="21.02" width="61.0" x="502.0" y="168.99"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="end" id="zgc-end">
        <omgdc:Bounds height="32.0" width="32.0" x="630.0" y="160.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="21.02" width="28.0" x="632.0" y="199.45"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="zgc-flow4">
        <omgdi:waypoint x="585.0" y="177.5"/>
        <omgdi:waypoint x="630.0" y="176.0"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="21.02" width="6.0" x="604.5" y="166.24"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="zgc-flow3">
        <omgdi:waypoint x="435.0" y="177.5"/>
        <omgdi:waypoint x="480.0" y="177.5"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="21.02" width="6.0" x="454.5" y="166.99"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="zgc-flow2">
        <omgdi:waypoint x="285.0" y="177.5"/>
        <omgdi:waypoint x="330.0" y="177.5"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="21.02" width="6.0" x="304.5" y="166.99"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="zgc-flow1">
        <omgdi:waypoint x="132.0" y="176.0"/>
        <omgdi:waypoint x="180.0" y="177.5"/>
        <bpmndi:BPMNLabel>
          <omgdc:Bounds height="21.02" width="6.0" x="153.0" y="166.24"/>
        </bpmndi:BPMNLabel>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

springboot activemq集成 springboot2整合activiti7_mysql_02

工作流常用方法

1. 流程部署

//工作流引擎
    private static ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault().buildProcessEngine();
    //流程文件名称
    private final static String BPMN = "activiti.bpmn";
    //流程定义ID
    private final static String ID = "activitiTest:1:210004";
    //businessKey 绑定业务ID
    private final static String BUSINESSKEY = "activiti";
    //DeploymentID 部署ID
    private final static String DEPLOYMENT = "212501";

    /**
     * 流程部署
     */
    @Test
    public void deploy() {
        RepositoryService repositoryService = processEngine.getRepositoryService();//资源对象
        Deployment deployment = repositoryService.createDeployment().key(BUSINESSKEY).addClasspathResource(BPMN).name("财务审批流程").deploy();
        System.out.println("流程ID:" + deployment.getId());
        System.out.println("流程名称:" + deployment.getName());
    }

执行完就可以看到act_re_procdef,act_re_deployment已经有数据了

act_re_procdef 流程信息
act_re_deployment 部署信息

2:流程启动

/**
     * 流程启动
     */
    RuntimeService runtimeService = processEngine.getRuntimeService();
        //根据key,对应act_re_procdef表的主键
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(ID, BUSINESSKEY);

        //获取流程实例的相关信息
        String processDefinitionId = processInstance.getProcessDefinitionId();
        System.out.println("流程定义的id = " + processDefinitionId);
        String id = processInstance.getId();
        System.out.println("流程执行实例的id = " + id);

3:查询代办任务

/**
     * 查询代办任务
     */
    @Test
    public void task() {
        TaskService taskService = processEngine.getTaskService();
        /**
         * processDefinitionId: act_re_procdef
         * taskAssignee taskName
         */
        List<Task> taskList = taskService.createTaskQuery().processDefinitionId(ID).taskAssignee("").list();
        //遍历任务列表
        for (Task task : taskList) {
            String processDefinitionId = task.getProcessDefinitionId();
            System.out.println("流程定义id = " + processDefinitionId);
            String processInstanceId = task.getProcessInstanceId();
            System.out.println("流程实例id = " + processInstanceId);
            String assignee1 = task.getAssignee();
            System.out.println("任务负责人 = " + assignee1);
            String id = task.getId();
            System.out.println("任务id = " + id);
            String name = task.getName();
            System.out.println("任务名称 = " + name);
        }
    }

4:办理任务

/**
     * 办理任务
     */
    @Test
    public void taskHandling() {
        List<Task> taskList = processEngine.getTaskService().createTaskQuery().processDefinitionId(ID).taskAssignee("").list();
        if (taskList != null) {
            for (Task task : taskList) {
                System.out.println(task.getName());
                //办理任务
                processEngine.getTaskService().complete(task.getId());
            }
        }
    }

可以看到在历史节点中能看到我们流程的办理情况

springboot activemq集成 springboot2整合activiti7_xml_03

5:查询流程定义

@Test
    public void readProcess() {
        ProcessDefinitionQuery processDefinitionQuery = processEngine.getRepositoryService().createProcessDefinitionQuery();

        ProcessDefinition processDefinition = processDefinitionQuery.processDefinitionId(ID).singleResult();

        List<ProcessDefinition> processDefinitionList = processDefinitionQuery.processDefinitionId(ID).orderByProcessDefinitionId().desc().list();

        System.out.println(processDefinitionList.get(0).getKey());

        System.out.println(processDefinition.getKey());

    }

6:删除部署流程

/**
     * 删除部署流程与关联数据
     * void deleteDeployment(String deploymentId, boolean cascade); 强制删除,不论流程是否结束
     * void deleteDeployment(String deploymentId); 流程未结束,无法删除
     */
    @Test
    public void deleteProcess() {
        processEngine.getRepositoryService().deleteDeployment(DEPLOYMENT, true);
    }

7:流程资源查询

/**
     * 流程资源查询
     * bpmn | png 资源文件等等
     */
    @Test
    public void processResources() throws IOException {
        ProcessDefinitionQuery processDefinitionQuery = processEngine.getRepositoryService().createProcessDefinitionQuery();

        ProcessDefinition processDefinition = processDefinitionQuery.processDefinitionId(ID).singleResult();

        System.out.println("流程名:" + processDefinition.getName());
        System.out.println("资源名:" + processDefinition.getResourceName());
        System.out.println("图片名:" + processDefinition.getDiagramResourceName());

        //取得资源输入流
        RepositoryService repositoryService = processEngine.getRepositoryService();
        InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), processDefinition.getResourceName());

        //复制到本地
        String path = "D:\\activiti\\" + processDefinition.getResourceName();
        File file = new File(path);
        if (!file.exists()) {
            file.getParentFile().mkdirs();
        }
        FileCopyUtils.copy(resourceAsStream, new FileOutputStream(path));
    }

可以看到我们的流程资源已经复制到本地了

springboot activemq集成 springboot2整合activiti7_activiti_04

8:历史流程资源

/**
     * @Classname ActivitiApplicationTest
     * @Date 2020/11/14 16:45
     * @Created zgc
     * @Describe 历史流程资源
     */
    @Test
    public void readHistory() {
        HistoryService historyService = processEngine.getHistoryService();
        //历史流程定义资源
        HistoricProcessInstance historicProcessInstance = historyService.createHistoricProcessInstanceQuery().processDefinitionId(ID).singleResult();
        System.out.println("历史流程定义实例ID:" + historicProcessInstance.getId());
        System.out.println("历史流程定义名称:" + historicProcessInstance.getProcessDefinitionName());
        //历史流程用户节点资源
        List<HistoricTaskInstance> list = historyService.createHistoricTaskInstanceQuery().list();
        int index = 1;
        for (HistoricTaskInstance historicTaskInstance : list) {
            System.out.println("历史步骤 " + index + " : " + historicTaskInstance.getName() + "==========>节点负责人:" + historicTaskInstance.getAssignee());
            index++;
        }
        HistoricTaskInstance singleResult = historyService.createHistoricTaskInstanceQuery().processDefinitionId(ID).taskAssignee("张三").singleResult();
        System.out.println("负责人:" + singleResult.getAssignee() + ",任务名:" + singleResult.getName());
        //历史流程完整节点资源
        List<HistoricActivityInstance> activityInstanceList = historyService.createHistoricActivityInstanceQuery().list();
        int index1 = 1;
        for (HistoricActivityInstance activityInstance : activityInstanceList) {
            System.out.println("历史完整步骤 " + index1 + " : " + activityInstance.getActivityName() + "==========>节点类型:" + activityInstance.getActivityType());
            index1++;
        }
    }

9:流程定义挂起,流程定义恢复

/**
     * @Classname ActivitiApplicationTest
     * @Date 2020/11/14 17:07
     * @Created zgc
     * @Describe 流程挂起,流程恢复 挂起流程定义
     */
    @Test
    public void processStopAll() {
        ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionId(ID).singleResult();
        /*
            流程挂起状态:返回true,反之false
         */
        boolean suspended = processDefinition.isSuspended();
        if (suspended) {
            /*
                挂起状态:
                    defID
                    级联启动
                    延迟启动
             */
            processEngine.getRepositoryService().activateProcessDefinitionById(ID, true, null);
            System.out.println(ID +" 流程恢复");
        } else {
            /*
                正常状态:
                    defID
                    级联挂起
                    延迟挂起
             */
            processEngine.getRepositoryService().suspendProcessDefinitionById(ID, true, null);
            System.out.println(ID +" 流程挂起");
        }
    }

10:任务挂起,任务恢复

分割线

/**
     * @Classname ActivitiApplicationTest
     * @Date 2020/11/14 17:07
     * @Created zgc
     * @Describe 流程挂起,流程恢复 挂起单条任务
     */
    @Test
    public void processStopOne() {
        List<ProcessInstance> processInstanceList = processEngine.getRuntimeService().createProcessInstanceQuery().processDefinitionId(ID).list();
        for (ProcessInstance processInstance : processInstanceList) {
             /*
            流程挂起状态:返回true,反之false
         */
            boolean suspended = processInstance.isSuspended();
            if (suspended) {
            /*
                挂起状态:
             */
                processEngine.getRuntimeService().activateProcessInstanceById(processInstance.getId());
                System.out.println(processInstance.getId() +" 流程恢复");
            } else {
            /*
                正常状态:
             */
                processEngine.getRuntimeService().suspendProcessInstanceById(processInstance.getId());
                System.out.println(processInstance.getId() +" 流程挂起");
            }
        }
    }

根据流程变量进行任务办理流程

1:财务审批实体类

@Setter
@Getter
@NoArgsConstructor
@AllArgsConstructor
@ToString
/**
 * @Classname Finance
 * @Date      2020/11/14 20:50
 * @Created   zgc
 * @Describe  财务审批实体
 */
public class Finance implements Serializable {

    private static final long serialVersionUID = -7777387035032541168L;

    /**
     * 主键
     */
    private Integer id;

    /**
     * 申请人名字
     */
    private String name;

    /**
     * 开始时间
     */
    private Date startTime;

    /**
     * 结束时间
     */
    private Date endTime;

    /**
     * 报销金额
     */
    private BigDecimal num;

    /**
     * 报销内容
     */
    private String reason;

    /**
     * 报销类型
     */
    private String type;

}

2:流程部署

3:带参数启动

/**
     * @Classname ActivitiApplicationTest
     * @Date      2020/11/14 21:03
     * @Created   zgc
     * @Describe  携带参数启动
     */
    @Test
    public void startParams() {
        RuntimeService runtimeService = processEngine.getRuntimeService();

        Finance finance = new Finance();
        finance.setId(UUID.randomUUID().toString());
        finance.setName("张女士");
        finance.setNum(new BigDecimal("23588.00"));
        finance.setReason("会所按摩");
        finance.setStartTime(new Date());
        finance.setType("商务费用");

        Map<String, Object> params = new HashMap<>();
        params.put("finance",finance);

        ProcessInstance processInstance = runtimeService.startProcessInstanceById(ID, BUSINESSKEY,params);

        //获取流程实例的相关信息
        String processDefinitionId = processInstance.getProcessDefinitionId();
        System.out.println("流程定义的id = " + processDefinitionId);
        String id = processInstance.getId();
        System.out.println("流程执行实例的id = " + id);

    }

4:各节点完成任务

@Test
    public void task1(){
        Task result = processEngine.getTaskService().createTaskQuery().processDefinitionId(ID).taskAssignee("张女士").singleResult();
        System.out.println("执行流程实例ID:"+result.getProcessInstanceId() + ",流程名称:" + result.getName() + ",经办人为:" + result.getAssignee());
        processEngine.getTaskService().complete(result.getId());
    }
    @Test
    public void task2(){
        Task result = processEngine.getTaskService().createTaskQuery().processDefinitionId(ID).taskAssignee("程经理").singleResult();
        System.out.println("执行流程实例ID:"+result.getProcessInstanceId() + ",流程名称:" + result.getName() + ",经办人为:" + result.getAssignee());
        processEngine.getTaskService().complete(result.getId());
    }
    @Test
    public void task3(){
        Task result = processEngine.getTaskService().createTaskQuery().processDefinitionId(ID).taskAssignee("赵总").singleResult();
        System.out.println("执行流程实例ID:"+result.getProcessInstanceId() + ",流程名称:" + result.getName() + ",经办人为:" + result.getAssignee());
        processEngine.getTaskService().complete(result.getId());
    }
三个节点执行后,任务完成,task表清空,历史人物表中记录历史任务信息

springboot activemq集成 springboot2整合activiti7_activiti_05

分割线

1:设置任务候选人

bpmn文件,多个候选人通过activiti:candidateUsers标签和逗号隔开

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:yaoqiang="http://bpmn.sourceforge.net" exporter="Yaoqiang BPMN Editor" exporterVersion="5.3" expressionLanguage="http://www.w3.org/1999/XPath" id="_1605260257556" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://bpmn.sourceforge.net/schemas/BPMN20.xsd">
  <process id="activitiTest"  name="财务审批">
    <startEvent id="start"  name="开始" ></startEvent>
    <userTask activiti:candidateUsers="张女士,钱女士" id="task1" name="填写财务报销单"></userTask>
    <sequenceFlow id="flow1" sourceRef="start" targetRef="task1"/>
    <userTask activiti:assignee="程经理" id="task2" name="财务部经理审批"></userTask>
    <sequenceFlow id="flow2" sourceRef="task1" targetRef="task2"/>
    <userTask activiti:assignee="赵总"  id="task3" name="总经理审批"></userTask>
    <sequenceFlow id="flow3" sourceRef="task2" targetRef="task3"/>
    <endEvent id="end" name="结束"></endEvent>
    <sequenceFlow id="flow4" sourceRef="task3" targetRef="end"/>
  </process>
</definitions>

当一个节点有多个人的时候,多个候选人都能查出该条任务信息
替换bpmn文件,重新流程部署

2:流程部署

3:流程启动

4:查询候选人任务

taskCandidateUser 通过该参数从之前保存节点中的多个候选人都可以查出当前任务,非当前节点中的人查不到

/**
     * @Classname ActivitiApplicationTest
     * @Date      2020/11/14 21:41
     * @Created   zgc
     * @Describe  查询候选人任务
     */
    @Test
    public void candidateUsers(){
        TaskService taskService = processEngine.getTaskService();
        /**
         * processDefinitionId: act_re_procdef
         * taskCandidateUser 候选人名称
         */
        List<Task> taskList = taskService.createTaskQuery().processDefinitionId(ID).taskCandidateUser("钱女士").list();
        List<Task> taskList1 = taskService.createTaskQuery().processDefinitionId(ID).taskCandidateUser("张女士").list();
        //遍历任务列表
        for (Task task : taskList) {
            String processDefinitionId = task.getProcessDefinitionId();
            System.out.println("流程定义id = " + processDefinitionId);
            String processInstanceId = task.getProcessInstanceId();
            System.out.println("流程实例id = " + processInstanceId);
            String assignee1 = task.getAssignee();
            System.out.println("任务负责人 = " + assignee1);
            String id = task.getId();
            System.out.println("任务id = " + id);
            String name = task.getName();
            System.out.println("任务名称 = " + name);
        }
    }

5:拾取候选者任务

/**
     * @Classname ActivitiApplicationTest
     * @Date 2020/11/15 10:19
     * @Created zgc
     * @Describe 拾取候选人任务,拾取完成后候选任务成为个人任务,其他候选者查询不到该任务
     */
    @Test
    public void chaimGroupTask() {
        String user = "钱女士";
        Task result = processEngine.getTaskService().createTaskQuery().processDefinitionId(ID).taskCandidateUser(user).singleResult();
        //result is not null 代表当前用户是
        if (result != null) {
            processEngine.getTaskService().claim(result.getId(), user);
            System.out.println(user + "拾取任务成功,任务ID为:"+result.getId());
        }
    }

5:当候选人拾取任务时,别的候选人已经查询不到该任务信息了,通过查询待办任务来查询当前任务节点的负责人

6:负责人退还|转让 个人任务到组任务中

/**
     * @Classname ActivitiApplicationTest
     * @Date      2020/11/15 10:42
     * @Created   zgc
     * @Describe  退还个人任务到组任务 | 转让该任务给别的候选人(也可以转让给非候选人,不建议)
     */
    @Test
    public void returnGroupTask(){
        String assignee = "钱女士";
        String user = "张女士";
        TaskService taskService = processEngine.getTaskService();
        Task result = taskService.createTaskQuery().processDefinitionId(ID).taskAssignee(assignee).singleResult();
        if (result != null){
            taskService.setAssignee(result.getId(),null);
            System.out.println(assignee + "退还|转让任务成功,任务ID为:"+result.getId());
        }
    }

7:办理任务

分割线

使用 SpringBoot-Activti提供的Api

spring-activiti.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--  配置数据源 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="url" value="jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&useSSL=false&serverTimezone=GMT%2B8&allowPublicKeyRetrieval=true"/>
    </bean>

    <!-- 配置事务管理器 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!-- 工作流引擎配置Bean -->
    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
        <!-- 数据源 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 使用Spring的事务管理器 -->
        <property name="transactionManager" ref="transactionManager"/>
        <!-- 数据库的策略 -->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>

    <!-- 流程引擎 -->
    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
        <property name="processEngineConfiguration" ref="processEngineConfiguration"/>
    </bean>
    <!-- 资源服务service -->
    <bean id="repositoryService" factory-bean="processEngine"
          factory-method="getRepositoryService"/>
    <!-- 流程运行service -->
    <bean id="runtimeService" factory-bean="processEngine"
          factory-method="getRuntimeService"/>
    <!-- 任务管理service -->
    <bean id="taskService" factory-bean="processEngine"
          factory-method="getTaskService"/>
    <!-- 历史管理service -->
    <bean id="historyService" factory-bean="processEngine"
          factory-method="getHistoryService"/>
</beans>

spring-activiti.bpmn

稍微改动一点,和之前的做下区分

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:yaoqiang="http://bpmn.sourceforge.net" exporter="Yaoqiang BPMN Editor" exporterVersion="5.3" expressionLanguage="http://www.w3.org/1999/XPath" id="_1605260257556" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://bpmn.sourceforge.net/schemas/BPMN20.xsd">
  <process id="spring-activitiTest"  name="辞职申请">
    <startEvent id="start"  name="开始" ></startEvent>
    <userTask activiti:candidateUsers="张女士,钱女士" id="task1" name="填写离职单"></userTask>
    <sequenceFlow id="flow1" sourceRef="start" targetRef="task1"/>
    <userTask activiti:assignee="程经理" id="task2" name="技术部经理审批"></userTask>
    <sequenceFlow id="flow2" sourceRef="task1" targetRef="task2"/>
    <userTask activiti:assignee="赵总"  id="task3" name="总经理审批"></userTask>
    <sequenceFlow id="flow3" sourceRef="task2" targetRef="task3"/>
    <endEvent id="end" name="结束"></endEvent>
    <sequenceFlow id="flow4" sourceRef="task3" targetRef="end"/>
  </process>
</definitions>

整合SpringSecurity

我是用的是 activiti7.1 M6 版本,强制引用SpringSecurity,无法关闭,除非退到M4版本左右才可关闭使用SpringSecurity

SpringSecurity配置文件,idea 建议开启自动导包

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    private Logger logger = LoggerFactory.getLogger(SpringSecurityConfig.class);

    @Override
    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService());
    }

    @Bean
    public UserDetailsService myUserDetailsService() {

        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();

        String[][] usersGroupsAndRoles = {
                {"张三", "password", "ROLE_ACTIVITI_USER", "GROUP_activiti1"},
                {"李四", "password", "ROLE_ACTIVITI_USER", "GROUP_activiti1"},
                {"王五", "password", "ROLE_ACTIVITI_USER", "GROUP_activiti1"},
                {"赵六", "password", "ROLE_ACTIVITI_USER", "GROUP_activiti2"},
                {"admin", "password", "ROLE_ACTIVITI_ADMIN"},
        };

        for (String[] user : usersGroupsAndRoles) {
            List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
            logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");
            inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]),
                    authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));
        }


        return inMemoryUserDetailsManager;
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                .and()
                .httpBasic();


    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
@Component
public class UserService {

    @Resource
    private UserDetailsService userDetailsService;

    public void logInAs(String username) {

        UserDetails user = userDetailsService.loadUserByUsername(username);
        if (user == null) {
            throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");
        }

        SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
            @Override
            public Collection<? extends GrantedAuthority> getAuthorities() {
                return user.getAuthorities();
            }

            @Override
            public Object getCredentials() {
                return user.getPassword();
            }

            @Override
            public Object getDetails() {
                return user;
            }

            @Override
            public Object getPrincipal() {
                return user;
            }

            @Override
            public boolean isAuthenticated() {
                return true;
            }

            @Override
            public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {

            }

            @Override
            public String getName() {
                return user.getUsername();
            }
        }));
        org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
    }