简介:
工作流是开发者迈向更高级的一个阶梯,能够帮助我们在更规范清晰的业务场景下更加高效的实现开发工作。
适用场景:
任何一个中间件的引入都会带来额外的复杂度,如果不能正确的认识和使用她,那么她就是累赘。工作流是一个很庞大的中间层,一般用于企业中台的流程管理,保证流程的清晰规范高效的同时,大大减少了开发的工作量。
当我们需要开发一个多角色参与的业务流程时,我们们就需要工作流,如请假审批流程、会议室申请、物资采购流程、网购从下单到物流跟踪签收整个后台流程等
工作流技术现状:
工作流最初都是基于jboss的jbpm,代表性的是activiti和flowable。activiti开发团队解散重组后,部分activiti核心开发人员开启了flowable项目,原开发人员则推进activiti7版本的开发。
网上能找到的工作流学习资料比较少,目前activiti7分别在5月和6月推出了7.1.0M1和M2版本,flowable也到了6.4.1版本,但事实上这两个目前都是还在开发中的半成品。如果想要使用的话,还是寻求稳定版本较好,activiti的稳定版本有5.22和6.0.0,我在这里以6.0.0版本为例介绍下我的学习过程。
知识基础:
activiti是以BPMN规范为基础的,所以需要了解BPMN的基础知识,这里只作简单介绍。
bpmn通过流程图和用户交互,用工作流自己的表来维护流程数据,其中activiti是28张表,以act_开头,act_hi_是历史记录类表、act_ru_是运行时流程数据维护表。
BPMN基本对象:
事件event: 代表开始和结束,流程图表示为圆
活动Task(activity):代表处理活动的角色,流程图是一个矩形
网关gateway:代表角色的处理选择,决定流程走向,流程图是一个菱形
流向flow:代表流程图走向,流程图是一个单方向的箭头线
活动任务 Task:
userTask 人机交互任务,必须有人参与操作的任务
ServiceTask Task服务任务,机器自动化
SendTask 发送任务,类似于ServiceTask
ReceiveTask 状态任务,一般用户表示活动状态,需要singal进行流转
ManualTask 线下手工执行任务
BusinessRuleTask 业务规则任务
ScriptTask 脚本任务
AbstractTask 抽象任务
网关 Gateways:
parallel Gateway 并行网关 菱形中一个加号,不会解析条件
Exclusive Gateway 排他网关 菱形或者菱形中一个乘号
Inclusive Gateway 包容网关 菱形中一个圆,走完所有符合条件的flow
工作准备:
安装eclipse并安装activiti流程图绘制插件
打开Eclipse -> Help -> Install New SoftWare-> Add
name任意 location:http://www.activiti.org/designer/update/
如果网络不好请架梯子翻墙并反复尝试,网上离线安装的方法我试过了,不可用。
idea玩家可以setting-plugin-actiBPM install下载安装
不过学习阶段不推荐使用idea插件,本人一开始就用的actiBPM,结果画出来的的流程图部署时总是报错,而且actiBPM的表单支持非常糟糕
还有其他的绘图工具如bpmn-js,还在开发中,功能并不完善不推荐
学习开始:
1.流程图工程新建
新建activiti项目:eclipse-文件-新建-其他-activiti-activitiProject
新建activiti空白文件:eclipse-文件-新建-其他-activiti-activitiDiagram
2.使用右侧的画板,画一个简单的审批流程图如图
3.填充流程图信息[重点]
流程图里可以填充的信息选择非常多,每一条信息,在代码中都是有用的,在尝试成功部署运行流程图后,慢慢去理解他每一个配置的意义。此处只展示实现该简单流程需要的配置
3.1文件命名
鼠标点击空白处,填充如下信息,用来命名该bpmn文件的名称和id
3.2userTask审批填充
鼠标点击领导审批矩形框,填充领导审批业务的名称和id
选择属性下的Form-NEW,填写领导审批需要的表单信息,其中type为string,全小写。完成后如图所示
人事审批和重新申请以此类推。
3.3网关flow条件填充
点击领导审批后面的网关指向人事的线,在属性的Main config里填写审批判断条件,此处的tlApprove和之前领导审批Form中的单词大小写要一致
点击该网关指向重新申请的线作同样的修改,其他类推,如图。
填充完毕保存。至此,一个简易的流程就画完了总结下,其实我们必须要填的就两个地方:1.form中xxApprove用于存储用户传给系统的指令(同意/反对)2.网关后面flow的判断条件,写法和jsp一样${xxApprove == flag}
4.搭建一个java工程,部署并运行该流程图
4.1使用idea新建一个普通工程
file-new-project-spring initializr-next...finish;可以把demo改成自己喜欢的名字,如activiti6
4.2添加activiti依赖
在pom文件中添加activiti依赖和h2database依赖
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-engine</artifactId>
<version>6.0.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.3.176</version>
</dependency>
4.3添加bpmn文件
在resource目录下新建一个processes目录,将刚刚eclipse中画好的bpmn文件复制进来
4.4添加日志配置文件
在resource目录下新建一个名为logback.xml的文件,输入如下xml配置
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<property name="log.dir" scan="true" scanPeriod="30 seconds"/>
<property name="encoding" value="UTF-8"/>
<property name="plain" value="%msg%n"/>
<property name="std" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"/>
<!-- 控制台输出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${plain}</pattern>
<charset>${encoding}</charset>
</encoder>
</appender>
<!-- 日志输出级别 -->
<logger name="root">
<level value="ERROR"/>
</logger>
<logger name="com.example">
<level value="DEBUG"/>
</logger>
<root>
<appender-ref ref="stdout" />
</root>
</configuration>
4.5新建一个测试类用来部署并运行我们的bpmn文件
package com.example.activiti6.generate;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngineConfiguration;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.TaskService;
import org.activiti.engine.form.FormProperty;
import org.activiti.engine.impl.form.StringFormType;
import org.activiti.engine.repository.Deployment;
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.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
/**
* @ClassName demotest
* @Description TODO
* @Author cheng
* @Date 2019/6/23 13:53
**/
public class demotest {
private static final Logger logger = LoggerFactory.getLogger(demotest.class);
public static void main(String[] args) {
logger.info("流程启动");
//创建流程引擎
ProcessEngine processEngine = ProcessEngineConfiguration.createStandaloneInMemProcessEngineConfiguration().buildProcessEngine();
logger.info("流程器名称:{};流程器版本号:{}",processEngine.getName(),ProcessEngine.VERSION);
//部署流程定义文件
RepositoryService repositoryService = processEngine.getRepositoryService();
Deployment deploy = repositoryService.createDeployment().addClasspathResource("processes/MyProcess.bpmn").deploy();
ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploy.getId()).singleResult();
logger.info("流程定义文件名称:{};流程定义文件id:{}",processDefinition.getName(),processDefinition.getId());
//启动运行流程
ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceById(processDefinition.getId());
logger.info("流程实例的定义key:{}",processInstance.getProcessDefinitionKey());
//处理流程任务
Scanner scanner = new Scanner(System.in);
while(processInstance != null && !processInstance.isEnded()){
TaskService taskService = processEngine.getTaskService();
List<Task> list = taskService.createTaskQuery().list();
for (Task task : list) {
logger.info("待处理任务:{}",task.getName());
List<FormProperty> formProperties = processEngine.getFormService().getTaskFormData(task.getId()).getFormProperties();
Map<String,Object> variables = new HashMap<>();
for (FormProperty formProperty : formProperties) {
String line = null;
if(StringFormType.class.isInstance(formProperty.getType())){
logger.info("请输入:{}",formProperty.getName());
line = scanner.nextLine();
variables.put(formProperty.getId(),line);
}else{
logger.info("您输入的类型不支持:{}",formProperty.getType());
}
logger.info("您输入的内容是:{}",line);
}
taskService.complete(task.getId(),variables);
//查询当前流程实例
processInstance = processEngine.getRuntimeService().createProcessInstanceQuery().processInstanceId(processInstance.getId()).singleResult();
}
logger.info("待处理任务数量:{}",list.size());
}
}
}
5.执行流程
执行结果如下图,本次工作流入门体验完毕,下一节将进一步讲解在工作中如何实际使用activiti完成开发任务