activiti學習筆記

activiti 工作流

學習地址

工作流涉及到的表

表名 中文含義
act_re_deployment 部署信息表
act_re_model 流程設計模型部署表
act_re_procdef 流程定義數據表
act_ru_excution 運行時流程執(zhí)行實例表
act_ru_identitylink 運行時流程人員表,主要存儲任務節(jié)點與參與者相關信息
act_tu_task 運行時任務節(jié)點表
act_ru_variable 運行時流程標量數據表
act_hi_actinst 歷史節(jié)點表
act_hi_attachment 歷史附件表
act_hi_comment 歷史意見表
act_hi_identitylink 歷史流程人員表
act_hi_detail 歷史詳情表,提供歷史變量的查詢
act_hi_procinst 歷史流程實例表
act_hi_taskinst 歷史任務實例表
act_hi_varinst 歷史變量表
act_id_group 用戶組信息表
act_id_info 用戶擴展信息表
act_id_membership 用戶與用戶組對應信息表
act_id_user 用戶信息表
act_ge_bytearray 二進制數據表
act_ge_property 屬性數據表,存儲整個流程引擎級別的數據,初始化表結構時,會默認插入三條記錄

常用Service

Service 說明
RepositoryService 資源管理類
RuntimeService 流程運行管理類
TaskService 任務管理類
HistoryService 歷史管理類
ManagerService 引擎管理類

RepositoryService

是activiti的資源管理類,提供了管理和控制流程發(fā)布包和流程定義的操作。

使用工作流建模工具設計的業(yè)務流程圖需要使用此service將流程定義文件的內容部署到計算機。
除了部署流程定義以外還可以:
查詢引擎中的發(fā)布包和流程定義。
暫?;蚣せ畎l(fā)布包,對應全部和特定流程定義。 暫停意味著它們不能再執(zhí)行任何操作了,激活是對應的反向操作。
獲得多種資源,像是包含在發(fā)布包里的文件, 或引擎自動生成的流程圖。
獲得流程定義的pojo版本, 可以用來通過java解析流程,而不必通過xml

RuntimeService

它是activiti的流程運行管理類??梢詮倪@個服務類中獲取很多關于流程執(zhí)行相關的信息

TaskService

是activiti的任務管理類??梢詮倪@個類中獲取任務的信息

HistoryService

是activiti的歷史管理類,可以查詢歷史信息,執(zhí)行流程時,引擎會保存很多數據(根據配置),比如流程實例啟動時間,任務的參與者, 完成任務的時間,每個流程實例的執(zhí)行路徑,等等。

這個服務主要通過查詢功能來獲得這些數據。

ManagementService

是activiti的引擎管理類,提供了對 Activiti 流程引擎的管理和維護功能,這些功能不在工作流驅動的應用程序中使用,主要用于 Activiti 系統(tǒng)的日常維護。

bpmn組件

組件 名稱
Connection 連接
Event 事件
Task 任務
Gateway 網關
Container 容器
Boundary event 邊界事件
Intermediate event 中間事件

畫流程圖

bpmn1.png
bpmn2.png

myholiday為流程的key

bpmn3.png
bpmn4.png

assignee為處理人

普通使用

pom文件

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <activiti-version>5.18.0</activiti-version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-engine</artifactId>
        <version>${activiti-version}</version>
    </dependency>
    <dependency>
        <groupId>org.activiti</groupId>
        <artifactId>activiti-spring</artifactId>
        <version>${activiti-version}</version>
    </dependency>
    <dependency>
        <groupId>org.codehaus.groovy</groupId>
        <artifactId>groovy-all</artifactId>
        <version>2.4.3</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.19</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.6</version>
    </dependency>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-jdk14</artifactId>
        <version>1.7.6</version>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.6</version>
    </dependency>
</dependencies>

activiti.cfg.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <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?useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="root"/>
        <property name="maxIdle" value="1"/>
        <property name="maxActive" value="3"/>
    </bean>
    <bean id="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="dataSource" ref="dataSource"/>
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
</beans>

databaseSchemaUpdate

作用
false(默認) 檢查數據庫表的版本和依賴庫的版本, 如果版本不匹配就拋出異常
true 構建流程引擎時,執(zhí)行檢查,如果需要就執(zhí)行更新。 如果表不存在,就創(chuàng)建
create-drop 構建流程引擎時創(chuàng)建數據庫表, 關閉流程引擎時刪除這些表
drop-create 先刪除表再創(chuàng)建表
create 構建流程引擎時創(chuàng)建數據庫表, 關閉流程引擎時不刪除這些表

測試代碼

