Spring Boot整合Activiti工作流详解

news2025/3/28 6:28:46

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_drop
    db-history-used: true
    history-level: full
    check-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 {

    @Override
    protected 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();

    }

    

    @Bean
    public 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;
    }

    @Bean
    public 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(),
   

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

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

相关文章

力扣刷题-热题100题-第23题(c++、python)

206. 反转链表 - 力扣&#xff08;LeetCode&#xff09;https://leetcode.cn/problems/reverse-linked-list/solutions/551596/fan-zhuan-lian-biao-by-leetcode-solution-d1k2/?envTypestudy-plan-v2&envIdtop-100-liked 常规法 记录前一个指针&#xff0c;当前指针&am…

vue3 项目的最新eslint9 + prettier 配置

注意&#xff1a;eslint目前升级到9版本了 在 ESLint v9 中&#xff0c;配置文件已经从 .eslintrc 迁移到了 eslint.config.js 配置的方式和之前的方式不太一样了&#xff01;&#xff01;&#xff01;&#xff01; 详见自己的语雀文档&#xff1a;5、新版eslint9prettier 配…

SAP GUI Script for C# SAP脚本开发快速指南与默认主题问题

SAP GUI Script for C# 快速指南 SAP 脚本的快速使用与设置. 解决使用SAP脚本执行后,默认打开的SAP是经典主题的问题 1. 解决默认主题问题 如果您使用的是SAP GUI 740&#xff0c;并遇到无法打开对话框的问题&#xff0c;请先将主题设置为经典主题&#xff08;Classic Theme…

FFmpeg + ‌Qt‌ 简单视频播放器代码

一个基于 ‌FFmpeg 4.x‌ 和 ‌Qt‌ 的简单视频播放器代码示例&#xff0c;实现视频解码和渲染到 Qt 窗口的功能。 1&#xff09;ffmpeg库界面&#xff0c;视频解码支持软解和硬解方式。 2&#xff09;QImage/QPixmap显示视频图片。 ‌1. Qt 项目配置&#xff08;.pro 文件&…

Unity跨平台构建快速回顾

知识点来源&#xff1a;人间自有韬哥在&#xff0c;豆包 目录 一、发布应用程序1. 修改发布必备设置1.1 打开设置面板1.2 修改公司名、游戏项目名、版本号和默认图标1.3 修改 Package Name 和 Minimum API Level 2. 发布应用程序2.1 配置 Build Settings2.2 选择发布选项2.3 构…

【嵌入式学习2】内存管理

## C语言编译过程 预处理&#xff1a;宏定义展开、头文件展开、条件编译&#xff0c;这里并不会检查语法&#xff0c;将#include #define这些头文件内容插入到源码中 gcc -E main.c -o main.i 编译&#xff1a;检查语法&#xff0c;将预处理后文件编译生成汇编文件&#xff…

TDengine又新增一可视化工具 Perspective

概述 Perspective 是一款开源且强大的数据可视化库&#xff0c;由 Prospective.co 开发&#xff0c;运用 WebAssembly 和 Web Workers 技术&#xff0c;在 Web 应用中实现交互式实时数据分析&#xff0c;能在浏览器端提供高性能可视化能力。借助它&#xff0c;开发者可构建实时…

【Linux文件IO】Linux中标准IO的API的描述和基本用法

Linux中标准IO的API的描述和基本用法 一、标准IO相关API1、文件的打开和关闭示例代码&#xff1a; 2、文件的读写示例代码&#xff1a;用标准IO&#xff08;fread、fwrite&#xff09;实现文件拷贝(任何文件均可拷贝) 3、文件偏移设置示例代码&#xff1a; 4、fgets fputs fget…

Ant Design Vue Select 选择器 全选 功能

Vue.js的组件库Ant Design Vue Select 选择器没有全选功能&#xff0c;如下图所示&#xff1a; 在项目中&#xff0c;我们自己实现了全选和清空功能&#xff0c;如下所示&#xff1a; 代码如下所示&#xff1a; <!--* 参数配置 - 风力发电 - 曲线图 * 猴王软件学院 - 大强 …

系统与网络安全------网络应用基础(1)

