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_*                               歷史表

基本使用結束,但不夠動態,完成任務完全寫死,會造成每個流程的部署都需要改動代碼,下一節我們擴展一下~

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