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中添加以下依赖:
org.activiti activiti-spring-boot-starter 7.1.0.M6
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 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(),
(图片来源网络,侵删)
(图片来源网络,侵删)
(图片来源网络,侵删)
免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。