基于 Spring Boot 2.x 使用 Activiti 创建一个简易的请假流程
文章目录
- 1 摘要
- 2 准备工作
- 3 请假流程图
- 3 核心代码
- 3.1 数据库表
- 3.2 流程管理工具类
- 3.3 流程的使用
- 4 推荐参考资料
- 5 Github 源码
1 摘要
工作流是需要和业务结合起来,才能够发挥其作用。本文将介绍基于 Spring Boot 2、Activiti 6 创建一个简易的请假流程。
2 准备工作
(1) Activiti 入门教程(官方示例) — 2020-07-21
(2) SpringBoot 2.x 快速集成 Activiti — 2020-07-21
(3) 绘制一个 Activiti BPMN 流程图 — 2020-07-22
(4) Activiti 核心 API 在 Spring Boot 2.x 中的简易使用教程 — 2020-7-23
3 请假流程图
流程图:
bpmn 文件:
./activiti-workflow/src/main/resources/processes/student_leave_2.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/processdef"><process id="student_leave_2" name="student_leave_2" isExecutable="true"><startEvent id="startEvent1" name="开始"></startEvent><userTask id="sid-486A7692-35E4-476A-94EF-3566C01B8F39" name="学生申请" activiti:assignee="${student}"><extensionElements><modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete></extensionElements></userTask><userTask id="sid-23DCF2CD-D1E0-4B66-9853-352B2E505782" name="班主任审批" activiti:assignee="${teacher}"><extensionElements><modeler:initiator-can-complete xmlns:modeler="http://activiti.com/modeler"><![CDATA[false]]></modeler:initiator-can-complete></extensionElements></userTask><endEvent id="sid-52FDCD77-D54C-4340-B624-93B92D941E77" name="结束"></endEvent><sequenceFlow id="sid-96C3385F-899C-4149-B695-CEF25F5213CB" sourceRef="startEvent1" targetRef="sid-486A7692-35E4-476A-94EF-3566C01B8F39"></sequenceFlow><sequenceFlow id="sid-5602EE93-61DC-4326-80A6-C76661889900" name="提交申请" sourceRef="sid-486A7692-35E4-476A-94EF-3566C01B8F39" targetRef="sid-23DCF2CD-D1E0-4B66-9853-352B2E505782"></sequenceFlow><sequenceFlow id="sid-EABF7DE5-1A32-4EFF-9627-9B5AB70D5E8A" name="同意" sourceRef="sid-23DCF2CD-D1E0-4B66-9853-352B2E505782" targetRef="sid-52FDCD77-D54C-4340-B624-93B92D941E77"></sequenceFlow></process></definitions>
3 核心代码
3.1 数据库表
学生请假流程的数据库表
./doc/sql/activiti_leave_create.sql
/*==============================================================*/
/* DBMS name: MySQL 5.0 */
/* Created on: 2020/7/13 16:53:12 */
/*==============================================================*/drop table if exists leave_info;/*==============================================================*/
/* Table: leave_info */
/*==============================================================*/
create table leave_info
(id varchar(40) not null comment '编号',student_name varchar(30) comment '学生姓名',student_id varchar(40) comment '学生编号',leave_reason varchar(100) comment '请假原因',leave_duration int comment '请假时长(单位:天)',primary key (id)
)
ENGINE = INNODB DEFAULT
CHARSET = UTF8;alter table leave_info comment '请假信息';
3.2 流程管理工具类
关于流程的部署与使用,作者的思路是这样的:对于一张流程图,在一个系统中只部署一次,后边每次都是启动这个流程的实例,这样可以避免重复部署同一个流程,在查询代理人流程任务时,也能避免冲突。流程引擎使用单例模式创建,因为创建流程引擎是一个耗时耗资源的重量级操作。
./activiti-workflow/src/main/java/com/ljq/demo/springboot/activiti/common/util/ActivitiManager.java
package com.ljq.demo.springboot.activiti.common.util;import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollectionUtil;
import com.ljq.demo.springboot.activiti.dao.ActivitiDeployDao;
import com.ljq.demo.springboot.activiti.model.vo.HistoricTaskInstanceVO;
import com.ljq.demo.springboot.activiti.model.vo.TaskVO;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task;import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** @Description: Activiti 流程管理* @Author: junqiang.lu* @Date: 2020/7/9*/
public class ActivitiManager {private static volatile ProcessEngine processEngine;/*** 启动流程** @param processFile* @param processKey* @param deployDao* @param businessKey* @param variable* @return*/public static ProcessInstance startProcess(String processFile, String processKey, ActivitiDeployDao deployDao,String businessKey,Map<String, Object> variable) {// 判断是否部署过int count = deployDao.checkDeployed(processFile);if (count < 1) {deployProcess(processFile);}// 初始化流程引擎,并且启动流程ProcessInstance instance = init().getRuntimeService().startProcessInstanceByKey(processKey,businessKey,variable);return instance;}/*** 查询单个流程的历史记录** @param processKey* @param businessKey* @return*/public static List<HistoricTaskInstanceVO> queryHistoryTask(String processKey, String businessKey) {List<HistoricTaskInstance> historicTaskInstanceList = init().getHistoryService().createHistoricTaskInstanceQuery().processDefinitionKey(processKey).processInstanceBusinessKey(businessKey).list();List<HistoricTaskInstanceVO> taskInstanceVOList = new ArrayList<>();historicTaskInstanceList.stream().forEach(taskInstance -> {HistoricTaskInstanceVO taskInstanceVO = new HistoricTaskInstanceVO();BeanUtil.copyProperties(taskInstance, taskInstanceVO, CopyOptions.create().ignoreError().ignoreNullValue());List<Comment> commentList = init().getTaskService().getTaskComments(taskInstance.getId());if (CollectionUtil.isNotEmpty(commentList)) {taskInstanceVO.setComment(commentList.get(0).getFullMessage());}taskInstanceVOList.add(taskInstanceVO);});return taskInstanceVOList;}/*** 查询流程实例** @param processKey* @param businessKey* @return*/public static ProcessInstance queryProcessInstance(String processKey,String businessKey) {ProcessInstance processInstance = init().getRuntimeService().createProcessInstanceQuery().processDefinitionKey(processKey).processInstanceBusinessKey(businessKey).singleResult();return processInstance;}/*** 查询当前代理人在当前流程中待办理任务(列表)** @param processKey* @param assignee* @return*/public static List<TaskVO> queryTaskList(String processKey, String assignee) {List<Task> taskList = init().getTaskService().createTaskQuery().processDefinitionKey(processKey).taskAssignee(assignee).list();List<TaskVO> taskVOList = new ArrayList<>();taskList.stream().forEach(task -> {TaskVO taskVO = new TaskVO();BeanUtil.copyProperties(task, taskVO);taskVOList.add(taskVO);});return taskVOList;}/*** 完成当前节点任务** @param taskId* @param processInstanceId* @param comment*/public static void complete(String taskId, String processInstanceId, String comment) {TaskService taskService = init().getTaskService();taskService.addComment(taskId, processInstanceId, comment);taskService.complete(taskId);}/*** 删除流程实例(包括流程历史)** @param processInstanceId* @param deleteReason*/public static void deleteProcessInstance(String processInstanceId, String deleteReason) {init().getRuntimeService().deleteProcessInstance(processInstanceId, deleteReason);}/*** 初始化*/private static ProcessEngine init() {if (processEngine == null) {synchronized (ActivitiManager.class) {if (processEngine == null) {processEngine = ProcessEngines.getDefaultProcessEngine();System.out.println("初始化流程引擎");}}}return processEngine;}/*** 部署流程** @param processFile* @return*/private static Deployment deployProcess(String processFile) {Deployment deployment = init().getRepositoryService().createDeployment().addClasspathResource(processFile).deploy();return deployment;}}
3.3 流程的使用
流程的使用主要是在业务层(Service 层)
学生请假流程的业务层实现类:
./activiti-workflow/src/main/java/com/ljq/demo/springboot/activiti/service/impl/LeaveInfoServiceImpl.java
(1) 学生发起一个请假申请(启动流程)
/*** 新增(单条)** @param leaveInfoAddParam* @return* @throws Exception*/@Override@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})public ApiResult add(LeaveInfoAddParam leaveInfoAddParam) throws Exception {// 请求参数获取LeaveInfoEntity leaveInfoParam = new LeaveInfoEntity();BeanUtil.copyProperties(leaveInfoAddParam, leaveInfoParam, CopyOptions.create().ignoreNullValue().ignoreError());String id = IDGenerator.getID();leaveInfoParam.setId(id);// 保存leaveInfoDao.save(leaveInfoParam);// 指定节点办理人Map<String, Object> variable = new HashMap<>(16);variable.put("student", leaveInfoAddParam.getStudentId());variable.put("teacher", leaveInfoAddParam.getTeacherId());variable.put("teacherName", "张三丰");ProcessInstance instance = ActivitiManager.startProcess(ActivitiConst.STUDENT_LEAVE_PROCESS_FILE,ActivitiConst.STUDENT_LEAVE_PROCESS_KEY, deployDao, id, variable);return ApiResult.success(id);}
(2) 查询请假详情(请假流程历史记录)
/*** 查询详情(单条)** @param leaveInfoInfoParam* @return* @throws Exception*/@Overridepublic ApiResult info(LeaveInfoInfoParam leaveInfoInfoParam) throws Exception {// 查询LeaveInfoEntity leaveInfoDB = leaveInfoDao.queryObject(leaveInfoInfoParam.getId());if (Objects.isNull(leaveInfoDB)) {return ApiResult.success();}List<HistoricTaskInstanceVO> historicTaskInstanceVOList = ActivitiManager.queryHistoryTask(ActivitiConst.STUDENT_LEAVE_PROCESS_KEY, leaveInfoDB.getId());Map<String, Object> extraDataMap = new HashMap<>(16);extraDataMap.put("history", historicTaskInstanceVOList);return ApiResult.success(leaveInfoDB, extraDataMap);}
(3) 查询待审批列表(根据代理人查询待执行流程任务)
/*** 查询待审批列表** @param jobListParam* @return* @throws Exception*/@Overridepublic ApiResult jobList(LeaveInfoJobListParam jobListParam) throws Exception {List<TaskVO> taskVOList = ActivitiManager.queryTaskList(ActivitiConst.STUDENT_LEAVE_PROCESS_KEY, jobListParam.getUserId());return ApiResult.success(taskVOList);}
(4) 完成审批(完成任务节点)
根据不同的用户,完成任务的节点也不相同,对于学生而言,完成审批即为「提交请假请求」,对于老师而言,即为「审批通过」
/*** 完成审批** @param approvalParam* @return* @throws Exception*/@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})@Overridepublic ApiResult approval(LeaveInfoApprovalParam approvalParam) throws Exception {List<TaskVO> taskVOList = ActivitiManager.queryTaskList(ActivitiConst.STUDENT_LEAVE_PROCESS_KEY,approvalParam.getUserId());if (taskVOList.stream().filter(task -> Objects.equals(task.getId(), approvalParam.getTaskId())).findFirst().isPresent()) {ActivitiManager.complete(approvalParam.getTaskId(), approvalParam.getProcessInstanceId(),approvalParam.getComment());} else {return ApiResult.failure(ResponseCode.LEAVE_INFO_WORKFLOW_TASK_NOT_EXIST);}return ApiResult.success();}
(5) 删除请假信息(删除流程实例,包含历史记录)
/*** 删除(单条)** @param leaveInfoDeleteParam* @return* @throws Exception*/@Override@Transactional(propagation = Propagation.REQUIRED, rollbackFor = {Exception.class})public ApiResult delete(LeaveInfoDeleteParam leaveInfoDeleteParam) throws Exception {LeaveInfoEntity leaveInfoDB = leaveInfoDao.queryObject(leaveInfoDeleteParam.getId());if (Objects.isNull(leaveInfoDB) || !Objects.equals(leaveInfoDB.getStudentId(),leaveInfoDeleteParam.getUserId())) {return ApiResult.failure(ResponseCode.LEAVE_INFO_NOT_EXIST);}// 查询请假流程实例ProcessInstance processInstance = ActivitiManager.queryProcessInstance(ActivitiConst.STUDENT_LEAVE_PROCESS_KEY,leaveInfoDeleteParam.getId());// 删除请假流程实例ActivitiManager.deleteProcessInstance(processInstance.getId(), leaveInfoDeleteParam.getDeleteReason());// 删除请假信息leaveInfoDao.delete(leaveInfoDB);return ApiResult.success();}
至此,一个简单的请假流程已经实现了。更多的使用细节,可参考项目源码。
4 推荐参考资料
springboot2.04与activiti 6.0集成
SpringBoot2集成Activiti6
SpringBoot activiti 系列教程
Activiti工作流实战开发
Activiti第一篇【介绍、配置开发环境、快速入门】
Activiti5 学习笔记(八)—— comment 批注
Activiti学习(四)——流程变量的设置和获取
Activiti 删除部署与流程实例
5 Github 源码
Gtihub 源码地址 : https://github.com/Flying9001/springBootDemo
个人公众号:404Code,分享半个互联网人的技术与思考,感兴趣的可以关注.
基于 Spring Boot 2.x 使用 Activiti 创建一个简易的请假流程相关推荐
- 基于Spring Boot的桥牌计分系统
桥牌是一种非常受欢迎的纸牌游戏,因其策略性强.有趣而备受喜爱.但是,计算桥牌比赛分数是一个十分繁琐的过程,需要考虑多种因素,例如合约.支配点数等等.因此,我们开发了一个基于Spring Boot的桥牌 ...
- Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台项目
项目介绍 Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台 基于 Layui 的后台管理系统模板,扩展 Layui 原生 U ...
- 基于Spring Boot+Cloud构建微云架构
链接:my.oschina.net/u/3636867/blog/1802517 前言 首先,最想说的是,当你要学习一套最新的技术时,官网的英文文档是学习的最佳渠道.因为网上流传的多数资料是官网翻译而 ...
- 基于Spring Boot和Spring Cloud实现微服务架构学习--转
原文地址:http://blog.csdn.net/enweitech/article/details/52582918 看了几周spring相关框架的书籍和官方demo,是时候开始总结下这中间的学习 ...
- Spring Boot2.x-10 基于Spring Boot 2.1.2 + Mybatis 2.0.0实现多数据源,支持事务
文章目录 概述 思路 步骤 Step1 多数据源配置文件applicaiton.yml Step2 初始化多个数据源 Step3 配置多个数据源 验证测试 支持事务 Step1 配置类中通过@Bean ...
- 基于 Spring Boot 和 Spring Cloud 实现微服务架构
前言 首先,最想说的是,当你要学习一套最新的技术时,官网的英文文档是学习的最佳渠道.因为网上流传的多数资料是官网翻译而来,很多描述的重点也都偏向于作者自身碰到的问题,这样就很容易让你理解和操作出现偏差 ...
- c# 基于layui的通用后台管理系统_基于spring boot和vuejs的通用后台管理系统脚手架 guns-lite...
Guns-lite 前言 guns-lite是在guns的基础上将数据库层由mybatis替换为spring data jpa的系统. guns-lite是一个基于spring boot的后台管理系统 ...
- 基于Spring Boot和Spring Cloud实现微服务架构学习
目录 Spring 顶级框架 Spring cloud子项目 WHAT - 什么是微服务 微服务简介 微服务的具体特征 SOA vs Microservice HOW - 怎么具体实践微服务 客户端如 ...
- exchange邮件服务器_基于Spring Boot快速实现发送邮件功能
邮件在项目中经常会被用到,比如用邮件发送通知.比如,通过邮件注册.认证.找回密码.系统报警通知.报表信息等.本篇文章带大家通过SpringBoot快速实现一个发送邮件的功能. 邮件协议 下面先简单了解 ...
最新文章
- ASP.NET mvc 自定义验证和Filter过滤器传参
- rest 接口怎么传list_如何设计一个优雅的RESTFUL的接口
- sql server set赋值和select 赋值的区别以及使用方法
- Yarn取代job/task tracker
- 复现经典:《统计学习方法》第14章 聚类方法
- Bean的依赖注入概念
- 算法—快速排序Sqrt (C语言)
- mysql insert 不需要日志_MySQL数据库性能优化(1)「转」
- zabbix-自定义监控项
- 通过密钥 SFTP(二)限定用户根目录
- linux下socket调试,linux下socket调试
- .NET Reflector + ILSpy 反编译过程
- 山东航天九通车联网:大数据技术引领为智慧物流赋能
- 【小y设计】二维码条形码打印编辑器
- java cloassLoader大仙儿
- go语言 格式化输出fmt.Printf()使用大全
- 牛逼,Python3竟然内置找茬神器!一起来找茬吧
- 生成符合SCI论文投稿要求的高清图方法
- 从单体结构到微服务架构的转变,微服务入门
- nginx -s reload原理
热门文章
- 哈工大网络安全实验四报告
- 软件安全设计(威胁建模实现)
- android 360短信拦截短信验证码,莫名收到短信验证码?360借条安全专家提醒小心新骗局...
- java根据坐标在PDF指定位置添加文本
- 赫兹的单位换算_赫兹是什么单位名称(赫兹单位名称及换算方法)
- Xmos startkit 开篇-硬件介绍
- 缺失MSVCP120D.dll和MSVCR120D.dll
- java截取字符串中间的某个部分
- 单极化天线和双极化天线的区别
- 2021BTAJ面试真题详解,kafka查看topic数据内容