SpringBoot2.X集成Activiti7实现工作流任务创建、执行(附源码)

代码亲测有效,demo源码已给出。
GitHub代码地址:acitviti-demo

1. 环境准备

Idea安装actiBPM插件

SpringBoot2.X集成Activiti7实现工作流任务创建、执行(附源码)_第1张图片因为某些原因,现在在market_place里面搜不到这个插件,大家可以去官网下载(https://plugins.jetbrains.com/),搜索actiBPM。

应该没什么严格的版本要求,我下载的是2.E-8,然后得到actibpm.jar,然后File -> Settings -> Plugins
SpringBoot2.X集成Activiti7实现工作流任务创建、执行(附源码)_第2张图片
然后选中你下载的actibpm.jar,按提示重启就行了。SpringBoot2.X集成Activiti7实现工作流任务创建、执行(附源码)_第3张图片
在new的时候有BpmnFile就成功了。

2. Demo流程介绍

(1)部署activiti

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

(2)流程定义

使用 activiti 流程建模工具(activity-designer)定义业务流程(.bpmn 文件) 。
.bpmn 文件就是业务流程定义文件,通过 xml 定义业务流程。

(3)流程定义部署

向 activiti 部署业务流程定义(.bpmn 文件)。
使用 activiti 提供的 api 向 activiti 中部署.bpmn 文件(一般情况还需要一块儿部署业务流程的图
片.png)

(4)启动一个流程实例(ProcessInstance)

启动一个流程实例表示开始一次业务流程的运行,比如员工请假流程部署完成,如果张三要请
假就可以启动一个流程实例,如果李四要请假也启动一个流程实例,两个流程的执行互相不影
响,就好比定义一个 java 类,实例化两个对象一样,部署的流程就好比 java 类,启动一个流程
实例就好比 new 一个 java 对象。

(5)用户查询待办任务(Task)

因为现在系统的业务流程已经交给 activiti 管理,通过 activiti 就可以查询当前流程执行到哪了,
当前用户需要办理什么任务了,这些 activiti帮我们管理了,而不像上边需要我们在 sql语句中的where
条件中指定当前查询的状态值是多少。

(6)用户办理任务

用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如采
购单创建后由部门经理审核,这个过程也是由 activiti 帮我们完成了,不需要我们在代码中硬编码指
定下一个任务办理人了。

(7)流程结束

当任务办理完成没有下一个任务/结点了,这个流程实例就完成了。

3. 具体实施

3.1 构建项目

构建一个SpringBoot项目,名称为activity-demo,包名为com.act,artifactId为acttivity
添加依赖:

 <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-jdbcartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>

        
        <dependency>
            <groupId>org.activitigroupId>
            <artifactId>activiti-spring-boot-starterartifactId>
            <version>7.0.0.Beta2version>
        dependency>

        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>3.4.5version>
        dependency>
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
        dependency>
    dependencies>

配置文件:

spring:
  datasource:
    url: jdbc:mysql://localhost:3333/activiti?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT
    username : root
    password :
    driver-class-name: com.mysql.jdbc.Driver

数据库信息,和驱动名根据自己版本做修改

在启动类注解后加一个配置,排除SpringSecurity的自动扫描

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)

3.2 创建流程图

SpringBoot默认防止流程图的地方在src/resources/processes下,建立processes文件夹,然后新建一个team01.bpm,拖动形成如图所示,在中间两个环节中Groups中填上activitiTeam(这是一个用户组,代表用户组里面的人可以执行流程中的任务,下面代码中会有用户组的信息):

SpringBoot2.X集成Activiti7实现工作流任务创建、执行(附源码)_第4张图片

后期完成后,你通过修改流程图即可改变任务流程的执行顺序。不必去改变代码。

3.3 测试代码

activiti依赖于SpringSecurity,所以我们这里搞一个简单的SpringSecurity配置,新建一个config包,创建一个DemoApplicationConfiguration 类

package com.act.acttivity.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Configuration
public class DemoApplicationConfiguration {

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

    @Bean
    public UserDetailsService myUserDetailsService() {

        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();

        String[][] usersGroupsAndRoles = {
                {"salaboy", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"ryandawsonuk", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"erdemedeiros", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"},
                {"system", "password", "ROLE_ACTIVITI_USER"},
                {"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;
    }


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

}

新建一个SecurityUtil类,logInAs()方法可以直接根据用户名完成登录

package com.act.acttivity.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import java.util.Collection;

@Component
public class SecurityUtil {

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

    @Autowired
    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");
        }
        logger.info("> Logged in as: " + username);
        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);
    }
}

