Activiti快速入門


1.什么是Activiti

在解釋activiti之前我們看一下什么是工作流。
工作流(Workflow),就是“業務過程的部分或整體在計算機應用環境下的自動化”,它主要解決的是“使在多個參與者之間按照某種預定義的規則傳遞文檔、信息或任務的過程自動進行,從而實現某個預期的業務目標,或者促使此目標的實現”。
我的理解是,工作流將一套大的業務邏輯分解成業務邏輯段, 並統一控制這些業務邏輯段的執行條件,執行順序以及相互通信。 實現業務邏輯的分解和解耦。
Activiti是一個開源的工作流引擎,它實現了BPMN 2.0規范,可以發布設計好的流程定義,並通過api進行流程調度。
BPMN即業務流程建模與標注(Business Process Model and Notation,BPMN) ,描述流程的基本符號,包括這些圖元如何組合成一個業務流程圖(Business Process Diagram)。

BPMN的流程圖長這樣子

 

activiti5.13使用了23張表支持整個工作流框架,底層使用mybatis操作數據庫。這些數據庫表為


1)ACT_RE_*: 'RE'表示repository。 這個前綴的表包含了流程定義相關的靜態資源(圖片,規則等)。
2)ACT_RU_*: 'RU'表示runtime。 運行時表,包含流程實例,任務,變量,異步任務等運行中的數據。流程結束時這些記錄會被刪除。
3)ACT_ID_*: 'ID'表示identity。 這些表包含用戶和組的信息。
4)ACT_HI_*: 'HI'表示history。 這些表包含歷史數據,比如歷史流程實例,變量,任務等。
5)ACT_GE_*: 通用數據,bytearray表保存文件等字節流對象。

工作流進行的基本過程如下:
定義流程(框架外) -> 部署流程定義 -> 啟動流程實例, 框架移動到任務1 -> 拾取組任務 -> 辦理個人任務, 框架移動到任務2 -> 拾取組任務 -> 辦理個人任務...

組任務是多個用戶都可以完成的任務。沒有組任務直接辦理個人任務; 有組任務需先通過拾取將組任務變成個人任務, 然后再辦理。

個人任務/組任務在表中的區別

個人任務: 表act_ru_task的ASSIGNEE段即指定的辦理人

組任務: 表act_ru_task的ASSIGNEE段為null, 相關信息在表act_ru_identitylink中, 組任務1見userid段;  組任務2見groupid段, 當然還需查詢act_id_xxx表才能精確到人.

 

2.Activiti的使用

2.1 創建processEngine

processEngine控制着工作流整個流程

public class processEngine {
    @Test
    public void createProcessEngine1() {
        String resource = "activiti-context.xml";    // 配置文件
        String beanName = "processEngineConfiguration";  // 配置文件中bean name
        // 從配置文件創建配置對象
        ProcessEngineConfiguration config = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource(resource, beanName);
        // 根據配置創建引擎對象
        ProcessEngine processEngine = config.buildProcessEngine();
    }

    /**
     *  一條語句創建processEngine, 要求:
     * 1、配置文件必須在classpath根目錄下
     * 2、配置文件名必須為activiti-context.xml或activiti.cfg.xml
     * 3、工廠對象的id必須為processEngine
     */
    @Test
    public void createProcessEngine2() {
        ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
    }
}
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    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
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
  http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    <!-- 配置 -->
    <bean id="processEngineConfiguration"
         class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="jdbcDriver"  value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl"  value="jdbc:mysql:///test_activiti"/>
        <property name="jdbcUsername"  value="root"/>
        <property name="jdbcPassword"  value="root"/>
        <!-- 創建processEngine時, activiti自動創建23張表 -->
        <property name="databaseSchemaUpdate" value="true"/>
    </bean>
    <!-- 使用配置創建引擎對象 -->
    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
        <property name="processEngineConfiguration" ref="processEngineConfiguration"/>
    </bean>
</beans>

當然, 可以與spring進一步整合, 使用spring方式獲取processEngine.  applicationContext.xml如下

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    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
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver" />
        <property name="url" value="jdbc:mysql:///activiti_day2" />
        <property name="username" value="root" />
        <property name="password" value="root" />
    </bean>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!-- 流程引擎配置對象 -->
    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
        <!-- 注入數據源 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 注入事務管理器對象 -->
        <property name="transactionManager" ref="transactionManager"/>
        <property name="databaseSchemaUpdate" value="true" />
    </bean>
    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
        <property name="processEngineConfiguration" ref="processEngineConfiguration" />
    </bean>
</beans>

 

2.2 部署流程定義

流程是由用戶通過bpmn等文件(底層xml)定義的, 即上面列舉的的bpmn流程圖

