Spring Boot整合Activiti工作流详解

发布于:2025-03-26 ⋅ 阅读:(17) ⋅ 点赞:(0)

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(),