Activiti 工作流 学习笔记

环境信息:
IntelliJ IDEA 2021.3.3 (Ultimate Edition)
JDK 1.8

Activiti 7

文章目录

  • Activiti 7
    • 一、了解工作流
      • 1.1 什么是工作流
      • 1.2 工作流引擎
      • 1.3 常见的工作流引擎
      • 1.4 Activiti 概述
        • 1.4.1 Activiti介绍
        • 1.4.2 建模语言BPMN
        • 1.4.3 Activiti使用流程
        • 1.4.4 流程设计工具
        • 1.4.5 下载activiti-explorer
        • 1.4.6 解压获取部署包
        • 1.4.7 部署 activiti-explorer.war
        • 1.4.8 访问activiti-explorer
      • 1.5 Activiti 7
        • 1.5.1 Activiti7 新特性介绍
    • 二、Activiti 流程操作
      • 2.1 流程定义
        • 2.1.1 新建模型
        • 2.1.2 开始节点
        • 2.1.3 任务节点
        • 2.1.4 结束节点
        • 2.1.5 设置节点属性
        • 2.1.6 设置流程定义key
        • 2.1.7保存流程定义模型
        • 2.1.8 下载流程定义文件
        • 2.1.9 下载流程定义图片
        • 2.1.10 将资源文件放入项目
    • 三、实践项目
      • 3.1 创建 Spring Boot 项目:
      • 3.2 配置文件:
      • 3.3 引入依赖
      • 3.4 新写一个Controller
      • 3.5 添加Activiti Maven依赖
        • 3.5.1 数据库
          • 1、 表的命名规则和作用
          • 2、Activiti 数据表介绍
      • 3.6 Activiti 7 经典类
        • 3.6.1 流程部署 Deployment
        • 3.6.2 流程定义 ProcessDefinition
        • 3.6.3 流程实例 ProcessInstance
        • 3.6.4 任务处理Task
        • 3.6.5 历史任务 HistoricTaskInstance
          • A. 历史综合信息 HistoricTaskInstance
          • B. 历史变量:HistoriVariableInstance
      • 3.7 UEL表达式
      • 3.8 BPMN 2.0 网关
      • 3.9 Activiti 新特性
      • 3.10 Spring Security
      • 3.11 BPMN.js
          • a. 下载BPMN.js
          • b. 安装Node.js

一、了解工作流

1.1 什么是工作流

  工作流(Workflow),就是通过计算机对业务流程自动化执行管理。它主要解决的是“使在多个参与者之间按照某种**预定义的规则(约定·好的)**自动进行传递文档、信息或任务的过程,从而实现某个预期的业务目标,或者促使此目标的实现”。通俗来讲,就是业务上一个完整的审批流程。 例如:员工的请假,出差,外出采购,合同审核等等,这些过程,都是一个工作流。

1.2 工作流引擎

  对于工作流的处理,如果采用原始的方式,我们需要拿着各种文件到各个负责人那里去签字,需要在多个部门之间不断审批,这种方式费时费力。而我们可以借助软件系统来协助我们处理这些审批流程,这样就出现了工作流系统,使用工作流系统后可以极大的提高工作效率。

工作流模型:
填写请假单 -> 部门经理审批 -> 总经理审批 -> 人事备案

  要实现上述的流程,我们自己可以通过字段标识来实现这个审批效果,在业务表中加个字段,比如填写请假单用1标识,部门经理用2标识,总经理用3标识,人事备案用4标识,好像看起来没啥问题,也实现了审批效果。可是一旦我们的流程出现变化,这个时候我们就需要改动我们的代码,这显然是不可取的,需要用专业的方式来实现工作流的管理,并且可以做到业务流程变化之后,我们的程序可以不用改变,流程自动完成。如果可以实现这样的效果,那么我们的业务系统的适应能力就得到了极大提升。在这样的背景下,就出现了工作流引擎

为什么使用工作流引擎,能实现业务流程改变,不用修改代码,流程还能自动推进?

(1)我们先来说说为什么流程改变,不用修改代码:我们的工作流引擎都实现了一个规范,这个规范要求我们的流程管理与状态字段无关,始终都是读取业务流程图的下一个节点。当业务更新的时候我们只需要更新业务流程图就行了。这就实现了业务流程改变,不用修改代码。

(2)再来说说流程自动推进,这个原理就更简单了,就拿上面的请假模型来说,工作流引擎会用一张数据库表来记录当前处在的节点。当填写完请假单后肯定是要轮到部门经理来审批了,所以我们一旦完成了请假单填写那么这条记录将会被从这张表删除掉,并且会把下一个节点部门经理的信息插入到这张表中,当我们用部门经理的信息去这张表中查询的时候就能查出部门经理相关的审批的信息了,以此类推,这样层层递进,就实现了流程的自动递交了。

1.3 常见的工作流引擎

主流框架有:ActivitiJBPMCamundaFlowable,国产的盘古BPM、云程

1.4 Activiti 概述

1.4.1 Activiti介绍

  Activiti是一个工作流引擎,可以将业务系统中复杂的业务流程抽取出来,使用专门的建模语言BPMN进行定义,业务流程按照预先定义的流程进行执行。实现了系统的流程由Activiti进行管理,减少业务系统由于流程变更进行系统升级改造的工作流量,从而提高系统的健壮性,同时也减少了系统开发维护成本。

官方网站:https://www.activiti.org

Activiti 工作流 学习笔记_第1张图片

1.4.2 建模语言BPMN

  BPM(Business Process Management)即业务流程管理,是一种规范化的构造端到端的业务流程,以持续提高组织业务效率。
  BPM 软件就是根据企业中业务环境的变化,推进人与人之间、人与系统之间以及系统与系统之间的整理及调整的经营方法与解决方案的 IT 工具。使用 BPM 软件对企业内部及外部的业务流程的整个生命周期进行建模、自动化、管理监控和优化,可以降低企业成本,提高利润。
  BPMN(Business Process Model And Notation)即业务流程模型和符号,是一套标准的业务流程建模符号,使用 BPMN 提供的符号可以创建业务流程。Activit 就是使用 BPMN 进行流程建模、流程执行管理的。
  BPMN2.0 是业务流程建模符号 2.0 的缩写,它由 Business Process Management Initiative 这个非营利协会创建并不断发展。BPMN2.0 是使用一些符号来明确业务流程设计流程图的一套符号规范,能增进业务建模时的沟通效率。目前 BPMN2.0 是最新的版本,它用于在 BPM 上下文中进行布局和可视化的沟通。

BPMN2.0 的基本符号主要包含:

  • 事件 Event

    开始:表示一个流程的开始

    中间:发生的开始和结束事件之间,影响处理的流程

    结束:表示该过程结束

Activiti 工作流 学习笔记_第2张图片

  • 活动 Activities

      活动是工作或任务的一个通用术语。一个活动可以是一个任务,还可以是一个当前流程的子处理流程;其次,你还可以为活动指定不同的类型。常见活动如下:

Activiti 工作流 学习笔记_第3张图片

  • 网关 GateWay

    用于表示流程的分支与合并,有几种常用网关需要了解:
    Activiti 工作流 学习笔记_第4张图片排他网关: 只有一条路径会被选择
    并(平)行网关: 所有路径会被同时选择。
    包容网关: 可以同时执行多条线路,也可以在网关上设置条件。
    事件网关: 专门为中间捕获事件设置的,允许设置多个输出流指向多个不同的中间捕获事件。当流程执行到事件网关后,流程处于等待状态,需要等待抛出事件才能将等待状态转换为活动状态
    Activiti 工作流 学习笔记_第5张图片

  • 流向 Flow

    流是连接两个流程节点的连线,常见的流向包含以下几种:
    顺序流:用一个带实心箭头的实心线表示,用于指定活动执行的顺序
    信息流:用一条带箭头的虚线表示,用于描述两个独立的业务参与者(业务实体/业务角色)之间发送和接受的消息流动
    关联:用一根带有线箭头的点线表示,用于将相关的数据、文本和其他人工信息与流对象联系起来。用于展示活动的输入和输出
    Activiti 工作流 学习笔记_第6张图片

