Activiti6在Springboot下的使用 1 基本service使用

说明

最近需要弄一个通用的工作流引擎(前后端分离,前端绘制流程),选用Activiti6技术(6文档较多 7目前没有正式版,原理都差不多,7删除了几张表和service),在此记录一下Activiti6在Springboot下的使用(仅介绍后端,前端绘制略过)。主要使用到的activiti service如下:

RepositoryService:对流程定义进行管理。 
RuntimeService:对流程实例的管理。 
TaskService:对流程任务进行管理。 
IdentityService:管理用户和用户组。 
ManagementService:提供对activiti数据库的直接访问【一般不用】。 
HistoryService:对流程的历史数据进行操作。 
FormService:动态表单。

快速开始

Activiti6是有Springboot版本的可以在maven仓库搜索->activiti6-Springboot,想要研究Activiti7的戳->activiti7-Springboot

首先更新pom.xml引入Activiti6,注意:Activiti6-starter支持到Springboot1.X的版本,不能使用2.X版本

<?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 http://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>1.5.21.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>cn.yunlingfy</groupId>
    <artifactId>springboot-activiti</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springboot-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-actuator</artifactId>
        </dependency>
        <!-- 使用undertow做服务器 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-undertow</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

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

        <!-- 配置阿里druid连接池,通过改变spring.datasource.type设置数据源 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.10</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.activiti/activiti-spring-boot-starter -->
        <!--<dependency>-->
        <!--<groupId>org.activiti</groupId>-->
        <!--<artifactId>activiti-spring-boot-starter</artifactId>-->
        <!--<version>7.1.0.M2</version>-->
        <!--</dependency>-->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter-basic</artifactId>
            <version>6.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.persistence</groupId>
            <artifactId>persistence-api</artifactId>
            <version>1.0</version>
        </dependency>

        <!-- 转换模型需要使用,可以剔除 -->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-json-converter</artifactId>
            <version>6.0.0</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,如果需要项目启动的时候自动部署bpmn流程,请在resource目录下新建processes文件夹,在里面放.bpmn文件,并将spring.check-process-definitions设置为true

server:
  port: 8082
  # 下面是配置undertow作为服务器的参数
  undertow:
    # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程
    io-threads: 4
    # 阻塞任务线程池, 当执行类似servlet请求阻塞操作, undertow会从这个线程池中取得线程,它的值设置取决于系统的负载
    worker-threads: 20
    # 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内存管理
    # 每块buffer的空间大小,越小的空间被利用越充分
    buffer-size: 1024
    # 是否分配的直接内存
    direct-buffers: true
spring:
  application:
    name: springboot-activiti
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driverClassName: com.mysql.jdbc.Driver
    url: jdbc:mysql://tencentyun:3506/activiti6?useSSL=false&characterEncoding=utf8
    username: root
    password: root
    druid:
      # 连接池的配置信息
      # 初始化大小,最小,最大
      initial-size: 5
      min-idle: 5
      maxActive: 20
      # 配置获取连接等待超时的时间
      maxWait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      timeBetweenEvictionRunsMillis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      minEvictableIdleTimeMillis: 300000
      validationQuery: SELECT 1 FROM DUAL
      testWhileIdle: true
      testOnBorrow: false
      testOnReturn: false
      # 打开PSCache,并且指定每个连接上PSCache的大小
      poolPreparedStatements: true
      maxPoolPreparedStatementPerConnectionSize: 20
      # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      filters: stat,wall,log4j
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connectionProperties: druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
      # 配置DruidStatFilter
      web-stat-filter:
        enabled: true
        url-pattern: "/*"
        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
      # 配置DruidStatViewServlet
      stat-view-servlet:
        url-pattern: "/druid/*"
        # IP白名单(没有配置或者为空,则允许所有访问)
        #        allow: 127.0.0.1,192.168.163.1
        # IP黑名单 (存在共同时,deny优先于allow)
        #        deny: 192.168.1.73
        #  禁用HTML页面上的“Reset All”功能
        reset-enable: false
        # 登录名
        login-username: admin
        # 登录密码
        login-password: 123456
  activiti:
    # 设置activiti数据库执行的策略类似hibernate的数据库update设置,一般初次运行时设置为true
#    database-schema-update: true
    # 关闭验证自动部署/processes下的文件
    check-process-definitions: false
    # 保存历史数据的最高级别
    history-level: full
    # 表示使用历史表
    db-history-used: true
