Springboot整合Activiti详解


文章目录

  • 版本依赖
  • 配置文件
  • 需要注意的问题
  • 画流程图
  • activiti服务类进行编写
    • 流程部署
    • 流程定义
    • 启动流程
    • 流程实例
  • 测试流程
    • 启动流程
    • 完成任务
    • 受理任务


版本依赖

  • 开发工具 IDEA
  • SpringBoot 2.4.5(这里我试过SpringBoot 3.1.1版本,Activiti没有启动,应该是依赖冲突了,后改成了2.4.5版本)
  • Activiti 7.1.0.M6

父项目pom.xml

    <dependencyManagement><dependencies><!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.4.5</version><type>pom</type><scope>import</scope></dependency><!-- https://mvnrepository.com/artifact/org.activiti.dependencies/activiti-dependencies --><dependency><groupId>org.activiti.dependencies</groupId><artifactId>activiti-dependencies</artifactId><version>7.1.0.M6</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>

子项目pom.xml

    <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency></dependencies>

配置文件

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Drivertype: com.zaxxer.hikari.HikariDataSourceusername: rootpassword: 111111url: jdbc:mysql://127.0.0.1:3306/activiti?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false&useInformationSchema=truehikari:#连接池做大连接数maximum-pool-size: 30#连接池空闲连接最小数量#minimum-idle: 10#允许连接在连接池中闲置最长时间#idle-timeout: 30000#池中连接最长生命周期max-lifetime: 120000#等待来自池的连接的最大毫秒数connection-timeout: 30000activiti:#自动更新数据库结构#1.flase:默认值。activiti在启动时,对比数据库表中保存的版本,如果没有表或者版本不匹配,将抛出异常#2.true: activiti会对数据库中所有表进行更新操作。如果表不存在,则自动创建#3.create_drop: 在activiti启动时创建表,在关闭时删除表(必须手动关闭引擎,才能删除表)#4.drop-create: 在activiti启动时删除原来的旧表,然后在创建新表(不需要手动关闭引擎)database-schema-update: true#activiti7默认不生成历史信息表,开启历史表db-history-used: true#记录历史等级 可配置的历史级别有none, activity, audit, full#none:不保存任何的历史数据,因此,在流程执行过程中,这是最高效的。#activity:级别高于none,保存流程实例与流程行为,其他数据不保存。#audit:除activity级别会保存的数据外,还会保存全部的流程任务及其属性。audit为history的默认值。#full:保存历史数据的最高级别,除了会保存audit级别的数据外,还会保存其他全部流程相关的细节数据,包括一些流程参数等。history-level: full#自动检查、部署流程定义文件check-process-definitions: false# asyncExecutorActivate是指activiti在流程引擎启动就激活AsyncExecutor,异步:true-开启(默认)、false-关闭async-executor-activate: true

配置文件这里注意要设置 database-sechema-update的属性为true,才会自动创建表。

需要注意的问题

在初次启动时可能会报错,报错如下:

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.security.core.userdetails.UserDetailsService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1790)at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1346)at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1300)at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:887)at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:791)... 142 more

这里是需要一个UserDetailsService的Bean,需要创建一个类去实现UserDetailsService接口

UserDetailsServiceImpl.class

package org.example.config;import org.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;@Component
public class UserDetailsServiceImpl implements UserDetailsService {@AutowiredUserService userService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return userService.findOneUserByName(username);}
}

UserService.class

