Activiti工作流的正確打開方式

一,什麼是工作流?

1,工作流(Workflow),就是“業務過程的部分或整體在計算機應用環境下的自動化”,它主要解決的是“使在多個參與者之間按照某種預定義的規則傳遞文檔、信息或任務的過程自動進行,從而實現某個預期的業務目標,或者促使此目標的實現。

2,工作流管理系統(Workflow Management System, WfMS)是一個軟件系統,它完成工作量的定義和管理,並按照在系統中預先定義好的工作流邏輯進行工作流實例的執行。工作流管理系統不是企業的業務系統,而是爲企業的業務系統的運行提供了一個軟件的支撐環境。

3,常見的工作流框架有ActivityJBPMOSWorkFlowWorkFlow

二,實現自動化

1,業務流程圖要規範化,要遵守一套標準。

2,業務流程圖本質上是一個xml文件,這樣就可以存入所有數據。

3,讀取業務流程圖的過程就是解析xml文件的過程。

4,讀取一個業務流程圖中的節點就相當於是解析一個xml結構,進一步將數據插入到mysql的表中,形成一條記錄。

5,將所有的節點都讀取並存入mysql表中。

6,後面只要讀取mysql表中的記錄就行了,讀一條記錄就相當於讀一個節點。

7,業務流程的推進,後面就轉化爲讀表中數據,並且處理數據,結束時這一行數據就可以刪除了,刪除就代表該業務流程結束。

三, ActivitiDesigner流程設計器安裝與使用(Eclipse版本)

Eclipse版本的流程設計器下載地址:鏈接:https://pan.baidu.com/s/1kgPqXlA75JzBSZ0xvkAG1w 提取碼:vgl1

下載完成後解壓,然後替換Eclipse安裝目錄列表中的相同的兩個目錄裏面的jar包,也就是移到Eclipse的安裝目錄的那兩個文件夾,然後重新打開Eclipse,就已經有流程設計器插件了。

在這裏插入圖片描述

四,ActivitiDesigner流程設計器安裝與使用(idea版本)

安裝actiBPM插件,並重啓idea

在這裏插入圖片描述

安裝完成後,在資源文件夾resources右鍵新建actiBPMFiles,然後就可以畫流程圖了。

五,Activiti支持的數據庫及準備工作

Activiti工作流需要數據庫的25張表支撐。

數據庫:mysql

創建表方式:通過運行java程序創建表。

1,快速創建一個springboot項目

過程不用多說

2,加入maven依賴的座標(jar包)

<properties>
        <java.version>1.8</java.version>
        <slf4j.version>1.6.6</slf4j.version>
        <log4j.version>1.2.12</log4j.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-engine</artifactId>
            <version>7.0.0.Beta1</version>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring</artifactId>
            <version>7.0.0.Beta1</version>
        </dependency>
		
        <!--activiti整合springboot的座標-->
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-spring-boot-starter</artifactId>
            <version>7.0.0.Beta2</version>
        </dependency>
        
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-model</artifactId>
            <version>7.0.0.Beta1</version>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-converter</artifactId>
            <version>7.0.0.Beta1</version>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-json-converter</artifactId>
            <version>7.0.0.Beta1</version>
        </dependency>

        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-bpmn-layout</artifactId>
            <version>7.0.0.Beta1</version>
        </dependency>

        <dependency>
            <groupId>org.activiti.cloud</groupId>
            <artifactId>activiti-cloud-services-api</artifactId>
            <version>7.0.0.Beta1</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <!-- log start -->
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <!-- log end -->

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.5</version>
        </dependency>

        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <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>

3,配置Activiti連接數據庫的配置文件

這裏使用dbcp連接池連接mysql數據庫,在resources資源文件夾下新建文件activiti.cfg.xml文件。

前提是mysql數據庫新建一個庫名爲activiti

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
						http://www.springframework.org/schema/contex http://www.springframework.org/schema/context/spring-context.xsd
						http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
	<!--數據源配置dbcp-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql://localhost:3306/activiti" />
        <property name="username" value="root" />
        <property name="password" value="1234" />
        <property name="maxActive" value="3" />
        <property name="maxIdle" value="1" />
    </bean>
	<!--activiti單獨運行的ProcessEngine配置,使用單獨啓動方式-->
    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="dataSource" ref="dataSource"></property>
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>

    <!--<bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/itcast0711activiti"/>
        <property name="jdbcUsername" value="root"/>
        <property name="jdbcPassword" value="root"/>
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>-->
</beans>

4,運行代碼來創建mysql所需要的25張表

