本章主要是通過構建一個流程應用,來了解通過idea來創建流程圖和編寫代碼來創建一個流程應用。
目錄
1、IDEA安裝插件actiBPM
啓動idea,通過 File-setting 找到 plugins項目
點擊安裝,安裝完成後根據提示重啓IDEA。
2、創建一個maven工程
接下來,我們需要創建一個新的maven工程
3、創建BPMN流程圖
下面我們通過這個工程創建一個二級審批流程
3.1 創建一個BPMN 流程
下面我們就可以在這個界面上畫流程圖了(話說這個界面槽點多多)
我們要畫的流程是一個二級審批流程:
開始--員工填寫請假流程--部門主管審批—人力審批—結束
3.2 修改元素的名稱
對每個元素進行修改名稱,修改後的內容如下圖所示
設定屬性: form Property中的 id, name, type
注意上圖中的類型後面實踐中證明是錯誤的,只要都用小寫就好,否則會報錯誤。(string, date等)
好,流程的配置到這裏,暫時先放一下,我們回到工程
4、BPMN定義文件xml化
將我們設計的BPMN定義文件,複製,複製的時候重命名,通常是在後面增加20.xml。 20表示遵循BPMN2.0規範,重命名後,可以直接打開xml查看,和我們熟知的xml文檔沒有什麼差別。
下面是本例中xml內容:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" 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" xmlns:tns="http://www.activiti.org/test" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" expressionLanguage="http://www.w3.org/1999/XPath" id="m1576563040688" name="" targetNamespace="http://www.activiti.org/test" typeLanguage="http://www.w3.org/2001/XMLSchema">
<process id="myProcess_1" isClosed="false" isExecutable="true" name="二級審批流程" processType="None">
<startEvent id="_2" name="開始"/>
<userTask activiti:exclusive="true" id="_3" name="員工填寫">
<extensionElements>
<activiti:formProperty id="f1" name="員工姓名" type="string"/>
<activiti:formProperty id="f2" name="申請內容" type="string"/>
<activiti:formProperty id="f3" name="提交時間" type="date"/>
</extensionElements>
</userTask>
<sequenceFlow id="_4" sourceRef="_2" targetRef="_3"/>
<exclusiveGateway gatewayDirection="Unspecified" id="_5" name="提交or取消"/>
<userTask activiti:exclusive="true" id="_6" name="主管審批">
<extensionElements>
<activiti:formProperty id="f4" name="審批人姓名" type="string"/>
<activiti:formProperty id="f5" name="審批內容" type="string"/>
<activiti:formProperty id="f6" name="審批時間" type="date"/>
</extensionElements>
‘
</userTask>
<sequenceFlow id="_7" sourceRef="_3" targetRef="_5"/>
<sequenceFlow id="_8" sourceRef="_5" targetRef="_6"/>
<userTask activiti:exclusive="true" id="_11" name="人力審批">
<extensionElements>
<activiti:formProperty id="f7" name="人力姓名" type="string"/>
<activiti:formProperty id="f8" name="審批內容" type="string"/>
<activiti:formProperty id="f9" name="審批時間" type="date"/>
</extensionElements>
</userTask>
<exclusiveGateway gatewayDirection="Unspecified" id="_12" name="主管審批校驗"/>
<exclusiveGateway gatewayDirection="Unspecified" id="_13" name="人力審批校驗"/>
<endEvent id="_14" name="結束"/>
<sequenceFlow id="_15" sourceRef="_6" targetRef="_12"/>
<sequenceFlow id="_16" sourceRef="_12" targetRef="_11"/>
<sequenceFlow id="_17" sourceRef="_11" targetRef="_13"/>
<sequenceFlow id="_18" sourceRef="_13" targetRef="_14"/>
<sequenceFlow id="_19" sourceRef="_12" targetRef="_3"/>
<sequenceFlow id="_9" sourceRef="_13" targetRef="_3"/>
<sequenceFlow id="_10" sourceRef="_5" targetRef="_14"/>
</process>
<bpmndi:BPMNDiagram documentation="background=#3C3F41;count=1;horizontalcount=1;orientation=0;width=842.4;height=1195.2;imageableWidth=832.4;imageableHeight=1185.2;imageableX=5.0;imageableY=5.0" id="Diagram-_1" name="New Diagram">
<bpmndi:BPMNPlane bpmnElement="myProcess_1">
<bpmndi:BPMNShape bpmnElement="_2" id="Shape-_2">
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="185.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" id="Shape-_3">
<omgdc:Bounds height="55.0" width="85.0" x="105.0" y="175.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_5" id="Shape-_5" isMarkerVisible="false">
<omgdc:Bounds height="32.0" width="32.0" x="270.0" y="180.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_6" id="Shape-_6">
<omgdc:Bounds height="55.0" width="85.0" x="350.0" y="175.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_11" id="Shape-_11">
<omgdc:Bounds height="55.0" width="85.0" x="580.0" y="175.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="55.0" width="85.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_12" id="Shape-_12" isMarkerVisible="false">
<omgdc:Bounds height="32.0" width="32.0" x="500.0" y="190.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_13" id="Shape-_13" isMarkerVisible="false">
<omgdc:Bounds height="32.0" width="32.0" x="750.0" y="180.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_14" id="Shape-_14">
<omgdc:Bounds height="32.0" width="32.0" x="840.0" y="185.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="32.0" width="32.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_15" id="BPMNEdge__15" sourceElement="_6" targetElement="_12">
<omgdi:waypoint x="435.0" y="202.5"/>
<omgdi:waypoint x="500.0" y="206.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_17" id="BPMNEdge__17" sourceElement="_11" targetElement="_13">
<omgdi:waypoint x="665.0" y="202.5"/>
<omgdi:waypoint x="750.0" y="196.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_16" id="BPMNEdge__16" sourceElement="_12" targetElement="_11">
<omgdi:waypoint x="532.0" y="206.0"/>
<omgdi:waypoint x="580.0" y="202.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_19" id="BPMNEdge__19" sourceElement="_12" targetElement="_3">
<omgdi:waypoint x="516.0" y="190.0"/>
<omgdi:waypoint x="368.0" y="125.0"/>
<omgdi:waypoint x="147.5" y="175.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_18" id="BPMNEdge__18" sourceElement="_13" targetElement="_14">
<omgdi:waypoint x="782.0" y="196.0"/>
<omgdi:waypoint x="840.0" y="201.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_4" id="BPMNEdge__4" sourceElement="_2" targetElement="_3">
<omgdi:waypoint x="32.0" y="201.0"/>
<omgdi:waypoint x="105.0" y="202.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_7" id="BPMNEdge__7" sourceElement="_3" targetElement="_5">
<omgdi:waypoint x="190.0" y="202.5"/>
<omgdi:waypoint x="270.0" y="196.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_8" id="BPMNEdge__8" sourceElement="_5" targetElement="_6">
<omgdi:waypoint x="302.0" y="196.0"/>
<omgdi:waypoint x="350.0" y="202.5"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_9" id="BPMNEdge__9" sourceElement="_13" targetElement="_3">
<omgdi:waypoint x="766.0" y="180.0"/>
<omgdi:waypoint x="470.0" y="100.0"/>
<omgdi:waypoint x="147.5" y="175.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_10" id="BPMNEdge__10" sourceElement="_5" targetElement="_14">
<omgdi:waypoint x="286.0" y="212.0"/>
<omgdi:waypoint x="530.0" y="295.0"/>
<omgdi:waypoint x="856.0" y="217.0"/>
<bpmndi:BPMNLabel>
<omgdc:Bounds height="0.0" width="0.0" x="0.0" y="0.0"/>
</bpmndi:BPMNLabel>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
5、修改pom文件添加依賴
在pom文件中,添加我們這工程的依賴,具體見如下pom.xml文件
<?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>com.study.myactiviti</groupId>
<artifactId>myactiviti-app</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</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>25.0-jre</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
</dependencies>
</project>
6、創建應用入口DemoMain類
在入口類中,我們要實現整個流程引擎應用的串聯。因此應用需要如下幾個步驟
- 創建流程引擎
2. 部署流程定義文件xml
3、啓動流程
4、處理流程
這一步主要是獲取任務列表,並獲取任務中的表單屬性,對錶單屬性進行處理。上面代碼中引入了scanner進行命令式交互。
完成代碼見下面:
package com.study.activiti;
import com.google.common.collect.Maps;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Scanner;
/**
* Describe:啓動類
*
* @author cwqsolo
* @date 2019/12/17
*/
public class DemoMain {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoMain.class);
public static void main(String[] args) throws ParseException {
LOGGER.info("啓動應用------>>>");
//步驟1--創建流程引擎
ProcessEngine processEngine = getProcessEngine();
//步驟2---部署流程定義
ProcessDefinition processDefinition = getProcessDefinition(processEngine);
//步驟3--啓動流程
ProcessInstance processInstance = getProcessInstance(processEngine, processDefinition);
LOGGER.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();
LOGGER.info("待處理任務數量 {}", list.size());
for (Task task : list) {
LOGGER.info("待處理任務 {}", task.getName());
FormService formService = processEngine.getFormService();
TaskFormData taskFormData = formService.getTaskFormData(task.getId());
List<FormProperty> formProperties = taskFormData.getFormProperties();
HashMap<String, Object> objectObjectHashMap = Maps.newHashMap();
//從流程中獲取表單,並存入到hashmap
for (FormProperty formProperty : formProperties) {
String s = null;
if (StringFormType.class.isInstance(formProperty.getType())) {
LOGGER.info("請輸入 {} ?", formProperty.getName());
s = scanner.nextLine();
objectObjectHashMap.put(formProperty.getId(), s);
} else if (DateFormType.class.isInstance(formProperty.getType())) {
LOGGER.info("請輸入 {} ? 格式 ( yyyy-MM-dd)", formProperty.getName());
s = scanner.nextLine();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date parse = simpleDateFormat.parse(s);
objectObjectHashMap.put(formProperty.getId(), parse);
} else {
LOGGER.info("類型暫時不支持 {} ", formProperty.getType());
}
LOGGER.info("您輸入的內容是 [{}] ", s);
LOGGER.info("請輸入 {} ?", formProperty.getName());
}
//提交表單
taskService.complete(task.getId(), objectObjectHashMap);
//獲取流程最新的狀態
processInstance = processEngine.getRuntimeService()
.createProcessInstanceQuery()
.processInstanceId(processInstance.getId())
.singleResult();
}
}
LOGGER.info("------>>>結束應用");
}
private static ProcessInstance getProcessInstance(ProcessEngine processEngine, ProcessDefinition processDefinition) {
//獲取流程引擎的一個運行時服務
RuntimeService runtimeService = processEngine.getRuntimeService();
//根據id啓動流程實例, 啓動流程實例的方法有多種,上面獲取了id,可以採用這個方法
return runtimeService.startProcessInstanceById(processDefinition.getId());
}
/**
* 部署流程定義
* @param processEngine
* @return
*/
private static ProcessDefinition getProcessDefinition(ProcessEngine processEngine) {
//由流程引擎創建一個流程庫服務對象
RepositoryService repositoryService = processEngine.getRepositoryService();
//流程庫服務對象創建一個流程部署構建對象
DeploymentBuilder deployment = repositoryService.createDeployment();
//流程構建對象加載配置文件
deployment.addClasspathResource("./comm2Level.bpmn20.xml");
//創建指定流程配置的部署對象並獲取部署ID
Deployment deploy = deployment.deploy();
String id = deploy.getId();
//根據流程庫服務對象獲取流程定義對象
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(id).singleResult();
LOGGER.info("流程定義文件 {}, 流程ID {}", processDefinition.getName(), processDefinition.getId());
return processDefinition;
}
/**
* 創建流程引擎
* @return
*/
private static ProcessEngine getProcessEngine() {
//流程引擎的創建有多種方法,本例中使用mysql,因此需要從數據源來創建, 數據源定義在activiti.cfg.xml 文件中
ProcessEngineConfiguration cfg = ProcessEngineConfiguration.createProcessEngineConfigurationFromResourceDefault();
LOGGER.info("configuration = {}", cfg);
//獲取引擎
ProcessEngine processEngine = cfg.buildProcessEngine();
String name = processEngine.getName();
String version = ProcessEngine.VERSION;
LOGGER.info("獲取流程引擎 {}, 版本爲{}", name, version);
return processEngine;
}
}
7、測試驗證
下面將我們的demo應用跑起來,則會一步一步進行任務處理,在每個任務界面進行表單內容的輸入
8、問題處理小結
8.1、Idea工程報錯, write-unsafe
問題描述:系統報write-unsafe錯誤,不過不影響工程。
解決:
修改idea的啓動參數,即在下圖紅框處文件中,在最後添加-Dfile.encoding=UTF-8
8.2、流程定義獲取name亂碼
問題描述:
解決:
檢查數據庫中記錄,發現也是亂碼,推斷是入數據庫就出問題。檢查發現mysql的url沒有配置字符集,增加UTF-8配置,如下圖紅框,問題解決