public class TestActiviti {

     private ProcessEngine getProcessEngine() {
        ProcessEngineConfiguration processEngineConfiguration = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml");
        return processEngineConfiguration.buildProcessEngine();
    }
    
    @Test
    public void testProcessEngine() {
        ProcessEngine processEngine = getProcessEngine();
    }

    /**
     * 部署流程
     */
    @Test
    public void testDeployProcess() {
        ProcessEngine processEngine = getProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        Deployment deployment = repositoryService.createDeployment().addClasspathResource("myholiday.bpmn").addClasspathResource("myholiday" +
                ".png").name("請假流程").deploy();
        System.out.println("流程部署ID:" + deployment.getId());
        System.out.println("流程名稱:" + deployment.getName());
    }

    /**
     * 啟動一個流程實例
     */
    @Test
    public void testStartProcessInstance() {
        ProcessEngine processEngine = getProcessEngine();
        RuntimeService runtimeService = processEngine.getRuntimeService();
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myholiday");
        System.out.println("流程定義ID:" + processInstance.getProcessDefinitionId());
        System.out.println("流程實例ID:" + processInstance.getId());
        System.out.println("當前活動ID:" + processInstance.getActivityId());
    }

    /**
     * 查詢個人待執(zhí)行任務
     */
    @Test
    public void findPersonalTaskList() {
        String assignee = "zhangsan";
        ProcessEngine processEngine = getProcessEngine();
        TaskService taskService = processEngine.getTaskService();
        List<Task> taskList = taskService.createTaskQuery().processDefinitionKey("myholiday").taskAssignee(assignee).list();
        for (Task task : taskList) {
            System.out.println("流程實例ID:" + task.getProcessInstanceId());
            System.out.println("任務ID:" + task.getId());
            System.out.println("任務負責人:" + task.getAssignee());
            System.out.println("任務名稱:" + task.getName());
        }
    }

    /**
     * 完成任務
     */
    @Test
    public void completeTask() {
        String taskId = "12504";
        TaskService taskService = getProcessEngine().getTaskService();
        taskService.complete(taskId);
        System.out.println("任務完成ID = " + taskId);
    }

    /**
     * 流程定義查詢
     */
    @Test
    public void queryProcessDefinition() {
        String processDefinitionKey = "myholiday";
        RepositoryService repositoryService = getProcessEngine().getRepositoryService();
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionKey(processDefinitionKey).orderByProcessDefinitionVersion().desc().list();
        for (ProcessDefinition processDefinition : list) {
            System.out.println("------------------------");
            System.out.println("流程部署id:" + processDefinition.getDeploymentId());
            System.out.println("流程定義id:" + processDefinition.getId());
            System.out.println("流程定義名稱:" + processDefinition.getName());
            System.out.println("流程定義key:" + processDefinition.getKey());
            System.out.println("流程定義版本:" + processDefinition.getVersion());
        }
    }

    /**
     * 刪除流程定義
     */
    @Test
    public void deleteDeployment() {
        String deploymentId = "1";
        RepositoryService repositoryService = getProcessEngine().getRepositoryService();
        // 刪除流程定義,如果該流程定義已有流程實例啟動,則刪除時會拋出異常
        repositoryService.deleteDeployment(deploymentId);

        // 設置true表示級聯(lián)刪除流程定義,即使有已經啟用的流程實例也可以刪除,默認為false,級聯(lián)刪除會將流程及相關所有記錄全部刪除
        repositoryService.deleteDeployment(deploymentId, true);

    }

    /**
     * 獲取部署資源
     *
     * @throws IOException
     */
    @Test
    public void testGetProcessResource() throws IOException {
        ProcessEngine processEngine = getProcessEngine();
        RepositoryService repositoryService = processEngine.getRepositoryService();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId("1").singleResult();
        String resourceBpmn = processDefinition.getResourceName();
        String resourcePng = processDefinition.getDiagramResourceName();
        try (InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                resourceBpmn)) {
            FileUtils.copyInputStreamToFile(resourceAsStream, new File("H:/test.bpmn"));
        } catch (Exception e) {
            e.printStackTrace();
        }

        try (InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(),
                resourcePng)) {
            FileUtils.copyInputStreamToFile(resourceAsStream, new File("H:/test.png"));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testHistory(){
        HistoryService historyService = getProcessEngine().getHistoryService();
        List<HistoricActivityInstance> list = historyService.createHistoricActivityInstanceQuery().processInstanceId("12501").list();
        for (HistoricActivityInstance historicActivityInstance : list) {
            System.out.println(historicActivityInstance.getActivityId());
            System.out.println(historicActivityInstance.getActivityName());
            System.out.println(historicActivityInstance.getProcessDefinitionId());
            System.out.println(historicActivityInstance.getProcessInstanceId());
            System.out.println("==============================");
        }
    }
}

