天天看點

Activiti6.0 多級審批流程 代碼執行Activiti6.0 多級審批流程 代碼執行

Activiti6.0 多級審批流程 代碼執行

背景:公司要求有流程審批系統以便于适應不同客戶的審批層級;确定以Activiti6.0作為流程引擎;

這裡給出一個簡單的多級審批流程:

  • 建立流程圖,我這裡流程圖在eclipse中生成的,使用了Activiti Eclipse BPMN 2.0 Designer插件;流程圖為.bpmn格式,我們在解析的時候,可以重命名為.bpmn20.xml;

這裡給出一個多級審批的流程圖:

Activiti6.0 多級審批流程 代碼執行Activiti6.0 多級審批流程 代碼執行

這個圖我加了一些文字說明,醜了點,将就着看吧;

然後我給出這個流程圖對應的源碼:檔案全名稱為ApprovalProcess.bpmn20.xml

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn"
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
             xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
             typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
             targetNamespace="http://www.activiti.org/test">
    <process id="myProcess" name="流程審批" isExecutable="true">
        <startEvent id="startEvent" name="開始"></startEvent>
        <userTask id="Submit" name="填寫資訊送出校對">
            <extensionElements>
                <activiti:formProperty id="SubmitMessage" name="送出時填寫的資訊" type="string"
                                       required="true"></activiti:formProperty>
                <activiti:formProperty id="SubmitUserName" name="申請人" type="string"
                                       required="true"></activiti:formProperty>
                <activiti:formProperty id="SubmitTime" name="送出時間" type="date" datePattern="yyyy-MM-dd"
                                       required="true"></activiti:formProperty>
                <activiti:formProperty id="SubmitType" name="是否送出校對" type="string"
                                       required="true"></activiti:formProperty>
            </extensionElements>
        </userTask>
        <sequenceFlow id="flow1" sourceRef="startEvent" targetRef="Submit"></sequenceFlow>
        <exclusiveGateway id="decideSubmit" name="送出校對或者取消"></exclusiveGateway>
        <sequenceFlow id="flow2" sourceRef="Submit" targetRef="decideSubmit"></sequenceFlow>
        <userTask id="Proofread" name="校對">
            <extensionElements>
                <activiti:formProperty id="ProofreadMessage" name="校對時填寫的資訊" type="string"
                                       required="true"></activiti:formProperty>
                <activiti:formProperty id="ProofreadUserName" name="校對人" type="string"
                                       required="true"></activiti:formProperty>
                <activiti:formProperty id="ProofreadTime" name="校對時間" type="date" datePattern="yyyy-MM-dd"
                                       required="true"></activiti:formProperty>
                <activiti:formProperty id="ProofreadType" name="是否校對通過" type="string"
                                       required="true"></activiti:formProperty>
            </extensionElements>
        </userTask>
        <sequenceFlow id="flow3" sourceRef="decideSubmit" targetRef="Proofread">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${SubmitType=="Y" || SubmitType=="y"}]]></conditionExpression>
        </sequenceFlow>
        <exclusiveGateway id="decideProofread" name="校對通過或者失敗"></exclusiveGateway>
        <sequenceFlow id="flow4" sourceRef="Proofread" targetRef="decideProofread"></sequenceFlow>
        <userTask id="Review" name="稽核">
            <extensionElements>
                <activiti:formProperty id="ReviewMesage" name="稽核時填寫的資訊" type="string"
                                       required="true"></activiti:formProperty>
                <activiti:formProperty id="ReviewUserName" name="稽核人" type="string"
                                       required="true"></activiti:formProperty>
                <activiti:formProperty id="ReviewTime" name="稽核時間" type="date" datePattern="yyyy-MM-dd"
                                       required="true"></activiti:formProperty>
                <activiti:formProperty id="ReviewType" name="是否稽核通過" type="string"
                                       required="true"></activiti:formProperty>
            </extensionElements>
        </userTask>
        <sequenceFlow id="flow5" sourceRef="decideProofread" targetRef="Review">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${ProofreadType=="Y" || ProofreadType=="y"}]]></conditionExpression>
        </sequenceFlow>
        <exclusiveGateway id="decideReview" name="稽核通過或者失敗"></exclusiveGateway>
        <sequenceFlow id="flow6" sourceRef="Review" targetRef="decideReview"></sequenceFlow>
        <endEvent id="endEvent1" name="結束"></endEvent>
        <sequenceFlow id="flow7" sourceRef="decideReview" targetRef="endEvent1">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${ReviewType=="Y" || ReviewType=="y"}]]></conditionExpression>
        </sequenceFlow>
        <endEvent id="endEventCancel" name="取消"></endEvent>
        <sequenceFlow id="flow8" sourceRef="decideSubmit" targetRef="endEventCancel">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${SubmitType=="N" || SubmitType=="n"}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="flow9" sourceRef="decideProofread" targetRef="Submit">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${ProofreadType=="N" || ProofreadType=="n"}]]></conditionExpression>
        </sequenceFlow>
        <sequenceFlow id="flow10" sourceRef="decideReview" targetRef="Submit">
            <conditionExpression xsi:type="tFormalExpression">
                <![CDATA[${ReviewType=="N" || ReviewType=="n"}]]></conditionExpression>
        </sequenceFlow>
    </process>
    <bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
        <bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
            <bpmndi:BPMNShape bpmnElement="startEvent" id="BPMNShape_startEvent">
                <omgdc:Bounds height="35.0" width="35.0" x="130.0" y="260.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="Submit" id="BPMNShape_Submit">
                <omgdc:Bounds height="55.0" width="105.0" x="210.0" y="250.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="decideSubmit" id="BPMNShape_decideSubmit">
                <omgdc:Bounds height="40.0" width="40.0" x="360.0" y="258.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="Proofread" id="BPMNShape_Proofread">
                <omgdc:Bounds height="55.0" width="105.0" x="445.0" y="251.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="decideProofread" id="BPMNShape_decideProofread">
                <omgdc:Bounds height="40.0" width="40.0" x="595.0" y="259.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="Review" id="BPMNShape_Review">
                <omgdc:Bounds height="55.0" width="105.0" x="680.0" y="252.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="decideReview" id="BPMNShape_decideReview">
                <omgdc:Bounds height="40.0" width="40.0" x="830.0" y="260.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="endEvent1" id="BPMNShape_endEvent1">
                <omgdc:Bounds height="35.0" width="35.0" x="915.0" y="263.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNShape bpmnElement="endEventCancel" id="BPMNShape_endEventCancel">
                <omgdc:Bounds height="35.0" width="35.0" x="440.0" y="350.0"></omgdc:Bounds>
            </bpmndi:BPMNShape>
            <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
                <omgdi:waypoint x="165.0" y="277.0"></omgdi:waypoint>
                <omgdi:waypoint x="210.0" y="277.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
                <omgdi:waypoint x="315.0" y="277.0"></omgdi:waypoint>
                <omgdi:waypoint x="360.0" y="278.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
                <omgdi:waypoint x="400.0" y="278.0"></omgdi:waypoint>
                <omgdi:waypoint x="445.0" y="278.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
                <omgdi:waypoint x="550.0" y="278.0"></omgdi:waypoint>
                <omgdi:waypoint x="595.0" y="279.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
                <omgdi:waypoint x="635.0" y="279.0"></omgdi:waypoint>
                <omgdi:waypoint x="680.0" y="279.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow6" id="BPMNEdge_flow6">
                <omgdi:waypoint x="785.0" y="279.0"></omgdi:waypoint>
                <omgdi:waypoint x="830.0" y="280.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow7" id="BPMNEdge_flow7">
                <omgdi:waypoint x="870.0" y="280.0"></omgdi:waypoint>
                <omgdi:waypoint x="915.0" y="280.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow8" id="BPMNEdge_flow8">
                <omgdi:waypoint x="380.0" y="298.0"></omgdi:waypoint>
                <omgdi:waypoint x="380.0" y="367.0"></omgdi:waypoint>
                <omgdi:waypoint x="440.0" y="367.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow9" id="BPMNEdge_flow9">
                <omgdi:waypoint x="615.0" y="299.0"></omgdi:waypoint>
                <omgdi:waypoint x="614.0" y="401.0"></omgdi:waypoint>
                <omgdi:waypoint x="262.0" y="398.0"></omgdi:waypoint>
                <omgdi:waypoint x="262.0" y="305.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
            <bpmndi:BPMNEdge bpmnElement="flow10" id="BPMNEdge_flow10">
                <omgdi:waypoint x="850.0" y="260.0"></omgdi:waypoint>
                <omgdi:waypoint x="850.0" y="87.0"></omgdi:waypoint>
                <omgdi:waypoint x="565.0" y="87.0"></omgdi:waypoint>
                <omgdi:waypoint x="262.0" y="87.0"></omgdi:waypoint>
                <omgdi:waypoint x="262.0" y="250.0"></omgdi:waypoint>
            </bpmndi:BPMNEdge>
        </bpmndi:BPMNPlane>
    </bpmndi:BPMNDiagram>
