来源 | www.jvm123.com
链接:
www.jvm123.com/2019/08/springboot-activiti/
新建springBoo t项目时勾选activiti,或者在已建立的springBoot项目添加以下依赖:
< dependency >
< groupId > org.activiti </ groupId >
< artifactId > activiti-spring-boot-starter-basic </ artifactId >
< version > 6.0.0 </ version >
</ dependency >
数据源和activiti配置:
server :
port : 8081
spring :
datasource :
url : jdbc : mysql : //localhost:3306/act5?useSSL=true
driver-class-name : com.mysql.jdbc.Driver
username : root
password : root
# activiti default configuration
activiti :
database-schema-update : true
check-process-definitions : true
process-definition-location-prefix : classpath :/processes/
# process-definition-location-suffixes :
# - **.bpmn
# - **.bpmn20.xml
history-level
: full
在activiti的默认配置中,process-definition-location-prefix 是指定activiti流程描述文件的前缀(即路径),启动时,activiti就会去寻找此路径下的流程描述文件,并且自动部署;suffix 是一个String数组,表示描述文件的默认后缀名,默认以上两种。
package com.yawn.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.HttpStatus;
import org.springframework.web.servlet.config.annotation.*;
/**
* Created by yawn on 2017/8/5.
@EnableWebMvc
@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addResourceHandlers (ResourceHandlerRegistry registry) {
registry.addResourceHandler( "/static/**" ).addResourceLocations( "classpath:/static/" );
registry.addResourceHandler( "/templates/**" ).addResourceLocations( "classpath:/templates/" );
super .addResourceHandlers(registry);
}
@Override
public void addViewControllers (ViewControllerRegistry registry) {
registry.addViewController( "/index" );
registry.addViewController( "/user" );
registry.addRedirectViewController( "/" , "/templates/login.html" );
// registry.addStatusController("/403", HttpStatus.FORBIDDEN);
super .addViewControllers(registry);
}
}
这里配置静态资源和直接访问的页面:在本示例项目中,添加了thymeleaf依赖解析视图,主要采用异步方式获取数据,通过angularJS进行前端数据的处理和展示。
配置了数据源和activiti后,启动项目,activiti 的各个服务组件就已经被加入到spring容器中了,所以就可以直接注入使用了。如果在未自动配置的spring环境中,可以使用通过指定bean的init-method来配置activiti的服务组件。
以以下请假流程为例:
private static final String PROCESS_DEFINE_KEY = "vacationProcess" ;
public Object startVac( String userName, Vacation vac) {
identityService.setAuthenticatedUserId(userName);
// 开始流程
ProcessInstance vacationInstance = runtimeService.startProcessInstanceByKey(PROCESS_DEFINE_KEY);
// 查询当前任务
Task currentTask = taskService.createTaskQuery().processInstanceId(vacationInstance.getId()).singleResult();
// 申明任务
taskService.claim(currentTask.getId(), userName);
Map< String , Object > vars = new HashMap<>( 4 );
vars.put( "applyUser" , userName);
vars.put( "days" , vac.getDays());
vars.put( "reason" , vac.getReason());
// 完成任务
taskService.complete(currentTask.getId(), vars);
return true ;
}
public Object myAudit( String userName) {
List<Task> taskList = taskService.createTaskQuery().taskCandidateUser(userName)
.orderByTaskCreateTime().desc().list();
// taskList.addAll(list);
List<VacTask> vacTaskList = new ArrayList<>();
for (Task task : taskList) {
VacTask vacTask = new VacTask();
vacTask.setId(task.getId());
vacTask.setName(task.getName());
vacTask.setCreateTime(task.getCreateTime());
String instanceId = task.getProcessInstanceId();
ProcessInstance instance = runtimeService.createProcessInstanceQuery().processInstanceId(instanceId).singleResult();
Vacation vac = getVac(instance);
vacTask.setVac(vac);
vacTaskList.add(vacTask);
}
return vacTaskList;
}
private Vacation getVac(ProcessInstance instance) {
Integer days = runtimeService.getVariable(instance.getId(), "days" , Integer.class);
String reason = runtimeService.getVariable(instance.getId(), "reason" , String .class);
Vacation vac = new Vacation();
vac.setApplyUser(instance.getStartUserId());
vac.setDays(days);
vac.setReason(reason);
Date startTime = instance.getStartTime(); // activiti 6 才有
vac.setApplyTime(startTime);
vac.setApplyStatus(instance.isEnded() ? "申请结束" : "等待审批" );
return vac;
}
package com.yawn.entity;
import java.util.Date;
/**
* @author Created by yawn on 2018-01-09 14:31
public class VacTask {
private String id;
private String name;
private Vacation vac;
private Date createTime;
// getter setter ...
}
public Object passAudit( String userName, VacTask vacTask) {
String taskId = vacTask.getId();
String result = vacTask.getVac().getResult();
Map< String , Object > vars = new HashMap<>();
vars.put( "result" , result);
vars.put( "auditor" , userName);
vars.put( "auditTime" , new Date ());
taskService.claim(taskId, userName);
taskService.complete(taskId, vars);
return true ;
}
同理,result是审批的结果,也是在完成审批任务时需要传入的参数;taskId是刚才老板查询到的当前需要自己完成的审批任务ID。(如果流程在这里设置分支,可以通过判断result的值来跳转到不同的任务)
由于已完成的请假在数据库runtime表中查不到(runtime表只保存正在进行的流程示例信息),所以需要在history表中查询。
public Object myVacRecord(String userName) {
List <HistoricProcessInstance> hisProInstance = historyService.createHistoricProcessInstanceQuery()
.processDefinitionKey(PROCESS_DEFINE_KEY).startedBy(userName).finished()
.orderByProcessInstanceEndTime().desc(). list ();
List <Vacation> vacList = new ArrayList<>();
for (HistoricProcessInstance hisInstance : hisProInstance) {
Vacation vacation = new Vacation();
vacation.setApplyUser(hisInstance.getStartUserId());
vacation.setApplyTime(hisInstance.getStartTime());
vacation.setApplyStatus( "申请结束" );
List <HistoricVariableInstance> varInstanceList = historyService.createHistoricVariableInstanceQuery()
.processInstanceId(hisInstance.getId()). list ();
ActivitiUtil.setVars(vacation, varInstanceList);
vacList.add(vacation);
}
return vacList;
}
请假记录即查出历史流程实例,再查出关联的历史参数,将历史流程实例和历史参数设置到Vcation对象(VO对象)中去,即可返回,用来展示。
package com.yawn.util;
import org.activiti.engine.history.HistoricVariableInstance;
import java.lang.reflect.Field;
import java.util.List;
/**
* activiti中使用得到的工具方法
* @author Created by yawn on 2018-01-10 16:32
public class ActivitiUtil {
/**
* 将历史参数列表设置到实体中去
* @param entity 实体
* @param varInstanceList 历史参数列表
public static <T> void setVars (T entity, List<HistoricVariableInstance> varInstanceList) {
Class<?> tClass = entity.getClass();
try {
for (HistoricVariableInstance varInstance : varInstanceList) {
Field field = tClass.getDeclaredField(varInstance.getVariableName());
if (field == null ) {
continue ;
}
field.setAccessible( true );
field.set(entity, varInstance.getValue());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
此外,以上是查询历史流程实例和历史参数后,设置VO对象的通用方法:可以根据参数列表中的参数,将与VO对象属性同名的参数设置到VO对象中去。
< div ng-controller = "myAudit" >
< h2 ng-init = "myAudit()" > 待我审核的请假 </ h2 >
< table border = "0" >
< tr >
< td > 任务名称 </ td >
< td > 任务时间 </ td >
< td > 申请人 </ td >
< td > 申请时间 </ td >
< td > 天数 </ td >
< td > 事由 </ td >
< td > 操作 </ td >
</ tr >
< tr ng-repeat = "vacTask in vacTaskList" >
< td > {{vacTask.name}} </ td >
< td > {{vacTask.createTime | date:'yyyy-MM-dd HH:mm:ss'}} </ td >
< td > {{vacTask.vac.applyUser}} </ td >
< td > {{vacTask.vac.applyTime | date:'yyyy-MM-dd HH:mm:ss'}} </ td >
< td > {{vacTask.vac.days}} </ td >
< td > {{vacTask.vac.reason}} </ td >
< td >
< button type = "button" ng-click = "passAudit(vacTask.id, 1)" > 审核通过 </ button >
< button type = "button" ng-click = "passAudit(vacTask.id, 0)" > 审核拒绝 </ button >
</ td >
</ tr >
</ table >
</ div >
app.controller("myAudit", function ($scope, $http, $window) {
$scope.vacTaskList = [];
$scope.myAudit = function () {
$http.get(
"/myAudit"
).then(function (response) {
$scope.vacTaskList = response.data;
})
};
$scope.passAudit = function (taskId, result) {
$http.post(
"/passAudit",
{
"id": taskId,
"vac": {
"result": result >= 1 ? "审核通过" : "审核拒绝"
}
}
).then(function (response) {
if (response.data === true) {
alert("操作成功!");
$window.location.reload();
} else {
alert("操作失败!");
}
})
}
});
以上是一个springBoot 与 activiti 6.0 整合的示例项目的部分代码与说明,完整的项目代码在:
https://gitee.com/yawensilence/activiti-demo6-springboot 。
原文请查看作者博客:
http://www.jvm123.com/2019/08/springboot-activiti/
- END -
如果看到这里,说明你喜欢这篇文章,请 转发 、点赞 。 扫描下方二维码 或者微信搜索「 perfect_iscas 」,添加好友后即可获得 10套程序员全栈课程+1000套PPT和简历模板 , 向我私聊「 进群 」二字即可进入高质量交流群。
↓ 扫描二维码进群↓
喜 欢 文 章 , 点 个 在看