SpringBoot 2.1.7.RELEASE + Activiti 5.18.0 喂饭级练习手册

环境准备

win10

eclipse 2023-03

eclipse Activiti插件

Mysql 5.x

Activiti的作用等不再赘叙,直接上代码和细节

POM

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>2.1.7.RELEASE</version>
	<relativePath /> <!-- lookup parent from repository -->
</parent>

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	
	<dependency>
		<groupId>org.activiti</groupId>
		<artifactId>activiti-spring-boot-starter-basic</artifactId>
		<version>5.18.0</version>
	</dependency>
	
	<dependency>
		<groupId>mysql</groupId>
		<artifactId>mysql-connector-java</artifactId>
	</dependency>
	
	<dependency>
		<groupId>com.zaxxer</groupId>
		<artifactId>HikariCP</artifactId>
	</dependency>
</dependencies>

启动类

import org.activiti.spring.boot.SecurityAutoConfiguration;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class App {
	
	public static void main(String[] args) {
		SpringApplication.run(App.class, args);
	}
}

配置文件 application.yml

首次启动时,会自动创建activiti的表

spring:
  datasource:
    driveClassName: com.mysql.cj.jdbc.Driver
    #&nullCatalogMeansCurrent=true
    url: jdbc:mysql://127.0.0.1:3306/act?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconnect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
    username: root
    password: root
    hikari:
      mininum-idle: 5
      idle-timeout: 30000
      connection-timeout: 30000
      maxinum-pool-size: 10
      max-lifetime: 60000
      connect-test-query: select 1

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/test">
  <process id="testP" name="测试一把" isExecutable="true">
    <startEvent id="startevent1" name="Start">
      <extensionElements>
        <activiti:executionListener event="start" delegateExpression="${startListener}"></activiti:executionListener>
      </extensionElements>
    </startEvent>
    <endEvent id="endevent1" name="End">
      <extensionElements>
        <activiti:executionListener event="end" delegateExpression="${endListener}"></activiti:executionListener>
      </extensionElements>
    </endEvent>
    <serviceTask id="servicetask1" name="Service Task1" activiti:delegateExpression="${myServiceTask1}"></serviceTask>
    <serviceTask id="servicetask2" name="Service Task2" activiti:delegateExpression="${myServiceTask2}"></serviceTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="servicetask1"></sequenceFlow>
    <sequenceFlow id="flow2" sourceRef="servicetask1" targetRef="servicetask2"></sequenceFlow>
    <userTask id="usertask1" name="User Task">
      <extensionElements>
        <activiti:taskListener event="create" delegateExpression="${userTaskListener}"></activiti:taskListener>
      </extensionElements>
    </userTask>
    <sequenceFlow id="flow3" sourceRef="servicetask2" targetRef="usertask1"></sequenceFlow>
    <exclusiveGateway id="exclusivegateway1" name="Exclusive Gateway"></exclusiveGateway>
    <serviceTask id="servicetask3" name="Service Task3" activiti:delegateExpression="${myServiceTask3}"></serviceTask>
    <serviceTask id="servicetask4" name="Service Task4" activiti:delegateExpression="${myServiceTask4}"></serviceTask>
    <sequenceFlow id="flow4" sourceRef="usertask1" targetRef="exclusivegateway1"></sequenceFlow>
    <sequenceFlow id="flow5" sourceRef="exclusivegateway1" targetRef="servicetask3">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${age > 18}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow6" sourceRef="exclusivegateway1" targetRef="servicetask4">
      <conditionExpression xsi:type="tFormalExpression"><![CDATA[${age <= 18}]]></conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="flow7" sourceRef="servicetask3" targetRef="endevent1"></sequenceFlow>
    <sequenceFlow id="flow8" sourceRef="servicetask4" targetRef="endevent1"></sequenceFlow>
  </process>
  <bpmndi:... 这些不展示了/>
</definitions>

部署流程

import org.activiti.engine.RepositoryService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 部署流程
 * 
 * @author wusc
 * @since 2023年8月8日16:16:40
 */
@Component
public class ProcessDeployConfig {

	private final Logger logger = LoggerFactory.getLogger(ProcessDeployConfig.class);

	@Autowired
	private RepositoryService repositoryService;

	@PostConstruct
	public void deploy() {
		logger.info("部署流程");
		repositoryService.createDeployment()
			.name("我今天来测试一把")
			.addClasspathResource("testP.bpmn")// 我这个就放在src/main/resources目录下
			.deploy();
	}
}

启动流程

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