定義好的流程需要部署給activiti才能被其使用

   /**
     * 部署流程定義 
     * 一套定義文件只有一個流程定義Key, 但可以被部署多次形成多個版本(部署表里多個id和流程定義表里多個id)
     * 涉及的表:act_re_deployment(部署表)、act_re_procdef(流程定義表)、act_ge_bytearray(二進制表)
     */
    @Test
    public void test() throws FileNotFoundException {
        DeploymentBuilder deploymentBuilder = processEngine.getRepositoryService().createDeployment();
        // 逐個文件部署
        // deploymentBuilder.addClasspathResource("qjlc.bpmn");
        // deploymentBuilder.addClasspathResource("qjlc.png");
        // 壓縮文件打包部署, 推薦
        ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(new File("d:\\processDef.zip")));
        deploymentBuilder.addZipInputStream(zipInputStream );
        
        Deployment deployment = deploymentBuilder.deploy();
    }

 

2.3 啟動流程實例

     /**
     * 啟動一個流程實例
     * 涉及的表:
     * act_ru_execution(流程實例表), 管理流程進度
     * act_ru_task(任務表), 進行到哪一個流程的哪一個任務, 該由誰完成
     */
    @Test
    public void test() throws Exception{
        String processDefinitionKey = "qjlc";
        //方式一:根據流程定義id啟動流程實例
        //String processDefinitionId = "qjlc:6:904";
        //ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceById(processDefinitionId);
        
        //方式二:根據流程定義Key啟動流程實例   推薦!流程定義有多個版本時會選擇最新版本
        ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey);
    }        

 

2.4 辦理任務

  /**
    * 辦理任務, 辦理后框架自動移動到下一任務
    * 涉及的表: act_ru_execution(流程實例表)、act_ru_task(任務表)
    */
    @Test
    public void test() throws Exception{
        String taskId = "1304";
        processEngine.getTaskService().complete(taskId);
    }

 

2.5 其他操作

   /**
    * 查詢流程定義
    * 涉及的表:act_re_procdef
    */
    @Test
    public void test(){
        ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery();
        // 查詢條件過濾
        query.processDefinitionKey("qjlc");
        query.orderByProcessDefinitionVersion().asc();
        List<ProcessDefinition> list = query.listPage(0, 10);
        for (ProcessDefinition processDefinition : list) {
            System.out.println(processDefinition.getId());
        }
    }

activiti中查詢的套路:  processEngine.getXXXService().createXXXQuery().list()/singleResult()
processEngine.getRepositoryService().createDeploymentQuery().list(); // 查詢部署
processEngine.getRuntimeService().createProcessInstanceQuery().list(); // 查詢流程實例
processEngine.getTaskService().createTaskQuery().list(); // 查詢個人任務
processEngine.getIdentityService().createUserQuery().list(); // 查詢用戶
processEngine.getHistoryService().createHistoricActivityInstanceQuery().list(); //查詢歷史
過濾條件
查詢個人任務 query.taskAssignee()
查詢組任務    query.taskCandidate()

幾個javabean(和表對應):
Deployment------act_re_deployment
ProcessDefinition-----act_re_procdef
ProcessInstance------act_ru_execution
Task-----act_ru_task
幾個Query對象
DeploymentQuery------act_re_deployment
ProcessDefinitionQuery-----act_re_procdef
ProcessInstanceQuery------act_ru_execution
TaskQuery-----act_ru_task
幾個Service
RepositoryService----操作部署表、流程定義表等靜態資源信息表
RuntimeService----操作流程實例表、任務表等動態信息表
TaskService-----操作任務表
HistoryService----操作歷史表
IdentityService----操作用戶表、組表、關系表

  // 刪除流程定義
    @Test
    public void test1(){
        String deploymentId = "101";  //部署id
        boolean cascade = false;  // 級聯刪除, 設置為true的話, 有正在跑的流程實例及任務也會被刪除
        processEngine.getRepositoryService().deleteDeployment(deploymentId, cascade);
    }
    // 刪除流程實例
    @Test
    public void test2() throws Exception{
        String processInstanceId = "1201";
        String deleteReason = "不請假了";  // 可以添加刪除原因
        processEngine.getRuntimeService().deleteProcessInstance(processInstanceId, deleteReason);
    }
  // 根據部署id, 獲取定義文件 @Test public void test3() throws Exception{ String deploymentId = "201"; //部署id // 先獲得定義文件的名字 List<String> names = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId); for (String name : names) { InputStream in = processEngine.getRepositoryService().getResourceAsStream(deploymentId, name); FileUtils.copyInputStreamToFile(in, new File("d:\\"+name)); in.close(); } } // 根據流程定義id, 獲取定義文件 @Test public void test4() throws Exception{ String processDefinitionId = "qjlc:6:904"; //流程定義id InputStream pngStream = processEngine.getRepositoryService().getProcessDiagram(processDefinitionId); FileUtils.copyInputStreamToFile(pngStream, new File("d:\\abc.png")); }