流程實例

參與者(可以是用戶也可以是程序)按照流程定義內容發(fā)起一個流程,這就是一個流程實例。是動態(tài)的。

bpmn7.png

啟動流程實例

@Test
public void testStartProcessInstance() {
    ProcessEngine processEngine = getProcessEngine();
    RuntimeService runtimeService = processEngine.getRuntimeService();
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myholiday");
    System.out.println("流程定義ID:" + processInstance.getProcessDefinitionId());
    System.out.println("流程實例ID:" + processInstance.getId());
    System.out.println("當前活動ID:" + processInstance.getActivityId());
}

businessKey 業(yè)務標識

bpmn5.png
@Test
public void testStartProcessInstance() {
    ProcessEngine processEngine = getProcessEngine();
    RuntimeService runtimeService = processEngine.getRuntimeService();
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myholiday","testBusinessKey");
    System.out.println("流程定義ID:" + processInstance.getProcessDefinitionId());
    System.out.println("流程實例ID:" + processInstance.getId());
    System.out.println("當前活動ID:" + processInstance.getActivityId());
}

啟動流程時指定businessKey,就會在act_ru_execution(流程實例的執(zhí)行表)中存儲businessKey。

Businesskey:業(yè)務標識,通常為業(yè)務表的主鍵,業(yè)務標識和流程實例一一對應。

業(yè)務標識來源于業(yè)務系統(tǒng)。存儲業(yè)務標識就是根據業(yè)務標識來關聯(lián)查詢業(yè)務系統(tǒng)的數據。
比如:請假流程啟動一個流程實例,就可以將請假單的id作為業(yè)務標識存儲到activiti中,將來查詢activiti的流程實例信息就可以獲取請假單的id從而關聯(lián)查詢業(yè)務系統(tǒng)數據庫得到請假單信息

所操作庫表

SELECT * FROM act_ru_execution #流程實例執(zhí)行表,記錄當前流程實例的執(zhí)行情況

流程實例執(zhí)行,如果當前只有一個分支時,一個流程實例只有一條記錄且執(zhí)行表的主鍵id和流程實例id相同,如果當前有多個分支正在運行則該執(zhí)行表中有多條記錄,存在執(zhí)行表的主鍵和流程實例id不相同的記錄。

不論當前有幾個分支總會有一條記錄的執(zhí)行表的主鍵和流程實例id相同
一個流程實例運行完成,此表中與流程實例相關的記錄刪除。

SELECT * FROM act_ru_task #任務執(zhí)行表,記錄當前執(zhí)行的任務

啟動流程實例,流程當前執(zhí)行到第一個任務結點,此表會插入一條記錄表示當前任務的執(zhí)行情況,如果任務完成則記錄刪除

SELECT * FROM act_ru_identitylink #任務參與者,記錄當前參與任務的用戶或組

SELECT * FROM act_hi_procinst #流程實例歷史表

流程實例啟動,會在此表插入一條記錄,流程實例運行完成記錄也不會刪除

SELECT * FROM act_hi_taskinst #任務歷史表,記錄所有任務

開始一個任務,不僅在act_ru_task表插入記錄,也會在歷史任務表插入一條記錄,任務歷史表的主鍵就是任務id,任務完成此表記錄不刪除。

SELECT * FROM act_hi_actinst #活動歷史表,記錄所有活動

活動包括任務,所以此表中不僅記錄了任務,還記錄了流程執(zhí)行過程的其它活動,比如開始事件、結束事件

關聯(lián)businessKey

在activiti實際應用時,查詢流程實例列表時可能要顯示出業(yè)務系統(tǒng)的一些相關信息,

比如:查詢當前運行的請假流程列表需要將請假單名稱、請假天數等信息顯示出來,請假天數等信息在業(yè)務系統(tǒng)中存在,而并沒有在activiti數據庫中存在,所以是無法通過activiti的api查詢到請假天數等信息。

在查詢流程實例時,通過businessKey(業(yè)務標識 )關聯(lián)查詢業(yè)務系統(tǒng)的請假單表,查詢出請假天數等信息。
通過下面的代碼就可以獲取activiti中所對應實例保存的業(yè)務Key。

而這個業(yè)務Key一般都會保存相關聯(lián)的業(yè)務操作表的主鍵,再通過主鍵ID去查詢業(yè)務信息,比如通過請假單的ID,去查詢更多的請假信息(請假人,請假時間,請假天數,請假事由等)

