专栏首页品茗ITSpring和Activiti工作流整合详解

Spring和Activiti工作流整合详解

Spring和Activiti工作流整合详解

一、概述

Activiti作为一个流行的开源工作流引擎,正在不断发展,其6.0版本以API形式提供服务,而之前版本基本都是要求我们的应用以JDK方式与其交互,只能将其携带到我们的应用中,而API方式则可以服务器独立运行方式,能够形成一个专网内工作流引擎资源共享的方式。

本篇activiti工作流基于5.17.0。

**如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以<a

href="https://jq.qq.com/?_wv=1027&k=52sgH1J"

target="_blank">

加入我们的java学习圈,点击即可加入

</a>

,共同学习,节约学习时间,减少很多在学习中遇到的难题。**

二、环境配置

本文假设你已经引入Spring必备的一切了,已经是个Spring项目了,如果不会搭建,可以打开这篇文章看一看《Spring和Spring Mvc 5整合详解》

工作流的流转,也需要数据库的支持,这里选用myabtis,如果不会配置mybatis,可以打开这篇文章看一看《Spring和Mybatis整合详解》

2.1 Activiti的数据库表

使用activiti前,首先要在数据库中将activiti需要的sql导入到数据库中。

先去官网:https://www.activiti.org/下载个activiti。

在这里插入图片描述

想使用sdk的方式流转工作流,则下载5.0的版本。

下载完成后,解压开,里面就有sql,按照要求导入sql即可。这里不多说,我的表有这些:

在这里插入图片描述

2.2 maven依赖

这里只贴出activiti的依赖,spring和mybatis的相关依赖就不贴出来了。

<dependency>
	<groupId>org.activiti</groupId>
	<artifactId>activiti-engine</artifactId>
	<version>${activiti.version}</version>
</dependency>
<dependency>
	<groupId>org.activiti</groupId>
	<artifactId>activiti-spring</artifactId>
	<version>${activiti.version}</version>
</dependency>
<dependency>
	<groupId>org.activiti</groupId>
	<artifactId>activiti-rest</artifactId>
	<version>${activiti.version}</version>
</dependency>

2.3 Spring配置

activiti的使用需要配置bpmn流程文件,和Spring若干bean。

spring-activiti.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

	<import resource="classpath*:spring-mybatis.xml"/>

    <bean id="processEngineConfiguration" class="org.activiti.spring.SpringProcessEngineConfiguration">
        <property name="dataSource" ref="dataSource" />
        <property name="transactionManager" ref="transactionManager" />
        <property name="databaseSchemaUpdate" value="true" />
        <property name="jobExecutorActivate" value="true" />
        <property name="deploymentResources">
            <list>
                <value>classpath*:activiti/customProcess.bpmn</value>
            </list>
        </property>
        <property name="activityFontName" value="宋体"/>  
        <property name="labelFontName" value="宋体"/>  
    </bean>
    <bean id="processEngine" class="org.activiti.spring.ProcessEngineFactoryBean">
        <property name="processEngineConfiguration" ref="processEngineConfiguration" />
    </bean>
    <bean id="repositoryService" factory-bean="processEngine"
        factory-method="getRepositoryService" />
    <bean id="runtimeService" factory-bean="processEngine"
        factory-method="getRuntimeService" />
    <bean id="taskService" factory-bean="processEngine"
        factory-method="getTaskService" />
    <bean id="historyService" factory-bean="processEngine"
        factory-method="getHistoryService" />
    <bean id="managementService" factory-bean="processEngine"
        factory-method="getManagementService" />
    <bean id="IdentityService" factory-bean="processEngine"
        factory-method="getIdentityService" />

</beans>

这里,processEngineConfiguration指向bpmn,并指明数据源。

processEngine为工作流引擎声明。

repositoryService、runtimeService、taskService、historyService、managementService、IdentityService都是对activiti自己的数据库表的增删改查。

2.4 Activiti流程配置

下面是工作流流程配置文件,配置了一个流程,Start--》commit--》CustomerServiceApproval--》ManagerApproval--》End。

activiti/customProcess.bpmn:

