前语

“我正在参加「启航计划」”

作者简介: 不愿过江东丶,一个来自二线城市的程序员,致力于用“猥琐”办法处理繁琐问题,让杂乱的问题变得通俗易懂。

支撑作者: 点赞、重视、留言~

最近大聪明在开发智能工作体系(OA),其中的一个功用模块便是工作流批阅。可是由于大聪明之前也没触摸过工作流相关的内容,并且百度出来的内容几乎都是不完好的,也没办法直接借鉴,大聪明干脆就硬着头皮一点一点去搭建代码,在经历了若干次波折和失败后,也总算完成了工作流的相关功用。想到各位小伙伴在工作中或许也会遇到类似的问题,那么今天大聪明就跟咱们共享一下 SpringBoot 整合 Activiti7 完成工作流的全进程。

SpringBoot 整合 Activiti7 完成工作流

什么是工作流

在正式讲解之前,咱们先简略的了解一下什么是工作流

工作流(Workflow),便是经过计算机对事务流程主动化履行办理。它首要处理的是“使在多个参与者之间依照某种预界说的规则主动进行传递文档、信息或使命的进程,从而完成某个预期的事务目标,或者促进此目标的完成”。那么在一个体系中工作流的功用便是对体系的事务流程进行主动化办理,而工作流是建立在事务流程的基础上,所以一个软件的体系中心根本上仍是体系的事务流程,工作流仅仅协助进行事务流程办理。即使没有工作流事务体系也能够开发运转,只不过有了工作流能够更好的办理事务流程,进步体系的可扩展性。尤其是在 OA 体系、物流服务业相关体系、银证险等金融服务业相关体系等等体系中,都会看到工作流的影子。而 Activiti 就能够协助咱们完成工作流的相关功用…

Activiti

Activiti 是一个工作流引擎, Activiti 能够将事务体系中杂乱的事务流程抽取出来,运用专门的建模语言 BPMN2.0 进行界说,事务流程依照预先界说的流程进行履行,完成了体系的流程由 Activiti 进行办理,削减事务体系由于流程改变进行体系升级改造的工作量,从而进步体系的健壮性,同时也削减了体系开发维护成本。

Activiti 官网传送门:www.activiti.org/

看完上面的内容,相信各位小伙伴对工作流和 Activiti 都有了一个初步的了解,接下来就开端今天的要点,看看如安在 SpringBoot 项目中整合 Activiti7。

详细完成进程

制作流程图

要完成工作流,咱们首要就要制作一个流程图,我这儿运用的是 IDEA,需求先装一个 Activiti 插件(actiBPM),插件安装成功后,需求重启 IDEA。

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流
IDEA重启完成后,咱们在项目下找到 resources 文件夹,在 resources 下新建一个名为 processes 的文件夹,在 processes 文件夹下新建 .bpmn 文件,开端制作咱们的流程图(这儿就制作一个简略的请假流程图)

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流
这儿有四点是需求咱们留意的:

留意事项一: 有些小伙伴修正流程图节点时,会呈现中文乱码的状况,此刻咱们需求将编码统一设置为 UTF-8。

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流
修正完 IDEA 的配置后,咱们再翻开 IDEA 的安装途径,找到以下两个文件,并在文件最后一行新增:-Dfile.encoding=UTF-8

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流
一般来说,经过以上两个步骤的修正,中文乱码的问题就能够被处理了。假如发现中文乱码的问题没有处理,咱们再去修正一下 Custom VM Options,在配置的结尾增加一句:-Dfile.encoding=UTF-8

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流
留意事项二: 咱们新建的 .bpmn 文件,一定要放在 resources/processes 途径下,只有.bpmn 文件在该途径下,发动项目时才会主动把咱们的流程图信息刺进到数据库中去。