隨便哪個地方寫下面的測試類,然後運行,那麼數據庫中就已經創建了25張表。

 //測試activiti所需要的25張表的生成
    @Test
    public void testGenTable() {
        //1,創建ProcessEngineConfiguration對象
        ProcessEngineConfiguration configuration=
 	ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        //2,創建ProcessEngine對象
        ProcessEngine processEngine = configuration.buildProcessEngine();
        //3,打印processEngine對象
        System.out.println(processEngine);
    }

在這裏插入圖片描述

5,上述25張數據庫表的命名規則

Activiti表都以ACT_開頭。

  • ACT_RE_*,這個前綴的表包含了流程定義和流程靜態資源(圖片,規則等等)
  • ACT_RU_*,這些運行時的表,包含流程實例,任務,變量,異步任務,等運行中的數據,Activiti只在流程實例執行過程中保存這些數據,在流程結束後就會刪除這些記錄。
  • ACT_HI_*,這些表包含歷史數據,比如歷史流程實例,變量,任務等等。
  • ACT_GE_*,通用數據,用於不同場景下。

六,Activiti架構圖

img

從圖中可以看出,通過ProcessEngine對象可以得到下面的好多Service,而下面的Service的作用就是直接對數據庫中的25張表進行增刪改查操作,很簡便,不用再自己寫增刪改查了。

Activiti架構圖說明

  • ProcessEngineConfiguration類,主要作用是加載activiti.cfg.xml文件。

  • ProcessEngine類,作用是幫助我們可以快速得到各個service接口,並且可以生成activiti的工作環境,25張表。

  • Service接口,作用是可以快速實現數據庫25張表的操作。

    • ResposityService

    • RuntimeService

    • TaskService

    • HistoryService

1,使用idea的ActiveDesigner完成流程定義

新建流程holiday.bpmn,在resources資源文件夾下新建BPNMFile文件,並畫一個流程,這裏舉例請假流程。

在這裏插入圖片描述

1,點擊白色區域,看左邊的idname,每一個流程圖都有唯一的idname,可以自定義。

2,點擊任務格子,看左邊的Assignee,如下圖,是指定該任務分配給誰來執行的。都分配一下,這裏分配結果==》填寫請假單:zhangsan;部門經理審批:lisi;總經理審批:wangwu。

在這裏插入圖片描述

2,idea中製作Activiti流程定義的png文件

畫完圖後,但是並沒有生成png圖片,這個時候重命名剛纔創建的文件,把後綴改成xml,然後右鍵

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OCz5ae6n-1584088100296)(D:\工作\學習資料\我的筆記(圖片)\1583144076116.png)]

然後保存到與剛纔創建文件的一個文件夾內就可以了

下面是保存按鈕

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-w2ckHfX9-1584088100296)(D:\工作\學習資料\我的筆記(圖片)\1583144893670.png)]

如果流程圖是中文亂碼的情況,那麼在idea安裝目錄的下面兩個文件添加一句代碼。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-knqRfCLj-1584088100297)(D:\工作\學習資料\我的筆記(圖片)\1583144972938.png)]

-Dfile.encoding=UTF-8

然後重新啓動idea就可以了,重新走一遍上面的過程即可。

3,Activiti實現流程定義部署的步驟及實現

寫一個測試類進行測試流程部署。

 //流程定義的部署
    @Test
    public void testDeploy(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到repositoryService實例
        RepositoryService repositoryService = processEngine.getRepositoryService();

        //3,進行部署
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource("holiday.bpmn") //添加bpmn資源
                .addClasspathResource("holiday.png")
                .name("請假申請流程")
                .deploy();

        //4,輸出部署的一些信息
        System.out.println(deployment.getId());
        System.out.println(deployment.getName());
    }

打印出

1
請假申請流程

看數據庫表act_re_deployment表新增了一條數據。(該表存放部署信息)

在這裏插入圖片描述

act_re_procdef表也新增了一條數據。(該表存放流程定義的一些信息)

在這裏插入圖片描述

act_ge_bytearray新增了兩條,這是存儲的文件。(該表存放流程定義的bpmn文件及png文件)

在這裏插入圖片描述

總的來說,就是部署一個新的流程時,數據庫發生變化的是上面的三張表。

4,Activiti實現流程實例啓動的步驟及實現

提及一個概念:一個流程定義可以對應多個流程實例。

寫一個測試類,啓動流程實例。

 //啓動流程實例:前提是已經完成了流程定義的部署工作
    @Test
    public void testBeginFlow(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到RuntimeService實例
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //3,創建流程實例,流程定義的key需要知道流程唯一id:holiday
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday");
        //4,輸出實例相關信息
        System.out.println("實例部署id:"+processInstance.getDeploymentId());
        System.out.println("流程實例id:"+processInstance.getId());
        System.out.println("活動id:"+processInstance.getActivityId());
    }