info:
  name: activiti-springboot
  version: 0.1

编写启动类(不需要配置Activiti的配置,这里只是多添加了一个Mybatis的通用mapper,不用mybatis的可以去除):

注意:如果不使用activiti自带的Spring Security,需要在这里去除Security的配置

package cn.yunlingfy.springbootactiviti;

import org.activiti.spring.boot.SecurityAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import tk.mybatis.spring.annotation.MapperScan;

@SpringBootApplication(exclude = {SecurityAutoConfiguration.class})
public class SpringbootActivitiApplication {

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

}

下面开始使用Activiti6啦~


Activiti6基本service的使用

package cn.yunlingfy.springbootactiviti.api.controller;

import cn.yunlingfy.springbootactiviti.infra.util.UploadFileMgr;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.Group;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.engine.HistoryService;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping("/")
public class LoginController {
    private Logger logger = Logger.getLogger(this.getClass());    //log4j日志
    // 仓库服务类
    @Autowired
    private RepositoryService repositoryService;
    // 运行服务类
    @Autowired
    private RuntimeService runtimeService;
    // 用户任务服务类
    @Autowired
    private TaskService taskService;
    // 身份管理和认证(建议自建表)
    @Autowired
    private IdentityService identityService;
    // 历史表
    @Autowired
    private HistoryService historyService;

    @RequestMapping(value = "/getData", method = {RequestMethod.POST, RequestMethod.GET})
    public String getData() {
        return "dsa";
    }

    @RequestMapping(value = "/testDenied", method = {RequestMethod.POST, RequestMethod.GET})
    public String testDenied() {
        return "";
    }

    @RequestMapping(value = "/init", method = {RequestMethod.POST, RequestMethod.GET})
    public String initUser(){
        Group group1 = identityService.newGroup("dev");
        group1.setName("dev");
        group1.setType("devassignment");
        identityService.saveGroup(group1);//建立HR组

        Group group2 = identityService.newGroup("create");
        group2.setName("create");
        group2.setType("createassignment");
        identityService.saveGroup(group2);//建立ZJ组

        Group group3 = identityService.newGroup("admin");
        group3.setName("admin");
        group3.setType("adminassignment");
        identityService.saveGroup(group3);//建立员工组

        //newUser传的是key【不是名字】
        identityService.saveUser(identityService.newUser("aaa"));// 程序员
        identityService.saveUser(identityService.newUser("bbb"));// 部长
        identityService.saveUser(identityService.newUser("ccc"));// 经理
        identityService.saveUser(identityService.newUser("ddd"));// 经理

        identityService.createMembership("aaa", "dev");
        identityService.createMembership("bbb", "dev");
        identityService.createMembership("bbb", "create");
        identityService.createMembership("ccc", "admin");
        identityService.createMembership("ddd", "admin");
        return "success";
    }
    /**
     * 部署流程定义
     */
    @RequestMapping(value = "/deployment", method = {RequestMethod.POST, RequestMethod.GET})
    public String deployment() {
        Deployment deployment = repositoryService.createDeployment()//创建一个部署对象
                .name("申请流程_1")
                .addClasspathResource("processes/c877c4b9-b07e-4a00-a17a-e8c7407c5b42.bpmn")
                .deploy();
        System.out.println("部署ID:" + deployment.getId());
        System.out.println("部署名称:" + deployment.getName());
        return "success";
    }

    /**
     * 删除流程定义
     */
    @RequestMapping(value = "/deleteProcess", method = {RequestMethod.POST, RequestMethod.GET})
    public void deleteProcess(String deploymentId) {
        /**不带级联的删除:只能删除没有启动的流程,如果流程启动,就会抛出异常*/
//     repositoryService.deleteDeployment(deploymentId);

        /**级联删除:不管流程是否启动,都能可以删除(emmm大概是一锅端)*/
        repositoryService.deleteDeployment(deploymentId, true);
        System.out.println("删除成功!");
    }

    /**
     * 启动流程实例分配任务给个人
     */
    @RequestMapping(value = "/start", method = {RequestMethod.POST, RequestMethod.GET})
    public void start(String processDefinitionKey, String userKey) {
//        String processDefinitionKey = "myProcess";//每一个流程有对应的一个key这个是某一个流程内固定的写在bpmn内的
        // 如果流程启动需要参数
        HashMap<String, Object> variables = new HashMap<>();
        variables.put("userKey", userKey);//userKey在上文的流程变量中指定了

        ProcessInstance instance = runtimeService.startProcessInstanceByKey(processDefinitionKey, variables);
//        ProcessInstance instance = runtimeService.startProcessInstanceById(processDefinitionId);

        System.out.println("流程实例ID:" + instance.getId());
        System.out.println("流程定义ID:" + instance.getProcessDefinitionId());
    }

