Springboot整合activiti7(排除springSecurity版)

  • Post author:
  • Post category:其他




1. activiti7概述

Alfresco软件在2010年5月17日宣布Activiti业务流程管理(BPM)开源项目的正式启动,其首席架构师由业务流程管理BPM的专家 Tom Baeyens担任,Tom Baeyens就是原来jbpm的架构师,而jbpm是一个非常有名的工作流引擎,当然activiti也是一个工作流引擎。

Activiti是一个工作流引擎, activiti可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN2.0进行定义,业务流程按照预先定义的流程进行执行,实现了系统的流程由activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作量,从而提高系统的健壮性,同时也减少了系统开发维护成本。

官方网站:

https://www.activiti.org/

在这里插入图片描述



2. 整合步骤



2.1 创建一个springboot工程

引入对应的maven依赖

<?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.4.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.sky.demo</groupId>
    <artifactId>activiti-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>activiti-demo</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-data-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web-services</artifactId>
        </dependency>
        <!--   springSecurity 依赖   -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <!-- mysql  -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--引入activiti的springboot启动器 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>7.1.0.M6</version>
<!--            <exclusions>-->
<!--                <exclusion>-->
<!--                    <artifactId>mybatis</artifactId>-->
<!--                    <groupId>org.mybatis</groupId>-->
<!--                </exclusion>-->
<!--            </exclusions>-->
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </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>

配置yml文件

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql:///activiti?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: root
  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: false
    # asyncExecutorActivate是指activiti在流程引擎启动就激活AsyncExecutor,异步:true-开启(默认)、false-关闭
    async-executor-activate: true
    #流程定义文件存放目录,要具体到某个目录
    #      process-definition-location-prefix: classpath:/processes/holliday/
    #process-definition-location-suffixes: #流程文件格式
    #  - **.bpmn20.xml
    #  - **.bpmn
server:
  port: 80



2.2 利用工具创建对应的activiti流程图

如下图所示:

在这里插入图片描述