打印:

實例部署id:null
流程實例id:2501
活動id:null

看數據表act_hi_procinst新增了一條數據。

在這裏插入圖片描述

啓動一個流程實例影響數據庫的表:

  • act_hi_actinst 已完成的活動
  • act_hi_identitylink 參與者信息
  • act_hi_procinst 流程實例
  • act_hi_taskinst 任務實例
  • act_ru_execution 執行表
  • act_ru_identitylink 參與者信息
  • act_ru_task 任務

5,Activiti實現指定用戶查詢任務列表 [查詢待辦]

流程啓動後,各個任務的負責人就可以查詢自己當前需要處理的任務,查詢出來的任務都是該用戶的待辦任務。

寫一個測試類,查詢指定用戶下面的待辦任務列表。

 //查詢當前用戶的任務列表
    @Test
    public void query(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到TaskService對象
        TaskService taskService = processEngine.getTaskService();
        //3,根據流程定義的key,負責人assignee來實現當前用戶的任務列表查詢
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey("holiday")
                .taskAssignee("zhangsan")
                .list();
        //4,任務列表的展示
        for(Task task:list){
            System.out.println("流程實例id:"+task.getProcessInstanceId());
            System.out.println("任務id:"+task.getId());
            System.out.println("任務負責人:"+task.getAssignee());
            System.out.println("任務名稱:"+task.getName());
        }
    }

打印:

流程實例id:2501
任務id:2505
任務負責人:zhangsan
任務名稱:填寫請假單

6,Activiti實現當前用戶任務處理

寫一個測試類,處理當前用戶的待辦任務。

    //處理任務
    @Test
    public void chuli(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到TaskService對象
        TaskService taskService = processEngine.getTaskService();
        //3,處理任務,結合當前用戶任務列表的查詢操作,得到任務ID:2505
        taskService.complete("2505");
    }

運行之後,該任務就完成了,然後就跳到下一個審批的節點了。

看數據庫act_hi_taskinst表的變化,填寫請假單有了結束時間,然後新增了一條部門經理審批,並且結束時間爲NULL,這就說明跳到下一個節點了。

在這裏插入圖片描述

處理任務操作背後影響的表:

  • act_hi_actinst 歷史數據
  • act_hi_identitylink 參與者信息
  • act_hi_taskinst
  • act_ru_execution
  • act_ru_identitylink
  • act_ru_task 該表從始至終只存一條數據,就是當前執行的節點信息(走到哪一步了),如果流程執行完了則會刪除這一條數據。

7,Activiti流程部署的zip包方式的補充

holiday.bpmnholiday.png壓縮成holiday.zip包進行部署,效果和上面的(標題3)一樣。

 //流程定義的部署
    @Test
    public void testDeploy(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到repositoryService實例
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //轉化出ZipInputStream流對象
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("holiday.zip");
        //將inputStream轉化爲ZipInputStream流
        ZipInputStream zipInputStream=new ZipInputStream(inputStream);

        //3,進行部署
        Deployment deployment = repositoryService.createDeployment()
                .addZipInputStream(zipInputStream)
                .name("請假申請流程")
                .deploy();

        //4,輸出部署的一些信息
        System.out.println(deployment.getId());
        System.out.println(deployment.getName());
    }

這種方法也可以的。

8,Activiti流程定義信息查詢

說明:什麼是流程定義?流程定義就是啓動了的流程圖,比方說上面啓動了一個請假申請流程,那麼該流程定義就是指這個請假申請流程。

寫一個測試類,查詢流程定義信息。

//流程定義查詢
    @Test
    public void defined(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到RepositoryService對象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //3,得到ProcessDefinitionQuery對象,可以認爲它就是一個查詢器
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        //4,設置條件,並查詢出當前的所有流程定義,查詢條件:流程定義的key=holiday

        List<ProcessDefinition> holiday = processDefinitionQuery.processDefinitionKey("holiday")
                .orderByProcessDefinitionVersion()  //設置排序方式,根據流程定義的版本號進行排序
                .desc()     //降序排列
                .list();

        //5,輸出流程定義信息
        for(ProcessDefinition processDefinition:holiday){
            System.out.println("流程定義ID:"+processDefinition.getId());
            System.out.println("流程定義名稱:"+processDefinition.getName());
            System.out.println("流程定義的Key:"+processDefinition.getKey());
            System.out.println("流程定義的版本號:"+processDefinition.getVersion());
            System.out.println("流程部署的ID:"+processDefinition.getDeploymentId());
        }
    }