留意事项三: 有些小伙伴在修正流程图的时分,或许会遇到不显现左侧的特点修正框的状况。那么咱们从 IDEA 左上角 File→挑选setting→找到Color Scheme 菜单,将主题色彩改为 IntelliJ Light,修正之后咱们重启一下IDEA,再翻开 .bpmn 文件,就会发现现已默许在运用 BPMN Editor 了,挑选其中的节点就能看到特点参数。这次咱们再把主题色彩改成自己喜爱的色彩,BPMN Editor 依然会正常显现了。(这种操作就很迷,也不知道为啥会这样,可是这么做却很有效)

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流
留意事项四: 咱们在修正节点信息时,输入的 Assigne 特点或许会呈现保存不成功的状况,也便是在咱们输入特点后,封闭 .bpmn 文件再翻开,或许会发现刚刚输入的 Assigne 的特点值消失了。其实特点信息现已保存成功了,咱们将 .bpmn 改成 .xml 就能够看到特点信息,可是为什么会呈现这样的状况仍是一个未解之谜

代码完成

到这儿,咱们的流程图就现已制作成功了,接下来就开端给 SpringBoot 项目整合 Activiti7。首要咱们仍是需求增加 Activiti 的 Maven 依靠

<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-spring-boot-starter</artifactId>
    <version>7.0.0.Beta2</version>
    <exclusions>
        <exclusion>
            <!-- 要点坑,不扫除mybatis的话,在发动项目时会报错mybatisplus缺少类   -->
            <artifactId>mybatis</artifactId>
            <groupId>org.mybatis</groupId>
        </exclusion>
    </exclusions>
</dependency>
<!--画图 -->
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-image-generator</artifactId>
    <version>5.22.0</version>
</dependency>
<dependency>
    <groupId>org.activiti</groupId>
    <artifactId>activiti-bpmn-layout</artifactId>
    <version>6.0.0.RC1</version>
</dependency>

然后咱们修正一下 .yml 配置文件,需求在 spring 节点下新增关于 Activiti7 的配置

spring:
    activiti:
        #1.flase:默许值。activiti在发动时,对比数据库表中保存的版别,假如没有表或者版别不匹配,将抛出反常
        #2.true: activiti会对数据库中所有表进行更新操作。假如表不存在,则主动创立
        #3.create_drop: 在activiti发动时创立表,在封闭时删去表(必须手动封闭引擎,才能删去表)
        #4.drop-create: 在activiti发动时删去原来的旧表,然后在创立新表(不需求手动封闭引擎)
        database-schema-update: true
        # 检测前史信息表是否存在,activiti7默许不生成前史信息表,敞开前史表
        db-history-used: true
        # 前史记录存储等级
        history-level: full
        check-process-definitions: true

接下来咱们再修正一下发动文件,将 Activiti7 中自带的 Security 安全框架扫除掉(由于我这儿运用的是 Shiro 安全框架,Security 就没什么用处了)。

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class, SecurityAutoConfiguration.class,
        SecurityAutoConfiguration.class,
        ManagementWebSecurityAutoConfiguration.class})
public class OAApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(OAApplication.class, args);
        System.out.println("发动成功~");
    }
}

留意: 上面扫除 Security 安全框架的操作对于 Activiti 7.1.0.M6 这个版别是没用的,由于这个版别的代码强引用了 SpringSecurity 里的内容,比如在 Activiti 的 SpringBoot 配置类中,强引用 UserDetailsService,没有这个就会报错,所以咱们还需求把版别降到7.1.0.M4及以下。

接下来咱们发动一下项目,假如数据库中多出了 Activiti 的25张表,那就代表咱们成功的将 Activiti7 和 SpringBoot项目整合到一起了(每张表的含义如下图所示)。

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流
大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流
这时分咱们翻开 act_ge_bytearray 表,就会发现里面多了一条记录,也就阐明咱们一开端制作的请假流程图被参加到了数据库中

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流

留意: 假如发动项目时呈现 Unknown column ‘VERSION_‘ in ‘field list‘ 错误,那么大概率是由于你运用了 Activiti 7.1.0.M5,针对这个问题咱们有两种处理办法:第一种是在 act_re_deployment 表中参加 VERSION_ 和 PROJECT_RELEASE_VERSION_ 这两个字段就好了 ;第二种办法是下降 Activiti 的版别。暂时还不清楚为什么会呈现这个问题,这个应该是一个官方的BUG。

接下来咱们就写几个办法,模拟一下请假流程