import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ActivitiController {

	private final Logger logger = LoggerFactory.getLogger(ActivitiController.class);

	@Autowired
	private RuntimeService runtimeService;// 直接引用

	@RequestMapping("/act/start")
	public String start(
			@RequestParam(value = "name") String name, 
			@RequestParam(value = "phone") String phone) {
		// 全局变量,可以放订单信息、用户信息、流程状态、流程步骤等
		Map<String, Object> paramMap = new HashMap<>();
		paramMap.put("name", name);
		paramMap.put("phone", phone);
		
		// 任务唯一KEY,这里一般用orderId/orderNo 等唯一键来代替,后面处理任务时有用到
		String uuid = UUID.randomUUID().toString();
		logger.info("uuid:{}", uuid);
		
		// 流程KEY,BPMN XML的ID
		String processKey = "testP";
		runtimeService.startProcessInstanceByKey(processKey, uuid, paramMap);
		return "success";
	}
}

Start 节点

start/end 不设置监听器也可以 

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * 执行监听器,流程启动时触发
 * 
 * @author wusc
 * @since 2023年8月8日16:01:48
 */
@Component("startListener")
public class StartListener implements ExecutionListener {

	private static final long serialVersionUID = 422573009417731884L;

	private final Logger logger = LoggerFactory.getLogger(StartListener.class);

	@Override
	public void notify(DelegateExecution execution) throws Exception {
		logger.info("[{}]流程开始", execution.getEngineServices().getRepositoryService()
				.getProcessDefinition(execution.getProcessDefinitionId()).getKey());
	}
}

 

 系统任务

系统任务是自动化处理的,需要指定哪个类来执行逻辑

Service Task1、Task2处理方式相同,execute()方法里面写自己的逻辑即可

import java.util.Map;

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * 系统自动任务,需要实现JavaDelegate,execute()执行完毕,当前任务完成,自动进入下一任务
 * 
 * myServiceTask1,实例名 = 上图Main config里面的变量
 * 
 * @author wusc
 * @since 2023年8月8日16:28:55
 *
 */
@Component("myServiceTask1")
public class MyServiceTask1 implements JavaDelegate {

	private final Logger logger = LoggerFactory.getLogger(MyServiceTask1.class);

	@Override
	public void execute(DelegateExecution execution) throws Exception {
		logger.info("activitiId:{}", execution.getCurrentActivityId());
		logger.info("activitiName:{}", execution.getCurrentActivityName());

		Map<String, Object> paramMap = execution.getVariables();
		logger.info("获取全局变量,name={}", paramMap.get("name"));
		logger.info("获取全局变量,phone={}", paramMap.get("phone"));
		
		// 一套增删改查
	}
}

用户任务 

与系统任务不同,用户任务不指定哪个类来执行逻辑;

1. 设置监听器,给任务绑定用户;

2. 通过controller接收http请求触发,经唯一键和用户标识查到当前任务,手动Complete;

 

import org.activiti.engine.delegate.DelegateTask;
import org.activiti.engine.delegate.TaskListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

/**
 * 用户任务就是具体某个人的任务,所以需要先分配给具体用户,然后该用户来处理
 * 
 * @author wusc
 * @since 2023年8月8日16:13:58
 *
 */
@Component("userTaskListener")
public class UserTaskListener implements TaskListener {

	private static final long serialVersionUID = -3300154910592367754L;

	private final Logger logger = LoggerFactory.getLogger(UserTaskListener.class);

	@Override
	public void notify(DelegateTask delegateTask) {
		logger.info("分配用户任务到具体用户身上");
		delegateTask.setOwner("Pony");// 这个任务属于Pony(这里可以用 用户ID来填充)
		delegateTask.setAssignee("wusc");// 这个任务Pony没空处理,wusc可以帮他处理,也就是二人都可以处理
	}
}

处理用户任务,taskService.complete


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

import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ActivitiController {

	private final Logger logger = LoggerFactory.getLogger(ActivitiController.class);

	@Autowired
	private TaskService taskService;


	@RequestMapping("/complete")
	public String complete(
			@RequestParam(value = "uuid") String uuid, 
			@RequestParam(value = "age") int age) {
		logger.info("age={}", age);
		
		Task task = taskService.createTaskQuery()
				.processInstanceBusinessKey(uuid)// 通过唯一键定位
				.taskAssignee("wusc")// 查自己的任务
				.list()
				.get(0);
		
		logger.info("完成任务,taskId={}", task.getId());

		Map<String, Object> userMap = new HashMap<>();
		userMap.put("age", age);
		taskService.complete(task.getId(), userMap);
		return "success";
	}

}

 排他网关

排他网关通过age变量确定走哪一条路

 

 系统任务

同Service Task1一样,在execute()方法内些自己的逻辑即可,方法执行完自动到下一个节点

import java.util.Map;

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.JavaDelegate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component("myServiceTask3")
public class MyServiceTask3 implements JavaDelegate {

