Activiti作为一个流行的开源工作流引擎,正在不断发展,其6.0版本以API形式提供服务,而之前版本基本都是要求我们的应用以JDK方式与其交互,只能将其携带到我们的应用中,而API方式则可以服务器独立运行方式,能够形成一个专网内工作流引擎资源共享的方式。
本篇activiti工作流基于5.17.0。
首发地址:
品茗IT-同步发布
品茗IT 提供在线支持:
一键快速构建Spring项目工具
一键快速构建SpringBoot项目工具
一键快速构建SpringCloud项目工具
一站式Springboot项目生成
Mysql一键生成Mybatis注解Mapper
如果大家正在寻找一个java的学习环境,或者在开发中遇到困难,可以加入我们的java学习圈,点击即可加入,共同学习,节约学习时间,减少很多在学习中遇到的难题。
本文假设你已经引入Spring必备的一切了,已经是个Spring项目了,如果不会搭建,可以打开这篇文章看一看《Spring和Spring Mvc 5整合详解》。
工作流的流转,也需要数据库的支持,这里选用myabtis,如果不会配置mybatis,可以打开这篇文章看一看《Spring和Mybatis整合详解》。
使用activiti前,首先要在数据库中将activiti需要的sql导入到数据库中。
先去官网:https://www.activiti.org/下载个activiti。
想使用sdk的方式流转工作流,则下载5.0的版本。
下载完成后,解压开,里面就有sql,按照要求导入sql即可。这里不多说,我的表有这些:
这里只贴出activiti的依赖,spring和mybatis的相关依赖就不贴出来了。
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-engineartifactId>
<version>${activiti.version}version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-springartifactId>
<version>${activiti.version}version>
dependency>
<dependency>
<groupId>org.activitigroupId>
<artifactId>activiti-restartifactId>
<version>${activiti.version}version>
dependency>
activiti的使用需要配置bpmn流程文件,和Spring若干bean。
spring-activiti.xml:
<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.bpmnvalue>
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自己的数据库表的增删改查。
下面是工作流流程配置文件,配置了一个流程,Start–》commit–》CustomerServiceApproval–》ManagerApproval–》End。
activiti/customProcess.bpmn:
<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 配置了一个监听器,监听流程流转。
流程监听器,我这里啥也不监听了,但是还是要写出来。
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: 让工作流流转起来。
我们定义一个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);
}
}
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:
ProductTask:
详细完整的实体及Mapper的sql,可以访问品茗IT-博客《Spring和Activiti工作流整合详解》进行查看
全部代码可以在Spring组件化构建https://www.pomit.cn/java/spring/spring.html中的Activiti组件中查看,并下载。
Spring组件化构建
SpringBoot组件化构建
SpringCloud服务化构建