SpringBoot2集成Activiti7教程

废话不多,直接上教程,需要什么概念之类的自己去搜

idea准备工作

安装 actiBPM插件.

在这里插入图片描述

部分用户安装可能会出点小插曲,自己百度一下都能轻松解决.

1.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.1.13.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>cn.xxdaha</groupId>
	<artifactId>approval</artifactId>
	<version>1.0.0</version>
	<name>approval</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>8</java.version>
		<maven.compiler.source>${java.version}</maven.compiler.source>
		<maven.compiler.target>${java.version}</maven.compiler.target>
		<activiti-dependencies.version>7.1.24</activiti-dependencies.version>
	</properties>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.activiti.dependencies</groupId>
				<artifactId>activiti-dependencies</artifactId>
				<version>${activiti-dependencies.version}</version>
				<scope>import</scope>
				<type>pom</type>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.activiti</groupId>
			<artifactId>activiti-spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<scope>runtime</scope>
		</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>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>com.google.code.gson</groupId>
			<artifactId>gson</artifactId>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>${maven-compiler-plugin.version}</version>
				<configuration>
					<release>${java.version}</release>
					<showDeprecation>true</showDeprecation>
					<showWarnings>true</showWarnings>
					<optimize>true</optimize>
				</configuration>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>alfresco</id>
			<name>Activiti Releases</name>
			<url>https://artifacts.alfresco.com/nexus/content/repositories/activiti-releases/</url>
			<releases>
				<enabled>true</enabled>
			</releases>
		</repository>
	</repositories>

</project>
最新本SpringBoot与最新版Activiti整合报错,版本选择 2.1.13, 其他版本未尝试.

2. application.yml文件

server:
    port: 8080
    session-timeout: 60
    tomcat:
        max-threads: 100
        uri-encoding: UTF-8

spring:
  resources:
    static-locations: classpath:/
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.239.129:3306/activiti?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
    username: root
    password: Aa112233..
    hikari:
      minimum-idle: 5
      maximum-pool-size: 20
      connection-test-query: SELECT 1
  activiti:
    database-schema: ACTIVITI
    database-schema-update: true # false:false为默认值,设置为该值后,Activiti在启动时,会对比数据库表中保存的版本,如果没有表或者版本不匹配时,将在启动时抛出异常。
                                 # true:设置为该值后,Activiti会对数据库中所有的表进行更新,如果表不存在,则Activiti会自动创建。
                                 # create-drop:Activiti启动时,会执行数据库表的创建操作,在Activiti关闭时,执行数据库表的删除操作。
                                 # drop-create:Activiti启动时,执行数据库表的删除操作在Activiti关闭时,会执行数据库表的创建操作。

    history-level: full          #none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。
                                 #activity:级别高于none,保存流程实例与流程行为,其他数据不保存。
                                 #audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。
                                 #full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。

    db-history-used: true        #表示使用历史表,如果不配置,则工程启动后可以检查数据库,历史表没有建立,则流程图及运行节点无法展示。

数据库连接那里自己改改.

3.准备Security相关配置:

SpringBoot集成Activiti7时, 默认引入spring-Security,下拉jar包列表可以找到.

在这里插入图片描述

凡是要认证,都得有认证过程.官方demo中有 SecurityUtil, 这里直接拿过来用.

/**
 * @author xxdaha
 * @date 2020-04-20 10:42
 * @explain
 */
@Component
public class SecurityComponent {
    @Autowired
    private UserDetailsService userDetailsService;

    public void logInAs(String username) {
        UserDetails user = userDetailsService.loadUserByUsername(username);
        if (user == null) {
            throw new IllegalStateException("用户未定义");
        }
        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);
    }
}

凡是要认证,就得有数据源.官方demo中有DemoApplicationConfiguration,直接拿过来用.

/**
 * @author xxdaha
 * @date 2020-04-20 10:45
 * @explain
 */
@Configuration
@EnableWebSecurity
public class ApplicationConfiguration extends WebSecurityConfigurerAdapter {

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

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

    @Bean
    public UserDetailsService myUserDetailsService() {

        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();

        String[][] usersGroupsAndRoles = {
                {"zhangsan", "123", "ROLE_ACTIVITI_USER", "GROUP_emp"},
                {"lisi", "123", "ROLE_ACTIVITI_USER", "GROUP_boss"},
                {"wangwu", "123", "ROLE_ACTIVITI_USER", "GROUP_money"},
        };

        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();
    }
}
usersGroupsAndRoles数组中就是认证数据源,项目中要从数据库认证的自己改,这里图方便.

在启动类中排除SecurityAutoConfiguration.

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)

在这里插入图片描述

4. 画流程图

在resources下新建一个processes文件夹,新建一个bpmn文件.

ps:要装actiBPM插件才有这个文件选项.
这里流程图如下:

在这里插入图片描述

流程图属性以及说明:
Assignee: 直接指定任务处理人.采用变量,方便启动流程实例的指定申请人.

在这里插入图片描述

Candidate Groups: 指定某个组(角色)的人员进行审批(完成任务).开发中可能这个选项用的比较多,毕竟有时候审批人不止一个.

在这里插入图片描述
在这里插入图片描述

可选: 可以将bpmn文件复制粘贴来同级目录下xml文件, 或者直接用其他记事本工具打开bpmn文件,可以发现里面就是一堆坐标信息.发现bpmn本质上也就是个xml文件.
右键bpmn或者重新生成的xml文件, 生成图片.

在这里插入图片描述

可以导出图片保存.

在这里插入图片描述

5.开始测试

1.新建数据库activiti(项目启动时, activiti跟根据配置在数据库新建相关表.包含历史记录表共25张)

2.写测试类,我这里直接拿SpringBoot项目生成时自带的.

在这里插入图片描述

3.注入需要使用的api

@Autowired
private RepositoryService repositoryService;
@Autowired
private ProcessRuntime processRuntime; // activiti7新api
@Autowired
private SecurityComponent securityComponent; //
@Autowired
private TaskRuntime taskRuntime; // activiti7新api
@Autowired
private TaskService taskService;
@Autowired
private HistoryService historyService;
@Autowired
private RuntimeService runtimeService;

4.部署:这里直接跳过部署,因为SpringBoot与Activiti7集成启动时,自动读取Processes目录下的bpmn文件,解析节点信息加载到数据库进行部署.

5.查看部署的流程信息定义

/**
 * @param
 * @return void
 * @author xxDaha
 * @date 2020/4/20
 * @explain 流程定义信息的查看
 */
@Test
public void testPro() {
    securityComponent.logInAs("zhangsan"); // security的认证工作 不加会报错 没有权限 An Authentication object was not found in the SecurityContext
    Page processDefinitionPage = processRuntime
            .processDefinitions(Pageable.of(0, 10));
    //查看已部署流程个数
    System.out.println("已部署流程数" + processDefinitionPage.getTotalItems());
    //得到每一个部署流程定义
    for (Object o : processDefinitionPage.getContent()) {
        System.out.println("已部署详细流程定义" + o);
    }
}
认证用户名"zhangsan",是我们刚才在demo配置文件里面配置的,可以自行更换.
方法执行完毕,刷新数据库,发现多了25张表,点开act_re_deployment, act_re_procdef,act_ge_bytearray查看,输出信息与刚刚部署的数据一致.

6 启动一个流程实例(可以看做申请对象).

/**
     * @author xxdaha
     * @date 2020/4/21
     * @explain
     * @param
     * @return void
     * act_hi_actinst: 已完成的活动信息
     * act_hi_identitylink: 参与者信息
     * act_hi_procinst: 流程实例
     * act_hi_taskinst: 任务实例
     * act_ru_execution: 执行表
     * act_ru_identitylink: 参与者信息
     * act_ru_task: 任务
     */
    @Test
    public void testStart() {
        String name = "zhangsan";
        securityComponent.logInAs(name);
        Map map = new HashMap();
        map.put("emp", name);
        map.put("money", "5000");
        map.put("time", new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
        org.activiti.api.process.model.ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("approval")  //流程定义的key
                .withName("zhangsanApproval") //流程实例的名称
                .withVariables(map)
                .build());
        System.out.println("启动实例流程" + processInstance);
    }
这里是直接给流程图中的emp属性赋值了zhangsan.刚才流程图中name的值为变量{emp}.所以任务执行人为zhangsan. 变量数据保存在act_ru_cariable表中.

7.zhangsan查询自己代办的任务.

/**
 * @param
 * @return void
 * @author xxDaha
 * @date 2020/4/20
 * @explain 查询任务状态与变量
 */
@Test
public void findTask() {
    securityComponent.logInAs("zhangsan"); //指定用户认证信息
    Page<org.activiti.api.task.model.Task> tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
    if (tasks != null && tasks.getTotalItems() > 0) {
        //有任务
        for (org.activiti.api.task.model.Task task : tasks.getContent()) {
            // 遍历任务列表
            System.out.println("任务详情: " + task);
            String money = (String) taskService.getVariable(task.getId(), "money");
            String time = (String) taskService.getVariable(task.getId(), "time");
            System.out.println("启动时设置的变量, money: " + money + ", " + "time: " + time);
        }
    }
}
也可以修改变量值(taskService.setVariable()).想玩自己试.

8.zhangsan查询并完成自己代办的任务.

/**
 * @param
 * @return void
 * @author xxDaha
 * @date 2020/4/20
 * @explain 相关表:
 * act_hi_actinst
 * act_hi_identitylink
 * act_hi_taskinst
 * act_ru_identitylink
 * act_ru_task
 */