String businessKey = processInstance.getBusinessKey();

流程實例的掛起與激活

某些情況可能由于流程變更需要將當前運行的流程暫停而不是直接刪除,流程暫停后將不會繼續(xù)執(zhí)行

全部流程實例掛起

操作流程定義為掛起狀態(tài),該流程定義下邊所有的流程實例全部暫停

流程定義為掛起狀態(tài)該流程定義將不允許啟動新的流程實例,同時該流程定義下所有的流程實例將全部掛起暫停執(zhí)行

@Test
public void suspendOrActivateProcessDefinition() {
    // 流程定義id
    String processDefinitionId = "myholiday:1:4";
    RepositoryService repositoryService = getProcessEngine().getRepositoryService();
    ProcessDefinition processDefinition =
        repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult();
    System.out.println(processDefinition.getDeploymentId());
    boolean suspend = processDefinition.isSuspended();
    if (suspend) {
        //如果暫停則激活,這里將流程定義下的所有流程實例全部激活
        repositoryService.activateProcessDefinitionById(processDefinitionId, true, null);
        System.out.println("流程定義:" + processDefinitionId + "激活");
    } else {
        //如果激活則掛起,這里將流程定義下的所有流程實例全部掛起
        repositoryService.suspendProcessDefinitionById(processDefinitionId, true, null);
        System.out.println("流程定義:" + processDefinitionId + "掛起");
    }
}

單個流程實例掛起

操作流程實例對象,針對單個流程執(zhí)行掛起操作,某個流程實例掛起則此流程不再繼續(xù)執(zhí)行,完成該流程實例的當前任務將報異常

@Test
public void suspendOrActiveProcessInstance() {
    // 流程實例id
    String processInstanceId = "2501";
    // 獲取RunTimeService
    RuntimeService runtimeService = getProcessEngine().getRuntimeService();
    //根據流程實例id查詢流程實例
    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
    boolean suspend = processInstance.isSuspended();
    if (suspend) { //如果暫停則激活
        runtimeService.activateProcessInstanceById(processInstanceId);
        System.out.println("流程實例:" + processInstanceId + "激活");
    } else {
        //如果激活則掛起
        runtimeService.suspendProcessInstanceById(processInstanceId);
        System.out.println("流程實例:" + processInstanceId + "掛起");
    }
}

個人任務

分配任務負責人

固定分配

bpmn8.png

在properties視圖中,填寫Assignee項為任務負責人。

注意事項

由于固定分配方式,任務只管一步一步執(zhí)行任務,執(zhí)行到每一個任務將按照bpmn的配置去分配任務負責人。

表達式分配

UEL 表達式

activiti支持UEL-value和UEL-method

  • UEL-value
bpmn9.png

assignee這個變量是activiti的一個流程變量

bpmn10.png

user也是activiti的一個流程變量,user.assignee表示通過調用user的getter方法獲取值

  • UEL-method
bpmn11.png

holidayBean是Spring容器中的一的bean,表示調用該bean的getUsername()方法

  • UEL-method與UEL-value結合

    ${ldapService.findManagerForEmployee(emp)}
    ldapService是spring容器的一個bean,findManagerForEmployee是該bean的一個方法,emp是activiti流程變量,emp作為參數傳到ldapService.findManagerForEmployee方法中

  • 其他

    表達式支持解析基礎類型、bean、list、array和map,也可作為條件判斷。
    如下:
    ${order.price > 100 && order.price < 250}

使用流程變量分配任務

定義任務分配流程變量

bpmn9.png

設置流程變量

//啟動流程實例時設計流程變量 
//定義流程變量 
Map<String, Object> variables = new HashMap<String, Object>(); 
//設置流程變量assignee 
variables.put("assignee", "張三"); 
ProcessInstance processInstance = runtimeService .startProcessInstanceByKey(processDefinitionKey, variables);
注意事項

由于使用了表達式分配,必須保證在任務執(zhí)行過程表達式執(zhí)行成功,比如:
某個任務使用了表達式${order.price > 100 && order.price < 250},當執(zhí)行該任務時必須保證order在流程變量中存在,否則activiti異常

監(jiān)聽器分配

任務監(jiān)聽器是發(fā)生對應的任務相關事件時,執(zhí)行自定義Java邏輯或表達式

任務監(jiān)聽事件包括:

bpmn12.png

create:任務創(chuàng)建后觸發(fā)

assignment:任務分配后觸發(fā)

complete:任務完成后觸發(fā)

