1. 程式人生 > >Activiti工作流簡單入門

Activiti工作流簡單入門

BPMN 2.0

BPMN最初由業務流程倡議組織(BPMI)定案,現在BPMI併入到OMG(Object Management Group)了,則由OMG建立規範和維護。

BPMN 2.0正式更名為(Business Process Model And Notation)業務流程符號和模型,也有人繼續稱呼為業務流程建模標記法(Business Process Modeling Notaion),不過無所謂,不管是jBPM、Activiti還是國人開發的FixFlow,都遵循BPMN規範。

Maven配置

JBoss的開源框架都是比較龐大的,不過相對Activiti體積要小一點。下面為配置的依賴項。

<!--Junit-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>${junit.version}</version>
</dependency>

<!--activiti-->
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId
>
activiti-engine</artifactId> <version>${activiti.version}</version> </dependency> <dependency> <groupId>org.activiti</groupId> <artifactId>activiti-bpmn-layout</artifactId> <version>${activiti.version}</version> </dependency
>
<dependency> <groupId>org.activiti</groupId> <artifactId>activiti-spring</artifactId> <version>${activiti.version}</version> </dependency> <!--apache元件--> ...

整合資料庫

為什麼要整合資料庫?如果不整合資料庫,我們大可以使用Quartz這些框架來做流程任務。實際上,Work Flow是用於一種長週期的幾乎非同步的專案執行環境中,並且我們時刻需要將工作流程的狀態記錄下來,就是一種既注重結果,又注重過程的事務中,因此,整合資料庫很有必要。

下面為配置源資料的XML檔案,並且將databaseSchemaUpdate屬性配置為drop-create,即在執行前刪除原有的資料內容。

<?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="processEngineConfiguration" class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
        <property name="databaseSchemaUpdate" value="drop-create"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db_activiti?useUnicode=true&amp;characterEncoding=utf-8"/>
        <property name="jdbcDriver" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUsername" value="root"/>
        <property name="jdbcPassword" value="****"/>
        <property name="jobExecutorActivate" value="true"/>
    </bean>
</beans>

預設配置檔名為activiti.cfg.xml,可以在原始檔中找到。

另外,如果有必要,請將MySQL設定為區分大小寫,即lower_case_table_names = 0。

資料內容會在執行前自動建立,詳細表結構內容可參考官網完整資訊。

建立工作流檔案

由於BPMN規範的作用,一些高階的IDE會自動識別字尾為*.bpmn的檔案,不過這些都無所謂,bpmn檔案實際上就是XML檔案,只是加上了一些圖形的標記,如width、height、x和y的座標,下面為一個招聘面試流程,只包含流程節點,不包含位置標記節點。

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:activiti="http://activiti.org/bpmn"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             targetNamespace="Examples"
             xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL
             http://www.omg.org/spec/BPMN/2.0/20100501/BPMN20.xsd">
    <process id="Interview" name="某公司2012年實習生招聘流程">
        <documentation>招聘工作流程</documentation>
        <startEvent id="start" name="實習生招聘啟動"/>
        <userTask id="bishi" name="筆試" activiti:candidateGroups="人力資源部"/>
        <sequenceFlow id="flow1" name="" sourceRef="start" targetRef="bishi"/>
        <userTask id="yimian" name="技術一面" activiti:candidateGroups="技術部"/>
        <sequenceFlow id="flow2" name="" sourceRef="bishi" targetRef="yimian"/>
        <userTask id="ermian" name="技術二面" activiti:candidateGroups="技術部"/>
        <sequenceFlow id="flow3" name="" sourceRef="yimian" targetRef="ermian"/>
        <userTask id="hrmian" name="HR面" activiti:candidateGroups="人力資源部"/>
        <sequenceFlow id="flow4" name="" sourceRef="ermian" targetRef="hrmian"/>
        <userTask id="luyong" name="錄用,發放Offer" activiti:candidateGroups="人力資源部"/>
        <sequenceFlow id="flow5" name="" sourceRef="hrmian" targetRef="luyong"/>
        <endEvent id="end" name="實習生招聘結束"/>
        <sequenceFlow id="flow6" name="" sourceRef="luyong" targetRef="end"/>
    </process>

</definitions>

為了便於閱讀,一些高階IDE可以轉化為圖形符號,如下圖:

測試執行

有了流程引擎的配置檔案和流程檔案後,就可以編寫程式碼啟動流程引擎並載入該流程檔案了。測試清單如下:

@Test
public void processTests(){
    // 載入配置檔案
    ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti.cfg.xml").buildProcessEngine();
    RepositoryService repositoryService = processEngine.getRepositoryService();
    RuntimeService runtimeService = processEngine.getRuntimeService();
    repositoryService.createDeployment().addClasspathResource("Interview.bpmn").deploy();
    String processId = runtimeService.startProcessInstanceByKey("Interview").getId();

    TaskService taskService = processEngine.getTaskService();
    //得到筆試的流程
    System.out.println("\n***************筆試流程開始***************");

    List<Task> tasks = taskService.createTaskQuery().taskCandidateGroup("人力資源部").list();
    for (Task task : tasks) {
        System.out.println("人力資源部的任務:name:"+task.getName()+",id:"+task.getId());
        taskService.claim(task.getId(), "張三");
    }

    System.out.println("張三的任務數量:"+taskService.createTaskQuery().taskAssignee("張三").count());
    tasks = taskService.createTaskQuery().taskAssignee("張三").list();
    for (Task task : tasks) {
        System.out.println("張三的任務:name:"+task.getName()+",id:"+task.getId());
        taskService.complete(task.getId());
    }

    System.out.println("張三的任務數量:"+taskService.createTaskQuery().taskAssignee("張三").count());
    System.out.println("***************筆試流程結束***************");

    System.out.println("\n***************一面流程開始***************");
    tasks = taskService.createTaskQuery().taskCandidateGroup("技術部").list();
    for (Task task : tasks) {
        System.out.println("技術部的任務:name:"+task.getName()+",id:"+task.getId());
        taskService.claim(task.getId(), "李四");
    }

    System.out.println("李四的任務數量:"+taskService.createTaskQuery().taskAssignee("李四").count());
    for (Task task : tasks) {
        System.out.println("李四的任務:name:"+task.getName()+",id:"+task.getId());
        taskService.complete(task.getId());
    }

    System.out.println("李四的任務數量:"+taskService.createTaskQuery().taskAssignee("李四").count());
    System.out.println("***************一面流程結束***************");

    System.out.println("\n***************二面流程開始***************");
    tasks = taskService.createTaskQuery().taskCandidateGroup("技術部").list();
    for (Task task : tasks) {
        System.out.println("技術部的任務:name:"+task.getName()+",id:"+task.getId());
        taskService.claim(task.getId(), "李四");
    }

    System.out.println("李四的任務數量:"+taskService.createTaskQuery().taskAssignee("李四").count());
    for (Task task : tasks) {
        System.out.println("李四的任務:name:"+task.getName()+",id:"+task.getId());
        taskService.complete(task.getId());
    }

    System.out.println("李四的任務數量:"+taskService.createTaskQuery().taskAssignee("李四").count());
    System.out.println("***************二面流程結束***************");

    System.out.println("***************HR面流程開始***************");
    tasks = taskService.createTaskQuery().taskCandidateGroup("人力資源部").list();
    for (Task task : tasks) {
        System.out.println("技術部的任務:name:"+task.getName()+",id:"+task.getId());
        taskService.claim(task.getId(), "李四");
    }

    System.out.println("李四的任務數量:"+taskService.createTaskQuery().taskAssignee("李四").count());
    for (Task task : tasks) {
        System.out.println("李四的任務:name:"+task.getName()+",id:"+task.getId());
        taskService.complete(task.getId());
    }

    System.out.println("李四的任務數量:"+taskService.createTaskQuery().taskAssignee("李四").count());
    System.out.println("***************HR面流程結束***************");

    System.out.println("\n***************錄用流程開始***************");
    tasks = taskService.createTaskQuery().taskCandidateGroup("人力資源部").list();
    for (Task task : tasks) {
        System.out.println("技術部的任務:name:"+task.getName()+",id:"+task.getId());
        taskService.claim(task.getId(), "李四");
    }

    System.out.println("李四的任務數量:"+taskService.createTaskQuery().taskAssignee("李四").count());
    for (Task task : tasks) {
        System.out.println("李四的任務:name:"+task.getName()+",id:"+task.getId());
        taskService.complete(task.getId());
    }

    System.out.println("李四的任務數量:"+taskService.createTaskQuery().taskAssignee("李四").count());
    System.out.println("***************錄用流程結束***************");

    HistoryService historyService = processEngine.getHistoryService();
    HistoricProcessInstance historicProcessInstance = historyService
            .createHistoricProcessInstanceQuery()
            .processInstanceId(processId).singleResult();
    System.out.println("\n流程結束時間:"+historicProcessInstance.getEndTime());
}

程式碼清單中使用 ProcessEngines類載入預設的流程配置檔案(activiti.cfg.xml),再獲取各個服務元件的例項。 RepositoryService主要用於管理流程的資源, RuntimeService主要用於流程執行時的流程管理,TaskService主要用於管理流程任務。最後, HistoricProcessInstance會將工作的流程歷史記錄下來。