@Test
public void completeTask() {
    String name = "zhangsan";
    securityComponent.logInAs(name); //指定用户认证信息
    Page<org.activiti.api.task.model.Task> tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
    if (tasks != null && tasks.getTotalItems() > 0) {
        //有任务
        for (org.activiti.api.task.model.Task task : tasks.getContent()) {
            // 遍历任务列表
            System.out.println("任务详情: " + task);
            if (name.equals(task.getAssignee())) {
                // 获得任务
                //完成任务
                org.activiti.api.task.model.Task complete = taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
                System.out.println("完成任务返回值: " + complete);
            }
        }
    }
    // 此时再查询一遍任务列表
    int count = 0;
    tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
    if (tasks != null && tasks.getTotalItems() > 0) {
        //有任务
        for (org.activiti.api.task.model.Task task : tasks.getContent()) {
            System.out.println("任务详情: " + task);
            // 获得任务
            if (name.equals(task.getAssignee())) {
                count++;
            }
        }
    }
    if (count == 0) {
        System.out.println("您当前没有任何任务哦");
    }
}
之前启动流程的时候是指定了任务处理人为zhangsan,所以这里判断了一下名字一致,避免领到别人的其他没有指定处理人的任务.也可以省略掉.

9.boss角色(组)的人员查看自己的任务, 获取任务并且审批.

    /**
     * @param
     * @return void
     * @author xxDaha
     * @date 2020/4/20
     * @explain boss获取任务进行审批
     */
    @Test
    public void bossCompleteTask() {
        String name = "lisi";
        securityComponent.logInAs(name); //指定用户认证信息
        Page<org.activiti.api.task.model.Task> tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
        if (tasks != null && tasks.getTotalItems() > 0) {
            //有任务
            for (org.activiti.api.task.model.Task task : tasks.getContent()) {
                // 遍历任务列表
                System.out.println("任务详情: " + task);
                // 获得任务
                org.activiti.api.task.model.Task claim = taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
                System.out.println("拾取, 获得任务返回值 : " + claim);
//                if (task != null) {
                // 如果设置为null,归还组任务,该 任务没有负责人 可以重新被拾取
//                    taskService.setAssignee(task.getId(), null);
//                }
                //完成任务
                org.activiti.api.task.model.Task complete = taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
                System.out.println("完成任务返回值: " + complete);

            }
        }
        // 此时再查询一遍任务列表
        int count = 0;
        tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
        if (tasks != null && tasks.getTotalItems() > 0) {
            //有任务
            for (org.activiti.api.task.model.Task task : tasks.getContent()) {
                System.out.println("剩余任务详情: " + task);
                // 获得任务
                if (name.equals(task.getAssignee())) {
                    count++;
                }
            }
        }
        if (count == 0) {
            System.out.println("您当前没有任何任务哦");
        }
    }
boss审批后,可以查看act_ru_task中的数据.其他表可以自己看看.
上例中有放弃任务,归还任务池的,那么也有指定任务给其他同事的,这里组里没有多个角色,就随便举例一下
    /**
     * @param
     * @return void
     * @author xxDaha
     * @date 2020/4/20
     * @explain 将任务交接给其他人处理
     */
    @Test
    public void setAssigneeToCandidateUser() {
        // 当前待办任务
        String taskId = "taskId";
        // 任务负责人
        String userId = "lisi";
        // 将此任务交给其它候选人办理该 任务
        String candidateuser = "lisi";
        // 校验userId是否是taskId的负责人,如果是负责人才可以归还组任务
        Task task = taskService.createTaskQuery().taskId(taskId)
                .taskAssignee(userId).singleResult();
        if (task != null) {
            taskService.setAssignee(taskId, candidateuser);
        }
    }

10. money(角色)组的任务获取任务进行完成, 审批.

/**
 * @param
 * @return void
 * @author xxDaha
 * @date 2020/4/20
 * @explain 财务获取任务进行审批
 */
@Test
public void moneyCompleteTask() {
    String name = "wangwu";
    securityComponent.logInAs(name); //指定用户认证信息
    Page<org.activiti.api.task.model.Task> tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
    if (tasks != null && tasks.getTotalItems() > 0) {
        //有任务
        for (org.activiti.api.task.model.Task task : tasks.getContent()) {
            // 遍历任务列表
            System.out.println("任务详情: " + task);
            // 获得任务
            org.activiti.api.task.model.Task claim = taskRuntime.claim(TaskPayloadBuilder.claim().withTaskId(task.getId()).build());
            System.out.println("拾取, 获得任务返回值 : " + claim);
            //完成任务
            org.activiti.api.task.model.Task complete = taskRuntime.complete(TaskPayloadBuilder.complete().withTaskId(task.getId()).build());
            System.out.println("完成任务返回值: " + complete);

        }
    }
    // 此时再查询一遍任务列表
    int count = 0;
    tasks = taskRuntime.tasks(Pageable.of(0, 10)); //分页查询任务列表
    if (tasks != null && tasks.getTotalItems() > 0) {
        //有任务
        for (org.activiti.api.task.model.Task task : tasks.getContent()) {
            System.out.println("剩余任务详情: " + task);
            // 获得任务
            if (name.equals(task.getAssignee())) {
                count++;
            }
        }
    }
    if (count == 0) {
        System.out.println("您当前没有任何任务哦");
    }
}

**

完结 ! 查询历史什么的就不列举了! 撒花 !

发布了1 篇原创文章 · 获赞 0 · 访问量 28

猜你喜欢

转载自blog.csdn.net/xxdaha/article/details/105639993