资料整理于网络资料、书本资料、AI&#xff0c;仅供个人学习参考。 TCP/IP协议及配置 概述 TCP/IP协议族 计算机之间进行通信时必须共同遵循的一种通信规定 最广泛使用的通信协议的集合 包括大量Internet应用中的标准协议 支持跨网络架构、跨操作系统平台的数据通信 主机…

JDK 24 发布,新特性解读!

一、版本演进与技术格局新动向 北京时间3月20日&#xff0c;Oracle正式发布Java SE 24。作为继Java 21之后的第三个非LTS版本&#xff0c;其技术革新力度远超预期——共集成24项JEP提案&#xff0c;相当于Java 22&#xff08;12项&#xff09;与Java 23&#xff08;12项&#…

k8s中service概述(二)NodePort

NodePort 是 Kubernetes 中一种用于对外暴露服务的 Service 类型。它通过在集群的每个节点上开放一个静态端口&#xff08;NodePort&#xff09;&#xff0c;使得外部用户可以通过节点的 IP 地址和该端口访问集群内部的服务。以下是关于 NodePort Service 的详细说明&#xff1…

Oracle归档配置及检查

配置归档位置到 USE_DB_RECOVERY_FILE_DEST&#xff0c;并设置存储大小 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…

刷机维修进阶教程-----adb禁用错了系统app导致无法开机 如何保数据无损恢复机型

在刷机维修过程中 。我们会遇到一些由于客户使用adb指令来禁用手机app而导致手机无法开机进入系统的故障机型。通常此类问题机型有好几种解决方法。但如果客户需要保数据来恢复机型。其实操作也是很简单的.还有类似误删除应用导致不开机等等如何保数据。 通过博文了解💝💝�…

Vue3 实战:基于 mxGraph 与 WebSocket 的动态流程图构建

本文将详细介绍如何在 Vue3 项目中集成 mxGraph 可视化库&#xff0c;并通过 WebSocket 实现画布元素的实时更新。适合有 Vue 基础的前端开发者学习参考。 一、技术栈准备 Vue3&#xff1a;采用 Composition API 开发mxGraph&#xff1a;JavaScript 流程图库&#xff08;版本 …

Ubuntu AX200 iwlwifi-cc-46.3cfab8da.0.tgz无法下载的解决办法

文章目录 前言一、检查网卡是否被识别二、确认内核模块是否可用1.AX200 wifi 要求内核5.12.检查 iwlwifi.ko 是否存在&#xff1a;3.如果未找到&#xff0c;可能是内核模块未正确生成。尝试安装 linux-modules-extra&#xff1a;4.再次检查 iwlwifi.ko 是否存在&#xff1a;5.确…

蓝桥杯,利用 Vue.js 构建简易任务管理器

在日常开发中&#xff0c;我们经常需要处理各种任务和计划。一个简单且高效的任务管理器可以帮助我们更好地组织和安排时间。今天&#xff0c;我将向大家展示如何使用 Vue.js 构建一个简易的任务管理器。这个项目不仅能够帮助我们更好地理解 Vue.js 的基本语法和功能&#xff0…

Elasticsearch + Docker:实现容器化部署指南

Elasticsearch是一款强大的分布式搜索和分析引擎&#xff0c;广泛应用于日志分析、全文检索、实时数据分析等场景。而Docker作为一种轻量级的容器化技术&#xff0c;能够帮助开发者快速部署和管理应用。将Elasticsearch与Docker结合&#xff0c;不仅可以简化部署流程&#xff0…

win32汇编环境,网络编程入门之十一

;win32汇编环境,网络编程入门之十一 ;在上一教程里&#xff0c;我们学习了如何读取大容量的网页内容&#xff0c;在这一教程里&#xff0c;我们学习一下如何在wininet或winhttp机制中提取网页中的超链接 ;>>>>>>>>>>>>>>>>>…

穿越之程序员周树人的狂人日记Part3__人机共生纪元

穿越之程序员周树人的狂人日记Part3__人机共生纪元 代码知识点&#xff1a;协程、内存管理、版本控制 故事一【协程陷阱】择偶标准的多核运算 故事二【内存泄漏】中产幻觉的垃圾回收 故事三【版本控制】人设仓库的强制推送 故事四【容器化生存】&#xff1a;员工生存之现状 静夜…