import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * @program: zy-oa
 * @description: ActivitiController 
 * @author: 庄霸.liziye
 * @create: 2022-05-10 09:30
 **/
@Controller
@RequestMapping("/activiti")
public class ActivitiController {
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private TaskService taskService;
    /**
     * 发动请假流程
     **/
    @GetMapping("/start")
    public void start() {
        String instanceKey = "myProcess_1";
        System.out.println("敞开请假流程...");
        Map<String, Object> map = new HashMap<String, Object>();
        //在holiday.bpmn中,填写请假单的使命办理人为动态传入的userId,此处模拟一个id
        map.put("userId", "10001");
        ProcessInstance instance = runtimeService.startProcessInstanceByKey(instanceKey, map);
        System.out.println("发动流程实例成功: "+instance);
        System.out.println("流程实例ID: "+instance.getId());
        System.out.println("流程界说ID: "+instance.getProcessDefinitionId());
    }
    /**
     * 员工请求
     **/
    @GetMapping("/employeeApply")
    public void employeeApply() {
        String instanceId = "2379b55f-d007-11ec-b669-380025172ff4"; // 使命ID
        String leaveDays = "18"; // 请假天数
        String leaveReason = "回家成婚"; // 请假原因
        Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult();
        if (task == null) {
            System.out.println("使命ID: "+instanceId+" 查询到使命为空!");
        }
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("days", leaveDays);
        map.put("date", new Date());
        map.put("reason", leaveReason);
        taskService.complete(task.getId(), map);
        System.out.println("履行【员工请求】环节,流程推动到【上级审核】环节");
    }
    /**
     * 检查请假信息
     **/
    @GetMapping("/showTaskInfo")
    public void showTaskInfo (){
        String instanceId = "2379b55f-d007-11ec-b669-380025172ff4"; // 使命ID
        Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult();
        String days = (String) taskService.getVariable(task.getId(), "days");
        Date date = (Date) taskService.getVariable(task.getId(), "date");
        String reason = (String) taskService.getVariable(task.getId(), "reason");
        String userId = (String) taskService.getVariable(task.getId(), "userId");
        System.out.println("请假天数:  " + days);
        System.out.println("请假理由:  " + reason);
        System.out.println("请假人id:  " + userId);
        System.out.println("请假日期:  " + date.toString());
    }
    /**
     * 领导批阅
     **/
    @GetMapping("/departmentAudit")
    public void departmentAudit() {
        String instanceId = "2379b55f-d007-11ec-b669-380025172ff4"; // 使命ID
        String departmentalOpinion = "祝贺祝贺,早生贵子";
        Task task = taskService.createTaskQuery().processInstanceId(instanceId).singleResult();
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("departmentalOpinion", departmentalOpinion);
        taskService.complete(task.getId(), map);
        System.out.println("增加批阅意见,请假流程完毕");
    }
}

在履行流程时,咱们需求先发动请假流程,生成一个流程的实例

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流
请假流程发动后,会生成一个流程实例ID(使命ID),上图中的 2379b55f-d007-11ec-b669-380025172ff4 便是对应的流程实例ID(使命ID)。后面的员工请求、检查流程信息、领导批阅都是需求基于这个流程实例ID(使命ID)完成的。

员工请求

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流

检查流程信息

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流

领导批阅

大聪明教你学Java | SpringBoot 整合 Activiti7 实现工作流

以上便是 SpringBoot 项目整合 Activiti7 完成工作流的完好步骤,在整合进程中有很多需求留意的小细节,各位小伙伴在完成进程中一定要细心哦~

小结

自己经历有限,有些当地或许讲的没有特别到位,假如您在阅读的时分想到了什么问题,欢迎在谈论区留言,咱们后续再一一讨论‍

期望各位小伙伴动动自己可爱的小手,来一波点赞+重视 (✿◡‿◡) 让更多小伙伴看到这篇文章~ 蟹蟹呦(●’◡’●)

假如文章中有错误,欢迎咱们留言指正;若您有更好、更独到的了解,欢迎您在留言区留下您的名贵主意。

爱你所爱 行你所行 听从你心 无问东西