</definitions>
           
  • maven項目引入dependency依賴
<?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>

    <groupId>org.sd</groupId>
    <artifactId>process</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.activiti</groupId>
            <artifactId>activiti-engine</artifactId>
            <version>6.0.0</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>

        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>28.2-jre</version>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.200</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>
           

這裡說明一下因為是代碼測試,是以資料庫使用的是H2記憶體資料庫;

  • 流程執行代碼
package org.sd.processs;

import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.activiti.engine.*;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.form.TaskFormData;
import org.activiti.engine.impl.form.DateFormType;
import org.activiti.engine.impl.form.StringFormType;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.Executors;

/**
 * @author sword_happy
 */
@Slf4j
public class ProcessMain {

    public static void main(String[] args) throws ParseException {
        log.info("啟動我們的程式!");
        //1 建立流程引擎,基于記憶體的資料庫處理
        ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration();
        ProcessEngine processEngine = cfg.buildProcessEngine();
        String name = processEngine.getName();
        String version = ProcessEngine.VERSION;

        log.info("流程引擎名稱:[{}] 版本:[{}]", name, version);
        //2 部署流程定義檔案
        RepositoryService repositoryService = processEngine.getRepositoryService();
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment();
        deploymentBuilder.addClasspathResource("ApprovalProcess.bpmn20.xml");
        Deployment deployment = deploymentBuilder.deploy();
        String deploymentId = deployment.getId();

        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deploymentId)
                .singleResult();