在test下面就可以创建一个ActivityTest,代码如下,有详细注解,依次编写执行即可实现流程定义、启动实例、任务查询、任务完成:

package com.act.acttivity;

import com.act.acttivity.config.SecurityUtil;
import org.activiti.api.process.model.ProcessDefinition;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.runtime.TaskRuntime;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * SpringBoot与Junit整合,测试流程定义的相关操作
 *  任务完成
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class ActivityTest {
    @Autowired
    private ProcessRuntime processRuntime; //实现流程定义相关操作

    @Autowired
    private TaskRuntime taskRuntime; //实现任务相关操作

    @Autowired
    private SecurityUtil securityUtil;//SpringSecurity相关的工具类

    //流程定义信息的查看
    @Test
    public void testDefinition(){
        securityUtil.logInAs("salaboy");//SpringSecurity认证
        //分页查询出流程定义信息
        Page<ProcessDefinition> processDefinitionPage = processRuntime
                .processDefinitions(Pageable.of(0, 10));
        System.out.println("可用的流程定义数量:" + processDefinitionPage.getTotalItems());//查看已部署的流程个数
        for (ProcessDefinition pd : processDefinitionPage.getContent()) {
            System.out.println("流程定义:" + pd);
        }
    }

    /**
     * 启动流程实例
     */
    @Test
    public void testStartProcess() {
        securityUtil.logInAs("salaboy");
        ProcessInstance pi = processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("myProcess_1")
                .build());//启动流程实例
        System.out.println("流程实例ID:" + pi.getId());
    }

    /**
     * 查询任务,并完成自己的任务
     */
    @Test
    public void testTask() {
        securityUtil.logInAs("ryandawsonuk");//认证
        Page<Task> taskPage=taskRuntime.tasks(Pageable.of(0,10));
        if (taskPage.getTotalItems()>0){
            for (Task task:taskPage.getContent()){
                System.out.println("任务:"+task);

                //拾取任务
                taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());

                //执行任务
                taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
            }
        }

        //完成后再次查询任务
        System.out.println("再次查询任务的结果:");
        Page<Task> taskPage2=taskRuntime.tasks(Pageable.of(0,10));
        if (taskPage2.getTotalItems()>0){
            System.out.println("剩余任务:"+taskPage2.getContent());
        }
    }
}

执行任务截图,第一次执行了first任务,剩余second任务:
在这里插入图片描述
再执行一次,second任务被执行,没有剩余任务:
SpringBoot2.X集成Activiti7实现工作流任务创建、执行(附源码)_第5张图片
你可以改变流程图中second任务的用户组,然后测试用该任务对应用户组中的用户去执行second任务。

3.4 在controller中进行任务执行

新建一个controller包,在下面创建TestController类:

package com.act.acttivity.controller;

import com.act.acttivity.config.SecurityUtil;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.runtime.TaskRuntime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

    @Autowired
    private ProcessRuntime processRuntime; //实现流程定义相关操作

    @Autowired
    private TaskRuntime taskRuntime; //实现任务相关操作

    @Autowired
    private SecurityUtil securityUtil;//SpringSecurity相关的工具类

    @RequestMapping("/hello")
    public String hello (){
        return "hello activity";
    }

    //查询任务,如果有任务就执行任务
    @RequestMapping("/doTask")
    public void testTask(){
        securityUtil.logInAs("ryandawsonuk");//认证
        Page<Task> taskPage=taskRuntime.tasks(Pageable.of(0,10));
        if (taskPage.getTotalItems()>0){
            for (Task task:taskPage.getContent()){
                System.out.println("任务:"+task);

                //拾取任务
                taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());

                //执行任务
                taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
            }
        }

        //完成后再次查询任务
        System.out.println("再次查询任务的结果:");
        Page<Task> taskPage2=taskRuntime.tasks(Pageable.of(0,10));
        if (taskPage2.getTotalItems()>0){
            System.out.println("任务:"+taskPage2.getContent());
        }
    }


}

注意:执行任务的时候如果已经没有任务了可执行Test中的启动流程实例方法来重新创建任务。

再次放上GitHub地址:acitviti-demo

你可能感兴趣的:(工作流引擎)