在 Spring Boot 项目中使用 activiti
新建springBoot项目时勾选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数组,表示描述文件的默认后缀名,默认以上两种。
springMVC配置:
- 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的服务组件。
以以下请假流程为例:
1. 开始流程并“申请请假”(员工)
- 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;
- }
在此方法中,Vaction 是申请时的具体信息,在完成“申请请假”任务时,可以将这些信息设置成参数。
2. 审批请假(老板)
(1)查询需要自己审批的请假
- public Object myAudit(String userName) {
- List<Task> taskList = taskService.createTaskQuery().taskCandidateUser(userName)
- .orderByTaskCreateTime().desc().list();
- // / 多此一举 taskList中包含了以下内容(用户的任务中包含了所在用户组的任务)
- // Group group = identityService.createGroupQuery().groupMember(userName).singleResult();
- // List<Task> list = taskService.createTaskQuery().taskCandidateGroup(group.getId()).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 ...
- }
老板查询自己当前需要审批的任务,并且将任务和参数设置到一个VacTask对象,用于页面的展示。
(2)审批请假
- 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的值来跳转到不同的任务)
3. 查询记录
由于已完成的请假在数据库runtime表中查不到(runtime表只保存正在进行的流程示例信息),所以需要在history表中查询。
(1) 查询请假记录
- 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对象中去。
4. 前端展示和操作
(1)审批列表和审批操作示例
- <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("操作失败!");
- }
- })
- }
- });
本人免费整理了Java高级资料,涵盖了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高并发分布式等教程,一共30G,需要自己领取。
传送门:https://mp.weixin.qq.com/s/igMojff-bbmQ6irCGO3mqA
在 Spring Boot 项目中使用 activiti的更多相关文章
- 你真的理解 Spring Boot 项目中的 parent 吗?
前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId ...
- Spring Boot项目中使用Swagger2
Swagger2是一款restful接口文档在线生成和在线接口调试工具,Swagger2在Swagger1.x版本的基础上做了些改进,下面是在一个Spring Boot项目中引入Swagger2的简要 ...
- 在Spring Boot项目中使用Spock框架
转载:https://www.jianshu.com/p/f1e354d382cd Spock框架是基于Groovy语言的测试框架,Groovy与Java具备良好的互操作性,因此可以在Spring B ...
- Spring Boot2 系列教程(三)理解 Spring Boot 项目中的 parent
前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId ...
- Spring Boot项目中使用Mockito
本文首发于个人网站:Spring Boot项目中使用Mockito Spring Boot可以和大部分流行的测试框架协同工作:通过Spring JUnit创建单元测试:生成测试数据初始化数据库用于测试 ...
- 在Spring Boot项目中使用Spock测试框架
本文首发于个人网站:在Spring Boot项目中使用Spock测试框架 Spock框架是基于Groovy语言的测试框架,Groovy与Java具备良好的互操作性,因此可以在Spring Boot项目 ...
- Spring Boot项目中如何定制拦截器
本文首发于个人网站:Spring Boot项目中如何定制拦截器 Servlet 过滤器属于Servlet API,和Spring关系不大.除了使用过滤器包装web请求,Spring MVC还提供Han ...
- Spring Boot项目中如何定制PropertyEditors
本文首发于个人网站:Spring Boot项目中如何定制PropertyEditors 在Spring Boot: 定制HTTP消息转换器一文中我们学习了如何配置消息转换器用于HTTP请求和响应数据, ...
- Spring Boot项目中如何定制servlet-filters
本文首发于个人网站:Spring Boot项目中如何定制servlet-filters 在实际的web应用程序中,经常需要在请求(request)外面增加包装用于:记录调用日志.排除有XSS威胁的字符 ...
随机推荐
- 2019-9-9:渗透测试,基础学习,phpmyadmin getshell方法,基于时间的盲注,基于报错的注入,笔记
phpmyadmin getshell方法1,查看是否有导入导出设置 show global variables like '%secure-file-priv%';2,如果secure-file-p ...
- linuxLVM
一.概念性的东西 LVM2:Logical Volume Manager ,Cersion 2 LVM,依赖于内核的dm模块(将一个或多个底层的设备组织成一个逻辑设备的模块).可以将多个物理分区通过软 ...
- pyspark报错Exception: Java gateway process exited before sending its port number解决方法
1.问题 搭建spark的python环境好后简单使用,源代码如下: 然后就给我丢了一堆错误: 2.解决办法 这里指定一下Java的环境就可以了,添加代码: import os os.environ[ ...
- 不用任何第三方,写一个RTMP直播推流器
2016年是移动直播爆发年,不到半年的时间内无数移动直播App掀起了全民直播的热潮.然而个人觉得直播的门槛相对较高,从推流端到服务端器到播放端,无不需要专业的技术来支撑,仅仅推流端就有不少需要学习的知 ...
- Netty源码分析之ChannelPipeline(二)—ChannelHandler的添加与删除
上篇文章中,我们对Netty中ChannelPipeline的构造与初始化进行了分析与总结,本篇文章我们将对ChannelHandler的添加与删除操作进行具体的的代码分析: 一.ChannelHan ...
- 2019-2020-3 20199317《Linux内核原理与分析》第三周作业
第2章 操作系统是如何工作的 1 计算机的三大法宝 存储程序计算机:冯诺依曼结构 函数调用堆栈机制:记录调用的路径和参数的空间 中断机制:由CPU和内核代码共同实现了保存现场和恢复现场, ...
- 启动项目报错:org.apache.catalina.LifecycleException: Failed to start component
原因 环境异常重启,项目java进程未关闭,原项目的端口依旧在占用. 一般为8080端口被占用 解决方法 以下两种方法都可以解决,原理相同(结束异常进程) 1. 简单粗暴: 打开任务管理器找到java ...
- 微信小程序——页面栈
刚开始用小程序的时候没怎么在意页面的跳转,也没仔细看文档中说的页面栈的内容.只要能跳转就行,wx.navigateTo,wx.redirectTo 这些方法一顿乱用.最后在做一个十层页面(以前页面栈是 ...
- DS1302时钟芯片驱动程序
/***************************************************************************** FileName : DS1302.c F ...
- java内存模型和内存结构
java内存模型说的是多线程,网上可能会有写误导,并不是什么堆.栈.方法区,很多人都会搞混.说白了就是多线程中主线程和本地线程之间的一个数据可见性问题. jmm:java内存模型:jvm:java内存 ...