Spring Boot整合Activiti工作流详解
1. 概述
Spring Boot与Activiti的整合可以大大简化工作流应用的开发。Spring Boot提供了自动配置和依赖管理,而Activiti则提供了强大的工作流功能。通过整合,我们可以快速构建基于工作流的业务系统。
本文将详细介绍Spring Boot与Activiti的整合方法,并通过一个请假流程的例子来演示整合的效果。
2. 环境准备
2.1 开发环境
- JDK 1.8+
- Maven 3.5+
- Spring Boot 2.3.x (兼容Activiti 7.x)
- Activiti 7.x
- MySQL 5.7+
- IDE(IntelliJ IDEA或Eclipse)
2.2 Maven依赖
创建一个Spring Boot项目,在pom.xml中添加以下依赖:
<!-- Activiti -->
<dependency><groupId>org.activiti</groupId><artifactId>activiti-spring-boot-starter</artifactId><version>7.1.0.M6</version>
</dependency>
2.3 配置文件
在src/main/resources目录下创建application.yml文件:
# Activiti配置(与Swagger冲突,两者只能开启一个)activiti:# 这里使用create_drop确保表被创建database-schema-update: create_dropdb-history-used: truehistory-level: fullcheck-process-definitions: true# 明确指定流程定义位置process-definition-location-prefix: classpath:/processes/
2.4 安全配置
由于Activiti 7与Spring Security集成,需要创建一个安全配置类:
package com.example.testlogin.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
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 org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Overrideprotected void configure(HttpSecurity http) throws Exception {http.cors().configurationSource(corsConfigurationSource()) // 使用配置的CORS策略.and().csrf().disable() // 禁用CSRF保护.authorizeRequests().antMatchers("/api/login").permitAll().antMatchers("/api/excel/upload").permitAll().antMatchers("/api/excel/export").permitAll().antMatchers("/api/test/select").permitAll().antMatchers("/api/test/delete").permitAll().antMatchers("/api/test/upload").permitAll().antMatchers("/api/test/download").permitAll().antMatchers("/api/test/exportFile").permitAll().antMatchers("/api/test/importFile").permitAll().antMatchers("/api/test/importSheetFile").permitAll().antMatchers("/api/test/exportSheetFile").permitAll().antMatchers("/api/test/fillFile").permitAll().antMatchers("/api/test/fillFileList").permitAll().antMatchers("/api/test/fillFileSheetList").permitAll().antMatchers("/api/test/downloadFile").permitAll().antMatchers("/api/message/send").permitAll().antMatchers("/api/modbus/holdingRegisters").permitAll().antMatchers("/time/count").permitAll().antMatchers("/pic/**").permitAll().antMatchers("/files/**").permitAll().antMatchers("/swagger-ui/**", "/swagger-resources/**", "/v3/api-docs/**", "/v2/api-docs/**", "/webjars/**").permitAll()// 允许访问swagger.antMatchers("/webSocket/image").permitAll()// 允许未认证用户访问.antMatchers("/chat").permitAll() // 允许访问WebSocket的chat端点.antMatchers("/chat/*").permitAll() // 允许访问WebSocket的chat端点// 重要修改:保护Activiti相关资源,要求认证.antMatchers("/activiti/**", "/leave/**", "/process/**", "/task/**").authenticated()// 静态资源可以公开访问.antMatchers("/css/**", "/js/**", "/images/**").permitAll()// 主页和其他公共页面.antMatchers("/", "/index", "/home").permitAll()// 其他请求需要认证.anyRequest().authenticated().and()// 使用默认登录页面 - 移除loginPage配置.formLogin().defaultSuccessUrl("/index").permitAll().and().logout().logoutSuccessUrl("/login?logout").permitAll();// 允许HTTP基本认证(Activiti Rest API可能需要)http.httpBasic();// 允许iframe嵌入,用于Activiti表单和流程设计器http.headers().frameOptions().sameOrigin();}@Beanpublic UserDetailsService userDetailsService() {// 创建用户InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();// 添加用户:员工String[][] usersGroupsAndRoles = {{"jack", "password", "ROLE_ACTIVITI_USER", "GROUP_employees"},{"rose", "password", "ROLE_ACTIVITI_USER", "GROUP_employees"},{"tom", "password", "ROLE_ACTIVITI_USER", "GROUP_employees"},{"jerry", "password", "ROLE_ACTIVITI_USER", "GROUP_employees"},{"manager", "password", "ROLE_ACTIVITI_USER", "GROUP_managers"},{"hr", "password", "ROLE_ACTIVITI_USER", "GROUP_hr"},{"admin", "password", "ROLE_ACTIVITI_ADMIN", "ROLE_ACTIVITI_USER"},};for (String[] user : usersGroupsAndRoles) {List<String> authoritiesStrings = Arrays.asList(Arrays.copyOfRange(user, 2, user.length));inMemoryUserDetailsManager.createUser(new User(user[0],passwordEncoder().encode(user[1]),authoritiesStrings.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList())));}// 添加匿名用户inMemoryUserDetailsManager.createUser(User.withUsername("anonymousUser").password("") // 空密码.authorities("ROLE_ANONYMOUS").build());return inMemoryUserDetailsManager;}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}}
2.5 启动类配置
创建Spring Boot启动类:
package com.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class ActivitiSpringBootDemoApplication {public static void main(String[] args) {SpringApplication.run(ActivitiSpringBootDemoApplication.class, args);}
}
3. 业务模型设计
3.1 请假实体类
创建一个请假实体类:
package com.example.testlogin.entity;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
@Data
public class LeaveRequest implements Serializable {private static final long serialVersionUID = 1L;private Long id;private String userId;private String userName;private Date startDate;private Date endDate;private Integer days;private String reason;private String leaveType;// 请假类型:年假、病假、事假等private String status; // 状态:草稿、提交、审批中、已批准、已拒绝private String processInstanceId; // 流程实例ID
}
3.2 数据访问层
创建请假数据访问接口:
package com.example.testlogin.repository;
import com.example.testlogin.entity.LeaveRequest;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Repository;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;@Repository
public class LeaveRequestRepository {private final JdbcTemplate jdbcTemplate;public LeaveRequestRepository(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}// 创建请假表public void createTable() {jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS leave_request (" +"id BIGINT AUTO_INCREMENT PRIMARY KEY," +"user_id VARCHAR(100) NOT NULL," +"user_name VARCHAR(100) NOT NULL," +"start_date DATE NOT NULL," +"end_date DATE NOT NULL," +"days INT NOT NULL," +"leave_type DATE NOT NULL," +"reason VARCHAR(500)," +"status VARCHAR(50) NOT NULL," +"process_instance_id VARCHAR(100)" +")");}// 保存请假申请public void save(LeaveRequest leaveRequest) {try {System.out.println("保存请假申请: ID=" + leaveRequest.getId() +", 申请人=" + leaveRequest.getUserName() +", 状态=" + leaveRequest.getStatus());if (leaveRequest.getId() == null) {String sql = "INSERT INTO leave_request (user_id, user_name, start_date, end_date,leave_type, days, reason, status, process_instance_id) " +"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";System.out.println("执行SQL: " + sql);System.out.println("参数: [" +leaveRequest.getUserId() + ", " +leaveRequest.getUserName() + ", " +leaveRequest.getStartDate() + ", " +leaveRequest.getEndDate() + ", " +leaveRequest.getLeaveType() + ", " +leaveRequest.getDays() + ", " +leaveRequest.getReason() + ", " +leaveRequest.getStatus() + ", " +leaveRequest.getProcessInstanceId() + "]");jdbcTemplate.update(sql,leaveRequest.getUserId(),leaveRequest.getUserName(),leaveRequest.getStartDate(),leaveRequest.getEndDate(),leaveRequest.getLeaveType(),leaveRequest.getDays(),leaveRequest.getReason(),leaveRequest.getStatus(),leaveRequest.getProcessInstanceId());System.out.println("插入请假申请成功");} else {String sql = "UPDATE leave_request SET user_id=?, user_name=?, start_date=?, end_date=?, leave_type=?, days=?, reason=?, status=?, process_instance_id=? " +"WHERE id=?";System.out.println("执行SQL: " + sql);System.out.println("参数: [" +leaveRequest.getUserId() + ", " +leaveRequest.getUserName() + ", " +leaveRequest.getStartDate() + ", " +leaveRequest.getEndDate() + ", " +leaveRequest.getLeaveType() + ", " +leaveRequest.getDays() + ", " +leaveRequest.getReason() + ", " +leaveRequest.getStatus() + ", " +leaveRequest.getProcessInstanceId() + ", " +leaveRequest.getId() + "]");int rowsAffected = jdbcTemplate.update(sql,leaveRequest.getUserId(),leaveRequest.getUserName(),相关文章:
Spring Boot整合Activiti工作流详解
1. 概述 Spring Boot与Activiti的整合可以大大简化工作流应用的开发。Spring Boot提供了自动配置和依赖管理,而Activiti则提供了强大的工作流功能。通过整合,我们可以快速构建基于工作流的业务系统。 本文将详细介绍Spring Boot与Activiti的整合方法,并通过一个请假流程的…...
C# System.Text.Encoding 使用详解
总目录 前言 在C#编程中,处理字符串和字节数组之间的转换是一个常见的任务。System.Text.Encoding类及其派生类提供了丰富的功能,帮助开发者实现不同字符编码之间的转换。本文将详细讲解System.Text.Encoding类的使用方法,包括常用编码的介绍…...
力扣刷题-热题100题-第23题(c++、python)
206. 反转链表 - 力扣(LeetCode)https://leetcode.cn/problems/reverse-linked-list/solutions/551596/fan-zhuan-lian-biao-by-leetcode-solution-d1k2/?envTypestudy-plan-v2&envIdtop-100-liked 常规法 记录前一个指针,当前指针&am…...
机器学习-基于KNN算法手动实现kd树
目录 一、概括 二、KD树的构建流程 1.循环选轴 2.选择分裂点 三、kd树的查询 1.输入我们要搜索的点 2.递归向下遍历: 3.记录最近点 4.回溯父节点: 四、KD树的优化与变种: 五、KD树代码: 上一章我们将了机器学习-手搓KN…...
Unity Shader 的编程流程和结构
Unity Shader 的编程流程和结构 Unity Shader 的编程主要由以下三个核心部分组成:Properties(属性)、SubShader(子着色器) 和 Fallback(回退)。下面是它们的具体作用和结构: 1. Pr…...
vue3 项目的最新eslint9 + prettier 配置
注意:eslint目前升级到9版本了 在 ESLint v9 中,配置文件已经从 .eslintrc 迁移到了 eslint.config.js 配置的方式和之前的方式不太一样了!!!! 详见自己的语雀文档:5、新版eslint9prettier 配…...
SAP GUI Script for C# SAP脚本开发快速指南与默认主题问题
SAP GUI Script for C# 快速指南 SAP 脚本的快速使用与设置. 解决使用SAP脚本执行后,默认打开的SAP是经典主题的问题 1. 解决默认主题问题 如果您使用的是SAP GUI 740,并遇到无法打开对话框的问题,请先将主题设置为经典主题(Classic Theme…...
JAVA泛型的作用
1. 类型安全(Type Safety) 在泛型出现之前,集合类(如 ArrayList、HashMap)只能存储 Object 类型元素,导致以下问题: 问题:从集合中取出元素时,需手动强制类型转…...
Git Flow 分支管理策略
优势 清晰的分支结构:每个分支都有明确的用途,便于团队协作。 稳定的 master 分支:生产环境代码始终稳定。 灵活的发布管理:通过发布分支和热修复分支,可以灵活管理版本发布和紧急修复。 主要分支 master 分支 代表…...
FFmpeg + Qt 简单视频播放器代码
一个基于 FFmpeg 4.x 和 Qt 的简单视频播放器代码示例,实现视频解码和渲染到 Qt 窗口的功能。 1)ffmpeg库界面,视频解码支持软解和硬解方式。 2)QImage/QPixmap显示视频图片。 1. Qt 项目配置(.pro 文件&…...
Unity跨平台构建快速回顾
知识点来源:人间自有韬哥在,豆包 目录 一、发布应用程序1. 修改发布必备设置1.1 打开设置面板1.2 修改公司名、游戏项目名、版本号和默认图标1.3 修改 Package Name 和 Minimum API Level 2. 发布应用程序2.1 配置 Build Settings2.2 选择发布选项2.3 构…...
【嵌入式学习2】内存管理
## C语言编译过程 预处理:宏定义展开、头文件展开、条件编译,这里并不会检查语法,将#include #define这些头文件内容插入到源码中 gcc -E main.c -o main.i 编译:检查语法,将预处理后文件编译生成汇编文件ÿ…...
密码学(Public-Key Cryptography and Discrete Logarithms)
Public-Key Cryptography and Discrete Logarithms Discrete Logarithm 核心概念:离散对数是密码学中一个重要的数学问题,特别是在有限域和循环群中。它基于指数运算在某些群中是单向函数这一特性。也就是说,给定一个群 G G G和一个生成元 …...
TDengine又新增一可视化工具 Perspective
概述 Perspective 是一款开源且强大的数据可视化库,由 Prospective.co 开发,运用 WebAssembly 和 Web Workers 技术,在 Web 应用中实现交互式实时数据分析,能在浏览器端提供高性能可视化能力。借助它,开发者可构建实时…...
【Linux文件IO】Linux中标准IO的API的描述和基本用法
Linux中标准IO的API的描述和基本用法 一、标准IO相关API1、文件的打开和关闭示例代码: 2、文件的读写示例代码:用标准IO(fread、fwrite)实现文件拷贝(任何文件均可拷贝) 3、文件偏移设置示例代码: 4、fgets fputs fget…...
深度学习篇---PaddleDetectionPaddleOCR
文章目录 前言1.代码2.代码介绍2.1 **导入模块**2.2 **配置区域**2.3 ExpressInfoProcessor类2.4 **主程序**: 3.使用说明3.1环境准备3.2模型准备3.3数据库初始化3.4串口配置3.5信息提取优化3.6注意事项 前言 本文简单介绍了PaddleDetection和PaddleOCR相结合的示例…...
Ant Design Vue Select 选择器 全选 功能
Vue.js的组件库Ant Design Vue Select 选择器没有全选功能,如下图所示: 在项目中,我们自己实现了全选和清空功能,如下所示: 代码如下所示: <!--* 参数配置 - 风力发电 - 曲线图 * 猴王软件学院 - 大强 …...
系统与网络安全------网络应用基础(1)
资料整理于网络资料、书本资料、AI,仅供个人学习参考。 TCP/IP协议及配置 概述 TCP/IP协议族 计算机之间进行通信时必须共同遵循的一种通信规定 最广泛使用的通信协议的集合 包括大量Internet应用中的标准协议 支持跨网络架构、跨操作系统平台的数据通信 主机…...
ZIP_STORED和ZIP_LZMA没有compresslevel参数!
在使用py的zipfile库进行压缩的时候,有这么一个函数: def write(self, filename, arcnameNone,compress_typeNone, compresslevelNone): 一般我们在压缩文件进去的时候都是用这个函数的; 对于compresslevel这个函数,它是用来指…...
坦克大战(c++)
今天我给大家分享一个c游戏。 废话不多说,作品展示: #include <stdio.h> #include <windows.h> #include <time.h> //里规格:长39*278 (真坐标)(假坐标宽为39) 高39 //外规格:长…...
论文阅读:2023 EMNLP SeqXGPT: Sentence-level AI-generated text detection
总目录 大模型安全相关研究:https://blog.csdn.net/WhiffeYF/article/details/142132328 SeqXGPT: Sentence-level AI-generated text detection https://aclanthology.org/2023.emnlp-main.73/ https://github.com/Jihuai-wpy/SeqXGPT https://www.doubao.com/chat/21003…...
JDK 24 发布,新特性解读!
一、版本演进与技术格局新动向 北京时间3月20日,Oracle正式发布Java SE 24。作为继Java 21之后的第三个非LTS版本,其技术革新力度远超预期——共集成24项JEP提案,相当于Java 22(12项)与Java 23(12项&#…...
k8s中service概述(二)NodePort
NodePort 是 Kubernetes 中一种用于对外暴露服务的 Service 类型。它通过在集群的每个节点上开放一个静态端口(NodePort),使得外部用户可以通过节点的 IP 地址和该端口访问集群内部的服务。以下是关于 NodePort Service 的详细说明࿱…...
Oracle归档配置及检查
配置归档位置到 USE_DB_RECOVERY_FILE_DEST,并设置存储大小 startup mount; !mkdir /db/archivelog ALTER SYSTEM SET db_recovery_file_dest_size100G SCOPEBOTH; ALTER SYSTEM SET db_recovery_file_dest/db/archivelog SCOPEBOTH; ALTER SYSTEM SET log_archive…...
计算机二级:函数基础题
函数基础题 第一题 rinput("请输入半径:") c3.1415926*r*2 print("{:.0f}".format(c))输出: Type Error第二题 a7 b2 print(a%2)输出 1第三题 ab4 def my_ab(ab,xy):abpow(ab,xy)print(ab,end"\n") my_ab(ab,2)prin…...
Python爬虫-爬取AliExpress商品搜索词排名数据
前言 本文是该专栏的第49篇,后面会持续分享python爬虫干货知识,记得关注。 本文,笔者以AliExpress平台为例。基于Python爬虫,通过某个指定的“搜索关键词”,批量获取该“搜索关键词”的商品排名数据。 具体实现思路和详细逻辑,笔者将在正文结合完整代码进行详细介绍。废…...
AI 时代,我们需要什么样的数据库?
AI 时代,我们需要什么样的数据库? 人工智能正在悄然改变软件开发的方式。过去一年间,诸如 GitHub Spark、Replit 和 Bolt 等新兴 AI 工具层出不穷,能够快速生成简单的前端应用,甚至无需传统意义上的后端服务就能部署上…...
刷机维修进阶教程-----adb禁用错了系统app导致无法开机 如何保数据无损恢复机型
在刷机维修过程中 。我们会遇到一些由于客户使用adb指令来禁用手机app而导致手机无法开机进入系统的故障机型。通常此类问题机型有好几种解决方法。但如果客户需要保数据来恢复机型。其实操作也是很简单的.还有类似误删除应用导致不开机等等如何保数据。 通过博文了解💝💝�…...
Vue3 实战:基于 mxGraph 与 WebSocket 的动态流程图构建
本文将详细介绍如何在 Vue3 项目中集成 mxGraph 可视化库,并通过 WebSocket 实现画布元素的实时更新。适合有 Vue 基础的前端开发者学习参考。 一、技术栈准备 Vue3:采用 Composition API 开发mxGraph:JavaScript 流程图库(版本 …...
Ubuntu AX200 iwlwifi-cc-46.3cfab8da.0.tgz无法下载的解决办法
文章目录 前言一、检查网卡是否被识别二、确认内核模块是否可用1.AX200 wifi 要求内核5.12.检查 iwlwifi.ko 是否存在:3.如果未找到,可能是内核模块未正确生成。尝试安装 linux-modules-extra:4.再次检查 iwlwifi.ko 是否存在:5.确…...