	private final Logger logger = LoggerFactory.getLogger(MyServiceTask3.class);
	
	@Override
	public void execute(DelegateExecution execution) throws Exception {
		logger.info("activitiId:{}", execution.getCurrentActivityId());
		logger.info("activitiName:{}", execution.getCurrentActivityName());
		
		Map<String, Object> paramMap = execution.getVariables();
		logger.info("获取全局变量,age={}", paramMap.get("age"));
	}
	
}

 End 节点

import org.activiti.engine.delegate.DelegateExecution;
import org.activiti.engine.delegate.ExecutionListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;


@Component("endListener")
public class EndListener implements ExecutionListener {

	private static final long serialVersionUID = 422573009417731884L;

	private final Logger logger = LoggerFactory.getLogger(EndListener.class);

	@Override
	public void notify(DelegateExecution execution) throws Exception {
		logger.info("[{}]流程结束", execution.getEngineServices().getRepositoryService()
				.getProcessDefinition(execution.getProcessDefinitionId()).getKey());
	}
}

执行日志

通过日志可以看到,启动流程后,Service Task1、Service Task2自动执行了,执行到User Task时,触发了任务监听器,给用户任务分配了具体用户

业务KEY=78bc0087-79a9-4205-bdc5-7144d936ffa9,调用完成任务接口

因为age=18,经过排他网关判断,进入Service Task4节点,最后流程结束

 

 思考题

看日志,调用完成任务接口,Service Task4的线程ID与接口处理业务时的线程ID是相同的,这是为啥?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/64627.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Matplotlib引领数据图表绘制

Matplotlib引领数据图表绘制 前言图像得组成画图设置 figure设置标题设置坐标轴设置 label 和 legend添加注释使用子图中文乱码解决保存图形显示图形条形图直方图散点图饼状图 总结 前言 在数据科学领域&#xff0c;数据可视化是一种强大的工具&#xff0c;能够将复杂的数据转…

华为推出手机系统云翻新服务:什么是云翻新?如何使用?

华为手机系统云翻新是华为推出的一项功能&#xff0c;旨在通过云服务提供系统翻新的服务。它可以帮助用户对手机的系统进行优化和更新&#xff0c;以提高手机的性能和流畅度。具体而言&#xff0c;华为手机系统云翻新功能提供了免费的云空间&#xff0c;用户可以将手机中的系统…

【C++笔记】C++启航之为C语言填坑的语法

【C笔记】C启航之为C语言填坑的语法 一、命名空间1、为什么要引入命名空间&#xff1f;2、命名空间的基本用法3、展开命名空间4、命名空间的套娃5、命名空间的自动合并 二、缺省参数1、为什么要引入缺省参数&#xff1f;2、缺省参数的基本用法3、缺省的参数必须从右向左4、缺省…

用Python批量复制文件,方法有9种,方便快捷

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 当我们复制一个文件时用复制粘贴就可以了&#xff0c;如果是多个文件呢&#xff1f; 就会很麻烦了&#xff01; 今天给大家介绍一下用Python批量复制文件&#xff0c;方法有九种&#xff01;希望对你有帮助 1. Shutil Copy…

【音视频】vms布署说明

目录 外场布署场景&#xff08;99%&#xff09; 研发实验场景&#xff08;1%&#xff09; 高级玩法 证书安装方法 外场布署场景&#xff08;99%&#xff09; 下面两种场景&#xff0c;为本产品主要应用场景&#xff0c;2023-08-08日后&#xff08;统一所有证书&#xff09;…

macOS下Django环境搭建-docker运行Django

1. macOS升级pip /Library/Developer/CommandLineTools/usr/bin/python3 -m pip install --upgrade pip 2. 卸载Python3.9.5版本 $ sudo rm -rf /usr/local/bin/python3 $ sudo rm -rf /usr/local/bin/pip3 $ sudo rm -rf /Library/Frameworks/Python.framework 3. 安装P…

Vue3自定义简单的Swiper滑动组件-触控板滑动鼠标滑动左右箭头滑动-demo

代码实现了一个基本的滑动功能&#xff0c;通过鼠标按下、鼠标松开和鼠标移动事件来监听滑动操作。 具体实现逻辑如下&#xff1a; 在 onMounted 钩子函数中&#xff0c;我们为滚动容器添加了三个事件监听器&#xff1a;mousedown 事件&#xff1a;当鼠标按下时&#xff0c;设置…

TCP 三次握手,四次挥手

1、三次握手 第一次握手 SYN 等于1&#xff0c;SeqX 第二次握手 SYN等于1 ACK等于1&#xff0c;SeqY&#xff0c;AckX1 第三次SYN等于0 ACK等于1&#xff0c;SeqX1&#xff0c;AckY1 ackRow都是对应请求seqraw&#xff0c;三次握手后&#xff0c;Seq就是服务器前一个包中的ac…