对应的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="myevection" name="evection" isExecutable="true">
    <startEvent id="startEvent1"></startEvent>
    <userTask id="sid-628E55F8-EFFE-4491-B1E4-5F1AA480E7F4" name="创建出差申请" activiti:assignee="#{applicant}">
      <extensionElements>
        <modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete>
      </extensionElements>
    </userTask>
    <userTask id="sid-A05C15BC-4B33-4754-918E-6C3FF1307A1D" name="经理审批"></userTask>
    <userTask id="sid-25A1C48A-E915-4528-A81C-4E6444EDA303" name="总经理审批"></userTask>
    <userTask id="sid-3E040DA1-D6D9-4CF2-9A52-454815198C54" name="财务审批"></userTask>
    <endEvent id="sid-2AEB8D07-0941-4A3C-B156-6443794C271D"></endEvent>
    <sequenceFlow id="sid-0D2CB4C8-181C-491F-A6D0-ECF3CD873D4B" sourceRef="startEvent1" targetRef="sid-628E55F8-EFFE-4491-B1E4-5F1AA480E7F4"></sequenceFlow>
    <sequenceFlow id="sid-D244E043-BB65-4451-9D88-9E3A4831B402" sourceRef="sid-628E55F8-EFFE-4491-B1E4-5F1AA480E7F4" targetRef="sid-A05C15BC-4B33-4754-918E-6C3FF1307A1D"></sequenceFlow>
    <sequenceFlow id="sid-7FBEF724-B32F-48F2-BE3C-25EF8BDCE598" sourceRef="sid-A05C15BC-4B33-4754-918E-6C3FF1307A1D" targetRef="sid-25A1C48A-E915-4528-A81C-4E6444EDA303"></sequenceFlow>
    <sequenceFlow id="sid-852C5E41-0678-4983-8D66-89A9B5DDC1F8" sourceRef="sid-25A1C48A-E915-4528-A81C-4E6444EDA303" targetRef="sid-3E040DA1-D6D9-4CF2-9A52-454815198C54"></sequenceFlow>
    <sequenceFlow id="sid-45E84278-6725-4EE5-8A1D-7920E78DEA4B" sourceRef="sid-3E040DA1-D6D9-4CF2-9A52-454815198C54" targetRef="sid-2AEB8D07-0941-4A3C-B156-6443794C271D"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_myevection">
    <bpmndi:BPMNPlane bpmnElement="myevection" id="BPMNPlane_myevection">
      <bpmndi:BPMNShape bpmnElement="startEvent1" id="BPMNShape_startEvent1">
        <omgdc:Bounds height="30.0" width="30.0" x="195.0" y="105.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-628E55F8-EFFE-4491-B1E4-5F1AA480E7F4" id="BPMNShape_sid-628E55F8-EFFE-4491-B1E4-5F1AA480E7F4">
        <omgdc:Bounds height="80.0" width="100.0" x="160.0" y="165.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-A05C15BC-4B33-4754-918E-6C3FF1307A1D" id="BPMNShape_sid-A05C15BC-4B33-4754-918E-6C3FF1307A1D">
        <omgdc:Bounds height="80.0" width="100.0" x="160.0" y="280.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-25A1C48A-E915-4528-A81C-4E6444EDA303" id="BPMNShape_sid-25A1C48A-E915-4528-A81C-4E6444EDA303">
        <omgdc:Bounds height="80.0" width="100.0" x="160.0" y="390.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-3E040DA1-D6D9-4CF2-9A52-454815198C54" id="BPMNShape_sid-3E040DA1-D6D9-4CF2-9A52-454815198C54">
        <omgdc:Bounds height="80.0" width="100.0" x="160.0" y="510.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-2AEB8D07-0941-4A3C-B156-6443794C271D" id="BPMNShape_sid-2AEB8D07-0941-4A3C-B156-6443794C271D">
        <omgdc:Bounds height="28.0" width="28.0" x="196.0" y="630.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="sid-45E84278-6725-4EE5-8A1D-7920E78DEA4B" id="BPMNEdge_sid-45E84278-6725-4EE5-8A1D-7920E78DEA4B">
        <omgdi:waypoint x="210.0" y="590.0"></omgdi:waypoint>
        <omgdi:waypoint x="210.0" y="630.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-D244E043-BB65-4451-9D88-9E3A4831B402" id="BPMNEdge_sid-D244E043-BB65-4451-9D88-9E3A4831B402">
        <omgdi:waypoint x="210.0" y="245.0"></omgdi:waypoint>
        <omgdi:waypoint x="210.0" y="280.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-7FBEF724-B32F-48F2-BE3C-25EF8BDCE598" id="BPMNEdge_sid-7FBEF724-B32F-48F2-BE3C-25EF8BDCE598">
        <omgdi:waypoint x="210.0" y="360.0"></omgdi:waypoint>
        <omgdi:waypoint x="210.0" y="390.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-0D2CB4C8-181C-491F-A6D0-ECF3CD873D4B" id="BPMNEdge_sid-0D2CB4C8-181C-491F-A6D0-ECF3CD873D4B">
        <omgdi:waypoint x="210.0" y="135.0"></omgdi:waypoint>
        <omgdi:waypoint x="210.0" y="165.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-852C5E41-0678-4983-8D66-89A9B5DDC1F8" id="BPMNEdge_sid-852C5E41-0678-4983-8D66-89A9B5DDC1F8">
        <omgdi:waypoint x="210.0" y="470.0"></omgdi:waypoint>
        <omgdi:waypoint x="210.0" y="510.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>



2.3 把对应的流程图,放置在创建的项目资源文件夹里面

如下图所示:

在这里插入图片描述



2.4 编写相应的测试代码



说明:需要注意,这里假设不用springsecurity做权限认证和授权,仅用来辅助activiti查询数据库的用户角色相关功能。activiti中,把springsecurity绑定在activi中。



对应的项目结构如下图所示:

在这里插入图片描述



2.4.1 配置SpringSecurity不自动装配

在这里插入图片描述

默认情况下,springsecurity会自动配置,从而,在访问项目接口的时候,需要输入用户名和密码。这里在项目启动文件中,设置排除选项,不启动springsecurity的默认配置。代码如下所示:

package com.sky.demo.activitidemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @author fjl
 */
@SpringBootApplication(exclude = {org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class})
public class ActivitiDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(ActivitiDemoApplication.class, args);
	}

}

对应的要点截图如下:

在这里插入图片描述


这样,访问接口的时候,就不要求用户认证了。即springSecurity的认证就不起作用了。



2.4.2 编写 WebSecurityConfigurerAdapter抽象类的实现子类并配置注解@Component

在这里插入图片描述

在SpringBoot容器中,如果不存在该WebSecurityConfigurerAdapter的实现类,启动的时候,会报错,报错信息如下图所示:

在这里插入图片描述

对应的代码如下所示:

package com.sky.demo.activitidemo.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;


/**
 * @author fjl
 */
@Configuration
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {}



2.4.3 实现UserDetailsService接口

在这里插入图片描述

该接口的作用是,activiti在调用接口的时候,需要利用该接口与数据库打交道,如果没有实现该接口,在使用activiti提供的服务类进行查询的时候,会报错,如下图所示:

在这里插入图片描述