<?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:xsd="http://www.w3.org/2001/XMLSchema" 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="productAdvice" name="product Advice" isExecutable="true">
    <startEvent id="startevent1" name="Start"></startEvent>
    <endEvent id="endevent1" name="End"></endEvent>
    <userTask id="usertask1" name="commit"></userTask>
    <userTask id="usertask2" name="CustomerServiceApproval"></userTask>
    <userTask id="usertask3" name="ManagerApproval"></userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="usertask1">
      <extensionElements>
        <activiti:executionListener event="create" class="cn.pomit.springwork.activiti.listener.CommitExecutionListener"></activiti:executionListener>
      </extensionElements>
    </sequenceFlow>
    <sequenceFlow id="flow2" sourceRef="usertask1" targetRef="usertask2"></sequenceFlow>
    <sequenceFlow id="flow3" sourceRef="usertask2" targetRef="usertask3"></sequenceFlow>
    <sequenceFlow id="flow4" sourceRef="usertask3" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_productAdvice">
    <bpmndi:BPMNPlane bpmnElement="productAdvice" id="BPMNPlane_productAdvice">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="330.0" y="20.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent1" id="BPMNShape_endevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="330.0" y="330.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="55.0" width="105.0" x="295.0" y="80.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask2" id="BPMNShape_usertask2">
        <omgdc:Bounds height="81.0" width="105.0" x="295.0" y="160.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask3" id="BPMNShape_usertask3">
        <omgdc:Bounds height="55.0" width="105.0" x="295.0" y="260.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="347.0" y="55.0"></omgdi:waypoint>
        <omgdi:waypoint x="347.0" y="80.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="347.0" y="135.0"></omgdi:waypoint>
        <omgdi:waypoint x="347.0" y="160.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="347.0" y="241.0"></omgdi:waypoint>
        <omgdi:waypoint x="347.0" y="260.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow4" id="BPMNEdge_flow4">
        <omgdi:waypoint x="347.0" y="315.0"></omgdi:waypoint>
        <omgdi:waypoint x="347.0" y="330.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

这里,activiti:executionListener 配置了一个监听器,监听流程流转。

三、Activiti工作流功能

流程监听器,我这里啥也不监听了,但是还是要写出来。

CommitExecutionListener:

package cn.pomit.springwork.activiti.listener;

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;

public class CommitExecutionListener implements ExecutionListener{

	/**
	 * 
	 */
	private static final long serialVersionUID = 6482750935517963649L;

	@Override
	public void notify(DelegateExecution execution) throws Exception {
		String eventName = execution.getEventName();
		System.out.println("BeforCommitExecutionListener:"+eventName);
		
		
	}

}

工作流处理过程的service:

ProductService:

package cn.pomit.springwork.activiti.service;

import java.util.ArrayList;
import java.util.List;

import org.activiti.engine.HistoryService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import cn.pomit.springwork.mybatis.domain.UserInfo;
import cn.pomit.springwork.mybatis.domain.activiti.ProductTask;
import cn.pomit.springwork.mybatis.mapper.activiti.ProductMapper;
import cn.pomit.springwork.mybatis.service.UserInfoService;

@Service
public class ProductService {
	protected Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	private RuntimeService runtimeService;

	@Autowired
	private TaskService taskService;

	@Autowired
	private RepositoryService repositoryService;

	@Autowired
	private HistoryService historyService;

	@Autowired
	ProductMapper productMapper;

	@Autowired
	UserInfoService appUserService;

	/**
	 * 产生工作流
	 * 
	 * @param userTask
	 * @param userid
	 */
	public void genTask(ProductTask userTask, String userid) {
		ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("productAdvice");
		Task tmp = taskService.createTaskQuery().processInstanceId(processInstance.getProcessInstanceId())
				.singleResult();

		userTask.setInstanceId(processInstance.getProcessInstanceId());
		tmp.setAssignee(userid);
		taskService.complete(tmp.getId());

		userTask.setUserName(userid);
		productMapper.save(userTask);
	}

	/**
	 * 获取提交的工作
	 * 
	 * @param userid
	 * @return
	 * @throws Exception
	 */
	public List<ProductTask> applyList(String userid) throws Exception {
		List<ProductTask> tasks = productMapper.getUserTask(userid);
		return tasks;
	}

	/**
	 * 获取待处理的工作
	 * 
	 * @param userid
	 * @return
	 * @throws Exception
	 */
	public List<ProductTask> waitList(String userid) throws Exception {
		String userType = findTaskType(userid);
		logger.info("准备查询userType为{}的任务", userType);
		List<Task> tasks = taskService.createTaskQuery().taskName(userType).orderByTaskCreateTime().asc().list();
		List<ProductTask> utasks = new ArrayList<ProductTask>();
		for (int i = 0; i < tasks.size(); i++) {
			ProductTask userTaskTmp = productMapper.getUserTaskByInstanceId(tasks.get(i).getProcessInstanceId());
			if (userTaskTmp != null) {
				userTaskTmp.setTaskId(tasks.get(i).getId());
				utasks.add(userTaskTmp);
			}
		}

		return utasks;
	}

