工作流(Workflow),就是通过计算机对业务流程自动化执行管理。它主要解决的是“**使在多个参与者之间按照某种预定义的规则自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现**”。工作流就是通过计算机技术对业务流程进行自动化管理。实现多个参与者按照预定的流程去自动执行业务流程。工作流管理系统的目标:管理工作的流程以确保工作在正确的时间被期望的人员所执行——在自动化进行的业务过程中插入人工的执行和干预。
消费品行业,制造业,电信服务业,银证险等金融服务业,物流服务业,物业服务业,物业管理,大中型进出口贸易公司,政府事业机构,研究院所及教育服务业等,特别是大的跨国企业和集团公司。
总结一句话:凡是有组织机构的公司都有可能用到工作流。
关键业务流程:订单、报价处理、采购处理、合同审核、客户电话处理、供应链管理等
行政管理类:出差申请、加班申请、请假申请、用车申请、各种办公用品申请、购买申请、日报周报等凡是原来手工流转处理的行政表单。
人事管理类:员工培训安排、绩效考评、职位变动处理、员工档案信息管理等。
财务相关类:付款请求、应收款处理、日常报销处理、出差报销、预算和计划申请等。
客户服务类:客户信息管理、客户投诉、请求处理、售后服务管理等。
特殊服务类:ISO系列对应流程、质量管理对应流程、产品数据信息管理、贸易公司报关处理、物流公司货物跟踪处理等各种通过表单逐步手工流转完成的任务均可应用工作流软件自动规范地实施。
Activiti5.13、JBPM4.4、OSWorkFlow、WorkFlow......
工作流框架底层需要有数据库提供支持,activiti5.13版本,有23张表。JBPM4.4框架底层有数据库支持,18张表。JBPM底层使用hibernate操作数据库。Activiti框架底层使用的mybatis操作数据库。
Activiti项目是一项新的基于Apache许可的开源**BPM**平台,从基础开始构建,旨在提供支持新的**BPMN 2.0**标准,包括支持对象管理组(OMG),面对新技术的机遇,诸如互操作性和云架构,提供技术实现。
创始人Tom Baeyens是**JBoss** jBPM的项目架构师,以及另一位架构师Joram Barrez,一起加入到创建Alfresco这项首次实现Apache开源许可的BPMN 2.0引擎开发中来。Activiti前身是jbpm4,Activiti架构和jbpm4基本上是一样的。
架构师Tom Baeyens说:“Activiti有非常大的影响力来改变目前BPM的生态。Activiti的Apache授权,完整的功能,将使Activiti到达一个新的水平。Activiti将推动业界的创新,因为BPM技术可以广泛而自由地被应用。通过实现这些想法以及开源社区的努力,也让Activiti成为事实上的 BPM和BPMN标准执行“。它的特色是提供了eclipse插件,开发人员可以通过插件直接绘画出**业务流程图**。
**ProcessEngine**对象,这是Activiti工作的核心。负责生成流程运行时的各种实例及数据、监控和管理流程的运行,该对象是流程使用核心,通过该对象可以获得流程任务的相关对象
**BPM,即业务流程管理**,是一种以规范化的构造端到端的卓越业务流程为中心,以持续的提高组织业务绩效为目的的系统化方法,常见商业管理教育如EMBA、MBA等均将BPM包含在内。
**BPMN:**业务流程建模与标注,包括这些图元如何组合成一个业务流程图(Business Process Diagram);讨论BPMN的各种的用途,包括以何种精度来影响一个流程图中的模型;BPMN作为一个标准的价值,以及BPMN未来发展的远景。
具体发展历史如下:
流对象:一个业务流程图有三个流对象的核心元素。
Activiti使用Bpmn2.0标准进行流程定义(流程建模),bpmn采用xml描述 。
如何使用bpmn进行流程建模:
通常情况下,使用建模工具进行流程图绘制,生成一个图形,通过工具查看bpmn的xml描述 。
如果熟练,手动编写pbmn的xml文档。(对坐标和结点的大小通过工具完成)
JDK1.6或者更高版本
支持的数据库有:h2,mysql,oracle,db2.......等
支持Activiti运行的jar包,可以通过**maven**依赖引入
1)JDK可以到sun的官网下载
http://www.oracle.com/technetwork/java/javase/downloads/index.html
2)数据库,例如:mysql可以在官网上下载。
http://www.mysql.com
3)activiti也可以到Activiti官方网站下载得到。
http://activiti.org/download.html
在有网络的情况下,安装流程设计器步骤如下:
配置新装插件的地址和名称
Name: Activiti BPMN 2.0 designer
Location: http://activiti.org/designer/update/
点击复选框
在Detail部分记得选中 “Contact all updates sites…” , 因为它会检查所有当前安装所需要的插件并可以被Eclipse下载.
安装完以后,点击新建工程new->Other…打开面板,如果看到下图内容:
说明安装成功了。
在没有网络的情况下,安装流程设计器步骤如下:
第一步:获得插件的安装包
第二步:解压安装包,复制到eclipse中的dropins目录中
第三步:重启eclipse,点击新建工程new->Other…打开面板,如果看到下图内容:
说明安装成功了
第一步:
点击FIile-Settings
第二步:点击Plusins 在右面的输入框中输入actiBPM
点击安装(这里由于我已经安装过嘞,所以没有显示),然后重启idea
第四步:
在eclipse左边工作栏右键New选择创建Maven Project项目,创建一个名为ActivitiDemo的项目
点击Finish完成。
然后在pom.xml文件中添加以下依赖
<dependencies>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-engineartifactId>
<version>5.22.0version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-springartifactId>
<version>5.22.0version>
dependency>
<dependency>
<groupId>org.codehaus.groovygroupId>
<artifactId>groovy-allartifactId>
<version>2.4.3version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-apiartifactId>
<version>1.7.6version>
dependency>
<dependency>
<groupId>org.slf4jgroupId>
<artifactId>slf4j-jdk14artifactId>
<version>1.7.6version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.38version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-war-pluginartifactId>
<configuration>
<version>3.1version>
configuration>
plugin>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>2.3.2version>
<configuration>
<source>1.7source>
<target>1.7target>
<encoding>UTF-8encoding>
configuration>
plugin>
plugins>
build>
框架提供了和hibernate类似的自动建表功能
使用配置文件
要求配置文件名称必须为activiti-context.xml或者activiti.cfg.xml,配置的信息必须为
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql:///activitidb" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="123" />
<property name="databaseSchemaUpdate" value="true" />
bean>
beans>
@Test
public void createTable1(){
ProcessEngine processEngine = ProcessEngineConfiguration.createProcessEngineConfigurationFromResource("activiti-context.xml","processEngineConfiguration").buildProcessEngine();
System.out.println("------processEngine----:" + processEngine);
}
使用默认配置文件
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<bean id="processEngineConfiguration"
class="org.activiti.engine.impl.cfg.StandaloneProcessEngineConfiguration">
<property name="jdbcDriver" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl" value="jdbc:mysql:///activitidb" />
<property name="jdbcUsername" value="root" />
<property name="jdbcPassword" value="123" />
<property name="databaseSchemaUpdate" value="true" />
bean>
<bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
<property name="processEngineConfiguration" ref="processEngineConfiguration" />
bean>
beans>
@Test
public void createTable2(){
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
System.out.println(processEngine);
}
然后运行该测试方法,如果运行成功,在数据库中应该会产生25张Activiti的相关数据表
下面是概括了几个常用的数据表
Activiti的后台是有数据库的支持,所有的表都以ACT_开头。 第二部分是表示表的用途的两个字母标识。 用途也和服务的API对应。
Ø 流程部署相关表
Ø 流程实例相关表
Ø Task 任务相关表
Ø 流程变量表
说明:
在Activiti中最核心的类,其他的类都是由他而来。
产生方式
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
3)可以产生RepositoryService
RepositoryService repositoryService =processEngine.getRepositoryService();
RuntimeService runtimeService = processEngine.getRuntimeService();
TaskService taskService =processEngine.getTaskService();
各个Service的作用
RepositoryService | 管理流程定义 |
---|---|
RuntimeService | 执行管理,包括启动,推进,删除流程实例等操作 |
TaskService | 任务管理 |
Activiti的仓库服务类。所谓的仓库指流程定义文档的两个文件:bpmn文件和流程图片
该service可以用来删除部署的流程定义。
是Activiti的流程执行服务类,可以从这个服务类中获取很多关于流程执行的相关的信息。
是Activiti的任务服务类。可以从这个类中获取任务的相关信息,如当前正在执行的个人待办和用户组待办任务。
是Activiti的查询历史信息的类,在一个流程执行完成后,这个对象为我们提供查询历史信息,可以跟踪流程实例对应所有待办节点的运行情况。
流程定义类,可以从这里获得资源文件等。
7.7 ProcessInstance
代表流程定义的执行实例,当一个部署的流程图启动后,该流程只有一条流程实例数据,但是它的流程任务可以有多个,每个任务对应流程图中相应的流程节点。
点击ActivitiTest项目,在src/main/java目录下创建一个diagrams目录用来存放流程图
在当前项目右键选择Activiti Diagram流程图输入流程图名称HelloWorld,然后点击OK
在控制面板的右边栏有相关的画图图标操作
其中一个流程必须包含一个开始节点和一个结束节点,结束节点可以有多个。
然后使用StartEvent, UserTask,EndEvent画出下面的流程图,然后用Connection中的SequenceFlow连线连接起来。
1)选中第一个节点,在 General中的name属性中输入当前节点的名称,在Main config中的Assignee中输入该节点的处理人,然后以此类推将3个节点的值设置完成。
![1542677483179](/1542677483179.png)
2)然后在流程图旁边的空白出点击一下,输入流程的ID和Name值,然后保存
/**
* 部署流程定义(操作数据表:act_re_deployment、act_re_procdef、act_ge_bytearray)
*/
@Test
public void test1(){
ProcessEngine processEngine = ProcessEngines.getDefaultProcessEngine();
RepositoryService service = processEngine.getRepositoryService();
Deployment deployment = service.createDeployment()
.name("helloworld入门")
.addClasspathResource("diagrams/HelloWorld.bpmn")
.addClasspathResource("diagrams/HelloWorld.png")
.deploy();
System.out.println(deployment.getId());
System.out.println(deployment.getName());
}
/**
* 查询流程定义列表
*/
@Test
public void test2() {
// 流程定义查询对象,用于查询表act_re_procdef
ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery();
// 添加过滤条件
query.processDefinitionKey(“HelloWorldKsy”);
// 添加排序条件
query.orderByProcessDefinitionVersion().desc();
// 添加分页查询
query.listPage(0, 10);
List list = query.list();
for (ProcessDefinition pd : list) {
System.out.println(pd.getId() + “–” + pd.getName());
}
}
其中runtimeService.startProcessInstanceByKey(“HelloWorldKey”);中的HelloWorldKey对应流程图中的ID值,在数据表中对应act_re_procdef流程定义表中的key字段
@Test
public void flowStart(){
String processDefinitionKey = "HelloWorldKsy";
//获取正在执行流程实例和执行对象相关的service
RuntimeService runtimeService = processEngine.getRuntimeService();
//使用流程定义key启动流程实例,key对象是HelloWorld.bpmn文件中的ID属性值,对应的是act_re_procdef表中的key
ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey);
System.out.println(processInstance.getId()); //流程实例ID
}
启动完流程后在act_ru_execution表中会产生一条数据,这条数据为当前流程正在执行的任务,其中act_id_字段的值对应流程图节点的ID值
在act_ru_task表中会产生一条任务数据,execution_id_对应act_ru_execution主键,proc_inst_id_为流程实例ID,name_值为流程节点名称,assignee_字段为该待办当前的处理
/**
* 查询个人任务列表
*/
@Test
public void test7() {
TaskQuery query = processEngine.getTaskService().createTaskQuery();
String assignee = "张三";
query.taskAssignee(assignee);
List list = query.list();
for (Task task : list) {
System.out.println("待办任务ID:"+task.getId());
System.out.println("待办任务名称:"+task.getName());
System.out.println("任务创建时间:"+task.getCreateTime());
System.out.println("任务办理人:"+task.getAssignee());
System.out.println("流程实例ID:"+task.getProcessInstanceId());
System.out.println("执行对象ID:"+task.getExecutionId());
System.out.println("流程定义ID:"+task.getProcessDefinitionId());
}
}
/**
* 办理任务
*/
@Test
public void test8(){
String taskId= "7504";
processEngine.getTaskService().complete(taskId);
System.out.println("办理完成,任务ID是:"+taskId);
}
其中taskId对应act_ru_task表的主键ID,因为当前待办任务为第一个节点提交申请,当执行完这个待办后下一条待办数据将会流转到审批【部门经理】那,然后以此类推可以将部门经理和总经理的待办任务全部查询和执行完成。
Activiti框架提供的Service对象
Activiti框架提供的对象(和表有对应关系)
/**
* 部署流程定义 方式一:读取单个的流程定义文件 方式二:读取zip压缩文件
*/
@Test
public void test1() {
DeploymentBuilder deploymentBuilder = processEngine.getRepositoryService().createDeployment();
// 方式一:读取单个的流程定义文件
/*
deploymentBuilder.addClasspathResource("test1.bpmn");
deploymentBuilder.addClasspathResource("test1.png");
Deployment deployment = deploymentBuilder.deploy();
*/
// 方式二:读取zip压缩文件
ZipInputStream zipInputStream = new ZipInputStream(this.getClass().getClassLoader().getResourceAsStream("process.zip"));
deploymentBuilder.addZipInputStream(zipInputStream);
deploymentBuilder.name("请假流程部署");
Deployment deployment = deploymentBuilder.deploy();
}
/**
* 查询部署列表
*/
@Test
public void test2() {
// 部署查询对象,查询表act_re_deployment
DeploymentQuery query = processEngine.getRepositoryService().createDeploymentQuery();
List<Deployment> list = query.list();
for (Deployment deployment : list) {
String id = deployment.getId();
String name = deployment.getName();
System.out.println(id+"---"+name);
}
}
/**
* 查询流程定义列表
*/
@Test
public void test3() {
// 流程定义查询对象,查询表act_re_procdef
ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery();
List<ProcessDefinition> list = query.list();
for (ProcessDefinition pd : list) {
String deploymentId = pd.getDeploymentId();
System.out.println(pd.getName() + "" + pd.getId()+"部署ID:"+deploymentId);
}
}
/**
* 删除部署信息
*/
@Test
public void test4() {
String deploymentId = "1";
// processEngine.getRepositoryService().deleteDeployment(deploymentId);
processEngine.getRepositoryService().deleteDeployment(deploymentId, true); //级联删除
}
/**
* 删除流程定义(通过删除部署信息达到删除流程定义的目的)
*/
@Test
public void test5() {
String deploymentId = "22501";
// processEngine.getRepositoryService().deleteDeployment(deploymentId);
processEngine.getRepositoryService().deleteDeployment(deploymentId,true);
}
/**
* 查询一次部署对应的流程定义文件名称和对应的输入流(bpmn png)
*/
@Test
public void test6() throws Exception {
String deploymentId = "20001";
List<String> names = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId);
for (String name : names) {
System.out.println(name);
InputStream in = processEngine.getRepositoryService().getResourceAsStream(deploymentId, name);
// 将文件保存到本地磁盘
File file = new File("d:\\" + name);
File f1=new File( file.getParent());
if(f1.exists()==false){
f1.mkdirs();
}
OutputStream out = new FileOutputStream(file);
byte[] b = new byte[1024];
int len = 0;
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
out.close();
in.close();
}
}
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.4version>
dependency>
/**
* 查询一次部署对应的流程定义文件名称和对应的输入流(bpmn png)
*/
@Test
public void test6_1() throws Exception {
String deploymentId = "20001";
List<String> names = processEngine.getRepositoryService().getDeploymentResourceNames(deploymentId);
for (String name : names) {
System.out.println(name);
InputStream in = processEngine.getRepositoryService().getResourceAsStream(deploymentId, name);
// 将文件保存到本地磁盘
File file = new File("d:\\" + name);
File f1=new File( file.getParent());
if(f1.exists()==false){
f1.mkdirs();
}
FileUtils.copyInputStreamToFile(in, new File("d:\\" + name));
in.close();
}
}
/**
* 获得png文件的输入流
*/
@Test
public void test7() throws Exception {
String processDefinitionId = "HelloWorldKsy:2:2504";
InputStream pngInputStream = processEngine.getRepositoryService().getProcessDiagram(processDefinitionId);
FileUtils.copyInputStreamToFile(pngInputStream, new File("d:\\my.png"));
}
/**
* 启动流程实例 方式一:根据流程定义的id启动 方式二:根据流程定义的key启动(自动选择最新版本的流程定义启动流程实例)
*/
@Test
public void test8() {
/*
* String processDefinitionId = "HelloWorldKsy:2:2504"; ProcessInstance
* processInstance =
* processEngine.getRuntimeService().startProcessInstanceById
* (processDefinitionId ); System.out.println(processInstance.getId());
*/
String processDefinitionKey = "HelloWorldKsy";
ProcessInstance processInstance = processEngine.getRuntimeService().startProcessInstanceByKey(processDefinitionKey);
System.out.println(processInstance.getId());
}
根据Key启动流程,会使用最新的的流程定义的Key。选择最新的流程定义进行启动!
/**
* 查询流程实例列表,查询act_ru_execution表
*/
@Test
public void test9(){
//流程实例查询对象,查询act_ru_execution表
ProcessInstanceQuery query = processEngine.getRuntimeService().createProcessInstanceQuery();
query.processDefinitionKey("HelloWorldKsy");
query.orderByProcessInstanceId().desc();
query.listPage(0, 2);
List<ProcessInstance> list = query.list();
for (ProcessInstance pi : list) {
System.out.println(pi.getId() + " " + pi.getActivityId());
}
}
/**
* 结束流程实例,操作的表act_ru_execution act_ru_task
*/
@Test
public void test10(){
String processInstanceId = "10001";
processEngine.getRuntimeService().deleteProcessInstance(processInstanceId , "我愿意");
}
/**
* 查询任务列表
*/
@Test
public void test11() {
// 任务查询对象,查询act_ru_task表
TaskQuery query = processEngine.getTaskService().createTaskQuery();
String assignee = "张三";
query.taskAssignee(assignee);
query.orderByTaskCreateTime().desc();
List<Task> list = query.list();
for (Task task : list) {
System.out.println(task.getId());
}
}
/**
* 办理任务
*/
@Test
public void test12(){
String taskId = "32504";
processEngine.getTaskService().complete(taskId);
}
/**
* 查询最新版本的流程定义列表
*/
@Test
public void test14() {
ProcessDefinitionQuery query = processEngine.getRepositoryService().createProcessDefinitionQuery();
query.orderByProcessDefinitionVersion().asc();
List<ProcessDefinition> list = query.list();
Map<String, ProcessDefinition> map = new HashMap<String, ProcessDefinition>();
for (ProcessDefinition pd : list) {
map.put(pd.getKey(), pd);
}
ArrayList<ProcessDefinition> lastList = new ArrayList<>(map.values());
for (ProcessDefinition processDefinition : lastList) {
System.out.println(processDefinition.getName() + " " + processDefinition.getVersion());
}
}