该实现类的代码如下所示:

package com.sky.demo.activitidemo.config;

import com.sky.demo.activitidemo.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

/**
 * @author fjl
 */
@Slf4j
@Component
public class SelfUserDetailsServiceImpl implements UserDetailsService {

    private final UserService userService;

    public SelfUserDetailsServiceImpl(UserService userService) {
        this.userService = userService;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return userService.findOneUserByName(username);
    }
}

对应截图如下所示:

在这里插入图片描述



2.4.4 真正操作数据库的服务类编写

在这里插入图片描述

对应的代码如下所示:

package com.sky.demo.activitidemo.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    private final Logger logger = LoggerFactory.getLogger(UserService.class);
    public User findOneUserByName(String username) {
        logger.info("username:");
        logger.info(username);
        List<GrantedAuthority> authorities= AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
        // 密码置空
        return new User(username,"",authorities);
    }
}

对应的截图如下:

在这里插入图片描述



2.4.5 activiti的抽象服务类编写

在这里插入图片描述

对应的代码如下:

package com.sky.demo.activitidemo.service;

import org.activiti.engine.*;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

/**
 * @author fjl
 */
@Service
public class ActService {
    @Autowired
    RepositoryService repositoryService;
    @Autowired
    RuntimeService runtimeService;
    @Autowired
    TaskService taskService;

    /**
     * 1. 部署流程服务
     * @return
     */
    public Deployment deployProcessByZip(InputStream inputStream){
        ZipInputStream zipInputStream = new ZipInputStream(inputStream);
        return repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                .deploy();
    }

    /**
     * 2. 查询流程定义(查询所有的已部署流程)-- 需要查询最新的部署流程
     * TODO  待测试
     * @return
     */
    public List<ProcessDefinition> getAllProcessDefinition(){
        ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();
        return query.orderByProcessDefinitionVersion().desc().list();
    }


    /**
     * 3. 查询所有流程实例
     * @return
     */
    public List<ProcessInstance> getAllProcessInstance(){
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();
        return list;
    }

    /**
     * 4. 查询所有流程任务
     */
    public List<Task> getAllProcessTask(String username){
        List<Task> list = taskService.createTaskQuery().list();

        List<Task> list2 = list.stream().filter(item -> {
            return item.getAssignee().equals(username);
        }).collect(Collectors.toList());
        return list2;
    }

    /**
     * 完成指定任务--知道任务id
     * --完成任务的时候,需要根据用户角色,
     */
    public void completeTask(Task task, Map<String,Object> map){
        taskService.complete(task.getId(), map);
    }

    /**
     * 启动流程实例
     */
    public ProcessInstance startProcess(String processDefinitionKey,String businessKey,Map<String,Object> map){
        return runtimeService.startProcessInstanceById(processDefinitionKey, businessKey, map);
    }
}

下面对该代码进行截图解析:

在这里插入图片描述

上图中可以看到,如果需要使用activiti中提供的服务类,直接注入即可。即springboot整合activiti启动过程中,已经在容器中自动装配了对应的服务类,需要的时候,仅仅需要取来用即可。



2.4.6 activiti的操作逻辑梳理

springboot启动过程中,根据yml中的配置项,可以自动在指定的数据库中,生成流程引擎需要的25张表。

在这里插入图片描述

activiti的操作逻辑是这样的,数据库中,会提供25张表,专门供流程引擎用。

那么,流程引擎的执行流程是怎么样的呢?

(a)部署流程文件(流程类——相当于面向对象中的类文件)

(b)查询数据库中的可用流程类(即可以发起哪些流程实例,比如,可以发起请假流程、盖章流程,等等)

(c)根据查询的流程类(id或其他字段)启动流程实例

(d)启动流程实例后,一个流程实例,会根据流程图中不同步骤,存在对应的流程任务。发起人发起后,会生成一个属于自己的该实例的任务。

(e)对于某一个用户,需要做的,仅仅是查询系统中是否有自己的任务,然后完成该任务即可(或者市审批通过、或者市审批流转(驳回))。

(f)具体在使用该流程引擎的时候,需要区分开流程表和业务表的不同,以及整合两者之间的关系,此时,就需要用到businessKey。

在ActService中,编写了一些抽象的方法,供需要的时候调用:

  • 流程部署服务,供需要的时候,通过压缩包的形式,部署指定的流程。

    在这里插入图片描述

  • 查询流程定义(查询所有的已部署的流程定义类),在需要发起流程实例(比如,申请请假,或者申请用章的时候,使用)

    在这里插入图片描述

  • 查询所有正在运行的流程实例,可以根据此,查询目前正在运行的有哪些流程。

    在这里插入图片描述

  • 查询指定用户的所有任务。在前端展示的时候,可以基于此,查询到该用户有哪些待完成的任务。

    在这里插入图片描述

  • 完成指定任务,可以根据需要,设置map中的值,从而指派接下来由随来完成流程图中的下一个任务。

    在这里插入图片描述

  • 启动流程实例,这里通过流程定义Key,来启动对应的流程实例。

    在这里插入图片描述