    /**
     * 查询当前人的个人任务
     */
    @RequestMapping(value = "/findTask", method = {RequestMethod.POST, RequestMethod.GET})
    public void findTask(String assignee) {
//        String assignee = "aaa";
        List<Task> list = taskService.createTaskQuery()//创建任务查询对象
                .taskAssignee(assignee)//指定个人任务查询
                .list();
        if (list != null && list.size() > 0) {
            for (Task task : list) {
                System.out.println("任务ID:" + task.getId());
                System.out.println("任务名称:" + task.getName());
                System.out.println("任务的创建时间:" + task.getCreateTime());
                System.out.println("任务的办理人:" + task.getAssignee());
                System.out.println("流程实例ID:" + task.getProcessInstanceId());
                System.out.println("执行对象ID:" + task.getExecutionId());
                System.out.println("流程定义ID:" + task.getProcessDefinitionId());
            }
        }
    }

    /**
     * aaa完成任务
     */
    @RequestMapping(value = "/aaaCompleteTask", method = {RequestMethod.POST, RequestMethod.GET})
    public void aaaCompleteTask(String taskId, String days) {
        //任务ID
//        String taskId = "47506";
//        String days="4";

        HashMap<String, Object> variables = new HashMap<>();
        variables.put("days", days);//userKey在上文的流程变量中指定了
//        taskService.claim(taskid,"ZJ2");//指定办理人
//        taskService.setAssignee(taskid, null);//回退为组任务状态

        taskService.complete(taskId, variables);
        System.out.println("完成任务:任务ID:" + taskId);
    }
    /**
     * bbb完成任务
     */
    @RequestMapping(value = "/bbbCompleteTask", method = {RequestMethod.POST, RequestMethod.GET})
    public void bbbCompleteTask(String taskId) {
        taskService.complete(taskId);
        System.out.println("bbb完成任务:任务ID:" + taskId);
    }
    /**
     * ccc完成任务
     */
    @RequestMapping(value = "/cccCompleteTask", method = {RequestMethod.POST, RequestMethod.GET})
    public void cccCompleteTask(String taskId) {
        taskService.complete(taskId);
        System.out.println("ccc完成任务:任务ID:" + taskId);
    }

    /**
     * 查询当前人的组任务
     */
    @RequestMapping(value = "/findTaskGroup", method = {RequestMethod.POST, RequestMethod.GET})
    public void findTaskGroup(String taskCandidateUser) {
        //String assignee = "PTM";
//        String taskCandidateUser="admin";
        List<Task> list = taskService.createTaskQuery()//创建任务查询对象
                .taskCandidateUser(taskCandidateUser)//指定组任务查询(也可以不指定)
//                .taskAssignee(assignee)//指定个人任务查询(如果完成任务时指定了办理人)
                .list();
        String taskid ="";
        String instanceId ="";
        if(list!=null && list.size()>0){
            for(Task task:list){
                System.out.println("任务ID:"+task.getId());
                System.out.println("任务名称:"+task.getName());
                System.out.println("任务的创建时间:"+task.getCreateTime());
                System.out.println("任务的办理人:"+task.getAssignee());
                System.out.println("流程实例ID:"+task.getProcessInstanceId());
                System.out.println("执行对象ID:"+task.getExecutionId());
                System.out.println("流程定义ID:"+task.getProcessDefinitionId());
                taskid=task.getId();
                instanceId = task.getProcessInstanceId();
            }
        }
        //查询组任务成员[两种方式],runtime查询没有taskId,task查询没有InstanceId
        //List<IdentityLink> listIdentity = taskService.getIdentityLinksForTask(taskid);
        List<IdentityLink> listIdentity = runtimeService.getIdentityLinksForProcessInstance(instanceId);
        for(IdentityLink identityLink:listIdentity ){
            System.out.println("userId="+identityLink.getUserId());
            System.out.println("taskId="+identityLink.getTaskId());
            System.out.println("piId="+identityLink.getProcessInstanceId());
        }
//        String assignee = "a";
//        List<Task> list = taskService.createTaskQuery()//创建任务查询对象
////                .taskCandidateUser("ZJ")//指定组任务查询
//                .taskAssignee(assignee)
//                .list();
//        String taskid = "";
//        String instanceId = "";
//        if (list != null && list.size() > 0) {
//            for (Task task : list) {
//                System.out.println("任务ID:" + task.getId());
//                System.out.println("任务名称:" + task.getName());
//                System.out.println("任务的创建时间:" + task.getCreateTime());
//                System.out.println("任务的办理人:" + task.getAssignee());
//                System.out.println("流程实例ID:" + task.getProcessInstanceId());
//                System.out.println("执行对象ID:" + task.getExecutionId());
//                System.out.println("流程定义ID:" + task.getProcessDefinitionId());
//            }
//        }
    }