        log.info("流程定義檔案 [{}],流程ID [{}]", processDefinition.getName(), processDefinition.getId());

        //3 啟動運作流程
        RuntimeService runtimeService = processEngine.getRuntimeService();
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(processDefinition.getId());

        log.info("啟動流程 [{}]", processInstance.getProcessDefinitionKey());

        //4 處理流程任務
        Scanner scanner = new Scanner(System.in);
        while (processInstance != null && !processInstance.isEnded()) {

            TaskService taskService = processEngine.getTaskService();
            List<Task> list = taskService.createTaskQuery().list();

            System.out.println("共計[" + list.size() + "]個任務");

            for (Task task : list) {
                log.info("待處理任務 [{}]", task.getName());

                FormService formService = processEngine.getFormService();

                TaskFormData taskFormData = formService.getTaskFormData(task.getId());

                List<FormProperty> formProperties = taskFormData.getFormProperties();

                Map<String, Object> variables = Maps.newHashMap();
                for (FormProperty formProperty : formProperties) {
                    String line = null;
                    if (StringFormType.class.isInstance(formProperty.getType())) {
                        log.info("請輸入 {} ?", formProperty.getName());
                        line = scanner.nextLine();
                        variables.put(formProperty.getId(), line);
                    } else if (DateFormType.class.isInstance(formProperty.getType())) {
                        log.info("請輸入 {} ? 格式 (yyyy-MM-dd)", formProperty.getName());
                        line = scanner.nextLine();
                        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
                        Date date = simpleDateFormat.parse(line);
                        variables.put(formProperty.getId(), date);
                    } else {
                        log.info("類型暫不支援 {}", formProperty.getType());
                    }
                    log.info("您輸入的内容是 [{}] ?", line);
                }

                taskService.complete(task.getId(), variables);
                processInstance = processEngine.getRuntimeService()
                        .createProcessInstanceQuery()
                        .processInstanceId(processInstance.getId())
                        .singleResult();
            }

        }

        log.info("結束我們的程式!");
    }
}
           

OK,到此為止,大功告成;

提示一下,這裡是基于H2的記憶體資料庫,是以構造對象的時候使用了Memory的方法;

後面的一些對象都是通過ProcessEngine進行擷取或者間接擷取的;==>意味着在Spring中,我們使用Activiti各種service的時候,隻需要添加@Autowired注解就可以直接使用;

過段時間我會再寫一篇基于SpringBoot的activiti6.0多級審批的部落格.

希望本篇部落格對你有幫助;