打印出了流程定義的所有信息。

流程定義ID:holiday:1:4
流程定義名稱:請假流程
流程定義的Key:holiday
流程定義的版本號:1
流程部署的ID:1

9,Activiti流程定義信息刪除

刪除已經部署成功的流程定義。

 	@Test
    public void del(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到RepositoryService對象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //3,執行刪除流程定義,參數代表流程部署的id
        repositoryService.deleteDeployment("1");
    }

刪除流程定義影響的表和部署流程定義影響的表是一樣的,刪除是減去,部署是增加,都是同樣的三張表。

注意事項:

1,當我們正在執行的這一套流程沒有完全審批結束的時候,如果此時要刪除流程定義信息就會失敗。

2,如果要強制刪除,可以使用repositoryService.deleteDeployment("1",true);參數true代表級聯刪除,此時就會先刪除沒有完成的流程結點,最後就可以刪除流程定義信息,false值代表不級聯刪除,不寫默認爲false

七,Activiti實現資源文件保存的需求與方案分析

1,Activiti流程定義資源

通過流程定義對象獲取流程定義資源,獲取bpmnpng

真實應用場景:用戶想查看這個請假申請流程具體有哪些步驟要走?

技術方案:

  1. 第一種方式使用Activitiapi來實現;
  2. 其實就是原理層面,可以使用jdbcblob類型,clob類型數據的讀取和保存。
  3. IO流轉換,使用commons-io.jar包可以輕鬆解決IO操作。

需求:

  1. 數據庫裏有一個現成的流程定義,現在要把數據庫中存放該流程定義的資源文件讀取到硬盤的指定位置。
  2. act_ge_bytearray表中讀取兩個資源文件,並將兩個資源文件保存到路徑:D:\

2,Activiti實現資源文件保存的實現步驟

寫一個測試類實現資源文件保存到本地磁盤。

裏面輸入流和輸出流轉換需要用到了commons-io.jar包,這裏引入一個在idea中快速搜索並選擇版本號並導入依賴的方法。

Alt+Insert鍵,選擇Dependency,搜索依賴的名字,並選擇版本號,確定導入即可。

//保存資源文件
    @Test
    public void save() throws IOException {
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到RepositoryService對象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //3,得到ProcessDefinitionQuery對象,可以認爲它就是一個查詢器
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
        //4,設置查詢條件
        processDefinitionQuery.processDefinitionKey("holiday");//參數是流程定義的key
        //5,執行查詢操作,查詢出想要的流程定義
        ProcessDefinition processDefinition = processDefinitionQuery.singleResult();
        //6,通過流程定義信息,得到部署ID
        String deploymentId = processDefinition.getDeploymentId();

        //7,通過repositoryService的方法,實現讀取圖片信息及bpmn文件信息(輸入流)
        //getResourceAsStream()方法的參數說明:第一個參數是部署id,第二個參數代表資源名稱
        //processDefinition.getDiagramResourceName() 獲取png圖片資源的名稱
        //processDefinition.getResourceName() 獲取bpmn文件的名稱
        InputStream pngIs = repositoryService.getResourceAsStream(deploymentId, processDefinition.getDiagramResourceName());
        InputStream bpmnIs = repositoryService.getResourceAsStream(deploymentId, processDefinition.getResourceName());

        //8,構建出OutputStream流
        OutputStream pngOs=new FileOutputStream("D:\\工作\\"+processDefinition.getDiagramResourceName());
        OutputStream bpmnOs=new FileOutputStream("D:\\工作\\"+processDefinition.getResourceName());

        //9,輸入流、輸出流的轉換(commons-io.jar中的方法)
        IOUtils.copy(pngIs,pngOs);
        IOUtils.copy(bpmnIs,bpmnOs);

        //10,關閉流
        pngOs.close();
        bpmnOs.close();
        pngIs.close();
        bpmnIs.close();
    }

查看本地D:/工作路徑下,有了holiday.pngholiday.bpmn文件,已經下載到了本地。

3,Activiti歷史信息的查詢

即使流程定義已經刪除了,但是流程執行的歷史信息依然保存在activitiact_hi_*相關的表中。所以我們還是可以查詢流程執行的歷史信息,可以通過HistoryService來查看相關的歷史記錄。