all:所有事件發(fā)生都觸發(fā)

java邏輯 或表達式:
表達式參考上邊的介紹的UEL表達式

定義任務監(jiān)聽類,且類必須實現(xiàn)org.activiti.engine.delegate.TaskListener接口

注意事項

使用監(jiān)聽器分配方式,按照監(jiān)聽事件去執(zhí)行監(jiān)聽類的notify方法,方法如果不能正常執(zhí)行也會影響任務的執(zhí)行。

查詢任務

查詢任務負責人的待辦任務:

// 查詢當前個人待執(zhí)行的任務
@Test
public void findPersonalTaskList1() {
    // 流程定義key
    String processDefinitionKey = "myholiday"; 
    // 任務負責人
    String assignee = "lisi";
    // 創(chuàng)建TaskService
    TaskService taskService = getProcessEngine().getTaskService();
    List<Task> list = taskService.createTaskQuery() .processDefinitionKey(processDefinitionKey).includeProcessVariables().taskAssignee(assignee).list();
    for (Task task : list) {
        System.out.println("----------------------------");
        System.out.println("流程實例id:" + task.getProcessInstanceId());
        System.out.println("任務id:" + task.getId());
        System.out.println("任務負責人:" + task.getAssignee());
        System.out.println("任務名稱:" + task.getName());
    }
}

關聯(lián)businessKey

在查詢待辦任務時,通過businessKey(業(yè)務標識 )關聯(lián)查詢業(yè)務系統(tǒng)的請假單表,查詢出請假天數等信息

@Test
public void testGetBusinessKey(){
    // 1.獲取流程引擎
    ProcessEngine processEngine = getProcessEngine();
    // 2.創(chuàng)建runtimeService對象和TaskService對象
    RuntimeService runtimeService = processEngine.getRuntimeService();
    TaskService taskService = processEngine.getTaskService();
    // 3.通過taskService查詢到個人任務 一般用list查詢
    Task task = taskService.createTaskQuery().processDefinitionKey("myholiday").taskAssignee("lisi").singleResult();
    // 4.通過task對象獲取流程實例id
    String processInstanceId = task.getProcessInstanceId();
    // 5.通過流程實例id獲取流程實例對象
    ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
    // 6.獲取businessKey
    String businessKey = processInstance.getBusinessKey();
    // 7.根據businessKey可以獲取到請假單信息
    System.out.println(businessKey);
}

辦理任務

//完成任務
@Test
public void completeTask() {
    String taskId = "12504";
    TaskService taskService = getProcessEngine().getTaskService();
    taskService.complete(taskId);
    System.out.println("任務完成ID = " + taskId);
}

實際應用中,應校驗任務的負責人是否具有該任務的辦理權限

@Test
public void comleteTask(){
    String taskId = "12504";
    String assignee = "zhaoliu";
    TaskService taskService = getProcessEngine().getTaskService();
    Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(assignee).singleResult();
    if(task!=null){
        taskService.complete(taskId);
        System.out.println("任務完成ID = " + taskId);
    }
}

流程變量

流程變量在activiti中是一個非常重要的角色,流程運轉有時需要靠流程變量,業(yè)務系統(tǒng)和activiti結合時少不了流程變量,流程變量就是activiti在管理工作流時根據管理需要而設置的變量。
比如在請假流程流轉時如果請假天數大于3天則由總經理審核,否則由人事直接審核,請假天數就可以設置為流程變量,在流程流轉時使用。

注意:雖然流程變量中可以存儲業(yè)務數據可以通過activiti的api查詢流程變量從而實現(xiàn) 查詢業(yè)務數據,但是不建議這樣使用,因為業(yè)務數據查詢由業(yè)務系統(tǒng)負責,activiti設置流程變量是為了流程執(zhí)行需要而創(chuàng)建。

流程變量類型

  • string
  • integer
  • short
  • double
  • boolean
  • long
  • date
  • binary
  • serializable

如果將pojo存儲到流程變量中,必須實現(xiàn)序列化接口serializable,為了防止由于新增字段無法反序列化,需要生成serialVersionUID。

流程變量作用域

流程變量的作用域默認是一個流程實例(processInstance),也可以是一個任務(task)或一個執(zhí)行實例(execution)

這三個作用域流程實例的范圍最大,可以稱為global變量,任務和執(zhí)行實例僅僅是針對一個任務和一個執(zhí)行實例范圍,范圍沒有流程實例大,稱為local變量

  • global變量中變量名不允許重復,設置相同名稱的變量,后設置的值會覆蓋前設置的變量值。
  • local變量由于在不同的任務或不同的執(zhí)行實例中,作用域互不影響,變量名可以相同沒有影響。
  • local變量名也可以和global變量名相同,沒有影響。