	/**
	 * 工作流流转
	 * 
	 * @param taskid
	 * @param processid
	 * @param userid
	 * @return
	 * @throws Exception
	 */
	public synchronized Boolean processCommit(String taskid, String instanceId, String userid) throws Exception {
		logger.info("审批taskid:{},instanceId:{}", taskid, instanceId);
		try {
			ProductTask userTask = productMapper.getUserTaskByInstanceId(instanceId);
			taskService.complete(taskid);
			userTask.setCurviewer(userid);
			productMapper.updateStatus(userTask);
		} catch (Exception e) {
			return false;
		}
		return true;
	}

	/**
	 * 根据用户类型获取任务名称
	 * 
	 * @param userId
	 * @return
	 */
	public String findTaskType(String userId) {
		UserInfo appUser = appUserService.getUserInfoByUserName(userId);
		String userType = appUser.getUserType();
		if (userType == null)
			return null;
		if (!StringUtils.isEmpty(userType)) {
			if ("2001".equals(userType)) {
				return "CustomerServiceApproval";
			} else if ("0000".equals(userType)) {
				return "ManagerApproval";
			} else {
				return "commit";
			}
		}
		return null;
	}

	/**
	 * 获取处理过的任务
	 * 
	 * @param userid
	 * @return
	 */
	public List<ProductTask> manageList(String userid) {
		List<ProductTask> utasks = productMapper.getUserTaskByCurrentViwer(userid);

		return utasks;
	}
}

这个service中:

  • genTask 是工作流的开端,产生任务之后,即流转到下一个节点。
  • applyList,申请的任务列表。
  • waitList:待处理的任务。
  • processCommit: 让工作流流转起来。

四、测试Activiti工作流

我们定义一个web接口来做测试。

ActivitiRest:

package cn.pomit.springwork.activiti.web;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import cn.pomit.springwork.activiti.service.ProductService;
import cn.pomit.springwork.mybatis.domain.activiti.ProductTask;

@RestController
@RequestMapping("/activiti")
public class ActivitiRest {

	@Autowired
	ProductService productService;

	@RequestMapping(value = "/add/{name}")
	public String add(@RequestBody ProductTask productTask, @PathVariable("name") String name) {
		productService.genTask(productTask, name);
		return "Success";
	}

	@RequestMapping(value = "/applyList/{name}")
	public List<ProductTask> applyList(@PathVariable("name") String name) throws Exception {
		return productService.applyList(name);
	}

	@RequestMapping(value = "/waitList/{name}")
	public List<ProductTask> waitList(@PathVariable("name") String name) throws Exception {
		return productService.waitList(name);
	}

	@RequestMapping(value = "/next/{name}")
	public Boolean processCommit(@PathVariable("name") String name, @RequestParam("instanceId") String instanceId,
			@RequestParam("taskId") String taskId) throws Exception {
		return productService.processCommit(taskId, instanceId, name);
	}
}

五、过程中用到的其他数据库相关service、mapper、实体

ProductMapper :

package cn.pomit.springwork.mybatis.mapper.activiti;

import java.util.List;

import cn.pomit.springwork.mybatis.domain.activiti.ProductTask;

public interface ProductMapper {

	public void save(ProductTask userTask);
	
	public List<ProductTask> getUserTask(String userid);
	
	public ProductTask getUserTaskByInstanceId(String instanceId);
	
	public void updateStatus(ProductTask userTask);

	public List<ProductTask> getUserTaskByCurrentViwer(String userid);
}

ProductMapper 对应的mybatis-productTask.xml:

<?xml version="1.0" encoding="UTF-8" ?>  
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >  
<mapper namespace="cn.pomit.springwork.mybatis.mapper.activiti.ProductMapper">  
    <resultMap id="BaseResultMap" type="cn.pomit.springwork.mybatis.domain.activiti.ProductTask">  
        <result column="instance_id" property="instanceId" />  
        <result column="user_name" property="userName" /> 
        <result column="title" property="title" />
        <result column="task_type" property="taskType" />  
        <result column="content" property="content" />  
        <result column="curviewer" property="curviewer" />
    </resultMap>  
  	<sql id="Base_Column_List" >  
    	instance_id, user_name, title, task_type, content, curviewer
  	</sql>  
	<select id="getUserTask" resultMap="BaseResultMap" parameterType="java.lang.String" >  
	   select   
	   <include refid="Base_Column_List" />  
	   from se_product_task  
	   where user_name = #{userName,jdbcType=VARCHAR}  
	</select>  
	
	<select id="getUserTaskByInstanceId" resultMap="BaseResultMap" parameterType="java.lang.String" >  
	   select   
	   <include refid="Base_Column_List" />  
	   from se_product_task  
	   where instance_id = #{instanceId,jdbcType=VARCHAR}  
	</select>  
	
	<select id="getUserTaskByCurrentViwer" resultMap="BaseResultMap" parameterType="java.lang.String" >  
	   select   
	   <include refid="Base_Column_List" />  
	   from se_product_task  
	   where curviewer = #{curviewer,jdbcType=VARCHAR}  
	</select>   
	
	<insert id="save" parameterType="cn.pomit.springwork.mybatis.domain.activiti.ProductTask">
	  insert into se_product_task(instance_id, user_name, title, task_type, content, curviewer) 
	  		values(#{instanceId}, #{userName}, #{title}, #{taskType}, #{content}, #{curviewer})
	</insert>
	
	<update id="updateStatus" parameterType="cn.pomit.springwork.mybatis.domain.activiti.ProductTask">
		update se_product_task
		<set>
			<if test="curviewer != null">curviewer=#{curviewer}</if>
		</set>
		where instance_id=#{instanceId}
	</update>
</mapper>  

ProductTask:

package cn.pomit.springwork.mybatis.domain.activiti;

import java.io.Serializable;

public class ProductTask implements Serializable {
	/**
	 * 
	 */
	private static final long serialVersionUID = 3481818865839076537L;

	String instanceId;
	String userName;
	String taskType;
	String content;
	String title;
	String curviewer;
	String taskId;

	public String getInstanceId() {
		return instanceId;
	}

	public void setInstanceId(String instanceId) {
		this.instanceId = instanceId;
	}

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getTaskType() {
		return taskType;
	}

	public void setTaskType(String taskType) {
		this.taskType = taskType;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getCurviewer() {
		return curviewer;
	}

	public void setCurviewer(String curviewer) {
		this.curviewer = curviewer;
	}

	public String getTaskId() {
		return taskId;
	}

	public void setTaskId(String taskId) {
		this.taskId = taskId;
	}

	public static long getSerialversionuid() {
		return serialVersionUID;
	}

}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • SpringBoot入门建站全系列(十九)集成Activiti做工作流

    Activiti作为一个流行的开源工作流引擎,正在不断发展,其6.0版本以API形式提供服务,而之前版本基本都是要求我们的应用以JDK方式与其交互,只能将其携带...

    品茗IT
  • SpringCloud技术指南系列(三)服务注册发现之Eureka服务调用

    目前服务发现的解决方案有Eureka,Consul,Zookeeper等,这三个是SpringCloud官方支持的。

    品茗IT
  • SpringBoot入门建站全系列(十九)集成Activiti做工作流

    Activiti作为一个流行的开源工作流引擎,正在不断发展,其6.0版本以API形式提供服务,而之前版本基本都是要求我们的应用以JDK方式与其交互,只能将其携带...

    品茗IT
  • 揭秘前端精度丢失之谜!!!

    今天测试突然跟我说页面显示的数值和数据库的对应不上,一开始我以为是程序问题,把数据给修改了,但是后面检查程序发现,没有任务问题,用postman请求,发现数据也...

    林老师带你学编程
  • 华为春招实习记录

    思考了很久,接收了华为的实习意向书,还没有与HR接触过,也不知道什么时候会有后续的通知。 杭州,3.30面试,4.9号HR电话询问实习时间意向,4.17发的录...

    牛客网
  • 利用Chrome的Heap Snapshot功能分析一个时间段内的内存占用率

    在下图测试代码第13行和第16行设断点. 以调试方式运行,首先断点在第13行处触发:

    Jerry Wang
  • 设计模式学习笔记之工厂模式

    这是一篇学习笔记,内容很多是来源于网上的资料,然后按照自己学习情况进行的总结,有些是自身的感受,有些是网上比较好的资料的引用。 我的个人博客:海加尔金鹰

    海加尔金鹰
  • 前端不止:Web内容的无障碍性 | 洞见

    截至 2017 年 6 月,中国网民规模达 7.51 亿,中国手机网民规模达 7.24 亿, 中国网民中农村网民占比 26.7%,规模为 2.01 亿。

    ThoughtWorks
  • 16.FragmentTabHost用法

    六月的雨
  • Windows2008 IIS 7 运行wordpress媒体中心上传文件图片出现HTTP错误的解决办法

    服务器从windows2003升级到windows2008,原wordpress打开媒体中心上传图片或者文件出现了HTTP错误,如图:折腾了几天

    QQ1399937604

扫码关注云+社区

领取腾讯云代金券