2.5 测试文件中的流程执行

在这里插入图片描述

对应的代码如下所示:

package com.sky.demo.activitidemo;



import com.sky.demo.activitidemo.service.ActService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SpringBootTest
class ActivitiDemoApplicationTests {
    @Autowired
    ActService actService;
    @Autowired
    RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;


    /**
     * 启动流程
     */
    @Test
    public void TestStartProcess() {
        // 1. 查询流程定义列表 -- 这里相当于展示所有可以发起的流程。需要定义一个表,用来存储某类用户角色能够发起某类流程。
        List<ProcessDefinition> processDefinitionList = actService.getAllProcessDefinition();
        processDefinitionList.forEach(System.out::println);
        ProcessDefinition processDefinition = processDefinitionList.get(0);
        // 选择某一个流程类,启动一个流程。
        String businessKey="b1";
        Map<String, Object> map=new HashMap<>();
        // 如果流程的执行过程中,没有选择多个人可以完成某个流程的情况。可以指定流程变量。
        map.put("applicant","zhangsan");
        actService.startProcess(processDefinition.getId(),businessKey,map);

    }

    /**
     * 通过用户id查找流程。
     */
    @Test
    public void findTasksByUserId() {
        String userId ="zhangsan";
        List<Task> resultTask = taskService.createTaskQuery().taskCandidateOrAssigned(userId).list();
        System.out.println("任务列表:"+resultTask);
        // 任务列表里面。创建一个查询。通过流程id,获取指定类型的流程。
        String processInstanceId = resultTask.get(0).getProcessInstanceId();
        System.out.println("流程实例Id:"+processInstanceId);
    }
    /**
     * 完成个人任务
     */
    @Test
    public void completTask() {
        // 流程定义的key
        String key = "myevection";
        // 完成任务
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskCandidateOrAssigned("zhangsan")
                .list();
        Task task = list.get(0);
        String processInstanceId = null;
        if (task != null) {
            // 根据任务id来 完成任务。
            //taskService.addCandidateUser(task.getId(),"lisi");
            //taskService.addCandidateUser(task.getId(),"wangwu");
            processInstanceId = task.getProcessInstanceId();
            System.out.println("流程实例id1:"+processInstanceId);
            taskService.complete(task.getId());
            String processInstanceId1 = task.getProcessInstanceId();
            System.out.println("流程实例id2:"+processInstanceId1);
            System.out.println();
            System.out.println("任务已完成:" + task.getId());
            List<Task> list1 = taskService.createTaskQuery().processInstanceId(processInstanceId).list();
            if (list1.size()>0){
                Task task1 = list1.get(0);
                taskService.addCandidateUser(task1.getId(),"lisi");
                taskService.addCandidateUser(task1.getId(),"wangwu");
            }
        }

    }
    /**
     * 候选人拾取任务
     */
    @Test
    public void claimTask(){

        3. 当前任务的id
        //String taskId = "5002";
        //4. 候选人
        String user = "wangwu";
        List<Task> list = taskService.createTaskQuery().taskCandidateUser(user).list();
        //Task task = list.get(0);

        System.out.println(list);
        //if(task != null){
        //    //5. 拾取任务
        //    taskService.claim(task.getId(),user);
        //    System.out.println("taskId:"+task.getId()+" 用户:"+user+" 拾取任务完成");
        //
        //}
    }



}



2.6 说明

activiti涉及的内容较多。难以做到把具体涉及到业务的时候,可能存在的一些问题描述清楚。上面仅展示一个可以运行的demo.后续,遇到具体的问题,再展开分析。



3. 参考资源

请假流程的业务表设计

Activiti6自学之路(九)——请假申请和请假审批数据库表设计


在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述



4. 结语

本文的重点在于整合activiti7,并排除springSecurity的授权功能。实际开发过程中,首先需要区分业务数据和流程数据,其次需要能够把两者有机的整合起来,在实际开发OA类的系统的时候,需要重点关注状态模式,只有把流程引擎和状态模式两个结合起来,才可能够开发出符合OCP原则的系统。不至于出现if~else if~ else if ~else的扩展性差的系统。后续,将对状态模式进行学习研究。



版权声明:本文为fanjianglin原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。