/查詢歷史數據
    @Test
    public void select(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到HistoryService對象
        HistoryService historyService = processEngine.getHistoryService();
        //3,得到查詢器
        HistoricActivityInstanceQuery instanceQuery = historyService.createHistoricActivityInstanceQuery();
        //4,執行查詢
        instanceQuery.processInstanceId("2501"); //設置流程實例的id
        List<HistoricActivityInstance> list = instanceQuery.orderByHistoricActivityInstanceStartTime().asc().list();//排序
        //5,遍歷查詢結果
        for(HistoricActivityInstance instance:list){
            System.out.println(instance.getActivityId());
            System.out.println(instance.getActivityName());
            System.out.println(instance.getProcessDefinitionId());
            System.out.println(instance.getProcessInstanceId());
            System.out.println("===========================");
        }
    }

打印

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OpgI4LLC-1584088100299)(D:\工作\學習資料\我的筆記(圖片)\1583565935597.png)]

八,Activiti與業務系統整合開發的原理分析

Activiti:數據庫中的25張表只是負責Activiti的工作正常流程,和業務數據沒有關係。

業務系統:存放業務數據。

比如請假流程,提交的表單信息是存到業務數據庫中,但是要把Activiti和業務系統關聯起來,意思就是,提交一個請假審批,Activiti怎麼知道這個審批流程是誰提交的?因此關聯的方法是把業務主鍵(請假表的id字段)保存到Activiti裏面。

存放到哪裏?

act_ru_execution表中的bussiness_key字段就是業務主鍵(業務標識)。

在這裏插入圖片描述

1,Activiti實現與業務系統businessKey整合操作

在上面啓動流程實例時,參數是一個,也就是流程id。

 		//3,創建流程實例,流程定義的key需要知道流程唯一id:holiday
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday");

但是要把業務表(請假表)的業務標識(請假表的id)和Activiti整合起來,需要傳入第二個參數businessKey,第二個參數就是請假表的id

@Test
    public void bang(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到RuntimeService實例
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //3,創建流程實例,流程定義的key需要知道流程唯一id:holiday
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("holiday","1001");
        System.out.println(processInstance.getBusinessKey());
    }

打印:1001

觀察act_ru_execution表,已經和業務id綁定了。
在這裏插入圖片描述

2,Activiti全部流程的掛起與激活

需求:公司制度發生了變化,原本沒有批完的流程怎麼辦?

某些情況可能由於流程變更需要當前運行的流程暫停而不是直接刪除,流程暫停後將不會繼續執行。操作流程定義爲掛起狀態,該流程定義下邊所有的流程實例全部暫停,並不允許啓動新的流程實例。

	 //流程定義全部掛起
    @Test
    public void guaqi(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到RepositoryService對象
        RepositoryService repositoryService = processEngine.getRepositoryService();
        //3,查詢流程定義的對象
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
		 //可以根據流程定義的id查詢指定流程定義,不過id要到Activiti數據庫表中去看,			act_re_procdef表
         //  .processDefinitionId()   
             .processDefinitionKey("holiday") //也可以根據流程定義的key查詢
             .singleResult();
        //4,得到當前流程定義的實例是否都爲暫停狀態
        boolean suspended = processDefinition.isSuspended();
        //5,如果都爲暫停狀態,則全部激活
       if(suspended){
           repositoryService.activateProcessDefinitionById(processDefinition.getId(),true,null);
           System.out.println("流程定義:"+processDefinition.getId()+"被激活");
       }else{
           repositoryService.suspendProcessDefinitionById(processDefinition.getId(),true,null);
           System.out.println("流程定義:"+processDefinition.getId()+"被掛起");
       }
    }

運行測試類,打印

流程定義:holiday:1:4被掛起

再次運行測試類,打印

流程定義:holiday:1:4被激活

3,Activiti單個流程實例的掛起與激活

processInstanceId(“2501”),是通過流程實例id查詢到的流程實例對象

//單個流程實例的掛起
    @Test
    public void single(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到RuntimeService對象
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //3,查詢流程實例對象
        ProcessInstance holiday = runtimeService.createProcessInstanceQuery().processInstanceId("2501").singleResult();
        //4,查看當前流程定義的實例是否爲暫停狀態
        boolean suspended = holiday.isSuspended();
        //判斷
        if(suspended){
            runtimeService.activateProcessInstanceById(holiday.getId());
            System.out.println("流程實例:"+holiday.getId()+"被激活");
        }else{
            runtimeService.suspendProcessInstanceById(holiday.getId());
            System.out.println("流程實例:"+holiday.getId()+"被掛起");
        }
    }

打印

流程實例:2501被掛起

4,Activiti中個人任務分配的固定方式和UEL方式

