activiti与springboot整合

创建工程

1、pom
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.0.RELEASE</version>
</parent>
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</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-test</artifactId>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring-boot-starter</artifactId>
        <version>7.0.0.Beta2</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.29</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
2、application.yml
spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/activiti?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
    username: root
    password: root
  activiti:
#    false:默认值。在activiti启动时,会对比数据库表中保存的版本,如果没有表或版本不匹配,将抛出异常。(生产环境常用)
#    true:activiti会对数据库中所有表进行更新操作。如果表不存在,将会创建。(开发环境常用)
#    create-drop:在activiti启动时创建表,在关闭时删除表,必须手动关闭引擎才会删除表。(单元测试用)
#    drop-create:在activiti启动时删除旧表,然后创建新表,不需要关闭引擎。
    database-schema-update: true
    #检测历史表是否存在,activiti7默认没有开启数据库历史记录,true启动数据库历史记录
    db-history-used: true
    #记录历史等级,可配置的历史级别有:none,activity,audit,full
    # none:不保存任何的历史数据,因此在流程执行过程中,这是最高效的
    # activity:级别高于none,保存流程实例与流程行为,其他数据不保存
    # audit:除保存activity保存的数据外,还会保存全部的流程任务及其属性。history-level的默认值
    # full:保存历史数据的最高级别,会额外保存全部流程相关的细节数据。
    history-level: full
    #校验流程文件,默认校验resources下的process文件夹的流程文件
    check-process-definitions: false
3、启动类
@SpringBootApplication
public class ActivitiApplication {

    public static void main(String[] args) {
        SpringApplication.run(ActivitiApplication.class, args);
    }
}
4、经典类测试

同之前一样,配置完成后,创建相应的Service服务即可进行操作

@RunWith(SpringRunner.class)
@SpringBootTest(classes = ActivitiApplication.class)
public class Test01 {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Test
    public void deployment() {
        repositoryService.createDeployment()
                .name("activiti-springboot")
                .addClasspathResource("bpmn/activiti-springboot.bpmn20.xml")
                .deploy();
    }

    @Test
    public void startProcess() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("assignee0", "zhangsan");
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("activiti-springboot", map);
        System.out.println(processInstance.getProcessDefinitionKey());
    }
}
5、新特性测试

在activiti7的新特性:

  1. 更好的支持springboot
  2. 引入springsecurity安全机制
  3. 核心API进行了封装
  4. 对云发布、分布式支持等
5.1、添加springsecurity配置

activiti7与springboot整合后,默认情况下就集成了springsecurity,所以我们需要准备springsecurity的相关配置。

不然会提示异常。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lHsy1Lmc-1658298356742)(https://cdn.jsdelivr.net/gh/le1024/image1/le/image-20211223152441669.png)]

SecurityUtil

@Slf4j
@Component
public class SecurityUtil {

    @Autowired
    private UserDetailsService userDetailsService;

    /**
     * 模拟用户登录
     * @param username 用户名
     */
    public void login(String username) {
        UserDetails user = userDetailsService.loadUserByUsername(username);
        if (user == null) {
            throw new IllegalArgumentException("user doesn`t exist");
        }
        log.info("login :" + username);

        SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
            private static final long serialVersionUID = 3397680986162930722L;

            public Collection<? extends GrantedAuthority> getAuthorities() {
                return user.getAuthorities();
            }

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

            public Object getDetails() {
                return user;
            }

            public Object getPrincipal() {
                return user;
            }

            public boolean isAuthenticated() {
                return true;
            }

            public void setAuthenticated(boolean b) throws IllegalArgumentException {

            }

            public String getName() {
                return user.getUsername();
            }
        }));

        org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
    }

}

SecurityConfig

@Slf4j
@Configuration
public class SecurityConfig {

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
        //添加用户
        String[][] userGroupsAndRoles = {
                {"jack", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"rose", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"tom", "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 : userGroupsAndRoles) {
            List<String> authorities = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));
            log.info("注册新用户:{},用户权限:{}", user[0], authorities);
            inMemoryUserDetailsManager.createUser(
                    new User(
                            user[0],
                            passwordEncoder().encode(user[1]),
                            authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())
                    )
            );
        }
        return inMemoryUserDetailsManager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}
5.2、创建bpmn文件

根据自己的需要设计,学习教程中如下:

Spring集成activiti不引入security activiti springboot 整合_activiti

5.3、测试类
/*
     * 直接引入activiti7新增的api,通过新的api操作
     */
    @Autowired
    private ProcessRuntime processRuntime;
    @Autowired
    private TaskRuntime taskRuntime;
    @Autowired
    private SecurityUtil securityUtil;

    @Autowired
    private RepositoryService repositoryService;

    /**
     * 部署流程
     */
    @Test
    public void deployment() {
        repositoryService.createDeployment()
                .name("activiti-springboot")
                .addClasspathResource("bpmn/activiti-springboot.bpmn20.xml")
                .deploy();
    }

    /**
     * 查看流程定义
     */
    @Test
    public void viewProcessDefinition() {
        //需要模拟登录一个用户,不然会报异常
        securityUtil.login("system");
        Page<ProcessDefinition> definitionPage = processRuntime.processDefinitions(Pageable.of(0, 10));
        System.out.println("可用的流程定数数量:" + definitionPage.getTotalItems());

        for (ProcessDefinition definition : definitionPage.getContent()) {
            System.out.println("流程定义id:" + definition.getId());
        }
    }

启动流程实例

/**
     * 启动流程实例
     */
    @Test
    public void startProcess() {
        //需要模拟登录一个用户
        securityUtil.login("system");

        //设置变量
        Map<String, Object> map = new HashMap<>();
        map.put("assignee0", "jack");

        ProcessInstance instance = processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("activiti-springboot")
                .withVariables(map)
                .build());

        System.out.println("流程实例id:" + instance.getId());
    }

查询个人任务

/**
     * 查询个人任务
     */
    @Test
    public void queryTask() {
        //执行完流程实例后分配的任务是给jack了,现在用jack登录
        securityUtil.login("jack");

        Page<Task> taskPage = taskRuntime.tasks(Pageable.of(0, 10));

        for (Task task : taskPage.getContent()) {
            System.out.println("任务id:" + task.getId());
            System.out.println("任务name:" + task.getName());
        }

    }

完成个人任务

/**
     * 完成个人任务
     */
    @Test
    public void completeTask() {
        //执行完流程实例后分配的任务是给jack了,现在用jack登录
        securityUtil.login("jack");
        Page<Task> taskPage = taskRuntime.tasks(Pageable.of(0, 10));

        for (Task task : taskPage.getContent()) {
            //完成任务
            taskRuntime.complete(TaskPayloadBuilder
                    .complete()
                    .withTaskId(task.getId())
                    .build());
        }
    }

拾取任务

需要创建一份新的流程,实现组任务,不然拾取不了

/**
     * 拾取任务
     * 登录人必须是任务的候选人才可以,自行配置好测试
     */
    @Test
    public void claimTask() {
        securityUtil.login("rose");

        taskRuntime.claim(TaskPayloadBuilder
                .claim()
                //任务id应该是查询出来的,这里直接从数据库复制了
                .withTaskId("f2ca7116-63d5-11ec-b2e7-005056c00008")
                .build());
        System.out.println("拾取任务成功");
    }