流程變量的使用方法

第一步:設置流程變量

第二部:通過UEL表達式使用流程變量

? 1)可以在assignee處設置UEL表達式,表達式的值為任務的負責人

? 比如:${assignee},assignee就是一個流程變量名稱

? Activiti獲取UEL表達式的值 ,即流程變量assignee的值 ,將assignee的值作為任務的負責人進行任務分配

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

? 比如:{price>=10000}和{price<10000}: price就是一個流程變量名稱,uel表達式結果類型為布爾類型

? 如果UEL表達式是true,要決定 流程執(zhí)行走向。

使用global變量控制流程走向

bpmn13.png

請假天數小于3天,直接進入人事經理存檔,大于等于3天,需要總經理審批

設置global流程變量

在部門經理審核前設置流程變量,變量值為請假單信息(包括請假天數),部門經理審核后可以根據流程變量的值決定流程走向

啟動流程時設置

在啟動流程時設置流程變量,變量的作用域是整個流程實例。

通過map<key,value>設置流程變量,map中可以設置多個變量,這個key就是流程變量的名字。

@Test
public void testStartProcessInstanceSetVariable() {
    ProcessEngine processEngine = getProcessEngine();
    RuntimeService runtimeService = processEngine.getRuntimeService();
    Map<String, Object> variables = new HashMap();
    variables.put("num", 3);
    ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myholiday", "testBusinessKey",variables);
    System.out.println("流程定義ID:" + processInstance.getProcessDefinitionId());
    System.out.println("流程實例ID:" + processInstance.getId());
    System.out.println("當前活動ID:" + processInstance.getActivityId());
}

說明:

startProcessInstanceByKey(processDefinitionKey, variables)流程變量作用域是一個流程實例,流程變量使用Map存儲,同一個流程實例設置變量map中key相同,后者覆蓋前者。

任務辦理時設置

在完成任務時設置流程變量,該流程變量只有在該任務完成后其它結點才可使用該變量,它的作用域是整個流程實例,如果設置的流程變量的key在流程實例中已存在相同的名字則后設置的變量替換前邊設置的變量

@Test
public void completeTaskSetVariable() {
    String taskId = "12504";
    TaskService taskService = getProcessEngine().getTaskService();
    Map<String, Object> variables = new HashMap<>();
    variables.put("num",3);
    taskService.complete(taskId,variables);
    System.out.println("任務完成ID = " + taskId);
}

說明:
通過當前任務設置流程變量,需要指定當前任務id,如果當前執(zhí)行的任務id不存在則拋出異常。
任務辦理時也是通過map<key,value>設置流程變量,一次可以設置多個變量。

通過當前流程實例設置

通過流程實例id設置全局變量,該流程實例必須未執(zhí)行完成。

@Test
public void setGlobalVariableByExecutionId(){
    //當前流程實例執(zhí)行 id,通常設置為當前執(zhí)行的流程實例
    String executionId="2601";
    RuntimeService runtimeService = getProcessEngine().getRuntimeService();
    // 可以設置為對象,根據UEL表達式來設置
    Holiday holiday = new Holiday();
    //通過流程實例 id設置流程變量
    runtimeService.setVariable(executionId, "holiday", holiday);
    //一次設置多個值
    // runtimeService.setVariables(executionId, variables)
}

注意:
executionId必須當前未結束 流程實例的執(zhí)行id,通常此id設置流程實例 的id。
也可以通過runtimeService.getVariable()獲取流程變量

通過當前任務設置

@Test
public void setGlobalVariableByTaskId(){
    //當前待辦任務id
    String taskId="1404";
    TaskService taskService = getProcessEngine().getTaskService();
    taskService.setVariable(taskId,"num",3);
}

注意:
任務id必須是當前待辦任務id,act_ru_task中存在。

如果該任務已結束,會拋出異常:org.activiti.engine.ActivitiObjectNotFoundException

也可以通過taskService.getVariable()獲取流程變量

注意事項

  1. 如果UEL表達式中流程變量名不存在則報錯。
  2. 如果UEL表達式中流程變量值為空NULL,流程不按UEL表達式去執(zhí)行,而流程結束 。
  3. 如果UEL表達式都不符合條件,流程結束
  4. 如果連線不設置條件,會走flow序號小的那條線

操作數據庫

