文章目录

  • 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 创建一个简易的请假流程相关推荐

  1. 基于Spring Boot的桥牌计分系统

    桥牌是一种非常受欢迎的纸牌游戏,因其策略性强.有趣而备受喜爱.但是,计算桥牌比赛分数是一个十分繁琐的过程,需要考虑多种因素,例如合约.支配点数等等.因此,我们开发了一个基于Spring Boot的桥牌 ...

  2. Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台项目

    项目介绍 Spring Boot + Security + MyBatis + Thymeleaf + Activiti 快速开发平台 基于 Layui 的后台管理系统模板,扩展 Layui 原生 U ...

  3. 基于Spring Boot+Cloud构建微云架构

    链接:my.oschina.net/u/3636867/blog/1802517 前言 首先,最想说的是,当你要学习一套最新的技术时,官网的英文文档是学习的最佳渠道.因为网上流传的多数资料是官网翻译而 ...

  4. 基于Spring Boot和Spring Cloud实现微服务架构学习--转

    原文地址:http://blog.csdn.net/enweitech/article/details/52582918 看了几周spring相关框架的书籍和官方demo,是时候开始总结下这中间的学习 ...

  5. Spring Boot2.x-10 基于Spring Boot 2.1.2 + Mybatis 2.0.0实现多数据源,支持事务

    文章目录 概述 思路 步骤 Step1 多数据源配置文件applicaiton.yml Step2 初始化多个数据源 Step3 配置多个数据源 验证测试 支持事务 Step1 配置类中通过@Bean ...

  6. 基于 Spring Boot 和 Spring Cloud 实现微服务架构

    前言 首先,最想说的是,当你要学习一套最新的技术时,官网的英文文档是学习的最佳渠道.因为网上流传的多数资料是官网翻译而来,很多描述的重点也都偏向于作者自身碰到的问题,这样就很容易让你理解和操作出现偏差 ...

  7. c# 基于layui的通用后台管理系统_基于spring boot和vuejs的通用后台管理系统脚手架 guns-lite...

    Guns-lite 前言 guns-lite是在guns的基础上将数据库层由mybatis替换为spring data jpa的系统. guns-lite是一个基于spring boot的后台管理系统 ...

  8. 基于Spring Boot和Spring Cloud实现微服务架构学习

    目录 Spring 顶级框架 Spring cloud子项目 WHAT - 什么是微服务 微服务简介 微服务的具体特征 SOA vs Microservice HOW - 怎么具体实践微服务 客户端如 ...

  9. exchange邮件服务器_基于Spring Boot快速实现发送邮件功能

    邮件在项目中经常会被用到,比如用邮件发送通知.比如,通过邮件注册.认证.找回密码.系统报警通知.报表信息等.本篇文章带大家通过SpringBoot快速实现一个发送邮件的功能. 邮件协议 下面先简单了解 ...

最新文章

  1. ASP.NET mvc 自定义验证和Filter过滤器传参
  2. rest 接口怎么传list_如何设计一个优雅的RESTFUL的接口
  3. sql server set赋值和select 赋值的区别以及使用方法
  4. Yarn取代job/task tracker
  5. 复现经典:《统计学习方法》第14章 聚类方法
  6. Bean的依赖注入概念
  7. 算法—快速排序Sqrt (C语言)
  8. mysql insert 不需要日志_MySQL数据库性能优化(1)「转」
  9. zabbix-自定义监控项
  10. 通过密钥 SFTP(二)限定用户根目录
  11. linux下socket调试,linux下socket调试
  12. .NET Reflector + ILSpy 反编译过程
  13. 山东航天九通车联网:大数据技术引领为智慧物流赋能
  14. 【小y设计】二维码条形码打印编辑器
  15. java cloassLoader大仙儿
  16. go语言 格式化输出fmt.Printf()使用大全
  17. 牛逼,Python3竟然内置找茬神器!一起来找茬吧
  18. 生成符合SCI论文投稿要求的高清图方法
  19. 从单体结构到微服务架构的转变,微服务入门
  20. nginx -s reload原理

热门文章

  1. 哈工大网络安全实验四报告
  2. 软件安全设计(威胁建模实现)
  3. android 360短信拦截短信验证码,莫名收到短信验证码?360借条安全专家提醒小心新骗局...
  4. java根据坐标在PDF指定位置添加文本
  5. 赫兹的单位换算_赫兹是什么单位名称(赫兹单位名称及换算方法)
  6. Xmos startkit 开篇-硬件介绍
  7. 缺失MSVCP120D.dll和MSVCR120D.dll
  8. java截取字符串中间的某个部分
  9. 单极化天线和双极化天线的区别
  10. 2021BTAJ面试真题详解,kafka查看topic数据内容