上面所述,每個節點設置的Assignee對應的值爲zhangsan,代表某個人,那就是固定方式分配,但是被定死了,只有張三才能完成該節點,但是肯定是不行的,請假是所有員工都可以申請的。

引出UEL方式

UEL方式,也稱爲統一表達式。

寫法:${變量名}

在這裏插入圖片描述

什麼時候設置表達式?

在啓動流程實例的時候設置流程變量,如下

 //啓動流程實例,動態設置assignee
    @Test
    public void dongtai(){
        //1,創建processEngine對象
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
        //2,得到RuntimeService對象
        RuntimeService runtimeService = processEngine.getRuntimeService();
        //3,設置assignee的取值,用戶可以在界面上設置流程的執行人
        Map<String,Object> map=new HashMap<>();
        map.put("assignee0","zhangsan");
        map.put("assignee1","lisi");
        map.put("assignee2","wangwu");
        //4,啓動流程實例,同時還要設置流程定義的assignee的值
        ProcessInstance holiday = runtimeService.startProcessInstanceById("holiday", map);
        System.out.println(holiday.getName());
    }

前提:在界面上要分別設置assignee的值爲assignee0assignee1assignee2

結合項目使用的話,獲取到提交請假單的名字或者唯一標識,然後動態賦值,然後存到activiti數據庫中名字就是該設置的名字。

5,監聽器方式實現activiti流程assignee設置

在界面中添加監聽器。

在這裏插入圖片描述

定義任務監聽類,該類必須實現org.activiti.delegate.TaskListener接口。

public class MyTaskListener implements TaskListener(){
    @Override
    public void notify(Delegate delegate){
        //這裏指定任務負責人
        delegate.setAssignee("張三");
    }
}

九,Activiti的流程變量概述及作用

1,白話引入Activiti流程變量

比如說現在有一個貸款流程,發起流程的用戶如果貸款數目小於100000,那麼業務經理審批完之後就可以到會計節點,然後放貸款,但是如果貸款數目大於100000了,那麼就要到總經理節點審批,總經理審批完之後才能到會計節點。

那麼問題來了,Activiti怎麼知道大於100000就要到總經理審批節點呢?那麼就需要用到流程變量了。

2,Activiti流程變量的作用域

global變量:一個任務或一個流程實例。可以理解爲全局的,所有節點都可以使用該變量。

local變量:僅僅針對一個任務和一個執行實例範圍,範圍沒有流程實例大。可以理解爲局部的,只有一個節點可以使用。

global變量名不可以重複,如果名字重複,則會被覆蓋。

3,Activiti流程變量的使用方式

可以在連線上設置UEL表達式,決定流程走向。

比如:price>100000{price>100000}和{price<100000},price就是變量名稱。

點擊流程圖的連線,設置流程變量即可。

在這裏插入圖片描述

4,Activiti流程變量案例

需求:員工創建請假單,由部門經理審覈,部門經理審覈通過之後請假3天以下由人事經理直接審覈,3天以上先由總經理審覈,總經理審覈通過之後再由人事經理存檔。

定義BPMN文件:

在這裏插入圖片描述

設置請假單申請assignee:zhangsan,部門審批assignee:lisi,總經理審批assignee:wangwu,人事經理存檔assignee:zhaoliu。

畫好圖之後,點擊連線(人事經理前面的連線設置爲${holiday.num<3},總經理前面的連線設置爲${holiday.num>3}),這是UEL方式賦值,可以對象點出來的屬性這種,對象就是下面的實體類。

然後重複上面的動作:改bpmn文件後綴爲xml文件,然後生成png文件,然後再把xml恢復成bpmn

其實不生成png也沒啥問題,工作流一點兒影響都沒有。

//請假實體類
@Data
public class Holiday implements Serializable{
    private Integer id;
    private String holidayName;//申請人名字
    private Date beginDate;//開始時間
    private Date endDate;//結束時間
    private Float num;//請假天數
    private String reason;//請假事由
    private String type;//請假類型
}

注意:如果將實體類存儲到流程變量中,必須實現序列化接口seriallizable,否則在存儲這個實體類的時候就會報異常,爲了防止由於新增字段無法反序列化,需要生成serialVersionUID

(2)部署流程定義

//流程變量的測試
public class HolidayTest {
    //新的請假流程定義的部署
    @Test
    public void start(){
        //得到ProcessEngine
        ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
        //得到RepositoryService
        RepositoryService repositoryService = defaultProcessEngine.getRepositoryService();
        //部署
        Deployment deploy = repositoryService.createDeployment()
                .addClasspathResource("holiday4.bpmn")
                .addClasspathResource("holiday4.png")
                .name("請假流程-測試流程變量")
                .deploy();

        System.out.println(deploy.getId());
        System.out.println(deploy.getName());
    }
}