    @RequestMapping(value = "/findHistory", method = {RequestMethod.POST, RequestMethod.GET})
    public void HistoryProcessInstance() {
        List<HistoricProcessInstance> datas = historyService.createHistoricProcessInstanceQuery()
                .finished().list();
        for (HistoricProcessInstance historicProcessInstance : datas) {
            System.out.println("流程实例id: " + historicProcessInstance.getId());
            System.out.println("部署id: " + historicProcessInstance.getDeploymentId());
            System.out.println("开始event: " + historicProcessInstance.getStartActivityId());
            System.out.println("结束event: " + historicProcessInstance.getEndActivityId());
            System.out.println("流程名称: " + historicProcessInstance.getName());
            System.out.println("PROC_DEF_ID: " + historicProcessInstance.getProcessDefinitionId());
            System.out.println("流程定义Key: " + historicProcessInstance.getProcessDefinitionKey());
            System.out.println("流程定义名称: " + historicProcessInstance.getProcessDefinitionName());
        }
    }

    /****************** 分割线 *****************************/

    /**
     * 开启流程实例
     */
    @RequestMapping(value = "/startProcessInstance", method = {RequestMethod.POST, RequestMethod.GET})
    public void startProcessInstance(String processDefinitionKey) {
        //流程定义的key
//        String processDefinitionKey = "myProcess";
        //key对应MyProcess.bpmn文件中id的属性值,使用key值启动,默认是按照最新版本的流程定义启动
        ProcessInstance pi = runtimeService.startProcessInstanceByKey(processDefinitionKey);
        System.out.println("流程实例ID:" + pi.getId());//流程实例ID
        System.out.println("流程定义ID:" + pi.getProcessDefinitionId());//流程定义ID
    }

    /**
     * 查询流程实例
     */
    @RequestMapping(value = "/searchProcessInstance", method = {RequestMethod.POST, RequestMethod.GET})
    public void searchProcessInstance(String processDefinitionKey) {
//        String processDefinitionKey = "myProcess";
        ProcessInstance pi = runtimeService.createProcessInstanceQuery()
                .processDefinitionKey(processDefinitionKey)
                .singleResult();
        System.out.println("流程实例ID:" + pi.getId());
        System.out.println("流程定义ID:" + pi.getProcessDefinitionId());
    }

    /**
     * 流程实例的删除
     */
    @RequestMapping(value = "/deleteProcessInstanceTest", method = {RequestMethod.POST, RequestMethod.GET})
    public void deleteProcessInstanceTest(String processDefinitionKey) {
//        String processDefinitionKey = "myProcess";
        ProcessInstance pi = runtimeService.createProcessInstanceQuery()
                .processDefinitionKey(processDefinitionKey)
                .singleResult();
        String processInstanceId = pi.getProcessInstanceId();
        System.out.println("流程实例ID:" + pi.getId());
        runtimeService.deleteProcessInstance(processInstanceId, "删除测试");
    }
}

提示:taskService.createTaskQuery()可以添加很多的查询参数,具体可以看代码提示

上面的操作会使用到的重要的表如下:

act_id_user                        用户定义表(建议不用,功能不全)

act_id_group                      分组表(同样不建议使用)

act_re_deployment            部署信息表

act_re_procdef                   流程定义表

act_de_model                     流程模型表(模型信息)

act_ge_bytearray                模型二进制表(保存模型记录)

act_ru_task                         正在运行的任务(流程走完后这张表会清除信息,然后放入历史表)

act_hi_*                               历史表

基本使用结束,但不够动态,完成任务完全写死,会造成每个流程的部署都需要改动代码,下一节我们扩展一下~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章