Activiti6.0学习实践(3)-应用工程(helloworld)构建
本章主要是通过构建一个流程应用,来了解通过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配置,如下图红框,问题解决