package org.example.service;import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class UserService {public User findOneUserByName(String username){List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");return new User(username,"",authorities);}
}

启动成功后,数据库会生成25张表
在这里插入图片描述

画流程图

在使用IDEA时,需要下载一个插件
在这里插入图片描述
当这个插件安装好以后,新建文件会多一个选项
在这里插入图片描述
新建的xml文件可以通过view BPMN Diagram,转化为图表操作
在这里插入图片描述
在这里插入图片描述
如何通过图表操作画流程这里就不说了,可以自己摸索看看。

activiti服务类进行编写

package org.example.service;import org.activiti.engine.*;
import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentQuery;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;@Service
public class ActivitiService {@AutowiredRepositoryService repositoryService;@AutowiredRuntimeService runtimeService;@AutowiredTaskService taskService;@AutowiredHistoryService historyService;/*** 部署流程服务*/public void deployProcess(){String file = "bpmn/test.bpmn20.xml";Deployment deployment = repositoryService.createDeployment().addClasspathResource(file).name("流程部署测试").deploy();System.out.println("流程部署名称:"+deployment.getName());}/*** 查询所有部署的流程定义* @return*/public List<ProcessDefinition> getAllProcessDefinition(){ProcessDefinitionQuery query = repositoryService.createProcessDefinitionQuery();return query.orderByProcessDefinitionVersion().desc().list();}/*** 查询所有的部署* @return*/public List<Deployment> getAllDeployment(){DeploymentQuery query = repositoryService.createDeploymentQuery();return query.list();}/*** 查询所有流程实例* @return*/public List<ProcessInstance> getAllProcessInstance(){List<ProcessInstance> list = runtimeService.createProcessInstanceQuery().list();return list;}public List<Task> getAllProcessTask(){List<Task> list = taskService.createTaskQuery().list();return list;}/*** 查询用户待完成和待认领的任务* @param username* @return*/public List<Task> getAllProcessTaskByCandidateOrAssigned(String username){List<Task> list = taskService.createTaskQuery().taskCandidateOrAssigned(username).list();return list;}public List<Task> getAllProcessTaskByCandidate(String username){List<Task> list = taskService.createTaskQuery().taskCandidateUser(username).list();return list;}/*** 查询用户的待完成任务* @param username* @return*/public List<Task> getAllProcessTaskByAssigned(String username){List<Task> list = taskService.createTaskQuery().taskAssignee(username).list();return list;}public void complateTask(Task task, Map<String, Object> map){taskService.complete(task.getId(),map);}public void complateTask(Task task){taskService.complete(task.getId());}public void claimTask(Task task,String username){taskService.claim(task.getId(),username);}public ProcessInstance startProcess(String processDefinitionKey,String businessKey,Map<String,Object> map){return runtimeService.startProcessInstanceById(processDefinitionKey,businessKey,map);}public String getProcessDefinitionXml(String processDefinitionId) {ProcessDefinition processDefinition = repositoryService.getProcessDefinition(processDefinitionId);String resourceName = processDefinition.getResourceName();InputStream resourceAsStream = repositoryService.getResourceAsStream(processDefinition.getDeploymentId(), resourceName);try {return IOUtils.toString(resourceAsStream, StandardCharsets.UTF_8);} catch (IOException e) {// handle exception}return null;}public List<HistoricProcessInstance> getHistoryByBusinessKey(String businessKey){List<HistoricProcessInstance> list = historyService.createHistoricProcessInstanceQuery().processInstanceBusinessKey(businessKey).list();return list;}
}

在这里插入图片描述
上图是,如果需要使用activiti中提供的服务类,直接注入即可。即springboot整合activiti启动过程中,已经在容器中自动装配了对应的服务类,需要的时候,仅仅需要取来用即可。如果启动以后报错,容器没有自动装配对应的服务类,那很有可能是依赖冲突,activiti没有正常启动。

流程部署

流程部署有多种方式,这是通过资源路径部署的,也可以通过压缩包的形式部署流程。那就需要用到DeploymentBuilderaddZipInputStream方法,这里也是用到了设计模式中的建造者模式。
在这里插入图片描述

在这里插入图片描述

流程定义

这是一个查询所有部署的流程定义的方法
在这里插入图片描述
当一个流程部署以后,就会生成一个流程定义,流程图的xml信息也会存入数据库。每次部署流程图都会生成一个新的流程定义。
在这里插入图片描述

启动流程

启动流程可以通过流程定义的id或者key,如果想通过流程定义的id启动一个流程可以使用startProcessInstanceById方法,如果想通过流程定义的key启动一个流程可以使用startProcessInstanceByKey方法。在这里插入图片描述
这里要明白流程定义id和key的区别,流程定义的key是在画流程的时候可以设置的,通过设置流程的id,这个就是流程的key。
在这里插入图片描述

在这里插入图片描述
那流程的id呢,这是部署以后在数据库表的id。可以根据需要自行选择启动流程的方法。

流程实例

查询所有启动的流程实例
在这里插入图片描述
每次开始一个流程以后,就会生成一个流程实例。通过这个方法可以查看所有的流程实例。

测试流程

package org.example;import org.activiti.engine.history.HistoricProcessInstance;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.assertj.core.util.DateUtil;
import org.example.common.PrintUtil;
import org.example.entity.User;
import org.example.service.ActivitiService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.*;@SpringBootTest
public class SpringBootApplicationTest {@ResourceDataSource dataSource;@AutowiredActivitiService activitiService;@Testvoid context() throws SQLException {System.out.println(dataSource.getConnection());}@Testpublic void deployProcess(){activitiService.deployProcess();}@Testpublic void TestStartProcess(){ProcessDefinition processDefinition = activitiService.getAllProcessDefinition().get(0);String bussinessKey = this.getClass().getSimpleName();Map<String , Object> map = new HashMap<String , Object>();map.put("creator","Echo");activitiService.startProcess(processDefinition.getId(),bussinessKey,map);}@Testpublic void findAllDeployment(){List<Deployment> list = activitiService.getAllDeployment();PrintUtil.printTable(list);}@Testpublic void findAllProcessDefinition(){List<ProcessDefinition> list = activitiService.getAllProcessDefinition();PrintUtil.printTable(list);}@Testpublic void findAllProcessInstance(){List<ProcessInstance> list = activitiService.getAllProcessInstance();PrintUtil.printTable(list);}@Testpublic void findAllProcessTask(){List<Task> list = activitiService.getAllProcessTask();PrintUtil.printTable(list);}@Testpublic void findTaskByUserId(){String userId = "keaizp";List<Task> list = activitiService.getAllProcessTaskByCandidateOrAssigned(userId);PrintUtil.printTable(list);}@Testpublic void claimTask(){String userId="keaizp";List<Task> list = activitiService.getAllProcessTaskByCandidate(userId);PrintUtil.printTable(list);activitiService.claimTask(list.get(0),userId);list = activitiService.getAllProcessTaskByCandidate("zengpei");PrintUtil.printTable(list);}@Testpublic void complateUserTask(){String userId = "Echo";List<Task> list = activitiService.getAllProcessTaskByAssigned(userId);List<String> candidateUsers = new ArrayList<String>();candidateUsers.add("keaizp");candidateUsers.add("zengpei");Map<String,Object> map = new HashMap<String,Object>();map.put("approver",candidateUsers);activitiService.complateTask(list.get(0),map);}@Testpublic void processDefinitionXml(){List<ProcessDefinition> list = activitiService.getAllProcessDefinition();String xmlStr = activitiService.getProcessDefinitionXml(list.get(0).getId());System.out.println(xmlStr);}@Testpublic void findHistoryByBusinessKey(){List<HistoricProcessInstance> list = activitiService.getHistoryByBusinessKey("2023-07-02T22:45:45");PrintUtil.printTable(list);}@Testpublic void printTest(){List<User> users = new ArrayList<>();users.add(new User("keaizpeeeeeeeee",24,"male"));users.add(new User("Echo",24,"male"));users.add(new User("nick",24,"male"));users.add(new User("amy",24,"male"));PrintUtil.printTableFixed(users,16);}
}

启动流程

在这里插入图片描述
这里我用的是一个格式化的日期做bussinessKey,实际业务中最好使用 流程作业类型 + 一个业务台账id,比如:请假申请要走审批流程, 离职申请也要走审批流程,如果只用id作为bussinessKey的话,bussinessKey就无法满足唯一性。所以最好 前面加上作业类型。这里的map传入的是流程图里面的参数。
在这里插入图片描述
传入受理人,这里就会产生一个Task,就是用户的代办任务。

完成任务

在这里插入图片描述
当用户登录系统的时候,应该给用户提示,用户有未完成的代办事项,然后给出用户代办事项列表,做完一切以后,就是完成用户任务,这个时候,可以传入下一流程节点参与任务的人,当然你也可以传入几个候选人,candidate Users就是 候选人。可以传入一个用户id的列表,然后这几个人就会收到可以受理的任务,可以选择是否接受该任务。只要接受了任务,其他候选人就无法再接受此任务,同时这个任务的Assignee就会设置成受理人的id

受理任务

在这里插入图片描述
因为用户“keaizp”成为了候选者,他就可以看到自己可以受理的这个任务,用claim方法就可以受理该任务,当受理任务完成以后,再去看看另外一名候选者的受理任务,会发现已经没有待受理的任务了。


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Nginx-负载均衡

文章目录 nginx 负载均衡负载均衡策略&#xff08;方法、算法&#xff09;nginx配置round-robin加权轮询least-connectedip-hasp使用Https realip后端real server不使用realip模块后端real server使用realip模块 ab压力测试不同负载四层负载 7层负载4层和7层 nginx 负载均衡 负…

银河麒麟系统无法进入桌面拷贝备份文件

最近使用VMWare搭建银河麒麟系统升级后&#xff0c;无法进入桌面&#xff0c;而是进入tty1界面 这个时候如何想导出里面的文件就可以用文件共享的方式右键到虚拟机设置-选项&#xff0c;如图所示 选择一个共享目录 如d盘vm目录 登录tty1账号密码 ls列出文件 如图进行文件拷贝…

如何优雅的将 Docker 镜像从 1.43G 瘦身到 22.4MB

Docker 镜像的大小对于系统的 CI/CD 等都有影响&#xff0c;尤其是云部署场景。我们在生产实践中都会做瘦身的操作&#xff0c;尽最大的可能使用 Size 小的镜像完成功能。下文是一个简单的 ReactJS 程序上线的瘦身体验&#xff0c;希望可以帮助大家找到镜像瘦身的方向和灵感。 …

Python 列表 extend()函数使用详解

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;小白零基础《Python入门到精通》 extend函数使用详解 1、可以接收的参数1.1、添加字符串1.2、添加元组1.3、添加字…

【动手学深度学习】pytorch-参数管理

pytorch-参数管理 概述 我们的目标是找到使损失函数最小化的模型参数值。 经过训练后&#xff0c;我们将需要使用这些参数来做出未来的预测。 此外&#xff0c;有时我们希望提取参数&#xff0c;以便在其他环境中复用它们&#xff0c; 将模型保存下来&#xff0c;以便它可以在…

【USRP X410】LabVIEW参考架构软件,用于使用Ettus USRP X410对无线系统进行原型验证

LabVIEW参考架构软件&#xff0c;用于使用Ettus USRP X410对无线系统进行原型验证 设备 1 MHz to 7.2 GHz&#xff0c;400 MHz带宽&#xff0c;GPS驯服OCXO&#xff0c;USRP软件无线电设备 - Ettus USRP X410集成硬件和软件&#xff0c;可帮助您制作高性能无线系统的原型&…

500万PV的网站需要多少台服务器?

1. 衡量业务量的指标 衡量业务量的指标项有很多&#xff0c;比如&#xff0c;常见Web类应用中的PV、UV、IP。而比较贴近业务的指标项就是大家通常所说的业务用户数。但这个用户数比较笼统&#xff0c;其实和真实访问量有比较大的差距&#xff0c;所以为了更贴近实际业务量及压力…

Django_使用redis缓存数据

目录 一、配置redis 二、缓存Django的默认session 三、使用django的缓存机制缓存数据 四、自定义缓存数据 源码等资料获取方法 一、配置redis 在settings中添加配置参数 # Django的缓存配置 CACHES {"default": {"BACKEND": "django_redis.ca…

【网站开发】jq (jquery)实现瀑布流布局

要实现网站瀑布流效果&#xff0c;可以使用HTML、CSS和jquery来完成。下面是一种常见的实现方式&#xff1a; 注意要引入jQuery库。 代码如下&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title>…

【Unity面试篇】Unity 面试题总结甄选 |Unity进阶篇 | ❤️持续更新❤️

前言 关于Unity面试题相关的所有知识点&#xff1a;&#x1f431;‍&#x1f3cd;2023年Unity面试题大全&#xff0c;共十万字面试题总结【收藏一篇足够面试&#xff0c;持续更新】为了方便大家可以重点复习某个模块&#xff0c;所以将各方面的知识点进行了拆分并更新整理了新…

2023上半年工作总结

目录 一、目标达成情况总结&#xff1a; 二、工作/学习成果总结&#xff1a; 三、下半年规划总结&#xff1a; 一、目标达成情况总结&#xff1a; 其实我并没有给自己定多少目标&#xff0c;特别上长期的目标很少制定&#xff0c;总体来说2023年上班过的很平淡&#xff0c;波…

【Spring Boot】Spring Boot日志详情:基于lombok的日志输出

文章目录 1. 何为日志文件&#xff1f;2. 日志文件的作用3. 日志文件的基本使用3.1 Spring Boot中的日志3.2 自定义日志打印 4 日志级别4.1 日志级别的作用4.2 日志级别的分类4.3 日志级别的设置 5 日志持久化6 基于lombok的日志输出6.1 lombok 简单输出日志案例6.2 浅谈 lombo…

基于单片机空气质量检测二氧化碳 一氧化碳温湿度PM2.5检测系统的设计与实现

功能介绍 以51单片机作为主控系统&#xff1b;对空气空气中有毒有害气体进行监测&#xff1b;使用LCD1602液晶显示&#xff0c;采集到的PM2.5值通过单片机串口传输&#xff1b;通过传感器对室内PM2.5粉尘进行检查&#xff1b;通过按键设置的上限值&#xff1b;当检测到有毒气体…

SpringBoot2+Vue2实战(十八)修改密码

一、修改密码&#xff1a; Header.vue <el-dropdown-item style"font-size: 14px; padding: 5px 0"><router-link to"/password" style"text-decoration: none">修改密码</router-link></el-dropdown-item> router/i…

【网络】socket——预备知识 | 套接字 | UDP网络通信

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《网络》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 在前面本喵对网络的整体轮廓做了一个大概的介绍&#xff0c;比如分层&#xff0c;协议等等内容&#x…

Unity 之 超级详细的隐私问题解决方案

Unity 之 助力游戏增长 -- 解决隐私问题 一&#xff0c;平台测试隐私问题二&#xff0c;解决方式一2.1 勾选自定义Mainifest2.2 修改自定义Mainifest2.3 隐私协议弹窗逻辑 三&#xff0c;解决方式二3.1 导出安卓工程3.2 创建上层Activity3.3 配置AndroidManifest 四&#xff0…

第一百零六天学习记录:数据结构与算法基础:单链表(王卓教学视频)

线性表的链式表示和实现 结点在存储器中的位置是任意的&#xff0c;即逻辑上相邻的数据元素在物理上不一定相邻 线性表的链式表示又称为非顺序映像或链式映像。 用一组物理位置任意的存储单元来存放线性表的数据元素。 这组存储单元既可以是连续的&#xff0c;也可以是不连续的…

论文笔记--Goat: Fine-tuned LLaMA Outperforms GPT-4 on Arithmetic Tasks

论文笔记--Goat: Fine-tuned LLaMA Outperforms GPT-4 on Arithmetic Tasks 1. 文章简介2. 文章概括3 文章重点技术3.1 LLM的选择3.2 算数任务的可学习性(learnability)3.3 大模型的加减乘除 4. 数值实验结果5. 文章亮点6. 原文传送门7. References 1. 文章简介 标题&#xff…

java中使用HttpRequest发送请求调用自己的接口

(539条消息) java中使用HttpRequest发送请求_java httprequest_thankful_chn的博客-CSDN博客 <dependency><groupId>com.github.kevinsawicki</groupId><artifactId>http-request</artifactId><version>5.6</version></dependenc…

网络的构成要素【图解TCP/IP(笔记七)】

文章目录 网络的构成要素通信媒介与数据链路网卡中继器网桥/2层交换机路由器/3层交换机4&#xff5e;7层交换机网关各种设备及其对应网络分层概览 网络的构成要素 通信媒介与数据链路 计算机之间通过电缆相互连接。电缆可以分为很多种&#xff0c;包括双绞线电缆、光纤电缆、同…