設置流程變量會在當前執(zhí)行流程變量表插入記錄,同時也會在歷史流程變量表也插入記錄。
SELECT * FROM act_ru_variable #當前流程變量表
記錄當前運行流程實例可使用的流程變量,包括 global和local變量
Id_:主鍵
Type_:變量類型
Name_:變量名稱
Execution_id_:所屬流程實例執(zhí)行id,global和local變量都存儲
Proc_inst_id_:所屬流程實例id,global和local變量都存儲
Task_id_:所屬任務id,local變量存儲
Bytearray_:serializable類型變量存儲對應act_ge_bytearray表的id
Double_:double類型變量值
Long_:long類型變量值
Text_:text類型變量值
SELECT * FROM act_hi_varinst #歷史流程變量表
記錄所有已創(chuàng)建的流程變量,包括 global和local變量
字段意義參考當前流程變量表

設置local流程變量

任務辦理時設置

任務辦理時設置local流程變量,當前運行的流程實例只能在該任務結束前使用,任務結束該變量無法在當前流程實例使用,可以通過查詢歷史任務查詢。

通過taskService.setVariablesLocal()方法進行設置

說明:
設置作用域為任務的local變量,每個任務可以設置同名的變量,互不影響。

local變量在任務結束后無法在當前流程實例執(zhí)行中使用,如果后續(xù)的流程執(zhí)行需要用到此變量則會報錯

組任務

Candidate-users 任務候選人

在流程定義中在任務結點的assignee固定設置任務負責人,在流程定義時將參與者固定設置在.bpmn文件中,如果臨時任務負責人變更則需要修改流程定義,系統(tǒng)可擴展性差。
針對這種情況可以給任務設置多個候選人,可以從候選人中選擇參與者來完成任務

在流程圖中任務節(jié)點的配置中設置candidate-users(候選人),多個候選人之間用逗號分開


bpmn14.png

辦理組任務

流程

  1. 查詢組任務

    指定候選人,查詢該候選人當前的代辦任務

    候選人不能辦理任務

  2. 拾?。╟laim)任務

    改組任務的所有候選人都能拾取。

    將候選人的組任務,變成個人任務。原來候選人就變成了該任務的負責人

    如果失去后不想辦理該組任務,需要將拾取的個人任務歸還到組里,將個人任務變成組任務

  3. 查詢個人任務

    查詢方式同個人任務部分,根據assignee查詢用戶負責的個人任務

  4. 辦理個人任務

用戶查詢組任務

@Test
public void findGroupTaskList() {
    // 流程定義key
    String processDefinitionKey = "myholiday";
    // 任務候選人
    String candidateUser = "zhangsan";
    // 創(chuàng)建TaskService
    TaskService taskService = getProcessEngine().getTaskService();
    //查詢組任務
    List<Task> list = taskService.createTaskQuery().processDefinitionKey(processDefinitionKey).taskCandidateUser(candidateUser).list();
    for (Task task : list) {
        System.out.println("----------------------------");
        System.out.println("流程實例id:" + task.getProcessInstanceId());
        System.out.println("任務id:" + task.getId());
        System.out.println("任務負責人:" + task.getAssignee());
        System.out.println("任務名稱:" + task.getName());
    }
}

用戶拾取組任務

@Test
public void claimTask() {
    TaskService taskService = getProcessEngine().getTaskService();
    //要拾取的任務id 
    String taskId = "6302";
    //任務候選人id 
    String userId = "lisi";
    //拾取任務 
    // 即使該用戶不是候選人也能拾取(建議拾取時校驗是否有資格) 
    // 校驗該用戶有沒有拾取任務的資格 
    //根據候選人查詢 
    Task task = taskService.createTaskQuery().taskId(taskId).taskCandidateUser(userId).singleResult();
    if (task != null) {
        taskService.claim(taskId, userId);
        System.out.println("任務拾取成功");
    }
}

說明:

即使該用戶不是候選人也能拾取,建議拾取時校驗是否有資格
組任務拾取后,該任務已有負責人,通過候選人將查詢不到該任務

用戶查詢個人代辦任務

同個人任務

@Test
public void findPersonalTaskList() {
    // 流程定義key 
    String processDefinitionKey = "holiday4";
    // 任務負責人 
    String assignee = "zhangsan";
    // 創(chuàng)建TaskService 
    TaskService taskService = getProcessEngine().getTaskService();
    List<Task> list = taskService.createTaskQuery().processDefinitionKey(processDefinitionKey).taskAssignee(assignee).list();
    for (Task task : list) {
        System.out.println("----------------------------");
        System.out.println("流程實例id:" + task.getProcessInstanceId());
        System.out.println("任務id:" + task.getId());
        System.out.println("任務負責人:" + task.getAssignee());
        System.out.println("任務名稱:" + task.getName());
    }
}