運行,這就部署好了該流程定義。

(3)啓動流程實例,同時設置流程變量

 //啓動流程實例,同時設置流程變量
    @Test
    public void begin(){
        //得到ProcessEngine
        ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
        //得到RuntimeService
        RuntimeService runtimeService = defaultProcessEngine.getRuntimeService();
        //流程定義的key:myProcess_1
        String key="myProcess_1";
        Map<String,Object> map=new HashMap<>();
        //設置請假天數
        Holiday holiday=new Holiday();
        holiday.setNum(1F);
        map.put("holiday",holiday);
        //啓動流程實例,並設置流程變量的值
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, map);
        System.out.println(processInstance.getName());
        System.out.println(processInstance.getProcessDefinitionId());
    }

運行,就啓動了一個流程實例。

(4)測試流程分支

依次調用此方法完成任務,zhangsan,lisi,wangwu/zhaoliu---------判斷流程變量的請假天數

    @Test
    public void redify(){
        //得到ProcessEngine
        ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
        //得到taskService
        TaskService taskService = defaultProcessEngine.getTaskService();
        //查詢當前用戶是否存在任務
        String key="myProcess_1";
        Task task = taskService.createTaskQuery().processDefinitionKey(key)
                .taskAssignee("zhangsan").singleResult();
        //如果有任務,結束
        if(task!=null){
            taskService.complete(task.getId());
            System.out.println("任務執行完畢");
        }
    }

張三執行完之後,看act_hi_actinst表,張三填寫申請已經結束,來到了李四部門經理審批。

在這裏插入圖片描述

然後再執行李四()把zhangsan改成lisi,運行上面的測試類。

運行完之後,李四的任務也執行完畢了,也就是部門審批完了,看act_hi_actinst表的變化。

在這裏插入圖片描述

發現來到了人事經理存檔的節點,是因爲上面設置的請假天數是1天,所以直接來到了人事,如果請假天數大於3天,那麼會先到總經理審批節點,然後纔到人事節點。

同理,把lisi改成zhaoliu,也就是執行人事經理存檔,運行,看act_hi_actinst表的變化。

在這裏插入圖片描述

流程執行完畢。

5,Activiti完成任務時設置流程變量的值

上面的流程變量的賦值是創建流程實例就有值了,而完成任務設置值是完成了該節點之後然後流程變量纔有的值。

在這裏插入圖片描述

6,通過流程實例ID設置流程變量的值

流程實例創建好之後可以得到該實例id,然後在啓動流程實例的時候通過流程實例id設置流程變量的值。

    //啓動流程實例,同時設置流程變量
    @Test
    public void begin(){
        //得到ProcessEngine
        ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
        //得到RuntimeService
        RuntimeService runtimeService = defaultProcessEngine.getRuntimeService();
        //流程定義的key:myProcess_1
        String key="myProcess_1";
        Map<String,Object> map=new HashMap<>();
        //設置請假天數
        Holiday holiday=new Holiday();
        holiday.setNum(1F);
        map.put("holiday",holiday);
        //啓動流程實例,並設置流程變量的值
        //第一個參數:流程實例的id
        //第二個參數:流程變量名
        //第三個參數:流程變量所對應的值
        runtimeService.setVariable("2501","holiday",holiday);
//        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(key, map);
//        System.out.println(processInstance.getName());
//        System.out.println(processInstance.getProcessDefinitionId());
    }

7,通過當前任務ID來設置流程變量的值

首先需要得到當前任務的id。

String taskId="1404";
TaskService taskService=processEngine.getTaskService();
//使用taskService來設置
taskService.setVariable(taskId,"holiday",holiday);

十,Activiti組任務

需求:前面講述的,每個節點只能有一個任務負責人,現在要一個節點可以有多個任務負責人。

設置任務候選人:

流程圖任務節點的配置中配置candiddate-users(候選人),多個候選人之間用逗號隔開。

在這裏插入圖片描述

1,Activiti組任務辦理流程

  • 第一步:查詢組任務,指定候選人,查詢該候選人當前的待辦任務。候選人不能辦理業務。

  • 第二步:拾取任務,該組任務的所有候選人都可以拾取,將候選人的組任務變成個人任務,原來候選人就變成了該任務的負責人。

    • 如果拾取後不想辦理該業務怎麼辦?

    • 需要將已經拾取的個人任務歸還到組裏邊,將個人任務變成了組任務。

  • 第三步:查詢個人任務,查詢方式同個人任務部分,根據assignee查詢用戶負責的個人任務。

  • 第四步:辦理個人任務。