流程示例:
![Activiti 工作流 学习笔记_第7张图片

1.4.3 Activiti使用流程

第一步: 引入依赖并初始化数据库

  既然activiti是一个框架,那么我们肯定是需要引入对应的jar包坐标的,具体参考代码中的。

第二步: 通过工具绘画流程图

  使用 activiti 流程建模工具(activity-designer)定义业务流程(.bpmn 文件)

.bpmn 文件就是业务流程定义文件,通过 xml 定义业务流程。

第三步:流程定义部署;

向 activiti 部署业务流程定义(.bpmn 文件),使用 activiti 提供的 api 向 activiti 中部署.bpmn 文件,

  通俗来讲,就是让activiti认识要使用的流程

第四步: 启动一个流程实例(Process Instance)

  启动一个流程实例表示开始一次业务流程的运行,比如员工请假流程部署完成,如果张三要请假就可以启动一个流程实例,如果李四要请假也启动一个流程实例,两个流程的执行互相不影响,就好比定义一个java类,实例化两个对象一样,部署的流程就好比java类,启动一个流程实例就好比new一个java对象

第五步: 用户查询待办任务(Task)

  因为现在系统的业务流程已经交给 activiti 管理,通过 activiti 就可以查询当前流程执行到哪了,当前用户需要办理什么任务了,这些 activiti帮我们管理了。实际上我们学习activiti也只是学习它的API怎么使用,因为很多功能activiti都已经封装好了,我们会调用就行了

第六步: 用户办理任务

  用户查询待办任务后,就可以办理某个任务,如果这个任务办理完成还需要其它用户办理,比如请假单创建后由部门经理审核,这个过程也是由 activiti 帮我们完成了,不需要我们在代码中硬编码指定下一个任务办理人了

第七步: 流程结束

  当任务办理完成没有下一个任务节点了,这个流程实例就完成了。

1.4.4 流程设计工具

actiBPM
Activiti 工作流 学习笔记_第8张图片

BPMN-JS
Activiti 工作流 学习笔记_第9张图片

  IDEA版本小于等于2019,可使用Activiti插件actiBPM,大于该版本的IDEA可使用Activiti BPMN visualizer插件绘制流程设计。现使用:Activiti Modeler

  Activiti Modeler 是 Activiti 官方提供的一款在线流程设计的前端插件,开发人员可以方便在线进行流程设计,保存流程模型,部署至流程定义等等,后续我们的项目也是集成Activiti Modeler绘制流程定义。

1.4.5 下载activiti-explorer

官网下载:https://www.activiti.org/get-started

Activiti 工作流 学习笔记_第10张图片

1.4.6 解压获取部署包

解压activiti-5.22.0.zip,在activiti-5.22.0\wars目录下获取 activiti-explorer.war

activiti
activiti

1.4.7 部署 activiti-explorer.war

activiti-explorer.war放到 tomcat部署目录(Tomcat目录下\webapps),启动 tomcat:双击 apache-tomcat的安装目录\bin\startup.bat

Activiti 工作流 学习笔记_第11张图片

1.4.8 访问activiti-explorer

http://localhost:8080/activiti-explorer

默认登录账号:kermit kermit

Activiti 工作流 学习笔记_第12张图片
Activiti 工作流 学习笔记_第13张图片

上面有很多功能,我们关注流程设计即可,如下图:

点击上图:流程 --> 新建模型 --> 输入模型名称(请假)–> 创建

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jjgZDKBx-1686055679452)(./doc/photo/15后.PNG)]

1.5 Activiti 7

  Activiti项目是基于Apache License许可的开源项目,项目前主管是 Tom Baeyens。Activiti项目前身是JBPM,因此Activiti初始版本是5,Activiti项目支持BPMN标准。

1.5.1 Activiti7 新特性介绍

  • 与Spring Boot更好的原生支持,旧版需要配置activiti.cfg.xml文件,还需要手动加载,新版本只需配置 yml文件即可

  • 引入SpringSecurity作为默认用户与角色的默认安全机制(相比旧版自己配置有点强硬)

  • 核心API进行了封装,封装了不少如none值等判断,让开发者更关注业务流程即可,

  • 对云发布,分布式支持等,去掉了用户和表单两个接口类,需要自己手动添加。

  • 取消了FormService IdentityService 模块,将原先RepositorySertvice,RuntimeService,ManagementService,TaskService,HistoryService五个模块合并成ProcessRuntimeTaskRuntime两个模块

Activiti 工作流 学习笔记_第14张图片

二、Activiti 流程操作

2.1 流程定义

我们定义一个请假流程

2.1.1 新建模型

Activiti 工作流 学习笔记_第15张图片
Activiti 工作流 学习笔记_第16张图片
Activiti 工作流 学习笔记_第17张图片

2.1.2 开始节点

Activiti 工作流 学习笔记_第18张图片

2.1.3 任务节点

Activiti 工作流 学习笔记_第19张图片

2.1.4 结束节点

Activiti 工作流 学习笔记_第20张图片

2.1.5 设置节点属性

指定标签名称:张三审批,节点任务负责人:zhangsan

Activiti 工作流 学习笔记_第21张图片
指定标签名称:李四审批,节点任务负责人:lisi

Activiti 工作流 学习笔记_第22张图片

2.1.6 设置流程定义key

Activiti 工作流 学习笔记_第23张图片

2.1.7保存流程定义模型

activiti
Activiti 工作流 学习笔记_第24张图片

2.1.8 下载流程定义文件

Activiti 工作流 学习笔记_第25张图片

下载文件为:qingjia.bpmn20.xml

activiti


<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="qingjia" isExecutable="true">
    <startEvent id="sid-1AB654AE-D3A2-4240-82A7-F8D959B29A9A">startEvent>
    <userTask id="sid-0DAD9B06-6F38-4616-BC16-156989CB12BA" name="张三审批" activiti:assignee="zhangsan">userTask>
    <sequenceFlow id="sid-581CCDA2-D9E0-4C5B-83AE-93F409832ED4" sourceRef="sid-1AB654AE-D3A2-4240-82A7-F8D959B29A9A" targetRef="sid-0DAD9B06-6F38-4616-BC16-156989CB12BA">sequenceFlow>
    <endEvent id="sid-E210AB99-58D8-43D0-B24A-CFF3E85A9512">endEvent>
    <userTask id="sid-E9C4E51D-9F58-4C99-B1A8-E258AD244426" name="李四" activiti:assignee="lisi">userTask>
    <sequenceFlow id="sid-9978CDC0-34F4-40ED-8A7C-80E907514297" sourceRef="sid-0DAD9B06-6F38-4616-BC16-156989CB12BA" targetRef="sid-E9C4E51D-9F58-4C99-B1A8-E258AD244426">sequenceFlow>
    <sequenceFlow id="sid-DE57A74C-09D5-42FE-BE85-A0EAE8CC89EF" sourceRef="sid-E9C4E51D-9F58-4C99-B1A8-E258AD244426" targetRef="sid-E210AB99-58D8-43D0-B24A-CFF3E85A9512">sequenceFlow>
  process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_qingjia">
    <bpmndi:BPMNPlane bpmnElement="qingjia" id="BPMNPlane_qingjia">
      <bpmndi:BPMNShape bpmnElement="sid-1AB654AE-D3A2-4240-82A7-F8D959B29A9A" id="BPMNShape_sid-1AB654AE-D3A2-4240-82A7-F8D959B29A9A">
        <omgdc:Bounds height="30.0" width="30.0" x="136.19997740686003" y="107.99999655783185">omgdc:Bounds>
      bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-0DAD9B06-6F38-4616-BC16-156989CB12BA" id="BPMNShape_sid-0DAD9B06-6F38-4616-BC16-156989CB12BA">
        <omgdc:Bounds height="79.99999999999999" width="100.0" x="224.99999329447763" y="82.99999532103554">omgdc:Bounds>
      bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-E210AB99-58D8-43D0-B24A-CFF3E85A9512" id="BPMNShape_sid-E210AB99-58D8-43D0-B24A-CFF3E85A9512">
        <omgdc:Bounds height="28.00000000000007" width="28.0" x="539.9999919533731" y="108.99999493360532">omgdc:Bounds>
      bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="sid-E9C4E51D-9F58-4C99-B1A8-E258AD244426" id="BPMNShape_sid-E9C4E51D-9F58-4C99-B1A8-E258AD244426">
        <omgdc:Bounds height="80.00000000000001" width="100.0" x="374.9999944120646" y="82.99999369680909">omgdc:Bounds>
      bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="sid-581CCDA2-D9E0-4C5B-83AE-93F409832ED4" id="BPMNEdge_sid-581CCDA2-D9E0-4C5B-83AE-93F409832ED4">
        <omgdi:waypoint x="166.19997740686003" y="122.9999964079777">omgdi:waypoint>
        <omgdi:waypoint x="224.99999329447763" y="122.99999582054932">omgdi:waypoint>
      bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-9978CDC0-34F4-40ED-8A7C-80E907514297" id="BPMNEdge_sid-9978CDC0-34F4-40ED-8A7C-80E907514297">
        <omgdi:waypoint x="324.99999329447763" y="122.99999477962673">omgdi:waypoint>
        <omgdi:waypoint x="374.9999944120646" y="122.99999423821791">omgdi:waypoint>
      bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="sid-DE57A74C-09D5-42FE-BE85-A0EAE8CC89EF" id="BPMNEdge_sid-DE57A74C-09D5-42FE-BE85-A0EAE8CC89EF">
        <omgdi:waypoint x="474.9999944120646" y="122.9999941761875">omgdi:waypoint>
        <omgdi:waypoint x="539.9999919533731" y="122.99999479937941">omgdi:waypoint>
      bpmndi:BPMNEdge>
    bpmndi:BPMNPlane>
  bpmndi:BPMNDiagram>
definitions>

2.1.9 下载流程定义图片

单击右键上图图片,图片另存为:qingjia.PNG