Unity数字可视化学校_昼夜(三)

1、删除不需要的 UI using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI;public class EnvControl : MonoBehaviour {//UIprivate Button btnTime;private Text txtTime; //材质public List<Material> matListnew Li…

解决word打字卡顿问题的方法

❤ 2023.8.5 ❤ 最近整理论文&#xff0c;本来我是wps死忠粉&#xff0c;奈何wps不支持latex公式。。。 无奈用起了word&#xff0c;但是谁想字数稍微多了一点&#xff0c;word就卡得欲仙欲死&#xff0c;打个字过去2s才显示出来&#xff0c;删除的时候都不知道自己删了几个字…

QGIS二次开发三:显示Shapefile

Shapefile 为 OGR 所支持的最重要的数据格式之一&#xff0c;自然可以被 QGIS 加载。那么该如何显示Shapefile呢&#xff1f; 一、先上代码 #include <qgsapplication.h> #include <qgsproviderregistry.h> #include <qgsmapcanvas.h> #include <qgsvec…

每天一道leetcode:剑指 Offer 50. 第一个只出现一次的字符(适合初学者)

今日份题目&#xff1a; 在字符串 s 中找出第一个只出现一次的字符。如果没有&#xff0c;返回一个单空格。 s 只包含小写字母。 示例1 输入&#xff1a;s "abaccdeff" 输出&#xff1a;b 示例2 输入&#xff1a;s "" 输出&#xff1a; 提示 0 …

MySQL流程控制(二十八)

二八佳人体似酥&#xff0c;腰悬利剑斩愚夫&#xff0c;虽然不见人头落,暗里教君骨髓枯。 上一章简单介绍了MySQL变量(二十七) ,如果没有看过,请观看上一章 一. 定义条件与处理程序 定义条件是事先定义程序执行过程中可能遇到的问题&#xff0c;处理程序定义了在遇到问题时应…

BIO,NIO,AIO总结

文章目录 1. BIO (Blocking I/O)1.1 传统 BIO1.2 伪异步 IO1.3 代码示例 1.4 总结2. NIO (New I/O)2.1 NIO 简介2.2 NIO的特性/NIO与IO区别1)Non-blocking IO&#xff08;非阻塞IO&#xff09;2)Buffer(缓冲区)3)Channel (通道)4)Selector (选择器) 2.3 NIO 读数据和写数据方式…

SpringMVC基于SpringBoot的最基础框架搭建——包含数据库连接

SpringMVC基于SpringBoot的最基础框架搭建——包含数据库连接 背景目标依赖配置文件如下项目结构如下相关配置如下启动代码如下Controller如下启动成功接口调用成功 背景 工作做了一段时间&#xff0c;回忆起之前有个公司有线下笔试&#xff0c;要求考生做一个什么功能&#x…

无涯教程-Perl - fileno函数

描述 此函数返回指定的FILEHANDLE的文件描述符号(由C和POSIX函数使用)。通常,这仅在使用select函数和任何低级tty函数时才有用。 语法 以下是此函数的简单语法- fileno FILEHANDLE返回值 此函数返回FILEHANDLE的文件描述符(数字),失败时不确定。 Perl 中的 fileno函数 - 无…

考研算法38天:反序输出 【字符串的翻转】

题目 题目收获 很简单的一道题&#xff0c;但是还是有收获的&#xff0c;我发现我连scanf的字符串输入都忘记咋用了。。。。。我一开始写的 #include <iostream> #include <cstring> using namespace std;void deserve(string &str){int n str.size();int…

c语言每日一练(3)

前言&#xff1a;每日一练系列&#xff0c;每一期都包含5道选择题&#xff0c;2道编程题&#xff0c;博主会尽可能详细地进行讲解&#xff0c;令初学者也能听的清晰。每日一练系列会持续更新&#xff0c;暑假时三天之内必有一更&#xff0c;到了开学之后&#xff0c;将看学业情…

element-plus:el-date-picker日期只选择年月不要日

<el-date-picker v-model"value" type"month" format"YYYY-MM" value-format"YYYY-MM" />使用format属性将时间显示格式修改为YYYY–MM 年月格式 使用value-format将绑定值的格式修改为YYYY–MM年月格式

vcruntime140_1.dll修复的方法大全,缺失vcruntime140_1.dll解决方法分享

vcruntime140_1.dll这个文件在电脑里属于挺重要的一个文件&#xff0c;一但它缺失了&#xff0c;那么很多程序都是运行不了的&#xff0c;今天我们就来讲解一下这个vcruntime140_1.dll修复以及它的一些作用和属性。 一.vcruntime140_1.dll的作用 vcruntime140_1.dll是Microso…