RuoYi集成Activiti:工作流引擎与流程设计器全指南
引言:告别繁琐审批,拥抱可视化工作流
你是否还在为RuoYi项目中手动处理审批流程而烦恼?是否希望通过可视化界面设计复杂的业务流程?本文将带你从零开始,在RuoYi框架中集成Activiti工作流引擎与流程设计器,彻底解决传统审批流程开发效率低、维护成本高的痛点。读完本文,你将获得:
- 完整的Activiti与RuoYi集成方案
- 可视化流程设计器的部署与使用
- 实战级请假流程开发案例
- 常见集成问题的解决方案
环境准备与版本兼容性分析
基础环境要求
| 环境/工具 | 版本要求 | 备注 |
|---|---|---|
| JDK | 1.8+ | 与RuoYi保持一致 |
| Maven | 3.6+ | 确保依赖下载正常 |
| MySQL | 5.7+ | Activiti需InnoDB引擎 |
| RuoYi | 4.8.1 | 本文基于此版本开发 |
| Activiti | 7.1.0.M6 | 兼容Spring Boot 2.5.x |
版本兼容性矩阵
兼容性说明:Activiti 7.1.0.M6与Spring Boot 2.5.x存在最佳兼容性,需排除Spring Security依赖避免与RuoYi的Shiro框架冲突。
Activiti依赖集成与配置
添加Maven依赖
修改项目根目录下的pom.xml,在dependencyManagement节点添加Activiti版本控制:
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-dependencies</artifactId>
<version>7.1.0.M6</version>
<type>pom</type>
<scope>import</scope>
</dependency>
在ruoyi-admin模块的pom.xml中添加具体依赖:
<dependencies>
<!-- Activiti核心依赖 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<exclusions>
<!-- 排除Spring Security依赖 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Activiti Modeler依赖 -->
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-modeler</artifactId>
<version>6.0.0</version>
<exclusions>
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
配置Activiti引擎
在ruoyi-admin/src/main/resources/application.yml中添加Activiti配置:
spring:
activiti:
# 自动更新数据库结构
database-schema-update: true
# 历史记录级别
history-level: full
# 校验流程定义文件
check-process-definitions: true
# 流程定义文件位置
process-definition-location-prefix: classpath:/processes/
# 使用历史表
db-history-used: true
# 关闭异步执行器
async-executor-activate: false
数据库表自动生成
启动项目后,Activiti会自动在数据库中创建28张工作流相关表,主要包括:
| 表名前缀 | 用途 |
|---|---|
| ACT_RE_* | 流程存储库 |
| ACT_RU_* | 运行时数据 |
| ACT_HI_* | 历史数据 |
| ACT_GE_* | 通用数据 |
注意:确保数据库用户有足够权限创建表,首次启动会较慢,因为需要执行DDL语句。
Activiti Modeler集成与配置
部署Modeler静态资源
- 创建
ruoyi-admin/src/main/resources/static/modeler目录 - 从Activiti源码或Maven依赖中提取Modeler静态资源(
editor-app、modeler.html等) - 修改
modeler.html中的CDN链接为国内镜像:
<!-- 替换为国内CDN -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/bootstrap/3.4.1/js/bootstrap.min.js"></script>
<script src="https://cdn.bootcdn.net/ajax/libs/angular.js/1.8.2/angular.min.js"></script>
配置Modeler Servlet
创建Activiti配置类:
@Configuration
public class ActivitiConfig {
@Bean
public ServletRegistrationBean<DispatcherServlet> activitiModelerServlet() {
DispatcherServlet servlet = new DispatcherServlet();
WebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
servlet.setApplicationContext(applicationContext);
ServletRegistrationBean<DispatcherServlet> registrationBean = new ServletRegistrationBean<>(servlet, "/modeler/*");
registrationBean.setName("activitiModeler");
registrationBean.setLoadOnStartup(1);
return registrationBean;
}
}
集成RuoYi权限控制
修改modeler.html添加RuoYi的登录验证:
// 在页面加载前验证登录状态
$.ajax({
url: '/system/user/getInfo',
type: 'get',
async: false,
success: function(data) {
if (data.code !== 200) {
window.location.href = '/login';
}
}
});
RuoYi菜单集成与前端配置
添加菜单记录
执行SQL插入菜单数据:
INSERT INTO `sys_menu` (`menu_id`, `menu_name`, `parent_id`, `order_num`, `path`, `component`, `is_frame`, `is_cache`, `menu_type`, `visible`, `status`, `perms`, `icon`, `create_by`, `create_time`, `update_by`, `update_time`, `remark`) VALUES
(1000, '工作流管理', 0, 10, 'workflow', NULL, 1, 0, 'M', '0', '0', NULL, 'flow', 'admin', '2025-09-08', 'admin', '2025-09-08', '工作流管理模块'),
(1001, '流程设计', 1000, 1, 'modeler', 'modeler/modeler.html', 0, 0, 'C', '0', '0', 'workflow:model:view', 'form', 'admin', '2025-09-08', 'admin', '2025-09-08', '流程设计器');
配置菜单权限
添加角色权限:
INSERT INTO `sys_role_menu` (`role_id`, `menu_id`) VALUES (1, 1000), (1, 1001);
流程设计与部署实战
创建第一个流程定义
- 访问RuoYi系统,进入「工作流管理」→「流程设计」
- 点击「新建流程」,输入流程名称"请假流程"
- 使用拖拽方式设计流程:
- 开始事件 → 用户任务(申请人) → 用户任务(部门经理) → 用户任务(总经理) → 结束事件
- 设置每个用户任务的办理人:
- 申请人:
${applyUserId} - 部门经理:
${deptManagerId} - 总经理:
${gmId}
- 申请人:
流程定义文件示例
保存后的流程定义文件leave.bpmn20.xml位于src/main/resources/processes/:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL http://www.omg.org/spec/BPMN/2.0/20100501/BPMN20.xsd"
id="Definitions_1" targetNamespace="http://activiti.org/test">
<process id="leaveProcess" name="请假流程" isExecutable="true">
<startEvent id="startEvent1"/>
<userTask id="applyTask" name="申请人提交" activiti:assignee="${applyUserId}"/>
<userTask id="deptApproveTask" name="部门经理审批" activiti:assignee="${deptManagerId}"/>
<userTask id="gmApproveTask" name="总经理审批" activiti:assignee="${gmId}"/>
<endEvent id="endEvent1"/>
<sequenceFlow id="flow1" sourceRef="startEvent1" targetRef="applyTask"/>
<sequenceFlow id="flow2" sourceRef="applyTask" targetRef="deptApproveTask"/>
<sequenceFlow id="flow3" sourceRef="deptApproveTask" targetRef="gmApproveTask"/>
<sequenceFlow id="flow4" sourceRef="gmApproveTask" targetRef="endEvent1"/>
</process>
</definitions>
工作流业务代码实现
流程服务接口
public interface IProcessService {
/**
* 部署流程
*/
AjaxResult deployProcess(MultipartFile file);
/**
* 启动流程实例
*/
String startProcessInstance(String processKey, Map<String, Object> variables);
/**
* 查询个人任务
*/
List<TaskVO> queryPersonalTasks(Long userId);
/**
* 完成任务
*/
AjaxResult completeTask(String taskId, Map<String, Object> variables);
}
服务实现类
@Service
public class ProcessServiceImpl implements IProcessService {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
@Autowired
private TaskService taskService;
@Override
public AjaxResult deployProcess(MultipartFile file) {
try {
String fileName = file.getOriginalFilename();
Deployment deployment = repositoryService.createDeployment()
.addInputStream(fileName, file.getInputStream())
.name(fileName)
.deploy();
return AjaxResult.success("部署成功", deployment.getId());
} catch (Exception e) {
return AjaxResult.error("部署失败:" + e.getMessage());
}
}
@Override
public String startProcessInstance(String processKey, Map<String, Object> variables) {
ProcessInstance instance = runtimeService.startProcessInstanceByKey(processKey, variables);
return instance.getId();
}
@Override
public List<TaskVO> queryPersonalTasks(Long userId) {
List<Task> tasks = taskService.createTaskQuery()
.taskAssignee(userId.toString())
.orderByTaskCreateTime().desc()
.list();
return tasks.stream().map(task -> {
TaskVO vo = new TaskVO();
vo.setTaskId(task.getId());
vo.setTaskName(task.getName());
vo.setProcessInstanceId(task.getProcessInstanceId());
vo.setCreateTime(task.getCreateTime());
return vo;
}).collect(Collectors.toList());
}
@Override
public AjaxResult completeTask(String taskId, Map<String, Object> variables) {
try {
taskService.complete(taskId, variables);
return AjaxResult.success("任务已完成");
} catch (Exception e) {
return AjaxResult.error("任务完成失败:" + e.getMessage());
}
}
}
控制器实现
@RestController
@RequestMapping("/workflow/process")
public class ProcessController extends BaseController {
@Autowired
private IProcessService processService;
@PostMapping("/deploy")
public AjaxResult deployProcess(@RequestParam("file") MultipartFile file) {
return processService.deployProcess(file);
}
@PostMapping("/start")
public AjaxResult startProcess(@RequestParam String processKey,
@RequestBody Map<String, Object> variables) {
String instanceId = processService.startProcessInstance(processKey, variables);
return AjaxResult.success("流程已启动", instanceId);
}
@GetMapping("/tasks")
public AjaxResult queryTasks() {
Long userId = ShiroUtils.getUserId();
List<TaskVO> tasks = processService.queryPersonalTasks(userId);
return AjaxResult.success(tasks);
}
@PostMapping("/complete/{taskId}")
public AjaxResult completeTask(@PathVariable String taskId,
@RequestBody Map<String, Object> variables) {
return processService.completeTask(taskId, variables);
}
}
实战案例:请假流程实现
业务流程设计
前端页面实现
<div class="row">
<div class="col-sm-12">
<div class="card">
<div class="card-body">
<form id="leaveForm">
<div class="form-group">
<label>请假类型</label>
<select class="form-control" name="leaveType">
<option value="annual">年假</option>
<option value="sick">病假</option>
<option value="personal">事假</option>
</select>
</div>
<div class="form-group">
<label>请假天数</label>
<input type="number" class="form-control" name="days" required>
</div>
<div class="form-group">
<label>请假理由</label>
<textarea class="form-control" name="reason" rows="3"></textarea>
</div>
<button type="button" class="btn btn-primary" onclick="submitLeave()">提交申请</button>
</form>
</div>
</div>
</div>
</div>
<script>
function submitLeave() {
var formData = $('#leaveForm').serializeObject();
// 获取流程变量
var variables = {
applyUserId: [[${shiro:getUserId()}]],
deptManagerId: [[${deptManagerId}]],
gmId: [[${gmId}]],
leaveType: formData.leaveType,
days: formData.days,
reason: formData.reason
};
$.ajax({
url: '/workflow/process/start?processKey=leaveProcess',
type: 'post',
contentType: 'application/json',
data: JSON.stringify(variables),
success: function(data) {
if (data.code === 200) {
layer.msg('请假申请已提交', {icon: 1});
setTimeout(function() {
parent.location.reload();
}, 1000);
} else {
layer.msg(data.msg, {icon: 2});
}
}
});
}
</script>
流程测试与验证
- 以普通用户登录,提交请假申请
- 切换到部门经理账号,查看待办任务并审批
- 切换到总经理账号,查看待办任务并审批
- 查看流程历史记录,验证流程是否按预期执行
常见问题与解决方案
| 问题 | 解决方案 |
|---|---|
| Spring Security冲突 | 排除Activiti依赖中的Spring Security模块 |
| Modeler无法加载 | 检查静态资源路径和AngularJS版本兼容性 |
| 中文乱码 | 在pom.xml中配置资源过滤,设置UTF-8编码 |
| 流程定义部署失败 | 检查BPMN文件格式,确保使用正确的命名空间 |
| 任务分配不生效 | 确保Assignee表达式正确,用户ID转换为字符串 |
总结与展望
通过本文的步骤,我们成功在RuoYi框架中集成了Activiti工作流引擎和流程设计器,实现了可视化流程设计、流程部署、任务管理等核心功能。工作流技术的引入,能够显著提升企业级应用中业务流程的灵活性和可维护性。
未来可以进一步扩展:
- 集成表单设计器,实现动态表单与流程的绑定
- 添加流程监控与统计功能
- 实现流程版本管理与流程实例迁移
- 开发移动端工作流审批应用
希望本文能帮助你顺利在RuoYi项目中应用工作流技术,提升系统的业务处理能力。如有任何问题,欢迎在评论区留言讨论。
收藏本文,关注作者获取更多RuoYi高级开发技巧,下期将带来《RuoYi微服务改造实战》。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