Activiti 工作流 学习笔记_第26张图片

2.1.10 将资源文件放入项目

一般我们将在项目resources下新建BPMN资源文件夹,将qingjia.bpmn20.xml与qingjia.PNG放入BPMN文件夹中:

Activiti 工作流 学习笔记_第27张图片

三、实践项目

3.1 创建 Spring Boot 项目:

配置项目相关信息:
Activiti 工作流 学习笔记_第28张图片

选择Spring相关插件:

  • Spring Web
  • JDBC API 连接数据库
  • MySQL Driver 使用的是MySQL,因此配置MySQL驱动
  • Mybatis framework 使用Mybatis框架作为持久层的框架

Activiti 工作流 学习笔记_第29张图片
选择JDK版本:1.8
Activiti 工作流 学习笔记_第30张图片

Maven配置(同时Maven最好引用阿里镜像
Activiti 工作流 学习笔记_第31张图片

3.2 配置文件:

server:
  port: 8080 # 端口号
  servlet:
    context-path: /  # 访问起始路径
spring:
  datasource:  # 数据库连接
    username: root  # 用户名
    password: root  # 密码
    url: jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&nullCatalogMeansCurrent=true 
    driver-class-name: com.mysql.cj.jdbc.Driver  # 驱动

3.3 引入依赖


<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.3.2.RELEASEversion>
        <relativePath/> 
    parent>
    <groupId>com.test.activitigroupId>
    <artifactId>activitiartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>activitiname>
    <description>Demo project for Spring Bootdescription>

    <properties>
        <maven.compiler.source>8maven.compiler.source>
        <maven.compiler.target>8maven.compiler.target>
    properties>

        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-jdbcartifactId>
            dependency>

            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
            dependency>

            <dependency>
                <groupId>org.mybatis.spring.bootgroupId>
                <artifactId>mybatis-spring-boot-starterartifactId>
                <version>2.2.2version>
            dependency>

            
            <dependency>
                <groupId>mysqlgroupId>
                <artifactId>mysql-connector-javaartifactId>
                <scope>runtimescope>
            dependency>

            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintagegroupId>
                        <artifactId>junit-vintage-engineartifactId>
                    exclusion>
                exclusions>
            dependency>

            
            <dependency>
                <groupId>org.activitigroupId>
                <artifactId>activiti-spring-boot-starterartifactId>
                <version>7.1.0.M4version>
            dependency>

            <dependency>
                <groupId>org.activiti.dependenciesgroupId>
                <artifactId>activiti-dependenciesartifactId>
                <version>7.1.0.M4version>
                <type>pomtype>
            dependency>

            
            <dependency>
                <groupId>org.projectlombokgroupId>
                <artifactId>lombokartifactId>
                <optional>trueoptional>
            dependency>
        dependencies>
project>

3.4 新写一个Controller

创建HelloController.java

package com.test.activiti.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {
    @RequestMapping(value = "hello",method = RequestMethod.GET) //Get 浏览器传参 Post 提交表单方式传参
    public String hello(){
        return "Activiti7 学习";
    }
}

启动后访问:

Activiti 工作流 学习笔记_第32张图片

3.5 添加Activiti Maven依赖

https://activiti.gitbook.io/activiti-7-developers-guide/getting-started/getting-started-activiti-core

Activiti 工作流 学习笔记_第33张图片

当然也可以阿里云查询:

https://developer.aliyun.com/mvn/search

Activiti 工作流 学习笔记_第34张图片
Activiti 工作流 学习笔记_第35张图片



<dependency>
	<groupId>org.activitigroupId>
    <artifactId>activiti-spring-boot-starterartifactId>
    <version>7.1.0.M4version>
dependency>

<dependency>
    <groupId>org.activiti.dependenciesgroupId>
    <artifactId>activiti-dependenciesartifactId>
    <version>7.1.0.M4version>
    <type>pomtype>
dependency>

注意:M5版本以上,会出现每次启动数据库后新生成一条默认数据的BUG,M4版本就不会。

# application.yml
server:
  port: 8080 # 端口号
  servlet:
    context-path: /  # 访问起始路径
spring:
  datasource:  # 数据库连接
    username: root  # 用户名
    password: root  # 密码
    url: jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC&nullCatalogMeansCurrent=true
    driver-class-name: com.mysql.cj.jdbc.Driver  # 驱动
# activiti7 历史表创建
spring.activiti.history-level: full # 完整存储
spring.activiti.db-history-used: true # 开启历史表存储
# 自动部署 (关闭)
spring.activiti.check-process-definitions: false

如果是Activiti7以前的版本会需要另写一个activiti.cfg.xml配置文件,启动项目时需要启动。当然activiti 7 也是支持这样的,但是已经能够适配SpringBoot配置文件,这样配置方式是没必要的。

Activiti 7 之前版本:https://www.activiti.org/userguide/

Activiti 工作流 学习笔记_第36张图片
配置完成后,运行即可:生成多个表,User表需要自己创建。

Activiti 工作流 学习笔记_第37张图片

3.5.1 数据库

  Activiti 的运行支持必须要有这 25 张表的支持,主要是在业务流程运行过程中,记录参与流程的用户主体,用户组信息,以及流程的定义,流程执行时的信息,和流程的历史信息等等。

1、 表的命名规则和作用

  观察创建的表,我们发现 Activiti 的表都以 act_ 开头,紧接着是表示表的用途的两个字母标识,也和 Activiti 所提供的服务的 API 对应:

  • ACT_RE:RE 表示 repository,这个前缀的表包含了部署的流程定义和流程静态资源 (图片、规则、等等)
  • ACT_RU:RU 表示 runtime,这些表运行时,会包含流程实例、任务、变量、异步任务等流程业务进行中的数据。Activiti 只在流程实例执行过程中保存这些数据,在流程结束时就会删除这些记录。这样表就可以一直保持很小的体积,并且速度很快
  • ACT_HI:HI 表示 history,这些表包含一些历史数据,比如历史流程实例、变量、任务等等
  • ACT_GE:GE 表示 general,通用数据
2、Activiti 数据表介绍
表分类 表名 解释
一般数据
[ACT_EVT_LOG] 事件处理日志
[ACT_GE_BYTEARRAY] 通用的流程定义和流程资源 (二进制表)
[ACT_GE_PROPERTY] 系统相关属性 (存BPMN的二进制表)
流程历史记录
[ACT_HI_ACTINST] 历史节点
[ACT_HI_ATTACHMENT] 历史的流程附件
[ACT_HI_COMMENT] 历史的说明性信息
[ACT_HI_DETAIL] 历史的流程运行中的细节信息
[ACT_HI_IDENTITYLINK] 历史的流程运行过程中用户关系
[ACT_HI_PROCINST] 历史的流程实例
[ACT_HI_TASKINST] 历史的任务实例
[ACT_HI_VARINST] 历史的流程运行中的变量信息
流程定义表
[ACT_RE_DEPLOYMENT] 部署单元信息 (部署表)
[ACT_RE_MODEL] 模型信息
[ACT_RE_PROCDEF] 已部署的流程定义
运行实例表
[ACT_RU_EVENT_SUBSCR] 运行时事件
[ACT_RU_EXECUTION] 运行时流程执行实例
[ACT_RU_IDENTITYLINK] 运行时用户关系信息,存储任务节点与参与者的相关信息
[ACT_RU_JOB] 运行时作业
[ACT_RU_TASK] 运行时任务
[ACT_RU_VARIABLE] 运行时变量表

3.6 Activiti 7 经典类

3.6.1 流程部署 Deployment

Deployment:添加资源文件、获取部署信息、部署时间

  1. 指定流程key
  2. 制定流程名称
  3. 任务指定执行人
  4. 上传bpmn、上传bpmn和图片、上传zip
  5. 增删改查
  6. 查询列表和查询xml
package com.test.activiti;

import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.InputStream;
import java.util.List;

@SpringBootTest
public class Part1_Deployment {

    @Autowired
    private RepositoryService repositoryService;

    /**
     * 通过bpmn部署流程
     */
    @Test
    public void initDeploymentBPMN() {
        String filename = "BPMN/diagram.bpmn"; //resources 目录下
        //String pngname = "BPMN/bpmn.PNG";
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource(filename)
                //.addClasspathResource(pngname)//图片 原来需要上传bpmnxml文件的同时上传一张相应行的图片用来展现
                .name("流程部署测试BPMN") //如果数据库遇到多个相同文件,会在之后追加_V2
                .deploy();
        System.out.println(deployment.getName());
    }
}

注意:deployment 选择org.activiti.engine.repository 包:

activiti

运行成功
Activiti 工作流 学习笔记_第38张图片
activiti

/**
 * 通过bpmn部署流程
 */
@Test
public void initDeploymentBPMN() {
    String pngname = "BPMN/bpmn.PNG";//resources 目录下
    Deployment deployment = repositoryService.createDeployment()
            .addClasspathResource(pngname)//图片 原来需要上传bpmnxml文件的同时上传一张相应行的图片用来展现
            .name("流程部署测试BPMN") //如果数据库遇到多个相同文件,会在之后追加_V2
            .deploy();
    System.out.println(deployment.getName());
}

Activiti 工作流 学习笔记_第39张图片
activiti
activiti
Activiti 工作流 学习笔记_第40张图片

   /**
     * 通过zip部署流程
     */
    @Test
    public void initDemploymentZIP(){
        InputStream fileInputStream = this.getClass()
                .getClassLoader()
                .getResourceAsStream("BPMN/bpmn.zip");
        ZipInputStream zip = new ZipInputStream(fileInputStream);
        Deployment deployment = repositoryService.createDeployment()
                .addZipInputStream(zip)
                .name("流程部署测试zip")//zip中存在xml和jpg文件,存入数据库会自动解压生成两条数据
                .deploy();
        System.out.println(deployment.getName());
    }

Activiti 工作流 学习笔记_第41张图片

之前:

activiti

之后:
activiti
Activiti 工作流 学习笔记_第42张图片

可以看到zip文件上传,压缩文件被解压缩了。

   /**
     * 查询流程部署
     */
    @Test
    public void getDeployments(){
        List<Deployment>  list =repositoryService.createDeploymentQuery().list();
        for (Deployment deployment :list){
            System.out.println("Id:"+deployment.getId());
            System.out.println("Name:"+deployment.getName());
            System.out.println("Time:"+deployment.getDeploymentTime());
            System.out.println("Key:"+deployment.getKey());
        }
    }

Activiti 工作流 学习笔记_第43张图片
Activiti 工作流 学习笔记_第44张图片

全部代码:

package com.test.activiti;

import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.InputStream;
import java.util.List;
import java.util.zip.ZipInputStream;

@SpringBootTest
public class Part1_Deployment {

    @Autowired
    private RepositoryService repositoryService;
    
     /**
     * 通过bpmn部署流程
     */
    @Test
    public void initDeploymentBPMN() {
        String filename = "BPMN/diagram.bpmn"; //resources 目录下
        //String pngname = "BPMN/bpmn.PNG";
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource(filename)
                //.addClasspathResource(pngname)//图片 原来需要上传bpmnxml文件的同时上传一张相应行的图片用来展现
                .name("流程部署测试BPMN") //如果数据库遇到多个相同文件,会在之后追加_V2
                .deploy();
        System.out.println(deployment.getName());
    }
    
    /**
     * 通过zip部署流程
     */
    @Test
    public void initDemploymentZIP(){
        InputStream fileInputStream = this.getClass()
                .getClassLoader()
                .getResourceAsStream("BPMN/bpmn.zip");
        ZipInputStream zip = new ZipInputStream(fileInputStream);
        Deployment deployment = repositoryService.createDeployment()
                .addZipInputStream(zip)
                .name("流程部署测试zip")//zip中存在xml和jpg文件,存入数据库会自动解压生成两条数据
                .deploy();
        System.out.println(deployment.getName());
    }

    /**
     * 查询流程部署
     */
    @Test
    public void getDeployments(){
        List  list =repositoryService.createDeploymentQuery().list();
        for (Deployment deployment :list){
            System.out.println("Id:"+deployment.getId());
            System.out.println("Name:"+deployment.getName());
            System.out.println("Time:"+deployment.getDeploymentTime());
            System.out.println("Key:"+deployment.getKey());
        }
    }
}

3.6.2 流程定义 ProcessDefinition

流程部署
Deployment:添加资源文件、获取部署信息、部署时间

流程定义
ProcessDefinition:获取版本号、key、资源名称、部署ID等
key不变 就是同一个流程,多加v2 v3的版本

package com.test.activiti;

import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.ProcessDefinition;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;

@SpringBootTest
public class Part2_ProcessDefinition {

    @Autowired
    private RepositoryService repositoryService;

    /**
     * 查询流程定义
     */
    @Test
    public void getDefinitions(){
        // ProcessDefinition 是 org.activiti.engine.repository.ProcessDefinition 这个类
        List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery()
                .list();
        for (ProcessDefinition pd: list){
            System.out.println("---------流程定义-----------");
            System.out.println("Name:"+pd.getName());
            System.out.println("Key:"+pd.getKey());
            System.out.println("ResourceName:"+pd.getResourceName());
            System.out.println("DeploymentId:"+pd.getDeploymentId());
            System.out.println("Version:"+pd.getVersion());
            System.out.println("===========================");
        }
    }

    @Test
    public  void delDefinition(){
        String pdID = "af658828-c940-11ed-9559-005056c00008";
        repositoryService.deleteDeployment(pdID,true); //true 删除下列 所有的历史, false会保留历史任务,一般真实情况下都是false要留痕迹
        System.out.println("删除流程定义成功!");
    }
}

查询结果:
Activiti 工作流 学习笔记_第45张图片

要删除的:

Activiti 工作流 学习笔记_第46张图片

删除成功
Activiti 工作流 学习笔记_第47张图片

删除后结果
Activiti 工作流 学习笔记_第48张图片
activiti
Activiti 工作流 学习笔记_第49张图片

3.6.3 流程实例 ProcessInstance

流程定义 ProcessDefinition 与 流程实例 ProcessInstance 是一对多关系, 一个流程定义能变成多个流程实例
理解为:行动计划与具体行动的关系。
流程定义 ProcessDefinition 相当于活动方案
流程实例 ProcessInstance 每一次执行这个方案的实际行动

package com.test.activiti;

import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class Part3_ProcessInstance {

    @Autowired
    private RuntimeService runtimeService;

    //初始化流程实例列表
    @Test
    public void initProcessInstance(){
        //1、获取页面表单填报的内容,请假时间,请假事由,String fromData
        //2、fromData 写入业务表,返回业务表主键ID==businessKey
        //3、把业务数据与Acitivi7流程数据关联
        //4、可以执行多次该方法
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_claim","claim");
        System.out.println("流程实例ID:"+processInstance.getProcessInstanceId());
    }
}

注意:ProcessInstance 使用 org.activiti.engine.runtime.ProcessInstance

activiti
运行时流程执行实例
activiti
Activiti 工作流 学习笔记_第50张图片

数据库 增加

act_ru_execution 两条,因为是两个节点,(不包括结束节点)
act_ru_identitylink 运行市里人员
activiti

    //获取流程实例列表
    @Test
    public void getProcessInstance(){
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();
        for (ProcessInstance pi: list) {
            System.out.println("--------流程实例--------");
            System.out.println("ProcessInstanceId:"+pi.getProcessInstanceId());//流程实例id uuid
            System.out.println("ProcessDefinitionId:"+pi.getProcessDefinitionId()); //流程定义id:processDefinitionKey + 版本号 + uuid
            System.out.println("执行结束isEnded:"+pi.isEnded());
            System.out.println("是否挂起流程实例isSuspended:"+pi.isSuspended());
        }
    }

Activiti 工作流 学习笔记_第51张图片

	//暂停流程实例
    @Test
    public void stopProcessInstances(){
        runtimeService.suspendProcessInstanceById("8f229d56-ca09-11ed-8386-005056c00008");//流程实例id
        System.out.println("挂起流程实例");
    }

Activiti 工作流 学习笔记_第52张图片
Activiti 工作流 学习笔记_第53张图片

  //激活流程实例
    @Test
    public void activitieProcessInstances(){
        runtimeService.activateProcessInstanceById("8f229d56-ca09-11ed-8386-005056c00008");//流程实例id
        System.out.println("激活流程实例");
    }

Activiti 工作流 学习笔记_第54张图片
Activiti 工作流 学习笔记_第55张图片

	//删除流程实例
    @Test
    public void delProcessInstances(){
        runtimeService.deleteProcessInstance("8f229d56-ca09-11ed-8386-005056c00008","删除原因:删除测试");//流程实例id 删除原因deleteReason
        System.out.println("删除流程实例");
    }

Activiti 工作流 学习笔记_第56张图片
Activiti 工作流 学习笔记_第57张图片
activiti

package com.test.activiti;

import org.activiti.engine.RuntimeService;
import org.activiti.engine.runtime.ProcessInstance;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class Part3_ProcessInstance {

    @Autowired
    private RuntimeService runtimeService;

    //初始化流程实例列表
    @Test
    public void initProcessInstance(){
        //1、获取页面表单填报的内容,请假时间,请假事由,String fromData
        //2、fromData 写入业务表,返回业务表主键ID==businessKey
        //3、把业务数据与Acitivi7流程数据关联
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("myProcess_claim","claim");
        System.out.println("流程实例ID:"+processInstance.getProcessInstanceId());
    }

    //获取流程实例列表
    @Test
    public void getProcessInstance(){
        List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();
        for (ProcessInstance pi: list) {
            System.out.println("--------流程实例--------");
            System.out.println("ProcessInstanceId:"+pi.getProcessInstanceId());//流程实例id uuid
            System.out.println("ProcessDefinitionId:"+pi.getProcessDefinitionId()); //流程定义id:processDefinitionKey + 版本号 + uuid
            System.out.println("执行结束isEnded:"+pi.isEnded());
            System.out.println("是否挂起流程实例isSuspended:"+pi.isSuspended());
        }
    }

    //暂停流程实例
    @Test
    public void stopProcessInstances(){
        runtimeService.suspendProcessInstanceById("8f229d56-ca09-11ed-8386-005056c00008");//流程实例id
        System.out.println("挂起流程实例");
    }

    //激活流程实例
    @Test
    public void activitieProcessInstances(){
        runtimeService.activateProcessInstanceById("8f229d56-ca09-11ed-8386-005056c00008");//流程实例id
        System.out.println("激活流程实例");
    }

    //删除流程实例
    @Test
    public void delProcessInstances(){
        runtimeService.deleteProcessInstance("8f229d56-ca09-11ed-8386-005056c00008","删除原因:删除测试");//流程实例id 删除原因deleteReason
        System.out.println("删除流程实例");
    }
}

3.6.4 任务处理Task

用户任务属性面板
Assignee: 执行人、代理人
Candidate Users: 候选人(多个用户)
Candidate Groups: 候选组
Due Date: 任务到期时间

新设计工作流:

Activiti 工作流 学习笔记_第58张图片
涉及的表:

运行时
ACT_RU_TASK运行时任务节点

先部署事件:
Activiti 工作流 学习笔记_第59张图片
Activiti 工作流 学习笔记_第60张图片
Activiti 工作流 学习笔记_第61张图片
Activiti 工作流 学习笔记_第62张图片

package com.test.activiti;

import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class Part4_Task {

    @Autowired
    private TaskService taskService;

    //任务查询
    @Test
    public void getTasks(){
        List<Task> list  = taskService.createTaskQuery().list();
        for (Task tk : list){
            System.out.println("Id:"+tk.getId());
            System.out.println("Name:"+tk.getName());
            System.out.println("执行人Assignee:"+tk.getAssignee());
        }
    }
}

Activiti 工作流 学习笔记_第63张图片
Activiti 工作流 学习笔记_第64张图片

    //查询我的待办任务
    @Test
    public void getTasksByAssignee(){
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee("bajie") //或者填wukong
                .list();
        for (Task tk : list){
            System.out.println("Id:"+tk.getId());
            System.out.println("Name:"+tk.getName());
            System.out.println("执行人Assignee:"+tk.getAssignee());
        }
    }

Activiti 工作流 学习笔记_第65张图片
activiti
activiti

    //执行任务
    @Test
    public void completeTask(){ //848df66b-ca1a-11ed-b383-005056c00008 候选人任务
        taskService.complete("848df66b-ca1a-11ed-b383-005056c00008");//d74d1e61-ca16-11ed-9636-005056c00008
        System.out.println("完成任务");
    }

activiti
Activiti 工作流 学习笔记_第66张图片
Activiti 工作流 学习笔记_第67张图片
activiti
activiti
activiti
Activiti 工作流 学习笔记_第68张图片
Activiti 工作流 学习笔记_第69张图片

Activiti 工作流 学习笔记_第70张图片
Activiti 工作流 学习笔记_第71张图片
Activiti 工作流 学习笔记_第72张图片
Activiti 工作流 学习笔记_第73张图片
activiti
Activiti 工作流 学习笔记_第74张图片
activiti
Activiti 工作流 学习笔记_第75张图片
Activiti 工作流 学习笔记_第76张图片
activiti
Activiti 工作流 学习笔记_第77张图片

	//拾取任务
    @Test
    public void claimTask(){
//        //查询候选人
//        List list = taskService.createTaskQuery()
//                .taskCandidateUser("bajie") //Activiti6 之前可以 Activiti7不行
//                .list();
        Task task = taskService.createTaskQuery()
                .taskId("848df66b-ca1a-11ed-b383-005056c00008")
                .singleResult();
        taskService.claim("848df66b-ca1a-11ed-b383-005056c00008","bajie");//任务id taskId 用户id userId
    }
    //归还任务
    @Test
    public void setTaskAssignee(){
        Task task = taskService.createTaskQuery()
                .taskId("848df66b-ca1a-11ed-b383-005056c00008")
                .singleResult();
        //taskService.setAssignee("848df66b-ca1a-11ed-b383-005056c00008","null"); //归还后候选任务
        taskService.setAssignee("848df66b-ca1a-11ed-b383-005056c00008","wukong");//交办 交给已知执行人
    }

全部代码:

package com.test.activiti;

import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class Part4_Task {

    @Autowired
    private TaskService taskService;

    //任务查询
    @Test
    public void getTasks(){
        List<Task> list  = taskService.createTaskQuery().list();
        for (Task tk : list){
            System.out.println("Id:"+tk.getId());
            System.out.println("Name:"+tk.getName());
            System.out.println("执行人Assignee:"+tk.getAssignee());
        }
    }

    //查询我的待办任务
    @Test
    public void getTasksByAssignee(){
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee("wukong")
                .list();
        for (Task tk : list){
            System.out.println("Id:"+tk.getId());
            System.out.println("Name:"+tk.getName());
            System.out.println("执行人Assignee:"+tk.getAssignee());
        }
    }

    //执行任务
    @Test
    public void completeTask(){ //848df66b-ca1a-11ed-b383-005056c00008 候选人任务
        taskService.complete("848df66b-ca1a-11ed-b383-005056c00008");//d74d1e61-ca16-11ed-9636-005056c00008
        System.out.println("完成任务");
    }

    //拾取任务
    @Test
    public void claimTask(){
//        //查询候选人
//        List list = taskService.createTaskQuery()
//                .taskCandidateUser("bajie") //Activiti6 之前可以 Activiti7不行
//                .list();
        Task task = taskService.createTaskQuery()
                .taskId("848df66b-ca1a-11ed-b383-005056c00008")
                .singleResult();
        taskService.claim("848df66b-ca1a-11ed-b383-005056c00008","bajie");
    }

    //归还任务
    @Test
    public void setTaskAssignee(){
        Task task = taskService.createTaskQuery()
                .taskId("848df66b-ca1a-11ed-b383-005056c00008")
                .singleResult();
        //taskService.setAssignee("848df66b-ca1a-11ed-b383-005056c00008","null"); //归还后候选任务
        taskService.setAssignee("848df66b-ca1a-11ed-b383-005056c00008","wukong");//交办 交给已知执行人
    }
}

3.6.5 历史任务 HistoricTaskInstance

  • 历史综合信息:HistoricTaskInstance
  • 历史变量:HistoriVariableInstance
A. 历史综合信息 HistoricTaskInstance
package com.test.activiti;

import org.activiti.engine.HistoryService;
import org.activiti.engine.history.HistoricTaskInstance;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class Part5_HistoricTaskInstance {

    @Autowired
    private HistoryService historyService;

    //根据用户名查询历史记录
    @Test
    public void HistoricTaskInstanceByUser(){
        List<HistoricTaskInstance> list = historyService
                .createHistoricTaskInstanceQuery()
                .orderByHistoricTaskInstanceEndTime()
                .asc()//正序 desc 倒叙
                .taskAssignee("bajie")
                .list();
        for (HistoricTaskInstance hi : list){
            System.out.println("Id:"+hi.getId());
            System.out.println("ProcessInstanceId:"+hi.getProcessInstanceId());
            System.out.println("Name:"+hi.getName());
        }
    }
}

Activiti 工作流 学习笔记_第78张图片

B. 历史变量:HistoriVariableInstance
    //根据流程实例ID查询历史
    @Test
    public void HistoricTaskInstanceByProcessInstance(){
        List<HistoricTaskInstance> list = historyService
                .createHistoricTaskInstanceQuery()
                .orderByHistoricTaskInstanceEndTime()
                .asc()//正序 desc 倒叙
                .processInstanceId("84891467-ca1a-11ed-b383-005056c00008") //审核人任务 这里所有的id在真实场景下都应该是变量
                .list();
        for (HistoricTaskInstance hi : list){
            System.out.println("Id:"+hi.getId());
            System.out.println("ProcessInstanceId:"+hi.getProcessInstanceId());
            System.out.println("Name:"+hi.getName());
        }
    }

Activiti 工作流 学习笔记_第79张图片
历史节点
Activiti 工作流 学习笔记_第80张图片
历史流程实例
Activiti 工作流 学习笔记_第81张图片
历史人物实例
Activiti 工作流 学习笔记_第82张图片
历史流程人员:每一个人物环节操作,执行人谁?

Activiti 工作流 学习笔记_第83张图片
activiti

3.7 UEL表达式

EL表达式语言(Expression Language)
UEL统一表达式语言(Unified Expression Languange) 使用activiti时,动态赋值用的

  • 表达式以 ${ 开始,以 } 结束,例如 ${day > 100}
  • 支持逻辑运算 ${userName == "bajie" and pwd == "123 }
  • 支持变量与实体类赋值
  • **使用场景:**对执行流程变量赋值,但不要反复赋值(可能存在BUG)。
  • 流程实例启动环节可以赋值,任务完成环节。

对应activiti数据表
act_ru_variable 运行时参数表
act_hi_varinst 历史参数表 varinst参数实例缩写

UEL表达式的保留字
and eq gt
instanceof div or
le false empty
not Lt ge
UEL表达式的运算符
运算符 功能 示例 结果
+ ${1 + 1 } 2
- div ${1 - 1 } 0
* ${2 * 2 } 4
/或div 2/1或{2 div 1} 2
/或div 2/0或{2 div 0} Infinity
% 或 mod 求余 {3%2}或{3mod2} 1
% 或 mod 求余 {3%0}或{3mod0} 异常 java.lang.ArithmenticException:/by zero

Activiti 工作流 学习笔记_第84张图片
Activiti 工作流 学习笔记_第85张图片
Activiti 工作流 学习笔记_第86张图片
Activiti 工作流 学习笔记_第87张图片

package com.test.activiti;

import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.runtime.ProcessInstance;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.HashMap;
import java.util.Map;

@SpringBootTest
public class Part6_UEL {

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    //任务 流程实例启动环节 任务完成 任意时刻
    //启动流程实例带参数,执行执行人
    @Test
    public void initProcessInstanceWithArgs() {
        //1、获取页面表单填报的内容,请假时间,请假事由,String fromData
        //2、fromData 写入业务表,返回业务表主键ID==businessKey
        //3、把业务数据与Acitivi7流程数据关联
        //4、流程变量
        Map<String,Object> variables = new HashMap<String,Object>();
        variables.put("ZhiXingRen","wukong");
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey("myProcess_claim","claim",variables);
        System.out.println("流程实例ID:"+processInstance.getProcessInstanceId());
    }

    //完成任务带参数
    @Test
    public void completeTaskWItchArgs(){
        Map<String,Object> variables = new HashMap<String,Object>();
        variables.put("pay","101");
        taskService.complete("",null);
        System.out.println("完成任务");
    }

    //启动流程实例带参数,使用实体类  //npmn 必须是小写
    @Test
    public void initProcessInstanceWitchClassArgs(){
        UEL_POJO uel_pojo = new UEL_POJO();
        uel_pojo.setZhixingren("bajie");

        Map<String,Object> variables = new HashMap<String,Object>();
        variables.put("uelpojo",uel_pojo);
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey(
                        "myProcess_claim",
                        "bKey002",
                        variables);
        System.out.println("流程实例ID:"+processInstance.getProcessInstanceId());
    }

    //任务完成环节带参数,指定多个候选人
    @Test
    public void initProcessInstanceWithCandiDateArgs(){
        Map<String,Object> variables = new HashMap<String,Object>();
        variables.put("houxuanren","wukong,tangseng");
        taskService.complete("",null);
        System.out.println("完成任务");
    }

    //直接指定流程变量
    @Test
    public void otherArgs(){
        runtimeService.setVariable("","pay","101");
//        runtimeService.setVariables();
//        runtimeService.setVariable();
//        runtimeService.setVariables();
    }

    //局部变量
    @Test
    public void otherLocalArgs(){
        runtimeService.setVariableLocal("","pay","101");
        //        runtimeService.setVariables();
//        runtimeService.setVariable();
//        runtimeService.setVariables();
    }
}

Activiti 工作流 学习笔记_第88张图片
Activiti 工作流 学习笔记_第89张图片
Activiti 工作流 学习笔记_第90张图片
Activiti 工作流 学习笔记_第91张图片

流程部署:

/**
     * 通过bpmn部署流程
     */
    @Test
    public void initDeploymentBPMN2() {
        String filename = "BPMN/Part6_UEL_V2.bpmn"; //resources 目录下Part6_UEL_V2.bpmn 文件
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource(filename)
                .name("流程Part6_UEL_V2.bpmn部署") //如果数据库遇到多个相同文件,会在之后追加_V2
                .deploy();
        System.out.println(deployment.getName());
    }

Activiti 工作流 学习笔记_第92张图片

初始化实例:

//初始化流程实例列表
    @Test
    public void initProcessInstance(){
        //1、获取页面表单填报的内容,请假时间,请假事由,String fromData
        //2、fromData 写入业务表,返回业务表主键ID==businessKey
        //3、把业务数据与Acitivi7流程数据关联
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey("myProcess_1uel_V2","claim"); // 流程实例的key processDefinitionKey, 业务表单关联 businessKey (暂时没用)
        System.out.println("流程实例ID:"+processInstance.getProcessInstanceId());
    }

Activiti 工作流 学习笔记_第93张图片

bajie存在任务:

 //查询我的待办任务
    @Test
    public void getTasksByAssignee(){
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee("bajie")
                .list();
        for (Task tk : list){
            System.out.println("Id:"+tk.getId());
            System.out.println("Name:"+tk.getName());
            System.out.println("执行人Assignee:"+tk.getAssignee());
        }
    }

Activiti 工作流 学习笔记_第94张图片
没有tangseng的任务:

 //查询我的待办任务
    @Test
    public void getTasksByAssignee(){
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee("tangseng")
                .list();
        for (Task tk : list){
            System.out.println("Id:"+tk.getId());
            System.out.println("Name:"+tk.getName());
            System.out.println("执行人Assignee:"+tk.getAssignee());
        }
    }

Activiti 工作流 学习笔记_第95张图片

完成任务,并将pay赋值 101

    //完成任务带参数
    @Test
    public void completeTaskWItchArgs(){
        Map<String,Object> variables = new HashMap<String,Object>();
        variables.put("pay","101");
        taskService.complete("e8e412fa-ebe6-11ed-be61-005056c00008",variables);
        System.out.println("完成任务");
    }

Activiti 工作流 学习笔记_第96张图片

此时唐僧存在任务了:

//查询我的待办任务
    @Test
    public void getTasksByAssignee(){
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee("tangseng")
                .list();
        for (Task tk : list){
            System.out.println("Id:"+tk.getId());
            System.out.println("Name:"+tk.getName());
            System.out.println("执行人Assignee:"+tk.getAssignee());
        }
    }

Activiti 工作流 学习笔记_第97张图片

声明实体类:

package com.test.activiti;

import lombok.Data;
import java.io.Serializable;

@Data //lombok
public class UEL_POJO implements Serializable {
    private String zhixingren;
    private String pay;

}
    //直接指定流程变量 全局变量
    @Test
    public void otherArgs(){
        runtimeService.setVariable("","pay","101");//流程实例id,参数,参数值
//        runtimeService.setVariables();//可设置多个参数
//        taskService.setVariable();
//        runtimeService.setVariables();// 可赋值多个
    }

    //局部变量 当前环节设置有用
    @Test
    public void otherLocalArgs(){
        runtimeService.setVariableLocal("","pay","101");//流程实例id,参数,参数值
        //        runtimeService.setVariables();
//        runtimeService.setVariable();
//        runtimeService.setVariables();
    }

3.8 BPMN 2.0 网关

Activiti 工作流 学习笔记_第98张图片

事件,事件大于等于两个

并行网关
Activiti 工作流 学习笔记_第99张图片

 /**
     * 通过bpmn部署流程
     */
    @Test
    public void initDeploymentBPMN2() {
        String filename = "BPMN/Part7_1_Parallel.bpmn"; //resources 目录下
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource(filename)
                .name("流程Part7_1_Parallel.bpmn部署") //如果数据库遇到多个相同文件,会在之后追加_V2
                .deploy();
        System.out.println(deployment.getName());
    }

Activiti 工作流 学习笔记_第100张图片

 //启动流程实例
    @Test
    public void initProcessInstanceWitchArgs(){
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey(
                        "myProcess_Parallel");
        System.out.println("流程示例ID"+processInstance.getProcessDefinitionId());
    }6b7330e6-ed49-11ed-a19e-005056c00008
        39887e29-ed4b-11ed-8e48-005056c00008

Activiti 工作流 学习笔记_第101张图片
activiti

 /**
     * 通过bpmn部署流程
     */
    @Test
    public void initDeploymentBPMN2() {
        String filename = "BPMN/Part7_2_Exclusive.bpmn"; //resources 目录下
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource(filename)
                .name("流程Part7_2_Exclusive.bpmn部署") //如果数据库遇到多个相同文件,会在之后追加_V2
                .deploy();
        System.out.println(deployment.getName());
    }
//启动流程实例
    @Test
    public void initProcessInstanceWitchArgs(){
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey(
                        "myProcess_Exclusive");
        System.out.println("流程示例ID"+processInstance.getProcessDefinitionId());
    }8e0074f2-ed4e-11ed-b766-005056c00008

Activiti 工作流 学习笔记_第102张图片

包含网关:多个结果
Activiti 工作流 学习笔记_第103张图片

/**
     * 通过bpmn部署流程
     */
    @Test
    public void initDeploymentBPMN2() {
        String filename = "BPMN/Part7_4_Inclusive.bpmn"; //resources 目录下
        Deployment deployment = repositoryService.createDeployment()
                .addClasspathResource(filename)
                .name("流程Part7_4_Inclusive.bpmn部署") //如果数据库遇到多个相同文件,会在之后追加_V2
                .deploy();
        System.out.println(deployment.getName());
    }

Activiti 工作流 学习笔记_第104张图片

    //启动流程实例
    @Test
    public void initProcessInstanceWitchArgs(){
        ProcessInstance processInstance = runtimeService
                .startProcessInstanceByKey(
                        "myProcess_Inclusive");
        System.out.println("流程示例ID"+processInstance.getProcessDefinitionId());
    }633cd72a-ed63-11ed-8848-005056c00008
        97a4f9d6-ed63-11ed-8d87-005056c00008
        97a8f17a-ed63-11ed-8d87-005056c00008

Activiti 工作流 学习笔记_第105张图片

//查询我的待办任务
    @Test
    public void getTasksByAssignee(){
        List<Task> list = taskService.createTaskQuery()
                .taskAssignee("bajie")
                .list();
        for (Task tk : list){
            System.out.println("Id:"+tk.getId());
            System.out.println("Name:"+tk.getName());
            System.out.println("执行人Assignee:"+tk.getAssignee());
        }
    }

Activiti 工作流 学习笔记_第106张图片

	//完成任务带参数
    @Test
    public void completeTaskWItchArgs(){
        Map<String,Object> variables = new HashMap<String,Object>();
        variables.put("day","1");
        taskService.complete("97a8f17a-ed63-11ed-8d87-005056c00008",variables);
        System.out.println("完成任务");
    }

Activiti 工作流 学习笔记_第107张图片

   //任务查询
    @Test
    public void getTasks(){
        List<Task> list  = taskService.createTaskQuery().list();
        for (Task tk : list){
            System.out.println("Id:"+tk.getId());
            System.out.println("Name:"+tk.getName());
            System.out.println("执行人Assignee:"+tk.getAssignee());
        }
    }

Activiti 工作流 学习笔记_第108张图片

 @Test
    public void completeTask2(){
        //沙僧包含任务:a7958645-ed64-11ed-9b87-005056c00008
        //悟空包含任务:a7958647-ed64-11ed-9b87-005056c00008
        taskService.complete("a7958645-ed64-11ed-9b87-005056c00008");
        taskService.complete("a7958647-ed64-11ed-9b87-005056c00008");
        System.out.println("完成任务");
    }

Activiti 工作流 学习笔记_第109张图片
Activiti 工作流 学习笔记_第110张图片

3.9 Activiti 新特性

/*
 * Copyright 2010-2020 Alfresco Software, Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.test.activiti;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Component;

import java.util.Collection;

@Component
public class SecurityUtil {
        //登陆类 借助spring security
    private Logger logger = LoggerFactory.getLogger(SecurityUtil.class);

    @Autowired
    private UserDetailsService userDetailsService;

    public void logInAs(String username) { //用username去登录

        UserDetails user = userDetailsService.loadUserByUsername(username);
        if (user == null) {
            throw new IllegalStateException("User " + username + " doesn't exist, please provide a valid user");
        }
        logger.info("> Logged in as: " + username);
        SecurityContextHolder.setContext(new SecurityContextImpl(new Authentication() {
            @Override
            public Collection<? extends GrantedAuthority> getAuthorities() {
                return user.getAuthorities();
            }

            @Override
            public Object getCredentials() {
                return user.getPassword();
            }

            @Override
            public Object getDetails() {
                return user;
            }

            @Override
            public Object getPrincipal() {
                return user;
            }

            @Override
            public boolean isAuthenticated() {
                return true;
            }

            @Override
            public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {

            }

            @Override
            public String getName() {
                return user.getUsername();
            }
        }));
        org.activiti.engine.impl.identity.Authentication.setAuthenticatedUserId(username);
    }
}

用户创建:(仅用于测试)

/*
 * Copyright 2010-2020 Alfresco Software, Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.test.activiti;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import static java.util.Arrays.asList;

//@Configuration
public class DemoApplicationConfiguration {

    private Logger logger = LoggerFactory.getLogger(DemoApplicationConfiguration.class);

    //@Bean
    public UserDetailsService myUserDetailsService() {

        InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();

        String[][] usersGroupsAndRoles = {
                {"bob", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},
                {"bajie", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},//john
                {"wukong", "password", "ROLE_ACTIVITI_USER", "GROUP_activitiTeam"},//hannah
                {"other", "password", "ROLE_ACTIVITI_USER", "GROUP_otherTeam"},
                {"system", "password", "ROLE_ACTIVITI_USER"},
                {"admin", "password", "ROLE_ACTIVITI_ADMIN"},
        };

        for (String[] user : usersGroupsAndRoles) {
            List<String> authoritiesStrings = asList(Arrays.copyOfRange(user, 2, user.length));
            logger.info("> Registering new user: " + user[0] + " with the following Authorities[" + authoritiesStrings + "]");
            inMemoryUserDetailsManager.createUser(new User(user[0], passwordEncoder().encode(user[1]),  //创建用户密码 创建用户 创建项目 创建用户
                    authoritiesStrings.stream().map(s -> new SimpleGrantedAuthority(s)).collect(Collectors.toList())));
        }

        return inMemoryUserDetailsManager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

}

写代码:

package com.test.activiti;

import org.activiti.api.model.shared.model.VariableInstance;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.core.authority.AuthorityUtils;

import java.util.List;

@SpringBootTest
public class Part8_ProcessRuntime {

    @Autowired
    private ProcessRuntime processRuntime;

    @Autowired
    private SecurityUtil securityUtil;
   
}

Activiti 工作流 学习笔记_第111张图片

//获取流程实例
    @Test
    public void getProcessInstance() {
        securityUtil.logInAs("bajie");
        Page<ProcessInstance> processInstancePage = processRuntime
                .processInstances(Pageable.of(0, 100));//分页查询

        System.out.println("流程实例数量:" + processInstancePage.getTotalItems());

        List<ProcessInstance> list = processInstancePage.getContent();

        for (ProcessInstance pi : list) {
            System.out.println("Id:" + pi.getId());
            System.out.println("Name:" + pi.getName());
            System.out.println("StartDate:" + pi.getStartDate());
            System.out.println("Status:" + pi.getStatus());
            System.out.println("ProcessDefinitionId:" + pi.getProcessDefinitionId()); //流程定义Id
            System.out.println("ProcessDefinitionKey:" + pi.getProcessDefinitionKey());//流程定义key
            
        }
    }

7 engine下,5和6都是api下

activiti
Activiti 工作流 学习笔记_第112张图片
Activiti 工作流 学习笔记_第113张图片
Activiti 工作流 学习笔记_第114张图片

//启动流程实例
    @Test
    public void startProcessInstance() {
        securityUtil.logInAs("bajie");
        ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
                .start().withProcessDefinitionKey("myProcess_ProcessRuntime")
                .withName("第一个流程实例名称")
                //.withVariable("","")
                .withBusinessKey("自定义Key")
                .build()
        );
    }

Activiti 工作流 学习笔记_第115张图片

//获取流程实例
    @Test
    public void getProcessInstance() {
        securityUtil.logInAs("bajie");
        Page<ProcessInstance> processInstancePage = processRuntime
                .processInstances(Pageable.of(0, 100));//分页查询

        System.out.println("流程实例数量:" + processInstancePage.getTotalItems());

        List<ProcessInstance> list = processInstancePage.getContent();

        for (ProcessInstance pi : list) {
            System.out.println("Id:" + pi.getId());
            System.out.println("Name:" + pi.getName());
            System.out.println("StartDate:" + pi.getStartDate());
            System.out.println("Status:" + pi.getStatus());
            System.out.println("ProcessDefinitionId:" + pi.getProcessDefinitionId()); //流程定义Id
            System.out.println("ProcessDefinitionKey:" + pi.getProcessDefinitionKey());//流程定义key

        }
    }

Activiti 工作流 学习笔记_第116张图片

//删除流程实例
    @Test
    public void delProcessINstance() {
        securityUtil.logInAs("bajie");
        ProcessInstance processInstance = processRuntime.delete(ProcessPayloadBuilder
                .delete()
                .withProcessInstanceId("31a677d2-ed6b-11ed-b88c-005056c00008")
                .build());
    }

Activiti 工作流 学习笔记_第117张图片
Activiti 工作流 学习笔记_第118张图片

 //挂起流程示例
    @Test
    public void suspendProcessInstance() {
        securityUtil.logInAs("bajie");
        ProcessInstance processInstance = processRuntime.suspend(ProcessPayloadBuilder
                .suspend()
                .withProcessInstanceId("129663bb-ebe4-11ed-afeb-005056c00008")
                .build());
    }

之前

Activiti 工作流 学习笔记_第119张图片
Activiti 工作流 学习笔记_第120张图片
Activiti 工作流 学习笔记_第121张图片

//激活流程实例
    @Test
    public void resumeProcessInstance() {
        securityUtil.logInAs("bajie");
        ProcessInstance processInstance = processRuntime.resume(ProcessPayloadBuilder
                .resume()
                .withProcessInstanceId("129663bb-ebe4-11ed-afeb-005056c00008")
                .build());
    }

Activiti 工作流 学习笔记_第122张图片
Activiti 工作流 学习笔记_第123张图片

//流程实例参数
    @Test
    public void getVariable(){
        securityUtil.logInAs("bajie");
        List<VariableInstance> list = processRuntime.variables(ProcessPayloadBuilder
                .variables()
                .withProcessInstanceId("a")
                .build());
        for (VariableInstance vi : list){
            System.out.println("-------------");
            System.out.println("Name:"+vi.getName());
            System.out.println("Value:"+vi.getValue());
            System.out.println("TaskId:"+vi.getTaskId());
            System.out.println("ProcessInstance:"+vi.getProcessInstanceId());
        }
    }

排他网关:

Activiti 工作流 学习笔记_第124张图片
Activiti 工作流 学习笔记_第125张图片

TaskRuntime

package com.test.activiti;

import org.activiti.api.runtime.shared.query.Page;
import org.activiti.api.runtime.shared.query.Pageable;
import org.activiti.api.task.model.Task;
import org.activiti.api.task.model.builders.TaskPayloadBuilder;
import org.activiti.api.task.runtime.TaskRuntime;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class Part9_TaskRuntime {


    @Autowired
    private TaskRuntime taskRuntime;


    @Autowired
    private SecurityUtil securityUtil;


    //获取当前登录用户任务
    @Test
    public void getTask() {
        securityUtil.logInAs("bajie"); //真实情况下没有
        Page<Task> tasks = taskRuntime.tasks(Pageable.of(0, 100));
        List<Task> list = tasks.getContent();
        for (Task tk : list) {
            System.out.println("Id:" + tk.getId());
            System.out.println("Name:" + tk.getName());
            System.out.println("Status:" + tk.getStatus());
            System.out.println("CreatedDate:" + tk.getCreatedDate());
            if (tk.getAssignee() == null) {
                //候选人为当前登录用户,null的时候需要前端拾取
                System.out.println("Assignee: 待拾取任务");
            } else {
                System.out.println("Assignee:" + tk.getAssignee());
            }
        }
    }
}

/*
* taskRuntime.tasks()源码
*/
 public Page<Task> tasks(Pageable pageable) {
        String authenticatedUserId = this.securityManager.getAuthenticatedUserId(); //用到了spring security
        if (authenticatedUserId != null && !authenticatedUserId.isEmpty()) {
            List<String> userGroups = this.userGroupManager.getUserGroups(authenticatedUserId);
            return this.tasks(pageable, TaskPayloadBuilder.tasks().withAssignee(authenticatedUserId).withGroups(userGroups).build());
        } else {
            throw new IllegalStateException("You need an authenticated user to perform a task query");
        }
    }

Activiti 工作流 学习笔记_第126张图片
Activiti 工作流 学习笔记_第127张图片

 //完成任务
    @Test
    public void completeTask() {
        securityUtil.logInAs("bajie"); //真实情况下没有
        Task task = taskRuntime.task("3afd8512-ebe3-11ed-92b6-005056c00008");
        if (task.getAssignee() == null) {
            taskRuntime.claim(TaskPayloadBuilder.claim()
                    .withTaskId(task.getId())
                    .build());
        }
        taskRuntime.complete(TaskPayloadBuilder
                .complete()
                .withTaskId(task.getId())
                .build());
        System.out.println("任务执行完成");
    }

Activiti 工作流 学习笔记_第128张图片
Activiti 工作流 学习笔记_第129张图片

3.10 Spring Security

  • 认证
  • 鉴权、授权

用户三种来源

  • application.properties 配置用户
  • 代码中配置内存用户
  • 从数据库中加载用户

知识点

  • 内存登陆改为数据库登陆
  • SpringSecurity 配置文件详解
  • 登录相应处理方案
package com.test.activiti.security;


import com.test.activiti.mapper.UserInfoBeanMapper;
import com.test.activiti.pojo.UserInfoBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;


@Component //变成一个组件
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UserInfoBeanMapper userInfoBeanMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//        //写死
//        String password = passwordEncoder().encode("111");
//
//        return new User(username
//                ,password
//                ,AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ACTIVITI_USER")); //给用户增加角色
//
        //读取数据库判断用户
        //如果用户是null抛出异常
        //返回用户信息

        UserInfoBean userInfoBean = userInfoBeanMapper.selectByUserName(username);
        if (userInfoBean == null) {
            throw new UsernameNotFoundException("数据库中无此用户");
        }
        return userInfoBean;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

创建实体类:

package com.test.activiti.pojo;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.Collection;
import java.util.stream.Collectors;

@Component
public class UserInfoBean implements UserDetails {
    private Long id;
    private String name;
    private String username;
    private String password;
    private String address;
    private String roles;

    public String getAddress(){
        return address;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return Arrays.stream(roles.split(","))
                .map(s -> new SimpleGrantedAuthority(s))
                        .collect(Collectors.toList());
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

mapper:

package com.test.activiti.mapper;

import com.test.activiti.pojo.UserInfoBean;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Component;

@Mapper
@Component
public interface UserInfoBeanMapper {
    
    @Select("select * from user where username = #{username}")
    UserInfoBean selectByUserName(@Param("username")String username);
}

这里因为之前tomcat用了80端口,因此此项目启动用8081端口启用:

Activiti 工作流 学习笔记_第130张图片

package com.test.activiti.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class ActivitiSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private LoginSuccessHandler loginSuccessHandler;

    @Autowired
    private LoginFailHandler loginFailHandler;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception{
        httpSecurity
                .formLogin()
                //登陆方法
                .loginPage("/login")
                .loginProcessingUrl("/login") //登陆验证之后的处理
                //登陆后
                .successHandler(loginSuccessHandler)
                .failureHandler(loginFailHandler)
                .and()
                .authorizeRequests()
                .anyRequest()
                .permitAll()
                .and()
                .logout()
                .permitAll()
                .and().csrf().disable()
                .headers()
                .frameOptions()
                .disable();
    }
}

登陆成功处理类:

package com.test.activiti.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.activiti.util.AjaxResponse;
import com.test.activiti.util.GlobalConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component("loginSuccessHandler")
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authentication) throws IOException, ServletException {
        AuthenticationSuccessHandler.super.onAuthenticationSuccess(request, response, chain, authentication);
    }

    @Override //后台从页面拿到什么值 HttpServletRequest 后台向页面输出什么值 HttpServletResponse
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write("登陆成功LoginSuccessHandler");
//        response.getWriter().write("获取登录名:"+authentication.getName());

//        response.getWriter().write(objectMapper.writeValueAsString(AjaxResponse.AjaxData(0,"成功",authentication.getName()))
//        );

        response.getWriter().write(
                objectMapper.writeValueAsString(
                        AjaxResponse.AjaxData(
                                GlobalConfig.ResponseCode.SUCCESS.getCode(),
                                GlobalConfig.ResponseCode.SUCCESS.getDesc(),
                                authentication.getName())
        ));
    }
}