通過javabean能訪問到某些需要的字段, 例如

processInstance.getActivityId() -> 當前執行的任務名

processDefinition.getDiagramResourceName() -> 定義文件中圖片的名字

 

2.6  流程變量

多個任務間可以通過流程變量通信.

流程變量以key-value形式存放, 存於表 act_ru_variable. 在同一流程實例里, 不同方式設置變量, key相同時會覆蓋

    // 啟動流程實例時 設置流程變量
    @Test
    public void test1() {
        String processDefinitionKey = "bxlc";
        Map<String, Object> variables = new HashMap<String, Object>();
        variables.put("key", "value");
        ProcessInstance pi = processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey, variables);
    }
    // 辦理任務時 設置流程變量, 更實用!
    @Test
    public void test2() {
        String taskId = "206";
        Map<String, Object> variables = new HashMap<>();
        variables.put("key", "value");
        processEngine.getTaskService().complete(taskId, variables);
    }
    // 通過RuntimeService 設置流程變量
    @Test
    public void test3() {
        String executionId = "201"; // 流程實例id
        Map<String, Object> variables = new HashMap<>();
        variables.put("key", "value");
        //processEngine.getRuntimeService().setVariable(executionId, variableName, value);
        processEngine.getRuntimeService().setVariables(executionId, variables);
    }
    // 通過TaskService 設置流程變量
    @Test
    public void test4() {
        String taskId = "304";
        String key = "key";
        Object value = "value";
        processEngine.getTaskService().setVariable(taskId , key, value);
    }

 

// 通過RuntimeService 獲取流程變量
    @Test
    public void test5() {
        String executionId = "201";
        Object value = processEngine.getTaskService().getVariable(executionId, "user");
        System.out.println(value);
    }
     // 通過TaskService 獲取流程變量
    @Test
    public void test6() {
        String taskId = "304";
        Object value = processEngine.getTaskService().getVariable(taskId, "user");
        System.out.println(value);
    }

流程變量還可以通過在定義流程用表達式${}.  框架在該段任務執行前從act_ru_variable表里動態獲取

另外, 啟動流程實例還有一個重載函數, 除了流程變量variables還能指定業務主鍵businessKey

processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey, businessKey, variables);

businessKey一般設置為業務表的主鍵值, 在使用activiti的時候, 通過查詢業務表主鍵, 能方便地查詢出業務的最新狀態

 

 

2.7  組任務

 

組任務1

  // 查詢組任務
    @Test
    public void test1() {
        TaskQuery query = processEngine.getTaskService().createTaskQuery();
        // 使用候選人查詢組任務
        String candidateUser = "財務二";
        query.taskCandidateUser(candidateUser);
        List<Task> list = query.list();
        for (Task task : list) {
            System.out.println(task.getId());
        }
    }
    // 拾取組任務
    @Test
    public void test2() {
        String taskId = "1102";
        processEngine.getTaskService().claim(taskId , "財務二");
    }
    // 辦理組任務, 無需指定辦理人
    @Test
    public void test3() throws Exception{
        String taskId = "1102";
        processEngine.getTaskService().complete(taskId);
    }

組任務2

  // activiti使用自己的用戶與組的權限表, 因此需要設置. 但需注意要與框架外用戶/組同步設置
    @Test
    public void test2() {
        // 創建組
        Group group = new GroupEntity();
        group.setId("財務組");
        processEngine.getIdentityService().saveGroup(group);
        // 創建用戶
        User user = new UserEntity();
        user.setId("2");
        processEngine.getIdentityService().saveUser(user);
        // 維護用戶與組的關系
        processEngine.getIdentityService().createMembership("2", "財務組");
    }
    // 查詢組任務
    @Test
    public void test2() {
        TaskQuery query = processEngine.getTaskService().createTaskQuery();
        String candidateUser = "2";
        // 使用候選人過濾
        query.taskCandidateUser(candidateUser);
        // 使用組過濾
        //query.taskCandidateGroup("財務組");
        List<Task> list = query.list();
        for (Task task : list) {
            System.out.println(task.getId());
        }
    }
    // 拾取組任務
    @Test
    public void test3() {
        String taskId = "1902";
        processEngine.getTaskService().claim(taskId , "2");
    }
    // 辦理組任務略

 

2.8 排他網關

設置分支條件

 

3. 一些使用經驗

1)

考慮到工作流中的一個任務, 對應一個業務段, 可以將taskDefinitionKey設置成strus action類的method, 使之具有一定的通用性

2)

兩種對流程定義的查詢, 后者能獲得更多定義的細節信息  processDefinitionEntity.findActivity(taskId) 工作流中某任務的信息

repositoryService.createProcessDefinitionQuery().processDefinitionId(processDefinitionId).singleResult()

(ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM