工作流引擎Activiti系列(一)——初识

版权声明:http://blog.csdn.net/anxpp https://blog.csdn.net/anxpp/article/details/53912439

1、介绍

    几乎任何一个公司的软件开发都会涉及到流程,以往我们可能是这么实现的:业务表添加标志位标识流程的节点状态,关联批注表实现审核意见,根据一些业务数据分析处理逻辑,分配任务到用户,节点的调度,审批等.....这其实是很繁琐的,且不说开发起来比较混乱,维护起来更是难上加难:

    

    Activiti刚好就能解决几乎所有的这些问题,当流程开发变得简单有趣。

    官网:https://www.activiti.org/

    官方文档:https://www.activiti.org/userguide/

    Activiti项目是一项新的基于Apache许可的开源BPM平台,从基础开始构建,旨在提供支持新的BPMN 2.0标准,包括支持对象管理组(OMG),面对新技术的机遇,诸如互操作性和云架构,提供技术实现。

    作为开发者,使用Activiti带给我的直接好处:

  1.     天然支持Spring(Spring需要在配置文件中自己定义Activiti的Bean,Spring Boot则不需要)
  2.     流程定义通过画流程图实现(官方提供了相关工具,Eclipse也有插件支持),简单直观,易维护,易修改。
  3.     审批条件参数化,流程分支简单实现。
  4.     审批人可使用多种方式设置(支持用户、用户组、角色、候选组以及监听器动态设置),灵活,简单。
  5.     统一的审批接口,并不需要判断流程当前节点和走向。
  6.     提供强大的JPA查询,同时支持Name Query和Native Query。
  7.     流程数据与业务数据分离。

    后续文章会一步步介绍Activiti的功能,主要使用基于Spring Boot的工程,也会提供单纯的Spring工程Demo。

2、示例

    此处演示一个小示例,暂不解释代码,仅仅看看是怎样用activiti实现的。

    场景就是请假,不过这里稍稍多了点内容,就是请假由请假人对应部门的领导审批,而不是统一的某一部分人。

    2.1、使用Spring Boot工程

    首先创建Spring boot工程,为了演示方便,使用内存数据库,完整pom文件如下:

<?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.anxpp</groupId>
	<artifactId>ActivitiDemo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>ActivitiDemo</name>
	<description>ActivitiDemo</description>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.4.3.RELEASE</version>
		<relativePath /> <!-- lookup parent from repository -->
	</parent>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
	</properties>
	<dependencies>
		<!-- <dependency> -->
		<!-- <groupId>org.mybatis.spring.boot</groupId> -->
		<!-- <artifactId>mybatis-spring-boot-starter</artifactId> -->
		<!-- <version>1.1.1</version> -->
		<!-- </dependency> -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.activiti</groupId>
			<artifactId>spring-boot-starter-basic</artifactId>
			<version>5.17.0</version>
		</dependency>
		<!-- 内存数据库  -->
		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
		</dependency>
		<!-- <dependency> -->
		<!-- <groupId>mysql</groupId> -->
		<!-- <artifactId>mysql-connector-java</artifactId> -->
		<!-- <scope>runtime</scope> -->
		<!-- </dependency> -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
	<distributionManagement>
		<repository>
			<id>Releases</id>
			<name>Nexus Release Repository</name>
			<url>http://anxpp.com/nexus/content/repositories/releases/</url>
		</repository>
		<snapshotRepository>
			<id>Snapshots</id>
			<name>Nexus Snapshot Repository</name>
			<url>http://anxpp.com/nexus/content/repositories/snapshots/</url>
		</snapshotRepository>
	</distributionManagement>
	<repositories>
		<repository>
			<id>nexus</id>
			<name>Nexus</name>
			<url>http://anxpp.com/nexus/content/groups/public/</url>
			<layout>default</layout>
			<releases>
				<enabled>true</enabled>
			</releases>
			<snapshots>
				<enabled>true</enabled>
			</snapshots>
		</repository>
	</repositories>
</project>

    2.2、流程定义

    这里使用eclipse的activiti designer插件,插件安装方法另见:Eclipse安装Activiti Designer插件

    如我所愿,这里以请假流程,使用流程设计器得到如下流程定义:

    流程定义

    这个流程相当简单。下面是定义的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: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="My process" isExecutable="true">
    <extensionElements>
      <activiti:executionListener event="end" class="com.anxpp.demo.activiti.simple.listener.SimpleProcessEndListener"></activiti:executionListener>
    </extensionElements>
    <startEvent id="startevent_simple" name="Start"></startEvent>
    <userTask id="usertask1" name="领导审批">
      <extensionElements>
        <activiti:taskListener event="create" class="com.anxpp.demo.activiti.simple.listener.LeaderCheckListener"></activiti:taskListener>
      </extensionElements>
    </userTask>
    <endEvent id="endevent_simple" name="End"></endEvent>
    <sequenceFlow id="flow_toCheck" sourceRef="startevent_simple" targetRef="usertask_leadercheck"></sequenceFlow>
    <sequenceFlow id="flow_toEnd" sourceRef="usertask_leadercheck" targetRef="endevent_simple"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
    <bpmndi:BPMNPlane bpmnElement="myProcess" id="BPMNPlane_myProcess">
      <bpmndi:BPMNShape bpmnElement="startevent_simple" id="BPMNShape_startevent_simple">
        <omgdc:Bounds height="35.0" width="35.0" x="170.0" y="290.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="55.0" width="105.0" x="290.0" y="280.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent_simple" id="BPMNShape_endevent_simple">
        <omgdc:Bounds height="35.0" width="35.0" x="500.0" y="290.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

    里面包含一个用户任务、一个流程监听器(流程结束后回写业务数据状态)、一个任务监听器(以为审批是由申请员工对应部门的领导审核的,使用监听器可以灵活的设置任务审批候选人)。

    2.3、监听器

    监听器分任务监听器和流程监听器。

    任务监听器