这里需要使用接口测试工具来进行测试:

Activiti 工作流 学习笔记_第131张图片
Activiti 工作流 学习笔记_第132张图片

测试登录失败:

package com.test.activiti.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.test.activiti.util.AjaxResponse;
import com.test.activiti.util.GlobalConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class LoginFailHandler implements AuthenticationFailureHandler {


    @Autowired
    private ObjectMapper objectMapper;


    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        response.setStatus(500);
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());

        response.setContentType("application/json;charset=UTF-8");
//        response.getWriter().write("登陆失败,原因是:"+ exception.getMessage());

        response.getWriter().write(
                objectMapper.writeValueAsString(
                        AjaxResponse.AjaxData(
                                GlobalConfig.ResponseCode.ERROR.getCode(),
                                GlobalConfig.ResponseCode.ERROR.getDesc(),
                                "登陆失败,原因是:"+ exception.getMessage())
                ));

    }
}

Activiti 工作流 学习笔记_第133张图片

3.11 BPMN.js

a. 下载BPMN.js

bpmn:https://bpmn.io/

Activiti 工作流 学习笔记_第134张图片
Activiti 工作流 学习笔记_第135张图片

bpmn download:https://bpmn.io/toolkit/bpmn-js/download/

点击 Explore Examples

Activiti 工作流 学习笔记_第136张图片
Activiti 工作流 学习笔记_第137张图片

i18n可以根据需要修改语言配置

properties-panel可以创建BPMN.js的控制面板

Activiti 工作流 学习笔记_第138张图片

b. 安装Node.js
  • 下载地址https://nodejs.org/en/download/
  • Node.js安装指导
  • 运行:
    • npm install
    • npm run dev

Activiti 工作流 学习笔记_第139张图片
Activiti 工作流 学习笔记_第140张图片
Activiti 工作流 学习笔记_第141张图片
Activiti 工作流 学习笔记_第142张图片
Activiti 工作流 学习笔记_第143张图片

可自定义配置
Activiti 工作流 学习笔记_第144张图片

你可能感兴趣的:(Java开发,学习,笔记,java,spring)