2,Activiti查詢候選人的組任務

 //查詢組任務
    @Test
    public void find(){
        ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = defaultProcessEngine.getTaskService();
        String key="myProcess_1";//流程定義的key
        String candidate_users="zhangsan";//指定某一個候選人
        //查詢
        List<Task> list = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskCandidateUser(candidate_users)//設置候選用戶
                .list();
        //輸出
        for(Task task:list){
            System.out.println(task.getProcessInstanceId());
            System.out.println(task.getId());
            System.out.println(task.getName());
            System.out.println(task.getAssignee());
        }
    }

其中

System.out.println(task.getAssignee());

打印爲null,是因爲查到的任務沒有具體的負責人,需要拾取任務。說明張三隻是候選人,還不是任務負責人。

3,Activiti拾取組任務

候選人拾取組任務後該任務變成自己的個人任務。

 //查詢組任務
    @Test
    public void find(){
        ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = defaultProcessEngine.getTaskService();
        String key="myProcess_1";//流程定義的key
        String candidate_users="zhangsan";//指定某一個候選人
        //查詢
        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
                .taskCandidateUser(candidate_users)
                .singleResult();
        if(task!=null){
            //第一個參數:任務id,第二個參數:候選人
            taskService.claim(task.getId(),candidate_users);
            System.out.println("任務拾取完畢");
        }
    }

候選人張三拾取組任務以後,該任務就已經有任務負責人了(assignee),就是老張了。

4,Activiti組任務-用戶任務查詢&完成任務

上面張三已經拾取了組任務,變成了個人任務,現在張三查詢自己的個人任務並完成任務。

@Test
    public void find(){
        ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = defaultProcessEngine.getTaskService();
        String key="myProcess_1";//流程定義的key
        String candidate_users="zhangsan";
        //查詢
        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
               .taskAssignee(candidate_users)//這裏就可以指定具體的任務負責人了
                .singleResult();
        if(task!=null){
            taskService.complete(task.getId());
            System.out.println("任務執行完畢");
        }
    }

5,Activiti組任務-當前用戶歸還組任務

如果個人不想辦理該組任務,可以歸還組任務,歸還後該用戶不再是該任務的負責人。

 //查詢組任務
    @Test
    public void find(){
        ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = defaultProcessEngine.getTaskService();
        String key="myProcess_1";//流程定義的key
        String candidate_users="zhangsan";
        //查詢
        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
               .taskAssignee(candidate_users)
                .singleResult();
        if(task!=null){
            //如果設置爲null,歸還組任務,該任務沒有負責人
            taskService.setAssignee(task.getId(),null);
        }
    }

說明:建議歸還任務前校驗該用戶是否是該任務的負責人。

也可以通過setAssignee方法將任務委託給其他用戶負責,注意被委託的用戶可以不是候選人。

6,Activiti組任務-任務交接的實現

任務交接:任務負責人將任務交給其他候選人辦理該業務。

 //查詢組任務
    @Test
    public void find(){
        ProcessEngine defaultProcessEngine = ProcessEngines.getDefaultProcessEngine();
        TaskService taskService = defaultProcessEngine.getTaskService();
        String key="myProcess_1";//流程定義的key
        String candidate_users="zhangsan";
        //查詢
        Task task = taskService.createTaskQuery()
                .processDefinitionKey(key)
               .taskAssignee(candidate_users)
                .singleResult();
        if(task!=null){
            taskService.setAssignee(task.getId(),"lisi");
            System.out.println("交接任務成功");
        }
    }

十一,Activiti網關

1,排他網關

排他網關用來在流程中實現決策,當流程執行到這個網關,所有分支都會判斷條件是否爲true,如果爲true則執行該分支。

注意:排他網關智慧選擇一個爲true的分支執行,即使有兩個分支條件都爲true,排他網關也會只選擇一條分支去執行。

這是關鍵,因爲如果兩個分支條件都滿足,那同時走兩個分支是不現實的,要不然流程就全亂套了。

在這裏插入圖片描述

使用方法:在流程圖界面拖一個排他網關放在中間就行了。

2,並行網關

作用:並行網關把一個流程分成多條分支,併發執行,執行完之後再把這些分支聚合到一起成一個流程,然後繼續往下走。

如果有一個分支沒有執行完,那麼流程不會繼續走,並行網關會一直等所有分支都執行完畢,並聚合在一起之後才接着走流程。

並行網關並不會解析條件,即使順序流中定義了條件,也會被忽略。

在這裏插入圖片描述

3,包含網關

這裏忽略。

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