/**
 * 领导审核监听器
 * @author anxpp.com
 * 2016年12月24日 下午12:10:01
 */
public class LeaderCheckListener implements TaskListener{
	private static final long serialVersionUID = 4285398130708457006L;
	private final static Logger log = LoggerFactory.getLogger(LeaderCheckListener.class);
	@Override
	public void notify(DelegateTask task) {
		log.info("领导审核监听器...");
		//设置任务处理候选人
		UserService userService = SpringUtil.getBean(UserService.class);
		List<String> leaders = userService.getSimpleCheckerByDept(Long.valueOf(task.getVariable("dept").toString()));
		log.info(leaders.toString());
		log.info(task.getVariable("dept").toString());
		task.addCandidateUsers(leaders);
	}
}

 流程监听器(此处并无实际代码,只给写法):

/**
 * 流程监听器
 * @author anxpp.com
 * 2016年12月24日 下午12:33:58
 */
public class SimpleProcessEndListener implements ExecutionListener{
	private static final long serialVersionUID = 5212042435691138021L;
	private final static Logger log = LoggerFactory.getLogger(SimpleProcessEndListener.class);
	@Override
	public void notify(DelegateExecution arg0) throws Exception {
		log.info("流程结束监听器...");
		//TODO 修改业务数据状态
	}
}

    单元测试

package com.anxpp.demo.activiti;
import java.util.Iterator;
import java.util.List;
import org.activiti.engine.task.Task;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.anxpp.demo.activiti.core.entity.User;
import com.anxpp.demo.activiti.core.service.UserService;
import com.anxpp.demo.activiti.simple.Config.Constant;
import com.anxpp.demo.activiti.simple.core.entity.ApplySimple;
import com.anxpp.demo.activiti.simple.core.service.ApplySimpleService;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ActivitiDemoApplicationTests {
	private final static Logger log = LoggerFactory.getLogger(ActivitiDemoApplicationTests.class);
	private static Long DEPT_TINY_SOFTWARE_STUDIO = 1L;
	private static Long DEPT_OTHER = 2L;
	@Autowired
	UserService userService;
	@Autowired
	ApplySimpleService simpleService;
	/**
	 * 测试程序启动
	 */
	@Test
	public void contextLoads() {
	}
	/**
	 * 测试数据库操作
	 */
	@Test
	public void testCURD(){
		Long countUser = userService.countUser();
		User user = new User();
		user.setName("anxpp0");
		user.setDept(DEPT_TINY_SOFTWARE_STUDIO);
		userService.save(user);
		Assert.assertEquals(new Long(countUser+1), userService.countUser());
	}
	/**
	 * 测试简单流程
	 */
	@Test
	public void testSimpleActiviti(){
		long startAt = System.currentTimeMillis();
		//添加申请用户
		User user = new User();
		user.setName("anxpp");
		user.setDept(DEPT_TINY_SOFTWARE_STUDIO);
		user.setPosition(Constant.POSITION_GENERAL);
		userService.save(user);
		//添加审核用户
		User userLeader1 = new User();
		userLeader1.setName("anxpp1");
		userLeader1.setDept(DEPT_TINY_SOFTWARE_STUDIO);
		userLeader1.setPosition(Constant.POSITION_LEADER);
		userService.save(userLeader1);
		User userLeader2 = new User();
		userLeader2.setName("anxpp2");
		userLeader2.setDept(DEPT_OTHER);
		userLeader2.setPosition(Constant.POSITION_LEADER);
		userService.save(userLeader2);
		Long countHis = simpleService.countProcess();
		Long countLeader1Task = simpleService.countTask(userLeader1.getId());
		Long countLeader2Task = simpleService.countTask(userLeader2.getId());
		//创建请假申请
		ApplySimple applySimple = new ApplySimple();
		applySimple.setInsertBy(user.getId());
		applySimple.setComtent("有事请假...");
		//启动请假流程
		simpleService.startProcess(applySimple);
		/**断言历史流程+1*/
		Assert.assertEquals(simpleService.countProcess(), new Long(countHis+1));
		/**断言领导任务变化*/
		Assert.assertEquals(simpleService.countTask(userLeader1.getId()), new Long(countLeader1Task+1));
		Assert.assertEquals(countLeader2Task, simpleService.countTask(userLeader2.getId()));
		//获取用户任务
		List<Task> taskUserLeader1 = simpleService.getTaskByUid(userLeader1.getId());
		//处理任务
		Iterator<Task> iterator = taskUserLeader1.iterator();
		while(iterator.hasNext()){
			Task task = iterator.next();
			/**断言任务节点名称*/
			Assert.assertEquals("领导审批", task.getName());
			simpleService.completeSimpleCheck(task.getId(), ApplySimpleService.STATE_PASS);
		}
		/**断言领导任务变化*/
		Assert.assertEquals(countLeader1Task, simpleService.countTask(userLeader1.getId()));
		Assert.assertEquals(countLeader2Task, simpleService.countTask(userLeader2.getId()));
		System.err.println("asdf");
		log.info("测试完成,花费时间:"+(System.currentTimeMillis()-startAt));
	}
}

    运行单元测试,OK!通过!

    完整实例源码地址:https://github.com/anxpp/activitiSimpleDemo.git

猜你喜欢

转载自blog.csdn.net/anxpp/article/details/53912439