用戶辦理個人任務

同個人任務

@Test
public void comleteTask(){
    String taskId = "12504";
    String assignee = "zhaoliu";
    TaskService taskService = getProcessEngine().getTaskService();
    Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(assignee).singleResult();
    if(task!=null){
        taskService.complete(taskId);
        System.out.println("任務完成ID = " + taskId);
    }
}

歸還組任務

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

// 歸還組任務,由個人任務變?yōu)榻M任務,還可以進行任務交接
@Test
public void setAssigneeToGroupTask() {
    // 查詢任務使用TaskService
    TaskService taskService = getProcessEngine().getTaskService();
    // 當前待辦任務
    String taskId = "6004";
    // 任務負責人
    String userId = "zhangsan";
    // 校驗userId是否是taskId的負責人,如果是負責人才可以歸還組任務
    Task task = taskService.createTaskQuery().taskId(taskId)
            .taskAssignee(userId).singleResult();
    if (task != null) {
        // 如果設置為null,歸還組任務,該 任務沒有負責人
        taskService.setAssignee(taskId, null);
    }
}

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

也可以通過setAssignee方法將任務委托給其它用戶負責

注意被委托的用戶可以不是候選人(建議不要這樣使用)

任務交接

任務交接,任務負責人將任務交給其它候選人辦理該任務

@Test
public void setAssigneeToCandidateUser() {
    TaskService taskService = getProcessEngine().getTaskService();
    // 當前待辦任務
    String taskId = "6004";
    // 任務負責人
    String userId = "zhangsan";
    // 校驗userId是否是taskId的負責人,如果是負責人才可以歸還組任務
    Task task = taskService.createTaskQuery().taskId(taskId).taskAssignee(userId).singleResult();
    if (task != null) {
        // 將此任務交給其它候選人辦理該 任務
        String candidateuser = "zhangsan";
        // 根據候選人和組任務id查詢,如果有記錄說明該 候選人有資格拾取該 任務
        Task task2 = taskService.createTaskQuery().taskId(taskId).taskCandidateUser(candidateuser).singleResult();
        if (task2 != null) {
            // 才可以交接
            taskService.setAssignee(taskId, candidateuser);
        }
    }
}

庫表操作

SELECT * FROM act_ru_task #任務執(zhí)行表,記錄當前執(zhí)行的任務,由于該任務當前是組任務,所有assignee為空,當拾取任務后該字段就是拾取用戶的id

SELECT * FROM act_ru_identitylink #任務參與者,記錄當前參考任務用戶或組,當前任務如果設置了候選人,會向該表插入候選人記錄,有幾個候選就插入幾個

與act_ru_identitylink對應的還有一張歷史表act_hi_identitylink,向act_ru_identitylink插入記錄的同時也會向歷史表插入記錄。

網關

排他網關(ExclusiveGateway)

排他網關(也叫異或(XOR)網關,或叫基于數據的排他網關),用來在流程中實現(xiàn)決策。

當流程執(zhí)行到這個網關,所有分支都會判斷條件是否為true,如果為true則執(zhí)行該分支。

排他網關只會選擇一個為true的分支執(zhí)行。(即使有兩個分支條件都為true,排他網關也會只選擇一條分支去執(zhí)行)

bpmn15.png

當圖中連線上的condition分支條件都不滿足時,流程會結束(異常結束)


bpmn16.png

當使用排他網關后,如果網關出去的所以分支條件都不滿足,則會拋出異常。

經過排他網關必須要有一條且只有一條分支走。

并行網關(ParallelGateway)

并行網關允許將流程分成多條分支,也可以把多條分支匯聚到一起,并行網關的功能是基于進入和外出順序流的

  • fork分支:
    并行后的所有外出順序流,為每個順序流都創(chuàng)建一個并發(fā)分支。

  • join匯聚:
    所有到達并行網關,在此等待的進入分支, 直到所有進入順序流的分支都到達以后, 流程就會通過匯聚網關

如果同一個并行網關有多個進入和多個外出順序流, 它就同時具有分支和匯聚功能。 這時,網關會先匯聚所有進入的順序流,然后再切分成多個并行分支

bpmn17.png

財務結算和行政考勤是兩個execution分支,在act_ru_execution表有兩條記錄分別是財務結算和行政考勤,act_ru_execution還有一條記錄表示該流程實例。
待財務結算和考勤任務全部完成,在匯聚點匯聚,通過parallelGateway并行網關。
并行網關在業(yè)務應用中常用于會簽任務,會簽任務即多個參與者共同辦理的任務。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容